views:

702

answers:

3

Alright. I figured it out. transfer.flags needed to be a byte instead of an int. Silly me. Now I'm getting an error code from ioctl, errno 16, which I think means the device is busy. What a workaholic. I've asked on the libusb mailing list.

Below is what I have so far. This isn't really that much code. Most of it is ctypes structures for libusb. Scroll down to the bottom to see the actual code where the error occurs.

from ctypes import *

VENDOR_ID = 0x04d8
PRODUCT_ID = 0xc002
_USBLCD_MAX_DATA_LEN = 24
LIBUSB_ENDPOINT_IN = 0x80
LIBUSB_ENDPOINT_OUT = 0x00

class EnumerationType(type(c_uint)):  
    def __new__(metacls, name, bases, dict):  
     if not "_members_" in dict:  
      _members_ = {}  
      for key,value in dict.items():  
       if not key.startswith("_"):  
        _members_[key] = value  
      dict["_members_"] = _members_  
     cls = type(c_uint).__new__(metacls, name, bases, dict)  
     for key,value in cls._members_.items():  
      globals()[key] = value  
     return cls  

    def __contains__(self, value):
     return value in self._members_.values()

    def __repr__(self):
     return "<Enumeration %s>" % self.__name__

class Enumeration(c_uint):
    __metaclass__ = EnumerationType
    _members_ = {}
    def __init__(self, value):
     for k,v in self._members_.items():
      if v == value:
       self.name = k
       break
     else:
      raise ValueError("No enumeration member with value %r" % value)
     c_uint.__init__(self, value)


    @classmethod
    def from_param(cls, param):
     if isinstance(param, Enumeration):
      if param.__class__ != cls:
       raise ValueError("Cannot mix enumeration members")
      else:
       return param
     else:
      return cls(param)

    def __repr__(self):
     return "<member %s=%d of %r>" % (self.name, self.value, self.__class__)


class LIBUSB_TRANSFER_STATUS(Enumeration):
    _members_ = {'LIBUSB_TRANSFER_COMPLETED':0,
      'LIBUSB_TRANSFER_ERROR':1,
      'LIBUSB_TRANSFER_TIMED_OUT':2,
      'LIBUSB_TRANSFER_CANCELLED':3,
      'LIBUSB_TRANSFER_STALL':4,
      'LIBUSB_TRANSFER_NO_DEVICE':5,
      'LIBUSB_TRANSFER_OVERFLOW':6}

class LIBUSB_TRANSFER_FLAGS(Enumeration):
    _members_ = {'LIBUSB_TRANSFER_SHORT_NOT_OK':1<<0,
      'LIBUSB_TRANSFER_FREE_BUFFER':1<<1,
      'LIBUSB_TRANSFER_FREE_TRANSFER':1<<2}

class LIBUSB_TRANSFER_TYPE(Enumeration):
    _members_ = {'LIBUSB_TRANSFER_TYPE_CONTROL':0,
      'LIBUSB_TRANSFER_TYPE_ISOCHRONOUS':1,
      'LIBUSB_TRANSFER_TYPE_BULK':2,
      'LIBUSB_TRANSFER_TYPE_INTERRUPT':3}

class LIBUSB_CONTEXT(Structure):
    pass

class LIBUSB_DEVICE(Structure):
    pass

class LIBUSB_DEVICE_HANDLE(Structure):
    pass

class LIBUSB_CONTROL_SETUP(Structure):
    _fields_ = [("bmRequestType", c_int),
      ("bRequest", c_int),
      ("wValue", c_int),
      ("wIndex", c_int),
      ("wLength", c_int)]

class LIBUSB_ISO_PACKET_DESCRIPTOR(Structure):
    _fields_ = [("length", c_int),
      ("actual_length", c_int),
      ("status", LIBUSB_TRANSFER_STATUS)]

class LIBUSB_TRANSFER(Structure):
    pass

LIBUSB_TRANSFER_CB_FN = CFUNCTYPE(c_void_p, POINTER(LIBUSB_TRANSFER))

LIBUSB_TRANSFER._fields_ = [("dev_handle", POINTER(LIBUSB_DEVICE_HANDLE)),
            ("flags", c_ubyte),
            ("endpoint", c_ubyte),
            ("type", c_ubyte),
            ("timeout", c_uint),
            ("status", LIBUSB_TRANSFER_STATUS),
            ("length", c_int),
            ("actual_length", c_int),
            ("callback", LIBUSB_TRANSFER_CB_FN),
            ("user_data", c_void_p),
            ("buffer", POINTER(c_ubyte)),
            ("num_iso_packets", c_int),
            ("iso_packet_desc", POINTER(LIBUSB_ISO_PACKET_DESCRIPTOR))]


class TIMEVAL(Structure):
    _fields_ = [('tv_sec', c_long), ('tv_usec', c_long)]

lib = cdll.LoadLibrary("libusb-1.0.so")
lib.libusb_open_device_with_vid_pid.restype = POINTER(LIBUSB_DEVICE_HANDLE)
lib.libusb_alloc_transfer.restype = POINTER(LIBUSB_TRANSFER)

def libusb_fill_interrupt_transfer(transfer, dev_handle, endpoint, buffer, length, callback, user_data, timeout):
    transfer[0].dev_handle = dev_handle
    transfer[0].endpoint = chr(endpoint)
    transfer[0].type = chr(LIBUSB_TRANSFER_TYPE_INTERRUPT)
    transfer[0].timeout = timeout
    transfer[0].buffer = buffer
    transfer[0].length = length
    transfer[0].user_data = user_data
    transfer[0].callback = LIBUSB_TRANSFER_CB_FN(callback)

def cb_transfer(transfer):
    print "Transfer status %d" % transfer.status

if __name__ == "__main__":
    context = POINTER(LIBUSB_CONTEXT)()
    lib.libusb_init(None)
    transfer = lib.libusb_alloc_transfer(0)
    handle = lib.libusb_open_device_with_vid_pid(None, VENDOR_ID, PRODUCT_ID)
    size = _USBLCD_MAX_DATA_LEN
    buffer = c_char_p(size)
    libusb_fill_interrupt_transfer(transfer, handle, LIBUSB_ENDPOINT_IN + 1, buffer, size, cb_transfer, None, 0)

    r = lib.libusb_submit_transfer(transfer) # This is returning -2, should be => 0.
    if r < 0:
     print "libusb_submit_transfer failed", r

    while r >= 0:
     print "Poll before"
     tv = TIMEVAL(1, 0)
     r = lib.libusb_handle_events_timeout(None, byref(tv))
     print "Poll after", r
A: 

where is the initial declaration of transfer? I am not familiar with python, but is this ok to assign values to fields in your struct without defining what data type it should be?

A.Rashad
Well, in Python you don't have to declare types. You can dynamically assign values of different types to variables. However, ctypes needs to know some information about the types. The return type of lib.libusb_alloc_transfer is declared here: lib.libusb_alloc_transfer.restype = POINTER(LIBUSB_TRANSFER)
Scott
+1  A: 
  • Have you checked to make sure the return values of libusb_alloc_transfer and libusb_open_device_with_vid_pid are valid?
  • Have you tried annotating the library functions with the appropriate argtypes?
  • You may run in to trouble with transfer[0].callback = LIBUSB_TRANSFER_CB_FN(callback)—you're not keeping any references to the CFunctionType object returned from LIBUSB_TRANSFER_CB_FN(), and so that object might be getting released and overwritten.

The next step, I suppose, would be to install a version of libusb with debugging symbols, boot up GDB, set a breakpoint at libusb_submit_transfer(), make sure the passed-in libusb_transfer is sane, and see what's triggering the error to be returned.

Miles
I'm a bit confused, Miles. Isn't transfer[0].callback a reference?
Scott
I'm not 100% sure about this, but as far as I know: only the transfer variable exists as a Python object. When you assign to one of the pointed-to struct's members, the ctypes library sets the value in the struct directly, but does not maintain a reference to the ctypes object that wraps the value. If you access transfer[0].callback, you will obtain a new ctypes wrapper object—one with a different id() than the one you assigned in. You must keep the original LIBUSB_TRANSFER_CB_FN object alive for the callback to continue to be valid.
Miles
A: 

Running it as root once fixed the busy flag.

Scott