2009-02-17 14:18:11 +03:00
/*
* ledtrig - gio . c - LED Trigger Based on GPIO events
*
* Copyright 2009 Felipe Balbi < me @ felipebalbi . com >
*
* 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/module.h>
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/gpio.h>
# include <linux/interrupt.h>
# include <linux/workqueue.h>
# include <linux/leds.h>
# include "leds.h"
struct gpio_trig_data {
struct led_classdev * led ;
struct work_struct work ;
unsigned desired_brightness ; /* desired brightness when led is on */
unsigned inverted ; /* true when gpio is inverted */
unsigned gpio ; /* gpio that triggers the leds */
} ;
static irqreturn_t gpio_trig_irq ( int irq , void * _led )
{
struct led_classdev * led = _led ;
struct gpio_trig_data * gpio_data = led - > trigger_data ;
/* just schedule_work since gpio_get_value can sleep */
schedule_work ( & gpio_data - > work ) ;
return IRQ_HANDLED ;
} ;
static void gpio_trig_work ( struct work_struct * work )
{
struct gpio_trig_data * gpio_data = container_of ( work ,
struct gpio_trig_data , work ) ;
int tmp ;
2009-08-07 03:04:51 +04:00
if ( ! gpio_data - > gpio )
return ;
2009-02-17 14:18:11 +03:00
2009-08-07 03:04:51 +04:00
tmp = gpio_get_value ( gpio_data - > gpio ) ;
if ( gpio_data - > inverted )
tmp = ! tmp ;
2009-02-17 14:18:11 +03:00
2009-08-07 03:04:51 +04:00
if ( tmp ) {
if ( gpio_data - > desired_brightness )
led_set_brightness ( gpio_data - > led ,
gpio_data - > desired_brightness ) ;
else
led_set_brightness ( gpio_data - > led , LED_FULL ) ;
} else {
led_set_brightness ( gpio_data - > led , LED_OFF ) ;
}
2009-02-17 14:18:11 +03:00
}
static ssize_t gpio_trig_brightness_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct led_classdev * led = dev_get_drvdata ( dev ) ;
struct gpio_trig_data * gpio_data = led - > trigger_data ;
return sprintf ( buf , " %u \n " , gpio_data - > desired_brightness ) ;
}
static ssize_t gpio_trig_brightness_store ( struct device * dev ,
struct device_attribute * attr , const char * buf , size_t n )
{
struct led_classdev * led = dev_get_drvdata ( dev ) ;
struct gpio_trig_data * gpio_data = led - > trigger_data ;
unsigned desired_brightness ;
int ret ;
ret = sscanf ( buf , " %u " , & desired_brightness ) ;
if ( ret < 1 | | desired_brightness > 255 ) {
dev_err ( dev , " invalid value \n " ) ;
return - EINVAL ;
}
gpio_data - > desired_brightness = desired_brightness ;
return n ;
}
static DEVICE_ATTR ( desired_brightness , 0644 , gpio_trig_brightness_show ,
gpio_trig_brightness_store ) ;
static ssize_t gpio_trig_inverted_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct led_classdev * led = dev_get_drvdata ( dev ) ;
struct gpio_trig_data * gpio_data = led - > trigger_data ;
return sprintf ( buf , " %s \n " , gpio_data - > inverted ? " yes " : " no " ) ;
}
static ssize_t gpio_trig_inverted_store ( struct device * dev ,
struct device_attribute * attr , const char * buf , size_t n )
{
struct led_classdev * led = dev_get_drvdata ( dev ) ;
struct gpio_trig_data * gpio_data = led - > trigger_data ;
unsigned inverted ;
int ret ;
ret = sscanf ( buf , " %u " , & inverted ) ;
if ( ret < 1 ) {
dev_err ( dev , " invalid value \n " ) ;
return - EINVAL ;
}
gpio_data - > inverted = ! ! inverted ;
2009-08-27 01:29:32 +04:00
/* After inverting, we need to update the LED. */
schedule_work ( & gpio_data - > work ) ;
2009-02-17 14:18:11 +03:00
return n ;
}
static DEVICE_ATTR ( inverted , 0644 , gpio_trig_inverted_show ,
gpio_trig_inverted_store ) ;
static ssize_t gpio_trig_gpio_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct led_classdev * led = dev_get_drvdata ( dev ) ;
struct gpio_trig_data * gpio_data = led - > trigger_data ;
return sprintf ( buf , " %u \n " , gpio_data - > gpio ) ;
}
static ssize_t gpio_trig_gpio_store ( struct device * dev ,
struct device_attribute * attr , const char * buf , size_t n )
{
struct led_classdev * led = dev_get_drvdata ( dev ) ;
struct gpio_trig_data * gpio_data = led - > trigger_data ;
unsigned gpio ;
int ret ;
ret = sscanf ( buf , " %u " , & gpio ) ;
if ( ret < 1 ) {
dev_err ( dev , " couldn't read gpio number \n " ) ;
flush_work ( & gpio_data - > work ) ;
return - EINVAL ;
}
2009-08-27 01:29:31 +04:00
if ( gpio_data - > gpio = = gpio )
return n ;
2009-02-17 14:18:11 +03:00
if ( ! gpio ) {
2009-08-27 01:29:31 +04:00
if ( gpio_data - > gpio ! = 0 )
free_irq ( gpio_to_irq ( gpio_data - > gpio ) , led ) ;
gpio_data - > gpio = 0 ;
2009-02-17 14:18:11 +03:00
return n ;
}
ret = request_irq ( gpio_to_irq ( gpio ) , gpio_trig_irq ,
IRQF_SHARED | IRQF_TRIGGER_RISING
| IRQF_TRIGGER_FALLING , " ledtrig-gpio " , led ) ;
2009-08-27 01:29:31 +04:00
if ( ret ) {
2009-02-17 14:18:11 +03:00
dev_err ( dev , " request_irq failed with error %d \n " , ret ) ;
2009-08-27 01:29:31 +04:00
} else {
if ( gpio_data - > gpio ! = 0 )
free_irq ( gpio_to_irq ( gpio_data - > gpio ) , led ) ;
gpio_data - > gpio = gpio ;
}
2009-02-17 14:18:11 +03:00
return ret ? ret : n ;
}
static DEVICE_ATTR ( gpio , 0644 , gpio_trig_gpio_show , gpio_trig_gpio_store ) ;
static void gpio_trig_activate ( struct led_classdev * led )
{
struct gpio_trig_data * gpio_data ;
int ret ;
gpio_data = kzalloc ( sizeof ( * gpio_data ) , GFP_KERNEL ) ;
if ( ! gpio_data )
return ;
ret = device_create_file ( led - > dev , & dev_attr_gpio ) ;
if ( ret )
goto err_gpio ;
ret = device_create_file ( led - > dev , & dev_attr_inverted ) ;
if ( ret )
goto err_inverted ;
ret = device_create_file ( led - > dev , & dev_attr_desired_brightness ) ;
if ( ret )
goto err_brightness ;
gpio_data - > led = led ;
led - > trigger_data = gpio_data ;
INIT_WORK ( & gpio_data - > work , gpio_trig_work ) ;
return ;
err_brightness :
device_remove_file ( led - > dev , & dev_attr_inverted ) ;
err_inverted :
device_remove_file ( led - > dev , & dev_attr_gpio ) ;
err_gpio :
kfree ( gpio_data ) ;
}
static void gpio_trig_deactivate ( struct led_classdev * led )
{
struct gpio_trig_data * gpio_data = led - > trigger_data ;
if ( gpio_data ) {
device_remove_file ( led - > dev , & dev_attr_gpio ) ;
device_remove_file ( led - > dev , & dev_attr_inverted ) ;
device_remove_file ( led - > dev , & dev_attr_desired_brightness ) ;
flush_work ( & gpio_data - > work ) ;
2009-08-27 01:29:31 +04:00
if ( gpio_data - > gpio ! = 0 )
free_irq ( gpio_to_irq ( gpio_data - > gpio ) , led ) ;
2009-02-17 14:18:11 +03:00
kfree ( gpio_data ) ;
}
}
static struct led_trigger gpio_led_trigger = {
. name = " gpio " ,
. activate = gpio_trig_activate ,
. deactivate = gpio_trig_deactivate ,
} ;
static int __init gpio_trig_init ( void )
{
return led_trigger_register ( & gpio_led_trigger ) ;
}
module_init ( gpio_trig_init ) ;
static void __exit gpio_trig_exit ( void )
{
led_trigger_unregister ( & gpio_led_trigger ) ;
}
module_exit ( gpio_trig_exit ) ;
MODULE_AUTHOR ( " Felipe Balbi <me@felipebalbi.com> " ) ;
MODULE_DESCRIPTION ( " GPIO LED trigger " ) ;
MODULE_LICENSE ( " GPL " ) ;