pax_global_header00006660000000000000000000000064135334021420014507gustar00rootroot0000000000000052 comment=c9fa3c68a1b2c9790c731602b8bae2b513e80605 .gitignore000066400000000000000000000001001353340214200130320ustar00rootroot00000000000000# Executables uhubctl # Object files *.o # Mac symbols *.dSYM COPYING000066400000000000000000000432541353340214200121160ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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 the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. Formula/000077500000000000000000000000001353340214200124605ustar00rootroot00000000000000Formula/uhubctl.rb000066400000000000000000000004531353340214200144550ustar00rootroot00000000000000class Uhubctl < Formula desc "control USB hubs powering per-port" homepage "https://github.com/mvp/uhubctl" head "https://github.com/mvp/uhubctl.git" depends_on "libusb" def install system "make" bin.install "uhubctl" end test do system "#{bin}/uhubctl", "-v" end end LICENSE000066400000000000000000000013021353340214200120540ustar00rootroot00000000000000uhubctl – USB hub per-port power control. Copyright (c) 2009-2019, 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 the Free Software Foundation, version 2. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Makefile000066400000000000000000000022001353340214200125050ustar00rootroot00000000000000# uhubctl Makefile # UNAME_S := $(shell uname -s) DESTDIR ?= prefix ?= /usr sbindir ?= $(prefix)/sbin INSTALL := install INSTALL_DIR := $(INSTALL) -m 755 -d INSTALL_PROGRAM := $(INSTALL) -m 755 RM := rm -rf CC ?= gcc CFLAGS ?= -g -O0 CFLAGS += -Wall -Wextra -std=c99 -pedantic GIT_VERSION := $(shell git describe --abbrev=8 --dirty --always --tags) ifeq ($(GIT_VERSION),) GIT_VERSION := $(shell cat VERSION) endif CFLAGS += -DPROGRAM_VERSION=\"$(GIT_VERSION)\" ifeq ($(UNAME_S),Linux) LDFLAGS += -Wl,-z,relro -lusb-1.0 endif ifeq ($(UNAME_S),Darwin) ifneq ($(wildcard /opt/local/include),) # MacPorts CFLAGS += -I/opt/local/include LDFLAGS += -L/opt/local/lib endif 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 $(CC) $(CPPFLAGS) $(CFLAGS) $@.c -o $@ $(LDFLAGS) install: $(INSTALL_DIR) $(DESTDIR)$(sbindir) $(INSTALL_PROGRAM) $(PROGRAM) $(DESTDIR)$(sbindir) clean: $(RM) $(PROGRAM).o $(PROGRAM).dSYM $(PROGRAM) README.md000066400000000000000000000426221353340214200123400ustar00rootroot00000000000000uhubctl ======= `uhubctl` is utility to control USB power per-port on smart USB hubs. Smart hub is defined as one that implements per-port power switching. Original idea for this code was inspired by hub-ctrl.c by Niibe Yutaka: 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. Some of them are no longer manufactured and can be hard to find. This is list of known compatible USB hubs: | Manufacturer | Product | Ports | USB | VID:PID | Release | EOL | |:-------------------|:-----------------------------------------------------|:------|:----|:----------|:--------|:-----| | 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 | | | 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 | | B+B SmartWorx | UHR204 | 4 | 2.0 |`0856:DB00`| 2013 | | | B+B SmartWorx | USH304 | 4 | 3.0 |`04B4:6506`| 2017 | | | Belkin | F5U701-BLK | 7 | 2.0 | | 2008 | 2012 | | Circuitco | Beagleboard-xM (internal USB hub) | 4 | 2.0 |`0424:9514`| 2010 | | | Club3D | CSV-3242HD Dual Display Docking Station | 4 | 3.0 |`2109:2811`| 2015 | | | CyberPower | CP-H420P | 4 | 2.0 |`0409:0059`| 2004 | | | Cypress | CY4608 HX2VL development kit | 4 | 2.0 |`04B4:6570`| 2012 | | | D-Link | DUB-H4 rev D1 (black edition) | 4 | 2.0 |`05E3:0608`| 2012 | | | D-Link | DUB-H7 rev A (silver edition) | 7 | 2.0 |`2001:F103`| 2005 | 2010 | | D-Link | DUB-H7 rev D1 (black edition) | 7 | 2.0 |`05E3:0608`| 2012 | | | Dell | P2416D 24" QHD Monitor | 4 | 2.0 | | 2017 | | | Dell | UltraSharp 1704FPT 17" LCD Monitor | 4 | 2.0 |`0424:A700`| 2005 | 2015 | | Elecom | U2H-G4S | 4 | 2.0 | | 2006 | 2011 | | GlobalScale | ESPRESSObin SBUD102 V5 | 1 | 3.0 |`1D6B:0003`| 2017 | | | Hawking Technology | UH214 | 4 | 2.0 | | 2003 | 2008 | | IOI | U3H415E1 | 4 | 3.0 | | 2012 | | | j5create | JUH470 (works only in USB2 mode) | 3 | 3.0 |`05E3:0610`| 2014 | | | Lenovo | ThinkPad EU Ultra Dockingstation (40A20090EU) | 6 | 2.0 |`17EF:100F`| 2015 | | | Lenovo | ThinkPad X200 Ultrabase 42X4963 | 3 | 2.0 |`17EF:1005`| 2008 | 2011 | | Lenovo | ThinkPad X6 Ultrabase 42W3107 | 4 | 2.0 |`17EF:1000`| 2006 | 2009 | | Lindy | USB serial converter 4 port | 4 | 1.1 |`058F:9254`| 2008 | | | Linksys | USB2HUB4 | 4 | 2.0 | | 2004 | 2010 | | Maplin | A08CQ | 7 | 2.0 |`0409:0059`| 2008 | 2011 | | Microchip | EVB-USB2517 | 7 | 2.0 | | 2008 | | | Moxa | Uport-407 | 7 | 2.0 |`110A:0407`| 2009 | | | Phidgets | HUB0003_0 | 7 | 2.0 |`1A40:0201`| 2017 | | | Plugable | USB3-HUB7BC | 7 | 3.0 |`2109:0813`| 2015 | | | Plugable | USB3-HUB7C | 7 | 3.0 |`2109:0813`| 2015 | | | Plugable | USB3-HUB7-81X | 7 | 3.0 |`2109:0813`| 2012 | | | Plugable | USB2-HUB10S | 10 | 2.0 | | 2010 | | | Raspberry Pi | Model B+, 2 B, 3 B (port 2 only) | 4 | 2.0 | | 2011 | | | Raspberry Pi | Model 3 B+ | 6 | 2.0 |`0424:2514`| 2018 | | | Renesas | uPD720202 PCIe USB 3.0 host controller | 2 | 3.0 | | 2013 | | | Rosewill | RHUB-210 | 4 | 2.0 |`0409:005A`| 2011 | 2014 | | Sanwa Supply | USB-HUB14GPH | 4 | 1.1 | | 2001 | 2003 | | 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 | | This table is by no means complete. If your hub works with `uhubctl`, but is not listed above, please report it by opening new issue at https://github.com/mvp/uhubctl/issues, so we can add it to supported table. In your report, please provide exact product model and add output from `uhubctl`. Note that quite a few modern motherboards have built-in root hubs that do support this feature - you may not even need to buy any external hub. WARNING: turning off built-in USB ports may cut off your keyboard or mouse, so be careful what ports you are turning off! USB 3.0 duality note ==================== If you have USB 3.0 hub connected to USB3 upstream port, it will be detected as 2 independent virtual hubs: USB2 and USB3, and your USB devices will be connected to USB2 or USB3 virtual hub depending on their capabilities and connection speed. To control power for such hubs, it is necessary to turn off/on power on **both** USB2 and USB3 virtual hubs for power off/on changes to take effect. `uhubctl` will try to do this automatically (unless you disable this behavior with option `-e`). 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`. Compiling ========= This utility was tested to compile and work on Linux (Ubuntu/Debian, Redhat/Fedora/CentOS, Arch Linux, Gentoo, openSUSE, Buildroot), FreeBSD, NetBSD and Mac OS X. While `uhubctl` compiles on Windows, USB power switching does not work on Windows because `libusb` is using `winusb.sys` driver, which according to Microsoft does not support [necessary USB control requests](https://social.msdn.microsoft.com/Forums/sqlserver/en-US/f680b63f-ca4f-4e52-baa9-9e64f8eee101). This may be fixed if `libusb` starts supporting different driver on Windows. First, you need to install library libusb-1.0 (version 1.0.12 or later): * 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` * FreeBSD: libusb is included by default * NetBSD: `sudo pkgin install libusb1 gmake pkg-config` * Windows: TBD? To fetch uhubctl source: git clone https://github.com/mvp/uhubctl 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: ``` brew tap mvp/uhubctl https://github.com/mvp/uhubctl brew install --HEAD uhubctl ``` Usage ===== You can control the power on a USB port(s) like this: uhubctl -a off -p 2 This means operate on default smart hub and turn power off (`-a off`, or `-a 0`) on port 2 (`-p 2`). Supported actions are `off`/`on`/`cycle` (or `0`/`1`/`2`). `cycle` means turn power off, wait some delay (configurable with `-d`) and turn it back on. Ports can be comma separated list, and may use `-` for ranges e.g. `2`, or `2,4`, or `2-5`, or `1-2,5-8`. If you have more than one smart USB hub connected, you should choose specific hub to control using `-l` (location) parameter. To find hub locations, simply run `uhubctl` without any parameters. Hub locations look like `b-x.y.z`, where `b` is USB bus number, and `x`, `y`, `z`... are port numbers for all hubs in chain, starting from root hub for a given USB bus. This address is semi-stable - it will not change if you unplug/replug (or turn off/on) USB device into the same physical USB port (this method is also used in Linux kernel). Linux USB permissions ===================== On Linux, you should configure `udev` USB permissions (otherwise you will have to run it as root using `sudo uhubctl`). To fix USB permissions, first run `sudo uhubctl` and note all `vid:pid` for hubs you need to control. Then, add one or more udev rules like below to file `/etc/udev/rules.d/52-usb.rules` (replace with your vendor id): SUBSYSTEM=="usb", ATTR{idVendor}=="2001", 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" and then add permitted users to `dialout` group: sudo usermod -a -G dialout $USER For your `udev` rule changes to take effect, reboot or run: sudo udevadm trigger --attr-match=subsystem=usb FAQ === #### _What is USB per-port power switching?_ 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. You can find what kind of power switching your hardware supports by using `sudo lsusb -v`: No power switching: wHubCharacteristic 0x000a No power switching (usb 1.0) Per-port overcurrent protection Ganged power switching: wHubCharacteristic 0x0008 Ganged power switching Per-port overcurrent protection Per-port power switching: wHubCharacteristic 0x0009 Per-port power switching Per-port overcurrent protection #### _How do I check if my USB hub is supported by `uhubctl`?_ 1. Run `sudo uhubctl`. If your hub is not listed, it is not supported. Alternatively, you can run `sudo lsusb -v` and check for `Per-port power switching` - if you cannot see such line in lsusb output, hub is not supported. 2. Check for VBUS (voltage) off support: plug a phone, USB light or USB fan into USB port of your hub. Try using `uhubctl` to turn power off on that port, and check that phone stops charging, USB light stops shining or USB fan stops spinning. If VBUS doesn't turn off, your hub manufacturer did not include circuitry to actually cut power off. Such hub would still work to cut off USB data connection, but it cannot turn off power, and we do not consider this supported device. 3. If tests above were successful, please report your hub by opening new issue at https://github.com/mvp/uhubctl/issues, so we can add it to list of supported devices. #### _USB devices are not removed after port power down on Linux_ After powering down USB port, udev does not get any event, so it keeps the device files around. However, trying to access the device files will lead to an IO error. This is Linux kernel issue. It may be eventually fixed in kernel, see more discussion [here](https://bit.ly/2JzczjZ). Basically what happens here is that kernel USB driver knows about power off, but doesn't send notification about it to udev. You can use this workaround for this issue: sudo uhubctl -a off -l ${location} -p ${port} sudo udevadm trigger --action=remove /sys/bus/usb/devices/${location}.${port}/ Device file will be removed by udev, but USB device will be still visible in `lsusb`. Note that path `/sys/bus/usb/devices/${location}.${port}` will only exist if device was detected on that port. When you turn power back on, device should re-enumerate properly (no need to call `udevadm` again). #### _Power comes back on after few seconds on Linux_ 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 - `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). #### _Multiple 4-port hubs are detected, but I only have one 7-port hub connected_ Many hub manufacturers build their USB hubs using basic 4 port USB chips. E.g. to make 7 port hub, they daisy-chain two 4 port hubs - 1 port is lost to daisy-chaining, so it makes it 4+4-1=7 port hub. Simularly, 10 port hub could be built as 3 4-port hubs daisy-chained together, which gives 4+4+4-2=10 usable ports. Note that you should never try to change power state for ports used to daisy-chain internal hubs together. Doing so will confuse internal hub circuitry and will cause unpredictable behavior. #### _Raspberry Pi turns power off on all ports, not just the one I specified_ This is limitation of Raspberry Pi hardware design. For reference, Raspberry Pi models have following internal USB topology: * B+/2B/3B: one USB hub `1-1`, with port `1` for Ethernet+wifi, and ports `2-5` ganged, controlled by port `2`. (Trying to control ports 3,4,5 won't do anything). * 3B+: 2 hubs - main hub `1-1`, all 4 ports ganged, all controlled by port `2`. Second hub `1-1.1` (daisy-chained to main): 3 independently controlled ports, `1` is used for Ethernet+wifi, and ports `2,3` are available with proper per-port power switching. In other words, 2 out of total 4 ports wired outside do support independent power switching on 3B+. * 4B: Hardware supports power switching, but current firmware is reporting broken USB descriptors. For more details see Raspberry Pi issue https://github.com/raspberrypi/linux/issues/3079. USB3 hub `2`, 4 ports, per-port power switching. BOS `ContainerID` not reported (required for USB3). USB2 hub `1`, 1 port, no usable ports, connects hub `1-1` below. USB2 hub `1-1`, 4 ports, dual to USB3 hub above. Hub descriptor incorrectly reports ganged power switching. USB2 hub `3`, 1 port, OTG controller, incorrectly reports ganged power switching. As a workaround, you can buy any external USB hub from supported list, attach it to any USB port of Raspberry Pi, and control power on its ports independently. Notable projects using uhubctl ============================== | Project | Description | |:---------------------------------------------------------|:------------------------------------------------------| | [Morse code USB light](https://git.io/fj1F4) | Flash a message in Morse code with USB light | | [Webcam USB light](https://git.io/fj1FB) | Turn on/off LED when webcam is turned on/off | | [Cinema Lightbox](https://goo.gl/fjCvkz) | Turn on/off Cinema Lightbox from iOS Home app | | [Build Status Light](https://goo.gl/3GA82o) | Create a build status light in under 10 minutes | | [Buildenlights](https://git.io/fj1FC) | GitLab/GitHub project build status as green/red light | | [Weather Station](https://goo.gl/3b1FzC) | Reset Weather Station when it freezes | | [sysmoQMOD](https://goo.gl/8wvcKA) | Reset cellular modems when necessary | | [Smog Sensor](https://bit.ly/2EMwgCk) | Raspberry Pi based smog sensor power reset | | [Terrible Cluster](https://goo.gl/XjiXFu) | Power on/off Raspberry Pi cluster nodes as needed | | [Ideal Music Server](https://bit.ly/2UJq6Z9) | Turn off unused USB ports to improve audio quality | | [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 | Copyright ========= Copyright (C) 2009-2019 Vadim Mikhailov This file can be distributed under the terms and conditions of the GNU General Public License version 2. VERSION000066400000000000000000000000131353340214200121150ustar00rootroot00000000000000v2.1.0-dev uhubctl.c000066400000000000000000001037321353340214200126730ustar00rootroot00000000000000/* * Copyright (c) 2009-2019 Vadim Mikhailov * * Utility to turn USB port power on/off * for USB hubs that support per-port power switching. * * This file can be distributed under the terms and conditions of the * GNU General Public License version 2. * */ #define _XOPEN_SOURCE 500 #include #include #include #include #include #include #include #if defined(_WIN32) #include #include #include #define strcasecmp _stricmp #define strncasecmp _strnicmp #else #include #endif #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(_WIN32) #include #else #include #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) #define LIBUSB_DT_SUPERSPEED_HUB 0x2a #endif #if _POSIX_C_SOURCE >= 199309L #include /* for nanosleep */ #endif /* cross-platform sleep function */ void sleep_ms(int milliseconds) { #if defined(_WIN32) Sleep(milliseconds); #elif _POSIX_C_SOURCE >= 199309L struct timespec ts; ts.tv_sec = milliseconds / 1000; ts.tv_nsec = (milliseconds % 1000) * 1000000; nanosleep(&ts, NULL); #else usleep(milliseconds * 1000); #endif } /* Max number of hub ports supported */ #define MAX_HUB_PORTS 14 #define ALL_HUB_PORTS ((1 << MAX_HUB_PORTS) - 1) /* bitmask */ #define USB_CTRL_GET_TIMEOUT 5000 #define USB_PORT_FEAT_POWER (1 << 3) #define POWER_KEEP (-1) #define POWER_OFF 0 #define POWER_ON 1 #define POWER_CYCLE 2 #define MAX_HUB_CHAIN 8 /* Per USB 3.0 spec max hub chain is 7 */ /* Partially borrowed from linux/usb/ch11.h */ #pragma pack(push,1) struct usb_hub_descriptor { unsigned char bDescLength; unsigned char bDescriptorType; unsigned char bNbrPorts; unsigned char wHubCharacteristics[2]; unsigned char bPwrOn2PwrGood; unsigned char bHubContrCurrent; unsigned char data[1]; /* use 1 to avoid zero-sized array warning */ }; #pragma pack(pop) /* * Hub Status and Hub Change results * See USB 2.0 spec Table 11-19 and Table 11-20 */ #pragma pack(push,1) struct usb_port_status { int16_t wPortStatus; int16_t wPortChange; }; #pragma pack(pop) /* * wPortStatus bit field * See USB 2.0 spec Table 11-21 */ #define USB_PORT_STAT_CONNECTION 0x0001 #define USB_PORT_STAT_ENABLE 0x0002 #define USB_PORT_STAT_SUSPEND 0x0004 #define USB_PORT_STAT_OVERCURRENT 0x0008 #define USB_PORT_STAT_RESET 0x0010 #define USB_PORT_STAT_L1 0x0020 /* bits 6 to 7 are reserved */ #define USB_PORT_STAT_POWER 0x0100 #define USB_PORT_STAT_LOW_SPEED 0x0200 #define USB_PORT_STAT_HIGH_SPEED 0x0400 #define USB_PORT_STAT_TEST 0x0800 #define USB_PORT_STAT_INDICATOR 0x1000 /* bits 13 to 15 are reserved */ #define USB_SS_BCD 0x0300 /* * Additions to wPortStatus bit field from USB 3.0 * See USB 3.0 spec Table 10-10 */ #define USB_PORT_STAT_LINK_STATE 0x01e0 #define USB_SS_PORT_STAT_POWER 0x0200 #define USB_SS_PORT_STAT_SPEED 0x1c00 #define USB_PORT_STAT_SPEED_5GBPS 0x0000 /* Valid only if port is enabled */ /* Bits that are the same from USB 2.0 */ #define USB_SS_PORT_STAT_MASK (USB_PORT_STAT_CONNECTION | \ USB_PORT_STAT_ENABLE | \ USB_PORT_STAT_OVERCURRENT | \ USB_PORT_STAT_RESET) /* * Definitions for PORT_LINK_STATE values * (bits 5-8) in wPortStatus */ #define USB_SS_PORT_LS_U0 0x0000 #define USB_SS_PORT_LS_U1 0x0020 #define USB_SS_PORT_LS_U2 0x0040 #define USB_SS_PORT_LS_U3 0x0060 #define USB_SS_PORT_LS_SS_DISABLED 0x0080 #define USB_SS_PORT_LS_RX_DETECT 0x00a0 #define USB_SS_PORT_LS_SS_INACTIVE 0x00c0 #define USB_SS_PORT_LS_POLLING 0x00e0 #define USB_SS_PORT_LS_RECOVERY 0x0100 #define USB_SS_PORT_LS_HOT_RESET 0x0120 #define USB_SS_PORT_LS_COMP_MOD 0x0140 #define USB_SS_PORT_LS_LOOPBACK 0x0160 /* * wHubCharacteristics (masks) * See USB 2.0 spec Table 11-13, offset 3 */ #define HUB_CHAR_LPSM 0x0003 /* Logical Power Switching Mode mask */ #define HUB_CHAR_COMMON_LPSM 0x0000 /* All ports at once power switching */ #define HUB_CHAR_INDV_PORT_LPSM 0x0001 /* Per-port power switching */ #define HUB_CHAR_NO_LPSM 0x0002 /* No power switching */ #define HUB_CHAR_COMPOUND 0x0004 /* hub is part of a compound device */ #define HUB_CHAR_OCPM 0x0018 /* Over-Current Protection Mode mask */ #define HUB_CHAR_COMMON_OCPM 0x0000 /* All ports at once over-current protection */ #define HUB_CHAR_INDV_PORT_OCPM 0x0008 /* Per-port over-current protection */ #define HUB_CHAR_NO_OCPM 0x0010 /* No over-current protection support */ #define HUB_CHAR_TTTT 0x0060 /* TT Think Time mask */ #define HUB_CHAR_PORTIND 0x0080 /* per-port indicators (LEDs) */ /* List of all USB devices enumerated by libusb */ static struct libusb_device **usb_devs = NULL; struct descriptor_strings { char vendor[64]; char product[64]; char serial[64]; char description[256]; }; struct hub_info { struct libusb_device *dev; int bcd_usb; int nports; int ppps; 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; struct descriptor_strings ds; }; /* Array of all enumerated USB hubs */ #define MAX_HUBS 128 static struct hub_info hubs[MAX_HUBS]; static int hub_count = 0; static int hub_phys_count = 0; /* default options */ static char opt_vendor[16] = ""; 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_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 const struct option long_options[] = { { "location", required_argument, NULL, 'l' }, { "vendor", required_argument, NULL, 'n' }, { "level", required_argument, NULL, 'L' }, { "ports", required_argument, NULL, 'p' }, { "action", required_argument, NULL, 'a' }, { "delay", required_argument, NULL, 'd' }, { "repeat", required_argument, NULL, 'r' }, { "wait", required_argument, NULL, 'w' }, { "exact", no_argument, NULL, 'e' }, { "reset", no_argument, NULL, 'R' }, { "version", no_argument, NULL, 'v' }, { "help", no_argument, NULL, 'h' }, { 0, 0, NULL, 0 }, }; static int print_usage() { printf( "uhubctl %s: utility to control USB port power for smart hubs.\n" "Usage: uhubctl [options]\n" "Without options, show status for all smart hubs.\n" "\n" "Options [defaults in brackets]:\n" "--action, -a - action to off/on/cycle (0/1/2) for affected ports.\n" "--ports, -p - ports to operate on [all hub ports].\n" "--location, -l - limit hub by location [all smart hubs].\n" "--level -L - limit hub by location level (e.g. a-b.c is level 3).\n" "--vendor, -n - limit hub by vendor id [%s] (partial ok).\n" "--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" "--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, strlen(opt_vendor) ? opt_vendor : "any", opt_delay, opt_repeat, opt_wait ); return 0; } /* trim trailing spaces from a string */ static char* rtrim(char* str) { int i; for (i = strlen(str)-1; i>=0 && isspace(str[i]); i--) { str[i] = 0; } return str; } /* * Convert port list into bitmap. * Following port list specifications are equivalent: * 1,3,4,5,11,12,13 * 1,3-5,11-13 * Returns: bitmap of specified ports, max port is MAX_HUB_PORTS. */ static int ports2bitmap(char* const portlist) { int ports = 0; char* position = portlist; char* comma; char* dash; int len; int i; while (position) { char buf[8] = {0}; comma = strchr(position, ','); len = sizeof(buf) - 1; if (comma) { if (len > comma - position) len = comma - position; strncpy(buf, position, len); position = comma + 1; } else { strncpy(buf, position, len); position = NULL; } /* Check if we have port range, e.g.: a-b */ int a=0, b=0; a = atoi(buf); dash = strchr(buf, '-'); if (dash) { b = atoi(dash+1); } else { b = a; } if (a > b) { fprintf(stderr, "Bad port spec %d-%d, first port must be less than last\n", a, b); exit(1); } if (a <= 0 || a > MAX_HUB_PORTS || b <= 0 || b > MAX_HUB_PORTS) { fprintf(stderr, "Bad port spec %d-%d, port numbers must be from 1 to %d\n", a, b, MAX_HUB_PORTS); exit(1); } for (i=a; i<=b; i++) { ports |= (1 << (i-1)); } } return ports; } /* * Compatibility wrapper around libusb_get_port_numbers() */ 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) /* * libusb_get_port_path is deprecated since libusb v1.0.16, * therefore use libusb_get_port_numbers when supported */ pcount = libusb_get_port_numbers(dev, buf, bufsize); #else pcount = libusb_get_port_path(NULL, dev, buf, bufsize); #endif return pcount; } /* * get USB hub properties. * most hub_info fields are filled, except for description. * returns 0 for success and error code for failure. */ static int get_hub_info(struct libusb_device *dev, struct hub_info *info) { int rc = 0; int len = 0; struct libusb_device_handle *devh = NULL; unsigned char buf[LIBUSB_DT_HUB_NONVAR_SIZE + 2 + 3] = {0}; struct usb_hub_descriptor *uhd = (struct usb_hub_descriptor *)buf; int minlen = LIBUSB_DT_HUB_NONVAR_SIZE + 2; struct libusb_device_descriptor desc; rc = libusb_get_device_descriptor(dev, &desc); if (rc) return rc; if (desc.bDeviceClass != LIBUSB_CLASS_HUB) return LIBUSB_ERROR_INVALID_PARAM; int bcd_usb = libusb_le16_to_cpu(desc.bcdUSB); int desc_type = bcd_usb >= USB_SS_BCD ? LIBUSB_DT_SUPERSPEED_HUB : LIBUSB_DT_HUB; rc = libusb_open(dev, &devh); if (rc == 0) { len = libusb_control_transfer(devh, LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_DEVICE, /* hub status */ LIBUSB_REQUEST_GET_DESCRIPTOR, desc_type << 8, 0, buf, sizeof(buf), USB_CTRL_GET_TIMEOUT ); if (len >= minlen) { unsigned char port_numbers[MAX_HUB_CHAIN] = {0}; info->dev = dev; info->bcd_usb = bcd_usb; info->nports = uhd->bNbrPorts; snprintf( info->vendor, sizeof(info->vendor), "%04x:%04x", libusb_le16_to_cpu(desc.idVendor), libusb_le16_to_cpu(desc.idProduct) ); /* 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; int k; for (k=0; klocation, s); } info->ppps = 0; /* Logical Power Switching Mode */ int lpsm = uhd->wHubCharacteristics[0] & HUB_CHAR_LPSM; /* Over-Current Protection Mode */ int ocpm = uhd->wHubCharacteristics[0] & HUB_CHAR_OCPM; /* LPSM must be supported per-port, and OCPM per port or ganged */ if ((lpsm == HUB_CHAR_INDV_PORT_LPSM) && (ocpm == HUB_CHAR_INDV_PORT_OCPM || ocpm == HUB_CHAR_COMMON_OCPM)) { info->ppps = 1; } } else { rc = len; } /* Get container_id: */ bzero(info->container_id, sizeof(info->container_id)); struct libusb_bos_descriptor *bos; rc = libusb_get_bos_descriptor(devh, &bos); if (rc == 0) { int cap; for (cap=0; cap < bos->bNumDeviceCaps; cap++) { if (bos->dev_capability[cap]->bDevCapabilityType == LIBUSB_BT_CONTAINER_ID) { struct libusb_container_id_descriptor *container_id; rc = libusb_get_container_id_descriptor(NULL, bos->dev_capability[cap], &container_id); if (rc == 0) { int i; for (i=0; i<16; i++) { sprintf(info->container_id+i*2, "%02x", container_id->ContainerID[i]); } info->container_id[i*2] = 0; libusb_free_container_id_descriptor(container_id); } } } libusb_free_bos_descriptor(bos); } libusb_close(devh); } return rc; } /* * Assuming that devh is opened device handle for USB hub, * return state for given hub port. * In case of error, returns -1 (inspect errno for more information). */ static int get_port_status(struct libusb_device_handle *devh, int port) { int rc; struct usb_port_status ust; if (devh == NULL) return -1; rc = libusb_control_transfer(devh, LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_OTHER, /* port status */ LIBUSB_REQUEST_GET_STATUS, 0, port, (unsigned char*)&ust, sizeof(ust), USB_CTRL_GET_TIMEOUT ); if (rc < 0) { return rc; } return ust.wPortStatus; } /* * Get USB device descriptor strings and summary description. * * Summary will use following format: * * " , " * * vid:pid will be always present, but vendor, product or serial * may be skipped if they are empty or not enough permissions to read them. * will be present only for USB hubs. * * Returns 0 for success and error code for failure. * In case of failure return buffer is not altered. */ static int get_device_description(struct libusb_device * dev, struct descriptor_strings * ds) { int rc; int id_vendor = 0; int id_product = 0; char ports[64] = ""; struct libusb_device_descriptor desc; struct libusb_device_handle *devh = NULL; rc = libusb_get_device_descriptor(dev, &desc); if (rc) return rc; bzero(ds, sizeof(*ds)); id_vendor = libusb_le16_to_cpu(desc.idVendor); id_product = libusb_le16_to_cpu(desc.idProduct); 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 (desc.bDeviceClass == LIBUSB_CLASS_HUB) { 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); } } libusb_close(devh); } snprintf(ds->description, sizeof(ds->description), "%04x:%04x%s%s%s%s%s%s%s", id_vendor, id_product, ds->vendor[0] ? " " : "", ds->vendor, ds->product[0] ? " " : "", ds->product, ds->serial[0] ? " " : "", ds->serial, ports ); return 0; } /* * show status for hub ports * portmask is bitmap of ports to display * if portmask is 0, show all ports */ static int print_port_status(struct hub_info * hub, int portmask) { int port_status; struct libusb_device_handle * devh = NULL; int rc = 0; int hub_bus; int dev_bus; unsigned char hub_pn[MAX_HUB_CHAIN]; unsigned char dev_pn[MAX_HUB_CHAIN]; int hub_plen; int dev_plen; struct libusb_device *dev = hub->dev; rc = libusb_open(dev, &devh); if (rc == 0) { hub_bus = libusb_get_bus_number(dev); hub_plen = get_port_numbers(dev, hub_pn, sizeof(hub_pn)); int port; for (port = 1; port <= hub->nports; port++) { if (portmask > 0 && (portmask & (1 << (port-1))) == 0) continue; port_status = get_port_status(devh, port); if (port_status == -1) { fprintf(stderr, "cannot read port %d status, %s (%d)\n", port, strerror(errno), errno); break; } printf(" Port %d: %04x", port, port_status); struct descriptor_strings ds; bzero(&ds, sizeof(ds)); struct libusb_device * udev; int i = 0; while ((udev = usb_devs[i++]) != NULL) { dev_bus = libusb_get_bus_number(udev); /* only match devices on the same bus: */ 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) && libusb_get_port_number(udev) == port) { rc = get_device_description(udev, &ds); if (rc == 0) break; } } if (hub->bcd_usb < USB_SS_BCD) { if (port_status == 0) { printf(" 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"); } } 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_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 (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_CONNECTION) printf(" [%s]", ds.description); printf("\n"); } libusb_close(devh); } return 0; } /* * Find all USB hubs and fill hubs[] array. * Set actionable to 1 on all hubs that we are going to operate on * (this applies possible constraints like location or vendor). * Returns count of found actionable physical hubs * (USB3 hubs are counted once despite having USB2 dual partner). * In case of error returns negative error code. */ static int usb_find_hubs() { struct libusb_device *dev; int perm_ok = 1; int rc = 0; int i = 0; int j = 0; while ((dev = usb_devs[i++]) != NULL) { struct libusb_device_descriptor desc; rc = libusb_get_device_descriptor(dev, &desc); /* only scan for hubs: */ if (rc == 0 && desc.bDeviceClass != LIBUSB_CLASS_HUB) continue; struct hub_info info; bzero(&info, sizeof(info)); rc = get_hub_info(dev, &info); if (rc) { perm_ok = 0; /* USB permission issue? */ } 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; } } memcpy(&hubs[hub_count], &info, sizeof(info)); hub_count++; } } } if (!opt_exact) { /* Handle USB2/3 duality: */ for (i=0; i 0 && strlen(hubs[j].ds.serial) > 0) && strcmp(hubs[i].ds.serial, hubs[j].ds.serial) != 0) { continue; } /* Hubs should have the same number of ports: */ if (hubs[i].nports != hubs[j].nports) continue; /* And the same level: */ if (hubs[i].level != hubs[j].level) continue; /* Finally, we claim a match: */ match = j; break; } if (match >= 0) { if (!hubs[match].actionable) { /* Use 2 to signify that this is derived dual device */ hubs[match].actionable = 2; } } } } hub_phys_count = 0; for (i=0; i 1 && opt_action >= 0) { fprintf(stderr, "Error: changing port state for multiple hubs at once is not supported.\n" "Use -l to limit operation to one hub!\n" ); exit(1); } int k; /* k=0 for power OFF, k=1 for power ON */ for (k=0; k<2; k++) { /* up to 2 power actions - off/on */ if (k == 0 && opt_action == POWER_ON ) continue; if (k == 1 && opt_action == POWER_OFF) continue; if (k == 1 && opt_action == POWER_KEEP) continue; int i; for (i=0; i 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); } } } } /* USB3 hubs need extra delay to actually turn off: */ if (k==0 && hubs[i].bcd_usb >= USB_SS_BCD) sleep_ms(150); printf("Sent power %s request\n", request == LIBUSB_REQUEST_CLEAR_FEATURE ? "off" : "on" ); printf("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"); rc = libusb_reset_device(devh); if (rc < 0) { perror("Reset failed!\n"); } else { printf("Reset successful!\n"); } } } libusb_close(devh); } if (k == 0 && opt_action == POWER_CYCLE) sleep_ms((int)(opt_delay * 1000)); } rc = 0; cleanup: if (usb_devs) libusb_free_device_list(usb_devs, 1); usb_devs = NULL; libusb_exit(NULL); return rc; } uhubctl_git.bb000066400000000000000000000010371353340214200136720ustar00rootroot00000000000000DESCRIPTION = "uhubctl - USB hub per-port power control" HOMEPAGE = "https://github.com/mvp/uhubctl" LICENSE = "GPLv2" LIC_FILES_CHKSUM = "file://COPYING;md5=b234ee4d69f5fce4486a80fdaf4a4263 \ file://LICENSE;md5=7a7d8e0fdffe495ff61f52ceee61b2f7" 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}/*"