4
votes

I'm looking for a way to turn power off (and back on) for a USB port. Solution can be in C, bash, etc. I'm using a BeagleBone running 32-bit Ubuntu 16.04 for armhf.

> uname -srvm
Linux 4.4.6-ti-r15 #1 SMP Tue Apr 5 12:32:22 UTC 2016 armv7l

I've tried many things discussed on StackOverflow and AskUbuntu, including:

#include <linux/usbdevice_fs.h>

int main(void)
{
    int fd = open( "/dev/bus/usb/001/002", O_WRONLY );
    if (fd < 0) return 1;

    int rc = ioctl( fd, USBDEVFS_RESET, 0 );
    if (rc < 0) return 2;

    close( fd );
    return 0;
}

The USB device I need to turn off (and eventually back on) is a Champtek FS310 barcode reader which shows up as a magnetic card strip reader when I run lsusb:

> lsusb
Bus 001 Device 002: ID 040b:6543 Weltrend Semiconductor Manhattan Magnetic Card Strip Reader
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

> lsusb -t
/:  Bus 01.Port 1: Dev 1, Class=root_hub, Driver=musb-hdrc/1p, 480M
    |__ Port 1: Dev 2, If 0, Class=Human Interface Device, Driver=usbhid, 1.5M
    |__ Port 1: Dev 2, If 1, Class=Human Interface Device, Driver=usbhid, 1.5M

I've found that running these two commands results in the device turning off:

echo "1-1" > /sys/bus/usb/drivers/usb/unbind
echo "1-1" > /sys/bus/usb/drivers/usb/bind

Strangely enough, it only turns off during "bind", not "unbind". But once it turns off this way, the only way I've found to turn it back on is to reboot the computer, which is not a usable solution.

1
Possible duplicate of Turn USB power off/on with BeagleBone Black kernel >= 3.8user149341

1 Answers

3
votes

Indeed, that other question did have a technique that worked for what I was trying to do. Note this isn't a generic Linux answer, it will only work on BeagleBone Black and similar devices. (I tested on a BeagleBone Green.) Working backwards from the devmem2 example, this block of C++ code turns the USB power off, then back on:

const size_t page_size_in_bytes = getpagesize();
const size_t address_gpio3_13   = 0x47401c60; // see comment below
const size_t address_start      = address_gpio3_13 / page_size_in_bytes * page_size_in_bytes;
const size_t address_offset     = address_gpio3_13 - address_start;

int fd = open("/dev/mem", O_RDWR);
void *addr = mmap( 0, page_size_in_bytes, PROT_READ | PROT_WRITE, MAP_SHARED, fd, address_start );

uint8_t *byte_ptr = reinterpret_cast<uint8_t*>(addr);

byte_ptr[address_offset] = 0x00;    // turn off USB
std::this_thread::sleep_for( std::chrono::milliseconds(500) );
byte_ptr[address_offset] = 0x01;    // turn on USB

munmap( addr, page_size_in_bytes );

close(fd);

(Error handling not included.)

The magic number 0x47401c60 really is a magic number. According to some posts, it looks like a NDA needs to be signed to get access to some of the USB-related documentation. In the ARM335X Technical Reference Manual, the only mention of the 0x47401Cxx address space is the following on page 156:

Block Name    Start Address    End Address
USB1 Core     0x4740_1C00      0x4740_1FFF