2007-05-05 22:45:53 +04:00
/*
* mac80211 configuration hooks for cfg80211
*
2007-12-19 04:03:31 +03:00
* Copyright 2006 , 2007 Johannes Berg < johannes @ sipsolutions . net >
2007-05-05 22:45:53 +04:00
*
* This file is GPLv2 as found in COPYING .
*/
2007-12-19 04:03:30 +03:00
# include <linux/ieee80211.h>
2007-05-05 22:45:53 +04:00
# include <linux/nl80211.h>
# include <linux/rtnetlink.h>
2007-09-17 22:56:21 +04:00
# include <net/net_namespace.h>
2007-12-19 04:03:33 +03:00
# include <linux/rcupdate.h>
2007-05-05 22:45:53 +04:00
# include <net/cfg80211.h>
# include "ieee80211_i.h"
2009-04-23 20:52:52 +04:00
# include "driver-ops.h"
2007-09-19 01:29:21 +04:00
# include "cfg.h"
2008-04-08 23:14:40 +04:00
# include "rate.h"
2008-02-23 17:17:17 +03:00
# include "mesh.h"
2008-09-11 02:01:58 +04:00
static bool nl80211_type_check ( enum nl80211_iftype type )
2007-09-28 23:52:27 +04:00
{
switch ( type ) {
case NL80211_IFTYPE_ADHOC :
case NL80211_IFTYPE_STATION :
case NL80211_IFTYPE_MONITOR :
2008-02-23 17:17:17 +03:00
# ifdef CONFIG_MAC80211_MESH
case NL80211_IFTYPE_MESH_POINT :
# endif
2008-10-30 20:50:30 +03:00
case NL80211_IFTYPE_AP :
case NL80211_IFTYPE_AP_VLAN :
2008-04-14 17:37:03 +04:00
case NL80211_IFTYPE_WDS :
2008-09-11 02:01:58 +04:00
return true ;
2007-09-28 23:52:27 +04:00
default :
2008-09-11 02:01:58 +04:00
return false ;
2007-09-28 23:52:27 +04:00
}
}
2007-05-05 22:45:53 +04:00
static int ieee80211_add_iface ( struct wiphy * wiphy , char * name ,
2008-02-23 17:17:06 +03:00
enum nl80211_iftype type , u32 * flags ,
struct vif_params * params )
2007-05-05 22:45:53 +04:00
{
struct ieee80211_local * local = wiphy_priv ( wiphy ) ;
2008-01-31 21:48:23 +03:00
struct net_device * dev ;
struct ieee80211_sub_if_data * sdata ;
int err ;
2007-05-05 22:45:53 +04:00
2008-09-11 02:01:58 +04:00
if ( ! nl80211_type_check ( type ) )
2007-05-05 22:45:53 +04:00
return - EINVAL ;
2008-09-11 02:01:58 +04:00
err = ieee80211_if_add ( local , name , & dev , type , params ) ;
if ( err | | type ! = NL80211_IFTYPE_MONITOR | | ! flags )
2008-01-31 21:48:23 +03:00
return err ;
sdata = IEEE80211_DEV_TO_SUB_IF ( dev ) ;
sdata - > u . mntr_flags = * flags ;
return 0 ;
2007-05-05 22:45:53 +04:00
}
static int ieee80211_del_iface ( struct wiphy * wiphy , int ifindex )
{
struct net_device * dev ;
2008-08-03 04:04:37 +04:00
struct ieee80211_sub_if_data * sdata ;
2007-05-05 22:45:53 +04:00
2007-09-28 23:52:27 +04:00
/* we're under RTNL */
dev = __dev_get_by_index ( & init_net , ifindex ) ;
2007-05-05 22:45:53 +04:00
if ( ! dev )
2008-07-09 16:40:35 +04:00
return - ENODEV ;
2007-05-05 22:45:53 +04:00
2008-08-03 04:04:37 +04:00
sdata = IEEE80211_DEV_TO_SUB_IF ( dev ) ;
ieee80211_if_remove ( sdata ) ;
2007-05-05 22:45:53 +04:00
2008-07-09 16:40:35 +04:00
return 0 ;
2007-05-05 22:45:53 +04:00
}
2009-06-09 23:04:43 +04:00
static int ieee80211_change_iface ( struct wiphy * wiphy ,
struct net_device * dev ,
2008-02-23 17:17:06 +03:00
enum nl80211_iftype type , u32 * flags ,
struct vif_params * params )
2007-09-28 23:52:27 +04:00
{
struct ieee80211_sub_if_data * sdata ;
2008-07-09 16:40:36 +04:00
int ret ;
2007-09-28 23:52:27 +04:00
2008-09-11 02:01:58 +04:00
if ( ! nl80211_type_check ( type ) )
2007-09-28 23:52:27 +04:00
return - EINVAL ;
sdata = IEEE80211_DEV_TO_SUB_IF ( dev ) ;
2008-09-11 02:01:58 +04:00
ret = ieee80211_if_change_type ( sdata , type ) ;
2008-07-09 16:40:36 +04:00
if ( ret )
return ret ;
2007-09-28 23:52:27 +04:00
2008-09-16 22:22:21 +04:00
if ( netif_running ( sdata - > dev ) )
return - EBUSY ;
2008-02-23 17:17:19 +03:00
if ( ieee80211_vif_is_mesh ( & sdata - > vif ) & & params - > mesh_id_len )
2008-09-11 02:01:49 +04:00
ieee80211_sdata_set_mesh_id ( sdata ,
params - > mesh_id_len ,
params - > mesh_id ) ;
2008-02-23 17:17:17 +03:00
2008-09-11 02:01:58 +04:00
if ( sdata - > vif . type ! = NL80211_IFTYPE_MONITOR | | ! flags )
2008-01-31 21:48:23 +03:00
return 0 ;
sdata - > u . mntr_flags = * flags ;
2007-09-28 23:52:27 +04:00
return 0 ;
}
2007-12-19 04:03:30 +03:00
static int ieee80211_add_key ( struct wiphy * wiphy , struct net_device * dev ,
2009-05-09 22:06:47 +04:00
u8 key_idx , const u8 * mac_addr ,
2007-12-19 04:03:30 +03:00
struct key_params * params )
{
struct ieee80211_sub_if_data * sdata ;
struct sta_info * sta = NULL ;
enum ieee80211_key_alg alg ;
2008-02-25 18:27:45 +03:00
struct ieee80211_key * key ;
2008-04-08 19:56:52 +04:00
int err ;
2007-12-19 04:03:30 +03:00
sdata = IEEE80211_DEV_TO_SUB_IF ( dev ) ;
switch ( params - > cipher ) {
case WLAN_CIPHER_SUITE_WEP40 :
case WLAN_CIPHER_SUITE_WEP104 :
alg = ALG_WEP ;
break ;
case WLAN_CIPHER_SUITE_TKIP :
alg = ALG_TKIP ;
break ;
case WLAN_CIPHER_SUITE_CCMP :
alg = ALG_CCMP ;
break ;
2009-01-08 14:32:02 +03:00
case WLAN_CIPHER_SUITE_AES_CMAC :
alg = ALG_AES_CMAC ;
break ;
2007-12-19 04:03:30 +03:00
default :
return - EINVAL ;
}
2009-05-11 22:57:58 +04:00
key = ieee80211_key_alloc ( alg , key_idx , params - > key_len , params - > key ,
params - > seq_len , params - > seq ) ;
2008-02-25 18:27:45 +03:00
if ( ! key )
return - ENOMEM ;
2008-04-08 19:56:52 +04:00
rcu_read_lock ( ) ;
2007-12-19 04:03:30 +03:00
if ( mac_addr ) {
sta = sta_info_get ( sdata - > local , mac_addr ) ;
2008-02-25 18:27:45 +03:00
if ( ! sta ) {
ieee80211_key_free ( key ) ;
2008-04-08 19:56:52 +04:00
err = - ENOENT ;
goto out_unlock ;
2008-02-25 18:27:45 +03:00
}
2007-12-19 04:03:30 +03:00
}
2008-02-25 18:27:45 +03:00
ieee80211_key_link ( key , sdata , sta ) ;
2008-04-08 19:56:52 +04:00
err = 0 ;
out_unlock :
rcu_read_unlock ( ) ;
return err ;
2007-12-19 04:03:30 +03:00
}
static int ieee80211_del_key ( struct wiphy * wiphy , struct net_device * dev ,
2009-05-09 22:06:47 +04:00
u8 key_idx , const u8 * mac_addr )
2007-12-19 04:03:30 +03:00
{
struct ieee80211_sub_if_data * sdata ;
struct sta_info * sta ;
int ret ;
sdata = IEEE80211_DEV_TO_SUB_IF ( dev ) ;
2008-04-08 19:56:52 +04:00
rcu_read_lock ( ) ;
2007-12-19 04:03:30 +03:00
if ( mac_addr ) {
2008-04-08 19:56:52 +04:00
ret = - ENOENT ;
2007-12-19 04:03:30 +03:00
sta = sta_info_get ( sdata - > local , mac_addr ) ;
if ( ! sta )
2008-04-08 19:56:52 +04:00
goto out_unlock ;
2007-12-19 04:03:30 +03:00
2008-02-25 18:27:45 +03:00
if ( sta - > key ) {
2008-02-25 18:27:46 +03:00
ieee80211_key_free ( sta - > key ) ;
2008-02-25 18:27:45 +03:00
WARN_ON ( sta - > key ) ;
2008-04-08 19:56:52 +04:00
ret = 0 ;
}
2007-12-19 04:03:30 +03:00
2008-04-08 19:56:52 +04:00
goto out_unlock ;
2007-12-19 04:03:30 +03:00
}
2008-04-08 19:56:52 +04:00
if ( ! sdata - > keys [ key_idx ] ) {
ret = - ENOENT ;
goto out_unlock ;
}
2007-12-19 04:03:30 +03:00
2008-02-25 18:27:46 +03:00
ieee80211_key_free ( sdata - > keys [ key_idx ] ) ;
2008-02-25 18:27:45 +03:00
WARN_ON ( sdata - > keys [ key_idx ] ) ;
2007-12-19 04:03:30 +03:00
2008-04-08 19:56:52 +04:00
ret = 0 ;
out_unlock :
rcu_read_unlock ( ) ;
return ret ;
2007-12-19 04:03:30 +03:00
}
2007-12-19 04:03:31 +03:00
static int ieee80211_get_key ( struct wiphy * wiphy , struct net_device * dev ,
2009-05-09 22:06:47 +04:00
u8 key_idx , const u8 * mac_addr , void * cookie ,
2007-12-19 04:03:31 +03:00
void ( * callback ) ( void * cookie ,
struct key_params * params ) )
{
2008-07-29 15:22:52 +04:00
struct ieee80211_sub_if_data * sdata ;
2007-12-19 04:03:31 +03:00
struct sta_info * sta = NULL ;
u8 seq [ 6 ] = { 0 } ;
struct key_params params ;
struct ieee80211_key * key ;
u32 iv32 ;
u16 iv16 ;
int err = - ENOENT ;
2008-07-29 15:22:52 +04:00
sdata = IEEE80211_DEV_TO_SUB_IF ( dev ) ;
2008-04-08 19:56:52 +04:00
rcu_read_lock ( ) ;
2007-12-19 04:03:31 +03:00
if ( mac_addr ) {
sta = sta_info_get ( sdata - > local , mac_addr ) ;
if ( ! sta )
goto out ;
key = sta - > key ;
} else
key = sdata - > keys [ key_idx ] ;
if ( ! key )
goto out ;
memset ( & params , 0 , sizeof ( params ) ) ;
switch ( key - > conf . alg ) {
case ALG_TKIP :
params . cipher = WLAN_CIPHER_SUITE_TKIP ;
2008-05-15 03:26:19 +04:00
iv32 = key - > u . tkip . tx . iv32 ;
iv16 = key - > u . tkip . tx . iv16 ;
2007-12-19 04:03:31 +03:00
2009-04-23 20:52:52 +04:00
if ( key - > flags & KEY_FLAG_UPLOADED_TO_HARDWARE )
drv_get_tkip_seq ( sdata - > local ,
key - > conf . hw_key_idx ,
& iv32 , & iv16 ) ;
2007-12-19 04:03:31 +03:00
seq [ 0 ] = iv16 & 0xff ;
seq [ 1 ] = ( iv16 > > 8 ) & 0xff ;
seq [ 2 ] = iv32 & 0xff ;
seq [ 3 ] = ( iv32 > > 8 ) & 0xff ;
seq [ 4 ] = ( iv32 > > 16 ) & 0xff ;
seq [ 5 ] = ( iv32 > > 24 ) & 0xff ;
params . seq = seq ;
params . seq_len = 6 ;
break ;
case ALG_CCMP :
params . cipher = WLAN_CIPHER_SUITE_CCMP ;
seq [ 0 ] = key - > u . ccmp . tx_pn [ 5 ] ;
seq [ 1 ] = key - > u . ccmp . tx_pn [ 4 ] ;
seq [ 2 ] = key - > u . ccmp . tx_pn [ 3 ] ;
seq [ 3 ] = key - > u . ccmp . tx_pn [ 2 ] ;
seq [ 4 ] = key - > u . ccmp . tx_pn [ 1 ] ;
seq [ 5 ] = key - > u . ccmp . tx_pn [ 0 ] ;
params . seq = seq ;
params . seq_len = 6 ;
break ;
case ALG_WEP :
if ( key - > conf . keylen = = 5 )
params . cipher = WLAN_CIPHER_SUITE_WEP40 ;
else
params . cipher = WLAN_CIPHER_SUITE_WEP104 ;
break ;
2009-01-08 14:32:02 +03:00
case ALG_AES_CMAC :
params . cipher = WLAN_CIPHER_SUITE_AES_CMAC ;
seq [ 0 ] = key - > u . aes_cmac . tx_pn [ 5 ] ;
seq [ 1 ] = key - > u . aes_cmac . tx_pn [ 4 ] ;
seq [ 2 ] = key - > u . aes_cmac . tx_pn [ 3 ] ;
seq [ 3 ] = key - > u . aes_cmac . tx_pn [ 2 ] ;
seq [ 4 ] = key - > u . aes_cmac . tx_pn [ 1 ] ;
seq [ 5 ] = key - > u . aes_cmac . tx_pn [ 0 ] ;
params . seq = seq ;
params . seq_len = 6 ;
break ;
2007-12-19 04:03:31 +03:00
}
params . key = key - > conf . key ;
params . key_len = key - > conf . keylen ;
callback ( cookie , & params ) ;
err = 0 ;
out :
2008-04-08 19:56:52 +04:00
rcu_read_unlock ( ) ;
2007-12-19 04:03:31 +03:00
return err ;
}
2007-12-19 04:03:30 +03:00
static int ieee80211_config_default_key ( struct wiphy * wiphy ,
struct net_device * dev ,
u8 key_idx )
{
struct ieee80211_sub_if_data * sdata ;
2008-04-08 19:56:52 +04:00
rcu_read_lock ( ) ;
2007-12-19 04:03:30 +03:00
sdata = IEEE80211_DEV_TO_SUB_IF ( dev ) ;
ieee80211_set_default_key ( sdata , key_idx ) ;
2008-04-08 19:56:52 +04:00
rcu_read_unlock ( ) ;
2007-12-19 04:03:30 +03:00
return 0 ;
}
2009-01-08 14:32:02 +03:00
static int ieee80211_config_default_mgmt_key ( struct wiphy * wiphy ,
struct net_device * dev ,
u8 key_idx )
{
struct ieee80211_sub_if_data * sdata ;
rcu_read_lock ( ) ;
sdata = IEEE80211_DEV_TO_SUB_IF ( dev ) ;
ieee80211_set_default_mgmt_key ( sdata , key_idx ) ;
rcu_read_unlock ( ) ;
return 0 ;
}
2008-02-23 17:17:17 +03:00
static void sta_set_sinfo ( struct sta_info * sta , struct station_info * sinfo )
{
2008-02-25 18:27:46 +03:00
struct ieee80211_sub_if_data * sdata = sta - > sdata ;
2008-02-23 17:17:17 +03:00
sinfo - > filled = STATION_INFO_INACTIVE_TIME |
STATION_INFO_RX_BYTES |
2008-12-12 00:04:19 +03:00
STATION_INFO_TX_BYTES |
2009-02-17 14:24:57 +03:00
STATION_INFO_RX_PACKETS |
STATION_INFO_TX_PACKETS |
2008-12-12 00:04:19 +03:00
STATION_INFO_TX_BITRATE ;
2008-02-23 17:17:17 +03:00
sinfo - > inactive_time = jiffies_to_msecs ( jiffies - sta - > last_rx ) ;
sinfo - > rx_bytes = sta - > rx_bytes ;
sinfo - > tx_bytes = sta - > tx_bytes ;
2009-02-17 14:24:57 +03:00
sinfo - > rx_packets = sta - > rx_packets ;
sinfo - > tx_packets = sta - > tx_packets ;
2008-02-23 17:17:17 +03:00
2008-12-12 00:04:19 +03:00
if ( sta - > local - > hw . flags & IEEE80211_HW_SIGNAL_DBM ) {
sinfo - > filled | = STATION_INFO_SIGNAL ;
sinfo - > signal = ( s8 ) sta - > last_signal ;
}
sinfo - > txrate . flags = 0 ;
if ( sta - > last_tx_rate . flags & IEEE80211_TX_RC_MCS )
sinfo - > txrate . flags | = RATE_INFO_FLAGS_MCS ;
if ( sta - > last_tx_rate . flags & IEEE80211_TX_RC_40_MHZ_WIDTH )
sinfo - > txrate . flags | = RATE_INFO_FLAGS_40_MHZ_WIDTH ;
if ( sta - > last_tx_rate . flags & IEEE80211_TX_RC_SHORT_GI )
sinfo - > txrate . flags | = RATE_INFO_FLAGS_SHORT_GI ;
if ( ! ( sta - > last_tx_rate . flags & IEEE80211_TX_RC_MCS ) ) {
struct ieee80211_supported_band * sband ;
sband = sta - > local - > hw . wiphy - > bands [
sta - > local - > hw . conf . channel - > band ] ;
sinfo - > txrate . legacy =
sband - > bitrates [ sta - > last_tx_rate . idx ] . bitrate ;
} else
sinfo - > txrate . mcs = sta - > last_tx_rate . idx ;
2008-02-23 17:17:19 +03:00
if ( ieee80211_vif_is_mesh ( & sdata - > vif ) ) {
2008-02-23 17:17:17 +03:00
# ifdef CONFIG_MAC80211_MESH
sinfo - > filled | = STATION_INFO_LLID |
STATION_INFO_PLID |
STATION_INFO_PLINK_STATE ;
sinfo - > llid = le16_to_cpu ( sta - > llid ) ;
sinfo - > plid = le16_to_cpu ( sta - > plid ) ;
sinfo - > plink_state = sta - > plink_state ;
# endif
2008-02-23 17:17:19 +03:00
}
2008-02-23 17:17:17 +03:00
}
static int ieee80211_dump_station ( struct wiphy * wiphy , struct net_device * dev ,
int idx , u8 * mac , struct station_info * sinfo )
{
struct ieee80211_local * local = wdev_priv ( dev - > ieee80211_ptr ) ;
struct sta_info * sta ;
2008-02-25 18:27:46 +03:00
int ret = - ENOENT ;
rcu_read_lock ( ) ;
2008-02-23 17:17:17 +03:00
sta = sta_info_get_by_idx ( local , idx , dev ) ;
2008-02-25 18:27:46 +03:00
if ( sta ) {
ret = 0 ;
2008-09-11 02:02:02 +04:00
memcpy ( mac , sta - > sta . addr , ETH_ALEN ) ;
2008-02-25 18:27:46 +03:00
sta_set_sinfo ( sta , sinfo ) ;
}
2008-02-23 17:17:17 +03:00
2008-02-25 18:27:46 +03:00
rcu_read_unlock ( ) ;
2008-02-23 17:17:17 +03:00
2008-02-25 18:27:46 +03:00
return ret ;
2008-02-23 17:17:17 +03:00
}
2007-12-19 04:03:37 +03:00
static int ieee80211_get_station ( struct wiphy * wiphy , struct net_device * dev ,
2008-02-23 17:17:06 +03:00
u8 * mac , struct station_info * sinfo )
2007-12-19 04:03:37 +03:00
{
struct ieee80211_local * local = wdev_priv ( dev - > ieee80211_ptr ) ;
struct sta_info * sta ;
2008-02-25 18:27:46 +03:00
int ret = - ENOENT ;
2007-12-19 04:03:37 +03:00
2008-02-25 18:27:46 +03:00
rcu_read_lock ( ) ;
2007-12-19 04:03:37 +03:00
/* XXX: verify sta->dev == dev */
2008-02-25 18:27:46 +03:00
sta = sta_info_get ( local , mac ) ;
if ( sta ) {
ret = 0 ;
sta_set_sinfo ( sta , sinfo ) ;
}
rcu_read_unlock ( ) ;
return ret ;
2007-12-19 04:03:37 +03:00
}
2007-12-19 04:03:33 +03:00
/*
* This handles both adding a beacon and setting new beacon info
*/
static int ieee80211_config_beacon ( struct ieee80211_sub_if_data * sdata ,
struct beacon_parameters * params )
{
struct beacon_data * new , * old ;
int new_head_len , new_tail_len ;
int size ;
int err = - EINVAL ;
old = sdata - > u . ap . beacon ;
/* head must not be zero-length */
if ( params - > head & & ! params - > head_len )
return - EINVAL ;
/*
* This is a kludge . beacon interval should really be part
* of the beacon information .
*/
2009-04-23 18:10:04 +04:00
if ( params - > interval & &
( sdata - > vif . bss_conf . beacon_int ! = params - > interval ) ) {
sdata - > vif . bss_conf . beacon_int = params - > interval ;
ieee80211_bss_info_change_notify ( sdata ,
BSS_CHANGED_BEACON_INT ) ;
2007-12-19 04:03:33 +03:00
}
/* Need to have a beacon head if we don't have one yet */
if ( ! params - > head & & ! old )
return err ;
/* sorry, no way to start beaconing without dtim period */
if ( ! params - > dtim_period & & ! old )
return err ;
/* new or old head? */
if ( params - > head )
new_head_len = params - > head_len ;
else
new_head_len = old - > head_len ;
/* new or old tail? */
if ( params - > tail | | ! old )
/* params->tail_len will be zero for !params->tail */
new_tail_len = params - > tail_len ;
else
new_tail_len = old - > tail_len ;
size = sizeof ( * new ) + new_head_len + new_tail_len ;
new = kzalloc ( size , GFP_KERNEL ) ;
if ( ! new )
return - ENOMEM ;
/* start filling the new info now */
/* new or old dtim period? */
if ( params - > dtim_period )
new - > dtim_period = params - > dtim_period ;
else
new - > dtim_period = old - > dtim_period ;
/*
* pointers go into the block we allocated ,
* memory is | beacon_data | head | tail |
*/
new - > head = ( ( u8 * ) new ) + sizeof ( * new ) ;
new - > tail = new - > head + new_head_len ;
new - > head_len = new_head_len ;
new - > tail_len = new_tail_len ;
/* copy in head */
if ( params - > head )
memcpy ( new - > head , params - > head , new_head_len ) ;
else
memcpy ( new - > head , old - > head , new_head_len ) ;
/* copy in optional tail */
if ( params - > tail )
memcpy ( new - > tail , params - > tail , new_tail_len ) ;
else
if ( old )
memcpy ( new - > tail , old - > tail , new_tail_len ) ;
rcu_assign_pointer ( sdata - > u . ap . beacon , new ) ;
synchronize_rcu ( ) ;
kfree ( old ) ;
2009-04-23 18:13:26 +04:00
ieee80211_bss_info_change_notify ( sdata , BSS_CHANGED_BEACON_ENABLED |
BSS_CHANGED_BEACON ) ;
return 0 ;
2007-12-19 04:03:33 +03:00
}
static int ieee80211_add_beacon ( struct wiphy * wiphy , struct net_device * dev ,
struct beacon_parameters * params )
{
2008-07-29 15:22:52 +04:00
struct ieee80211_sub_if_data * sdata ;
2007-12-19 04:03:33 +03:00
struct beacon_data * old ;
2008-07-29 15:22:52 +04:00
sdata = IEEE80211_DEV_TO_SUB_IF ( dev ) ;
2007-12-19 04:03:33 +03:00
old = sdata - > u . ap . beacon ;
if ( old )
return - EALREADY ;
return ieee80211_config_beacon ( sdata , params ) ;
}
static int ieee80211_set_beacon ( struct wiphy * wiphy , struct net_device * dev ,
struct beacon_parameters * params )
{
2008-07-29 15:22:52 +04:00
struct ieee80211_sub_if_data * sdata ;
2007-12-19 04:03:33 +03:00
struct beacon_data * old ;
2008-07-29 15:22:52 +04:00
sdata = IEEE80211_DEV_TO_SUB_IF ( dev ) ;
2007-12-19 04:03:33 +03:00
old = sdata - > u . ap . beacon ;
if ( ! old )
return - ENOENT ;
return ieee80211_config_beacon ( sdata , params ) ;
}
static int ieee80211_del_beacon ( struct wiphy * wiphy , struct net_device * dev )
{
2008-07-29 15:22:52 +04:00
struct ieee80211_sub_if_data * sdata ;
2007-12-19 04:03:33 +03:00
struct beacon_data * old ;
2008-07-29 15:22:52 +04:00
sdata = IEEE80211_DEV_TO_SUB_IF ( dev ) ;
2007-12-19 04:03:33 +03:00
old = sdata - > u . ap . beacon ;
if ( ! old )
return - ENOENT ;
rcu_assign_pointer ( sdata - > u . ap . beacon , NULL ) ;
synchronize_rcu ( ) ;
kfree ( old ) ;
2009-04-23 18:13:26 +04:00
ieee80211_bss_info_change_notify ( sdata , BSS_CHANGED_BEACON_ENABLED ) ;
return 0 ;
2007-12-19 04:03:33 +03:00
}
2007-12-19 04:03:35 +03:00
/* Layer 2 Update frame (802.2 Type 1 LLC XID Update response) */
struct iapp_layer2_update {
u8 da [ ETH_ALEN ] ; /* broadcast */
u8 sa [ ETH_ALEN ] ; /* STA addr */
__be16 len ; /* 6 */
u8 dsap ; /* 0 */
u8 ssap ; /* 0 */
u8 control ;
u8 xid_info [ 3 ] ;
} __attribute__ ( ( packed ) ) ;
static void ieee80211_send_layer2_update ( struct sta_info * sta )
{
struct iapp_layer2_update * msg ;
struct sk_buff * skb ;
/* Send Level 2 Update Frame to update forwarding tables in layer 2
* bridge devices */
skb = dev_alloc_skb ( sizeof ( * msg ) ) ;
if ( ! skb )
return ;
msg = ( struct iapp_layer2_update * ) skb_put ( skb , sizeof ( * msg ) ) ;
/* 802.2 Type 1 Logical Link Control (LLC) Exchange Identifier (XID)
* Update response frame ; IEEE Std 802.2 - 1998 , 5.4 .1 .2 .1 */
memset ( msg - > da , 0xff , ETH_ALEN ) ;
2008-09-11 02:02:02 +04:00
memcpy ( msg - > sa , sta - > sta . addr , ETH_ALEN ) ;
2007-12-19 04:03:35 +03:00
msg - > len = htons ( 6 ) ;
msg - > dsap = 0 ;
msg - > ssap = 0x01 ; /* NULL LSAP, CR Bit: Response */
msg - > control = 0xaf ; /* XID response lsb.1111F101.
* F = 0 ( no poll command ; unsolicited frame ) */
msg - > xid_info [ 0 ] = 0x81 ; /* XID format identifier */
msg - > xid_info [ 1 ] = 1 ; /* LLC types/classes: Type 1 LLC */
msg - > xid_info [ 2 ] = 0 ; /* XID sender's receive window size (RW) */
2008-02-25 18:27:46 +03:00
skb - > dev = sta - > sdata - > dev ;
skb - > protocol = eth_type_trans ( skb , sta - > sdata - > dev ) ;
2007-12-19 04:03:35 +03:00
memset ( skb - > cb , 0 , sizeof ( skb - > cb ) ) ;
netif_rx ( skb ) ;
}
static void sta_apply_parameters ( struct ieee80211_local * local ,
struct sta_info * sta ,
struct station_parameters * params )
{
u32 rates ;
int i , j ;
2008-01-24 21:38:38 +03:00
struct ieee80211_supported_band * sband ;
2008-02-25 18:27:46 +03:00
struct ieee80211_sub_if_data * sdata = sta - > sdata ;
2009-05-11 22:57:56 +04:00
u32 mask , set ;
2007-12-19 04:03:35 +03:00
2008-10-14 18:58:37 +04:00
sband = local - > hw . wiphy - > bands [ local - > oper_channel - > band ] ;
2009-05-11 22:57:56 +04:00
spin_lock_bh ( & sta - > lock ) ;
mask = params - > sta_flags_mask ;
set = params - > sta_flags_set ;
2008-02-25 18:27:47 +03:00
2009-05-11 22:57:56 +04:00
if ( mask & BIT ( NL80211_STA_FLAG_AUTHORIZED ) ) {
2007-12-19 04:03:35 +03:00
sta - > flags & = ~ WLAN_STA_AUTHORIZED ;
2009-05-11 22:57:56 +04:00
if ( set & BIT ( NL80211_STA_FLAG_AUTHORIZED ) )
2007-12-19 04:03:35 +03:00
sta - > flags | = WLAN_STA_AUTHORIZED ;
2009-05-11 22:57:56 +04:00
}
2007-12-19 04:03:35 +03:00
2009-05-11 22:57:56 +04:00
if ( mask & BIT ( NL80211_STA_FLAG_SHORT_PREAMBLE ) ) {
2007-12-19 04:03:35 +03:00
sta - > flags & = ~ WLAN_STA_SHORT_PREAMBLE ;
2009-05-11 22:57:56 +04:00
if ( set & BIT ( NL80211_STA_FLAG_SHORT_PREAMBLE ) )
2007-12-19 04:03:35 +03:00
sta - > flags | = WLAN_STA_SHORT_PREAMBLE ;
2009-05-11 22:57:56 +04:00
}
2007-12-19 04:03:35 +03:00
2009-05-11 22:57:56 +04:00
if ( mask & BIT ( NL80211_STA_FLAG_WME ) ) {
2007-12-19 04:03:35 +03:00
sta - > flags & = ~ WLAN_STA_WME ;
2009-05-11 22:57:56 +04:00
if ( set & BIT ( NL80211_STA_FLAG_WME ) )
2007-12-19 04:03:35 +03:00
sta - > flags | = WLAN_STA_WME ;
2009-05-11 22:57:56 +04:00
}
2009-01-08 14:31:59 +03:00
2009-05-11 22:57:56 +04:00
if ( mask & BIT ( NL80211_STA_FLAG_MFP ) ) {
2009-01-08 14:31:59 +03:00
sta - > flags & = ~ WLAN_STA_MFP ;
2009-05-11 22:57:56 +04:00
if ( set & BIT ( NL80211_STA_FLAG_MFP ) )
2009-01-08 14:31:59 +03:00
sta - > flags | = WLAN_STA_MFP ;
2007-12-19 04:03:35 +03:00
}
2009-05-11 22:57:56 +04:00
spin_unlock_bh ( & sta - > lock ) ;
2007-12-19 04:03:35 +03:00
2009-05-24 18:42:30 +04:00
/*
* cfg80211 validates this ( 1 - 2007 ) and allows setting the AID
* only when creating a new station entry
*/
if ( params - > aid )
sta - > sta . aid = params - > aid ;
2008-02-25 18:27:47 +03:00
/*
* FIXME : updating the following information is racy when this
* function is called from ieee80211_change_station ( ) .
* However , all this information should be static so
* maybe we should just reject attemps to change it .
*/
2007-12-19 04:03:35 +03:00
if ( params - > listen_interval > = 0 )
sta - > listen_interval = params - > listen_interval ;
if ( params - > supported_rates ) {
rates = 0 ;
2008-01-24 21:38:38 +03:00
2007-12-19 04:03:35 +03:00
for ( i = 0 ; i < params - > supported_rates_len ; i + + ) {
int rate = ( params - > supported_rates [ i ] & 0x7f ) * 5 ;
2008-01-24 21:38:38 +03:00
for ( j = 0 ; j < sband - > n_bitrates ; j + + ) {
if ( sband - > bitrates [ j ] . bitrate = = rate )
2007-12-19 04:03:35 +03:00
rates | = BIT ( j ) ;
}
}
2008-09-11 04:45:11 +04:00
sta - > sta . supp_rates [ local - > oper_channel - > band ] = rates ;
2007-12-19 04:03:35 +03:00
}
2008-02-23 17:17:17 +03:00
2008-10-09 14:13:49 +04:00
if ( params - > ht_capa )
2008-10-14 18:58:37 +04:00
ieee80211_ht_cap_ie_to_sta_ht_cap ( sband ,
params - > ht_capa ,
2008-10-09 14:13:49 +04:00
& sta - > sta . ht_cap ) ;
2008-08-25 12:58:58 +04:00
2008-02-23 17:17:19 +03:00
if ( ieee80211_vif_is_mesh ( & sdata - > vif ) & & params - > plink_action ) {
2008-02-23 17:17:17 +03:00
switch ( params - > plink_action ) {
case PLINK_ACTION_OPEN :
mesh_plink_open ( sta ) ;
break ;
case PLINK_ACTION_BLOCK :
mesh_plink_block ( sta ) ;
break ;
}
2008-02-23 17:17:19 +03:00
}
2007-12-19 04:03:35 +03:00
}
static int ieee80211_add_station ( struct wiphy * wiphy , struct net_device * dev ,
u8 * mac , struct station_parameters * params )
{
2008-07-29 15:22:52 +04:00
struct ieee80211_local * local = wiphy_priv ( wiphy ) ;
2007-12-19 04:03:35 +03:00
struct sta_info * sta ;
struct ieee80211_sub_if_data * sdata ;
2008-02-25 18:27:47 +03:00
int err ;
2008-12-12 18:08:31 +03:00
int layer2_update ;
2007-12-19 04:03:35 +03:00
if ( params - > vlan ) {
sdata = IEEE80211_DEV_TO_SUB_IF ( params - > vlan ) ;
2008-09-11 02:01:58 +04:00
if ( sdata - > vif . type ! = NL80211_IFTYPE_AP_VLAN & &
sdata - > vif . type ! = NL80211_IFTYPE_AP )
2007-12-19 04:03:35 +03:00
return - EINVAL ;
} else
sdata = IEEE80211_DEV_TO_SUB_IF ( dev ) ;
2008-02-27 11:56:40 +03:00
if ( compare_ether_addr ( mac , dev - > dev_addr ) = = 0 )
return - EINVAL ;
if ( is_multicast_ether_addr ( mac ) )
return - EINVAL ;
sta = sta_info_alloc ( sdata , mac , GFP_KERNEL ) ;
2008-02-25 18:27:47 +03:00
if ( ! sta )
return - ENOMEM ;
2007-12-19 04:03:35 +03:00
sta - > flags = WLAN_STA_AUTH | WLAN_STA_ASSOC ;
sta_apply_parameters ( local , sta , params ) ;
2008-09-18 20:14:18 +04:00
rate_control_rate_init ( sta ) ;
2007-12-19 04:03:35 +03:00
2008-12-12 18:08:31 +03:00
layer2_update = sdata - > vif . type = = NL80211_IFTYPE_AP_VLAN | |
sdata - > vif . type = = NL80211_IFTYPE_AP ;
2008-02-25 18:27:47 +03:00
rcu_read_lock ( ) ;
err = sta_info_insert ( sta ) ;
if ( err ) {
2008-04-01 17:21:00 +04:00
/* STA has been freed */
2008-12-12 18:08:31 +03:00
if ( err = = - EEXIST & & layer2_update ) {
/* Need to update layer 2 devices on reassociation */
sta = sta_info_get ( local , mac ) ;
if ( sta )
ieee80211_send_layer2_update ( sta ) ;
}
2008-02-25 18:27:47 +03:00
rcu_read_unlock ( ) ;
return err ;
}
2008-12-12 18:08:31 +03:00
if ( layer2_update )
2008-02-25 18:27:47 +03:00
ieee80211_send_layer2_update ( sta ) ;
rcu_read_unlock ( ) ;
2007-12-19 04:03:35 +03:00
return 0 ;
}
static int ieee80211_del_station ( struct wiphy * wiphy , struct net_device * dev ,
u8 * mac )
{
2008-07-29 15:22:52 +04:00
struct ieee80211_local * local = wiphy_priv ( wiphy ) ;
struct ieee80211_sub_if_data * sdata ;
2007-12-19 04:03:35 +03:00
struct sta_info * sta ;
2008-07-29 15:22:52 +04:00
sdata = IEEE80211_DEV_TO_SUB_IF ( dev ) ;
2007-12-19 04:03:35 +03:00
if ( mac ) {
2008-04-10 17:36:09 +04:00
rcu_read_lock ( ) ;
2007-12-19 04:03:35 +03:00
/* XXX: get sta belonging to dev */
sta = sta_info_get ( local , mac ) ;
2008-04-10 17:36:09 +04:00
if ( ! sta ) {
rcu_read_unlock ( ) ;
2007-12-19 04:03:35 +03:00
return - ENOENT ;
2008-04-10 17:36:09 +04:00
}
2007-12-19 04:03:35 +03:00
2008-02-25 18:27:46 +03:00
sta_info_unlink ( & sta ) ;
2008-04-10 17:36:09 +04:00
rcu_read_unlock ( ) ;
2008-03-31 21:23:02 +04:00
sta_info_destroy ( sta ) ;
2007-12-19 04:03:35 +03:00
} else
2008-02-25 18:27:46 +03:00
sta_info_flush ( local , sdata ) ;
2007-12-19 04:03:35 +03:00
return 0 ;
}
static int ieee80211_change_station ( struct wiphy * wiphy ,
struct net_device * dev ,
u8 * mac ,
struct station_parameters * params )
{
2008-07-29 15:22:52 +04:00
struct ieee80211_local * local = wiphy_priv ( wiphy ) ;
2007-12-19 04:03:35 +03:00
struct sta_info * sta ;
struct ieee80211_sub_if_data * vlansdata ;
2008-04-10 17:36:09 +04:00
rcu_read_lock ( ) ;
2007-12-19 04:03:35 +03:00
/* XXX: get sta belonging to dev */
sta = sta_info_get ( local , mac ) ;
2008-04-10 17:36:09 +04:00
if ( ! sta ) {
rcu_read_unlock ( ) ;
2007-12-19 04:03:35 +03:00
return - ENOENT ;
2008-04-10 17:36:09 +04:00
}
2007-12-19 04:03:35 +03:00
2008-02-25 18:27:46 +03:00
if ( params - > vlan & & params - > vlan ! = sta - > sdata - > dev ) {
2007-12-19 04:03:35 +03:00
vlansdata = IEEE80211_DEV_TO_SUB_IF ( params - > vlan ) ;
2008-09-11 02:01:58 +04:00
if ( vlansdata - > vif . type ! = NL80211_IFTYPE_AP_VLAN & &
vlansdata - > vif . type ! = NL80211_IFTYPE_AP ) {
2008-04-10 17:36:09 +04:00
rcu_read_unlock ( ) ;
2007-12-19 04:03:35 +03:00
return - EINVAL ;
2008-04-10 17:36:09 +04:00
}
2007-12-19 04:03:35 +03:00
2008-07-29 15:22:52 +04:00
sta - > sdata = vlansdata ;
2007-12-19 04:03:35 +03:00
ieee80211_send_layer2_update ( sta ) ;
}
sta_apply_parameters ( local , sta , params ) ;
2008-04-10 17:36:09 +04:00
rcu_read_unlock ( ) ;
2007-12-19 04:03:35 +03:00
return 0 ;
}
2008-02-23 17:17:17 +03:00
# ifdef CONFIG_MAC80211_MESH
static int ieee80211_add_mpath ( struct wiphy * wiphy , struct net_device * dev ,
u8 * dst , u8 * next_hop )
{
2008-07-29 15:22:52 +04:00
struct ieee80211_local * local = wiphy_priv ( wiphy ) ;
struct ieee80211_sub_if_data * sdata ;
2008-02-23 17:17:17 +03:00
struct mesh_path * mpath ;
struct sta_info * sta ;
int err ;
2008-07-29 15:22:52 +04:00
sdata = IEEE80211_DEV_TO_SUB_IF ( dev ) ;
2008-02-25 18:27:46 +03:00
rcu_read_lock ( ) ;
2008-02-23 17:17:17 +03:00
sta = sta_info_get ( local , next_hop ) ;
2008-02-25 18:27:46 +03:00
if ( ! sta ) {
rcu_read_unlock ( ) ;
2008-02-23 17:17:17 +03:00
return - ENOENT ;
2008-02-25 18:27:46 +03:00
}
2008-02-23 17:17:17 +03:00
2008-08-03 04:04:37 +04:00
err = mesh_path_add ( dst , sdata ) ;
2008-02-25 18:27:46 +03:00
if ( err ) {
rcu_read_unlock ( ) ;
2008-02-23 17:17:17 +03:00
return err ;
2008-02-25 18:27:46 +03:00
}
2008-02-23 17:17:17 +03:00
2008-08-03 04:04:37 +04:00
mpath = mesh_path_lookup ( dst , sdata ) ;
2008-02-23 17:17:17 +03:00
if ( ! mpath ) {
rcu_read_unlock ( ) ;
return - ENXIO ;
}
mesh_path_fix_nexthop ( mpath , sta ) ;
2008-02-25 18:27:46 +03:00
2008-02-23 17:17:17 +03:00
rcu_read_unlock ( ) ;
return 0 ;
}
static int ieee80211_del_mpath ( struct wiphy * wiphy , struct net_device * dev ,
u8 * dst )
{
2008-08-03 04:04:37 +04:00
struct ieee80211_sub_if_data * sdata = IEEE80211_DEV_TO_SUB_IF ( dev ) ;
2008-02-23 17:17:17 +03:00
if ( dst )
2008-08-03 04:04:37 +04:00
return mesh_path_del ( dst , sdata ) ;
2008-02-23 17:17:17 +03:00
2008-08-03 04:04:37 +04:00
mesh_path_flush ( sdata ) ;
2008-02-23 17:17:17 +03:00
return 0 ;
}
static int ieee80211_change_mpath ( struct wiphy * wiphy ,
struct net_device * dev ,
u8 * dst , u8 * next_hop )
{
2008-07-29 15:22:52 +04:00
struct ieee80211_local * local = wiphy_priv ( wiphy ) ;
struct ieee80211_sub_if_data * sdata ;
2008-02-23 17:17:17 +03:00
struct mesh_path * mpath ;
struct sta_info * sta ;
2008-07-29 15:22:52 +04:00
sdata = IEEE80211_DEV_TO_SUB_IF ( dev ) ;
2008-02-25 18:27:46 +03:00
rcu_read_lock ( ) ;
2008-02-23 17:17:17 +03:00
sta = sta_info_get ( local , next_hop ) ;
2008-02-25 18:27:46 +03:00
if ( ! sta ) {
rcu_read_unlock ( ) ;
2008-02-23 17:17:17 +03:00
return - ENOENT ;
2008-02-25 18:27:46 +03:00
}
2008-02-23 17:17:17 +03:00
2008-08-03 04:04:37 +04:00
mpath = mesh_path_lookup ( dst , sdata ) ;
2008-02-23 17:17:17 +03:00
if ( ! mpath ) {
rcu_read_unlock ( ) ;
return - ENOENT ;
}
mesh_path_fix_nexthop ( mpath , sta ) ;
2008-02-25 18:27:46 +03:00
2008-02-23 17:17:17 +03:00
rcu_read_unlock ( ) ;
return 0 ;
}
static void mpath_set_pinfo ( struct mesh_path * mpath , u8 * next_hop ,
struct mpath_info * pinfo )
{
if ( mpath - > next_hop )
2008-09-11 02:02:02 +04:00
memcpy ( next_hop , mpath - > next_hop - > sta . addr , ETH_ALEN ) ;
2008-02-23 17:17:17 +03:00
else
memset ( next_hop , 0 , ETH_ALEN ) ;
pinfo - > filled = MPATH_INFO_FRAME_QLEN |
MPATH_INFO_DSN |
MPATH_INFO_METRIC |
MPATH_INFO_EXPTIME |
MPATH_INFO_DISCOVERY_TIMEOUT |
MPATH_INFO_DISCOVERY_RETRIES |
MPATH_INFO_FLAGS ;
pinfo - > frame_qlen = mpath - > frame_queue . qlen ;
pinfo - > dsn = mpath - > dsn ;
pinfo - > metric = mpath - > metric ;
if ( time_before ( jiffies , mpath - > exp_time ) )
pinfo - > exptime = jiffies_to_msecs ( mpath - > exp_time - jiffies ) ;
pinfo - > discovery_timeout =
jiffies_to_msecs ( mpath - > discovery_timeout ) ;
pinfo - > discovery_retries = mpath - > discovery_retries ;
pinfo - > flags = 0 ;
if ( mpath - > flags & MESH_PATH_ACTIVE )
pinfo - > flags | = NL80211_MPATH_FLAG_ACTIVE ;
if ( mpath - > flags & MESH_PATH_RESOLVING )
pinfo - > flags | = NL80211_MPATH_FLAG_RESOLVING ;
if ( mpath - > flags & MESH_PATH_DSN_VALID )
pinfo - > flags | = NL80211_MPATH_FLAG_DSN_VALID ;
if ( mpath - > flags & MESH_PATH_FIXED )
pinfo - > flags | = NL80211_MPATH_FLAG_FIXED ;
if ( mpath - > flags & MESH_PATH_RESOLVING )
pinfo - > flags | = NL80211_MPATH_FLAG_RESOLVING ;
pinfo - > flags = mpath - > flags ;
}
static int ieee80211_get_mpath ( struct wiphy * wiphy , struct net_device * dev ,
u8 * dst , u8 * next_hop , struct mpath_info * pinfo )
{
2008-07-29 15:22:52 +04:00
struct ieee80211_sub_if_data * sdata ;
2008-02-23 17:17:17 +03:00
struct mesh_path * mpath ;
2008-07-29 15:22:52 +04:00
sdata = IEEE80211_DEV_TO_SUB_IF ( dev ) ;
2008-02-23 17:17:17 +03:00
rcu_read_lock ( ) ;
2008-08-03 04:04:37 +04:00
mpath = mesh_path_lookup ( dst , sdata ) ;
2008-02-23 17:17:17 +03:00
if ( ! mpath ) {
rcu_read_unlock ( ) ;
return - ENOENT ;
}
memcpy ( dst , mpath - > dst , ETH_ALEN ) ;
mpath_set_pinfo ( mpath , next_hop , pinfo ) ;
rcu_read_unlock ( ) ;
return 0 ;
}
static int ieee80211_dump_mpath ( struct wiphy * wiphy , struct net_device * dev ,
int idx , u8 * dst , u8 * next_hop ,
struct mpath_info * pinfo )
{
2008-07-29 15:22:52 +04:00
struct ieee80211_sub_if_data * sdata ;
2008-02-23 17:17:17 +03:00
struct mesh_path * mpath ;
2008-07-29 15:22:52 +04:00
sdata = IEEE80211_DEV_TO_SUB_IF ( dev ) ;
2008-02-23 17:17:17 +03:00
rcu_read_lock ( ) ;
2008-08-03 04:04:37 +04:00
mpath = mesh_path_lookup_by_idx ( idx , sdata ) ;
2008-02-23 17:17:17 +03:00
if ( ! mpath ) {
rcu_read_unlock ( ) ;
return - ENOENT ;
}
memcpy ( dst , mpath - > dst , ETH_ALEN ) ;
mpath_set_pinfo ( mpath , next_hop , pinfo ) ;
rcu_read_unlock ( ) ;
return 0 ;
}
2008-10-21 23:03:48 +04:00
static int ieee80211_get_mesh_params ( struct wiphy * wiphy ,
struct net_device * dev ,
struct mesh_config * conf )
{
struct ieee80211_sub_if_data * sdata ;
sdata = IEEE80211_DEV_TO_SUB_IF ( dev ) ;
memcpy ( conf , & ( sdata - > u . mesh . mshcfg ) , sizeof ( struct mesh_config ) ) ;
return 0 ;
}
static inline bool _chg_mesh_attr ( enum nl80211_meshconf_params parm , u32 mask )
{
return ( mask > > ( parm - 1 ) ) & 0x1 ;
}
static int ieee80211_set_mesh_params ( struct wiphy * wiphy ,
struct net_device * dev ,
const struct mesh_config * nconf , u32 mask )
{
struct mesh_config * conf ;
struct ieee80211_sub_if_data * sdata ;
sdata = IEEE80211_DEV_TO_SUB_IF ( dev ) ;
/* Set the config options which we are interested in setting */
conf = & ( sdata - > u . mesh . mshcfg ) ;
if ( _chg_mesh_attr ( NL80211_MESHCONF_RETRY_TIMEOUT , mask ) )
conf - > dot11MeshRetryTimeout = nconf - > dot11MeshRetryTimeout ;
if ( _chg_mesh_attr ( NL80211_MESHCONF_CONFIRM_TIMEOUT , mask ) )
conf - > dot11MeshConfirmTimeout = nconf - > dot11MeshConfirmTimeout ;
if ( _chg_mesh_attr ( NL80211_MESHCONF_HOLDING_TIMEOUT , mask ) )
conf - > dot11MeshHoldingTimeout = nconf - > dot11MeshHoldingTimeout ;
if ( _chg_mesh_attr ( NL80211_MESHCONF_MAX_PEER_LINKS , mask ) )
conf - > dot11MeshMaxPeerLinks = nconf - > dot11MeshMaxPeerLinks ;
if ( _chg_mesh_attr ( NL80211_MESHCONF_MAX_RETRIES , mask ) )
conf - > dot11MeshMaxRetries = nconf - > dot11MeshMaxRetries ;
if ( _chg_mesh_attr ( NL80211_MESHCONF_TTL , mask ) )
conf - > dot11MeshTTL = nconf - > dot11MeshTTL ;
if ( _chg_mesh_attr ( NL80211_MESHCONF_AUTO_OPEN_PLINKS , mask ) )
conf - > auto_open_plinks = nconf - > auto_open_plinks ;
if ( _chg_mesh_attr ( NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES , mask ) )
conf - > dot11MeshHWMPmaxPREQretries =
nconf - > dot11MeshHWMPmaxPREQretries ;
if ( _chg_mesh_attr ( NL80211_MESHCONF_PATH_REFRESH_TIME , mask ) )
conf - > path_refresh_time = nconf - > path_refresh_time ;
if ( _chg_mesh_attr ( NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT , mask ) )
conf - > min_discovery_timeout = nconf - > min_discovery_timeout ;
if ( _chg_mesh_attr ( NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT , mask ) )
conf - > dot11MeshHWMPactivePathTimeout =
nconf - > dot11MeshHWMPactivePathTimeout ;
if ( _chg_mesh_attr ( NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL , mask ) )
conf - > dot11MeshHWMPpreqMinInterval =
nconf - > dot11MeshHWMPpreqMinInterval ;
if ( _chg_mesh_attr ( NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME ,
mask ) )
conf - > dot11MeshHWMPnetDiameterTraversalTime =
nconf - > dot11MeshHWMPnetDiameterTraversalTime ;
return 0 ;
}
2008-02-23 17:17:17 +03:00
# endif
2008-08-07 21:07:01 +04:00
static int ieee80211_change_bss ( struct wiphy * wiphy ,
struct net_device * dev ,
struct bss_parameters * params )
{
struct ieee80211_sub_if_data * sdata ;
u32 changed = 0 ;
sdata = IEEE80211_DEV_TO_SUB_IF ( dev ) ;
if ( params - > use_cts_prot > = 0 ) {
2008-10-11 03:51:51 +04:00
sdata - > vif . bss_conf . use_cts_prot = params - > use_cts_prot ;
2008-08-07 21:07:01 +04:00
changed | = BSS_CHANGED_ERP_CTS_PROT ;
}
if ( params - > use_short_preamble > = 0 ) {
2008-10-11 03:51:51 +04:00
sdata - > vif . bss_conf . use_short_preamble =
2008-08-07 21:07:01 +04:00
params - > use_short_preamble ;
changed | = BSS_CHANGED_ERP_PREAMBLE ;
}
if ( params - > use_short_slot_time > = 0 ) {
2008-10-11 03:51:51 +04:00
sdata - > vif . bss_conf . use_short_slot =
2008-08-07 21:07:01 +04:00
params - > use_short_slot_time ;
changed | = BSS_CHANGED_ERP_SLOT ;
}
2008-10-30 17:59:22 +03:00
if ( params - > basic_rates ) {
int i , j ;
u32 rates = 0 ;
struct ieee80211_local * local = wiphy_priv ( wiphy ) ;
struct ieee80211_supported_band * sband =
wiphy - > bands [ local - > oper_channel - > band ] ;
for ( i = 0 ; i < params - > basic_rates_len ; i + + ) {
int rate = ( params - > basic_rates [ i ] & 0x7f ) * 5 ;
for ( j = 0 ; j < sband - > n_bitrates ; j + + ) {
if ( sband - > bitrates [ j ] . bitrate = = rate )
rates | = BIT ( j ) ;
}
}
sdata - > vif . bss_conf . basic_rates = rates ;
changed | = BSS_CHANGED_BASIC_RATES ;
}
2008-08-07 21:07:01 +04:00
ieee80211_bss_info_change_notify ( sdata , changed ) ;
return 0 ;
}
2008-10-30 17:59:24 +03:00
static int ieee80211_set_txq_params ( struct wiphy * wiphy ,
struct ieee80211_txq_params * params )
{
struct ieee80211_local * local = wiphy_priv ( wiphy ) ;
struct ieee80211_tx_queue_params p ;
if ( ! local - > ops - > conf_tx )
return - EOPNOTSUPP ;
memset ( & p , 0 , sizeof ( p ) ) ;
p . aifs = params - > aifs ;
p . cw_max = params - > cwmax ;
p . cw_min = params - > cwmin ;
p . txop = params - > txop ;
2009-04-23 20:52:52 +04:00
if ( drv_conf_tx ( local , params - > queue , & p ) ) {
2008-10-30 17:59:24 +03:00
printk ( KERN_DEBUG " %s: failed to set TX queue "
2009-06-09 18:18:32 +04:00
" parameters for queue %d \n " ,
wiphy_name ( local - > hw . wiphy ) , params - > queue ) ;
2008-10-30 17:59:24 +03:00
return - EINVAL ;
}
return 0 ;
}
2008-11-26 17:15:24 +03:00
static int ieee80211_set_channel ( struct wiphy * wiphy ,
struct ieee80211_channel * chan ,
2008-12-12 09:27:43 +03:00
enum nl80211_channel_type channel_type )
2008-11-26 17:15:24 +03:00
{
struct ieee80211_local * local = wiphy_priv ( wiphy ) ;
local - > oper_channel = chan ;
2008-12-12 09:27:43 +03:00
local - > oper_channel_type = channel_type ;
2008-11-26 17:15:24 +03:00
return ieee80211_hw_config ( local , IEEE80211_CONF_CHANGE_CHANNEL ) ;
}
2009-01-19 19:20:53 +03:00
# ifdef CONFIG_PM
static int ieee80211_suspend ( struct wiphy * wiphy )
{
return __ieee80211_suspend ( wiphy_priv ( wiphy ) ) ;
}
static int ieee80211_resume ( struct wiphy * wiphy )
{
return __ieee80211_resume ( wiphy_priv ( wiphy ) ) ;
}
# else
# define ieee80211_suspend NULL
# define ieee80211_resume NULL
# endif
2009-02-10 23:25:55 +03:00
static int ieee80211_scan ( struct wiphy * wiphy ,
struct net_device * dev ,
struct cfg80211_scan_request * req )
{
struct ieee80211_sub_if_data * sdata ;
sdata = IEEE80211_DEV_TO_SUB_IF ( dev ) ;
if ( sdata - > vif . type ! = NL80211_IFTYPE_STATION & &
sdata - > vif . type ! = NL80211_IFTYPE_ADHOC & &
2009-04-16 19:44:53 +04:00
sdata - > vif . type ! = NL80211_IFTYPE_MESH_POINT & &
( sdata - > vif . type ! = NL80211_IFTYPE_AP | | sdata - > u . ap . beacon ) )
2009-02-10 23:25:55 +03:00
return - EOPNOTSUPP ;
return ieee80211_request_scan ( sdata , req ) ;
}
2009-03-19 14:39:22 +03:00
static int ieee80211_auth ( struct wiphy * wiphy , struct net_device * dev ,
struct cfg80211_auth_request * req )
{
struct ieee80211_sub_if_data * sdata ;
sdata = IEEE80211_DEV_TO_SUB_IF ( dev ) ;
switch ( req - > auth_type ) {
case NL80211_AUTHTYPE_OPEN_SYSTEM :
sdata - > u . mgd . auth_algs = IEEE80211_AUTH_ALG_OPEN ;
break ;
case NL80211_AUTHTYPE_SHARED_KEY :
sdata - > u . mgd . auth_algs = IEEE80211_AUTH_ALG_SHARED_KEY ;
break ;
case NL80211_AUTHTYPE_FT :
sdata - > u . mgd . auth_algs = IEEE80211_AUTH_ALG_FT ;
break ;
case NL80211_AUTHTYPE_NETWORK_EAP :
sdata - > u . mgd . auth_algs = IEEE80211_AUTH_ALG_LEAP ;
break ;
default :
return - EOPNOTSUPP ;
}
memcpy ( sdata - > u . mgd . bssid , req - > peer_addr , ETH_ALEN ) ;
sdata - > u . mgd . flags & = ~ IEEE80211_STA_AUTO_BSSID_SEL ;
sdata - > u . mgd . flags | = IEEE80211_STA_BSSID_SET ;
/* TODO: req->chan */
sdata - > u . mgd . flags | = IEEE80211_STA_AUTO_CHANNEL_SEL ;
if ( req - > ssid ) {
sdata - > u . mgd . flags | = IEEE80211_STA_SSID_SET ;
memcpy ( sdata - > u . mgd . ssid , req - > ssid , req - > ssid_len ) ;
sdata - > u . mgd . ssid_len = req - > ssid_len ;
sdata - > u . mgd . flags & = ~ IEEE80211_STA_AUTO_SSID_SEL ;
}
kfree ( sdata - > u . mgd . sme_auth_ie ) ;
sdata - > u . mgd . sme_auth_ie = NULL ;
sdata - > u . mgd . sme_auth_ie_len = 0 ;
if ( req - > ie ) {
sdata - > u . mgd . sme_auth_ie = kmalloc ( req - > ie_len , GFP_KERNEL ) ;
if ( sdata - > u . mgd . sme_auth_ie = = NULL )
return - ENOMEM ;
memcpy ( sdata - > u . mgd . sme_auth_ie , req - > ie , req - > ie_len ) ;
sdata - > u . mgd . sme_auth_ie_len = req - > ie_len ;
}
sdata - > u . mgd . flags | = IEEE80211_STA_EXT_SME ;
sdata - > u . mgd . state = IEEE80211_STA_MLME_DIRECT_PROBE ;
ieee80211_sta_req_auth ( sdata ) ;
return 0 ;
}
static int ieee80211_assoc ( struct wiphy * wiphy , struct net_device * dev ,
struct cfg80211_assoc_request * req )
{
struct ieee80211_sub_if_data * sdata ;
int ret ;
sdata = IEEE80211_DEV_TO_SUB_IF ( dev ) ;
if ( memcmp ( sdata - > u . mgd . bssid , req - > peer_addr , ETH_ALEN ) ! = 0 | |
! ( sdata - > u . mgd . flags & IEEE80211_STA_AUTHENTICATED ) )
return - ENOLINK ; /* not authenticated */
sdata - > u . mgd . flags & = ~ IEEE80211_STA_AUTO_BSSID_SEL ;
sdata - > u . mgd . flags | = IEEE80211_STA_BSSID_SET ;
/* TODO: req->chan */
sdata - > u . mgd . flags | = IEEE80211_STA_AUTO_CHANNEL_SEL ;
if ( req - > ssid ) {
sdata - > u . mgd . flags | = IEEE80211_STA_SSID_SET ;
memcpy ( sdata - > u . mgd . ssid , req - > ssid , req - > ssid_len ) ;
sdata - > u . mgd . ssid_len = req - > ssid_len ;
sdata - > u . mgd . flags & = ~ IEEE80211_STA_AUTO_SSID_SEL ;
} else
sdata - > u . mgd . flags | = IEEE80211_STA_AUTO_SSID_SEL ;
ret = ieee80211_sta_set_extra_ie ( sdata , req - > ie , req - > ie_len ) ;
2009-05-28 06:56:05 +04:00
if ( ret & & ret ! = - EALREADY )
2009-03-19 14:39:22 +03:00
return ret ;
2009-05-06 23:09:37 +04:00
if ( req - > use_mfp ) {
sdata - > u . mgd . mfp = IEEE80211_MFP_REQUIRED ;
sdata - > u . mgd . flags | = IEEE80211_STA_MFP_ENABLED ;
} else {
sdata - > u . mgd . mfp = IEEE80211_MFP_DISABLED ;
sdata - > u . mgd . flags & = ~ IEEE80211_STA_MFP_ENABLED ;
}
2009-05-11 22:57:57 +04:00
if ( req - > control_port )
sdata - > u . mgd . flags | = IEEE80211_STA_CONTROL_PORT ;
else
sdata - > u . mgd . flags & = ~ IEEE80211_STA_CONTROL_PORT ;
2009-03-19 14:39:22 +03:00
sdata - > u . mgd . flags | = IEEE80211_STA_EXT_SME ;
sdata - > u . mgd . state = IEEE80211_STA_MLME_ASSOCIATE ;
ieee80211_sta_req_auth ( sdata ) ;
return 0 ;
}
static int ieee80211_deauth ( struct wiphy * wiphy , struct net_device * dev ,
struct cfg80211_deauth_request * req )
{
2009-04-19 21:57:45 +04:00
struct ieee80211_sub_if_data * sdata = IEEE80211_DEV_TO_SUB_IF ( dev ) ;
2009-03-19 14:39:22 +03:00
2009-04-19 21:57:45 +04:00
/* TODO: req->ie, req->peer_addr */
2009-03-19 14:39:22 +03:00
return ieee80211_sta_deauthenticate ( sdata , req - > reason_code ) ;
}
static int ieee80211_disassoc ( struct wiphy * wiphy , struct net_device * dev ,
struct cfg80211_disassoc_request * req )
{
2009-04-19 21:57:45 +04:00
struct ieee80211_sub_if_data * sdata = IEEE80211_DEV_TO_SUB_IF ( dev ) ;
2009-03-19 14:39:22 +03:00
2009-04-19 21:57:45 +04:00
/* TODO: req->ie, req->peer_addr */
2009-03-19 14:39:22 +03:00
return ieee80211_sta_disassociate ( sdata , req - > reason_code ) ;
}
2009-04-19 23:25:43 +04:00
static int ieee80211_join_ibss ( struct wiphy * wiphy , struct net_device * dev ,
struct cfg80211_ibss_params * params )
{
struct ieee80211_sub_if_data * sdata = IEEE80211_DEV_TO_SUB_IF ( dev ) ;
return ieee80211_ibss_join ( sdata , params ) ;
}
static int ieee80211_leave_ibss ( struct wiphy * wiphy , struct net_device * dev )
{
struct ieee80211_sub_if_data * sdata = IEEE80211_DEV_TO_SUB_IF ( dev ) ;
return ieee80211_ibss_leave ( sdata ) ;
}
2009-04-20 20:39:05 +04:00
static int ieee80211_set_wiphy_params ( struct wiphy * wiphy , u32 changed )
{
struct ieee80211_local * local = wiphy_priv ( wiphy ) ;
2009-04-23 20:52:52 +04:00
int err ;
2009-04-20 20:39:05 +04:00
if ( changed & WIPHY_PARAM_RTS_THRESHOLD ) {
2009-04-23 20:52:52 +04:00
err = drv_set_rts_threshold ( local , wiphy - > rts_threshold ) ;
2009-04-20 20:39:05 +04:00
2009-04-23 20:52:52 +04:00
if ( err )
return err ;
2009-04-20 20:39:05 +04:00
}
if ( changed & WIPHY_PARAM_RETRY_SHORT )
local - > hw . conf . short_frame_max_tx_count = wiphy - > retry_short ;
if ( changed & WIPHY_PARAM_RETRY_LONG )
local - > hw . conf . long_frame_max_tx_count = wiphy - > retry_long ;
if ( changed &
( WIPHY_PARAM_RETRY_SHORT | WIPHY_PARAM_RETRY_LONG ) )
ieee80211_hw_config ( local , IEEE80211_CONF_CHANGE_RETRY_LIMITS ) ;
return 0 ;
}
2009-06-02 15:01:39 +04:00
static int ieee80211_set_tx_power ( struct wiphy * wiphy ,
enum tx_power_setting type , int dbm )
{
struct ieee80211_local * local = wiphy_priv ( wiphy ) ;
struct ieee80211_channel * chan = local - > hw . conf . channel ;
u32 changes = 0 ;
switch ( type ) {
case TX_POWER_AUTOMATIC :
local - > user_power_level = - 1 ;
break ;
case TX_POWER_LIMITED :
if ( dbm < 0 )
return - EINVAL ;
local - > user_power_level = dbm ;
break ;
case TX_POWER_FIXED :
if ( dbm < 0 )
return - EINVAL ;
/* TODO: move to cfg80211 when it knows the channel */
if ( dbm > chan - > max_power )
return - EINVAL ;
local - > user_power_level = dbm ;
break ;
}
ieee80211_hw_config ( local , changes ) ;
return 0 ;
}
static int ieee80211_get_tx_power ( struct wiphy * wiphy , int * dbm )
{
struct ieee80211_local * local = wiphy_priv ( wiphy ) ;
* dbm = local - > hw . conf . power_level ;
return 0 ;
}
2009-06-02 15:01:41 +04:00
static void ieee80211_rfkill_poll ( struct wiphy * wiphy )
{
struct ieee80211_local * local = wiphy_priv ( wiphy ) ;
drv_rfkill_poll ( local ) ;
}
2009-07-01 23:26:51 +04:00
# ifdef CONFIG_NL80211_TESTMODE
int ieee80211_testmode_cmd ( struct wiphy * wiphy , void * data , int len )
{
struct ieee80211_local * local = wiphy_priv ( wiphy ) ;
if ( ! local - > ops - > testmode_cmd )
return - EOPNOTSUPP ;
return local - > ops - > testmode_cmd ( & local - > hw , data , len ) ;
}
# endif
2007-05-05 22:45:53 +04:00
struct cfg80211_ops mac80211_config_ops = {
. add_virtual_intf = ieee80211_add_iface ,
. del_virtual_intf = ieee80211_del_iface ,
2007-09-28 23:52:27 +04:00
. change_virtual_intf = ieee80211_change_iface ,
2007-12-19 04:03:30 +03:00
. add_key = ieee80211_add_key ,
. del_key = ieee80211_del_key ,
2007-12-19 04:03:31 +03:00
. get_key = ieee80211_get_key ,
2007-12-19 04:03:30 +03:00
. set_default_key = ieee80211_config_default_key ,
2009-01-08 14:32:02 +03:00
. set_default_mgmt_key = ieee80211_config_default_mgmt_key ,
2007-12-19 04:03:33 +03:00
. add_beacon = ieee80211_add_beacon ,
. set_beacon = ieee80211_set_beacon ,
. del_beacon = ieee80211_del_beacon ,
2007-12-19 04:03:35 +03:00
. add_station = ieee80211_add_station ,
. del_station = ieee80211_del_station ,
. change_station = ieee80211_change_station ,
2007-12-19 04:03:37 +03:00
. get_station = ieee80211_get_station ,
2008-02-23 17:17:17 +03:00
. dump_station = ieee80211_dump_station ,
# ifdef CONFIG_MAC80211_MESH
. add_mpath = ieee80211_add_mpath ,
. del_mpath = ieee80211_del_mpath ,
. change_mpath = ieee80211_change_mpath ,
. get_mpath = ieee80211_get_mpath ,
. dump_mpath = ieee80211_dump_mpath ,
2008-10-21 23:03:48 +04:00
. set_mesh_params = ieee80211_set_mesh_params ,
. get_mesh_params = ieee80211_get_mesh_params ,
2008-02-23 17:17:17 +03:00
# endif
2008-08-07 21:07:01 +04:00
. change_bss = ieee80211_change_bss ,
2008-10-30 17:59:24 +03:00
. set_txq_params = ieee80211_set_txq_params ,
2008-11-26 17:15:24 +03:00
. set_channel = ieee80211_set_channel ,
2009-01-19 19:20:53 +03:00
. suspend = ieee80211_suspend ,
. resume = ieee80211_resume ,
2009-02-10 23:25:55 +03:00
. scan = ieee80211_scan ,
2009-03-19 14:39:22 +03:00
. auth = ieee80211_auth ,
. assoc = ieee80211_assoc ,
. deauth = ieee80211_deauth ,
. disassoc = ieee80211_disassoc ,
2009-04-19 23:25:43 +04:00
. join_ibss = ieee80211_join_ibss ,
. leave_ibss = ieee80211_leave_ibss ,
2009-04-20 20:39:05 +04:00
. set_wiphy_params = ieee80211_set_wiphy_params ,
2009-06-02 15:01:39 +04:00
. set_tx_power = ieee80211_set_tx_power ,
. get_tx_power = ieee80211_get_tx_power ,
2009-06-02 15:01:41 +04:00
. rfkill_poll = ieee80211_rfkill_poll ,
2009-07-01 23:26:51 +04:00
CFG80211_TESTMODE_CMD ( ieee80211_testmode_cmd )
2007-05-05 22:45:53 +04:00
} ;