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/dbus/manager/helpers.c
Bartosz Golaszewski 84700a6d5f dbus: manager: move g_tree_remove() out of g_assert() in gpiodbus_id_free()
g_assert() is compiled out when G_DISABLE_ASSERT is defined, which would
cause the g_tree_remove() call to disappear entirely, leading to ID leaks
and eventual resource exhaustion. Move the side-effecting call outside
the assertion, consistent with the pattern used elsewhere in the daemon.

Fixes: a5ab76da1e ("dbus: add the D-Bus daemon, command-line client and tests")
Assisted-by: Claude Opus 4.6
Reviewed-by: Vincent Fazio <vfazio@xes-inc.com>
Tested-by: Vincent Fazio <vfazio@xes-inc.com>
Link: https://patch.msgid.link/20260604-pre-2-3-fixes-v2-15-781bcaf47475@oss.qualcomm.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-06-08 10:01:21 +02:00

435 lines
12 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
// SPDX-FileCopyrightText: 2023-2024 Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
#include "helpers.h"
gint gpiodbus_id_cmp(gconstpointer a, gconstpointer b,
gpointer user_data G_GNUC_UNUSED)
{
const gint *id_a = a;
const gint *id_b = b;
if (*id_a < *id_b)
return -1;
else if (*id_a > *id_b)
return 1;
return 0;
}
static gboolean find_lowest(gpointer key, gpointer value G_GNUC_UNUSED,
gpointer data)
{
gint *lowest = data, *curr = key;
if (*lowest == *curr)
(*lowest)++;
return FALSE;
}
gint gpiodbus_id_alloc(GTree *id_root)
{
gint lowest = 0, *key;
g_tree_foreach(id_root, find_lowest, &lowest);
key = g_malloc(sizeof(*key));
*key = lowest;
g_tree_insert(id_root, key, NULL);
return lowest;
}
void gpiodbus_id_free(GTree *id_root, gint id)
{
gboolean removed;
removed = g_tree_remove(id_root, &id);
g_assert(removed);
}
gboolean
gpiodbus_chip_set_props(GpiodbusChip *skeleton, GpiodglibChip *chip,
GError **err)
{
g_autoptr(GpiodglibChipInfo) info = NULL;
g_autofree gchar *label = NULL;
g_autofree gchar *path = NULL;
g_autofree gchar *name = NULL;
info = gpiodglib_chip_get_info(chip, err);
if (!info)
return FALSE;
name = gpiodglib_chip_info_dup_name(info);
label = gpiodglib_chip_info_dup_label(info);
gpiodbus_chip_set_name(skeleton, name);
gpiodbus_chip_set_label(skeleton, label);
gpiodbus_chip_set_num_lines(skeleton,
gpiodglib_chip_info_get_num_lines(info));
path = gpiodglib_chip_dup_path(chip);
gpiodbus_chip_set_path(skeleton, path);
g_dbus_interface_skeleton_flush(G_DBUS_INTERFACE_SKELETON(skeleton));
return TRUE;
}
static const gchar *map_direction(GpiodglibLineDirection direction)
{
switch (direction) {
case GPIODGLIB_LINE_DIRECTION_INPUT:
return "input";
case GPIODGLIB_LINE_DIRECTION_OUTPUT:
return "output";
default:
g_error("invalid direction value returned by libgpiod-glib");
}
}
static const gchar *map_edge(GpiodglibLineEdge edge)
{
switch (edge) {
case GPIODGLIB_LINE_EDGE_NONE:
return "none";
case GPIODGLIB_LINE_EDGE_FALLING:
return "falling";
case GPIODGLIB_LINE_EDGE_RISING:
return "rising";
case GPIODGLIB_LINE_EDGE_BOTH:
return "both";
default:
g_error("invalid edge value returned by libgpiod-glib");
}
}
static const gchar *map_bias(GpiodglibLineBias bias)
{
switch (bias) {
case GPIODGLIB_LINE_BIAS_UNKNOWN:
return "unknown";
case GPIODGLIB_LINE_BIAS_DISABLED:
return "disabled";
case GPIODGLIB_LINE_BIAS_PULL_UP:
return "pull-up";
case GPIODGLIB_LINE_BIAS_PULL_DOWN:
return "pull-down";
default:
g_error("invalid bias value returned by libgpiod-glib");
}
}
static const gchar *map_drive(GpiodglibLineDrive drive)
{
switch (drive) {
case GPIODGLIB_LINE_DRIVE_PUSH_PULL:
return "push-pull";
case GPIODGLIB_LINE_DRIVE_OPEN_DRAIN:
return "open-drain";
case GPIODGLIB_LINE_DRIVE_OPEN_SOURCE:
return "open-source";
default:
g_error("invalid drive value returned by libgpiod-glib");
}
}
static const gchar *map_clock(GpiodglibLineClock event_clock)
{
switch (event_clock) {
case GPIODGLIB_LINE_CLOCK_MONOTONIC:
return "monotonic";
case GPIODGLIB_LINE_CLOCK_REALTIME:
return "realtime";
case GPIODGLIB_LINE_CLOCK_HTE:
return "hte";
default:
g_error("invalid event clock value returned by libgpiod-glib");
}
}
void gpiodbus_line_set_props(GpiodbusLine *skeleton, GpiodglibLineInfo *info)
{
g_autofree gchar *consumer = gpiodglib_line_info_dup_consumer(info);
g_autofree gchar *name = gpiodglib_line_info_dup_name(info);
gpiodbus_line_set_offset(skeleton,
gpiodglib_line_info_get_offset(info));
gpiodbus_line_set_name(skeleton, name);
gpiodbus_line_set_used(skeleton, gpiodglib_line_info_is_used(info));
gpiodbus_line_set_consumer(skeleton, consumer);
gpiodbus_line_set_direction(skeleton,
map_direction(gpiodglib_line_info_get_direction(info)));
gpiodbus_line_set_edge_detection(skeleton,
map_edge(gpiodglib_line_info_get_edge_detection(info)));
gpiodbus_line_set_bias(skeleton,
map_bias(gpiodglib_line_info_get_bias(info)));
gpiodbus_line_set_drive(skeleton,
map_drive(gpiodglib_line_info_get_drive(info)));
gpiodbus_line_set_active_low(skeleton,
gpiodglib_line_info_is_active_low(info));
gpiodbus_line_set_debounced(skeleton,
gpiodglib_line_info_is_debounced(info));
gpiodbus_line_set_debounce_period_us(skeleton,
gpiodglib_line_info_get_debounce_period_us(info));
gpiodbus_line_set_event_clock(skeleton,
map_clock(gpiodglib_line_info_get_event_clock(info)));
g_dbus_interface_skeleton_flush(G_DBUS_INTERFACE_SKELETON(skeleton));
}
static gint line_offset_cmp(gconstpointer a, gconstpointer b)
{
GpiodbusObject *line_obj = (GpiodbusObject *)a;
GpiodbusLine *line;
const guint *offset = b;
line = gpiodbus_object_peek_line(line_obj);
return gpiodbus_line_get_offset(line) != *offset;
}
void gpiodbus_request_set_props(GpiodbusRequest *skeleton,
GpiodglibLineRequest *request, GpiodbusChip *chip,
GDBusObjectManager *line_manager)
{
g_autolist(GpiodbusObject) line_objs = NULL;
g_autoptr(GStrvBuilder) builder = NULL;
g_autoptr(GArray) offsets = NULL;
g_auto(GStrv) paths = NULL;
GList *found;
guint i;
offsets = gpiodglib_line_request_get_requested_offsets(request);
line_objs = g_dbus_object_manager_get_objects(line_manager);
builder = g_strv_builder_new();
for (i = 0; i < offsets->len; i++) {
found = g_list_find_custom(line_objs,
&g_array_index(offsets, guint, i),
line_offset_cmp);
if (found)
g_strv_builder_add(builder,
g_dbus_object_get_object_path(
G_DBUS_OBJECT(found->data)));
}
paths = g_strv_builder_end(builder);
gpiodbus_request_set_chip_path(skeleton,
g_dbus_interface_skeleton_get_object_path(
G_DBUS_INTERFACE_SKELETON(chip)));
gpiodbus_request_set_line_paths(skeleton, (const gchar *const *)paths);
g_dbus_interface_skeleton_flush(G_DBUS_INTERFACE_SKELETON(skeleton));
}
static gboolean
set_settings_from_variant(GpiodglibLineSettings *settings, const gchar *key,
GVariant *val)
{
GpiodglibLineDirection direction;
GpiodglibLineClock event_clock;
GpiodglibLineDrive drive;
GpiodglibLineEdge edge;
GpiodglibLineBias bias;
const gchar *str;
/* FIXME: Make it into a nice set of hashmaps and callbacks. */
if (g_strcmp0(key, "direction") == 0) {
str = g_variant_get_string(val, NULL);
if (g_strcmp0(str, "input") == 0) {
direction = GPIODGLIB_LINE_DIRECTION_INPUT;
} else if (g_strcmp0(str, "output") == 0) {
direction = GPIODGLIB_LINE_DIRECTION_OUTPUT;
} else if (g_strcmp0(str, "as-is") == 0) {
direction = GPIODGLIB_LINE_DIRECTION_AS_IS;
} else {
g_critical("invalid direction value received: '%s'",
str);
return FALSE;
}
gpiodglib_line_settings_set_direction(settings, direction);
} else if (g_strcmp0(key, "edge") == 0) {
str = g_variant_get_string(val, NULL);
if (g_strcmp0(str, "falling") == 0) {
edge = GPIODGLIB_LINE_EDGE_FALLING;
} else if (g_strcmp0(str, "rising") == 0) {
edge = GPIODGLIB_LINE_EDGE_RISING;
} else if (g_strcmp0(str, "both") == 0) {
edge = GPIODGLIB_LINE_EDGE_BOTH;
} else {
g_critical("invalid edge value received: '%s'", str);
return FALSE;
}
gpiodglib_line_settings_set_edge_detection(settings, edge);
} else if (g_strcmp0(key, "active-low") == 0) {
if (g_variant_get_boolean(val))
gpiodglib_line_settings_set_active_low(settings, TRUE);
} else if (g_strcmp0(key, "bias") == 0) {
str = g_variant_get_string(val, NULL);
if (g_strcmp0(str, "as-is") == 0) {
bias = GPIODGLIB_LINE_BIAS_AS_IS;
} else if (g_strcmp0(str, "pull-up") == 0) {
bias = GPIODGLIB_LINE_BIAS_PULL_UP;
} else if (g_strcmp0(str, "pull-down") == 0) {
bias = GPIODGLIB_LINE_BIAS_PULL_DOWN;
} else if (g_strcmp0(str, "disabled") == 0) {
bias = GPIODGLIB_LINE_BIAS_DISABLED;
} else {
g_critical("invalid bias value received: '%s'", str);
return FALSE;
}
gpiodglib_line_settings_set_bias(settings, bias);
} else if (g_strcmp0(key, "drive") == 0) {
str = g_variant_get_string(val, NULL);
if (g_strcmp0(str, "push-pull") == 0) {
drive = GPIODGLIB_LINE_DRIVE_PUSH_PULL;
} else if (g_strcmp0(str, "open-drain") == 0) {
drive = GPIODGLIB_LINE_DRIVE_OPEN_DRAIN;
} else if (g_strcmp0(str, "open-source") == 0) {
drive = GPIODGLIB_LINE_DRIVE_OPEN_SOURCE;
} else {
g_critical("invalid drive value received: '%s'", str);
return FALSE;
}
gpiodglib_line_settings_set_drive(settings, drive);
} else if (g_strcmp0(key, "debounce-period") == 0) {
gpiodglib_line_settings_set_debounce_period_us(settings,
g_variant_get_int64(val));
} else if (g_strcmp0(key, "event-clock") == 0) {
str = g_variant_get_string(val, NULL);
if (g_strcmp0(str, "monotonic") == 0) {
event_clock = GPIODGLIB_LINE_CLOCK_MONOTONIC;
} else if (g_strcmp0(str, "realtime") == 0) {
event_clock = GPIODGLIB_LINE_CLOCK_REALTIME;
} else if (g_strcmp0(str, "hte") == 0) {
event_clock = GPIODGLIB_LINE_CLOCK_HTE;
} else {
g_critical("invalid event clock value received: '%s'",
str);
return FALSE;
}
gpiodglib_line_settings_set_event_clock(settings, event_clock);
} else {
g_critical("invalid config option received: '%s'", key);
return FALSE;
}
return TRUE;
}
GpiodglibLineConfig *gpiodbus_line_config_from_variant(GVariant *variant)
{
g_autoptr(GpiodglibLineSettings) settings = NULL;
g_autoptr(GpiodglibLineConfig) config = NULL;
g_autoptr(GVariant) output_values_v = NULL;
g_autoptr(GVariant) line_configs_v = NULL;
g_autoptr(GArray) values = NULL;
g_autoptr(GError) err = NULL;
GVariantIter iter0, iter1;
guint offset;
gboolean ret;
GVariant *v;
gchar *k;
gint val;
line_configs_v = g_variant_get_child_value(variant, 0);
output_values_v = g_variant_get_child_value(variant, 1);
config = gpiodglib_line_config_new();
settings = gpiodglib_line_settings_new(NULL);
g_variant_iter_init(&iter0, line_configs_v);
while ((v = g_variant_iter_next_value(&iter0))) {
g_autoptr(GVariant) line_settings_v = NULL;
g_autoptr(GVariant) line_config_v = v;
g_autoptr(GVariant) offsets_v = NULL;
g_autoptr(GArray) offsets = NULL;
offsets_v = g_variant_get_child_value(line_config_v, 0);
line_settings_v = g_variant_get_child_value(line_config_v, 1);
gpiodglib_line_settings_reset(settings);
g_variant_iter_init(&iter1, line_settings_v);
while (g_variant_iter_next(&iter1, "{sv}", &k, &v)) {
g_autoptr(GVariant) val = v;
g_autofree gchar *key = k;
ret = set_settings_from_variant(settings, key, val);
if (!ret)
return NULL;
}
offsets = g_array_sized_new(FALSE, TRUE, sizeof(guint),
g_variant_n_children(offsets_v));
g_variant_iter_init(&iter1, offsets_v);
while (g_variant_iter_next(&iter1, "u", &offset))
g_array_append_val(offsets, offset);
ret = gpiodglib_line_config_add_line_settings(config, offsets,
settings, &err);
if (!ret) {
g_critical("failed to add line settings: %s",
err->message);
return NULL;
}
}
values = g_array_sized_new(FALSE, TRUE, sizeof(gint),
g_variant_n_children(output_values_v));
g_variant_iter_init(&iter0, output_values_v);
while (g_variant_iter_next(&iter0, "i", &val))
g_array_append_val(values, val);
if (values->len > 0) {
ret = gpiodglib_line_config_set_output_values(config, values,
&err);
if (!ret) {
g_critical("failed to set output values: %s",
err->message);
return NULL;
}
}
return g_object_ref(config);
}
GpiodglibRequestConfig *gpiodbus_request_config_from_variant(GVariant *variant)
{
g_autoptr(GpiodglibRequestConfig) config = NULL;
GVariantIter iter;
GVariant *v;
gchar *k;
config = gpiodglib_request_config_new(NULL);
g_variant_iter_init(&iter, variant);
while (g_variant_iter_next(&iter, "{sv}", &k, &v)) {
g_autoptr(GVariant) val = v;
g_autofree gchar *key = k;
if (g_strcmp0(key, "consumer") == 0) {
gpiodglib_request_config_set_consumer(config,
g_variant_get_string(val, NULL));
} else if (g_strcmp0(key, "event-buffer-size") == 0) {
gpiodglib_request_config_set_event_buffer_size(config,
g_variant_get_uint32(val));
} else {
g_critical("invalid request config option received: '%s'",
key);
return NULL;
}
}
return g_object_ref(config);
}