92 Commits
v1.8 ... v2.1.0

Author SHA1 Message Date
Vadim Mikhailov
c9fa3c68a1 Release version 2.1.0 2019-09-02 23:12:50 -07:00
Vadim Mikhailov
beea0c0fc7 Use longer git hash for dirty version 2019-09-02 23:12:46 -07:00
Vadim Mikhailov
1856ae9d59 Use --location for long option name
Add --location as long option name - it is still backwards compatible with --loc.
2019-09-02 23:09:56 -07:00
Jonathan Liu
2b9b336038 Add Phidgets HUB0003_0 as supported device 2019-08-29 22:25:16 -07:00
Vadim Mikhailov
1961aa02e9 Add explicit workaround for USB device not removed after port power down on Linux 2019-07-31 13:14:24 -07:00
Vadim Mikhailov
5db248771e Add support for NetBSD
This adds support to be able to build and use uhubctl on NetBSD by using pkg-config to get proper CFLAGS and LDFLAGS.
Perhaps we should use pkg-config universally across all platforms,
but I don't want to potentially break support for existing plaftorms
because pkg-config does not seem to be unconditionally available everywhere.
2019-07-26 18:00:49 -07:00
Vadim Mikhailov
9cae5e275f Add ThinkPad X6 Ultrabase as supported device
Reported in #165.
2019-07-23 21:31:20 -07:00
Vadim Mikhailov
324acc66fd Add status for Raspberry 4B support
Added a link to Raspberry 4B issue https://github.com/raspberrypi/linux/issues/3079.
Once this is fixed in firmware, uhubctl should start working on 4B.
2019-07-22 23:21:47 -07:00
Vadim Mikhailov
15f2d37d79 Add buildenlights (red/green project status lights) to notable projects 2019-07-22 22:53:00 -07:00
Vadim Mikhailov
5f8426105c USB2/3 duality: require serial numbers match only if both present
I have observed a hub which had serial number present for USB2 compatibility hub,
but not for USB3, which broke USB2/3 duality handling:

    Current status for hub 4-4 [0451:8140, USB 3.00, 4 ports]
      Port 1: 06a0 power Rx.Detect
      Port 2: 06a0 power Rx.Detect
      Port 3: 06a0 power Rx.Detect
      Port 4: 06a0 power Rx.Detect
    Current status for hub 3-13 [0451:8142 210C08692601, USB 2.10, 4 ports]
      Port 1: 0100 power
      Port 2: 0100 power
      Port 3: 0100 power
      Port 4: 0100 power

This fix will only compare serial numbers if they are both present.
2019-07-15 10:45:21 -07:00
Vadim Mikhailov
945d08e62c Add FAQ
Added FAQ with issues that are often asked  and tend to have duplicate issues opened.
2019-07-13 18:14:55 -07:00
Vadim Mikhailov
078f9ee9c6 Add Dell P2416D 24" QHD Monitor as supported device
Reported in #164.
2019-07-11 11:48:23 -07:00
Vadim Mikhailov
75023dc28c Stop searching if matching device is found
This is minor fix for issue #161.
2019-07-04 10:46:25 -07:00
Vadim Mikhailov
a510d091af Implemented reading ContainerID from BOS descriptor to detect USB2/3 dual devices
Per USB 3.0 spec, chapter 11.2:

> Within a USB 3.0 hub, both the SuperSpeed and USB 2.0 hub devices shall implement in the hub framework
> a common standardized ContainerID to enable software to identify the physical relationship of the hub devices.
> The ContainerID descriptor is a part of the BOS descriptor set.

* Read ContainerID from BOS descriptor.
* get_device_description() now returns struct with vendor, product, serial number as well as full description.
* Use ContainerID to improve finding dual hubs. This seems to work very well on any OS.
  However, this may still fail if two or more identical hubs use the same hardcoded ContainerID
  In this case, we still try to look for other clues like serial number match if possible.

This fixes issue #161.
2019-07-04 00:45:22 -07:00
Vadim Mikhailov
d8f75116c8 Add Dell UltraSharp 1704FPT as supported device
Reported in #157.
2019-06-16 22:50:05 -07:00
Vadim Mikhailov
94a95a7011 Fixed info about Moxa Uport-407 7-port hub 2019-06-16 21:12:17 -07:00
Vadim Mikhailov
04238bf939 Add VID:PID for Moxa Uport-407 and also update release year
This fixes issue #154.
2019-06-15 15:12:18 -07:00
Vadim Mikhailov
a9da51fb1c Fixed help text for level, some code style fixes 2019-05-17 14:38:17 -07:00
mvp
40cdb66d66 Merge pull request #152 from wjzhou/tux/level-match
add a level hub filter
2019-05-17 14:33:00 -07:00
Wujun Zhou
ee0287077b fix %d is used by the printf 2019-05-17 15:47:16 -04:00
Wujun Zhou
798d2323a9 fix help text for level 2019-05-17 15:43:23 -04:00
Wujun Zhou
bd3e398f4e add comment for the same level check in dual 2019-05-17 15:40:39 -04:00
Wujun Zhou
74a5d721c5 use 0 as opt_level wildcard 2019-05-17 15:39:10 -04:00
Wujun Zhou
134decf02b c style change 2019-05-17 15:27:38 -04:00
Wujun Zhou
dab7ab8320 check for level when match dual 2019-05-14 14:30:46 -04:00
Wujun Zhou
1926954998 add level parameter 2019-05-14 11:48:59 -04:00
Vadim Mikhailov
e93d1a06a2 Use version from VERSION file if git describe fails
This fixes issue #100.

We prefer to determine version from git tags (using git describe).
However, sometimes people don't use git or don't have it installed,
which leads to empty version used.
To solve this, we use version from VERSION file as a backup.
2019-04-28 11:38:17 -07:00
Vadim Mikhailov
fc7aabb529 Support fractional seconds for delay
Allow power cycle delay to be fractional seconds, e.g. not only 2 sec,
but also like 0.3 sec or 1.5 sec.
2019-04-16 15:23:19 -07:00
Vadim Mikhailov
f80467f0fb Fixed broken links in notable projects
Unfortunately, goo.gl does not allow to create new short links anymore,
so slowly switching over to bit.ly.
2019-03-18 11:13:06 -07:00
Vadim Mikhailov
8bbf37f301 Add "USB fan for Raspberry Pi" to notable projects 2019-03-14 18:29:26 -07:00
Vadim Mikhailov
ae4be0a9d5 Note that on *BSD gmake should be used to build. 2019-03-05 15:37:54 -08:00
Vadim Mikhailov
00d1a408b8 Fix build on FreeBSD
* FreeBSD has the same issue as Darwin for snprintf, and same ifdef hack to solve.
* FreeBSD's libusb is stuck at 1.0.12, and does not support LIBUSB_DT_SUPERSPEED_HUB.
  Use ifdef hack to bypass.
* FreeBSD does not have libusb_get_parent() - use libusb_get_port_numbers() to detect parent device.
2019-03-05 15:27:13 -08:00
Vadim Mikhailov
c237ec17ba Add explanation how to configure Linux USB udev permissions 2019-02-24 17:17:22 -08:00
Vadim Mikhailov
1773ac2605 Add ESPRESSObin SBUD102 as supported device 2019-02-24 17:13:56 -08:00
Vadim Mikhailov
343e02b634 Improve USB2/3 duality handling
If hub and its dual have identical description except for vid:pid and USB version strings,
we consider this preferred match.
2019-02-24 11:15:22 -08:00
Vadim Mikhailov
f2a50dd4cb Add OctoPrint-USBControl to notable projects 2019-02-15 11:11:13 -08:00
Vadim Mikhailov
2b88680d3b Add TI TUSB4041PAPEVM as supported device
Reported by @rshaub in issue #137.
2019-02-06 12:49:33 -08:00
Vadim Mikhailov
43edb16298 Remove Transcend TS-HUB3K from supported list
Remove Transcend TS-HUB3K from supported list per issue #135 (does not turn off VBUS).
2019-02-06 12:39:17 -08:00
Vadim Mikhailov
21336c7ab1 Remove TP-Link UH700 from supported list
It turns out that TP-Link UH700 does not support VBUS cut off (issue #133).
2019-01-11 11:32:03 -08:00
Vadim Mikhailov
6c4e5cdf75 Add "USB drive data recovery" to notable projects 2019-01-04 15:54:25 -08:00
Vadim Mikhailov
04320872f5 Add support for comma separated list of ranges to port list parser
* Add support for comma separated list of ports, and possibly ranges,
  e.g. it is possible to use syntax like `1,3-5,9-12`.
* Increase MAX_HUB_PORTS to 14.

WARNING: this will break your scripts if you used syntax like `-p 1234` before.
Replace that syntax with `1,2,3,4` or `1-4` instead.
2019-01-04 15:42:33 -08:00
Vadim Mikhailov
2de7def9df Improve USB3 support
* Fixed `uhubctl -l <hub>` displaying unrelated devices for USB 3.
* Some USB3 hubs use USB_SS_PORT_LS_SS_DISABLED when port is off, but some keep USB_SS_PORT_LS_RX_DETECT.
  To handle this, check USB_SS_PORT_STAT_POWER bit to detect if port is off.
2019-01-04 15:42:33 -08:00
Vadim Mikhailov
f2d016a27c Bump copyright year 2019-01-04 15:42:33 -08:00
mvp
72ac02c324 Merge pull request #128 from silum/master
Update company name and add another compatible hub
2018-11-16 10:17:39 -08:00
Deneys S. Maartens
7ad7d21336 README.md: add B+B USH304 2018-11-16 13:46:50 +02:00
Deneys S. Maartens
4b61cb9e0b README.md: update B+B UHR204
- rename 'B&B Electronics' to 'B+B SmartWorx' to reflect recent
rebranding
- add VID:POD
2018-11-16 13:46:41 +02:00
Vadim Mikhailov
cfee02f014 Fix typo, some spelling formatting issues 2018-11-14 22:06:51 -08:00
Vadim Mikhailov
1aaae4bbb8 Update link to https 2018-11-14 21:58:35 -08:00
Vadim Mikhailov
069c67c836 Respect CPPFLAGS
This should fix issue #125.
2018-11-14 21:53:44 -08:00
Vadim Mikhailov
465aa9bb95 Remove Delock 87445 from supported list
According to report from @vkahl: https://github.com/mvp/uhubctl/issues/123,
Delock 87445 is not detected on various Linux systems.

Originally this hub was reported to work in https://github.com/mvp/uhubctl/pull/107.
I don't know if there were different hardware revisions of Delock 87445,
but it is safer to remove it from supported devices list until proven otherwise.

Also, I am skeptical that it will work correctly as it does not have external power supply.
2018-11-14 11:08:24 -08:00
Vadim Mikhailov
a65c576ca3 Add Club3D CSV-3242HD as supported device
Reported by @matthijskooijman in https://github.com/mvp/uhubctl/issues/122.
2018-11-08 09:51:01 -08:00
Vadim Mikhailov
a2b3695595 Add TP-Link UH700 and Transcend TS-HUB3K as supported devices
Reported by @yrjola in https://github.com/mvp/uhubctl/issues/121.
2018-11-01 17:47:10 -07:00
Vadim Mikhailov
7bf08c0f0f Add USB drives with no phantom load to notable projects 2018-10-12 11:26:03 -07:00
mvp
a9474fe12f Add IOI StarTech U3H415E1 as supported device
Reported by serjepatoff in https://github.com/mvp/uhubctl/issues/116.
2018-09-24 22:53:21 -07:00
mvp
bfc4477189 Merge pull request #114 from Linumiz/bb
Add bitbake recipe file for building with Yocto
2018-09-21 14:33:25 -07:00
Parthiban Nallathambi
5a0995b7b2 Add bitbake recipe file for building with Yocto
- bitbake recipe for yocto building, pulls the latest
src from master

Signed-off-by: Parthiban Nallathambi <parthiban@linumiz.com>
2018-09-19 19:07:44 +02:00
Vadim Mikhailov
dda0ab22f2 Add a note about Raspberry Pi compatibility issue with AmazonBasics hubs
Apparently, Amazon basics hubs are not detectable on Raspberry Pi,
which is documented at https://elinux.org/RPi_Powered_USB_Hubs#Problem_USB_Hubs.
Add a note about this in supported devices table.
2018-09-18 17:47:34 -07:00
mvp
de2c0f96cc Add StarTech ST4200USBM as supported device
Reported by Joey Hess in https://github.com/mvp/uhubctl/issues/112.
2018-08-16 20:45:57 -07:00
Vadim Mikhailov
77c7a7ace8 Add build status light to notable projects 2018-07-19 17:51:47 -07:00
Vadim Mikhailov
8eb39a6138 Reformatted notable projects section of README 2018-07-13 13:36:02 -07:00
mvp
32bc22ea4f Add notable projects
Added list of interesting projects that use uhubctl.
2018-07-06 15:33:29 -07:00
mvp
eaa2795025 Add Delock 87445 as supported device
Originally reported in https://github.com/mvp/uhubctl/pull/107.
2018-07-06 15:03:05 -07:00
Vadim Mikhailov
17094135fc j5create JUH470 only works in USB2 mode
Issue #103: j5create JUH470 doesn't turn off in USB3 mode, but seems to work in USB2 mode.
2018-06-15 10:25:59 -07:00
Vadim Mikhailov
4e18d53426 Add D-Link DUB H7 rev D1 (black) as supported device
Add D-Link DUB H7 rev D1 (black) to supported list as reported in issue #104.
2018-06-15 10:22:36 -07:00
mvp
51ff805c79 Add Renesas uPD720202 PCIe USB 3.0 host controller as supported device
Added new device per this report: https://unix.stackexchange.com/q/165447/321945#comment765232_321945.
Perhaps we should replace this with specific products that use Renesas uPD720202 chipset.
2018-05-08 19:55:14 -07:00
mvp
0cfd1f7341 Add Raspberry Pi Model 3 B+ as supported device
Unlike previous models, Raspberry Pi 3 B+ has two internally daisy-chained hubs:

$ uhubctl
Current status for hub 1-1.1 [0424:2514, USB 2.00, 3 ports]
  Port 1: 0503 power highspeed enable connect [0424:7800]
  Port 2: 0100 power
  Port 3: 0100 power
Current status for hub 1-1 [0424:2514, USB 2.00, 4 ports]
  Port 1: 0503 power highspeed enable connect [0424:2514, USB 2.00, 3 ports]
  Port 2: 0503 power
  Port 3: 0100 power
  Port 4: 0100 power

and this would require using `-l 1-1` or `-l 1-1.1` to control ports on specific hub.

However, I don't have actual RPi 3B+ hardware at hand and can't test if per-port power
switching is fully supported - all previous models only supported ganged switching
for ports 2-5.
2018-05-08 19:49:52 -07:00
mvp
e6f102a55d Add columns for port count and USB version into device table
Also clarify what format is used for hub locations.
2018-04-30 16:07:02 -07:00
mvp
003ba1ff66 Code formatting cleanups 2018-04-30 11:49:50 -07:00
mvp
2311a1c76f Fixed displaying duplicate hubs in status output
This should fix issue #91.
2018-04-15 16:50:21 -07:00
mvp
4661d892bf Add Moxa Uport-407 as supported device 2018-04-15 16:05:37 -07:00
mvp
448e84933a Remove mention of building libusb 1.0.22 from head
Now that issue #33 is resolved (libusb-1.0.22 is released and is included in Homebrew),
we can remove workaround mention from README.
2018-04-05 15:17:35 -07:00
mvp
8da851a50d Merge pull request #87 from nunojpg/master
move action to outer loop and hub to inner loop (fixes #86)
2018-04-02 00:16:43 -07:00
Nuno Goncalves
91ba867ce2 move action to outer loop and hub to inner loop (fixes #86)
Signed-off-by: Nuno Goncalves <nunojpg@gmail.com>
2018-04-02 08:09:31 +01:00
mvp
5efb1487e5 Merge pull request #84 from quintusfelix/master
Add Lindy USB serial converter as supported device
2018-04-01 00:14:07 -07:00
Boerje Sewing
c712868525 Add Lindy USB serial converter as supported device
Signed-off-by: Boerje Sewing <Boerje.Sewing@who-ing.de>
2018-03-28 10:47:48 +02:00
Vadim Mikhailov
fa90f13ba8 Add j5create JUH470 as supported device
This closes issue #78.
2018-03-12 19:34:24 -07:00
Vadim Mikhailov
16ffd56b4b Add Plugable USB3-HUB7C as supported device
Missed to add this model from issue #54.
2018-03-12 19:34:18 -07:00
Vadim Mikhailov
6e05aee30e uhubctl 2.0.0
* Clarify that uhubctl will automatically handle USB3 duality.
* Bump copyright year.
* Mention more Linux distributions where uhubctl was tested to work.
2018-03-10 18:23:01 -08:00
Vadim Mikhailov
812197fe4b Add support for automatic USB2/3 dual power switch
When compatible USB3 hub connected to USB3 upstream port,
it is detected as 2 independent virtual hubs: USB2 and USB3,
and USB devices will be connected to USB2 or USB3 virtual hub
depending on their capabilities and connection speed.

To control power for physical USB3 hubs, we must turn off/on power
on both USB2 and USB3 virtual hubs for power off/on changes to take effect.

Included fixes:

* Enumerate all USB hubs, but mark ones we will work on with actionable flag.
  Because of this, increased max size of hubs array to 128 (maybe need even more?).
* Moved all code that was getting hub info into get_hub_info().
* Display "power" status as first status flag if port is powered.
  (uhubctl is about USB port power, and we should display power as first flag).
* Display "off" status for USB3 port when it is off (was "5gbps SS.Disabled").
  This makes both USB2 and USB3 status to display "off" consistently.
* Added expert option --exact which disables USB3 duality handling.
* Added small wait (150ms) after turning off USB3 port.
  It seems that USB3 reacts slowly to power off command.
  Perhaps we need to implement more deterministic approach for this,
  e.g. keep querying new power state few times until it has finally changed.
  Oddly enough, power on is always instant and does not need waiting.

This fixes issue #72.
2018-03-10 17:37:46 -08:00
Vadim Mikhailov
4d84b62fc3 Add Plugable USB3-HUB7-81X as supported device
Adding this hub to supported list per this report:
https://github.com/mvp/uhubctl/issues/54#issuecomment-369695596
2018-03-01 12:32:09 -08:00
Vadim Mikhailov
60ba7c915d Add Plugable USB3-HUB7BC as supported device
It turns out that with uhubctl improved USB3 support
this hub actually works great if we turn off power
on both USB2 and USB3 virtual hubs ports (see issue #54).
2018-03-01 11:34:59 -08:00
Vadim Mikhailov
eeff46fa0c Clarify support status for AmazonBasics hubs
It turns out that AmazonBasics hubs actually work well -
USB3 hubs require changing power state for both USB2 and USB3
virtual hubs when connected to USB3 upstream port.

Perhaps we should implement changing state for both USB2 and USB3
automatically, but this will be tracked in separate issue.
2018-02-18 13:02:53 -08:00
Vadim Mikhailov
41dadcea34 Add a note about USB 3.0 hubs duality and usage quirks 2018-02-15 18:30:19 -08:00
mvp
bccb920526 Merge pull request #71 from mvp/usb3
USB 3.0 support
2018-02-15 11:46:27 -08:00
Vadim Mikhailov
fb5d521290 Use appropriate power mask for USB2 vs USB3
USB3 should use USB_SS_PORT_STAT_POWER,
which is different from USB2 value USB_PORT_STAT_POWER.

Thanks to Stanislas Bertrand for spotting this bug!
2018-02-14 15:54:23 -08:00
Vadim Mikhailov
8e667cde10 Add support for USB 3.0
* add support for USB 3.0 (superspeed USB)
* use snprintf (which is weird on Mac)
* get device description in unified way
* trim trailing whitespace from vendor/product strings
* finally remove deprecated -i option
* require hub location to change power if more than one supported hub detected
2018-02-13 12:01:33 -08:00
Vadim Mikhailov
022e321f71 Clarify support status for AmazonBasics hubs
AmazonBasics hubs have conflicting reports about
power switching support (issues #17, #26, #50, #54, #57, #58).
Apparently, there are some hardware revisions
that support it properly, and others do not.

To avoid confusion, we say that not all AmazonBasics hubs
work as expected. That said, it would be great to find out
which hardware revisions actually work.
2018-02-02 17:23:02 -08:00
Vadim Mikhailov
91e987527a Add D-Link DUB-H4 rev D1 as supported device 2018-02-02 11:14:37 -08:00
Vadim Mikhailov
a1b7411c49 Add Cypress CY7C65632 HX2VL as supported device 2018-02-01 15:23:17 -08:00
mvp
79e44f13c7 Merge pull request #63 from jlmcgehee21/patch-1
Add 4 Port Amazon Basics Hub
2018-01-15 14:11:42 -08:00
Jeff McGehee
5aaa9c54e8 Add 4 Port Amazon Basics Hub
```
$ uhubctl
Current status for hub 20-1, vendor 2109:2811, 4 ports
   Port 1: 0100 power
   Port 2: 0100 power
   Port 3: 0100 power
   Port 4: 0100 power
```
2018-01-15 10:24:15 -05:00
Vadim Mikhailov
e53d6d2f36 Added note about Raspberry Pi behavior
Raspberry Pi can only use USB port 2 to control power on all ports.
Trying to control any other port is not guaranteed.
For some models, USB port 1 is wired to control Ethernet/Wifi.

Also, mention Mac support issues and workarounds.
2017-10-02 13:03:12 -07:00
6 changed files with 783 additions and 227 deletions

View File

@@ -1,6 +1,6 @@
uhubctl USB hub per-port power control.
Copyright (c) 2009-2017, Vadim Mikhailov
Copyright (c) 2009-2019, Vadim Mikhailov
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View File

@@ -14,7 +14,10 @@ RM := rm -rf
CC ?= gcc
CFLAGS ?= -g -O0
CFLAGS += -Wall -Wextra -std=c99 -pedantic
GIT_VERSION := $(shell git describe --abbrev=4 --dirty --always --tags)
GIT_VERSION := $(shell git describe --abbrev=8 --dirty --always --tags)
ifeq ($(GIT_VERSION),)
GIT_VERSION := $(shell cat VERSION)
endif
CFLAGS += -DPROGRAM_VERSION=\"$(GIT_VERSION)\"
ifeq ($(UNAME_S),Linux)
@@ -34,10 +37,15 @@ ifeq ($(UNAME_S),FreeBSD)
LDFLAGS += -lusb
endif
ifeq ($(UNAME_S),NetBSD)
CFLAGS += $(shell pkg-config --cflags libusb-1.0)
LDFLAGS += $(shell pkg-config --libs libusb-1.0)
endif
PROGRAM = uhubctl
$(PROGRAM): $(PROGRAM).c
$(CC) $(CFLAGS) $@.c -o $@ $(LDFLAGS)
$(CC) $(CPPFLAGS) $(CFLAGS) $@.c -o $@ $(LDFLAGS)
install:
$(INSTALL_DIR) $(DESTDIR)$(sbindir)

271
README.md
View File

@@ -1,11 +1,11 @@
uhubctl
=======
uhubctl is utility to control USB power per-port on smart USB hubs.
`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
https://www.gniibe.org/development/ac-power-control-by-USB-hub
Compatible USB hubs
@@ -16,36 +16,58 @@ Some of them are no longer manufactured and can be hard to find.
This is list of known compatible USB hubs:
| Manufacturer | Product | VID:PID | Release | EOL |
|:-------------------|:-------------------------------------------------------|:----------|:--------|:-----|
| AmazonBasics | HU3770V1, 7 Port USB 3.0 Hub with 12V/3A Power Adapter |`2109:2811`| 2013 | |
| Apple | Thunderbolt Display 27" (internal USB hub) | | 2011 | 2016 |
| Apple | USB Keyboard With Numeric Pad (internal USB hub) | | 2011 | |
| Asus | Z87-PLUS Motherboard (onboard USB hubs) | | 2013 | 2016 |
| B&B Electronics | UHR204 | | 2013 | |
| Belkin | F5U701-BLK | | 2008 | 2012 |
| Circuitco | Beagleboard-xM (internal USB hub) |`0424:9514`| 2010 | |
| CyberPower | CP-H420P |`0409:0059`| 2004 | |
| D-Link | DUB-H7 (silver edition only, new black not working) |`2001:F103`| 2005 | 2010 |
| Elecom | U2H-G4S | | 2006 | 2011 |
| Hawking Technology | UH214 | | 2003 | 2008 |
| Lenovo | ThinkPad EU Ultra Dockingstation (40A20090EU) |`17EF:100F`| 2015 | |
| Lenovo | ThinkPad X200 Ultrabase 42X4963 |`17EF:1005`| 2008 | 2011 |
| Linksys | USB2HUB4 | | 2004 | 2010 |
| Maplin | A08CQ |`0409:0059`| 2008 | 2011 |
| Microchip | EVB-USB2517 | | 2008 | |
| Plugable | USB2-HUB10S | | 2010 | |
| Raspberry Pi | Model B+, Model 2 B, Model 3 B | | 2011 | |
| Rosewill | RHUB-210 |`0409:005A`| 2011 | 2014 |
| Sanwa Supply | USB-HUB14GPH | | 2001 | 2003 |
| Sunix | SHB4200MA |`0409:0058`| 2006 | 2009 |
| Targus | PAUH212U | | 2004 | 2009 |
| Manufacturer | Product | Ports | USB | VID:PID | Release | EOL |
|:-------------------|:-----------------------------------------------------|:------|:----|:----------|:--------|:-----|
| AmazonBasics | HU3641V1 ([RPi issue](https://goo.gl/CLt46M)) | 4 | 3.0 |`2109:2811`| 2013 | |
| AmazonBasics | HU3770V1 ([RPi issue](https://goo.gl/CLt46M)) | 7 | 3.0 |`2109:2811`| 2013 | |
| Apple | Thunderbolt Display 27" (internal USB hub) | 6 | 2.0 | | 2011 | 2016 |
| Apple | USB Keyboard With Numeric Pad (internal USB hub) | 3 | 2.0 | | 2011 | |
| Asus | Z87-PLUS Motherboard (onboard USB hub) | 4 | 3.0 | | 2013 | 2016 |
| B+B SmartWorx | UHR204 | 4 | 2.0 |`0856:DB00`| 2013 | |
| B+B SmartWorx | USH304 | 4 | 3.0 |`04B4:6506`| 2017 | |
| Belkin | F5U701-BLK | 7 | 2.0 | | 2008 | 2012 |
| Circuitco | Beagleboard-xM (internal USB hub) | 4 | 2.0 |`0424:9514`| 2010 | |
| Club3D | CSV-3242HD Dual Display Docking Station | 4 | 3.0 |`2109:2811`| 2015 | |
| CyberPower | CP-H420P | 4 | 2.0 |`0409:0059`| 2004 | |
| Cypress | CY4608 HX2VL development kit | 4 | 2.0 |`04B4:6570`| 2012 | |
| D-Link | DUB-H4 rev D1 (black edition) | 4 | 2.0 |`05E3:0608`| 2012 | |
| D-Link | DUB-H7 rev A (silver edition) | 7 | 2.0 |`2001:F103`| 2005 | 2010 |
| D-Link | DUB-H7 rev D1 (black edition) | 7 | 2.0 |`05E3:0608`| 2012 | |
| Dell | P2416D 24" QHD Monitor | 4 | 2.0 | | 2017 | |
| Dell | UltraSharp 1704FPT 17" LCD Monitor | 4 | 2.0 |`0424:A700`| 2005 | 2015 |
| Elecom | U2H-G4S | 4 | 2.0 | | 2006 | 2011 |
| GlobalScale | ESPRESSObin SBUD102 V5 | 1 | 3.0 |`1D6B:0003`| 2017 | |
| Hawking Technology | UH214 | 4 | 2.0 | | 2003 | 2008 |
| IOI | U3H415E1 | 4 | 3.0 | | 2012 | |
| j5create | JUH470 (works only in USB2 mode) | 3 | 3.0 |`05E3:0610`| 2014 | |
| Lenovo | ThinkPad EU Ultra Dockingstation (40A20090EU) | 6 | 2.0 |`17EF:100F`| 2015 | |
| Lenovo | ThinkPad X200 Ultrabase 42X4963 | 3 | 2.0 |`17EF:1005`| 2008 | 2011 |
| Lenovo | ThinkPad X6 Ultrabase 42W3107 | 4 | 2.0 |`17EF:1000`| 2006 | 2009 |
| Lindy | USB serial converter 4 port | 4 | 1.1 |`058F:9254`| 2008 | |
| Linksys | USB2HUB4 | 4 | 2.0 | | 2004 | 2010 |
| Maplin | A08CQ | 7 | 2.0 |`0409:0059`| 2008 | 2011 |
| Microchip | EVB-USB2517 | 7 | 2.0 | | 2008 | |
| Moxa | Uport-407 | 7 | 2.0 |`110A:0407`| 2009 | |
| Phidgets | HUB0003_0 | 7 | 2.0 |`1A40:0201`| 2017 | |
| Plugable | USB3-HUB7BC | 7 | 3.0 |`2109:0813`| 2015 | |
| Plugable | USB3-HUB7C | 7 | 3.0 |`2109:0813`| 2015 | |
| Plugable | USB3-HUB7-81X | 7 | 3.0 |`2109:0813`| 2012 | |
| Plugable | USB2-HUB10S | 10 | 2.0 | | 2010 | |
| Raspberry Pi | Model B+, 2 B, 3 B (port 2 only) | 4 | 2.0 | | 2011 | |
| Raspberry Pi | Model 3 B+ | 6 | 2.0 |`0424:2514`| 2018 | |
| Renesas | uPD720202 PCIe USB 3.0 host controller | 2 | 3.0 | | 2013 | |
| Rosewill | RHUB-210 | 4 | 2.0 |`0409:005A`| 2011 | 2014 |
| Sanwa Supply | USB-HUB14GPH | 4 | 1.1 | | 2001 | 2003 |
| StarTech | ST4200USBM | 4 | 2.0 |`0409:005A`| 2004 | |
| Sunix | SHB4200MA | 4 | 2.0 |`0409:0058`| 2006 | 2009 |
| Targus | PAUH212U | 7 | 2.0 | | 2004 | 2009 |
| Texas Instruments | TUSB4041PAPEVM | 4 | 2.1 |`0451:8142`| 2015 | |
This table is by no means complete.
If your hub works with uhubctl, but is not listed above, please report it
If your hub works with `uhubctl`, but is not listed above, please report it
by opening new issue at https://github.com/mvp/uhubctl/issues,
so we can add it to supported table. In your report, please provide
exact product model and add output from uhubctl.
exact product model and add output from `uhubctl`.
Note that quite a few modern motherboards have built-in root hubs that
do support this feature - you may not even need to buy any external hub.
@@ -53,11 +75,24 @@ WARNING: turning off built-in USB ports may cut off your keyboard or mouse,
so be careful what ports you are turning off!
USB 3.0 duality note
====================
If you have USB 3.0 hub connected to USB3 upstream port, it will be detected
as 2 independent virtual hubs: USB2 and USB3, and your USB devices will be connected
to USB2 or USB3 virtual hub depending on their capabilities and connection speed.
To control power for such hubs, it is necessary to turn off/on power on **both** USB2 and USB3
virtual hubs for power off/on changes to take effect. `uhubctl` will try to do this automatically
(unless you disable this behavior with option `-e`).
Unfortunately, while most hubs will cut off data USB connection, some may still not cut off VBUS to port,
which means connected phone may still continue to charge from port that is powered off by `uhubctl`.
Compiling
=========
This utility was tested to compile and work on Linux
(Ubuntu, Redhat/Fedora/CentOS), FreeBSD and Mac OS X.
(Ubuntu/Debian, Redhat/Fedora/CentOS, Arch Linux, Gentoo, openSUSE, Buildroot), FreeBSD, NetBSD and Mac OS X.
While `uhubctl` compiles on Windows, USB power switching does not work on Windows because `libusb`
is using `winusb.sys` driver, which according to Microsoft does not support
@@ -70,14 +105,20 @@ First, you need to install library libusb-1.0 (version 1.0.12 or later):
* Redhat: `sudo yum install libusb1-devel`
* MacOSX: `brew install libusb`, or `sudo port install libusb-devel`
* FreeBSD: libusb is included by default
* NetBSD: `sudo pkgin install libusb1 gmake pkg-config`
* Windows: TBD?
To compile, simply run `make` - this will generate `uhubctl` binary.
To fetch uhubctl source:
Alternatively, for macOS you can build an executable with homebrew's custom tap:
git clone https://github.com/mvp/uhubctl
To compile, simply run `make` - this will generate `uhubctl` binary.
Note that on some OS (e.g. FreeBSD/NetBSD) you need to use `gmake` instead to build.
Also, for Mac OS X you can install `uhubctl` with Homebrew custom tap:
```
brew tap mvp/uhubctl https://github.com/mvp/uhubctl/
brew tap mvp/uhubctl https://github.com/mvp/uhubctl
brew install --HEAD uhubctl
```
@@ -86,22 +127,178 @@ Usage
You can control the power on a USB port(s) like this:
uhubctl -a off -p 235
uhubctl -a off -p 2
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`).
on port 2 (`-p 2`). 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.
Ports can be comma separated list, and may use `-` for ranges e.g. `2`, or `2,4`, or `2-5`, or `1-2,5-8`.
If you have more than one smart USB hub connected, you should choose
specific hub to control using `-l` (location) parameter.
To find hub locations, simply run `uhubctl` without any parameters.
Hub locations look like `b-x.y.z`, where `b` is USB bus number, and `x`, `y`, `z`...
are port numbers for all hubs in chain, starting from root hub for a given USB bus.
This address is semi-stable - it will not change if you unplug/replug (or turn off/on)
USB device into the same physical USB port (this method is also used in Linux kernel).
Linux USB permissions
=====================
On Linux, you should configure `udev` USB permissions (otherwise you will have to run it as root using `sudo uhubctl`).
To fix USB permissions, first run `sudo uhubctl` and note all `vid:pid` for hubs you need to control.
Then, add one or more udev rules like below to file `/etc/udev/rules.d/52-usb.rules` (replace with your vendor id):
SUBSYSTEM=="usb", ATTR{idVendor}=="2001", MODE="0666"
If you don't like wide open mode `0666`, you can restrict access by group like this:
SUBSYSTEM=="usb", ATTR{idVendor}=="2001", MODE="0664", GROUP="dialout"
and then add permitted users to `dialout` group:
sudo usermod -a -G dialout $USER
For your `udev` rule changes to take effect, reboot or run:
sudo udevadm trigger --attr-match=subsystem=usb
FAQ
===
#### _What is USB per-port power switching?_
According to USB 2.0 specification, USB hubs can advertise no power switching,
ganged (all ports at once) power switching or per-port (individual) power switching.
Note that `uhubctl` will only detect USB hubs which support per-port power switching.
You can find what kind of power switching your hardware supports by using `sudo lsusb -v`:
No power switching:
wHubCharacteristic 0x000a
No power switching (usb 1.0)
Per-port overcurrent protection
Ganged power switching:
wHubCharacteristic 0x0008
Ganged power switching
Per-port overcurrent protection
Per-port power switching:
wHubCharacteristic 0x0009
Per-port power switching
Per-port overcurrent protection
#### _How do I check if my USB hub is supported by `uhubctl`?_
1. Run `sudo uhubctl`. If your hub is not listed, it is not supported.
Alternatively, you can run `sudo lsusb -v` and check for
`Per-port power switching` - if you cannot see such line in lsusb output,
hub is not supported.
2. Check for VBUS (voltage) off support: plug a phone, USB light
or USB fan into USB port of your hub.
Try using `uhubctl` to turn power off on that port, and check
that phone stops charging, USB light stops shining or USB fan stops spinning.
If VBUS doesn't turn off, your hub manufacturer did not include circuitry
to actually cut power off. Such hub would still work
to cut off USB data connection, but it cannot turn off power,
and we do not consider this supported device.
3. If tests above were successful, please report your hub
by opening new issue at https://github.com/mvp/uhubctl/issues,
so we can add it to list of supported devices.
#### _USB devices are not removed after port power down on Linux_
After powering down USB port, udev does not get any event, so it keeps the device files around.
However, trying to access the device files will lead to an IO error.
This is Linux kernel issue. It may be eventually fixed in kernel, see more discussion [here](https://bit.ly/2JzczjZ).
Basically what happens here is that kernel USB driver knows about power off,
but doesn't send notification about it to udev.
You can use this workaround for this issue:
sudo uhubctl -a off -l ${location} -p ${port}
sudo udevadm trigger --action=remove /sys/bus/usb/devices/${location}.${port}/
Device file will be removed by udev, but USB device will be still visible in `lsusb`.
Note that path `/sys/bus/usb/devices/${location}.${port}` will only exist if device was detected on that port.
When you turn power back on, device should re-enumerate properly (no need to call `udevadm` again).
#### _Power comes back on after few seconds on Linux_
Some device drivers in kernel are surprised by USB device being turned off and automatically try to power it back on.
You can use option `-r N` where N is some number from 10 to 1000 to fix this -
`uhubctl` will try to turn power off many times in quick succession, and it should suppress that.
This may be eventually fixed in kernel, see more discussion [here](https://bit.ly/2JzczjZ).
#### _Multiple 4-port hubs are detected, but I only have one 7-port hub connected_
Many hub manufacturers build their USB hubs using basic 4 port USB chips.
E.g. to make 7 port hub, they daisy-chain two 4 port hubs - 1 port is lost to daisy-chaining,
so it makes it 4+4-1=7 port hub. Simularly, 10 port hub could be built as 3 4-port hubs
daisy-chained together, which gives 4+4+4-2=10 usable ports.
Note that you should never try to change power state for ports used to daisy-chain internal hubs together.
Doing so will confuse internal hub circuitry and will cause unpredictable behavior.
#### _Raspberry Pi turns power off on all ports, not just the one I specified_
This is limitation of Raspberry Pi hardware design.
For reference, Raspberry Pi models have following internal USB topology:
* B+/2B/3B: one USB hub `1-1`, with port `1` for Ethernet+wifi, and ports `2-5` ganged, controlled by port `2`.
(Trying to control ports 3,4,5 won't do anything).
* 3B+: 2 hubs - main hub `1-1`, all 4 ports ganged, all controlled by port `2`.
Second hub `1-1.1` (daisy-chained to main): 3 independently controlled ports, `1` is used for Ethernet+wifi,
and ports `2,3` are available with proper per-port power switching.
In other words, 2 out of total 4 ports wired outside do support independent power switching on 3B+.
* 4B: Hardware supports power switching, but current firmware is reporting broken USB descriptors.
For more details see Raspberry Pi issue https://github.com/raspberrypi/linux/issues/3079.
USB3 hub `2`, 4 ports, per-port power switching. BOS `ContainerID` not reported (required for USB3).
USB2 hub `1`, 1 port, no usable ports, connects hub `1-1` below.
USB2 hub `1-1`, 4 ports, dual to USB3 hub above. Hub descriptor incorrectly reports ganged power switching.
USB2 hub `3`, 1 port, OTG controller, incorrectly reports ganged power switching.
As a workaround, you can buy any external USB hub from supported list,
attach it to any USB port of Raspberry Pi, and control power on its ports independently.
Notable projects using uhubctl
==============================
| Project | Description |
|:---------------------------------------------------------|:------------------------------------------------------|
| [Morse code USB light](https://git.io/fj1F4) | Flash a message in Morse code with USB light |
| [Webcam USB light](https://git.io/fj1FB) | Turn on/off LED when webcam is turned on/off |
| [Cinema Lightbox](https://goo.gl/fjCvkz) | Turn on/off Cinema Lightbox from iOS Home app |
| [Build Status Light](https://goo.gl/3GA82o) | Create a build status light in under 10 minutes |
| [Buildenlights](https://git.io/fj1FC) | GitLab/GitHub project build status as green/red light |
| [Weather Station](https://goo.gl/3b1FzC) | Reset Weather Station when it freezes |
| [sysmoQMOD](https://goo.gl/8wvcKA) | Reset cellular modems when necessary |
| [Smog Sensor](https://bit.ly/2EMwgCk) | Raspberry Pi based smog sensor power reset |
| [Terrible Cluster](https://goo.gl/XjiXFu) | Power on/off Raspberry Pi cluster nodes as needed |
| [Ideal Music Server](https://bit.ly/2UJq6Z9) | Turn off unused USB ports to improve audio quality |
| [USB drives with no phantom load](https://goo.gl/qfrmGK) | Power USB drives only when needed to save power |
| [USB drive data recovery](https://goo.gl/4MddLr) | Recover data from failing USB hard drive |
| [Control power to 3D printer](https://git.io/fh5Tr) | OctoPrint web plugin for USB power control |
| [USB fan for Raspberry Pi](https://bit.ly/2TRV6sM) | Control USB fan to avoid Raspberry Pi overheating |
Copyright
=========
Copyright (C) 2009-2017 Vadim Mikhailov
Copyright (C) 2009-2019 Vadim Mikhailov
This file can be distributed under the terms and conditions of the
GNU General Public License version 2.

1
VERSION Normal file
View File

@@ -0,0 +1 @@
v2.1.0-dev

704
uhubctl.c
View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009-2017 Vadim Mikhailov
* Copyright (c) 2009-2019 Vadim Mikhailov
*
* Utility to turn USB port power on/off
* for USB hubs that support per-port power switching.
@@ -29,12 +29,20 @@
#include <unistd.h>
#endif
#if defined(__FreeBSD__) || defined(_WIN32)
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(_WIN32)
#include <libusb.h>
#else
#include <libusb-1.0/libusb.h>
#endif
#if defined(__APPLE__) || defined(__FreeBSD__) /* snprintf is not available in pure C mode */
int snprintf(char * __restrict __str, size_t __size, const char * __restrict __format, ...) __printflike(3, 4);
#endif
#if !defined(LIBUSB_API_VERSION) || (LIBUSB_API_VERSION <= 0x01000103)
#define LIBUSB_DT_SUPERSPEED_HUB 0x2a
#endif
#if _POSIX_C_SOURCE >= 199309L
#include <time.h> /* for nanosleep */
#endif
@@ -55,14 +63,8 @@ void sleep_ms(int milliseconds)
#endif
}
/* 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
/* Max number of hub ports supported */
#define MAX_HUB_PORTS 14
#define ALL_HUB_PORTS ((1 << MAX_HUB_PORTS) - 1) /* bitmask */
#define USB_CTRL_GET_TIMEOUT 5000
@@ -78,6 +80,7 @@ void sleep_ms(int milliseconds)
/* Partially borrowed from linux/usb/ch11.h */
#pragma pack(push,1)
struct usb_hub_descriptor {
unsigned char bDescLength;
unsigned char bDescriptorType;
@@ -87,6 +90,7 @@ struct usb_hub_descriptor {
unsigned char bHubContrCurrent;
unsigned char data[1]; /* use 1 to avoid zero-sized array warning */
};
#pragma pack(pop)
/*
* Hub Status and Hub Change results
@@ -117,6 +121,41 @@ struct usb_port_status {
#define USB_PORT_STAT_INDICATOR 0x1000
/* bits 13 to 15 are reserved */
#define USB_SS_BCD 0x0300
/*
* Additions to wPortStatus bit field from USB 3.0
* See USB 3.0 spec Table 10-10
*/
#define USB_PORT_STAT_LINK_STATE 0x01e0
#define USB_SS_PORT_STAT_POWER 0x0200
#define USB_SS_PORT_STAT_SPEED 0x1c00
#define USB_PORT_STAT_SPEED_5GBPS 0x0000
/* Valid only if port is enabled */
/* Bits that are the same from USB 2.0 */
#define USB_SS_PORT_STAT_MASK (USB_PORT_STAT_CONNECTION | \
USB_PORT_STAT_ENABLE | \
USB_PORT_STAT_OVERCURRENT | \
USB_PORT_STAT_RESET)
/*
* Definitions for PORT_LINK_STATE values
* (bits 5-8) in wPortStatus
*/
#define USB_SS_PORT_LS_U0 0x0000
#define USB_SS_PORT_LS_U1 0x0020
#define USB_SS_PORT_LS_U2 0x0040
#define USB_SS_PORT_LS_U3 0x0060
#define USB_SS_PORT_LS_SS_DISABLED 0x0080
#define USB_SS_PORT_LS_RX_DETECT 0x00a0
#define USB_SS_PORT_LS_SS_INACTIVE 0x00c0
#define USB_SS_PORT_LS_POLLING 0x00e0
#define USB_SS_PORT_LS_RECOVERY 0x0100
#define USB_SS_PORT_LS_HOT_RESET 0x0120
#define USB_SS_PORT_LS_COMP_MOD 0x0140
#define USB_SS_PORT_LS_LOOPBACK 0x0160
/*
* wHubCharacteristics (masks)
* See USB 2.0 spec Table 11-13, offset 3
@@ -139,45 +178,62 @@ struct usb_port_status {
/* 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];
struct descriptor_strings {
char vendor[64];
char product[64];
char serial[64];
char description[256];
};
/* Array of USB hubs we are going to operate on */
#define MAX_HUBS 64
struct hub_info {
struct libusb_device *dev;
int bcd_usb;
int nports;
int ppps;
int actionable; /* true if this hub is subject to action */
char container_id[33]; /* container ID as hex string */
char vendor[16];
char location[32];
int level;
struct descriptor_strings ds;
};
/* Array of all enumerated USB hubs */
#define MAX_HUBS 128
static struct hub_info hubs[MAX_HUBS];
static int hub_count = 0;
static int hub_phys_count = 0;
/* default options */
static char opt_vendor[16] = "";
static char opt_location[16] = ""; /* Hub location a-b.c.d */
static char opt_location[32] = ""; /* Hub location a-b.c.d */
static int opt_level = 0; /* Hub location level (e.g., a-b is level 2, a-b.c is level 3)*/
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 double opt_delay = 2;
static int opt_repeat = 1;
static int opt_wait = 20; /* wait before repeating in ms */
static int opt_reset = 0; /* reset hub after operation(s) */
static int opt_exact = 0; /* exact location match - disable USB3 duality handling */
static int opt_reset = 0; /* reset hub after operation(s) */
static const struct option long_options[] = {
{ "loc", required_argument, NULL, 'l' },
{ "location", required_argument, NULL, 'l' },
{ "vendor", required_argument, NULL, 'n' },
/* -i is deprecated, keep it only for backwards compatibility: */
{ "internal", no_argument, NULL, 'i' },
{ "level", required_argument, NULL, 'L' },
{ "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' },
{ "exact", no_argument, NULL, 'e' },
{ "reset", no_argument, NULL, 'R' },
{ "version", no_argument, NULL, 'v' },
{ "help", no_argument, NULL, 'h' },
{ 0, 0, NULL, 0 },
};
int print_usage()
static int print_usage()
{
printf(
"uhubctl %s: utility to control USB port power for smart hubs.\n"
@@ -187,10 +243,12 @@ int print_usage()
"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"
"--location, -l - limit hub by location [all smart hubs].\n"
"--level -L - limit hub by location level (e.g. a-b.c is level 3).\n"
"--vendor, -n - limit hub by vendor id [%s] (partial ok).\n"
"--delay, -d - delay for cycle action [%d sec].\n"
"--delay, -d - delay for cycle action [%g sec].\n"
"--repeat, -r - repeat power off count [%d] (some devices need it to turn off).\n"
"--exact, -e - exact location (no USB3 duality handling).\n"
"--reset, -R - reset hub after each power-on action, causing all devices to reassociate.\n"
"--wait, -w - wait before repeat power off [%d ms].\n"
"--version, -v - print program version.\n"
@@ -206,40 +264,151 @@ int print_usage()
return 0;
}
/* trim trailing spaces from a string */
static char* rtrim(char* str)
{
int i;
for (i = strlen(str)-1; i>=0 && isspace(str[i]); i--) {
str[i] = 0;
}
return str;
}
/*
* 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
* Convert port list into bitmap.
* Following port list specifications are equivalent:
* 1,3,4,5,11,12,13
* 1,3-5,11-13
* Returns: bitmap of specified ports, max port is MAX_HUB_PORTS.
*/
int is_smart_hub(struct libusb_device *dev, int min_current)
static int ports2bitmap(char* const portlist)
{
int ports = 0;
char* position = portlist;
char* comma;
char* dash;
int len;
int i;
while (position) {
char buf[8] = {0};
comma = strchr(position, ',');
len = sizeof(buf) - 1;
if (comma) {
if (len > comma - position)
len = comma - position;
strncpy(buf, position, len);
position = comma + 1;
} else {
strncpy(buf, position, len);
position = NULL;
}
/* Check if we have port range, e.g.: a-b */
int a=0, b=0;
a = atoi(buf);
dash = strchr(buf, '-');
if (dash) {
b = atoi(dash+1);
} else {
b = a;
}
if (a > b) {
fprintf(stderr, "Bad port spec %d-%d, first port must be less than last\n", a, b);
exit(1);
}
if (a <= 0 || a > MAX_HUB_PORTS || b <= 0 || b > MAX_HUB_PORTS) {
fprintf(stderr, "Bad port spec %d-%d, port numbers must be from 1 to %d\n", a, b, MAX_HUB_PORTS);
exit(1);
}
for (i=a; i<=b; i++) {
ports |= (1 << (i-1));
}
}
return ports;
}
/*
* Compatibility wrapper around libusb_get_port_numbers()
*/
static int get_port_numbers(libusb_device *dev, uint8_t *buf, uint8_t bufsize)
{
int pcount;
#if defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01000102)
/*
* libusb_get_port_path is deprecated since libusb v1.0.16,
* therefore use libusb_get_port_numbers when supported
*/
pcount = libusb_get_port_numbers(dev, buf, bufsize);
#else
pcount = libusb_get_port_path(NULL, dev, buf, bufsize);
#endif
return pcount;
}
/*
* get USB hub properties.
* most hub_info fields are filled, except for description.
* returns 0 for success and error code for failure.
*/
static int get_hub_info(struct libusb_device *dev, struct hub_info *info)
{
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;
unsigned char buf[LIBUSB_DT_HUB_NONVAR_SIZE + 2 + 3] = {0};
struct usb_hub_descriptor *uhd = (struct usb_hub_descriptor *)buf;
int minlen = LIBUSB_DT_HUB_NONVAR_SIZE + 2;
struct libusb_device_descriptor desc;
rc = libusb_get_device_descriptor(dev, &desc);
if (rc)
return rc;
if (desc.bDeviceClass != LIBUSB_CLASS_HUB)
return 0;
return LIBUSB_ERROR_INVALID_PARAM;
int bcd_usb = libusb_le16_to_cpu(desc.bcdUSB);
int desc_type = bcd_usb >= USB_SS_BCD ? LIBUSB_DT_SUPERSPEED_HUB
: LIBUSB_DT_HUB;
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,
desc_type << 8,
0,
buf, sizeof(buf),
USB_CTRL_GET_TIMEOUT
);
if (len >= minlen) {
unsigned char port_numbers[MAX_HUB_CHAIN] = {0};
info->dev = dev;
info->bcd_usb = bcd_usb;
info->nports = uhd->bNbrPorts;
snprintf(
info->vendor, sizeof(info->vendor),
"%04x:%04x",
libusb_le16_to_cpu(desc.idVendor),
libusb_le16_to_cpu(desc.idProduct)
);
/* Convert bus and ports array into USB location string */
int bus = libusb_get_bus_number(dev);
snprintf(info->location, sizeof(info->location), "%d", bus);
int pcount = get_port_numbers(dev, port_numbers, MAX_HUB_CHAIN);
info->level = pcount + 1;
int k;
for (k=0; k<pcount; k++) {
char s[8];
snprintf(s, sizeof(s), "%s%d", k==0 ? "-" : ".", port_numbers[k]);
strcat(info->location, s);
}
info->ppps = 0;
/* Logical Power Switching Mode */
int lpsm = uhd->wHubCharacteristics[0] & HUB_CHAR_LPSM;
/* Over-Current Protection Mode */
@@ -249,22 +418,39 @@ int is_smart_hub(struct libusb_device *dev, int min_current)
(ocpm == HUB_CHAR_INDV_PORT_OCPM ||
ocpm == HUB_CHAR_COMMON_OCPM))
{
rc = uhd->bNbrPorts;
/* Internal hubs have zero bHubContrCurrent.
* Ignore them if requested:
*/
if (min_current > 0 && uhd->bHubContrCurrent < min_current) {
rc = -1;
}
info->ppps = 1;
}
} else {
rc = len;
}
/* Get container_id: */
bzero(info->container_id, sizeof(info->container_id));
struct libusb_bos_descriptor *bos;
rc = libusb_get_bos_descriptor(devh, &bos);
if (rc == 0) {
int cap;
for (cap=0; cap < bos->bNumDeviceCaps; cap++) {
if (bos->dev_capability[cap]->bDevCapabilityType == LIBUSB_BT_CONTAINER_ID) {
struct libusb_container_id_descriptor *container_id;
rc = libusb_get_container_id_descriptor(NULL, bos->dev_capability[cap], &container_id);
if (rc == 0) {
int i;
for (i=0; i<16; i++) {
sprintf(info->container_id+i*2, "%02x", container_id->ContainerID[i]);
}
info->container_id[i*2] = 0;
libusb_free_container_id_descriptor(container_id);
}
}
}
libusb_free_bos_descriptor(bos);
}
libusb_close(devh);
}
return rc;
}
/*
* Assuming that devh is opened device handle for USB hub,
* return state for given hub port.
@@ -292,22 +478,99 @@ static int get_port_status(struct libusb_device_handle *devh, int port)
return ust.wPortStatus;
}
/*
* Get USB device descriptor strings and summary description.
*
* Summary will use following format:
*
* "<vid:pid> <vendor> <product> <serial>, <USB x.yz, N ports>"
*
* vid:pid will be always present, but vendor, product or serial
* may be skipped if they are empty or not enough permissions to read them.
* <USB x.yz, N ports> will be present only for USB hubs.
*
* Returns 0 for success and error code for failure.
* In case of failure return buffer is not altered.
*/
static int get_device_description(struct libusb_device * dev, struct descriptor_strings * ds)
{
int rc;
int id_vendor = 0;
int id_product = 0;
char ports[64] = "";
struct libusb_device_descriptor desc;
struct libusb_device_handle *devh = NULL;
rc = libusb_get_device_descriptor(dev, &desc);
if (rc)
return rc;
bzero(ds, sizeof(*ds));
id_vendor = libusb_le16_to_cpu(desc.idVendor);
id_product = libusb_le16_to_cpu(desc.idProduct);
rc = libusb_open(dev, &devh);
if (rc == 0) {
if (desc.iManufacturer) {
libusb_get_string_descriptor_ascii(devh,
desc.iManufacturer, (unsigned char*)ds->vendor, sizeof(ds->vendor));
rtrim(ds->vendor);
}
if (desc.iProduct) {
libusb_get_string_descriptor_ascii(devh,
desc.iProduct, (unsigned char*)ds->product, sizeof(ds->product));
rtrim(ds->product);
}
if (desc.iSerialNumber) {
libusb_get_string_descriptor_ascii(devh,
desc.iSerialNumber, (unsigned char*)ds->serial, sizeof(ds->serial));
rtrim(ds->serial);
}
if (desc.bDeviceClass == LIBUSB_CLASS_HUB) {
struct hub_info info;
rc = get_hub_info(dev, &info);
if (rc == 0) {
snprintf(ports, sizeof(ports), ", USB %x.%02x, %d ports",
info.bcd_usb >> 8, info.bcd_usb & 0xFF, info.nports);
}
}
libusb_close(devh);
}
snprintf(ds->description, sizeof(ds->description),
"%04x:%04x%s%s%s%s%s%s%s",
id_vendor, id_product,
ds->vendor[0] ? " " : "", ds->vendor,
ds->product[0] ? " " : "", ds->product,
ds->serial[0] ? " " : "", ds->serial,
ports
);
return 0;
}
/*
* 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)
static int print_port_status(struct hub_info * hub, int portmask)
{
int port_status;
struct libusb_device_handle * devh = NULL;
int rc = 0;
int hub_bus;
int dev_bus;
unsigned char hub_pn[MAX_HUB_CHAIN];
unsigned char dev_pn[MAX_HUB_CHAIN];
int hub_plen;
int dev_plen;
struct libusb_device *dev = hub->dev;
rc = libusb_open(dev, &devh);
if (rc == 0) {
hub_bus = libusb_get_bus_number(dev);
hub_plen = get_port_numbers(dev, hub_pn, sizeof(hub_pn));
int port;
for (port = 1; port <= nports; port++) {
for (port = 1; port <= hub->nports; port++) {
if (portmask > 0 && (portmask & (1 << (port-1))) == 0) continue;
port_status = get_port_status(devh, port);
@@ -318,67 +581,70 @@ static int hub_port_status(struct libusb_device * dev, int nports, int portmask)
break;
}
printf(" Port %d: %04x", port, port_status);
printf(" Port %d: %04x", port, port_status);
int id_vendor = 0;
int id_product = 0;
unsigned char vendor[64] = "";
unsigned char product[64] = "";
unsigned char serial[64] = "";
struct descriptor_strings ds;
bzero(&ds, sizeof(ds));
struct libusb_device * udev;
int i = 0;
while ((udev = usb_devs[i++]) != NULL) {
if (libusb_get_parent(udev) == dev &&
dev_bus = libusb_get_bus_number(udev);
/* only match devices on the same bus: */
if (dev_bus != hub_bus) continue;
dev_plen = get_port_numbers(udev, dev_pn, sizeof(dev_pn));
if ((dev_plen == hub_plen + 1) &&
(memcmp(hub_pn, dev_pn, hub_plen) == 0) &&
libusb_get_port_number(udev) == port)
{
struct libusb_device_descriptor desc;
struct libusb_device_handle *devh = NULL;
rc = libusb_get_device_descriptor(udev, &desc);
if (rc)
continue;
id_vendor = desc.idVendor;
id_product = desc.idProduct;
rc = libusb_open(udev, &devh);
if (rc)
continue;
if (desc.iManufacturer) {
libusb_get_string_descriptor_ascii(devh,
desc.iManufacturer, vendor, sizeof(vendor));
}
if (desc.iProduct) {
libusb_get_string_descriptor_ascii(devh,
desc.iProduct, product, sizeof(product));
}
if (desc.iSerialNumber) {
libusb_get_string_descriptor_ascii(devh,
desc.iSerialNumber, serial, sizeof(serial));
}
libusb_close(devh);
break;
rc = get_device_description(udev, &ds);
if (rc == 0)
break;
}
}
printf("%s%s%s%s%s%s%s%s%s%s%s ",
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" : ""
);
if (port_status & USB_PORT_STAT_CONNECTION) {
printf("[%04x:%04x%s%s%s%s%s%s]",
id_vendor, id_product,
vendor[0] ? " " : "", vendor,
product[0] ? " " : "", product,
serial[0] ? " " : "", serial
);
if (hub->bcd_usb < USB_SS_BCD) {
if (port_status == 0) {
printf(" off");
} else {
if (port_status & USB_PORT_STAT_POWER) printf(" power");
if (port_status & USB_PORT_STAT_INDICATOR) printf(" indicator");
if (port_status & USB_PORT_STAT_TEST) printf(" test");
if (port_status & USB_PORT_STAT_HIGH_SPEED) printf(" highspeed");
if (port_status & USB_PORT_STAT_LOW_SPEED) printf(" lowspeed");
if (port_status & USB_PORT_STAT_SUSPEND) printf(" suspend");
}
} else {
if (!(port_status & USB_SS_PORT_STAT_POWER)) {
printf(" off");
} else {
int link_state = port_status & USB_PORT_STAT_LINK_STATE;
if (port_status & USB_SS_PORT_STAT_POWER) printf(" power");
if ((port_status & USB_SS_PORT_STAT_SPEED)
== USB_PORT_STAT_SPEED_5GBPS)
{
printf(" 5gbps");
}
if (link_state == USB_SS_PORT_LS_U0) printf(" U0");
if (link_state == USB_SS_PORT_LS_U1) printf(" U1");
if (link_state == USB_SS_PORT_LS_U2) printf(" U2");
if (link_state == USB_SS_PORT_LS_U3) printf(" U3");
if (link_state == USB_SS_PORT_LS_SS_DISABLED) printf(" SS.Disabled");
if (link_state == USB_SS_PORT_LS_RX_DETECT) printf(" Rx.Detect");
if (link_state == USB_SS_PORT_LS_SS_INACTIVE) printf(" SS.Inactive");
if (link_state == USB_SS_PORT_LS_POLLING) printf(" Polling");
if (link_state == USB_SS_PORT_LS_RECOVERY) printf(" Recovery");
if (link_state == USB_SS_PORT_LS_HOT_RESET) printf(" HotReset");
if (link_state == USB_SS_PORT_LS_COMP_MOD) printf(" Compliance");
if (link_state == USB_SS_PORT_LS_LOOPBACK) printf(" Loopback");
}
}
if (port_status & USB_PORT_STAT_RESET) printf(" reset");
if (port_status & USB_PORT_STAT_OVERCURRENT) printf(" oc");
if (port_status & USB_PORT_STAT_ENABLE) printf(" enable");
if (port_status & USB_PORT_STAT_CONNECTION) printf(" connect");
if (port_status & USB_PORT_STAT_CONNECTION) printf(" [%s]", ds.description);
printf("\n");
}
libusb_close(devh);
@@ -386,75 +652,135 @@ static int hub_port_status(struct libusb_device * dev, int nports, int portmask)
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.
* Find all USB hubs and fill hubs[] array.
* Set actionable to 1 on all hubs that we are going to operate on
* (this applies possible constraints like location or vendor).
* Returns count of found actionable physical hubs
* (USB3 hubs are counted once despite having USB2 dual partner).
* In case of error returns 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;
int j = 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, 0);
if (nports < 0) {
continue;
struct hub_info info;
bzero(&info, sizeof(info));
rc = get_hub_info(dev, &info);
if (rc) {
perm_ok = 0; /* USB permission issue? */
}
if (nports > 0) { /* smart hub */
get_device_description(dev, &info.ds);
if (info.ppps) { /* PPPS is supported */
if (hub_count < MAX_HUBS) {
hubs[hub_count].dev = dev;
hubs[hub_count].nports = nports;
/* Convert bus and ports array into USB location string */
sprintf(
hubs[hub_count].vendor,
"%04x:%04x",
desc.idVendor, desc.idProduct
);
int bus = libusb_get_bus_number(dev);
sprintf(hubs[hub_count].location, "%d", bus);
#if defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01000102)
/*
* libusb_get_port_path is deprecated since libusb v1.0.16,
* therefore use libusb_get_port_numbers when supported
*/
int pcount = libusb_get_port_numbers(dev, port_numbers, MAX_HUB_CHAIN);
#else
int pcount = libusb_get_port_path(NULL, dev, port_numbers, MAX_HUB_CHAIN);
#endif
int k;
for (k=0; k<pcount; k++) {
char s[8];
sprintf(s, "%s%d", k==0 ? "-" : ".", port_numbers[k]);
strcat(hubs[hub_count].location, s);
info.actionable = 1;
if (strlen(opt_location) > 0) {
if (strcasecmp(opt_location, info.location)) {
info.actionable = 0;
}
}
/* 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;
if (opt_level > 0) {
if (opt_level != info.level) {
info.actionable = 0;
}
}
if (strlen(opt_vendor) > 0) {
if (strncasecmp(opt_vendor, info.vendor, strlen(opt_vendor))) {
info.actionable = 0;
}
}
memcpy(&hubs[hub_count], &info, sizeof(info));
hub_count++;
}
}
}
if (perm_ok == 0 && hub_count == 0) {
if (!opt_exact) {
/* Handle USB2/3 duality: */
for (i=0; i<hub_count; i++) {
/* Check only actionable hubs: */
if (hubs[i].actionable != 1)
continue;
/* Must have non empty container ID: */
if (strlen(hubs[i].container_id) == 0)
continue;
int match = -1;
for (j=0; j<hub_count; j++) {
if (i==j)
continue;
/* Find hub which is USB2/3 dual to the hub above */
/* Hub and its dual must be different types: one USB2, another USB3: */
if ((hubs[i].bcd_usb < USB_SS_BCD) ==
(hubs[j].bcd_usb < USB_SS_BCD))
continue;
/* Must have non empty container ID: */
if (strlen(hubs[j].container_id) == 0)
continue;
/* Per USB 3.0 spec chapter 11.2, container IDs must match: */
if (strcmp(hubs[i].container_id, hubs[j].container_id) != 0)
continue;
/* At this point, it should be enough to claim a match.
* However, some devices use hardcoded non-unique container ID.
* We should do few more checks below if multiple such devices are present.
*/
/* If serial numbers are both present, they must match: */
if ((strlen(hubs[i].ds.serial) > 0 && strlen(hubs[j].ds.serial) > 0) &&
strcmp(hubs[i].ds.serial, hubs[j].ds.serial) != 0)
{
continue;
}
/* Hubs should have the same number of ports: */
if (hubs[i].nports != hubs[j].nports)
continue;
/* And the same level: */
if (hubs[i].level != hubs[j].level)
continue;
/* Finally, we claim a match: */
match = j;
break;
}
if (match >= 0) {
if (!hubs[match].actionable) {
/* Use 2 to signify that this is derived dual device */
hubs[match].actionable = 2;
}
}
}
}
hub_phys_count = 0;
for (i=0; i<hub_count; i++) {
if (!hubs[i].actionable)
continue;
if (hubs[i].bcd_usb < USB_SS_BCD || opt_exact) {
hub_phys_count++;
}
}
if (perm_ok == 0 && hub_phys_count == 0) {
return LIBUSB_ERROR_ACCESS;
}
return hub_count;
return hub_phys_count;
}
int main(int argc, char *argv[])
{
int rc;
@@ -462,7 +788,7 @@ int main(int argc, char *argv[])
int option_index = 0;
for (;;) {
c = getopt_long(argc, argv, "l:n:a:p:d:r:w:hviR",
c = getopt_long(argc, argv, "l:L:n:a:p:d:r:w:hveR",
long_options, &option_index);
if (c == -1)
break; /* no more options left */
@@ -479,27 +805,18 @@ int main(int argc, char *argv[])
case 'l':
strncpy(opt_location, optarg, sizeof(opt_location));
break;
case 'L':
opt_level = atoi(optarg);
break;
case 'n':
strncpy(opt_vendor, optarg, sizeof(opt_vendor));
break;
case 'i':
/* ignored for backwards compatibility */
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);
}
opt_ports = ports2bitmap(optarg);
}
break;
case 'a':
@@ -514,11 +831,14 @@ int main(int argc, char *argv[])
}
break;
case 'd':
opt_delay = atoi(optarg);
opt_delay = atof(optarg);
break;
case 'r':
opt_repeat = atoi(optarg);
break;
case 'e':
opt_exact = 1;
break;
case 'R':
opt_reset = 1;
break;
@@ -590,46 +910,53 @@ int main(int argc, char *argv[])
goto cleanup;
}
if (hub_count > 1 && opt_action >= 0) {
if (hub_phys_count > 1 && opt_action >= 0) {
fprintf(stderr,
"Warning: changing port state for multiple hubs at once.\n"
"Error: changing port state for multiple hubs at once is not supported.\n"
"Use -l to limit operation to one hub!\n"
);
exit(1);
}
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 */
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;
}
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;
if (k == 1 && opt_action == POWER_OFF)
continue;
if (k == 1 && opt_action == POWER_KEEP)
continue;
int i;
for (i=0; i<hub_count; i++) {
if (hubs[i].actionable == 0)
continue;
printf("Current status for hub %s [%s]\n",
hubs[i].location, hubs[i].ds.description
);
print_port_status(&hubs[i], 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 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))
int power_mask = hubs[i].bcd_usb < USB_SS_BCD ? USB_PORT_STAT_POWER
: USB_SS_PORT_STAT_POWER;
if (k == 0 && !(port_status & power_mask))
continue;
if (k == 1 && (port_status & USB_PORT_STAT_POWER))
if (k == 1 && (port_status & power_mask))
continue;
int repeat = 1;
if (k == 0)
repeat = opt_repeat;
if (!(port_status & ~USB_PORT_STAT_POWER))
if (!(port_status & ~power_mask))
repeat = 1;
while (repeat-- > 0) {
rc = libusb_control_transfer(devh,
@@ -646,15 +973,16 @@ int main(int argc, char *argv[])
}
}
}
if (k==0 && opt_action == POWER_CYCLE)
sleep_ms(opt_delay * 1000);
/* USB3 hubs need extra delay to actually turn off: */
if (k==0 && hubs[i].bcd_usb >= USB_SS_BCD)
sleep_ms(150);
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
printf("New status for hub %s [%s]\n",
hubs[i].location, hubs[i].ds.description
);
hub_port_status(hubs[i].dev, hubs[i].nports, opt_ports);
print_port_status(&hubs[i], opt_ports);
if (k == 1 && opt_reset == 1) {
printf("Resetting hub...\n");
@@ -668,6 +996,8 @@ int main(int argc, char *argv[])
}
libusb_close(devh);
}
if (k == 0 && opt_action == POWER_CYCLE)
sleep_ms((int)(opt_delay * 1000));
}
rc = 0;
cleanup:

20
uhubctl_git.bb Normal file
View File

@@ -0,0 +1,20 @@
DESCRIPTION = "uhubctl - USB hub per-port power control"
HOMEPAGE = "https://github.com/mvp/uhubctl"
LICENSE = "GPLv2"
LIC_FILES_CHKSUM = "file://COPYING;md5=b234ee4d69f5fce4486a80fdaf4a4263 \
file://LICENSE;md5=7a7d8e0fdffe495ff61f52ceee61b2f7"
DEPENDS = "libusb1"
RDEPENDS_${PN} = "libusb1"
SRC_URI = "git://github.com/mvp/uhubctl.git"
SRCREV = "${AUTOREV}"
PV = "git"
S = "${WORKDIR}/git"
do_install_append () {
install -d ${D}${bindir}
install -Dm 0755 ${S}/uhubctl ${D}${bindir}/
}
FILES_${PN} += "${bindir}/*"