87 Commits

Author SHA1 Message Date
Vadim Mikhailov
014b55ac5d Release version 2.4.0 2021-02-13 16:21:16 -08:00
Vadim Mikhailov
92736a8604 Honor PKG_CONFIG variable if exists
Some build systems want to pass location for pkg-config.

Closes #315.
2021-02-11 17:48:11 -08:00
Vadim Mikhailov
9f1de74523 Clarify that we do not maintain list of unsupported hubs
Closes #312.
2021-02-05 15:40:21 -08:00
Vadim Mikhailov
9b73c8e314 Note that PAUH212 is also supported
Closes #311.
2021-02-05 15:32:05 -08:00
Vadim Mikhailov
fa449eac0b Add a note that D-Link DUB-H4 rev B7+ is not supported
Closes #307.
2021-01-21 13:16:46 -08:00
Vadim Mikhailov
9b5efa0ec0 Simplify code that powers on/off
* Simplified code that powers on/off.
* Fixed minor bug for repeat counter (always once to power off, and opt_repeat times to power on).
* Only warn about USB permissions if no compatible hubs are detected.
  Old behavior would always warn, even if permissions were missing for any hub (even unsupported ones).
2021-01-17 14:34:11 -08:00
comzine
b99a0805bb Added feature toggle mode (#305)
Added feature toggle mode

Co-authored-by: Tobias Weber <comzine@gmail.com>
2021-01-17 11:28:44 -08:00
Vadim Mikhailov
6ac502a07a Add ExSys EX-1113HMS as compatible device
Closes #303.
2021-01-15 09:28:02 -08:00
Vadim Mikhailov
f76dc98653 Refactor README section for Raspberry Pi
Move workaround closer to the question context.
2020-12-16 09:04:26 -08:00
Vadim Mikhailov
592d60b9ab Add a note to Anker AK-68ANHUB-BV7A-0004
Closes #300.
2020-12-16 08:49:30 -08:00
Vadim Mikhailov
4b663f3d15 Add Aukey CB-C59 as compatible device
Closes #299.
2020-12-15 11:43:05 -08:00
Vadim Mikhailov
6a43ea9c5c Add Controlling USB LED Strip to notable projects 2020-12-14 11:30:39 -08:00
Vadim Mikhailov
6f136f17fe Add Port NWUSB01 as compatible device
Closes #298.
2020-12-14 11:23:29 -08:00
Vadim Mikhailov
8d8b66c5c6 Release version 2.3.0 as stable brew tap 2020-12-13 14:56:43 -08:00
Vadim Mikhailov
1b52efddbd Release version 2.3.0 2020-12-13 14:31:05 -08:00
Vadim Mikhailov
71a4274615 Print uhubctl version as last item in --help output 2020-12-13 13:08:32 -08:00
Vadim Mikhailov
d98e6deb6e README clarifications
* Clarify how to build and install
* Clarify USB permissions
* Mention force flag -f
2020-12-13 12:53:28 -08:00
Vadim Mikhailov
ac5a704449 Fix homebrew formula
* Document installing from stable version by default.
* Automatically install pkg-config which benefits getting proper CFLAGS/LDFLAGS.
* Specify license field.
2020-12-11 00:53:40 -08:00
Vadim Mikhailov
1e65ff9b05 Remove MacOS libusb workaround
Now that libusb-1.0.24 is released https://github.com/libusb/libusb/issues/618,
workaround is no longer needed.

Also, replace MacOS X references to just MacOS (since it works on MacOS 11 Big Sur too).

This finally fixes issue #227.
2020-12-10 15:32:44 -08:00
Vadim Mikhailov
77aeb7f30b Implement searching by device description
Add switch --search (-s) to limit hubs by attached device description.
It is recommended to specify search string that is as unique as possible, e.g. device serial number.
It should work for "off" and "cycle" actions, but not for "on" because device would not be visible on USB.

Closes #209.
2020-12-08 18:40:13 -08:00
Vadim Mikhailov
e65d077712 Do not allow just commit id to be used as version
Homebrew is forcing `--no-tags` for cloned repos.
Because we used `git describe --always` brew was choosing raw commit id as version, which is quite bad.
By removing `--always` we will use contents of VERSION file as a backup.
2020-12-08 15:24:41 -08:00
Vadim Mikhailov
0cc09299fe Add a switch to allow operation on unsupported hubs
Show power switching type supported by a hub:

* ppps   - per-port power switching
* ganged - ganged power switching
* nops   - no power switching

Add --force (-f) switch to allow operation on non-ppps hubs.
Use `-f` at your own risk, this most likely will not work to actually switch power.

For ganged hubs, you may need to turn power off for all hub ports to get any effect.
For nops hubs, it is not likely to work at all, but might be useful for informational purposes.

Also, instead of printing recipe on how to fix USB permissions on Linux,
forward user to web page with detailed explanations.

Closes #260, #280.
2020-12-07 18:43:22 -08:00
Vadim Mikhailov
44f963d00f Improve USB2/3 duality matching
Use priority based duality matching.

* Support Raspberry Pi 4B better (as it has USB2 hub one level deeper than its USB3 counterpart).
* Support M1 Macs (as they seem to place all USB devices to bus 2).
* Support Apple mini-dock (it advertises 2 USB2 ports but only 1 USB3 port).
* Should support multiple identical hubs (with the same container id) on Linux.
2020-12-07 11:32:29 -08:00
Vadim Mikhailov
d3a79ace46 Use pkg-config when available
Remove custom handling for different platforms for CFLAGS and LDFLAGS
and prefer using pkg-config when available.
However, this is tested to still build ok on Linux on Mac howebrew
even if pkg-config is not available.

Closes #292, should also simplify issues raised by #142, #147, #289 and 5db248771e.
2020-12-05 21:24:17 -08:00
Vadim Mikhailov
0e733b6901 Add a note that D-Link DUB-H7 rev F is not supported
Closes #291.
2020-12-01 11:37:07 -08:00
Vadim Mikhailov
50d5501d91 Clarify how to configure udev permissions for USB3 hubs 2020-11-27 11:07:36 -08:00
Jim Klimov
9183aef17e Makefile: add support for "SunOS" (e.g. with modern illumos) 2020-11-26 14:56:16 -08:00
Vadim Mikhailov
4b10fdbd24 Add Anker AK-68ANHUB-BV7A-0004 as supported device
Closes #287.
2020-11-25 09:12:04 -08:00
Julius Henke
3cc17baae1 Add Dell S2719DGF to compatible hubs list 2020-11-24 12:09:22 -08:00
Vadim Mikhailov
db8c4a59f3 Document another workaround for getting working libusb on MacOS
Previous workaround of getting working `libusb` on MacOS is no longer supported by Homebrew,
so now recommended fix is to install `libusb` from HEAD.

This workaround will be removed once libusb 1.0.24 is released and is provided by Homebrew by default.
2020-11-11 12:35:59 -08:00
Vadim Mikhailov
77d8851c34 Add Microchip EVB-USB2534BC as supported device
Closes #277.
2020-11-01 10:39:34 -08:00
Vadim Mikhailov
dcb1e611f7 Add Rosonway RSH-16 as compatible device
This hub is built from 5 * 4-port USB chips in daisy chain. Unfortunately, it advertises
identical bos container IDs for all 5 hubs of them, which will cause issues controlling power in USB3 mode -
one would have to call uhubctl twice with `-e` to turn off both USB2 and USB3 hub pair ports properly.

Rosonway also sells this hub under RSHTECH brand in some markets.

Closes #276.
2020-10-23 10:44:06 -07:00
Vadim Mikhailov
a7b5f6b362 Add a note about USB authorization on Linux 2020-10-06 10:54:08 -07:00
Vadim Mikhailov
2a4df4ee6c Warn about possible incompatibility for Dell P2416D 2020-10-06 10:53:57 -07:00
Vadim Mikhailov
f610598227 Fixed accidentally dropped AmazonBasics HUC9002V1ESL
Also split HU900* and HUC900* items to two separate lines to avoid table becoming too wide.
2020-09-14 23:35:18 -07:00
Vadim Mikhailov
3bc22e3cd7 Clarify RPi4B support for USB-C OTG port
Closes #266.
2020-09-09 12:10:17 -07:00
Vadim Mikhailov
0f6218dfb8 Add AmazonBasics HUC9003V1EBL 7-port USB 3.1 hub as supported device
Closes #261.
2020-08-11 20:48:36 -07:00
Vadim Mikhailov
91035987e4 Mention that D-Link DUB-H4 rev F is not supported
Closes #259.
2020-08-05 15:22:25 -07:00
Vadim Mikhailov
c6927085ec Add NVidia Jetson Nano B01 as supported device
Closes #258.
2020-08-01 01:24:40 -07:00
Vadim Mikhailov
5b1ae79b05 Clarify RPi4B firmware requirement and checking current version
Firmware VL805 00137ac was an alpha verson that was never released.
Change firmware requirement to stable version VL805 00137ad,
and provide easy way to check if it is compliant.
2020-07-25 13:34:43 -07:00
Vadim Mikhailov
b8a1b808e7 Remove StarTech ST4200USBM from supported table
There are multiple reports that VBUS off is not supported by ST4200USBM.
Closes #254.
2020-07-06 11:07:26 -07:00
Vadim Mikhailov
f75bda6896 Removed Belkin F5U701-BLK from supported devices list
Unfortunately, F5U701-BLK does not support per-port power switching, as reported in #252.
2020-06-29 18:15:17 -07:00
Vadim Mikhailov
e171813596 Update device table for AmazonBasics 10 port hub HU[C]9002V1{SBL,ESL}
Apparently all following hubs: HU9002V1SBL, HU9002V1ESL, HUC9002V1SBL, HUC9002V1EBL
are almost the same, with C models including USB-C cable.
Update device table to reflect this.

Closes #246.
2020-06-25 13:25:48 -07:00
Vadim Mikhailov
5257d8567d Add D-Link DUB-H7 rev E as supported device
Closes #244.
2020-06-17 16:34:57 -07:00
Vadim Mikhailov
5ee8260c71 Release version 2.2.0 2020-05-22 13:39:47 -07:00
Vadim Mikhailov
8ba36bd8df Improve getting version from git describe
* To determine version, only consider tags starting with v{digit}
* Strip v prefix from version
2020-05-22 13:21:45 -07:00
Vadim Mikhailov
aaf87accad Add Seagate Backup Plus Hub STEL8000100 as supported device
Closes #238.
2020-05-16 15:16:46 -07:00
Sarah Foster
58385b87e6 Update README.md
Just a small typo correction
2020-04-27 22:03:45 -07:00
Vadim Mikhailov
7bf80a08d1 Update Rasperry Pi 4B USB topology note
Apparently, controlling only port 4 is not enough to actually turn power off for RPi4B.
Instead, note that all 4 ports should be turned off. Closes #233.
2020-04-24 17:45:01 -07:00
Vadim Mikhailov
b73213dd6d Mention testing VBUS off in README before reporting new devices 2020-04-14 22:53:38 -07:00
Vadim Mikhailov
e327f5aeb1 Move keyboard/mouse warning to usage section 2020-04-14 22:47:21 -07:00
Vadim Mikhailov
9b824b5587 Add Control USB Lamp With Voice to notable projects
Also fix some broken links.
2020-04-14 22:38:49 -07:00
Vadim Mikhailov
aa864285b1 Add LG Electronics 38WK95C-W as supported device
Closes #231.
2020-04-14 22:15:16 -07:00
Vadim Mikhailov
fa524cf7df Add AmazonBasics HUC9002V1ESL as supported device
AmazonBasics HUC9002V1ESL seems to be EU version of HU9002V1SBL and works the same.
Closes #229.
2020-04-04 13:27:11 -07:00
Vadim Mikhailov
7fd3080f90 Add Raspberry Pi Reboot Router into notable projects 2020-04-04 10:38:40 -07:00
Vadim Mikhailov
e3734e052f Provide workaround for using uhubctl on MacOS Catalina
This provides workaround for issue #227.
Will be fully solved only by https://github.com/libusb/libusb/issues/707.
2020-03-26 21:30:02 -07:00
Vadim Mikhailov
d576db98fb Mention udisksctl in README 2020-02-25 22:21:43 -08:00
Vadim Mikhailov
9e402917dc Add Bytecc BT-UH340 as compatible device
Reported in #217.
2020-02-25 22:11:56 -08:00
Vadim Mikhailov
b0d321a041 Add D-Link DUB-H4 rev E as supported device
Reported in #212.
2020-02-17 11:26:25 -08:00
Vadim Mikhailov
0d2aa24bae Remove statement about proper per-port switching on RPi3B+
Per discussion in issue #215, proper per-port power switching
is not necessarily supported on RPi3B+ secondary hub ports.
2020-02-17 11:21:17 -08:00
Vadim Mikhailov
bd92bcef2f Add a warning and link how to update RPi4B firmware to make power switching work 2020-01-28 11:24:49 -08:00
Vadim Mikhailov
ea70731854 Use in-document anchors to Raspberry Pi usage examples 2020-01-27 23:41:30 -08:00
Vadim Mikhailov
6366aea0bd Refactor Raspberry Pi USB topology table with sub-headers 2020-01-27 23:33:53 -08:00
Vadim Mikhailov
232dcc6f6d Clarify support for different Raspberry Pi models with example commands
* Reformat markdown
* Add example uhubctl commands to turn power off for affected port(s)
* Removed some low level techical details about USB descriptors
2020-01-21 23:58:17 -08:00
Vadim Mikhailov
5691db3379 Add Buffalo BSH4A05U3BK as supported device 2020-01-21 23:04:02 -08:00
Vadim Mikhailov
fae3da7e6f Bump copyright year 2020-01-20 12:14:48 -08:00
Vadim Mikhailov
c79c9ac667 Add Basler 2000036234 as supported device
Reported in #208.
2020-01-20 12:09:36 -08:00
Ryan O'Leary
7f25162ed3 Add AmazonBasics HU9002V1SBL as a Supported Device
Signed-off-by: Ryan O'Leary <ryanoleary@google.com>
2020-01-07 23:01:04 -08:00
Vadim Mikhailov
6f84663c6a Clarify VBUS off requirements for Raspberry Pi 4 2020-01-05 14:05:25 -08:00
Vadim Mikhailov
4aae44ced0 Add support for Raspberry Pi 4
Hack to support Raspberry Pi 4:

* Override ganged power switching reported by USB2 hub as per-port. This is not necessarily true,
  but is consistent with per-port power switching returned by dual USB3 root hub.
  Proper fix is for Raspberry Pi 4 USB firmware to return consistent descriptor information.

* When root USB3 hub is detected on RPi4 without ContainerID, it is assumed to have
  ContainerID of dual USB2 hub, so USB2/3 duality is still working as expected.
  Proper fix would be for USB3 root hub to report correct ContainerID.
2020-01-05 13:43:14 -08:00
Mark Deneen
9857849c0e Update license hash to correct value, fixing yocto building 2019-11-18 13:51:49 -08:00
Vadim Mikhailov
821cdb8833 Update Dell U2415 info 2019-11-15 00:11:09 -08:00
Vadim Mikhailov
c387442b0c Add D-Link DUB-H4 rev B as supported device
Reported in #188.
2019-11-12 17:58:21 -08:00
Vadim Mikhailov
b690bd376a Add Belkin F5U101 as supported device
Reported in #187.
2019-11-12 17:58:21 -08:00
Vadim Mikhailov
c220668adc Add Dell UltraSharp U2415 as supported device
Reported in #191
2019-11-12 12:01:07 -08:00
Karsten Hohmeier
3e10680f22 Fix compile warning about format truncation by increasing buffer size for description 2019-11-05 13:52:24 -08:00
Vadim Mikhailov
39b407a863 Add Juiced Systems 6+1+1 USB 3.0 Gigabit Ethernet Smart Charging Hub (6HUB-01) as supported device
Reported in #184.
2019-11-01 22:27:14 -07:00
Vadim Mikhailov
d227c8326f Don't complain about USB permission problems if we are already root 2019-10-23 22:52:44 -07:00
Vadim Mikhailov
743ecf226b Relax level check when looking for USB2/3 dual hubs
Some devices (notably Raspberry 4B) have USB3 hub that advertises
its USB2 compatibility partner at different depth level.
Raspberry 4B onboard hub doesn't yet support uhubctl directly
(but it should once https://github.com/raspberrypi/linux/issues/3079 is fixed),
but this breaks level check restriction for any external USB3 hub attached to RPi4B
- so this check has been removed.
2019-10-16 23:25:36 -07:00
Vadim Mikhailov
fd611df1e7 Remove Coolgear USBG-4U3MLR2 from supported list
After deeper look, it turns out that this hub doesn't really work,
see more in issue #177.
2019-10-16 23:20:53 -07:00
Vadim Mikhailov
a6cdc3dc4b Remove Plugable USB2-HUB10S from supported list
As reported in issue #181, Plugable USB2-HUB10S does not support per-port power switching.
2019-10-08 18:15:19 -07:00
Vadim Mikhailov
609d0bcae1 Clarify which revisions of D-Link hubs are working 2019-10-05 20:48:00 -07:00
Vadim Mikhailov
54365acb8d Update info for Coolgear USBG-4U3MLR2 2019-10-01 10:33:36 -07:00
Vadim Mikhailov
6e5782c3b7 Add Coolgear USBG-4U3MLR2 as supported device
Reported in #177.
2019-09-26 22:23:07 -07:00
Vadim Mikhailov
59447ce431 Add ThinkPad Ultra Docking Station 40AJ0135EU as supported device
Reported in #176.
2019-09-24 12:47:15 -07:00
Vadim Mikhailov
05647714b6 Apply debian hardening suggestions for Linux
Follow hardening tips per https://wiki.debian.org/Hardening.
We have already applied `-z relro` before, now add `-z now` as well.
2019-09-20 17:43:36 -07:00
Vadim Mikhailov
4818eddc03 FreeBSD is using bNumDeviceCapabilities instead of bNumDeviceCaps
FreeBSD libusb is different from mainline libusb, and needs hacks here and there :-(

This resolves issue #175.
2019-09-20 15:29:30 -07:00
7 changed files with 387 additions and 189 deletions

View File

@@ -1,9 +1,17 @@
class Uhubctl < Formula
desc "control USB hubs powering per-port"
desc "USB hub per-port power control"
homepage "https://github.com/mvp/uhubctl"
head "https://github.com/mvp/uhubctl.git"
url "https://github.com/mvp/uhubctl/archive/v2.3.0.tar.gz"
sha256 "714f733592d3cb6ba8efc84fbc03b1beed2323918ff33aef01cdb956755be7b7"
license "GPL-2.0"
depends_on "libusb"
depends_on "pkg-config" => :build
livecheck do
url :stable
end
def install
system "make"

View File

@@ -1,6 +1,6 @@
uhubctl USB hub per-port power control.
Copyright (c) 2009-2019, Vadim Mikhailov
Copyright (c) 2009-2020, 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

@@ -10,38 +10,31 @@ INSTALL := install
INSTALL_DIR := $(INSTALL) -m 755 -d
INSTALL_PROGRAM := $(INSTALL) -m 755
RM := rm -rf
PKG_CONFIG ?= pkg-config
CC ?= gcc
CFLAGS ?= -g -O0
CFLAGS += -Wall -Wextra -std=c99 -pedantic
GIT_VERSION := $(shell git describe --abbrev=8 --dirty --always --tags)
GIT_VERSION := $(shell git describe --match "v[0-9]*" --abbrev=8 --dirty --tags | cut -c2-)
ifeq ($(GIT_VERSION),)
GIT_VERSION := $(shell cat VERSION)
GIT_VERSION := $(shell cat VERSION)
endif
CFLAGS += -DPROGRAM_VERSION=\"$(GIT_VERSION)\"
# Use hardening options on Linux
ifeq ($(UNAME_S),Linux)
LDFLAGS += -Wl,-z,relro -lusb-1.0
LDFLAGS += -Wl,-zrelro,-znow
endif
ifeq ($(UNAME_S),Darwin)
ifneq ($(wildcard /opt/local/include),)
# MacPorts
CFLAGS += -I/opt/local/include
LDFLAGS += -L/opt/local/lib
endif
# Use pkg-config if available
ifneq (,$(shell which $(PKG_CONFIG)))
CFLAGS += $(shell $(PKG_CONFIG) --cflags libusb-1.0)
LDFLAGS += $(shell $(PKG_CONFIG) --libs libusb-1.0)
else
# But it should still build if pkg-config is not available (e.g. Linux or Mac homebrew)
LDFLAGS += -lusb-1.0
endif
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

175
README.md
View File

@@ -20,59 +20,77 @@ This is list of known compatible USB hubs:
|:-------------------|:-----------------------------------------------------|:------|:----|:----------|:--------|:-----|
| 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 | |
| AmazonBasics | HU9003V1EBL | 7 | 3.1 |`2109:2817`| 2018 | |
| AmazonBasics | HU9002V1SBL, HU9002V1ESL | 10 | 3.1 |`2109:2817`| 2018 | |
| AmazonBasics | HUC9002V1SBL, HUC9002V1EBL, HUC9002V1ESL | 10 | 3.1 |`2109:2817`| 2018 | |
| Anker | AK-68ANHUB-BV7A-0004 ([note](https://git.io/JLnZb)) | 7 | 3.0 |`2109:0812`| 2014 | |
| 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 |
| Aukey | CB-C59 | 4 | 3.1 |`2109:2813`| 2017 | |
| 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 |
| Basler | 2000036234 | 4 | 3.0 |`0451:8046`| 2016 | |
| Belkin | F5U101 | 4 | 2.0 |`0451:2046`| 2005 | 2010 |
| Buffalo | BSH4A05U3BK | 4 | 3.0 |`05E3:0610`| 2015 | |
| Bytecc | BT-UH340 | 4 | 3.0 |`2109:8110`| 2010 | |
| 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 | |
| D-Link | DUB-H4 rev B (silver). Note: rev B7+ not supported | 4 | 2.0 |`05E3:0605`| 2005 | 2010 |
| D-Link | DUB-H4 rev D,E (black). Note: rev A,C,F not supported| 4 | 2.0 |`05E3:0608`| 2012 | |
| D-Link | DUB-H7 rev A (silver) | 7 | 2.0 |`2001:F103`| 2005 | 2010 |
| D-Link | DUB-H7 rev D,E (black). Note: rev B,C,F not supported| 7 | 2.0 |`05E3:0608`| 2012 | |
| Dell | P2416D 24" QHD Monitor ([note](https://git.io/JUAu8))| 4 | 2.0 | | 2017 | |
| Dell | S2719DGF 27" WQHD Gaming-Monitor | 5 | 3.1 |`0424:5734`| 2018 | |
| Dell | UltraSharp 1704FPT 17" LCD Monitor | 4 | 2.0 |`0424:A700`| 2005 | 2015 |
| Dell | UltraSharp U2415 24" LCD Monitor | 5 | 3.0 | | 2014 | |
| Elecom | U2H-G4S | 4 | 2.0 | | 2006 | 2011 |
| ExSys | EX-1113HMS | 16 | 3.1 | | 2018 | |
| 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 | |
| Juiced Systems | 6HUB-01 | 7 | 3.0 |`0BDA:0411`| 2014 | 2018 |
| LG Electronics | 38WK95C-W monitor | 4 | 3.0 |`0451:8142`| 2018 | |
| Lenovo | ThinkPad Ultra Docking Station (40A20090EU) | 6 | 2.0 |`17EF:100F`| 2015 | |
| Lenovo | ThinkPad Ultra Docking Station (40AJ0135EU) | 7 | 3.1 |`17EF:3070`| 2018 | |
| 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 | |
| Microchip | EVB-USB2534BC | 4 | 2.0 | | 2013 | |
| Moxa | Uport-407 | 7 | 2.0 |`110A:0407`| 2009 | |
| NVidia | Jetson Nano B01 ([details](https://git.io/JJaFR)) | 4 | 3.0 | | 2019 | |
| 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 | |
| Port Inc | NWUSB01 | 4 | 1.1 |`0451:1446`| 1999 | 2003 |
| Raspberry Pi | B+, 2B, 3B ([see below](#raspberry-pi-b2b3b)) | 4 | 2.0 | | 2011 | |
| Raspberry Pi | 3B+ ([see below](#raspberry-pi-3b)) | 4 | 2.0 |`0424:2514`| 2018 | |
| Raspberry Pi | 4B ([see below](#raspberry-pi-4b)) | 4 | 3.0 |`2109:3431`| 2019 | |
| Renesas | uPD720202 PCIe USB 3.0 host controller | 2 | 3.0 | | 2013 | |
| Rosewill | RHUB-210 | 4 | 2.0 |`0409:005A`| 2011 | 2014 |
| Rosonway | RSH-A16 ([note](https://git.io/JTawg)) | 16 | 3.2 |`0bda:0411`| 2020 | |
| Sanwa Supply | USB-HUB14GPH | 4 | 1.1 | | 2001 | 2003 |
| StarTech | ST4200USBM | 4 | 2.0 |`0409:005A`| 2004 | |
| Seagate | Backup Plus Hub STEL8000100 | 2 | 3.0 |`0BC2:AB44`| 2016 | |
| Sunix | SHB4200MA | 4 | 2.0 |`0409:0058`| 2006 | 2009 |
| Targus | PAUH212U | 7 | 2.0 | | 2004 | 2009 |
| Targus | PAUH212/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
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`
and please test VBUS off support as described below in FAQ.
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.
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
@@ -92,48 +110,69 @@ Compiling
=========
This utility was tested to compile and work on Linux
(Ubuntu/Debian, Redhat/Fedora/CentOS, Arch Linux, Gentoo, openSUSE, Buildroot), FreeBSD, NetBSD and Mac OS X.
(Ubuntu/Debian, Redhat/Fedora/CentOS, Arch Linux, Gentoo, openSUSE, Buildroot), FreeBSD, NetBSD, SunOS and MacOS.
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
[necessary USB control requests](https://social.msdn.microsoft.com/Forums/sqlserver/en-US/f680b63f-ca4f-4e52-baa9-9e64f8eee101).
This may be fixed if `libusb` starts supporting different driver on Windows.
First, you need to install library libusb-1.0 (version 1.0.12 or later):
Note that it is highly recommended to have `pkg-config` installed (many platforms provide it by default).
First, you need to install library libusb-1.0 (version 1.0.12 or later, 1.0.16 or later is recommended):
* Ubuntu: `sudo apt-get install libusb-1.0-0-dev`
* Redhat: `sudo yum install libusb1-devel`
* MacOSX: `brew install libusb`, or `sudo port install libusb-devel`
* MacOS: `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 fetch uhubctl source:
To fetch uhubctl source and compile it:
git clone https://github.com/mvp/uhubctl
cd uhubctl
make
This should generate `uhubctl` binary.
You can install it in your system as `/usr/sbin/uhubctl` using:
sudo make install
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:
Also, on MacOS you can install `uhubctl` with all necessary dependencies in one shot using Homebrew tap:
```
brew tap mvp/uhubctl https://github.com/mvp/uhubctl
brew install --HEAD uhubctl
brew install uhubctl
```
To build/install from master branch, use `--HEAD`:
```
brew install uhubctl --HEAD
```
Usage
=====
> :warning: On Linux, use `sudo` or configure USB permissions as described below!
To list all supported hubs:
uhubctl
You can control the power on a USB port(s) like this:
uhubctl -a off -p 2
This means operate on default smart hub and turn power off (`-a off`, or `-a 0`)
on port 2 (`-p 2`). Supported actions are `off`/`on`/`cycle` (or `0`/`1`/`2`).
on port 2 (`-p 2`). Supported actions are `off`/`on`/`cycle`/`toggle` (or `0`/`1`/`2`/`3`).
`cycle` means turn power off, wait some delay (configurable with `-d`) and turn it back on.
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`.
> :warning: Turning off built-in USB ports may cut off your keyboard or mouse,
so be careful which ports you are turning off!
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.
@@ -148,10 +187,16 @@ 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):
Then, add one or more udev rules like below to file `/etc/udev/rules.d/52-usb.rules` (replace 2001 with your vendor id):
SUBSYSTEM=="usb", ATTR{idVendor}=="2001", MODE="0666"
Note that for USB3 hubs, some hubs use different vendor ID for USB2 vs USB3 components of the same chip,
and both need permissions to make uhubctl work properly. E.g. for Raspberry Pi 4B, you need to add these 2 lines:
SUBSYSTEM=="usb", ATTR{idVendor}=="2109", MODE="0666"
SUBSYSTEM=="usb", ATTR{idVendor}=="1d6b", 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"
@@ -173,7 +218,8 @@ FAQ
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.
Note that by default `uhubctl` will only detect USB hubs which support per-port power switching
(but you can force it to try operating on unsupported hubs with option `-f`).
You can find what kind of power switching your hardware supports by using `sudo lsusb -v`:
No power switching:
@@ -212,6 +258,8 @@ Per-port power switching:
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.
Please do not report unsupported hubs, unless it is different
hardware revision of some already listed supported model.
#### _USB devices are not removed after port power down on Linux_
@@ -240,12 +288,21 @@ 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).
Disabling USB authorization for device in question before turning power off with `uhubctl` should help:
echo 0 > sudo tee /sys/bus/usb/devices/${location}.${port}/authorized
If your device is USB mass storage, invoking `udisksctl` before calling `uhubctl` should help too:
sudo udisksctl power-off --block-device /dev/disk/...`
sudo uhubctl -a off ...
#### _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
so it makes it 4+4-1=7 port hub. Similarly, 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.
@@ -254,25 +311,50 @@ Doing so will confuse internal hub circuitry and will cause unpredictable behavi
#### _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,
This is the limitation of Raspberry Pi hardware design.
As a workaround, you can buy any external USB hub from supported list above,
attach it to any USB port of Raspberry Pi, and control power on its ports independently.
For reference, supported Raspberry Pi models have following internal USB topology:
##### Raspberry Pi B+,2B,3B
* Single hub `1-1`, ports 2-5 ganged, all controlled by port `2`:
uhubctl -l 1-1 -p 2 -a 0
Trying to control ports `3`,`4`,`5` will not do anything.
Port `1` controls power for Ethernet+WiFi.
##### Raspberry Pi 3B+
* Main hub `1-1`, all 4 ports ganged, all controlled by port `2` (turns off secondary hub ports as well).
Port `1` connects hub `1-1.1` below, ports `2` and `3` are wired outside, port `4` not wired.
uhubctl -l 1-1 -p 2 -a 0
* Secondary hub `1-1.1` (daisy-chained to main): 3 ports,
port `1` is used for Ethernet+WiFi, and ports `2` and `3` are wired outside.
##### Raspberry Pi 4B
> :warning: If your VL805 firmware is older than `00137ad` (check with `sudo rpi-eeprom-update`),
you have to [update firmware](https://www.raspberrypi.org/documentation/hardware/raspberrypi/booteeprom.md)
to make power switching work on RPi 4B.
* USB2 hub `1`, 1 port, only connects hub `1-1` below.
* USB2 hub `1-1`, 4 ports ganged, dual to USB3 hub `2` below:
uhubctl -l 1-1 -a 0
* USB3 hub `2`, 4 ports ganged, dual to USB2 hub `1-1` above:
uhubctl -l 2 -a 0
* USB2 hub `3`, 1 port, OTG controller. Power switching is [not supported](https://git.io/JUc5Q).
Notable projects using uhubctl
@@ -285,20 +367,23 @@ Notable projects using uhubctl
| [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 |
| [sysmoQMOD](https://bit.ly/2VtWrVt) | 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 |
| [Ideal Music Server](https://bit.ly/39MeVFQ) | 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 |
| [Raspberry Pi Reboot Router](https://bit.ly/3aNbQqs) | Automatically reboot router if internet isn't working |
| [Control USB Lamp With Voice](https://bit.ly/2VtW2SX) | Voice Control of USB Lamp using Siri and Raspberry Pi |
| [Control USB LED Strip](https://bit.ly/3oVWfeZ) | Controlling USB powered LED Light Strip |
Copyright
=========
Copyright (C) 2009-2019 Vadim Mikhailov
Copyright (C) 2009-2020 Vadim Mikhailov
This file can be distributed under the terms and conditions of the
GNU General Public License version 2.

View File

@@ -1 +1 @@
v2.1.0-dev
2.4.0

356
uhubctl.c
View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009-2019 Vadim Mikhailov
* Copyright (c) 2009-2020 Vadim Mikhailov
*
* Utility to turn USB port power on/off
* for USB hubs that support per-port power switching.
@@ -75,6 +75,7 @@ void sleep_ms(int milliseconds)
#define POWER_OFF 0
#define POWER_ON 1
#define POWER_CYCLE 2
#define POWER_TOGGLE 3
#define MAX_HUB_CHAIN 8 /* Per USB 3.0 spec max hub chain is 7 */
@@ -182,19 +183,22 @@ struct descriptor_strings {
char vendor[64];
char product[64];
char serial[64];
char description[256];
char description[512];
};
struct hub_info {
struct libusb_device *dev;
int bcd_usb;
int super_speed; /* 1 if super speed hub, and 0 otherwise */
int nports;
int ppps;
int lpsm; /* logical power switching mode */
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;
uint8_t bus;
uint8_t port_numbers[MAX_HUB_CHAIN];
int pn_len; /* length of port numbers */
struct descriptor_strings ds;
};
@@ -206,6 +210,7 @@ static int hub_phys_count = 0;
/* default options */
static char opt_vendor[16] = "";
static char opt_search[64] = ""; /* Search by attached device description */
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 */
@@ -215,10 +220,12 @@ static int opt_repeat = 1;
static int opt_wait = 20; /* wait before repeating in ms */
static int opt_exact = 0; /* exact location match - disable USB3 duality handling */
static int opt_reset = 0; /* reset hub after operation(s) */
static int opt_force = 0; /* force operation even on unsupported hubs */
static const struct option long_options[] = {
{ "location", required_argument, NULL, 'l' },
{ "vendor", required_argument, NULL, 'n' },
{ "search", required_argument, NULL, 's' },
{ "level", required_argument, NULL, 'L' },
{ "ports", required_argument, NULL, 'p' },
{ "action", required_argument, NULL, 'a' },
@@ -226,6 +233,7 @@ static const struct option long_options[] = {
{ "repeat", required_argument, NULL, 'r' },
{ "wait", required_argument, NULL, 'w' },
{ "exact", no_argument, NULL, 'e' },
{ "force", no_argument, NULL, 'f' },
{ "reset", no_argument, NULL, 'R' },
{ "version", no_argument, NULL, 'v' },
{ "help", no_argument, NULL, 'h' },
@@ -236,30 +244,33 @@ static const struct option long_options[] = {
static int print_usage()
{
printf(
"uhubctl %s: utility to control USB port power for smart hubs.\n"
"uhubctl: utility to control USB port power for smart hubs.\n"
"Usage: uhubctl [options]\n"
"Without options, show status for all smart hubs.\n"
"\n"
"Options [defaults in brackets]:\n"
"--action, -a - action to off/on/cycle (0/1/2) for affected ports.\n"
"--action, -a - action to off/on/cycle/toggle (0/1/2/3) for affected ports.\n"
"--ports, -p - ports to operate on [all hub ports].\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"
"--search, -s - limit hub by attached device description.\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"
"--force, -f - force operation even on unsupported hubs.\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"
"--help, -h - print this text.\n"
"\n"
"Send bugs and requests to: https://github.com/mvp/uhubctl\n",
PROGRAM_VERSION,
"Send bugs and requests to: https://github.com/mvp/uhubctl\n"
"version: %s\n",
strlen(opt_vendor) ? opt_vendor : "any",
opt_delay,
opt_repeat,
opt_wait
opt_wait,
PROGRAM_VERSION
);
return 0;
}
@@ -385,9 +396,9 @@ static int get_hub_info(struct libusb_device *dev, struct hub_info *info)
);
if (len >= minlen) {
unsigned char port_numbers[MAX_HUB_CHAIN] = {0};
info->dev = dev;
info->bcd_usb = bcd_usb;
info->super_speed = (bcd_usb >= USB_SS_BCD);
info->nports = uhd->bNbrPorts;
snprintf(
info->vendor, sizeof(info->vendor),
@@ -397,53 +408,67 @@ static int get_hub_info(struct libusb_device *dev, struct hub_info *info)
);
/* 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;
info->bus = libusb_get_bus_number(dev);
snprintf(info->location, sizeof(info->location), "%d", info->bus);
info->pn_len = get_port_numbers(dev, info->port_numbers, sizeof(info->port_numbers));
int k;
for (k=0; k<pcount; k++) {
for (k=0; k < info->pn_len; k++) {
char s[8];
snprintf(s, sizeof(s), "%s%d", k==0 ? "-" : ".", port_numbers[k]);
snprintf(s, sizeof(s), "%s%d", k==0 ? "-" : ".", info->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 */
int ocpm = uhd->wHubCharacteristics[0] & HUB_CHAR_OCPM;
/* LPSM must be supported per-port, and OCPM per port or ganged */
if ((lpsm == HUB_CHAR_INDV_PORT_LPSM) &&
(ocpm == HUB_CHAR_INDV_PORT_OCPM ||
ocpm == HUB_CHAR_COMMON_OCPM))
{
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]);
/* 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;
#ifdef __FreeBSD__
for (cap=0; cap < bos->bNumDeviceCapabilities; cap++) {
#else
for (cap=0; cap < bos->bNumDeviceCaps; cap++) {
#endif
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);
}
info->container_id[i*2] = 0;
libusb_free_container_id_descriptor(container_id);
}
}
libusb_free_bos_descriptor(bos);
/* Raspberry Pi 4B hack for USB3 root hub: */
if (strlen(info->container_id)==0 &&
strcasecmp(info->vendor, "1d6b:0003")==0 &&
info->pn_len==0 &&
info->nports==4 &&
bcd_usb==USB_SS_BCD)
{
strcpy(info->container_id, "5cf3ee30d5074925b001802d79434c30");
}
}
libusb_free_bos_descriptor(bos);
/* Logical Power Switching Mode */
int lpsm = uhd->wHubCharacteristics[0] & HUB_CHAR_LPSM;
if (lpsm == HUB_CHAR_COMMON_LPSM && info->nports == 1) {
/* For 1 port hubs, ganged power switching is the same as per-port: */
lpsm = HUB_CHAR_INDV_PORT_LPSM;
}
/* Raspberry Pi 4B reports inconsistent descriptors, override: */
if (lpsm == HUB_CHAR_COMMON_LPSM && strcasecmp(info->vendor, "2109:3431")==0) {
lpsm = HUB_CHAR_INDV_PORT_LPSM;
}
info->lpsm = lpsm;
rc = 0;
} else {
rc = len;
}
libusb_close(devh);
}
@@ -499,7 +524,7 @@ static int get_device_description(struct libusb_device * dev, struct descriptor_
int rc;
int id_vendor = 0;
int id_product = 0;
char ports[64] = "";
char hub_specific[64] = "";
struct libusb_device_descriptor desc;
struct libusb_device_handle *devh = NULL;
rc = libusb_get_device_descriptor(dev, &desc);
@@ -529,8 +554,16 @@ static int get_device_description(struct libusb_device * dev, struct descriptor_
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);
const char * lpsm_type;
if (info.lpsm == HUB_CHAR_INDV_PORT_LPSM) {
lpsm_type = "ppps";
} else if (info.lpsm == HUB_CHAR_COMMON_LPSM) {
lpsm_type = "ganged";
} else {
lpsm_type = "nops";
}
snprintf(hub_specific, sizeof(hub_specific), ", USB %x.%02x, %d ports, %s",
info.bcd_usb >> 8, info.bcd_usb & 0xFF, info.nports, lpsm_type);
}
}
libusb_close(devh);
@@ -541,7 +574,7 @@ static int get_device_description(struct libusb_device * dev, struct descriptor_
ds->vendor[0] ? " " : "", ds->vendor,
ds->product[0] ? " " : "", ds->product,
ds->serial[0] ? " " : "", ds->serial,
ports
hub_specific
);
return 0;
}
@@ -558,17 +591,9 @@ 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 <= hub->nports; port++) {
if (portmask > 0 && (portmask & (1 << (port-1))) == 0) continue;
@@ -588,12 +613,15 @@ static int print_port_status(struct hub_info * hub, int portmask)
struct libusb_device * udev;
int i = 0;
while ((udev = usb_devs[i++]) != NULL) {
uint8_t dev_bus;
uint8_t dev_pn[MAX_HUB_CHAIN];
int dev_plen;
dev_bus = libusb_get_bus_number(udev);
/* only match devices on the same bus: */
if (dev_bus != hub_bus) continue;
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) &&
if ((dev_plen == hub->pn_len + 1) &&
(memcmp(hub->port_numbers, dev_pn, hub->pn_len) == 0) &&
libusb_get_port_number(udev) == port)
{
rc = get_device_description(udev, &ds);
@@ -602,7 +630,7 @@ static int print_port_status(struct hub_info * hub, int portmask)
}
}
if (hub->bcd_usb < USB_SS_BCD) {
if (!hub->super_speed) {
if (port_status == 0) {
printf(" off");
} else {
@@ -680,30 +708,63 @@ static int usb_find_hubs()
rc = get_hub_info(dev, &info);
if (rc) {
perm_ok = 0; /* USB permission issue? */
continue;
}
get_device_description(dev, &info.ds);
if (info.ppps) { /* PPPS is supported */
if (hub_count < MAX_HUBS) {
info.actionable = 1;
if (strlen(opt_location) > 0) {
if (strcasecmp(opt_location, info.location)) {
info.actionable = 0;
if (info.lpsm != HUB_CHAR_INDV_PORT_LPSM && !opt_force) {
continue;
}
info.actionable = 1;
if (strlen(opt_search) > 0) {
/* Search by attached device description */
info.actionable = 0;
struct libusb_device * udev;
int k = 0;
while ((udev = usb_devs[k++]) != NULL) {
uint8_t dev_pn[MAX_HUB_CHAIN];
uint8_t dev_bus = libusb_get_bus_number(udev);
/* only match devices on the same bus: */
if (dev_bus != info.bus) continue;
int dev_plen = get_port_numbers(udev, dev_pn, sizeof(dev_pn));
if ((dev_plen == info.pn_len + 1) &&
(memcmp(info.port_numbers, dev_pn, info.pn_len) == 0))
{
struct descriptor_strings ds;
bzero(&ds, sizeof(ds));
rc = get_device_description(udev, &ds);
if (rc != 0)
break;
if (strstr(ds.description, opt_search)) {
info.actionable = 1;
opt_ports &= 1 << (dev_pn[dev_plen-1] - 1);
break;
}
}
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 (strlen(opt_location) > 0) {
if (strcasecmp(opt_location, info.location)) {
info.actionable = 0;
}
}
if (opt_level > 0) {
if (opt_level != info.pn_len + 1) {
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));
if (hub_count < MAX_HUBS) {
hub_count++;
} else {
/* That should be impossible - but we don't want to crash! */
fprintf(stderr, "Too many hubs!");
break;
}
}
if (!opt_exact) {
/* Handle USB2/3 duality: */
@@ -714,7 +775,8 @@ static int usb_find_hubs()
/* Must have non empty container ID: */
if (strlen(hubs[i].container_id) == 0)
continue;
int match = -1;
int best_match = -1;
int best_score = -1;
for (j=0; j<hub_count; j++) {
if (i==j)
continue;
@@ -722,8 +784,7 @@ static int usb_find_hubs()
/* 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))
if (hubs[i].super_speed == hubs[j].super_speed)
continue;
/* Must have non empty container ID: */
@@ -739,6 +800,14 @@ static int usb_find_hubs()
* We should do few more checks below if multiple such devices are present.
*/
/* Hubs should have the same number of ports */
if (hubs[i].nports != hubs[j].nports) {
/* Except for some weird hubs like Apple mini-dock (has 2 usb2 + 1 usb3 ports) */
if (hubs[i].nports + hubs[j].nports > 3) {
continue;
}
}
/* 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)
@@ -746,22 +815,56 @@ static int usb_find_hubs()
continue;
}
/* Hubs should have the same number of ports: */
if (hubs[i].nports != hubs[j].nports)
continue;
/* We have first possible candidate, but need to keep looking for better one */
/* And the same level: */
if (hubs[i].level != hubs[j].level)
continue;
if (best_score < 1) {
best_score = 1;
best_match = j;
}
/* Finally, we claim a match: */
match = j;
break;
/* Checks for various levels of USB2 vs USB3 path similarity... */
uint8_t* p1 = hubs[i].port_numbers;
uint8_t* p2 = hubs[j].port_numbers;
int l1 = hubs[i].pn_len;
int l2 = hubs[j].pn_len;
int s1 = hubs[i].super_speed;
int s2 = hubs[j].super_speed;
/* Check if port path is the same after removing top level (needed for M1 Macs): */
if (l1 >= 1 && l1 == l2 && memcmp(p1 + 1, p2 + 1, l1 - 1)==0) {
if (best_score < 2) {
best_score = 2;
best_match = j;
}
}
/* Raspberry Pi 4B hack (USB2 hub is one level deeper than USB3): */
if (l1 + s1 == l2 + s2 && l1 >= s2 && memcmp(p1 + s2, p2 + s1, l1 - s2)==0) {
if (best_score < 3) {
best_score = 3;
best_match = j;
}
}
/* Check if port path is exactly the same: */
if (l1 == l2 && memcmp(p1, p2, l1)==0) {
if (best_score < 4) {
best_score = 4;
best_match = j;
}
/* Give even higher priority if `usb2bus + 1 == usb3bus` (Linux specific): */
if (hubs[i].bus - s1 == hubs[j].bus - s2) {
if (best_score < 5) {
best_score = 5;
best_match = j;
}
}
}
}
if (match >= 0) {
if (!hubs[match].actionable) {
if (best_match >= 0) {
if (!hubs[best_match].actionable) {
/* Use 2 to signify that this is derived dual device */
hubs[match].actionable = 2;
hubs[best_match].actionable = 2;
}
}
}
@@ -770,11 +873,19 @@ static int usb_find_hubs()
for (i=0; i<hub_count; i++) {
if (!hubs[i].actionable)
continue;
if (hubs[i].bcd_usb < USB_SS_BCD || opt_exact) {
if (!hubs[i].super_speed || opt_exact) {
hub_phys_count++;
}
}
if (perm_ok == 0 && hub_phys_count == 0) {
#ifdef __gnu_linux__
if (geteuid() != 0) {
fprintf(stderr,
"There were permission problems while accessing USB.\n"
"Follow https://git.io/JIB2Z for a fix!\n"
);
}
#endif
return LIBUSB_ERROR_ACCESS;
}
return hub_phys_count;
@@ -788,7 +899,7 @@ int main(int argc, char *argv[])
int option_index = 0;
for (;;) {
c = getopt_long(argc, argv, "l:L:n:a:p:d:r:w:hveR",
c = getopt_long(argc, argv, "l:L:n:a:p:d:r:w:s:hvefR",
long_options, &option_index);
if (c == -1)
break; /* no more options left */
@@ -811,6 +922,9 @@ int main(int argc, char *argv[])
case 'n':
strncpy(opt_vendor, optarg, sizeof(opt_vendor));
break;
case 's':
strncpy(opt_search, optarg, sizeof(opt_search));
break;
case 'p':
if (!strcasecmp(optarg, "all")) { /* all ports is the default */
break;
@@ -829,6 +943,9 @@ int main(int argc, char *argv[])
if (!strcasecmp(optarg, "cycle") || !strcasecmp(optarg, "2")) {
opt_action = POWER_CYCLE;
}
if (!strcasecmp(optarg, "toggle") || !strcasecmp(optarg, "3")) {
opt_action = POWER_TOGGLE;
}
break;
case 'd':
opt_delay = atof(optarg);
@@ -836,6 +953,9 @@ int main(int argc, char *argv[])
case 'r':
opt_repeat = atoi(optarg);
break;
case 'f':
opt_force = 1;
break;
case 'e':
opt_exact = 1;
break;
@@ -889,23 +1009,11 @@ int main(int argc, char *argv[])
rc = usb_find_hubs();
if (rc <= 0) {
fprintf(stderr,
"No compatible smart hubs detected%s%s!\n"
"No compatible devices detected%s%s!\n"
"Run with -h to get usage info.\n",
strlen(opt_location) ? " at location " : "",
opt_location
);
#ifdef __gnu_linux__
if (rc < 0) {
fprintf(stderr,
"There were permission problems while accessing USB.\n"
"To fix this, run this tool as root using 'sudo uhubctl',\n"
"or add one or more udev rules like below\n"
"to file '/etc/udev/rules.d/52-usb.rules':\n"
"SUBSYSTEM==\"usb\", ATTR{idVendor}==\"2001\", MODE=\"0666\"\n"
"then run 'sudo udevadm trigger --attr-match=subsystem=usb'\n"
);
}
#endif
rc = 1;
goto cleanup;
}
@@ -925,6 +1033,9 @@ int main(int argc, char *argv[])
continue;
if (k == 1 && opt_action == POWER_KEEP)
continue;
// if toggle requested, do it only once when `k == 0`
if (k == 1 && opt_action == POWER_TOGGLE)
continue;
int i;
for (i=0; i<hub_count; i++) {
if (hubs[i].actionable == 0)
@@ -947,17 +1058,18 @@ int main(int argc, char *argv[])
for (port=1; port <= hubs[i].nports; port++) {
if ((1 << (port-1)) & ports) {
int port_status = get_port_status(devh, port);
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))
int power_mask = hubs[i].super_speed ? USB_SS_PORT_STAT_POWER
: USB_PORT_STAT_POWER;
int powered_on = port_status & power_mask;
if (opt_action == POWER_TOGGLE) {
request = powered_on ? LIBUSB_REQUEST_CLEAR_FEATURE
: LIBUSB_REQUEST_SET_FEATURE;
}
if (k == 0 && !powered_on && opt_action != POWER_TOGGLE)
continue;
if (k == 1 && (port_status & power_mask))
if (k == 1 && powered_on)
continue;
int repeat = 1;
if (k == 0)
repeat = opt_repeat;
if (!(port_status & ~power_mask))
repeat = 1;
int repeat = powered_on ? opt_repeat : 1;
while (repeat-- > 0) {
rc = libusb_control_transfer(devh,
LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_OTHER,
@@ -974,7 +1086,7 @@ int main(int argc, char *argv[])
}
}
/* USB3 hubs need extra delay to actually turn off: */
if (k==0 && hubs[i].bcd_usb >= USB_SS_BCD)
if (k==0 && hubs[i].super_speed)
sleep_ms(150);
printf("Sent power %s request\n",
request == LIBUSB_REQUEST_CLEAR_FEATURE ? "off" : "on"

View File

@@ -2,7 +2,7 @@ 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"
file://LICENSE;md5=a79e6a142b69522fe7757fe7313895eb"
DEPENDS = "libusb1"
RDEPENDS_${PN} = "libusb1"