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.
189 lines
5.5 KiB
C
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);
|
|
}
|