diff --git a/src/fbd-dev-led-multicolor.c b/src/fbd-dev-led-multicolor.c new file mode 100644 index 0000000..e0732aa --- /dev/null +++ b/src/fbd-dev-led-multicolor.c @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2023 Purism SPC + * SPDX-License-Identifier: GPL-3.0+ + * Author: Guido Günther + * + * See Documentation/ABI/testing/sysfs-class-led-trigger-pattern + */ + +#define G_LOG_DOMAIN "fbd-dev-led" + +#include "fbd-dev-led-priv.h" +#include "fbd-dev-led-multicolor.h" +#include "fbd-enums.h" +#include "fbd-udev.h" + +#include + +#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" + + +typedef struct _FbdDevLedMulticolor { + FbdDevLed parent; + + guint red_index; + guint green_index; + guint blue_index; +} FbdDevLedMulticolor; + + +G_DEFINE_TYPE (FbdDevLedMulticolor, fbd_dev_led_multicolor, FBD_TYPE_DEV_LED) + + +static gboolean +fbd_dev_led_probe_multicolor (FbdDevLed *led, GError **error) +{ + g_autoptr (GError) err = NULL; + FbdDevLedMulticolor *self = FBD_DEV_LED_MULTICOLOR (led); + 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 multicololor 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_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) { + self->red_index = counter; + counter++; + } else if (g_strcmp0 (index[i], LED_MULTI_INDEX_GREEN) == 0) { + self->green_index = counter; + counter++; + } else if (g_strcmp0 (index[i], LED_MULTI_INDEX_BLUE) == 0) { + self->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_start_periodic_multicolor (FbdDevLed *led, + FbdFeedbackLedColor color, + guint max_brightness_percentage, + guint freq) +{ + FbdDevLedMulticolor *self = FBD_DEV_LED_MULTICOLOR (led); + GUdevDevice *dev = fbd_dev_led_get_device (led); + g_autofree char *intensity = NULL; + g_autoptr (GError) err = NULL; + g_autofree gchar *str = 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[self->red_index] = max_brightness; + colors[self->green_index] = max_brightness; + colors[self->blue_index] = max_brightness; + break; + case FBD_FEEDBACK_LED_COLOR_RED: + colors[self->red_index] = max_brightness; + colors[self->green_index] = 0; + colors[self->blue_index] = 0; + break; + case FBD_FEEDBACK_LED_COLOR_GREEN: + colors[self->red_index] = 0; + colors[self->green_index] = max_brightness; + colors[self->blue_index] = 0; + break; + case FBD_FEEDBACK_LED_COLOR_BLUE: + colors[self->red_index] = 0; + colors[self->green_index] = 0; + colors[self->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; + } + + /* Chain up to parent class to set the pattern */ + return FBD_DEV_LED_CLASS (fbd_dev_led_multicolor_parent_class)->start_periodic ( + led, color, max_brightness_percentage, freq); +} + + +static gboolean +fbd_dev_led_has_color_multicolor (FbdDevLed *led, FbdFeedbackLedColor color) +{ + return color == FBD_FEEDBACK_LED_COLOR_RGB; +} + + +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_probe_multicolor; + fbd_dev_led_class->start_periodic = fbd_dev_led_start_periodic_multicolor; + fbd_dev_led_class->has_color = fbd_dev_led_has_color_multicolor; +} + + +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); +} diff --git a/src/fbd-dev-led-multicolor.h b/src/fbd-dev-led-multicolor.h new file mode 100644 index 0000000..79f17fe --- /dev/null +++ b/src/fbd-dev-led-multicolor.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2023 Purism SPC + * + * SPDX-License-Identifier: GPL-3.0+ + */ +#pragma once + +#include "fbd-dev-led.h" + +G_BEGIN_DECLS + +#define FBD_TYPE_DEV_LED_MULTICOLOR fbd_dev_led_multicolor_get_type() + +G_DECLARE_FINAL_TYPE (FbdDevLedMulticolor, fbd_dev_led_multicolor, FBD, DEV_LED_MULTICOLOR, FbdDevLed) + +FbdDevLed *fbd_dev_led_multicolor_new (GUdevDevice *dev, GError **err); + +G_END_DECLS diff --git a/src/fbd-dev-led-priv.h b/src/fbd-dev-led-priv.h new file mode 100644 index 0000000..32b34e1 --- /dev/null +++ b/src/fbd-dev-led-priv.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 Purism SPC + * + * SPDX-License-Identifier: GPL-3.0+ + */ +#pragma once + +#include "fbd-dev-led.h" +#include "fbd-feedback-led.h" + +#include +#include + +G_BEGIN_DECLS + +#define LED_MAX_BRIGHTNESS_ATTR "max_brightness" + +GUdevDevice *fbd_dev_led_get_device (FbdDevLed *led); +void fbd_dev_led_set_max_brightness (FbdDevLed *led, guint max_brightness); +void fbd_dev_led_set_color (FbdDevLed *led, FbdFeedbackLedColor color); + +G_END_DECLS diff --git a/src/fbd-dev-led.c b/src/fbd-dev-led.c index 25705c4..a68b3da 100644 --- a/src/fbd-dev-led.c +++ b/src/fbd-dev-led.c @@ -9,13 +9,13 @@ #define G_LOG_DOMAIN "fbd-dev-led" #include "fbd-dev-led.h" +#include "fbd-dev-led-priv.h" #include "fbd-enums.h" #include "fbd-udev.h" #include #define LED_BRIGHTNESS_ATTR "brightness" -#define LED_MAX_BRIGHTNESS_ATTR "max_brightness" #define LED_MULTI_INDEX_ATTR "multi_index" #define LED_MULTI_INDEX_RED "red" #define LED_MULTI_INDEX_GREEN "green" @@ -33,9 +33,6 @@ static GParamSpec *props[PROP_LAST_PROP]; typedef struct _FbdDevLedPrivate { GUdevDevice *dev; guint max_brightness; - guint red_index; - guint green_index; - guint blue_index; /* * We just use the colors from the feedback until we * do rgb mixing, etc @@ -61,11 +58,9 @@ fbd_dev_led_probe_default (FbdDevLed *led, GError **error) /* We don't know anything about diffusors that can combine different color LEDSs so go with fixed colors until the kernel gives us enough information */ - for (int i = 0; i <= FBD_FEEDBACK_LED_COLOR_LAST; i++) { + for (int i = 0; i < FBD_FEEDBACK_LED_COLOR_RGB; i++) { g_autofree char *color = NULL; g_autofree char *enum_name = NULL; - const gchar * const *index; - guint counter = 0; gchar *c; enum_name = g_enum_to_string (FBD_TYPE_FEEDBACK_LED_COLOR, i); @@ -74,34 +69,15 @@ fbd_dev_led_probe_default (FbdDevLed *led, GError **error) if (g_strstr_len (name, -1, color)) { g_autoptr (GError) err = NULL; guint brightness = g_udev_device_get_sysfs_attr_as_int (priv->dev, LED_MAX_BRIGHTNESS_ATTR); - index = g_udev_device_get_sysfs_attr_as_strv (priv->dev, LED_MULTI_INDEX_ATTR); if (!brightness) continue; priv->color = i; - priv->max_brightness = brightness; - - if (index) { - for (int j = 0; j < g_strv_length ((gchar **) index); j++) { - g_debug ("Index: %s", index[j]); - if (g_strcmp0 (index[j], LED_MULTI_INDEX_RED) == 0) { - priv->red_index = counter; - counter++; - } else if (g_strcmp0 (index[j], LED_MULTI_INDEX_GREEN) == 0) { - priv->green_index = counter; - counter++; - } else if (g_strcmp0 (index[j], LED_MULTI_INDEX_BLUE) == 0) { - priv->blue_index = counter; - counter++; - } else { - g_warning ("Unsupport LED color index: %d %s", counter, index[j]); - } - } - } + fbd_dev_led_set_max_brightness (led, brightness); path = g_udev_device_get_sysfs_path (priv->dev); - g_debug ("LED at '%s' usable", path); + g_debug ("LED at '%s' usable for %s", path, color); success = TRUE; break; } @@ -133,43 +109,6 @@ fbd_dev_led_start_periodic_default (FbdDevLed *led, g_return_val_if_fail (FBD_IS_DEV_LED (led), FALSE); priv = fbd_dev_led_get_instance_private (led); - if (priv->color == FBD_FEEDBACK_LED_COLOR_RGB) { - g_autofree char *intensity = NULL; - guint colors[] = { 0, 0, 0 }; - switch (color) { - case FBD_FEEDBACK_LED_COLOR_WHITE: - colors[priv->red_index] = priv->max_brightness; - colors[priv->green_index] = priv->max_brightness; - colors[priv->blue_index] = priv->max_brightness; - break; - case FBD_FEEDBACK_LED_COLOR_RED: - colors[priv->red_index] = priv->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] = priv->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] = priv->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, priv->max_brightness); - success = fbd_udev_set_sysfs_path_attr_as_string (priv->dev, LED_MULTI_INTENSITY_ATTR, intensity, &err); - if (!success) { - g_warning ("Failed to set multi intensity: %s", err->message); - g_clear_error (&err); - } - } - max = priv->max_brightness * (max_brightness_percentage / 100.0); /* ms mHz T/2 */ t = 1000.0 * 1000.0 / freq / 2.0; @@ -192,7 +131,7 @@ fbd_dev_led_has_color_default (FbdDevLed *led, FbdFeedbackLedColor color) g_return_val_if_fail (FBD_IS_DEV_LED (led), FALSE); priv = fbd_dev_led_get_instance_private (led); - return (priv->color == FBD_FEEDBACK_LED_COLOR_RGB || priv->color == color); + return priv->color == color; } @@ -343,3 +282,52 @@ fbd_dev_led_has_color (FbdDevLed *led, FbdFeedbackLedColor color) return fbd_dev_led_class->has_color (led, color); } + + +guint +fbd_dev_led_get_max_brightness (FbdDevLed *led) +{ + FbdDevLedPrivate *priv; + + g_return_val_if_fail (FBD_IS_DEV_LED (led), 0); + priv = fbd_dev_led_get_instance_private (led); + + return priv->max_brightness; +} + +/* Functions for derived classes */ + +GUdevDevice * +fbd_dev_led_get_device (FbdDevLed *led) +{ + FbdDevLedPrivate *priv; + + g_return_val_if_fail (FBD_IS_DEV_LED (led), NULL); + priv = fbd_dev_led_get_instance_private (led); + + return priv->dev; +} + + +void +fbd_dev_led_set_color (FbdDevLed *led, FbdFeedbackLedColor color) +{ + FbdDevLedPrivate *priv; + + g_return_if_fail (FBD_IS_DEV_LED (led)); + priv = fbd_dev_led_get_instance_private (led); + + priv->color = color; +} + + +void +fbd_dev_led_set_max_brightness (FbdDevLed *led, guint max_brightness) +{ + FbdDevLedPrivate *priv; + + g_return_if_fail (FBD_IS_DEV_LED (led)); + priv = fbd_dev_led_get_instance_private (led); + + priv->max_brightness = max_brightness; +} diff --git a/src/fbd-dev-led.h b/src/fbd-dev-led.h index cddfad9..139deec 100644 --- a/src/fbd-dev-led.h +++ b/src/fbd-dev-led.h @@ -19,6 +19,7 @@ G_DECLARE_DERIVABLE_TYPE (FbdDevLed, fbd_dev_led, FBD, DEV_LED, GObject) FbdDevLed *fbd_dev_led_new (GUdevDevice *dev, GError **err); gboolean fbd_dev_led_set_brightness (FbdDevLed *led, guint brightness); +guint fbd_dev_led_get_max_brightness (FbdDevLed *led); gboolean fbd_dev_led_start_periodic (FbdDevLed *led, FbdFeedbackLedColor color, guint max_brightness_percentage, diff --git a/src/fbd-dev-leds.c b/src/fbd-dev-leds.c index 75a6c7a..34b4314 100644 --- a/src/fbd-dev-leds.c +++ b/src/fbd-dev-leds.c @@ -11,6 +11,7 @@ #include "fbd.h" #include "fbd-enums.h" #include "fbd-dev-led.h" +#include "fbd-dev-led-multicolor.h" #include "fbd-dev-leds.h" #include "fbd-feedback-led.h" #include "fbd-udev.h" @@ -78,12 +79,18 @@ initable_init (GInitable *initable, continue; } - led = fbd_dev_led_new (dev, &err); + /* Try multicolor first, fall back to single color */ + led = fbd_dev_led_multicolor_new (dev, &err); + if (led == NULL) { + g_debug ("Led not usable as multicolor: '%s'", err->message); + led = fbd_dev_led_new (dev, &err); + if (led == NULL) + g_debug ("Led not usable as single color: '%s'", err->message); + } + if (led) { self->leds = g_slist_append (self->leds, led); found = TRUE; - } else { - g_debug ("Led not usable: %s", err->message); } } diff --git a/src/meson.build b/src/meson.build index 510306f..8d2dcdb 100644 --- a/src/meson.build +++ b/src/meson.build @@ -16,6 +16,7 @@ sources = [ 'fbd-dev-vibra.c', 'fbd-dev-sound.c', 'fbd-dev-led.c', + 'fbd-dev-led-multicolor.c', 'fbd-dev-leds.c', 'fbd-event.c', 'fbd-feedback-base.c',