@@ -47,6 +47,10 @@ int snprintf(char * __restrict __str, size_t __size, const char * __restrict __f
47
47
#include <time.h> /* for nanosleep */
48
48
#endif
49
49
50
+ #ifdef __gnu_linux__
51
+ #include <fcntl.h> /* for open() / O_WRONLY */
52
+ #endif
53
+
50
54
/* cross-platform sleep function */
51
55
52
56
void sleep_ms (int milliseconds )
@@ -222,6 +226,9 @@ static int opt_exact = 0; /* exact location match - disable USB3 duality handl
222
226
static int opt_reset = 0 ; /* reset hub after operation(s) */
223
227
static int opt_force = 0 ; /* force operation even on unsupported hubs */
224
228
static int opt_nodesc = 0 ; /* skip querying device description */
229
+ #ifdef __gnu_linux__
230
+ static int opt_nosysfs = 0 ; /* don't use the Linux sysfs port disable interface, even if available */
231
+ #endif
225
232
226
233
static const struct option long_options [] = {
227
234
{ "location" , required_argument , NULL , 'l' },
@@ -236,6 +243,9 @@ static const struct option long_options[] = {
236
243
{ "exact" , no_argument , NULL , 'e' },
237
244
{ "force" , no_argument , NULL , 'f' },
238
245
{ "nodesc" , no_argument , NULL , 'N' },
246
+ #ifdef __gnu_linux__
247
+ { "nosysfs" , no_argument , NULL , 'S' },
248
+ #endif
239
249
{ "reset" , no_argument , NULL , 'R' },
240
250
{ "version" , no_argument , NULL , 'v' },
241
251
{ "help" , no_argument , NULL , 'h' },
@@ -262,6 +272,9 @@ static int print_usage()
262
272
"--exact, -e - exact location (no USB3 duality handling).\n"
263
273
"--force, -f - force operation even on unsupported hubs.\n"
264
274
"--nodesc, -N - do not query device description (helpful for unresponsive devices).\n"
275
+ #ifdef __gnu_linux__
276
+ "--nosysfs, -S - do not use the Linux sysfs port disable interface.\n"
277
+ #endif
265
278
"--reset, -R - reset hub after each power-on action, causing all devices to reassociate.\n"
266
279
"--wait, -w - wait before repeat power off [%d ms].\n"
267
280
"--version, -v - print program version.\n"
@@ -507,6 +520,108 @@ static int get_port_status(struct libusb_device_handle *devh, int port)
507
520
}
508
521
509
522
523
+ #ifdef __gnu_linux__
524
+ /*
525
+ * Try to use the Linux sysfs interface to power a port off/on.
526
+ * Returns 0 on success.
527
+ */
528
+
529
+ static int set_port_status_linux (struct libusb_device_handle * devh , struct hub_info * hub , int port , int on )
530
+ {
531
+ int configuration = 0 ;
532
+ char disable_path [PATH_MAX ];
533
+
534
+ int rc = libusb_get_configuration (devh , & configuration );
535
+ if (rc < 0 ) {
536
+ return rc ;
537
+ }
538
+
539
+ // The "disable" sysfs interface is available starting with kernel version 6.0.
540
+ // For earlier kernel versions the open() call will fail and we fall
541
+ // back to using libusb.
542
+ snprintf (disable_path , PATH_MAX ,
543
+ "/sys/bus/usb/devices/%s:%d.0/%s-port%i/disable" ,
544
+ hub -> location , configuration , hub -> location , port
545
+ );
546
+
547
+ int disable_fd = open (disable_path , O_WRONLY );
548
+ if (disable_fd >= 0 ) {
549
+ rc = write (disable_fd , on ? "0" : "1" , 1 );
550
+ close (disable_fd );
551
+ }
552
+
553
+ if (disable_fd < 0 || rc < 0 ) {
554
+ // ENOENT is the expected error when running on Linux kernel < 6.0 where
555
+ // the interface does not exist yet. No need to report anything in this case.
556
+ // If the file exists but another error occurs it is most likely a permission
557
+ // issue. Print an error message mostly geared towards setting up udev.
558
+ if (errno != ENOENT ) {
559
+ fprintf (stderr ,
560
+ "Failed to set port status by writing to %s (%s).\n"
561
+ "Follow https://git.io/JIB2Z to make sure that udev is set up correctly.\n"
562
+ "Falling back to libusb based port control.\n"
563
+ "Use -S to skip trying the sysfs interface and printing this message.\n" ,
564
+ disable_path , strerror (errno )
565
+ );
566
+ }
567
+
568
+ return -1 ;
569
+ }
570
+
571
+ return 0 ;
572
+ }
573
+ #endif
574
+
575
+
576
+ /*
577
+ * Use a control transfer via libusb to turn a port off/on.
578
+ * Returns >= 0 on success.
579
+ */
580
+
581
+ static int set_port_status_libusb (struct libusb_device_handle * devh , int port , int on )
582
+ {
583
+ int rc = 0 ;
584
+ int request = on ? LIBUSB_REQUEST_SET_FEATURE
585
+ : LIBUSB_REQUEST_CLEAR_FEATURE ;
586
+ int repeat = on ? 1 : opt_repeat ;
587
+
588
+ while (repeat -- > 0 ) {
589
+ rc = libusb_control_transfer (devh ,
590
+ LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_OTHER ,
591
+ request , USB_PORT_FEAT_POWER ,
592
+ port , NULL , 0 , USB_CTRL_GET_TIMEOUT
593
+ );
594
+ if (rc < 0 ) {
595
+ perror ("Failed to control port power!\n" );
596
+ }
597
+ if (repeat > 0 ) {
598
+ sleep_ms (opt_wait );
599
+ }
600
+ }
601
+
602
+ return rc ;
603
+ }
604
+
605
+
606
+ /*
607
+ * Try different methods to power a port off/on.
608
+ * Return >= 0 on success.
609
+ */
610
+
611
+ static int set_port_status (struct libusb_device_handle * devh , struct hub_info * hub , int port , int on )
612
+ {
613
+ #ifdef __gnu_linux__
614
+ if (!opt_nosysfs ) {
615
+ if (set_port_status_linux (devh , hub , port , on ) == 0 ) {
616
+ return 0 ;
617
+ }
618
+ }
619
+ #endif
620
+
621
+ return set_port_status_libusb (devh , port , on );
622
+ }
623
+
624
+
510
625
/*
511
626
* Get USB device descriptor strings and summary description.
512
627
*
@@ -904,7 +1019,7 @@ int main(int argc, char *argv[])
904
1019
int option_index = 0 ;
905
1020
906
1021
for (;;) {
907
- c = getopt_long (argc , argv , "l:L:n:a:p:d:r:w:s:hvefRN " ,
1022
+ c = getopt_long (argc , argv , "l:L:n:a:p:d:r:w:s:hvefRNS " ,
908
1023
long_options , & option_index );
909
1024
if (c == -1 )
910
1025
break ; /* no more options left */
@@ -964,6 +1079,11 @@ int main(int argc, char *argv[])
964
1079
case 'N' :
965
1080
opt_nodesc = 1 ;
966
1081
break ;
1082
+ #ifdef __gnu_linux__
1083
+ case 'S' :
1084
+ opt_nosysfs = 1 ;
1085
+ break ;
1086
+ #endif
967
1087
case 'e' :
968
1088
opt_exact = 1 ;
969
1089
break ;
@@ -1060,45 +1180,29 @@ int main(int argc, char *argv[])
1060
1180
if (rc == 0 ) {
1061
1181
/* will operate on these ports */
1062
1182
int ports = ((1 << hubs [i ].nports ) - 1 ) & opt_ports ;
1063
- int request = ( k == 0 ) ? LIBUSB_REQUEST_CLEAR_FEATURE
1064
- : LIBUSB_REQUEST_SET_FEATURE ;
1183
+ int should_be_on = k ;
1184
+
1065
1185
int port ;
1066
1186
for (port = 1 ; port <= hubs [i ].nports ; port ++ ) {
1067
1187
if ((1 << (port - 1 )) & ports ) {
1068
1188
int port_status = get_port_status (devh , port );
1069
1189
int power_mask = hubs [i ].super_speed ? USB_SS_PORT_STAT_POWER
1070
1190
: USB_PORT_STAT_POWER ;
1071
- int powered_on = port_status & power_mask ;
1191
+ int is_on = (port_status & power_mask ) != 0 ;
1192
+
1072
1193
if (opt_action == POWER_TOGGLE ) {
1073
- request = powered_on ? LIBUSB_REQUEST_CLEAR_FEATURE
1074
- : LIBUSB_REQUEST_SET_FEATURE ;
1194
+ should_be_on = !is_on ;
1075
1195
}
1076
- if (k == 0 && !powered_on && opt_action != POWER_TOGGLE )
1077
- continue ;
1078
- if (k == 1 && powered_on )
1079
- continue ;
1080
- int repeat = powered_on ? opt_repeat : 1 ;
1081
- while (repeat -- > 0 ) {
1082
- rc = libusb_control_transfer (devh ,
1083
- LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_OTHER ,
1084
- request , USB_PORT_FEAT_POWER ,
1085
- port , NULL , 0 , USB_CTRL_GET_TIMEOUT
1086
- );
1087
- if (rc < 0 ) {
1088
- perror ("Failed to control port power!\n" );
1089
- }
1090
- if (repeat > 0 ) {
1091
- sleep_ms (opt_wait );
1092
- }
1196
+
1197
+ if (is_on != should_be_on ) {
1198
+ rc = set_port_status (devh , & hubs [i ], port , should_be_on );
1093
1199
}
1094
1200
}
1095
1201
}
1096
1202
/* USB3 hubs need extra delay to actually turn off: */
1097
1203
if (k == 0 && hubs [i ].super_speed )
1098
1204
sleep_ms (150 );
1099
- printf ("Sent power %s request\n" ,
1100
- request == LIBUSB_REQUEST_CLEAR_FEATURE ? "off" : "on"
1101
- );
1205
+ printf ("Sent power %s request\n" , should_be_on ? "on" : "off" );
1102
1206
printf ("New status for hub %s [%s]\n" ,
1103
1207
hubs [i ].location , hubs [i ].ds .description
1104
1208
);
0 commit comments