1 '''
2 The Windows (Win32) HID interface module.
3 Dynamically loaded on Windows platforms.
4 Refer to the hid module for available functions
5 '''
6
7
8
9
10 import logging
11 import struct
12
13 from ctypes import *
14 from ctypes.wintypes import *
15
16 from hid import HIDDevice
17
18 DIGCF_ALLCLASSES=0x00000004
19 DIGCF_DEVICEINTERFACE=0x00000010
20 DIGCF_PRESENT=0x00000002
21 DIGCF_PROFILE=0x00000008
22
23 FORMAT_MESSAGE_FROM_SYSTEM=0x00001000
24 FORMAT_MESSAGE_ALLOCATE_BUFFER=0x00000100
25 FORMAT_MESSAGE_IGNORE_INSERTS=0x00000200
26
27 GENERIC_READ=0x80000000
28 GENERIC_WRITE=0x40000000
29
30 FILE_SHARE_READ=0x00000001
31 FILE_SHARE_WRITE=0x00000002
32
33 OPEN_EXISTING=3
34
35 INVALID_HANDLE_VALUE=-1
36
37 FILE_FLAG_OVERLAPPED=0x40000000
38
39
40 WAIT_TIMEOUT=0x00000102
41 WAIT_OBJECT_0=0x00000000
42
43
44 GUID=c_uint8*16
45 USHORT=c_ushort
46
47 LPVOID=c_void_p
48 LPCVOID=c_void_p
49
50
51 HidGuid=GUID()
52 hid_dll=windll.hid
53 hid_dll.HidD_GetHidGuid(byref(HidGuid))
54
55 setupapi_dll=windll.setupapi
56
57 Kernel32=windll.Kernel32
58
59 ULONG_PTR=ULONG
60
62 _fields_ = [
63 ("Internal", ULONG_PTR),
64 ("InternalHigh", ULONG_PTR),
65 ("Offset", DWORD),
66 ("OffsetHigh", DWORD),
67 ("hEvent",HANDLE)
68 ]
70 self.Offset=0
71 self.OffsetHigh=0
72
73 LPOVERLAPPED=POINTER(OVERLAPPED)
74
75
76 LPOVERLAPPED_COMPLETION_ROUTINE=WINFUNCTYPE(None,DWORD,DWORD,LPOVERLAPPED)
77
78
79 ReadFileEx=Kernel32.ReadFileEx
80 ReadFileEx.argtypes = [HANDLE,LPVOID,DWORD,LPOVERLAPPED,LPOVERLAPPED_COMPLETION_ROUTINE]
81
82 WriteFileEx=Kernel32.WriteFileEx
83 WriteFileEx.argtypes = [HANDLE,LPCVOID,DWORD,LPOVERLAPPED,LPOVERLAPPED_COMPLETION_ROUTINE]
84
96
98 _fields_ = [
99 ("cbSize", DWORD),
100 ("InterfaceClassGuid", GUID),
101 ("Flags", DWORD),
102 ("Reserved", POINTER(ULONG))
103 ]
105 self.cbSize=sizeof(self)
106
108 '''dynamically declare the structure, so we will have the right size
109 allocated for the DevicePath field
110 However cbSize will be the size of the _fixed_ size
111 '''
112
113
114
115
116 class SP_DEVICE_INTERFACE_DETAIL_DATA(Structure):
117 _fields_ = [
118 ("cbSize", DWORD),
119 ("DevicePath", c_char * size),
120 ]
121 detailData=SP_DEVICE_INTERFACE_DETAIL_DATA()
122 detailData.cbSize=sizeof(DWORD)+sizeof(c_char*1)
123 return detailData
124
126 _fields_ = [
127 ("Size", ULONG),
128 ("VendorID", USHORT),
129 ("ProductID", USHORT),
130 ("VersionNumber", USHORT)
131 ]
133 self.Size=sizeof(self)
134
135
137
138 - def __init__(self,device_path,vendor,product):
139 HIDDevice.__init__(self,vendor,product)
140 self._device_path=device_path
141
142 self._device_handle=None
143 self._CloseHandle=Kernel32.CloseHandle
144
146 return self._device_handle is not None
147
158
160 self._running=False
161 if not self.is_open():
162 logging.info("opening device")
163 self._device_handle=self._open_handle()
164
165 if self._device_handle == INVALID_HANDLE_VALUE:
166 self._device_handle=None
167 raise RuntimeError("could not open device")
168 else:
169 self._write_overlapped=OVERLAPPED()
170
171
172
173
175
176 HIDDevice.close(self)
177
178 if self._device_handle:
179
180 import logging
181 logging.info("closing _device_handle")
182 self._CloseHandle(self._device_handle)
183 self._device_handle=None
184
185 self._write_overlapped=None
186
187
189 '''
190 "set" a report - send the data to the device (which must have been opened previously)
191 '''
192 HIDDevice.set_report(self,report_data,report_id)
193
194 report_buffer=(c_ubyte*(len(report_data)+1))()
195 report_buffer[0]=report_id
196 for i,c in enumerate(report_data):
197 report_buffer[i+1]=struct.unpack('B',c)[0]
198
199 def completion_callback(dwErrorCode,dwNumberOfBytesTransfered,lpOverlapped):
200 pass
201
202 overlap_completion=LPOVERLAPPED_COMPLETION_ROUTINE(completion_callback)
203
204 result=WriteFileEx(
205 self._device_handle,
206 report_buffer,
207 len(report_buffer),
208 self._write_overlapped,
209 overlap_completion )
210
211 if not result:
212 raise RuntimeError("WriteFileEx failed")
213
214 if Kernel32.SleepEx(100,1) == 0:
215 raise RuntimeError("timed out when writing to device")
216
217
219 '''
220 run on a thread to handle reading events from the device
221 '''
222 if not self.is_open():
223 raise RuntimeError("device not open")
224
225 logging.info("starting _run_interrupt_callback_loop")
226
227
228 report_buffer=(c_ubyte*(report_buffer_size+1))()
229 overlapped=OVERLAPPED()
230
231 def completion_callback(dwErrorCode,dwNumberOfBytesTransfered,lpOverlapped):
232 report_data="".join([struct.pack('B',b) for b in report_buffer])
233 report_data=report_data[1:]
234 self._callback(self,report_data)
235
236 overlap_completion=LPOVERLAPPED_COMPLETION_ROUTINE(completion_callback)
237
238
239 while self._running and self.is_open():
240 result=ReadFileEx(self._device_handle,report_buffer,len(report_buffer),byref(overlapped),overlap_completion)
241 if not result:
242 raise RuntimeError("ReadFileEx failed")
243 Kernel32.SleepEx(100,1)
244
245
246 Kernel32.CancelIo(self._device_handle)
247
248
250 '''
251 query the host computer for all available HID devices
252 and returns a list of any found
253 '''
254 devices=[]
255 hDevInfo=setupapi_dll.SetupDiGetClassDevsA(byref(HidGuid),None,None,DIGCF_PRESENT | DIGCF_DEVICEINTERFACE)
256
257 try:
258 for memberIndex in range(0,256):
259 deviceInterface=SP_DEVICE_INTERFACE_DATA()
260
261 result=setupapi_dll.SetupDiEnumDeviceInterfaces(hDevInfo,0,byref(HidGuid),memberIndex,byref(deviceInterface))
262
263 if not result:
264 break
265
266 requiredSize=DWORD()
267
268
269 if not setupapi_dll.SetupDiGetDeviceInterfaceDetailA(hDevInfo,byref(deviceInterface),None,0,byref(requiredSize),None):
270 GetLastErrorMessage()
271
272
273 detailData=SP_DEVICE_INTERFACE_DETAIL_DATA_OF_SIZE(requiredSize.value)
274
275 if not setupapi_dll.SetupDiGetDeviceInterfaceDetailA(hDevInfo,byref(deviceInterface),byref(detailData),requiredSize,None,None):
276 raise RuntimeError(GetLastErrorMessage())
277
278 DeviceHandle=None
279 try:
280 DeviceHandle=Kernel32.CreateFileA(
281 detailData.DevicePath,
282 GENERIC_READ | GENERIC_WRITE,
283 FILE_SHARE_READ | FILE_SHARE_WRITE,
284 None,
285 OPEN_EXISTING,
286 0,
287 None
288 )
289
290
291 if DeviceHandle != INVALID_HANDLE_VALUE:
292 Attributes=HIDD_ATTRIBUTES()
293
294 result=hid_dll.HidD_GetAttributes(
295 DeviceHandle,
296 byref(Attributes)
297 )
298
299 if result:
300 device=Win32HIDDevice(detailData.DevicePath,Attributes.VendorID,Attributes.ProductID)
301 devices.append(device)
302 else:
303 logging.info("failed to open device to read attributes")
304
305 finally:
306 if DeviceHandle and DeviceHandle != INVALID_HANDLE_VALUE:
307 Kernel32.CloseHandle(DeviceHandle)
308 finally:
309 setupapi_dll.SetupDiDestroyDeviceInfoList(hDevInfo)
310
311 return devices
312
313 __all__ = ['find_hid_devices','Win32HIDDevice']
314