diff options
-rw-r--r-- | src/usb.c | 146 |
1 files changed, 111 insertions, 35 deletions
@@ -61,6 +61,7 @@ struct usb_device { int wMaxPacketSize; uint64_t speed; struct libusb_device_descriptor devdesc; + unsigned char transfer_buffer[1024 + LIBUSB_CONTROL_SETUP_SIZE]; }; static struct collection device_list; @@ -258,6 +259,91 @@ static int start_rx_loop(struct usb_device *dev) return 0; } +static void get_serial_callback(struct libusb_transfer *transfer) +{ + unsigned int di, si; + struct usb_device *usbdev = transfer->user_data; + + if(transfer->status != LIBUSB_TRANSFER_COMPLETED) { + usbmuxd_log(LL_ERROR, "Failed to request serial for device %d-%d (%i)", usbdev->bus, usbdev->address, transfer->status); + libusb_free_transfer(transfer); + return; + } + + /* De-unicode, taken from libusb */ + unsigned char *data = libusb_control_transfer_get_data(transfer); + for (di = 0, si = 2; si < data[0] && di < sizeof(usbdev->serial)-1; si += 2) { + if ((data[si] & 0x80) || (data[si + 1])) /* non-ASCII */ + usbdev->serial[di++] = '?'; + else + usbdev->serial[di++] = data[si]; + } + usbdev->serial[di] = 0; + + usbmuxd_log(LL_INFO, "Got serial '%s' for device %d-%d", usbdev->serial, usbdev->bus, usbdev->address); + + libusb_free_transfer(transfer); + + /* Finish setup now */ + if(device_add(usbdev) < 0) { + usb_disconnect(usbdev); + return; + } + + // Spin up NUM_RX_LOOPS parallel usb data retrieval loops + // Old usbmuxds used only 1 rx loop, but that leaves the + // USB port sleeping most of the time + int rx_loops = NUM_RX_LOOPS; + for (rx_loops = NUM_RX_LOOPS; rx_loops > 0; rx_loops--) { + if(start_rx_loop(usbdev) < 0) { + usbmuxd_log(LL_WARNING, "Failed to start RX loop number %d", NUM_RX_LOOPS - rx_loops); + } + } + + // Ensure we have at least 1 RX loop going + if (rx_loops == NUM_RX_LOOPS) { + usbmuxd_log(LL_FATAL, "Failed to start any RX loop for device %d-%d", + usbdev->bus, usbdev->address); + device_remove(usbdev); + usb_disconnect(usbdev); + return; + } else if (rx_loops > 0) { + usbmuxd_log(LL_WARNING, "Failed to start all %d RX loops. Going on with %d loops. " + "This may have negative impact on device read speed.", + NUM_RX_LOOPS, NUM_RX_LOOPS - rx_loops); + } else { + usbmuxd_log(LL_DEBUG, "All %d RX loops started successfully", NUM_RX_LOOPS); + } +} + +static void get_langid_callback(struct libusb_transfer *transfer) +{ + int res; + struct usb_device *usbdev = transfer->user_data; + + if(transfer->status != LIBUSB_TRANSFER_COMPLETED) { + usbmuxd_log(LL_ERROR, "Failed to request lang ID for device %d-%d (%i)", usbdev->bus, + usbdev->address, transfer->status); + libusb_free_transfer(transfer); + return; + } + + unsigned char *data = libusb_control_transfer_get_data(transfer); + uint16_t langid = (uint16_t)(data[2] | (data[3] << 8)); + usbmuxd_log(LL_INFO, "Got lang ID %u for device %d-%d", langid, usbdev->bus, usbdev->address); + + /* re-use the same transfer */ + libusb_fill_control_setup(usbdev->transfer_buffer, LIBUSB_ENDPOINT_IN, LIBUSB_REQUEST_GET_DESCRIPTOR, + (uint16_t)((LIBUSB_DT_STRING << 8) | usbdev->devdesc.iSerialNumber), + langid, sizeof(usbdev->transfer_buffer)); + libusb_fill_control_transfer(transfer, usbdev->dev, usbdev->transfer_buffer, get_serial_callback, usbdev, 1000); + + if((res = libusb_submit_transfer(transfer)) < 0) { + usbmuxd_log(LL_ERROR, "Could not request transfer for device %d-%d (%d)", usbdev->bus, usbdev->address, res); + libusb_free_transfer(transfer); + } +} + static int usb_device_add(libusb_device* dev) { int j, res; @@ -265,6 +351,7 @@ static int usb_device_add(libusb_device* dev) uint8_t bus = libusb_get_bus_number(dev); uint8_t address = libusb_get_device_address(dev); struct libusb_device_descriptor devdesc; + struct libusb_transfer *transfer; int found = 0; FOREACH(struct usb_device *usbdev, &device_list) { if(usbdev->bus == bus && usbdev->address == address) { @@ -287,7 +374,8 @@ static int usb_device_add(libusb_device* dev) return -1; libusb_device_handle *handle; usbmuxd_log(LL_INFO, "Found new device with v/p %04x:%04x at %d-%d", devdesc.idVendor, devdesc.idProduct, bus, address); - // potentially blocking operations follow; they will only run when new devices are detected, which is acceptable + // No blocking operation can follow: it may be run in the libusb hotplug callback and libusb will refuse any + // blocking call if((res = libusb_open(dev, &handle)) != 0) { usbmuxd_log(LL_WARNING, "Could not open device %d-%d: %d", bus, address, res); return -1; @@ -386,14 +474,15 @@ static int usb_device_add(libusb_device* dev) return -1; } - if((res = libusb_get_string_descriptor_ascii(handle, devdesc.iSerialNumber, (uint8_t *)usbdev->serial, 256)) <= 0) { - usbmuxd_log(LL_WARNING, "Could not get serial number for device %d-%d: %d", bus, address, res); - libusb_release_interface(handle, usbdev->interface); + transfer = libusb_alloc_transfer(0); + if(!transfer) { + usbmuxd_log(LL_WARNING, "Failed to allocate transfer for device %d-%d: %d", bus, address, res); libusb_close(handle); free(usbdev); return -1; } - usbdev->serial[res] = 0; + + usbdev->serial[0] = 0; usbdev->bus = bus; usbdev->address = address; usbdev->devdesc = devdesc; @@ -427,40 +516,27 @@ static int usb_device_add(libusb_device* dev) usbmuxd_log(LL_INFO, "USB Speed is %g MBit/s for device %d-%d", (double)(usbdev->speed / 1000000.0), usbdev->bus, usbdev->address); - collection_init(&usbdev->tx_xfers); - collection_init(&usbdev->rx_xfers); - - collection_add(&device_list, usbdev); - - if(device_add(usbdev) < 0) { - usb_disconnect(usbdev); + /** + * From libusb: + * Asking for the zero'th index is special - it returns a string + * descriptor that contains all the language IDs supported by the + * device. + **/ + libusb_fill_control_setup(usbdev->transfer_buffer, LIBUSB_ENDPOINT_IN, LIBUSB_REQUEST_GET_DESCRIPTOR, LIBUSB_DT_STRING << 8, 0, sizeof(usbdev->transfer_buffer)); + libusb_fill_control_transfer(transfer, handle, usbdev->transfer_buffer, get_langid_callback, usbdev, 1000); + + if((res = libusb_submit_transfer(transfer)) < 0) { + usbmuxd_log(LL_ERROR, "Could not request transfer for device %d-%d (%d)", usbdev->bus, usbdev->address, res); + libusb_free_transfer(transfer); + libusb_close(handle); + free(usbdev); return -1; } - // Spin up NUM_RX_LOOPS parallel usb data retrieval loops - // Old usbmuxds used only 1 rx loop, but that leaves the - // USB port sleeping most of the time - int rx_loops = NUM_RX_LOOPS; - for (rx_loops = NUM_RX_LOOPS; rx_loops > 0; rx_loops--) { - if(start_rx_loop(usbdev) < 0) { - usbmuxd_log(LL_WARNING, "Failed to start RX loop number %d", NUM_RX_LOOPS - rx_loops); - } - } + collection_init(&usbdev->tx_xfers); + collection_init(&usbdev->rx_xfers); - // Ensure we have at least 1 RX loop going - if (rx_loops == NUM_RX_LOOPS) { - usbmuxd_log(LL_FATAL, "Failed to start any RX loop for device %d-%d", - usbdev->bus, usbdev->address); - device_remove(usbdev); - usb_disconnect(usbdev); - return -1; - } else if (rx_loops > 0) { - usbmuxd_log(LL_WARNING, "Failed to start all %d RX loops. Going on with %d loops. " - "This may have negative impact on device read speed.", - NUM_RX_LOOPS, NUM_RX_LOOPS - rx_loops); - } else { - usbmuxd_log(LL_DEBUG, "All %d RX loops started successfully", NUM_RX_LOOPS); - } + collection_add(&device_list, usbdev); return 0; } |