summaryrefslogtreecommitdiff
path: root/ioctl/__init__.py
blob: 5ac87f339dda65de32fc4f13405d9dfad9b2ca7f (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
# SPDX-License-Identifier: MIT

import array
import fcntl

from typing import Optional, Union


__version__ = '0.2.2'
__all__ = ['__version__', 'IOCTL']


class IOCTL(object):
    '''
    Constructs and performs ioctl(s)
    See include/asm-generic/ioctl.h
    '''
    NRBITS: int = 8
    TYPEBITS: int = 8

    SIZEBITS: int = 14
    DIRBITS: int = 2

    NRMASK: int = (1 << NRBITS) - 1
    TYPEMASK: int = (1 << TYPEBITS) - 1
    SIZEMASK: int = (1 << SIZEBITS) - 1
    DIRMASK: int = (1 << DIRBITS) - 1

    NRSHIFT: int = 0
    TYPESHIFT: int = NRSHIFT + NRBITS
    SIZESHIFT: int = TYPESHIFT + TYPEBITS
    DIRSHIFT: int = SIZESHIFT + SIZEBITS

    class Direction:
        NONE = 0
        WRITE = 1
        READ = 2

    def __init__(self, dir: int, ty: str, nr: int, size: int = 0, bad: bool = False) -> None:
        assert self.Direction.NONE <= dir <= self.Direction.READ + self.Direction.WRITE

        if dir == self.Direction.NONE:
            size = 0
        elif not bad:
            assert size, size <= self.SIZEMASK

        self.op = (
            (dir << self.DIRSHIFT) |
            (ord(ty) << self.TYPESHIFT) |
            (nr << self.NRSHIFT) |
            (size << self.SIZESHIFT)
        )

    def perform(self, fd: int, buf: Optional[Union[str, bytes, 'array.array[int]']] = None) -> bytearray:
        '''
        Performs the ioctl
        '''
        size = self.unpack_size(self.op)

        if buf is None:
            buf = (size * '\x00').encode()

        return bytearray(fcntl.ioctl(fd, self.op, buf))  # type: ignore

    @classmethod
    def unpack_dir(cls, nr: int) -> int:
        return (nr >> cls.DIRSHIFT) & cls.DIRMASK

    @classmethod
    def unpack_type(cls, nr: int) -> int:
        return (nr >> cls.TYPESHIFT) & cls.TYPEMASK

    @classmethod
    def unpack_nr(cls, nr: int) -> int:
        return (nr >> cls.NRSHIFT) & cls.NRMASK

    @classmethod
    def unpack_size(cls, nr: int) -> int:
        return (nr >> cls.SIZESHIFT) & cls.SIZEMASK

    @classmethod
    def IO(cls, ty: str, nr: int) -> 'IOCTL':
        '''
        Constructor for no direction
        '''
        return cls(cls.Direction.NONE, ty, nr)

    @classmethod
    def IOR(cls, ty: str, nr: int, size: int) -> 'IOCTL':
        '''
        Constructor for read
        '''
        return cls(cls.Direction.READ, ty, nr, size)

    @classmethod
    def IOW(cls, ty: str, nr: int, size: int) -> 'IOCTL':
        '''
        Constructor for write
        '''
        return cls(cls.Direction.WRITE, ty, nr, size)

    @classmethod
    def IORW(cls, ty: str, nr: int, size: int) -> 'IOCTL':
        '''
        Constructor for read & write
        '''
        return cls(cls.Direction.READ | cls.Direction.WRITE, ty, nr, size)