1
0
mirror of https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git synced 2026-10-24 00:48:53 +03:00
Commit Graph

1590 Commits

Author SHA1 Message Date
Bartosz Golaszewski
3076847a6c bindings: python: gpiod v2.5.0
Changelog:
- add support for free-threaded (no-GIL) CPython
- generate wheels for CPython 3.14t (free-threaded)
- drop support for Python 3.9
- migrate the C extension and test modules to multi-phase module
  initialization
- release the GIL in chip_get_info()
- fix heap-buffer overflow bugs when setting/getting line values
- check for errors after PyIter_Next() when setting output values
- check for errors after PyIter_Next() when reading request values
- fix reference count imbalances (missing Py_None and line-name
  increments, erroneous decref on PyList_SetItem() failure)
- allow closing a Chip multiple times without raising an error
- allow releasing a LineRequest multiple times without raising an error
- remove a duplicated edge-detection setting
- add a CHANGELOG.md to track changes between releases
- copy the license file into source and binary distributions
- require setuptools v77 or newer
- remove wheel from the build dependencies
- add multi-threaded tests
- add a dedicated lint dependency group and update linter configuration
- apply linter recommendations and add type annotations across examples,
  setup and helper scripts
- modernize the C extension using upgrade_pythoncapi.py suggestions
- minor internal cleanups and performance improvements

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
python-v2.5.0
2026-06-17 09:20:20 +02:00
Bartosz Golaszewski
31e184a41e bindings: python: use the correct libgpiod API version define in setup.py
Since commit a62c732936 ("core: change the behavior of
gpiod_api_version()"), the libgpiod build expects the
GPIOD_API_VERSION_STR external define to contain the API version string.
The setup.py script in python bindings was not updated so it fails to
build the library sources into the generated wheel. Use the correct
define.

Fixes: a62c732936 ("core: change the behavior of gpiod_api_version()")
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-06-17 09:13:19 +02:00
Bartosz Golaszewski
4f2508fb8e libgpiod: start v2.4 development cycle
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-06-17 09:13:19 +02:00
Bartosz Golaszewski
bbcd0a5d64 libgpiod v2.3
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
v2.3
2026-06-16 13:59:02 +02:00
Bartosz Golaszewski
0ef5d37711 NEWS: updates for v2.3
Add release notes for libgpiod v2.3.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-06-16 13:55:29 +02:00
Bartosz Golaszewski
2de4e60262 tools: gpioset: store interactive sleep period in a long long
The interactive 'sleep' command stored the result of parse_period() in
an int. parse_period() returns a long long number of microseconds, so a
period above INT_MAX microseconds (~36 minutes, e.g. "sleep 40m")
overflowed: it could wrap to a negative value and be wrongly rejected as
an invalid period, or to an incorrect positive value producing the wrong
sleep duration.

Declare period_us as long long to match parse_period()'s return type.
The error check (period_us < 0) and the sleep_us() call, which takes an
unsigned long long, both remain correct. The non-interactive hold and
toggle period paths already use unsigned long long and were unaffected.

Add a test that issues "sleep 40m" at the interactive prompt and verifies
the tool starts sleeping rather than emitting an "invalid period" error

Fixes: c34a572c53 ("tools: add minutes as a new supported time unit")
Assisted-by: Claude Opus 4.8
Link: https://patch.msgid.link/20260608-more-pre-2-3-fixes-v1-5-577a5ba426a5@oss.qualcomm.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-06-15 09:38:16 +02:00
Bartosz Golaszewski
c43a6cc5a5 tools: reject trailing garbage in parse_period()
parse_period() consumed an optional unit suffix ('us', 'ms', 's', 'm')
but never verified that the suffix was the end of the string. As a
result inputs with trailing garbage such as "100msx", "100usx" or
"100sx" were silently accepted and parsed as if the garbage were not
there, while inconsistently "100mx" was already rejected.

Require that the whole string be consumed by checking *end == '\0'
after the unit suffix has been processed, mirroring what parse_uint()
already does. All valid forms still leave end pointing at the
terminating NUL at that point, so only malformed input is rejected.

Add a test that passes an invalid suffix and verifies it's rejected.

Fixes: 6702eed1a5 ("tools: fix an integer overflow bug in parse_period()")
Assisted-by: Claude Opus 4.8
Link: https://patch.msgid.link/20260608-more-pre-2-3-fixes-v1-4-577a5ba426a5@oss.qualcomm.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-06-15 09:38:16 +02:00
Bartosz Golaszewski
696ffc986b tools: gpionotify: don't watch lines on the wrong chip
Commit 0de77e2abf ("tools: gpionotify: don't leak info returned by
gpiod_chip_watch_line_info()") moved the call to
gpiod_chip_watch_line_info() out of the condition that guarded it. The
original code relied on the short-circuit evaluation of the logical AND
to only request a watch when the line actually belonged to the chip
currently being processed:

	if ((resolver->lines[j].chip_num == i) &&
	    !gpiod_chip_watch_line_info(chip, resolver->lines[j].offset))
		die_perror(...);

After the refactor the watch is requested unconditionally for every
resolved line on every chip, using the line's offset as-is. When lines
are spread across multiple chips this installs watches at foreign
offsets on the wrong chip. Such a watch typically succeeds (the offset
usually exists on the other chip too), persists - freeing the returned
gpiod_line_info does not remove it - and makes gpionotify report
spurious info events for lines the user never asked to watch.

Restore the guard so that each line is only ever watched on the chip it
belongs to, while keeping the gpiod_line_info_free() leak fix. As a side
effect a watch failure on the correct chip is now always treated as
fatal instead of only when it coincided with a matching chip_num.

Add a test-case that requests two lines with distinct offsets on two
different chips and then toggles one line's offset on the other chip. No
event must be reported for that, as the offset is not watched there.

Fixes: 0de77e2abf ("tools: gpionotify: don't leak info returned by gpiod_chip_watch_line_info()")
Assisted-by: Claude Opus 4.8
Link: https://patch.msgid.link/20260608-more-pre-2-3-fixes-v1-3-577a5ba426a5@oss.qualcomm.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-06-15 09:38:16 +02:00
Bartosz Golaszewski
32db8215a8 core: edge-event: return 0 when zero events are requested
gpiod_edge_event_buffer_read_fd() is documented to read at most
max_events events and to return the number of events read. When called
with max_events == 0 it nonetheless issued a read() for 0 bytes, which
returns 0, and the subsequent short-read check
((unsigned int)rd < sizeof(*event_data)) then treated this as an I/O
error, setting errno to EIO and returning -1.

Requesting zero events is not an error - it is a no-op that reads
nothing. Handle it explicitly by clearing num_events and returning 0
before attempting the read.

Add a test case verifying the behavior.

Fixes: b7ba732e6a ("treewide: libgpiod v2 implementation")
Assisted-by: Claude Opus 4.8
Link: https://patch.msgid.link/20260608-more-pre-2-3-fixes-v1-2-577a5ba426a5@oss.qualcomm.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-06-15 09:38:16 +02:00
Bartosz Golaszewski
65b5ed66e4 core: line-config: use gpiod_line_settings_free() to release settings
When releasing a no-longer-referenced settings node in
gpiod_line_config_add_line_settings(), the embedded settings object was
freed with a raw free() instead of gpiod_line_settings_free(), unlike
every other release site (e.g. free_refs()).

This is currently harmless because gpiod_line_settings_free() is just a
free(), but bypassing the type's destructor is a latent leak should the
struct ever gain heap-allocated members. Use the proper destructor for
consistency.

Fixes: 2560a857ce ("core: keep memory usage of struct line_config in check")
Assisted-by: Claude Opus 4.8
Link: https://patch.msgid.link/20260608-more-pre-2-3-fixes-v1-1-577a5ba426a5@oss.qualcomm.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-06-15 09:38:16 +02:00
Bartosz Golaszewski
84700a6d5f dbus: manager: move g_tree_remove() out of g_assert() in gpiodbus_id_free()
g_assert() is compiled out when G_DISABLE_ASSERT is defined, which would
cause the g_tree_remove() call to disappear entirely, leading to ID leaks
and eventual resource exhaustion. Move the side-effecting call outside
the assertion, consistent with the pattern used elsewhere in the daemon.

Fixes: a5ab76da1e ("dbus: add the D-Bus daemon, command-line client and tests")
Assisted-by: Claude Opus 4.6
Reviewed-by: Vincent Fazio <vfazio@xes-inc.com>
Tested-by: Vincent Fazio <vfazio@xes-inc.com>
Link: https://patch.msgid.link/20260604-pre-2-3-fixes-v2-15-781bcaf47475@oss.qualcomm.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-06-08 10:01:21 +02:00
Bartosz Golaszewski
d7d6ae8dd4 bindings: python: release the GIL in chip_get_info()
Other methods that call into the C library (chip_get_line_info,
chip_watch_line_info, etc.) properly release the GIL with
Py_BEGIN_ALLOW_THREADS/Py_END_ALLOW_THREADS, but chip_get_info() was
missed. Add the missing GIL release for consistency and to avoid
blocking other Python threads during the ioctl.

Fixes: b7ba732e6a ("treewide: libgpiod v2 implementation")
Assisted-by: Claude Opus 4.6
Reviewed-by: Vincent Fazio <vfazio@xes-inc.com>
Tested-by: Vincent Fazio <vfazio@xes-inc.com>
Link: https://patch.msgid.link/20260604-pre-2-3-fixes-v2-14-781bcaf47475@oss.qualcomm.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-06-08 10:01:21 +02:00
Bartosz Golaszewski
e6fb6554e9 bindings: python: check for errors after PyIter_Next() in set_output_values
PyIter_Next() returns NULL both at end-of-iteration and when an exception
is raised. The code breaks out of the loop without checking
PyErr_Occurred(), potentially swallowing Python exceptions and continuing
with a partially filled buffer.

Add the missing error check.

Fixes: 27a69b0bfd ("bindings: python: add the output_values argument to Chip.request_lines()")
Assisted-by: Claude Opus 4.6
Reviewed-by: Vincent Fazio <vfazio@xes-inc.com>
Tested-by: Vincent Fazio <vfazio@xes-inc.com>
Link: https://patch.msgid.link/20260604-pre-2-3-fixes-v2-13-781bcaf47475@oss.qualcomm.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-06-08 10:01:21 +02:00
Bartosz Golaszewski
3c04ff9949 bindings: python: check for errors after PyIter_Next() in request_get_values
PyIter_Next() returns NULL both at end-of-iteration and when an exception
is raised. The code breaks out of the loop without checking
PyErr_Occurred(), potentially swallowing Python exceptions and continuing
with a partially filled buffer.

Add the missing error check.

Fixes: b7ba732e6a ("treewide: libgpiod v2 implementation")
Assisted-by: Claude Opus 4.6
Reviewed-by: Vincent Fazio <vfazio@xes-inc.com>
Tested-by: Vincent Fazio <vfazio@xes-inc.com>
Link: https://patch.msgid.link/20260604-pre-2-3-fixes-v2-12-781bcaf47475@oss.qualcomm.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-06-08 10:01:21 +02:00
Bartosz Golaszewski
62283cc98c bindings: rust: use repr(transparent) on edge Event and simplify casts
The Event struct wraps a single *mut gpiod_edge_event pointer and is cast
from raw pointers in event_buffer.rs. Without #[repr(transparent)], the
layout compatibility is not guaranteed by the language specification.

Add #[repr(transparent)] to make the pointer cast sound, and replace
try_into().unwrap() with a simple 'as' cast for the sequence number
getters since c_ulong and usize are the same width on Linux.

Fixes: 91f9373c65 ("bindings: rust: Add libgpiod crate")
Assisted-by: Claude Opus 4.6
Reviewed-by: Vincent Fazio <vfazio@xes-inc.com>
Tested-by: Vincent Fazio <vfazio@xes-inc.com>
Link: https://patch.msgid.link/20260604-pre-2-3-fixes-v2-11-781bcaf47475@oss.qualcomm.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-06-08 10:01:21 +02:00
Bartosz Golaszewski
33a92137f1 bindings: rust: implement Eq and Ord for chip::Info
chip::Info already implements PartialEq and PartialOrd (both based on
comparing chip names as UTF-8 strings), but not their total-order
counterparts Eq and Ord. The omission is inconsistent: types with a
total order should implement Ord so callers can use the ergonomic sort()
/ min() / max() APIs.

Add the missing impls. Ord::cmp() delegates to partial_cmp() and falls
back to Ordering::Equal for the unreachable case where a chip name is
not valid UTF-8. Update gpiochip_devices() to use cmp() directly now
that Ord is available.

Assisted-by: Claude Sonnet 4.6
Acked-by: Vincent Fazio <vfazio@xes-inc.com>
Reviewed-by: Vincent Fazio <vfazio@xes-inc.com>
Tested-by: Vincent Fazio <vfazio@xes-inc.com>
Link: https://patch.msgid.link/20260604-pre-2-3-fixes-v2-10-781bcaf47475@oss.qualcomm.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-06-08 10:01:21 +02:00
Bartosz Golaszewski
b94f9591e6 bindings: rust: avoid potential panic in gpiochip_devices()
chip::Info::partial_cmp() returns None when either chip name cannot be
decoded as UTF-8. Passing that None straight to unwrap() in
gpiochip_devices() would panic at runtime. Use unwrap_or(Ordering::Equal)
so that chips with undecodable names are treated as equal rather than
causing a panic.

Fixes: 91f9373c65 ("bindings: rust: Add libgpiod crate")
Assisted-by: Claude Sonnet 4.6
Acked-by: Vincent Fazio <vfazio@xes-inc.com>
Reviewed-by: Vincent Fazio <vfazio@xes-inc.com>
Tested-by: Vincent Fazio <vfazio@xes-inc.com>
Link: https://patch.msgid.link/20260604-pre-2-3-fixes-v2-9-781bcaf47475@oss.qualcomm.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-06-08 10:01:21 +02:00
Bartosz Golaszewski
b5225b6d76 bindings: cxx: use get_mapped_value() for all enum mappings in line_info
The direction() method correctly uses get_mapped_value() which converts
std::out_of_range to the project's bad_mapping exception, but bias(),
drive(), edge_detection(), and event_clock() call .at() directly. This
causes them to throw std::out_of_range instead of the expected
bad_mapping exception on unexpected values.

Use get_mapped_value() consistently for all enum mapping lookups.

Fixes: 78020eae9d ("bindings: cxx: remove duplicate mapping helper")
Assisted-by: Claude Opus 4.6
Reviewed-by: Vincent Fazio <vfazio@xes-inc.com>
Tested-by: Vincent Fazio <vfazio@xes-inc.com>
Link: https://patch.msgid.link/20260604-pre-2-3-fixes-v2-8-781bcaf47475@oss.qualcomm.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-06-08 10:01:21 +02:00
Bartosz Golaszewski
e773070e6e bindings: cxx: drop redundant class qualifier in line_request::set_value
The definition of set_value() carried a duplicate class name prefix
(line_request::line_request::set_value) which compiles correctly but
is misleading and inconsistent with every other method definition in
the same file.

Fixes: b7ba732e6a ("treewide: libgpiod v2 implementation")
Assisted-by: Claude Sonnet 4.6
Acked-by: Vincent Fazio <vfazio@xes-inc.com>
Reviewed-by: Vincent Fazio <vfazio@xes-inc.com>
Tested-by: Vincent Fazio <vfazio@xes-inc.com>
Link: https://patch.msgid.link/20260604-pre-2-3-fixes-v2-7-781bcaf47475@oss.qualcomm.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-06-08 10:01:21 +02:00
Bartosz Golaszewski
f4ee1f36f9 bindings: cxx: fix consumer string in line_info stream operator
The consumer field in operator<< for line_info was using info.name()
instead of info.consumer() when building the quoted consumer string.
As a result any non-empty consumer would be printed as the line name,
not the actual consumer string.

Fixes: b7ba732e6a ("treewide: libgpiod v2 implementation")
Assisted-by: Claude Sonnet 4.6
Acked-by: Vincent Fazio <vfazio@xes-inc.com>
Reviewed-by: Vincent Fazio <vfazio@xes-inc.com>
Tested-by: Vincent Fazio <vfazio@xes-inc.com>
Link: https://patch.msgid.link/20260604-pre-2-3-fixes-v2-6-781bcaf47475@oss.qualcomm.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-06-08 10:01:21 +02:00
Bartosz Golaszewski
bb19198661 tests: gpiosim: remove dead container_of() assignment in gpiosim_dev_enable()
The assignment to bank via container_of() immediately before the
list_for_each_entry() loop is dead code: the loop macro reinitialises
bank on every iteration and the initial value is never read.

Assisted-by: Claude Sonnet 4.6
Acked-by: Vincent Fazio <vfazio@xes-inc.com>
Reviewed-by: Vincent Fazio <vfazio@xes-inc.com>
Tested-by: Vincent Fazio <vfazio@xes-inc.com>
Link: https://patch.msgid.link/20260604-pre-2-3-fixes-v2-5-781bcaf47475@oss.qualcomm.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-06-08 10:01:21 +02:00
Bartosz Golaszewski
ff535296ff tools: gpionotify: use the parse_config() return value to advance argv
Every other tool (gpioinfo, gpiomon, gpioset …) uses the value returned
by parse_config() to advance argc/argv past the parsed options:

    i = parse_config(argc, argv, &cfg);
    argc -= i;
    argv += i;

gpionotify was directly using the global optind variable instead of i,
which is equivalent today because parse_config() returns optind, but is
inconsistent and would silently break if the function were ever changed
to return something else.

Assisted-by: Claude Sonnet 4.6
Acked-by: Vincent Fazio <vfazio@xes-inc.com>
Reviewed-by: Vincent Fazio <vfazio@xes-inc.com>
Tested-by: Vincent Fazio <vfazio@xes-inc.com>
Link: https://patch.msgid.link/20260604-pre-2-3-fixes-v2-4-781bcaf47475@oss.qualcomm.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-06-08 10:01:21 +02:00
Bartosz Golaszewski
8098a02b46 core: add explicit NUL-termination after strncpy() calls
Both gpiod_chip_info_from_uapi() and gpiod_line_info_from_uapi() copy
kernel UAPI name strings with strncpy() into buffers that are one byte
larger than GPIO_MAX_NAME_SIZE. If the source string is exactly
GPIO_MAX_NAME_SIZE bytes long strncpy() will not append a NUL, so the
strings are only guaranteed to be terminated because the structs are
zeroed with memset() beforehand.

Make the termination unconditional and independent of the zeroing by
writing the NUL byte explicitly after each strncpy() call.

Fixes: c040d4e15e ("core: fix 1-byte buffer over-read bugs in gpiod_chip_info_from_uapi()")
Fixes: 5533f277aa ("core: fix line-info strings termination")
Assisted-by: Claude Sonnet 4.6
Acked-by: Vincent Fazio <vfazio@xes-inc.com>
Reviewed-by: Vincent Fazio <vfazio@xes-inc.com>
Tested-by: Vincent Fazio <vfazio@xes-inc.com>
Link: https://patch.msgid.link/20260604-pre-2-3-fixes-v2-3-781bcaf47475@oss.qualcomm.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-06-08 10:01:21 +02:00
Bartosz Golaszewski
e5cc32989e core: replace strcpy() with strncpy() in gpiod_request_config_to_uapi()
Both source and destination buffers are char[GPIO_MAX_NAME_SIZE] and
the source is always null-terminated, so strcpy() is safe here, but
since the copy length is statically known, strncpy() is more explicit.

Assisted-by: Claude Sonnet 4.6
Acked-by: Vincent Fazio <vfazio@xes-inc.com>
Reviewed-by: Vincent Fazio <vfazio@xes-inc.com>
Tested-by: Vincent Fazio <vfazio@xes-inc.com>
Link: https://patch.msgid.link/20260604-pre-2-3-fixes-v2-2-781bcaf47475@oss.qualcomm.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-06-08 10:01:21 +02:00
Bartosz Golaszewski
69d0c58051 core: fix signed/unsigned type mismatch in gpiod_line_request_get_value()
The local variable used to store the return value of
gpiod_line_request_get_values_subset() was declared as unsigned int
while the function returns int (-1 on error). The check for the error
condition worked by accident because assigning -1 to an unsigned type
produces UINT_MAX, which is non-zero, but using the wrong type is
misleading and technically invoking implementation-defined behaviour.
Use int throughout.

Fixes: b7ba732e6a ("treewide: libgpiod v2 implementation")
Assisted-by: Claude Sonnet 4.6
Acked-by: Vincent Fazio <vfazio@xes-inc.com>
Reviewed-by: Vincent Fazio <vfazio@xes-inc.com>
Tested-by: Vincent Fazio <vfazio@xes-inc.com>
Link: https://patch.msgid.link/20260604-pre-2-3-fixes-v2-1-781bcaf47475@oss.qualcomm.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-06-08 10:01:21 +02:00
Bartosz Golaszewski
f0f56bbba3 treewide: add REUSE.toml
We have some .txt files that contain SPDX headers to make `reuse lint`
happy. Let's move them into REUSE.toml instead for better readability of
their content.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-06-02 14:51:42 +02:00
Bartosz Golaszewski
7950002228 build: display the summary of build options after meson setup
It's useful to know which options ended up really being selected by
meson (for instance for --auto-features) so use the dedicated meson
function to display the summary.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-06-02 14:51:32 +02:00
Vincent Fazio
c0650edd44 bindings: python: add a changelog
Add a changelog to summarize changes to the bindings between releases.

Signed-off-by: Vincent Fazio <vfazio@gmail.com>
Link: https://patch.msgid.link/20260527215922.18678-4-vfazio@gmail.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-05-28 11:01:02 +02:00
Vincent Fazio
c9378d6c63 bindings: python: support free-threaded CPython
PEP 703 [0] discusses making the GIL optional in certain builds of the
CPython interpreter.

This build option was available experiementally in Python 3.13 but has
since been stabilized in Python 3.14 per PEP 779 [1].

According to the porting guide [2], there is no strict requirement that
C extensions must be thread-safe.

Experiments have shown that no logic changes are required if callers
use sychronization mechanisms provided by the Python standard library.

The documentation has been updated to call this out specifically using
terminology from the porting guide [3].

[0]: https://peps.python.org/pep-0703/
[1]: https://peps.python.org/pep-0779/
[2]: https://py-free-threading.github.io/porting/#define-and-document-thread-safety-guarantees
[3]: https://py-free-threading.github.io/documentation-principles/#free-threading-terminology

Signed-off-by: Vincent Fazio <vfazio@gmail.com>
Link: https://patch.msgid.link/20260527215922.18678-3-vfazio@gmail.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-05-28 10:59:30 +02:00
Vincent Fazio
9d8940f38f bindings: python: tests: add multi-threaded tests
Add new multi-threaded test cases to ensure the bindings operate as
expected and do not lead to crashes or data corruption.

Signed-off-by: Vincent Fazio <vfazio@gmail.com>
Link: https://patch.msgid.link/20260527215922.18678-2-vfazio@gmail.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-05-28 10:59:12 +02:00
Bartosz Golaszewski
ec46215f7b core: allow modifying settings for existing offsets in a full line-config
gpiod_line_config_add_line_settings() checks the remaining capacity
using the raw num_offsets from the caller. If the config is already at
the 64-line limit, any call - even one that would only update settings
for already configured offsets - would fail with E2BIG making it
impossible to reconfigure a line once the config is full.

Extend gpiod_line_config_add_line_settings() to only count offsets that
would require a new config slot and use this number to check if the
line-config object still has capacity.

Add a test case allowing to verify the fix.

Fixes: b7ba732e6a ("treewide: libgpiod v2 implementation")
Link: https://patch.msgid.link/20260522-line-config-full-config-bug-v1-1-b138958bf05d@oss.qualcomm.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-05-26 11:00:13 +02:00
Bartosz Golaszewski
0fe66719dd tools: reuse libgpiotools
Reuse as much of the functionality provided by the new libgpiotools
shared library in tools-common while retaining the functions used to
validate command-line arguments, print help text or bail-out on errors.
We keep the existing interfaces to gpio-tools and use them to wrap the
API exposed by libgpiotools as we want to keep bailing out on errors
without requiring tools to do a lot of repeated error checking.

Acked-by: Kent Gibson <warthog618@gmail.com>
Link: https://patch.msgid.link/20260513-tools-common-shared-lib-v1-2-58ac28a9f1d5@oss.qualcomm.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-05-18 10:35:51 +02:00
Bartosz Golaszewski
78fd3f3b62 tools: provide the libgpiotools shared library
In order to enable users of the project to reuse the high-level functions
provided to gpio-tools via the static tools-common library (for
instance: for line lookup by name), expose a sub-set of its
functionality in new shared library: libgpiotools, accompanied by a new
public header and providing a stable API/ABI.

Add a new rst page documenting the public API of the libgpiotools shared
library and integrate it into the sphinx build.

Closes: https://github.com/brgl/libgpiod/discussions/178
Acked-by: Kent Gibson <warthog618@gmail.com>
Link: https://patch.msgid.link/20260513-tools-common-shared-lib-v1-1-58ac28a9f1d5@oss.qualcomm.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-05-18 10:35:51 +02:00
Bartosz Golaszewski
5eed939e0d build: use int as the type of soversion variables
The soversion argument of meson's both_libraries() accepts int and str
types. Use int as it's less error-prone and can be more easily compared.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-05-12 12:50:15 +02:00
Bartosz Golaszewski
cadfccff02 build: fix linking against libgpiodbus
Drop the 'sources' argument from meson's declare_dependency() for
libgpiodbus as this causes the build system to include the source files
in the source list of the targets using the dependency rather than link
against the libgpiodbus shared library.

Fixes: 97bab4cc87 ("build: replace autotools with meson & ninja")
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-05-12 12:32:33 +02:00
Bartosz Golaszewski
a7b443f2c7 bindings: python: don't decref objects on PyList_SetItem() failure
PyList_SetItem() steals the reference to the object we want to add
regardless of whether it succeeds or fails. Don't decrease the reference
count of the object we failed to add in error path.

Fixes: b7ba732e6a ("treewide: libgpiod v2 implementation")
Closes: https://github.com/brgl/libgpiod/issues/187
Reviewed-by: Vincent Fazio <vfazio@xes-inc.com>
Link: https://patch.msgid.link/20260511111538.60844-1-bartosz.golaszewski@oss.qualcomm.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-05-12 09:29:54 +02:00
Bartosz Golaszewski
5e998c353a build: only build man pages if we can run programs
Use the meson can_run_host_binaries() function to determine whether we
should even descend into the man/ directory as the man pages are built
using help2man which requires us to actually run the executables.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-05-11 11:57:06 +02:00
Bartosz Golaszewski
774ca52793 man: update my email address in the template
Use my kernel.org address in the man pages.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-05-07 10:01:24 +02:00
Bartosz Golaszewski
bda239723d tools: provide and use die_oom()
Instead of repeating the 'die("out of memory");' statement, provide a
variant of die() that emits this message and use it on OOM failures in
all gpio-tools.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-05-07 09:39:26 +02:00
Bartosz Golaszewski
57ef137d52 build: make the package version PEP-440 compliant
Recent versions of python tooling - like, for instance, one used by
gi-docgen - may reject PEP-440 non-compliant version strings and thus
fail to generate docs in some environments. Change the '-devel' suffix
to '+devel' to make these tools happy.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-05-05 13:26:35 +02:00
Bartosz Golaszewski
e5fc7d8901 docs: install meson and ninja for the readthedocs build
Update .readthedocs.yaml to pull in the correct packages for its docker
build.

Fixes: 97bab4cc87 ("build: replace autotools with meson & ninja")
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-05-05 11:20:08 +02:00
Bartosz Golaszewski
315c48f81c docs: disable auto-features in sphinx build
When generating docs, don't enable more options than is necessary. This
could pull additional dependencies that we don't want.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-05-04 15:44:14 +02:00
Bartosz Golaszewski
b2b1267d1c docs: fix build with meson
Migration to meson broke the sphinx build for docs. Wire the gi-docgen
generation into the meson build and fix docs/conf.py to use the new
build system.

Fixes: 97bab4cc87 ("build: replace autotools with meson & ninja")
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-05-04 12:32:04 +02:00
Bartosz Golaszewski
a7f3feb1a6 build: put the project URL in one place
Define and ue the project_url variable instead of repeating the URL in
several places.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-05-04 12:30:02 +02:00
Bartosz Golaszewski
78bfc44f0d bindings: rust: remove leftover autotools bits
Remove leftover mentions of Makefiles from Cargo.toml files in rust
bindings. They are no longer needed after the migration to meson.

Fixes: 97bab4cc87 ("build: replace autotools with meson & ninja")
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-05-04 11:40:01 +02:00
Bartosz Golaszewski
1dc5b7eccf build: remove leftover autotools .pc.in templates
The meson build already generates all pkg-config files via
pkgconfig.generate() in the respective meson.build files. The .pc.in
templates were not cleaned up during the autotools-to-meson migration
and are dead code. Drop them.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-05-04 11:04:20 +02:00
Bartosz Golaszewski
97bab4cc87 build: replace autotools with meson & ninja
Port the project from autotools to meson. All build options are
preserved with the same semantics.

The goal of this change is to produce the same set of files as the
current autotools approach: libraries with correct SONAME/version,
headers, pkg-config files, GObject introspection data, Python extension
modules, D-Bus interface and configuration files. This is done to enable
a one-liner test command running all available test suites.

The only thing we drop is the `make docs` target as it's rarely used and
docs can be generated easily with sphinx directly. They are also not
required for testing.

The Rust workspace is built via a custom target that invokes cargo and
uses SYSTEM_DEPS_* environment variables to point the libgpiod-sys and
gpiosim-sys crates at the locally built C libraries.

In addition to porting the build files to meson, update the docs, remove
unneded items from .gitignore files (or remove them entirely if they're
no longer needed) and update the shunit2 test scripts to no longer care
about source directories.

Tested-by: Erik Wierich <erik@riscstar.com>
Tested-by: Vincent Fazio <vfazio@xes-inc.com>
Link: https://patch.msgid.link/20260428-meson-build-v3-1-8da6976fb0a2@oss.qualcomm.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-05-04 09:51:03 +02:00
Vincent Fazio
7acbbcf778 bindings: python: tests: migrate the gpiosim module to multi-phase init
Single-phase initialization has been classified as legacy within CPython
documentation [0] with multi-phase being its successor [1].

As such, switch to the new methodology.

Switching requires converting `gpiosim.Chip` to be a heap-type [2] in
order to access the module's state [3].

[0]: https://docs.python.org/3/c-api/extension-modules.html#legacy-single-phase-initialization
[1]: https://docs.python.org/3/c-api/extension-modules.html#multi-phase-initialization
[2]: https://docs.python.org/3/c-api/typeobj.html#heap-types
[3]: https://docs.python.org/3/howto/isolating-extensions.html#heap-types

Signed-off-by: Vincent Fazio <vfazio@gmail.com>
Link: https://patch.msgid.link/20260423222125.29097-9-vfazio@gmail.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-04-24 11:08:11 +02:00
Vincent Fazio
bf4211f3e1 bindings: python: tests: migrate the system module to multi-phase init
Single-phase initialization has been classified as legacy within CPython
documentation [0] with multi-phase being its successor [1].

As such, switch to the new methodology.

[0]: https://docs.python.org/3/c-api/extension-modules.html#legacy-single-phase-initialization
[1]: https://docs.python.org/3/c-api/extension-modules.html#multi-phase-initialization

Signed-off-by: Vincent Fazio <vfazio@gmail.com>
Link: https://patch.msgid.link/20260423222125.29097-8-vfazio@gmail.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-04-24 11:07:35 +02:00
Vincent Fazio
89ffee8349 bindings: python: migrate the gpiod._ext module to multi-phase init
Single-phase initialization has been classified as legacy within CPython
documentation [0] with multi-phase being its successor [1].

As such, switch to the new methodology.

[0]: https://docs.python.org/3/c-api/extension-modules.html#legacy-single-phase-initialization
[1]: https://docs.python.org/3/c-api/extension-modules.html#multi-phase-initialization

Signed-off-by: Vincent Fazio <vfazio@gmail.com>
Link: https://patch.msgid.link/20260423222125.29097-7-vfazio@gmail.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-04-24 11:01:34 +02:00