From: Bartosz Golaszewski <bartosz.golaszewski@xxxxxxxxxx> Add a new argument to Chip.request_lines() that allows the user to pass a list of output values for configured lines instead of using several LineSettings objects between which the only difference is the output value. Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@xxxxxxxxxx> --- bindings/python/gpiod/chip.py | 6 ++ bindings/python/gpiod/ext/line-config.c | 64 +++++++++++++++++++++ bindings/python/tests/tests_line_request.py | 14 +++++ 3 files changed, 84 insertions(+) diff --git a/bindings/python/gpiod/chip.py b/bindings/python/gpiod/chip.py index ad2eddd..4f5f9b4 100644 --- a/bindings/python/gpiod/chip.py +++ b/bindings/python/gpiod/chip.py @@ -6,10 +6,12 @@ from .chip_info import ChipInfo from .exception import ChipClosedError from .info_event import InfoEvent from .internal import poll_fd +from .line import Value from .line_info import LineInfo from .line_settings import LineSettings, _line_settings_to_ext from .line_request import LineRequest from collections import Counter +from collections.abc import Iterable from datetime import timedelta from errno import ENOENT from select import select @@ -221,6 +223,7 @@ class Chip: config: dict[tuple[Union[int, str]], Optional[LineSettings]], consumer: Optional[str] = None, event_buffer_size: Optional[int] = None, + output_values: Optional[Iterable[Value]] = None, ) -> LineRequest: """ Request a set of lines for exclusive usage. @@ -279,6 +282,9 @@ class Chip: offsets, _line_settings_to_ext(settings or LineSettings()) ) + if output_values: + line_cfg.set_output_values(output_values) + req_internal = self._chip.request_lines(line_cfg, consumer, event_buffer_size) request = LineRequest(req_internal) diff --git a/bindings/python/gpiod/ext/line-config.c b/bindings/python/gpiod/ext/line-config.c index 173ca6b..0bba112 100644 --- a/bindings/python/gpiod/ext/line-config.c +++ b/bindings/python/gpiod/ext/line-config.c @@ -89,12 +89,76 @@ line_config_add_line_settings(line_config_object *self, PyObject *args) Py_RETURN_NONE; } +static PyObject * +line_config_set_output_values(line_config_object *self, PyObject *args) +{ + PyObject *values, *iter, *next, *val_stripped; + enum gpiod_line_value *valbuf; + Py_ssize_t num_values, pos; + int ret; + + values = PyTuple_GetItem(args, 0); + if (!values) + return NULL; + + num_values = PyObject_Size(values); + if (num_values < 0) + return NULL; + + valbuf = PyMem_Calloc(num_values, sizeof(*valbuf)); + if (!valbuf) + return PyErr_NoMemory(); + + iter = PyObject_GetIter(values); + if (!iter) { + PyMem_Free(valbuf); + return NULL; + } + + for (pos = 0;; pos++) { + next = PyIter_Next(iter); + if (!next) { + Py_DECREF(iter); + break; + } + + val_stripped = PyObject_GetAttrString(next, "value"); + Py_DECREF(next); + if (!val_stripped) { + PyMem_Free(valbuf); + Py_DECREF(iter); + return NULL; + } + + valbuf[pos] = PyLong_AsLong(val_stripped); + Py_DECREF(val_stripped); + if (PyErr_Occurred()) { + PyMem_Free(valbuf); + Py_DECREF(iter); + return NULL; + } + } + + ret = gpiod_line_config_set_output_values(self->cfg, + valbuf, num_values); + PyMem_Free(valbuf); + if (ret) + return Py_gpiod_SetErrFromErrno(); + + Py_RETURN_NONE; +} + static PyMethodDef line_config_methods[] = { { .ml_name = "add_line_settings", .ml_meth = (PyCFunction)line_config_add_line_settings, .ml_flags = METH_VARARGS, }, + { + .ml_name = "set_output_values", + .ml_meth = (PyCFunction)line_config_set_output_values, + .ml_flags = METH_VARARGS, + }, { } }; diff --git a/bindings/python/tests/tests_line_request.py b/bindings/python/tests/tests_line_request.py index c0ac768..1dc2c71 100644 --- a/bindings/python/tests/tests_line_request.py +++ b/bindings/python/tests/tests_line_request.py @@ -402,6 +402,20 @@ class LineRequestConsumerString(TestCase): self.assertEqual(info.consumer, "?") +class LineRequestSetOutputValues(TestCase): + def test_request_with_globally_set_output_values(self): + sim = gpiosim.Chip(num_lines=4) + with gpiod.request_lines( + sim.dev_path, + config={(0, 1, 2, 3): gpiod.LineSettings(direction=Direction.OUTPUT)}, + output_values=(Value.ACTIVE, Value.INACTIVE, Value.ACTIVE, Value.INACTIVE), + ) as request: + self.assertEqual(sim.get_value(0), SimVal.ACTIVE) + self.assertEqual(sim.get_value(1), SimVal.INACTIVE) + self.assertEqual(sim.get_value(2), SimVal.ACTIVE) + self.assertEqual(sim.get_value(3), SimVal.INACTIVE) + + class ReconfigureRequestedLines(TestCase): def setUp(self): self.sim = gpiosim.Chip(num_lines=8, line_names={3: "foo", 4: "bar", 6: "baz"}) -- 2.37.2