summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore8
-rw-r--r--LICENSE23
-rw-r--r--Makefile9
-rw-r--r--README.md71
-rw-r--r--uhubctl.c560
5 files changed, 671 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0ab4458
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+# Executables
+uhubctl
+
+# Object files
+*.o
+
+# Mac symbols
+*.dSYM
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..c245a08
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,23 @@
+Copyright (c) 2009-2014, Vadim Mikhailov.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..7ae9575
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,9 @@
+CFLAGS = -g -O0
+LDFLAGS = -lusb-1.0
+PROGRAM = uhubctl
+
+$(PROGRAM): $(PROGRAM).o
+ cc $(CFLAGS) $@.c -o $@ $(LDFLAGS)
+
+clean:
+ rm -rf $(PROGRAM).o $(PROGRAM).dSYM $(PROGRAM)
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..84421e3
--- /dev/null
+++ b/README.md
@@ -0,0 +1,71 @@
+uhubctl
+=======
+
+uhubctl is utility to control USB power per-port on smart USB hubs.
+Smart hub is defined as one that implements per-port power switching.
+
+Original idea for this code was inspired by hub-ctrl.c by Niibe Yutaka:
+http://www.gniibe.org/development/ac-power-control-by-USB-hub
+
+
+Compatible USB hubs
+===================
+
+Note that very few hubs actually support per-port power switching.
+Some of them are no longer manufactured and can be hard to find.
+
+This is list of known compatible USB hubs:
+
+D-Link: DUB-H7 (Silver edition, new Black edition is not compatible!)
+Elecom: U2H-G4S
+Sanwa Supply: USB-HUB14GPH
+Targus: PAUH212
+Hawking Technology: UH214
+Apple: USB Keyboard With Numeric Pad (internal USB hub)
+Apple: Thunderbolt Display 27" (internal USB hub)
+
+Some modern motherboards have built-in root hubs that do support
+this feature, but you need to use option -i to enable it.
+
+
+Compiling
+=========
+
+This utility was tested to compile and work on Linux
+(Ubuntu, Redhat/Fedora/CentOS) and on Mac OS X.
+It should be possible to compile it for Windows as well -
+please report if you succeed in doing that.
+
+First, you need to install library libusb-1.0 (version 1.0.16 or later):
+
+Ubuntu: sudo apt-get install libusb-1.0-0-dev
+Redhat: sudo yum install libusb1-devel
+MacOSX: brew install libusb
+Windows: TBD?
+
+To compile, simply run "make" - this will generate uhubctl binary.
+
+Usage
+=====
+
+You can control the power on a USB port(s) like this:
+
+ uhubctl -a off -p 235
+
+This means operate on default smart hub and turn power off ("-a off", or "-a 0")
+on ports 2,3,5 ("-p 235"). Supported actions are off/on/cycle (or 0/1/2).
+cycle means turn power off, wait some delay (configurable with "-d") and turn it back on.
+
+On Linux, you may need to run it with sudo, or to configure udev USB permissions.
+
+If you have more than one smart USB hub connected, you should choose
+specific hub to control using -l (location) parameter.
+
+
+Copyright
+=========
+
+Copyright (C) 2009-2014 Vadim Mikhailov
+
+This file can be distributed under the terms and conditions of the
+GNU General Public License version 2.
diff --git a/uhubctl.c b/uhubctl.c
new file mode 100644
index 0000000..53cba40
--- /dev/null
+++ b/uhubctl.c
@@ -0,0 +1,560 @@
+/*
+ * Copyright (c) 2009-2014 Vadim Mikhailov
+ *
+ * Utility to turn USB port power on/off
+ * for USB hubs that support per-port power switching.
+ *
+ * This file can be distributed under the terms and conditions of the
+ * GNU General Public License version 2.
+ *
+ */
+
+#define PROGRAM_VERSION "1.5"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include <libusb-1.0/libusb.h>
+
+/* Max number of hub ports supported.
+ * This is somewhat artifically limited by "-p" option parser.
+ * If "-p" parser is improved, we can support up to 32 ports.
+ * However, biggest number of ports on smart hub I've seen was 8.
+ * I've also observed onboard USB hub with whopping 14 ports,
+ * but that hub did not support per-port power switching.
+ */
+#define MAX_HUB_PORTS 9
+#define ALL_HUB_PORTS ((1 << MAX_HUB_PORTS) - 1) /* bitmask */
+
+#define USB_CTRL_GET_TIMEOUT 5000
+
+#define USB_PORT_FEAT_POWER (1 << 3)
+
+#define POWER_KEEP (-1)
+#define POWER_OFF 0
+#define POWER_ON 1
+#define POWER_CYCLE 2
+
+#define MAX_HUB_CHAIN 8 /* Per USB 3.0 spec max hub chain is 7 */
+
+/* Partially borrowed from linux/usb/ch11.h */
+
+struct usb_hub_descriptor {
+ unsigned char bDescLength;
+ unsigned char bDescriptorType;
+ unsigned char bNbrPorts;
+ unsigned char wHubCharacteristics[2];
+ unsigned char bPwrOn2PwrGood;
+ unsigned char bHubContrCurrent;
+ unsigned char data[1]; /* use 1 to avoid zero-sized array warning */
+};
+
+/*
+ * Hub Status and Hub Change results
+ * See USB 2.0 spec Table 11-19 and Table 11-20
+ */
+struct usb_port_status {
+ int16_t wPortStatus;
+ int16_t wPortChange;
+} __attribute__ ((packed));
+
+/*
+ * wPortStatus bit field
+ * See USB 2.0 spec Table 11-21
+ */
+#define USB_PORT_STAT_CONNECTION 0x0001
+#define USB_PORT_STAT_ENABLE 0x0002
+#define USB_PORT_STAT_SUSPEND 0x0004
+#define USB_PORT_STAT_OVERCURRENT 0x0008
+#define USB_PORT_STAT_RESET 0x0010
+#define USB_PORT_STAT_L1 0x0020
+/* bits 6 to 7 are reserved */
+#define USB_PORT_STAT_POWER 0x0100
+#define USB_PORT_STAT_LOW_SPEED 0x0200
+#define USB_PORT_STAT_HIGH_SPEED 0x0400
+#define USB_PORT_STAT_TEST 0x0800
+#define USB_PORT_STAT_INDICATOR 0x1000
+/* bits 13 to 15 are reserved */
+
+/*
+ * wHubCharacteristics (masks)
+ * See USB 2.0 spec Table 11-13, offset 3
+ */
+#define HUB_CHAR_LPSM 0x0003 /* Logical Power Switching Mode mask */
+#define HUB_CHAR_COMMON_LPSM 0x0000 /* All ports at once power switching */
+#define HUB_CHAR_INDV_PORT_LPSM 0x0001 /* Per-port power switching */
+#define HUB_CHAR_NO_LPSM 0x0002 /* No power switching */
+
+#define HUB_CHAR_COMPOUND 0x0004 /* hub is part of a compound device */
+
+#define HUB_CHAR_OCPM 0x0018 /* Over-Current Protection Mode mask */
+#define HUB_CHAR_COMMON_OCPM 0x0000 /* All ports at once over-current protection */
+#define HUB_CHAR_INDV_PORT_OCPM 0x0008 /* Per-port over-current protection */
+#define HUB_CHAR_NO_OCPM 0x0010 /* No over-current protection support */
+
+#define HUB_CHAR_TTTT 0x0060 /* TT Think Time mask */
+#define HUB_CHAR_PORTIND 0x0080 /* per-port indicators (LEDs) */
+
+/* List of all USB devices enumerated by libusb */
+static struct libusb_device **usb_devs = NULL;
+
+struct hub_info {
+ struct libusb_device *dev;
+ int nports;
+ char vendor[16];
+ char location[32];
+};
+
+/* Array of USB hubs we are going to operate on */
+#define MAX_HUBS 64
+static struct hub_info hubs[MAX_HUBS];
+static int hub_count = 0;
+
+/* default options */
+static char opt_vendor[16] = "";
+static char opt_location[16] = ""; /* Hub location a-b.c.d */
+static int opt_internal = 0; /* Allow scanning internal hubs */
+static int opt_ports = ALL_HUB_PORTS; /* Bitmask of ports to operate on */
+static int opt_action = POWER_KEEP;
+static int opt_delay = 2;
+static int opt_repeat = 1;
+static int opt_wait = 20; /* wait before repeating in ms */
+
+static const struct option long_options[] = {
+ { "loc", required_argument, NULL, 'l' },
+ { "vendor", required_argument, NULL, 'n' },
+ { "internal", no_argument, NULL, 'i' },
+ { "ports", required_argument, NULL, 'p' },
+ { "action", required_argument, NULL, 'a' },
+ { "delay", required_argument, NULL, 'd' },
+ { "repeat", required_argument, NULL, 'r' },
+ { "wait", required_argument, NULL, 'w' },
+ { "version", no_argument, NULL, 'v' },
+ { "help", no_argument, NULL, 'h' },
+ { 0, 0, NULL, 0 },
+};
+
+int print_usage()
+{
+ printf(
+ "uhubctl v%s: utility to control USB port power for smart hubs.\n"
+ "Usage: uhubctl [options]\n"
+ "Without options, show status for all smart hubs.\n"
+ "\n"
+ "Options [defaults in brackets]:\n"
+ "--action, -a - action to off/on/cycle (0/1/2) for affected ports.\n"
+ "--ports, -p - ports to operate on [all hub ports].\n"
+ "--loc, -l - limit hub by location [all smart hubs].\n"
+ "--vendor, -n - limit hub by vendor id [%s] (partial ok).\n"
+ "--delay, -d - delay for cycle action [%d sec].\n"
+ "--repeat, -r - repeat power off count [%d] (some devices need it to turn off).\n"
+ "--wait, -w - wait before repeat power off [%d ms].\n"
+ "--internal, -i - include internal hubs [off].\n"
+ "--version, -v - print program version.\n"
+ "--help, -h - print this text.\n"
+ "\n"
+ "Send bugs and requests to: https://github.com/mvp/uhubctl\n",
+ PROGRAM_VERSION,
+ strlen(opt_vendor) ? opt_vendor : "any",
+ opt_delay,
+ opt_repeat,
+ opt_wait
+ );
+ return 0;
+}
+/* checks if hub is smart hub
+ * use min_current above 0 to only consider external hubs
+ * (external hubs have non-zero bHubContrCurrent)
+ * return value is number of hub ports
+ * 0 means hub is not smart
+ * -1 means there is access error
+ */
+
+int is_smart_hub(struct libusb_device *dev, int min_current)
+{
+ int rc = 0;
+ int len = 0;
+ struct libusb_device_handle *devh = NULL;
+ unsigned char buf[256] = {0};
+ struct usb_hub_descriptor *uhd =
+ (struct usb_hub_descriptor *)buf;
+ int minlen = sizeof(struct usb_hub_descriptor) - 1;
+ struct libusb_device_descriptor desc;
+ rc = libusb_get_device_descriptor(dev, &desc);
+ if (desc.bDeviceClass != LIBUSB_CLASS_HUB)
+ return 0;
+ rc = libusb_open(dev, &devh);
+ if (rc == 0) {
+ len = libusb_control_transfer(devh,
+ LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_CLASS
+ | LIBUSB_RECIPIENT_DEVICE, /* hub status */
+ LIBUSB_REQUEST_GET_DESCRIPTOR,
+ LIBUSB_DT_HUB << 8,
+ 0,
+ buf, sizeof(buf),
+ USB_CTRL_GET_TIMEOUT
+ );
+ if (len >= minlen) {
+ /* Logical Power Switching Mode */
+ int lpsm = uhd->wHubCharacteristics[0] & HUB_CHAR_LPSM;
+ /* Over-Current Protection Mode */
+ int ocpm = uhd->wHubCharacteristics[0] & HUB_CHAR_OCPM;
+ /* Both LPSM and OCPM must be supported per-port: */
+ if ((lpsm == HUB_CHAR_INDV_PORT_LPSM) &&
+ (ocpm == HUB_CHAR_INDV_PORT_OCPM))
+ {
+ rc = uhd->bNbrPorts;
+ /* Internal hubs have zero bHubContrCurrent.
+ * Ignore them if requested:
+ */
+ if (min_current > 0 && uhd->bHubContrCurrent < min_current) {
+ rc = -1;
+ }
+ }
+ } else {
+ rc = len;
+ }
+ libusb_close(devh);
+ }
+ return rc;
+}
+
+/* Assuming that devh is opened device handle for USB hub,
+ * return state for given hub port.
+ * In case of error, returns -1 (inspect errno for more information).
+ */
+
+static int get_port_status(struct libusb_device_handle *devh, int port)
+{
+ int rc;
+ struct usb_port_status ust;
+ if (devh == NULL)
+ return -1;
+
+ rc = libusb_control_transfer(devh,
+ LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_CLASS
+ | LIBUSB_RECIPIENT_OTHER, /* port status */
+ LIBUSB_REQUEST_GET_STATUS, 0,
+ port, (unsigned char*)&ust, sizeof(ust),
+ USB_CTRL_GET_TIMEOUT
+ );
+
+ if (rc < 0) {
+ return rc;
+ }
+ return ust.wPortStatus;
+}
+
+/* show status for hub ports
+ * nports is number of hub ports
+ * portmask is bitmap of ports to display
+ * if portmask is 0, show all ports
+ */
+static int hub_port_status(struct libusb_device * dev, int nports, int portmask)
+{
+ int port_status;
+ struct libusb_device_handle * devh = NULL;
+ int rc = 0;
+ rc = libusb_open(dev, &devh);
+ if (rc == 0) {
+ int port;
+ for (port = 1; port <= nports; port++) {
+ if (portmask > 0 && (portmask & (1 << (port-1))) == 0) continue;
+
+ port_status = get_port_status(devh, port);
+ if (port_status == -1) {
+ fprintf(stderr,
+ "cannot read port %d status, %s (%d)\n",
+ port, strerror(errno), errno);
+ break;
+ }
+
+ printf(" Port %d: %04x", port, port_status);
+ printf("%s%s%s%s%s%s%s%s%s%s%s\n",
+ port_status & USB_PORT_STAT_INDICATOR ? " indicator" : "",
+ port_status & USB_PORT_STAT_TEST ? " test" : "",
+ port_status & USB_PORT_STAT_HIGH_SPEED ? " highspeed" : "",
+ port_status & USB_PORT_STAT_LOW_SPEED ? " lowspeed" : "",
+ port_status & USB_PORT_STAT_POWER ? " power" : "",
+ port_status & USB_PORT_STAT_RESET ? " reset" : "",
+ port_status & USB_PORT_STAT_OVERCURRENT ? " oc" : "",
+ port_status & USB_PORT_STAT_SUSPEND ? " suspend" : "",
+ port_status & USB_PORT_STAT_ENABLE ? " enable" : "",
+ port_status & USB_PORT_STAT_CONNECTION ? " connect" : "",
+ port_status == 0 ? " off" : ""
+ );
+ }
+ libusb_close(devh);
+ }
+ return 0;
+}
+
+/*
+ * Find all smart hubs that we are going to work on and fill hubs[] array.
+ * This applies possible constraints like location or vendor.
+ * Returns count of found hubs or negative error code.
+ */
+
+static int usb_find_hubs()
+{
+ struct libusb_device *dev;
+ unsigned char port_numbers[MAX_HUB_CHAIN] = {0};
+ int perm_ok = 1;
+ int rc = 0;
+ int i = 0;
+ while ((dev = usb_devs[i++]) != NULL) {
+ struct libusb_device_descriptor desc;
+ rc = libusb_get_device_descriptor(dev, &desc);
+ /* only scan for hubs: */
+ if (rc == 0 && desc.bDeviceClass != LIBUSB_CLASS_HUB)
+ continue;
+ int nports = is_smart_hub(dev, opt_internal ? 0 : 1);
+ if (nports < 0) {
+ perm_ok = 0; /* USB permission issue? */
+ }
+ if (nports > 0) { /* smart hub */
+ if (hub_count < MAX_HUBS) {
+ hubs[hub_count].dev = dev;
+ hubs[hub_count].nports = nports;
+
+ /* Convert bus and ports array into USB location string */
+ snprintf(
+ hubs[hub_count].vendor,
+ sizeof(hubs[hub_count].vendor),
+ "%04x:%04x",
+ desc.idVendor, desc.idProduct
+ );
+
+ int bus = libusb_get_bus_number(dev);
+ sprintf(hubs[hub_count].location, "%d", bus);
+ int pcount = libusb_get_port_numbers(dev, port_numbers, MAX_HUB_CHAIN);
+ int k;
+ for (k=0; k<pcount; k++) {
+ char s[8];
+ snprintf(s, sizeof(s), "%s%d", k==0 ? "-" : ".", port_numbers[k]);
+ strcat(hubs[hub_count].location, s);
+ }
+
+ /* apply location and other filters: */
+ if (strlen(opt_location)>0 && strcasecmp(opt_location, hubs[hub_count].location))
+ continue;
+ if (strlen(opt_vendor)>0 && strncasecmp(opt_vendor, hubs[hub_count].vendor, strlen(opt_vendor)))
+ continue;
+
+ hub_count++;
+ }
+ }
+ }
+ if (perm_ok == 0 && hub_count == 0) {
+ return LIBUSB_ERROR_ACCESS;
+ }
+ return hub_count;
+}
+
+int main(int argc, char *argv[])
+{
+ int rc;
+ int c = 0;
+ int option_index = 0;
+
+ for (;;) {
+ c = getopt_long(argc, argv, "l:n:a:p:d:r:w:hvi",
+ long_options, &option_index);
+ if (c == -1)
+ break; /* no more options left */
+ switch (c) {
+ case 0:
+ /* If this option set a flag, do nothing else now. */
+ if (long_options[option_index].flag != 0)
+ break;
+ printf("option %s", long_options[option_index].name);
+ if (optarg)
+ printf(" with arg %s", optarg);
+ printf("\n");
+ break;
+ case 'l':
+ strncpy(opt_location, optarg, sizeof(opt_location));
+ opt_internal = 1;
+ break;
+ case 'n':
+ strncpy(opt_vendor, optarg, sizeof(opt_vendor));
+ break;
+ case 'i':
+ strncpy(opt_vendor, "", sizeof(opt_vendor));
+ opt_internal = 1; /* enable internal hubs if location was specified */
+ break;
+ case 'p':
+ if (!strcasecmp(optarg, "all")) { /* all ports is the default */
+ break;
+ }
+ if (strlen(optarg)) {
+ /* parse port list */
+ opt_ports = 0;
+ size_t i;
+ for (i=0; i<strlen(optarg); i++) {
+ if (!isdigit(optarg[i]) || optarg[i] == '0') {
+ printf("%s must be list of ports 1 to %d\n", optarg, MAX_HUB_PORTS);
+ }
+ int d = optarg[i]-'1';
+ opt_ports |= (1 << d);
+ }
+ }
+ break;
+ case 'a':
+ if (!strcasecmp(optarg, "off") || !strcasecmp(optarg, "0")) {
+ opt_action = POWER_OFF;
+ }
+ if (!strcasecmp(optarg, "on") || !strcasecmp(optarg, "1")) {
+ opt_action = POWER_ON;
+ }
+ if (!strcasecmp(optarg, "cycle") || !strcasecmp(optarg, "2")) {
+ opt_action = POWER_CYCLE;
+ }
+ break;
+ case 'd':
+ opt_delay = atoi(optarg);
+ break;
+ case 'r':
+ opt_repeat = atoi(optarg);
+ break;
+ case 'w':
+ opt_wait = atoi(optarg);
+ break;
+ case 'v':
+ printf("%s\n", PROGRAM_VERSION);
+ exit(0);
+ break;
+ case 'h':
+ print_usage();
+ exit(1);
+ break;
+ case '?':
+ /* getopt_long has already printed an error message here */
+ break;
+ default:
+ abort();
+ }
+ }
+
+ rc = libusb_init(NULL);
+ if (rc < 0) {
+ fprintf(stderr,
+ "Error initializing USB!\n"
+ );
+ exit(1);
+ }
+
+ rc = libusb_get_device_list(NULL, &usb_devs);
+ if (rc < 0) {
+ fprintf(stderr,
+ "Cannot enumerate USB devices!\n"
+ );
+ rc = 1;
+ goto cleanup;
+ }
+
+ rc = usb_find_hubs();
+ if (rc <= 0) {
+ fprintf(stderr,
+ "No compatible smart hubs detected%s%s!\n"
+ "Run with -h to get usage info.\n",
+ strlen(opt_location) ? " at location " : "",
+ opt_location
+ );
+#ifdef __gnu_linux__
+ if (rc < 0) {
+ fprintf(stderr,
+ "There were permission problems while accessing USB.\n"
+ "To fix this, run this tool as root using 'sudo uhubctl',\n"
+ "or add one or more udev rules like below\n"
+ "to file '/etc/udev/rules.d/52-usb.rules':\n"
+ "SUBSYSTEM==\"usb\", ATTR{idVendor}==\"2001\", MODE=\"0666\"\n"
+ "then run 'sudo udevadm trigger --attr-match=subsystem=usb'\n"
+ );
+ }
+#endif
+ rc = 1;
+ goto cleanup;
+ }
+
+ if (hub_count > 1 && opt_action >= 0) {
+ fprintf(stderr,
+ "Warning: changing port state for multiple hubs at once.\n"
+ "Use -l to limit operation to one hub!\n"
+ );
+ }
+ int i;
+ for (i=0; i<hub_count; i++) {
+ printf("Current status for hub %s, vendor %s, %d ports\n",
+ hubs[i].location, hubs[i].vendor, hubs[i].nports
+ );
+ hub_port_status(hubs[i].dev, hubs[i].nports, opt_ports);
+ if (opt_action == POWER_KEEP) { /* no action, show status */
+ continue;
+ }
+ struct libusb_device_handle * devh = NULL;
+ rc = libusb_open(hubs[i].dev, &devh);
+ if (rc == 0) {
+ /* will operate on these ports */
+ int ports = ((1 << hubs[i].nports) - 1) & opt_ports;
+ int k; /* k=0 for power OFF, k=1 for power ON */
+ for (k=0; k<2; k++) { /* up to 2 power actions - off/on */
+ if (k == 0 && opt_action == POWER_ON )
+ continue;
+ if (k == 1 && opt_action == POWER_OFF)
+ continue;
+ int request = (k == 0) ? LIBUSB_REQUEST_CLEAR_FEATURE
+ : LIBUSB_REQUEST_SET_FEATURE;
+ int port;
+ for (port=1; port <= hubs[i].nports; port++) {
+ if ((1 << (port-1)) & ports) {
+ int port_status = get_port_status(devh, port);
+ if (k == 0 && !(port_status & USB_PORT_STAT_POWER))
+ continue;
+ if (k == 1 && (port_status & USB_PORT_STAT_POWER))
+ continue;
+ int repeat = 1;
+ if (k == 0)
+ repeat = opt_repeat;
+ if (!(port_status & ~USB_PORT_STAT_POWER))
+ repeat = 1;
+ while (repeat-- > 0) {
+ rc = libusb_control_transfer(devh,
+ LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_OTHER,
+ request, USB_PORT_FEAT_POWER,
+ port, NULL, 0, USB_CTRL_GET_TIMEOUT
+ );
+ if (rc < 0) {
+ perror("Failed to control port power!\n");
+ }
+ if (repeat > 0) {
+ usleep(opt_wait * 1000);
+ }
+ }
+ }
+ }
+ if (k==0 && opt_action == POWER_CYCLE)
+ sleep(opt_delay);
+ printf("Sent power %s request\n",
+ request == LIBUSB_REQUEST_CLEAR_FEATURE ? "off" : "on"
+ );
+ printf("New status for hub %s, vendor %s, %d ports\n",
+ hubs[i].location, hubs[i].vendor, hubs[i].nports
+ );
+ hub_port_status(hubs[i].dev, hubs[i].nports, opt_ports);
+ }
+ libusb_close(devh);
+ }
+ }
+ rc = 0;
+cleanup:
+ if (usb_devs)
+ libusb_free_device_list(usb_devs, 1);
+ usb_devs = NULL;
+ libusb_exit(NULL);
+ return rc;
+}