Reset your USB device on Ubuntu when you can't unplug it

Hello there,

sometime ago an USB device stopped working at a POS -Point of Sale- after it was running for a long period.

Unknown reason, after searching, it had some kind of EMP (electromagnetic pulse): something happens between the USB connectors (motherboard), cable... whatever.

Manual solution was not valid: unplug/plug the USB cable or powercord when the problem appeared (we know it as soon as it doesn't respond the operations).

So the action is to reset its module/driver (OS) and the USB device.


First issue

The OS module in this case is CDC_ACM, ok, but the path to communicate with the device (using netcat/socat tools) is not the same to restart it.

We must guess as much as device's information as we can to guarante that we reset the exact one.

How do we do this?

Let's assume the terminal path is "/dev/ttyACM0", device is connected, open your terminal and type this:
>udevadm info -q path -n /dev/ttyACM0
/devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0/tty/ttyACM0

With this we get an address of the device where we'll get extract more info about it.

The following command shows:
>udevadm info -a -p /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0/tty/ttyACM0

Udevadm info starts with the device specified by the devpath and then
walks up the chain of parent devices. It prints for every device
found, all possible attributes in the udev rules key format.
A rule to match, can be composed by the attributes of the device
and the attributes from one single parent device.

  looking at device '/devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0/tty/ttyACM0':
    KERNEL=="ttyACM0"
    SUBSYSTEM=="tty"
    DRIVER==""

  looking at parent device '/devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0':
    KERNELS=="1-1.2:1.0"
    SUBSYSTEMS=="usb"
    DRIVERS=="cdc_acm"
    ATTRS{bInterfaceClass}=="02"
    ATTRS{bmCapabilities}=="6"
    ATTRS{bInterfaceSubClass}=="02"
    ATTRS{bInterfaceProtocol}=="01"
    ATTRS{bNumEndpoints}=="01"
    ATTRS{supports_autosuspend}=="1"
    ATTRS{bAlternateSetting}==" 0"
    ATTRS{bInterfaceNumber}=="00"

  looking at parent device '/devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2':
    KERNELS=="1-1.2"
    SUBSYSTEMS=="usb"
    DRIVERS=="usb"
    ATTRS{bDeviceSubClass}=="00"
    ATTRS{bDeviceProtocol}=="00"
    ATTRS{devpath}=="1.2"
    ATTRS{idVendor}=="11cb"
    ATTRS{speed}=="12"
    ATTRS{bNumInterfaces}==" 2"
    ATTRS{bConfigurationValue}=="1"
    ATTRS{bMaxPacketSize0}=="64"
    ATTRS{busnum}=="1"
    ATTRS{devnum}=="4"
    ATTRS{configuration}==""
    ATTRS{bMaxPower}==" 50mA"
    ATTRS{authorized}=="1"
    ATTRS{bmAttributes}=="c0"
    ATTRS{bNumConfigurations}=="1"
    ATTRS{maxchild}=="0"
    ATTRS{bcdDevice}=="0100"
    ATTRS{avoid_reset_quirk}=="0"
    ATTRS{quirks}=="0x0"
    ATTRS{version}==" 1.10"
    ATTRS{urbnum}=="86"
    ATTRS{manufacturer}=="ACME Inc"
    ATTRS{removable}=="removable"
    ATTRS{idProduct}=="0219"
    ATTRS{bDeviceClass}=="02"
    ATTRS{product}=="Trident USB Device 1.1"

  looking at parent device '/devices/pci0000:00/0000:00:1a.0/usb1/1-1':
    KERNELS=="1-1"
    SUBSYSTEMS=="usb"
    DRIVERS=="usb"
    ATTRS{bDeviceSubClass}=="00"
    ATTRS{bDeviceProtocol}=="01"
    ATTRS{devpath}=="1"
    ATTRS{idVendor}=="8087"
    ATTRS{speed}=="480"
    ATTRS{bNumInterfaces}==" 1"
    ATTRS{bConfigurationValue}=="1"
    ATTRS{bMaxPacketSize0}=="64"
    ATTRS{busnum}=="1"
    ATTRS{devnum}=="2"
    ATTRS{configuration}==""
    ATTRS{bMaxPower}=="  0mA"
    ATTRS{authorized}=="1"
    ATTRS{bmAttributes}=="e0"
    ATTRS{bNumConfigurations}=="1"
    ATTRS{maxchild}=="4"
    ATTRS{bcdDevice}=="0000"
    ATTRS{avoid_reset_quirk}=="0"
    ATTRS{quirks}=="0x0"
    ATTRS{version}==" 2.00"
    ATTRS{urbnum}=="54"
    ATTRS{removable}=="fixed"
    ATTRS{idProduct}=="0024"
    ATTRS{bDeviceClass}=="09"

  looking at parent device '/devices/pci0000:00/0000:00:1a.0/usb1':
    KERNELS=="usb1"
    SUBSYSTEMS=="usb"
    DRIVERS=="usb"
    ATTRS{bDeviceSubClass}=="00"
    ATTRS{bDeviceProtocol}=="00"
    ATTRS{devpath}=="0"
    ATTRS{idVendor}=="1d6b"
    ATTRS{speed}=="480"
    ATTRS{bNumInterfaces}==" 1"
    ATTRS{bConfigurationValue}=="1"
    ATTRS{bMaxPacketSize0}=="64"
    ATTRS{authorized_default}=="1"
    ATTRS{busnum}=="1"
    ATTRS{devnum}=="1"
    ATTRS{configuration}==""
    ATTRS{bMaxPower}=="  0mA"
    ATTRS{authorized}=="1"
    ATTRS{bmAttributes}=="e0"
    ATTRS{bNumConfigurations}=="1"
    ATTRS{maxchild}=="2"
    ATTRS{bcdDevice}=="0305"
    ATTRS{avoid_reset_quirk}=="0"
    ATTRS{quirks}=="0x0"
    ATTRS{serial}=="0000:00:1a.0"
    ATTRS{version}==" 2.00"
    ATTRS{urbnum}=="24"
    ATTRS{manufacturer}=="Linux 3.5.0-54-generic ehci_hcd"
    ATTRS{removable}=="unknown"
    ATTRS{idProduct}=="0002"
    ATTRS{bDeviceClass}=="09"
    ATTRS{product}=="EHCI Host Controller"

  looking at parent device '/devices/pci0000:00/0000:00:1a.0':
    KERNELS=="0000:00:1a.0"
    SUBSYSTEMS=="pci"
    DRIVERS=="ehci_hcd"
    ATTRS{irq}=="23"
    ATTRS{subsystem_vendor}=="0x1043"
    ATTRS{broken_parity_status}=="0"
    ATTRS{class}=="0x0c0320"
    ATTRS{companion}==""
    ATTRS{consistent_dma_mask_bits}=="32"
    ATTRS{dma_mask_bits}=="32"
    ATTRS{local_cpus}=="ff"
    ATTRS{device}=="0x1c2d"
    ATTRS{uframe_periodic_max}=="100"
    ATTRS{msi_bus}==""
    ATTRS{local_cpulist}=="0-7"
    ATTRS{vendor}=="0x8086"
    ATTRS{subsystem_device}=="0x844d"

  looking at parent device '/devices/pci0000:00':
    KERNELS=="pci0000:00"
    SUBSYSTEMS==""
    DRIVERS==""

We have several devices and we want to reset one USB device, not all.

So we analyze the above output checking for some attributes that can match with our 'ACME' device: manufacturer and product (name). SUBSYSTEMS and DRIVERS attributes must be "usb".

If you know more about the device like idVendor, idProduct... you will have more precision to find it.

Ok, it seems our device info is in the section (path):
'/devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2'


Second issue

Unfortunately, this path cannot be used to reset the device, but it has info we need.

To get the 'real' path of the USB device, we need to find the 'busnum' and 'devnum' attributes from the device section:

    ATTRS{busnum}=="1"
    ATTRS{devnum}=="4"

Finally we can compose the USB path:
/dev/bus/usb/{busnum}/{devnum} or
/dev/bus/usb/001/004


Resetting the module and USB device

In terminal you can unload/load the CDC_ACM module like this (admin mode):
>rmmod cdc_acm
>modprobe cdc_acm

After that, in C/C++ using the USB path we do the following:
    int fd = open(USBdevicePath, O_WRONLY);
    if (fd < 0) {       
        printf ("Error opening USB device path\n");
    }
    else
    {
        printf("Resetting USB device %s\n", USBdevicePath);

        rc = ioctl(fd, USBDEVFS_RESET, 0);

        if (rc < 0) {
          printf ("Error in ioctl\n");
        }
        else
        {
          printf("Reset successful\n");
        }

        close(fd);
     }


If there is not any error, your USB device will respond again at your commands.

How to compile OpenSSL with Visual Studio

Hi there! Sometimes there is not precompiled OpenSSL available for the latest Visual C++ runtime, and you need it. Here it is a quick gu...