def __init__(self, path): # Resolve the path to device handle device_entry = iokit.IORegistryEntryFromPath(K_IO_MASTER_PORT_DEFAULT, path) if not device_entry: raise errors.OsHidError( 'Device path does not match any HID device on ' 'the system') self.device_handle = iokit.IOHIDDeviceCreate(K_CF_ALLOCATOR_DEFAULT, device_entry) if not self.device_handle: raise errors.OsHidError( 'Failed to obtain device handle from registry ' 'entry') iokit.IOObjectRelease(device_entry) self.device_path = path # Open device result = iokit.IOHIDDeviceOpen(self.device_handle, 0) if result != K_IO_RETURN_SUCCESS: raise errors.OsHidError( 'Failed to open device for communication: {}'.format(result)) # Create read queue self.read_queue = queue.Queue() # Create and start read thread self.run_loop_ref = None self.read_thread = threading.Thread(target=DeviceReadThread, args=(self, )) self.read_thread.daemon = True self.read_thread.start() # Read max report sizes for in/out self.internal_max_in_report_len = GetDeviceIntProperty( self.device_handle, HID_DEVICE_PROPERTY_MAX_INPUT_REPORT_SIZE) if not self.internal_max_in_report_len: raise errors.OsHidError('Unable to obtain max in report size') self.internal_max_out_report_len = GetDeviceIntProperty( self.device_handle, HID_DEVICE_PROPERTY_MAX_OUTPUT_REPORT_SIZE) if not self.internal_max_out_report_len: raise errors.OsHidError('Unable to obtain max out report size') # Register read callback self.in_report_buffer = (ctypes.c_uint8 * self.internal_max_in_report_len)() iokit.IOHIDDeviceRegisterInputReportCallback( self.device_handle, self.in_report_buffer, self.internal_max_in_report_len, REGISTERED_READ_CALLBACK, ctypes.py_object(self.read_queue))
def Enumerate(): """See base class.""" # Init a HID manager hid_mgr = iokit.IOHIDManagerCreate(None, None) if not hid_mgr: raise errors.OsHidError('Unable to obtain HID manager reference') iokit.IOHIDManagerSetDeviceMatching(hid_mgr, None) # Get devices from HID manager device_set_ref = iokit.IOHIDManagerCopyDevices(hid_mgr) if not device_set_ref: raise errors.OsHidError( 'Failed to obtain devices from HID manager') num = iokit.CFSetGetCount(device_set_ref) devices = (IO_HID_DEVICE_REF * num)() iokit.CFSetGetValues(device_set_ref, devices) # Retrieve and build descriptor dictionaries for each device descriptors = [] for dev in devices: d = base.DeviceDescriptor() d.vendor_id = GetDeviceIntProperty(dev, HID_DEVICE_PROPERTY_VENDOR_ID) d.product_id = GetDeviceIntProperty( dev, HID_DEVICE_PROPERTY_PRODUCT_ID) d.product_string = GetDeviceStringProperty( dev, HID_DEVICE_PROPERTY_PRODUCT) d.usage = GetDeviceIntProperty(dev, HID_DEVICE_PROPERTY_PRIMARY_USAGE) d.usage_page = GetDeviceIntProperty( dev, HID_DEVICE_PROPERTY_PRIMARY_USAGE_PAGE) d.report_id = GetDeviceIntProperty(dev, HID_DEVICE_PROPERTY_REPORT_ID) d.path = GetDevicePath(dev) descriptors.append(d.ToPublicDict()) # Clean up CF objects cf.CFRelease(device_set_ref) cf.CFRelease(hid_mgr) return descriptors
def Write(self, packet): """See base class.""" report_id = 0 out_report_buffer = (ctypes.c_uint8 * self.internal_max_out_report_len)() out_report_buffer[:] = packet[:] result = iokit.IOHIDDeviceSetReport(self.device_handle, K_IO_HID_REPORT_TYPE_OUTPUT, report_id, out_report_buffer, self.internal_max_out_report_len) # Non-zero status indicates failure if result != K_IO_RETURN_SUCCESS: raise errors.OsHidError('Failed to write report to device')
def GetDeviceIntProperty(dev_ref, key): """Reads int property from the HID device.""" cf_key = CFStr(key) type_ref = iokit.IOHIDDeviceGetProperty(dev_ref, cf_key) cf.CFRelease(cf_key) if not type_ref: return None if cf.CFGetTypeID(type_ref) != cf.CFNumberGetTypeID(): raise errors.OsHidError('Expected number type, got {}'.format( cf.CFGetTypeID(type_ref))) out = ctypes.c_int32() ret = cf.CFNumberGetValue(type_ref, K_CF_NUMBER_SINT32_TYPE, ctypes.byref(out)) if not ret: return None return out.value
def GetDeviceStringProperty(dev_ref, key): """Reads string property from the HID device.""" cf_key = CFStr(key) type_ref = iokit.IOHIDDeviceGetProperty(dev_ref, cf_key) cf.CFRelease(cf_key) if not type_ref: return None if cf.CFGetTypeID(type_ref) != cf.CFStringGetTypeID(): raise errors.OsHidError('Expected string type, got {}'.format( cf.CFGetTypeID(type_ref))) type_ref = ctypes.cast(type_ref, CF_STRING_REF) out = ctypes.create_string_buffer(DEVICE_STRING_PROPERTY_BUFFER_SIZE) ret = cf.CFStringGetCString(type_ref, out, DEVICE_STRING_PROPERTY_BUFFER_SIZE, K_CF_STRING_ENCODING_UTF8) if not ret: return None return out.value.decode('utf8')
def Enumerate(): hidraw_devices = [] try: hidraw_devices = os.listdir('/sys/class/hidraw') except FileNotFoundError: raise errors.OsHidError('No hidraw device is available') for dev in hidraw_devices: rd_path = (os.path.join('/sys/class/hidraw', dev, 'device/report_descriptor')) uevent_path = os.path.join('/sys/class/hidraw', dev, 'device/uevent') rd_file = open(rd_path, 'rb') uevent_file = open(uevent_path, 'rb') desc = base.DeviceDescriptor() desc.path = os.path.join('/dev/', dev) ParseReportDescriptor(rd_file.read(), desc) ParseUevent(uevent_file.read(), desc) rd_file.close() uevent_file.close() yield desc.ToPublicDict()