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
135
136
137
138
139
140
141
142
143
144
|
# SPDX-License-Identifier: MIT
import array
import ctypes
import fcntl
import os
from typing import BinaryIO, List, Tuple
import ioctl
__all__ = [
'HidrawReportDescriptor',
'HidrawDevinfo',
'Hidraw',
]
class HidrawReportDescriptor(ctypes.Structure):
HID_MAX_DESCRIPTOR_SIZE = 4096
_fields_ = [
('size', ctypes.c_uint),
('value', ctypes.c_ubyte * HID_MAX_DESCRIPTOR_SIZE),
]
class HidrawDevinfo(ctypes.Structure):
_fields_ = [
('bustype', ctypes.c_uint),
('vendor', ctypes.c_ushort),
('product', ctypes.c_ushort),
]
class Hidraw(object):
'''
Represents a hidraw node
See linux/hidraw.h
'''
HIDIOCGRDESCSIZE = 0x01
HIDIOCGRDESC = 0x02
HIDIOCGRAWINFO = 0x03
HIDIOCGRAWNAME = 0x04
HIDIOCGRAWPHYS = 0x05
HIDIOCSFEATURE = 0x06
HIDIOCGFEATURE = 0x07
HIDIOCGRAWUNIQ = 0x08
HID_NAME_SIZE = 1024
def __init__(self, path: str) -> None:
self._path = path
self._fd = open(path, 'rb+')
fcntl.fcntl(self._fd, fcntl.F_SETFL, os.O_NONBLOCK)
def __str__(self) -> str:
return f'Hidraw({self.path})'
@property
def path(self) -> str:
'''
Node path
'''
return self._path
@property
def fd(self) -> BinaryIO:
'''
Node file descriptor
'''
return self._fd
@property
def report_descriptor_size(self) -> int:
'''
Size of the report descriptor of the hidraw node
'''
return ctypes.c_uint.from_buffer(
ioctl.IOCTL.IOR(
'H', self.HIDIOCGRDESCSIZE, ctypes.sizeof(ctypes.c_uint)
).perform(self._fd)
).value
@property
def report_descriptor(self) -> List[int]:
'''
Report descriptor of the hidraw node
'''
# fcntl.ioctl does not support such big buffer sizes when using the default buffer so we need to provide our own buffer
buf = array.array(
'B', self.report_descriptor_size.to_bytes(4, 'little') +
HidrawReportDescriptor.HID_MAX_DESCRIPTOR_SIZE * b'\x00'
)
ioctl.IOCTL.IOR(
'H', self.HIDIOCGRDESC, ctypes.sizeof(HidrawReportDescriptor)
).perform(self._fd, buf=buf)
ret = HidrawReportDescriptor.from_buffer(buf)
return list(ret.value)[:ret.size]
@property
def info(self) -> Tuple[int, int, int]:
'''
Device info of the hidraw node
'''
dev_info = HidrawDevinfo.from_buffer(
ioctl.IOCTL.IOR(
'H', self.HIDIOCGRAWINFO, ctypes.sizeof(HidrawDevinfo)
).perform(self._fd)
)
return dev_info.bustype, dev_info.vendor, dev_info.product
@property
def name(self) -> str:
'''
HID name of the hidraw node
'''
return ioctl.IOCTL.IOR(
'H', self.HIDIOCGRAWNAME, self.HID_NAME_SIZE
).perform(self._fd).decode('utf-8').strip('\x00')
@property
def phys(self) -> str:
'''
Physical name of the hidraw node
'''
return ioctl.IOCTL.IOR(
'H', self.HIDIOCGRAWPHYS, self.HID_NAME_SIZE
).perform(self._fd).decode('utf-8').strip('\x00')
@property
def uniq(self) -> str:
'''
Unique name of the hidraw node
'''
return ioctl.IOCTL.IOR(
'H', self.HIDIOCGRAWUNIQ, self.HID_NAME_SIZE
).perform(self._fd).decode('utf-8').strip('\x00')
# TODO: HIDIOCSFEATURE, HIDIOCGFEATURE
|