blob: 8bfb4c4f7662bb4dae1cfc88202b70aa7ebec8ec [file] [log] [blame]
// SPDX-License-Identifier: LGPL-2.1-or-later
// SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <bartekgola@gmail.com>
#include <Python.h>
#include <gpiod.h>
#define LINE_REQUEST_MAX_LINES 64
typedef struct {
PyObject_HEAD;
struct gpiod_chip *chip;
} gpiod_ChipObject;
typedef struct {
PyObject_HEAD;
struct gpiod_line *line;
gpiod_ChipObject *owner;
} gpiod_LineObject;
typedef struct {
PyObject_HEAD;
struct gpiod_line_event event;
gpiod_LineObject *source;
} gpiod_LineEventObject;
typedef struct {
PyObject_HEAD;
PyObject **lines;
Py_ssize_t num_lines;
Py_ssize_t iter_idx;
} gpiod_LineBulkObject;
typedef struct {
PyObject_HEAD;
unsigned int offset;
gpiod_ChipObject *owner;
} gpiod_LineIterObject;
static gpiod_LineBulkObject *gpiod_LineToLineBulk(gpiod_LineObject *line);
static gpiod_LineObject *gpiod_MakeLineObject(gpiod_ChipObject *owner,
struct gpiod_line *line);
enum {
gpiod_LINE_REQ_DIR_AS_IS = 1,
gpiod_LINE_REQ_DIR_IN,
gpiod_LINE_REQ_DIR_OUT,
gpiod_LINE_REQ_EV_FALLING_EDGE,
gpiod_LINE_REQ_EV_RISING_EDGE,
gpiod_LINE_REQ_EV_BOTH_EDGES,
};
enum {
gpiod_LINE_REQ_FLAG_OPEN_DRAIN = GPIOD_BIT(0),
gpiod_LINE_REQ_FLAG_OPEN_SOURCE = GPIOD_BIT(1),
gpiod_LINE_REQ_FLAG_ACTIVE_LOW = GPIOD_BIT(2),
gpiod_LINE_REQ_FLAG_BIAS_DISABLED = GPIOD_BIT(3),
gpiod_LINE_REQ_FLAG_BIAS_PULL_DOWN = GPIOD_BIT(4),
gpiod_LINE_REQ_FLAG_BIAS_PULL_UP = GPIOD_BIT(5),
};
enum {
gpiod_DIRECTION_INPUT = 1,
gpiod_DIRECTION_OUTPUT,
};
enum {
gpiod_DRIVE_PUSH_PULL,
gpiod_DRIVE_OPEN_DRAIN,
gpiod_DRIVE_OPEN_SOURCE,
};
enum {
gpiod_BIAS_UNKNOWN = 1,
gpiod_BIAS_DISABLED,
gpiod_BIAS_PULL_UP,
gpiod_BIAS_PULL_DOWN,
};
enum {
gpiod_RISING_EDGE = 1,
gpiod_FALLING_EDGE,
};
static bool gpiod_ChipIsClosed(gpiod_ChipObject *chip)
{
if (!chip->chip) {
PyErr_SetString(PyExc_ValueError,
"I/O operation on closed file");
return true;
}
return false;
}
static PyObject *gpiod_CallMethodPyArgs(PyObject *obj, const char *method,
PyObject *args, PyObject *kwds)
{
PyObject *callable, *ret;
callable = PyObject_GetAttrString((PyObject *)obj, method);
if (!callable)
return NULL;
ret = PyObject_Call(callable, args, kwds);
Py_DECREF(callable);
return ret;
}
static int gpiod_LineEvent_init(PyObject *Py_UNUSED(ignored0),
PyObject *Py_UNUSED(ignored1),
PyObject *Py_UNUSED(ignored2))
{
PyErr_SetString(PyExc_NotImplementedError,
"Only gpiod.Line can create new LineEvent objects.");
return -1;
}
static void gpiod_LineEvent_dealloc(gpiod_LineEventObject *self)
{
if (self->source)
Py_DECREF(self->source);
PyObject_Del(self);
}
PyDoc_STRVAR(gpiod_LineEvent_get_type_doc,
"Event type of this line event (integer).");
PyObject *gpiod_LineEvent_get_type(gpiod_LineEventObject *self,
PyObject *Py_UNUSED(ignored))
{
int rv;
if (self->event.event_type == GPIOD_LINE_EVENT_RISING_EDGE)
rv = gpiod_RISING_EDGE;
else
rv = gpiod_FALLING_EDGE;
return Py_BuildValue("I", rv);
}
PyDoc_STRVAR(gpiod_LineEvent_get_sec_doc,
"Seconds value of the line event timestamp (integer).");
PyObject *gpiod_LineEvent_get_sec(gpiod_LineEventObject *self,
PyObject *Py_UNUSED(ignored))
{
return Py_BuildValue("I", self->event.ts.tv_sec);
}
PyDoc_STRVAR(gpiod_LineEvent_get_nsec_doc,
"Nanoseconds value of the line event timestamp (integer).");
PyObject *gpiod_LineEvent_get_nsec(gpiod_LineEventObject *self,
PyObject *Py_UNUSED(ignored))
{
return Py_BuildValue("I", self->event.ts.tv_nsec);
}
PyDoc_STRVAR(gpiod_LineEvent_get_source_doc,
"Line object representing the GPIO line on which this event\n"
"occurred (gpiod.Line object).");
gpiod_LineObject *gpiod_LineEvent_get_source(gpiod_LineEventObject *self,
PyObject *Py_UNUSED(ignored))
{
Py_INCREF(self->source);
return self->source;
}
static PyGetSetDef gpiod_LineEvent_getset[] = {
{
.name = "type",
.get = (getter)gpiod_LineEvent_get_type,
.doc = gpiod_LineEvent_get_type_doc,
},
{
.name = "sec",
.get = (getter)gpiod_LineEvent_get_sec,
.doc = gpiod_LineEvent_get_sec_doc,
},
{
.name = "nsec",
.get = (getter)gpiod_LineEvent_get_nsec,
.doc = gpiod_LineEvent_get_nsec_doc,
},
{
.name = "source",
.get = (getter)gpiod_LineEvent_get_source,
.doc = gpiod_LineEvent_get_source_doc,
},
{ }
};
static PyObject *gpiod_LineEvent_repr(gpiod_LineEventObject *self)
{
PyObject *line_repr, *ret;
const char *edge;
if (self->event.event_type == GPIOD_LINE_EVENT_RISING_EDGE)
edge = "RISING EDGE";
else
edge = "FALLING EDGE";
line_repr = PyObject_CallMethod((PyObject *)self->source,
"__repr__", "");
ret = PyUnicode_FromFormat("'%s (%ld.%ld) source(%S)'",
edge, self->event.ts.tv_sec,
self->event.ts.tv_nsec, line_repr);
Py_DECREF(line_repr);
return ret;
}
PyDoc_STRVAR(gpiod_LineEventType_doc,
"Represents a single GPIO line event. This object is immutable and can only\n"
"be created by an instance of gpiod.Line.");
static PyTypeObject gpiod_LineEventType = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "gpiod.LineEvent",
.tp_basicsize = sizeof(gpiod_LineEventObject),
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = gpiod_LineEventType_doc,
.tp_new = PyType_GenericNew,
.tp_init = (initproc)gpiod_LineEvent_init,
.tp_dealloc = (destructor)gpiod_LineEvent_dealloc,
.tp_getset = gpiod_LineEvent_getset,
.tp_repr = (reprfunc)gpiod_LineEvent_repr,
};
static int gpiod_Line_init(PyObject *Py_UNUSED(ignored0),
PyObject *Py_UNUSED(ignored1),
PyObject *Py_UNUSED(ignored2))
{
PyErr_SetString(PyExc_NotImplementedError,
"Only gpiod.Chip can create new Line objects.");
return -1;
}
static void gpiod_Line_dealloc(gpiod_LineObject *self)
{
if (self->owner)
Py_DECREF(self->owner);
PyObject_Del(self);
}
PyDoc_STRVAR(gpiod_Line_owner_doc,
"owner() -> Chip object owning the line\n"
"\n"
"Get the GPIO chip owning this line.");
static PyObject *gpiod_Line_owner(gpiod_LineObject *self,
PyObject *Py_UNUSED(ignored))
{
Py_INCREF(self->owner);
return (PyObject *)self->owner;
}
PyDoc_STRVAR(gpiod_Line_offset_doc,
"offset() -> integer\n"
"\n"
"Get the offset of the GPIO line.");
static PyObject *gpiod_Line_offset(gpiod_LineObject *self,
PyObject *Py_UNUSED(ignored))
{
if (gpiod_ChipIsClosed(self->owner))
return NULL;
return Py_BuildValue("I", gpiod_line_offset(self->line));
}
PyDoc_STRVAR(gpiod_Line_name_doc,
"name() -> string\n"
"\n"
"Get the name of the GPIO line.");
static PyObject *gpiod_Line_name(gpiod_LineObject *self,
PyObject *Py_UNUSED(ignored))
{
const char *name;
if (gpiod_ChipIsClosed(self->owner))
return NULL;
name = gpiod_line_name(self->line);
if (name)
return PyUnicode_FromFormat("%s", name);
Py_RETURN_NONE;
}
PyDoc_STRVAR(gpiod_Line_consumer_doc,
"consumer() -> string\n"
"\n"
"Get the consumer string of the GPIO line.");
static PyObject *gpiod_Line_consumer(gpiod_LineObject *self,
PyObject *Py_UNUSED(ignored))
{
const char *consumer;
if (gpiod_ChipIsClosed(self->owner))
return NULL;
consumer = gpiod_line_consumer(self->line);
if (consumer)
return PyUnicode_FromFormat("%s", consumer);
Py_RETURN_NONE;
}
PyDoc_STRVAR(gpiod_Line_direction_doc,
"direction() -> integer\n"
"\n"
"Get the direction setting of this GPIO line.");
static PyObject *gpiod_Line_direction(gpiod_LineObject *self,
PyObject *Py_UNUSED(ignored))
{
PyObject *ret;
int dir;
if (gpiod_ChipIsClosed(self->owner))
return NULL;
dir = gpiod_line_direction(self->line);
if (dir == GPIOD_LINE_DIRECTION_INPUT)
ret = Py_BuildValue("I", gpiod_DIRECTION_INPUT);
else
ret = Py_BuildValue("I", gpiod_DIRECTION_OUTPUT);
return ret;
}
PyDoc_STRVAR(gpiod_Line_is_active_low_doc,
"is_active_low() -> boolean\n"
"\n"
"Check if this line's signal is inverted");
static PyObject *gpiod_Line_is_active_low(gpiod_LineObject *self,
PyObject *Py_UNUSED(ignored))
{
if (gpiod_ChipIsClosed(self->owner))
return NULL;
if (gpiod_line_is_active_low(self->line))
Py_RETURN_TRUE;
Py_RETURN_FALSE;
}
PyDoc_STRVAR(gpiod_Line_bias_doc,
"bias() -> integer\n"
"\n"
"Get the bias setting of this GPIO line.");
static PyObject *gpiod_Line_bias(gpiod_LineObject *self,
PyObject *Py_UNUSED(ignored))
{
int bias;
if (gpiod_ChipIsClosed(self->owner))
return NULL;
bias = gpiod_line_bias(self->line);
switch (bias) {
case GPIOD_LINE_BIAS_PULL_UP:
return Py_BuildValue("I", gpiod_BIAS_PULL_UP);
case GPIOD_LINE_BIAS_PULL_DOWN:
return Py_BuildValue("I", gpiod_BIAS_PULL_DOWN);
case GPIOD_LINE_BIAS_DISABLED:
return Py_BuildValue("I", gpiod_BIAS_DISABLED);
case GPIOD_LINE_BIAS_UNKNOWN:
default:
return Py_BuildValue("I", gpiod_BIAS_UNKNOWN);
}
}
PyDoc_STRVAR(gpiod_Line_is_used_doc,
"is_used() -> boolean\n"
"\n"
"Check if this line is used by the kernel or other user space process.");
static PyObject *gpiod_Line_is_used(gpiod_LineObject *self,
PyObject *Py_UNUSED(ignored))
{
if (gpiod_ChipIsClosed(self->owner))
return NULL;
if (gpiod_line_is_used(self->line))
Py_RETURN_TRUE;
Py_RETURN_FALSE;
}
PyDoc_STRVAR(gpiod_Line_drive_doc,
"drive() -> integer\n"
"\n"
"Get the current drive setting of this GPIO line.");
static PyObject *gpiod_Line_drive(gpiod_LineObject *self,
PyObject *Py_UNUSED(ignored))
{
int drive;
if (gpiod_ChipIsClosed(self->owner))
return NULL;
drive = gpiod_line_drive(self->line);
switch (drive) {
case GPIOD_LINE_DRIVE_OPEN_DRAIN:
return Py_BuildValue("I", gpiod_DRIVE_OPEN_DRAIN);
case GPIOD_LINE_DRIVE_OPEN_SOURCE:
return Py_BuildValue("I", gpiod_DRIVE_OPEN_SOURCE);
case GPIOD_LINE_DRIVE_PUSH_PULL:
default:
return Py_BuildValue("I", gpiod_DRIVE_PUSH_PULL);
}
}
PyDoc_STRVAR(gpiod_Line_request_doc,
"request(consumer[, type[, flags[, default_val]]]) -> None\n"
"\n"
"Request this GPIO line.\n"
"\n"
" consumer\n"
" Name of the consumer.\n"
" type\n"
" Type of the request.\n"
" flags\n"
" Other configuration flags.\n"
" default_val\n"
" Default value of this line."
"\n"
"Note: default_vals argument (sequence of default values passed down to\n"
"LineBulk.request()) is still supported for backward compatibility but is\n"
"now deprecated when requesting single lines.");
static PyObject *gpiod_Line_request(gpiod_LineObject *self,
PyObject *args, PyObject *kwds)
{
PyObject *ret, *def_val, *def_vals;
gpiod_LineBulkObject *bulk_obj;
int rv;
if (kwds && PyDict_Size(kwds) > 0) {
def_val = PyDict_GetItemString(kwds, "default_val");
def_vals = PyDict_GetItemString(kwds, "default_vals");
} else {
def_val = def_vals = NULL;
}
if (def_val && def_vals) {
PyErr_SetString(PyExc_TypeError,
"Cannot pass both default_val and default_vals arguments at the same time");
return NULL;
}
if (def_val) {
/*
* If default_val was passed as a single value, we wrap it
* in a tuple and add it to the kwds dictionary to be passed
* down to LineBulk.request(). We also remove the 'default_val'
* entry from kwds.
*
* I'm not sure if it's allowed to modify the kwds dictionary
* but it doesn't seem to cause any problems. If it does then
* we can simply copy the dictionary before calling
* LineBulk.request().
*/
rv = PyDict_DelItemString(kwds, "default_val");
if (rv)
return NULL;
def_vals = Py_BuildValue("(O)", def_val);
if (!def_vals)
return NULL;
rv = PyDict_SetItemString(kwds, "default_vals", def_vals);
if (rv) {
Py_DECREF(def_vals);
return NULL;
}
}
bulk_obj = gpiod_LineToLineBulk(self);
if (!bulk_obj)
return NULL;
ret = gpiod_CallMethodPyArgs((PyObject *)bulk_obj,
"request", args, kwds);
Py_DECREF(bulk_obj);
return ret;
}
PyDoc_STRVAR(gpiod_Line_get_value_doc,
"get_value() -> integer\n"
"\n"
"Read the current value of this GPIO line.");
static PyObject *gpiod_Line_get_value(gpiod_LineObject *self,
PyObject *Py_UNUSED(ignored))
{
gpiod_LineBulkObject *bulk_obj;
PyObject *vals, *ret;
bulk_obj = gpiod_LineToLineBulk(self);
if (!bulk_obj)
return NULL;
vals = PyObject_CallMethod((PyObject *)bulk_obj, "get_values", "");
Py_DECREF(bulk_obj);
if (!vals)
return NULL;
ret = PyList_GetItem(vals, 0);
Py_INCREF(ret);
Py_DECREF(vals);
return ret;
}
PyDoc_STRVAR(gpiod_Line_set_value_doc,
"set_value(value) -> None\n"
"\n"
"Set the value of this GPIO line.\n"
"\n"
" value\n"
" New value (integer)");
static PyObject *gpiod_Line_set_value(gpiod_LineObject *self, PyObject *args)
{
gpiod_LineBulkObject *bulk_obj;
PyObject *val, *vals, *ret;
int rv;
rv = PyArg_ParseTuple(args, "O", &val);
if (!rv)
return NULL;
bulk_obj = gpiod_LineToLineBulk(self);
if (!bulk_obj)
return NULL;
vals = Py_BuildValue("(O)", val);
if (!vals) {
Py_DECREF(bulk_obj);
return NULL;
}
ret = PyObject_CallMethod((PyObject *)bulk_obj,
"set_values", "(O)", vals);
Py_DECREF(bulk_obj);
Py_DECREF(vals);
return ret;
}
PyDoc_STRVAR(gpiod_Line_set_config_doc,
"set_config(direction,flags,value) -> None\n"
"\n"
"Set the configuration of this GPIO line.\n"
"\n"
" direction\n"
" New direction (integer)\n"
" flags\n"
" New flags (integer)\n"
" value\n"
" New value (integer)");
static PyObject *gpiod_Line_set_config(gpiod_LineObject *self, PyObject *args)
{
gpiod_LineBulkObject *bulk_obj;
PyObject *dirn, *flags, *val, *vals, *ret;
int rv;
val = NULL;
rv = PyArg_ParseTuple(args, "OO|O", &dirn, &flags, &val);
if (!rv)
return NULL;
bulk_obj = gpiod_LineToLineBulk(self);
if (!bulk_obj)
return NULL;
if (val) {
vals = Py_BuildValue("(O)", val);
if (!vals) {
Py_DECREF(bulk_obj);
return NULL;
}
ret = PyObject_CallMethod((PyObject *)bulk_obj,
"set_config", "OO(O)", dirn, flags, vals);
Py_DECREF(vals);
} else {
ret = PyObject_CallMethod((PyObject *)bulk_obj,
"set_config", "OO", dirn, flags);
}
Py_DECREF(bulk_obj);
return ret;
}
PyDoc_STRVAR(gpiod_Line_set_flags_doc,
"set_flags(flags) -> None\n"
"\n"
"Set the flags of this GPIO line.\n"
"\n"
" flags\n"
" New flags (integer)");
static PyObject *gpiod_Line_set_flags(gpiod_LineObject *self, PyObject *args)
{
gpiod_LineBulkObject *bulk_obj;
PyObject *ret;
bulk_obj = gpiod_LineToLineBulk(self);
if (!bulk_obj)
return NULL;
ret = PyObject_CallMethod((PyObject *)bulk_obj,
"set_flags", "O", args);
Py_DECREF(bulk_obj);
return ret;
}
PyDoc_STRVAR(gpiod_Line_set_direction_input_doc,
"set_direction_input() -> None\n"
"\n"
"Set the direction of this GPIO line to input.\n");
static PyObject *gpiod_Line_set_direction_input(gpiod_LineObject *self,
PyObject *Py_UNUSED(ignored))
{
gpiod_LineBulkObject *bulk_obj;
PyObject *ret;
bulk_obj = gpiod_LineToLineBulk(self);
if (!bulk_obj)
return NULL;
ret = PyObject_CallMethod((PyObject *)bulk_obj,
"set_direction_input", "");
Py_DECREF(bulk_obj);
return ret;
}
PyDoc_STRVAR(gpiod_Line_set_direction_output_doc,
"set_direction_output(value) -> None\n"
"\n"
"Set the direction of this GPIO line to output.\n"
"\n"
" value\n"
" New value (integer)");
static PyObject *gpiod_Line_set_direction_output(gpiod_LineObject *self,
PyObject *args)
{
gpiod_LineBulkObject *bulk_obj;
PyObject *val, *vals, *ret;
int rv;
const char *fmt;
val = NULL;
rv = PyArg_ParseTuple(args, "|O", &val);
if (!rv)
return NULL;
if (val) {
fmt = "(O)";
vals = Py_BuildValue(fmt, val);
} else {
vals = Py_BuildValue("()");
fmt = "O"; /* pass empty args to bulk */
}
if (!vals)
return NULL;
bulk_obj = gpiod_LineToLineBulk(self);
if (!bulk_obj)
return NULL;
ret = PyObject_CallMethod((PyObject *)bulk_obj,
"set_direction_output", fmt, vals);
Py_DECREF(bulk_obj);
Py_DECREF(vals);
return ret;
}
PyDoc_STRVAR(gpiod_Line_release_doc,
"release() -> None\n"
"\n"
"Release this GPIO line.");
static PyObject *gpiod_Line_release(gpiod_LineObject *self,
PyObject *Py_UNUSED(ignored))
{
gpiod_LineBulkObject *bulk_obj;
PyObject *ret;
bulk_obj = gpiod_LineToLineBulk(self);
if (!bulk_obj)
return NULL;
ret = PyObject_CallMethod((PyObject *)bulk_obj, "release", "");
Py_DECREF(bulk_obj);
return ret;
}
PyDoc_STRVAR(gpiod_Line_event_wait_doc,
"event_wait([sec[ ,nsec]]) -> boolean\n"
"\n"
"Wait for a line event to occur on this GPIO line.\n"
"\n"
" sec\n"
" Number of seconds to wait before timeout.\n"
" nsec\n"
" Number of nanoseconds to wait before timeout.\n"
"\n"
"Returns True if an event occurred on this line before timeout. False\n"
"otherwise.");
static PyObject *gpiod_Line_event_wait(gpiod_LineObject *self,
PyObject *args, PyObject *kwds)
{
gpiod_LineBulkObject *bulk_obj;
PyObject *events;
bulk_obj = gpiod_LineToLineBulk(self);
if (!bulk_obj)
return NULL;
events = gpiod_CallMethodPyArgs((PyObject *)bulk_obj,
"event_wait", args, kwds);
Py_DECREF(bulk_obj);
if (!events)
return NULL;
if (events == Py_None) {
Py_DECREF(Py_None);
Py_RETURN_FALSE;
}
Py_DECREF(events);
Py_RETURN_TRUE;
}
PyDoc_STRVAR(gpiod_Line_event_read_doc,
"event_read() -> gpiod.LineEvent object\n"
"\n"
"Read a single line event from this GPIO line object.");
static gpiod_LineEventObject *gpiod_Line_event_read(gpiod_LineObject *self,
PyObject *Py_UNUSED(ignored))
{
gpiod_LineEventObject *ret;
int rv;
if (gpiod_ChipIsClosed(self->owner))
return NULL;
ret = PyObject_New(gpiod_LineEventObject, &gpiod_LineEventType);
if (!ret)
return NULL;
ret->source = NULL;
Py_BEGIN_ALLOW_THREADS;
rv = gpiod_line_event_read(self->line, &ret->event);
Py_END_ALLOW_THREADS;
if (rv) {
Py_DECREF(ret);
return (gpiod_LineEventObject *)PyErr_SetFromErrno(
PyExc_OSError);
}
Py_INCREF(self);
ret->source = self;
return ret;
}
PyDoc_STRVAR(gpiod_Line_event_read_multiple_doc,
"event_read_multiple() -> list of gpiod.LineEvent object\n"
"\n"
"Read multiple line events from this GPIO line object.");
static PyObject *gpiod_Line_event_read_multiple(gpiod_LineObject *self,
PyObject *Py_UNUSED(ignored))
{
struct gpiod_line_event evbuf[16];
gpiod_LineEventObject *event;
int rv, num_events, i;
PyObject *events;
if (gpiod_ChipIsClosed(self->owner))
return NULL;
memset(evbuf, 0, sizeof(evbuf));
Py_BEGIN_ALLOW_THREADS;
num_events = gpiod_line_event_read_multiple(self->line, evbuf,
sizeof(evbuf) / sizeof(*evbuf));
Py_END_ALLOW_THREADS;
if (num_events < 0)
return PyErr_SetFromErrno(PyExc_OSError);
events = PyList_New(num_events);
if (!events)
return NULL;
for (i = 0; i < num_events; i++) {
event = PyObject_New(gpiod_LineEventObject,
&gpiod_LineEventType);
if (!event) {
Py_DECREF(events);
return NULL;
}
memcpy(&event->event, &evbuf[i], sizeof(event->event));
Py_INCREF(self);
event->source = self;
rv = PyList_SetItem(events, i, (PyObject *)event);
if (rv < 0) {
Py_DECREF(events);
Py_DECREF(event);
return NULL;
}
}
return events;
}
PyDoc_STRVAR(gpiod_Line_event_get_fd_doc,
"event_get_fd() -> integer\n"
"\n"
"Get the event file descriptor number associated with this line.");
static PyObject *gpiod_Line_event_get_fd(gpiod_LineObject *self,
PyObject *Py_UNUSED(ignored))
{
int fd;
if (gpiod_ChipIsClosed(self->owner))
return NULL;
fd = gpiod_line_event_get_fd(self->line);
if (fd < 0) {
PyErr_SetFromErrno(PyExc_OSError);
return NULL;
}
return PyLong_FromLong(fd);
}
static PyObject *gpiod_Line_repr(gpiod_LineObject *self)
{
PyObject *chip_name, *ret;
const char *line_name;
if (gpiod_ChipIsClosed(self->owner))
return NULL;
chip_name = PyObject_CallMethod((PyObject *)self->owner, "name", "");
if (!chip_name)
return NULL;
line_name = gpiod_line_name(self->line);
ret = PyUnicode_FromFormat("'%S:%u /%s/'", chip_name,
gpiod_line_offset(self->line),
line_name ?: "unnamed");
Py_DECREF(chip_name);
return ret;
}
static PyMethodDef gpiod_Line_methods[] = {
{
.ml_name = "owner",
.ml_meth = (PyCFunction)gpiod_Line_owner,
.ml_flags = METH_NOARGS,
.ml_doc = gpiod_Line_owner_doc,
},
{
.ml_name = "offset",
.ml_meth = (PyCFunction)gpiod_Line_offset,
.ml_flags = METH_NOARGS,
.ml_doc = gpiod_Line_offset_doc,
},
{
.ml_name = "name",
.ml_meth = (PyCFunction)gpiod_Line_name,
.ml_flags = METH_NOARGS,
.ml_doc = gpiod_Line_name_doc,
},
{
.ml_name = "consumer",
.ml_meth = (PyCFunction)gpiod_Line_consumer,
.ml_flags = METH_NOARGS,
.ml_doc = gpiod_Line_consumer_doc,
},
{
.ml_name = "direction",
.ml_meth = (PyCFunction)gpiod_Line_direction,
.ml_flags = METH_NOARGS,
.ml_doc = gpiod_Line_direction_doc,
},
{
.ml_name = "is_active_low",
.ml_meth = (PyCFunction)gpiod_Line_is_active_low,
.ml_flags = METH_NOARGS,
.ml_doc = gpiod_Line_is_active_low_doc,
},
{
.ml_name = "bias",
.ml_meth = (PyCFunction)gpiod_Line_bias,
.ml_flags = METH_NOARGS,
.ml_doc = gpiod_Line_bias_doc,
},
{
.ml_name = "is_used",
.ml_meth = (PyCFunction)gpiod_Line_is_used,
.ml_flags = METH_NOARGS,
.ml_doc = gpiod_Line_is_used_doc,
},
{
.ml_name = "drive",
.ml_meth = (PyCFunction)gpiod_Line_drive,
.ml_flags = METH_NOARGS,
.ml_doc = gpiod_Line_drive_doc,
},
{
.ml_name = "request",
.ml_meth = (PyCFunction)(void (*)(void))gpiod_Line_request,
.ml_flags = METH_VARARGS | METH_KEYWORDS,
.ml_doc = gpiod_Line_request_doc,
},
{
.ml_name = "get_value",
.ml_meth = (PyCFunction)gpiod_Line_get_value,
.ml_flags = METH_NOARGS,
.ml_doc = gpiod_Line_get_value_doc,
},
{
.ml_name = "set_value",
.ml_meth = (PyCFunction)gpiod_Line_set_value,
.ml_flags = METH_VARARGS,
.ml_doc = gpiod_Line_set_value_doc,
},
{
.ml_name = "set_config",
.ml_meth = (PyCFunction)gpiod_Line_set_config,
.ml_flags = METH_VARARGS,
.ml_doc = gpiod_Line_set_config_doc,
},
{
.ml_name = "set_flags",
.ml_meth = (PyCFunction)gpiod_Line_set_flags,
.ml_flags = METH_VARARGS,
.ml_doc = gpiod_Line_set_flags_doc,
},
{
.ml_name = "set_direction_input",
.ml_meth = (PyCFunction)gpiod_Line_set_direction_input,
.ml_flags = METH_NOARGS,
.ml_doc = gpiod_Line_set_direction_input_doc,
},
{
.ml_name = "set_direction_output",
.ml_meth = (PyCFunction)gpiod_Line_set_direction_output,
.ml_flags = METH_VARARGS,
.ml_doc = gpiod_Line_set_direction_output_doc,
},
{
.ml_name = "release",
.ml_meth = (PyCFunction)gpiod_Line_release,
.ml_flags = METH_NOARGS,
.ml_doc = gpiod_Line_release_doc,
},
{
.ml_name = "event_wait",
.ml_meth = (PyCFunction)(void (*)(void))gpiod_Line_event_wait,
.ml_flags = METH_VARARGS | METH_KEYWORDS,
.ml_doc = gpiod_Line_event_wait_doc,
},
{
.ml_name = "event_read",
.ml_meth = (PyCFunction)gpiod_Line_event_read,
.ml_flags = METH_NOARGS,
.ml_doc = gpiod_Line_event_read_doc,
},
{
.ml_name = "event_read_multiple",
.ml_meth = (PyCFunction)gpiod_Line_event_read_multiple,
.ml_flags = METH_NOARGS,
.ml_doc = gpiod_Line_event_read_multiple_doc,
},
{
.ml_name = "event_get_fd",
.ml_meth = (PyCFunction)gpiod_Line_event_get_fd,
.ml_flags = METH_NOARGS,
.ml_doc = gpiod_Line_event_get_fd_doc,
},
{ }
};
PyDoc_STRVAR(gpiod_LineType_doc,
"Represents a GPIO line.\n"
"\n"
"The lifetime of this object is managed by the chip that owns it. Once\n"
"the corresponding gpiod.Chip is closed, a gpiod.Line object must not be\n"
"used.\n"
"\n"
"Line objects can only be created by the owning chip.");
static PyTypeObject gpiod_LineType = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "gpiod.Line",
.tp_basicsize = sizeof(gpiod_LineObject),
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = gpiod_LineType_doc,
.tp_new = PyType_GenericNew,
.tp_init = (initproc)gpiod_Line_init,
.tp_dealloc = (destructor)gpiod_Line_dealloc,
.tp_repr = (reprfunc)gpiod_Line_repr,
.tp_methods = gpiod_Line_methods,
};
static bool gpiod_LineBulkOwnerIsClosed(gpiod_LineBulkObject *self)
{
gpiod_LineObject *line = (gpiod_LineObject *)self->lines[0];
return gpiod_ChipIsClosed(line->owner);
}
static int gpiod_LineBulk_init(gpiod_LineBulkObject *self,
PyObject *args, PyObject *Py_UNUSED(ignored))
{
PyObject *lines, *iter, *next;
Py_ssize_t i;
int rv;
rv = PyArg_ParseTuple(args, "O", &lines);
if (!rv)
return -1;
self->num_lines = PyObject_Size(lines);
if (self->num_lines < 1) {
PyErr_SetString(PyExc_TypeError,
"Argument must be a non-empty sequence");
return -1;
}
if (self->num_lines > LINE_REQUEST_MAX_LINES) {
PyErr_SetString(PyExc_TypeError,
"Too many objects in the sequence");
return -1;
}
self->lines = PyMem_Calloc(self->num_lines, sizeof(PyObject *));
if (!self->lines) {
PyErr_SetString(PyExc_MemoryError, "Out of memory");
return -1;
}
iter = PyObject_GetIter(lines);
if (!iter) {
PyMem_Free(self->lines);
return -1;
}
for (i = 0;;) {
next = PyIter_Next(iter);
if (!next) {
Py_DECREF(iter);
break;
}
if (next->ob_type != &gpiod_LineType) {
PyErr_SetString(PyExc_TypeError,
"Argument must be a sequence of GPIO lines");
Py_DECREF(next);
Py_DECREF(iter);
goto errout;
}
self->lines[i++] = next;
}
self->iter_idx = -1;
return 0;
errout:
if (i > 0) {
for (--i; i >= 0; i--)
Py_DECREF(self->lines[i]);
}
PyMem_Free(self->lines);
self->lines = NULL;
return -1;
}
static void gpiod_LineBulk_dealloc(gpiod_LineBulkObject *self)
{
Py_ssize_t i;
if (!self->lines)
return;
for (i = 0; i < self->num_lines; i++)
Py_DECREF(self->lines[i]);
PyMem_Free(self->lines);
PyObject_Del(self);
}
static PyObject *gpiod_LineBulk_iternext(gpiod_LineBulkObject *self)
{
if (self->iter_idx < 0) {
self->iter_idx = 0; /* First element */
} else if (self->iter_idx >= self->num_lines) {
self->iter_idx = -1;
return NULL; /* Last element */
}
Py_INCREF(self->lines[self->iter_idx]);
return self->lines[self->iter_idx++];
}
PyDoc_STRVAR(gpiod_LineBulk_to_list_doc,
"to_list() -> list of gpiod.Line objects\n"
"\n"
"Convert this LineBulk to a list");
static PyObject *gpiod_LineBulk_to_list(gpiod_LineBulkObject *self,
PyObject *Py_UNUSED(ignored))
{
PyObject *list;
Py_ssize_t i;
int rv;
list = PyList_New(self->num_lines);
if (!list)
return NULL;
for (i = 0; i < self->num_lines; i++) {
Py_INCREF(self->lines[i]);
rv = PyList_SetItem(list, i, self->lines[i]);
if (rv < 0) {
Py_DECREF(list);
return NULL;
}
}
return list;
}
static struct gpiod_line_bulk *
gpiod_LineBulkObjToCLineBulk(gpiod_LineBulkObject *bulk_obj)
{
struct gpiod_line_bulk *bulk;
gpiod_LineObject *line_obj;
Py_ssize_t i;
bulk = gpiod_line_bulk_new(bulk_obj->num_lines);
if (!bulk) {
PyErr_SetFromErrno(PyExc_OSError);
return NULL;
}
for (i = 0; i < bulk_obj->num_lines; i++) {
line_obj = (gpiod_LineObject *)bulk_obj->lines[i];
gpiod_line_bulk_add_line(bulk, line_obj->line);
}
return bulk;
}
static void gpiod_MakeRequestConfig(struct gpiod_line_request_config *conf,
const char *consumer,
int request_type, int flags)
{
memset(conf, 0, sizeof(*conf));
conf->consumer = consumer;
switch (request_type) {
case gpiod_LINE_REQ_DIR_IN:
conf->request_type = GPIOD_LINE_REQUEST_DIRECTION_INPUT;
break;
case gpiod_LINE_REQ_DIR_OUT:
conf->request_type = GPIOD_LINE_REQUEST_DIRECTION_OUTPUT;
break;
case gpiod_LINE_REQ_EV_FALLING_EDGE:
conf->request_type = GPIOD_LINE_REQUEST_EVENT_FALLING_EDGE;
break;
case gpiod_LINE_REQ_EV_RISING_EDGE:
conf->request_type = GPIOD_LINE_REQUEST_EVENT_RISING_EDGE;
break;
case gpiod_LINE_REQ_EV_BOTH_EDGES:
conf->request_type = GPIOD_LINE_REQUEST_EVENT_BOTH_EDGES;
break;
case gpiod_LINE_REQ_DIR_AS_IS:
default:
conf->request_type = GPIOD_LINE_REQUEST_DIRECTION_AS_IS;
break;
}
if (flags & gpiod_LINE_REQ_FLAG_OPEN_DRAIN)
conf->flags |= GPIOD_LINE_REQUEST_FLAG_OPEN_DRAIN;
if (flags & gpiod_LINE_REQ_FLAG_OPEN_SOURCE)
conf->flags |= GPIOD_LINE_REQUEST_FLAG_OPEN_SOURCE;
if (flags & gpiod_LINE_REQ_FLAG_ACTIVE_LOW)
conf->flags |= GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW;
if (flags & gpiod_LINE_REQ_FLAG_BIAS_DISABLED)
conf->flags |= GPIOD_LINE_REQUEST_FLAG_BIAS_DISABLED;
if (flags & gpiod_LINE_REQ_FLAG_BIAS_PULL_DOWN)
conf->flags |= GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_DOWN;
if (flags & gpiod_LINE_REQ_FLAG_BIAS_PULL_UP)
conf->flags |= GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP;
}
PyDoc_STRVAR(gpiod_LineBulk_request_doc,
"request(consumer[, type[, flags[, default_vals]]]) -> None\n"
"\n"
"Request all lines held by this LineBulk object.\n"
"\n"
" consumer\n"
" Name of the consumer.\n"
" type\n"
" Type of the request.\n"
" flags\n"
" Other configuration flags.\n"
" default_vals\n"
" List of default values.\n");
static PyObject *gpiod_LineBulk_request(gpiod_LineBulkObject *self,
PyObject *args, PyObject *kwds)
{
static char *kwlist[] = { "consumer",
"type",
"flags",
"default_vals",
NULL };
int rv, type = gpiod_LINE_REQ_DIR_AS_IS, flags = 0,
vals[LINE_REQUEST_MAX_LINES], val;
PyObject *def_vals_obj = NULL, *iter, *next;
struct gpiod_line_request_config conf;
const int *default_vals = NULL;
struct gpiod_line_bulk *bulk;
Py_ssize_t num_def_vals;
char *consumer = NULL;
Py_ssize_t i;
if (gpiod_LineBulkOwnerIsClosed(self))
return NULL;
rv = PyArg_ParseTupleAndKeywords(args, kwds, "s|iiO", kwlist,
&consumer, &type,
&flags, &def_vals_obj);
if (!rv)
return NULL;
bulk = gpiod_LineBulkObjToCLineBulk(self);
if (!bulk)
return NULL;
gpiod_MakeRequestConfig(&conf, consumer, type, flags);
if (def_vals_obj) {
memset(vals, 0, sizeof(vals));
num_def_vals = PyObject_Size(def_vals_obj);
if (num_def_vals != self->num_lines) {
PyErr_SetString(PyExc_TypeError,
"Number of default values is not the same as the number of lines");
return NULL;
}
iter = PyObject_GetIter(def_vals_obj);
if (!iter)
return NULL;
for (i = 0;; i++) {
next = PyIter_Next(iter);
if (!next) {
Py_DECREF(iter);
break;
}
val = PyLong_AsUnsignedLong(next);
Py_DECREF(next);
if (PyErr_Occurred()) {
Py_DECREF(iter);
return NULL;
}
vals[i] = !!val;
}
default_vals = vals;
}
bulk = gpiod_LineBulkObjToCLineBulk(self);
if (!bulk)
return NULL;
Py_BEGIN_ALLOW_THREADS;
rv = gpiod_line_request_bulk(bulk, &conf, default_vals);
gpiod_line_bulk_free(bulk);
Py_END_ALLOW_THREADS;
if (rv)
return PyErr_SetFromErrno(PyExc_OSError);
Py_RETURN_NONE;
}
PyDoc_STRVAR(gpiod_LineBulk_get_values_doc,
"get_values() -> list of integers\n"
"\n"
"Read the values of all the lines held by this LineBulk object. The index\n"
"of each value in the returned list corresponds to the index of the line\n"
"in this gpiod.LineBulk object.");
static PyObject *gpiod_LineBulk_get_values(gpiod_LineBulkObject *self,
PyObject *Py_UNUSED(ignored))
{
int rv, vals[LINE_REQUEST_MAX_LINES];
struct gpiod_line_bulk *bulk;
PyObject *val_list, *val;
Py_ssize_t i;
if (gpiod_LineBulkOwnerIsClosed(self))
return NULL;
bulk = gpiod_LineBulkObjToCLineBulk(self);
if (!bulk)
return NULL;
memset(vals, 0, sizeof(vals));
Py_BEGIN_ALLOW_THREADS;
rv = gpiod_line_get_value_bulk(bulk, vals);
gpiod_line_bulk_free(bulk);
Py_END_ALLOW_THREADS;
if (rv)
return PyErr_SetFromErrno(PyExc_OSError);
val_list = PyList_New(self->num_lines);
if (!val_list)
return NULL;
for (i = 0; i < self->num_lines; i++) {
val = Py_BuildValue("i", vals[i]);
if (!val) {
Py_DECREF(val_list);
return NULL;
}
rv = PyList_SetItem(val_list, i, val);
if (rv < 0) {
Py_DECREF(val);
Py_DECREF(val_list);
return NULL;
}
}
return val_list;
}
static int gpiod_TupleToIntArray(PyObject *src, int *dst, Py_ssize_t nv)
{
Py_ssize_t num_vals, i;
PyObject *iter, *next;
int val;
num_vals = PyObject_Size(src);
if (num_vals != nv) {
PyErr_SetString(PyExc_TypeError,
"Number of values must correspond to the number of lines");
return -1;
}
iter = PyObject_GetIter(src);
if (!iter)
return -1;
for (i = 0;; i++) {
next = PyIter_Next(iter);
if (!next) {
Py_DECREF(iter);
break;
}
val = PyLong_AsLong(next);
Py_DECREF(next);
if (PyErr_Occurred()) {
Py_DECREF(iter);
return -1;
}
dst[i] = (int)val;
}
return 0;
}
PyDoc_STRVAR(gpiod_LineBulk_set_values_doc,
"set_values(values) -> None\n"
"\n"
"Set the values of all the lines held by this LineBulk object.\n"
"\n"
" values\n"
" List of values (integers) to set.\n"
"\n"
"The number of values in the list passed as argument must be the same as\n"
"the number of lines held by this gpiod.LineBulk object. The index of each\n"
"value corresponds to the index of each line in the object.\n");
static PyObject *gpiod_LineBulk_set_values(gpiod_LineBulkObject *self,
PyObject *args)
{
int rv, vals[LINE_REQUEST_MAX_LINES];
struct gpiod_line_bulk *bulk;
PyObject *val_list;
if (gpiod_LineBulkOwnerIsClosed(self))
return NULL;
memset(vals, 0, sizeof(vals));
rv = PyArg_ParseTuple(args, "O", &val_list);
if (!rv)
return NULL;
rv = gpiod_TupleToIntArray(val_list, vals, self->num_lines);
if (rv)
return NULL;
bulk = gpiod_LineBulkObjToCLineBulk(self);
if (!bulk)
return NULL;
Py_BEGIN_ALLOW_THREADS;
rv = gpiod_line_set_value_bulk(bulk, vals);
gpiod_line_bulk_free(bulk);
Py_END_ALLOW_THREADS;
if (rv)
return PyErr_SetFromErrno(PyExc_OSError);
Py_RETURN_NONE;
}
PyDoc_STRVAR(gpiod_LineBulk_set_config_doc,
"set_config(direction,flags,values) -> None\n"
"\n"
"Set the configuration of all the lines held by this LineBulk object.\n"
"\n"
" direction\n"
" New direction (integer)\n"
" flags\n"
" New flags (integer)\n"
" values\n"
" List of values (integers) to set when direction is output.\n"
"\n"
"The number of values in the list passed as argument must be the same as\n"
"the number of lines held by this gpiod.LineBulk object. The index of each\n"
"value corresponds to the index of each line in the object.\n");
static PyObject *gpiod_LineBulk_set_config(gpiod_LineBulkObject *self,
PyObject *args)
{
int rv, vals[LINE_REQUEST_MAX_LINES];
struct gpiod_line_bulk *bulk;
PyObject *val_list;
const int *valp;
int dirn, flags;
if (gpiod_LineBulkOwnerIsClosed(self))
return NULL;
val_list = NULL;
rv = PyArg_ParseTuple(args, "ii|(O)", &dirn, &flags, &val_list);
if (!rv)
return NULL;
if (val_list == NULL) {
valp = NULL;
} else {
memset(vals, 0, sizeof(vals));
rv = gpiod_TupleToIntArray(val_list, vals, self->num_lines);
if (rv)
return NULL;
valp = vals;
}
bulk = gpiod_LineBulkObjToCLineBulk(self);
if (!bulk)
return NULL;
Py_BEGIN_ALLOW_THREADS;
rv = gpiod_line_set_config_bulk(bulk, dirn, flags, valp);
Py_END_ALLOW_THREADS;
if (rv)
return PyErr_SetFromErrno(PyExc_OSError);
Py_RETURN_NONE;
}
PyDoc_STRVAR(gpiod_LineBulk_set_flags_doc,
"set_flags(flags) -> None\n"
"\n"
"Set the flags of all the lines held by this LineBulk object.\n"
"\n"
" flags\n"
" New flags (integer)");
static PyObject *gpiod_LineBulk_set_flags(gpiod_LineBulkObject *self,
PyObject *args)
{
struct gpiod_line_bulk *bulk;
int rv, flags;
if (gpiod_LineBulkOwnerIsClosed(self))
return NULL;
rv = PyArg_ParseTuple(args, "i", &flags);
if (!rv)
return NULL;
bulk = gpiod_LineBulkObjToCLineBulk(self);
if (!bulk)
return NULL;
Py_BEGIN_ALLOW_THREADS;
rv = gpiod_line_set_flags_bulk(bulk, flags);
gpiod_line_bulk_free(bulk);
Py_END_ALLOW_THREADS;
if (rv)
return PyErr_SetFromErrno(PyExc_OSError);
Py_RETURN_NONE;
}
PyDoc_STRVAR(gpiod_LineBulk_set_direction_input_doc,
"set_direction_input() -> None\n"
"\n"
"Set the direction of all the lines held by this LineBulk object to input.\n");
static PyObject *gpiod_LineBulk_set_direction_input(gpiod_LineBulkObject *self,
PyObject *Py_UNUSED(ignored))
{
struct gpiod_line_bulk *bulk;
int rv;
if (gpiod_LineBulkOwnerIsClosed(self))
return NULL;
bulk = gpiod_LineBulkObjToCLineBulk(self);
if (!bulk)
return NULL;
Py_BEGIN_ALLOW_THREADS;
rv = gpiod_line_set_direction_input_bulk(bulk);
gpiod_line_bulk_free(bulk);
Py_END_ALLOW_THREADS;
if (rv)
return PyErr_SetFromErrno(PyExc_OSError);
Py_RETURN_NONE;
}
PyDoc_STRVAR(gpiod_LineBulk_set_direction_output_doc,
"set_direction_output(value) -> None\n"
"\n"
"Set the direction of all the lines held by this LineBulk object to output.\n"
"\n"
" values\n"
" List of values (integers) to set when direction is output.\n"
"\n"
"The number of values in the list passed as argument must be the same as\n"
"the number of lines held by this gpiod.LineBulk object. The index of each\n"
"value corresponds to the index of each line in the object.\n");
static PyObject *gpiod_LineBulk_set_direction_output(
gpiod_LineBulkObject *self,
PyObject *args)
{
int rv, vals[LINE_REQUEST_MAX_LINES];
struct gpiod_line_bulk *bulk;
PyObject *val_list;
const int *valp;
if (gpiod_LineBulkOwnerIsClosed(self))
return NULL;
val_list = NULL;
rv = PyArg_ParseTuple(args, "|O", &val_list);
if (!rv)
return NULL;
if (val_list == NULL)
valp = NULL;
else {
memset(vals, 0, sizeof(vals));
rv = gpiod_TupleToIntArray(val_list, vals, self->num_lines);
if (rv)
return NULL;
valp = vals;
}
bulk = gpiod_LineBulkObjToCLineBulk(self);
if (!bulk)
return NULL;
Py_BEGIN_ALLOW_THREADS;
rv = gpiod_line_set_direction_output_bulk(bulk, valp);
gpiod_line_bulk_free(bulk);
Py_END_ALLOW_THREADS;
if (rv)
return PyErr_SetFromErrno(PyExc_OSError);
Py_RETURN_NONE;
}
PyDoc_STRVAR(gpiod_LineBulk_release_doc,
"release() -> None\n"
"\n"
"Release all lines held by this LineBulk object.");
static PyObject *gpiod_LineBulk_release(gpiod_LineBulkObject *self,
PyObject *Py_UNUSED(ignored))
{
struct gpiod_line_bulk *bulk;
if (gpiod_LineBulkOwnerIsClosed(self))
return NULL;
bulk = gpiod_LineBulkObjToCLineBulk(self);
if (!bulk)
return NULL;
gpiod_line_release_bulk(bulk);
gpiod_line_bulk_free(bulk);
Py_RETURN_NONE;
}
PyDoc_STRVAR(gpiod_LineBulk_event_wait_doc,
"event_wait([sec[ ,nsec]]) -> gpiod.LineBulk object or None\n"
"\n"
"Poll the lines held by this LineBulk Object for line events.\n"
"\n"
" sec\n"
" Number of seconds to wait before timeout.\n"
" nsec\n"
" Number of nanoseconds to wait before timeout.\n"
"\n"
"Returns a gpiod.LineBulk object containing references to lines on which\n"
"events occurred or None if we reached the timeout without any event\n"
"occurring.");
static PyObject *gpiod_LineBulk_event_wait(gpiod_LineBulkObject *self,
PyObject *args, PyObject *kwds)
{
static char *kwlist[] = { "sec", "nsec", NULL };
struct gpiod_line_bulk *bulk, *ev_bulk;
gpiod_LineObject *line_obj;
gpiod_ChipObject *owner;
long sec = 0, nsec = 0;
struct timespec ts;
PyObject *ret;
unsigned int idx;
int rv;
if (gpiod_LineBulkOwnerIsClosed(self))
return NULL;
rv = PyArg_ParseTupleAndKeywords(args, kwds,
"|ll", kwlist, &sec, &nsec);
if (!rv)
return NULL;
ts.tv_sec = sec;
ts.tv_nsec = nsec;
bulk = gpiod_LineBulkObjToCLineBulk(self);
if (!bulk)
return NULL;
ev_bulk = gpiod_line_bulk_new(self->num_lines);
if (!ev_bulk) {
gpiod_line_bulk_free(bulk);
return NULL;
}
Py_BEGIN_ALLOW_THREADS;
rv = gpiod_line_event_wait_bulk(bulk, &ts, ev_bulk);
gpiod_line_bulk_free(bulk);
Py_END_ALLOW_THREADS;
if (rv < 0) {
gpiod_line_bulk_free(ev_bulk);
return PyErr_SetFromErrno(PyExc_OSError);
} else if (rv == 0) {
gpiod_line_bulk_free(ev_bulk);
Py_RETURN_NONE;
}
ret = PyList_New(gpiod_line_bulk_num_lines(ev_bulk));
if (!ret) {
gpiod_line_bulk_free(ev_bulk);
return NULL;
}
owner = ((gpiod_LineObject *)(self->lines[0]))->owner;
for (idx = 0; idx < gpiod_line_bulk_num_lines(ev_bulk); idx++) {
line_obj = gpiod_MakeLineObject(owner, gpiod_line_bulk_get_line(ev_bulk, idx));
if (!line_obj) {
gpiod_line_bulk_free(ev_bulk);
Py_DECREF(ret);
return NULL;
}
rv = PyList_SetItem(ret, idx, (PyObject *)line_obj);
if (rv < 0) {
gpiod_line_bulk_free(ev_bulk);
Py_DECREF(ret);
return NULL;
}
}
gpiod_line_bulk_free(ev_bulk);
return ret;
}
static PyObject *gpiod_LineBulk_repr(gpiod_LineBulkObject *self)
{
PyObject *list, *list_repr, *chip_name, *ret;
gpiod_LineObject *line;
if (gpiod_LineBulkOwnerIsClosed(self))
return NULL;
list = gpiod_LineBulk_to_list(self, NULL);
if (!list)
return NULL;
list_repr = PyObject_Repr(list);
Py_DECREF(list);
if (!list_repr)
return NULL;
line = (gpiod_LineObject *)self->lines[0];
chip_name = PyObject_CallMethod((PyObject *)line->owner, "name", "");
if (!chip_name) {
Py_DECREF(list_repr);
return NULL;
}
ret = PyUnicode_FromFormat("%U%U", chip_name, list_repr);
Py_DECREF(chip_name);
Py_DECREF(list_repr);
return ret;
}
static PyMethodDef gpiod_LineBulk_methods[] = {
{
.ml_name = "to_list",
.ml_meth = (PyCFunction)gpiod_LineBulk_to_list,
.ml_doc = gpiod_LineBulk_to_list_doc,
.ml_flags = METH_NOARGS,
},
{
.ml_name = "request",
.ml_meth = (PyCFunction)(void (*)(void))gpiod_LineBulk_request,
.ml_doc = gpiod_LineBulk_request_doc,
.ml_flags = METH_VARARGS | METH_KEYWORDS,
},
{
.ml_name = "get_values",
.ml_meth = (PyCFunction)gpiod_LineBulk_get_values,
.ml_doc = gpiod_LineBulk_get_values_doc,
.ml_flags = METH_NOARGS,
},
{
.ml_name = "set_values",
.ml_meth = (PyCFunction)gpiod_LineBulk_set_values,
.ml_doc = gpiod_LineBulk_set_values_doc,
.ml_flags = METH_VARARGS,
},
{
.ml_name = "set_config",
.ml_meth = (PyCFunction)gpiod_LineBulk_set_config,
.ml_flags = METH_VARARGS,
.ml_doc = gpiod_LineBulk_set_config_doc,
},
{
.ml_name = "set_flags",
.ml_meth = (PyCFunction)gpiod_LineBulk_set_flags,
.ml_flags = METH_VARARGS,
.ml_doc = gpiod_LineBulk_set_flags_doc,
},
{
.ml_name = "set_direction_input",
.ml_meth = (PyCFunction)gpiod_LineBulk_set_direction_input,
.ml_flags = METH_NOARGS,
.ml_doc = gpiod_LineBulk_set_direction_input_doc,
},
{
.ml_name = "set_direction_output",
.ml_meth = (PyCFunction)gpiod_LineBulk_set_direction_output,
.ml_flags = METH_VARARGS,
.ml_doc = gpiod_LineBulk_set_direction_output_doc,
},
{
.ml_name = "release",
.ml_meth = (PyCFunction)gpiod_LineBulk_release,
.ml_doc = gpiod_LineBulk_release_doc,
.ml_flags = METH_NOARGS,
},
{
.ml_name = "event_wait",
.ml_meth = (PyCFunction)(void (*)(void))gpiod_LineBulk_event_wait,
.ml_doc = gpiod_LineBulk_event_wait_doc,
.ml_flags = METH_VARARGS | METH_KEYWORDS,
},
{ }
};
PyDoc_STRVAR(gpiod_LineBulkType_doc,
"Represents a set of GPIO lines.\n"
"\n"
"Objects of this type are immutable. The constructor takes as argument\n"
"a sequence of gpiod.Line objects. It doesn't accept objects of any other\n"
"type.");
static PyTypeObject gpiod_LineBulkType = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "gpiod.LineBulk",
.tp_basicsize = sizeof(gpiod_LineBulkObject),
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = gpiod_LineBulkType_doc,
.tp_new = PyType_GenericNew,
.tp_init = (initproc)gpiod_LineBulk_init,
.tp_dealloc = (destructor)gpiod_LineBulk_dealloc,
.tp_iter = PyObject_SelfIter,
.tp_iternext = (iternextfunc)gpiod_LineBulk_iternext,
.tp_repr = (reprfunc)gpiod_LineBulk_repr,
.tp_methods = gpiod_LineBulk_methods,
};
static gpiod_LineBulkObject *gpiod_LineToLineBulk(gpiod_LineObject *line)
{
gpiod_LineBulkObject *ret;
PyObject *args;
args = Py_BuildValue("((O))", line);
if (!args)
return NULL;
ret = (gpiod_LineBulkObject *)PyObject_CallObject(
(PyObject *)&gpiod_LineBulkType,
args);
Py_DECREF(args);
return ret;
}
static int gpiod_Chip_init(gpiod_ChipObject *self,
PyObject *args, PyObject *Py_UNUSED(ignored))
{
char *path;
int rv;
rv = PyArg_ParseTuple(args, "s", &path);
if (!rv)
return -1;
Py_BEGIN_ALLOW_THREADS;
self->chip = gpiod_chip_open(path);
Py_END_ALLOW_THREADS;
if (!self->chip) {
PyErr_SetFromErrno(PyExc_OSError);
return -1;
}
return 0;
}
static void gpiod_Chip_dealloc(gpiod_ChipObject *self)
{
if (self->chip)
gpiod_chip_unref(self->chip);
PyObject_Del(self);
}
static PyObject *gpiod_Chip_repr(gpiod_ChipObject *self)
{
if (gpiod_ChipIsClosed(self))
return NULL;
return PyUnicode_FromFormat("'%s /%s/ %u lines'",
gpiod_chip_name(self->chip),
gpiod_chip_label(self->chip),
gpiod_chip_num_lines(self->chip));
}
PyDoc_STRVAR(gpiod_Chip_close_doc,
"close() -> None\n"
"\n"
"Close the associated gpiochip descriptor. The chip object must no longer\n"
"be used after this method is called.\n");
static PyObject *gpiod_Chip_close(gpiod_ChipObject *self,
PyObject *Py_UNUSED(ignored))
{
if (gpiod_ChipIsClosed(self))
return NULL;
gpiod_chip_unref(self->chip);
self->chip = NULL;
Py_RETURN_NONE;
}
PyDoc_STRVAR(gpiod_Chip_enter_doc,
"Controlled execution enter callback.");
static PyObject *gpiod_Chip_enter(gpiod_ChipObject *chip,
PyObject *Py_UNUSED(ignored))
{
Py_INCREF(chip);
return (PyObject *)chip;
}
PyDoc_STRVAR(gpiod_Chip_exit_doc,
"Controlled execution exit callback.");
static PyObject *gpiod_Chip_exit(gpiod_ChipObject *chip,
PyObject *Py_UNUSED(ignored))
{
return PyObject_CallMethod((PyObject *)chip, "close", "");
}
PyDoc_STRVAR(gpiod_Chip_name_doc,
"name() -> string\n"
"\n"
"Get the name of the GPIO chip");
static PyObject *gpiod_Chip_name(gpiod_ChipObject *self,
PyObject *Py_UNUSED(ignored))
{
if (gpiod_ChipIsClosed(self))
return NULL;
return PyUnicode_FromFormat("%s", gpiod_chip_name(self->chip));
}
PyDoc_STRVAR(gpiod_Chip_label_doc,
"label() -> string\n"
"\n"
"Get the label of the GPIO chip");
static PyObject *gpiod_Chip_label(gpiod_ChipObject *self,
PyObject *Py_UNUSED(ignored))
{
if (gpiod_ChipIsClosed(self))
return NULL;
return PyUnicode_FromFormat("%s", gpiod_chip_label(self->chip));
}
PyDoc_STRVAR(gpiod_Chip_num_lines_doc,
"num_lines() -> integer\n"
"\n"
"Get the number of lines exposed by this GPIO chip.");
static PyObject *gpiod_Chip_num_lines(gpiod_ChipObject *self,
PyObject *Py_UNUSED(ignored))
{
if (gpiod_ChipIsClosed(self))
return NULL;
return Py_BuildValue("I", gpiod_chip_num_lines(self->chip));
}
static gpiod_LineObject *
gpiod_MakeLineObject(gpiod_ChipObject *owner, struct gpiod_line *line)
{
gpiod_LineObject *obj;
obj = PyObject_New(gpiod_LineObject, &gpiod_LineType);
if (!obj)
return NULL;
obj->line = line;
Py_INCREF(owner);
obj->owner = owner;
return obj;
}
PyDoc_STRVAR(gpiod_Chip_get_line_doc,
"get_line(offset) -> gpiod.Line object\n"
"\n"
"Get the GPIO line at given offset.\n"
"\n"
" offset\n"
" Line offset (integer)");
static gpiod_LineObject *
gpiod_Chip_get_line(gpiod_ChipObject *self, PyObject *args)
{
struct gpiod_line *line;
unsigned int offset;
int rv;
if (gpiod_ChipIsClosed(self))
return NULL;
rv = PyArg_ParseTuple(args, "I", &offset);
if (!rv)
return NULL;
Py_BEGIN_ALLOW_THREADS;
line = gpiod_chip_get_line(self->chip, offset);
Py_END_ALLOW_THREADS;
if (!line)
return (gpiod_LineObject *)PyErr_SetFromErrno(PyExc_OSError);
return gpiod_MakeLineObject(self, line);
}
static gpiod_LineBulkObject *gpiod_ListToLineBulk(PyObject *lines)
{
gpiod_LineBulkObject *bulk;
PyObject *arg;
arg = PyTuple_Pack(1, lines);
if (!arg)
return NULL;
bulk = (gpiod_LineBulkObject *)PyObject_CallObject(
(PyObject *)&gpiod_LineBulkType,
arg);
Py_DECREF(arg);
return bulk;
}
static gpiod_LineBulkObject *
gpiod_LineBulkObjectFromBulk(gpiod_ChipObject *chip, struct gpiod_line_bulk *bulk)
{
gpiod_LineBulkObject *bulk_obj;
gpiod_LineObject *line_obj;
struct gpiod_line *line;
unsigned int idx;
PyObject *list;
int rv;
list = PyList_New(gpiod_line_bulk_num_lines(bulk));
if (!list)
return NULL;
for (idx = 0; idx < gpiod_line_bulk_num_lines(bulk); idx++) {
line = gpiod_line_bulk_get_line(bulk, idx);
line_obj = gpiod_MakeLineObject(chip, line);
if (!line_obj) {
Py_DECREF(list);
return NULL;
}
rv = PyList_SetItem(list, idx, (PyObject *)line_obj);
if (rv < 0) {
Py_DECREF(line_obj);
Py_DECREF(list);
return NULL;
}
}
bulk_obj = gpiod_ListToLineBulk(list);
Py_DECREF(list);
if (!bulk_obj)
return NULL;
return bulk_obj;
}
PyDoc_STRVAR(gpiod_Chip_find_line_doc,
"find_line(name) -> integer or None\n"
"\n"
"Find the offset of the line with given name among lines exposed by this\n"
"GPIO chip.\n"
"\n"
" name\n"
" Line name (string)\n"
"\n"
"Returns the offset of the line with given name or None if it is not\n"
"associated with this chip.");
static PyObject *gpiod_Chip_find_line(gpiod_ChipObject *self, PyObject *args)
{
const char *name;
int rv, offset;
if (gpiod_ChipIsClosed(self))
return NULL;
rv = PyArg_ParseTuple(args, "s", &name);
if (!rv)
return NULL;
Py_BEGIN_ALLOW_THREADS;
offset = gpiod_chip_find_line(self->chip, name);
Py_END_ALLOW_THREADS;
if (offset < 0) {
if (errno == ENOENT)
Py_RETURN_NONE;
return PyErr_SetFromErrno(PyExc_OSError);
}
return Py_BuildValue("i", offset);
}
PyDoc_STRVAR(gpiod_Chip_get_lines_doc,
"get_lines(offsets) -> gpiod.LineBulk object\n"
"\n"
"Get a set of GPIO lines by their offsets.\n"
"\n"
" offsets\n"
" List of lines offsets.");
static gpiod_LineBulkObject *
gpiod_Chip_get_lines(gpiod_ChipObject *self, PyObject *args)
{
PyObject *offsets, *iter, *next, *lines, *arg;
gpiod_LineBulkObject *bulk;
Py_ssize_t num_offsets, i;
gpiod_LineObject *line;
int rv;
rv = PyArg_ParseTuple(args, "O", &offsets);
if (!rv)
return NULL;
num_offsets = PyObject_Size(offsets);
if (num_offsets < 1) {
PyErr_SetString(PyExc_TypeError,
"Argument must be a non-empty sequence of offsets");
return NULL;
}
lines = PyList_New(num_offsets);
if (!lines)
return NULL;
iter = PyObject_GetIter(offsets);
if (!iter) {
Py_DECREF(lines);
return NULL;
}
for (i = 0;;) {
next = PyIter_Next(iter);
if (!next) {
Py_DECREF(iter);
break;
}
arg = PyTuple_Pack(1, next);
Py_DECREF(next);
if (!arg) {
Py_DECREF(iter);
Py_DECREF(lines);
return NULL;
}
line = gpiod_Chip_get_line(self, arg);
Py_DECREF(arg);
if (!line) {
Py_DECREF(iter);
Py_DECREF(lines);
return NULL;
}
rv = PyList_SetItem(lines, i++, (PyObject *)line);
if (rv < 0) {
Py_DECREF(line);
Py_DECREF(iter);
Py_DECREF(lines);
return NULL;
}
}
bulk = gpiod_ListToLineBulk(lines);
Py_DECREF(lines);
if (!bulk)
return NULL;
return bulk;
}
PyDoc_STRVAR(gpiod_Chip_get_all_lines_doc,
"get_all_lines() -> gpiod.LineBulk object\n"
"\n"
"Get all lines exposed by this Chip.");
static gpiod_LineBulkObject *
gpiod_Chip_get_all_lines(gpiod_ChipObject *self, PyObject *Py_UNUSED(ignored))
{
gpiod_LineBulkObject *bulk_obj;
struct gpiod_line_bulk *bulk;
if (gpiod_ChipIsClosed(self))
return NULL;
bulk = gpiod_chip_get_all_lines(self->chip);
if (!bulk)
return (gpiod_LineBulkObject *)PyErr_SetFromErrno(
PyExc_OSError);
bulk_obj = gpiod_LineBulkObjectFromBulk(self, bulk);
gpiod_line_bulk_free(bulk);
return bulk_obj;
}
static PyMethodDef gpiod_Chip_methods[] = {
{
.ml_name = "close",
.ml_meth = (PyCFunction)gpiod_Chip_close,
.ml_flags = METH_NOARGS,
.ml_doc = gpiod_Chip_close_doc,
},
{
.ml_name = "__enter__",
.ml_meth = (PyCFunction)gpiod_Chip_enter,
.ml_flags = METH_NOARGS,
.ml_doc = gpiod_Chip_enter_doc,
},
{
.ml_name = "__exit__",
.ml_meth = (PyCFunction)gpiod_Chip_exit,
.ml_flags = METH_VARARGS,
.ml_doc = gpiod_Chip_exit_doc,
},
{
.ml_name = "name",
.ml_meth = (PyCFunction)gpiod_Chip_name,
.ml_flags = METH_NOARGS,
.ml_doc = gpiod_Chip_name_doc,
},
{
.ml_name = "label",
.ml_meth = (PyCFunction)gpiod_Chip_label,
.ml_flags = METH_NOARGS,
.ml_doc = gpiod_Chip_label_doc,
},
{
.ml_name = "num_lines",
.ml_meth = (PyCFunction)gpiod_Chip_num_lines,
.ml_flags = METH_NOARGS,
.ml_doc = gpiod_Chip_num_lines_doc,
},
{
.ml_name = "get_line",
.ml_meth = (PyCFunction)gpiod_Chip_get_line,
.ml_flags = METH_VARARGS,
.ml_doc = gpiod_Chip_get_line_doc,
},
{
.ml_name = "find_line",
.ml_meth = (PyCFunction)gpiod_Chip_find_line,
.ml_flags = METH_VARARGS,
.ml_doc = gpiod_Chip_find_line_doc,
},
{
.ml_name = "get_lines",
.ml_meth = (PyCFunction)gpiod_Chip_get_lines,
.ml_flags = METH_VARARGS,
.ml_doc = gpiod_Chip_get_lines_doc,
},
{
.ml_name = "get_all_lines",
.ml_meth = (PyCFunction)gpiod_Chip_get_all_lines,
.ml_flags = METH_NOARGS,
.ml_doc = gpiod_Chip_get_all_lines_doc,
},
{ }
};
PyDoc_STRVAR(gpiod_ChipType_doc,
"Represents a GPIO chip.\n"
"\n"
"Chip object manages all resources associated with the GPIO chip\n"
"it represents.\n"
"\n"
"The gpiochip device file is opened during the object's construction.\n"
"The Chip object's constructor takes a description string as argument the\n"
"meaning of which depends on the second, optional parameter which defines\n"
"the way the description string should be interpreted. The available\n"
"options are: OPEN_BY_NAME, OPEN_BY_NUMBER, OPEN_BY_PATH and OPEN_LOOKUP.\n"
"The last option means that libgpiod should open the chip based on the best\n"
"guess what the path is. This is also the default if the second argument is\n"
"missing.\n"
"\n"
"Callers must close the chip by calling the close() method when it's no\n"
"longer used.\n"
"\n"
"Example:\n"
"\n"
" chip = gpiod.Chip('gpiochip0', gpiod.Chip.OPEN_BY_NAME)\n"
" do_something(chip)\n"
" chip.close()\n"
"\n"
"The gpiod.Chip class also supports controlled execution ('with' statement).\n"
"\n"
"Example:\n"
"\n"
" with gpiod.Chip('0', gpiod.Chip.OPEN_BY_NUMBER) as chip:\n"
" do_something(chip)");
static PyTypeObject gpiod_ChipType = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "gpiod.Chip",
.tp_basicsize = sizeof(gpiod_ChipObject),
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = gpiod_ChipType_doc,
.tp_new = PyType_GenericNew,
.tp_init = (initproc)gpiod_Chip_init,
.tp_dealloc = (destructor)gpiod_Chip_dealloc,
.tp_repr = (reprfunc)gpiod_Chip_repr,
.tp_methods = gpiod_Chip_methods,
};
static int gpiod_LineIter_init(gpiod_LineIterObject *self,
PyObject *args, PyObject *Py_UNUSED(ignored))
{
gpiod_ChipObject *chip_obj;
int rv;
rv = PyArg_ParseTuple(args, "O!", &gpiod_ChipType,
(PyObject *)&chip_obj);
if (!rv)
return -1;
if (gpiod_ChipIsClosed(chip_obj))
return -1;
self->offset = 0;
self->owner = chip_obj;
Py_INCREF(chip_obj);
return 0;
}
static void gpiod_LineIter_dealloc(gpiod_LineIterObject *self)
{
PyObject_Del(self);
}
static gpiod_LineObject *gpiod_LineIter_next(gpiod_LineIterObject *self)
{
struct gpiod_line *line;
if (self->offset == gpiod_chip_num_lines(self->owner->chip))
return NULL; /* Last element. */
line = gpiod_chip_get_line(self->owner->chip, self->offset++);
if (!line)
return (gpiod_LineObject *)PyErr_SetFromErrno(PyExc_OSError);
return gpiod_MakeLineObject(self->owner, line);
}
PyDoc_STRVAR(gpiod_LineIterType_doc,
"Allows to iterate over all lines exposed by a GPIO chip.\n"
"\n"
"New line iterator is created by passing a reference to an open gpiod.Chip\n"
"object to the constructor of gpiod.LineIter.\n"
"\n"
"Caller doesn't need to handle the resource management for lines as their\n"
"lifetime is managed by the owning chip.\n"
"\n"
"Example:\n"
"\n"
" chip = gpiod.Chip('gpiochip0')\n"
" for line in gpiod.LineIter(chip):\n"
" do_stuff_with_line(line)");
static PyTypeObject gpiod_LineIterType = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "gpiod.LineIter",
.tp_basicsize = sizeof(gpiod_LineIterObject),
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = gpiod_LineIterType_doc,
.tp_new = PyType_GenericNew,
.tp_init = (initproc)gpiod_LineIter_init,
.tp_dealloc = (destructor)gpiod_LineIter_dealloc,
.tp_iter = PyObject_SelfIter,
.tp_iternext = (iternextfunc)gpiod_LineIter_next,
};
typedef struct {
const char *name;
PyTypeObject *typeobj;
} gpiod_PyType;
static gpiod_PyType gpiod_PyType_list[] = {
{ .name = "Chip", .typeobj = &gpiod_ChipType, },
{ .name = "Line", .typeobj = &gpiod_LineType, },
{ .name = "LineEvent", .typeobj = &gpiod_LineEventType, },
{ .name = "LineBulk", .typeobj = &gpiod_LineBulkType, },
{ .name = "LineIter", .typeobj = &gpiod_LineIterType, },
{ }
};
typedef struct {
PyTypeObject *typeobj;
const char *name;
long int val;
} gpiod_ConstDescr;
static gpiod_ConstDescr gpiod_ConstList[] = {
{
.typeobj = &gpiod_LineType,
.name = "DIRECTION_INPUT",
.val = gpiod_DIRECTION_INPUT,
},
{
.typeobj = &gpiod_LineType,
.name = "DIRECTION_OUTPUT",
.val = gpiod_DIRECTION_OUTPUT,
},
{
.typeobj = &gpiod_LineType,
.name = "DRIVE_PUSH_PULL",
.val = gpiod_DRIVE_PUSH_PULL,
},
{
.typeobj = &gpiod_LineType,
.name = "DRIVE_OPEN_DRAIN",
.val = gpiod_DRIVE_OPEN_DRAIN,
},
{
.typeobj = &gpiod_LineType,
.name = "DRIVE_OPEN_SOURCE",
.val = gpiod_DRIVE_OPEN_SOURCE,
},
{
.typeobj = &gpiod_LineType,
.name = "BIAS_UNKNOWN",
.val = gpiod_BIAS_UNKNOWN,
},
{
.typeobj = &gpiod_LineType,
.name = "BIAS_DISABLED",
.val = gpiod_BIAS_DISABLED,
},
{
.typeobj = &gpiod_LineType,
.name = "BIAS_PULL_UP",
.val = gpiod_BIAS_PULL_UP,
},
{
.typeobj = &gpiod_LineType,
.name = "BIAS_PULL_DOWN",
.val = gpiod_BIAS_PULL_DOWN,
},
{
.typeobj = &gpiod_LineEventType,
.name = "RISING_EDGE",
.val = gpiod_RISING_EDGE,
},
{
.typeobj = &gpiod_LineEventType,
.name = "FALLING_EDGE",
.val = gpiod_FALLING_EDGE,
},
{ }
};
PyDoc_STRVAR(gpiod_Module_is_gpiochip_device_doc,
"is_gpiochip_device(path) -> boolean\n"
"\n"
"Check if the file pointed to by path is a GPIO chip character device.\n"
"Returns true if so, False otherwise.\n"
"\n"
" path\n"
" Path to the file that should be checked.\n");
static PyObject *
gpiod_Module_is_gpiochip_device(PyObject *Py_UNUSED(self), PyObject *args)
{
const char *path;
int ret;
ret = PyArg_ParseTuple(args, "s", &path);
if (!ret)
return NULL;
if (gpiod_is_gpiochip_device(path))
Py_RETURN_TRUE;
Py_RETURN_FALSE;
}
static PyMethodDef gpiod_module_methods[] = {
{
.ml_name = "is_gpiochip_device",
.ml_meth = (PyCFunction)gpiod_Module_is_gpiochip_device,
.ml_flags = METH_VARARGS,
.ml_doc = gpiod_Module_is_gpiochip_device_doc,
},
{ }
};
PyDoc_STRVAR(gpiod_Module_doc,
"Python bindings for libgpiod.\n\
\n\
This module wraps the native C API of libgpiod in a set of python classes.");
static PyModuleDef gpiod_Module = {
PyModuleDef_HEAD_INIT,
.m_name = "gpiod",
.m_doc = gpiod_Module_doc,
.m_size = -1,
.m_methods = gpiod_module_methods,
};
typedef struct {
const char *name;
long int value;
} gpiod_ModuleConst;
static gpiod_ModuleConst gpiod_ModuleConsts[] = {
{
.name = "LINE_REQ_DIR_AS_IS",
.value = gpiod_LINE_REQ_DIR_AS_IS,
},
{
.name = "LINE_REQ_DIR_IN",
.value = gpiod_LINE_REQ_DIR_IN,
},
{
.name = "LINE_REQ_DIR_OUT",
.value = gpiod_LINE_REQ_DIR_OUT,
},
{
.name = "LINE_REQ_EV_FALLING_EDGE",
.value = gpiod_LINE_REQ_EV_FALLING_EDGE,
},
{
.name = "LINE_REQ_EV_RISING_EDGE",
.value = gpiod_LINE_REQ_EV_RISING_EDGE,
},
{
.name = "LINE_REQ_EV_BOTH_EDGES",
.value = gpiod_LINE_REQ_EV_BOTH_EDGES,
},
{
.name = "LINE_REQ_FLAG_OPEN_DRAIN",
.value = gpiod_LINE_REQ_FLAG_OPEN_DRAIN,
},
{
.name = "LINE_REQ_FLAG_OPEN_SOURCE",
.value = gpiod_LINE_REQ_FLAG_OPEN_SOURCE,
},
{
.name = "LINE_REQ_FLAG_ACTIVE_LOW",
.value = gpiod_LINE_REQ_FLAG_ACTIVE_LOW,
},
{
.name = "LINE_REQ_FLAG_BIAS_DISABLED",
.value = gpiod_LINE_REQ_FLAG_BIAS_DISABLED,
},
{
.name = "LINE_REQ_FLAG_BIAS_PULL_DOWN",
.value = gpiod_LINE_REQ_FLAG_BIAS_PULL_DOWN,
},
{
.name = "LINE_REQ_FLAG_BIAS_PULL_UP",
.value = gpiod_LINE_REQ_FLAG_BIAS_PULL_UP,
},
{ }
};
PyMODINIT_FUNC PyInit_gpiod(void)
{
gpiod_ConstDescr *const_descr;
gpiod_ModuleConst *mod_const;
PyObject *module, *val;
gpiod_PyType *type;
unsigned int i;
int rv;
module = PyModule_Create(&gpiod_Module);
if (!module)
return NULL;
for (i = 0; gpiod_PyType_list[i].typeobj; i++) {
type = &gpiod_PyType_list[i];
rv = PyType_Ready(type->typeobj);
if (rv)
return NULL;
Py_INCREF(type->typeobj);
rv = PyModule_AddObject(module, type->name,
(PyObject *)type->typeobj);
if (rv < 0)
return NULL;
}
for (i = 0; gpiod_ConstList[i].name; i++) {
const_descr = &gpiod_ConstList[i];
val = PyLong_FromLong(const_descr->val);
if (!val)
return NULL;
rv = PyDict_SetItemString(const_descr->typeobj->tp_dict,
const_descr->name, val);
Py_DECREF(val);
if (rv)
return NULL;
}
for (i = 0; gpiod_ModuleConsts[i].name; i++) {
mod_const = &gpiod_ModuleConsts[i];
rv = PyModule_AddIntConstant(module,
mod_const->name, mod_const->value);
if (rv < 0)
return NULL;
}
rv = PyModule_AddStringConstant(module, "__version__",
gpiod_version_string());
if (rv < 0)
return NULL;
return module;
}