@@ -188,13 +188,16 @@ struct descriptor_strings {
188
188
struct hub_info {
189
189
struct libusb_device * dev ;
190
190
int bcd_usb ;
191
+ int super_speed ; /* 1 if super speed hub, and 0 otherwise */
191
192
int nports ;
192
193
int ppps ;
193
194
int actionable ; /* true if this hub is subject to action */
194
195
char container_id [33 ]; /* container ID as hex string */
195
196
char vendor [16 ];
196
197
char location [32 ];
197
- int level ;
198
+ uint8_t bus ;
199
+ uint8_t port_numbers [MAX_HUB_CHAIN ];
200
+ int pn_len ; /* length of port numbers */
198
201
struct descriptor_strings ds ;
199
202
};
200
203
@@ -385,9 +388,9 @@ static int get_hub_info(struct libusb_device *dev, struct hub_info *info)
385
388
);
386
389
387
390
if (len >= minlen ) {
388
- unsigned char port_numbers [MAX_HUB_CHAIN ] = {0 };
389
391
info -> dev = dev ;
390
392
info -> bcd_usb = bcd_usb ;
393
+ info -> super_speed = (bcd_usb >= USB_SS_BCD );
391
394
info -> nports = uhd -> bNbrPorts ;
392
395
snprintf (
393
396
info -> vendor , sizeof (info -> vendor ),
@@ -397,14 +400,13 @@ static int get_hub_info(struct libusb_device *dev, struct hub_info *info)
397
400
);
398
401
399
402
/* Convert bus and ports array into USB location string */
400
- int bus = libusb_get_bus_number (dev );
401
- snprintf (info -> location , sizeof (info -> location ), "%d" , bus );
402
- int pcount = get_port_numbers (dev , port_numbers , MAX_HUB_CHAIN );
403
- info -> level = pcount + 1 ;
403
+ info -> bus = libusb_get_bus_number (dev );
404
+ snprintf (info -> location , sizeof (info -> location ), "%d" , info -> bus );
405
+ info -> pn_len = get_port_numbers (dev , info -> port_numbers , sizeof (info -> port_numbers ));
404
406
int k ;
405
- for (k = 0 ; k < pcount ; k ++ ) {
407
+ for (k = 0 ; k < info -> pn_len ; k ++ ) {
406
408
char s [8 ];
407
- snprintf (s , sizeof (s ), "%s%d" , k == 0 ? "-" : "." , port_numbers [k ]);
409
+ snprintf (s , sizeof (s ), "%s%d" , k == 0 ? "-" : "." , info -> port_numbers [k ]);
408
410
strcat (info -> location , s );
409
411
}
410
412
@@ -434,10 +436,10 @@ static int get_hub_info(struct libusb_device *dev, struct hub_info *info)
434
436
}
435
437
libusb_free_bos_descriptor (bos );
436
438
437
- /* Raspberry Pi 4 hack for USB3 root hub: */
439
+ /* Raspberry Pi 4B hack for USB3 root hub: */
438
440
if (strlen (info -> container_id )== 0 &&
439
441
strcasecmp (info -> vendor , "1d6b:0003" )== 0 &&
440
- info -> level == 1 &&
442
+ info -> pn_len == 0 &&
441
443
info -> nports == 4 &&
442
444
bcd_usb == USB_SS_BCD )
443
445
{
@@ -452,7 +454,7 @@ static int get_hub_info(struct libusb_device *dev, struct hub_info *info)
452
454
/* For 1 port hubs, ganged power switching is the same as per-port: */
453
455
lpsm = HUB_CHAR_INDV_PORT_LPSM ;
454
456
}
455
- /* Raspberry Pi 4 reports inconsistent descriptors, override: */
457
+ /* Raspberry Pi 4B reports inconsistent descriptors, override: */
456
458
if (lpsm == HUB_CHAR_COMMON_LPSM && strcasecmp (info -> vendor , "2109:3431" )== 0 ) {
457
459
lpsm = HUB_CHAR_INDV_PORT_LPSM ;
458
460
}
@@ -581,17 +583,9 @@ static int print_port_status(struct hub_info * hub, int portmask)
581
583
int port_status ;
582
584
struct libusb_device_handle * devh = NULL ;
583
585
int rc = 0 ;
584
- int hub_bus ;
585
- int dev_bus ;
586
- unsigned char hub_pn [MAX_HUB_CHAIN ];
587
- unsigned char dev_pn [MAX_HUB_CHAIN ];
588
- int hub_plen ;
589
- int dev_plen ;
590
586
struct libusb_device * dev = hub -> dev ;
591
587
rc = libusb_open (dev , & devh );
592
588
if (rc == 0 ) {
593
- hub_bus = libusb_get_bus_number (dev );
594
- hub_plen = get_port_numbers (dev , hub_pn , sizeof (hub_pn ));
595
589
int port ;
596
590
for (port = 1 ; port <= hub -> nports ; port ++ ) {
597
591
if (portmask > 0 && (portmask & (1 << (port - 1 ))) == 0 ) continue ;
@@ -611,12 +605,15 @@ static int print_port_status(struct hub_info * hub, int portmask)
611
605
struct libusb_device * udev ;
612
606
int i = 0 ;
613
607
while ((udev = usb_devs [i ++ ]) != NULL ) {
608
+ uint8_t dev_bus ;
609
+ uint8_t dev_pn [MAX_HUB_CHAIN ];
610
+ int dev_plen ;
614
611
dev_bus = libusb_get_bus_number (udev );
615
612
/* only match devices on the same bus: */
616
- if (dev_bus != hub_bus ) continue ;
613
+ if (dev_bus != hub -> bus ) continue ;
617
614
dev_plen = get_port_numbers (udev , dev_pn , sizeof (dev_pn ));
618
- if ((dev_plen == hub_plen + 1 ) &&
619
- (memcmp (hub_pn , dev_pn , hub_plen ) == 0 ) &&
615
+ if ((dev_plen == hub -> pn_len + 1 ) &&
616
+ (memcmp (hub -> port_numbers , dev_pn , hub -> pn_len ) == 0 ) &&
620
617
libusb_get_port_number (udev ) == port )
621
618
{
622
619
rc = get_device_description (udev , & ds );
@@ -625,7 +622,7 @@ static int print_port_status(struct hub_info * hub, int portmask)
625
622
}
626
623
}
627
624
628
- if (hub -> bcd_usb < USB_SS_BCD ) {
625
+ if (! hub -> super_speed ) {
629
626
if (port_status == 0 ) {
630
627
printf (" off" );
631
628
} else {
@@ -714,7 +711,7 @@ static int usb_find_hubs()
714
711
}
715
712
}
716
713
if (opt_level > 0 ) {
717
- if (opt_level != info .level ) {
714
+ if (opt_level != info .pn_len + 1 ) {
718
715
info .actionable = 0 ;
719
716
}
720
717
}
@@ -737,16 +734,16 @@ static int usb_find_hubs()
737
734
/* Must have non empty container ID: */
738
735
if (strlen (hubs [i ].container_id ) == 0 )
739
736
continue ;
740
- int match = -1 ;
737
+ int best_match = -1 ;
738
+ int best_score = -1 ;
741
739
for (j = 0 ; j < hub_count ; j ++ ) {
742
740
if (i == j )
743
741
continue ;
744
742
745
743
/* Find hub which is USB2/3 dual to the hub above */
746
744
747
745
/* Hub and its dual must be different types: one USB2, another USB3: */
748
- if ((hubs [i ].bcd_usb < USB_SS_BCD ) ==
749
- (hubs [j ].bcd_usb < USB_SS_BCD ))
746
+ if (hubs [i ].super_speed == hubs [j ].super_speed )
750
747
continue ;
751
748
752
749
/* Must have non empty container ID: */
@@ -762,25 +759,71 @@ static int usb_find_hubs()
762
759
* We should do few more checks below if multiple such devices are present.
763
760
*/
764
761
762
+ /* Hubs should have the same number of ports */
763
+ if (hubs [i ].nports != hubs [j ].nports ) {
764
+ /* Except for some weird hubs like Apple mini-dock (has 2 usb2 + 1 usb3 ports) */
765
+ if (hubs [i ].nports + hubs [j ].nports > 3 ) {
766
+ continue ;
767
+ }
768
+ }
769
+
765
770
/* If serial numbers are both present, they must match: */
766
771
if ((strlen (hubs [i ].ds .serial ) > 0 && strlen (hubs [j ].ds .serial ) > 0 ) &&
767
772
strcmp (hubs [i ].ds .serial , hubs [j ].ds .serial ) != 0 )
768
773
{
769
774
continue ;
770
775
}
771
776
772
- /* Hubs should have the same number of ports: */
773
- if (hubs [i ].nports != hubs [j ].nports )
774
- continue ;
777
+ /* We have first possible candidate, but need to keep looking for better one */
775
778
776
- /* Finally, we claim a match: */
777
- match = j ;
778
- break ;
779
+ if (best_score < 1 ) {
780
+ best_score = 1 ;
781
+ best_match = j ;
782
+ }
783
+
784
+ /* Checks for various levels of USB2 vs USB3 path similarity... */
785
+
786
+ uint8_t * p1 = hubs [i ].port_numbers ;
787
+ uint8_t * p2 = hubs [j ].port_numbers ;
788
+ int l1 = hubs [i ].pn_len ;
789
+ int l2 = hubs [j ].pn_len ;
790
+ int s1 = hubs [i ].super_speed ;
791
+ int s2 = hubs [j ].super_speed ;
792
+
793
+ /* Check if port path is the same after removing top level (needed for M1 Macs): */
794
+ if (l1 >= 1 && l1 == l2 && memcmp (p1 + 1 , p2 + 1 , l1 - 1 )== 0 ) {
795
+ if (best_score < 2 ) {
796
+ best_score = 2 ;
797
+ best_match = j ;
798
+ }
799
+ }
800
+
801
+ /* Raspberry Pi 4B hack (USB2 hub is one level deeper than USB3): */
802
+ if (l1 + s1 == l2 + s2 && l1 >= s2 && memcmp (p1 + s2 , p2 + s1 , l1 - s2 )== 0 ) {
803
+ if (best_score < 3 ) {
804
+ best_score = 3 ;
805
+ best_match = j ;
806
+ }
807
+ }
808
+ /* Check if port path is exactly the same: */
809
+ if (l1 == l2 && memcmp (p1 , p2 , l1 )== 0 ) {
810
+ if (best_score < 4 ) {
811
+ best_score = 4 ;
812
+ best_match = j ;
813
+ }
814
+ /* Give even higher priority if `usb2bus + 1 == usb3bus` (Linux specific): */
815
+ if (hubs [i ].bus - s1 == hubs [j ].bus - s2 ) {
816
+ if (best_score < 5 ) {
817
+ best_score = 5 ;
818
+ best_match = j ;
819
+ }
820
+ }
821
+ }
779
822
}
780
- if (match >= 0 ) {
781
- if (!hubs [match ].actionable ) {
823
+ if (best_match >= 0 ) {
824
+ if (!hubs [best_match ].actionable ) {
782
825
/* Use 2 to signify that this is derived dual device */
783
- hubs [match ].actionable = 2 ;
826
+ hubs [best_match ].actionable = 2 ;
784
827
}
785
828
}
786
829
}
@@ -789,7 +832,7 @@ static int usb_find_hubs()
789
832
for (i = 0 ; i < hub_count ; i ++ ) {
790
833
if (!hubs [i ].actionable )
791
834
continue ;
792
- if (hubs [i ].bcd_usb < USB_SS_BCD || opt_exact ) {
835
+ if (! hubs [i ].super_speed || opt_exact ) {
793
836
hub_phys_count ++ ;
794
837
}
795
838
}
@@ -966,8 +1009,8 @@ int main(int argc, char *argv[])
966
1009
for (port = 1 ; port <= hubs [i ].nports ; port ++ ) {
967
1010
if ((1 << (port - 1 )) & ports ) {
968
1011
int port_status = get_port_status (devh , port );
969
- int power_mask = hubs [i ].bcd_usb < USB_SS_BCD ? USB_PORT_STAT_POWER
970
- : USB_SS_PORT_STAT_POWER ;
1012
+ int power_mask = hubs [i ].super_speed ? USB_SS_PORT_STAT_POWER
1013
+ : USB_PORT_STAT_POWER ;
971
1014
if (k == 0 && !(port_status & power_mask ))
972
1015
continue ;
973
1016
if (k == 1 && (port_status & power_mask ))
@@ -993,7 +1036,7 @@ int main(int argc, char *argv[])
993
1036
}
994
1037
}
995
1038
/* USB3 hubs need extra delay to actually turn off: */
996
- if (k == 0 && hubs [i ].bcd_usb >= USB_SS_BCD )
1039
+ if (k == 0 && hubs [i ].super_speed )
997
1040
sleep_ms (150 );
998
1041
printf ("Sent power %s request\n" ,
999
1042
request == LIBUSB_REQUEST_CLEAR_FEATURE ? "off" : "on"
0 commit comments