2019-06-04 11:11:33 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2015-03-17 07:19:56 +03:00
/*
* LED support for the input layer
*
* Copyright 2010 - 2015 Samuel Thibault < samuel . thibault @ ens - lyon . org >
*/
# 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 ;
treewide: Use struct_size() for kmalloc()-family
One of the more common cases of allocation size calculations is finding
the size of a structure that has a zero-sized array at the end, along
with memory for some number of elements for that array. For example:
struct foo {
int stuff;
void *entry[];
};
instance = kmalloc(sizeof(struct foo) + sizeof(void *) * count, GFP_KERNEL);
Instead of leaving these open-coded and prone to type mistakes, we can
now use the new struct_size() helper:
instance = kmalloc(struct_size(instance, entry, count), GFP_KERNEL);
This patch makes the changes for kmalloc()-family (and kvmalloc()-family)
uses. It was done via automatic conversion with manual review for the
"CHECKME" non-standard cases noted below, using the following Coccinelle
script:
// pkey_cache = kmalloc(sizeof *pkey_cache + tprops->pkey_tbl_len *
// sizeof *pkey_cache->table, GFP_KERNEL);
@@
identifier alloc =~ "kmalloc|kzalloc|kvmalloc|kvzalloc";
expression GFP;
identifier VAR, ELEMENT;
expression COUNT;
@@
- alloc(sizeof(*VAR) + COUNT * sizeof(*VAR->ELEMENT), GFP)
+ alloc(struct_size(VAR, ELEMENT, COUNT), GFP)
// mr = kzalloc(sizeof(*mr) + m * sizeof(mr->map[0]), GFP_KERNEL);
@@
identifier alloc =~ "kmalloc|kzalloc|kvmalloc|kvzalloc";
expression GFP;
identifier VAR, ELEMENT;
expression COUNT;
@@
- alloc(sizeof(*VAR) + COUNT * sizeof(VAR->ELEMENT[0]), GFP)
+ alloc(struct_size(VAR, ELEMENT, COUNT), GFP)
// Same pattern, but can't trivially locate the trailing element name,
// or variable name.
@@
identifier alloc =~ "kmalloc|kzalloc|kvmalloc|kvzalloc";
expression GFP;
expression SOMETHING, COUNT, ELEMENT;
@@
- alloc(sizeof(SOMETHING) + COUNT * sizeof(ELEMENT), GFP)
+ alloc(CHECKME_struct_size(&SOMETHING, ELEMENT, COUNT), GFP)
Signed-off-by: Kees Cook <keescook@chromium.org>
2018-05-08 23:45:50 +03:00
leds = kzalloc ( struct_size ( leds , leds , num_leds ) , GFP_KERNEL ) ;
2015-03-17 07:19:56 +03:00
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 " ) ;