// SPDX-License-Identifier: LGPL-2.1-or-later // SPDX-FileCopyrightText: 2022 Bartosz Golaszewski #include "internal.h" typedef struct { PyObject_HEAD; struct gpiod_chip *chip; } chip_object; static int chip_init(chip_object *self, PyObject *args, PyObject *Py_UNUSED(ignored)) { struct gpiod_chip *chip; char *path; int ret; ret = PyArg_ParseTuple(args, "s", &path); if (!ret) return -1; Py_BEGIN_ALLOW_THREADS; chip = gpiod_chip_open(path); Py_END_ALLOW_THREADS; if (!chip) { Py_gpiod_SetErrFromErrno(); return -1; } self->chip = chip; return 0; } static void internal_chip_close(chip_object *self) { if (self->chip) { Py_BEGIN_ALLOW_THREADS; gpiod_chip_close(self->chip); Py_END_ALLOW_THREADS; self->chip = NULL; } } static void chip_finalize(chip_object *self) { internal_chip_close(self); } static PyObject *chip_path(chip_object *self, void *Py_UNUSED(ignored)) { return PyUnicode_FromString(gpiod_chip_get_path(self->chip)); } static PyObject *chip_fd(chip_object *self, void *Py_UNUSED(ignored)) { return PyLong_FromLong(gpiod_chip_get_fd(self->chip)); } static PyGetSetDef chip_getset[] = { { .name = "path", .get = (getter)chip_path, }, { .name = "fd", .get = (getter)chip_fd, }, { } }; static PyObject *chip_close(chip_object *self, PyObject *Py_UNUSED(ignored)) { internal_chip_close(self); Py_RETURN_NONE; } static PyObject *chip_get_info(chip_object *self, PyObject *Py_UNUSED(ignored)) { struct gpiod_chip_info *info; PyObject *type, *ret; type = Py_gpiod_GetModuleAttrString("gpiod.chip_info", "ChipInfo"); if (!type) return NULL; Py_BEGIN_ALLOW_THREADS; info = gpiod_chip_get_info(self->chip); Py_END_ALLOW_THREADS; if (!info) { Py_DECREF(type); return PyErr_SetFromErrno(PyExc_OSError); } ret = PyObject_CallFunction(type, "ssI", gpiod_chip_info_get_name(info), gpiod_chip_info_get_label(info), gpiod_chip_info_get_num_lines(info)); gpiod_chip_info_free(info); Py_DECREF(type); return ret; } static PyObject *make_line_info(struct gpiod_line_info *info) { PyObject *type, *ret; type = Py_gpiod_GetModuleAttrString("gpiod.line_info", "LineInfo"); if (!type) return NULL; ret = PyObject_CallFunction(type, "IsOsiOiiiiOk", gpiod_line_info_get_offset(info), gpiod_line_info_get_name(info), gpiod_line_info_is_used(info) ? Py_True : Py_False, gpiod_line_info_get_consumer(info), gpiod_line_info_get_direction(info), gpiod_line_info_is_active_low(info) ? Py_True : Py_False, gpiod_line_info_get_bias(info), gpiod_line_info_get_drive(info), gpiod_line_info_get_edge_detection(info), gpiod_line_info_get_event_clock(info), gpiod_line_info_is_debounced(info) ? Py_True : Py_False, gpiod_line_info_get_debounce_period_us(info)); Py_DECREF(type); return ret; } static PyObject *chip_get_line_info(chip_object *self, PyObject *args) { struct gpiod_line_info *info; unsigned int offset; PyObject *info_obj; int ret, watch; ret = PyArg_ParseTuple(args, "Ip", &offset, &watch); if (!ret) return NULL; Py_BEGIN_ALLOW_THREADS; if (watch) info = gpiod_chip_watch_line_info(self->chip, offset); else info = gpiod_chip_get_line_info(self->chip, offset); Py_END_ALLOW_THREADS; if (!info) return Py_gpiod_SetErrFromErrno(); info_obj = make_line_info(info); gpiod_line_info_free(info); return info_obj; } static PyObject *chip_get_line_name(chip_object *self, PyObject *args) { int ret; unsigned int offset; struct gpiod_line_info *info; PyObject *line_name; const char *name; ret = PyArg_ParseTuple(args, "I", &offset); if (!ret) return NULL; Py_BEGIN_ALLOW_THREADS; info = gpiod_chip_get_line_info(self->chip, offset); Py_END_ALLOW_THREADS; if (!info) return Py_gpiod_SetErrFromErrno(); name = gpiod_line_info_get_name(info); line_name = (name) ? PyUnicode_FromString(name) : Py_NewRef(Py_None); gpiod_line_info_free(info); return line_name; } static PyObject * chip_unwatch_line_info(chip_object *self, PyObject *args) { unsigned int offset; int ret; ret = PyArg_ParseTuple(args, "I", &offset); if (!ret) return NULL; Py_BEGIN_ALLOW_THREADS; ret = gpiod_chip_unwatch_line_info(self->chip, offset); Py_END_ALLOW_THREADS; if (ret) return Py_gpiod_SetErrFromErrno(); Py_RETURN_NONE; } static PyObject * chip_read_info_event(chip_object *self, PyObject *Py_UNUSED(ignored)) { PyObject *type, *info_obj, *event_obj; struct gpiod_info_event *event; struct gpiod_line_info *info; Py_BEGIN_ALLOW_THREADS; event = gpiod_chip_read_info_event(self->chip); Py_END_ALLOW_THREADS; if (!event) return Py_gpiod_SetErrFromErrno(); info = gpiod_info_event_get_line_info(event); info_obj = make_line_info(info); if (!info_obj) { gpiod_info_event_free(event); return NULL; } type = Py_gpiod_GetModuleAttrString("gpiod.info_event", "InfoEvent"); if (!type) { Py_DECREF(info_obj); gpiod_info_event_free(event); return NULL; } event_obj = PyObject_CallFunction(type, "iKO", gpiod_info_event_get_event_type(event), gpiod_info_event_get_timestamp_ns(event), info_obj); Py_DECREF(info_obj); Py_DECREF(type); gpiod_info_event_free(event); return event_obj; } static PyObject *chip_line_offset_from_id(chip_object *self, PyObject *args) { int ret, offset; char *name; ret = PyArg_ParseTuple(args, "s", &name); if (!ret) return NULL; Py_BEGIN_ALLOW_THREADS; offset = gpiod_chip_get_line_offset_from_name(self->chip, name); Py_END_ALLOW_THREADS; if (offset < 0) return Py_gpiod_SetErrFromErrno(); return PyLong_FromLong(offset); } static struct gpiod_request_config * make_request_config(PyObject *consumer_obj, PyObject *event_buffer_size_obj) { struct gpiod_request_config *req_cfg; size_t event_buffer_size; const char *consumer; req_cfg = gpiod_request_config_new(); if (!req_cfg) { Py_gpiod_SetErrFromErrno(); return NULL; } if (!Py_IsNone(consumer_obj)) { consumer = PyUnicode_AsUTF8(consumer_obj); if (!consumer) { gpiod_request_config_free(req_cfg); return NULL; } gpiod_request_config_set_consumer(req_cfg, consumer); } if (!Py_IsNone(event_buffer_size_obj)) { event_buffer_size = PyLong_AsSize_t(event_buffer_size_obj); if (PyErr_Occurred()) { gpiod_request_config_free(req_cfg); return NULL; } gpiod_request_config_set_event_buffer_size(req_cfg, event_buffer_size); } return req_cfg; } static PyObject *chip_request_lines(chip_object *self, PyObject *args) { PyObject *line_config, *consumer, *event_buffer_size, *req_obj; struct gpiod_request_config *req_cfg; struct gpiod_line_config *line_cfg; struct gpiod_line_request *request; int ret; ret = PyArg_ParseTuple(args, "OOO", &line_config, &consumer, &event_buffer_size); if (!ret) return NULL; line_cfg = Py_gpiod_LineConfigGetData(line_config); if (!line_cfg) return NULL; req_cfg = make_request_config(consumer, event_buffer_size); if (!req_cfg) return NULL; Py_BEGIN_ALLOW_THREADS; request = gpiod_chip_request_lines(self->chip, req_cfg, line_cfg); Py_END_ALLOW_THREADS; if (!request) { gpiod_request_config_free(req_cfg); return Py_gpiod_SetErrFromErrno(); } req_obj = Py_gpiod_MakeRequestObject(request, gpiod_request_config_get_event_buffer_size(req_cfg)); if (!req_obj) gpiod_line_request_release(request); gpiod_request_config_free(req_cfg); return req_obj; } static PyMethodDef chip_methods[] = { { .ml_name = "close", .ml_meth = (PyCFunction)chip_close, .ml_flags = METH_NOARGS, }, { .ml_name = "get_info", .ml_meth = (PyCFunction)chip_get_info, .ml_flags = METH_NOARGS, }, { .ml_name = "get_line_info", .ml_meth = (PyCFunction)chip_get_line_info, .ml_flags = METH_VARARGS, }, { .ml_name = "unwatch_line_info", .ml_meth = (PyCFunction)chip_unwatch_line_info, .ml_flags = METH_VARARGS, }, { .ml_name = "read_info_event", .ml_meth = (PyCFunction)chip_read_info_event, .ml_flags = METH_NOARGS, }, { .ml_name = "line_offset_from_id", .ml_meth = (PyCFunction)chip_line_offset_from_id, .ml_flags = METH_VARARGS, }, { .ml_name = "request_lines", .ml_meth = (PyCFunction)chip_request_lines, .ml_flags = METH_VARARGS, }, { .ml_name = "get_line_name", .ml_meth = (PyCFunction)chip_get_line_name, .ml_flags = METH_VARARGS, }, { } }; PyTypeObject chip_type = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "gpiod._ext.Chip", .tp_basicsize = sizeof(chip_object), .tp_flags = Py_TPFLAGS_DEFAULT, .tp_new = PyType_GenericNew, .tp_init = (initproc)chip_init, .tp_finalize = (destructor)chip_finalize, .tp_dealloc = (destructor)Py_gpiod_dealloc, .tp_getset = chip_getset, .tp_methods = chip_methods, };