Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package uhubctl for openSUSE:Factory checked in at 2022-11-06 12:43:02 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/uhubctl (Old) and /work/SRC/openSUSE:Factory/.uhubctl.new.2275 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "uhubctl" Sun Nov 6 12:43:02 2022 rev:6 rq:1033836 version:2.5.0 Changes: -------- --- /work/SRC/openSUSE:Factory/uhubctl/uhubctl.changes 2021-02-15 23:21:24.891854145 +0100 +++ /work/SRC/openSUSE:Factory/.uhubctl.new.2275/uhubctl.changes 2022-11-06 12:43:28.541838038 +0100 @@ -1,0 +2,14 @@ +Fri Nov 4 19:17:59 UTC 2022 - Martin Hauke <mar...@gmx.de> + +- Update to version 2.5.0 + * Added support for Linux sysfs based power switching provided + in Linux kernel 6.0+ - it allows to solve reliability issues + when turning power off on Linux. + * Added option --nodesc to skip querying device string + descriptors (necessary for some buggy devices which otherwise + would completely freeze). + * New simpler way to configure udev rules on Linux (one rule + works for any USB hub). + * Even more supported devices. + +------------------------------------------------------------------- Old: ---- uhubctl-2.4.0.tar.gz New: ---- uhubctl-2.5.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ uhubctl.spec ++++++ --- /var/tmp/diff_new_pack.VyTRjp/_old 2022-11-06 12:43:28.981840666 +0100 +++ /var/tmp/diff_new_pack.VyTRjp/_new 2022-11-06 12:43:28.989840713 +0100 @@ -1,7 +1,7 @@ # # spec file for package uhubctl # -# Copyright (c) 2021 SUSE LLC +# Copyright (c) 2022 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -17,7 +17,7 @@ Name: uhubctl -Version: 2.4.0 +Version: 2.5.0 Release: 0 Summary: USB hub per-port power control License: GPL-2.0-only ++++++ uhubctl-2.4.0.tar.gz -> uhubctl-2.5.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/uhubctl-2.4.0/.gitignore new/uhubctl-2.5.0/.gitignore --- old/uhubctl-2.4.0/.gitignore 2021-02-14 01:21:16.000000000 +0100 +++ new/uhubctl-2.5.0/.gitignore 2022-11-02 04:26:08.000000000 +0100 @@ -6,3 +6,7 @@ # Mac symbols *.dSYM + +# Patches +*.patch +*.diff diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/uhubctl-2.4.0/Formula/uhubctl.rb new/uhubctl-2.5.0/Formula/uhubctl.rb --- old/uhubctl-2.4.0/Formula/uhubctl.rb 2021-02-14 01:21:16.000000000 +0100 +++ new/uhubctl-2.5.0/Formula/uhubctl.rb 2022-11-02 04:26:08.000000000 +0100 @@ -2,8 +2,8 @@ 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" + url "https://github.com/mvp/uhubctl/archive/v2.4.0.tar.gz" + sha256 "391f24fd1f89cacce801df38ecc289b34c3627bc08ee69eec515af7e1a283d97" license "GPL-2.0" depends_on "libusb" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/uhubctl-2.4.0/LICENSE new/uhubctl-2.5.0/LICENSE --- old/uhubctl-2.4.0/LICENSE 2021-02-14 01:21:16.000000000 +0100 +++ new/uhubctl-2.5.0/LICENSE 2022-11-02 04:26:08.000000000 +0100 @@ -1,6 +1,6 @@ uhubctl ??? USB hub per-port power control. -Copyright (c) 2009-2020, Vadim Mikhailov +Copyright (c) 2009-2022, 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 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/uhubctl-2.4.0/README.md new/uhubctl-2.5.0/README.md --- old/uhubctl-2.4.0/README.md 2021-02-14 01:21:16.000000000 +0100 +++ new/uhubctl-2.5.0/README.md 2022-11-02 04:26:08.000000000 +0100 @@ -20,10 +20,13 @@ |:-------------------|:-----------------------------------------------------|:------|:----|:----------|:--------|:-----| | 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://bit.ly/3awM2Ei)) | 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-A83650A1 ([note](https://git.io/JzpPb)) | 4 | 3.1 |`2109:0817`| 2020 | | | Anker | AK-68ANHUB-BV7A-0004 ([note](https://git.io/JLnZb)) | 7 | 3.0 |`2109:0812`| 2014 | | +| 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 | Z87-PLUS Motherboard (onboard USB hub) | 4 | 3.0 | | 2013 | 2016 | @@ -32,55 +35,78 @@ | B+B SmartWorx | USH304 | 4 | 3.0 |`04B4:6506`| 2017 | | | Basler | 2000036234 | 4 | 3.0 |`0451:8046`| 2016 | | | Belkin | F5U101 | 4 | 2.0 |`0451:2046`| 2005 | 2010 | +| BenQ | PD2700U 4K Monitor (works only in USB2 mode) | 4 | 3.1 |`05E3:0610`| 2018 | | +| BenQ | PD3220U | 4 | 3.1 |`05E3:0610`| 2019 | | | Buffalo | BSH4A05U3BK | 4 | 3.0 |`05E3:0610`| 2015 | | -| Bytecc | BT-UH340 | 4 | 3.0 |`2109:8110`| 2010 | | +| Bytecc | BT-UH340 ([warning](https://bit.ly/35BNi5U)) | 4 | 3.0 |`2109:8110`| 2010 | | +| Centech | CT-USB4HUB ReTRY HUB | 4 | 3.1 |`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 | +| Cypress | CY4608 HX2VL devkit ([note](https://bit.ly/3sMPfpu)) | 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). 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 | 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 | | +| Delock | 62537 | 4 | 3.1 | | 2020 | | +| 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 | | | 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 | | | IOI | U3H415E1 | 4 | 3.0 | | 2012 | | -| j5create | JUH470 (works only in USB2 mode) | 3 | 3.0 |`05E3:0610`| 2014 | | +| j5create | JUH377 ([note](https://bit.ly/3Mx9eQI)) | 7 | 3.0 | | 2016 | | +| j5create | JUH470 ([note](https://bit.ly/3CRWamP)) | 3 | 3.0 |`05E3:0610`| 2014 | | | Juiced Systems | 6HUB-01 | 7 | 3.0 |`0BDA:0411`| 2014 | 2018 | +| LG Electronics | 27MD5KL-B monitor | 4 | 3.1 |`043E:9A60`| 2019 | | +| LG Electronics | 27UK850-W monitor | 2 | 3.0 | | 2018 | | | LG Electronics | 38WK95C-W monitor | 4 | 3.0 |`0451:8142`| 2018 | | | Lenovo | ThinkPad Ultra Docking Station (40A20090EU) | 6 | 2.0 |`17EF:100F`| 2015 | | | Lenovo | ThinkPad Ultra Docking Station (40AJ0135EU) | 7 | 3.1 |`17EF:3070`| 2018 | | | Lenovo | ThinkPad X200 Ultrabase 42X4963 | 3 | 2.0 |`17EF:1005`| 2008 | 2011 | | Lenovo | ThinkPad X6 Ultrabase 42W3107 | 4 | 2.0 |`17EF:1000`| 2006 | 2009 | +| Lenovo | ThinkPlus 4-in-1 USB-C hub 4X90W86497 | 3 | 3.0 | | 2021 | | +| Lenovo | ThinkVision T24i-10 Monitor | 4 | 2.0 |`17EF:0610`| 2018 | | | Lindy | USB serial converter 4 port | 4 | 1.1 |`058F:9254`| 2008 | | -| Linksys | USB2HUB4 | 4 | 2.0 | | 2004 | 2010 | +| Linksys | USB2HUB4 ([note](https://git.io/JYiDZ)) | 4 | 2.0 | | 2004 | 2010 | | Maplin | A08CQ | 7 | 2.0 |`0409:0059`| 2008 | 2011 | | Microchip | EVB-USB2517 | 7 | 2.0 | | 2008 | | | Microchip | EVB-USB2534BC | 4 | 2.0 | | 2013 | | +| Microchip | EVB-USB5807 | 7 | 3.1 | | 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 | | | Phidgets | HUB0003_0 | 7 | 2.0 |`1A40:0201`| 2017 | | | Plugable | USB3-HUB7BC | 7 | 3.0 |`2109:0813`| 2015 | | -| Plugable | USB3-HUB7C | 7 | 3.0 |`2109:0813`| 2015 | | -| Plugable | USB3-HUB7-81X | 7 | 3.0 |`2109:0813`| 2012 | | +| Plugable | 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-HUB7-81X (only works for 2 charge ports) | 7 | 3.0 |`2109:0813`| 2012 | | +| 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 | | | 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://bit.ly/3kYZUsA)) | 7 | 3.0 |`2109:0817`| 2021 | | +| 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 | | | 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 | | | Sunix | SHB4200MA | 4 | 2.0 |`0409:0058`| 2006 | 2009 | | 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.1 |`2109:0817`| 2021 | | This table is by no means complete. If your hub works with `uhubctl`, but is not listed above, please report it @@ -112,6 +138,9 @@ 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. +> :warning: MacOS 12.4 x86 has [USB stack bug](https://github.com/libusb/libusb/issues/1156) which breaks `uhubctl` operation. +Solution is to upgrade to MacOS 12.5 which has this bug fixed. + 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). @@ -186,20 +215,35 @@ ===================== On Linux, you should configure `udev` USB permissions (otherwise you will have to run it as root using `sudo uhubctl`). + +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 one or more udev rules like below to file `/etc/udev/rules.d/52-usb.rules` (replace 2001 with your vendor id): +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", ATTR{idVendor}=="2001", MODE="0666" + SUBSYSTEM=="usb", DRIVER=="hub", 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", \ + 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", MODE="0666", ATTR{idVendor}=="2109" + SUBSYSTEM=="usb", DRIVER=="hub", 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", MODE="0664", GROUP="dialout" + # Linux 6.0 or later (its ok to have this block present for older Linux kernels): + SUBSYSTEM=="usb", DRIVER=="hub", \ + 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 +253,7 @@ 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 @@ -264,6 +309,8 @@ #### _USB devices are not removed after port power down on Linux_ +> :arrow_right: This is fixed by [#450](https://github.com/mvp/uhubctl/pull/450) if you are using Linux kernel 6.0 or later. + 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. @@ -282,6 +329,8 @@ #### _Power comes back on after few seconds on Linux_ +> :arrow_right: This is fixed by [#450](https://github.com/mvp/uhubctl/pull/450) if you are using Linux kernel 6.0 or later. + 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 - @@ -314,6 +363,7 @@ 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: @@ -378,12 +428,16 @@ | [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 | +| [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 | +| [Do it yourself PPPS](https://git.io/J3lHs) | Solder wires in your USB hub to support uhubctl | +| [Python Wrapper for uhubctl](https://github.com/nbuchwitz/python3-uhubctl) | Module to use uhubctl with Python | Copyright ========= -Copyright (C) 2009-2020 Vadim Mikhailov +Copyright (C) 2009-2022 Vadim Mikhailov This file can be distributed under the terms and conditions of the GNU General Public License version 2. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/uhubctl-2.4.0/VERSION new/uhubctl-2.5.0/VERSION --- old/uhubctl-2.4.0/VERSION 2021-02-14 01:21:16.000000000 +0100 +++ new/uhubctl-2.5.0/VERSION 2022-11-02 04:26:08.000000000 +0100 @@ -1 +1 @@ -2.4.0 +2.5.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/uhubctl-2.4.0/udev/rules.d/52-usb.rules new/uhubctl-2.5.0/udev/rules.d/52-usb.rules --- old/uhubctl-2.4.0/udev/rules.d/52-usb.rules 1970-01-01 01:00:00.000000000 +0100 +++ new/uhubctl-2.5.0/udev/rules.d/52-usb.rules 2022-11-02 04:26:08.000000000 +0100 @@ -0,0 +1,24 @@ +# uhubctl ??? USB hub per-port power control https://github.com/mvp/uhubctl +# +# Copyright (c) 2009-2022, 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", 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", \ + 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\"" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/uhubctl-2.4.0/uhubctl.c new/uhubctl-2.5.0/uhubctl.c --- old/uhubctl-2.4.0/uhubctl.c 2021-02-14 01:21:16.000000000 +0100 +++ new/uhubctl-2.5.0/uhubctl.c 2022-11-02 04:26:08.000000000 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2020 Vadim Mikhailov + * Copyright (c) 2009-2022 Vadim Mikhailov * * Utility to turn USB port power on/off * for USB hubs that support per-port power switching. @@ -47,6 +47,10 @@ #include <time.h> /* for nanosleep */ #endif +#ifdef __gnu_linux__ +#include <fcntl.h> /* for open() / O_WRONLY */ +#endif + /* cross-platform sleep function */ void sleep_ms(int milliseconds) @@ -221,6 +225,18 @@ 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 */ +#ifdef __gnu_linux__ +static int opt_nosysfs = 0; /* don't use the Linux sysfs port disable interface, even if available */ +#endif + + +static const char short_options[] = + "l:L:n:a:p:d:r:w:s:hvefRN" +#ifdef __gnu_linux__ + "S" +#endif +; static const struct option long_options[] = { { "location", required_argument, NULL, 'l' }, @@ -234,6 +250,10 @@ { "wait", required_argument, NULL, 'w' }, { "exact", no_argument, NULL, 'e' }, { "force", no_argument, NULL, 'f' }, + { "nodesc", no_argument, NULL, 'N' }, +#ifdef __gnu_linux__ + { "nosysfs", no_argument, NULL, 'S' }, +#endif { "reset", no_argument, NULL, 'R' }, { "version", no_argument, NULL, 'v' }, { "help", no_argument, NULL, 'h' }, @@ -259,6 +279,10 @@ "--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" +#ifdef __gnu_linux__ + "--nosysfs, -S - do not use the Linux sysfs port disable interface.\n" +#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" @@ -504,6 +528,113 @@ } +#ifdef __gnu_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. + */ + 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) +{ +#ifdef __gnu_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); +} + + /* * Get USB device descriptor strings and summary description. * @@ -535,20 +666,22 @@ id_product = libusb_le16_to_cpu(desc.idProduct); rc = libusb_open(dev, &devh); if (rc == 0) { - if (desc.iManufacturer) { - libusb_get_string_descriptor_ascii(devh, - desc.iManufacturer, (unsigned char*)ds->vendor, sizeof(ds->vendor)); - rtrim(ds->vendor); - } - if (desc.iProduct) { - libusb_get_string_descriptor_ascii(devh, - desc.iProduct, (unsigned char*)ds->product, sizeof(ds->product)); - rtrim(ds->product); - } - if (desc.iSerialNumber) { - libusb_get_string_descriptor_ascii(devh, - desc.iSerialNumber, (unsigned char*)ds->serial, sizeof(ds->serial)); - rtrim(ds->serial); + if (!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; @@ -899,8 +1032,7 @@ int option_index = 0; 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 +1046,16 @@ 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 'p': if (!strcasecmp(optarg, "all")) { /* all ports is the default */ @@ -956,6 +1088,14 @@ case 'f': opt_force = 1; break; + case 'N': + opt_nodesc = 1; + break; +#ifdef __gnu_linux__ + case 'S': + opt_nosysfs = 1; + break; +#endif case 'e': opt_exact = 1; break; @@ -1033,7 +1173,7 @@ 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 +1192,29 @@ 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; + 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 );