112
|
1 from ctypes import Structure, POINTER, CDLL
|
|
2 from ctypes import c_uint16, c_uint8, c_int, c_ssize_t
|
|
3 from ctypes import byref
|
|
4
|
|
5 # libusb wrapper:
|
|
6 libusb = CDLL('libusb-1.0.so')
|
|
7
|
|
8 # helper:
|
|
9 def buildfunc(name, argtypes, restype):
|
|
10 f = getattr(libusb, name)
|
|
11 f.argtypes = argtypes
|
|
12 f.restype = restype
|
113
|
13 globals()[name] = f
|
112
|
14 return f
|
|
15 def enum(**enums):
|
|
16 reverse = dict((value, key) for key, value in enums.items())
|
|
17 enums['reverse_mapping'] = reverse
|
|
18 return type('enum', (), enums)
|
|
19
|
|
20 # enums
|
|
21 libusb_class_code = enum(PER_INTERFACE=0, AUDIO=1, COMM=2, HID=3, \
|
|
22 PHYSICAL=5, PRINTER=7, PTP=6, MASS_STORAGE=8, HUB=9, \
|
|
23 DATA=10, SMART_CARD=0xb, CONTENT_SECURITY=0xd, VIDEO=0xe, \
|
|
24 PERSONAL_HEALTHCARE=0xf, DIAGNOSTIC_DEVICE=0xdc, WIRELESS=0xe,\
|
|
25 APPLICATION=0xfe, VENDOR_SPEC=0xff)
|
|
26 libusb_speed = enum(UNKNOWN=0, LOW=1, FULL=2, HIGH=3, SUPER=4)
|
|
27 libusb_error = enum(SUCCES=0, ERROR_IO=-1, ERROR_INVALID_PARAM=-2, \
|
|
28 ERROR_ACCESS=-3, ERROR_NO_DEVICE=-4, ERROR_NOT_FOUND=-5, \
|
|
29 ERROR_BUSY=-6, ERROR_TIMEOUT=-7, ERROR_OVERFLOW=-8, \
|
|
30 ERROR_PIPE=-9, ERROR_INTERRUPTED=-10, ERROR_NO_MEM=-11, \
|
|
31 ERROR_NOT_SUPPORTED=-12, ERROR_OTHER=-99)
|
|
32 libusb_transfer_status = enum(\
|
|
33 COMPLETED=0, ERROR=1, TIMED_OUT=2, \
|
|
34 CANCELLED=3, STALL=4, NO_DEVICE=5, OVERFLOW=6)
|
|
35
|
|
36 # types
|
|
37 class libusb_context(Structure):
|
|
38 pass
|
|
39 libusb_context_p = POINTER(libusb_context)
|
|
40 libusb_context_p_p = POINTER(libusb_context_p)
|
|
41
|
|
42 class libusb_device(Structure):
|
|
43 pass
|
|
44 libusb_device_p = POINTER(libusb_device)
|
|
45 libusb_device_p_p = POINTER(libusb_device_p)
|
|
46 libusb_device_p_p_p = POINTER(libusb_device_p_p)
|
|
47
|
113
|
48 class libusb_device_handle(Structure):
|
|
49 pass
|
|
50 libusb_device_handle_p = POINTER(libusb_device_handle)
|
|
51 libusb_device_handle_p_p = POINTER(libusb_device_handle_p)
|
|
52
|
112
|
53 class libusb_device_descriptor(Structure):
|
|
54 _fields_ = [
|
|
55 ('bLength', c_uint8),
|
|
56 ('bDescriptorType', c_uint8),
|
|
57 ('bcdUSB', c_uint16),
|
|
58 ('bDeviceClass', c_uint8),
|
|
59 ('bDeviceSubClass', c_uint8),
|
|
60 ('bDeviceProtocol', c_uint8),
|
|
61 ('bMaxPacketSize0', c_uint8),
|
|
62 ('idVendor', c_uint16),
|
|
63 ('idProduct', c_uint16),
|
|
64 ('bcdDevice', c_uint16),
|
|
65 ('iManufacturer', c_uint8),
|
|
66 ('iProduct', c_uint8),
|
|
67 ('iSerialNumber', c_uint8),
|
|
68 ('iNumConfigurations', c_uint8)
|
|
69 ]
|
|
70 libusb_device_descriptor_p = POINTER(libusb_device_descriptor)
|
|
71
|
|
72 # functions
|
113
|
73 buildfunc('libusb_init', [libusb_context_p_p], c_int)
|
112
|
74
|
113
|
75 buildfunc('libusb_get_device_list', \
|
112
|
76 [libusb_context_p, libusb_device_p_p_p], c_ssize_t)
|
113
|
77 buildfunc('libusb_free_device_list', [libusb_device_p_p, c_int], None)
|
|
78 buildfunc('libusb_get_bus_number', [libusb_device_p], c_uint8)
|
|
79 buildfunc('libusb_get_device_address', [libusb_device_p], c_uint8)
|
|
80 buildfunc('libusb_get_device_speed', [libusb_device_p], c_int)
|
|
81 buildfunc('libusb_unref_device', [libusb_device_p], None)
|
|
82 buildfunc('libusb_open', [libusb_device_p, libusb_device_handle_p_p], c_int)
|
|
83 buildfunc('libusb_close', [libusb_device_handle_p], None)
|
|
84 buildfunc('libusb_get_configuration', \
|
|
85 [libusb_device_handle_p, POINTER(c_int)], c_int)
|
|
86 buildfunc('libusb_set_configuration', [libusb_device_handle_p, c_int], c_int)
|
|
87 buildfunc('libusb_claim_interface', [libusb_device_handle_p, c_int], c_int)
|
112
|
88
|
|
89
|
113
|
90 buildfunc('libusb_get_device_descriptor',\
|
|
91 [libusb_device_p, libusb_device_descriptor_p], c_int)
|
112
|
92
|
|
93 # pythonic API:
|
|
94
|
|
95 class UsbError(Exception):
|
|
96 def __init__(self, msg, errorcode):
|
|
97 if errorcode in libusb_error.reverse_mapping:
|
|
98 errorcode = libusb_error.reverse_mapping[errorcode]
|
|
99 msg = msg + 'Error code: {0}'.format(errorcode)
|
|
100 super().__init__(msg)
|
|
101
|
|
102 class UsbContext(object):
|
|
103 """ A usb context in case of multiple use """
|
|
104 def __init__(self):
|
|
105 self.context_p = libusb_context_p()
|
|
106 r = libusb_init(byref(self.context_p))
|
|
107 if r != 0:
|
|
108 raise UsbError('libusb_init error!', r)
|
|
109 def getDeviceList(self):
|
|
110 devlist = libusb_device_p_p()
|
|
111 count = libusb_get_device_list(self.context_p, byref(devlist))
|
|
112 if count < 0:
|
|
113 raise UsbError('Error getting device list', count)
|
|
114 l = [UsbDevice(self, device_p.contents) for device_p in devlist[0:count]]
|
|
115 libusb_free_device_list(devlist, 0)
|
|
116 return l
|
|
117 DeviceList = property(getDeviceList)
|
|
118
|
|
119 class UsbDevice:
|
|
120 """ A detected usb device """
|
|
121 def __init__(self, context, device_p):
|
|
122 self.context = context
|
|
123 self.dev_p = device_p
|
|
124 def __del__(self):
|
|
125 libusb_unref_device(self.dev_p)
|
|
126 def getBusNumber(self):
|
|
127 return libusb_get_bus_number(self.dev_p)
|
|
128 BusNumber = property(getBusNumber)
|
|
129 def getDeviceAddress(self):
|
|
130 return libusb_get_device_address(self.dev_p)
|
|
131 DeviceAddress = property(getDeviceAddress)
|
|
132 def getSpeed(self):
|
|
133 s = libusb_get_device_speed(self.dev_p)
|
|
134 if s in libusb_speed.reverse_mapping:
|
|
135 s = libusb_speed.reverse_mapping[s]
|
|
136 return s
|
|
137 Speed = property(getSpeed)
|
|
138 def getDescriptor(self):
|
|
139 descriptor = libusb_device_descriptor()
|
|
140 r = libusb_get_device_descriptor(self.dev_p, byref(descriptor))
|
|
141 if r != 0:
|
|
142 raise UsbError('Error getting descriptor', r)
|
|
143 return descriptor
|
|
144 Descriptor = property(getDescriptor)
|
|
145 VendorId = property(lambda self: self.Descriptor.idVendor)
|
|
146 ProductId = property(lambda self: self.Descriptor.idProduct)
|
|
147 NumConfigurations = property(lambda self: self.Descriptor.bNumConfigurations)
|
|
148 def open(self):
|
|
149 """ Opens this device and returns a handle """
|
|
150 handle_p = libusb_device_handle_p()
|
|
151 r = libusb_open(self.dev_p, byref(handle_p))
|
|
152 if r != 0:
|
|
153 raise UsbError('error opening device', r)
|
113
|
154 return UsbDeviceHandle(self, handle_p)
|
112
|
155 def __repr__(self):
|
113
|
156 r2 = 'Usb device: bus {0} address {1} {2:04X}:{3:04X} speed {4}' \
|
|
157 .format( \
|
|
158 self.BusNumber, self.DeviceAddress, self.VendorId, \
|
|
159 self.ProductId, self.Speed)
|
112
|
160 return r2
|
|
161
|
|
162 class UsbDeviceHandle:
|
|
163 """ Handle to a detected usb device """
|
|
164 def __init__(self, device, handle_p):
|
|
165 self.device = device
|
|
166 self.handle_p = handle_p
|
113
|
167 def getConfiguration(self):
|
|
168 config = c_int()
|
|
169 r = libusb_get_configuration(self.handle_p, byref(config))
|
|
170 if r != 0: raise UsbError('Error getting configuration', r)
|
|
171 return config.value
|
|
172 def setConfiguration(self, config):
|
|
173 r = libusb_set_configuration(self.handle_p, config)
|
|
174 if r != 0: raise UsbError('Error setting configuration', r)
|
|
175 Configuration = property(getConfiguration, setConfiguration)
|
|
176 def claimInterface(self, interface_number):
|
|
177 r = libusb_claim_interface(self.handle_p, interface_number)
|
|
178 if r != 0: raise UsbError('Error claiming interface', r)
|
|
179
|
112
|
180
|
|
181 class UsbTransfer:
|
|
182 def __init__(self):
|
|
183 libusb_alloc_transfer(0)
|