From patchwork Wed Feb 24 01:20:10 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Samuel Thibault X-Patchwork-Id: 81571 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter.kernel.org (8.14.3/8.14.3) with ESMTP id o1O1U3qX001425 for ; Wed, 24 Feb 2010 01:30:03 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751998Ab0BXBaA (ORCPT ); Tue, 23 Feb 2010 20:30:00 -0500 Received: from toccata.ens-lyon.org ([140.77.166.68]:36612 "EHLO toccata.ens-lyon.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751609Ab0BXBaA (ORCPT ); Tue, 23 Feb 2010 20:30:00 -0500 X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter.kernel.org [140.211.167.41]); Wed, 24 Feb 2010 01:30:04 +0000 (UTC) X-Greylist: delayed 585 seconds by postgrey-1.27 at vger.kernel.org; Tue, 23 Feb 2010 20:29:59 EST Received: from localhost (localhost [127.0.0.1]) by toccata.ens-lyon.org (Postfix) with ESMTP id D99218408B; Wed, 24 Feb 2010 02:20:12 +0100 (CET) X-Virus-Scanned: Debian amavisd-new at toccata.ens-lyon.org Received: from toccata.ens-lyon.org ([127.0.0.1]) by localhost (toccata.ens-lyon.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id E7+ihHme3tNA; Wed, 24 Feb 2010 02:20:12 +0100 (CET) Received: from const.ipv6 (host58-205-dynamic.31-79-r.retail.telecomitalia.it [79.31.205.58]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by toccata.ens-lyon.org (Postfix) with ESMTPSA id 371D084088; Wed, 24 Feb 2010 02:20:12 +0100 (CET) Received: from samy by const.ipv6 with local (Exim 4.71) (envelope-from ) id 1Nk5v8-0001Bk-8P; Wed, 24 Feb 2010 02:20:10 +0100 Date: Wed, 24 Feb 2010 02:20:10 +0100 From: Samuel Thibault To: "H. Peter Anvin" , Pavel Machek , Alexey Dobriyan , akpm@linux-foundation.org, linux-kernel@vger.kernel.org, alan@lxorguk.ukuu.org.uk, mgarski@post.pl, linux-input@vger.kernel.org Subject: [PATCH] Route kbd leds through the generic leds layer Message-ID: <20100224012010.GA4062@const> Mail-Followup-To: Samuel Thibault , "H. Peter Anvin" , Pavel Machek , Alexey Dobriyan , akpm@linux-foundation.org, linux-kernel@vger.kernel.org, alan@lxorguk.ukuu.org.uk, mgarski@post.pl, linux-input@vger.kernel.org MIME-Version: 1.0 Content-Disposition: inline User-Agent: Mutt/1.5.12-2006-07-14 Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org difference with the first version is that now only input leds actually available on some device are registered. The first version was acked by Pavel Machek. Samuel diff -ur linux-2.6.32-orig/Documentation/leds-class.txt linux-2.6.32-perso/Documentation/leds-class.txt --- linux-2.6.32-orig/Documentation/leds-class.txt 2009-12-03 13:41:42.000000000 +0100 +++ linux-2.6.32-perso/Documentation/leds-class.txt 2010-02-21 04:12:59.000000000 +0100 @@ -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 -ur linux-2.6.32-orig/drivers/char/keyboard.c linux-2.6.32-perso/drivers/char/keyboard.c --- linux-2.6.32-orig/drivers/char/keyboard.c 2009-12-03 13:42:46.000000000 +0100 +++ linux-2.6.32-perso/drivers/char/keyboard.c 2010-02-23 20:41:07.000000000 +0100 @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -140,6 +141,9 @@ static char rep; /* flag telling character repeat */ static unsigned char ledstate = 0xff; /* undefined */ +#ifdef CONFIG_LEDS_INPUT +static unsigned char lockstate = 0xff; /* undefined */ +#endif static unsigned char ledioctl; static struct ledptr { @@ -997,6 +1001,23 @@ return leds; } +#ifdef CONFIG_LEDS_INPUT +/* When input-based leds are enabled, we route keyboard "leds" through triggers + */ +DEFINE_LED_TRIGGER(ledtrig_scrolllock); +DEFINE_LED_TRIGGER(ledtrig_numlock); +DEFINE_LED_TRIGGER(ledtrig_capslock); +DEFINE_LED_TRIGGER(ledtrig_kanalock); +DEFINE_LED_TRIGGER(ledtrig_shiftlock); +DEFINE_LED_TRIGGER(ledtrig_altgrlock); +DEFINE_LED_TRIGGER(ledtrig_ctrllock); +DEFINE_LED_TRIGGER(ledtrig_altlock); +DEFINE_LED_TRIGGER(ledtrig_shiftllock); +DEFINE_LED_TRIGGER(ledtrig_shiftrlock); +DEFINE_LED_TRIGGER(ledtrig_ctrlllock); +DEFINE_LED_TRIGGER(ledtrig_ctrlrlock); +#endif + /* * This routine is the bottom half of the keyboard interrupt * routine, and runs with all interrupts enabled. It does @@ -1013,19 +1034,63 @@ static void kbd_bh(unsigned long dummy) { - struct list_head *node; unsigned char leds = getleds(); if (leds != ledstate) { +#ifdef CONFIG_LEDS_INPUT + led_trigger_event(ledtrig_scrolllock, + leds & (1 << VC_SCROLLOCK) ? INT_MAX : LED_OFF); + led_trigger_event(ledtrig_numlock, + leds & (1 << VC_NUMLOCK) ? INT_MAX : LED_OFF); + led_trigger_event(ledtrig_capslock, + leds & (1 << VC_CAPSLOCK) ? INT_MAX : LED_OFF); + led_trigger_event(ledtrig_kanalock, + leds & (1 << VC_KANALOCK) ? INT_MAX : LED_OFF); +#else + struct list_head *node; list_for_each(node, &kbd_handler.h_list) { struct input_handle *handle = to_handle_h(node); - input_inject_event(handle, EV_LED, LED_SCROLLL, !!(leds & 0x01)); - input_inject_event(handle, EV_LED, LED_NUML, !!(leds & 0x02)); - input_inject_event(handle, EV_LED, LED_CAPSL, !!(leds & 0x04)); + input_inject_event(handle, EV_LED, LED_SCROLLL, + !!(leds & (1 << VC_SCROLLOCK))); + input_inject_event(handle, EV_LED, LED_NUML, + !!(leds & (1 << VC_NUMLOCK))); + input_inject_event(handle, EV_LED, LED_CAPSL, + !!(leds & (1 << VC_CAPSLOCK))); input_inject_event(handle, EV_SYN, SYN_REPORT, 0); } +#endif } +#ifdef CONFIG_LEDS_INPUT + if (kbd->lockstate != lockstate) { + led_trigger_event(ledtrig_shiftlock, + kbd->lockstate & (1<lockstate & (1<lockstate & (1<lockstate & (1<lockstate & (1<lockstate & (1<lockstate & (1<lockstate & (1<lockstate; +#endif + ledstate = leds; } @@ -1357,6 +1422,7 @@ kfree(handle); } +#ifndef CONFIG_LEDS_INPUT /* * Start keyboard handler on the new keyboard by refreshing LED state to * match the rest of the system. @@ -1367,13 +1433,17 @@ tasklet_disable(&keyboard_tasklet); if (leds != 0xff) { - input_inject_event(handle, EV_LED, LED_SCROLLL, !!(leds & 0x01)); - input_inject_event(handle, EV_LED, LED_NUML, !!(leds & 0x02)); - input_inject_event(handle, EV_LED, LED_CAPSL, !!(leds & 0x04)); + input_inject_event(handle, EV_LED, LED_SCROLLL, + !!(leds & (1 << VC_SCROLLOCK))); + input_inject_event(handle, EV_LED, LED_NUML, + !!(leds & (1 << VC_NUMLOCK))); + input_inject_event(handle, EV_LED, LED_CAPSL, + !!(leds & (1 << VC_CAPSLOCK))); input_inject_event(handle, EV_SYN, SYN_REPORT, 0); } tasklet_enable(&keyboard_tasklet); } +#endif static const struct input_device_id kbd_ids[] = { { @@ -1395,7 +1465,9 @@ .event = kbd_event, .connect = kbd_connect, .disconnect = kbd_disconnect, +#ifndef CONFIG_LEDS_INPUT .start = kbd_start, +#endif .name = "kbd", .id_table = kbd_ids, }; @@ -1419,6 +1491,21 @@ if (error) return error; +#ifdef CONFIG_LEDS_INPUT + led_trigger_register_simple("scrolllock", &ledtrig_scrolllock); + led_trigger_register_simple("numlock", &ledtrig_numlock); + led_trigger_register_simple("capslock", &ledtrig_capslock); + led_trigger_register_simple("kanalock", &ledtrig_kanalock); + led_trigger_register_simple("shiftlock", &ledtrig_shiftlock); + led_trigger_register_simple("altgrlock", &ledtrig_altgrlock); + led_trigger_register_simple("ctrllock", &ledtrig_ctrllock); + led_trigger_register_simple("altlock", &ledtrig_altlock); + led_trigger_register_simple("shiftllock", &ledtrig_shiftllock); + led_trigger_register_simple("shiftrlock", &ledtrig_shiftrlock); + led_trigger_register_simple("ctrlllock", &ledtrig_ctrlllock); + led_trigger_register_simple("ctrlrlock", &ledtrig_ctrlrlock); +#endif + tasklet_enable(&keyboard_tasklet); tasklet_schedule(&keyboard_tasklet); diff -ur linux-2.6.32-orig/drivers/leds/Kconfig linux-2.6.32-perso/drivers/leds/Kconfig --- linux-2.6.32-orig/drivers/leds/Kconfig 2009-12-03 13:42:57.000000000 +0100 +++ linux-2.6.32-perso/drivers/leds/Kconfig 2010-02-24 00:52:55.000000000 +0100 @@ -4,9 +4,6 @@ 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 @@ -17,6 +14,13 @@ comment "LED drivers" +config LEDS_INPUT + tristate "LED Support using input keyboards" + depends on LEDS_CLASS + help + This option enables support for the LEDs on keyboard managed + by the input layer. + config LEDS_ATMEL_PWM tristate "LED Support using Atmel PWM outputs" depends on LEDS_CLASS && ATMEL_PWM diff -ur linux-2.6.32-orig/drivers/leds/leds-input.c linux-2.6.32-perso/drivers/leds/leds-input.c --- linux-2.6.32-orig/drivers/leds/leds-input.c 2010-02-21 04:13:41.000000000 +0100 +++ linux-2.6.32-perso/drivers/leds/leds-input.c 2010-02-24 01:04:09.000000000 +0100 @@ -0,0 +1,186 @@ +/* + * LED support for the input layer + * + * Copyright 2010 Samuel Thibault + * + * 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 +#include +#include +#include + +/* Protects concurrency of led state and registration */ +static DEFINE_SPINLOCK(input_led_lock); +/* Current state */ +static unsigned long input_led_leds[BITS_TO_LONGS(LED_CNT)]; +/* array of all led classes */ +static struct led_classdev input_leds[LED_CNT]; +/* which led classes are registered */ +static unsigned long input_led_registered[BITS_TO_LONGS(LED_CNT)]; +/* our handler */ +static struct input_handler input_led_handler; + +/* Led state change, update all keyboards */ +static void input_led_set(struct led_classdev *cdev, + enum led_brightness brightness) +{ + int led = cdev - input_leds; + unsigned long flags; + struct input_handle *handle; + + spin_lock_irqsave(&input_led_lock, flags); + list_for_each_entry(handle, &input_led_handler.h_list, h_node) { + input_inject_event(handle, EV_LED, led, !!brightness); + input_inject_event(handle, EV_SYN, SYN_REPORT, 0); + } + if (brightness) + set_bit(led, input_led_leds); + else + clear_bit(led, input_led_leds); + spin_unlock_irqrestore(&input_led_lock, flags); +} + +static struct led_classdev input_leds[LED_CNT] = { +#define DEFINE_INPUT_LED(input_led, nam, deftrig) \ + [input_led] = { \ + .name = "input::"nam, \ + .max_brightness = 1, \ + .brightness_set = input_led_set, \ + .default_trigger = deftrig, \ + } +DEFINE_INPUT_LED(LED_NUML, "numlock", "numlock"), +DEFINE_INPUT_LED(LED_CAPSL, "capslock", "capslock"), +DEFINE_INPUT_LED(LED_SCROLLL, "scrolllock", "scrolllock"), +DEFINE_INPUT_LED(LED_COMPOSE, "compose", NULL), +DEFINE_INPUT_LED(LED_KANA, "kana", "kanalock"), +DEFINE_INPUT_LED(LED_SLEEP, "sleep", NULL), +DEFINE_INPUT_LED(LED_SUSPEND, "suspend", NULL), +DEFINE_INPUT_LED(LED_MUTE, "mute", NULL), +DEFINE_INPUT_LED(LED_MISC, "misc", NULL), +DEFINE_INPUT_LED(LED_MAIL, "mail", NULL), +DEFINE_INPUT_LED(LED_CHARGING, "charging", NULL), +}; + +static int input_led_connect(struct input_handler *handler, + struct input_dev *dev, + const struct input_device_id *id) +{ + struct input_handle *handle; + int i, error; + unsigned long flags; + + if (!test_bit(EV_LED, dev->keybit)) + return -ENODEV; + + handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL); + if (!handle) + return -ENOMEM; + + handle->dev = dev; + handle->handler = handler; + handle->name = "input leds"; + + error = input_register_handle(handle); + if (error) { + kfree(handle); + return error; + } + + spin_lock_irqsave(&input_led_lock, flags); + for (i = 0; i < LED_CNT; i++) + if (input_leds[i].name + && !test_bit(i, input_led_registered) + && test_bit(i, dev->ledbit)) + /* This keyboard has led i, try to register it */ + if (!led_classdev_register(NULL, &input_leds[i])) + set_bit(i, input_led_registered); + spin_unlock_irqrestore(&input_led_lock, flags); + + return 0; +} + +static void input_led_disconnect(struct input_handle *handle) +{ + int unregister,i; + input_unregister_handle(handle); + kfree(handle); + + for (i = 0; i < LED_CNT; i++) { + if (!test_bit(i, input_led_registered)) + continue; + + unregister = 1; + list_for_each_entry(handle, &input_led_handler.h_list, h_node) { + if (test_bit(i, handle->dev->ledbit)) { + unregister = 0; + break; + } + } + if (!unregister) + continue; + + led_classdev_unregister(&input_leds[i]); + clear_bit(i, input_led_registered); + } +} + +/* New keyboard, update its leds */ +static void input_led_start(struct input_handle *handle) +{ + unsigned long flags; + int i; + + spin_lock_irqsave(&input_led_lock, flags); + for (i = 0; i < LED_CNT; i++) + if (input_leds[i].name && test_bit(i, handle->dev->ledbit)) + input_inject_event(handle, EV_LED, i, + test_bit(i, input_led_leds)); + input_inject_event(handle, EV_SYN, SYN_REPORT, 0); + spin_unlock_irqrestore(&input_led_lock, flags); +} + +static const struct input_device_id input_led_ids[] = { + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT, + .evbit = { BIT_MASK(EV_LED) }, + }, + + { }, /* Terminating entry */ +}; + +static struct input_handler input_led_handler = { + .connect = input_led_connect, + .disconnect = input_led_disconnect, + .start = input_led_start, + .name = "input leds", + .id_table = input_led_ids, +}; + +static int __init input_led_init(void) +{ + return input_register_handler(&input_led_handler); +} + +static void __exit input_led_exit(void) +{ + int i; + + input_unregister_handler(&input_led_handler); + + for (i = 0; i < LED_CNT; i++) + if (test_bit(i, input_led_registered)) { + led_classdev_unregister(&input_leds[i]); + clear_bit(i, input_led_registered); + } +} + +module_init(input_led_init); +module_exit(input_led_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("User LED support for input layer"); +MODULE_AUTHOR("Samuel Thibault "); diff -ur linux-2.6.32-orig/drivers/leds/Makefile linux-2.6.32-perso/drivers/leds/Makefile --- linux-2.6.32-orig/drivers/leds/Makefile 2009-12-03 13:42:57.000000000 +0100 +++ linux-2.6.32-perso/drivers/leds/Makefile 2010-02-21 03:37:08.000000000 +0100 @@ -5,6 +5,7 @@ obj-$(CONFIG_LEDS_TRIGGERS) += led-triggers.o # LED Platform Drivers +obj-$(CONFIG_LEDS_INPUT) += leds-input.o obj-$(CONFIG_LEDS_ATMEL_PWM) += leds-atmel-pwm.o obj-$(CONFIG_LEDS_BD2802) += leds-bd2802.o obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o