|
| 1 | +""" |
| 2 | +This file provides a way to interact with the `/dev/io` device file on |
| 3 | +FreeBSD. |
| 4 | +""" |
| 5 | + |
| 6 | +from typing import Final, IO |
| 7 | +from fcntl import ioctl |
| 8 | +import struct |
| 9 | + |
| 10 | +from .baseportio import PortIOClass |
| 11 | + |
| 12 | +IODEV_PIO_READ: Final = 0 |
| 13 | +IODEV_PIO_WRITE: Final = 1 |
| 14 | + |
| 15 | + |
| 16 | +def _IOC(inout: int, group: str, num: int, length: int): |
| 17 | + """ |
| 18 | + Create an ioctl command number. |
| 19 | + Based on the FreeBSD kernels sys/sys/ioccom.h. |
| 20 | + """ |
| 21 | + IOCPARM_SHIFT: Final = 13 # number of bits for ioctl size |
| 22 | + IOCPARM_MASK: Final = ((1 << IOCPARM_SHIFT) - 1) # parameter length mask |
| 23 | + |
| 24 | + return (((inout) | (((length) & IOCPARM_MASK) << 16) | (ord(group) << 8) | (num))) |
| 25 | + |
| 26 | + |
| 27 | +def _IOWR(group: str, num: int, length: int): |
| 28 | + """ |
| 29 | + Create an ioctl command number for read/write commands. |
| 30 | + Based on the FreeBSD kernels sys/sys/ioccom.h. |
| 31 | + """ |
| 32 | + IOC_VOID: Final = 0x20000000 # no parameters |
| 33 | + IOC_OUT: Final = 0x40000000 # copy out parameters |
| 34 | + IOC_IN: Final = 0x80000000 # copy in parameters |
| 35 | + IOC_INOUT: Final = (IOC_IN|IOC_OUT) # copy parameters in and out |
| 36 | + IOC_DIRMASK: Final = (IOC_VOID|IOC_OUT|IOC_IN) # mask for IN/OUT/VOID |
| 37 | + return _IOC(IOC_INOUT, group, num, length) |
| 38 | + |
| 39 | + |
| 40 | +def IODEV_PIO(): |
| 41 | + """ |
| 42 | + Create an ioctl command number for the `/dev/io` device file. |
| 43 | + """ |
| 44 | + |
| 45 | + # struct iodev_pio_req { |
| 46 | + # u_int access; |
| 47 | + # u_int port; |
| 48 | + # u_int width; |
| 49 | + # u_int val; |
| 50 | + # }; |
| 51 | + |
| 52 | + length = struct.calcsize("IIII") |
| 53 | + return _IOWR("I", 0, length) |
| 54 | + |
| 55 | + |
| 56 | +class FreeBsdPortIO(PortIOClass): |
| 57 | + """ |
| 58 | + A class to interact with the `/dev/io` device file on FreeBSD. |
| 59 | + """ |
| 60 | + |
| 61 | + _dev_io: IO | None = None |
| 62 | + |
| 63 | + def __init__(self): |
| 64 | + """ |
| 65 | + Initialize the `/dev/port` device file. |
| 66 | + """ |
| 67 | + self._dev_io = open("/dev/io", "wb", buffering=0) |
| 68 | + |
| 69 | + def __del__(self): |
| 70 | + """ |
| 71 | + Close the `/dev/port` device file. |
| 72 | + """ |
| 73 | + if self._dev_io: |
| 74 | + self._dev_io.close() |
| 75 | + |
| 76 | + def out_bytes(self, data: bytes, port: int) -> None: |
| 77 | + """ |
| 78 | + Write data to the specified port. |
| 79 | + :param data: Data to write. |
| 80 | + :param port: Port to write to. |
| 81 | + """ |
| 82 | + iodev_pio_req = struct.pack( |
| 83 | + "IIII", IODEV_PIO_WRITE, port, len(data), int.from_bytes(data, "little") |
| 84 | + ) |
| 85 | + ioctl(self._dev_io, IODEV_PIO(), iodev_pio_req) |
| 86 | + |
| 87 | + def outb(self, data: int, port: int) -> None: |
| 88 | + """ |
| 89 | + Write a byte (8 bit) to the specified port. |
| 90 | + :param data: Byte to write. |
| 91 | + :param port: Port to write to. |
| 92 | + """ |
| 93 | + self.out_bytes(data.to_bytes(1, "little"), port) |
| 94 | + |
| 95 | + def outw(self, data: int, port: int) -> None: |
| 96 | + """ |
| 97 | + Write a word (16 bit) to the specified port. |
| 98 | + :param data: Word to write. |
| 99 | + :param port: Port to write to. |
| 100 | + """ |
| 101 | + self.out_bytes(data.to_bytes(2, "little"), port) |
| 102 | + |
| 103 | + def outl(self, data: int, port: int) -> None: |
| 104 | + """ |
| 105 | + Write a long (32 bit) to the specified port. |
| 106 | + :param data: Long to write. |
| 107 | + :param port: Port to write to. |
| 108 | + """ |
| 109 | + self.out_bytes(data.to_bytes(4, "little"), port) |
| 110 | + |
| 111 | + def in_bytes(self, port: int, num: int) -> bytes: |
| 112 | + """ |
| 113 | + Read data from the specified port. |
| 114 | + :param port: Port to read from. |
| 115 | + :param num: Number of bytes to read (1 - 4). |
| 116 | + :return: Data read. |
| 117 | + """ |
| 118 | + iodev_pio_req = struct.pack("IIII", IODEV_PIO_READ, port, num, 0) |
| 119 | + return ioctl(self._dev_io, IODEV_PIO(), iodev_pio_req)[struct.calcsize("III") :] |
| 120 | + |
| 121 | + def inb(self, port: int) -> int: |
| 122 | + """ |
| 123 | + Read a byte (8 bit) from the specified port. |
| 124 | + :param port: Port to read from. |
| 125 | + :return: Byte read. |
| 126 | + """ |
| 127 | + return int.from_bytes(self.in_bytes(port, 1), "little") |
| 128 | + |
| 129 | + def inw(self, port: int) -> int: |
| 130 | + """ |
| 131 | + Read a word (16 bit) from the specified port. |
| 132 | + :param port: Port to read from. |
| 133 | + :return: Word read. |
| 134 | + """ |
| 135 | + return int.from_bytes(self.in_bytes(port, 2), "little") |
| 136 | + |
| 137 | + def inl(self, port: int) -> int: |
| 138 | + """ |
| 139 | + Read a long (32 bit) from the specified port. |
| 140 | + :param port: Port to read from. |
| 141 | + :return: Long read. |
| 142 | + """ |
| 143 | + return int.from_bytes(self.in_bytes(port, 4), "little") |
| 144 | + |
| 145 | + def ioperm(self, port: int, num: int, turn_on: bool) -> None: |
| 146 | + """ |
| 147 | + `ioperm` stub function. The iopl will already be raised from opening `/dev/io` and is not required. |
| 148 | + """ |
| 149 | + pass |
| 150 | + |
| 151 | + def iopl(self, level: int) -> None: |
| 152 | + """ |
| 153 | + `iopl` stub function. The iopl will already be raised from opening `/dev/io` and is not required. |
| 154 | + """ |
| 155 | + pass |
0 commit comments