diff options
author | Alexis Ballier | 2018-01-08 02:00:12 +0100 |
---|---|---|
committer | Nikias Bassen | 2018-01-08 02:39:38 +0100 |
commit | d29b74db77433a80fa2eb293216e8f572ffe4c57 (patch) | |
tree | 2e487667cd3d62532913b3dd8e3c0db465c5b500 | |
parent | 789b16a4a42ab56899092392f1c2d5dde6e9dd34 (diff) | |
download | usbmuxd-d29b74db77433a80fa2eb293216e8f572ffe4c57.tar.gz usbmuxd-d29b74db77433a80fa2eb293216e8f572ffe4c57.tar.bz2 |
usb: Use libusb asynchronous I/O for getting initial device information.
usb_device_add may now be called from libusb main loop via the hotplug
callbacks.
No blocking call must occur there and libusb 1.0.21 now returns an error
when trying to perform blocking I/O in this callback.
Should fix the error when hotpluging a device reported in #81
-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; } |