mirror of
https://github.com/mvp/uhubctl.git
synced 2026-05-17 08:26:29 +03:00
Compare commits
92 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c9fa3c68a1 | ||
|
|
beea0c0fc7 | ||
|
|
1856ae9d59 | ||
|
|
2b9b336038 | ||
|
|
1961aa02e9 | ||
|
|
5db248771e | ||
|
|
9cae5e275f | ||
|
|
324acc66fd | ||
|
|
15f2d37d79 | ||
|
|
5f8426105c | ||
|
|
945d08e62c | ||
|
|
078f9ee9c6 | ||
|
|
75023dc28c | ||
|
|
a510d091af | ||
|
|
d8f75116c8 | ||
|
|
94a95a7011 | ||
|
|
04238bf939 | ||
|
|
a9da51fb1c | ||
|
|
40cdb66d66 | ||
|
|
ee0287077b | ||
|
|
798d2323a9 | ||
|
|
bd3e398f4e | ||
|
|
74a5d721c5 | ||
|
|
134decf02b | ||
|
|
dab7ab8320 | ||
|
|
1926954998 | ||
|
|
e93d1a06a2 | ||
|
|
fc7aabb529 | ||
|
|
f80467f0fb | ||
|
|
8bbf37f301 | ||
|
|
ae4be0a9d5 | ||
|
|
00d1a408b8 | ||
|
|
c237ec17ba | ||
|
|
1773ac2605 | ||
|
|
343e02b634 | ||
|
|
f2a50dd4cb | ||
|
|
2b88680d3b | ||
|
|
43edb16298 | ||
|
|
21336c7ab1 | ||
|
|
6c4e5cdf75 | ||
|
|
04320872f5 | ||
|
|
2de7def9df | ||
|
|
f2d016a27c | ||
|
|
72ac02c324 | ||
|
|
7ad7d21336 | ||
|
|
4b61cb9e0b | ||
|
|
cfee02f014 | ||
|
|
1aaae4bbb8 | ||
|
|
069c67c836 | ||
|
|
465aa9bb95 | ||
|
|
a65c576ca3 | ||
|
|
a2b3695595 | ||
|
|
7bf08c0f0f | ||
|
|
a9474fe12f | ||
|
|
bfc4477189 | ||
|
|
5a0995b7b2 | ||
|
|
dda0ab22f2 | ||
|
|
de2c0f96cc | ||
|
|
77c7a7ace8 | ||
|
|
8eb39a6138 | ||
|
|
32bc22ea4f | ||
|
|
eaa2795025 | ||
|
|
17094135fc | ||
|
|
4e18d53426 | ||
|
|
51ff805c79 | ||
|
|
0cfd1f7341 | ||
|
|
e6f102a55d | ||
|
|
003ba1ff66 | ||
|
|
2311a1c76f | ||
|
|
4661d892bf | ||
|
|
448e84933a | ||
|
|
8da851a50d | ||
|
|
91ba867ce2 | ||
|
|
5efb1487e5 | ||
|
|
c712868525 | ||
|
|
fa90f13ba8 | ||
|
|
16ffd56b4b | ||
|
|
6e05aee30e | ||
|
|
812197fe4b | ||
|
|
4d84b62fc3 | ||
|
|
60ba7c915d | ||
|
|
eeff46fa0c | ||
|
|
41dadcea34 | ||
|
|
bccb920526 | ||
|
|
fb5d521290 | ||
|
|
8e667cde10 | ||
|
|
022e321f71 | ||
|
|
91e987527a | ||
|
|
a1b7411c49 | ||
|
|
79e44f13c7 | ||
|
|
5aaa9c54e8 | ||
|
|
e53d6d2f36 |
2
LICENSE
2
LICENSE
@@ -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
|
||||
|
||||
12
Makefile
12
Makefile
@@ -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
271
README.md
@@ -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.
|
||||
|
||||
704
uhubctl.c
704
uhubctl.c
@@ -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
20
uhubctl_git.bb
Normal 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}/*"
|
||||
Reference in New Issue
Block a user