152 Commits

Author SHA1 Message Date
Vadim Mikhailov
4b2c4c211f Add IBM SK-8815 keyboard as compatible device
Closes #647.
2026-01-16 12:47:18 -08:00
Vadim Mikhailov
da8a260538 Remove Dell Wyse 5070 - does not support vbus off
Closes #646, originally added in #642.
2026-01-16 12:36:50 -08:00
Vadim Mikhailov
a9cd307a4a Add Sipolar A-173 as compatible device
Closes #645.
2026-01-16 12:32:05 -08:00
Vadim Mikhailov
f45034e0d0 Add DLink DUB-2332 as compatible device 2025-12-19 16:50:14 -08:00
Vadim Mikhailov
74ac3e6bc3 Add Dell Wyse 5070 as compatible device 2025-12-19 16:48:52 -08:00
Vadim Mikhailov
1117cc7ce2 Add a note how to install uhubctl from FreeBSD ports
uhubctl is now available in FreeBSD ports collection.
Soon it should be available as a package too,
which will make install even simpler (`pkg install uhubctl`).

Closes #640.
2025-11-10 18:15:15 -08:00
Vadim Mikhailov
65e3b74e60 Add MacBook Air 2010 as compatible device
Closes #638.
2025-11-10 18:01:05 -08:00
Vadim Mikhailov
ceadf4c13a Add a warning for Sugoi USB2-HUB4X
New hw revision of Sugoi USB2-HUB4X does not support per-port power control,
but seems to work with ganged power switching (requires -f).

Closes #636.
2025-10-04 11:54:44 -07:00
Vadim Mikhailov
ad94660713 Add a note for Maplin A08CQ
Only older model with red plastic in USB ports is working correctly.
Per report in issue #635, there are different samples
with white plastic in USB ports which do not support PPPS
and should be avoided.

Closes #635.
2025-09-19 13:14:31 -07:00
Vadim Mikhailov
ca7610ab01 Add Lenovo ThinkPad Mini Dock Series 3 as compatible device
Closes #628.
2025-09-19 13:05:56 -07:00
Vadim Mikhailov
c9edaf3da8 Add a note how to make uhubctl work on MacOS 26 Tahoe
Closes #623.
2025-09-18 10:03:05 -07:00
Shun Hasegawa
9a6e6d3a6d Add note to ReTRY HUB (#631) 2025-09-18 09:39:09 -07:00
Vadim Mikhailov
1af4949ce8 Lenovo USB-C to 4 Port USB-A Hub only works in USB2 mode
This hub only works in USB2 mode, and ports are ganged in a weird way.
Added a note in device table.

Closes #625.
2025-06-13 08:58:25 -07:00
Vadim Mikhailov
5dda8a874b Add hub|usb udev recipe to README as well
Related to #618.
2025-06-06 22:40:54 -07:00
Vadim Mikhailov
786dcb2277 Improve Linux udev rules to support more systems
Some Linux systems detect USB hubs with driver as `hub`, and some as `usb`.
Improve udev permission rules to match both options.

Closes #618.
2025-06-05 21:32:31 -07:00
Vadim Mikhailov
fab805bc12 Add Targus ACH155 as compatible device
Per submitter, Targus ACH155 port 3 turns on/off power on all four ports.

Closes #620.
2025-06-04 23:09:40 -07:00
Vadim Mikhailov
a05a250644 Add Rosonway RSH-ST10C-6 as compatible device
Closes #616.
2025-05-30 23:54:36 -07:00
Vadim Mikhailov
b3fa9e7e63 Improve computer model detection when device tree is not available
Closes #614.
2025-05-30 23:23:04 -07:00
Vadim Mikhailov
fbe812bd83 Add an option to search/limit hub by description
Adding new option to limit hub by description.
Existing -s option limits by attached device description,
and new feature -H limits by hub instead.
Thanks to @pendragonsound for submitting patch!

Closes #611.
2025-04-19 10:35:44 -07:00
Vadim Mikhailov
d96308569c uhubctl is in homebrew core now - remove obsolete files, update readme
* remove custom homebrew tap - uhubctl is included in homebrew core now.
* remove bitbake recipe - it does not seem to be used for years now,
  and it should not have been committed in first place.
* add `installing` paragraph to readme. since most relevant OS include uhubctl
  in standard package managers now, most people don't want to compile it,
  but instead know how to install it quickly.
* update short links to not use bit.ly as it became very spammy now.
* update few expired links to point to archive.org cache.
* add `all` target to Makefile (FreeBSD ports want `all` target to always exist).
2025-04-18 23:13:56 -07:00
Vadim Mikhailov
d85f100c98 homebrew audit: livecheck must be before depends_on 2025-04-17 13:51:05 -07:00
Vadim Mikhailov
65f6d48cd1 homebrew audit one more fix 2025-04-17 13:44:56 -07:00
Vadim Mikhailov
14852054e2 making homebrew audit happy 2025-04-17 13:42:33 -07:00
Vadim Mikhailov
d00a167ab4 homebrew audit insists on checksum before head 2025-04-17 13:35:31 -07:00
Vadim Mikhailov
16001b090d minor fixes to homebrew formula so it passes strict audit checks 2025-04-17 13:28:19 -07:00
Vadim Mikhailov
42cdfb0b82 Fix udev rules to support Linux kernels 6.8+
Linux kernels before had devpath containing `usbN-portM`.
Newer kernels just use `port` symlink instead, which breaks our udev rules.
Changing glob from `*-port*` to `*port*` makes it work for both old and new kernels.

Closes issues #608, #609.
2025-02-23 12:51:49 -08:00
Vadim Mikhailov
c74755be42 Fix FreeBSD build, update docs regarding pkg-config 2025-01-21 19:45:22 -08:00
Vadim Mikhailov
07abf4cbea Fix warnings when compiled in C++ mode
Fixed all warnings when compiling with `c++ -std=c++11 ...`:
* bzero() appears to be deprecated, replaced with memset()
* fixed few const warnings
* suppressed zero array warnings (emitted by libusb headers)
2025-01-21 19:25:50 -08:00
Vadim Mikhailov
d483981f95 Add thermal camera control as notable project
Described in #594.
2025-01-18 14:59:52 -08:00
Vadim Mikhailov
2ca14eb3dd Add Mac Mini M4 as compatible device
Closes #606.
2025-01-18 14:44:59 -08:00
Vadim Mikhailov
40f10578cb Remove Rosonway RSH-A107 (and ikuai A107) from supported list
There are multiple reports that RSH-A107 does not turn off VBUS:
While it is possible that some hardware revisions which are working,
looks like most don't. It is better to remove it from the list.

Closes #604.
2025-01-18 14:32:56 -08:00
Vadim Mikhailov
7604f84c95 Add StarTech HB31C2A2CB as compatible device
Closes #601.
2025-01-18 14:24:32 -08:00
Vadim Mikhailov
f4b0d2ad31 Add RSH-A37S as compatible device
Closes #598.
2025-01-18 14:15:35 -08:00
Vadim Mikhailov
a7df8f8367 Cleanup ifdef usage
* No need to check for both __gnu_linux__ and __linux__, latter is enough.
* Require libusb 1.0.13 to build - this makes LIBUSB_API_VERSION always defined.
* Include `<libusb.h>` not `<libusb-1.0/libusb.h>` - this will always work
  if `pkg-config` is detected. For backwards compatibility still try building
  without `pkg-config` present, but it is much less reliable and will not work on Mac.
* Bump copyright year.
2025-01-18 14:04:34 -08:00
Tormod Volden
a957b21815 Add --sysdev/-y option for direct device node access (#600)
* Add --sysdev/-u option for direct device node access

Only the device specified by the given device path will be accessed,
instead of scanning the USB bus.

Most useful if you use udev rules to create stable device node aliases for hubs,
independent of bus topology.

For instance, if your matching udev rules include SYMLINK+="MYSMARTHUB1"
you can call uhubctl with --sysdev /dev/MYSMARTHUB1 instead of using -l
and a non-stable bus location to specify it.

Signed-off-by: Tormod Volden <debian.tormod@gmail.com>
2025-01-18 11:50:25 -08:00
Vadim Mikhailov
2dbc6ce2d9 Add recipe for compiling on OpenSUSE
Closes #592.
2024-10-15 21:49:00 -07:00
mvp
302b3c0c77 always include <fcntl.h>
4a233824c4 fixed issue for Raspberry Pi on Linux, but broke compilation on Mac.
Fixing it by always including <fcntl.h>.
2024-10-13 13:10:22 -07:00
Vadim Mikhailov
4a233824c4 Add StarTech DKT30CSDHPD3 as compatible device
Closes #588.
2024-09-17 18:09:57 -07:00
Vadim Mikhailov
e0d1c34eed Improve support for Raspberry Pi 4B and 5:
* Detect if we are running on Raspberry 4B or 5
* Add RPi 4B and 5 conditionals for RPi hacks
* Relax some conditions which may vary depending on RPi hardware and/or OS
  (in particular Ubuntu hub numbering differs from Raspbian).
* For RPi5, changed README to suggest using busses 2 and 4 instead of 1 and 3.
  This makes the same recipe work on both Raspbian and Ubuntu.

This fixes #587.
2024-09-16 20:23:33 -07:00
mvp
153c5da267 Fixed snprintf override which breaks on old MacOS
`snprintf` is available in Mac/FreeBSD headers only if _XOPEN_SOURCE is 600 or higher.
This closes #586.
2024-09-12 10:42:23 -07:00
mvp
bfc11e6b05 Remove Buffalo BSH4A05U3BK from supported list
Buffalo BSH4A05U3BK does not seem to support VBUS off, removing from the list.

Closes #584.
2024-08-31 22:29:58 -07:00
mvp
c9abed425c Release version 2.6.0 as stable brew tap 2024-08-31 16:57:18 -07:00
mvp
352f5878e9 Release version 2.6.0 2024-08-31 16:30:35 -07:00
mvp
169842ae97 Improve Linux detection
__gnu_linux__ is not always present on all Linux platforms -
add checking for __linux__ too.
2024-08-31 16:19:59 -07:00
mvp
8de8c9487f Add Philips 346B1C as compatible device
Closes #582.
2024-08-31 16:01:38 -07:00
mvp
3dbc1d9e41 Add Rosonway RSH-A107 (aka ikuai A107) as compatible device
Closes #572.
2024-08-31 15:52:12 -07:00
Misha Bunte
83ce372810 Fix flag to turn on and off 2024-08-16 14:05:14 -07:00
Misha Bunte
f22541cf64 Add flash (inverted cycle) option - turn on and off (#580)
* Add flash (inverted cycle) option - to turn port on then off with a delay
2024-08-13 13:24:09 -07:00
Christian Svensson
2df4681826 Construct root USB device sysfs path
Fixes #578. See issue for discussion.

Signed-off-by: Christian Svensson <blue@cmd.nu>
2024-08-05 12:32:33 -07:00
mvp
359d887927 Fix typo for driver name
Closes #574.
2024-08-04 20:51:41 -07:00
Christian Svensson
1f920f6b15 Fix endian conversions to work for big-endian
Before:
```
Current status for hub 3 [6b1d:0200 Linux 6.1.98 xhci-hcd xHCI Host Controller 0000:01:00.0, USB 0.02, 2 ports, ppps]
  Port 1: 0305 power lowspeed suspend connect [9911:c068 Sierra Wireless, Incorporated MC7304-CP]
  Port 2: 0301 power lowspeed connect [0304:0160 FTDI FT232R USB UART A10OEDEW]
```

After:

```
Current status for hub 1 [1d6b:0002 Linux 6.1.98 ehci_hcd EHCI Host Controller 16f0000000000.ehci, USB 2.00, 2 ports, nops]
  Port 1: 0503 power highspeed enable connect [0424:2240 Generic Ultra Fast Media 000000225001]
  Port 2: 0100 power
Current status for hub 3 [1d6b:0002 Linux 6.1.98 xhci-hcd xHCI Host Controller 0000:01:00.0, USB 2.00, 2 ports, ppps]
  Port 1: 0503 power highspeed enable connect [1199:68c0 Sierra Wireless, Incorporated MC7304-CP]
  Port 2: 0103 power enable connect [0403:6001 FTDI FT232R USB UART A10OEDEW]
```

Tested on both x86-64 and MIPS64.

Signed-off-by: Christian Svensson <blue@cmd.nu>
2024-08-04 19:59:56 -07:00
mvp
746891d888 Add VirtualHere USB3 4-port hub as compatible device
Closes #571.
2024-06-05 16:26:20 -07:00
mvp
83f624f3b5 Removed Plugable USB3-HUB7-81X from supported list
Closes #559.
2024-05-21 11:14:23 -07:00
Hans Märki
61fd83ff10 Add Rosonway A10, and ST07C as compatible device (#567) 2024-05-21 11:06:36 -07:00
Jan Lübbe
cd99f0de34 Add labgrid to README as project using uhubctl (#566)
labgrid uses uhubctl in the USBPowerDriver to control power for boards
connected on switchable USB hubs [1].

[1] https://github.com/labgrid-project/labgrid/blob/master/labgrid/driver/powerdriver.py
2024-05-06 00:57:57 -07:00
Vadim Mikhailov
3d804c1ab7 Add support for Raspberry Pi 5
Added hack to support Raspberry Pi 5.
Updated README with recipe how to control power on RPi5.

Closes #547.
2024-04-19 16:08:19 -07:00
Vadim Mikhailov
3e86729800 Add Lenovo USB-C to 4 Port USB-A Hub as compatible device
Closes #561.
2024-04-19 16:06:29 -07:00
Vadim Mikhailov
d556081fbb Add LG 27GL850-B as compatible device
Closes #560.
2024-04-19 16:02:47 -07:00
Vadim Mikhailov
1199b9ddbc Add Targus PA095UZ as compatible device
Closes #558.
2024-04-19 15:59:46 -07:00
Vadim Mikhailov
f70c2ced96 Fix build on Mac
Recent libusb seems to have removed extra symlink `libusb-1.0/libusb.h`,
which breaks build on Mac.

TODO: consider always using `<libusb.h>`, but that would
put hard dependency on `pkg-config` usage.
2024-04-19 15:49:19 -07:00
Vadim Mikhailov
4deae1731c Update README, bump copyright year
* Remove mention of MacOS bug only applicable to old MacOS 12.4 x86
* Simplify docs related to Linux kernel 6.0 fixes
* Bump copyright year
2024-02-20 22:40:28 -08:00
Vadim Mikhailov
5cc9f154e1 Remove Cyberpower CP-H420P from supported list
Removing CP-H420P because it does not seem to support Vbus control.

Closes #557.
2024-02-20 22:08:08 -08:00
mvp
7a0fbd266f Update README.md (#553)
Fix devices sorting order
2024-01-24 11:48:24 -08:00
Nicolai
e18434cd2e Add Revolution Pi to list of supported devices (#552)
Revolution Pi Core and Connect family implement per port power switching
for all USB ports.

Signed-off-by: Nicolai Buchwitz <nb+github@tipi-net.de>
2024-01-24 11:42:36 -08:00
mvp
a34319a6d4 Add Gigabyte G27Q as compatible device
Closes #551.
2024-01-21 17:34:39 -08:00
mvp
d18db0d2be Add Acer BE270U as compatible device
Closes #550.
2024-01-21 17:29:02 -08:00
mvp
f37dcb5cbf Update EOL dates for some hubs
Updated EOL date for B+B SmartWorx USH304 to 2019.
https://www.mouser.com/PCN/Advantech_Corporation_EOL_BB_USH304.pdf.
Updated EOL date for Delock 62537, last available date seems to be 2021.

Closes #528.
2023-10-07 12:53:45 -07:00
mvp
5bb7197f51 Remove Anker AK-A83650A1 from supported list
There are multiple reports in #361 that Anker PowerExpand 6-in-1 USB-C hub
does not support vbus off - removing it from supported list.
2023-09-23 10:36:18 -07:00
mvp
5b2c40db83 Add Sugoi USB2-HUB4X as compatible device
Closes #525.
2023-09-21 20:25:07 -07:00
mvp
0d28753bcf Add Das Keyboard 4 as compatible device
Closes #522.
2023-09-21 20:12:35 -07:00
mvp
54ab9e72d4 Add Dell Wyse 3040 as compatible device
Dell Wyse 3040 is odd - it has 6 ports advertised on USB2 and 7 ports on USB3,
and both USB2+USB3 do not advertise power switching support.
However, using -f it seems that power switching does work.
I assume it needs separate control for USB2+USB3 using -e as well.

Closes #512.
2023-07-30 16:26:33 -07:00
mvp
b16ba1b36d Add Coolgear USBG-12U2ML as compatible device
Closes #514.
2023-07-17 22:40:50 -07:00
mvp
30372ce59f Add open source hardware hub design based on CH335F to notable projects
Closes #516.
2023-07-17 21:13:50 -07:00
Vadim Mikhailov
a9f2e7832d Use another URL shortener, GFM relative links do not seem to work correctly 2023-05-23 13:58:27 -07:00
Vadim Mikhailov
cec0435144 Use different issue linking method 2023-05-23 13:54:09 -07:00
Vadim Mikhailov
586b424557 Fix issue relative link 2023-05-23 13:50:24 -07:00
Vadim Mikhailov
1d3b841429 Add a warning link for StarTech HB30A4AIB different batch
Add a warning link for StarTech HB30A4AIB - new hardware is using
different USB chipset and different USB vid:pid,
which does not support power switching correctly.

Closes #509.
2023-05-23 13:39:04 -07:00
Vadim Mikhailov
2f45ac6138 Add Microchip EVB9512 as compatible device
Closes #505.
2023-05-02 11:55:33 -07:00
Vadim Mikhailov
4c235868eb Remove ExSys EX-1113HMS from supported list
ExSys EX-1113HMS was tested to not support turning VBUS off.
Remove from supported list.

Closes #502.
2023-04-05 22:46:29 -07:00
Vadim Mikhailov
33f1ecb2dd Add explicit void prototypes to make modern compilers happy 2023-03-31 14:44:54 -07:00
Vadim Mikhailov
8f355837b7 Add StarTech HB30A4AIB as compatible device
Closes #497.
2023-03-31 14:37:43 -07:00
Vadim Mikhailov
f2d97a3aad Add Belkin F5U238UKCRL-MOB as compatible device
Closes #492.
2023-02-13 21:31:06 -08:00
Vadim Mikhailov
e847dc461d Revert "Fixed brew sha256 for v2.5.0.tar.gz"
This reverts commit 61fd84b0ad.

It should fix issue #490.
2023-01-31 16:08:06 -08:00
Vadim Mikhailov
61fd84b0ad Fixed brew sha256 for v2.5.0.tar.gz
This used to work fine, perhaps something has changed in how Github
creates archives so archive checksum does not match anymore?
Confirmed that actual file content at https://github.com/mvp/uhubctl/archive/v2.5.0.tar.gz
is exactly the same as content for `git checkout v2.5.0`.
Updated checksum to expected value so `brew install uhubctl` should pass.
Closes #489.
2023-01-30 14:53:36 -08:00
Vadim Mikhailov
a54835e0ea Add Asus Z77 Sabertooth motherboard as compatible device
Closes #486.
2023-01-27 11:26:29 -08:00
Vadim Mikhailov
41e1bacf41 Bump copyright year 2023-01-27 11:23:08 -08:00
mvp
2be4125977 Add LG Electronics 27UN83A-W monitor as compatible device
Closes #482.
2023-01-04 20:48:47 -08:00
mvp
2ef5f0eb0b Fixed udev rule example to use DRIVER="usb"
It seems that DRIVER="hub" almost never works,
but DRIVER="hub" always does.
2023-01-04 20:37:58 -08:00
mvp
19dfdad7f6 Fix device table specs for some hubs incorrectly listed as USB 3.1
This is additional fix for #447.

We use old convention for USB versions in device table:

USB 3.2 spec version | Max speed | Old style USB version
---------------------+-----------+----------------------
USB 3.2 Gen 1	     |  5 gbps   | USB 3.0
USB 3.2 Gen 2        | 10 gbps   | USB 3.1
USB 3.2 Gen 2x2      | 20 gbps   | USB 3.2

All affected hubs were USB 3.2 Gen 1, which corresponds to original USB 3.0.
2022-11-02 20:24:10 -07:00
mvp
ce2fd9a419 Release version 2.5.0 as stable brew tap 2022-11-01 20:49:11 -07:00
mvp
20276ad5ce Release version 2.5.0 2022-11-01 20:26:08 -07:00
mvp
914060b4b5 Add simpler way to configure udev rules and ready to use udev rule file
It turns out that we can allow udev access to any USB hub using DRIVER=="hub"
udev filter, this should make it easier for linux ditros to add proper udev rules.

This change adds more documentation to README, and also adds ready to use udev rule
that should work for any USB hub and any Linux kernel version.
2022-11-01 20:19:00 -07:00
gfrancesco
5a46019e15 ADD: BenQ PD3220U monitor to compatibility list (#464) 2022-10-26 22:55:09 -07:00
Vadim Mikhailov
b6189d1409 Consistently use only C style comments 2022-10-05 12:04:56 -07:00
Vadim Mikhailov
554a5c7eb3 Note that PR #450 has fixed few annoyances listed in FAQ 2022-09-22 16:22:09 -07:00
Vadim Mikhailov
732ad2d90e Fix crash on non-Linux if -S is used
Also move short options near long options table so it is easier to maintain.
2022-09-22 11:24:32 -07:00
Vadim Mikhailov
dd5ff8f79d Fix unused variable compilation warning on non Linux platforms 2022-09-22 11:09:24 -07:00
mvp
a802fa75aa Merge pull request #450 from hnez/linux-sysfs
Use sysfs interface on recent Linux kernels
2022-09-22 11:05:23 -07:00
Leonard Göhrs
24e1540006 Add udev rules for Linux sysfs power switching to README.md
The udev rules for the sysfs case are a bit more complex than those for the
libusb based interface, as udev has built-in support for changing permissions
on device files but not for sysfs attributes.
Instead we have to use chmod / chown to set permissions and owners.

You may notice the " || true" parts in the RUN part of the rules in addition
to the -f parameters. These are there to make sure that no error is logged by
udev even if the disable attribute does not exist. chmod/chown still return
an error exit code even with -f if the requested path does not exist.
" || true" makes sure this error is not propagated to udev.

Signed-off-by: Leonard Göhrs <l.goehrs@pengutronix.de>
2022-09-22 11:40:03 +02:00
Leonard Göhrs
aa7fc0a126 Add support for Linux sysfs based power switching
Starting with Linux kernel 6.0[1] there will be a sysfs interface to power USB
ports off/on from userspace.
Try to use this interface before falling back to the usual libusb based
power switching (e.g. when running on a kernel <6.0 or if file permissions do
not allow using the sysfs interface).

The main benefit of using the sysfs interface is that the kernel does not get
confused about the state of a port, so retrying should no longer be required.

[1]: https://lore.kernel.org/all/20220607114522.3359148-1-m.grzeschik@pengutronix.de/

Signed-off-by: Leonard Göhrs <l.goehrs@pengutronix.de>
2022-09-22 11:40:03 +02:00
Vadim Mikhailov
7099809b24 Add Apple Pro Display XDR as compatible device
Closes #453.
2022-09-06 18:03:10 -07:00
mvp
a1bc86824b Add Inateck HB2025A as compatible device
This works only if connected via USB2.

Closes #451.
2022-09-05 10:46:29 -07:00
Leonard Göhrs
9d66811175 Split out port status setting logic into separate function
Split out current libusb based power on/off logic into a separate function
in anticipation of a other power switching implementations.

Signed-off-by: Leonard Göhrs <l.goehrs@pengutronix.de>
2022-09-02 12:28:04 +02:00
Vadim Mikhailov
9171fc6bad Fix description for j5create JUH470 quirks 2022-09-01 22:35:15 -07:00
Vadim Mikhailov
d75b10d696 Fixed USB version for few supported devices to match their spec
Per USB 3.2 spec, any USB 3.x device can claim itself as USB 3.2 per following table:

| Old style USB version | USB 3.2 specification | Speed, gbps |
|-----------------------|-----------------------|-------------|
| USB 3.0               | USB 3.2 Gen 1         | 5           |
| USB 3.1               | USB 3.2 Gen 2         | 10          |
| USB 3.2               | USB 3.2 Gen 2x2       | 20          |

https://www.usb.org/sites/default/files/usb_3_2_language_product_and_packaging_guidelines_final.pdf

And this is what happens - some devices claim themselves to be USB 3.2 despite only supporting 5 or 10 gbps -
and unfortunately this is correct per USB 3.2 spec. But it is very confusing for people shopping for better product.

Not ideal solution, but a workaround is to use old style USB version designators in our compatibility table
so people do not choose USB 3.2 device listed if it cannot support speeds higher than 5 gbps.

Closes #447.
2022-08-31 15:58:13 -07:00
Tim Ruffing
88e5641fe1 Make sure option strings are null-terminated 2022-08-24 15:59:01 -07:00
Nicolai
b51b5addee Add python3-uhubctl to project list
Add python3-uhubctl to project list
2022-08-24 12:04:15 -07:00
Vadim Mikhailov
a76f45f377 Remove D-Link DUB-H4 rev B from the list
D-Link DUB-H4 rev B does not seem to support VBUS off, remove from supported list.
Closes #440.
2022-08-24 12:02:24 -07:00
mvp
941e356c24 Add HP USB-C Dock G5 as compatible device
Closes #441.
2022-08-22 14:58:56 -07:00
Vadim Mikhailov
e1e4d45066 Add Rosonway RSH-A13 as compatible device
This works only if connected via USB2.

Closes #434.
2022-08-05 11:03:34 -07:00
Vadim Mikhailov
9263ab266e Add Dell U3419W as compatible device
Closes #435.
2022-08-02 09:38:58 -07:00
Vadim Mikhailov
54248b8f29 Note that issue #419 is fixed in MacOS 12.5
Closes #419.
2022-07-20 16:47:09 -07:00
Vadim Mikhailov
06e7240db6 Add Seeed Studio reTerminal CM4104032 as compatible device
Closes #429.
2022-07-12 15:57:28 -07:00
Vadim Mikhailov
e1709c7b62 Add a note for AmazonBasics HU9002V1EBL
Closes #428.
2022-07-12 15:51:48 -07:00
Vadim Mikhailov
38ff03200d Add Rosonway RSH-A13 as compatible device (with some caveats)
Closes #423.
2022-07-01 17:17:45 -07:00
Vadim Mikhailov
24e3680c4b Add a warning that libusb (and uhubctl) are broken on MacOS 12.4 and later
Highlight issue #419 and https://github.com/libusb/libusb/issues/1156.
2022-06-20 11:56:44 -07:00
Vadim Mikhailov
5418edf9d1 Add a warning link that RSHTech RSH-A16 may not work
Rosonway RSH-A16 works, while RSHTech RSH-A16 may not.
Closes #417.
2022-06-17 17:55:52 -07:00
Vadim Mikhailov
a6159d68e7 Add NVidia Jetson Xavier NX as compatible device
Closes #420.
2022-05-27 13:24:16 -07:00
Vadim Mikhailov
ee98e43a43 Update copyright year 2022-05-10 18:13:08 -07:00
Vadim Mikhailov
4f38fde9da Add LG Electronics 27UK850-W monitor as compatible device
Closes #414.
2022-05-10 18:11:25 -07:00
Vadim Mikhailov
a022e60112 Add Lenovo ThinkPlus 4X90W86497 as compatible device
Closes #414.
2022-05-10 18:04:25 -07:00
Vadim Mikhailov
a0f8c87e08 Add Rosonway RSH-518C as compatible device
Closes #407.
2022-05-10 16:25:22 -07:00
Vadim Mikhailov
6f1c71d5a5 Add j5create JUH377 as compatible device
Closes #406.
2022-03-07 10:25:19 -08:00
Vadim Mikhailov
7496d53f87 Add a note for Cypress CY4608 HX2VL
Closes #404.
2022-03-07 10:10:33 -08:00
Vadim Mikhailov
56ea37fc46 Add a warning that some revisions of Bytecc BT-UH340 may not work
Closes #401.
2022-02-28 15:41:08 -08:00
Vadim Mikhailov
ed27158c11 Add Lenovo T24i-10 as compatible device
Closes #384.
2022-02-02 20:22:07 -08:00
Vadim Mikhailov
53fd1f39ce Remove StarTech ST4300USB3 from compatible list, does not seem to properly support VBUS off
Closes #390.
2022-02-01 17:16:22 -08:00
Vadim Mikhailov
950fce2325 Add Centech CT-USB4HUB as compatible device
Closes #393.
2022-02-01 17:13:50 -08:00
Mark Furland
61187d755c Add pluggable USB3-HUB7BC; note USB3-HUB7-81x is 2 port only
Closes #378.
2021-12-17 13:10:32 -08:00
Vadim Mikhailov
610e85ff2d Add StarTech ST4300USB3 as compatible device
Closes #376.
2021-11-29 11:22:47 -08:00
Vadim Mikhailov
5779054ccc Add a note about D-Link DUB-H7 rev. G not supported
Closes #377.
2021-11-29 11:16:41 -08:00
Vadim Mikhailov
f8cb89f4be Add option --nodesc to skip querying device string descriptors
Sometimes target devices may become unresponsive for control messages to read string descriptors.
Option --nodesc (-N) allows to still reset such devices.

Also, as preventative measure, check if libusb_get_string_descriptor_ascii returns any error,
then stop querying any other string descriptor from the same device.
2021-10-29 11:49:59 -07:00
Vadim Mikhailov
1a7d34780f Add Microchip EVB-USB5807 as compatible device
Closes #369.
2021-10-20 16:56:31 -07:00
Vadim Mikhailov
f6e99bc35d Add HP P5Q58UT and LG 27MD5KL-B as compatible devices
Adding two devices reported by @alanzchen in #366.
2021-10-18 22:20:00 -07:00
Vadim Mikhailov
db3ae900d8 Add BenQ PD2700U as compatible device
Closes #362.
2021-09-28 19:15:13 -07:00
Vadim Mikhailov
75b1f668f9 Add Anker AK-A83650A1 as compatible device
Closes #361.
2021-09-28 19:15:08 -07:00
Vadim Mikhailov
37f494ff31 Update UUGear MEGA4 info 2021-08-12 13:33:05 -07:00
Vadim Mikhailov
bc80baa9e9 Add UUGear MEGA4 as compatible device
Add UUGear MEGA4 USB 3.1 PPPS hub for Raspberry PI 4B to supported list,
it was specifically designed to support uhubctl in #316.
https://www.uugear.com/news/new-product-mega4-usb-3-1-ppps-hub-for-raspberry-pi-4
2021-08-09 09:57:41 -07:00
Vadim Mikhailov
6cd357e150 Remove UNITEK Y-3098ABK from supported list
Too many reports in #352 stating it is not supporting VBUS.
2021-08-09 09:32:34 -07:00
Vadim Mikhailov
5ba647ee27 Add Unitek Y-3098ABK as compatible device
Closes #352.
2021-07-07 13:26:55 -07:00
Vadim Mikhailov
69c444b75d Add Hardkernel ODROID-C4 as compatible device
Closes #348.
2021-05-31 20:31:10 -07:00
Vadim Mikhailov
f20007e25e Add Delock 62537 as compatible device
Closes #346.
2021-05-17 22:08:47 -07:00
Vadim Mikhailov
46d0de4331 Re-add Delock 87445 to supported list
It seems to work after all (originally reported in #107, then removed in #123).
It looks like there are different hardware revisions, some work well, some are not detected - added a note.
2021-05-17 22:04:11 -07:00
Vadim Mikhailov
23c15ffcf8 Add diyppps to notable projects
Closes #343.
2021-05-01 22:13:32 -07:00
Vadim Mikhailov
16f7a6dd23 Add AmazonBasics HUC9002V1ESL as compatible device
AmazonBasics HUC900xxx models seem to be identical to HU900xxx except for included USB-C cable.
2021-05-01 22:05:42 -07:00
Vadim Mikhailov
7bafc7ffb3 Add a note for Linksys USB2HUB4
Closes #331.
2021-04-02 14:27:53 -07:00
Vadim Mikhailov
0a42b91382 Add Webcam On-Air Sign to notable projects 2021-04-01 09:42:04 -07:00
Vadim Mikhailov
2820f9fa27 DLink DUB-H4 rev B4 does not work
Closes #328.
2021-03-29 15:48:24 -07:00
Vadim Mikhailov
fb85325629 Add AmazonBasics U3-7HUB as supported device
AmazonBasics U3-7HUB power switching works, but only on 1 charge port with lightning symbol.
This should also work for similar AmazonBasics hubs U3-4HUB and U3-10HUB.

Closes #325.
2021-03-18 17:02:38 -07:00
Vadim Mikhailov
e5ae7b9906 Add Plugable USB3-HUB10-C2 as supported device
Plugable USB3-HUB10-C2 power switching works, but only on 2 charge ports.
This is also true for USB3-HUB7C, updated note accordingly.

Closes #322.
2021-03-05 12:55:00 -08:00
Vadim Mikhailov
bf3971423a Add Autobrew to list of notable projects 2021-03-05 12:46:04 -08:00
Vadim Mikhailov
7757db54ac Release version 2.4.0 as stable brew tap 2021-02-13 16:48:24 -08:00
9 changed files with 620 additions and 198 deletions

7
.gitignore vendored
View File

@@ -6,3 +6,10 @@ uhubctl
# Mac symbols
*.dSYM
# Patches
*.patch
*.diff
# IDE config dirs
.*/

View File

@@ -1,24 +0,0 @@
class Uhubctl < Formula
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"
bin.install "uhubctl"
end
test do
system "#{bin}/uhubctl", "-v"
end
end

View File

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

View File

@@ -14,7 +14,7 @@ PKG_CONFIG ?= pkg-config
CC ?= gcc
CFLAGS ?= -g -O0
CFLAGS += -Wall -Wextra -std=c99 -pedantic
CFLAGS += -Wall -Wextra -Wno-zero-length-array -std=c99 -pedantic
GIT_VERSION := $(shell git describe --match "v[0-9]*" --abbrev=8 --dirty --tags | cut -c2-)
ifeq ($(GIT_VERSION),)
GIT_VERSION := $(shell cat VERSION)
@@ -31,12 +31,17 @@ 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)
# But it should still build even if pkg-config is not available
CFLAGS += -I/usr/include/libusb-1.0
LDFLAGS += -lusb-1.0
endif
PROGRAM = uhubctl
.PHONY: all install clean
all: $(PROGRAM)
$(PROGRAM): $(PROGRAM).c
$(CC) $(CPPFLAGS) $(CFLAGS) $@.c -o $@ $(LDFLAGS)

274
README.md
View File

@@ -11,76 +11,130 @@ https://www.gniibe.org/development/ac-power-control-by-USB-hub
Compatible USB hubs
===================
Note that very few hubs actually support per-port power switching.
Note that not many USB hubs correctly support per-port power switching.
Some of them are no longer manufactured and can be hard to find.
This is list of known compatible USB hubs:
| Manufacturer | Product | Ports | USB | VID:PID | Release | EOL |
|:-------------------|:-----------------------------------------------------|:------|:----|:----------|:--------|:-----|
| Acer | BE270U monitor ([see](https://tinyurl.com/acer550)) | 4 | 3.0 |`2109:2811`| 2016 | |
| 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 | HU9003V1EBL, HUC9003V1EBL | 7 | 3.1 |`2109:2817`| 2018 | |
| AmazonBasics | HU9002V1SBL, HU9002V1EBL, HU9002V1ESL ([note](https://tinyurl.com/HU9002)) | 10 | 3.1 |`2109:2817`| 2018 | |
| AmazonBasics | HUC9002V1SBL, HUC9002V1EBL, HUC9002V1ESL | 10 | 3.1 |`2109:2817`| 2018 | |
| AmazonBasics | U3-7HUB (only works for 1 charge port) | 7 | 3.0 |`2109:2813`| 2020 | |
| Anker | AK-68ANHUB-BV7A-0004 ([note](https://git.io/JLnZb)) | 7 | 3.0 |`2109:0812`| 2014 | |
| Apple | Mac Mini M4 (2 front ports only) | 2 | 3.2 |`05AC:800B`| 2024 | |
| Apple | MacBook Air (Late 2010) | 2 | 2.0 |`1D6B:0002`| 2010 | 2020 |
| Apple | Pro Display XDR MWPE2LL/A (internal USB hub) | 4 | 2.0 |`05AC:9139`| 2019 | |
| 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 | Z77 Sabertooth Motherboard (onboard USB hub) | 6 | 2.0 | | 2012 | |
| Asus | Z87-PLUS Motherboard (onboard USB hub) | 4 | 3.0 | | 2013 | 2016 |
| Aukey | CB-C59 | 4 | 3.1 |`2109:2813`| 2017 | |
| Aukey | CB-C59 | 4 | 3.0 |`2109:2813`| 2017 | |
| B+B SmartWorx | UHR204 | 4 | 2.0 |`0856:DB00`| 2013 | |
| B+B SmartWorx | USH304 | 4 | 3.0 |`04B4:6506`| 2017 | |
| B+B SmartWorx | USH304 | 4 | 3.0 |`04B4:6506`| 2017 | 2019 |
| 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 | |
| Belkin | F5U238UKCRL-MOB | 4 | 2.0 |`0409:0059`| 2004 | 2010 |
| BenQ | PD2700U 4K Monitor (works only in USB2 mode) | 4 | 3.0 |`05E3:0610`| 2018 | |
| BenQ | PD3220U | 4 | 3.1 |`05E3:0610`| 2019 | |
| Bytecc | BT-UH340 ([warning](https://tinyurl.com/BT-UH340-1)) | 4 | 3.0 |`2109:8110`| 2010 | |
| Centech | CT-USB4HUB ReTRY HUB ([note](https://github.com/mvp/uhubctl/issues/630)) | 4 | 3.0 |`0424:2744`| 2017 | |
| 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 B (silver). Note: rev B7+ not supported | 4 | 2.0 |`05E3:0605`| 2005 | 2010 |
| Coolgear | USBG-12U2ML | 12 | 2.0 |`05e3:0607`| 2015 | |
| Cypress | CY4608 HX2VL ([note](https://tinyurl.com/CY4608-1)) | 4 | 2.0 |`04B4:6570`| 2012 | |
| D-Link | DUB-2332 rev A1 | 3 | 3.0 | | 2022 | |
| 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 | |
| D-Link | DUB-H7 rev D,E (black). Rev B,C,F,G 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 | S2719DGF 27" WQHD Gaming-Monitor | 5 | 3.0 |`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 | |
| Dell | UltraSharp U3419W 34" Curved Monitor | 6 | 3.0 | | 2020 | |
| Dell | Wyse 3040 ([-f required](https://tinyurl.com/wyse3k))| 6 | 3.0 | | 2017 | |
| Delock | 62537 | 4 | 3.0 | | 2017 | 2021 |
| Delock | 87445 ([note](https://git.io/Jsuz5)) | 4 | 2.0 |`05E3:0608`| 2009 | 2013 |
| Elecom | U2H-G4S | 4 | 2.0 | | 2006 | 2011 |
| ExSys | EX-1113HMS | 16 | 3.1 | | 2018 | |
| Gigabyte | G27Q monitor ([see](http://tinyurl.com/G27Q551)) | 4 | 3.0 |`2109:0817`| 2020 | |
| GlobalScale | ESPRESSObin SBUD102 V5 | 1 | 3.0 |`1D6B:0003`| 2017 | |
| Hardkernel | ODROID-C4 ([note](https://git.io/JG0mP)) | 4 | 3.0 | | 2020 | |
| Hawking Technology | UH214 | 4 | 2.0 | | 2003 | 2008 |
| Hewlett Packard | USB-C Dock G5 5TW10AA | 5 | 3.0 |`03F0:076B`| 2019 | |
| Hewlett Packard | P5Q58UT | 3 | 3.0 | | 2019 | |
| IBM | SK-8815 USB Keyboard | 3 | 1.1 |`04B3:301A`| 2004 | |
| Inateck | HB2025A ([USB2 only](https://tinyurl.com/HB2025A-1)) | 4 | 3.1 |`2109:2822`| 2021 | |
| IOI | U3H415E1 | 4 | 3.0 | | 2012 | |
| j5create | JUH470 (works only in USB2 mode) | 3 | 3.0 |`05E3:0610`| 2014 | |
| j5create | JUH377 ([note](https://tinyurl.com/JUH377)) | 7 | 3.0 | | 2016 | |
| j5create | JUH470 ([note](https://tinyurl.com/JUH470)) | 3 | 3.0 |`05E3:0610`| 2014 | |
| Juiced Systems | 6HUB-01 | 7 | 3.0 |`0BDA:0411`| 2014 | 2018 |
| KUNBUS GmbH | RevPi Connect (+) / S / SE | 2 | 2.0 |`0424:9514`| 2018 | |
| KUNBUS GmbH | RevPi Connect 4 | 2 | 3.0 | | 2022 | |
| KUNBUS GmbH | RevPi Core 3 / S / SE | 2 | 2.0 |`0424:9514`| 2017 | |
| LG Electronics | 27MD5KL-B monitor | 4 | 3.0 |`043E:9A60`| 2019 | |
| LG Electronics | 27GL850-B monitor | 4 | 3.0 |`0451:8142`| 2019 | |
| LG Electronics | 27UK850-W monitor | 2 | 3.0 | | 2018 | |
| LG Electronics | 27UN83A-W monitor | 2 | 3.0 |`0451:8142`| 2020 | |
| LG Electronics | 38WK95C-W monitor | 4 | 3.0 |`0451:8142`| 2018 | |
| Lenovo | ThinkPad Mini Dock Series 3 (433710U) | 6 | 2.0 |`17EF:100A`| 2016 | |
| 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 |
| Lenovo | ThinkPlus 4-in-1 USB-C hub 4X90W86497 | 3 | 3.0 | | 2021 | |
| Lenovo | ThinkVision T24i-10 Monitor | 4 | 2.0 |`17EF:0610`| 2018 | |
| Lenovo | USB-C to 4 Port USB-A Hub ([USB2 only](https://github.com/mvp/uhubctl/issues/625)) | 4 | 2.0 |`17EF:103A`| 2020 | |
| 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 |
| Linksys | USB2HUB4 ([note](https://git.io/JYiDZ)) | 4 | 2.0 | | 2004 | 2010 |
| Maplin | A08CQ ([red ports only](https://tinyurl.com/gh635)) | 7 | 2.0 |`0409:0059`| 2008 | 2011 |
| Metadot | Das Keyboard 4 | 2 | 3.0 | | 2014 | |
| Microchip | EVB9512 | 2 | 2.0 | | 2009 | |
| Microchip | EVB-USB2517 | 7 | 2.0 | | 2008 | |
| Microchip | EVB-USB2534BC | 4 | 2.0 | | 2013 | |
| Microchip | EVB-USB5807 | 7 | 3.0 | | 2016 | |
| Moxa | Uport-407 | 7 | 2.0 |`110A:0407`| 2009 | |
| NVidia | Jetson Nano B01 ([details](https://git.io/JJaFR)) | 4 | 3.0 | | 2019 | |
| NVidia | Jetson Xavier NX ([details](https://tinyurl.com/Xavier-NX)) | 4 | 3.0 | | 2020 | |
| Phidgets | HUB0003_0 | 7 | 2.0 |`1A40:0201`| 2017 | |
| Philips | 346B1C UltraWide 34" Curved Monitor | 4 | 3.0 |`05E3:0610`| 2019 | |
| 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 | USB3-HUB7C (only works for 2 charge ports) | 7 | 3.0 |`2109:0813`| 2015 | |
| Plugable | USBC-HUB7BC (works for 6/7 ports, not the rightmost) | 7 | 3.0 |`2109:0817`| 2021 | |
| Plugable | USB3-HUB10-C2 (only works for 2 charge ports) | 10 | 3.0 | | 2014 | |
| 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 | |
| Raspberry Pi | 5 ([see below](#raspberry-pi-5)) | 4 | 3.0 |`1d6b:0002`| 2023 | |
| 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 | |
| Rosonway | RSH-518C ([note](https://tinyurl.com/RSH518)) | 7 | 3.0 |`2109:0817`| 2021 | |
| Rosonway | RSH-A10 ([see](https://tinyurl.com/2ppyyaj8)) | 10 | 3.0 |`0bda:0411`| 2020 | |
| Rosonway | RSH-A13 ([warning](https://tinyurl.com/RSH-A13)) | 13 | 3.1 |`2109:2822`| 2021 | |
| Rosonway | RSH-A16 ([note](https://git.io/JTawg), [warning](https://tinyurl.com/RSH-A16)) | 16 | 3.0 |`0bda:0411`| 2020 | |
| Rosonway | RSH-A37S | 7 | 3.0 |`2109:2822`| 2021 | |
| Rosonway | RSH-A104 ([USB2 only](https://tinyurl.com/RSH-A104)) | 4 | 3.1 |`2109:2822`| 2022 | |
| Rosonway | RSH-ST07C ([only 4](https://tinyurl.com/4pjnujrn)) | 7 | 3.0 |`2109:2822`| 2023 | |
| Rosonway | RSH-ST10C-6 ([note](https://tinyurl.com/428vpa2f)) | 10 | 3.1 | | 2024 | |
| Sanwa Supply | USB-HUB14GPH | 4 | 1.1 | | 2001 | 2003 |
| Seagate | Backup Plus Hub STEL8000100 | 2 | 3.0 |`0BC2:AB44`| 2016 | |
| Seeed Studio | reTerminal CM4104032 | 2 | 2.0 |`0424:2514`| 2021 | |
| Sipolar | A-173 | 7 | 3.0 |`0BDA:0411`| 2016 | |
| StarTech | DKT30CSDHPD3 USB-C Travel Dock | 3 | 3.0 |`2109:2817`| 2018 | |
| StarTech | HB30A4AIB ([warning](https://tinyurl.com/ycxravwk)) | 4 | 3.0 |`2109:2817`| 2018 | |
| StarTech | HB31C2A2CB ([note](https://github.com/mvp/uhubctl/issues/601)) | 5 | 3.0 |`14B0:013D`| 2020 | |
| Sunix | SHB4200MA | 4 | 2.0 |`0409:0058`| 2006 | 2009 |
| System Talks | Sugoi USB2-HUB4X ([warning](https://github.com/mvp/uhubctl/issues/636))|4|2.0| | 2007 | |
| Targus | ACH155 (port 3 controls all ports) | 4 | 3.0 | | 2022 | |
| Targus | PA095UZ | 2 | 2.0 | | 2004 | |
| Targus | PAUH212/PAUH212U | 7 | 2.0 | | 2004 | 2009 |
| Texas Instruments | TUSB4041PAPEVM | 4 | 2.1 |`0451:8142`| 2015 | |
| UUGear | MEGA4 (for Raspberry Pi 4B) | 4 | 3.0 |`2109:0817`| 2021 | |
| VirtualHere | USB3 4-port hub ([note](https://tinyurl.com/vhusb)) | 4 | 3.0 | | 2024 | |
This table is by no means complete.
If your hub works with `uhubctl`, but is not listed above, please report it
@@ -105,27 +159,53 @@ virtual hubs for power off/on changes to take effect. `uhubctl` will try to do t
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`.
Installing
==========
For Linux and MacOS uhubctl is available in standard package managers
and can be installed with following commands:
* MacOS: `brew install uhubctl` or `sudo port install uhubctl`
- :warning: MacOS 26+ requires [fix](https://github.com/libusb/libusb/pull/1700).
Until libusb version 1.0.30 with this fix is released, install libusb from master:
brew uninstall libusb --ignore-dependencies
brew install libusb --head
* Ubuntu/Debian/Raspbian: `sudo apt install uhubctl`
* Redhat/EPEL/Fedora/CentOS: `sudo yum install uhubctl`
* OpenSUSE: `sudo zypper install uhubctl`
* FreeBSD: `cd /usr/ports/sysutils/uhubctl && sudo make install`
However, uhubctl installed from standard package manager may not
necessarily be latest version, or even severely lag behind current version.
If [latest published](https://github.com/mvp/uhubctl/releases) uhubctl version
is newer than what your package manager offers, you may need to compile and install
from source as described below.
Compiling
=========
This utility was tested to compile and work on Linux
(Ubuntu/Debian, Redhat/Fedora/CentOS, Arch Linux, Gentoo, openSUSE, Buildroot), FreeBSD, NetBSD, SunOS and MacOS.
This utility was tested to compile and work on Linux (Ubuntu/Debian/Raspbian,
Redhat/EPEL/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).
[necessary USB control requests](https://web.archive.org/web/20210225235523/https://social.msdn.microsoft.com/Forums/sqlserver/en-US/f680b63f-ca4f-4e52-baa9-9e64f8eee101/how-to-send-an-quotusb-control-requestquot-to-an-usbhub?forum=wdk).
This may be fixed if `libusb` starts supporting different driver on Windows.
Note that it is highly recommended to have `pkg-config` installed (many platforms provide it by default).
Note that it is highly recommended to have utility `pkgconf` (or `pkg-config`) installed
(often it is installed by default).
First, you need to install library libusb-1.0 (version 1.0.12 or later, 1.0.16 or later is recommended):
First, you need to install library libusb-1.0 (version 1.0.13 or later is required,
1.0.23 or later is recommended):
* Ubuntu: `sudo apt-get install libusb-1.0-0-dev`
* Redhat: `sudo yum install libusb1-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`
* Ubuntu: `sudo apt-get install libusb-1.0-0-dev pkgconf`
* Redhat: `sudo yum install libusb1-devel pkgconf`
* OpenSUSE: `sudo zypper install libusb-1_0-devel pkgconf`
* MacOS: `brew install libusb pkgconf`, or `sudo port install libusb-devel pkgconf`
* FreeBSD: `pkg install gmake pkgconf` (libusb is included by default)
* NetBSD: `sudo pkgin install libusb1 gmake pkgconf`
* Windows: TBD?
To fetch uhubctl source and compile it:
@@ -141,17 +221,6 @@ You can install it in your system as `/usr/sbin/uhubctl` using:
Note that on some OS (e.g. FreeBSD/NetBSD) you need to use `gmake` instead to build.
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 uhubctl
```
To build/install from master branch, use `--HEAD`:
```
brew install uhubctl --HEAD
```
Usage
=====
@@ -186,20 +255,35 @@ 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 2001 with your vendor id):
SUBSYSTEM=="usb", ATTR{idVendor}=="2001", MODE="0666"
Starting with Linux Kernel 6.0 there is a standard interface to turn USB hub ports on or off,
and `uhubctl` will try to use it (instead of `libusb`) to set the port status.
This is why there are additional rules for 6.0+ kernels.
There is no harm in having these rules on systems running older kernel versions.
To fix USB permissions, first run `sudo uhubctl` and note all `vid:pid` for hubs you need to control.
Then, add udev rules like below to file `/etc/udev/rules.d/52-usb.rules`
(replace `2001` with your hub vendor id, or completely remove `ATTR{idVendor}` filter to allow any USB hub access):
SUBSYSTEM=="usb", DRIVER=="hub|usb", MODE="0666", ATTR{idVendor}=="2001"
# Linux 6.0 or later (its ok to have this block present for older Linux kernels):
SUBSYSTEM=="usb", DRIVER=="hub|usb", \
RUN="/bin/sh -c \"chmod -f 666 $sys$devpath/*port*/disable || true\""
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:
and both need permissions to make uhubctl work properly.
E.g. for Raspberry Pi 4B, you need to add these 2 lines (or remove idVendor filter):
SUBSYSTEM=="usb", ATTR{idVendor}=="2109", MODE="0666"
SUBSYSTEM=="usb", ATTR{idVendor}=="1d6b", MODE="0666"
SUBSYSTEM=="usb", DRIVER=="hub|usb", MODE="0666", ATTR{idVendor}=="2109"
SUBSYSTEM=="usb", DRIVER=="hub|usb", MODE="0666", ATTR{idVendor}=="1d6b"
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"
SUBSYSTEM=="usb", DRIVER=="hub|usb", MODE="0664", GROUP="dialout"
# Linux 6.0 or later (its ok to have this block present for older Linux kernels):
SUBSYSTEM=="usb", DRIVER=="hub|usb", \
RUN+="/bin/sh -c \"chown -f root:dialout $sys$devpath/*port*/disable || true\"" \
RUN+="/bin/sh -c \"chmod -f 660 $sys$devpath/*port*/disable || true\""
and then add permitted users to `dialout` group:
@@ -209,6 +293,7 @@ For your `udev` rule changes to take effect, reboot or run:
sudo udevadm trigger --attr-match=subsystem=usb
For your convenience, ready to use udev rule is provided [here](https://github.com/mvp/uhubctl/blob/master/udev/rules.d/52-usb.rules).
FAQ
@@ -267,11 +352,10 @@ Per-port power switching:
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.
This is Linux kernel [issue](https://tinyurl.com/ym7yvuzw) and is [fixed](https://github.com/mvp/uhubctl/pull/450)
since uhubctl 2.5.0 for systems with Linux kernel 6.0 or later.
You can use this workaround for this issue:
If you are still using Linux 5.x or older, 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}/
@@ -284,9 +368,13 @@ When you turn power back on, device should re-enumerate properly (no need to cal
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 -
This is Linux kernel [issue](https://tinyurl.com/ym7yvuzw) and is [fixed](https://github.com/mvp/uhubctl/pull/450)
since uhubctl 2.5.0 for systems with Linux kernel 6.0 or later.
If you are still using Linux 5.x or older:
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:
@@ -314,6 +402,7 @@ Doing so will confuse internal hub circuitry and will cause unpredictable behavi
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.
Also, there are supported hubs designed specifically for Raspberry Pi, e.g. UUGear MEGA4.
For reference, supported Raspberry Pi models have following internal USB topology:
@@ -340,7 +429,7 @@ For reference, supported Raspberry Pi models have following internal USB topolog
##### 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)
you have to [update firmware](https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#rpi-eeprom-update)
to make power switching work on RPi 4B.
* USB2 hub `1`, 1 port, only connects hub `1-1` below.
@@ -355,35 +444,72 @@ to make power switching work on RPi 4B.
* USB2 hub `3`, 1 port, OTG controller. Power switching is [not supported](https://git.io/JUc5Q).
##### Raspberry Pi 5
Raspberry Pi 5 has two USB2 ports and two USB3 ports (total 4).
These ports are connected to 4 distinct USB hubs `1`,`2`,`3`,`4` in really weird configuration
(but depending on OS and HW revision hubs of interest can be `2`,`3`,`4`,`5`).
If USB3 device is connected to blue socket, it will be detected on USB3 hub `2` or `4`.
If USB2 device is connected to any socket or USB3 device connected to black socket,
it will be detected on USB2 hub `1` or `3`.
Regardless of USB2/USB3 connection type, blue sockets are always port `1`,
and black sockets are always port `2`.
Each of 4 USB onboard hubs advertises as supporting per-port power switching, but this is not true.
In reality, Raspberry Pi 5 all 4 ports are ganged together in one group,
despite belonging to 4 different logical USB hubs.
To turn off VBUS power it has to be disabled across all onboard hubs and ports with:
```
uhubctl -l 2 -a 0
uhubctl -l 4 -a 0
```
To turn it back on:
```
uhubctl -l 2 -a 1
uhubctl -l 4 -a 1
```
Note that VBUS power goes down only if all ports are off -
enabling any single port enables VBUS back for all 4 ports.
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://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/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 |
| 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://tinyurl.com/sysmoQMOD) | Reset cellular modems when necessary |
| [Smog Sensor](https://tinyurl.com/smogsensor) | 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://tinyurl.com/ideal-m-srv) | 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://tinyurl.com/fan-rpi) | Control USB fan to avoid Raspberry Pi overheating |
| [Raspberry Pi Reboot Router](https://tinyurl.com/rpi-rtr)| Automatically reboot router if internet isn't working |
| [Control USB Lamp With Voice](https://tinyurl.com/usblmp)| Voice Control of USB Lamp using Siri and Raspberry Pi |
| [Control USB LED Strip](https://tinyurl.com/usbleds) | Controlling USB powered LED Light Strip |
| [Brew beer with Raspberry Pi](https://git.io/JtbLd) | Automated beer brewing system using Raspberry Pi |
| [Webcam On-Air Sign](https://tinyurl.com/uonair) | Automatically light up a sign when webcam is in use |
| [Do it yourself PPPS](https://git.io/J3lHs) | Solder wires in your USB hub to support uhubctl |
| [Open source PPPS hub](https://tinyurl.com/yckhystt) | Open source hardware project for uhubctl compatible hub |
| [Python Wrapper for uhubctl](https://github.com/nbuchwitz/python3-uhubctl) | Module to use uhubctl with Python |
| [labgrid](https://github.com/labgrid-project/labgrid) | Framework for testing embedded Linux on hardware |
| [Thermal Camera](https://tinyurl.com/5asne8hw) | Turn on/off robot's thermal camera when necessary |
Copyright
=========
Copyright (C) 2009-2020 Vadim Mikhailov
Copyright (C) 2009-2025 Vadim Mikhailov
This file can be distributed under the terms and conditions of the
GNU General Public License version 2.

View File

@@ -1 +1 @@
2.4.0
2.6.0

24
udev/rules.d/52-usb.rules Normal file
View File

@@ -0,0 +1,24 @@
# uhubctl USB hub per-port power control https://github.com/mvp/uhubctl
#
# Copyright (c) 2009-2025, Vadim Mikhailov
#
# This file can be distributed under the terms and conditions of the
# GNU General Public License version 2.
# uhubctl udev rules for rootless operation on Linux for users in group `dialout`.
#
# Copy this file to /etc/udev/rules.d, then reboot or run:
#
# sudo udevadm trigger --attr-match=subsystem=usb
#
# To add yourself to this permission group, run:
#
# sudo usermod -a -G dialout $USER
# This is for Linux before 6.0:
SUBSYSTEM=="usb", DRIVER=="hub|usb", MODE="0664", GROUP="dialout"
# This is for Linux 6.0 or later (ok to keep this block present for older Linux kernels):
SUBSYSTEM=="usb", DRIVER=="hub|usb", \
RUN+="/bin/sh -c \"chown -f root:dialout $sys$devpath/*port*/disable || true\"" \
RUN+="/bin/sh -c \"chmod -f 660 $sys$devpath/*port*/disable || true\""

456
uhubctl.c
View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009-2020 Vadim Mikhailov
* Copyright (c) 2009-2025 Vadim Mikhailov
*
* Utility to turn USB port power on/off
* for USB hubs that support per-port power switching.
@@ -9,7 +9,7 @@
*
*/
#define _XOPEN_SOURCE 500
#define _XOPEN_SOURCE 600
#include <stdio.h>
#include <stdlib.h>
@@ -18,6 +18,7 @@
#include <getopt.h>
#include <errno.h>
#include <ctype.h>
#include <fcntl.h>
#if defined(_WIN32)
#include <windows.h>
@@ -29,24 +30,28 @@
#include <unistd.h>
#endif
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(_WIN32)
#include <libusb.h>
#else
#include <libusb-1.0/libusb.h>
/* LIBUSBX_API_VERSION was first defined in libusb 1.0.13
and renamed to LIBUSB_API_VERSION since libusb 1.0.16 */
#if defined(LIBUSBX_API_VERSION) && !defined(LIBUSB_API_VERSION)
#define LIBUSB_API_VERSION LIBUSBX_API_VERSION
#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)
/* FreeBSD's libusb does not define LIBUSB_DT_SUPERSPEED_HUB */
#if !defined(LIBUSB_DT_SUPERSPEED_HUB)
#define LIBUSB_DT_SUPERSPEED_HUB 0x2a
#endif
#if !defined(LIBUSB_API_VERSION)
#error "libusb-1.0 is required!"
#endif
#if _POSIX_C_SOURCE >= 199309L
#include <time.h> /* for nanosleep */
#endif
/* cross-platform sleep function */
void sleep_ms(int milliseconds)
@@ -76,6 +81,7 @@ void sleep_ms(int milliseconds)
#define POWER_ON 1
#define POWER_CYCLE 2
#define POWER_TOGGLE 3
#define POWER_FLASH 4
#define MAX_HUB_CHAIN 8 /* Per USB 3.0 spec max hub chain is 7 */
@@ -211,6 +217,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_searchhub[64] = ""; /* Search by hub 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 */
@@ -221,11 +228,33 @@ 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 int opt_nodesc = 0; /* skip querying device description */
#if defined(__linux__)
static int opt_nosysfs = 0; /* don't use the Linux sysfs port disable interface, even if available */
#if (LIBUSB_API_VERSION >= 0x01000107) /* 1.0.23 */
static const char *opt_sysdev;
#endif
#endif
/* For Raspberry Pi detection and workarounds: */
static int is_rpi_4b = 0;
static int is_rpi_5 = 0;
static const char short_options[] =
"l:L:n:a:p:d:r:w:s:H:hvefRN"
#if defined(__linux__)
"S"
#if (LIBUSB_API_VERSION >= 0x01000107) /* 1.0.23 */
"y:"
#endif
#endif
;
static const struct option long_options[] = {
{ "location", required_argument, NULL, 'l' },
{ "vendor", required_argument, NULL, 'n' },
{ "search", required_argument, NULL, 's' },
{ "searchhub",required_argument, NULL, 'H' },
{ "level", required_argument, NULL, 'L' },
{ "ports", required_argument, NULL, 'p' },
{ "action", required_argument, NULL, 'a' },
@@ -234,6 +263,13 @@ static const struct option long_options[] = {
{ "wait", required_argument, NULL, 'w' },
{ "exact", no_argument, NULL, 'e' },
{ "force", no_argument, NULL, 'f' },
{ "nodesc", no_argument, NULL, 'N' },
#if defined(__linux__)
{ "nosysfs", no_argument, NULL, 'S' },
#if (LIBUSB_API_VERSION >= 0x01000107)
{ "sysdev", required_argument, NULL, 'y' },
#endif
#endif
{ "reset", no_argument, NULL, 'R' },
{ "version", no_argument, NULL, 'v' },
{ "help", no_argument, NULL, 'h' },
@@ -241,7 +277,7 @@ static const struct option long_options[] = {
};
static int print_usage()
static int print_usage(void)
{
printf(
"uhubctl: utility to control USB port power for smart hubs.\n"
@@ -249,16 +285,24 @@ static int print_usage()
"Without options, show status for all smart hubs.\n"
"\n"
"Options [defaults in brackets]:\n"
"--action, -a - action to off/on/cycle/toggle (0/1/2/3) for affected ports.\n"
"--action, -a - action to off/on/cycle/toggle/flash (0/1/2/3/4) 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"
"--searchhub,-H - limit hub by description.\n"
"--search, -s - limit hub by attached device description.\n"
"--delay, -d - delay for cycle action [%g sec].\n"
"--delay, -d - delay for cycle/flash 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"
"--nodesc, -N - do not query device description (helpful for unresponsive devices).\n"
#if defined(__linux__)
"--nosysfs, -S - do not use the Linux sysfs port disable interface.\n"
#if (LIBUSB_API_VERSION >= 0x01000107)
"--sysdev, -y - open system device node instead of scanning.\n"
#endif
#endif
"--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"
@@ -340,6 +384,70 @@ static int ports2bitmap(char* const portlist)
return ports;
}
/*
* Get model of the computer we are currently running on.
* On success return 0 and fill model string (null terminated).
* If model is not known or error occurred returns -1.
*
* Currently this can only return successfully on Linux,
* but in the future we may need it on other operating systems too.
*/
static int get_computer_model(char *model, int len)
{
int fd = open("/sys/firmware/devicetree/base/model", O_RDONLY);
if (fd >= 0) {
int bytes_read = read(fd, model, len-1);
close(fd);
if (bytes_read < 0) {
return -1;
}
model[bytes_read] = 0;
} else {
// devicetree is not available, try parsing /proc/cpuinfo instead.
// most Raspberry Pi have /proc/cpuinfo about 1KB, so 4KB buffer should be plenty:
char buffer[4096] = {0}; // fill buffer with all zeros
fd = open("/proc/cpuinfo", O_RDONLY);
if (fd < 0) {
return -1;
}
int bytes_read = read(fd, buffer, sizeof(buffer)-1);
close(fd);
if (bytes_read < 0) {
return -1;
}
buffer[bytes_read] = 0;
char* model_start = strstr(buffer, "Model\t\t: ");
if (model_start == NULL) {
return -1;
}
char* model_name = model_start + 9;
char* newline_pos = strchr(model_name, '\n');
if (newline_pos != NULL) {
*newline_pos = 0;
}
strncpy(model, model_name, len);
model[len-1] = 0;
}
return 0;
}
/*
* Check if we are running on given computer model using substring match.
* Returns 1 if yes and 0 otherwise.
*/
static int check_computer_model(const char *target)
{
char model[256] = "";
if (get_computer_model(model, sizeof(model)) == 0) {
if (strstr(model, target) != NULL) {
return 1;
}
}
return 0;
}
/*
* Compatibility wrapper around libusb_get_port_numbers()
@@ -348,7 +456,7 @@ static int ports2bitmap(char* const portlist)
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)
#if (LIBUSB_API_VERSION >= 0x01000102)
/*
* libusb_get_port_path is deprecated since libusb v1.0.16,
* therefore use libusb_get_port_numbers when supported
@@ -380,7 +488,7 @@ static int get_hub_info(struct libusb_device *dev, struct hub_info *info)
return rc;
if (desc.bDeviceClass != LIBUSB_CLASS_HUB)
return LIBUSB_ERROR_INVALID_PARAM;
int bcd_usb = libusb_le16_to_cpu(desc.bcdUSB);
int bcd_usb = desc.bcdUSB;
int desc_type = bcd_usb >= USB_SS_BCD ? LIBUSB_DT_SUPERSPEED_HUB
: LIBUSB_DT_HUB;
rc = libusb_open(dev, &devh);
@@ -403,8 +511,8 @@ static int get_hub_info(struct libusb_device *dev, struct hub_info *info)
snprintf(
info->vendor, sizeof(info->vendor),
"%04x:%04x",
libusb_le16_to_cpu(desc.idVendor),
libusb_le16_to_cpu(desc.idProduct)
desc.idVendor,
desc.idProduct
);
/* Convert bus and ports array into USB location string */
@@ -419,7 +527,7 @@ static int get_hub_info(struct libusb_device *dev, struct hub_info *info)
}
/* Get container_id: */
bzero(info->container_id, sizeof(info->container_id));
memset(info->container_id, 0, sizeof(info->container_id));
struct libusb_bos_descriptor *bos;
rc = libusb_get_bos_descriptor(devh, &bos);
if (rc == 0) {
@@ -445,7 +553,8 @@ static int get_hub_info(struct libusb_device *dev, struct hub_info *info)
libusb_free_bos_descriptor(bos);
/* Raspberry Pi 4B hack for USB3 root hub: */
if (strlen(info->container_id)==0 &&
if (is_rpi_4b &&
strlen(info->container_id)==0 &&
strcasecmp(info->vendor, "1d6b:0003")==0 &&
info->pn_len==0 &&
info->nports==4 &&
@@ -462,10 +571,32 @@ static int get_hub_info(struct libusb_device *dev, struct hub_info *info)
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) {
if (is_rpi_4b && lpsm == HUB_CHAR_COMMON_LPSM && strcasecmp(info->vendor, "2109:3431")==0) {
lpsm = HUB_CHAR_INDV_PORT_LPSM;
}
info->lpsm = lpsm;
/* Raspberry Pi 5 hack */
if (is_rpi_5 &&
strlen(info->container_id)==0 &&
info->lpsm==HUB_CHAR_INDV_PORT_LPSM &&
info->pn_len==0)
{
/* USB2 */
if (strcasecmp(info->vendor, "1d6b:0002")==0 &&
info->nports==2 &&
!info->super_speed)
{
strcpy(info->container_id, "Raspberry Pi 5 Fake Container Id");
}
/* USB3 */
if (strcasecmp(info->vendor, "1d6b:0003")==0 &&
info->nports==1 &&
info->super_speed)
{
strcpy(info->container_id, "Raspberry Pi 5 Fake Container Id");
}
}
rc = 0;
} else {
rc = len;
@@ -500,7 +631,121 @@ static int get_port_status(struct libusb_device_handle *devh, int port)
if (rc < 0) {
return rc;
}
return ust.wPortStatus;
return libusb_le16_to_cpu(ust.wPortStatus);
}
#if defined(__linux__)
/*
* Try to use the Linux sysfs interface to power a port off/on.
* Returns 0 on success.
*/
static int set_port_status_linux(struct libusb_device_handle *devh, struct hub_info *hub, int port, int on)
{
int configuration = 0;
char disable_path[PATH_MAX];
int rc = libusb_get_configuration(devh, &configuration);
if (rc < 0) {
return rc;
}
/*
* The "disable" sysfs interface is available only starting with kernel version 6.0.
* For earlier kernel versions the open() call will fail and we fall back to using libusb.
*/
if (hub->pn_len == 0) {
snprintf(disable_path, PATH_MAX,
"/sys/bus/usb/devices/%s-0:%d.0/usb%s-port%i/disable",
hub->location, configuration, hub->location, port
);
} else {
snprintf(disable_path, PATH_MAX,
"/sys/bus/usb/devices/%s:%d.0/%s-port%i/disable",
hub->location, configuration, hub->location, port
);
}
int disable_fd = open(disable_path, O_WRONLY);
if (disable_fd >= 0) {
rc = write(disable_fd, on ? "0" : "1", 1);
close(disable_fd);
}
if (disable_fd < 0 || rc < 0) {
/*
* ENOENT is the expected error when running on Linux kernel < 6.0 where
* sysfs disable interface does not exist yet - no need to report anything in this case.
* If the file exists but another error occurs it is most likely a permission issue.
* Print an error message mostly geared towards setting up udev.
*/
if (errno != ENOENT) {
fprintf(stderr,
"Failed to set port status by writing to %s (%s).\n"
"Follow https://git.io/JIB2Z to make sure that udev is set up correctly.\n"
"Falling back to libusb based port control.\n"
"Use -S to skip trying the sysfs interface and printing this message.\n",
disable_path, strerror(errno)
);
}
return -1;
}
return 0;
}
#endif
/*
* Use a control transfer via libusb to turn a port off/on.
* Returns >= 0 on success.
*/
static int set_port_status_libusb(struct libusb_device_handle *devh, int port, int on)
{
int rc = 0;
int request = on ? LIBUSB_REQUEST_SET_FEATURE
: LIBUSB_REQUEST_CLEAR_FEATURE;
int repeat = on ? 1 : opt_repeat;
while (repeat-- > 0) {
rc = libusb_control_transfer(devh,
LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_OTHER,
request, USB_PORT_FEAT_POWER,
port, NULL, 0, USB_CTRL_GET_TIMEOUT
);
if (rc < 0) {
perror("Failed to control port power!\n");
}
if (repeat > 0) {
sleep_ms(opt_wait);
}
}
return rc;
}
/*
* Try different methods to power a port off/on.
* Return >= 0 on success.
*/
static int set_port_status(struct libusb_device_handle *devh, struct hub_info *hub, int port, int on)
{
#if defined(__linux__)
if (!opt_nosysfs) {
if (set_port_status_linux(devh, hub, port, on) == 0) {
return 0;
}
}
#else
(void)hub;
#endif
return set_port_status_libusb(devh, port, on);
}
@@ -530,28 +775,31 @@ static int get_device_description(struct libusb_device * dev, struct descriptor_
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);
memset(ds, 0, sizeof(*ds));
id_vendor = desc.idVendor;
id_product = 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 (!opt_nodesc) {
if (desc.iManufacturer) {
rc = libusb_get_string_descriptor_ascii(devh,
desc.iManufacturer, (unsigned char*)ds->vendor, sizeof(ds->vendor));
rtrim(ds->vendor);
}
if (rc >= 0 && desc.iProduct) {
rc = libusb_get_string_descriptor_ascii(devh,
desc.iProduct, (unsigned char*)ds->product, sizeof(ds->product));
rtrim(ds->product);
}
if (rc >= 0 && desc.iSerialNumber) {
rc = 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;
memset(&info, 0, sizeof(info));
rc = get_hub_info(dev, &info);
if (rc == 0) {
const char * lpsm_type;
@@ -609,7 +857,7 @@ static int print_port_status(struct hub_info * hub, int portmask)
printf(" Port %d: %04x", port, port_status);
struct descriptor_strings ds;
bzero(&ds, sizeof(ds));
memset(&ds, 0, sizeof(ds));
struct libusb_device * udev;
int i = 0;
while ((udev = usb_devs[i++]) != NULL) {
@@ -690,7 +938,7 @@ static int print_port_status(struct hub_info * hub, int portmask)
* In case of error returns negative error code.
*/
static int usb_find_hubs()
static int usb_find_hubs(void)
{
struct libusb_device *dev;
int perm_ok = 1;
@@ -704,7 +952,7 @@ static int usb_find_hubs()
if (rc == 0 && desc.bDeviceClass != LIBUSB_CLASS_HUB)
continue;
struct hub_info info;
bzero(&info, sizeof(info));
memset(&info, 0, sizeof(info));
rc = get_hub_info(dev, &info);
if (rc) {
perm_ok = 0; /* USB permission issue? */
@@ -730,7 +978,7 @@ static int usb_find_hubs()
(memcmp(info.port_numbers, dev_pn, info.pn_len) == 0))
{
struct descriptor_strings ds;
bzero(&ds, sizeof(ds));
memset(&ds, 0, sizeof(ds));
rc = get_device_description(udev, &ds);
if (rc != 0)
break;
@@ -742,6 +990,12 @@ static int usb_find_hubs()
}
}
}
if (strlen(opt_searchhub) > 0) {
/* Search by hub description */
if (strstr(info.ds.description, opt_searchhub) == NULL) {
info.actionable = 0;
}
}
if (strlen(opt_location) > 0) {
if (strcasecmp(opt_location, info.location)) {
info.actionable = 0;
@@ -840,7 +1094,7 @@ static int usb_find_hubs()
}
/* 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 (is_rpi_4b && l1 + s1 == l2 + s2 && l1 >= s2 && memcmp(p1 + s2, p2 + s1, l1 - s2)==0) {
if (best_score < 3) {
best_score = 3;
best_match = j;
@@ -878,7 +1132,7 @@ static int usb_find_hubs()
}
}
if (perm_ok == 0 && hub_phys_count == 0) {
#ifdef __gnu_linux__
#if defined(__linux__)
if (geteuid() != 0) {
fprintf(stderr,
"There were permission problems while accessing USB.\n"
@@ -897,10 +1151,13 @@ int main(int argc, char *argv[])
int rc;
int c = 0;
int option_index = 0;
#if defined(__linux__) && (LIBUSB_API_VERSION >= 0x01000107)
int sys_fd;
libusb_device_handle *sys_devh = NULL;
#endif
for (;;) {
c = getopt_long(argc, argv, "l:L:n:a:p:d:r:w:s:hvefR",
long_options, &option_index);
c = getopt_long(argc, argv, short_options, long_options, &option_index);
if (c == -1)
break; /* no more options left */
switch (c) {
@@ -914,16 +1171,19 @@ int main(int argc, char *argv[])
printf("\n");
break;
case 'l':
strncpy(opt_location, optarg, sizeof(opt_location));
snprintf(opt_location, sizeof(opt_location), "%s", optarg);
break;
case 'L':
opt_level = atoi(optarg);
break;
case 'n':
strncpy(opt_vendor, optarg, sizeof(opt_vendor));
snprintf(opt_vendor, sizeof(opt_vendor), "%s", optarg);
break;
case 's':
strncpy(opt_search, optarg, sizeof(opt_search));
snprintf(opt_search, sizeof(opt_search), "%s", optarg);
break;
case 'H':
snprintf(opt_searchhub, sizeof(opt_searchhub), "%s", optarg);
break;
case 'p':
if (!strcasecmp(optarg, "all")) { /* all ports is the default */
@@ -946,6 +1206,9 @@ int main(int argc, char *argv[])
if (!strcasecmp(optarg, "toggle") || !strcasecmp(optarg, "3")) {
opt_action = POWER_TOGGLE;
}
if (!strcasecmp(optarg, "flash") || !strcasecmp(optarg, "4")) {
opt_action = POWER_FLASH;
}
break;
case 'd':
opt_delay = atof(optarg);
@@ -956,6 +1219,19 @@ int main(int argc, char *argv[])
case 'f':
opt_force = 1;
break;
case 'N':
opt_nodesc = 1;
break;
#if defined(__linux__)
case 'S':
opt_nosysfs = 1;
break;
#if (LIBUSB_API_VERSION >= 0x01000107)
case 'y':
opt_sysdev = optarg;
break;
#endif
#endif
case 'e':
opt_exact = 1;
break;
@@ -997,7 +1273,35 @@ int main(int argc, char *argv[])
exit(1);
}
#if defined(__linux__) && (LIBUSB_API_VERSION >= 0x01000107)
if (opt_sysdev) {
sys_fd = open(opt_sysdev, O_RDWR);
if (sys_fd < 0) {
fprintf(stderr, "Cannot open system node!\n");
rc = 1;
goto cleanup;
}
rc = libusb_wrap_sys_device(NULL, sys_fd, &sys_devh);
if (rc != 0) {
fprintf(stderr,
"Cannot use %s as USB hub device, failed to wrap system node!\n",
opt_sysdev);
rc = 1;
goto cleanup;
}
usb_devs = calloc(2, sizeof *usb_devs);
if (!usb_devs) {
fprintf(stderr, "Out of memory\n");
rc = 1;
goto cleanup;
}
usb_devs[0] = libusb_get_device(sys_devh);
} else {
rc = libusb_get_device_list(NULL, &usb_devs);
}
#else
rc = libusb_get_device_list(NULL, &usb_devs);
#endif
if (rc < 0) {
fprintf(stderr,
"Cannot enumerate USB devices!\n"
@@ -1006,6 +1310,9 @@ int main(int argc, char *argv[])
goto cleanup;
}
is_rpi_4b = check_computer_model("Raspberry Pi 4 Model B");
is_rpi_5 = check_computer_model("Raspberry Pi 5");
rc = usb_find_hubs();
if (rc <= 0) {
fprintf(stderr,
@@ -1033,7 +1340,7 @@ 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 toggle requested, do it only once when `k == 0` */
if (k == 1 && opt_action == POWER_TOGGLE)
continue;
int i;
@@ -1052,45 +1359,32 @@ int main(int argc, char *argv[])
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 should_be_on = k;
if (opt_action == POWER_FLASH) {
should_be_on = !should_be_on;
}
int port;
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].super_speed ? USB_SS_PORT_STAT_POWER
: USB_PORT_STAT_POWER;
int powered_on = port_status & power_mask;
int is_on = (port_status & power_mask) != 0;
if (opt_action == POWER_TOGGLE) {
request = powered_on ? LIBUSB_REQUEST_CLEAR_FEATURE
: LIBUSB_REQUEST_SET_FEATURE;
should_be_on = !is_on;
}
if (k == 0 && !powered_on && opt_action != POWER_TOGGLE)
continue;
if (k == 1 && powered_on)
continue;
int repeat = powered_on ? opt_repeat : 1;
while (repeat-- > 0) {
rc = libusb_control_transfer(devh,
LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_OTHER,
request, USB_PORT_FEAT_POWER,
port, NULL, 0, USB_CTRL_GET_TIMEOUT
);
if (rc < 0) {
perror("Failed to control port power!\n");
}
if (repeat > 0) {
sleep_ms(opt_wait);
}
if (is_on != should_be_on) {
rc = set_port_status(devh, &hubs[i], port, should_be_on);
}
}
}
/* USB3 hubs need extra delay to actually turn off: */
if (k==0 && hubs[i].super_speed)
sleep_ms(150);
printf("Sent power %s request\n",
request == LIBUSB_REQUEST_CLEAR_FEATURE ? "off" : "on"
);
printf("Sent power %s request\n", should_be_on ? "on" : "off");
printf("New status for hub %s [%s]\n",
hubs[i].location, hubs[i].ds.description
);
@@ -1108,13 +1402,23 @@ int main(int argc, char *argv[])
}
libusb_close(devh);
}
if (k == 0 && opt_action == POWER_CYCLE)
if (k == 0 && (opt_action == POWER_CYCLE || opt_action == POWER_FLASH))
sleep_ms((int)(opt_delay * 1000));
}
rc = 0;
cleanup:
#if defined(__linux__) && (LIBUSB_API_VERSION >= 0x01000107)
if (opt_sysdev && sys_fd >= 0) {
if (sys_devh)
libusb_close(sys_devh);
close(sys_fd);
free(usb_devs);
} else if (usb_devs)
libusb_free_device_list(usb_devs, 1);
#else
if (usb_devs)
libusb_free_device_list(usb_devs, 1);
#endif
usb_devs = NULL;
libusb_exit(NULL);
return rc;

View File

@@ -1,20 +0,0 @@
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=a79e6a142b69522fe7757fe7313895eb"
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}/*"