@@ -178,16 +178,24 @@ struct usb_port_status {
178178/* List of all USB devices enumerated by libusb */
179179static struct libusb_device * * usb_devs = NULL ;
180180
181+ struct descriptor_strings {
182+ char vendor [64 ];
183+ char product [64 ];
184+ char serial [64 ];
185+ char description [256 ];
186+ };
187+
181188struct hub_info {
182189 struct libusb_device * dev ;
183190 int bcd_usb ;
184191 int nports ;
185192 int ppps ;
186193 int actionable ; /* true if this hub is subject to action */
194+ char container_id [33 ]; /* container ID as hex string */
187195 char vendor [16 ];
188196 char location [32 ];
189197 int level ;
190- char description [ 256 ] ;
198+ struct descriptor_strings ds ;
191199};
192200
193201/* Array of all enumerated USB hubs */
@@ -415,6 +423,28 @@ static int get_hub_info(struct libusb_device *dev, struct hub_info *info)
415423 } else {
416424 rc = len ;
417425 }
426+ /* Get container_id: */
427+ bzero (info -> container_id , sizeof (info -> container_id ));
428+ struct libusb_bos_descriptor * bos ;
429+ rc = libusb_get_bos_descriptor (devh , & bos );
430+ if (rc == 0 ) {
431+ int cap ;
432+ for (cap = 0 ; cap < bos -> bNumDeviceCaps ; cap ++ ) {
433+ if (bos -> dev_capability [cap ]-> bDevCapabilityType == LIBUSB_BT_CONTAINER_ID ) {
434+ struct libusb_container_id_descriptor * container_id ;
435+ rc = libusb_get_container_id_descriptor (NULL , bos -> dev_capability [cap ], & container_id );
436+ if (rc == 0 ) {
437+ int i ;
438+ for (i = 0 ; i < 16 ; i ++ ) {
439+ sprintf (info -> container_id + i * 2 , "%02x" , container_id -> ContainerID [i ]);
440+ }
441+ info -> container_id [i * 2 ] = 0 ;
442+ libusb_free_container_id_descriptor (container_id );
443+ }
444+ }
445+ }
446+ libusb_free_bos_descriptor (bos );
447+ }
418448 libusb_close (devh );
419449 }
420450 return rc ;
@@ -450,52 +480,50 @@ static int get_port_status(struct libusb_device_handle *devh, int port)
450480
451481
452482/*
453- * Get USB device description as a string .
483+ * Get USB device descriptor strings and summary description .
454484 *
455- * It will use following format:
485+ * Summary will use following format:
456486 *
457487 * "<vid:pid> <vendor> <product> <serial>, <USB x.yz, N ports>"
458488 *
459489 * vid:pid will be always present, but vendor, product or serial
460490 * may be skipped if they are empty or not enough permissions to read them.
461491 * <USB x.yz, N ports> will be present only for USB hubs.
462492 *
463- * returns 0 for success and error code for failure.
464- * in case of failure description buffer is not altered.
493+ * Returns 0 for success and error code for failure.
494+ * In case of failure return buffer is not altered.
465495 */
466496
467- static int get_device_description (struct libusb_device * dev , char * description , int desc_len )
497+ static int get_device_description (struct libusb_device * dev , struct descriptor_strings * ds )
468498{
469499 int rc ;
470500 int id_vendor = 0 ;
471501 int id_product = 0 ;
472- char vendor [64 ] = "" ;
473- char product [64 ] = "" ;
474- char serial [64 ] = "" ;
475502 char ports [64 ] = "" ;
476503 struct libusb_device_descriptor desc ;
477504 struct libusb_device_handle * devh = NULL ;
478505 rc = libusb_get_device_descriptor (dev , & desc );
479506 if (rc )
480507 return rc ;
508+ bzero (ds , sizeof (* ds ));
481509 id_vendor = libusb_le16_to_cpu (desc .idVendor );
482510 id_product = libusb_le16_to_cpu (desc .idProduct );
483511 rc = libusb_open (dev , & devh );
484512 if (rc == 0 ) {
485513 if (desc .iManufacturer ) {
486514 libusb_get_string_descriptor_ascii (devh ,
487- desc .iManufacturer , (unsigned char * )vendor , sizeof (vendor ));
488- rtrim (vendor );
515+ desc .iManufacturer , (unsigned char * )ds -> vendor , sizeof (ds -> vendor ));
516+ rtrim (ds -> vendor );
489517 }
490518 if (desc .iProduct ) {
491519 libusb_get_string_descriptor_ascii (devh ,
492- desc .iProduct , (unsigned char * )product , sizeof (product ));
493- rtrim (product );
520+ desc .iProduct , (unsigned char * )ds -> product , sizeof (ds -> product ));
521+ rtrim (ds -> product );
494522 }
495523 if (desc .iSerialNumber ) {
496524 libusb_get_string_descriptor_ascii (devh ,
497- desc .iSerialNumber , (unsigned char * )serial , sizeof (serial ));
498- rtrim (serial );
525+ desc .iSerialNumber , (unsigned char * )ds -> serial , sizeof (ds -> serial ));
526+ rtrim (ds -> serial );
499527 }
500528 if (desc .bDeviceClass == LIBUSB_CLASS_HUB ) {
501529 struct hub_info info ;
@@ -507,12 +535,12 @@ static int get_device_description(struct libusb_device * dev, char* description,
507535 }
508536 libusb_close (devh );
509537 }
510- snprintf (description , desc_len ,
538+ snprintf (ds -> description , sizeof ( ds -> description ) ,
511539 "%04x:%04x%s%s%s%s%s%s%s" ,
512540 id_vendor , id_product ,
513- vendor [0 ] ? " " : "" , vendor ,
514- product [0 ] ? " " : "" , product ,
515- serial [0 ] ? " " : "" , serial ,
541+ ds -> vendor [0 ] ? " " : "" , ds -> vendor ,
542+ ds -> product [0 ] ? " " : "" , ds -> product ,
543+ ds -> serial [0 ] ? " " : "" , ds -> serial ,
516544 ports
517545 );
518546 return 0 ;
@@ -555,7 +583,8 @@ static int print_port_status(struct hub_info * hub, int portmask)
555583
556584 printf (" Port %d: %04x" , port , port_status );
557585
558- char description [256 ] = "" ;
586+ struct descriptor_strings ds ;
587+ bzero (& ds , sizeof (ds ));
559588 struct libusb_device * udev ;
560589 int i = 0 ;
561590 while ((udev = usb_devs [i ++ ]) != NULL ) {
@@ -567,7 +596,7 @@ static int print_port_status(struct hub_info * hub, int portmask)
567596 (memcmp (hub_pn , dev_pn , hub_plen ) == 0 ) &&
568597 libusb_get_port_number (udev ) == port )
569598 {
570- rc = get_device_description (udev , description , sizeof ( description ) );
599+ rc = get_device_description (udev , & ds );
571600 if (rc == 0 )
572601 break ;
573602 }
@@ -614,7 +643,7 @@ static int print_port_status(struct hub_info * hub, int portmask)
614643 if (port_status & USB_PORT_STAT_ENABLE ) printf (" enable" );
615644 if (port_status & USB_PORT_STAT_CONNECTION ) printf (" connect" );
616645
617- if (port_status & USB_PORT_STAT_CONNECTION ) printf (" [%s]" , description );
646+ if (port_status & USB_PORT_STAT_CONNECTION ) printf (" [%s]" , ds . description );
618647
619648 printf ("\n" );
620649 }
@@ -652,7 +681,7 @@ static int usb_find_hubs()
652681 if (rc ) {
653682 perm_ok = 0 ; /* USB permission issue? */
654683 }
655- get_device_description (dev , info .description , sizeof ( info . description ) );
684+ get_device_description (dev , & info .ds );
656685 if (info .ppps ) { /* PPPS is supported */
657686 if (hub_count < MAX_HUBS ) {
658687 info .actionable = 1 ;
@@ -682,63 +711,51 @@ static int usb_find_hubs()
682711 /* Check only actionable hubs: */
683712 if (hubs [i ].actionable != 1 )
684713 continue ;
714+ /* Must have non empty container ID: */
715+ if (strlen (hubs [i ].container_id ) == 0 )
716+ continue ;
685717 int match = -1 ;
686718 for (j = 0 ; j < hub_count ; j ++ ) {
687719 if (i == j )
688720 continue ;
689721
690- /* Find hub which is USB2/3 dual to the hub above.
691- * This is quite reliable and predictable on Linux
692- * but not on Mac, where we may match wrong hub :(
693- * It will work reliably on Mac if there is
694- * only one compatible USB3 hub is connected.
695- * Unfortunately, libusb does not provide any way
696- * to detect USB2/3 dual hubs.
697- * TODO: discover better way to find dual hub.
698- */
722+ /* Find hub which is USB2/3 dual to the hub above */
699723
700724 /* Hub and its dual must be different types: one USB2, another USB3: */
701725 if ((hubs [i ].bcd_usb < USB_SS_BCD ) ==
702726 (hubs [j ].bcd_usb < USB_SS_BCD ))
703727 continue ;
704728
705- /* But they must have the same vendor: */
706- if (strncasecmp (hubs [i ].vendor , hubs [j ].vendor , 4 ))
729+ /* Must have non empty container ID: */
730+ if (strlen (hubs [j ].container_id ) == 0 )
731+ continue ;
732+
733+ /* Per USB 3.0 spec chapter 11.2, container IDs must match: */
734+ if (strcmp (hubs [i ].container_id , hubs [j ].container_id ) != 0 )
707735 continue ;
708736
709- /* And the same number of ports: */
737+ /* At this point, it should be enough to claim a match.
738+ * However, some devices use hardcoded non-unique container ID.
739+ * We should do few more checks below if multiple such devices are present.
740+ */
741+
742+ /* If serial number is present, it must match: */
743+ if ((strlen (hubs [i ].ds .serial ) > 0 || strlen (hubs [j ].ds .serial ) > 0 ) &&
744+ strcmp (hubs [i ].ds .serial , hubs [j ].ds .serial ) != 0 )
745+ {
746+ continue ;
747+ }
748+
749+ /* Hubs should have the same number of ports: */
710750 if (hubs [i ].nports != hubs [j ].nports )
711751 continue ;
712752
713753 /* And the same level: */
714754 if (hubs [i ].level != hubs [j ].level )
715755 continue ;
716756
717- /* If description is the same, provisionally we choose this one as dual.
718- * If description contained serial number, this will be most reliable matching.
719- */
720- if (strlen (hubs [i ].description ) == strlen (hubs [j ].description )) {
721- /* strlen("vvvv:pppp ") + strlen(", USB x.yz, N ports") = 10+19 = 29 */
722- if (strlen (hubs [i ].description ) >= 29 ) {
723- if (strncmp (hubs [i ].description + 10 , hubs [j ].description + 10 , strlen (hubs [i ].description )- 29 ) == 0 ) {
724- match = j ;
725- }
726- }
727- }
728-
729- /* Running out of options - provisionally we choose this one as dual: */
730- if (match < 0 && !hubs [j ].actionable )
731- match = j ;
732-
733- /* But if there is exact port path match,
734- * we prefer it (true for Linux but not Mac):
735- */
736- char * p1 = strchr (hubs [i ].location , '-' );
737- char * p2 = strchr (hubs [j ].location , '-' );
738- if (p1 && p2 && strcasecmp (p1 , p2 )== 0 ) {
739- match = j ;
740- break ;
741- }
757+ /* Finally, we claim a match: */
758+ match = j ;
742759 }
743760 if (match >= 0 ) {
744761 if (!hubs [match ].actionable ) {
@@ -912,7 +929,7 @@ int main(int argc, char *argv[])
912929 if (hubs [i ].actionable == 0 )
913930 continue ;
914931 printf ("Current status for hub %s [%s]\n" ,
915- hubs [i ].location , hubs [i ].description
932+ hubs [i ].location , hubs [i ].ds . description
916933 );
917934 print_port_status (& hubs [i ], opt_ports );
918935 if (opt_action == POWER_KEEP ) { /* no action, show status */
@@ -962,7 +979,7 @@ int main(int argc, char *argv[])
962979 request == LIBUSB_REQUEST_CLEAR_FEATURE ? "off" : "on"
963980 );
964981 printf ("New status for hub %s [%s]\n" ,
965- hubs [i ].location , hubs [i ].description
982+ hubs [i ].location , hubs [i ].ds . description
966983 );
967984 print_port_status (& hubs [i ], opt_ports );
968985
0 commit comments