Linux USB: turning the power on and off?

后端 未结 13 1317
遥遥无期
遥遥无期 2020-12-07 10:41

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

相关标签:
13条回答
  • 2020-12-07 10:57

    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.

    0 讨论(0)
  • 2020-12-07 10:57

    Your are running just "echo" as root, try:

    echo suspend | sudo tee /sys/bus/usb/devices/usb3/power/level
    
    0 讨论(0)
  • 2020-12-07 10:58

    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.

    0 讨论(0)
  • 2020-12-07 11:00

    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.

    0 讨论(0)
  • 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);
    }
    
    0 讨论(0)
  • 2020-12-07 11:04

    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.

    0 讨论(0)
提交回复
热议问题