1
0
mirror of https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git synced 2026-10-24 00:48:53 +03:00
Files
libgpiod/bindings/glib/line-request.c
Bartosz Golaszewski e090088c21 bindings: add GLib bindings
Implement GObject-based GLib bindings for libgpiod. Include generating
GObject introspection data, tests and examples.

Tested-by: Alexander Sverdlin <alexander.sverdlin@siemens.com>
Link: https://lore.kernel.org/r/20240812-dbus-v5-3-ead288509217@linaro.org
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
2024-08-13 10:38:40 +02:00

453 lines
11 KiB
C

// SPDX-License-Identifier: LGPL-2.1-or-later
// SPDX-FileCopyrightText: 2023-2024 Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
#include <gio/gio.h>
#include "internal.h"
static const gsize event_buf_size = 64;
/**
* GpiodglibLineRequest:
*
* Line request object allows interacting with a set of requested GPIO lines.
*/
struct _GpiodglibLineRequest {
GObject parent_instance;
struct gpiod_line_request *handle;
struct gpiod_edge_event_buffer *event_buf;
GSource *edge_event_src;
guint edge_event_src_id;
enum gpiod_line_value *val_buf;
gboolean released;
};
typedef enum {
GPIODGLIB_LINE_REQUEST_PROP_CHIP_NAME = 1,
GPIODGLIB_LINE_REQUEST_PROP_REQUESTED_OFFSETS,
} GpiodglibLineRequestProp;
enum {
GPIODGLIB_LINE_REQUEST_SIGNAL_EDGE_EVENT,
GPIODGLIB_LINE_REQUEST_SIGNAL_LAST,
};
static guint signals[GPIODGLIB_LINE_REQUEST_SIGNAL_LAST];
G_DEFINE_TYPE(GpiodglibLineRequest, gpiodglib_line_request, G_TYPE_OBJECT);
static gboolean
gpiodglib_line_request_on_edge_event(GIOChannel *source G_GNUC_UNUSED,
GIOCondition condition G_GNUC_UNUSED,
gpointer data)
{
struct gpiod_edge_event *event_handle, *event_copy;
GpiodglibLineRequest *self = data;
gint ret, i;
ret = gpiod_line_request_read_edge_events(self->handle,
self->event_buf,
event_buf_size);
if (ret < 0)
return TRUE;
for (i = 0; i < ret; i++) {
g_autoptr(GpiodglibEdgeEvent) event = NULL;
event_handle = gpiod_edge_event_buffer_get_event(
self->event_buf, i);
event_copy = gpiod_edge_event_copy(event_handle);
if (!event_copy)
g_error("failed to copy the edge event");
event = _gpiodglib_edge_event_new(event_copy);
g_signal_emit(self,
signals[GPIODGLIB_LINE_REQUEST_SIGNAL_EDGE_EVENT],
0,
event);
}
return TRUE;
}
static void gpiodglib_line_request_get_property(GObject *obj, guint prop_id,
GValue *val, GParamSpec *pspec)
{
GpiodglibLineRequest *self = GPIODGLIB_LINE_REQUEST_OBJ(obj);
g_autofree guint *offsets = NULL;
g_autoptr(GArray) boxed = NULL;
gsize num_offsets;
g_assert(self->handle);
switch ((GpiodglibLineRequestProp)prop_id) {
case GPIODGLIB_LINE_REQUEST_PROP_CHIP_NAME:
if (gpiodglib_line_request_is_released(self))
g_value_set_static_string(val, NULL);
else
g_value_set_string(val,
gpiod_line_request_get_chip_name(self->handle));
break;
case GPIODGLIB_LINE_REQUEST_PROP_REQUESTED_OFFSETS:
boxed = g_array_new(FALSE, TRUE, sizeof(guint));
if (!gpiodglib_line_request_is_released(self)) {
num_offsets =
gpiod_line_request_get_num_requested_lines(
self->handle);
offsets = g_malloc0(num_offsets * sizeof(guint));
gpiod_line_request_get_requested_offsets(self->handle,
offsets,
num_offsets);
g_array_append_vals(boxed, offsets, num_offsets);
}
g_value_set_boxed(val, boxed);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec);
}
}
static void gpiodglib_line_request_dispose(GObject *obj)
{
GpiodglibLineRequest *self = GPIODGLIB_LINE_REQUEST_OBJ(obj);
if (self->edge_event_src_id)
g_source_remove(self->edge_event_src_id);
G_OBJECT_CLASS(gpiodglib_line_request_parent_class)->dispose(obj);
}
static void gpiodglib_line_request_finalize(GObject *obj)
{
GpiodglibLineRequest *self = GPIODGLIB_LINE_REQUEST_OBJ(obj);
if (!self->released)
gpiodglib_line_request_release(self);
g_clear_pointer(&self->event_buf, gpiod_edge_event_buffer_free);
g_clear_pointer(&self->val_buf, g_free);
G_OBJECT_CLASS(gpiodglib_line_request_parent_class)->finalize(obj);
}
static void
gpiodglib_line_request_class_init(GpiodglibLineRequestClass *line_request_class)
{
GObjectClass *class = G_OBJECT_CLASS(line_request_class);
class->get_property = gpiodglib_line_request_get_property;
class->dispose = gpiodglib_line_request_dispose;
class->finalize = gpiodglib_line_request_finalize;
/**
* GpiodglibLineRequest:chip-name
*
* Name of the GPIO chip this request was made on.
*/
g_object_class_install_property(class,
GPIODGLIB_LINE_REQUEST_PROP_CHIP_NAME,
g_param_spec_string("chip-name", "Chip Name",
"Name of the GPIO chip this request was made on.",
NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
/**
* GpiodglibLineRequest:requested-offsets
*
* Array of requested offsets.
*/
g_object_class_install_property(class,
GPIODGLIB_LINE_REQUEST_PROP_REQUESTED_OFFSETS,
g_param_spec_boxed("requested-offsets", "Requested offsets",
"Array of requested offsets.",
G_TYPE_ARRAY,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
/**
* GpiodglibLineRequest::edge-event:
* @chip: #GpiodglibLineRequest receiving the event
* @event: The #GpiodglibEdgeEvent
*
* Emitted when an edge event is detected on one of the requested GPIO
* line.
*/
signals[GPIODGLIB_LINE_REQUEST_SIGNAL_EDGE_EVENT] =
g_signal_new("edge-event",
G_TYPE_FROM_CLASS(line_request_class),
G_SIGNAL_RUN_LAST,
0,
NULL,
NULL,
g_cclosure_marshal_generic,
G_TYPE_NONE,
1,
GPIODGLIB_EDGE_EVENT_TYPE);
}
static void gpiodglib_line_request_init(GpiodglibLineRequest *self)
{
self->handle = NULL;
self->event_buf = NULL;
self->edge_event_src = NULL;
self->released = FALSE;
}
void gpiodglib_line_request_release(GpiodglibLineRequest *self)
{
g_assert(self);
g_clear_pointer(&self->edge_event_src, g_source_unref);
gpiod_line_request_release(self->handle);
self->released = TRUE;
}
gboolean gpiodglib_line_request_is_released(GpiodglibLineRequest *self)
{
g_assert(self);
return self->released;
}
static void set_err_request_released(GError **err)
{
g_set_error(err, GPIODGLIB_ERROR, GPIODGLIB_ERR_REQUEST_RELEASED,
"line request was released and cannot be used");
}
gchar *gpiodglib_line_request_dup_chip_name(GpiodglibLineRequest *self)
{
return _gpiodglib_dup_prop_string(G_OBJECT(self), "chip-name");
}
GArray *gpiodglib_line_request_get_requested_offsets(GpiodglibLineRequest *self)
{
return _gpiodglib_get_prop_boxed_array(G_OBJECT(self),
"requested-offsets");
}
gboolean gpiodglib_line_request_reconfigure_lines(GpiodglibLineRequest *self,
GpiodglibLineConfig *config,
GError **err)
{
struct gpiod_line_config *config_handle;
gint ret;
g_assert(self && self->handle);
if (gpiodglib_line_request_is_released(self)) {
set_err_request_released(err);
return FALSE;
}
if (!config) {
g_set_error(err, GPIODGLIB_ERROR, GPIODGLIB_ERR_INVAL,
"line-config is required to reconfigure lines");
return FALSE;
}
config_handle = _gpiodglib_line_config_get_handle(config);
ret = gpiod_line_request_reconfigure_lines(self->handle, config_handle);
if (ret) {
_gpiodglib_set_error_from_errno(err,
"failed to reconfigure lines");
return FALSE;
}
return TRUE;
}
gboolean
gpiodglib_line_request_get_value(GpiodglibLineRequest *self, guint offset,
GpiodglibLineValue *value, GError **err)
{
enum gpiod_line_value val;
g_assert(self && self->handle);
if (gpiodglib_line_request_is_released(self)) {
set_err_request_released(err);
return FALSE;
}
val = gpiod_line_request_get_value(self->handle, offset);
if (val == GPIOD_LINE_VALUE_ERROR) {
_gpiodglib_set_error_from_errno(err,
"failed to get line value for offset %u", offset);
return FALSE;
}
*value = _gpiodglib_line_value_from_library(val);
return TRUE;
}
gboolean gpiodglib_line_request_get_values_subset(GpiodglibLineRequest *self,
const GArray *offsets,
GArray **values, GError **err)
{
guint i;
int ret;
g_assert(self && self->handle);
if (gpiodglib_line_request_is_released(self)) {
set_err_request_released(err);
return FALSE;
}
if (!offsets || !values) {
g_set_error(err, GPIODGLIB_ERROR, GPIODGLIB_ERR_INVAL,
"offsets and values must not be NULL");
return FALSE;
}
ret = gpiod_line_request_get_values_subset(self->handle, offsets->len,
(const unsigned int *)offsets->data,
self->val_buf);
if (ret) {
_gpiodglib_set_error_from_errno(err, "failed to read line values");
return FALSE;
}
if (!(*values)) {
*values = g_array_sized_new(FALSE, TRUE,
sizeof(GpiodglibLineValue),
offsets->len);
}
g_array_set_size(*values, offsets->len);
for (i = 0; i < offsets->len; i++) {
GpiodglibLineValue *val = &g_array_index(*values,
GpiodglibLineValue, i);
*val = _gpiodglib_line_value_from_library(self->val_buf[i]);
}
return TRUE;
}
gboolean gpiodglib_line_request_get_values(GpiodglibLineRequest *self,
GArray **values, GError **err)
{
g_autoptr(GArray) offsets = NULL;
offsets = gpiodglib_line_request_get_requested_offsets(self);
return gpiodglib_line_request_get_values_subset(self, offsets,
values, err);
}
gboolean gpiodglib_line_request_set_value(GpiodglibLineRequest *self,
guint offset,
GpiodglibLineValue value,
GError **err)
{
int ret;
g_assert(self && self->handle);
if (gpiodglib_line_request_is_released(self)) {
set_err_request_released(err);
return FALSE;
}
ret = gpiod_line_request_set_value(self->handle, offset,
_gpiodglib_line_value_to_library(value));
if (ret) {
_gpiodglib_set_error_from_errno(err,
"failed to set line value for offset: %u", offset);
return FALSE;
}
return TRUE;
}
gboolean gpiodglib_line_request_set_values_subset(GpiodglibLineRequest *self,
const GArray *offsets,
const GArray *values,
GError **err)
{
guint i;
int ret;
g_assert(self && self->handle);
if (gpiodglib_line_request_is_released(self)) {
set_err_request_released(err);
return FALSE;
}
if (!offsets || !values) {
g_set_error(err, GPIODGLIB_ERROR, GPIODGLIB_ERR_INVAL,
"offsets and values must not be NULL");
return FALSE;
}
if (offsets->len != values->len) {
g_set_error(err, GPIODGLIB_ERROR, GPIODGLIB_ERR_INVAL,
"offsets and values must have the sme size");
return FALSE;
}
for (i = 0; i < values->len; i++)
self->val_buf[i] = _gpiodglib_line_value_to_library(
g_array_index(values,
GpiodglibLineValue, i));
ret = gpiod_line_request_set_values_subset(self->handle,
offsets->len,
(unsigned int *)offsets->data,
self->val_buf);
if (ret) {
_gpiodglib_set_error_from_errno(err,
"failed to set line values");
return FALSE;
}
return TRUE;
}
gboolean gpiodglib_line_request_set_values(GpiodglibLineRequest *self,
GArray *values, GError **err)
{
g_autoptr(GArray) offsets = NULL;
offsets = gpiodglib_line_request_get_requested_offsets(self);
return gpiodglib_line_request_set_values_subset(self, offsets,
values, err);
}
GpiodglibLineRequest *
_gpiodglib_line_request_new(struct gpiod_line_request *handle)
{
g_autoptr(GIOChannel) channel = NULL;
GpiodglibLineRequest *req;
gsize num_lines;
req = GPIODGLIB_LINE_REQUEST_OBJ(
g_object_new(GPIODGLIB_LINE_REQUEST_TYPE, NULL));
req->handle = handle;
req->event_buf = gpiod_edge_event_buffer_new(event_buf_size);
if (!req->event_buf)
g_error("failed to allocate the edge event buffer");
channel = g_io_channel_unix_new(
gpiod_line_request_get_fd(req->handle));
req->edge_event_src = g_io_create_watch(channel, G_IO_IN);
g_source_set_callback(
req->edge_event_src,
G_SOURCE_FUNC(gpiodglib_line_request_on_edge_event),
req, NULL);
req->edge_event_src_id = g_source_attach(req->edge_event_src, NULL);
num_lines = gpiod_line_request_get_num_requested_lines(req->handle);
req->val_buf = g_malloc0(sizeof(enum gpiod_line_value) * num_lines);
return req;
}