Skip to content

Commit 0cc0929

Browse files
committed
Add a switch to allow operation on unsupported hubs
Show power switching type supported by a hub: * ppps - per-port power switching * ganged - ganged power switching * nops - no power switching Add --force (-f) switch to allow operation on non-ppps hubs. Use `-f` at your own risk, this most likely will not work to actually switch power. For ganged hubs, you may need to turn power off for all hub ports to get any effect. For nops hubs, it is not likely to work at all, but might be useful for informational purposes. Also, instead of printing recipe on how to fix USB permissions on Linux, forward user to web page with detailed explanations. Closes #260, #280.
1 parent 44f963d commit 0cc0929

File tree

1 file changed

+32
-29
lines changed

1 file changed

+32
-29
lines changed

uhubctl.c

+32-29
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ struct hub_info {
190190
int bcd_usb;
191191
int super_speed; /* 1 if super speed hub, and 0 otherwise */
192192
int nports;
193-
int ppps;
193+
int lpsm; /* logical power switching mode */
194194
int actionable; /* true if this hub is subject to action */
195195
char container_id[33]; /* container ID as hex string */
196196
char vendor[16];
@@ -218,6 +218,7 @@ static int opt_repeat = 1;
218218
static int opt_wait = 20; /* wait before repeating in ms */
219219
static int opt_exact = 0; /* exact location match - disable USB3 duality handling */
220220
static int opt_reset = 0; /* reset hub after operation(s) */
221+
static int opt_force = 0; /* force operation even on unsupported hubs */
221222

222223
static const struct option long_options[] = {
223224
{ "location", required_argument, NULL, 'l' },
@@ -229,6 +230,7 @@ static const struct option long_options[] = {
229230
{ "repeat", required_argument, NULL, 'r' },
230231
{ "wait", required_argument, NULL, 'w' },
231232
{ "exact", no_argument, NULL, 'e' },
233+
{ "force", no_argument, NULL, 'f' },
232234
{ "reset", no_argument, NULL, 'R' },
233235
{ "version", no_argument, NULL, 'v' },
234236
{ "help", no_argument, NULL, 'h' },
@@ -252,6 +254,7 @@ static int print_usage()
252254
"--delay, -d - delay for cycle action [%g sec].\n"
253255
"--repeat, -r - repeat power off count [%d] (some devices need it to turn off).\n"
254256
"--exact, -e - exact location (no USB3 duality handling).\n"
257+
"--force, -f - force operation even on unsupported hubs.\n"
255258
"--reset, -R - reset hub after each power-on action, causing all devices to reassociate.\n"
256259
"--wait, -w - wait before repeat power off [%d ms].\n"
257260
"--version, -v - print program version.\n"
@@ -447,7 +450,6 @@ static int get_hub_info(struct libusb_device *dev, struct hub_info *info)
447450
}
448451
}
449452

450-
info->ppps = 0;
451453
/* Logical Power Switching Mode */
452454
int lpsm = uhd->wHubCharacteristics[0] & HUB_CHAR_LPSM;
453455
if (lpsm == HUB_CHAR_COMMON_LPSM && info->nports == 1) {
@@ -458,15 +460,8 @@ static int get_hub_info(struct libusb_device *dev, struct hub_info *info)
458460
if (lpsm == HUB_CHAR_COMMON_LPSM && strcasecmp(info->vendor, "2109:3431")==0) {
459461
lpsm = HUB_CHAR_INDV_PORT_LPSM;
460462
}
461-
/* Over-Current Protection Mode */
462-
int ocpm = uhd->wHubCharacteristics[0] & HUB_CHAR_OCPM;
463-
/* LPSM must be supported per-port, and OCPM per port or ganged */
464-
if ((lpsm == HUB_CHAR_INDV_PORT_LPSM) &&
465-
(ocpm == HUB_CHAR_INDV_PORT_OCPM ||
466-
ocpm == HUB_CHAR_COMMON_OCPM))
467-
{
468-
info->ppps = 1;
469-
}
463+
info->lpsm = lpsm;
464+
rc = 0;
470465
} else {
471466
rc = len;
472467
}
@@ -524,7 +519,7 @@ static int get_device_description(struct libusb_device * dev, struct descriptor_
524519
int rc;
525520
int id_vendor = 0;
526521
int id_product = 0;
527-
char ports[64] = "";
522+
char hub_specific[64] = "";
528523
struct libusb_device_descriptor desc;
529524
struct libusb_device_handle *devh = NULL;
530525
rc = libusb_get_device_descriptor(dev, &desc);
@@ -554,8 +549,16 @@ static int get_device_description(struct libusb_device * dev, struct descriptor_
554549
struct hub_info info;
555550
rc = get_hub_info(dev, &info);
556551
if (rc == 0) {
557-
snprintf(ports, sizeof(ports), ", USB %x.%02x, %d ports",
558-
info.bcd_usb >> 8, info.bcd_usb & 0xFF, info.nports);
552+
const char * lpsm_type;
553+
if (info.lpsm == HUB_CHAR_INDV_PORT_LPSM) {
554+
lpsm_type = "ppps";
555+
} else if (info.lpsm == HUB_CHAR_COMMON_LPSM) {
556+
lpsm_type = "ganged";
557+
} else {
558+
lpsm_type = "nops";
559+
}
560+
snprintf(hub_specific, sizeof(hub_specific), ", USB %x.%02x, %d ports, %s",
561+
info.bcd_usb >> 8, info.bcd_usb & 0xFF, info.nports, lpsm_type);
559562
}
560563
}
561564
libusb_close(devh);
@@ -566,7 +569,7 @@ static int get_device_description(struct libusb_device * dev, struct descriptor_
566569
ds->vendor[0] ? " " : "", ds->vendor,
567570
ds->product[0] ? " " : "", ds->product,
568571
ds->serial[0] ? " " : "", ds->serial,
569-
ports
572+
hub_specific
570573
);
571574
return 0;
572575
}
@@ -700,9 +703,10 @@ static int usb_find_hubs()
700703
rc = get_hub_info(dev, &info);
701704
if (rc) {
702705
perm_ok = 0; /* USB permission issue? */
706+
continue;
703707
}
704708
get_device_description(dev, &info.ds);
705-
if (info.ppps) { /* PPPS is supported */
709+
if (info.lpsm == HUB_CHAR_INDV_PORT_LPSM || opt_force) {
706710
if (hub_count < MAX_HUBS) {
707711
info.actionable = 1;
708712
if (strlen(opt_location) > 0) {
@@ -836,6 +840,14 @@ static int usb_find_hubs()
836840
hub_phys_count++;
837841
}
838842
}
843+
#ifdef __gnu_linux__
844+
if (perm_ok == 0 && geteuid() != 0) {
845+
fprintf(stderr,
846+
"There were permission problems while accessing USB.\n"
847+
"Follow https://git.io/JIB2Z for a fix!\n"
848+
);
849+
}
850+
#endif
839851
if (perm_ok == 0 && hub_phys_count == 0) {
840852
return LIBUSB_ERROR_ACCESS;
841853
}
@@ -850,7 +862,7 @@ int main(int argc, char *argv[])
850862
int option_index = 0;
851863

852864
for (;;) {
853-
c = getopt_long(argc, argv, "l:L:n:a:p:d:r:w:hveR",
865+
c = getopt_long(argc, argv, "l:L:n:a:p:d:r:w:hvefR",
854866
long_options, &option_index);
855867
if (c == -1)
856868
break; /* no more options left */
@@ -898,6 +910,9 @@ int main(int argc, char *argv[])
898910
case 'r':
899911
opt_repeat = atoi(optarg);
900912
break;
913+
case 'f':
914+
opt_force = 1;
915+
break;
901916
case 'e':
902917
opt_exact = 1;
903918
break;
@@ -956,18 +971,6 @@ int main(int argc, char *argv[])
956971
strlen(opt_location) ? " at location " : "",
957972
opt_location
958973
);
959-
#ifdef __gnu_linux__
960-
if (rc < 0 && geteuid() != 0) {
961-
fprintf(stderr,
962-
"There were permission problems while accessing USB.\n"
963-
"To fix this, run this tool as root using 'sudo uhubctl',\n"
964-
"or add one or more udev rules like below\n"
965-
"to file '/etc/udev/rules.d/52-usb.rules':\n"
966-
"SUBSYSTEM==\"usb\", ATTR{idVendor}==\"2001\", MODE=\"0666\"\n"
967-
"then run 'sudo udevadm trigger --attr-match=subsystem=usb'\n"
968-
);
969-
}
970-
#endif
971974
rc = 1;
972975
goto cleanup;
973976
}

0 commit comments

Comments
 (0)