Trouble using PyUSB to read/write from usb device (timeouts)

RichP picture RichP · Jul 29, 2016 · Viewed 11.6k times · Source

I am trying to interface with a device connected to my Ubuntu 16 system(Python ver 2.7.12). When I try to write to the device, I am getting a write timeout (similar with reading from the device).

According to what little documentation I have, the HID interface is implemented in interface 3, in addition to the standard endpoint (EP)0 the device supports EP4 IN (device to host) interrupt transfer type, and EP5 OUT( host to device) interrupt transfer type. It also states the USB Bulk is based on request-response message pair with a message format of 64 bytes with the first byte being the 'op code'. For a 'get version' command the op-code is 0x02, and the rest is ignored.

I am new to both Python and USB, so probably have this wrong but I am trying to write to the 'OUT' interface and then read from the 'IN' interface. I have noticed that when I plug the device into the Ubuntu system I get a new device /dev/usb/hiddev0 but couldn't seem to write to that so tried to connect to interface 3 and grab the In/Out interfaces. Also I added in what I thought was the call for PYUSB_DEBUG but I do not see any more output than without it.

With the following code I do see the device, and I get an IN/OUT interface but write/reads always timeout. I do not know what I am missing. Thank you very much for any help

import usb.core
import usb.util
from usb.core import *
import sys
import os
import binascii
import time
import serial    
import itertools


idV = 0x2abf
idP = 0x0505

# doesnt seem to write anything to log?!
os.environ['PYUSB_DEBUG'] = 'debug'
#os.environ['PYUSB_LOG_FILENAME'] = "pyusb.log" #never written


print "finding idVendor = {}, idProduct= {}".format(idV, idP)
device = usb.core.find(idVendor=idV, idProduct=idP)

if device is None:
    print ("Device not found")
    exit()


# free up the device from the kernal
for cfg in device:
    for intf in cfg:
        if device.is_kernel_driver_active(intf.bInterfaceNumber):
            try:
                device.detach_kernel_driver(intf.bInterfaceNumber)
            except usb.core.USBError as e:
                sys.exit("Could not detach kernel driver from interface({0}): {1}".format(intf.bInterfaceNumber, str(e)))

# try default conf
print "setting configuration"
device.set_configuration()
print "config set"

print "trying to claim device"
try:
    usb.util.claim_interface(device, 0)
    print "claimed device"
except usb.core.USBError as e:
    print "Error occurred claiming " + str(e)
    sys.exit("Error occurred on claiming")
print "device claimed"

# get enpoint instance
cfg = device.get_active_configuration()
print "***********"
for intf in cfg:
    print "intf= " + str(intf)
print "***********"

# from document:
# The HID interface is implemented on Interface 3, in addition to standard endpoint (er)0, the device supports
# EP4 IN (device to host) interrupt transfer type, and EP5 OUT (host to device) interrupt transfer type
#  Note: EP$ seems to come back as 0x84 while EP5 comes back as 0x05
intf = cfg[(3, 0)]

# get the BULK OUT descriptor
epo = usb.util.find_descriptor(
    intf,
    # match our first out endpoint
    custom_match= \
        lambda e: \
            usb.util.endpoint_direction(e.bEndpointAddress) == \
            usb.util.ENDPOINT_OUT)

assert epo is not None

# get the BULK IN descriptor
epi = usb.util.find_descriptor(
    intf,
    # match our first out endpoint
    custom_match= \
        lambda e: \
            usb.util.endpoint_direction(e.bEndpointAddress) == \
            usb.util.ENDPOINT_IN)

assert epi is not None

print "write the data"
# commands are 64 bytes long, first byte is command code, 02 is 'get version', it doesn't need any of the other bytes set
try:
    # don't think I can use [0x00]*63 because it will be all pointers to same object?, call them out to be safe
    test = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
    mybuff = b'\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
    #device.write(epo,mybuff,1)         #timeout on write
    #epo.write(mybuff)                  #timeout on write
    epo.write(mybuff.encode('utf-8'))   #timeout on write
except usb.core.USBError as e:
    print "Write USBError: " + str(e)
    sys.exit()

print "done writing"
print "try read?"
try:
    #info = device.read(0x84, 8)
    info = epo.read(epi.bEndpointAddress,epi.wMaxPacketSize)
except usb.core.USBError as e:
    print "Read USBError: " + str(e)
    sys.exit()

print "read: " + str(info)

Output:

finding idVendor = 10943, idProduct= 1285
setting configuration
config set
trying to claim device
claimed device
device claimed
***********
< skipping audio interfaces>
intf=     INTERFACE 3: Human Interface Device ====================
     bLength            :    0x9 (9 bytes)
     bDescriptorType    :    0x4 Interface
     bInterfaceNumber   :    0x3
     bAlternateSetting  :    0x0
     bNumEndpoints      :    0x2
     bInterfaceClass    :    0x3 Human Interface Device
     bInterfaceSubClass :    0x0
     bInterfaceProtocol :    0x0
     iInterface         :    0x8 PlaylistControl
      ENDPOINT 0x84: Interrupt IN ==========================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :   0x84 IN
       bmAttributes     :    0x3 Interrupt
       wMaxPacketSize   :   0x40 (64 bytes)
       bInterval        :    0xa
      ENDPOINT 0x5: Interrupt OUT ==========================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :    0x5 OUT
       bmAttributes     :    0x3 Interrupt
       wMaxPacketSize   :   0x40 (64 bytes)
       bInterval        :    0xa
***********
write the data
Write USBError: [Errno 110] Operation timed out

Answer

rishta picture rishta · Dec 25, 2016

I suppose you are reusing the device while testing your code and your problems occur when you are executing it multiple times. You should use

usb.util.dispose_resources(device)

at the end of your code. Try using

device.reset()

after

device = usb.core.find(...)

and before doing anything else with the device if you suspect the earlier code run closed unclean.