2008-09-08 19:44:25 +04:00
/*
2008-09-08 19:44:27 +04:00
* Scanning implementation
*
2008-09-08 19:44:25 +04:00
* Copyright 2003 , Jouni Malinen < jkmaline @ cc . hut . fi >
* Copyright 2004 , Instant802 Networks , Inc .
* Copyright 2005 , Devicescape Software , Inc .
* Copyright 2006 - 2007 Jiri Benc < jbenc @ suse . cz >
* Copyright 2007 , Michael Wu < flamingice @ sourmilk . 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 .
*/
2008-09-08 19:44:27 +04:00
/* TODO:
2009-02-10 23:25:55 +03:00
* figure out how to avoid that the " current BSS " expires
2009-02-10 23:25:56 +03:00
* use cfg80211 ' s BSS handling
2008-09-08 19:44:27 +04:00
*/
2008-09-08 19:44:25 +04:00
# include <linux/wireless.h>
# include <linux/if_arp.h>
2009-01-22 20:07:31 +03:00
# include <linux/rtnetlink.h>
2008-09-08 19:44:25 +04:00
# include <net/mac80211.h>
# include <net/iw_handler.h>
# include "ieee80211_i.h"
2008-09-08 19:44:27 +04:00
# include "mesh.h"
2008-09-08 19:44:25 +04:00
# define IEEE80211_PROBE_DELAY (HZ / 33)
# define IEEE80211_CHANNEL_TIME (HZ / 33)
# define IEEE80211_PASSIVE_CHANNEL_TIME (HZ / 5)
2008-09-08 19:44:27 +04:00
void ieee80211_rx_bss_list_init ( struct ieee80211_local * local )
{
2008-09-11 02:01:55 +04:00
spin_lock_init ( & local - > bss_lock ) ;
INIT_LIST_HEAD ( & local - > bss_list ) ;
2008-09-08 19:44:27 +04:00
}
void ieee80211_rx_bss_list_deinit ( struct ieee80211_local * local )
{
2008-09-11 02:01:55 +04:00
struct ieee80211_bss * bss , * tmp ;
2008-09-08 19:44:27 +04:00
2008-09-11 02:01:55 +04:00
list_for_each_entry_safe ( bss , tmp , & local - > bss_list , list )
2008-09-08 19:44:27 +04:00
ieee80211_rx_bss_put ( local , bss ) ;
}
2008-09-11 02:01:55 +04:00
struct ieee80211_bss *
2008-09-08 19:44:27 +04:00
ieee80211_rx_bss_get ( struct ieee80211_local * local , u8 * bssid , int freq ,
u8 * ssid , u8 ssid_len )
{
2008-09-11 02:01:55 +04:00
struct ieee80211_bss * bss ;
2008-09-08 19:44:27 +04:00
2008-09-11 02:01:55 +04:00
spin_lock_bh ( & local - > bss_lock ) ;
bss = local - > bss_hash [ STA_HASH ( bssid ) ] ;
2008-09-08 19:44:27 +04:00
while ( bss ) {
if ( ! bss_mesh_cfg ( bss ) & &
! memcmp ( bss - > bssid , bssid , ETH_ALEN ) & &
bss - > freq = = freq & &
bss - > ssid_len = = ssid_len & &
( ssid_len = = 0 | | ! memcmp ( bss - > ssid , ssid , ssid_len ) ) ) {
atomic_inc ( & bss - > users ) ;
break ;
}
bss = bss - > hnext ;
}
2008-09-11 02:01:55 +04:00
spin_unlock_bh ( & local - > bss_lock ) ;
2008-09-08 19:44:27 +04:00
return bss ;
}
2008-09-11 02:01:55 +04:00
/* Caller must hold local->bss_lock */
2008-09-08 19:44:27 +04:00
static void __ieee80211_rx_bss_hash_add ( struct ieee80211_local * local ,
2008-09-11 02:01:55 +04:00
struct ieee80211_bss * bss )
2008-09-08 19:44:27 +04:00
{
u8 hash_idx ;
if ( bss_mesh_cfg ( bss ) )
hash_idx = mesh_id_hash ( bss_mesh_id ( bss ) ,
bss_mesh_id_len ( bss ) ) ;
else
hash_idx = STA_HASH ( bss - > bssid ) ;
2008-09-11 02:01:55 +04:00
bss - > hnext = local - > bss_hash [ hash_idx ] ;
local - > bss_hash [ hash_idx ] = bss ;
2008-09-08 19:44:27 +04:00
}
2008-09-11 02:01:55 +04:00
/* Caller must hold local->bss_lock */
2008-09-08 19:44:27 +04:00
static void __ieee80211_rx_bss_hash_del ( struct ieee80211_local * local ,
2008-09-11 02:01:55 +04:00
struct ieee80211_bss * bss )
2008-09-08 19:44:27 +04:00
{
2008-09-11 02:01:55 +04:00
struct ieee80211_bss * b , * prev = NULL ;
b = local - > bss_hash [ STA_HASH ( bss - > bssid ) ] ;
2008-09-08 19:44:27 +04:00
while ( b ) {
if ( b = = bss ) {
if ( ! prev )
2008-09-11 02:01:55 +04:00
local - > bss_hash [ STA_HASH ( bss - > bssid ) ] =
2008-09-08 19:44:27 +04:00
bss - > hnext ;
else
prev - > hnext = bss - > hnext ;
break ;
}
prev = b ;
b = b - > hnext ;
}
}
2009-02-10 23:25:56 +03:00
static struct ieee80211_bss *
2008-09-08 19:44:27 +04:00
ieee80211_rx_bss_add ( struct ieee80211_local * local , u8 * bssid , int freq ,
u8 * ssid , u8 ssid_len )
{
2008-09-11 02:01:55 +04:00
struct ieee80211_bss * bss ;
2008-09-08 19:44:27 +04:00
bss = kzalloc ( sizeof ( * bss ) , GFP_ATOMIC ) ;
if ( ! bss )
return NULL ;
atomic_set ( & bss - > users , 2 ) ;
memcpy ( bss - > bssid , bssid , ETH_ALEN ) ;
bss - > freq = freq ;
if ( ssid & & ssid_len < = IEEE80211_MAX_SSID_LEN ) {
memcpy ( bss - > ssid , ssid , ssid_len ) ;
bss - > ssid_len = ssid_len ;
}
2008-09-11 02:01:55 +04:00
spin_lock_bh ( & local - > bss_lock ) ;
2008-09-08 19:44:27 +04:00
/* TODO: order by RSSI? */
2008-09-11 02:01:55 +04:00
list_add_tail ( & bss - > list , & local - > bss_list ) ;
2008-09-08 19:44:27 +04:00
__ieee80211_rx_bss_hash_add ( local , bss ) ;
2008-09-11 02:01:55 +04:00
spin_unlock_bh ( & local - > bss_lock ) ;
2008-09-08 19:44:27 +04:00
return bss ;
}
# ifdef CONFIG_MAC80211_MESH
2008-09-11 02:01:55 +04:00
static struct ieee80211_bss *
2008-09-08 19:44:27 +04:00
ieee80211_rx_mesh_bss_get ( struct ieee80211_local * local , u8 * mesh_id , int mesh_id_len ,
u8 * mesh_cfg , int freq )
{
2008-09-11 02:01:55 +04:00
struct ieee80211_bss * bss ;
2008-09-08 19:44:27 +04:00
2008-09-11 02:01:55 +04:00
spin_lock_bh ( & local - > bss_lock ) ;
bss = local - > bss_hash [ mesh_id_hash ( mesh_id , mesh_id_len ) ] ;
2008-09-08 19:44:27 +04:00
while ( bss ) {
if ( bss_mesh_cfg ( bss ) & &
! memcmp ( bss_mesh_cfg ( bss ) , mesh_cfg , MESH_CFG_CMP_LEN ) & &
bss - > freq = = freq & &
mesh_id_len = = bss - > mesh_id_len & &
( mesh_id_len = = 0 | | ! memcmp ( bss - > mesh_id , mesh_id ,
mesh_id_len ) ) ) {
atomic_inc ( & bss - > users ) ;
break ;
}
bss = bss - > hnext ;
}
2008-09-11 02:01:55 +04:00
spin_unlock_bh ( & local - > bss_lock ) ;
2008-09-08 19:44:27 +04:00
return bss ;
}
2008-09-11 02:01:55 +04:00
static struct ieee80211_bss *
2008-09-08 19:44:27 +04:00
ieee80211_rx_mesh_bss_add ( struct ieee80211_local * local , u8 * mesh_id , int mesh_id_len ,
u8 * mesh_cfg , int mesh_config_len , int freq )
{
2008-09-11 02:01:55 +04:00
struct ieee80211_bss * bss ;
2008-09-08 19:44:27 +04:00
2008-10-28 13:12:57 +03:00
if ( mesh_config_len ! = IEEE80211_MESH_CONFIG_LEN )
2008-09-08 19:44:27 +04:00
return NULL ;
bss = kzalloc ( sizeof ( * bss ) , GFP_ATOMIC ) ;
if ( ! bss )
return NULL ;
bss - > mesh_cfg = kmalloc ( MESH_CFG_CMP_LEN , GFP_ATOMIC ) ;
if ( ! bss - > mesh_cfg ) {
kfree ( bss ) ;
return NULL ;
}
if ( mesh_id_len & & mesh_id_len < = IEEE80211_MAX_MESH_ID_LEN ) {
bss - > mesh_id = kmalloc ( mesh_id_len , GFP_ATOMIC ) ;
if ( ! bss - > mesh_id ) {
kfree ( bss - > mesh_cfg ) ;
kfree ( bss ) ;
return NULL ;
}
memcpy ( bss - > mesh_id , mesh_id , mesh_id_len ) ;
}
atomic_set ( & bss - > users , 2 ) ;
memcpy ( bss - > mesh_cfg , mesh_cfg , MESH_CFG_CMP_LEN ) ;
bss - > mesh_id_len = mesh_id_len ;
bss - > freq = freq ;
2008-09-11 02:01:55 +04:00
spin_lock_bh ( & local - > bss_lock ) ;
2008-09-08 19:44:27 +04:00
/* TODO: order by RSSI? */
2008-09-11 02:01:55 +04:00
list_add_tail ( & bss - > list , & local - > bss_list ) ;
2008-09-08 19:44:27 +04:00
__ieee80211_rx_bss_hash_add ( local , bss ) ;
2008-09-11 02:01:55 +04:00
spin_unlock_bh ( & local - > bss_lock ) ;
2008-09-08 19:44:27 +04:00
return bss ;
}
# endif
2008-09-11 02:01:55 +04:00
static void ieee80211_rx_bss_free ( struct ieee80211_bss * bss )
2008-09-08 19:44:27 +04:00
{
kfree ( bss - > ies ) ;
kfree ( bss_mesh_id ( bss ) ) ;
kfree ( bss_mesh_cfg ( bss ) ) ;
kfree ( bss ) ;
}
void ieee80211_rx_bss_put ( struct ieee80211_local * local ,
2008-09-11 02:01:55 +04:00
struct ieee80211_bss * bss )
2008-09-08 19:44:27 +04:00
{
local_bh_disable ( ) ;
2008-09-11 02:01:55 +04:00
if ( ! atomic_dec_and_lock ( & bss - > users , & local - > bss_lock ) ) {
2008-09-08 19:44:27 +04:00
local_bh_enable ( ) ;
return ;
}
__ieee80211_rx_bss_hash_del ( local , bss ) ;
list_del ( & bss - > list ) ;
2008-09-11 02:01:55 +04:00
spin_unlock_bh ( & local - > bss_lock ) ;
2008-09-08 19:44:27 +04:00
ieee80211_rx_bss_free ( bss ) ;
}
2008-09-11 02:01:55 +04:00
struct ieee80211_bss *
2008-09-08 19:44:27 +04:00
ieee80211_bss_info_update ( struct ieee80211_local * local ,
struct ieee80211_rx_status * rx_status ,
struct ieee80211_mgmt * mgmt ,
size_t len ,
struct ieee802_11_elems * elems ,
2009-02-10 23:25:55 +03:00
struct ieee80211_channel * channel ,
bool beacon )
2008-09-08 19:44:27 +04:00
{
2008-09-11 02:01:55 +04:00
struct ieee80211_bss * bss ;
2009-02-10 23:25:55 +03:00
int clen , freq = channel - > center_freq ;
enum cfg80211_signal_type sigtype = CFG80211_SIGNAL_TYPE_NONE ;
s32 signal = 0 ;
if ( local - > hw . flags & IEEE80211_HW_SIGNAL_DBM ) {
sigtype = CFG80211_SIGNAL_TYPE_MBM ;
signal = rx_status - > signal * 100 ;
} else if ( local - > hw . flags & IEEE80211_HW_SIGNAL_UNSPEC ) {
sigtype = CFG80211_SIGNAL_TYPE_UNSPEC ;
signal = ( rx_status - > signal * 100 ) / local - > hw . max_signal ;
}
cfg80211_put_bss (
cfg80211_inform_bss_frame ( local - > hw . wiphy , channel ,
mgmt , len , signal , sigtype ,
GFP_ATOMIC ) ) ;
2008-09-08 19:44:27 +04:00
# ifdef CONFIG_MAC80211_MESH
if ( elems - > mesh_config )
bss = ieee80211_rx_mesh_bss_get ( local , elems - > mesh_id ,
elems - > mesh_id_len , elems - > mesh_config , freq ) ;
else
# endif
bss = ieee80211_rx_bss_get ( local , mgmt - > bssid , freq ,
elems - > ssid , elems - > ssid_len ) ;
if ( ! bss ) {
# ifdef CONFIG_MAC80211_MESH
if ( elems - > mesh_config )
bss = ieee80211_rx_mesh_bss_add ( local , elems - > mesh_id ,
elems - > mesh_id_len , elems - > mesh_config ,
elems - > mesh_config_len , freq ) ;
else
# endif
bss = ieee80211_rx_bss_add ( local , mgmt - > bssid , freq ,
elems - > ssid , elems - > ssid_len ) ;
if ( ! bss )
return NULL ;
} else {
#if 0
/* TODO: order by RSSI? */
2008-09-11 02:01:55 +04:00
spin_lock_bh ( & local - > bss_lock ) ;
list_move_tail ( & bss - > list , & local - > bss_list ) ;
spin_unlock_bh ( & local - > bss_lock ) ;
2008-09-08 19:44:27 +04:00
# endif
}
/* save the ERP value so that it is available at association time */
if ( elems - > erp_info & & elems - > erp_info_len > = 1 ) {
bss - > erp_value = elems - > erp_info [ 0 ] ;
bss - > has_erp_value = 1 ;
}
bss - > beacon_int = le16_to_cpu ( mgmt - > u . beacon . beacon_int ) ;
bss - > capability = le16_to_cpu ( mgmt - > u . beacon . capab_info ) ;
if ( elems - > tim ) {
struct ieee80211_tim_ie * tim_ie =
( struct ieee80211_tim_ie * ) elems - > tim ;
bss - > dtim_period = tim_ie - > dtim_period ;
}
/* set default value for buggy APs */
if ( ! elems - > tim | | bss - > dtim_period = = 0 )
bss - > dtim_period = 1 ;
bss - > supp_rates_len = 0 ;
if ( elems - > supp_rates ) {
clen = IEEE80211_MAX_SUPP_RATES - bss - > supp_rates_len ;
if ( clen > elems - > supp_rates_len )
clen = elems - > supp_rates_len ;
memcpy ( & bss - > supp_rates [ bss - > supp_rates_len ] , elems - > supp_rates ,
clen ) ;
bss - > supp_rates_len + = clen ;
}
if ( elems - > ext_supp_rates ) {
clen = IEEE80211_MAX_SUPP_RATES - bss - > supp_rates_len ;
if ( clen > elems - > ext_supp_rates_len )
clen = elems - > ext_supp_rates_len ;
memcpy ( & bss - > supp_rates [ bss - > supp_rates_len ] ,
elems - > ext_supp_rates , clen ) ;
bss - > supp_rates_len + = clen ;
}
bss - > band = rx_status - > band ;
bss - > timestamp = le64_to_cpu ( mgmt - > u . beacon . timestamp ) ;
bss - > last_update = jiffies ;
bss - > signal = rx_status - > signal ;
bss - > noise = rx_status - > noise ;
bss - > qual = rx_status - > qual ;
bss - > wmm_used = elems - > wmm_param | | elems - > wmm_info ;
if ( ! beacon )
bss - > last_probe_resp = jiffies ;
/*
* For probe responses , or if we don ' t have any information yet ,
* use the IEs from the beacon .
*/
if ( ! bss - > ies | | ! beacon ) {
if ( bss - > ies = = NULL | | bss - > ies_len < elems - > total_len ) {
kfree ( bss - > ies ) ;
bss - > ies = kmalloc ( elems - > total_len , GFP_ATOMIC ) ;
}
if ( bss - > ies ) {
memcpy ( bss - > ies , elems - > ie_start , elems - > total_len ) ;
bss - > ies_len = elems - > total_len ;
} else
bss - > ies_len = 0 ;
}
return bss ;
}
2008-09-08 19:44:25 +04:00
2009-02-04 15:58:48 +03:00
void ieee80211_rx_bss_remove ( struct ieee80211_sub_if_data * sdata , u8 * bssid ,
int freq , u8 * ssid , u8 ssid_len )
{
struct ieee80211_bss * bss ;
struct ieee80211_local * local = sdata - > local ;
bss = ieee80211_rx_bss_get ( local , bssid , freq , ssid , ssid_len ) ;
if ( bss ) {
atomic_dec ( & bss - > users ) ;
ieee80211_rx_bss_put ( local , bss ) ;
}
}
2008-09-08 19:44:26 +04:00
ieee80211_rx_result
2008-09-11 02:01:55 +04:00
ieee80211_scan_rx ( struct ieee80211_sub_if_data * sdata , struct sk_buff * skb ,
struct ieee80211_rx_status * rx_status )
2008-09-08 19:44:26 +04:00
{
struct ieee80211_mgmt * mgmt ;
2008-09-11 02:01:55 +04:00
struct ieee80211_bss * bss ;
2008-09-08 19:44:26 +04:00
u8 * elements ;
struct ieee80211_channel * channel ;
size_t baselen ;
int freq ;
__le16 fc ;
bool presp , beacon = false ;
struct ieee802_11_elems elems ;
if ( skb - > len < 2 )
return RX_DROP_UNUSABLE ;
mgmt = ( struct ieee80211_mgmt * ) skb - > data ;
fc = mgmt - > frame_control ;
if ( ieee80211_is_ctl ( fc ) )
return RX_CONTINUE ;
if ( skb - > len < 24 )
return RX_DROP_MONITOR ;
presp = ieee80211_is_probe_resp ( fc ) ;
if ( presp ) {
/* ignore ProbeResp to foreign address */
if ( memcmp ( mgmt - > da , sdata - > dev - > dev_addr , ETH_ALEN ) )
return RX_DROP_MONITOR ;
presp = true ;
elements = mgmt - > u . probe_resp . variable ;
baselen = offsetof ( struct ieee80211_mgmt , u . probe_resp . variable ) ;
} else {
beacon = ieee80211_is_beacon ( fc ) ;
baselen = offsetof ( struct ieee80211_mgmt , u . beacon . variable ) ;
elements = mgmt - > u . beacon . variable ;
}
if ( ! presp & & ! beacon )
return RX_CONTINUE ;
if ( baselen > skb - > len )
return RX_DROP_MONITOR ;
ieee802_11_parse_elems ( elements , skb - > len - baselen , & elems ) ;
if ( elems . ds_params & & elems . ds_params_len = = 1 )
freq = ieee80211_channel_to_frequency ( elems . ds_params [ 0 ] ) ;
else
freq = rx_status - > freq ;
channel = ieee80211_get_channel ( sdata - > local - > hw . wiphy , freq ) ;
if ( ! channel | | channel - > flags & IEEE80211_CHAN_DISABLED )
return RX_DROP_MONITOR ;
bss = ieee80211_bss_info_update ( sdata - > local , rx_status ,
mgmt , skb - > len , & elems ,
2009-02-10 23:25:55 +03:00
channel , beacon ) ;
2008-10-11 04:29:55 +04:00
if ( bss )
ieee80211_rx_bss_put ( sdata - > local , bss ) ;
2008-09-08 19:44:26 +04:00
dev_kfree_skb ( skb ) ;
return RX_QUEUED ;
}
2008-12-24 05:39:02 +03:00
void ieee80211_send_nullfunc ( struct ieee80211_local * local ,
2008-09-08 19:44:25 +04:00
struct ieee80211_sub_if_data * sdata ,
int powersave )
{
struct sk_buff * skb ;
struct ieee80211_hdr * nullfunc ;
__le16 fc ;
skb = dev_alloc_skb ( local - > hw . extra_tx_headroom + 24 ) ;
if ( ! skb ) {
printk ( KERN_DEBUG " %s: failed to allocate buffer for nullfunc "
" frame \n " , sdata - > dev - > name ) ;
return ;
}
skb_reserve ( skb , local - > hw . extra_tx_headroom ) ;
nullfunc = ( struct ieee80211_hdr * ) skb_put ( skb , 24 ) ;
memset ( nullfunc , 0 , 24 ) ;
fc = cpu_to_le16 ( IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC |
IEEE80211_FCTL_TODS ) ;
if ( powersave )
fc | = cpu_to_le16 ( IEEE80211_FCTL_PM ) ;
nullfunc - > frame_control = fc ;
memcpy ( nullfunc - > addr1 , sdata - > u . sta . bssid , ETH_ALEN ) ;
memcpy ( nullfunc - > addr2 , sdata - > dev - > dev_addr , ETH_ALEN ) ;
memcpy ( nullfunc - > addr3 , sdata - > u . sta . bssid , ETH_ALEN ) ;
2008-09-09 17:07:09 +04:00
ieee80211_tx_skb ( sdata , skb , 0 ) ;
2008-09-08 19:44:25 +04:00
}
2009-02-10 23:25:55 +03:00
void ieee80211_scan_completed ( struct ieee80211_hw * hw , bool aborted )
2008-09-08 19:44:25 +04:00
{
struct ieee80211_local * local = hw_to_local ( hw ) ;
struct ieee80211_sub_if_data * sdata ;
2008-09-11 02:01:55 +04:00
if ( WARN_ON ( ! local - > hw_scanning & & ! local - > sw_scanning ) )
2008-09-11 02:01:51 +04:00
return ;
2009-02-10 23:25:55 +03:00
if ( WARN_ON ( ! local - > scan_req ) )
return ;
2008-09-11 02:01:51 +04:00
2009-02-10 23:25:55 +03:00
if ( local - > scan_req ! = & local - > int_scan_req )
cfg80211_scan_done ( local - > scan_req , aborted ) ;
local - > scan_req = NULL ;
local - > last_scan_completed = jiffies ;
2008-09-08 19:44:25 +04:00
2008-09-11 02:01:55 +04:00
if ( local - > hw_scanning ) {
local - > hw_scanning = false ;
2008-10-09 14:18:51 +04:00
/*
* Somebody might have requested channel change during scan
* that we won ' t have acted upon , try now . ieee80211_hw_config
* will set the flag based on actual changes .
*/
ieee80211_hw_config ( local , 0 ) ;
2008-09-08 19:44:25 +04:00
goto done ;
}
2008-09-11 02:01:55 +04:00
local - > sw_scanning = false ;
2008-10-09 14:18:51 +04:00
ieee80211_hw_config ( local , IEEE80211_CONF_CHANGE_CHANNEL ) ;
2008-09-08 19:44:25 +04:00
netif_tx_lock_bh ( local - > mdev ) ;
netif_addr_lock ( local - > mdev ) ;
local - > filter_flags & = ~ FIF_BCN_PRBRESP_PROMISC ;
local - > ops - > configure_filter ( local_to_hw ( local ) ,
FIF_BCN_PRBRESP_PROMISC ,
& local - > filter_flags ,
local - > mdev - > mc_count ,
local - > mdev - > mc_list ) ;
netif_addr_unlock ( local - > mdev ) ;
netif_tx_unlock_bh ( local - > mdev ) ;
2009-01-22 20:07:31 +03:00
mutex_lock ( & local - > iflist_mtx ) ;
list_for_each_entry ( sdata , & local - > interfaces , list ) {
2009-01-26 21:11:57 +03:00
if ( ! netif_running ( sdata - > dev ) )
continue ;
2008-09-08 19:44:25 +04:00
/* Tell AP we're back */
2008-09-11 02:01:58 +04:00
if ( sdata - > vif . type = = NL80211_IFTYPE_STATION ) {
2008-09-08 19:44:25 +04:00
if ( sdata - > u . sta . flags & IEEE80211_STA_ASSOCIATED ) {
ieee80211_send_nullfunc ( local , sdata , 0 ) ;
netif_tx_wake_all_queues ( sdata - > dev ) ;
}
} else
netif_tx_wake_all_queues ( sdata - > dev ) ;
2009-01-22 20:07:31 +03:00
2009-02-10 23:25:42 +03:00
/* re-enable beaconing */
if ( sdata - > vif . type = = NL80211_IFTYPE_AP | |
sdata - > vif . type = = NL80211_IFTYPE_ADHOC | |
sdata - > vif . type = = NL80211_IFTYPE_MESH_POINT )
ieee80211_if_config ( sdata ,
IEEE80211_IFCC_BEACON_ENABLED ) ;
2008-09-08 19:44:25 +04:00
}
2009-01-22 20:07:31 +03:00
mutex_unlock ( & local - > iflist_mtx ) ;
2008-09-08 19:44:25 +04:00
done :
ieee80211_mlme_notify_scan_completed ( local ) ;
2008-09-11 02:01:49 +04:00
ieee80211_mesh_notify_scan_completed ( local ) ;
2008-09-08 19:44:25 +04:00
}
EXPORT_SYMBOL ( ieee80211_scan_completed ) ;
2008-09-11 02:01:55 +04:00
void ieee80211_scan_work ( struct work_struct * work )
2008-09-08 19:44:25 +04:00
{
struct ieee80211_local * local =
container_of ( work , struct ieee80211_local , scan_work . work ) ;
struct ieee80211_sub_if_data * sdata = local - > scan_sdata ;
struct ieee80211_channel * chan ;
2009-02-10 23:25:55 +03:00
int skip , i ;
2008-09-08 19:44:25 +04:00
unsigned long next_delay = 0 ;
2008-09-11 02:01:51 +04:00
/*
* Avoid re - scheduling when the sdata is going away .
*/
if ( ! netif_running ( sdata - > dev ) )
2008-09-08 19:44:25 +04:00
return ;
switch ( local - > scan_state ) {
case SCAN_SET_CHANNEL :
/* if no more bands/channels left, complete scan */
2009-02-10 23:25:55 +03:00
if ( local - > scan_channel_idx > = local - > scan_req - > n_channels ) {
ieee80211_scan_completed ( local_to_hw ( local ) , false ) ;
2008-09-08 19:44:25 +04:00
return ;
}
skip = 0 ;
2009-02-10 23:25:55 +03:00
chan = local - > scan_req - > channels [ local - > scan_channel_idx ] ;
2008-09-08 19:44:25 +04:00
if ( chan - > flags & IEEE80211_CHAN_DISABLED | |
2008-09-11 02:01:58 +04:00
( sdata - > vif . type = = NL80211_IFTYPE_ADHOC & &
2008-09-08 19:44:25 +04:00
chan - > flags & IEEE80211_CHAN_NO_IBSS ) )
skip = 1 ;
if ( ! skip ) {
local - > scan_channel = chan ;
2008-10-09 14:18:51 +04:00
if ( ieee80211_hw_config ( local ,
IEEE80211_CONF_CHANGE_CHANNEL ) )
2008-09-08 19:44:25 +04:00
skip = 1 ;
}
/* advance state machine to next channel/band */
local - > scan_channel_idx + + ;
if ( skip )
break ;
next_delay = IEEE80211_PROBE_DELAY +
usecs_to_jiffies ( local - > hw . channel_change_time ) ;
local - > scan_state = SCAN_SEND_PROBE ;
break ;
case SCAN_SEND_PROBE :
next_delay = IEEE80211_PASSIVE_CHANNEL_TIME ;
local - > scan_state = SCAN_SET_CHANNEL ;
2009-02-10 23:25:55 +03:00
if ( local - > scan_channel - > flags & IEEE80211_CHAN_PASSIVE_SCAN | |
! local - > scan_req - > n_ssids )
2008-09-08 19:44:25 +04:00
break ;
2009-02-10 23:25:55 +03:00
for ( i = 0 ; i < local - > scan_req - > n_ssids ; i + + )
ieee80211_send_probe_req (
sdata , NULL ,
local - > scan_req - > ssids [ i ] . ssid ,
local - > scan_req - > ssids [ i ] . ssid_len ) ;
2008-09-08 19:44:25 +04:00
next_delay = IEEE80211_CHANNEL_TIME ;
break ;
}
2008-09-11 02:01:51 +04:00
queue_delayed_work ( local - > hw . workqueue , & local - > scan_work ,
next_delay ) ;
2008-09-08 19:44:25 +04:00
}
2008-09-11 02:01:55 +04:00
int ieee80211_start_scan ( struct ieee80211_sub_if_data * scan_sdata ,
2009-02-10 23:25:55 +03:00
struct cfg80211_scan_request * req )
2008-09-08 19:44:25 +04:00
{
struct ieee80211_local * local = scan_sdata - > local ;
struct ieee80211_sub_if_data * sdata ;
2009-02-10 23:25:55 +03:00
if ( ! req )
2008-09-08 19:44:25 +04:00
return - EINVAL ;
2009-02-10 23:25:55 +03:00
if ( local - > scan_req & & local - > scan_req ! = req )
return - EBUSY ;
local - > scan_req = req ;
2008-09-08 19:44:25 +04:00
/* MLME-SCAN.request (page 118) page 144 (11.1.3.1)
* BSSType : INFRASTRUCTURE , INDEPENDENT , ANY_BSS
* BSSID : MACAddress
* SSID
* ScanType : ACTIVE , PASSIVE
* ProbeDelay : delay ( in microseconds ) to be used prior to transmitting
* a Probe frame during active scanning
* ChannelList
* MinChannelTime ( > = ProbeDelay ) , in TU
* MaxChannelTime : ( > = MinChannelTime ) , in TU
*/
/* MLME-SCAN.confirm
* BSSDescriptionSet
* ResultCode : SUCCESS , INVALID_PARAMETERS
*/
2008-09-11 02:01:55 +04:00
if ( local - > sw_scanning | | local - > hw_scanning ) {
2008-09-08 19:44:25 +04:00
if ( local - > scan_sdata = = scan_sdata )
return 0 ;
return - EBUSY ;
}
if ( local - > ops - > hw_scan ) {
2008-09-11 02:01:51 +04:00
int rc ;
2008-09-11 02:01:55 +04:00
local - > hw_scanning = true ;
2009-02-10 23:25:55 +03:00
rc = local - > ops - > hw_scan ( local_to_hw ( local ) , req ) ;
2008-09-11 02:01:51 +04:00
if ( rc ) {
2008-09-11 02:01:55 +04:00
local - > hw_scanning = false ;
2008-09-11 02:01:51 +04:00
return rc ;
2008-09-08 19:44:25 +04:00
}
2008-09-11 02:01:51 +04:00
local - > scan_sdata = scan_sdata ;
return 0 ;
2008-09-08 19:44:25 +04:00
}
2008-09-11 02:01:55 +04:00
local - > sw_scanning = true ;
2008-09-08 19:44:25 +04:00
2009-01-22 20:07:31 +03:00
mutex_lock ( & local - > iflist_mtx ) ;
list_for_each_entry ( sdata , & local - > interfaces , list ) {
2009-01-26 21:11:57 +03:00
if ( ! netif_running ( sdata - > dev ) )
continue ;
2009-02-10 23:25:42 +03:00
/* disable beaconing */
if ( sdata - > vif . type = = NL80211_IFTYPE_AP | |
sdata - > vif . type = = NL80211_IFTYPE_ADHOC | |
sdata - > vif . type = = NL80211_IFTYPE_MESH_POINT )
ieee80211_if_config ( sdata ,
IEEE80211_IFCC_BEACON_ENABLED ) ;
2009-01-22 20:07:31 +03:00
2008-09-11 02:01:58 +04:00
if ( sdata - > vif . type = = NL80211_IFTYPE_STATION ) {
2008-09-08 19:44:25 +04:00
if ( sdata - > u . sta . flags & IEEE80211_STA_ASSOCIATED ) {
netif_tx_stop_all_queues ( sdata - > dev ) ;
ieee80211_send_nullfunc ( local , sdata , 1 ) ;
}
} else
netif_tx_stop_all_queues ( sdata - > dev ) ;
}
2009-01-22 20:07:31 +03:00
mutex_unlock ( & local - > iflist_mtx ) ;
2008-09-08 19:44:25 +04:00
local - > scan_state = SCAN_SET_CHANNEL ;
local - > scan_channel_idx = 0 ;
local - > scan_sdata = scan_sdata ;
2009-02-10 23:25:55 +03:00
local - > scan_req = req ;
2008-09-08 19:44:25 +04:00
netif_addr_lock_bh ( local - > mdev ) ;
local - > filter_flags | = FIF_BCN_PRBRESP_PROMISC ;
local - > ops - > configure_filter ( local_to_hw ( local ) ,
FIF_BCN_PRBRESP_PROMISC ,
& local - > filter_flags ,
local - > mdev - > mc_count ,
local - > mdev - > mc_list ) ;
netif_addr_unlock_bh ( local - > mdev ) ;
/* TODO: start scan as soon as all nullfunc frames are ACKed */
queue_delayed_work ( local - > hw . workqueue , & local - > scan_work ,
IEEE80211_CHANNEL_TIME ) ;
return 0 ;
}
2008-09-11 02:01:55 +04:00
int ieee80211_request_scan ( struct ieee80211_sub_if_data * sdata ,
2009-02-10 23:25:55 +03:00
struct cfg80211_scan_request * req )
2008-09-08 19:44:25 +04:00
{
struct ieee80211_local * local = sdata - > local ;
2008-09-08 19:47:23 +04:00
struct ieee80211_if_sta * ifsta ;
2008-09-08 19:44:25 +04:00
2009-02-10 23:25:55 +03:00
if ( ! req )
return - EINVAL ;
if ( local - > scan_req & & local - > scan_req ! = req )
return - EBUSY ;
local - > scan_req = req ;
2008-09-11 02:01:58 +04:00
if ( sdata - > vif . type ! = NL80211_IFTYPE_STATION )
2009-02-10 23:25:55 +03:00
return ieee80211_start_scan ( sdata , req ) ;
2008-09-08 19:44:25 +04:00
2008-09-08 19:47:23 +04:00
/*
* STA has a state machine that might need to defer scanning
* while it ' s trying to associate / authenticate , therefore we
* queue it up to the state machine in that case .
*/
2008-09-11 02:01:55 +04:00
if ( local - > sw_scanning | | local - > hw_scanning ) {
2008-09-08 19:44:25 +04:00
if ( local - > scan_sdata = = sdata )
return 0 ;
return - EBUSY ;
}
2008-09-08 19:47:23 +04:00
ifsta = & sdata - > u . sta ;
2008-09-08 19:44:25 +04:00
set_bit ( IEEE80211_STA_REQ_SCAN , & ifsta - > request ) ;
queue_work ( local - > hw . workqueue , & ifsta - > work ) ;
2008-09-08 19:47:23 +04:00
2008-09-08 19:44:25 +04:00
return 0 ;
}