From f793ac4d04761891b209ad7037c884c9ec239731 Mon Sep 17 00:00:00 2001 From: Martin Rys Date: Tue, 11 Nov 2025 12:02:50 +0100 Subject: [PATCH 1/8] Move variables together --- ioctl_opt/__init__.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ioctl_opt/__init__.py b/ioctl_opt/__init__.py index cdb059e..f1f35a0 100644 --- a/ioctl_opt/__init__.py +++ b/ioctl_opt/__init__.py @@ -49,6 +49,12 @@ IOC_WRITE = 1 IOC_READ = 2 +IOC_IN = IOC_WRITE << _IOC_DIRSHIFT +IOC_OUT = IOC_READ << _IOC_DIRSHIFT +IOC_INOUT = (IOC_WRITE | IOC_READ) << _IOC_DIRSHIFT +IOCSIZE_MASK = _IOC_SIZEMASK << _IOC_SIZESHIFT +IOCSIZE_SHIFT = _IOC_SIZESHIFT + def IOC(dir, type, nr, size): """ dir @@ -136,12 +142,6 @@ def IOC_SIZE(nr): """ return (nr >> _IOC_SIZESHIFT) & _IOC_SIZEMASK -IOC_IN = IOC_WRITE << _IOC_DIRSHIFT -IOC_OUT = IOC_READ << _IOC_DIRSHIFT -IOC_INOUT = (IOC_WRITE | IOC_READ) << _IOC_DIRSHIFT -IOCSIZE_MASK = _IOC_SIZEMASK << _IOC_SIZESHIFT -IOCSIZE_SHIFT = _IOC_SIZESHIFT - if __name__ == '__main__': print('Sanity checks...') # hid.h From 369aab7663b71312c12030407cf190cd9a75dcdf Mon Sep 17 00:00:00 2001 From: Martin Rys Date: Tue, 11 Nov 2025 12:21:57 +0100 Subject: [PATCH 2/8] Add typing for most variables and returns --- ioctl_opt/__init__.py | 63 ++++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/ioctl_opt/__init__.py b/ioctl_opt/__init__.py index f1f35a0..7caba4e 100644 --- a/ioctl_opt/__init__.py +++ b/ioctl_opt/__init__.py @@ -26,36 +26,39 @@ nr (8-bits unsigned integer) Driver-imposed ioctl function number. """ +from __future__ import annotations import array import ctypes import struct -_IOC_NRBITS = 8 -_IOC_TYPEBITS = 8 -_IOC_SIZEBITS = 14 -_IOC_DIRBITS = 2 +from typing import Final -_IOC_NRMASK = (1 << _IOC_NRBITS) - 1 -_IOC_TYPEMASK = (1 << _IOC_TYPEBITS) - 1 -_IOC_SIZEMASK = (1 << _IOC_SIZEBITS) - 1 -_IOC_DIRMASK = (1 << _IOC_DIRBITS) - 1 +_IOC_NRBITS: Final[int] = 8 +_IOC_TYPEBITS: Final[int] = 8 +_IOC_SIZEBITS: Final[int] = 14 +_IOC_DIRBITS: Final[int] = 2 -_IOC_NRSHIFT = 0 -_IOC_TYPESHIFT = _IOC_NRSHIFT + _IOC_NRBITS -_IOC_SIZESHIFT = _IOC_TYPESHIFT + _IOC_TYPEBITS -_IOC_DIRSHIFT = _IOC_SIZESHIFT + _IOC_SIZEBITS +_IOC_NRMASK: Final[int] = (1 << _IOC_NRBITS) - 1 +_IOC_TYPEMASK: Final[int] = (1 << _IOC_TYPEBITS) - 1 +_IOC_SIZEMASK: Final[int] = (1 << _IOC_SIZEBITS) - 1 +_IOC_DIRMASK: Final[int] = (1 << _IOC_DIRBITS) - 1 -IOC_NONE = 0 -IOC_WRITE = 1 -IOC_READ = 2 +_IOC_NRSHIFT: Final[int] = 0 +_IOC_TYPESHIFT: Final[int] = _IOC_NRSHIFT + _IOC_NRBITS +_IOC_SIZESHIFT: Final[int] = _IOC_TYPESHIFT + _IOC_TYPEBITS +_IOC_DIRSHIFT: Final[int] = _IOC_SIZESHIFT + _IOC_SIZEBITS -IOC_IN = IOC_WRITE << _IOC_DIRSHIFT -IOC_OUT = IOC_READ << _IOC_DIRSHIFT -IOC_INOUT = (IOC_WRITE | IOC_READ) << _IOC_DIRSHIFT -IOCSIZE_MASK = _IOC_SIZEMASK << _IOC_SIZESHIFT -IOCSIZE_SHIFT = _IOC_SIZESHIFT +IOC_NONE: Final[int] = 0 +IOC_WRITE: Final[int] = 1 +IOC_READ: Final[int] = 2 -def IOC(dir, type, nr, size): +IOC_IN: Final[int] = IOC_WRITE << _IOC_DIRSHIFT +IOC_OUT: Final[int] = IOC_READ << _IOC_DIRSHIFT +IOC_INOUT: Final[int] = (IOC_WRITE | IOC_READ) << _IOC_DIRSHIFT +IOCSIZE_MASK: Final[int] = _IOC_SIZEMASK << _IOC_SIZESHIFT +IOCSIZE_SHIFT: Final[int] = _IOC_SIZESHIFT + +def IOC(dir: int, type: int, nr: int, size) -> int: """ dir One of IOC_NONE, IOC_WRITE, IOC_READ, or IOC_READ|IOC_WRITE. @@ -69,7 +72,7 @@ def IOC(dir, type, nr, size): assert size <= _IOC_SIZEMASK, size return (dir << _IOC_DIRSHIFT) | (type << _IOC_TYPESHIFT) | (nr << _IOC_NRSHIFT) | (size << _IOC_SIZESHIFT) -def IOC_TYPECHECK(t): +def IOC_TYPECHECK(t) -> int: """ Returns the size of given type, and check its suitability for use in an ioctl command number. @@ -85,13 +88,13 @@ def IOC_TYPECHECK(t): assert size <= _IOC_SIZEMASK, size return size -def IO(type, nr): +def IO(type: int, nr: int) -> int: """ An ioctl with no parameters. """ return IOC(IOC_NONE, type, nr, 0) -def IOR(type, nr, size): +def IOR(type: int, nr: int, size) -> int: """ An ioctl with read parameters. @@ -100,7 +103,7 @@ def IOR(type, nr, size): """ return IOC(IOC_READ, type, nr, IOC_TYPECHECK(size)) -def IOW(type, nr, size): +def IOW(type: int, nr: int, size) -> int: """ An ioctl with write parameters. @@ -109,7 +112,7 @@ def IOW(type, nr, size): """ return IOC(IOC_WRITE, type, nr, IOC_TYPECHECK(size)) -def IOWR(type, nr, size): +def IOWR(type: int, nr: int, size) -> int: """ An ioctl with both read an writes parameters. @@ -118,25 +121,25 @@ def IOWR(type, nr, size): """ return IOC(IOC_READ | IOC_WRITE, type, nr, IOC_TYPECHECK(size)) -def IOC_DIR(nr): +def IOC_DIR(nr: int) -> int: """ Extract direction from an ioctl command number. """ return (nr >> _IOC_DIRSHIFT) & _IOC_DIRMASK -def IOC_TYPE(nr): +def IOC_TYPE(nr: int) -> int: """ Extract type from an ioctl command number. """ return (nr >> _IOC_TYPESHIFT) & _IOC_TYPEMASK -def IOC_NR(nr): +def IOC_NR(nr: int) -> int: """ Extract nr from an ioctl command number. """ return (nr >> _IOC_NRSHIFT) & _IOC_NRMASK -def IOC_SIZE(nr): +def IOC_SIZE(nr: int) -> int: """ Extract size from an ioctl command number. """ From 72065ea125f44ac994e185fa1df08e1a7cb1e69a Mon Sep 17 00:00:00 2001 From: Martin Rys Date: Sat, 15 Nov 2025 11:28:52 +0100 Subject: [PATCH 3/8] PEP 257 --- ioctl_opt/__init__.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ioctl_opt/__init__.py b/ioctl_opt/__init__.py index 7caba4e..583ccb1 100644 --- a/ioctl_opt/__init__.py +++ b/ioctl_opt/__init__.py @@ -13,8 +13,7 @@ # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -""" -Pythonified linux asm-generic/ioctl.h . +"""Pythonified linux asm-generic/ioctl.h Produce IOCTL command numbers from their individual components, simplifying C header conversion to python (keeping magic constants and differences to From b748341d901102585dc9ec7b292b203961cc1d7e Mon Sep 17 00:00:00 2001 From: Martin Rys Date: Sat, 15 Nov 2025 11:29:15 +0100 Subject: [PATCH 4/8] Useless newline removal --- ioctl_opt/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ioctl_opt/__init__.py b/ioctl_opt/__init__.py index 583ccb1..8b80556 100644 --- a/ioctl_opt/__init__.py +++ b/ioctl_opt/__init__.py @@ -29,7 +29,6 @@ import array import ctypes import struct - from typing import Final _IOC_NRBITS: Final[int] = 8 From b010912223196da17865d625b0a9f9c1b840f14c Mon Sep 17 00:00:00 2001 From: Martin Rys Date: Sat, 15 Nov 2025 11:29:29 +0100 Subject: [PATCH 5/8] Remove future imports - not needed --- ioctl_opt/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ioctl_opt/__init__.py b/ioctl_opt/__init__.py index 8b80556..489cb73 100644 --- a/ioctl_opt/__init__.py +++ b/ioctl_opt/__init__.py @@ -25,7 +25,6 @@ nr (8-bits unsigned integer) Driver-imposed ioctl function number. """ -from __future__ import annotations import array import ctypes import struct From 1e58e7e84f71aa2e6c04c0cd9591cd3e706b61e5 Mon Sep 17 00:00:00 2001 From: Martin Rys Date: Sat, 15 Nov 2025 11:31:04 +0100 Subject: [PATCH 6/8] PEP 257 docstrings --- ioctl_opt/__init__.py | 34 +++++++++------------------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/ioctl_opt/__init__.py b/ioctl_opt/__init__.py index 489cb73..aa2006f 100644 --- a/ioctl_opt/__init__.py +++ b/ioctl_opt/__init__.py @@ -70,10 +70,7 @@ def IOC(dir: int, type: int, nr: int, size) -> int: return (dir << _IOC_DIRSHIFT) | (type << _IOC_TYPESHIFT) | (nr << _IOC_NRSHIFT) | (size << _IOC_SIZESHIFT) def IOC_TYPECHECK(t) -> int: - """ - Returns the size of given type, and check its suitability for use in an - ioctl command number. - """ + """Returns the size of given type, and check its suitability for use in an ioctl command number.""" if isinstance(t, (memoryview, bytearray)): size = len(t) elif isinstance(t, struct.Struct): @@ -86,14 +83,11 @@ def IOC_TYPECHECK(t) -> int: return size def IO(type: int, nr: int) -> int: - """ - An ioctl with no parameters. - """ + """An ioctl with no parameters.""" return IOC(IOC_NONE, type, nr, 0) def IOR(type: int, nr: int, size) -> int: - """ - An ioctl with read parameters. + """An ioctl with read parameters. size (ctype type or instance, memoryview, bytearray, struct.Struct, or array.array) Type/structure of the argument passed to ioctl's "arg" argument. @@ -101,8 +95,7 @@ def IOR(type: int, nr: int, size) -> int: return IOC(IOC_READ, type, nr, IOC_TYPECHECK(size)) def IOW(type: int, nr: int, size) -> int: - """ - An ioctl with write parameters. + """An ioctl with write parameters. size (ctype type or instance, memoryview, bytearray, struct.Struct, or array.array) Type/structure of the argument passed to ioctl's "arg" argument. @@ -110,8 +103,7 @@ def IOW(type: int, nr: int, size) -> int: return IOC(IOC_WRITE, type, nr, IOC_TYPECHECK(size)) def IOWR(type: int, nr: int, size) -> int: - """ - An ioctl with both read an writes parameters. + """An ioctl with both read an writes parameters. size (ctype type or instance, memoryview, bytearray, struct.Struct, or array.array) Type/structure of the argument passed to ioctl's "arg" argument. @@ -119,27 +111,19 @@ def IOWR(type: int, nr: int, size) -> int: return IOC(IOC_READ | IOC_WRITE, type, nr, IOC_TYPECHECK(size)) def IOC_DIR(nr: int) -> int: - """ - Extract direction from an ioctl command number. - """ + """Extract direction from an ioctl command number.""" return (nr >> _IOC_DIRSHIFT) & _IOC_DIRMASK def IOC_TYPE(nr: int) -> int: - """ - Extract type from an ioctl command number. - """ + """Extract type from an ioctl command number.""" return (nr >> _IOC_TYPESHIFT) & _IOC_TYPEMASK def IOC_NR(nr: int) -> int: - """ - Extract nr from an ioctl command number. - """ + """Extract nr from an ioctl command number.""" return (nr >> _IOC_NRSHIFT) & _IOC_NRMASK def IOC_SIZE(nr: int) -> int: - """ - Extract size from an ioctl command number. - """ + """Extract size from an ioctl command number.""" return (nr >> _IOC_SIZESHIFT) & _IOC_SIZEMASK if __name__ == '__main__': From 17969cfb9837f13450014a7cedf16d55f33c89a5 Mon Sep 17 00:00:00 2001 From: Martin Rys Date: Tue, 2 Dec 2025 19:40:43 +0100 Subject: [PATCH 7/8] Add latest changes --- ioctl_opt/__init__.py | 17 +++++++---------- pyproject.toml | 2 +- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/ioctl_opt/__init__.py b/ioctl_opt/__init__.py index aa2006f..8132f43 100644 --- a/ioctl_opt/__init__.py +++ b/ioctl_opt/__init__.py @@ -25,9 +25,8 @@ nr (8-bits unsigned integer) Driver-imposed ioctl function number. """ -import array import ctypes -import struct +from collections.abc import Buffer from typing import Final _IOC_NRBITS: Final[int] = 8 @@ -56,7 +55,8 @@ IOCSIZE_SHIFT: Final[int] = _IOC_SIZESHIFT def IOC(dir: int, type: int, nr: int, size) -> int: - """ + """Produce IOCTL command number from raw components. + dir One of IOC_NONE, IOC_WRITE, IOC_READ, or IOC_READ|IOC_WRITE. Direction is from the application's point of view, not kernel's. @@ -69,14 +69,11 @@ def IOC(dir: int, type: int, nr: int, size) -> int: assert size <= _IOC_SIZEMASK, size return (dir << _IOC_DIRSHIFT) | (type << _IOC_TYPESHIFT) | (nr << _IOC_NRSHIFT) | (size << _IOC_SIZESHIFT) -def IOC_TYPECHECK(t) -> int: +def IOC_TYPECHECK(t: Buffer | ctypes._CData | type[ctypes._CData]) -> int: """Returns the size of given type, and check its suitability for use in an ioctl command number.""" - if isinstance(t, (memoryview, bytearray)): - size = len(t) - elif isinstance(t, struct.Struct): - size = t.size - elif isinstance(t, array.array): - size = t.itemsize * len(t) + if isinstance(t, Buffer): + with memoryview(t) as t_view: + size = t_view.nbytes else: size = ctypes.sizeof(t) assert size <= _IOC_SIZEMASK, size diff --git a/pyproject.toml b/pyproject.toml index 09e75bb..a1fbcba 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ authors = [{name = "Vincent Pelletier", email = "plr.vincent@gmail.com"}] license = { text = "GPL-2.0-or-later" } keywords = ["ioctl"] - requires-python = ">=3.10" + requires-python = ">=3.12" classifiers = [ "Intended Audience :: Information Technology", "License :: OSI Approved :: GNU General Public License (GPL)", From 099bae94456b63ee74f5657978afad948a881019 Mon Sep 17 00:00:00 2001 From: Martin Rys Date: Tue, 2 Dec 2025 19:44:09 +0100 Subject: [PATCH 8/8] Docs --- ioctl_opt/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ioctl_opt/__init__.py b/ioctl_opt/__init__.py index 8132f43..e11b5a7 100644 --- a/ioctl_opt/__init__.py +++ b/ioctl_opt/__init__.py @@ -57,6 +57,8 @@ def IOC(dir: int, type: int, nr: int, size) -> int: """Produce IOCTL command number from raw components. + Consider using IO, IOR, IOW or IORW. + dir One of IOC_NONE, IOC_WRITE, IOC_READ, or IOC_READ|IOC_WRITE. Direction is from the application's point of view, not kernel's.