1# This is based on previous support for libhidapi which was dropped in
2# upstream commit f5d2eba.
3import ctypes as _C
4from struct import pack as _pack
5
6_native = _C.CDLL("libhidapi.so")
7
8class _NativeDeviceInfo(_C.Structure):
9	pass
10_NativeDeviceInfo._fields_ = [
11	('path', _C.c_char_p),
12	('vendor_id', _C.c_ushort),
13	('product_id', _C.c_ushort),
14	('serial', _C.c_wchar_p),
15	('release', _C.c_ushort),
16	('manufacturer', _C.c_wchar_p),
17	('product', _C.c_wchar_p),
18	('usage_page', _C.c_ushort),
19	('usage', _C.c_ushort),
20	('interface', _C.c_int),
21	('next_device', _C.POINTER(_NativeDeviceInfo))
22]
23
24from collections import namedtuple
25DeviceInfo = namedtuple('DeviceInfo', [
26	'path',
27	'vendor_id',
28	'product_id',
29	'serial',
30	'release',
31	'manufacturer',
32	'product',
33	'interface',
34	'driver',
35])
36del namedtuple
37
38def _makeDeviceInfo(native_device_info):
39	return DeviceInfo(
40		path=native_device_info.path.decode('ascii'),
41		vendor_id=hex(native_device_info.vendor_id)[2:].zfill(4),
42		product_id=hex(native_device_info.product_id)[2:].zfill(4),
43		serial=native_device_info.serial if native_device_info.serial else None,
44		release=hex(native_device_info.release)[2:],
45		manufacturer=native_device_info.manufacturer,
46		product=native_device_info.product,
47		interface=native_device_info.interface,
48		driver=None)
49
50_native.hid_init.argtypes = None
51_native.hid_init.restype = _C.c_int
52
53_native.hid_exit.argtypes = None
54_native.hid_exit.restype = _C.c_int
55
56_native.hid_enumerate.argtypes = [_C.c_ushort, _C.c_ushort]
57_native.hid_enumerate.restype = _C.POINTER(_NativeDeviceInfo)
58
59_native.hid_free_enumeration.argtypes = [_C.POINTER(_NativeDeviceInfo)]
60_native.hid_free_enumeration.restype = None
61
62_native.hid_open.argtypes = [_C.c_ushort, _C.c_ushort, _C.c_wchar_p]
63_native.hid_open.restype = _C.c_void_p
64
65_native.hid_open_path.argtypes = [_C.c_char_p]
66_native.hid_open_path.restype = _C.c_void_p
67
68_native.hid_close.argtypes = [_C.c_void_p]
69_native.hid_close.restype = None
70
71_native.hid_write.argtypes = [_C.c_void_p, _C.c_char_p, _C.c_size_t]
72_native.hid_write.restype = _C.c_int
73
74_native.hid_read.argtypes = [_C.c_void_p, _C.c_char_p, _C.c_size_t]
75_native.hid_read.restype = _C.c_int
76
77_native.hid_read_timeout.argtypes = [_C.c_void_p, _C.c_char_p, _C.c_size_t, _C.c_int]
78_native.hid_read_timeout.restype = _C.c_int
79
80_native.hid_set_nonblocking.argtypes = [_C.c_void_p, _C.c_int]
81_native.hid_set_nonblocking.restype = _C.c_int
82
83_native.hid_send_feature_report.argtypes = [_C.c_void_p, _C.c_char_p, _C.c_size_t]
84_native.hid_send_feature_report.restype = _C.c_int
85
86_native.hid_get_feature_report.argtypes = [_C.c_void_p, _C.c_char_p, _C.c_size_t]
87_native.hid_get_feature_report.restype = _C.c_int
88
89_native.hid_get_manufacturer_string.argtypes = [_C.c_void_p, _C.c_wchar_p, _C.c_size_t]
90_native.hid_get_manufacturer_string.restype = _C.c_int
91
92_native.hid_get_product_string.argtypes = [_C.c_void_p, _C.c_wchar_p, _C.c_size_t]
93_native.hid_get_product_string.restype = _C.c_int
94
95_native.hid_get_serial_number_string.argtypes = [_C.c_void_p, _C.c_wchar_p, _C.c_size_t]
96_native.hid_get_serial_number_string.restype = _C.c_int
97
98_native.hid_get_indexed_string.argtypes = [_C.c_void_p, _C.c_int, _C.c_wchar_p, _C.c_size_t]
99_native.hid_get_indexed_string.restype = _C.c_int
100
101_native.hid_error.argtypes = [_C.c_void_p]
102_native.hid_error.restype = _C.c_wchar_p
103
104def init():
105	return _native.hid_init() == 0
106
107def exit():
108	return _native.hid_exit() == 0
109
110def monitor_glib(callback, *device_filters):
111	pass
112
113def enumerate(vendor_id=None, product_id=None, interface_number=None, hid_driver=None):
114	devices = _native.hid_enumerate(vendor_id, product_id)
115	d = devices
116	while d:
117		if interface_number is None or interface_number == d.contents.interface:
118			yield _makeDeviceInfo(d.contents)
119		d = d.contents.next_device
120
121	if devices:
122		_native.hid_free_enumeration(devices)
123
124def open(vendor_id, product_id, serial=None):
125	return _native.hid_open(vendor_id, product_id, serial) or None
126
127def open_path(device_path):
128	if type(device_path) == str:
129		device_path = device_path.encode('ascii')
130	return _native.hid_open_path(device_path) or None
131
132def close(device_handle):
133	_native.hid_close(device_handle)
134
135def write(device_handle, data):
136	bytes_written = _native.hid_write(device_handle, _C.c_char_p(data), len(data))
137	if bytes_written != len(data):
138		raise IOError(_errno.EIO, 'written %d bytes out of expected %d' % (bytes_written, len(data)))
139
140def read(device_handle, bytes_count, timeout_ms=-1):
141	out_buffer = _C.create_string_buffer(b'\x00' * (bytes_count + 1))
142	bytes_read = _native.hid_read_timeout(device_handle, out_buffer, bytes_count, timeout_ms)
143	if bytes_read == -1:
144		return None
145	if bytes_read == 0:
146		return b''
147	return out_buffer[:bytes_read]
148
149def send_feature_report(device_handle, data, report_number=None):
150	if report_number is not None:
151		data = _pack(b'!B', report_number) + data
152	bytes_written = _native.hid_send_feature_report(device_handle, _C.c_char_p(data), len(data))
153	return bytes_written > -1
154
155def get_feature_report(device_handle, bytes_count, report_number=None):
156	out_buffer = _C.create_string_buffer('\x00' * (bytes_count + 2))
157	if report_number is not None:
158		out_buffer[0] = _pack(b'!B', report_number)
159	bytes_read = _native.hid_get_feature_report(device_handle, out_buffer, bytes_count)
160	if bytes_read > -1:
161		return out_buffer[:bytes_read]
162
163def _read_wchar(func, device_handle, index=None):
164	_BUFFER_SIZE = 64
165	buf = _C.create_unicode_buffer('\x00' * _BUFFER_SIZE)
166	if index is None:
167		ok = func(device_handle, buf, _BUFFER_SIZE)
168	else:
169		ok = func(device_handle, index, buf, _BUFFER_SIZE)
170	if ok == 0:
171		return buf.value
172
173def get_manufacturer(device_handle):
174	return _read_wchar(_native.hid_get_manufacturer_string, device_handle)
175
176def get_product(device_handle):
177	return _read_wchar(_native.hid_get_product_string, device_handle)
178
179def get_serial(device_handle):
180	serial = _read_wchar(_native.hid_get_serial_number_string, device_handle)
181	if serial is not None:
182		return ''.join(hex(ord(c)) for c in serial)
183
184def get_indexed_string(device_handle, index):
185	return _read_wchar(_native.hid_get_indexed_string, device_handle, index)
186