2008-11-17 18:53:42 +03:00
/*
* UWB radio ( channel ) management .
*
* Copyright ( C ) 2008 Cambridge Silicon Radio Ltd .
*
* 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 .
*
* 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 , see < http : //www.gnu.org/licenses/>.
*/
# include <linux/kernel.h>
# include <linux/uwb.h>
2011-07-10 21:18:02 +04:00
# include <linux/export.h>
2008-11-17 18:53:42 +03:00
# include "uwb-internal.h"
static int uwb_radio_select_channel ( struct uwb_rc * rc )
{
/*
* Default to channel 9 ( BG1 , TFC1 ) unless the user has
* selected a specific channel or there are no active PALs .
*/
if ( rc - > active_pals = = 0 )
return - 1 ;
if ( rc - > beaconing_forced )
return rc - > beaconing_forced ;
return 9 ;
}
/*
* Notify all active PALs that the channel has changed .
*/
static void uwb_radio_channel_changed ( struct uwb_rc * rc , int channel )
{
struct uwb_pal * pal ;
list_for_each_entry ( pal , & rc - > pals , node ) {
if ( pal - > channel & & channel ! = pal - > channel ) {
pal - > channel = channel ;
if ( pal - > channel_changed )
pal - > channel_changed ( pal , pal - > channel ) ;
}
}
}
/*
* Change to a new channel and notify any active PALs of the new
* channel .
*
* When stopping the radio , PALs need to be notified first so they can
* terminate any active reservations .
*/
static int uwb_radio_change_channel ( struct uwb_rc * rc , int channel )
{
int ret = 0 ;
2013-12-20 21:54:07 +04:00
struct device * dev = & rc - > uwb_dev . dev ;
dev_dbg ( dev , " %s: channel = %d, rc->beaconing = %d \n " , __func__ ,
channel , rc - > beaconing ) ;
2008-11-17 18:53:42 +03:00
if ( channel = = - 1 )
uwb_radio_channel_changed ( rc , channel ) ;
if ( channel ! = rc - > beaconing ) {
if ( rc - > beaconing ! = - 1 & & channel ! = - 1 ) {
/*
* FIXME : should signal the channel change
* with a Channel Change IE .
*/
ret = uwb_radio_change_channel ( rc , - 1 ) ;
if ( ret < 0 )
return ret ;
}
ret = uwb_rc_beacon ( rc , channel , 0 ) ;
}
if ( channel ! = - 1 )
uwb_radio_channel_changed ( rc , rc - > beaconing ) ;
return ret ;
}
/**
* uwb_radio_start - request that the radio be started
* @ pal : the PAL making the request .
*
2013-12-20 21:54:08 +04:00
* If the radio is not already active , a suitable channel is selected
2008-11-17 18:53:42 +03:00
* and beacons are started .
*/
int uwb_radio_start ( struct uwb_pal * pal )
{
struct uwb_rc * rc = pal - > rc ;
int ret = 0 ;
mutex_lock ( & rc - > uwb_dev . mutex ) ;
if ( ! pal - > channel ) {
pal - > channel = - 1 ;
rc - > active_pals + + ;
ret = uwb_radio_change_channel ( rc , uwb_radio_select_channel ( rc ) ) ;
}
mutex_unlock ( & rc - > uwb_dev . mutex ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( uwb_radio_start ) ;
/**
2014-04-28 23:53:26 +04:00
* uwb_radio_stop - request that the radio be stopped .
2008-11-17 18:53:42 +03:00
* @ pal : the PAL making the request .
*
* Stops the radio if no other PAL is making use of it .
*/
void uwb_radio_stop ( struct uwb_pal * pal )
{
struct uwb_rc * rc = pal - > rc ;
mutex_lock ( & rc - > uwb_dev . mutex ) ;
if ( pal - > channel ) {
rc - > active_pals - - ;
uwb_radio_change_channel ( rc , uwb_radio_select_channel ( rc ) ) ;
pal - > channel = 0 ;
}
mutex_unlock ( & rc - > uwb_dev . mutex ) ;
}
EXPORT_SYMBOL_GPL ( uwb_radio_stop ) ;
/*
* uwb_radio_force_channel - force a specific channel to be used
* @ rc : the radio controller .
* @ channel : the channel to use ; - 1 to force the radio to stop ; 0 to
* use the default channel selection algorithm .
*/
int uwb_radio_force_channel ( struct uwb_rc * rc , int channel )
{
int ret = 0 ;
mutex_lock ( & rc - > uwb_dev . mutex ) ;
rc - > beaconing_forced = channel ;
ret = uwb_radio_change_channel ( rc , uwb_radio_select_channel ( rc ) ) ;
mutex_unlock ( & rc - > uwb_dev . mutex ) ;
return ret ;
}
/*
* uwb_radio_setup - setup the radio manager
* @ rc : the radio controller .
*
* The radio controller is reset to ensure it ' s in a known state
* before it ' s used .
*/
int uwb_radio_setup ( struct uwb_rc * rc )
{
return uwb_rc_reset ( rc ) ;
}
/*
* uwb_radio_reset_state - reset any radio manager state
* @ rc : the radio controller .
*
* All internal radio manager state is reset to values corresponding
* to a reset radio controller .
*/
void uwb_radio_reset_state ( struct uwb_rc * rc )
{
struct uwb_pal * pal ;
mutex_lock ( & rc - > uwb_dev . mutex ) ;
list_for_each_entry ( pal , & rc - > pals , node ) {
if ( pal - > channel ) {
pal - > channel = - 1 ;
if ( pal - > channel_changed )
pal - > channel_changed ( pal , - 1 ) ;
}
}
rc - > beaconing = - 1 ;
rc - > scanning = - 1 ;
mutex_unlock ( & rc - > uwb_dev . mutex ) ;
}
/*
* uwb_radio_shutdown - shutdown the radio manager
* @ rc : the radio controller .
*
* The radio controller is reset .
*/
void uwb_radio_shutdown ( struct uwb_rc * rc )
{
uwb_radio_reset_state ( rc ) ;
uwb_rc_reset ( rc ) ;
}