How can I programmatically enable and disable the power to a particular USB port on Linux? Is such a thing even possible? Mac answers appreciated as well!
I was tryi
In OS X you can access a USB device from user space and request it to suspend.
You can find a general example in the USB Device Interface Guide. You will need to use the IOUSBDeviceInterface182 (or higher) USBDeviceSuspend method.
Note: Hubs and controller ports may have ganged power supplies, meaning the same switch is shared by multiple ports. if this is the case and your device is in the same group as another active device, it will not be powered down.
Your are running just "echo" as root, try:
echo suspend | sudo tee /sys/bus/usb/devices/usb3/power/level
A few usb hubs can switch their ports on and off, as explained in the link. I have yet to find a motherboard with usb ports which can be enabled or disabled.
The usbfs interaction seems to have changed a number of times since this question was originally answered. So, here's how I cycle hub port power on Ubuntu Oneiric Ocelot from a Bash shell.
Search for the bus and device number:
sudo lsusb -v|less
Locate the device in the bus / hub port hierarchy using the bus and device number:
sudo lsusb -t|less
The syntax seems to be 'bus-port.port.port.port.port...' For example, my mouse is connected to an external hub which connects to my computer's hub which internally connects to a root hub:
/: Bus 02.Port 1: Dev 1, Class=root_hub, Driver=ehci_hcd/2p, 480M
|__ Port 1: Dev 2, If 0, Class=hub, Driver=hub/6p, 480M
|__ Port 1: Dev 3, If 0, Class=hub, Driver=hub/3p, 480M
|__ Port 1: Dev 6, If 0, Class=HID, Driver=usbhid, 1.5M
So, '2-1.1.1' in the above case. Finally, cycle the port power:
echo '2-1.1.1'|sudo tee /sys/bus/usb/drivers/usb/unbind
sleep 1
echo '2-1.1.1'|sudo tee /sys/bus/usb/drivers/usb/bind
I haven't hooked up a protocol analyzer to see what's actually happening on the bus, but I know my mouse light turns off when I unbind it. I'm guessing at a lower layer this is interacting with the EHCI host controller to actually shut power off on the port. This is particularly useful for embedded devices, such as a UVC webcams, which never seem to function properly and would otherwise require a system reboot to reset.
See also the udevadm
command.
power/level
"on" means that the device should be resumed and autosuspend is not allowed.(Of course, system suspends are still allowed.)
"auto" is the normal state in which the kernel is allowed to autosuspend and autoresume the device.
"suspend" means that the device should remain suspended, and autoresume is not allowed. (But remote wakeup may still be allowed, since it is controlled separaely by the power/wakeup attribute.)
Step 1: so i have, usb1, usb2, usb3, usb4 ....
$ cat /sys/bus/usb/devices/usb*/power/level
auto
auto
auto
auto
Step 2: how would i know which one is which? (
# echo "on" | tee /sys/bus/usb/devices/usb*/power/level
# cat /sys/bus/usb/devices/usb*/power/level
on
on
on
on
Optional 1:
in case the lsusb shows and need to find specific one
#!/bin/bash
usb="046d:082d" # Find ME, Replace the ID
cam=$(lsusb | awk "/$usb/ {print $6}")
echo $cam
if [ ! -z "$cam" -a "$cam" != " " ]; then
for X in /sys/bus/usb/devices/*;
do
a=$(cat "$X/idVendor" 2>/dev/null)
b=$(cat "$X/idProduct" 2>/dev/null)
c="$a:$b"
if [ ! -z "$c" -a "$c" != " " ] && [ "$c" == "$usb" ]; then
d=$(echo $X | sed "s/\/sys\/bus\/usb\/devices\///g")
echo "[FOUND] $d"
#sudo sh -c "echo on > /sys/bus/usb/devices/$d/authorized"
sleep 2
#sudo sh -c "echo on > /sys/bus/usb/devices/$d/authorized"
lsusb
break
fi
done;
fi
Optional 2: in case none found - reboot fails to power cycle use Arduino relay over udp
#!/bin/bash
file="/var/www/html/video/now.jpeg"
function age() {
local filename=$1
local changed=`stat -c %Y "$filename"`
local now=`date +%s`
local elapsed
let elapsed=now-changed
echo $elapsed
}
while true
do
target="/dev/video99"
foundon="none"
warn="[WARNING]:"
ok="[OK]:"
for i in 0 1 2 3 4
do
tmp="/dev/video$i"
if [ -e $tmp ]; then
foundon="/dev/video$i"
#else
# echo "no $i"
fi
done
b="none"
if [ "$foundon" = "$b" ]; then
echo "$warn No camera is found - inform reboot or arduino boot"
else
echo "$ok ln -s $foundon $target"
### Camera is available but something is not correct so ###
file_age=$(age "$file")
echo The age of $file is $file_age seconds.
if [[ ! -f $file ]]; then
echo "file is not found. Kernel sucks for 500mA USB's"
else
echo "found file: $file_age"
if [[ $file_age -gt 240 ]]; then
echo "$warn greater then 240 seconds"
else
echo "$ok - less then 240 seconds"
fi
fi
fi
ls /dev/video*
sleep 5
done
Arduino relay:
#include <SPI.h>
#include <Ethernet.h>
#include <EthernetUdp.h>
byte mac[]={0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xAD};
IPAddress ip(10,109,4,166);
byte gateway[]= {10,109, 0, 1};
byte subnet[]= {255, 255, 248,0};
unsigned int localPort = 8888;
char packetBuffer[UDP_TX_PACKET_MAX_SIZE];
char ReplyBuffer[] = "ackv1";
EthernetUDP Udp;
int led1 = 2;
int led2 = 3;
void setup() {
Ethernet.begin(mac,ip);
//Ethernet.begin(mac, ip, '8.8.8.8', gateway, subnet);
Udp.begin(localPort);
pinMode(led1, OUTPUT);
pinMode(led2, OUTPUT);
digitalWrite(led1, LOW);
digitalWrite(led2, LOW);
}
void loop() {
int packetSize = Udp.parsePacket();
if(packetSize) {
delay(1000);
digitalWrite(led1, HIGH); // turn the LED off by making the voltage LOW
delay(3000);
digitalWrite(led1, LOW); // turn the LED on (HIGH is the voltage level)
delay(1000);
digitalWrite(led2, HIGH); // turn the LED off by making the voltage LOW
delay(3000);
digitalWrite(led2, LOW); // turn the LED on (HIGH is the voltage level)
Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
Udp.write(ReplyBuffer);
Udp.endPacket();
}
delay(10);
}
You could use uhubctl - command line utility to control USB power per port for compatible USB hubs.
It works only on hubs that support per-port power switching, but note that many modern motherboards have USB hubs that support this feature. Also, last version of uhubctl supports USB 3.0 hubs, and good news is that quite a few new USB 3.0 hubs are supporting this feature.
To compile:
git clone https://github.com/mvp/uhubctl
cd uhubctl
make
To list status of all hubs and ports that can be controlled by uhubctl:
uhubctl
To turn off power on port 5 of single compatible hub:
uhubctl -a 0 -p 5
To turn on power for all ports of all compatible hubs:
uhubctl -a 1
To toggle power off then on:
uhubctl -a 2 -p 5
Read more here.
Disclosure - I am the author of uhubctl.