int usb_get_device_name(libusb_device_handle *device_handle, char *name, int length) { int rc; libusb_device *device = libusb_get_device(device_handle); uint8_t bus_number = libusb_get_bus_number(device); uint8_t device_address = libusb_get_device_address(device); struct libusb_device_descriptor descriptor; char product[64]; char serial_number[64]; // get device descriptor rc = libusb_get_device_descriptor(device, &descriptor); if (rc < 0) { log_error("Could not get device descriptor for USB device (bus: %u, device: %u): %s (%d)", bus_number, device_address, usb_get_error_name(rc), rc); return -1; } // get product string descriptor rc = libusb_get_string_descriptor_ascii(device_handle, descriptor.iProduct, (unsigned char *)product, sizeof(product)); if (rc < 0) { log_error("Could not get product string descriptor for USB device (bus: %u, device: %u): %s (%d)", bus_number, device_address, usb_get_error_name(rc), rc); return -1; } // get serial number string descriptor rc = libusb_get_string_descriptor_ascii(device_handle, descriptor.iSerialNumber, (unsigned char *)serial_number, sizeof(serial_number)); if (rc < 0) { log_error("Could not get serial number string descriptor for USB device (bus: %u, device: %u): %s (%d)", bus_number, device_address, usb_get_error_name(rc), rc); return -1; } // format name snprintf(name, length, "%s [%s]", product, serial_number); return 0; }
static void usb_handle_events(void *opaque) { int rc; libusb_context *context = opaque; struct timeval tv; tv.tv_sec = 0; tv.tv_usec = 0; rc = libusb_handle_events_timeout(context, &tv); if (rc < 0) { log_error("Could not handle USB events: %s (%d)", usb_get_error_name(rc), rc); } }
static int usb_enumerate(void) { int result = -1; libusb_device **devices; libusb_device *device; int rc; int i = 0; struct libusb_device_descriptor descriptor; uint8_t bus_number; uint8_t device_address; bool known; int k; USBStack *usb_stack; // get all devices rc = libusb_get_device_list(_context, &devices); if (rc < 0) { log_error("Could not get USB device list: %s (%d)", usb_get_error_name(rc), rc); return -1; } // check for stacks for (device = devices[0]; device != NULL; device = devices[++i]) { bus_number = libusb_get_bus_number(device); device_address = libusb_get_device_address(device); rc = libusb_get_device_descriptor(device, &descriptor); if (rc < 0) { log_warn("Could not get device descriptor for USB device (bus: %u, device: %u), ignoring USB device: %s (%d)", bus_number, device_address, usb_get_error_name(rc), rc); continue; } if (descriptor.idVendor == USB_BRICK_VENDOR_ID && descriptor.idProduct == USB_BRICK_PRODUCT_ID) { if (descriptor.bcdDevice < USB_BRICK_DEVICE_RELEASE) { log_warn("USB device (bus: %u, device: %u) has unsupported protocol 1.0 firmware, please update firmware, ignoring USB device", bus_number, device_address); continue; } } else if (descriptor.idVendor == USB_RED_BRICK_VENDOR_ID && descriptor.idProduct == USB_RED_BRICK_PRODUCT_ID) { if (descriptor.bcdDevice < USB_RED_BRICK_DEVICE_RELEASE) { log_warn("USB device (bus: %u, device: %u) has unexpected release version, ignoring USB device", bus_number, device_address); continue; } } else { continue; } // check all known stacks known = false; for (k = 0; k < _usb_stacks.count; ++k) { usb_stack = array_get(&_usb_stacks, k); if (usb_stack->bus_number == bus_number && usb_stack->device_address == device_address) { // mark known USBStack as connected usb_stack->connected = true; known = true; break; } } if (known) { continue; } // create new USBStack object log_debug("Found new USB device (bus: %u, device: %u)", bus_number, device_address); usb_stack = array_append(&_usb_stacks); if (usb_stack == NULL) { log_error("Could not append to USB stacks array: %s (%d)", get_errno_name(errno), errno); goto cleanup; } if (usb_stack_create(usb_stack, bus_number, device_address) < 0) { array_remove(&_usb_stacks, _usb_stacks.count - 1, NULL); log_warn("Ignoring USB device (bus: %u, device: %u) due to an error", bus_number, device_address); continue; } // mark new stack as connected usb_stack->connected = true; log_info("Added USB device (bus: %u, device: %u) at index %d: %s", usb_stack->bus_number, usb_stack->device_address, _usb_stacks.count - 1, usb_stack->base.name); } result = 0; cleanup: libusb_free_device_list(devices, 1); return result; }
int usb_get_interface_endpoints(libusb_device_handle *device_handle, int interface_number, uint8_t *endpoint_in, uint8_t *endpoint_out) { int rc; libusb_device *device = libusb_get_device(device_handle); uint8_t bus_number = libusb_get_bus_number(device); uint8_t device_address = libusb_get_device_address(device); int i; struct libusb_config_descriptor *config_descriptor; const struct libusb_interface_descriptor *interface_descriptor; int k; const struct libusb_endpoint_descriptor *endpoint_descriptor; rc = libusb_get_config_descriptor(device, 0, &config_descriptor); if (rc < 0) { log_error("Could not get config descriptor for USB device (bus: %u, device: %u): %s (%d)", bus_number, device_address, usb_get_error_name(rc), rc); return -1; } if (config_descriptor->bNumInterfaces == 0) { log_error("Config descriptor for USB device (bus: %u, device: %u) contains no interfaces", bus_number, device_address); return -1; } for (i = 0; i < config_descriptor->bNumInterfaces; ++i) { if (config_descriptor->interface[i].num_altsetting < 1) { log_debug("Interface at index %d of USB device (bus: %u, device: %u) has no alt setting, ignoring it", i, bus_number, device_address); continue; } interface_descriptor = &config_descriptor->interface[i].altsetting[0]; if (interface_descriptor->bInterfaceNumber != interface_number) { continue; } if (interface_descriptor->bNumEndpoints != 2) { log_debug("Interface %d of USB device (bus: %u, device: %u) has %d endpoints, expecting 2, ignoring it", interface_descriptor->bInterfaceNumber, bus_number, device_address, interface_descriptor->bNumEndpoints); continue; } for (k = 0; k < interface_descriptor->bNumEndpoints; ++k) { endpoint_descriptor = &interface_descriptor->endpoint[k]; if (endpoint_descriptor->bEndpointAddress & LIBUSB_ENDPOINT_IN) { *endpoint_in = endpoint_descriptor->bEndpointAddress; } else { *endpoint_out = endpoint_descriptor->bEndpointAddress; } } libusb_free_config_descriptor(config_descriptor); return 0; } libusb_free_config_descriptor(config_descriptor); return -1; }
int usb_create_context(libusb_context **context) { int phase = 0; int rc; const struct libusb_pollfd **pollfds = NULL; const struct libusb_pollfd **pollfd; const struct libusb_pollfd **last_added_pollfd = NULL; rc = libusb_init(context); if (rc < 0) { log_error("Could not initialize libusb context: %s (%d)", usb_get_error_name(rc), rc); goto cleanup; } switch (log_get_effective_level()) { case LOG_LEVEL_ERROR: libusb_set_debug(*context, 1); break; case LOG_LEVEL_WARN: libusb_set_debug(*context, 2); break; case LOG_LEVEL_INFO: libusb_set_debug(*context, 3); break; case LOG_LEVEL_DEBUG: if (log_is_included(LOG_LEVEL_DEBUG, &_libusb_log_source, LOG_DEBUG_GROUP_LIBUSB)) { libusb_set_debug(*context, 4); } else { libusb_set_debug(*context, 3); } break; default: break; } phase = 1; // get pollfds from main libusb context pollfds = libusb_get_pollfds(*context); if (pollfds == NULL) { log_error("Could not get pollfds from libusb context"); goto cleanup; } for (pollfd = pollfds; *pollfd != NULL; ++pollfd) { if (event_add_source((*pollfd)->fd, EVENT_SOURCE_TYPE_USB, (*pollfd)->events, usb_handle_events, *context) < 0) { goto cleanup; } last_added_pollfd = pollfd; phase = 2; } phase = 3; // register pollfd notifiers libusb_set_pollfd_notifiers(*context, usb_add_pollfd, usb_remove_pollfd, *context); cleanup: switch (phase) { // no breaks, all cases fall through intentionally case 2: for (pollfd = pollfds; pollfd != last_added_pollfd; ++pollfd) { event_remove_source((*pollfd)->fd, EVENT_SOURCE_TYPE_USB); } case 1: libusb_exit(*context); default: break; } #if defined(_WIN32) || (defined(LIBUSB_API_VERSION) && LIBUSB_API_VERSION >= 0x01000104) // libusb 1.0.20 libusb_free_pollfds(pollfds); // avoids possible heap-mismatch on Windows #else free(pollfds); #endif return phase == 3 ? 0 : -1; }
int usb_transfer_submit(USBTransfer *usb_transfer) { uint8_t endpoint; int length; int rc; if (usb_transfer->submitted) { log_error("%s transfer %p (%p) is already submitted for %s", usb_transfer_get_type_name(usb_transfer->type, true), usb_transfer, usb_transfer->handle, usb_transfer->usb_stack->base.name); return -1; } switch (usb_transfer->type) { case USB_TRANSFER_TYPE_READ: endpoint = usb_transfer->usb_stack->endpoint_in; length = sizeof(Packet); break; case USB_TRANSFER_TYPE_WRITE: endpoint = usb_transfer->usb_stack->endpoint_out; length = usb_transfer->packet.header.length; break; default: log_error("Transfer for %s has invalid type", usb_transfer->usb_stack->base.name); return -1; } usb_transfer->submitted = true; libusb_fill_bulk_transfer(usb_transfer->handle, usb_transfer->usb_stack->device_handle, endpoint, (unsigned char *)&usb_transfer->packet, length, usb_transfer_wrapper, usb_transfer, 0); rc = libusb_submit_transfer(usb_transfer->handle); if (rc < 0) { log_error("Could not submit %s transfer %p (%p) to %s: %s (%d)", usb_transfer_get_type_name(usb_transfer->type, false), usb_transfer, usb_transfer->handle, usb_transfer->usb_stack->base.name, usb_get_error_name(rc), rc); usb_transfer->submitted = false; return -1; } log_packet_debug("Submitted %s transfer %p (%p) for %u bytes to %s", usb_transfer_get_type_name(usb_transfer->type, false), usb_transfer, usb_transfer->handle, length, usb_transfer->usb_stack->base.name); return 0; }
void usb_transfer_destroy(USBTransfer *usb_transfer) { struct timeval tv; time_t start; time_t now; int rc; log_debug("Destroying %s transfer %p (%p) for %s", usb_transfer_get_type_name(usb_transfer->type, false), usb_transfer, usb_transfer->handle, usb_transfer->usb_stack->base.name); if (usb_transfer->submitted) { usb_transfer->completed = false; usb_transfer->cancelled = true; rc = libusb_cancel_transfer(usb_transfer->handle); // if libusb_cancel_transfer fails with LIBUSB_ERROR_NO_DEVICE if the // device was disconnected before the transfer could be cancelled. but // the transfer might be cancelled anyway and we need to wait for the // transfer to complete. this can result in waiting for a transfer that // might not complete anymore. but if we don't wait for the transfer to // complete if it actually will complete then the libusb_device_handle // might be closed before the transfer completes. this results in a // crash by NULL pointer dereference because libusb assumes that the // libusb_device_handle is not closed as long as there are submitted // transfers. if (rc < 0 && rc != LIBUSB_ERROR_NO_DEVICE) { log_warn("Could not cancel pending %s transfer %p (%p) for %s: %s (%d)", usb_transfer_get_type_name(usb_transfer->type, false), usb_transfer, usb_transfer->handle, usb_transfer->usb_stack->base.name, usb_get_error_name(rc), rc); } else { tv.tv_sec = 0; tv.tv_usec = 0; start = time(NULL); now = start; // FIXME: don't wait 1 second per transfer while (!usb_transfer->completed && now >= start && now < start + 1) { rc = libusb_handle_events_timeout(usb_transfer->usb_stack->context, &tv); if (rc < 0) { log_error("Could not handle USB events during %s transfer %p (%p) cancellation for %s: %s (%d)", usb_transfer_get_type_name(usb_transfer->type, false), usb_transfer, usb_transfer->handle, usb_transfer->usb_stack->base.name, usb_get_error_name(rc), rc); } now = time(NULL); } if (!usb_transfer->completed) { log_warn("Attempt to cancel pending %s transfer %p (%p) for %s timed out", usb_transfer_get_type_name(usb_transfer->type, false), usb_transfer, usb_transfer->handle, usb_transfer->usb_stack->base.name); } } } if (!usb_transfer->submitted) { libusb_free_transfer(usb_transfer->handle); } else { log_warn("Leaking pending %s transfer %p (%p) for %s", usb_transfer_get_type_name(usb_transfer->type, false), usb_transfer, usb_transfer->handle, usb_transfer->usb_stack->base.name); } }