Hacking per-port power switching into an USB hub
This post will show you how to modify a USB hub to enable per-port software controlled power switching
Recently I started working on automating the second stage backups on my server. In contrast to plugging in manually a hard drive and start the backup script by hand I wanted to have a cron job which does the thing automatically. For this to work, the hard drive needs to be connected to the system all the time.
My server at home is an ODROID embedded board. One of the reasons, besides many others, why I prefer this over a “virtual private server” is cost. The ODROID only consumes 1.5W in idle, which is equivalent to 4€ per year. When I added my second stage backup hard disk, I noticed that the power consumption increased by 2W. Note that this is the consumption when the disk is not spinning. This is quite horrible, because the disk needs to run only once a month for some hours. Thats where my hacker instincs said: “There has to be done something to make this better”.
My first idea was to use one of those manually switchable hubs as seen in the image below.
I would have thrown an AVR in there, connected it to one of the USB ports and replaced the mechanical switches by transistors. This way I could have switched the hard disk completely off. This looked doable but also a bit too complicated. That’s why I continued to look if the USB standard offers a similar functionality. And apparently it does! It is call per port power switching (PPPS). Nice! See http://tiebing.blogspot.de/2011/01/use-linux-to-control-outlet.html for an introduction on how to check if your hub is compatible.
However, although most of the USB hubs advertise that they are able to do PPPS, they actually are not supporting this feature. The reason for this is that the chipsets inside the hub usually support PPPS (and advertise themselves as so) but the manufacturer of the hub omitted the switches (to save costs) and instead connected the USB ports directly to +5V.
The USB hub I had (no-name china crap “ID 0409:0059 NEC Corp. HighSpeed Hub”) did not advertise itself to support PPPS. I cracked it open anyways to see what chip it used. It was the uPD720112 from NEC. And according to the datasheet, it does support PPPS. The trick to enable it was to tie the configuration pin GANG_B to +3.3V. After that the chip did advertise itself as PPPS compatible :) Of course PPPS was still not working as this hub is missing the power switches.
According to the datasheet, the output pins PPB(1:4) for power control of the ports are open-drain Nmos active low drivers which are 5V tolerant. This is exactly what you need for controlling a high-side Pmos switch. Nice! All that there was to do is to cut the VBUS connection to the USB port and add a transistor and a resistor. The circuit of all this is below:
A picture of the modified USB hub with one switchable port (note that this picture shows my first attempt using a PNP bipolar instead of a Pmos):
And a picture of the hub controller where I patched into with some enameled wire:
To switch the port on and off, I used the following program: http://www.advistatech.com/software/hub-ctrl-20060120.c.
A nice feature in the case of a hard drive connected to a switchable port is that you can make the power switching transparent to the user. This can be done by the help of autofs.
The auto.master file looks like this:
The contents of auto.usbdisk are:
#!/bin/bash ./hub -b 1 -d 4 -P 1 -p 1 sleep 2 echo "-fstype=auto :/dev/$1"
The auto.usbdisk file is a shell script and needs the rights 755. To explain what this script does: ./hub is the program mentioned above to switch the USB port power on and off. The parameters are:
-b 1: USB bus id to which the hub is connected
-d 4: USB device id to which the hub is connected
-P 1: USB hub port which shall be switched on
-p 1: switch the power on
The parameters for the bus and device id of the hub can be found with the command lsusb. After switching on the port, the script waits for two seconds until the hard disk is recogcnized by Linux. Then the location of the hard disk is reported back to autofs by the echo statement.
If you access /mnt/usbdisk/sda1, autofs will call the script above, which turns on power to the hard disk. After that, autofs will mount the file system and you can access the data. Pretty neat!
After a timeout (the default is 600s) autofs will unmount the file system. I haven’t found a hook to this event to also turn off the power of the usb port. Instead, I wrote a little script which checks if the disk is still mounted. If this is not the case, it is safe to turn off the power.
#!/bin/bash if [ $# -ne 4 ] then echo "Usage: $0 <disk device> <busid> <deviceid> <portnum>" exit 1 fi if mount | grep "$1 on" > /dev/null then echo "$0: $1 is mounted. Doing nothing" else echo "$0: $1 is unmounted. Turning power off on bus $2 device $3 port $4" ./hub -b $2 -d $3 -P $4 -p fi
The script is called at a regular interval by cron. The crontab line for calling it every 15 minutes looks like this:
*/15 * * * * power_off_unmounted_disks.sh /dev/sda1 1 4 1
To be honest, a fixed bus id and device id is not the most elegant thing. A script which finds the bus and device id of the hub automatically is still on my todo list.
So, in summary this post showed you how to enable per-port power switching to off-the-shelve USB hubs. In my case, I use it for switching off two hard disks, a USB TV receiver and a USB sound card. This saves around 10W, which is equivalent to 30€ per year. The best thing about this is that this does not create any overhead for me as the devices are all powered on and off automatically.
UPDATE: Finished scripts
You can get the finished scripts here:
hg clone https://bitbucket.org/befi/general
(The scripts are inside the subdirectory usb_hup_ppps_scripts )
How to use them:
ln -s /dev/disk/by-uuid/7865fdg67fh6fd7888dfg5d /opt/data_partition
The name here is important. The first part is the name of the partition, _partition needs to be fixed!
*/15 * * * * power_off_unmounted_disks /opt/data_partition
This can be done with as many disks as you like. Only the first and the last step have to be repeated to add another disk.
How to use all this? Well, simply access /mnt/data and the disk will be powered on and mounted. That’s all :) The crontab entry then takes care of removing the power.
To explain the scripts: