2007-04-23 23:20:05 +04:00
/*
* This is the linux wireless configuration interface .
*
2008-08-30 03:26:43 +04:00
* Copyright 2006 - 2008 Johannes Berg < johannes @ sipsolutions . net >
2007-04-23 23:20:05 +04:00
*/
# include <linux/if.h>
# include <linux/module.h>
# include <linux/err.h>
# include <linux/list.h>
# include <linux/nl80211.h>
# include <linux/debugfs.h>
# include <linux/notifier.h>
# include <linux/device.h>
# include <net/genetlink.h>
# include <net/cfg80211.h>
# include <net/wireless.h>
2007-09-20 21:09:35 +04:00
# include "nl80211.h"
2007-04-23 23:20:05 +04:00
# include "core.h"
# include "sysfs.h"
/* name for sysfs, %d is appended */
# define PHY_NAME "phy"
MODULE_AUTHOR ( " Johannes Berg " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " wireless configuration support " ) ;
/* RCU might be appropriate here since we usually
* only read the list , and that can happen quite
* often because we need to do it for each command */
LIST_HEAD ( cfg80211_drv_list ) ;
2009-02-21 08:04:21 +03:00
/*
2009-02-21 08:20:39 +03:00
* This is used to protect the cfg80211_drv_list , cfg80211_regdomain ,
* country_ie_regdomain , the reg_beacon_list and the the last regulatory
* request receipt ( last_request ) .
2009-02-21 08:04:21 +03:00
*/
DEFINE_MUTEX ( cfg80211_mutex ) ;
2007-04-23 23:20:05 +04:00
/* for debugfs */
static struct dentry * ieee80211_debugfs_dir ;
2009-02-21 08:04:26 +03:00
/* requires cfg80211_mutex to be held! */
struct cfg80211_registered_device * cfg80211_drv_by_wiphy_idx ( int wiphy_idx )
2007-09-20 21:09:35 +04:00
{
struct cfg80211_registered_device * result = NULL , * drv ;
2009-02-21 08:04:20 +03:00
if ( ! wiphy_idx_valid ( wiphy_idx ) )
return NULL ;
2009-02-21 08:04:25 +03:00
assert_cfg80211_lock ( ) ;
2007-09-20 21:09:35 +04:00
list_for_each_entry ( drv , & cfg80211_drv_list , list ) {
2009-02-21 08:04:19 +03:00
if ( drv - > wiphy_idx = = wiphy_idx ) {
2007-09-20 21:09:35 +04:00
result = drv ;
break ;
}
}
return result ;
}
2009-02-21 08:04:26 +03:00
int get_wiphy_idx ( struct wiphy * wiphy )
{
struct cfg80211_registered_device * drv ;
if ( ! wiphy )
return WIPHY_IDX_STALE ;
drv = wiphy_to_dev ( wiphy ) ;
return drv - > wiphy_idx ;
}
/* requires cfg80211_drv_mutex to be held! */
struct wiphy * wiphy_idx_to_wiphy ( int wiphy_idx )
{
struct cfg80211_registered_device * drv ;
if ( ! wiphy_idx_valid ( wiphy_idx ) )
return NULL ;
assert_cfg80211_lock ( ) ;
drv = cfg80211_drv_by_wiphy_idx ( wiphy_idx ) ;
if ( ! drv )
return NULL ;
return & drv - > wiphy ;
}
2009-02-21 08:04:21 +03:00
/* requires cfg80211_mutex to be held! */
2009-03-24 11:35:46 +03:00
struct cfg80211_registered_device *
2007-09-20 21:09:35 +04:00
__cfg80211_drv_from_info ( struct genl_info * info )
{
int ifindex ;
2009-02-21 08:04:19 +03:00
struct cfg80211_registered_device * bywiphyidx = NULL , * byifidx = NULL ;
2007-09-20 21:09:35 +04:00
struct net_device * dev ;
int err = - EINVAL ;
2009-02-21 08:04:25 +03:00
assert_cfg80211_lock ( ) ;
2007-09-20 21:09:35 +04:00
if ( info - > attrs [ NL80211_ATTR_WIPHY ] ) {
2009-02-21 08:04:19 +03:00
bywiphyidx = cfg80211_drv_by_wiphy_idx (
2007-09-20 21:09:35 +04:00
nla_get_u32 ( info - > attrs [ NL80211_ATTR_WIPHY ] ) ) ;
err = - ENODEV ;
}
if ( info - > attrs [ NL80211_ATTR_IFINDEX ] ) {
ifindex = nla_get_u32 ( info - > attrs [ NL80211_ATTR_IFINDEX ] ) ;
dev = dev_get_by_index ( & init_net , ifindex ) ;
if ( dev ) {
if ( dev - > ieee80211_ptr )
byifidx =
wiphy_to_dev ( dev - > ieee80211_ptr - > wiphy ) ;
dev_put ( dev ) ;
}
err = - ENODEV ;
}
2009-02-21 08:04:19 +03:00
if ( bywiphyidx & & byifidx ) {
if ( bywiphyidx ! = byifidx )
2007-09-20 21:09:35 +04:00
return ERR_PTR ( - EINVAL ) ;
else
2009-02-21 08:04:19 +03:00
return bywiphyidx ; /* == byifidx */
2007-09-20 21:09:35 +04:00
}
2009-02-21 08:04:19 +03:00
if ( bywiphyidx )
return bywiphyidx ;
2007-09-20 21:09:35 +04:00
if ( byifidx )
return byifidx ;
return ERR_PTR ( err ) ;
}
struct cfg80211_registered_device *
cfg80211_get_dev_from_info ( struct genl_info * info )
{
struct cfg80211_registered_device * drv ;
2009-02-21 08:04:21 +03:00
mutex_lock ( & cfg80211_mutex ) ;
2007-09-20 21:09:35 +04:00
drv = __cfg80211_drv_from_info ( info ) ;
/* if it is not an error we grab the lock on
* it to assure it won ' t be going away while
* we operate on it */
if ( ! IS_ERR ( drv ) )
mutex_lock ( & drv - > mtx ) ;
2009-02-21 08:04:21 +03:00
mutex_unlock ( & cfg80211_mutex ) ;
2007-09-20 21:09:35 +04:00
return drv ;
}
struct cfg80211_registered_device *
cfg80211_get_dev_from_ifindex ( int ifindex )
{
struct cfg80211_registered_device * drv = ERR_PTR ( - ENODEV ) ;
struct net_device * dev ;
2009-02-21 08:04:21 +03:00
mutex_lock ( & cfg80211_mutex ) ;
2007-09-20 21:09:35 +04:00
dev = dev_get_by_index ( & init_net , ifindex ) ;
if ( ! dev )
goto out ;
if ( dev - > ieee80211_ptr ) {
drv = wiphy_to_dev ( dev - > ieee80211_ptr - > wiphy ) ;
mutex_lock ( & drv - > mtx ) ;
} else
drv = ERR_PTR ( - ENODEV ) ;
dev_put ( dev ) ;
out :
2009-02-21 08:04:21 +03:00
mutex_unlock ( & cfg80211_mutex ) ;
2007-09-20 21:09:35 +04:00
return drv ;
}
void cfg80211_put_dev ( struct cfg80211_registered_device * drv )
{
BUG_ON ( IS_ERR ( drv ) ) ;
mutex_unlock ( & drv - > mtx ) ;
}
2009-03-24 11:35:46 +03:00
/* requires cfg80211_mutex to be held */
2007-09-20 21:09:35 +04:00
int cfg80211_dev_rename ( struct cfg80211_registered_device * rdev ,
char * newname )
{
2008-05-09 01:30:18 +04:00
struct cfg80211_registered_device * drv ;
2009-02-21 08:04:19 +03:00
int wiphy_idx , taken = - 1 , result , digits ;
2007-09-20 21:09:35 +04:00
2009-03-24 11:35:46 +03:00
assert_cfg80211_lock ( ) ;
2008-05-09 01:30:18 +04:00
2007-09-20 21:09:35 +04:00
/* prohibit calling the thing phy%d when %d is not its number */
2009-02-21 08:04:19 +03:00
sscanf ( newname , PHY_NAME " %d%n " , & wiphy_idx , & taken ) ;
if ( taken = = strlen ( newname ) & & wiphy_idx ! = rdev - > wiphy_idx ) {
/* count number of places needed to print wiphy_idx */
2007-09-20 21:09:35 +04:00
digits = 1 ;
2009-02-21 08:04:19 +03:00
while ( wiphy_idx / = 10 )
2007-09-20 21:09:35 +04:00
digits + + ;
/*
* deny the name if it is phy < idx > where < idx > is printed
* without leading zeroes . taken = = strlen ( newname ) here
*/
if ( taken = = strlen ( PHY_NAME ) + digits )
2009-03-24 11:35:46 +03:00
return - EINVAL ;
2008-05-09 01:30:18 +04:00
}
/* Ignore nop renames */
if ( strcmp ( newname , dev_name ( & rdev - > wiphy . dev ) ) = = 0 )
2009-03-24 11:35:46 +03:00
return 0 ;
2008-05-09 01:30:18 +04:00
/* Ensure another device does not already have this name. */
2009-03-24 11:35:46 +03:00
list_for_each_entry ( drv , & cfg80211_drv_list , list )
2008-05-09 01:30:18 +04:00
if ( strcmp ( newname , dev_name ( & drv - > wiphy . dev ) ) = = 0 )
2009-03-24 11:35:46 +03:00
return - EINVAL ;
2007-09-20 21:09:35 +04:00
result = device_rename ( & rdev - > wiphy . dev , newname ) ;
if ( result )
2009-03-24 11:35:46 +03:00
return result ;
2007-09-20 21:09:35 +04:00
2008-10-08 12:23:48 +04:00
if ( rdev - > wiphy . debugfsdir & &
! debugfs_rename ( rdev - > wiphy . debugfsdir - > d_parent ,
2007-09-20 21:09:35 +04:00
rdev - > wiphy . debugfsdir ,
rdev - > wiphy . debugfsdir - > d_parent ,
newname ) )
printk ( KERN_ERR " cfg80211: failed to rename debugfs dir to %s! \n " ,
newname ) ;
2009-03-24 11:35:46 +03:00
nl80211_notify_dev_rename ( rdev ) ;
2007-09-20 21:09:35 +04:00
2009-03-24 11:35:46 +03:00
return 0 ;
2007-09-20 21:09:35 +04:00
}
2007-04-23 23:20:05 +04:00
/* exported functions */
struct wiphy * wiphy_new ( struct cfg80211_ops * ops , int sizeof_priv )
{
2008-09-22 22:35:37 +04:00
static int wiphy_counter ;
2007-04-23 23:20:05 +04:00
struct cfg80211_registered_device * drv ;
int alloc_size ;
2007-12-19 04:03:29 +03:00
WARN_ON ( ! ops - > add_key & & ops - > del_key ) ;
WARN_ON ( ops - > add_key & & ! ops - > del_key ) ;
2007-04-23 23:20:05 +04:00
alloc_size = sizeof ( * drv ) + sizeof_priv ;
drv = kzalloc ( alloc_size , GFP_KERNEL ) ;
if ( ! drv )
return NULL ;
drv - > ops = ops ;
2009-02-21 08:04:21 +03:00
mutex_lock ( & cfg80211_mutex ) ;
2007-04-23 23:20:05 +04:00
2009-02-21 08:04:19 +03:00
drv - > wiphy_idx = wiphy_counter + + ;
2007-04-27 07:50:35 +04:00
2009-02-21 08:04:20 +03:00
if ( unlikely ( ! wiphy_idx_valid ( drv - > wiphy_idx ) ) ) {
2008-09-22 22:35:37 +04:00
wiphy_counter - - ;
2009-02-21 08:04:21 +03:00
mutex_unlock ( & cfg80211_mutex ) ;
2007-04-23 23:20:05 +04:00
/* ugh, wrapped! */
kfree ( drv ) ;
return NULL ;
}
2009-02-21 08:04:21 +03:00
mutex_unlock ( & cfg80211_mutex ) ;
2008-09-22 22:35:37 +04:00
2007-04-23 23:20:05 +04:00
/* give it a proper name */
2009-02-21 08:04:19 +03:00
dev_set_name ( & drv - > wiphy . dev , PHY_NAME " %d " , drv - > wiphy_idx ) ;
2007-04-23 23:20:05 +04:00
mutex_init ( & drv - > mtx ) ;
mutex_init ( & drv - > devlist_mtx ) ;
INIT_LIST_HEAD ( & drv - > netdev_list ) ;
2009-02-10 23:25:55 +03:00
spin_lock_init ( & drv - > bss_lock ) ;
INIT_LIST_HEAD ( & drv - > bss_list ) ;
2007-04-23 23:20:05 +04:00
device_initialize ( & drv - > wiphy . dev ) ;
drv - > wiphy . dev . class = & ieee80211_class ;
drv - > wiphy . dev . platform_data = drv ;
return & drv - > wiphy ;
}
EXPORT_SYMBOL ( wiphy_new ) ;
int wiphy_register ( struct wiphy * wiphy )
{
struct cfg80211_registered_device * drv = wiphy_to_dev ( wiphy ) ;
int res ;
2008-01-24 21:38:38 +03:00
enum ieee80211_band band ;
struct ieee80211_supported_band * sband ;
bool have_band = false ;
int i ;
2008-08-30 03:26:43 +04:00
u16 ifmodes = wiphy - > interface_modes ;
2009-02-10 23:25:55 +03:00
if ( WARN_ON ( wiphy - > max_scan_ssids < 1 ) )
return - EINVAL ;
2008-08-30 03:26:43 +04:00
/* sanity check ifmodes */
WARN_ON ( ! ifmodes ) ;
ifmodes & = ( ( 1 < < __NL80211_IFTYPE_AFTER_LAST ) - 1 ) & ~ 1 ;
if ( WARN_ON ( ifmodes ! = wiphy - > interface_modes ) )
wiphy - > interface_modes = ifmodes ;
2008-01-24 21:38:38 +03:00
/* sanity check supported bands/channels */
for ( band = 0 ; band < IEEE80211_NUM_BANDS ; band + + ) {
sband = wiphy - > bands [ band ] ;
if ( ! sband )
continue ;
sband - > band = band ;
2009-01-21 17:13:48 +03:00
if ( WARN_ON ( ! sband - > n_channels | | ! sband - > n_bitrates ) )
return - EINVAL ;
/*
* Since we use a u32 for rate bitmaps in
* ieee80211_get_response_rate , we cannot
* have more than 32 legacy rates .
*/
if ( WARN_ON ( sband - > n_bitrates > 32 ) )
2008-01-24 21:38:38 +03:00
return - EINVAL ;
for ( i = 0 ; i < sband - > n_channels ; i + + ) {
sband - > channels [ i ] . orig_flags =
sband - > channels [ i ] . flags ;
sband - > channels [ i ] . orig_mag =
sband - > channels [ i ] . max_antenna_gain ;
sband - > channels [ i ] . orig_mpwr =
sband - > channels [ i ] . max_power ;
sband - > channels [ i ] . band = band ;
}
have_band = true ;
}
if ( ! have_band ) {
WARN_ON ( 1 ) ;
return - EINVAL ;
}
/* check and set up bitrates */
ieee80211_set_bitrate_flags ( wiphy ) ;
2009-02-21 08:04:21 +03:00
mutex_lock ( & cfg80211_mutex ) ;
2008-10-21 11:57:41 +04:00
2008-01-24 21:38:38 +03:00
/* set up regulatory info */
2009-03-10 05:07:41 +03:00
wiphy_update_regulatory ( wiphy , NL80211_REGDOM_SET_BY_CORE ) ;
2007-04-23 23:20:05 +04:00
res = device_add ( & drv - > wiphy . dev ) ;
if ( res )
goto out_unlock ;
list_add ( & drv - > list , & cfg80211_drv_list ) ;
/* add to debugfs */
drv - > wiphy . debugfsdir =
debugfs_create_dir ( wiphy_name ( & drv - > wiphy ) ,
ieee80211_debugfs_dir ) ;
2008-10-08 12:23:48 +04:00
if ( IS_ERR ( drv - > wiphy . debugfsdir ) )
drv - > wiphy . debugfsdir = NULL ;
2007-04-23 23:20:05 +04:00
2009-03-10 05:07:42 +03:00
if ( wiphy - > custom_regulatory ) {
struct regulatory_request request ;
request . wiphy_idx = get_wiphy_idx ( wiphy ) ;
request . initiator = NL80211_REGDOM_SET_BY_DRIVER ;
request . alpha2 [ 0 ] = ' 9 ' ;
request . alpha2 [ 1 ] = ' 9 ' ;
nl80211_send_reg_change_event ( & request ) ;
}
2007-04-23 23:20:05 +04:00
res = 0 ;
out_unlock :
2009-02-21 08:04:21 +03:00
mutex_unlock ( & cfg80211_mutex ) ;
2007-04-23 23:20:05 +04:00
return res ;
}
EXPORT_SYMBOL ( wiphy_register ) ;
void wiphy_unregister ( struct wiphy * wiphy )
{
struct cfg80211_registered_device * drv = wiphy_to_dev ( wiphy ) ;
2007-04-27 07:51:12 +04:00
/* protect the device list */
2009-02-21 08:04:21 +03:00
mutex_lock ( & cfg80211_mutex ) ;
2007-04-23 23:20:05 +04:00
2007-04-27 07:51:12 +04:00
BUG_ON ( ! list_empty ( & drv - > netdev_list ) ) ;
/*
* Try to grab drv - > mtx . If a command is still in progress ,
* hopefully the driver will refuse it since it ' s tearing
* down the device already . We wait for this command to complete
* before unlinking the item from the list .
* Note : as codified by the BUG_ON above we cannot get here if
* a virtual interface is still associated . Hence , we can only
* get to lock contention here if userspace issues a command
* that identified the hardware by wiphy index .
*/
2007-04-23 23:20:05 +04:00
mutex_lock ( & drv - > mtx ) ;
2007-04-27 07:51:12 +04:00
/* unlock again before freeing */
2007-04-23 23:20:05 +04:00
mutex_unlock ( & drv - > mtx ) ;
2008-11-13 01:22:02 +03:00
/* If this device got a regulatory hint tell core its
* free to listen now to a new shiny device regulatory hint */
reg_device_remove ( wiphy ) ;
2007-04-27 07:51:12 +04:00
list_del ( & drv - > list ) ;
2007-04-23 23:20:05 +04:00
device_del ( & drv - > wiphy . dev ) ;
debugfs_remove ( drv - > wiphy . debugfsdir ) ;
2009-02-21 08:04:21 +03:00
mutex_unlock ( & cfg80211_mutex ) ;
2007-04-23 23:20:05 +04:00
}
EXPORT_SYMBOL ( wiphy_unregister ) ;
void cfg80211_dev_free ( struct cfg80211_registered_device * drv )
{
2009-02-10 23:25:55 +03:00
struct cfg80211_internal_bss * scan , * tmp ;
2007-04-23 23:20:05 +04:00
mutex_destroy ( & drv - > mtx ) ;
mutex_destroy ( & drv - > devlist_mtx ) ;
2009-02-10 23:25:55 +03:00
list_for_each_entry_safe ( scan , tmp , & drv - > bss_list , list )
2009-02-10 23:25:57 +03:00
cfg80211_put_bss ( & scan - > pub ) ;
2007-04-23 23:20:05 +04:00
kfree ( drv ) ;
}
void wiphy_free ( struct wiphy * wiphy )
{
put_device ( & wiphy - > dev ) ;
}
EXPORT_SYMBOL ( wiphy_free ) ;
static int cfg80211_netdev_notifier_call ( struct notifier_block * nb ,
unsigned long state ,
void * ndev )
{
struct net_device * dev = ndev ;
struct cfg80211_registered_device * rdev ;
if ( ! dev - > ieee80211_ptr )
return 0 ;
rdev = wiphy_to_dev ( dev - > ieee80211_ptr - > wiphy ) ;
2008-09-16 16:55:09 +04:00
WARN_ON ( dev - > ieee80211_ptr - > iftype = = NL80211_IFTYPE_UNSPECIFIED ) ;
2007-04-23 23:20:05 +04:00
switch ( state ) {
case NETDEV_REGISTER :
mutex_lock ( & rdev - > devlist_mtx ) ;
list_add ( & dev - > ieee80211_ptr - > list , & rdev - > netdev_list ) ;
if ( sysfs_create_link ( & dev - > dev . kobj , & rdev - > wiphy . dev . kobj ,
" phy80211 " ) ) {
printk ( KERN_ERR " wireless: failed to add phy80211 "
" symlink to netdev! \n " ) ;
}
dev - > ieee80211_ptr - > netdev = dev ;
mutex_unlock ( & rdev - > devlist_mtx ) ;
break ;
case NETDEV_UNREGISTER :
mutex_lock ( & rdev - > devlist_mtx ) ;
if ( ! list_empty ( & dev - > ieee80211_ptr - > list ) ) {
sysfs_remove_link ( & dev - > dev . kobj , " phy80211 " ) ;
list_del_init ( & dev - > ieee80211_ptr - > list ) ;
}
mutex_unlock ( & rdev - > devlist_mtx ) ;
break ;
}
return 0 ;
}
static struct notifier_block cfg80211_netdev_notifier = {
. notifier_call = cfg80211_netdev_notifier_call ,
} ;
static int cfg80211_init ( void )
{
2008-09-10 10:19:48 +04:00
int err ;
err = wiphy_sysfs_init ( ) ;
2007-04-23 23:20:05 +04:00
if ( err )
goto out_fail_sysfs ;
err = register_netdevice_notifier ( & cfg80211_netdev_notifier ) ;
if ( err )
goto out_fail_notifier ;
2007-09-20 21:09:35 +04:00
err = nl80211_init ( ) ;
if ( err )
goto out_fail_nl80211 ;
2007-04-23 23:20:05 +04:00
ieee80211_debugfs_dir = debugfs_create_dir ( " ieee80211 " , NULL ) ;
2008-09-10 10:19:48 +04:00
err = regulatory_init ( ) ;
if ( err )
goto out_fail_reg ;
2007-04-23 23:20:05 +04:00
return 0 ;
2008-09-10 10:19:48 +04:00
out_fail_reg :
debugfs_remove ( ieee80211_debugfs_dir ) ;
2007-09-20 21:09:35 +04:00
out_fail_nl80211 :
unregister_netdevice_notifier ( & cfg80211_netdev_notifier ) ;
2007-04-23 23:20:05 +04:00
out_fail_notifier :
wiphy_sysfs_exit ( ) ;
out_fail_sysfs :
return err ;
}
2008-09-10 10:19:48 +04:00
2007-09-10 15:44:45 +04:00
subsys_initcall ( cfg80211_init ) ;
2007-04-23 23:20:05 +04:00
static void cfg80211_exit ( void )
{
debugfs_remove ( ieee80211_debugfs_dir ) ;
2007-09-20 21:09:35 +04:00
nl80211_exit ( ) ;
2007-04-23 23:20:05 +04:00
unregister_netdevice_notifier ( & cfg80211_netdev_notifier ) ;
wiphy_sysfs_exit ( ) ;
2008-09-10 10:19:48 +04:00
regulatory_exit ( ) ;
2007-04-23 23:20:05 +04:00
}
module_exit ( cfg80211_exit ) ;