2015-03-17 07:19:56 +03:00
/*
* 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 )
{
}
2015-07-23 00:56:39 +03:00
static int input_leds_get_count ( struct input_dev * dev )
{
unsigned int led_code ;
int count = 0 ;
for_each_set_bit ( led_code , dev - > ledbit , LED_CNT )
if ( input_led_info [ led_code ] . name )
count + + ;
return count ;
}
2015-03-17 07:19:56 +03:00
static int input_leds_connect ( struct input_handler * handler ,
struct input_dev * dev ,
const struct input_device_id * id )
{
struct input_leds * leds ;
2018-04-06 20:23:05 +03:00
struct input_led * led ;
2015-03-17 07:19:56 +03:00
unsigned int num_leds ;
unsigned int led_code ;
int led_no ;
int error ;
2015-07-23 00:56:39 +03:00
num_leds = input_leds_get_count ( dev ) ;
2015-03-17 07:19:56 +03:00
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 ) {
2018-04-06 20:23:05 +03:00
if ( ! input_led_info [ led_code ] . name )
continue ;
2015-03-17 07:19:56 +03:00
2018-04-06 20:23:05 +03:00
led = & leds - > leds [ led_no ] ;
2015-03-17 07:19:56 +03:00
led - > handle = & leds - > handle ;
led - > code = led_code ;
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 " ) ;