2007-05-07 00:34:20 -07:00
/*
2007-09-27 14:57:05 -07:00
* Copyright ( C ) 2006 - 2007 Ivo van Doorn
2007-05-07 00:34:20 -07:00
* Copyright ( C ) 2007 Dmitry Torokhov
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the
* Free Software Foundation , Inc . ,
* 59 Temple Place - Suite 330 , Boston , MA 02111 - 1307 , USA .
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/workqueue.h>
# include <linux/capability.h>
# include <linux/list.h>
# include <linux/mutex.h>
# include <linux/rfkill.h>
2007-11-02 20:18:11 +01:00
/* Get declaration of rfkill_switch_all() to shut up sparse. */
# include "rfkill-input.h"
2007-05-07 00:34:20 -07:00
MODULE_AUTHOR ( " Ivo van Doorn <IvDoorn@gmail.com> " ) ;
MODULE_VERSION ( " 1.0 " ) ;
MODULE_DESCRIPTION ( " RF switch support " ) ;
MODULE_LICENSE ( " GPL " ) ;
static LIST_HEAD ( rfkill_list ) ; /* list of registered rf switches */
static DEFINE_MUTEX ( rfkill_mutex ) ;
static enum rfkill_state rfkill_states [ RFKILL_TYPE_MAX ] ;
2007-09-27 21:33:12 +02:00
static void rfkill_led_trigger ( struct rfkill * rfkill ,
enum rfkill_state state )
{
# ifdef CONFIG_RFKILL_LEDS
struct led_trigger * led = & rfkill - > led_trigger ;
if ( ! led - > name )
return ;
if ( state = = RFKILL_STATE_OFF )
led_trigger_event ( led , LED_OFF ) ;
else
led_trigger_event ( led , LED_FULL ) ;
# endif /* CONFIG_RFKILL_LEDS */
}
2007-05-07 00:34:20 -07:00
static int rfkill_toggle_radio ( struct rfkill * rfkill ,
enum rfkill_state state )
{
2007-11-28 17:49:34 +01:00
int retval = 0 ;
2007-05-07 00:34:20 -07:00
if ( state ! = rfkill - > state ) {
retval = rfkill - > toggle_radio ( rfkill - > data , state ) ;
2007-09-27 21:33:12 +02:00
if ( ! retval ) {
2007-05-07 00:34:20 -07:00
rfkill - > state = state ;
2007-09-27 21:33:12 +02:00
rfkill_led_trigger ( rfkill , state ) ;
}
2007-05-07 00:34:20 -07:00
}
return retval ;
}
/**
* rfkill_switch_all - Toggle state of all switches of given type
* @ type : type of interfaces to be affeceted
* @ state : the new state
*
* This function toggles state of all switches of given type unless
* a specific switch is claimed by userspace in which case it is
* left alone .
*/
void rfkill_switch_all ( enum rfkill_type type , enum rfkill_state state )
{
struct rfkill * rfkill ;
mutex_lock ( & rfkill_mutex ) ;
rfkill_states [ type ] = state ;
list_for_each_entry ( rfkill , & rfkill_list , node ) {
if ( ! rfkill - > user_claim )
rfkill_toggle_radio ( rfkill , state ) ;
}
mutex_unlock ( & rfkill_mutex ) ;
}
EXPORT_SYMBOL ( rfkill_switch_all ) ;
static ssize_t rfkill_name_show ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
struct rfkill * rfkill = to_rfkill ( dev ) ;
return sprintf ( buf , " %s \n " , rfkill - > name ) ;
}
static ssize_t rfkill_type_show ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
struct rfkill * rfkill = to_rfkill ( dev ) ;
const char * type ;
switch ( rfkill - > type ) {
case RFKILL_TYPE_WLAN :
type = " wlan " ;
break ;
case RFKILL_TYPE_BLUETOOTH :
type = " bluetooth " ;
break ;
2007-09-13 09:21:31 +02:00
case RFKILL_TYPE_UWB :
type = " ultrawideband " ;
break ;
2007-05-07 00:34:20 -07:00
default :
BUG ( ) ;
}
return sprintf ( buf , " %s \n " , type ) ;
}
static ssize_t rfkill_state_show ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
struct rfkill * rfkill = to_rfkill ( dev ) ;
return sprintf ( buf , " %d \n " , rfkill - > state ) ;
}
static ssize_t rfkill_state_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
struct rfkill * rfkill = to_rfkill ( dev ) ;
unsigned int state = simple_strtoul ( buf , NULL , 0 ) ;
int error ;
if ( ! capable ( CAP_NET_ADMIN ) )
return - EPERM ;
2007-11-28 17:49:34 +01:00
if ( mutex_lock_interruptible ( & rfkill - > mutex ) )
return - ERESTARTSYS ;
2007-05-07 00:34:20 -07:00
error = rfkill_toggle_radio ( rfkill ,
state ? RFKILL_STATE_ON : RFKILL_STATE_OFF ) ;
2007-11-28 17:49:34 +01:00
mutex_unlock ( & rfkill - > mutex ) ;
2007-05-07 00:34:20 -07:00
2007-11-28 17:49:34 +01:00
return error ? error : count ;
2007-05-07 00:34:20 -07:00
}
static ssize_t rfkill_claim_show ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
struct rfkill * rfkill = to_rfkill ( dev ) ;
return sprintf ( buf , " %d " , rfkill - > user_claim ) ;
}
static ssize_t rfkill_claim_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
struct rfkill * rfkill = to_rfkill ( dev ) ;
bool claim = ! ! simple_strtoul ( buf , NULL , 0 ) ;
int error ;
if ( ! capable ( CAP_NET_ADMIN ) )
return - EPERM ;
/*
* Take the global lock to make sure the kernel is not in
* the middle of rfkill_switch_all
*/
error = mutex_lock_interruptible ( & rfkill_mutex ) ;
if ( error )
return error ;
2007-09-27 21:34:23 +02:00
if ( rfkill - > user_claim_unsupported ) {
error = - EOPNOTSUPP ;
goto out_unlock ;
}
2007-05-07 00:34:20 -07:00
if ( rfkill - > user_claim ! = claim ) {
if ( ! claim )
rfkill_toggle_radio ( rfkill ,
rfkill_states [ rfkill - > type ] ) ;
rfkill - > user_claim = claim ;
}
2007-09-27 21:34:23 +02:00
out_unlock :
2007-05-07 00:34:20 -07:00
mutex_unlock ( & rfkill_mutex ) ;
2007-09-27 21:34:23 +02:00
return error ? error : count ;
2007-05-07 00:34:20 -07:00
}
static struct device_attribute rfkill_dev_attrs [ ] = {
__ATTR ( name , S_IRUGO , rfkill_name_show , NULL ) ,
__ATTR ( type , S_IRUGO , rfkill_type_show , NULL ) ,
2007-07-18 15:38:03 -07:00
__ATTR ( state , S_IRUGO | S_IWUSR , rfkill_state_show , rfkill_state_store ) ,
2007-05-07 00:34:20 -07:00
__ATTR ( claim , S_IRUGO | S_IWUSR , rfkill_claim_show , rfkill_claim_store ) ,
__ATTR_NULL
} ;
static void rfkill_release ( struct device * dev )
{
struct rfkill * rfkill = to_rfkill ( dev ) ;
kfree ( rfkill ) ;
module_put ( THIS_MODULE ) ;
}
# ifdef CONFIG_PM
static int rfkill_suspend ( struct device * dev , pm_message_t state )
{
struct rfkill * rfkill = to_rfkill ( dev ) ;
if ( dev - > power . power_state . event ! = state . event ) {
if ( state . event = = PM_EVENT_SUSPEND ) {
mutex_lock ( & rfkill - > mutex ) ;
if ( rfkill - > state = = RFKILL_STATE_ON )
rfkill - > toggle_radio ( rfkill - > data ,
RFKILL_STATE_OFF ) ;
mutex_unlock ( & rfkill - > mutex ) ;
}
dev - > power . power_state = state ;
}
return 0 ;
}
static int rfkill_resume ( struct device * dev )
{
struct rfkill * rfkill = to_rfkill ( dev ) ;
if ( dev - > power . power_state . event ! = PM_EVENT_ON ) {
mutex_lock ( & rfkill - > mutex ) ;
if ( rfkill - > state = = RFKILL_STATE_ON )
rfkill - > toggle_radio ( rfkill - > data , RFKILL_STATE_ON ) ;
mutex_unlock ( & rfkill - > mutex ) ;
}
dev - > power . power_state = PMSG_ON ;
return 0 ;
}
# else
# define rfkill_suspend NULL
# define rfkill_resume NULL
# endif
static struct class rfkill_class = {
. name = " rfkill " ,
. dev_release = rfkill_release ,
. dev_attrs = rfkill_dev_attrs ,
. suspend = rfkill_suspend ,
. resume = rfkill_resume ,
} ;
static int rfkill_add_switch ( struct rfkill * rfkill )
{
2007-10-28 15:16:50 +01:00
int error ;
2007-05-07 00:34:20 -07:00
2007-10-28 15:16:50 +01:00
mutex_lock ( & rfkill_mutex ) ;
2007-05-07 00:34:20 -07:00
2007-10-28 15:16:50 +01:00
error = rfkill_toggle_radio ( rfkill , rfkill_states [ rfkill - > type ] ) ;
if ( ! error )
list_add_tail ( & rfkill - > node , & rfkill_list ) ;
2007-05-07 00:34:20 -07:00
mutex_unlock ( & rfkill_mutex ) ;
2007-10-28 15:16:50 +01:00
return error ;
2007-05-07 00:34:20 -07:00
}
static void rfkill_remove_switch ( struct rfkill * rfkill )
{
mutex_lock ( & rfkill_mutex ) ;
list_del_init ( & rfkill - > node ) ;
rfkill_toggle_radio ( rfkill , RFKILL_STATE_OFF ) ;
mutex_unlock ( & rfkill_mutex ) ;
}
/**
* rfkill_allocate - allocate memory for rfkill structure .
* @ parent : device that has rf switch on it
2007-09-13 09:20:42 +02:00
* @ type : type of the switch ( RFKILL_TYPE_ * )
2007-05-07 00:34:20 -07:00
*
* This function should be called by the network driver when it needs
* rfkill structure . Once the structure is allocated the driver shoud
* finish its initialization by setting name , private data , enable_radio
* and disable_radio methods and then register it with rfkill_register ( ) .
* NOTE : If registration fails the structure shoudl be freed by calling
* rfkill_free ( ) otherwise rfkill_unregister ( ) should be used .
*/
struct rfkill * rfkill_allocate ( struct device * parent , enum rfkill_type type )
{
struct rfkill * rfkill ;
struct device * dev ;
rfkill = kzalloc ( sizeof ( struct rfkill ) , GFP_KERNEL ) ;
2007-05-19 12:24:39 -07:00
if ( ! rfkill )
2007-05-07 00:34:20 -07:00
return NULL ;
mutex_init ( & rfkill - > mutex ) ;
INIT_LIST_HEAD ( & rfkill - > node ) ;
rfkill - > type = type ;
dev = & rfkill - > dev ;
dev - > class = & rfkill_class ;
dev - > parent = parent ;
device_initialize ( dev ) ;
__module_get ( THIS_MODULE ) ;
return rfkill ;
}
EXPORT_SYMBOL ( rfkill_allocate ) ;
/**
* rfkill_free - Mark rfkill structure for deletion
* @ rfkill : rfkill structure to be destroyed
*
* Decrements reference count of rfkill structure so it is destoryed .
* Note that rfkill_free ( ) should _not_ be called after rfkill_unregister ( ) .
*/
void rfkill_free ( struct rfkill * rfkill )
{
if ( rfkill )
put_device ( & rfkill - > dev ) ;
}
EXPORT_SYMBOL ( rfkill_free ) ;
2007-09-27 21:33:12 +02:00
static void rfkill_led_trigger_register ( struct rfkill * rfkill )
{
# ifdef CONFIG_RFKILL_LEDS
int error ;
rfkill - > led_trigger . name = rfkill - > dev . bus_id ;
error = led_trigger_register ( & rfkill - > led_trigger ) ;
if ( error )
rfkill - > led_trigger . name = NULL ;
# endif /* CONFIG_RFKILL_LEDS */
}
static void rfkill_led_trigger_unregister ( struct rfkill * rfkill )
{
# ifdef CONFIG_RFKILL_LEDS
if ( rfkill - > led_trigger . name )
led_trigger_unregister ( & rfkill - > led_trigger ) ;
# endif
}
2007-05-07 00:34:20 -07:00
/**
* rfkill_register - Register a rfkill structure .
* @ rfkill : rfkill structure to be registered
*
* This function should be called by the network driver when the rfkill
* structure needs to be registered . Immediately from registration the
* switch driver should be able to service calls to toggle_radio .
*/
int rfkill_register ( struct rfkill * rfkill )
{
static atomic_t rfkill_no = ATOMIC_INIT ( 0 ) ;
struct device * dev = & rfkill - > dev ;
int error ;
if ( ! rfkill - > toggle_radio )
return - EINVAL ;
2007-10-28 15:16:50 +01:00
if ( rfkill - > type > = RFKILL_TYPE_MAX )
return - EINVAL ;
2007-05-07 00:34:20 -07:00
2007-10-28 13:07:54 +01:00
snprintf ( dev - > bus_id , sizeof ( dev - > bus_id ) ,
" rfkill%ld " , ( long ) atomic_inc_return ( & rfkill_no ) - 1 ) ;
rfkill_led_trigger_register ( rfkill ) ;
2007-05-07 00:34:20 -07:00
error = rfkill_add_switch ( rfkill ) ;
2008-01-13 16:20:56 -05:00
if ( error ) {
rfkill_led_trigger_unregister ( rfkill ) ;
2007-05-07 00:34:20 -07:00
return error ;
2008-01-13 16:20:56 -05:00
}
2007-05-07 00:34:20 -07:00
error = device_add ( dev ) ;
if ( error ) {
2008-01-13 16:20:56 -05:00
rfkill_led_trigger_unregister ( rfkill ) ;
2007-05-07 00:34:20 -07:00
rfkill_remove_switch ( rfkill ) ;
return error ;
}
return 0 ;
}
EXPORT_SYMBOL ( rfkill_register ) ;
/**
* rfkill_unregister - Uegister a rfkill structure .
* @ rfkill : rfkill structure to be unregistered
*
* This function should be called by the network driver during device
* teardown to destroy rfkill structure . Note that rfkill_free ( ) should
* _not_ be called after rfkill_unregister ( ) .
*/
void rfkill_unregister ( struct rfkill * rfkill )
{
device_del ( & rfkill - > dev ) ;
rfkill_remove_switch ( rfkill ) ;
2007-10-28 13:07:54 +01:00
rfkill_led_trigger_unregister ( rfkill ) ;
2007-05-07 00:34:20 -07:00
put_device ( & rfkill - > dev ) ;
}
EXPORT_SYMBOL ( rfkill_unregister ) ;
/*
* Rfkill module initialization / deinitialization .
*/
static int __init rfkill_init ( void )
{
int error ;
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( rfkill_states ) ; i + + )
rfkill_states [ i ] = RFKILL_STATE_ON ;
error = class_register ( & rfkill_class ) ;
if ( error ) {
printk ( KERN_ERR " rfkill: unable to register rfkill class \n " ) ;
return error ;
}
return 0 ;
}
static void __exit rfkill_exit ( void )
{
class_unregister ( & rfkill_class ) ;
}
2007-10-28 14:39:02 +01:00
subsys_initcall ( rfkill_init ) ;
2007-05-07 00:34:20 -07:00
module_exit ( rfkill_exit ) ;