2006-03-31 14:31:05 +04:00
/*
* LED Triggers Core
*
2007-07-09 02:19:31 +04:00
* Copyright 2005 - 2007 Openedhand Ltd .
2006-03-31 14:31:05 +04:00
*
* Author : Richard Purdie < rpurdie @ openedhand . 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/list.h>
# include <linux/spinlock.h>
# include <linux/device.h>
# include <linux/sysdev.h>
# include <linux/timer.h>
2007-11-10 16:29:04 +03:00
# include <linux/rwsem.h>
2006-03-31 14:31:05 +04:00
# include <linux/leds.h>
# include "leds.h"
/*
* Nests outside led_cdev - > trigger_lock
*/
2007-11-10 16:29:04 +03:00
static DECLARE_RWSEM ( triggers_list_lock ) ;
2006-03-31 14:31:05 +04:00
static LIST_HEAD ( trigger_list ) ;
2008-03-09 23:59:57 +03:00
/* Used by LED Class */
2007-07-09 02:19:31 +04:00
ssize_t led_trigger_store ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
2006-03-31 14:31:05 +04:00
{
2007-07-09 02:19:31 +04:00
struct led_classdev * led_cdev = dev_get_drvdata ( dev ) ;
2006-03-31 14:31:05 +04:00
char trigger_name [ TRIG_NAME_MAX ] ;
struct led_trigger * trig ;
size_t len ;
trigger_name [ sizeof ( trigger_name ) - 1 ] = ' \0 ' ;
strncpy ( trigger_name , buf , sizeof ( trigger_name ) - 1 ) ;
len = strlen ( trigger_name ) ;
if ( len & & trigger_name [ len - 1 ] = = ' \n ' )
trigger_name [ len - 1 ] = ' \0 ' ;
if ( ! strcmp ( trigger_name , " none " ) ) {
2008-03-09 23:54:37 +03:00
led_trigger_remove ( led_cdev ) ;
2006-03-31 14:31:05 +04:00
return count ;
}
2007-11-10 16:29:04 +03:00
down_read ( & triggers_list_lock ) ;
2006-03-31 14:31:05 +04:00
list_for_each_entry ( trig , & trigger_list , next_trig ) {
if ( ! strcmp ( trigger_name , trig - > name ) ) {
2007-11-10 16:29:04 +03:00
down_write ( & led_cdev - > trigger_lock ) ;
2006-03-31 14:31:05 +04:00
led_trigger_set ( led_cdev , trig ) ;
2007-11-10 16:29:04 +03:00
up_write ( & led_cdev - > trigger_lock ) ;
2006-03-31 14:31:05 +04:00
2007-11-10 16:29:04 +03:00
up_read ( & triggers_list_lock ) ;
2006-03-31 14:31:05 +04:00
return count ;
}
}
2007-11-10 16:29:04 +03:00
up_read ( & triggers_list_lock ) ;
2006-03-31 14:31:05 +04:00
return - EINVAL ;
}
2008-03-09 23:59:57 +03:00
EXPORT_SYMBOL_GPL ( led_trigger_store ) ;
2006-03-31 14:31:05 +04:00
2007-07-09 02:19:31 +04:00
ssize_t led_trigger_show ( struct device * dev , struct device_attribute * attr ,
char * buf )
2006-03-31 14:31:05 +04:00
{
2007-07-09 02:19:31 +04:00
struct led_classdev * led_cdev = dev_get_drvdata ( dev ) ;
2006-03-31 14:31:05 +04:00
struct led_trigger * trig ;
int len = 0 ;
2007-11-10 16:29:04 +03:00
down_read ( & triggers_list_lock ) ;
down_read ( & led_cdev - > trigger_lock ) ;
2006-03-31 14:31:05 +04:00
if ( ! led_cdev - > trigger )
len + = sprintf ( buf + len , " [none] " ) ;
else
len + = sprintf ( buf + len , " none " ) ;
list_for_each_entry ( trig , & trigger_list , next_trig ) {
if ( led_cdev - > trigger & & ! strcmp ( led_cdev - > trigger - > name ,
trig - > name ) )
len + = sprintf ( buf + len , " [%s] " , trig - > name ) ;
else
len + = sprintf ( buf + len , " %s " , trig - > name ) ;
}
2007-11-10 16:29:04 +03:00
up_read ( & led_cdev - > trigger_lock ) ;
up_read ( & triggers_list_lock ) ;
2006-03-31 14:31:05 +04:00
len + = sprintf ( len + buf , " \n " ) ;
return len ;
}
2008-03-09 23:59:57 +03:00
EXPORT_SYMBOL_GPL ( led_trigger_show ) ;
2006-03-31 14:31:05 +04:00
/* Caller must ensure led_cdev->trigger_lock held */
void led_trigger_set ( struct led_classdev * led_cdev , struct led_trigger * trigger )
{
unsigned long flags ;
/* Remove any existing trigger */
if ( led_cdev - > trigger ) {
write_lock_irqsave ( & led_cdev - > trigger - > leddev_list_lock , flags ) ;
list_del ( & led_cdev - > trig_list ) ;
2008-03-09 23:59:57 +03:00
write_unlock_irqrestore ( & led_cdev - > trigger - > leddev_list_lock ,
flags ) ;
2006-03-31 14:31:05 +04:00
if ( led_cdev - > trigger - > deactivate )
led_cdev - > trigger - > deactivate ( led_cdev ) ;
2008-07-17 01:51:14 +04:00
led_cdev - > trigger = NULL ;
2006-10-01 10:27:41 +04:00
led_set_brightness ( led_cdev , LED_OFF ) ;
2006-03-31 14:31:05 +04:00
}
if ( trigger ) {
write_lock_irqsave ( & trigger - > leddev_list_lock , flags ) ;
list_add_tail ( & led_cdev - > trig_list , & trigger - > led_cdevs ) ;
write_unlock_irqrestore ( & trigger - > leddev_list_lock , flags ) ;
2008-07-17 01:51:14 +04:00
led_cdev - > trigger = trigger ;
2006-03-31 14:31:05 +04:00
if ( trigger - > activate )
trigger - > activate ( led_cdev ) ;
}
}
2008-03-09 23:59:57 +03:00
EXPORT_SYMBOL_GPL ( led_trigger_set ) ;
2006-03-31 14:31:05 +04:00
2008-03-09 23:54:37 +03:00
void led_trigger_remove ( struct led_classdev * led_cdev )
{
down_write ( & led_cdev - > trigger_lock ) ;
led_trigger_set ( led_cdev , NULL ) ;
up_write ( & led_cdev - > trigger_lock ) ;
}
2008-03-09 23:59:57 +03:00
EXPORT_SYMBOL_GPL ( led_trigger_remove ) ;
2008-03-09 23:54:37 +03:00
2006-03-31 14:31:05 +04:00
void led_trigger_set_default ( struct led_classdev * led_cdev )
{
struct led_trigger * trig ;
if ( ! led_cdev - > default_trigger )
return ;
2007-11-10 16:29:04 +03:00
down_read ( & triggers_list_lock ) ;
down_write ( & led_cdev - > trigger_lock ) ;
2006-03-31 14:31:05 +04:00
list_for_each_entry ( trig , & trigger_list , next_trig ) {
if ( ! strcmp ( led_cdev - > default_trigger , trig - > name ) )
led_trigger_set ( led_cdev , trig ) ;
}
2007-11-10 16:29:04 +03:00
up_write ( & led_cdev - > trigger_lock ) ;
up_read ( & triggers_list_lock ) ;
2006-03-31 14:31:05 +04:00
}
2008-03-09 23:59:57 +03:00
EXPORT_SYMBOL_GPL ( led_trigger_set_default ) ;
/* LED Trigger Interface */
2006-03-31 14:31:05 +04:00
int led_trigger_register ( struct led_trigger * trigger )
{
struct led_classdev * led_cdev ;
rwlock_init ( & trigger - > leddev_list_lock ) ;
INIT_LIST_HEAD ( & trigger - > led_cdevs ) ;
/* Add to the list of led triggers */
2007-11-10 16:29:04 +03:00
down_write ( & triggers_list_lock ) ;
2006-03-31 14:31:05 +04:00
list_add_tail ( & trigger - > next_trig , & trigger_list ) ;
2007-11-10 16:29:04 +03:00
up_write ( & triggers_list_lock ) ;
2006-03-31 14:31:05 +04:00
/* Register with any LEDs that have this as a default trigger */
2008-01-01 02:09:44 +03:00
down_read ( & leds_list_lock ) ;
2006-03-31 14:31:05 +04:00
list_for_each_entry ( led_cdev , & leds_list , node ) {
2007-11-10 16:29:04 +03:00
down_write ( & led_cdev - > trigger_lock ) ;
2006-03-31 14:31:05 +04:00
if ( ! led_cdev - > trigger & & led_cdev - > default_trigger & &
! strcmp ( led_cdev - > default_trigger , trigger - > name ) )
led_trigger_set ( led_cdev , trigger ) ;
2007-11-10 16:29:04 +03:00
up_write ( & led_cdev - > trigger_lock ) ;
2006-03-31 14:31:05 +04:00
}
2008-01-01 02:09:44 +03:00
up_read ( & leds_list_lock ) ;
2006-03-31 14:31:05 +04:00
return 0 ;
}
2008-03-09 23:59:57 +03:00
EXPORT_SYMBOL_GPL ( led_trigger_register ) ;
2006-03-31 14:31:05 +04:00
void led_trigger_unregister ( struct led_trigger * trigger )
{
struct led_classdev * led_cdev ;
/* Remove from the list of led triggers */
2007-11-10 16:29:04 +03:00
down_write ( & triggers_list_lock ) ;
2006-03-31 14:31:05 +04:00
list_del ( & trigger - > next_trig ) ;
2007-11-10 16:29:04 +03:00
up_write ( & triggers_list_lock ) ;
2006-03-31 14:31:05 +04:00
/* Remove anyone actively using this trigger */
2008-01-01 02:09:44 +03:00
down_read ( & leds_list_lock ) ;
2006-03-31 14:31:05 +04:00
list_for_each_entry ( led_cdev , & leds_list , node ) {
2007-11-10 16:29:04 +03:00
down_write ( & led_cdev - > trigger_lock ) ;
2006-03-31 14:31:05 +04:00
if ( led_cdev - > trigger = = trigger )
led_trigger_set ( led_cdev , NULL ) ;
2007-11-10 16:29:04 +03:00
up_write ( & led_cdev - > trigger_lock ) ;
2006-03-31 14:31:05 +04:00
}
2008-01-01 02:09:44 +03:00
up_read ( & leds_list_lock ) ;
2006-03-31 14:31:05 +04:00
}
2008-03-09 23:59:57 +03:00
EXPORT_SYMBOL_GPL ( led_trigger_unregister ) ;
2006-03-31 14:31:05 +04:00
2008-03-09 23:59:57 +03:00
/* Simple LED Tigger Interface */
void led_trigger_event ( struct led_trigger * trigger ,
enum led_brightness brightness )
2006-03-31 14:31:05 +04:00
{
2008-03-09 23:59:57 +03:00
struct list_head * entry ;
if ( ! trigger )
return ;
read_lock ( & trigger - > leddev_list_lock ) ;
list_for_each ( entry , & trigger - > led_cdevs ) {
struct led_classdev * led_cdev ;
led_cdev = list_entry ( entry , struct led_classdev , trig_list ) ;
led_set_brightness ( led_cdev , brightness ) ;
}
read_unlock ( & trigger - > leddev_list_lock ) ;
2006-03-31 14:31:05 +04:00
}
2008-03-09 23:59:57 +03:00
EXPORT_SYMBOL_GPL ( led_trigger_event ) ;
2006-03-31 14:31:05 +04:00
2008-03-09 23:59:57 +03:00
void led_trigger_register_simple ( const char * name , struct led_trigger * * tp )
{
struct led_trigger * trigger ;
int err ;
2006-03-31 14:31:05 +04:00
2008-03-09 23:59:57 +03:00
trigger = kzalloc ( sizeof ( struct led_trigger ) , GFP_KERNEL ) ;
2006-03-31 14:31:05 +04:00
2008-03-09 23:59:57 +03:00
if ( trigger ) {
trigger - > name = name ;
err = led_trigger_register ( trigger ) ;
if ( err < 0 )
printk ( KERN_WARNING " LED trigger %s failed to register "
" (%d) \n " , name , err ) ;
} else
printk ( KERN_WARNING " LED trigger %s failed to register "
" (no memory) \n " , name ) ;
* tp = trigger ;
}
2006-03-31 14:31:05 +04:00
EXPORT_SYMBOL_GPL ( led_trigger_register_simple ) ;
2008-03-09 23:59:57 +03:00
void led_trigger_unregister_simple ( struct led_trigger * trigger )
{
if ( trigger )
led_trigger_unregister ( trigger ) ;
kfree ( trigger ) ;
}
2006-03-31 14:31:05 +04:00
EXPORT_SYMBOL_GPL ( led_trigger_unregister_simple ) ;
MODULE_AUTHOR ( " Richard Purdie " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " LED Triggers Core " ) ;