Merge branch 'led-class' into 'main'
leds: Separate single color from multicolor leds See merge request Librem5/feedbackd!103
This commit is contained in:
@@ -0,0 +1,177 @@
|
|||||||
|
/*
|
||||||
|
* 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"
|
||||||
|
|
||||||
|
#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"
|
||||||
|
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -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 <gudev/gudev.h>
|
||||||
|
#include <glib-object.h>
|
||||||
|
|
||||||
|
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
|
||||||
@@ -0,0 +1,333 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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"
|
||||||
|
|
||||||
|
#include "fbd-dev-led.h"
|
||||||
|
#include "fbd-dev-led-priv.h"
|
||||||
|
#include "fbd-enums.h"
|
||||||
|
#include "fbd-udev.h"
|
||||||
|
|
||||||
|
#include <gio/gio.h>
|
||||||
|
|
||||||
|
#define LED_BRIGHTNESS_ATTR "brightness"
|
||||||
|
#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"
|
||||||
|
#define LED_PATTERN_ATTR "pattern"
|
||||||
|
|
||||||
|
enum {
|
||||||
|
PROP_0,
|
||||||
|
PROP_DEV,
|
||||||
|
PROP_LAST_PROP
|
||||||
|
};
|
||||||
|
static GParamSpec *props[PROP_LAST_PROP];
|
||||||
|
|
||||||
|
typedef struct _FbdDevLedPrivate {
|
||||||
|
GUdevDevice *dev;
|
||||||
|
guint max_brightness;
|
||||||
|
/*
|
||||||
|
* We just use the colors from the feedback until we
|
||||||
|
* do rgb mixing, etc
|
||||||
|
*/
|
||||||
|
FbdFeedbackLedColor color;
|
||||||
|
} FbdDevLedPrivate;
|
||||||
|
|
||||||
|
|
||||||
|
static void initable_iface_init (GInitableIface *iface);
|
||||||
|
|
||||||
|
G_DEFINE_TYPE_WITH_CODE (FbdDevLed, fbd_dev_led, G_TYPE_OBJECT,
|
||||||
|
G_ADD_PRIVATE (FbdDevLed)
|
||||||
|
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init))
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fbd_dev_led_probe_default (FbdDevLed *led, GError **error)
|
||||||
|
{
|
||||||
|
FbdDevLedPrivate *priv = fbd_dev_led_get_instance_private (led);
|
||||||
|
const gchar *name, *path;
|
||||||
|
gboolean success;
|
||||||
|
|
||||||
|
name = g_udev_device_get_name (priv->dev);
|
||||||
|
/* 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_RGB; i++) {
|
||||||
|
g_autofree char *color = NULL;
|
||||||
|
g_autofree char *enum_name = NULL;
|
||||||
|
gchar *c;
|
||||||
|
|
||||||
|
enum_name = g_enum_to_string (FBD_TYPE_FEEDBACK_LED_COLOR, i);
|
||||||
|
c = strrchr (enum_name, '_');
|
||||||
|
color = g_ascii_strdown (c+1, -1);
|
||||||
|
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);
|
||||||
|
|
||||||
|
if (!brightness)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
priv->color = i;
|
||||||
|
fbd_dev_led_set_max_brightness (led, brightness);
|
||||||
|
|
||||||
|
path = g_udev_device_get_sysfs_path (priv->dev);
|
||||||
|
g_debug ("LED at '%s' usable for %s", path, color);
|
||||||
|
success = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
g_set_error (error,
|
||||||
|
G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
||||||
|
"%s not usable as RBG LED", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fbd_dev_led_start_periodic_default (FbdDevLed *led,
|
||||||
|
FbdFeedbackLedColor color,
|
||||||
|
guint max_brightness_percentage,
|
||||||
|
guint freq)
|
||||||
|
{
|
||||||
|
FbdDevLedPrivate *priv;
|
||||||
|
g_autoptr (GError) err = NULL;
|
||||||
|
g_autofree gchar *str = NULL;
|
||||||
|
gboolean success = FALSE;
|
||||||
|
gdouble max;
|
||||||
|
gdouble t;
|
||||||
|
|
||||||
|
g_return_val_if_fail (FBD_IS_DEV_LED (led), FALSE);
|
||||||
|
priv = fbd_dev_led_get_instance_private (led);
|
||||||
|
|
||||||
|
max = priv->max_brightness * (max_brightness_percentage / 100.0);
|
||||||
|
/* ms mHz T/2 */
|
||||||
|
t = 1000.0 * 1000.0 / freq / 2.0;
|
||||||
|
str = g_strdup_printf ("0 %d %d %d\n", (gint)t, (gint)max, (gint)t);
|
||||||
|
g_debug ("Freq %d mHz, Brightness: %d%%, Blink pattern: %s", freq, max_brightness_percentage, str);
|
||||||
|
|
||||||
|
success = fbd_udev_set_sysfs_path_attr_as_string (priv->dev, LED_PATTERN_ATTR, str, &err);
|
||||||
|
if (!success)
|
||||||
|
g_warning ("Failed to set led pattern: %s", err->message);
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fbd_dev_led_has_color_default (FbdDevLed *led, FbdFeedbackLedColor color)
|
||||||
|
{
|
||||||
|
FbdDevLedPrivate *priv;
|
||||||
|
|
||||||
|
g_return_val_if_fail (FBD_IS_DEV_LED (led), FALSE);
|
||||||
|
priv = fbd_dev_led_get_instance_private (led);
|
||||||
|
|
||||||
|
return priv->color == color;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
fbd_dev_led_set_property (GObject *object,
|
||||||
|
guint property_id,
|
||||||
|
const GValue *value,
|
||||||
|
GParamSpec *pspec)
|
||||||
|
{
|
||||||
|
FbdDevLed *led = FBD_DEV_LED (object);
|
||||||
|
FbdDevLedPrivate *priv = fbd_dev_led_get_instance_private (led);
|
||||||
|
|
||||||
|
switch (property_id) {
|
||||||
|
case PROP_DEV:
|
||||||
|
priv->dev = g_value_dup_object (value);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
fbd_dev_led_get_property (GObject *object,
|
||||||
|
guint property_id,
|
||||||
|
GValue *value,
|
||||||
|
GParamSpec *pspec)
|
||||||
|
{
|
||||||
|
FbdDevLed *led = FBD_DEV_LED (object);
|
||||||
|
FbdDevLedPrivate *priv = fbd_dev_led_get_instance_private (led);
|
||||||
|
|
||||||
|
switch (property_id) {
|
||||||
|
case PROP_DEV:
|
||||||
|
g_value_set_object (value, priv->dev);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
fbd_dev_led_finalize (GObject *object)
|
||||||
|
{
|
||||||
|
FbdDevLed *self = FBD_DEV_LED (object);
|
||||||
|
FbdDevLedPrivate *priv = fbd_dev_led_get_instance_private (self);
|
||||||
|
|
||||||
|
g_clear_object (&priv->dev);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (fbd_dev_led_parent_class)->finalize (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
initable_init (GInitable *initable,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
FbdDevLedClass *fbd_dev_led_class = FBD_DEV_LED_GET_CLASS (initable);
|
||||||
|
|
||||||
|
return fbd_dev_led_class->probe (FBD_DEV_LED (initable), error);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
initable_iface_init (GInitableIface *iface)
|
||||||
|
{
|
||||||
|
iface->init = initable_init;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
fbd_dev_led_class_init (FbdDevLedClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
FbdDevLedClass *fbd_dev_led_class = FBD_DEV_LED_CLASS (klass);
|
||||||
|
|
||||||
|
object_class->get_property = fbd_dev_led_get_property;
|
||||||
|
object_class->set_property = fbd_dev_led_set_property;
|
||||||
|
object_class->finalize = fbd_dev_led_finalize;
|
||||||
|
|
||||||
|
fbd_dev_led_class->probe = fbd_dev_led_probe_default;
|
||||||
|
fbd_dev_led_class->start_periodic = fbd_dev_led_start_periodic_default;
|
||||||
|
fbd_dev_led_class->has_color = fbd_dev_led_has_color_default;
|
||||||
|
|
||||||
|
props[PROP_DEV] =
|
||||||
|
g_param_spec_object ("dev", "", "",
|
||||||
|
G_UDEV_TYPE_DEVICE,
|
||||||
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
|
||||||
|
|
||||||
|
g_object_class_install_properties (object_class, PROP_LAST_PROP, props);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
fbd_dev_led_init (FbdDevLed *self)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FbdDevLed *
|
||||||
|
fbd_dev_led_new (GUdevDevice *dev, GError **err)
|
||||||
|
{
|
||||||
|
return g_initable_new (FBD_TYPE_DEV_LED, NULL, err, "dev", dev, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
fbd_dev_led_set_brightness (FbdDevLed *led, guint brightness)
|
||||||
|
{
|
||||||
|
FbdDevLedPrivate *priv;
|
||||||
|
g_autoptr (GError) err = NULL;
|
||||||
|
|
||||||
|
g_return_val_if_fail (FBD_IS_DEV_LED (led), FALSE);
|
||||||
|
priv = fbd_dev_led_get_instance_private (led);
|
||||||
|
|
||||||
|
if (!fbd_udev_set_sysfs_path_attr_as_int (priv->dev, LED_BRIGHTNESS_ATTR, brightness, &err)) {
|
||||||
|
g_warning ("Failed to setup brightness: %s", err->message);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
fbd_dev_led_start_periodic (FbdDevLed *led,
|
||||||
|
FbdFeedbackLedColor color,
|
||||||
|
guint max_brightness_percentage,
|
||||||
|
guint freq)
|
||||||
|
{
|
||||||
|
FbdDevLedClass *fbd_dev_led_class = FBD_DEV_LED_GET_CLASS (led);
|
||||||
|
|
||||||
|
g_return_val_if_fail (FBD_IS_DEV_LED (led), FALSE);
|
||||||
|
|
||||||
|
return fbd_dev_led_class->start_periodic (led, color, max_brightness_percentage, freq);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
fbd_dev_led_has_color (FbdDevLed *led, FbdFeedbackLedColor color)
|
||||||
|
{
|
||||||
|
FbdDevLedClass *fbd_dev_led_class = FBD_DEV_LED_GET_CLASS (led);
|
||||||
|
|
||||||
|
g_return_val_if_fail (FBD_IS_DEV_LED (led), FALSE);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 Purism SPC
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0+
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "fbd-feedback-led.h"
|
||||||
|
|
||||||
|
#include <gudev/gudev.h>
|
||||||
|
|
||||||
|
#include <glib-object.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define FBD_TYPE_DEV_LED fbd_dev_led_get_type()
|
||||||
|
|
||||||
|
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,
|
||||||
|
guint freq);
|
||||||
|
gboolean fbd_dev_led_has_color (FbdDevLed *led, FbdFeedbackLedColor color);
|
||||||
|
|
||||||
|
struct _FbdDevLedClass {
|
||||||
|
GObjectClass parent_class;
|
||||||
|
|
||||||
|
gboolean (*probe) (FbdDevLed *led, GError **error);
|
||||||
|
gboolean (*start_periodic) (FbdDevLed *led,
|
||||||
|
FbdFeedbackLedColor color,
|
||||||
|
guint max_brightness_percentage,
|
||||||
|
guint freq);
|
||||||
|
gboolean (*has_color) (FbdDevLed *led,
|
||||||
|
FbdFeedbackLedColor color);
|
||||||
|
};
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
+23
-155
@@ -10,44 +10,24 @@
|
|||||||
|
|
||||||
#include "fbd.h"
|
#include "fbd.h"
|
||||||
#include "fbd-enums.h"
|
#include "fbd-enums.h"
|
||||||
|
#include "fbd-dev-led.h"
|
||||||
|
#include "fbd-dev-led-multicolor.h"
|
||||||
#include "fbd-dev-leds.h"
|
#include "fbd-dev-leds.h"
|
||||||
#include "fbd-feedback-led.h"
|
#include "fbd-feedback-led.h"
|
||||||
#include "fbd-udev.h"
|
#include "fbd-udev.h"
|
||||||
|
|
||||||
#include <gio/gio.h>
|
#include <gio/gio.h>
|
||||||
|
|
||||||
|
#define LED_SUBSYSTEM "leds"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SECTION:fbd-dev-led
|
* FbdDevLeds:
|
||||||
* @short_description: LED device interface
|
*
|
||||||
* @Title: FbdDevLeds
|
* LED device interface
|
||||||
*
|
*
|
||||||
* #FbdDevLeds is used to interface with LEDS via sysfs
|
* #FbdDevLeds is used to interface with LEDS via sysfs
|
||||||
* It currently only supports one pattern per led at a time.
|
* It currently only supports one pattern per led at a time.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define LED_BRIGHTNESS_ATTR "brightness"
|
|
||||||
#define LED_MAX_BRIGHTNESS_ATTR "max_brightness"
|
|
||||||
#define LED_MULTI_INDEX_ATTR "multi_index"
|
|
||||||
#define LED_MULTI_INTENSITY_ATTR "multi_intensity"
|
|
||||||
#define LED_MULTI_INDEX_RED "red"
|
|
||||||
#define LED_MULTI_INDEX_GREEN "green"
|
|
||||||
#define LED_MULTI_INDEX_BLUE "blue"
|
|
||||||
#define LED_PATTERN_ATTR "pattern"
|
|
||||||
#define LED_SUBSYSTEM "leds"
|
|
||||||
|
|
||||||
typedef struct _FbdDevLed {
|
|
||||||
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
|
|
||||||
*/
|
|
||||||
FbdFeedbackLedColor color;
|
|
||||||
} FbdDevLed;
|
|
||||||
|
|
||||||
typedef struct _FbdDevLeds {
|
typedef struct _FbdDevLeds {
|
||||||
GObject parent;
|
GObject parent;
|
||||||
|
|
||||||
@@ -60,27 +40,6 @@ static void initable_iface_init (GInitableIface *iface);
|
|||||||
G_DEFINE_TYPE_WITH_CODE (FbdDevLeds, fbd_dev_leds, G_TYPE_OBJECT,
|
G_DEFINE_TYPE_WITH_CODE (FbdDevLeds, fbd_dev_leds, G_TYPE_OBJECT,
|
||||||
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init));
|
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init));
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
fbd_dev_led_free (FbdDevLed *led)
|
|
||||||
{
|
|
||||||
g_object_unref (led->dev);
|
|
||||||
g_free (led);
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
fbd_dev_led_set_brightness (FbdDevLed *led, guint brightness)
|
|
||||||
{
|
|
||||||
g_autoptr (GError) err = NULL;
|
|
||||||
|
|
||||||
if (!fbd_udev_set_sysfs_path_attr_as_int (led->dev, LED_BRIGHTNESS_ATTR, brightness, &err)) {
|
|
||||||
g_warning ("Failed to setup brightness: %s", err->message);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static FbdDevLed *
|
static FbdDevLed *
|
||||||
find_led_by_color (FbdDevLeds *self, FbdFeedbackLedColor color)
|
find_led_by_color (FbdDevLeds *self, FbdFeedbackLedColor color)
|
||||||
{
|
{
|
||||||
@@ -88,7 +47,7 @@ find_led_by_color (FbdDevLeds *self, FbdFeedbackLedColor color)
|
|||||||
|
|
||||||
for (GSList *l = self->leds; l != NULL; l = l->next) {
|
for (GSList *l = self->leds; l != NULL; l = l->next) {
|
||||||
FbdDevLed *led = l->data;
|
FbdDevLed *led = l->data;
|
||||||
if (led->color == FBD_FEEDBACK_LED_COLOR_RGB || led->color == color)
|
if (fbd_dev_led_has_color (led, color))
|
||||||
return led;
|
return led;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,66 +70,27 @@ initable_init (GInitable *initable,
|
|||||||
leds = g_udev_client_query_by_subsystem (self->client, LED_SUBSYSTEM);
|
leds = g_udev_client_query_by_subsystem (self->client, LED_SUBSYSTEM);
|
||||||
|
|
||||||
for (GList *l = leds; l != NULL; l = l->next) {
|
for (GList *l = leds; l != NULL; l = l->next) {
|
||||||
|
g_autoptr (GError) err = NULL;
|
||||||
GUdevDevice *dev = G_UDEV_DEVICE (l->data);
|
GUdevDevice *dev = G_UDEV_DEVICE (l->data);
|
||||||
const gchar *name, *path;
|
FbdDevLed *led;
|
||||||
FbdDevLed *led = NULL;
|
|
||||||
|
|
||||||
if (g_strcmp0 (g_udev_device_get_property (dev, FEEDBACKD_UDEV_ATTR),
|
if (g_strcmp0 (g_udev_device_get_property (dev, FEEDBACKD_UDEV_ATTR),
|
||||||
FEEDBACKD_UDEV_VAL_LED)) {
|
FEEDBACKD_UDEV_VAL_LED)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
name = g_udev_device_get_name (dev);
|
|
||||||
|
|
||||||
/* We don't know anything about diffusors that can combine different
|
/* Try multicolor first, fall back to single color */
|
||||||
color LEDSs so go with fixed colors until the kernel gives us
|
led = fbd_dev_led_multicolor_new (dev, &err);
|
||||||
enough information */
|
if (led == NULL) {
|
||||||
for (int i = 0; i <= FBD_FEEDBACK_LED_COLOR_LAST; i++) {
|
g_debug ("Led not usable as multicolor: '%s'", err->message);
|
||||||
g_autofree char *color = NULL;
|
led = fbd_dev_led_new (dev, &err);
|
||||||
g_autofree char *enum_name = NULL;
|
if (led == NULL)
|
||||||
const gchar * const *index;
|
g_debug ("Led not usable as single color: '%s'", err->message);
|
||||||
guint counter = 0;
|
}
|
||||||
gchar *c;
|
|
||||||
|
|
||||||
enum_name = g_enum_to_string (FBD_TYPE_FEEDBACK_LED_COLOR, i);
|
if (led) {
|
||||||
c = strrchr (enum_name, '_');
|
self->leds = g_slist_append (self->leds, led);
|
||||||
color = g_ascii_strdown (c+1, -1);
|
found = TRUE;
|
||||||
if (g_strstr_len (name, -1, color)) {
|
|
||||||
g_autoptr (GError) err = NULL;
|
|
||||||
guint brightness = g_udev_device_get_sysfs_attr_as_int (dev, LED_MAX_BRIGHTNESS_ATTR);
|
|
||||||
index = g_udev_device_get_sysfs_attr_as_strv (dev, LED_MULTI_INDEX_ATTR);
|
|
||||||
|
|
||||||
if (!brightness)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
led = g_malloc0 (sizeof(FbdDevLed));
|
|
||||||
led->dev = g_object_ref (dev);
|
|
||||||
led->color = i;
|
|
||||||
led->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) {
|
|
||||||
led->red_index = counter;
|
|
||||||
counter++;
|
|
||||||
} else if (g_strcmp0 (index[j], LED_MULTI_INDEX_GREEN) == 0) {
|
|
||||||
led->green_index = counter;
|
|
||||||
counter++;
|
|
||||||
} else if (g_strcmp0 (index[j], LED_MULTI_INDEX_BLUE) == 0) {
|
|
||||||
led->blue_index = counter;
|
|
||||||
counter++;
|
|
||||||
} else {
|
|
||||||
g_warning ("Unsupport LED color index: %d %s", counter, index[j]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
path = g_udev_device_get_sysfs_path (dev);
|
|
||||||
g_debug ("LED at '%s' usable", path);
|
|
||||||
self->leds = g_slist_append (self->leds, led);
|
|
||||||
found = TRUE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,7 +117,7 @@ fbd_dev_leds_dispose (GObject *object)
|
|||||||
FbdDevLeds *self = FBD_DEV_LEDS (object);
|
FbdDevLeds *self = FBD_DEV_LEDS (object);
|
||||||
|
|
||||||
g_clear_object (&self->client);
|
g_clear_object (&self->client);
|
||||||
g_slist_free_full (self->leds, (GDestroyNotify)fbd_dev_led_free);
|
g_slist_free_full (self->leds, (GDestroyNotify)g_object_unref);
|
||||||
self->leds = NULL;
|
self->leds = NULL;
|
||||||
|
|
||||||
G_OBJECT_CLASS (fbd_dev_leds_parent_class)->dispose (object);
|
G_OBJECT_CLASS (fbd_dev_leds_parent_class)->dispose (object);
|
||||||
@@ -239,65 +159,13 @@ fbd_dev_leds_start_periodic (FbdDevLeds *self, FbdFeedbackLedColor color,
|
|||||||
guint max_brightness_percentage, guint freq)
|
guint max_brightness_percentage, guint freq)
|
||||||
{
|
{
|
||||||
FbdDevLed *led;
|
FbdDevLed *led;
|
||||||
gdouble max;
|
|
||||||
gdouble t;
|
|
||||||
g_autofree gchar *str = NULL;
|
|
||||||
|
|
||||||
g_autoptr (GError) err = NULL;
|
|
||||||
gboolean success;
|
|
||||||
|
|
||||||
g_return_val_if_fail (FBD_IS_DEV_LEDS (self), FALSE);
|
g_return_val_if_fail (FBD_IS_DEV_LEDS (self), FALSE);
|
||||||
g_return_val_if_fail (max_brightness_percentage <= 100.0, FALSE);
|
g_return_val_if_fail (max_brightness_percentage <= 100.0, FALSE);
|
||||||
led = find_led_by_color (self, color);
|
led = find_led_by_color (self, color);
|
||||||
g_return_val_if_fail (led, FALSE);
|
g_return_val_if_fail (led, FALSE);
|
||||||
|
|
||||||
if (led->color == FBD_FEEDBACK_LED_COLOR_RGB) {
|
return fbd_dev_led_start_periodic (led, color, max_brightness_percentage, freq);
|
||||||
g_autofree char *intensity = NULL;
|
|
||||||
guint colors[] = { 0, 0, 0 };
|
|
||||||
switch (color) {
|
|
||||||
case FBD_FEEDBACK_LED_COLOR_WHITE:
|
|
||||||
colors[led->red_index] = led->max_brightness;
|
|
||||||
colors[led->green_index] = led->max_brightness;
|
|
||||||
colors[led->blue_index] = led->max_brightness;
|
|
||||||
break;
|
|
||||||
case FBD_FEEDBACK_LED_COLOR_RED:
|
|
||||||
colors[led->red_index] = led->max_brightness;
|
|
||||||
colors[led->green_index] = 0;
|
|
||||||
colors[led->blue_index] = 0;
|
|
||||||
break;
|
|
||||||
case FBD_FEEDBACK_LED_COLOR_GREEN:
|
|
||||||
colors[led->red_index] = 0;
|
|
||||||
colors[led->green_index] = led->max_brightness;
|
|
||||||
colors[led->blue_index] = 0;
|
|
||||||
break;
|
|
||||||
case FBD_FEEDBACK_LED_COLOR_BLUE:
|
|
||||||
colors[led->red_index] = 0;
|
|
||||||
colors[led->green_index] = 0;
|
|
||||||
colors[led->blue_index] = led->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, led->max_brightness);
|
|
||||||
success = fbd_udev_set_sysfs_path_attr_as_string (led->dev, LED_MULTI_INTENSITY_ATTR, intensity, &err);
|
|
||||||
if (!success) {
|
|
||||||
g_warning ("Failed to set multi intensity: %s", err->message);
|
|
||||||
g_clear_error (&err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
max = led->max_brightness * (max_brightness_percentage / 100.0);
|
|
||||||
/* ms mHz T/2 */
|
|
||||||
t = 1000.0 * 1000.0 / freq / 2.0;
|
|
||||||
str = g_strdup_printf ("0 %d %d %d\n", (gint)t, (gint)max, (gint)t);
|
|
||||||
g_debug ("Freq %d mHz, Brightness: %d%%, Blink pattern: %s", freq, max_brightness_percentage, str);
|
|
||||||
success = fbd_udev_set_sysfs_path_attr_as_string (led->dev, LED_PATTERN_ATTR, str, &err);
|
|
||||||
if (!success)
|
|
||||||
g_warning ("Failed to set led pattern: %s", err->message);
|
|
||||||
|
|
||||||
return success;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean
|
gboolean
|
||||||
|
|||||||
+1
-1
@@ -6,9 +6,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "fbd-feedback-led.h"
|
#include "fbd-feedback-led.h"
|
||||||
|
#include "fbd-udev.h"
|
||||||
|
|
||||||
#include <glib-object.h>
|
#include <glib-object.h>
|
||||||
#include <gudev/gudev.h>
|
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
|||||||
@@ -42,9 +42,9 @@ G_DEFINE_TYPE_WITH_CODE (FbdFeedbackTheme, fbd_feedback_theme, G_TYPE_OBJECT,
|
|||||||
|
|
||||||
static JsonNode *
|
static JsonNode *
|
||||||
fbd_theme_serializable_serialize_property (JsonSerializable *serializable,
|
fbd_theme_serializable_serialize_property (JsonSerializable *serializable,
|
||||||
const gchar *property_name,
|
const gchar *property_name,
|
||||||
const GValue *value,
|
const GValue *value,
|
||||||
GParamSpec *pspec)
|
GParamSpec *pspec)
|
||||||
{
|
{
|
||||||
FbdFeedbackTheme *self = FBD_FEEDBACK_THEME (serializable);
|
FbdFeedbackTheme *self = FBD_FEEDBACK_THEME (serializable);
|
||||||
JsonNode *node = NULL;
|
JsonNode *node = NULL;
|
||||||
@@ -71,10 +71,10 @@ fbd_theme_serializable_serialize_property (JsonSerializable *serializable,
|
|||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
fbd_theme_serializable_deserialize_property (JsonSerializable *serializable,
|
fbd_theme_serializable_deserialize_property (JsonSerializable *serializable,
|
||||||
const gchar *property_name,
|
const gchar *property_name,
|
||||||
GValue *value,
|
GValue *value,
|
||||||
GParamSpec *pspec,
|
GParamSpec *pspec,
|
||||||
JsonNode *property_node)
|
JsonNode *property_node)
|
||||||
{
|
{
|
||||||
if (g_strcmp0 (property_name, "profiles") == 0) {
|
if (g_strcmp0 (property_name, "profiles") == 0) {
|
||||||
if (JSON_NODE_TYPE (property_node) == JSON_NODE_NULL) {
|
if (JSON_NODE_TYPE (property_node) == JSON_NODE_NULL) {
|
||||||
@@ -117,7 +117,7 @@ fbd_theme_serializable_deserialize_property (JsonSerializable *serializable,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
fbd_feedback_theme_set_property (GObject *object,
|
fbd_feedback_theme_set_property (GObject *object,
|
||||||
guint property_id,
|
guint property_id,
|
||||||
const GValue *value,
|
const GValue *value,
|
||||||
GParamSpec *pspec)
|
GParamSpec *pspec)
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ sources = [
|
|||||||
'fbd-error.c',
|
'fbd-error.c',
|
||||||
'fbd-dev-vibra.c',
|
'fbd-dev-vibra.c',
|
||||||
'fbd-dev-sound.c',
|
'fbd-dev-sound.c',
|
||||||
|
'fbd-dev-led.c',
|
||||||
|
'fbd-dev-led-multicolor.c',
|
||||||
'fbd-dev-leds.c',
|
'fbd-dev-leds.c',
|
||||||
'fbd-event.c',
|
'fbd-event.c',
|
||||||
'fbd-feedback-base.c',
|
'fbd-feedback-base.c',
|
||||||
|
|||||||
Reference in New Issue
Block a user