1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
|
import ctypes, ctypes.util
path = ctypes.util.find_library('hidapi')
if not path:
path = ctypes.util.find_library('hidapi-libusb')
if not path:
path = ctypes.util.find_library('hidapi-hidraw')
if not path:
raise ImportError('Cannot find hidapi library')
hidapi = ctypes.CDLL(path)
class c_hid_device_info(ctypes.Structure):
def __iter__(self):
p = self
yield DeviceInfo(p)
while p.next:
p = p.next.contents
yield DeviceInfo(p)
c_hid_device_info._fields_ = [
('path', ctypes.c_char_p),
('vendor_id', ctypes.c_ushort),
('product_id', ctypes.c_ushort),
('serial_number', ctypes.c_wchar_p),
('release_number', ctypes.c_ushort),
('manufacturer_string', ctypes.c_wchar_p),
('product_string', ctypes.c_wchar_p),
('usage_page', ctypes.c_ushort),
('usage', ctypes.c_ushort),
('interface_number', ctypes.c_int),
('next', ctypes.POINTER(c_hid_device_info)),
]
hidapi.hid_open.argtypes = [ctypes.c_ushort, ctypes.c_ushort, ctypes.c_wchar_p]
hidapi.hid_open.restype = ctypes.c_void_p
hidapi.hid_open_path.argtypes = [ctypes.c_char_p]
hidapi.hid_open_path.restype = ctypes.c_void_p
hidapi.hid_close.argtypes = [ctypes.c_void_p]
hidapi.hid_read_timeout.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_size_t, ctypes.c_int]
hidapi.hid_read.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_size_t]
hidapi.hid_write.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_size_t]
hidapi.hid_send_feature_report.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_size_t]
hidapi.hid_get_feature_report.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_size_t]
hidapi.hid_enumerate.restype = ctypes.POINTER(c_hid_device_info)
def enumerate(vid = 0, pid = 0):
info = hidapi.hid_enumerate(vid, pid)
if not info:
return []
l = list(info.contents)
hidapi.hid_free_enumeration(info)
return l
class HIDError(Exception):
pass
class DeviceInfo:
def __init__(self, cstruct):
self.path = cstruct.path
self.vendor_id = cstruct.vendor_id
self.product_id = cstruct.product_id
self.serial_number = cstruct.serial_number
self.release_number = cstruct.release_number
self.manufacturer_string = cstruct.manufacturer_string
self.product_string = cstruct.product_string
self.usage_page = cstruct.usage_page
self.usage = cstruct.usage
self.interface_number = cstruct.interface_number
def open(self):
return Device(path = self.path)
class Device:
def __init__(self, vid = None, pid = None, serial = None, path = None):
if path is not None:
self._dev = hidapi.hid_open_path(path)
elif vid is not None and pid is not None:
self._dev = hidapi.hid_open(vid, pid, serial)
else:
raise TypeError('vid/pid or path is required')
if not self._dev:
raise HIDError('failed to open device')
def __del__(self):
self.close()
def close(self):
if self._dev:
hidapi.hid_close(self._dev)
self._dev = None
def set_output_report(self, data, id = 0):
if hidapi.hid_write(self._dev, ctypes.c_char_p(chr(id) + data), len(data) + 1) != len(data) + 1:
raise HIDError('failed to set output')
def get_input_report(self, timeout = -1):
buf = ctypes.create_string_buffer(64)
ret = hidapi.hid_read_timeout(self._dev, buf, 64, timeout)
if ret < 0:
raise HIDError('failed to get input')
return buf[:ret]
def set_feature_report(self, data, id = 0):
if hidapi.hid_send_feature_report(self._dev, ctypes.c_char_p(chr(id) + data), len(data) + 1) != len(data) + 1:
raise HIDError('failed to set feature')
def get_feature_report(self, id = 0):
buf = ctypes.create_string_buffer(chr(id), 64)
ret = hidapi.hid_get_feature_report(self._dev, buf, 64)
if ret < 0:
raise HIDError('failed to get feature')
return buf[1:ret]
|