summaryrefslogtreecommitdiff
path: root/ioctl/hidraw.py
blob: 6c69c8e1e7c014582a2a58b72b055b8131a42c94 (plain)
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
# 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

    def __init__(self, path: str, read_length: int = 1024) -> None:
        self._path = path
        self._fd = open(path, 'rb+')
        self.read_length = read_length
        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.read_length
        ).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.read_length
        ).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.read_length
        ).perform(self._fd).decode('utf-8').strip('\x00')

    # TODO: HIDIOCSFEATURE, HIDIOCGFEATURE