summaryrefslogtreecommitdiff
path: root/ioctl/hidraw.py
blob: 13b476cccd287c993f826f6facfb530aed6770cb (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
# SPDX-License-Identifier: MIT

import array
import ctypes
import fcntl
import os

from typing import BinaryIO, List, Tuple

import ioctl


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

    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')

    # TODO: HIDIOCGRAWPHYS, HIDIOCSFEATURE, HIDIOCGFEATURE, HIDIOCGRAWUNIQ