2008-09-09 16:49:03 +04:00
/*
* spectrum management
*
* Copyright 2003 , Jouni Malinen < jkmaline @ cc . hut . fi >
* Copyright 2002 - 2005 , Instant802 Networks , Inc .
* Copyright 2005 - 2006 , Devicescape Software , Inc .
* Copyright 2006 - 2007 Jiri Benc < jbenc @ suse . cz >
* Copyright 2007 , Michael Wu < flamingice @ sourmilk . net >
* Copyright 2007 - 2008 , Intel Corporation
* Copyright 2008 , Johannes Berg < johannes @ sipsolutions . net >
*
* 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 .
*/
# include <linux/ieee80211.h>
# include <net/wireless.h>
# include <net/mac80211.h>
# include "ieee80211_i.h"
# include "sta_info.h"
# include "wme.h"
static void ieee80211_send_refuse_measurement_request ( struct ieee80211_sub_if_data * sdata ,
struct ieee80211_msrment_ie * request_ie ,
const u8 * da , const u8 * bssid ,
u8 dialog_token )
{
struct ieee80211_local * local = sdata - > local ;
struct sk_buff * skb ;
struct ieee80211_mgmt * msr_report ;
skb = dev_alloc_skb ( sizeof ( * msr_report ) + local - > hw . extra_tx_headroom +
sizeof ( struct ieee80211_msrment_ie ) ) ;
if ( ! skb ) {
printk ( KERN_ERR " %s: failed to allocate buffer for "
" measurement report frame \n " , sdata - > dev - > name ) ;
return ;
}
skb_reserve ( skb , local - > hw . extra_tx_headroom ) ;
msr_report = ( struct ieee80211_mgmt * ) skb_put ( skb , 24 ) ;
memset ( msr_report , 0 , 24 ) ;
memcpy ( msr_report - > da , da , ETH_ALEN ) ;
memcpy ( msr_report - > sa , sdata - > dev - > dev_addr , ETH_ALEN ) ;
memcpy ( msr_report - > bssid , bssid , ETH_ALEN ) ;
msr_report - > frame_control = cpu_to_le16 ( IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_ACTION ) ;
skb_put ( skb , 1 + sizeof ( msr_report - > u . action . u . measurement ) ) ;
msr_report - > u . action . category = WLAN_CATEGORY_SPECTRUM_MGMT ;
msr_report - > u . action . u . measurement . action_code =
WLAN_ACTION_SPCT_MSR_RPRT ;
msr_report - > u . action . u . measurement . dialog_token = dialog_token ;
msr_report - > u . action . u . measurement . element_id = WLAN_EID_MEASURE_REPORT ;
msr_report - > u . action . u . measurement . length =
sizeof ( struct ieee80211_msrment_ie ) ;
memset ( & msr_report - > u . action . u . measurement . msr_elem , 0 ,
sizeof ( struct ieee80211_msrment_ie ) ) ;
msr_report - > u . action . u . measurement . msr_elem . token = request_ie - > token ;
msr_report - > u . action . u . measurement . msr_elem . mode | =
IEEE80211_SPCT_MSR_RPRT_MODE_REFUSED ;
msr_report - > u . action . u . measurement . msr_elem . type = request_ie - > type ;
2009-01-08 14:32:07 +03:00
ieee80211_tx_skb ( sdata , skb , 1 ) ;
2008-09-09 16:49:03 +04:00
}
void ieee80211_process_measurement_req ( struct ieee80211_sub_if_data * sdata ,
struct ieee80211_mgmt * mgmt ,
size_t len )
{
/*
* Ignoring measurement request is spec violation .
* Mandatory measurements must be reported optional
* measurements might be refused or reported incapable
* For now just refuse
* TODO : Answer basic measurement as unmeasured
*/
ieee80211_send_refuse_measurement_request ( sdata ,
& mgmt - > u . action . u . measurement . msr_elem ,
mgmt - > sa , mgmt - > bssid ,
mgmt - > u . action . u . measurement . dialog_token ) ;
}
2009-01-06 06:58:37 +03:00
void ieee80211_chswitch_work ( struct work_struct * work )
{
struct ieee80211_sub_if_data * sdata =
2009-02-15 14:44:28 +03:00
container_of ( work , struct ieee80211_sub_if_data , u . mgd . chswitch_work ) ;
2009-01-06 06:58:37 +03:00
struct ieee80211_bss * bss ;
2009-02-15 14:44:28 +03:00
struct ieee80211_if_managed * ifmgd = & sdata - > u . mgd ;
2009-01-06 06:58:37 +03:00
if ( ! netif_running ( sdata - > dev ) )
return ;
2009-02-15 14:44:28 +03:00
bss = ieee80211_rx_bss_get ( sdata - > local , ifmgd - > bssid ,
2009-01-06 06:58:37 +03:00
sdata - > local - > hw . conf . channel - > center_freq ,
2009-02-15 14:44:28 +03:00
ifmgd - > ssid , ifmgd - > ssid_len ) ;
2009-01-06 06:58:37 +03:00
if ( ! bss )
goto exit ;
sdata - > local - > oper_channel = sdata - > local - > csa_channel ;
2009-02-10 23:26:00 +03:00
/* XXX: shouldn't really modify cfg80211-owned data! */
2009-01-06 06:58:37 +03:00
if ( ! ieee80211_hw_config ( sdata - > local , IEEE80211_CONF_CHANGE_CHANNEL ) )
2009-02-10 23:26:00 +03:00
bss - > cbss . channel = sdata - > local - > oper_channel ;
2009-01-06 06:58:37 +03:00
ieee80211_rx_bss_put ( sdata - > local , bss ) ;
exit :
2009-02-15 14:44:28 +03:00
ifmgd - > flags & = ~ IEEE80211_STA_CSA_RECEIVED ;
2009-01-06 06:58:37 +03:00
ieee80211_wake_queues_by_reason ( & sdata - > local - > hw ,
IEEE80211_QUEUE_STOP_REASON_CSA ) ;
}
void ieee80211_chswitch_timer ( unsigned long data )
{
struct ieee80211_sub_if_data * sdata =
( struct ieee80211_sub_if_data * ) data ;
2009-02-15 14:44:28 +03:00
struct ieee80211_if_managed * ifmgd = & sdata - > u . mgd ;
2009-01-06 06:58:37 +03:00
2009-02-15 14:44:28 +03:00
queue_work ( sdata - > local - > hw . workqueue , & ifmgd - > chswitch_work ) ;
2009-01-06 06:58:37 +03:00
}
void ieee80211_process_chanswitch ( struct ieee80211_sub_if_data * sdata ,
struct ieee80211_channel_sw_ie * sw_elem ,
struct ieee80211_bss * bss )
{
struct ieee80211_channel * new_ch ;
2009-02-15 14:44:28 +03:00
struct ieee80211_if_managed * ifmgd = & sdata - > u . mgd ;
2009-01-06 06:58:37 +03:00
int new_freq = ieee80211_channel_to_frequency ( sw_elem - > new_ch_num ) ;
/* FIXME: Handle ADHOC later */
if ( sdata - > vif . type ! = NL80211_IFTYPE_STATION )
return ;
2009-02-15 14:44:28 +03:00
if ( ifmgd - > state ! = IEEE80211_STA_MLME_ASSOCIATED )
2009-01-06 06:58:37 +03:00
return ;
if ( sdata - > local - > sw_scanning | | sdata - > local - > hw_scanning )
return ;
/* Disregard subsequent beacons if we are already running a timer
processing a CSA */
2009-02-15 14:44:28 +03:00
if ( ifmgd - > flags & IEEE80211_STA_CSA_RECEIVED )
2009-01-06 06:58:37 +03:00
return ;
new_ch = ieee80211_get_channel ( sdata - > local - > hw . wiphy , new_freq ) ;
if ( ! new_ch | | new_ch - > flags & IEEE80211_CHAN_DISABLED )
return ;
sdata - > local - > csa_channel = new_ch ;
if ( sw_elem - > count < = 1 ) {
2009-02-15 14:44:28 +03:00
queue_work ( sdata - > local - > hw . workqueue , & ifmgd - > chswitch_work ) ;
2009-01-06 06:58:37 +03:00
} else {
ieee80211_stop_queues_by_reason ( & sdata - > local - > hw ,
IEEE80211_QUEUE_STOP_REASON_CSA ) ;
2009-02-15 14:44:28 +03:00
ifmgd - > flags | = IEEE80211_STA_CSA_RECEIVED ;
mod_timer ( & ifmgd - > chswitch_timer ,
2009-02-10 23:26:00 +03:00
jiffies +
msecs_to_jiffies ( sw_elem - > count *
bss - > cbss . beacon_interval ) ) ;
2009-01-06 06:58:37 +03:00
}
}
2009-01-09 15:44:15 +03:00
void ieee80211_handle_pwr_constr ( struct ieee80211_sub_if_data * sdata ,
u16 capab_info , u8 * pwr_constr_elem ,
u8 pwr_constr_elem_len )
{
struct ieee80211_conf * conf = & sdata - > local - > hw . conf ;
if ( ! ( capab_info & WLAN_CAPABILITY_SPECTRUM_MGMT ) )
return ;
/* Power constraint IE length should be 1 octet */
if ( pwr_constr_elem_len ! = 1 )
return ;
if ( ( * pwr_constr_elem < = conf - > channel - > max_power ) & &
( * pwr_constr_elem ! = sdata - > local - > power_constr_level ) ) {
sdata - > local - > power_constr_level = * pwr_constr_elem ;
ieee80211_hw_config ( sdata - > local , 0 ) ;
}
}