summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Eliyahu Stern2022-12-22 15:00:14 +0200
committerGravatar Eliyahu Stern2022-12-22 15:02:28 +0200
commit6d0183dd1824774f47e6cc995d25a237fddf2cb8 (patch)
tree7c2d955773b3f456fa6ce33110019a4dbf9faa33
parentbccae83d475ac5a48441001cad3fa8c167ecbb80 (diff)
downloadusbmuxd-6d0183dd1824774f47e6cc995d25a237fddf2cb8.tar.gz
usbmuxd-6d0183dd1824774f47e6cc995d25a237fddf2cb8.tar.bz2
Support switching to different "modes" using vendor specific control messages.
Use USBMUXD_DEFAULT_DEVICE_MODE env. var. to let the user control desired mode.
-rw-r--r--src/usb.c117
-rw-r--r--src/usb.h4
2 files changed, 113 insertions, 8 deletions
diff --git a/src/usb.c b/src/usb.c
index 4ff2d07..ca941bc 100644
--- a/src/usb.c
+++ b/src/usb.c
@@ -65,6 +65,15 @@ struct usb_device {
65 struct libusb_device_descriptor devdesc; 65 struct libusb_device_descriptor devdesc;
66}; 66};
67 67
68struct mode_user_data {
69 uint8_t bus, address;
70 uint8_t bRequest;
71 uint16_t wValue;
72 uint16_t wIndex;
73 uint16_t wLength;
74 unsigned int timeout;
75};
76
68static struct collection device_list; 77static struct collection device_list;
69 78
70static struct timeval next_dev_poll_time; 79static struct timeval next_dev_poll_time;
@@ -357,6 +366,77 @@ static void get_langid_callback(struct libusb_transfer *transfer)
357 } 366 }
358} 367}
359 368
369static int submit_vendor_specific(struct libusb_device_handle *handle, struct mode_user_data *user_data, libusb_transfer_cb_fn callback) {
370 struct libusb_transfer* ctrl_transfer = libusb_alloc_transfer(0);
371 unsigned char* buffer = malloc(LIBUSB_CONTROL_SETUP_SIZE);
372 uint8_t bRequestType = LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN | LIBUSB_RECIPIENT_DEVICE;
373 libusb_fill_control_setup(buffer, bRequestType, user_data->bRequest, user_data->wValue, user_data->wIndex, user_data->wLength);
374
375 ctrl_transfer->flags = LIBUSB_TRANSFER_FREE_BUFFER | LIBUSB_TRANSFER_FREE_TRANSFER;
376 libusb_fill_control_transfer(ctrl_transfer, handle, buffer, callback, user_data, user_data->timeout);
377
378 return libusb_submit_transfer(ctrl_transfer);
379}
380
381static void switch_mode_cb(struct libusb_transfer* transfer) {
382 struct mode_user_data* user_data = transfer->user_data;
383
384 if(transfer->status != LIBUSB_TRANSFER_COMPLETED) {
385 usbmuxd_log(LL_ERROR, "Failed to request mode switch for device %i-%i (%i)", user_data->bus, user_data->address, transfer->status);
386 free(transfer->user_data);
387 return;
388 }
389
390 unsigned char *data = libusb_control_transfer_get_data(transfer);
391
392 usbmuxd_log(LL_INFO, "Received response %i for switch mode %i for device %i-%i", data[0], user_data->wIndex, user_data->bus, user_data->address);
393 free(transfer->user_data);
394}
395
396static void get_mode_cb(struct libusb_transfer* transfer) {
397 struct mode_user_data* user_data = transfer->user_data;
398 int res;
399
400 if(transfer->status != LIBUSB_TRANSFER_COMPLETED) {
401 usbmuxd_log(LL_ERROR, "Failed to request get mode for device %i-%i (%i)", user_data->bus, user_data->address, transfer->status);
402 free(transfer->user_data);
403 return;
404 }
405
406 unsigned char *data = libusb_control_transfer_get_data(transfer);
407
408 char* desired_mode = getenv(ENV_DEVICE_MODE);
409 if (!desired_mode) {
410 user_data->wIndex = 0x1;
411 }
412 else if (!strncmp(desired_mode, "2", 1)) {
413 user_data->wIndex = 0x2;
414 }
415 else if (!strncmp(desired_mode, "3", 1)) {
416 user_data->wIndex = 0x3;
417 }
418 // Response is 3:3:3 for initial mode, 5:3:3 otherwise.
419 // In later commit, should infer the mode from available configurations and interfaces.
420 usbmuxd_log(LL_INFO, "Received response %i:%i:%i for get_mode request for device %i-%i", data[0], data[1], data[2], user_data->bus, user_data->address);
421 if (user_data->wIndex > 1 && data[0] == 3 && data[1] == 3 && data[2] == 3) {
422 // 3:3:3 means the initial mode
423 usbmuxd_log(LL_WARNING, "Switching device %i-%i mode to %i", user_data->bus, user_data->address, user_data->wIndex);
424
425 user_data->bRequest = APPLE_VEND_SPECIFIC_SET_MODE;
426 user_data->wValue = 0;
427 user_data->wLength = 1;
428
429 if ((res = submit_vendor_specific(transfer->dev_handle, user_data, switch_mode_cb)) != 0) {
430 usbmuxd_log(LL_WARNING, "Could not request to switch mode %i for device %i-%i (%i)", user_data->wIndex, user_data->bus, user_data->address, res);
431 }
432 }
433 else {
434 // in other modes, usually 5:3:3
435 usbmuxd_log(LL_WARNING, "Skipping switch device %i-%i mode", user_data->bus, user_data->address);
436 free(transfer->user_data);
437 }
438}
439
360static int usb_device_add(libusb_device* dev) 440static int usb_device_add(libusb_device* dev)
361{ 441{
362 int j, res; 442 int j, res;
@@ -397,6 +477,25 @@ static int usb_device_add(libusb_device* dev)
397 return -1; 477 return -1;
398 } 478 }
399 479
480 // On top of configurations, Apple have multiple "modes" for devices, namely:
481 // 1: An "initial" mode with 4 configurations
482 // 2: "Valeria" mode, where configuration 5 is included with interface for H.265 video capture (activated when recording screen with QuickTime in macOS)
483 // 3: "CDC NCM" mode, where configuration 5 is included with interface for Ethernet/USB (activated using internet-sharing feature in macOS)
484 // Request current mode asynchroniously, so it can be changed in callback if needed
485 usbmuxd_log(LL_INFO, "Requesting current mode from device %i-%i", bus, address);
486 struct mode_user_data* user_data = malloc(sizeof(struct mode_user_data));
487 user_data->bus = bus;
488 user_data->address = address;
489 user_data->bRequest = APPLE_VEND_SPECIFIC_GET_MODE;
490 user_data->wValue = 0;
491 user_data->wIndex = 0;
492 user_data->wLength = 4;
493 user_data->timeout = 1000;
494
495 if (submit_vendor_specific(handle, user_data, get_mode_cb) != 0) {
496 usbmuxd_log(LL_WARNING, "Could not request current mode from device %d-%d", bus, address);
497 }
498 // Potentially, the rest of this function can be factored out and called from get_mode_callback/switch_mode_callback (where desired mode is known)
400 int desired_config = devdesc.bNumConfigurations; 499 int desired_config = devdesc.bNumConfigurations;
401 if (desired_config > 4) { 500 if (desired_config > 4) {
402 if (desired_config > 5) { 501 if (desired_config > 5) {
@@ -412,14 +511,16 @@ static int usb_device_add(libusb_device* dev)
412 desired_config = 4; 511 desired_config = 4;
413 break; 512 break;
414 } 513 }
415 if (config->bNumInterfaces != 3) { 514 // In Valeria mode, there are 3 interfaces and usbmuxd is at 2
416 usbmuxd_log(LL_WARNING, "Device %d-%d: Ignoring possibly bad configuration 5, choosing configuration 4 instead.", bus, address); 515 // In CDC NCM mode, there are 4 interfaces and usbmuxd is at 1
417 desired_config = 4; 516 // Otherwize, 0 is expected to be of a different class.
418 break; 517 int usbmux_intf_index = config->bNumInterfaces == 3 ? 2 : config->bNumInterfaces == 4 ? 1 : 0;
419 } 518 intf = &config->interface[usbmux_intf_index].altsetting[0];
420 intf = &config->interface[2].altsetting[0]; 519 if (
421 if (intf->bInterfaceClass != 0xFF || intf->bInterfaceSubClass != 0x2A || intf->bInterfaceProtocol != 0xFF) { 520 intf->bInterfaceClass != INTERFACE_CLASS ||
422 usbmuxd_log(LL_WARNING, "Device %d-%d: Ignoring possibly bad configuration 5, choosing configuration 4 instead.", bus, address); 521 intf->bInterfaceSubClass != INTERFACE_SUBCLASS ||
522 intf->bInterfaceProtocol != INTERFACE_PROTOCOL) {
523 usbmuxd_log(LL_WARNING, "Device %d-%d: can't find usbmux interface in configuration 5, choosing configuration 4 instead.", bus, address);
423 desired_config = 4; 524 desired_config = 4;
424 break; 525 break;
425 } 526 }
diff --git a/src/usb.h b/src/usb.h
index b1b9bb9..4e44cce 100644
--- a/src/usb.h
+++ b/src/usb.h
@@ -50,6 +50,10 @@
50#define PID_APPLE_SILICON_RESTORE_LOW 0x1901 50#define PID_APPLE_SILICON_RESTORE_LOW 0x1901
51#define PID_APPLE_SILICON_RESTORE_MAX 0x1905 51#define PID_APPLE_SILICON_RESTORE_MAX 0x1905
52 52
53#define ENV_DEVICE_MODE "USBMUXD_DEFAULT_DEVICE_MODE"
54#define APPLE_VEND_SPECIFIC_GET_MODE 0x45
55#define APPLE_VEND_SPECIFIC_SET_MODE 0x52
56
53struct usb_device; 57struct usb_device;
54 58
55int usb_init(void); 59int usb_init(void);