Package hid :: Module win32
[frames] | no frames]

Source Code for Module hid.win32

  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  #http://permalink.gmane.org/gmane.comp.python.ctypes/2410
 
  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 # needed so we can read and write at the same time 
 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  
 
61 -class OVERLAPPED(Structure):
62 _fields_ = [ 63 ("Internal", ULONG_PTR), 64 ("InternalHigh", ULONG_PTR), 65 ("Offset", DWORD), 66 ("OffsetHigh", DWORD), 67 ("hEvent",HANDLE) 68 ]
69 - def __init__(self):
70 self.Offset=0 71 self.OffsetHigh=0
72 73 LPOVERLAPPED=POINTER(OVERLAPPED) 74 75 # callback function type for ReadFileEx and WriteFileEx 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
85 -def GetLastErrorMessage():
86 error=Kernel32.GetLastError() 87 Kernel32.SetLastError(0) 88 msg=c_char_p() 89 90 Kernel32.FormatMessageA( 91 FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS, 92 None,error,0,byref(msg), 0, None) 93 msgStr='error #%d: %s' % (error,msg.value) 94 Kernel32.LocalFree(msg) 95 return msgStr
96
97 -class SP_DEVICE_INTERFACE_DATA(Structure):
98 _fields_ = [ 99 ("cbSize", DWORD), 100 ("InterfaceClassGuid", GUID), 101 ("Flags", DWORD), 102 ("Reserved", POINTER(ULONG)) 103 ]
104 - def __init__(self):
105 self.cbSize=sizeof(self)
106
107 -def SP_DEVICE_INTERFACE_DETAIL_DATA_OF_SIZE(size):
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 # DevicePath is normally declared as being char[1], but 114 # that only works because of C's lax boundary checking 115 # so we'll dynamically declare the size here 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
125 -class HIDD_ATTRIBUTES(Structure):
126 _fields_ = [ 127 ("Size", ULONG), 128 ("VendorID", USHORT), 129 ("ProductID", USHORT), 130 ("VersionNumber", USHORT) 131 ]
132 - def __init__(self):
133 self.Size=sizeof(self)
134 135
136 -class Win32HIDDevice(HIDDevice):
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
145 - def is_open(self):
146 return self._device_handle is not None
147
148 - def _open_handle(self):
149 return Kernel32.CreateFileA( 150 self._device_path, 151 GENERIC_READ | GENERIC_WRITE, 152 FILE_SHARE_READ | FILE_SHARE_WRITE, 153 None, 154 OPEN_EXISTING, 155 FILE_FLAG_OVERLAPPED, 156 None 157 )
158
159 - def open(self):
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
174 - def close(self):
175 # make sure we stop the thread first 176 HIDDevice.close(self) 177 178 if self._device_handle: 179 # re-import logging, as may have been deleted already 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
188 - def set_report(self,report_data,report_id=0):
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 # first byte is 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
218 - def _run_interrupt_callback_loop(self,report_buffer_size):
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 # +1 to allow for report id byte 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:] # remove first byte (report id) 234 self._callback(self,report_data)
235 236 overlap_completion=LPOVERLAPPED_COMPLETION_ROUTINE(completion_callback) 237 238 # do async reads until the thread stops 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 # thread is stopping so make sure we won't receive any more messages 246 Kernel32.CancelIo(self._device_handle) 247 248
249 -def find_hid_devices():
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): # work on assumption there won't be more than 255 devices attached, just in case 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 # last device 265 266 requiredSize=DWORD() 267 268 # find the size of the structure we'll need 269 if not setupapi_dll.SetupDiGetDeviceInterfaceDetailA(hDevInfo,byref(deviceInterface),None,0,byref(requiredSize),None): 270 GetLastErrorMessage() # ignore the error, as we just want to find the size 271 272 # then make the structure and call again 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 # if we opened it ok 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