genericdmm: more flexible device discovery

This commit is contained in:
Bert Vermeulen 2012-07-08 23:48:04 +02:00
parent 75337758d8
commit bbb40871c8
1 changed files with 127 additions and 202 deletions

View File

@ -46,9 +46,9 @@ static const int hwcaps[] = {
SR_HWCAP_LIMIT_SAMPLES,
SR_HWCAP_LIMIT_MSEC,
SR_HWCAP_CONTINUOUS,
SR_HWCAP_MODEL,
SR_HWCAP_CONN,
SR_HWCAP_SERIALCOMM,
SR_HWOPT_MODEL,
SR_HWOPT_CONN,
SR_HWOPT_SERIALCOMM,
0,
};
@ -63,6 +63,130 @@ static struct sr_dev_driver *gdi = &genericdmm_driver_info;
SR_PRIV libusb_context *genericdmm_usb_context = NULL;
static GSList *connect_usb(const char *conn)
{
struct sr_dev_inst *sdi;
struct context *ctx;
libusb_device **devlist;
struct libusb_device_descriptor des;
GSList *devices;
GRegex *reg;
GMatchInfo *match;
int vid, pid, bus, addr, devcnt, err, i;
char *mstr;
vid = pid = bus = addr = 0;
reg = g_regex_new(DMM_CONN_USB_VIDPID, 0, 0, NULL);
if (g_regex_match(reg, conn, 0, &match)) {
/* Extract VID. */
if ((mstr = g_match_info_fetch(match, 1)))
vid = strtoul(mstr, NULL, 16);
g_free(mstr);
/* Extract PID. */
if ((mstr = g_match_info_fetch(match, 2)))
pid = strtoul(mstr, NULL, 16);
g_free(mstr);
} else {
g_match_info_unref(match);
g_regex_unref(reg);
reg = g_regex_new(DMM_CONN_USB_BUSADDR, 0, 0, NULL);
if (g_regex_match(reg, conn, 0, &match)) {
/* Extract bus. */
if ((mstr = g_match_info_fetch(match, 0)))
bus = strtoul(mstr, NULL, 16);
g_free(mstr);
/* Extract address. */
if ((mstr = g_match_info_fetch(match, 0)))
addr = strtoul(mstr, NULL, 16);
g_free(mstr);
}
}
g_match_info_unref(match);
g_regex_unref(reg);
if (vid + pid + bus + addr == 0)
return NULL;
if (bus > 64) {
sr_err("invalid bus");
return NULL;
}
if (addr > 127) {
sr_err("invalid address");
return NULL;
}
/* Looks like a valid USB device specification, but is it connected? */
devices = NULL;
libusb_get_device_list(genericdmm_usb_context, &devlist);
for (i = 0; devlist[i]; i++) {
if ((err = libusb_get_device_descriptor(devlist[i], &des))) {
sr_err("genericdmm: failed to get device descriptor: %d", err);
continue;
}
if (vid + pid && (des.idVendor != vid || des.idProduct != pid))
/* VID/PID specified, but no match. */
continue;
if (bus + addr && (
libusb_get_bus_number(devlist[i]) != bus
|| libusb_get_device_address(devlist[i]) != addr))
/* Bus/address specified, but no match. */
continue;
/* Found one. */
if (!(ctx = g_try_malloc0(sizeof(struct context)))) {
sr_err("genericdmm: ctx malloc failed.");
return 0;
}
devcnt = g_slist_length(gdi->instances);
if (!(sdi = sr_dev_inst_new(devcnt, SR_ST_ACTIVE,
"Generic DMM", NULL, NULL))) {
sr_err("genericdmm: sr_dev_inst_new returned NULL.");
return NULL;
}
sdi->priv = ctx;
ctx->usb = sr_usb_dev_inst_new(
libusb_get_bus_number(devlist[i]),
libusb_get_device_address(devlist[i]), NULL);
devices = g_slist_append(devices, sdi);
}
libusb_free_device_list(devlist, 1);
return devices;
}
static GSList *connect_serial(const char *conn, const char *serialcomm)
{
GSList *devices;
devices = NULL;
/* TODO */
sr_dbg("not yet implemented");
return devices;
}
GSList *genericdmm_connect(const char *conn, const char *serialcomm)
{
GSList *devices;
if (serialcomm)
/* Must be a serial port. */
return connect_serial(conn, serialcomm);
if ((devices = connect_usb(conn)))
return devices;
return NULL;
}
static int hw_init(void)
{
@ -282,179 +406,10 @@ static const int *hw_hwcap_get_all(void)
return hwcaps;
}
static int parse_conn_vidpid(struct sr_dev_inst *sdi, const char *conn)
{
struct context *ctx;
libusb_device **devlist;
struct libusb_device_descriptor des;
GRegex *reg;
GMatchInfo *match;
int vid, pid, found, err, i;
char *vidstr, *pidstr;
found = FALSE;
reg = g_regex_new(DMM_CONN_USB_VIDPID, 0, 0, NULL);
if (g_regex_match(reg, conn, 0, &match)) {
/* Extract VID. */
if (!(vidstr = g_match_info_fetch(match, 0))) {
sr_err("failed to fetch VID from regex");
goto err;
}
vid = strtoul(vidstr, NULL, 16);
g_free(vidstr);
if (vid > 0xffff) {
sr_err("invalid VID");
goto err;
}
/* Extract PID. */
if (!(pidstr = g_match_info_fetch(match, 0))) {
sr_err("failed to fetch PID from regex");
goto err;
}
pid = strtoul(pidstr, NULL, 16);
g_free(pidstr);
if (pid > 0xffff) {
sr_err("invalid PID");
goto err;
}
/* Looks like a valid VID:PID, but is it connected? */
libusb_get_device_list(genericdmm_usb_context, &devlist);
for (i = 0; devlist[i]; i++) {
if ((err = libusb_get_device_descriptor(devlist[i], &des))) {
sr_err("genericdmm: failed to get device descriptor: %d", err);
goto err;
}
if (des.idVendor == vid && des.idProduct == pid) {
ctx = sdi->priv;
ctx->usb = sr_usb_dev_inst_new(
libusb_get_bus_number(devlist[i]),
libusb_get_device_address(devlist[i]), NULL);
found = TRUE;
break;
}
}
libusb_free_device_list(devlist, 1);
}
err:
if (match)
g_match_info_unref(match);
g_regex_unref(reg);
return found;
}
static int parse_conn_busaddr(struct sr_dev_inst *sdi, const char *conn)
{
struct context *ctx;
libusb_device **devlist;
struct libusb_device_descriptor des;
GRegex *reg;
GMatchInfo *match;
int bus, addr, found, err, i;
char *busstr, *addrstr;
found = FALSE;
reg = g_regex_new(DMM_CONN_USB_BUSADDR, 0, 0, NULL);
if (g_regex_match(reg, conn, 0, &match)) {
/* Extract bus. */
if (!(busstr = g_match_info_fetch(match, 0))) {
sr_err("failed to fetch bus from regex");
goto err;
}
bus = strtoul(busstr, NULL, 16);
g_free(busstr);
if (bus > 64) {
sr_err("invalid bus");
goto err;
}
/* Extract address. */
if (!(addrstr = g_match_info_fetch(match, 0))) {
sr_err("failed to fetch address from regex");
goto err;
}
addr = strtoul(addrstr, NULL, 16);
g_free(addrstr);
if (addr > 127) {
sr_err("invalid address");
goto err;
}
/* Looks like a valid bus/address, but is it connected? */
libusb_get_device_list(genericdmm_usb_context, &devlist);
for (i = 0; devlist[i]; i++) {
if ((err = libusb_get_device_descriptor(devlist[i], &des))) {
sr_err("genericdmm: failed to get device descriptor: %d", err);
goto err;
}
if (libusb_get_bus_number(devlist[i]) == bus
&& libusb_get_device_address(devlist[i]) == addr) {
ctx = sdi->priv;
ctx->usb = sr_usb_dev_inst_new(bus, addr, NULL);
found = TRUE;
break;
}
}
libusb_free_device_list(devlist, 1);
}
err:
if (match)
g_match_info_unref(match);
g_regex_unref(reg);
return found;
}
static int parse_conn_serial(struct sr_dev_inst *sdi, const char *conn)
{
int found;
found = FALSE;
/* TODO */
return found;
}
static int parse_conn(struct sr_dev_inst *sdi, const char *conn)
{
if (parse_conn_vidpid(sdi, conn))
return SR_OK;
if (parse_conn_busaddr(sdi, conn))
return SR_OK;
if (parse_conn_serial(sdi, conn))
return SR_OK;
sr_err("Invalid connection specification");
return SR_ERR;
}
static int parse_serialcomm(struct sr_dev_inst *sdi, const char *conn)
{
/* TODO */
/* set ctx->serial_* */
return SR_OK;
}
static int hw_dev_config_set(int dev_index, int hwcap, const void *value)
{
struct sr_dev_inst *sdi;
struct context *ctx;
int i;
if (!(sdi = sr_dev_inst_get(gdi->instances, dev_index))) {
sr_err("genericdmm: sdi was NULL.");
@ -483,36 +438,6 @@ static int hw_dev_config_set(int dev_index, int hwcap, const void *value)
sr_dbg("genericdmm: Setting LIMIT_SAMPLES to %" PRIu64 ".",
ctx->limit_samples);
break;
case SR_HWCAP_MODEL:
for (i = 0; dev_profiles[i].model; i++) {
if (!strcasecmp(dev_profiles[i].model, value)) {
ctx->profile = &dev_profiles[i];
/* Frontends access these fields directly, so we
* need to copy them over. */
sdi->vendor = g_strdup(dev_profiles[i].vendor);
sdi->model = g_strdup(dev_profiles[i].model);
/* This is the first time we actually know which
* DMM chip we're talking to, so let's init
* anything specific to it now */
if (ctx->profile->chip->init)
if (ctx->profile->chip->init(ctx) != SR_OK)
return SR_ERR;
break;
}
}
if (!ctx->profile) {
sr_err("unknown model %s", value);
return SR_ERR;
}
break;
case SR_HWCAP_CONN:
if (parse_conn(sdi, value) != SR_OK)
return SR_ERR_ARG;
break;
case SR_HWCAP_SERIALCOMM:
if (parse_serialcomm(sdi, value) != SR_OK)
return SR_ERR_ARG;
break;
default:
sr_err("genericdmm: Unknown capability: %d.", hwcap);
return SR_ERR;