diff options
| author | 2022-12-24 21:45:35 +0200 | |
|---|---|---|
| committer | 2022-12-24 21:45:35 +0200 | |
| commit | 84801d80af4cb9f2ca240441a690a720d65a4711 (patch) | |
| tree | 5d45fb7cbf68c0dd95da7df0995a28ea3e4ded6d | |
| parent | 24a02650d9f4e18f91c299e99a0da01c337d7656 (diff) | |
| download | usbmuxd-84801d80af4cb9f2ca240441a690a720d65a4711.tar.gz usbmuxd-84801d80af4cb9f2ca240441a690a720d65a4711.tar.bz2 | |
Guess current mode based on available configurations and interfaces.
Switch mode only if guess is different than desired mode.
| -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 | } |
