@@ -178,16 +178,24 @@ struct usb_port_status {
178
178
/* List of all USB devices enumerated by libusb */
179
179
static struct libusb_device * * usb_devs = NULL ;
180
180
181
+ struct descriptor_strings {
182
+ char vendor [64 ];
183
+ char product [64 ];
184
+ char serial [64 ];
185
+ char description [256 ];
186
+ };
187
+
181
188
struct hub_info {
182
189
struct libusb_device * dev ;
183
190
int bcd_usb ;
184
191
int nports ;
185
192
int ppps ;
186
193
int actionable ; /* true if this hub is subject to action */
194
+ char container_id [33 ]; /* container ID as hex string */
187
195
char vendor [16 ];
188
196
char location [32 ];
189
197
int level ;
190
- char description [ 256 ] ;
198
+ struct descriptor_strings ds ;
191
199
};
192
200
193
201
/* Array of all enumerated USB hubs */
@@ -415,6 +423,28 @@ static int get_hub_info(struct libusb_device *dev, struct hub_info *info)
415
423
} else {
416
424
rc = len ;
417
425
}
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
+ }
418
448
libusb_close (devh );
419
449
}
420
450
return rc ;
@@ -450,52 +480,50 @@ static int get_port_status(struct libusb_device_handle *devh, int port)
450
480
451
481
452
482
/*
453
- * Get USB device description as a string .
483
+ * Get USB device descriptor strings and summary description .
454
484
*
455
- * It will use following format:
485
+ * Summary will use following format:
456
486
*
457
487
* "<vid:pid> <vendor> <product> <serial>, <USB x.yz, N ports>"
458
488
*
459
489
* vid:pid will be always present, but vendor, product or serial
460
490
* may be skipped if they are empty or not enough permissions to read them.
461
491
* <USB x.yz, N ports> will be present only for USB hubs.
462
492
*
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.
465
495
*/
466
496
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 )
468
498
{
469
499
int rc ;
470
500
int id_vendor = 0 ;
471
501
int id_product = 0 ;
472
- char vendor [64 ] = "" ;
473
- char product [64 ] = "" ;
474
- char serial [64 ] = "" ;
475
502
char ports [64 ] = "" ;
476
503
struct libusb_device_descriptor desc ;
477
504
struct libusb_device_handle * devh = NULL ;
478
505
rc = libusb_get_device_descriptor (dev , & desc );
479
506
if (rc )
480
507
return rc ;
508
+ bzero (ds , sizeof (* ds ));
481
509
id_vendor = libusb_le16_to_cpu (desc .idVendor );
482
510
id_product = libusb_le16_to_cpu (desc .idProduct );
483
511
rc = libusb_open (dev , & devh );
484
512
if (rc == 0 ) {
485
513
if (desc .iManufacturer ) {
486
514
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 );
489
517
}
490
518
if (desc .iProduct ) {
491
519
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 );
494
522
}
495
523
if (desc .iSerialNumber ) {
496
524
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 );
499
527
}
500
528
if (desc .bDeviceClass == LIBUSB_CLASS_HUB ) {
501
529
struct hub_info info ;
@@ -507,12 +535,12 @@ static int get_device_description(struct libusb_device * dev, char* description,
507
535
}
508
536
libusb_close (devh );
509
537
}
510
- snprintf (description , desc_len ,
538
+ snprintf (ds -> description , sizeof ( ds -> description ) ,
511
539
"%04x:%04x%s%s%s%s%s%s%s" ,
512
540
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 ,
516
544
ports
517
545
);
518
546
return 0 ;
@@ -555,7 +583,8 @@ static int print_port_status(struct hub_info * hub, int portmask)
555
583
556
584
printf (" Port %d: %04x" , port , port_status );
557
585
558
- char description [256 ] = "" ;
586
+ struct descriptor_strings ds ;
587
+ bzero (& ds , sizeof (ds ));
559
588
struct libusb_device * udev ;
560
589
int i = 0 ;
561
590
while ((udev = usb_devs [i ++ ]) != NULL ) {
@@ -567,7 +596,7 @@ static int print_port_status(struct hub_info * hub, int portmask)
567
596
(memcmp (hub_pn , dev_pn , hub_plen ) == 0 ) &&
568
597
libusb_get_port_number (udev ) == port )
569
598
{
570
- rc = get_device_description (udev , description , sizeof ( description ) );
599
+ rc = get_device_description (udev , & ds );
571
600
if (rc == 0 )
572
601
break ;
573
602
}
@@ -614,7 +643,7 @@ static int print_port_status(struct hub_info * hub, int portmask)
614
643
if (port_status & USB_PORT_STAT_ENABLE ) printf (" enable" );
615
644
if (port_status & USB_PORT_STAT_CONNECTION ) printf (" connect" );
616
645
617
- if (port_status & USB_PORT_STAT_CONNECTION ) printf (" [%s]" , description );
646
+ if (port_status & USB_PORT_STAT_CONNECTION ) printf (" [%s]" , ds . description );
618
647
619
648
printf ("\n" );
620
649
}
@@ -652,7 +681,7 @@ static int usb_find_hubs()
652
681
if (rc ) {
653
682
perm_ok = 0 ; /* USB permission issue? */
654
683
}
655
- get_device_description (dev , info .description , sizeof ( info . description ) );
684
+ get_device_description (dev , & info .ds );
656
685
if (info .ppps ) { /* PPPS is supported */
657
686
if (hub_count < MAX_HUBS ) {
658
687
info .actionable = 1 ;
@@ -682,63 +711,51 @@ static int usb_find_hubs()
682
711
/* Check only actionable hubs: */
683
712
if (hubs [i ].actionable != 1 )
684
713
continue ;
714
+ /* Must have non empty container ID: */
715
+ if (strlen (hubs [i ].container_id ) == 0 )
716
+ continue ;
685
717
int match = -1 ;
686
718
for (j = 0 ; j < hub_count ; j ++ ) {
687
719
if (i == j )
688
720
continue ;
689
721
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 */
699
723
700
724
/* Hub and its dual must be different types: one USB2, another USB3: */
701
725
if ((hubs [i ].bcd_usb < USB_SS_BCD ) ==
702
726
(hubs [j ].bcd_usb < USB_SS_BCD ))
703
727
continue ;
704
728
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 )
707
735
continue ;
708
736
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: */
710
750
if (hubs [i ].nports != hubs [j ].nports )
711
751
continue ;
712
752
713
753
/* And the same level: */
714
754
if (hubs [i ].level != hubs [j ].level )
715
755
continue ;
716
756
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 ;
742
759
}
743
760
if (match >= 0 ) {
744
761
if (!hubs [match ].actionable ) {
@@ -912,7 +929,7 @@ int main(int argc, char *argv[])
912
929
if (hubs [i ].actionable == 0 )
913
930
continue ;
914
931
printf ("Current status for hub %s [%s]\n" ,
915
- hubs [i ].location , hubs [i ].description
932
+ hubs [i ].location , hubs [i ].ds . description
916
933
);
917
934
print_port_status (& hubs [i ], opt_ports );
918
935
if (opt_action == POWER_KEEP ) { /* no action, show status */
@@ -962,7 +979,7 @@ int main(int argc, char *argv[])
962
979
request == LIBUSB_REQUEST_CLEAR_FEATURE ? "off" : "on"
963
980
);
964
981
printf ("New status for hub %s [%s]\n" ,
965
- hubs [i ].location , hubs [i ].description
982
+ hubs [i ].location , hubs [i ].ds . description
966
983
);
967
984
print_port_status (& hubs [i ], opt_ports );
968
985
0 commit comments