X-Git-Url: https://sigrok.org/gitweb/?a=blobdiff_plain;f=src%2Fusb.c;h=feafe9d2a2ae0aefbb4389638f9e7676a643ba9f;hb=238a6ef234b7c63533085b241aa52392a377552c;hp=35a3e80ed58cc7d340317b1aeea116ba4d1f2d83;hpb=039a2fd78a61ad8c6e9159be9ba06b605a456155;p=libsigrok.git diff --git a/src/usb.c b/src/usb.c index 35a3e80e..feafe9d2 100644 --- a/src/usb.c +++ b/src/usb.c @@ -38,7 +38,6 @@ typedef int libusb_os_handle; #endif /** Custom GLib event source for libusb I/O. - * @internal */ struct usb_source { GSource base; @@ -139,7 +138,7 @@ static gboolean usb_source_dispatch(GSource *source, sr_err("Callback not set, cannot dispatch event."); return G_SOURCE_REMOVE; } - keep = (*(sr_receive_data_callback)callback)(-1, revents, user_data); + keep = (*SR_RECEIVE_DATA_CALLBACK(callback))(-1, revents, user_data); if (G_LIKELY(keep) && G_LIKELY(!g_source_is_destroyed(source))) { if (usource->timeout_us >= 0) @@ -184,7 +183,7 @@ static LIBUSB_CALL void usb_pollfd_added(libusb_os_handle fd, return; pollfd = g_slice_new(GPollFD); -#ifdef G_OS_WIN32 +#ifdef _WIN32 events = G_IO_IN; #endif pollfd->fd = (gintptr)fd; @@ -298,70 +297,116 @@ static GSource *usb_source_new(struct sr_session *session, } /** - * Find USB devices according to a connection string. + * Extract VID:PID or bus.addr from a connection string. * - * @param usb_ctx libusb context to use while scanning. - * @param conn Connection string specifying the device(s) to match. This - * can be of the form ".
", or ".". + * @param[in] conn Connection string. + * @param[out] vid Pointer to extracted vendor ID. Can be #NULL. + * @param[out] pid Pointer to extracted product ID. Can be #NULL. + * @param[out] bus Pointer to extracted bus number. Can be #NULL. + * @param[out] addr Pointer to extracted device number. Can be #NULL. * - * @return A GSList of struct sr_usb_dev_inst, with bus and address fields - * matching the device that matched the connection string. The GSList and - * its contents must be freed by the caller. + * @return SR_OK when parsing succeeded, SR_ERR* otherwise. + * + * @private + * + * The routine fills in the result variables, and returns the scan success + * in the return code. Callers can specify #NULL for variable references + * if they are not interested in specific aspects of the USB address. */ -SR_PRIV GSList *sr_usb_find(libusb_context *usb_ctx, const char *conn) +SR_PRIV int sr_usb_split_conn(const char *conn, + uint16_t *vid, uint16_t *pid, uint8_t *bus, uint8_t *addr) { - struct sr_usb_dev_inst *usb; - struct libusb_device **devlist; - struct libusb_device_descriptor des; - GSList *devices; + gboolean valid; GRegex *reg; GMatchInfo *match; - int vid, pid, bus, addr, b, a, ret, i; char *mstr; + uint32_t num; + + if (vid) *vid = 0; + if (pid) *pid = 0; + if (bus) *bus = 0; + if (addr) *addr = 0; - vid = pid = bus = addr = 0; + valid = TRUE; reg = g_regex_new(CONN_USB_VIDPID, 0, 0, NULL); if (g_regex_match(reg, conn, 0, &match)) { - if ((mstr = g_match_info_fetch(match, 1))) - vid = strtoul(mstr, NULL, 16); + /* Found a VID:PID style pattern. */ + if ((mstr = g_match_info_fetch(match, 1))) { + num = strtoul(mstr, NULL, 16); + if (num > 0xffff) + valid = FALSE; + if (vid) + *vid = num & 0xffff; + } g_free(mstr); - if ((mstr = g_match_info_fetch(match, 2))) - pid = strtoul(mstr, NULL, 16); + if ((mstr = g_match_info_fetch(match, 2))) { + num = strtoul(mstr, NULL, 16); + if (num > 0xffff) + valid = FALSE; + if (pid) + *pid = num & 0xffff; + } g_free(mstr); - sr_dbg("Trying to find USB device with VID:PID = %04x:%04x.", - vid, pid); } else { g_match_info_unref(match); g_regex_unref(reg); reg = g_regex_new(CONN_USB_BUSADDR, 0, 0, NULL); if (g_regex_match(reg, conn, 0, &match)) { - if ((mstr = g_match_info_fetch(match, 1))) - bus = strtoul(mstr, NULL, 10); + /* Found a bus.address style pattern. */ + if ((mstr = g_match_info_fetch(match, 1))) { + num = strtoul(mstr, NULL, 10); + if (num > 255) + valid = FALSE; + if (bus) + *bus = num & 0xff; + } g_free(mstr); - if ((mstr = g_match_info_fetch(match, 2))) - addr = strtoul(mstr, NULL, 10); + if ((mstr = g_match_info_fetch(match, 2))) { + num = strtoul(mstr, NULL, 10); + if (num > 127) + valid = FALSE; + if (addr) + *addr = num & 0x7f; + } g_free(mstr); - sr_dbg("Trying to find USB device with bus.address = " - "%d.%d.", bus, addr); } } g_match_info_unref(match); g_regex_unref(reg); - if (vid + pid + bus + addr == 0) { - sr_err("Neither VID:PID nor bus.address was specified."); - return NULL; - } + return valid ? SR_OK : SR_ERR_ARG; +} + +/** + * Find USB devices according to a connection string. + * + * @param usb_ctx libusb context to use while scanning. + * @param conn Connection string specifying the device(s) to match. This + * can be of the form ".
", or ".". + * + * @return A GSList of struct sr_usb_dev_inst, with bus and address fields + * matching the device that matched the connection string. The GSList and + * its contents must be freed by the caller. + */ +SR_PRIV GSList *sr_usb_find(libusb_context *usb_ctx, const char *conn) +{ + struct sr_usb_dev_inst *usb; + struct libusb_device **devlist; + struct libusb_device_descriptor des; + GSList *devices; + uint16_t vid, pid; + uint8_t bus, addr; + int b, a, ret, i; - if (bus > 255) { - sr_err("Invalid bus specified: %d.", bus); + ret = sr_usb_split_conn(conn, &vid, &pid, &bus, &addr); + if (ret != SR_OK) { + sr_err("Invalid input, or neither VID:PID nor bus.address specified."); return NULL; } - - if (addr > 127) { - sr_err("Invalid address specified: %d.", addr); + if (!(vid && pid) && !(bus && addr)) { + sr_err("Could neither determine VID:PID nor bus.address numbers."); return NULL; } @@ -375,25 +420,24 @@ SR_PRIV GSList *sr_usb_find(libusb_context *usb_ctx, const char *conn) continue; } - if (vid + pid && (des.idVendor != vid || des.idProduct != pid)) + if (vid && pid && (des.idVendor != vid || des.idProduct != pid)) continue; b = libusb_get_bus_number(devlist[i]); a = libusb_get_device_address(devlist[i]); - if (bus + addr && (b != bus || a != addr)) + if (bus && addr && (b != bus || a != addr)) continue; sr_dbg("Found USB device (VID:PID = %04x:%04x, bus.address = " "%d.%d).", des.idVendor, des.idProduct, b, a); - usb = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]), - libusb_get_device_address(devlist[i]), NULL); + usb = sr_usb_dev_inst_new(b, a, NULL); devices = g_slist_append(devices, usb); } libusb_free_device_list(devlist, 1); - sr_dbg("Found %d device(s).", g_slist_length(devices)); - + /* No log message for #devices found (caller will log that). */ + return devices; } @@ -459,7 +503,7 @@ SR_PRIV int usb_source_add(struct sr_session *session, struct sr_context *ctx, if (!source) return SR_ERR; - g_source_set_callback(source, (GSourceFunc)cb, cb_data, NULL); + g_source_set_callback(source, G_SOURCE_FUNC(cb), cb_data, NULL); ret = sr_session_source_add_internal(session, ctx->libusb_ctx, source); g_source_unref(source); @@ -510,3 +554,47 @@ SR_PRIV int usb_get_port_path(libusb_device *dev, char *path, int path_len) return SR_OK; } + +/** + * Check the USB configuration to determine if this device has a given + * manufacturer and product string. + * + * @return TRUE if the device's configuration profile strings + * configuration, FALSE otherwise. + */ +SR_PRIV gboolean usb_match_manuf_prod(libusb_device *dev, + const char *manufacturer, const char *product) +{ + struct libusb_device_descriptor des; + struct libusb_device_handle *hdl; + gboolean ret; + unsigned char strdesc[64]; + + hdl = NULL; + ret = FALSE; + while (!ret) { + /* Assume the FW has not been loaded, unless proven wrong. */ + libusb_get_device_descriptor(dev, &des); + + if (libusb_open(dev, &hdl) != 0) + break; + + if (libusb_get_string_descriptor_ascii(hdl, + des.iManufacturer, strdesc, sizeof(strdesc)) < 0) + break; + if (strcmp((const char *)strdesc, manufacturer)) + break; + + if (libusb_get_string_descriptor_ascii(hdl, + des.iProduct, strdesc, sizeof(strdesc)) < 0) + break; + if (strcmp((const char *)strdesc, product)) + break; + + ret = TRUE; + } + if (hdl) + libusb_close(hdl); + + return ret; +}