Hello community, here is the log from the commit of package uhubctl for openSUSE:Factory checked in at 2020-12-14 18:09:41 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/uhubctl (Old) and /work/SRC/openSUSE:Factory/.uhubctl.new.2328 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "uhubctl" Mon Dec 14 18:09:41 2020 rev:4 rq:855672 version:2.3.0 Changes: -------- --- /work/SRC/openSUSE:Factory/uhubctl/uhubctl.changes 2020-05-26 17:20:46.760163001 +0200 +++ /work/SRC/openSUSE:Factory/.uhubctl.new.2328/uhubctl.changes 2020-12-14 18:10:14.283691957 +0100 @@ -1,0 +2,10 @@ +Mon Dec 14 06:52:33 UTC 2020 - Martin Hauke <[email protected]> + +- Update to version 2.3.0 + * Much improved USB3 support, in particular for RPi 4B and + M1 Macs + * Allow forced operation on unsupported hubs - not likely to + work though + * More supported devices + +------------------------------------------------------------------- Old: ---- uhubctl-2.2.0.tar.gz New: ---- uhubctl-2.3.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ uhubctl.spec ++++++ --- /var/tmp/diff_new_pack.YNZ5dY/_old 2020-12-14 18:10:15.019692688 +0100 +++ /var/tmp/diff_new_pack.YNZ5dY/_new 2020-12-14 18:10:15.019692688 +0100 @@ -17,7 +17,7 @@ Name: uhubctl -Version: 2.2.0 +Version: 2.3.0 Release: 0 Summary: USB hub per-port power control License: GPL-2.0-only ++++++ uhubctl-2.2.0.tar.gz -> uhubctl-2.3.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/uhubctl-2.2.0/Formula/uhubctl.rb new/uhubctl-2.3.0/Formula/uhubctl.rb --- old/uhubctl-2.2.0/Formula/uhubctl.rb 2020-05-22 22:39:47.000000000 +0200 +++ new/uhubctl-2.3.0/Formula/uhubctl.rb 2020-12-13 23:31:05.000000000 +0100 @@ -1,9 +1,17 @@ class Uhubctl < Formula - desc "control USB hubs powering per-port" + desc "USB hub per-port power control" homepage "https://github.com/mvp/uhubctl" head "https://github.com/mvp/uhubctl.git" + url "https://github.com/mvp/uhubctl/archive/v2.2.0.tar.gz" + sha256 "e5a722cb41967903bedbab4eea566ab332241a7f05fc7bc9c386b9a5e1762d8b" + license "GPL-2.0" depends_on "libusb" + depends_on "pkg-config" => :build + + livecheck do + url :stable + end def install system "make" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/uhubctl-2.2.0/Makefile new/uhubctl-2.3.0/Makefile --- old/uhubctl-2.2.0/Makefile 2020-05-22 22:39:47.000000000 +0200 +++ new/uhubctl-2.3.0/Makefile 2020-12-13 23:31:05.000000000 +0100 @@ -14,34 +14,26 @@ CC ?= gcc CFLAGS ?= -g -O0 CFLAGS += -Wall -Wextra -std=c99 -pedantic -GIT_VERSION := $(shell git describe --match "v[0-9]*" --abbrev=8 --dirty --always --tags | cut -c2-) +GIT_VERSION := $(shell git describe --match "v[0-9]*" --abbrev=8 --dirty --tags | cut -c2-) ifeq ($(GIT_VERSION),) - GIT_VERSION := $(shell cat VERSION) + GIT_VERSION := $(shell cat VERSION) endif CFLAGS += -DPROGRAM_VERSION=\"$(GIT_VERSION)\" +# Use hardening options on Linux ifeq ($(UNAME_S),Linux) - LDFLAGS += -Wl,-zrelro,-znow -lusb-1.0 + LDFLAGS += -Wl,-zrelro,-znow endif -ifeq ($(UNAME_S),Darwin) -ifneq ($(wildcard /opt/local/include),) - # MacPorts - CFLAGS += -I/opt/local/include - LDFLAGS += -L/opt/local/lib -endif +# Use pkg-config if available +ifneq (,$(shell which pkg-config)) + CFLAGS += $(shell pkg-config --cflags libusb-1.0) + LDFLAGS += $(shell pkg-config --libs libusb-1.0) +else +# But it should still build if pkg-config is not available (e.g. Linux or Mac homebrew) LDFLAGS += -lusb-1.0 endif -ifeq ($(UNAME_S),FreeBSD) - LDFLAGS += -lusb -endif - -ifeq ($(UNAME_S),NetBSD) - CFLAGS += $(shell pkg-config --cflags libusb-1.0) - LDFLAGS += $(shell pkg-config --libs libusb-1.0) -endif - PROGRAM = uhubctl $(PROGRAM): $(PROGRAM).c diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/uhubctl-2.2.0/README.md new/uhubctl-2.3.0/README.md --- old/uhubctl-2.2.0/README.md 2020-05-22 22:39:47.000000000 +0200 +++ new/uhubctl-2.3.0/README.md 2020-12-13 23:31:05.000000000 +0100 @@ -20,8 +20,10 @@ |:-------------------|:-----------------------------------------------------|:------|:----|:----------|:--------|:-----| | 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 | HU9002V1SBL ([RPi issue](https://goo.gl/CLt46M)) | 10 | 3.1 |`2109:2817`| 2018 | | -| AmazonBasics | HUC9002V1ESL ([RPi issue](https://goo.gl/CLt46M)) | 10 | 3.1 |`2109:2817`| 2018 | | +| AmazonBasics | HU9003V1EBL | 7 | 3.1 |`2109:2817`| 2018 | | +| AmazonBasics | HU9002V1SBL, HU9002V1ESL | 10 | 3.1 |`2109:2817`| 2018 | | +| AmazonBasics | HUC9002V1SBL, HUC9002V1EBL, HUC9002V1ESL | 10 | 3.1 |`2109:2817`| 2018 | | +| Anker | AK-68ANHUB-BV7A-0004 | 7 | 3.0 |`2109:0812`| 2014 | | | Apple | Thunderbolt Display 27" (internal USB hub) | 6 | 2.0 | | 2011 | 2016 | | Apple | USB Keyboard With Numeric Pad (internal USB hub) | 3 | 2.0 | | 2011 | | | Asus | Z87-PLUS Motherboard (onboard USB hub) | 4 | 3.0 | | 2013 | 2016 | @@ -29,7 +31,6 @@ | 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 | -| Belkin | F5U701-BLK | 7 | 2.0 | | 2008 | 2012 | | Buffalo | BSH4A05U3BK | 4 | 3.0 |`05E3:0610`| 2015 | | | Bytecc | BT-UH340 | 4 | 3.0 |`2109:8110`| 2010 | | | Circuitco | Beagleboard-xM (internal USB hub) | 4 | 2.0 |`0424:9514`| 2010 | | @@ -37,10 +38,11 @@ | 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) | 4 | 2.0 |`05E3:0605`| 2005 | 2010 | -| D-Link | DUB-H4 rev D,E (black). Note: rev A,C not supported | 4 | 2.0 |`05E3:0608`| 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 (black). Note: rev B,C not supported | 7 | 2.0 |`05E3:0608`| 2012 | | -| Dell | P2416D 24" QHD Monitor | 4 | 2.0 | | 2017 | | +| D-Link | DUB-H7 rev D,E (black). Note: rev B,C,F not supported| 7 | 2.0 |`05E3:0608`| 2012 | | +| Dell | P2416D 24" QHD Monitor ([note](https://git.io/JUAu8))| 4 | 2.0 | | 2017 | | +| Dell | S2719DGF 27" WQHD Gaming-Monitor | 5 | 3.1 |`0424:5734`| 2018 | | | Dell | UltraSharp 1704FPT 17" LCD Monitor | 4 | 2.0 |`0424:A700`| 2005 | 2015 | | Dell | UltraSharp U2415 24" LCD Monitor | 5 | 3.0 | | 2014 | | | Elecom | U2H-G4S | 4 | 2.0 | | 2006 | 2011 | @@ -58,7 +60,9 @@ | Linksys | USB2HUB4 | 4 | 2.0 | | 2004 | 2010 | | Maplin | A08CQ | 7 | 2.0 |`0409:0059`| 2008 | 2011 | | Microchip | EVB-USB2517 | 7 | 2.0 | | 2008 | | +| Microchip | EVB-USB2534BC | 4 | 2.0 | | 2013 | | | Moxa | Uport-407 | 7 | 2.0 |`110A:0407`| 2009 | | +| NVidia | Jetson Nano B01 ([details](https://git.io/JJaFR)) | 4 | 3.0 | | 2019 | | | Phidgets | HUB0003_0 | 7 | 2.0 |`1A40:0201`| 2017 | | | Plugable | USB3-HUB7BC | 7 | 3.0 |`2109:0813`| 2015 | | | Plugable | USB3-HUB7C | 7 | 3.0 |`2109:0813`| 2015 | | @@ -68,9 +72,9 @@ | Raspberry Pi | 4B ([see below](#raspberry-pi-4b)) | 4 | 3.0 |`2109:3431`| 2019 | | | Renesas | uPD720202 PCIe USB 3.0 host controller | 2 | 3.0 | | 2013 | | | Rosewill | RHUB-210 | 4 | 2.0 |`0409:005A`| 2011 | 2014 | +| Rosonway | RSH-A16 ([note](https://git.io/JTawg)) | 16 | 3.2 |`0bda:0411`| 2020 | | | Sanwa Supply | USB-HUB14GPH | 4 | 1.1 | | 2001 | 2003 | | Seagate | Backup Plus Hub STEL8000100 | 2 | 3.0 |`0BC2:AB44`| 2016 | | -| StarTech | ST4200USBM | 4 | 2.0 |`0409:005A`| 2004 | | | Sunix | SHB4200MA | 4 | 2.0 |`0409:0058`| 2006 | 2009 | | Targus | PAUH212U | 7 | 2.0 | | 2004 | 2009 | | Texas Instruments | TUSB4041PAPEVM | 4 | 2.1 |`0451:8142`| 2015 | | @@ -103,46 +107,57 @@ ========= This utility was tested to compile and work on Linux -(Ubuntu/Debian, Redhat/Fedora/CentOS, Arch Linux, Gentoo, openSUSE, Buildroot), FreeBSD, NetBSD and Mac OS X. +(Ubuntu/Debian, Redhat/Fedora/CentOS, Arch Linux, Gentoo, openSUSE, Buildroot), FreeBSD, NetBSD, SunOS and MacOS. While `uhubctl` compiles on Windows, USB power switching does not work on Windows because `libusb` is using `winusb.sys` driver, which according to Microsoft does not support [necessary USB control requests](https://social.msdn.microsoft.com/Forums/sqlserver/en-US/f680b63f-ca4f-4e52-baa9-9e64f8eee101). This may be fixed if `libusb` starts supporting different driver on Windows. +Note that it is highly recommended to have `pkg-config` installed (many platforms provide it by default). + First, you need to install library libusb-1.0 (version 1.0.12 or later, 1.0.16 or later is recommended): * Ubuntu: `sudo apt-get install libusb-1.0-0-dev` * Redhat: `sudo yum install libusb1-devel` -* MacOSX: `brew install libusb`, or `sudo port install libusb-devel` - > :warning: `libusb-1.0.23` is [broken](https://github.com/libusb/libusb/issues/707) on MacOS Catalina! - You have to install `libusb-1.0.22` until [libusb issue 707](https://github.com/libusb/libusb/issues/707) is fixed, - or use this workaround to force use of older Mojave build: - - brew uninstall --ignore-dependencies libusb - brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/5314f1d/Formula/libusb.rb - +* MacOS: `brew install libusb`, or `sudo port install libusb-devel` * FreeBSD: libusb is included by default * NetBSD: `sudo pkgin install libusb1 gmake pkg-config` * Windows: TBD? -To fetch uhubctl source: +To fetch uhubctl source and compile it: git clone https://github.com/mvp/uhubctl + cd uhubctl + make + +This should generate `uhubctl` binary. +You can install it in your system as `/usr/sbin/uhubctl` using: + + sudo make install -To compile, simply run `make` - this will generate `uhubctl` binary. Note that on some OS (e.g. FreeBSD/NetBSD) you need to use `gmake` instead to build. -Also, for Mac OS X you can install `uhubctl` with Homebrew custom tap: +Also, on MacOS you can install `uhubctl` with all necessary dependencies in one shot using Homebrew tap: ``` brew tap mvp/uhubctl https://github.com/mvp/uhubctl -brew install --HEAD uhubctl +brew install uhubctl +``` +To build/install from master branch, use `--HEAD`: +``` +brew install uhubctl --HEAD ``` Usage ===== +> :warning: On Linux, use `sudo` or configure USB permissions as described below! + +To list all supported hubs: + + uhubctl + You can control the power on a USB port(s) like this: uhubctl -a off -p 2 @@ -169,10 +184,16 @@ On Linux, you should configure `udev` USB permissions (otherwise you will have to run it as root using `sudo uhubctl`). To fix USB permissions, first run `sudo uhubctl` and note all `vid:pid` for hubs you need to control. -Then, add one or more udev rules like below to file `/etc/udev/rules.d/52-usb.rules` (replace with your vendor id): +Then, add one or more udev rules like below to file `/etc/udev/rules.d/52-usb.rules` (replace 2001 with your vendor id): SUBSYSTEM=="usb", ATTR{idVendor}=="2001", MODE="0666" +Note that for USB3 hubs, some hubs use different vendor ID for USB2 vs USB3 components of the same chip, +and both need permissions to make uhubctl work properly. E.g. for Raspberry Pi 4B, you need to add these 2 lines: + + SUBSYSTEM=="usb", ATTR{idVendor}=="2109", MODE="0666" + SUBSYSTEM=="usb", ATTR{idVendor}=="1d6b", MODE="0666" + If you don't like wide open mode `0666`, you can restrict access by group like this: SUBSYSTEM=="usb", ATTR{idVendor}=="2001", MODE="0664", GROUP="dialout" @@ -194,7 +215,8 @@ According to USB 2.0 specification, USB hubs can advertise no power switching, ganged (all ports at once) power switching or per-port (individual) power switching. -Note that `uhubctl` will only detect USB hubs which support per-port power switching. +Note that by default `uhubctl` will only detect USB hubs which support per-port power switching +(but you can force it to try operating on unsupported hubs with option `-f`). You can find what kind of power switching your hardware supports by using `sudo lsusb -v`: No power switching: @@ -261,8 +283,11 @@ `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). -If your device is USB mass storage, invoking `udisksctl` before calling `uhubctl` -might help to mitigate this issue: +Disabling USB authorization for device in question before turning power off with `uhubctl` should help: + + echo 0 > sudo tee /sys/bus/usb/devices/${location}.${port}/authorized + +If your device is USB mass storage, invoking `udisksctl` before calling `uhubctl` should help too: sudo udisksctl power-off --block-device /dev/disk/...` sudo uhubctl -a off ... @@ -307,8 +332,9 @@ ##### Raspberry Pi 4B - > :warning: You may need to [update firmware](https://www.raspberrypi.org/documentation/hardware/raspberrypi/booteeprom.md) -to vl805 00137ac or later to make power switching work on RPi 4B. + > :warning: If your VL805 firmware is older than `00137ad` (check with `sudo rpi-eeprom-update`), +you have to [update firmware](https://www.raspberrypi.org/documentation/hardware/raspberrypi/booteeprom.md) +to make power switching work on RPi 4B. * USB2 hub `1`, 1 port, only connects hub `1-1` below. @@ -320,9 +346,7 @@ uhubctl -l 2 -a 0 - * USB2 hub `3`, 1 port, OTG controller: - - uhubctl -l 3 -p 1 -a 0 + * USB2 hub `3`, 1 port, OTG controller. Power switching is [not supported](https://git.io/JUc5Q). As a workaround, you can buy any external USB hub from supported list, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/uhubctl-2.2.0/VERSION new/uhubctl-2.3.0/VERSION --- old/uhubctl-2.2.0/VERSION 2020-05-22 22:39:47.000000000 +0200 +++ new/uhubctl-2.3.0/VERSION 2020-12-13 23:31:05.000000000 +0100 @@ -1 +1 @@ -v2.2.0-dev +2.3.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/uhubctl-2.2.0/uhubctl.c new/uhubctl-2.3.0/uhubctl.c --- old/uhubctl-2.2.0/uhubctl.c 2020-05-22 22:39:47.000000000 +0200 +++ new/uhubctl-2.3.0/uhubctl.c 2020-12-13 23:31:05.000000000 +0100 @@ -188,13 +188,16 @@ struct hub_info { struct libusb_device *dev; int bcd_usb; + int super_speed; /* 1 if super speed hub, and 0 otherwise */ int nports; - int ppps; + int lpsm; /* logical power switching mode */ int actionable; /* true if this hub is subject to action */ char container_id[33]; /* container ID as hex string */ char vendor[16]; char location[32]; - int level; + uint8_t bus; + uint8_t port_numbers[MAX_HUB_CHAIN]; + int pn_len; /* length of port numbers */ struct descriptor_strings ds; }; @@ -206,6 +209,7 @@ /* default options */ static char opt_vendor[16] = ""; +static char opt_search[64] = ""; /* Search by attached device description */ static char opt_location[32] = ""; /* Hub location a-b.c.d */ static int opt_level = 0; /* Hub location level (e.g., a-b is level 2, a-b.c is level 3)*/ static int opt_ports = ALL_HUB_PORTS; /* Bitmask of ports to operate on */ @@ -215,10 +219,12 @@ static int opt_wait = 20; /* wait before repeating in ms */ static int opt_exact = 0; /* exact location match - disable USB3 duality handling */ static int opt_reset = 0; /* reset hub after operation(s) */ +static int opt_force = 0; /* force operation even on unsupported hubs */ static const struct option long_options[] = { { "location", required_argument, NULL, 'l' }, { "vendor", required_argument, NULL, 'n' }, + { "search", required_argument, NULL, 's' }, { "level", required_argument, NULL, 'L' }, { "ports", required_argument, NULL, 'p' }, { "action", required_argument, NULL, 'a' }, @@ -226,6 +232,7 @@ { "repeat", required_argument, NULL, 'r' }, { "wait", required_argument, NULL, 'w' }, { "exact", no_argument, NULL, 'e' }, + { "force", no_argument, NULL, 'f' }, { "reset", no_argument, NULL, 'R' }, { "version", no_argument, NULL, 'v' }, { "help", no_argument, NULL, 'h' }, @@ -236,7 +243,7 @@ static int print_usage() { printf( - "uhubctl %s: utility to control USB port power for smart hubs.\n" + "uhubctl: utility to control USB port power for smart hubs.\n" "Usage: uhubctl [options]\n" "Without options, show status for all smart hubs.\n" "\n" @@ -246,20 +253,23 @@ "--location, -l - limit hub by location [all smart hubs].\n" "--level -L - limit hub by location level (e.g. a-b.c is level 3).\n" "--vendor, -n - limit hub by vendor id [%s] (partial ok).\n" + "--search, -s - limit hub by attached device description.\n" "--delay, -d - delay for cycle action [%g sec].\n" "--repeat, -r - repeat power off count [%d] (some devices need it to turn off).\n" "--exact, -e - exact location (no USB3 duality handling).\n" + "--force, -f - force operation even on unsupported hubs.\n" "--reset, -R - reset hub after each power-on action, causing all devices to reassociate.\n" "--wait, -w - wait before repeat power off [%d ms].\n" "--version, -v - print program version.\n" "--help, -h - print this text.\n" "\n" - "Send bugs and requests to: https://github.com/mvp/uhubctl\n", - PROGRAM_VERSION, + "Send bugs and requests to: https://github.com/mvp/uhubctl\n" + "version: %s\n", strlen(opt_vendor) ? opt_vendor : "any", opt_delay, opt_repeat, - opt_wait + opt_wait, + PROGRAM_VERSION ); return 0; } @@ -385,9 +395,9 @@ ); if (len >= minlen) { - unsigned char port_numbers[MAX_HUB_CHAIN] = {0}; info->dev = dev; info->bcd_usb = bcd_usb; + info->super_speed = (bcd_usb >= USB_SS_BCD); info->nports = uhd->bNbrPorts; snprintf( info->vendor, sizeof(info->vendor), @@ -397,14 +407,13 @@ ); /* Convert bus and ports array into USB location string */ - int bus = libusb_get_bus_number(dev); - snprintf(info->location, sizeof(info->location), "%d", bus); - int pcount = get_port_numbers(dev, port_numbers, MAX_HUB_CHAIN); - info->level = pcount + 1; + info->bus = libusb_get_bus_number(dev); + snprintf(info->location, sizeof(info->location), "%d", info->bus); + info->pn_len = get_port_numbers(dev, info->port_numbers, sizeof(info->port_numbers)); int k; - for (k=0; k<pcount; k++) { + for (k=0; k < info->pn_len; k++) { char s[8]; - snprintf(s, sizeof(s), "%s%d", k==0 ? "-" : ".", port_numbers[k]); + snprintf(s, sizeof(s), "%s%d", k==0 ? "-" : ".", info->port_numbers[k]); strcat(info->location, s); } @@ -434,10 +443,10 @@ } libusb_free_bos_descriptor(bos); - /* Raspberry Pi 4 hack for USB3 root hub: */ + /* Raspberry Pi 4B hack for USB3 root hub: */ if (strlen(info->container_id)==0 && strcasecmp(info->vendor, "1d6b:0003")==0 && - info->level==1 && + info->pn_len==0 && info->nports==4 && bcd_usb==USB_SS_BCD) { @@ -445,26 +454,18 @@ } } - info->ppps = 0; /* Logical Power Switching Mode */ int lpsm = uhd->wHubCharacteristics[0] & HUB_CHAR_LPSM; if (lpsm == HUB_CHAR_COMMON_LPSM && info->nports == 1) { /* For 1 port hubs, ganged power switching is the same as per-port: */ lpsm = HUB_CHAR_INDV_PORT_LPSM; } - /* Raspberry Pi 4 reports inconsistent descriptors, override: */ + /* Raspberry Pi 4B reports inconsistent descriptors, override: */ if (lpsm == HUB_CHAR_COMMON_LPSM && strcasecmp(info->vendor, "2109:3431")==0) { lpsm = HUB_CHAR_INDV_PORT_LPSM; } - /* Over-Current Protection Mode */ - int ocpm = uhd->wHubCharacteristics[0] & HUB_CHAR_OCPM; - /* LPSM must be supported per-port, and OCPM per port or ganged */ - if ((lpsm == HUB_CHAR_INDV_PORT_LPSM) && - (ocpm == HUB_CHAR_INDV_PORT_OCPM || - ocpm == HUB_CHAR_COMMON_OCPM)) - { - info->ppps = 1; - } + info->lpsm = lpsm; + rc = 0; } else { rc = len; } @@ -522,7 +523,7 @@ int rc; int id_vendor = 0; int id_product = 0; - char ports[64] = ""; + char hub_specific[64] = ""; struct libusb_device_descriptor desc; struct libusb_device_handle *devh = NULL; rc = libusb_get_device_descriptor(dev, &desc); @@ -552,8 +553,16 @@ struct hub_info info; rc = get_hub_info(dev, &info); if (rc == 0) { - snprintf(ports, sizeof(ports), ", USB %x.%02x, %d ports", - info.bcd_usb >> 8, info.bcd_usb & 0xFF, info.nports); + const char * lpsm_type; + if (info.lpsm == HUB_CHAR_INDV_PORT_LPSM) { + lpsm_type = "ppps"; + } else if (info.lpsm == HUB_CHAR_COMMON_LPSM) { + lpsm_type = "ganged"; + } else { + lpsm_type = "nops"; + } + snprintf(hub_specific, sizeof(hub_specific), ", USB %x.%02x, %d ports, %s", + info.bcd_usb >> 8, info.bcd_usb & 0xFF, info.nports, lpsm_type); } } libusb_close(devh); @@ -564,7 +573,7 @@ ds->vendor[0] ? " " : "", ds->vendor, ds->product[0] ? " " : "", ds->product, ds->serial[0] ? " " : "", ds->serial, - ports + hub_specific ); return 0; } @@ -581,17 +590,9 @@ int port_status; struct libusb_device_handle * devh = NULL; int rc = 0; - int hub_bus; - int dev_bus; - unsigned char hub_pn[MAX_HUB_CHAIN]; - unsigned char dev_pn[MAX_HUB_CHAIN]; - int hub_plen; - int dev_plen; struct libusb_device *dev = hub->dev; rc = libusb_open(dev, &devh); if (rc == 0) { - hub_bus = libusb_get_bus_number(dev); - hub_plen = get_port_numbers(dev, hub_pn, sizeof(hub_pn)); int port; for (port = 1; port <= hub->nports; port++) { if (portmask > 0 && (portmask & (1 << (port-1))) == 0) continue; @@ -611,12 +612,15 @@ struct libusb_device * udev; int i = 0; while ((udev = usb_devs[i++]) != NULL) { + uint8_t dev_bus; + uint8_t dev_pn[MAX_HUB_CHAIN]; + int dev_plen; dev_bus = libusb_get_bus_number(udev); /* only match devices on the same bus: */ - if (dev_bus != hub_bus) continue; + if (dev_bus != hub->bus) continue; dev_plen = get_port_numbers(udev, dev_pn, sizeof(dev_pn)); - if ((dev_plen == hub_plen + 1) && - (memcmp(hub_pn, dev_pn, hub_plen) == 0) && + if ((dev_plen == hub->pn_len + 1) && + (memcmp(hub->port_numbers, dev_pn, hub->pn_len) == 0) && libusb_get_port_number(udev) == port) { rc = get_device_description(udev, &ds); @@ -625,7 +629,7 @@ } } - if (hub->bcd_usb < USB_SS_BCD) { + if (!hub->super_speed) { if (port_status == 0) { printf(" off"); } else { @@ -703,30 +707,63 @@ rc = get_hub_info(dev, &info); if (rc) { perm_ok = 0; /* USB permission issue? */ + continue; } get_device_description(dev, &info.ds); - if (info.ppps) { /* PPPS is supported */ - if (hub_count < MAX_HUBS) { - info.actionable = 1; - if (strlen(opt_location) > 0) { - if (strcasecmp(opt_location, info.location)) { - info.actionable = 0; - } - } - if (opt_level > 0) { - if (opt_level != info.level) { - info.actionable = 0; - } - } - if (strlen(opt_vendor) > 0) { - if (strncasecmp(opt_vendor, info.vendor, strlen(opt_vendor))) { - info.actionable = 0; + if (info.lpsm != HUB_CHAR_INDV_PORT_LPSM && !opt_force) { + continue; + } + info.actionable = 1; + if (strlen(opt_search) > 0) { + /* Search by attached device description */ + info.actionable = 0; + struct libusb_device * udev; + int k = 0; + while ((udev = usb_devs[k++]) != NULL) { + uint8_t dev_pn[MAX_HUB_CHAIN]; + uint8_t dev_bus = libusb_get_bus_number(udev); + /* only match devices on the same bus: */ + if (dev_bus != info.bus) continue; + int dev_plen = get_port_numbers(udev, dev_pn, sizeof(dev_pn)); + if ((dev_plen == info.pn_len + 1) && + (memcmp(info.port_numbers, dev_pn, info.pn_len) == 0)) + { + struct descriptor_strings ds; + bzero(&ds, sizeof(ds)); + rc = get_device_description(udev, &ds); + if (rc != 0) + break; + if (strstr(ds.description, opt_search)) { + info.actionable = 1; + opt_ports &= 1 << (dev_pn[dev_plen-1] - 1); + break; } } - memcpy(&hubs[hub_count], &info, sizeof(info)); - hub_count++; } } + if (strlen(opt_location) > 0) { + if (strcasecmp(opt_location, info.location)) { + info.actionable = 0; + } + } + if (opt_level > 0) { + if (opt_level != info.pn_len + 1) { + info.actionable = 0; + } + } + if (strlen(opt_vendor) > 0) { + if (strncasecmp(opt_vendor, info.vendor, strlen(opt_vendor))) { + info.actionable = 0; + } + } + memcpy(&hubs[hub_count], &info, sizeof(info)); + if (hub_count < MAX_HUBS) { + hub_count++; + } else { + /* That should be impossible - but we don't want to crash! */ + fprintf(stderr, "Too many hubs!"); + break; + } } if (!opt_exact) { /* Handle USB2/3 duality: */ @@ -737,7 +774,8 @@ /* Must have non empty container ID: */ if (strlen(hubs[i].container_id) == 0) continue; - int match = -1; + int best_match = -1; + int best_score = -1; for (j=0; j<hub_count; j++) { if (i==j) continue; @@ -745,8 +783,7 @@ /* Find hub which is USB2/3 dual to the hub above */ /* Hub and its dual must be different types: one USB2, another USB3: */ - if ((hubs[i].bcd_usb < USB_SS_BCD) == - (hubs[j].bcd_usb < USB_SS_BCD)) + if (hubs[i].super_speed == hubs[j].super_speed) continue; /* Must have non empty container ID: */ @@ -762,6 +799,14 @@ * We should do few more checks below if multiple such devices are present. */ + /* Hubs should have the same number of ports */ + if (hubs[i].nports != hubs[j].nports) { + /* Except for some weird hubs like Apple mini-dock (has 2 usb2 + 1 usb3 ports) */ + if (hubs[i].nports + hubs[j].nports > 3) { + continue; + } + } + /* If serial numbers are both present, they must match: */ if ((strlen(hubs[i].ds.serial) > 0 && strlen(hubs[j].ds.serial) > 0) && strcmp(hubs[i].ds.serial, hubs[j].ds.serial) != 0) @@ -769,18 +814,56 @@ continue; } - /* Hubs should have the same number of ports: */ - if (hubs[i].nports != hubs[j].nports) - continue; + /* We have first possible candidate, but need to keep looking for better one */ - /* Finally, we claim a match: */ - match = j; - break; + if (best_score < 1) { + best_score = 1; + best_match = j; + } + + /* Checks for various levels of USB2 vs USB3 path similarity... */ + + uint8_t* p1 = hubs[i].port_numbers; + uint8_t* p2 = hubs[j].port_numbers; + int l1 = hubs[i].pn_len; + int l2 = hubs[j].pn_len; + int s1 = hubs[i].super_speed; + int s2 = hubs[j].super_speed; + + /* Check if port path is the same after removing top level (needed for M1 Macs): */ + if (l1 >= 1 && l1 == l2 && memcmp(p1 + 1, p2 + 1, l1 - 1)==0) { + if (best_score < 2) { + best_score = 2; + best_match = j; + } + } + + /* Raspberry Pi 4B hack (USB2 hub is one level deeper than USB3): */ + if (l1 + s1 == l2 + s2 && l1 >= s2 && memcmp(p1 + s2, p2 + s1, l1 - s2)==0) { + if (best_score < 3) { + best_score = 3; + best_match = j; + } + } + /* Check if port path is exactly the same: */ + if (l1 == l2 && memcmp(p1, p2, l1)==0) { + if (best_score < 4) { + best_score = 4; + best_match = j; + } + /* Give even higher priority if `usb2bus + 1 == usb3bus` (Linux specific): */ + if (hubs[i].bus - s1 == hubs[j].bus - s2) { + if (best_score < 5) { + best_score = 5; + best_match = j; + } + } + } } - if (match >= 0) { - if (!hubs[match].actionable) { + if (best_match >= 0) { + if (!hubs[best_match].actionable) { /* Use 2 to signify that this is derived dual device */ - hubs[match].actionable = 2; + hubs[best_match].actionable = 2; } } } @@ -789,10 +872,18 @@ for (i=0; i<hub_count; i++) { if (!hubs[i].actionable) continue; - if (hubs[i].bcd_usb < USB_SS_BCD || opt_exact) { + if (!hubs[i].super_speed || opt_exact) { hub_phys_count++; } } +#ifdef __gnu_linux__ + if (perm_ok == 0 && geteuid() != 0) { + fprintf(stderr, + "There were permission problems while accessing USB.\n" + "Follow https://git.io/JIB2Z for a fix!\n" + ); + } +#endif if (perm_ok == 0 && hub_phys_count == 0) { return LIBUSB_ERROR_ACCESS; } @@ -807,7 +898,7 @@ int option_index = 0; for (;;) { - c = getopt_long(argc, argv, "l:L:n:a:p:d:r:w:hveR", + c = getopt_long(argc, argv, "l:L:n:a:p:d:r:w:s:hvefR", long_options, &option_index); if (c == -1) break; /* no more options left */ @@ -830,6 +921,9 @@ case 'n': strncpy(opt_vendor, optarg, sizeof(opt_vendor)); break; + case 's': + strncpy(opt_search, optarg, sizeof(opt_search)); + break; case 'p': if (!strcasecmp(optarg, "all")) { /* all ports is the default */ break; @@ -855,6 +949,9 @@ case 'r': opt_repeat = atoi(optarg); break; + case 'f': + opt_force = 1; + break; case 'e': opt_exact = 1; break; @@ -908,23 +1005,11 @@ rc = usb_find_hubs(); if (rc <= 0) { fprintf(stderr, - "No compatible smart hubs detected%s%s!\n" + "No compatible devices detected%s%s!\n" "Run with -h to get usage info.\n", strlen(opt_location) ? " at location " : "", opt_location ); -#ifdef __gnu_linux__ - if (rc < 0 && geteuid() != 0) { - fprintf(stderr, - "There were permission problems while accessing USB.\n" - "To fix this, run this tool as root using 'sudo uhubctl',\n" - "or add one or more udev rules like below\n" - "to file '/etc/udev/rules.d/52-usb.rules':\n" - "SUBSYSTEM==\"usb\", ATTR{idVendor}==\"2001\", MODE=\"0666\"\n" - "then run 'sudo udevadm trigger --attr-match=subsystem=usb'\n" - ); - } -#endif rc = 1; goto cleanup; } @@ -966,8 +1051,8 @@ for (port=1; port <= hubs[i].nports; port++) { if ((1 << (port-1)) & ports) { int port_status = get_port_status(devh, port); - int power_mask = hubs[i].bcd_usb < USB_SS_BCD ? USB_PORT_STAT_POWER - : USB_SS_PORT_STAT_POWER; + int power_mask = hubs[i].super_speed ? USB_SS_PORT_STAT_POWER + : USB_PORT_STAT_POWER; if (k == 0 && !(port_status & power_mask)) continue; if (k == 1 && (port_status & power_mask)) @@ -993,7 +1078,7 @@ } } /* USB3 hubs need extra delay to actually turn off: */ - if (k==0 && hubs[i].bcd_usb >= USB_SS_BCD) + if (k==0 && hubs[i].super_speed) sleep_ms(150); printf("Sent power %s request\n", request == LIBUSB_REQUEST_CLEAR_FEATURE ? "off" : "on" _______________________________________________ openSUSE Commits mailing list -- [email protected] To unsubscribe, email [email protected] List Netiquette: https://en.opensuse.org/openSUSE:Mailing_list_netiquette List Archives: https://lists.opensuse.org/archives/list/[email protected]
