diff options
-rw-r--r-- | src/usb.c | 88 |
1 files changed, 73 insertions, 15 deletions
@@ -390,6 +390,71 @@ static struct usb_device* find_device(int bus, int address) | |||
390 | return NULL; | 390 | return NULL; |
391 | } | 391 | } |
392 | 392 | ||
393 | /// @brief guess the current mode | ||
394 | /// @param dev | ||
395 | /// @param usbdev | ||
396 | /// @param handle | ||
397 | /// @return 0 - undetermined, 1 - initial, 2 - valeria, 3 - cdc_ncm | ||
398 | static int guess_mode(struct libusb_device* dev, struct usb_device *usbdev) | ||
399 | { | ||
400 | int res, j; | ||
401 | int has_valeria = 0, has_cdc_ncm = 0, has_usbmux = 0; | ||
402 | struct libusb_device_descriptor devdesc = usbdev->devdesc; | ||
403 | struct libusb_config_descriptor *config; | ||
404 | int bus = usbdev->bus; | ||
405 | int address = usbdev->address; | ||
406 | |||
407 | if(devdesc.bNumConfigurations <= 4) { | ||
408 | // Assume this is initial mode | ||
409 | return 1; | ||
410 | } | ||
411 | |||
412 | if(devdesc.bNumConfigurations != 5) { | ||
413 | // No known modes with more then 5 configurations | ||
414 | return 0; | ||
415 | } | ||
416 | |||
417 | if((res = libusb_get_config_descriptor_by_value(dev, 5, &config)) != 0) { | ||
418 | usbmuxd_log(LL_NOTICE, "Could not get configuration 5 descriptor for device %i-%i: %s", bus, address, libusb_error_name(res)); | ||
419 | return 0; | ||
420 | } | ||
421 | |||
422 | // Require both usbmux and one of the other interfaces to determine this is a valid configuration | ||
423 | for(j = 0 ; j < config->bNumInterfaces ; j++) { | ||
424 | const struct libusb_interface_descriptor *intf = &config->interface[j].altsetting[0]; | ||
425 | if(intf->bInterfaceClass == INTERFACE_CLASS && | ||
426 | intf->bInterfaceSubClass == 42 && | ||
427 | intf->bInterfaceProtocol == 255) { | ||
428 | has_valeria = 1; | ||
429 | } | ||
430 | // https://github.com/torvalds/linux/blob/72a85e2b0a1e1e6fb4ee51ae902730212b2de25c/include/uapi/linux/usb/cdc.h#L22 | ||
431 | // 2 for Communication class, 0xd for CDC NCM subclass | ||
432 | if(intf->bInterfaceClass == 2 && | ||
433 | intf->bInterfaceSubClass == 0xd) { | ||
434 | has_cdc_ncm = 1; | ||
435 | } | ||
436 | if(intf->bInterfaceClass == INTERFACE_CLASS && | ||
437 | intf->bInterfaceSubClass == INTERFACE_SUBCLASS && | ||
438 | intf->bInterfaceProtocol == INTERFACE_PROTOCOL) { | ||
439 | has_usbmux = 1; | ||
440 | } | ||
441 | } | ||
442 | |||
443 | libusb_free_config_descriptor(config); | ||
444 | |||
445 | if(has_valeria && has_usbmux) { | ||
446 | usbmuxd_log(LL_NOTICE, "Found Valeria and Apple USB Multiplexor in device %i-%i configuration 5", bus, address); | ||
447 | return 2; | ||
448 | } | ||
449 | |||
450 | if(has_cdc_ncm && has_usbmux) { | ||
451 | usbmuxd_log(LL_NOTICE, "Found CDC-NCM and Apple USB Multiplexor in device %i-%i configuration 5", bus, address); | ||
452 | return 3; | ||
453 | } | ||
454 | |||
455 | return 0; | ||
456 | } | ||
457 | |||
393 | /// @brief Finds and sets the valid configuration, interface and endpoints on the usb_device | 458 | /// @brief Finds and sets the valid configuration, interface and endpoints on the usb_device |
394 | static int set_valid_configuration(struct libusb_device* dev, struct usb_device *usbdev, struct libusb_device_handle *handle) | 459 | static int set_valid_configuration(struct libusb_device* dev, struct usb_device *usbdev, struct libusb_device_handle *handle) |
395 | { | 460 | { |
@@ -625,25 +690,19 @@ static void get_mode_cb(struct libusb_transfer* transfer) | |||
625 | 690 | ||
626 | unsigned char *data = libusb_control_transfer_get_data(transfer); | 691 | unsigned char *data = libusb_control_transfer_get_data(transfer); |
627 | 692 | ||
628 | char* desired_mode = getenv(ENV_DEVICE_MODE); | 693 | int desired_mode = atoi(getenv(ENV_DEVICE_MODE)); |
629 | if(!desired_mode) { | 694 | int guessed_mode = guess_mode(context->dev, dev); |
630 | context->wIndex = 0x1; | 695 | |
631 | } | ||
632 | else if(!strncmp(desired_mode, "2", 1)) { | ||
633 | context->wIndex = 0x2; | ||
634 | } | ||
635 | else if(!strncmp(desired_mode, "3", 1)) { | ||
636 | context->wIndex = 0x3; | ||
637 | } | ||
638 | // Response is 3:3:3:0 for initial mode, 5:3:3:0 otherwise. | 696 | // Response is 3:3:3:0 for initial mode, 5:3:3:0 otherwise. |
639 | // In later commit, should infer the mode from available configurations and interfaces. | ||
640 | usbmuxd_log(LL_INFO, "Received response %i:%i:%i:%i for get_mode request for device %i-%i", data[0], data[1], data[2], data[3], context->bus, context->address); | 697 | usbmuxd_log(LL_INFO, "Received response %i:%i:%i:%i for get_mode request for device %i-%i", data[0], data[1], data[2], data[3], context->bus, context->address); |
641 | if(context->wIndex > 1 && data[0] == 3 && data[1] == 3 && data[2] == 3 && data[3] == 0) { | 698 | if(desired_mode >= 1 && desired_mode <= 3 && |
642 | // 3:3:3:0 means the initial mode | 699 | guessed_mode > 0 && // do not switch mode if guess failed |
700 | guessed_mode != desired_mode) { | ||
643 | usbmuxd_log(LL_WARNING, "Switching device %i-%i mode to %i", context->bus, context->address, context->wIndex); | 701 | usbmuxd_log(LL_WARNING, "Switching device %i-%i mode to %i", context->bus, context->address, context->wIndex); |
644 | 702 | ||
645 | context->bRequest = APPLE_VEND_SPECIFIC_SET_MODE; | 703 | context->bRequest = APPLE_VEND_SPECIFIC_SET_MODE; |
646 | context->wValue = 0; | 704 | context->wValue = 0; |
705 | context->wIndex = desired_mode; | ||
647 | context->wLength = 1; | 706 | context->wLength = 1; |
648 | 707 | ||
649 | if((res = submit_vendor_specific(transfer->dev_handle, context, switch_mode_cb)) != 0) { | 708 | if((res = submit_vendor_specific(transfer->dev_handle, context, switch_mode_cb)) != 0) { |
@@ -653,8 +712,7 @@ static void get_mode_cb(struct libusb_transfer* transfer) | |||
653 | } | 712 | } |
654 | } | 713 | } |
655 | else { | 714 | else { |
656 | // in other modes, usually 5:3:3:0 (but in any other unexpected case as well), complete init: | 715 | usbmuxd_log(LL_WARNING, "Skipping switch device %i-%i mode from %i to %i", context->bus, context->address, guessed_mode, desired_mode); |
657 | usbmuxd_log(LL_WARNING, "Skipping switch device %i-%i mode", context->bus, context->address); | ||
658 | device_complete_initialization(context, transfer->dev_handle); | 716 | device_complete_initialization(context, transfer->dev_handle); |
659 | free(context); | 717 | free(context); |
660 | } | 718 | } |