Files
feedbackd/src/fbd-dev-led-multicolor.c
T
Dylan Van Assche 22e4c33606 fbd-dev-led-multicolor: Support red, green, blue and white LED colors
Provide our own supports_color method to specify which colors are
actually implemented by the driver. RGB LEDs could provide different
colors as well such as orange, purple, etc. but those are not supported
yet.
2024-06-09 11:21:20 +02:00

189 lines
5.5 KiB
C

/*
* Copyright (C) 2023 Purism SPC
* SPDX-License-Identifier: GPL-3.0+
* Author: Guido Günther <agx@sigxcpu.org>
*
* See Documentation/ABI/testing/sysfs-class-led-trigger-pattern
*/
#define G_LOG_DOMAIN "fbd-dev-led-multicolor"
#include "fbd-dev-led-priv.h"
#include "fbd-dev-led-multicolor.h"
#include "fbd-enums.h"
#include "fbd-udev.h"
#include <gio/gio.h>
#define LED_MULTI_INDEX_ATTR "multi_index"
#define LED_MULTI_INDEX_RED "red"
#define LED_MULTI_INDEX_GREEN "green"
#define LED_MULTI_INDEX_BLUE "blue"
#define LED_MULTI_INTENSITY_ATTR "multi_intensity"
/**
* fbd-dev-led-multicolor:
*
* A multicolor led using the `multi_intensitiy` sysfs attribute
*/
typedef struct _FbdDevLedMulticolorPrivate {
guint red_index;
guint green_index;
guint blue_index;
} FbdDevLedMulticolorPrivate;
G_DEFINE_TYPE_WITH_PRIVATE (FbdDevLedMulticolor, fbd_dev_led_multicolor, FBD_TYPE_DEV_LED)
static gboolean
fbd_dev_led_multicolor_probe (FbdDevLed *led, GError **error)
{
FbdDevLedMulticolor *self = FBD_DEV_LED_MULTICOLOR (led);
FbdDevLedMulticolorPrivate *priv = fbd_dev_led_multicolor_get_instance_private (self);
GUdevDevice *dev = fbd_dev_led_get_device (led);
const gchar *name, *path;
const gchar * const *index;
guint counter = 0;
guint max_brightness;
name = g_udev_device_get_name (dev);
index = g_udev_device_get_sysfs_attr_as_strv (dev, LED_MULTI_INDEX_ATTR);
if (index == NULL) {
g_set_error (error,
G_FILE_ERROR, G_FILE_ERROR_FAILED,
"%s is no multicolor LED", name);
return FALSE;
}
max_brightness = g_udev_device_get_sysfs_attr_as_int (dev, LED_MAX_BRIGHTNESS_ATTR);
if (!max_brightness) {
g_set_error (error,
G_FILE_ERROR, G_FILE_ERROR_FAILED,
"%s has no max_brightness", name);
return FALSE;
}
fbd_dev_led_set_max_brightness (led, max_brightness);
fbd_dev_led_set_supported_color (led, FBD_FEEDBACK_LED_COLOR_RGB);
for (int i = 0; index[i] != NULL; i++) {
g_debug ("Index: %s", index[i]);
if (g_strcmp0 (index[i], LED_MULTI_INDEX_RED) == 0) {
priv->red_index = counter;
counter++;
} else if (g_strcmp0 (index[i], LED_MULTI_INDEX_GREEN) == 0) {
priv->green_index = counter;
counter++;
} else if (g_strcmp0 (index[i], LED_MULTI_INDEX_BLUE) == 0) {
priv->blue_index = counter;
counter++;
} else {
g_warning ("Unsupport LED color index: %d %s", counter, index[i]);
}
}
path = g_udev_device_get_sysfs_path (dev);
g_debug ("LED at '%s' usable as multicolor", path);
return TRUE;
}
static gboolean
fbd_dev_led_multicolor_set_color (FbdDevLed *led,
FbdFeedbackLedColor color)
{
FbdDevLedMulticolor *self = FBD_DEV_LED_MULTICOLOR (led);
FbdDevLedMulticolorPrivate *priv = fbd_dev_led_multicolor_get_instance_private (self);
GUdevDevice *dev = fbd_dev_led_get_device (led);
g_autofree char *intensity = NULL;
g_autoptr (GError) err = NULL;
gboolean success = FALSE;
guint max_brightness;
guint colors[] = { 0, 0, 0 };
max_brightness = fbd_dev_led_get_max_brightness (led);
switch (color) {
case FBD_FEEDBACK_LED_COLOR_WHITE:
colors[priv->red_index] = max_brightness;
colors[priv->green_index] = max_brightness;
colors[priv->blue_index] = max_brightness;
break;
case FBD_FEEDBACK_LED_COLOR_RED:
colors[priv->red_index] = max_brightness;
colors[priv->green_index] = 0;
colors[priv->blue_index] = 0;
break;
case FBD_FEEDBACK_LED_COLOR_GREEN:
colors[priv->red_index] = 0;
colors[priv->green_index] = max_brightness;
colors[priv->blue_index] = 0;
break;
case FBD_FEEDBACK_LED_COLOR_BLUE:
colors[priv->red_index] = 0;
colors[priv->green_index] = 0;
colors[priv->blue_index] = max_brightness;
break;
default:
g_warning("Unhandled color: %d\n", color);
return FALSE;
}
intensity = g_strdup_printf ("%d %d %d\n", colors[0], colors[1], colors[2]);
fbd_dev_led_set_brightness (led, max_brightness);
success = fbd_udev_set_sysfs_path_attr_as_string (dev,
LED_MULTI_INTENSITY_ATTR,
intensity,
&err);
if (!success) {
g_warning ("Failed to set multi intensity: %s", err->message);
return FALSE;
}
return TRUE;
}
static gboolean
fbd_dev_led_multicolor_supports_color (FbdDevLed *led,
FbdFeedbackLedColor color)
{
switch(color) {
case FBD_FEEDBACK_LED_COLOR_WHITE:
return TRUE;
case FBD_FEEDBACK_LED_COLOR_RED:
return TRUE;
case FBD_FEEDBACK_LED_COLOR_GREEN:
return TRUE;
case FBD_FEEDBACK_LED_COLOR_BLUE:
return TRUE;
default:
g_warning ("Color unsupported: %d", color);
return FALSE;
}
}
static void
fbd_dev_led_multicolor_class_init (FbdDevLedMulticolorClass *klass)
{
FbdDevLedClass *fbd_dev_led_class = FBD_DEV_LED_CLASS (klass);
fbd_dev_led_class->probe = fbd_dev_led_multicolor_probe;
fbd_dev_led_class->set_color = fbd_dev_led_multicolor_set_color;
fbd_dev_led_class->supports_color = fbd_dev_led_multicolor_supports_color;
}
static void
fbd_dev_led_multicolor_init (FbdDevLedMulticolor *self)
{
}
FbdDevLed *
fbd_dev_led_multicolor_new (GUdevDevice *dev, GError **err)
{
return g_initable_new (FBD_TYPE_DEV_LED_MULTICOLOR, NULL, err, "dev", dev, NULL);
}