From: "Manuel Schönlaub" <manuel.schoenlaub@gmail.com>
To: manuel.schoenlaub@gmail.com
Cc: lains@riseup.net, jikos@kernel.org,
benjamin.tissoires@redhat.com, linux-input@vger.kernel.org,
linux-kernel@vger.kernel.org
Subject: [PATCH] HID: logitech-hidpp: support Color LED feature (8071).
Date: Tue, 8 Mar 2022 16:50:57 -0700 [thread overview]
Message-ID: <Yifr4etBFPu1a2Ct@hermes> (raw)
The HID++ protocol allows to set multicolor (RGB) to a static color.
Multiple of such LED zones per device are supported.
This patch exports said LEDs so that they can be set from userspace.
Signed-off-by: Manuel Schönlaub <manuel.schoenlaub@gmail.com>
---
drivers/hid/hid-logitech-hidpp.c | 188 +++++++++++++++++++++++++++++++
1 file changed, 188 insertions(+)
diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index 81de88ab2..0b6c9c4b8 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -24,6 +24,8 @@
#include <linux/atomic.h>
#include <linux/fixp-arith.h>
#include <asm/unaligned.h>
+#include <linux/leds.h>
+#include <linux/led-class-multicolor.h>
#include "usbhid/usbhid.h"
#include "hid-ids.h"
@@ -96,6 +98,7 @@ MODULE_PARM_DESC(disable_tap_to_click,
#define HIDPP_CAPABILITY_BATTERY_VOLTAGE BIT(4)
#define HIDPP_CAPABILITY_BATTERY_PERCENTAGE BIT(5)
#define HIDPP_CAPABILITY_UNIFIED_BATTERY BIT(6)
+#define HIDPP_CAPABILITY_HIDPP20_COLORED_LEDS BIT(7)
#define lg_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c))
@@ -159,6 +162,12 @@ struct hidpp_battery {
u8 supported_levels_1004;
};
+struct hidpp_leds {
+ u8 feature_index;
+ u8 count;
+ struct led_classdev_mc leds[];
+};
+
/**
* struct hidpp_scroll_counter - Utility class for processing high-resolution
* scroll events.
@@ -201,6 +210,7 @@ struct hidpp_device {
u8 supported_reports;
struct hidpp_battery battery;
+ struct hidpp_leds *leds;
struct hidpp_scroll_counter vertical_wheel_counter;
u8 wireless_feature_index;
@@ -1708,6 +1718,134 @@ static int hidpp_battery_get_property(struct power_supply *psy,
return ret;
}
+/* -------------------------------------------------------------------------- */
+/* 0x8070: Color LED effect */
+/* -------------------------------------------------------------------------- */
+
+#define HIDPP_PAGE_LED_EFFECTS 0x8070
+
+#define CMD_COLOR_LED_EFFECTS_GET_INFO 0x00
+
+#define CMD_COLOR_LED_EFFECTS_SET_ZONE_STATE 0x31
+
+static int hidpp20_color_led_effect_get_info(struct hidpp_device *hidpp_dev,
+ u8 feature_index, u8 *count)
+{
+ struct hidpp_report response;
+ int ret;
+ u8 *params = (u8 *)response.fap.params;
+
+ ret = hidpp_send_fap_command_sync(hidpp_dev, feature_index,
+ CMD_COLOR_LED_EFFECTS_GET_INFO,
+ NULL, 0, &response);
+
+ if (ret > 0) {
+ hid_err(hidpp_dev->hid_dev,
+ "%s: received protocol error 0x%02x\n",
+ __func__, ret);
+ return -EPROTO;
+ }
+ if (ret)
+ return ret;
+
+ *count = params[0];
+ return 0;
+}
+
+static int hidpp20_color_effect_set(struct hidpp_device *hidpp_dev,
+ u8 zone, bool enabled,
+ u8 r, u8 b, u8 g)
+{
+ int ret;
+ u8 params[5];
+ struct hidpp_report response;
+
+ params[0] = zone;
+ params[1] = enabled ? 1 : 0;
+ params[2] = r;
+ params[3] = g;
+ params[4] = b;
+
+ ret = hidpp_send_fap_command_sync(hidpp_dev,
+ hidpp_dev->leds->feature_index,
+ CMD_COLOR_LED_EFFECTS_SET_ZONE_STATE,
+ params, sizeof(params), &response);
+
+ if (ret)
+ return ret;
+ return 0;
+}
+
+static int hidpp_set_brightness(struct led_classdev *cdev,
+ enum led_brightness brightness)
+{
+ int n;
+ struct device *dev = cdev->dev->parent;
+ struct hid_device *hid = to_hid_device(dev);
+ struct hidpp_device *hidpp = hid_get_drvdata(hid);
+
+ struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(cdev);
+ u8 red, green, blue;
+
+ led_mc_calc_color_components(mc_cdev, brightness);
+ red = mc_cdev->subled_info[0].brightness;
+ green = mc_cdev->subled_info[1].brightness;
+ blue = mc_cdev->subled_info[2].brightness;
+
+ for (n = 0; n < hidpp->leds->count; n++) {
+ if (cdev == &hidpp->leds->leds[n].led_cdev) {
+ return hidpp20_color_effect_set(hidpp, n,
+ brightness > 0,
+ red, green, blue);
+ }
+ }
+
+ return LED_OFF;
+}
+
+static int hidpp_mc_led_register(struct hidpp_device *hidpp_dev,
+ struct led_classdev_mc *mc_dev,
+ int zone)
+{
+ struct hid_device *hdev = hidpp_dev->hid_dev;
+ struct mc_subled *mc_led_info;
+ struct led_classdev *cdev;
+ int ret;
+
+ mc_led_info = devm_kmalloc_array(&hdev->dev, 3,
+ sizeof(*mc_led_info),
+ GFP_KERNEL | __GFP_ZERO);
+ if (!mc_led_info)
+ return -ENOMEM;
+
+ mc_led_info[0].color_index = LED_COLOR_ID_RED;
+ mc_led_info[1].color_index = LED_COLOR_ID_GREEN;
+ mc_led_info[2].color_index = LED_COLOR_ID_BLUE;
+
+ mc_dev->subled_info = mc_led_info;
+ mc_dev->num_colors = 3;
+
+ cdev = &mc_dev->led_cdev;
+ cdev->name = devm_kasprintf(&hdev->dev, GFP_KERNEL,
+ "%s:rgb:indicator-%d", hdev->uniq, zone);
+
+ if (!cdev->name)
+ return -ENOMEM;
+
+ cdev->brightness = 0;
+ cdev->max_brightness = 255;
+ cdev->flags |= LED_CORE_SUSPENDRESUME;
+ cdev->brightness_set_blocking = hidpp_set_brightness;
+
+ ret = devm_led_classdev_multicolor_register(&hdev->dev, mc_dev);
+ if (ret < 0) {
+ hid_err(hdev, "Cannot register multicolor LED device: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
/* -------------------------------------------------------------------------- */
/* 0x1d4b: Wireless device status */
/* -------------------------------------------------------------------------- */
@@ -3699,6 +3837,54 @@ static int hidpp_event(struct hid_device *hdev, struct hid_field *field,
return 1;
}
+static int hidpp_initialize_leds(struct hidpp_device *hidpp_dev)
+{
+ u8 count;
+ u8 feature_index;
+ u8 feature_type;
+ int i;
+ int ret;
+ struct hid_device *hdev;
+
+ hdev = hidpp_dev->hid_dev;
+ if (hidpp_dev->leds)
+ return 0;
+ if (hidpp_dev->protocol_major >= 2) {
+ ret = hidpp_root_get_feature(hidpp_dev,
+ HIDPP_PAGE_LED_EFFECTS,
+ &feature_index,
+ &feature_type);
+ if (ret)
+ return ret;
+
+ ret = hidpp20_color_led_effect_get_info(hidpp_dev, feature_index, &count);
+ if (ret)
+ return ret;
+
+ hidpp_dev->capabilities |= HIDPP_CAPABILITY_HIDPP20_COLORED_LEDS;
+ hidpp_dev->leds = devm_kzalloc(&hdev->dev,
+ struct_size(hidpp_dev->leds, leds, count),
+ GFP_KERNEL);
+
+ if (!hidpp_dev->leds)
+ return -ENOMEM;
+
+ hidpp_dev->leds->feature_index = feature_index;
+ hidpp_dev->leds->count = count;
+
+ for (i = 0; i < count; i++) {
+ ret = hidpp_mc_led_register(hidpp_dev, &hidpp_dev->leds->leds[i], i);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+
+ } else {
+ return 0;
+ }
+}
+
static int hidpp_initialize_battery(struct hidpp_device *hidpp)
{
static atomic_t battery_no = ATOMIC_INIT(0);
@@ -3943,6 +4129,8 @@ static void hidpp_connect_event(struct hidpp_device *hidpp)
if (hidpp->battery.ps)
power_supply_changed(hidpp->battery.ps);
+ hidpp_initialize_leds(hidpp);
+
if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL)
hi_res_scroll_enable(hidpp);
--
2.30.2
next reply other threads:[~2022-03-09 1:15 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-03-08 23:50 Manuel Schönlaub [this message]
2022-03-23 21:04 ` [PATCH] HID: logitech-hidpp: support Color LED feature (8071) Pavel Machek
2022-03-24 2:21 ` Manuel Schönlaub
2022-05-05 8:25 ` Pavel Machek
2022-03-23 21:22 ` Filipe Laíns
2022-03-23 22:24 ` Bastien Nocera
2022-03-24 3:28 ` Manuel Schönlaub
2022-03-24 9:32 ` Bastien Nocera
2022-03-24 16:10 ` Manuel Schönlaub
2022-05-05 8:29 ` Pavel Machek
2022-03-24 3:34 ` Manuel Schönlaub
2022-03-24 19:54 ` Benjamin Tissoires
2022-03-25 1:29 ` Manuel Schönlaub
2022-05-05 8:32 ` Pavel Machek
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=Yifr4etBFPu1a2Ct@hermes \
--to=manuel.schoenlaub@gmail.com \
--cc=benjamin.tissoires@redhat.com \
--cc=jikos@kernel.org \
--cc=lains@riseup.net \
--cc=linux-input@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.