mirror of
https://github.com/mvp/uhubctl.git
synced 2026-05-17 08:26:29 +03:00
Compare commits
25 Commits
v2.6.0
...
quiet-opti
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3e707e4a6e | ||
|
|
fbe812bd83 | ||
|
|
d96308569c | ||
|
|
d85f100c98 | ||
|
|
65f6d48cd1 | ||
|
|
14852054e2 | ||
|
|
d00a167ab4 | ||
|
|
16001b090d | ||
|
|
42cdfb0b82 | ||
|
|
c74755be42 | ||
|
|
07abf4cbea | ||
|
|
d483981f95 | ||
|
|
2ca14eb3dd | ||
|
|
40f10578cb | ||
|
|
7604f84c95 | ||
|
|
f4b0d2ad31 | ||
|
|
a7df8f8367 | ||
|
|
a957b21815 | ||
|
|
2dbc6ce2d9 | ||
|
|
302b3c0c77 | ||
|
|
4a233824c4 | ||
|
|
e0d1c34eed | ||
|
|
153c5da267 | ||
|
|
bfc11e6b05 | ||
|
|
c9abed425c |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -10,3 +10,6 @@ uhubctl
|
||||
# Patches
|
||||
*.patch
|
||||
*.diff
|
||||
|
||||
# IDE config dirs
|
||||
.*/
|
||||
|
||||
@@ -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.5.0.tar.gz"
|
||||
sha256 "d4452252f7862f7a45dd9c62f2ea7cd3a57ab5f5ab0e54a857d4c695699bbba3"
|
||||
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
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
uhubctl – USB hub per-port power control.
|
||||
|
||||
Copyright (c) 2009-2024, 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
|
||||
|
||||
9
Makefile
9
Makefile
@@ -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)
|
||||
|
||||
|
||||
125
README.md
125
README.md
@@ -11,7 +11,7 @@ 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:
|
||||
@@ -22,10 +22,11 @@ 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, HUC9003V1EBL | 7 | 3.1 |`2109:2817`| 2018 | |
|
||||
| AmazonBasics | HU9002V1SBL, HU9002V1EBL, HU9002V1ESL ([note](https://bit.ly/3awM2Ei)) | 10 | 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 | 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 | |
|
||||
@@ -39,13 +40,12 @@ This is list of known compatible USB hubs:
|
||||
| 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 | |
|
||||
| Buffalo | BSH4A05U3BK | 4 | 3.0 |`05E3:0610`| 2015 | |
|
||||
| Bytecc | BT-UH340 ([warning](https://bit.ly/35BNi5U)) | 4 | 3.0 |`2109:8110`| 2010 | |
|
||||
| Bytecc | BT-UH340 ([warning](https://tinyurl.com/BT-UH340-1)) | 4 | 3.0 |`2109:8110`| 2010 | |
|
||||
| Centech | CT-USB4HUB ReTRY HUB | 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 | |
|
||||
| Coolgear | USBG-12U2ML | 12 | 2.0 |`05e3:0607`| 2015 | |
|
||||
| Cypress | CY4608 HX2VL devkit ([note](https://bit.ly/3sMPfpu)) | 4 | 2.0 |`04B4:6570`| 2012 | |
|
||||
| Cypress | CY4608 HX2VL ([note](https://tinyurl.com/CY4608-1)) | 4 | 2.0 |`04B4:6570`| 2012 | |
|
||||
| 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). Rev B,C,F,G not supported | 7 | 2.0 |`05E3:0608`| 2012 | |
|
||||
@@ -58,16 +58,16 @@ This is list of known compatible USB hubs:
|
||||
| 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 |
|
||||
| Gigabyte | G27Q monitor ([see](http://tinyurl.com/G27Q551) | 4 | 3.0 |`2109:0817`| 2020 | |
|
||||
| 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 | |
|
||||
| Inateck | HB2025A ([USB2 only](https://bit.ly/3wXF5UO)) | 4 | 3.1 |`2109:2822`| 2021 | |
|
||||
| Inateck | HB2025A ([USB2 only](https://tinyurl.com/HB2025A-1)) | 4 | 3.1 |`2109:2822`| 2021 | |
|
||||
| IOI | U3H415E1 | 4 | 3.0 | | 2012 | |
|
||||
| j5create | JUH377 ([note](https://bit.ly/3Mx9eQI)) | 7 | 3.0 | | 2016 | |
|
||||
| j5create | JUH470 ([note](https://bit.ly/3CRWamP)) | 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 | |
|
||||
@@ -94,7 +94,7 @@ This is list of known compatible USB hubs:
|
||||
| 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://bit.ly/3PN2DDp)) | 4 | 3.0 | | 2020 | |
|
||||
| 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 | |
|
||||
@@ -108,17 +108,19 @@ This is list of known compatible USB hubs:
|
||||
| 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-518C ([note](https://bit.ly/3kYZUsA)) | 7 | 3.0 |`2109:0817`| 2021 | |
|
||||
| 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://bit.ly/3OToUOL)) | 13 | 3.1 |`2109:2822`| 2021 | |
|
||||
| Rosonway | RSH-A16 ([note](https://git.io/JTawg), [warning](https://bit.ly/39B0tGS)) | 16 | 3.0 |`0bda:0411`| 2020 | |
|
||||
| Rosonway | RSH-A104 ([USB2 only](https://bit.ly/3A0qiKF)) | 4 | 3.1 |`2109:2822`| 2022 | |
|
||||
| Rosonway | RSH-A107 (aka ikuai A107-5) | 7 | 3.1 |`0bda:5411`| 2022 | |
|
||||
| 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 | |
|
||||
| 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 | |
|
||||
| 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 | 4 | 2.0 | | 2007 | |
|
||||
| Targus | PA095UZ | 2 | 2.0 | | 2004 | |
|
||||
@@ -150,27 +152,47 @@ 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`
|
||||
* Ubuntu/Debian/Raspbian: `sudo apt install uhubctl`
|
||||
* Redhat/EPEL/Fedora/CentOS: `sudo yum install uhubctl`
|
||||
* OpenSUSE: `sudo zypper install uhubctl`
|
||||
|
||||
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:
|
||||
@@ -186,17 +208,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
|
||||
=====
|
||||
|
||||
@@ -244,7 +255,7 @@ Then, add udev rules like below to file `/etc/udev/rules.d/52-usb.rules`
|
||||
SUBSYSTEM=="usb", DRIVER=="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=="usb", \
|
||||
RUN="/bin/sh -c \"chmod -f 666 $sys$devpath/*-port*/disable || true\""
|
||||
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.
|
||||
@@ -258,8 +269,8 @@ If you don't like wide open mode `0666`, you can restrict access by group like t
|
||||
SUBSYSTEM=="usb", DRIVER=="usb", MODE="0664", GROUP="dialout"
|
||||
# Linux 6.0 or later (its ok to have this block present for older Linux kernels):
|
||||
SUBSYSTEM=="usb", DRIVER=="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\""
|
||||
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:
|
||||
|
||||
@@ -328,7 +339,7 @@ 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](https://bit.ly/2JzczjZ) and is [fixed](https://github.com/mvp/uhubctl/pull/450)
|
||||
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 this workaround for this issue:
|
||||
@@ -344,7 +355,7 @@ 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.
|
||||
|
||||
This is Linux kernel [issue](https://bit.ly/2JzczjZ) and is [fixed](https://github.com/mvp/uhubctl/pull/450)
|
||||
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:
|
||||
@@ -405,7 +416,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.
|
||||
@@ -423,7 +434,8 @@ to make power switching work on RPi 4B.
|
||||
##### 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.
|
||||
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`.
|
||||
@@ -437,15 +449,15 @@ 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 1 -a 0
|
||||
uhubctl -l 3 -a 0
|
||||
uhubctl -l 2 -a 0
|
||||
uhubctl -l 4 -a 0
|
||||
```
|
||||
|
||||
To turn it back on:
|
||||
|
||||
```
|
||||
uhubctl -l 1 -a 1
|
||||
uhubctl -l 3 -a 1
|
||||
uhubctl -l 2 -a 1
|
||||
uhubctl -l 4 -a 1
|
||||
```
|
||||
|
||||
Note that VBUS power goes down only if all ports are off -
|
||||
@@ -461,29 +473,30 @@ 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://bit.ly/2VtWrVt) | Reset cellular modems when necessary |
|
||||
| [Smog Sensor](https://bit.ly/2EMwgCk) | Raspberry Pi based smog sensor power reset |
|
||||
| [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://bit.ly/39MeVFQ) | Turn off unused USB ports to improve audio quality |
|
||||
| [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://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 |
|
||||
| [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://bit.ly/3witNsa) | Automatically light up a sign when webcam is in use |
|
||||
| [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-2024 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.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# uhubctl – USB hub per-port power control https://github.com/mvp/uhubctl
|
||||
#
|
||||
# Copyright (c) 2009-2024, 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.
|
||||
@@ -20,5 +20,5 @@ SUBSYSTEM=="usb", DRIVER=="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=="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\""
|
||||
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\""
|
||||
|
||||
280
uhubctl.c
280
uhubctl.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2024 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,8 +9,9 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#define _XOPEN_SOURCE 500
|
||||
#define _XOPEN_SOURCE 600
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -18,6 +19,7 @@
|
||||
#include <getopt.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <windows.h>
|
||||
@@ -29,27 +31,27 @@
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__) || 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
|
||||
|
||||
#if defined(__gnu_linux__) || defined(__linux__)
|
||||
#include <fcntl.h> /* for open() / O_WRONLY */
|
||||
#endif
|
||||
|
||||
/* cross-platform sleep function */
|
||||
|
||||
@@ -216,26 +218,37 @@ 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 */
|
||||
static int opt_action = POWER_KEEP;
|
||||
static double opt_delay = 2;
|
||||
static int opt_quiet = 0; /* quiet operation if set */
|
||||
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 int opt_nodesc = 0; /* skip querying device description */
|
||||
#if defined(__gnu_linux__) || defined(__linux__)
|
||||
#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:hvefRN"
|
||||
#if defined(__gnu_linux__) || defined(__linux__)
|
||||
"l:L:n:a:p:d:r:w:s:H:qhvefRN"
|
||||
#if defined(__linux__)
|
||||
"S"
|
||||
#if (LIBUSB_API_VERSION >= 0x01000107) /* 1.0.23 */
|
||||
"y:"
|
||||
#endif
|
||||
#endif
|
||||
;
|
||||
|
||||
@@ -243,6 +256,7 @@ 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' },
|
||||
@@ -251,9 +265,13 @@ static const struct option long_options[] = {
|
||||
{ "wait", required_argument, NULL, 'w' },
|
||||
{ "exact", no_argument, NULL, 'e' },
|
||||
{ "force", no_argument, NULL, 'f' },
|
||||
{ "quiet", no_argument, NULL, 'q' },
|
||||
{ "nodesc", no_argument, NULL, 'N' },
|
||||
#if defined(__gnu_linux__) || defined(__linux__)
|
||||
#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' },
|
||||
@@ -275,14 +293,19 @@ static int print_usage(void)
|
||||
"--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/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"
|
||||
"--quiet, -q - quiet operation, most output is suppressed.\n"
|
||||
"--nodesc, -N - do not query device description (helpful for unresponsive devices).\n"
|
||||
#if defined(__gnu_linux__) || defined(__linux__)
|
||||
#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"
|
||||
@@ -366,6 +389,60 @@ static int ports2bitmap(char* const portlist)
|
||||
}
|
||||
|
||||
|
||||
/* Standard printf but gated by quiet option */
|
||||
|
||||
int qprintf(const char *format, ...) {
|
||||
int count = 0;
|
||||
if (!opt_quiet) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
count = vprintf(format, args);
|
||||
va_end(args);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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) {
|
||||
return fd;
|
||||
}
|
||||
int bytes_read = read(fd, model, len-1);
|
||||
close(fd);
|
||||
if (bytes_read < 0) {
|
||||
return -1;
|
||||
}
|
||||
model[bytes_read] = 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()
|
||||
*/
|
||||
@@ -373,7 +450,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
|
||||
@@ -444,7 +521,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) {
|
||||
@@ -470,7 +547,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 &&
|
||||
@@ -487,32 +565,28 @@ 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 */
|
||||
|
||||
/* TODO: make this hack more reliable by querying Raspberry Pi model */
|
||||
|
||||
if (strlen(info->container_id)==0 &&
|
||||
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 &&
|
||||
(info->bus==1 || info->bus==3))
|
||||
!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 &&
|
||||
(info->bus==2 || info->bus==4))
|
||||
info->super_speed)
|
||||
{
|
||||
strcpy(info->container_id, "Raspberry Pi 5 Fake Container Id");
|
||||
}
|
||||
@@ -555,7 +629,7 @@ static int get_port_status(struct libusb_device_handle *devh, int port)
|
||||
}
|
||||
|
||||
|
||||
#if defined(__gnu_linux__) || defined(__linux__)
|
||||
#if defined(__linux__)
|
||||
/*
|
||||
* Try to use the Linux sysfs interface to power a port off/on.
|
||||
* Returns 0 on success.
|
||||
@@ -655,7 +729,7 @@ static int set_port_status_libusb(struct libusb_device_handle *devh, int port, i
|
||||
|
||||
static int set_port_status(struct libusb_device_handle *devh, struct hub_info *hub, int port, int on)
|
||||
{
|
||||
#if defined(__gnu_linux__) || defined(__linux__)
|
||||
#if defined(__linux__)
|
||||
if (!opt_nosysfs) {
|
||||
if (set_port_status_linux(devh, hub, port, on) == 0) {
|
||||
return 0;
|
||||
@@ -695,7 +769,7 @@ 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));
|
||||
memset(ds, 0, sizeof(*ds));
|
||||
id_vendor = desc.idVendor;
|
||||
id_product = desc.idProduct;
|
||||
rc = libusb_open(dev, &devh);
|
||||
@@ -719,7 +793,7 @@ static int get_device_description(struct libusb_device * dev, struct descriptor_
|
||||
}
|
||||
if (desc.bDeviceClass == LIBUSB_CLASS_HUB) {
|
||||
struct hub_info info;
|
||||
bzero(&info, sizeof(info));
|
||||
memset(&info, 0, sizeof(info));
|
||||
rc = get_hub_info(dev, &info);
|
||||
if (rc == 0) {
|
||||
const char * lpsm_type;
|
||||
@@ -774,10 +848,10 @@ static int print_port_status(struct hub_info * hub, int portmask)
|
||||
break;
|
||||
}
|
||||
|
||||
printf(" Port %d: %04x", port, port_status);
|
||||
qprintf(" 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) {
|
||||
@@ -800,48 +874,48 @@ static int print_port_status(struct hub_info * hub, int portmask)
|
||||
|
||||
if (!hub->super_speed) {
|
||||
if (port_status == 0) {
|
||||
printf(" off");
|
||||
qprintf(" off");
|
||||
} else {
|
||||
if (port_status & USB_PORT_STAT_POWER) printf(" power");
|
||||
if (port_status & USB_PORT_STAT_INDICATOR) printf(" indicator");
|
||||
if (port_status & USB_PORT_STAT_TEST) printf(" test");
|
||||
if (port_status & USB_PORT_STAT_HIGH_SPEED) printf(" highspeed");
|
||||
if (port_status & USB_PORT_STAT_LOW_SPEED) printf(" lowspeed");
|
||||
if (port_status & USB_PORT_STAT_SUSPEND) printf(" suspend");
|
||||
if (port_status & USB_PORT_STAT_POWER) qprintf(" power");
|
||||
if (port_status & USB_PORT_STAT_INDICATOR) qprintf(" indicator");
|
||||
if (port_status & USB_PORT_STAT_TEST) qprintf(" test");
|
||||
if (port_status & USB_PORT_STAT_HIGH_SPEED) qprintf(" highspeed");
|
||||
if (port_status & USB_PORT_STAT_LOW_SPEED) qprintf(" lowspeed");
|
||||
if (port_status & USB_PORT_STAT_SUSPEND) qprintf(" suspend");
|
||||
}
|
||||
} else {
|
||||
if (!(port_status & USB_SS_PORT_STAT_POWER)) {
|
||||
printf(" off");
|
||||
} else {
|
||||
int link_state = port_status & USB_PORT_STAT_LINK_STATE;
|
||||
if (port_status & USB_SS_PORT_STAT_POWER) printf(" power");
|
||||
if (port_status & USB_SS_PORT_STAT_POWER) qprintf(" power");
|
||||
if ((port_status & USB_SS_PORT_STAT_SPEED)
|
||||
== USB_PORT_STAT_SPEED_5GBPS)
|
||||
{
|
||||
printf(" 5gbps");
|
||||
}
|
||||
if (link_state == USB_SS_PORT_LS_U0) printf(" U0");
|
||||
if (link_state == USB_SS_PORT_LS_U1) printf(" U1");
|
||||
if (link_state == USB_SS_PORT_LS_U2) printf(" U2");
|
||||
if (link_state == USB_SS_PORT_LS_U3) printf(" U3");
|
||||
if (link_state == USB_SS_PORT_LS_SS_DISABLED) printf(" SS.Disabled");
|
||||
if (link_state == USB_SS_PORT_LS_RX_DETECT) printf(" Rx.Detect");
|
||||
if (link_state == USB_SS_PORT_LS_SS_INACTIVE) printf(" SS.Inactive");
|
||||
if (link_state == USB_SS_PORT_LS_POLLING) printf(" Polling");
|
||||
if (link_state == USB_SS_PORT_LS_RECOVERY) printf(" Recovery");
|
||||
if (link_state == USB_SS_PORT_LS_HOT_RESET) printf(" HotReset");
|
||||
if (link_state == USB_SS_PORT_LS_COMP_MOD) printf(" Compliance");
|
||||
if (link_state == USB_SS_PORT_LS_LOOPBACK) printf(" Loopback");
|
||||
if (link_state == USB_SS_PORT_LS_U0) qprintf(" U0");
|
||||
if (link_state == USB_SS_PORT_LS_U1) qprintf(" U1");
|
||||
if (link_state == USB_SS_PORT_LS_U2) qprintf(" U2");
|
||||
if (link_state == USB_SS_PORT_LS_U3) qprintf(" U3");
|
||||
if (link_state == USB_SS_PORT_LS_SS_DISABLED) qprintf(" SS.Disabled");
|
||||
if (link_state == USB_SS_PORT_LS_RX_DETECT) qprintf(" Rx.Detect");
|
||||
if (link_state == USB_SS_PORT_LS_SS_INACTIVE) qprintf(" SS.Inactive");
|
||||
if (link_state == USB_SS_PORT_LS_POLLING) qprintf(" Polling");
|
||||
if (link_state == USB_SS_PORT_LS_RECOVERY) qprintf(" Recovery");
|
||||
if (link_state == USB_SS_PORT_LS_HOT_RESET) qprintf(" HotReset");
|
||||
if (link_state == USB_SS_PORT_LS_COMP_MOD) qprintf(" Compliance");
|
||||
if (link_state == USB_SS_PORT_LS_LOOPBACK) qprintf(" Loopback");
|
||||
}
|
||||
}
|
||||
if (port_status & USB_PORT_STAT_RESET) printf(" reset");
|
||||
if (port_status & USB_PORT_STAT_OVERCURRENT) printf(" oc");
|
||||
if (port_status & USB_PORT_STAT_ENABLE) printf(" enable");
|
||||
if (port_status & USB_PORT_STAT_CONNECTION) printf(" connect");
|
||||
if (port_status & USB_PORT_STAT_RESET) qprintf(" reset");
|
||||
if (port_status & USB_PORT_STAT_OVERCURRENT) qprintf(" oc");
|
||||
if (port_status & USB_PORT_STAT_ENABLE) qprintf(" enable");
|
||||
if (port_status & USB_PORT_STAT_CONNECTION) qprintf(" connect");
|
||||
|
||||
if (port_status & USB_PORT_STAT_CONNECTION) printf(" [%s]", ds.description);
|
||||
if (port_status & USB_PORT_STAT_CONNECTION) qprintf(" [%s]", ds.description);
|
||||
|
||||
printf("\n");
|
||||
qprintf("\n");
|
||||
}
|
||||
libusb_close(devh);
|
||||
}
|
||||
@@ -872,7 +946,7 @@ static int usb_find_hubs(void)
|
||||
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? */
|
||||
@@ -898,7 +972,7 @@ static int usb_find_hubs(void)
|
||||
(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;
|
||||
@@ -910,6 +984,12 @@ static int usb_find_hubs(void)
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
@@ -1008,7 +1088,7 @@ static int usb_find_hubs(void)
|
||||
}
|
||||
|
||||
/* 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;
|
||||
@@ -1046,7 +1126,7 @@ static int usb_find_hubs(void)
|
||||
}
|
||||
}
|
||||
if (perm_ok == 0 && hub_phys_count == 0) {
|
||||
#if defined(__gnu_linux__) || defined(__linux__)
|
||||
#if defined(__linux__)
|
||||
if (geteuid() != 0) {
|
||||
fprintf(stderr,
|
||||
"There were permission problems while accessing USB.\n"
|
||||
@@ -1065,6 +1145,10 @@ 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, short_options, long_options, &option_index);
|
||||
@@ -1092,6 +1176,9 @@ int main(int argc, char *argv[])
|
||||
case 's':
|
||||
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 */
|
||||
break;
|
||||
@@ -1129,10 +1216,15 @@ int main(int argc, char *argv[])
|
||||
case 'N':
|
||||
opt_nodesc = 1;
|
||||
break;
|
||||
#if defined(__gnu_linux__) || defined(__linux__)
|
||||
#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;
|
||||
@@ -1140,6 +1232,9 @@ int main(int argc, char *argv[])
|
||||
case 'R':
|
||||
opt_reset = 1;
|
||||
break;
|
||||
case 'q':
|
||||
opt_quiet = 1;
|
||||
break;
|
||||
case 'w':
|
||||
opt_wait = atoi(optarg);
|
||||
break;
|
||||
@@ -1175,7 +1270,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"
|
||||
@@ -1184,6 +1307,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,
|
||||
@@ -1218,7 +1344,7 @@ int main(int argc, char *argv[])
|
||||
for (i=0; i<hub_count; i++) {
|
||||
if (hubs[i].actionable == 0)
|
||||
continue;
|
||||
printf("Current status for hub %s [%s]\n",
|
||||
qprintf("Current status for hub %s [%s]\n",
|
||||
hubs[i].location, hubs[i].ds.description
|
||||
);
|
||||
print_port_status(&hubs[i], opt_ports);
|
||||
@@ -1255,19 +1381,19 @@ int main(int argc, char *argv[])
|
||||
/* 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", should_be_on ? "on" : "off");
|
||||
printf("New status for hub %s [%s]\n",
|
||||
qprintf("Sent power %s request\n", should_be_on ? "on" : "off");
|
||||
qprintf("New status for hub %s [%s]\n",
|
||||
hubs[i].location, hubs[i].ds.description
|
||||
);
|
||||
print_port_status(&hubs[i], opt_ports);
|
||||
|
||||
if (k == 1 && opt_reset == 1) {
|
||||
printf("Resetting hub...\n");
|
||||
qprintf("Resetting hub...\n");
|
||||
rc = libusb_reset_device(devh);
|
||||
if (rc < 0) {
|
||||
perror("Reset failed!\n");
|
||||
} else {
|
||||
printf("Reset successful!\n");
|
||||
qprintf("Reset successful!\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1278,8 +1404,18 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
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;
|
||||
|
||||
@@ -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}/*"
|
||||
Reference in New Issue
Block a user