mirror of
https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git
synced 2026-10-28 13:55:08 +03:00
If "default_vals" is not provided in the kwds then default_vals are
passed uninitialized to gpiod_line_request_bulk(), so rename the
existing default_vals to vals and introduce a new default_vals that
points to vals, or NULL if no defaults have been passed.
Fixes: 96c524c495 (bindings: implement python bindings)
Reported-by: Pedro Botella <pbotella@gmail.com>
Signed-off-by: Kent Gibson <warthog618@gmail.com>
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
2947 lines
66 KiB
C
2947 lines
66 KiB
C
// SPDX-License-Identifier: LGPL-2.1-or-later
|
|
/*
|
|
* This file is part of libgpiod.
|
|
*
|
|
* Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com>
|
|
*/
|
|
|
|
#include <Python.h>
|
|
#include <gpiod.h>
|
|
|
|
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
|
|
struct gpiod_chip_iter *iter;
|
|
} gpiod_ChipIterObject;
|
|
|
|
typedef struct {
|
|
PyObject_HEAD
|
|
struct gpiod_line_iter *iter;
|
|
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_DISABLE = 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_ACTIVE_HIGH = 1,
|
|
gpiod_ACTIVE_LOW,
|
|
};
|
|
|
|
enum {
|
|
gpiod_BIAS_AS_IS = 1,
|
|
gpiod_BIAS_DISABLE,
|
|
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_active_state_doc,
|
|
"active_state() -> integer\n"
|
|
"\n"
|
|
"Get the active state setting of this GPIO line.");
|
|
|
|
static PyObject *gpiod_Line_active_state(gpiod_LineObject *self,
|
|
PyObject *Py_UNUSED(ignored))
|
|
{
|
|
PyObject *ret;
|
|
int active;
|
|
|
|
if (gpiod_ChipIsClosed(self->owner))
|
|
return NULL;
|
|
|
|
active = gpiod_line_active_state(self->line);
|
|
|
|
if (active == GPIOD_LINE_ACTIVE_STATE_HIGH)
|
|
ret = Py_BuildValue("I", gpiod_ACTIVE_HIGH);
|
|
else
|
|
ret = Py_BuildValue("I", gpiod_ACTIVE_LOW);
|
|
|
|
return ret;
|
|
}
|
|
|
|
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_DISABLE:
|
|
return Py_BuildValue("I", gpiod_BIAS_DISABLE);
|
|
case GPIOD_LINE_BIAS_AS_IS:
|
|
default:
|
|
return Py_BuildValue("I", gpiod_BIAS_AS_IS);
|
|
}
|
|
}
|
|
|
|
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_is_open_drain_doc,
|
|
"is_open_drain() -> boolean\n"
|
|
"\n"
|
|
"Check if this line represents an open-drain GPIO.");
|
|
|
|
static PyObject *gpiod_Line_is_open_drain(gpiod_LineObject *self,
|
|
PyObject *Py_UNUSED(ignored))
|
|
{
|
|
if (gpiod_ChipIsClosed(self->owner))
|
|
return NULL;
|
|
|
|
if (gpiod_line_is_open_drain(self->line))
|
|
Py_RETURN_TRUE;
|
|
|
|
Py_RETURN_FALSE;
|
|
}
|
|
|
|
PyDoc_STRVAR(gpiod_Line_is_open_source_doc,
|
|
"is_open_source() -> boolean\n"
|
|
"\n"
|
|
"Check if this line represents an open-source GPIO.");
|
|
|
|
static PyObject *gpiod_Line_is_open_source(gpiod_LineObject *self,
|
|
PyObject *Py_UNUSED(ignored))
|
|
{
|
|
if (gpiod_ChipIsClosed(self->owner))
|
|
return NULL;
|
|
|
|
if (gpiod_line_is_open_source(self->line))
|
|
Py_RETURN_TRUE;
|
|
|
|
Py_RETURN_FALSE;
|
|
}
|
|
|
|
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_is_requested_doc,
|
|
"is_requested() -> boolean\n"
|
|
"\n"
|
|
"Check if this user has ownership of this line.");
|
|
|
|
static PyObject *gpiod_Line_is_requested(gpiod_LineObject *self,
|
|
PyObject *Py_UNUSED(ignored))
|
|
{
|
|
if (gpiod_ChipIsClosed(self->owner))
|
|
return NULL;
|
|
|
|
if (gpiod_line_is_requested(self->line))
|
|
Py_RETURN_TRUE;
|
|
|
|
Py_RETURN_FALSE;
|
|
}
|
|
|
|
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_update_doc,
|
|
"update() -> None\n"
|
|
"\n"
|
|
"Re-read the line information from the kernel.");
|
|
|
|
static PyObject *gpiod_Line_update(gpiod_LineObject *self,
|
|
PyObject *Py_UNUSED(ignored))
|
|
{
|
|
int ret;
|
|
|
|
ret = gpiod_line_update(self->line);
|
|
if (ret)
|
|
return PyErr_SetFromErrno(PyExc_OSError);
|
|
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
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 = "active_state",
|
|
.ml_meth = (PyCFunction)gpiod_Line_active_state,
|
|
.ml_flags = METH_NOARGS,
|
|
.ml_doc = gpiod_Line_active_state_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 = "is_open_drain",
|
|
.ml_meth = (PyCFunction)gpiod_Line_is_open_drain,
|
|
.ml_flags = METH_NOARGS,
|
|
.ml_doc = gpiod_Line_is_open_drain_doc,
|
|
},
|
|
{
|
|
.ml_name = "is_open_source",
|
|
.ml_meth = (PyCFunction)gpiod_Line_is_open_source,
|
|
.ml_flags = METH_NOARGS,
|
|
.ml_doc = gpiod_Line_is_open_source_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 = "is_requested",
|
|
.ml_meth = (PyCFunction)gpiod_Line_is_requested,
|
|
.ml_flags = METH_NOARGS,
|
|
.ml_doc = gpiod_Line_is_requested_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 = "update",
|
|
.ml_meth = (PyCFunction)gpiod_Line_update,
|
|
.ml_flags = METH_NOARGS,
|
|
.ml_doc = gpiod_Line_update_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 > GPIOD_LINE_BULK_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 void gpiod_LineBulkObjToCLineBulk(gpiod_LineBulkObject *bulk_obj,
|
|
struct gpiod_line_bulk *bulk)
|
|
{
|
|
gpiod_LineObject *line_obj;
|
|
Py_ssize_t i;
|
|
|
|
gpiod_line_bulk_init(bulk);
|
|
|
|
for (i = 0; i < bulk_obj->num_lines; i++) {
|
|
line_obj = (gpiod_LineObject *)bulk_obj->lines[i];
|
|
gpiod_line_bulk_add(bulk, line_obj->line);
|
|
}
|
|
}
|
|
|
|
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_DISABLE)
|
|
conf->flags |= GPIOD_LINE_REQUEST_FLAG_BIAS_DISABLE;
|
|
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[GPIOD_LINE_BULK_MAX_LINES], val;
|
|
PyObject *def_vals_obj = NULL, *iter, *next;
|
|
struct gpiod_line_request_config conf;
|
|
struct gpiod_line_bulk bulk;
|
|
Py_ssize_t num_def_vals;
|
|
char *consumer = NULL;
|
|
const int *default_vals = 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;
|
|
|
|
gpiod_LineBulkObjToCLineBulk(self, &bulk);
|
|
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;
|
|
}
|
|
|
|
Py_BEGIN_ALLOW_THREADS;
|
|
rv = gpiod_line_request_bulk(&bulk, &conf, default_vals);
|
|
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[GPIOD_LINE_BULK_MAX_LINES];
|
|
struct gpiod_line_bulk bulk;
|
|
PyObject *val_list, *val;
|
|
Py_ssize_t i;
|
|
|
|
if (gpiod_LineBulkOwnerIsClosed(self))
|
|
return NULL;
|
|
|
|
gpiod_LineBulkObjToCLineBulk(self, &bulk);
|
|
|
|
memset(vals, 0, sizeof(vals));
|
|
Py_BEGIN_ALLOW_THREADS;
|
|
rv = gpiod_line_get_value_bulk(&bulk, vals);
|
|
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[GPIOD_LINE_BULK_MAX_LINES];
|
|
struct gpiod_line_bulk bulk;
|
|
PyObject *val_list;
|
|
|
|
if (gpiod_LineBulkOwnerIsClosed(self))
|
|
return NULL;
|
|
|
|
gpiod_LineBulkObjToCLineBulk(self, &bulk);
|
|
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;
|
|
|
|
Py_BEGIN_ALLOW_THREADS;
|
|
rv = gpiod_line_set_value_bulk(&bulk, vals);
|
|
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[GPIOD_LINE_BULK_MAX_LINES];
|
|
PyObject *val_list;
|
|
struct gpiod_line_bulk bulk;
|
|
const int *valp;
|
|
int dirn, flags;
|
|
|
|
if (gpiod_LineBulkOwnerIsClosed(self))
|
|
return NULL;
|
|
|
|
gpiod_LineBulkObjToCLineBulk(self, &bulk);
|
|
|
|
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;
|
|
}
|
|
|
|
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)
|
|
{
|
|
int rv;
|
|
struct gpiod_line_bulk bulk;
|
|
int flags;
|
|
|
|
if (gpiod_LineBulkOwnerIsClosed(self))
|
|
return NULL;
|
|
|
|
gpiod_LineBulkObjToCLineBulk(self, &bulk);
|
|
|
|
rv = PyArg_ParseTuple(args, "i", &flags);
|
|
if (!rv)
|
|
return NULL;
|
|
|
|
Py_BEGIN_ALLOW_THREADS;
|
|
rv = gpiod_line_set_flags_bulk(&bulk, flags);
|
|
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;
|
|
|
|
gpiod_LineBulkObjToCLineBulk(self, &bulk);
|
|
|
|
Py_BEGIN_ALLOW_THREADS;
|
|
rv = gpiod_line_set_direction_input_bulk(&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[GPIOD_LINE_BULK_MAX_LINES];
|
|
PyObject *val_list;
|
|
struct gpiod_line_bulk bulk;
|
|
const int *valp;
|
|
|
|
if (gpiod_LineBulkOwnerIsClosed(self))
|
|
return NULL;
|
|
|
|
gpiod_LineBulkObjToCLineBulk(self, &bulk);
|
|
|
|
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;
|
|
}
|
|
|
|
Py_BEGIN_ALLOW_THREADS;
|
|
rv = gpiod_line_set_direction_output_bulk(&bulk, valp);
|
|
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;
|
|
|
|
gpiod_LineBulkObjToCLineBulk(self, &bulk);
|
|
gpiod_line_release_bulk(&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;
|
|
struct gpiod_line *line, **line_ptr;
|
|
gpiod_LineObject *line_obj;
|
|
gpiod_ChipObject *owner;
|
|
long sec = 0, nsec = 0;
|
|
struct timespec ts;
|
|
PyObject *ret;
|
|
Py_ssize_t i;
|
|
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;
|
|
|
|
gpiod_LineBulkObjToCLineBulk(self, &bulk);
|
|
|
|
Py_BEGIN_ALLOW_THREADS;
|
|
rv = gpiod_line_event_wait_bulk(&bulk, &ts, &ev_bulk);
|
|
Py_END_ALLOW_THREADS;
|
|
if (rv < 0)
|
|
return PyErr_SetFromErrno(PyExc_OSError);
|
|
else if (rv == 0)
|
|
Py_RETURN_NONE;
|
|
|
|
ret = PyList_New(gpiod_line_bulk_num_lines(&ev_bulk));
|
|
if (!ret)
|
|
return NULL;
|
|
|
|
owner = ((gpiod_LineObject *)(self->lines[0]))->owner;
|
|
|
|
i = 0;
|
|
gpiod_line_bulk_foreach_line(&ev_bulk, line, line_ptr) {
|
|
line_obj = gpiod_MakeLineObject(owner, line);
|
|
if (!line_obj) {
|
|
Py_DECREF(ret);
|
|
return NULL;
|
|
}
|
|
|
|
rv = PyList_SetItem(ret, i++, (PyObject *)line_obj);
|
|
if (rv < 0) {
|
|
Py_DECREF(ret);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
enum {
|
|
gpiod_OPEN_LOOKUP = 1,
|
|
gpiod_OPEN_BY_NAME,
|
|
gpiod_OPEN_BY_PATH,
|
|
gpiod_OPEN_BY_LABEL,
|
|
gpiod_OPEN_BY_NUMBER,
|
|
};
|
|
|
|
static int gpiod_Chip_init(gpiod_ChipObject *self,
|
|
PyObject *args, PyObject *Py_UNUSED(ignored))
|
|
{
|
|
int rv, how = gpiod_OPEN_LOOKUP;
|
|
PyThreadState *thread;
|
|
char *descr;
|
|
|
|
rv = PyArg_ParseTuple(args, "s|i", &descr, &how);
|
|
if (!rv)
|
|
return -1;
|
|
|
|
thread = PyEval_SaveThread();
|
|
switch (how) {
|
|
case gpiod_OPEN_LOOKUP:
|
|
self->chip = gpiod_chip_open_lookup(descr);
|
|
break;
|
|
case gpiod_OPEN_BY_NAME:
|
|
self->chip = gpiod_chip_open_by_name(descr);
|
|
break;
|
|
case gpiod_OPEN_BY_PATH:
|
|
self->chip = gpiod_chip_open(descr);
|
|
break;
|
|
case gpiod_OPEN_BY_LABEL:
|
|
self->chip = gpiod_chip_open_by_label(descr);
|
|
break;
|
|
case gpiod_OPEN_BY_NUMBER:
|
|
self->chip = gpiod_chip_open_by_number(atoi(descr));
|
|
break;
|
|
default:
|
|
PyEval_RestoreThread(thread);
|
|
PyErr_BadArgument();
|
|
return -1;
|
|
}
|
|
PyEval_RestoreThread(thread);
|
|
if (!self->chip) {
|
|
PyErr_SetFromErrno(PyExc_OSError);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void gpiod_Chip_dealloc(gpiod_ChipObject *self)
|
|
{
|
|
if (self->chip)
|
|
gpiod_chip_close(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_close(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);
|
|
}
|
|
|
|
PyDoc_STRVAR(gpiod_Chip_find_line_doc,
|
|
"find_line(name) -> gpiod.Line object or None\n"
|
|
"\n"
|
|
"Get the GPIO line by name.\n"
|
|
"\n"
|
|
" name\n"
|
|
" Line name (string)\n"
|
|
"\n"
|
|
"Returns a gpiod.Line object or None if line with given name is not\n"
|
|
"associated with this chip.");
|
|
|
|
static gpiod_LineObject *
|
|
gpiod_Chip_find_line(gpiod_ChipObject *self, PyObject *args)
|
|
{
|
|
struct gpiod_line *line;
|
|
const char *name;
|
|
int rv;
|
|
|
|
if (gpiod_ChipIsClosed(self))
|
|
return NULL;
|
|
|
|
rv = PyArg_ParseTuple(args, "s", &name);
|
|
if (!rv)
|
|
return NULL;
|
|
|
|
Py_BEGIN_ALLOW_THREADS;
|
|
line = gpiod_chip_find_line(self->chip, name);
|
|
Py_END_ALLOW_THREADS;
|
|
if (!line) {
|
|
if (errno == ENOENT) {
|
|
Py_INCREF(Py_None);
|
|
return (gpiod_LineObject *)Py_None;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
gpiod_LineObject *line_obj;
|
|
struct gpiod_line *line;
|
|
unsigned int offset;
|
|
PyObject *list;
|
|
int rv;
|
|
|
|
if (gpiod_ChipIsClosed(self))
|
|
return NULL;
|
|
|
|
rv = gpiod_chip_get_all_lines(self->chip, &bulk);
|
|
if (rv)
|
|
return (gpiod_LineBulkObject *)PyErr_SetFromErrno(
|
|
PyExc_OSError);
|
|
|
|
list = PyList_New(gpiod_line_bulk_num_lines(&bulk));
|
|
if (!list)
|
|
return NULL;
|
|
|
|
gpiod_line_bulk_foreach_line_off(&bulk, line, offset) {
|
|
line_obj = gpiod_MakeLineObject(self, line);
|
|
if (!line_obj) {
|
|
Py_DECREF(list);
|
|
return NULL;
|
|
}
|
|
|
|
rv = PyList_SetItem(list, offset, (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_lines_doc,
|
|
"find_lines(names) -> gpiod.LineBulk object\n"
|
|
"\n"
|
|
"Look up a set of lines by their names.\n"
|
|
"\n"
|
|
" names\n"
|
|
" Sequence of line names.\n"
|
|
"\n"
|
|
"Unlike find_line(), this method raises an exception if at least one line\n"
|
|
"from the list doesn't exist.");
|
|
|
|
static gpiod_LineBulkObject *
|
|
gpiod_Chip_find_lines(gpiod_ChipObject *self, PyObject *args)
|
|
{
|
|
PyObject *names, *lines, *iter, *next, *arg;
|
|
gpiod_LineBulkObject *bulk;
|
|
Py_ssize_t num_names, i;
|
|
gpiod_LineObject *line;
|
|
int rv;
|
|
|
|
rv = PyArg_ParseTuple(args, "O", &names);
|
|
if (!rv)
|
|
return NULL;
|
|
|
|
num_names = PyObject_Size(names);
|
|
if (num_names < 1) {
|
|
PyErr_SetString(PyExc_TypeError,
|
|
"Argument must be a non-empty sequence of names");
|
|
return NULL;
|
|
}
|
|
|
|
lines = PyList_New(num_names);
|
|
if (!lines)
|
|
return NULL;
|
|
|
|
iter = PyObject_GetIter(names);
|
|
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);
|
|
if (!arg) {
|
|
Py_DECREF(iter);
|
|
Py_DECREF(lines);
|
|
return NULL;
|
|
}
|
|
|
|
line = gpiod_Chip_find_line(self, arg);
|
|
Py_DECREF(arg);
|
|
if (!line || (PyObject *)line == Py_None) {
|
|
Py_DECREF(iter);
|
|
Py_DECREF(lines);
|
|
if ((PyObject *)line == Py_None)
|
|
PyErr_SetString(PyExc_TypeError,
|
|
"Unable to find all lines from the list");
|
|
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);
|
|
return bulk;
|
|
}
|
|
|
|
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,
|
|
},
|
|
{
|
|
.ml_name = "find_lines",
|
|
.ml_meth = (PyCFunction)gpiod_Chip_find_lines,
|
|
.ml_flags = METH_VARARGS,
|
|
.ml_doc = gpiod_Chip_find_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_LABEL, OPEN_BY_NAME, OPEN_BY_NUMBER, OPEN_BY_PATH,\n"
|
|
"and OPEN_LOOKUP. The last option means that libgpiod should open the chip\n"
|
|
"based on the best guess what the path is. This is also the default if the\n"
|
|
"second argument is 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_ChipIter_init(gpiod_ChipIterObject *self,
|
|
PyObject *Py_UNUSED(ignored0),
|
|
PyObject *Py_UNUSED(ignored1))
|
|
{
|
|
self->iter = gpiod_chip_iter_new();
|
|
if (!self->iter) {
|
|
PyErr_SetFromErrno(PyExc_OSError);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void gpiod_ChipIter_dealloc(gpiod_ChipIterObject *self)
|
|
{
|
|
if (self->iter)
|
|
gpiod_chip_iter_free_noclose(self->iter);
|
|
|
|
PyObject_Del(self);
|
|
}
|
|
|
|
static gpiod_ChipObject *gpiod_ChipIter_next(gpiod_ChipIterObject *self)
|
|
{
|
|
gpiod_ChipObject *chip_obj;
|
|
struct gpiod_chip *chip;
|
|
|
|
Py_BEGIN_ALLOW_THREADS;
|
|
chip = gpiod_chip_iter_next_noclose(self->iter);
|
|
Py_END_ALLOW_THREADS;
|
|
if (!chip)
|
|
return NULL; /* Last element. */
|
|
|
|
chip_obj = PyObject_New(gpiod_ChipObject, &gpiod_ChipType);
|
|
if (!chip_obj) {
|
|
gpiod_chip_close(chip);
|
|
return NULL;
|
|
}
|
|
|
|
chip_obj->chip = chip;
|
|
|
|
return chip_obj;
|
|
}
|
|
|
|
PyDoc_STRVAR(gpiod_ChipIterType_doc,
|
|
"Allows to iterate over all GPIO chips in the system.\n"
|
|
"\n"
|
|
"The ChipIter's constructor takes no arguments.\n"
|
|
"\n"
|
|
"Each iteration yields the next open GPIO chip handle. The caller is\n"
|
|
"responsible for closing each chip\n"
|
|
"\n"
|
|
"Example:\n"
|
|
"\n"
|
|
" for chip in gpiod.ChipIter():\n"
|
|
" do_something_with_chip(chip)\n"
|
|
" chip.close()");
|
|
|
|
static PyTypeObject gpiod_ChipIterType = {
|
|
PyVarObject_HEAD_INIT(NULL, 0)
|
|
.tp_name = "gpiod.ChipIter",
|
|
.tp_basicsize = sizeof(gpiod_ChipIterObject),
|
|
.tp_flags = Py_TPFLAGS_DEFAULT,
|
|
.tp_doc = gpiod_ChipIterType_doc,
|
|
.tp_new = PyType_GenericNew,
|
|
.tp_init = (initproc)gpiod_ChipIter_init,
|
|
.tp_dealloc = (destructor)gpiod_ChipIter_dealloc,
|
|
.tp_iter = PyObject_SelfIter,
|
|
.tp_iternext = (iternextfunc)gpiod_ChipIter_next,
|
|
};
|
|
|
|
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;
|
|
|
|
Py_BEGIN_ALLOW_THREADS;
|
|
self->iter = gpiod_line_iter_new(chip_obj->chip);
|
|
Py_END_ALLOW_THREADS;
|
|
if (!self->iter) {
|
|
PyErr_SetFromErrno(PyExc_OSError);
|
|
return -1;
|
|
}
|
|
|
|
self->owner = chip_obj;
|
|
Py_INCREF(chip_obj);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void gpiod_LineIter_dealloc(gpiod_LineIterObject *self)
|
|
{
|
|
if (self->iter)
|
|
gpiod_line_iter_free(self->iter);
|
|
|
|
PyObject_Del(self);
|
|
}
|
|
|
|
static gpiod_LineObject *gpiod_LineIter_next(gpiod_LineIterObject *self)
|
|
{
|
|
struct gpiod_line *line;
|
|
|
|
line = gpiod_line_iter_next(self->iter);
|
|
if (!line)
|
|
return NULL; /* Last element. */
|
|
|
|
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,
|
|
};
|
|
|
|
PyDoc_STRVAR(gpiod_Module_find_line_doc,
|
|
"find_line(name) -> gpiod.Line object or None\n"
|
|
"\n"
|
|
"Lookup a GPIO line by name. Search all gpiochips. Returns a gpiod.Line\n"
|
|
"or None if a line with given name doesn't exist in the system.\n"
|
|
"\n"
|
|
"NOTE: the gpiod.Chip object owning the returned line must be closed\n"
|
|
"by the caller.\n"
|
|
"\n"
|
|
" name\n"
|
|
" Name of the line to find (string).");
|
|
|
|
static gpiod_LineObject *gpiod_Module_find_line(PyObject *Py_UNUSED(self),
|
|
PyObject *args)
|
|
{
|
|
gpiod_LineObject *line_obj;
|
|
gpiod_ChipObject *chip_obj;
|
|
struct gpiod_chip *chip;
|
|
struct gpiod_line *line;
|
|
const char *name;
|
|
int rv;
|
|
|
|
rv = PyArg_ParseTuple(args, "s", &name);
|
|
if (!rv)
|
|
return NULL;
|
|
|
|
Py_BEGIN_ALLOW_THREADS;
|
|
line = gpiod_line_find(name);
|
|
Py_END_ALLOW_THREADS;
|
|
if (!line) {
|
|
if (errno == ENOENT) {
|
|
Py_INCREF(Py_None);
|
|
return (gpiod_LineObject *)Py_None;
|
|
}
|
|
|
|
return (gpiod_LineObject *)PyErr_SetFromErrno(PyExc_OSError);
|
|
}
|
|
|
|
chip = gpiod_line_get_chip(line);
|
|
|
|
chip_obj = PyObject_New(gpiod_ChipObject, &gpiod_ChipType);
|
|
if (!chip_obj) {
|
|
gpiod_chip_close(chip);
|
|
return NULL;
|
|
}
|
|
|
|
chip_obj->chip = chip;
|
|
|
|
line_obj = gpiod_MakeLineObject(chip_obj, line);
|
|
if (!line_obj)
|
|
return NULL;
|
|
|
|
/*
|
|
* PyObject_New() set the reference count for the chip object at 1 and
|
|
* the call to gpiod_MakeLineObject() increased it to 2. However when
|
|
* we return the object to the line object to the python interpreter,
|
|
* there'll be only a single reference holder to the chip - the line
|
|
* object itself. Decrease the chip reference here manually.
|
|
*/
|
|
Py_DECREF(line_obj->owner);
|
|
|
|
return line_obj;
|
|
}
|
|
|
|
PyDoc_STRVAR(gpiod_Module_version_string_doc,
|
|
"version_string() -> string\n"
|
|
"\n"
|
|
"DEPRECATED: use __version__ module attribute instead.\n"
|
|
"Get the API version of the library as a human-readable string.");
|
|
|
|
static PyObject *gpiod_Module_version_string(PyObject *Py_UNUSED(ignored0),
|
|
PyObject *Py_UNUSED(ignored1))
|
|
{
|
|
return PyUnicode_FromFormat("%s", gpiod_version_string());
|
|
}
|
|
|
|
static PyMethodDef gpiod_module_methods[] = {
|
|
{
|
|
.ml_name = "find_line",
|
|
.ml_meth = (PyCFunction)gpiod_Module_find_line,
|
|
.ml_flags = METH_VARARGS,
|
|
.ml_doc = gpiod_Module_find_line_doc,
|
|
},
|
|
{
|
|
.ml_name = "version_string",
|
|
.ml_meth = (PyCFunction)gpiod_Module_version_string,
|
|
.ml_flags = METH_NOARGS,
|
|
.ml_doc = gpiod_Module_version_string_doc,
|
|
},
|
|
{ }
|
|
};
|
|
|
|
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, },
|
|
{ .name = "ChipIter", .typeobj = &gpiod_ChipIterType },
|
|
{ }
|
|
};
|
|
|
|
typedef struct {
|
|
PyTypeObject *typeobj;
|
|
const char *name;
|
|
long int val;
|
|
} gpiod_ConstDescr;
|
|
|
|
static gpiod_ConstDescr gpiod_ConstList[] = {
|
|
{
|
|
.typeobj = &gpiod_ChipType,
|
|
.name = "OPEN_LOOKUP",
|
|
.val = gpiod_OPEN_LOOKUP,
|
|
},
|
|
{
|
|
.typeobj = &gpiod_ChipType,
|
|
.name = "OPEN_BY_PATH",
|
|
.val = gpiod_OPEN_BY_PATH,
|
|
},
|
|
{
|
|
.typeobj = &gpiod_ChipType,
|
|
.name = "OPEN_BY_NAME",
|
|
.val = gpiod_OPEN_BY_NAME,
|
|
},
|
|
{
|
|
.typeobj = &gpiod_ChipType,
|
|
.name = "OPEN_BY_LABEL",
|
|
.val = gpiod_OPEN_BY_LABEL,
|
|
},
|
|
{
|
|
.typeobj = &gpiod_ChipType,
|
|
.name = "OPEN_BY_NUMBER",
|
|
.val = gpiod_OPEN_BY_NUMBER,
|
|
},
|
|
{
|
|
.typeobj = &gpiod_LineType,
|
|
.name = "DIRECTION_INPUT",
|
|
.val = gpiod_DIRECTION_INPUT,
|
|
},
|
|
{
|
|
.typeobj = &gpiod_LineType,
|
|
.name = "DIRECTION_OUTPUT",
|
|
.val = gpiod_DIRECTION_OUTPUT,
|
|
},
|
|
{
|
|
.typeobj = &gpiod_LineType,
|
|
.name = "ACTIVE_HIGH",
|
|
.val = gpiod_ACTIVE_HIGH,
|
|
},
|
|
{
|
|
.typeobj = &gpiod_LineType,
|
|
.name = "ACTIVE_LOW",
|
|
.val = gpiod_ACTIVE_LOW,
|
|
},
|
|
{
|
|
.typeobj = &gpiod_LineType,
|
|
.name = "BIAS_AS_IS",
|
|
.val = gpiod_BIAS_AS_IS,
|
|
},
|
|
{
|
|
.typeobj = &gpiod_LineType,
|
|
.name = "BIAS_DISABLE",
|
|
.val = gpiod_BIAS_DISABLE,
|
|
},
|
|
{
|
|
.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_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_DISABLE",
|
|
.value = gpiod_LINE_REQ_FLAG_BIAS_DISABLE,
|
|
},
|
|
{
|
|
.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;
|
|
}
|