2007-09-27 21:35:34 +02:00
/*
Broadcom B43 wireless driver
RFKILL support
Copyright ( c ) 2007 Michael Buesch < mb @ bu3sch . de >
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 ; see the file COPYING . If not , write to
the Free Software Foundation , Inc . , 51 Franklin Steet , Fifth Floor ,
Boston , MA 02110 - 1301 , USA .
*/
# include "rfkill.h"
# include "b43.h"
2008-08-27 18:53:02 +02:00
# include "phy_common.h"
2007-09-27 21:35:34 +02:00
2007-12-14 13:59:11 +01:00
# include <linux/kmod.h>
2007-09-27 21:35:34 +02:00
2007-09-28 14:22:33 +02:00
/* Returns TRUE, if the radio is enabled in hardware. */
static bool b43_is_hw_radio_enabled ( struct b43_wldev * dev )
2007-09-27 21:35:34 +02:00
{
2007-09-28 14:22:33 +02:00
if ( dev - > phy . rev > = 3 ) {
if ( ! ( b43_read32 ( dev , B43_MMIO_RADIO_HWENABLED_HI )
& B43_MMIO_RADIO_HWENABLED_HI_MASK ) )
return 1 ;
} else {
if ( b43_read16 ( dev , B43_MMIO_RADIO_HWENABLED_LO )
& B43_MMIO_RADIO_HWENABLED_LO_MASK )
return 1 ;
2007-09-27 21:35:34 +02:00
}
2007-09-28 14:22:33 +02:00
return 0 ;
2007-09-27 21:35:34 +02:00
}
2007-09-28 14:22:33 +02:00
/* The poll callback for the hardware button. */
static void b43_rfkill_poll ( struct input_polled_dev * poll_dev )
2007-09-27 21:35:34 +02:00
{
2007-09-28 14:22:33 +02:00
struct b43_wldev * dev = poll_dev - > private ;
2007-09-27 21:35:34 +02:00
struct b43_wl * wl = dev - > wl ;
2007-09-28 14:22:33 +02:00
bool enabled ;
2007-11-03 14:34:32 +01:00
bool report_change = 0 ;
2007-09-27 21:35:34 +02:00
2007-09-28 14:22:33 +02:00
mutex_lock ( & wl - > mutex ) ;
2007-12-14 13:59:11 +01:00
if ( unlikely ( b43_status ( dev ) < B43_STAT_INITIALIZED ) ) {
mutex_unlock ( & wl - > mutex ) ;
return ;
}
2007-09-28 14:22:33 +02:00
enabled = b43_is_hw_radio_enabled ( dev ) ;
if ( unlikely ( enabled ! = dev - > radio_hw_enable ) ) {
dev - > radio_hw_enable = enabled ;
2007-11-03 14:34:32 +01:00
report_change = 1 ;
2007-09-28 14:22:33 +02:00
b43info ( wl , " Radio hardware status changed to %s \n " ,
enabled ? " ENABLED " : " DISABLED " ) ;
2007-11-03 14:34:32 +01:00
}
mutex_unlock ( & wl - > mutex ) ;
2007-12-14 13:59:11 +01:00
/* send the radio switch event to the system - note both a key press
* and a release are required */
if ( unlikely ( report_change ) ) {
input_report_key ( poll_dev - > input , KEY_WLAN , 1 ) ;
input_report_key ( poll_dev - > input , KEY_WLAN , 0 ) ;
}
2007-09-27 21:35:34 +02:00
}
2007-10-28 17:27:10 +01:00
/* Called when the RFKILL toggled in software. */
2007-09-27 21:35:34 +02:00
static int b43_rfkill_soft_toggle ( void * data , enum rfkill_state state )
{
struct b43_wldev * dev = data ;
struct b43_wl * wl = dev - > wl ;
2007-12-14 13:59:11 +01:00
int err = - EBUSY ;
2007-09-27 21:35:34 +02:00
2007-11-03 14:34:32 +01:00
if ( ! wl - > rfkill . registered )
return 0 ;
2007-09-27 21:35:34 +02:00
2007-11-03 14:34:32 +01:00
mutex_lock ( & wl - > mutex ) ;
2007-12-14 13:59:11 +01:00
if ( b43_status ( dev ) < B43_STAT_INITIALIZED )
goto out_unlock ;
err = 0 ;
2007-09-27 21:35:34 +02:00
switch ( state ) {
2008-06-27 10:27:47 -04:00
case RFKILL_STATE_UNBLOCKED :
2007-09-27 21:35:34 +02:00
if ( ! dev - > radio_hw_enable ) {
/* No luck. We can't toggle the hardware RF-kill
* button from software . */
err = - EBUSY ;
goto out_unlock ;
}
if ( ! dev - > phy . radio_on )
2008-08-27 18:53:02 +02:00
b43_software_rfkill ( dev , state ) ;
2007-09-27 21:35:34 +02:00
break ;
2008-06-27 10:27:47 -04:00
case RFKILL_STATE_SOFT_BLOCKED :
2007-09-27 21:35:34 +02:00
if ( dev - > phy . radio_on )
2008-08-27 18:53:02 +02:00
b43_software_rfkill ( dev , state ) ;
2007-09-27 21:35:34 +02:00
break ;
2008-06-27 10:27:47 -04:00
default :
b43warn ( wl , " Received unexpected rfkill state %d. \n " , state ) ;
break ;
2007-09-27 21:35:34 +02:00
}
out_unlock :
mutex_unlock ( & wl - > mutex ) ;
return err ;
}
2009-02-24 02:16:42 -08:00
char * b43_rfkill_led_name ( struct b43_wldev * dev )
2007-09-27 21:35:34 +02:00
{
2007-11-03 14:34:32 +01:00
struct b43_rfkill * rfk = & ( dev - > wl - > rfkill ) ;
2007-09-27 21:35:34 +02:00
2007-11-03 14:34:32 +01:00
if ( ! rfk - > registered )
2007-09-27 21:35:34 +02:00
return NULL ;
2007-11-03 14:34:32 +01:00
return rfkill_get_led_name ( rfk - > rfkill ) ;
2007-09-27 21:35:34 +02:00
}
void b43_rfkill_init ( struct b43_wldev * dev )
{
struct b43_wl * wl = dev - > wl ;
struct b43_rfkill * rfk = & ( wl - > rfkill ) ;
int err ;
2007-11-03 14:34:32 +01:00
rfk - > registered = 0 ;
2007-09-28 14:22:33 +02:00
2007-11-03 14:34:32 +01:00
rfk - > rfkill = rfkill_allocate ( dev - > dev - > dev , RFKILL_TYPE_WLAN ) ;
if ( ! rfk - > rfkill )
goto out_error ;
2007-09-27 21:35:34 +02:00
snprintf ( rfk - > name , sizeof ( rfk - > name ) ,
" b43-%s " , wiphy_name ( wl - > hw - > wiphy ) ) ;
rfk - > rfkill - > name = rfk - > name ;
2008-06-28 14:20:40 +02:00
rfk - > rfkill - > state = RFKILL_STATE_UNBLOCKED ;
2007-09-27 21:35:34 +02:00
rfk - > rfkill - > data = dev ;
rfk - > rfkill - > toggle_radio = b43_rfkill_soft_toggle ;
rfk - > rfkill - > user_claim_unsupported = 1 ;
2007-09-28 14:22:33 +02:00
rfk - > poll_dev = input_allocate_polled_device ( ) ;
2008-01-13 18:30:14 +01:00
if ( ! rfk - > poll_dev ) {
rfkill_free ( rfk - > rfkill ) ;
goto err_freed_rfk ;
}
2007-11-03 14:34:32 +01:00
rfk - > poll_dev - > private = dev ;
rfk - > poll_dev - > poll = b43_rfkill_poll ;
rfk - > poll_dev - > poll_interval = 1000 ; /* msecs */
2007-12-14 13:59:11 +01:00
rfk - > poll_dev - > input - > name = rfk - > name ;
rfk - > poll_dev - > input - > id . bustype = BUS_HOST ;
rfk - > poll_dev - > input - > id . vendor = dev - > dev - > bus - > boardinfo . vendor ;
rfk - > poll_dev - > input - > evbit [ 0 ] = BIT ( EV_KEY ) ;
set_bit ( KEY_WLAN , rfk - > poll_dev - > input - > keybit ) ;
2007-11-03 14:34:32 +01:00
err = rfkill_register ( rfk - > rfkill ) ;
if ( err )
goto err_free_polldev ;
2007-12-14 13:59:11 +01:00
# ifdef CONFIG_RFKILL_INPUT_MODULE
/* B43 RF-kill isn't useful without the rfkill-input subsystem.
* Try to load the module . */
err = request_module ( " rfkill-input " ) ;
if ( err )
b43warn ( wl , " Failed to load the rfkill-input module. "
" The built-in radio LED will not work. \n " ) ;
# endif /* CONFIG_RFKILL_INPUT */
2008-09-16 14:56:49 -05:00
# if !defined(CONFIG_RFKILL_INPUT) && !defined(CONFIG_RFKILL_INPUT_MODULE)
b43warn ( wl , " The rfkill-input subsystem is not available. "
" The built-in radio LED will not work. \n " ) ;
# endif
2007-11-03 14:34:32 +01:00
err = input_register_polled_device ( rfk - > poll_dev ) ;
if ( err )
goto err_unreg_rfk ;
rfk - > registered = 1 ;
return ;
err_unreg_rfk :
rfkill_unregister ( rfk - > rfkill ) ;
err_free_polldev :
input_free_polled_device ( rfk - > poll_dev ) ;
rfk - > poll_dev = NULL ;
2008-01-13 18:30:14 +01:00
err_freed_rfk :
2007-11-03 14:34:32 +01:00
rfk - > rfkill = NULL ;
out_error :
rfk - > registered = 0 ;
b43warn ( wl , " RF-kill button init failed \n " ) ;
2007-09-27 21:35:34 +02:00
}
2007-11-03 14:34:32 +01:00
void b43_rfkill_exit ( struct b43_wldev * dev )
2007-09-27 21:35:34 +02:00
{
struct b43_rfkill * rfk = & ( dev - > wl - > rfkill ) ;
2007-11-03 14:34:32 +01:00
if ( ! rfk - > registered )
return ;
rfk - > registered = 0 ;
input_unregister_polled_device ( rfk - > poll_dev ) ;
rfkill_unregister ( rfk - > rfkill ) ;
2007-09-28 14:22:33 +02:00
input_free_polled_device ( rfk - > poll_dev ) ;
rfk - > poll_dev = NULL ;
2007-09-27 21:35:34 +02:00
rfk - > rfkill = NULL ;
}