diff mbox

[v2,1/3] Input: export LEDs as class devices in sysfs

Message ID 20150609174211.GH6338@dtor-ws (mailing list archive)
State New, archived
Headers show

Commit Message

Dmitry Torokhov June 9, 2015, 5:42 p.m. UTC
From: Samuel Thibault <samuel.thibault@ens-lyon.org>

This change creates a new input handler called "leds" that exports LEDs on input
devices as standard LED class devices in sysfs and allows controlling their
ptate via sysfs or via any of the standard LED triggers. This allows to
re-purpose and reassign LDEs on the keyboards to represent states other
than the standard keyboard states (CapsLock, NumLock, etc).

The old API of controlling input LEDs by writing into /dev/input/eventX
devices is still present and will take precedence over acessing via LEDs
subsystem (i.e. it may override state set by a trigger). If input device is
"grabbed" then requests coming through LED subsystem will be ignored.

Signed-off-by: Samuel Thibault <samuel.thibault@ens-lyon.org>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---

Changes since V1:

- Adjusted names of LEDs to better match triggers;
- Changed input_leds_brightness_get() to properly test led state
  (dev->led, not dev->ledbit);
- Changed input_leds_brightness_get() to return 0 or max_brightness, not
  LED_FULL;
- Initialize max_brightness to 1;
- Moved initialization of led-no.

 Documentation/leds/leds-class.txt |    3 -
 drivers/input/Kconfig             |   13 ++
 drivers/input/Makefile            |    1 
 drivers/input/input-leds.c        |  212 +++++++++++++++++++++++++++++++++++++
 drivers/leds/Kconfig              |    3 -
 5 files changed, 226 insertions(+), 6 deletions(-)
 create mode 100644 drivers/input/input-leds.c

--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Comments

Samuel Thibault June 10, 2015, 12:32 a.m. UTC | #1
Hello,

Thanks for the modified version.  This all seems to be working as
expected with multiple keyboards.

Samuel
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Dmitry Torokhov June 10, 2015, 1:24 a.m. UTC | #2
On Wed, Jun 10, 2015 at 02:32:32AM +0200, Samuel Thibault wrote:
> Hello,
> 
> Thanks for the modified version.  This all seems to be working as
> expected with multiple keyboards.

Excellent. Let's give Pavel and Pali chance to test it out and let's get
it in 4.2.

Thanks.
Pavel Machek June 11, 2015, 5:51 p.m. UTC | #3
On Tue 2015-06-09 18:24:54, Dmitry Torokhov wrote:
> On Wed, Jun 10, 2015 at 02:32:32AM +0200, Samuel Thibault wrote:
> > Hello,
> > 
> > Thanks for the modified version.  This all seems to be working as
> > expected with multiple keyboards.
> 
> Excellent. Let's give Pavel and Pali chance to test it out and let's get
> it in 4.2.

Tested-by: Pavel Machek <pavel@ucw.cz>
Acked-by: Pavel Machek <pavel@ucw.cz>
									Pavel
Pavel Machek June 15, 2015, 10:03 a.m. UTC | #4
On Tue 2015-06-09 18:24:54, Dmitry Torokhov wrote:
> On Wed, Jun 10, 2015 at 02:32:32AM +0200, Samuel Thibault wrote:
> > Hello,
> > 
> > Thanks for the modified version.  This all seems to be working as
> > expected with multiple keyboards.
> 
> Excellent. Let's give Pavel and Pali chance to test it out and let's get
> it in 4.2.

I guess Pali is busy... but please don't let that stop 4.2 merge :-).

Thanks,
									Pavel
Pali Rohár June 15, 2015, 10:51 a.m. UTC | #5
On Monday 15 June 2015 12:03:08 Pavel Machek wrote:
> On Tue 2015-06-09 18:24:54, Dmitry Torokhov wrote:
> > On Wed, Jun 10, 2015 at 02:32:32AM +0200, Samuel Thibault wrote:
> > > Hello,
> > > 
> > > Thanks for the modified version.  This all seems to be working as
> > > expected with multiple keyboards.
> > 
> > Excellent. Let's give Pavel and Pali chance to test it out and let's get
> > it in 4.2.
> 
> I guess Pali is busy... but please don't let that stop 4.2 merge :-).
> 
> Thanks,
> 									Pavel

Yes... Do you need to do tests also by me? Pavel and Samuel already
tested it, so in this case I would trust them and if there will be some
problems I will report them later... I also want to see this option
finally in 4.2 kernel.
Vlastimil Babka July 21, 2015, 11:14 a.m. UTC | #6
On 06/09/2015 07:42 PM, Dmitry Torokhov wrote:
> From: Samuel Thibault <samuel.thibault@ens-lyon.org>
> 
> This change creates a new input handler called "leds" that exports LEDs on input
> devices as standard LED class devices in sysfs and allows controlling their
> ptate via sysfs or via any of the standard LED triggers. This allows to
> re-purpose and reassign LDEs on the keyboards to represent states other
> than the standard keyboard states (CapsLock, NumLock, etc).
> 
> The old API of controlling input LEDs by writing into /dev/input/eventX
> devices is still present and will take precedence over acessing via LEDs
> subsystem (i.e. it may override state set by a trigger). If input device is
> "grabbed" then requests coming through LED subsystem will be ignored.
> 
> Signed-off-by: Samuel Thibault <samuel.thibault@ens-lyon.org>
> Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
> ---

> +	led_no = 0;
> +	for_each_set_bit(led_code, dev->ledbit, LED_CNT) {
> +		struct input_led *led = &leds->leds[led_no];
> +
> +		led->handle = &leds->handle;
> +		led->code = led_code;
> +
> +		if (WARN_ON(!input_led_info[led_code].name))
> +			continue;
> +

Hi,
I get several warnings on booting 4.2-rc2 here. Should I be worried?

[    2.782432] ------------[ cut here ]------------
[    2.782440] WARNING: CPU: 2 PID: 356 at ../drivers/input/input-leds.c:115 input_leds_connect+0x22b/0x260()
[    2.782441] Modules linked in: btrfs xor raid6_pq crc32c_intel radeon i2c_algo_bit sr_mod cdrom drm_kms_helper ttm e1000e drm xhci_pci ptp pps_core xhci_hcd sg
[    2.782453] CPU: 2 PID: 356 Comm: kworker/2:2 Not tainted 4.2.0-rc2-1.g288d56b-desktop #1
[    2.782454] Hardware name: Dell Inc. OptiPlex 9010/0M9KCM, BIOS A13 03/27/2013
[    2.782458] Workqueue: usb_hub_wq hub_event
[    2.782459]  ffffffff81a917b7 ffff880213ecf298 ffffffff8169f19d 0000000000000007
[    2.782462]  0000000000000000 ffff880213ecf2d8 ffffffff810674f6 ffff880213ecf2f8
[    2.782463]  ffff8802132fb000 0000000000000003 000000000000000b ffff8800369ff000
[    2.782465] Call Trace:
[    2.782470]  [<ffffffff8169f19d>] dump_stack+0x4c/0x6e
[    2.782474]  [<ffffffff810674f6>] warn_slowpath_common+0x86/0xc0
[    2.782476]  [<ffffffff810675ea>] warn_slowpath_null+0x1a/0x20
[    2.782478]  [<ffffffff8152ccdb>] input_leds_connect+0x22b/0x260
[    2.782480]  [<ffffffff815281b2>] input_attach_handler+0x1a2/0x1f0
[    2.782483]  [<ffffffff81528640>] input_register_device+0x440/0x4f0
[    2.782486]  [<ffffffff8156e494>] hidinput_connect+0x334/0x5d0
[    2.782488]  [<ffffffff815683d4>] hid_connect+0x324/0x400
[    2.782490]  [<ffffffff81578b7a>] ? usbhid_start+0x54a/0x780
[    2.782492]  [<ffffffff81569858>] hid_device_probe+0x118/0x140
[    2.782495]  [<ffffffff81482134>] driver_probe_device+0x1f4/0x460
[    2.782497]  [<ffffffff814824b9>] __device_attach_driver+0x79/0xa0
[    2.782499]  [<ffffffff81482440>] ? __driver_attach+0xa0/0xa0
[    2.782501]  [<ffffffff8147ff6b>] bus_for_each_drv+0x5b/0x90
[    2.782502]  [<ffffffff81481e58>] __device_attach+0xa8/0x120
[    2.782504]  [<ffffffff81482523>] device_initial_probe+0x13/0x20
[    2.782506]  [<ffffffff8148116a>] bus_probe_device+0x9a/0xb0
[    2.782508]  [<ffffffff8147edee>] device_add+0x3fe/0x670
[    2.782511]  [<ffffffff812bf9d3>] ? debugfs_create_file+0xd3/0x110
[    2.782513]  [<ffffffff8156956c>] hid_add_device+0xcc/0x2a0
[    2.782515]  [<ffffffff81578404>] usbhid_probe+0x2e4/0x410
[    2.782518]  [<ffffffff814fe0d2>] usb_probe_interface+0x1b2/0x2d0
[    2.782519]  [<ffffffff81482134>] driver_probe_device+0x1f4/0x460
[    2.782521]  [<ffffffff814824b9>] __device_attach_driver+0x79/0xa0
[    2.782523]  [<ffffffff81482440>] ? __driver_attach+0xa0/0xa0
[    2.782525]  [<ffffffff8147ff6b>] bus_for_each_drv+0x5b/0x90
[    2.782526]  [<ffffffff81481e58>] __device_attach+0xa8/0x120
[    2.782528]  [<ffffffff81482523>] device_initial_probe+0x13/0x20
[    2.782530]  [<ffffffff8148116a>] bus_probe_device+0x9a/0xb0
[    2.782531]  [<ffffffff8147edee>] device_add+0x3fe/0x670
[    2.782533]  [<ffffffff814fbfb1>] usb_set_configuration+0x501/0x8e0
[    2.782535]  [<ffffffff81253efe>] ? kernfs_add_one+0xee/0x140
[    2.782537]  [<ffffffff8150656e>] generic_probe+0x2e/0x80
[    2.782539]  [<ffffffff814fdee2>] usb_probe_device+0x32/0x70
[    2.782541]  [<ffffffff81482134>] driver_probe_device+0x1f4/0x460
[    2.782542]  [<ffffffff814824b9>] __device_attach_driver+0x79/0xa0
[    2.782544]  [<ffffffff81482440>] ? __driver_attach+0xa0/0xa0
[    2.782546]  [<ffffffff8147ff6b>] bus_for_each_drv+0x5b/0x90
[    2.782547]  [<ffffffff81481e58>] __device_attach+0xa8/0x120
[    2.782549]  [<ffffffff81482523>] device_initial_probe+0x13/0x20
[    2.782551]  [<ffffffff8148116a>] bus_probe_device+0x9a/0xb0
[    2.782552]  [<ffffffff8147edee>] device_add+0x3fe/0x670
[    2.782554]  [<ffffffff814f15c6>] ? usb_new_device+0x216/0x5d0
[    2.782556]  [<ffffffff814f164e>] usb_new_device+0x29e/0x5d0
[    2.782559]  [<ffffffff8148cff1>] ? pm_runtime_set_autosuspend_delay+0x51/0x60
[    2.782561]  [<ffffffff81506612>] ? __usb_detect_quirks+0x52/0x110
[    2.782563]  [<ffffffff814f2c3f>] hub_port_connect+0x31f/0x9c0
[    2.782565]  [<ffffffff814f39b1>] hub_event+0x6d1/0xb10
[    2.782568]  [<ffffffff8107f039>] process_one_work+0x159/0x470
[    2.782570]  [<ffffffff8107f398>] worker_thread+0x48/0x4a0
[    2.782572]  [<ffffffff8107f350>] ? process_one_work+0x470/0x470
[    2.782574]  [<ffffffff8107f350>] ? process_one_work+0x470/0x470
[    2.782576]  [<ffffffff81085089>] kthread+0xc9/0xe0
[    2.782579]  [<ffffffff81084fc0>] ? kthread_worker_fn+0x170/0x170
[    2.782581]  [<ffffffff816a55df>] ret_from_fork+0x3f/0x70
[    2.782583]  [<ffffffff81084fc0>] ? kthread_worker_fn+0x170/0x170
[    2.782585] ---[ end trace ce1cb4ca0f2cbd0b ]---
[    2.782586] ------------[ cut here ]------------

--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/Documentation/leds/leds-class.txt b/Documentation/leds/leds-class.txt
index 79699c2..62261c0 100644
--- a/Documentation/leds/leds-class.txt
+++ b/Documentation/leds/leds-class.txt
@@ -2,9 +2,6 @@ 
 LED handling under Linux
 ========================
 
-If you're reading this and thinking about keyboard leds, these are
-handled by the input subsystem and the led class is *not* needed.
-
 In its simplest form, the LED class just allows control of LEDs from
 userspace. LEDs appear in /sys/class/leds/. The maximum brightness of the
 LED is defined in max_brightness file. The brightness file will set the brightness
diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
index a11ff74..a35532e 100644
--- a/drivers/input/Kconfig
+++ b/drivers/input/Kconfig
@@ -25,6 +25,19 @@  config INPUT
 
 if INPUT
 
+config INPUT_LEDS
+	tristate "Export input device LEDs in sysfs"
+	depends on LEDS_CLASS
+	default INPUT
+	help
+	  Say Y here if you would like to export LEDs on input devices
+	  as standard LED class devices in sysfs.
+
+	  If unsure, say Y.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called input-leds.
+
 config INPUT_FF_MEMLESS
 	tristate "Support for memoryless force-feedback devices"
 	help
diff --git a/drivers/input/Makefile b/drivers/input/Makefile
index 5ca3f63..0c9302c 100644
--- a/drivers/input/Makefile
+++ b/drivers/input/Makefile
@@ -12,6 +12,7 @@  obj-$(CONFIG_INPUT_POLLDEV)	+= input-polldev.o
 obj-$(CONFIG_INPUT_SPARSEKMAP)	+= sparse-keymap.o
 obj-$(CONFIG_INPUT_MATRIXKMAP)	+= matrix-keymap.o
 
+obj-$(CONFIG_INPUT_LEDS)	+= input-leds.o
 obj-$(CONFIG_INPUT_MOUSEDEV)	+= mousedev.o
 obj-$(CONFIG_INPUT_JOYDEV)	+= joydev.o
 obj-$(CONFIG_INPUT_EVDEV)	+= evdev.o
diff --git a/drivers/input/input-leds.c b/drivers/input/input-leds.c
new file mode 100644
index 0000000..074a65e
--- /dev/null
+++ b/drivers/input/input-leds.c
@@ -0,0 +1,212 @@ 
+/*
+ * LED support for the input layer
+ *
+ * Copyright 2010-2015 Samuel Thibault <samuel.thibault@ens-lyon.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/leds.h>
+#include <linux/input.h>
+
+#if IS_ENABLED(CONFIG_VT)
+#define VT_TRIGGER(_name)	.trigger = _name
+#else
+#define VT_TRIGGER(_name)	.trigger = NULL
+#endif
+
+static const struct {
+	const char *name;
+	const char *trigger;
+} input_led_info[LED_CNT] = {
+	[LED_NUML]	= { "numlock", VT_TRIGGER("kbd-numlock") },
+	[LED_CAPSL]	= { "capslock", VT_TRIGGER("kbd-capslock") },
+	[LED_SCROLLL]	= { "scrolllock", VT_TRIGGER("kbd-scrolllock") },
+	[LED_COMPOSE]	= { "compose" },
+	[LED_KANA]	= { "kana", VT_TRIGGER("kbd-kanalock") },
+	[LED_SLEEP]	= { "sleep" } ,
+	[LED_SUSPEND]	= { "suspend" },
+	[LED_MUTE]	= { "mute" },
+	[LED_MISC]	= { "misc" },
+	[LED_MAIL]	= { "mail" },
+	[LED_CHARGING]	= { "charging" },
+};
+
+struct input_led {
+	struct led_classdev cdev;
+	struct input_handle *handle;
+	unsigned int code; /* One of LED_* constants */
+};
+
+struct input_leds {
+	struct input_handle handle;
+	unsigned int num_leds;
+	struct input_led leds[];
+};
+
+static enum led_brightness input_leds_brightness_get(struct led_classdev *cdev)
+{
+	struct input_led *led = container_of(cdev, struct input_led, cdev);
+	struct input_dev *input = led->handle->dev;
+
+	return test_bit(led->code, input->led) ? cdev->max_brightness : 0;
+}
+
+static void input_leds_brightness_set(struct led_classdev *cdev,
+				      enum led_brightness brightness)
+{
+	struct input_led *led = container_of(cdev, struct input_led, cdev);
+
+	input_inject_event(led->handle, EV_LED, led->code, !!brightness);
+}
+
+static void input_leds_event(struct input_handle *handle, unsigned int type,
+			     unsigned int code, int value)
+{
+}
+
+static int input_leds_connect(struct input_handler *handler,
+			      struct input_dev *dev,
+			      const struct input_device_id *id)
+{
+	struct input_leds *leds;
+	unsigned int num_leds;
+	unsigned int led_code;
+	int led_no;
+	int error;
+
+	num_leds = bitmap_weight(dev->ledbit, LED_CNT);
+	if (!num_leds)
+		return -ENXIO;
+
+	leds = kzalloc(sizeof(*leds) + num_leds * sizeof(*leds->leds),
+		       GFP_KERNEL);
+	if (!leds)
+		return -ENOMEM;
+
+	leds->num_leds = num_leds;
+
+	leds->handle.dev = dev;
+	leds->handle.handler = handler;
+	leds->handle.name = "leds";
+	leds->handle.private = leds;
+
+	error = input_register_handle(&leds->handle);
+	if (error)
+		goto err_free_mem;
+
+	error = input_open_device(&leds->handle);
+	if (error)
+		goto err_unregister_handle;
+
+	led_no = 0;
+	for_each_set_bit(led_code, dev->ledbit, LED_CNT) {
+		struct input_led *led = &leds->leds[led_no];
+
+		led->handle = &leds->handle;
+		led->code = led_code;
+
+		if (WARN_ON(!input_led_info[led_code].name))
+			continue;
+
+		led->cdev.name = kasprintf(GFP_KERNEL, "%s::%s",
+					   dev_name(&dev->dev),
+					   input_led_info[led_code].name);
+		if (!led->cdev.name) {
+			error = -ENOMEM;
+			goto err_unregister_leds;
+		}
+
+		led->cdev.max_brightness = 1;
+		led->cdev.brightness_get = input_leds_brightness_get;
+		led->cdev.brightness_set = input_leds_brightness_set;
+		led->cdev.default_trigger = input_led_info[led_code].trigger;
+
+		error = led_classdev_register(&dev->dev, &led->cdev);
+		if (error) {
+			dev_err(&dev->dev, "failed to register LED %s: %d\n",
+				led->cdev.name, error);
+			kfree(led->cdev.name);
+			goto err_unregister_leds;
+		}
+
+		led_no++;
+	}
+
+	return 0;
+
+err_unregister_leds:
+	while (--led_no >= 0) {
+		struct input_led *led = &leds->leds[led_no];
+
+		led_classdev_unregister(&led->cdev);
+		kfree(led->cdev.name);
+	}
+
+	input_close_device(&leds->handle);
+
+err_unregister_handle:
+	input_unregister_handle(&leds->handle);
+
+err_free_mem:
+	kfree(leds);
+	return error;
+}
+
+static void input_leds_disconnect(struct input_handle *handle)
+{
+	struct input_leds *leds = handle->private;
+	int i;
+
+	for (i = 0; i < leds->num_leds; i++) {
+		struct input_led *led = &leds->leds[i];
+
+		led_classdev_unregister(&led->cdev);
+		kfree(led->cdev.name);
+	}
+
+	input_close_device(handle);
+	input_unregister_handle(handle);
+
+	kfree(leds);
+}
+
+static const struct input_device_id input_leds_ids[] = {
+	{
+		.flags = INPUT_DEVICE_ID_MATCH_EVBIT,
+		.evbit = { BIT_MASK(EV_LED) },
+	},
+	{ },
+};
+MODULE_DEVICE_TABLE(input, input_leds_ids);
+
+static struct input_handler input_leds_handler = {
+	.event =	input_leds_event,
+	.connect =	input_leds_connect,
+	.disconnect =	input_leds_disconnect,
+	.name =		"leds",
+	.id_table =	input_leds_ids,
+};
+
+static int __init input_leds_init(void)
+{
+	return input_register_handler(&input_leds_handler);
+}
+module_init(input_leds_init);
+
+static void __exit input_leds_exit(void)
+{
+	input_unregister_handler(&input_leds_handler);
+}
+module_exit(input_leds_exit);
+
+MODULE_AUTHOR("Samuel Thibault <samuel.thibault@ens-lyon.org>");
+MODULE_AUTHOR("Dmitry Torokhov <dmitry.torokhov@gmail.com>");
+MODULE_DESCRIPTION("Input -> LEDs Bridge");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 966b960..4191614 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -11,9 +11,6 @@  menuconfig NEW_LEDS
 	  Say Y to enable Linux LED support.  This allows control of supported
 	  LEDs from both userspace and optionally, by kernel events (triggers).
 
-	  This is not related to standard keyboard LEDs which are controlled
-	  via the input system.
-
 if NEW_LEDS
 
 config LEDS_CLASS