2008-06-12 09:47:10 +08:00
/******************************************************************************
*
* GPL LICENSE SUMMARY
*
2009-01-08 10:20:02 -08:00
* Copyright ( c ) 2008 - 2009 Intel Corporation . All rights reserved .
2008-06-12 09:47:10 +08:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 ,
* USA
*
* The full GNU General Public License is included in this distribution
* in the file called LICENSE . GPL .
*
* Contact Information :
2008-12-09 11:28:58 -08:00
* Intel Linux Wireless < ilw @ linux . intel . com >
2008-06-12 09:47:10 +08:00
* Intel Corporation , 5200 N . E . Elam Young Parkway , Hillsboro , OR 97124 - 6497
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2008-09-24 18:13:14 -04:00
# include <linux/types.h>
2008-06-12 09:47:10 +08:00
# include <linux/etherdevice.h>
2008-09-24 18:13:14 -04:00
# include <net/lib80211.h>
# include <net/mac80211.h>
2008-06-12 09:47:10 +08:00
# include "iwl-eeprom.h"
# include "iwl-dev.h"
# include "iwl-core.h"
# include "iwl-sta.h"
# include "iwl-io.h"
# include "iwl-helpers.h"
/* For active scan, listen ACTIVE_DWELL_TIME (msec) on each channel after
* sending probe req . This should be set long enough to hear probe responses
* from more than one AP . */
2008-07-11 11:53:39 +08:00
# define IWL_ACTIVE_DWELL_TIME_24 (30) /* all times in msec */
# define IWL_ACTIVE_DWELL_TIME_52 (20)
# define IWL_ACTIVE_DWELL_FACTOR_24GHZ (3)
# define IWL_ACTIVE_DWELL_FACTOR_52GHZ (2)
2008-06-12 09:47:10 +08:00
/* For passive scan, listen PASSIVE_DWELL_TIME (msec) on each channel.
* Must be set longer than active dwell time .
* For the most reliable scan , set > AP beacon interval ( typically 100 msec ) . */
# define IWL_PASSIVE_DWELL_TIME_24 (20) /* all times in msec */
# define IWL_PASSIVE_DWELL_TIME_52 (10)
# define IWL_PASSIVE_DWELL_BASE (100)
# define IWL_CHANNEL_TUNE_TIME 5
2008-07-11 11:53:39 +08:00
2008-06-12 09:47:10 +08:00
/**
* iwl_scan_cancel - Cancel any currently executing HW scan
*
* NOTE : priv - > mutex is not required before calling this function
*/
int iwl_scan_cancel ( struct iwl_priv * priv )
{
if ( ! test_bit ( STATUS_SCAN_HW , & priv - > status ) ) {
clear_bit ( STATUS_SCANNING , & priv - > status ) ;
return 0 ;
}
if ( test_bit ( STATUS_SCANNING , & priv - > status ) ) {
if ( ! test_bit ( STATUS_SCAN_ABORTING , & priv - > status ) ) {
2009-01-27 14:27:56 -08:00
IWL_DEBUG_SCAN ( priv , " Queuing scan abort. \n " ) ;
2008-06-12 09:47:10 +08:00
set_bit ( STATUS_SCAN_ABORTING , & priv - > status ) ;
queue_work ( priv - > workqueue , & priv - > abort_scan ) ;
} else
2009-01-27 14:27:56 -08:00
IWL_DEBUG_SCAN ( priv , " Scan abort already in progress. \n " ) ;
2008-06-12 09:47:10 +08:00
return test_bit ( STATUS_SCANNING , & priv - > status ) ;
}
return 0 ;
}
EXPORT_SYMBOL ( iwl_scan_cancel ) ;
/**
* iwl_scan_cancel_timeout - Cancel any currently executing HW scan
* @ ms : amount of time to wait ( in milliseconds ) for scan to abort
*
* NOTE : priv - > mutex must be held before calling this function
*/
int iwl_scan_cancel_timeout ( struct iwl_priv * priv , unsigned long ms )
{
unsigned long now = jiffies ;
int ret ;
ret = iwl_scan_cancel ( priv ) ;
if ( ret & & ms ) {
mutex_unlock ( & priv - > mutex ) ;
while ( ! time_after ( jiffies , now + msecs_to_jiffies ( ms ) ) & &
test_bit ( STATUS_SCANNING , & priv - > status ) )
msleep ( 1 ) ;
mutex_lock ( & priv - > mutex ) ;
return test_bit ( STATUS_SCANNING , & priv - > status ) ;
}
return ret ;
}
EXPORT_SYMBOL ( iwl_scan_cancel_timeout ) ;
2009-01-23 13:45:20 -08:00
int iwl_send_scan_abort ( struct iwl_priv * priv )
2008-06-12 09:47:10 +08:00
{
int ret = 0 ;
struct iwl_rx_packet * res ;
struct iwl_host_cmd cmd = {
. id = REPLY_SCAN_ABORT_CMD ,
. meta . flags = CMD_WANT_SKB ,
} ;
/* If there isn't a scan actively going on in the hardware
* then we are in between scan bands and not actually
* actively scanning , so don ' t send the abort command */
if ( ! test_bit ( STATUS_SCAN_HW , & priv - > status ) ) {
clear_bit ( STATUS_SCAN_ABORTING , & priv - > status ) ;
return 0 ;
}
ret = iwl_send_cmd_sync ( priv , & cmd ) ;
if ( ret ) {
clear_bit ( STATUS_SCAN_ABORTING , & priv - > status ) ;
return ret ;
}
res = ( struct iwl_rx_packet * ) cmd . meta . u . skb - > data ;
if ( res - > u . status ! = CAN_ABORT_STATUS ) {
/* The scan abort will return 1 for success or
* 2 for " failure " . A failure condition can be
* due to simply not being in an active scan which
* can occur if we send the scan abort before we
* the microcode has notified us that a scan is
* completed . */
2009-01-27 14:27:56 -08:00
IWL_DEBUG_INFO ( priv , " SCAN_ABORT returned %d. \n " , res - > u . status ) ;
2008-06-12 09:47:10 +08:00
clear_bit ( STATUS_SCAN_ABORTING , & priv - > status ) ;
clear_bit ( STATUS_SCAN_HW , & priv - > status ) ;
}
2008-08-04 16:00:46 +08:00
priv - > alloc_rxb_skb - - ;
2008-06-12 09:47:10 +08:00
dev_kfree_skb_any ( cmd . meta . u . skb ) ;
return ret ;
}
2009-01-23 13:45:20 -08:00
EXPORT_SYMBOL ( iwl_send_scan_abort ) ;
2008-06-12 09:47:10 +08:00
/* Service response to REPLY_SCAN_CMD (0x80) */
static void iwl_rx_reply_scan ( struct iwl_priv * priv ,
struct iwl_rx_mem_buffer * rxb )
{
# ifdef CONFIG_IWLWIFI_DEBUG
struct iwl_rx_packet * pkt = ( struct iwl_rx_packet * ) rxb - > skb - > data ;
struct iwl_scanreq_notification * notif =
( struct iwl_scanreq_notification * ) pkt - > u . raw ;
2009-01-27 14:27:56 -08:00
IWL_DEBUG_RX ( priv , " Scan request status = 0x%x \n " , notif - > status ) ;
2008-06-12 09:47:10 +08:00
# endif
}
/* Service SCAN_START_NOTIFICATION (0x82) */
static void iwl_rx_scan_start_notif ( struct iwl_priv * priv ,
struct iwl_rx_mem_buffer * rxb )
{
struct iwl_rx_packet * pkt = ( struct iwl_rx_packet * ) rxb - > skb - > data ;
struct iwl_scanstart_notification * notif =
( struct iwl_scanstart_notification * ) pkt - > u . raw ;
priv - > scan_start_tsf = le32_to_cpu ( notif - > tsf_low ) ;
2009-01-27 14:27:56 -08:00
IWL_DEBUG_SCAN ( priv , " Scan start: "
2008-06-12 09:47:10 +08:00
" %d [802.11%s] "
" (TSF: 0x%08X:%08X) - %d (beacon timer %u) \n " ,
notif - > channel ,
notif - > band ? " bg " : " a " ,
2008-07-11 11:53:39 +08:00
le32_to_cpu ( notif - > tsf_high ) ,
le32_to_cpu ( notif - > tsf_low ) ,
notif - > status , notif - > beacon_timer ) ;
2008-06-12 09:47:10 +08:00
}
/* Service SCAN_RESULTS_NOTIFICATION (0x83) */
static void iwl_rx_scan_results_notif ( struct iwl_priv * priv ,
struct iwl_rx_mem_buffer * rxb )
{
# ifdef CONFIG_IWLWIFI_DEBUG
struct iwl_rx_packet * pkt = ( struct iwl_rx_packet * ) rxb - > skb - > data ;
struct iwl_scanresults_notification * notif =
( struct iwl_scanresults_notification * ) pkt - > u . raw ;
2009-01-27 14:27:56 -08:00
IWL_DEBUG_SCAN ( priv , " Scan ch.res: "
2008-06-12 09:47:10 +08:00
" %d [802.11%s] "
" (TSF: 0x%08X:%08X) - %d "
" elapsed=%lu usec (%dms since last) \n " ,
notif - > channel ,
notif - > band ? " bg " : " a " ,
le32_to_cpu ( notif - > tsf_high ) ,
le32_to_cpu ( notif - > tsf_low ) ,
le32_to_cpu ( notif - > statistics [ 0 ] ) ,
le32_to_cpu ( notif - > tsf_low ) - priv - > scan_start_tsf ,
jiffies_to_msecs ( elapsed_jiffies
( priv - > last_scan_jiffies , jiffies ) ) ) ;
# endif
priv - > last_scan_jiffies = jiffies ;
priv - > next_scan_jiffies = 0 ;
}
/* Service SCAN_COMPLETE_NOTIFICATION (0x84) */
static void iwl_rx_scan_complete_notif ( struct iwl_priv * priv ,
struct iwl_rx_mem_buffer * rxb )
{
2008-07-18 10:56:12 +04:00
# ifdef CONFIG_IWLWIFI_DEBUG
2008-06-12 09:47:10 +08:00
struct iwl_rx_packet * pkt = ( struct iwl_rx_packet * ) rxb - > skb - > data ;
struct iwl_scancomplete_notification * scan_notif = ( void * ) pkt - > u . raw ;
2009-01-27 14:27:56 -08:00
IWL_DEBUG_SCAN ( priv , " Scan complete: %d channels (TSF 0x%08X:%08X) - %d \n " ,
2008-06-12 09:47:10 +08:00
scan_notif - > scanned_channels ,
scan_notif - > tsf_low ,
scan_notif - > tsf_high , scan_notif - > status ) ;
2008-07-18 10:56:12 +04:00
# endif
2008-06-12 09:47:10 +08:00
/* The HW is no longer scanning */
clear_bit ( STATUS_SCAN_HW , & priv - > status ) ;
/* The scan completion notification came in, so kill that timer... */
cancel_delayed_work ( & priv - > scan_check ) ;
2009-01-27 14:27:56 -08:00
IWL_DEBUG_INFO ( priv , " Scan pass on %sGHz took %dms \n " ,
2008-06-28 01:19:40 -07:00
( priv - > scan_bands & BIT ( IEEE80211_BAND_2GHZ ) ) ?
" 2.4 " : " 5.2 " ,
2008-06-12 09:47:10 +08:00
jiffies_to_msecs ( elapsed_jiffies
( priv - > scan_pass_start , jiffies ) ) ) ;
2008-06-28 01:19:40 -07:00
/* Remove this scanned band from the list of pending
* bands to scan , band G precedes A in order of scanning
* as seen in iwl_bg_request_scan */
if ( priv - > scan_bands & BIT ( IEEE80211_BAND_2GHZ ) )
priv - > scan_bands & = ~ BIT ( IEEE80211_BAND_2GHZ ) ;
else if ( priv - > scan_bands & BIT ( IEEE80211_BAND_5GHZ ) )
priv - > scan_bands & = ~ BIT ( IEEE80211_BAND_5GHZ ) ;
2008-06-12 09:47:10 +08:00
/* If a request to abort was given, or the scan did not succeed
* then we reset the scan state machine and terminate ,
* re - queuing another scan if one has been requested */
if ( test_bit ( STATUS_SCAN_ABORTING , & priv - > status ) ) {
2009-01-27 14:27:56 -08:00
IWL_DEBUG_INFO ( priv , " Aborted scan completed. \n " ) ;
2008-06-12 09:47:10 +08:00
clear_bit ( STATUS_SCAN_ABORTING , & priv - > status ) ;
} else {
/* If there are more bands on this scan pass reschedule */
2008-06-28 01:19:40 -07:00
if ( priv - > scan_bands )
2008-06-12 09:47:10 +08:00
goto reschedule ;
}
priv - > last_scan_jiffies = jiffies ;
priv - > next_scan_jiffies = 0 ;
2009-01-27 14:27:56 -08:00
IWL_DEBUG_INFO ( priv , " Setting scan to off \n " ) ;
2008-06-12 09:47:10 +08:00
clear_bit ( STATUS_SCANNING , & priv - > status ) ;
2009-01-27 14:27:56 -08:00
IWL_DEBUG_INFO ( priv , " Scan took %dms \n " ,
2008-06-12 09:47:10 +08:00
jiffies_to_msecs ( elapsed_jiffies ( priv - > scan_start , jiffies ) ) ) ;
queue_work ( priv - > workqueue , & priv - > scan_completed ) ;
return ;
reschedule :
priv - > scan_pass_start = jiffies ;
queue_work ( priv - > workqueue , & priv - > request_scan ) ;
}
void iwl_setup_rx_scan_handlers ( struct iwl_priv * priv )
{
/* scan handlers */
priv - > rx_handlers [ REPLY_SCAN_CMD ] = iwl_rx_reply_scan ;
priv - > rx_handlers [ SCAN_START_NOTIFICATION ] = iwl_rx_scan_start_notif ;
priv - > rx_handlers [ SCAN_RESULTS_NOTIFICATION ] =
iwl_rx_scan_results_notif ;
priv - > rx_handlers [ SCAN_COMPLETE_NOTIFICATION ] =
iwl_rx_scan_complete_notif ;
}
EXPORT_SYMBOL ( iwl_setup_rx_scan_handlers ) ;
2009-01-23 13:45:12 -08:00
inline u16 iwl_get_active_dwell_time ( struct iwl_priv * priv ,
enum ieee80211_band band ,
u8 n_probes )
2008-06-12 09:47:10 +08:00
{
if ( band = = IEEE80211_BAND_5GHZ )
2008-07-11 11:53:39 +08:00
return IWL_ACTIVE_DWELL_TIME_52 +
IWL_ACTIVE_DWELL_FACTOR_52GHZ * ( n_probes + 1 ) ;
2008-06-12 09:47:10 +08:00
else
2008-07-11 11:53:39 +08:00
return IWL_ACTIVE_DWELL_TIME_24 +
IWL_ACTIVE_DWELL_FACTOR_24GHZ * ( n_probes + 1 ) ;
2008-06-12 09:47:10 +08:00
}
2009-01-23 13:45:12 -08:00
EXPORT_SYMBOL ( iwl_get_active_dwell_time ) ;
2008-06-12 09:47:10 +08:00
2009-01-23 13:45:12 -08:00
u16 iwl_get_passive_dwell_time ( struct iwl_priv * priv ,
enum ieee80211_band band )
2008-06-12 09:47:10 +08:00
{
2008-07-11 11:53:39 +08:00
u16 passive = ( band = = IEEE80211_BAND_2GHZ ) ?
2008-06-12 09:47:10 +08:00
IWL_PASSIVE_DWELL_BASE + IWL_PASSIVE_DWELL_TIME_24 :
IWL_PASSIVE_DWELL_BASE + IWL_PASSIVE_DWELL_TIME_52 ;
if ( iwl_is_associated ( priv ) ) {
/* If we're associated, we clamp the maximum passive
* dwell time to be 98 % of the beacon interval ( minus
* 2 * channel tune time ) */
passive = priv - > beacon_int ;
if ( ( passive > IWL_PASSIVE_DWELL_BASE ) | | ! passive )
passive = IWL_PASSIVE_DWELL_BASE ;
passive = ( passive * 98 ) / 100 - IWL_CHANNEL_TUNE_TIME * 2 ;
}
return passive ;
}
2009-01-23 13:45:12 -08:00
EXPORT_SYMBOL ( iwl_get_passive_dwell_time ) ;
2008-06-12 09:47:10 +08:00
static int iwl_get_channels_for_scan ( struct iwl_priv * priv ,
enum ieee80211_band band ,
2008-07-11 11:53:39 +08:00
u8 is_active , u8 n_probes ,
2008-06-12 09:47:10 +08:00
struct iwl_scan_channel * scan_ch )
{
const struct ieee80211_channel * channels = NULL ;
const struct ieee80211_supported_band * sband ;
const struct iwl_channel_info * ch_info ;
u16 passive_dwell = 0 ;
u16 active_dwell = 0 ;
int added , i ;
2008-07-11 11:53:38 +08:00
u16 channel ;
2008-06-12 09:47:10 +08:00
sband = iwl_get_hw_mode ( priv , band ) ;
if ( ! sband )
return 0 ;
channels = sband - > channels ;
2008-07-11 11:53:39 +08:00
active_dwell = iwl_get_active_dwell_time ( priv , band , n_probes ) ;
2008-06-12 09:47:10 +08:00
passive_dwell = iwl_get_passive_dwell_time ( priv , band ) ;
2008-07-11 11:53:39 +08:00
if ( passive_dwell < = active_dwell )
passive_dwell = active_dwell + 1 ;
2008-06-12 09:47:10 +08:00
for ( i = 0 , added = 0 ; i < sband - > n_channels ; i + + ) {
if ( channels [ i ] . flags & IEEE80211_CHAN_DISABLED )
continue ;
2008-07-11 11:53:38 +08:00
channel =
2008-06-12 09:47:10 +08:00
ieee80211_frequency_to_channel ( channels [ i ] . center_freq ) ;
2008-07-11 11:53:38 +08:00
scan_ch - > channel = cpu_to_le16 ( channel ) ;
2008-06-12 09:47:10 +08:00
2008-07-11 11:53:38 +08:00
ch_info = iwl_get_channel_info ( priv , band , channel ) ;
2008-06-12 09:47:10 +08:00
if ( ! is_channel_valid ( ch_info ) ) {
2009-01-27 14:27:56 -08:00
IWL_DEBUG_SCAN ( priv , " Channel %d is INVALID for this band. \n " ,
2008-07-11 11:53:38 +08:00
channel ) ;
2008-06-12 09:47:10 +08:00
continue ;
}
if ( ! is_active | | is_channel_passive ( ch_info ) | |
( channels [ i ] . flags & IEEE80211_CHAN_PASSIVE_SCAN ) )
2008-07-11 11:53:38 +08:00
scan_ch - > type = SCAN_CHANNEL_TYPE_PASSIVE ;
2008-06-12 09:47:10 +08:00
else
2008-07-11 11:53:38 +08:00
scan_ch - > type = SCAN_CHANNEL_TYPE_ACTIVE ;
2008-06-12 09:47:10 +08:00
2008-09-03 11:18:43 +08:00
if ( n_probes )
2008-07-11 11:53:39 +08:00
scan_ch - > type | = IWL_SCAN_PROBE_MASK ( n_probes ) ;
2008-06-12 09:47:10 +08:00
scan_ch - > active_dwell = cpu_to_le16 ( active_dwell ) ;
scan_ch - > passive_dwell = cpu_to_le16 ( passive_dwell ) ;
/* Set txpower levels to defaults */
2008-06-12 09:47:12 +08:00
scan_ch - > dsp_atten = 110 ;
2008-06-12 09:47:10 +08:00
2008-07-11 11:53:39 +08:00
/* NOTE: if we were doing 6Mb OFDM for scans we'd use
* power level :
* scan_ch - > tx_gain = ( ( 1 < < 5 ) | ( 2 < < 3 ) ) | 3 ;
*/
2008-06-12 09:47:10 +08:00
if ( band = = IEEE80211_BAND_5GHZ )
2008-06-12 09:47:12 +08:00
scan_ch - > tx_gain = ( ( 1 < < 5 ) | ( 3 < < 3 ) ) | 3 ;
2008-07-11 11:53:39 +08:00
else
2008-06-12 09:47:12 +08:00
scan_ch - > tx_gain = ( ( 1 < < 5 ) | ( 5 < < 3 ) ) ;
2008-06-12 09:47:10 +08:00
2009-01-27 14:27:56 -08:00
IWL_DEBUG_SCAN ( priv , " Scanning ch=%d prob=0x%X [%s %d] \n " ,
2008-07-11 11:53:39 +08:00
channel , le32_to_cpu ( scan_ch - > type ) ,
2008-07-11 11:53:38 +08:00
( scan_ch - > type & SCAN_CHANNEL_TYPE_ACTIVE ) ?
" ACTIVE " : " PASSIVE " ,
( scan_ch - > type & SCAN_CHANNEL_TYPE_ACTIVE ) ?
2008-06-12 09:47:10 +08:00
active_dwell : passive_dwell ) ;
scan_ch + + ;
added + + ;
}
2009-01-27 14:27:56 -08:00
IWL_DEBUG_SCAN ( priv , " total channels to scan %d \n " , added ) ;
2008-06-12 09:47:10 +08:00
return added ;
}
2008-06-12 09:47:12 +08:00
void iwl_init_scan_params ( struct iwl_priv * priv )
{
2008-10-14 12:32:45 -07:00
u8 ant_idx = fls ( priv - > hw_params . valid_tx_ant ) - 1 ;
2008-06-12 09:47:12 +08:00
if ( ! priv - > scan_tx_ant [ IEEE80211_BAND_5GHZ ] )
2008-10-14 12:32:45 -07:00
priv - > scan_tx_ant [ IEEE80211_BAND_5GHZ ] = ant_idx ;
2008-06-12 09:47:12 +08:00
if ( ! priv - > scan_tx_ant [ IEEE80211_BAND_2GHZ ] )
2008-10-14 12:32:45 -07:00
priv - > scan_tx_ant [ IEEE80211_BAND_2GHZ ] = ant_idx ;
2008-06-12 09:47:12 +08:00
}
2008-06-12 09:47:10 +08:00
int iwl_scan_initiate ( struct iwl_priv * priv )
{
if ( ! iwl_is_ready_rf ( priv ) ) {
2009-01-27 14:27:56 -08:00
IWL_DEBUG_SCAN ( priv , " Aborting scan due to not ready. \n " ) ;
2008-06-12 09:47:10 +08:00
return - EIO ;
}
if ( test_bit ( STATUS_SCANNING , & priv - > status ) ) {
2009-01-27 14:27:56 -08:00
IWL_DEBUG_SCAN ( priv , " Scan already in progress. \n " ) ;
2008-06-12 09:47:10 +08:00
return - EAGAIN ;
}
if ( test_bit ( STATUS_SCAN_ABORTING , & priv - > status ) ) {
2009-01-27 14:27:56 -08:00
IWL_DEBUG_SCAN ( priv , " Scan request while abort pending \n " ) ;
2008-06-12 09:47:10 +08:00
return - EAGAIN ;
}
2009-01-27 14:27:56 -08:00
IWL_DEBUG_INFO ( priv , " Starting scan... \n " ) ;
2008-06-28 01:19:40 -07:00
if ( priv - > cfg - > sku & IWL_SKU_G )
priv - > scan_bands | = BIT ( IEEE80211_BAND_2GHZ ) ;
if ( priv - > cfg - > sku & IWL_SKU_A )
priv - > scan_bands | = BIT ( IEEE80211_BAND_5GHZ ) ;
2008-06-12 09:47:10 +08:00
set_bit ( STATUS_SCANNING , & priv - > status ) ;
priv - > scan_start = jiffies ;
priv - > scan_pass_start = priv - > scan_start ;
queue_work ( priv - > workqueue , & priv - > request_scan ) ;
return 0 ;
}
EXPORT_SYMBOL ( iwl_scan_initiate ) ;
# define IWL_SCAN_CHECK_WATCHDOG (7 * HZ)
2009-01-23 13:45:12 -08:00
void iwl_bg_scan_check ( struct work_struct * data )
2008-06-12 09:47:10 +08:00
{
struct iwl_priv * priv =
container_of ( data , struct iwl_priv , scan_check . work ) ;
if ( test_bit ( STATUS_EXIT_PENDING , & priv - > status ) )
return ;
mutex_lock ( & priv - > mutex ) ;
if ( test_bit ( STATUS_SCANNING , & priv - > status ) | |
test_bit ( STATUS_SCAN_ABORTING , & priv - > status ) ) {
2009-01-27 14:27:56 -08:00
IWL_DEBUG_SCAN ( priv , " Scan completion watchdog resetting "
2008-06-12 09:47:10 +08:00
" adapter (%dms) \n " ,
jiffies_to_msecs ( IWL_SCAN_CHECK_WATCHDOG ) ) ;
if ( ! test_bit ( STATUS_EXIT_PENDING , & priv - > status ) )
iwl_send_scan_abort ( priv ) ;
}
mutex_unlock ( & priv - > mutex ) ;
}
2009-01-23 13:45:12 -08:00
EXPORT_SYMBOL ( iwl_bg_scan_check ) ;
2008-06-12 09:47:10 +08:00
/**
* iwl_supported_rate_to_ie - fill in the supported rate in IE field
*
* return : set the bit for each supported rate insert in ie
*/
static u16 iwl_supported_rate_to_ie ( u8 * ie , u16 supported_rate ,
u16 basic_rate , int * left )
{
u16 ret_rates = 0 , bit ;
int i ;
u8 * cnt = ie ;
u8 * rates = ie + 1 ;
for ( bit = 1 , i = 0 ; i < IWL_RATE_COUNT ; i + + , bit < < = 1 ) {
if ( bit & supported_rate ) {
ret_rates | = bit ;
rates [ * cnt ] = iwl_rates [ i ] . ieee |
( ( bit & basic_rate ) ? 0x80 : 0x00 ) ;
( * cnt ) + + ;
( * left ) - - ;
if ( ( * left < = 0 ) | |
( * cnt > = IWL_SUPPORTED_RATES_IE_LEN ) )
break ;
}
}
return ret_rates ;
}
static void iwl_ht_cap_to_ie ( const struct ieee80211_supported_band * sband ,
2008-06-12 09:47:12 +08:00
u8 * pos , int * left )
2008-06-12 09:47:10 +08:00
{
struct ieee80211_ht_cap * ht_cap ;
2008-10-09 12:13:49 +02:00
if ( ! sband | | ! sband - > ht_cap . ht_supported )
2008-06-12 09:47:10 +08:00
return ;
if ( * left < sizeof ( struct ieee80211_ht_cap ) )
return ;
* pos + + = sizeof ( struct ieee80211_ht_cap ) ;
ht_cap = ( struct ieee80211_ht_cap * ) pos ;
2008-10-09 12:13:49 +02:00
ht_cap - > cap_info = cpu_to_le16 ( sband - > ht_cap . cap ) ;
memcpy ( & ht_cap - > mcs , & sband - > ht_cap . mcs , 16 ) ;
2008-06-12 09:47:10 +08:00
ht_cap - > ampdu_params_info =
2008-10-09 12:13:49 +02:00
( sband - > ht_cap . ampdu_factor & IEEE80211_HT_AMPDU_PARM_FACTOR ) |
( ( sband - > ht_cap . ampdu_density < < 2 ) &
IEEE80211_HT_AMPDU_PARM_DENSITY ) ;
2008-06-12 09:47:10 +08:00
* left - = sizeof ( struct ieee80211_ht_cap ) ;
}
/**
* iwl_fill_probe_req - fill in all required fields and IE for probe request
*/
2008-06-12 09:47:12 +08:00
2009-01-23 13:45:12 -08:00
u16 iwl_fill_probe_req ( struct iwl_priv * priv ,
enum ieee80211_band band ,
struct ieee80211_mgmt * frame ,
int left )
2008-06-12 09:47:10 +08:00
{
int len = 0 ;
u8 * pos = NULL ;
u16 active_rates , ret_rates , cck_rates , active_rate_basic ;
const struct ieee80211_supported_band * sband =
iwl_get_hw_mode ( priv , band ) ;
2008-06-12 09:47:12 +08:00
2008-06-12 09:47:10 +08:00
/* Make sure there is enough space for the probe request,
* two mandatory IEs and the data */
left - = 24 ;
if ( left < 0 )
return 0 ;
frame - > frame_control = cpu_to_le16 ( IEEE80211_STYPE_PROBE_REQ ) ;
memcpy ( frame - > da , iwl_bcast_addr , ETH_ALEN ) ;
memcpy ( frame - > sa , priv - > mac_addr , ETH_ALEN ) ;
memcpy ( frame - > bssid , iwl_bcast_addr , ETH_ALEN ) ;
frame - > seq_ctrl = 0 ;
2008-06-12 09:47:12 +08:00
len + = 24 ;
2008-06-12 09:47:10 +08:00
/* ...next IE... */
2008-06-12 09:47:12 +08:00
pos = & frame - > u . probe_req . variable [ 0 ] ;
2008-06-12 09:47:10 +08:00
2008-06-12 09:47:12 +08:00
/* fill in our indirect SSID IE */
2008-06-12 09:47:10 +08:00
left - = 2 ;
if ( left < 0 )
return 0 ;
* pos + + = WLAN_EID_SSID ;
* pos + + = 0 ;
2008-06-12 09:47:12 +08:00
len + = 2 ;
2008-06-12 09:47:10 +08:00
/* fill in supported rate */
left - = 2 ;
if ( left < 0 )
return 0 ;
* pos + + = WLAN_EID_SUPP_RATES ;
* pos = 0 ;
/* exclude 60M rate */
active_rates = priv - > rates_mask ;
active_rates & = ~ IWL_RATE_60M_MASK ;
active_rate_basic = active_rates & IWL_BASIC_RATES_MASK ;
cck_rates = IWL_CCK_RATES_MASK & active_rates ;
ret_rates = iwl_supported_rate_to_ie ( pos , cck_rates ,
2008-06-12 09:47:12 +08:00
active_rate_basic , & left ) ;
2008-06-12 09:47:10 +08:00
active_rates & = ~ ret_rates ;
ret_rates = iwl_supported_rate_to_ie ( pos , active_rates ,
2008-06-12 09:47:12 +08:00
active_rate_basic , & left ) ;
2008-06-12 09:47:10 +08:00
active_rates & = ~ ret_rates ;
len + = 2 + * pos ;
pos + = ( * pos ) + 1 ;
2008-06-12 09:47:12 +08:00
2008-06-12 09:47:10 +08:00
if ( active_rates = = 0 )
goto fill_end ;
/* fill in supported extended rate */
/* ...next IE... */
left - = 2 ;
if ( left < 0 )
return 0 ;
/* ... fill it in... */
* pos + + = WLAN_EID_EXT_SUPP_RATES ;
* pos = 0 ;
2008-06-12 09:47:12 +08:00
iwl_supported_rate_to_ie ( pos , active_rates , active_rate_basic , & left ) ;
if ( * pos > 0 ) {
2008-06-12 09:47:10 +08:00
len + = 2 + * pos ;
2008-06-12 09:47:12 +08:00
pos + = ( * pos ) + 1 ;
} else {
pos - - ;
}
2008-06-12 09:47:10 +08:00
fill_end :
2008-06-12 09:47:12 +08:00
2008-06-12 09:47:10 +08:00
left - = 2 ;
if ( left < 0 )
return 0 ;
* pos + + = WLAN_EID_HT_CAPABILITY ;
* pos = 0 ;
iwl_ht_cap_to_ie ( sband , pos , & left ) ;
if ( * pos > 0 )
len + = 2 + * pos ;
2008-06-12 09:47:12 +08:00
2008-06-12 09:47:10 +08:00
return ( u16 ) len ;
}
2009-01-23 13:45:12 -08:00
EXPORT_SYMBOL ( iwl_fill_probe_req ) ;
2008-06-12 09:47:10 +08:00
static void iwl_bg_request_scan ( struct work_struct * data )
{
struct iwl_priv * priv =
container_of ( data , struct iwl_priv , request_scan ) ;
struct iwl_host_cmd cmd = {
. id = REPLY_SCAN_CMD ,
. len = sizeof ( struct iwl_scan_cmd ) ,
. meta . flags = CMD_SIZE_HUGE ,
} ;
struct iwl_scan_cmd * scan ;
struct ieee80211_conf * conf = NULL ;
2008-06-12 09:47:12 +08:00
int ret = 0 ;
2008-10-14 12:32:45 -07:00
u32 rate_flags = 0 ;
2008-06-12 09:47:10 +08:00
u16 cmd_len ;
enum ieee80211_band band ;
2008-07-11 11:53:39 +08:00
u8 n_probes = 2 ;
2008-10-06 16:05:29 +08:00
u8 rx_chain = priv - > hw_params . valid_rx_ant ;
2008-10-14 12:32:45 -07:00
u8 rate ;
2008-09-30 20:59:05 -04:00
DECLARE_SSID_BUF ( ssid ) ;
2008-06-12 09:47:10 +08:00
conf = ieee80211_get_hw_conf ( priv - > hw ) ;
mutex_lock ( & priv - > mutex ) ;
if ( ! iwl_is_ready ( priv ) ) {
2008-12-19 10:37:32 +08:00
IWL_WARN ( priv , " request scan called when driver not ready. \n " ) ;
2008-06-12 09:47:10 +08:00
goto done ;
}
2008-10-23 23:48:56 -07:00
/* Make sure the scan wasn't canceled before this queued work
2008-06-12 09:47:10 +08:00
* was given the chance to run . . . */
if ( ! test_bit ( STATUS_SCANNING , & priv - > status ) )
goto done ;
/* This should never be called or scheduled if there is currently
* a scan active in the hardware . */
if ( test_bit ( STATUS_SCAN_HW , & priv - > status ) ) {
2009-01-27 14:27:56 -08:00
IWL_DEBUG_INFO ( priv , " Multiple concurrent scan requests in parallel. "
2008-06-12 09:47:10 +08:00
" Ignoring second request. \n " ) ;
ret = - EIO ;
goto done ;
}
if ( test_bit ( STATUS_EXIT_PENDING , & priv - > status ) ) {
2009-01-27 14:27:56 -08:00
IWL_DEBUG_SCAN ( priv , " Aborting scan due to device shutdown \n " ) ;
2008-06-12 09:47:10 +08:00
goto done ;
}
if ( test_bit ( STATUS_SCAN_ABORTING , & priv - > status ) ) {
2009-01-27 14:27:56 -08:00
IWL_DEBUG_HC ( priv , " Scan request while abort pending. Queuing. \n " ) ;
2008-06-12 09:47:10 +08:00
goto done ;
}
if ( iwl_is_rfkill ( priv ) ) {
2009-01-27 14:27:56 -08:00
IWL_DEBUG_HC ( priv , " Aborting scan due to RF Kill activation \n " ) ;
2008-06-12 09:47:10 +08:00
goto done ;
}
if ( ! test_bit ( STATUS_READY , & priv - > status ) ) {
2009-01-27 14:27:56 -08:00
IWL_DEBUG_HC ( priv , " Scan request while uninitialized. Queuing. \n " ) ;
2008-06-12 09:47:10 +08:00
goto done ;
}
if ( ! priv - > scan_bands ) {
2009-01-27 14:27:56 -08:00
IWL_DEBUG_HC ( priv , " Aborting scan due to no requested bands \n " ) ;
2008-06-12 09:47:10 +08:00
goto done ;
}
if ( ! priv - > scan ) {
priv - > scan = kmalloc ( sizeof ( struct iwl_scan_cmd ) +
IWL_MAX_SCAN_SIZE , GFP_KERNEL ) ;
if ( ! priv - > scan ) {
ret = - ENOMEM ;
goto done ;
}
}
scan = priv - > scan ;
memset ( scan , 0 , sizeof ( struct iwl_scan_cmd ) + IWL_MAX_SCAN_SIZE ) ;
scan - > quiet_plcp_th = IWL_PLCP_QUIET_THRESH ;
scan - > quiet_time = IWL_ACTIVE_QUIET_TIME ;
if ( iwl_is_associated ( priv ) ) {
u16 interval = 0 ;
u32 extra ;
u32 suspend_time = 100 ;
u32 scan_suspend_time = 100 ;
unsigned long flags ;
2009-01-27 14:27:56 -08:00
IWL_DEBUG_INFO ( priv , " Scanning while associated... \n " ) ;
2008-06-12 09:47:10 +08:00
spin_lock_irqsave ( & priv - > lock , flags ) ;
interval = priv - > beacon_int ;
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
scan - > suspend_time = 0 ;
scan - > max_out_time = cpu_to_le32 ( 200 * 1024 ) ;
if ( ! interval )
interval = suspend_time ;
extra = ( suspend_time / interval ) < < 22 ;
scan_suspend_time = ( extra |
( ( suspend_time % interval ) * 1024 ) ) ;
scan - > suspend_time = cpu_to_le32 ( scan_suspend_time ) ;
2009-01-27 14:27:56 -08:00
IWL_DEBUG_SCAN ( priv , " suspend_time 0x%X beacon interval %d \n " ,
2008-06-12 09:47:10 +08:00
scan_suspend_time , interval ) ;
}
/* We should add the ability for user to lock to PASSIVE ONLY */
if ( priv - > one_direct_scan ) {
2009-01-27 14:27:56 -08:00
IWL_DEBUG_SCAN ( priv , " Start direct scan for '%s' \n " ,
2008-09-30 20:59:05 -04:00
print_ssid ( ssid , priv - > direct_ssid ,
priv - > direct_ssid_len ) ) ;
2008-06-12 09:47:10 +08:00
scan - > direct_scan [ 0 ] . id = WLAN_EID_SSID ;
scan - > direct_scan [ 0 ] . len = priv - > direct_ssid_len ;
memcpy ( scan - > direct_scan [ 0 ] . ssid ,
priv - > direct_ssid , priv - > direct_ssid_len ) ;
2008-07-11 11:53:39 +08:00
n_probes + + ;
2008-06-12 09:47:10 +08:00
} else {
2009-01-27 14:27:56 -08:00
IWL_DEBUG_SCAN ( priv , " Start indirect scan. \n " ) ;
2008-06-12 09:47:10 +08:00
}
scan - > tx_cmd . tx_flags = TX_CMD_FLG_SEQ_CTL_MSK ;
scan - > tx_cmd . sta_id = priv - > hw_params . bcast_sta_id ;
scan - > tx_cmd . stop_time . life_time = TX_CMD_LIFE_TIME_INFINITE ;
2008-06-28 01:19:40 -07:00
if ( priv - > scan_bands & BIT ( IEEE80211_BAND_2GHZ ) ) {
2008-06-12 09:47:12 +08:00
band = IEEE80211_BAND_2GHZ ;
2008-06-12 09:47:10 +08:00
scan - > flags = RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK ;
2008-10-14 12:32:45 -07:00
if ( priv - > active_rxon . flags & RXON_FLG_CHANNEL_MODE_PURE_40_MSK ) {
rate = IWL_RATE_6M_PLCP ;
} else {
rate = IWL_RATE_1M_PLCP ;
rate_flags = RATE_MCS_CCK_MSK ;
}
2008-06-12 09:47:10 +08:00
scan - > good_CRC_th = 0 ;
2008-06-28 01:19:40 -07:00
} else if ( priv - > scan_bands & BIT ( IEEE80211_BAND_5GHZ ) ) {
2008-06-12 09:47:12 +08:00
band = IEEE80211_BAND_5GHZ ;
2008-10-14 12:32:45 -07:00
rate = IWL_RATE_6M_PLCP ;
2008-06-12 09:47:10 +08:00
scan - > good_CRC_th = IWL_GOOD_CRC_TH ;
2008-06-12 09:47:12 +08:00
/* Force use of chains B and C (0x6) for scan Rx for 4965
* Avoid A ( 0x1 ) because of its off - channel reception on A - band .
2008-10-06 16:05:30 +08:00
*/
2008-06-12 09:47:12 +08:00
if ( ( priv - > hw_rev & CSR_HW_REV_TYPE_MSK ) = = CSR_HW_REV_TYPE_4965 )
rx_chain = 0x6 ;
2008-06-28 01:19:40 -07:00
} else {
2008-12-19 10:37:32 +08:00
IWL_WARN ( priv , " Invalid scan band count \n " ) ;
2008-06-12 09:47:10 +08:00
goto done ;
}
2008-10-14 12:32:45 -07:00
priv - > scan_tx_ant [ band ] =
iwl_toggle_tx_ant ( priv , priv - > scan_tx_ant [ band ] ) ;
rate_flags | = iwl_ant_idx_to_flags ( priv - > scan_tx_ant [ band ] ) ;
scan - > tx_cmd . rate_n_flags = iwl_hw_set_rate_n_flags ( rate , rate_flags ) ;
2008-10-06 16:05:30 +08:00
/* MIMO is not used here, but value is required */
2008-06-12 09:47:12 +08:00
scan - > rx_chain = RXON_RX_CHAIN_DRIVER_FORCE_MSK |
cpu_to_le16 ( ( 0x7 < < RXON_RX_CHAIN_VALID_POS ) |
( rx_chain < < RXON_RX_CHAIN_FORCE_SEL_POS ) |
( 0x7 < < RXON_RX_CHAIN_FORCE_MIMO_SEL_POS ) ) ;
2008-06-12 09:47:10 +08:00
cmd_len = iwl_fill_probe_req ( priv , band ,
2008-06-12 09:47:12 +08:00
( struct ieee80211_mgmt * ) scan - > data ,
IWL_MAX_SCAN_SIZE - sizeof ( * scan ) ) ;
2008-06-12 09:47:10 +08:00
scan - > tx_cmd . len = cpu_to_le16 ( cmd_len ) ;
2008-09-11 00:01:58 +02:00
if ( priv - > iw_mode = = NL80211_IFTYPE_MONITOR )
2008-06-12 09:47:10 +08:00
scan - > filter_flags = RXON_FILTER_PROMISC_MSK ;
2008-06-12 09:47:12 +08:00
scan - > filter_flags | = ( RXON_FILTER_ACCEPT_GRP_MSK |
RXON_FILTER_BCON_AWARE_MSK ) ;
2008-07-11 11:53:39 +08:00
scan - > channel_count =
iwl_get_channels_for_scan ( priv , band , 1 , /* active */
n_probes ,
( void * ) & scan - > data [ le16_to_cpu ( scan - > tx_cmd . len ) ] ) ;
2008-06-12 09:47:12 +08:00
if ( scan - > channel_count = = 0 ) {
2009-01-27 14:27:56 -08:00
IWL_DEBUG_SCAN ( priv , " channel count %d \n " , scan - > channel_count ) ;
2008-06-12 09:47:12 +08:00
goto done ;
}
2008-06-12 09:47:10 +08:00
cmd . len + = le16_to_cpu ( scan - > tx_cmd . len ) +
scan - > channel_count * sizeof ( struct iwl_scan_channel ) ;
cmd . data = scan ;
scan - > len = cpu_to_le16 ( cmd . len ) ;
set_bit ( STATUS_SCAN_HW , & priv - > status ) ;
ret = iwl_send_cmd_sync ( priv , & cmd ) ;
if ( ret )
goto done ;
queue_delayed_work ( priv - > workqueue , & priv - > scan_check ,
IWL_SCAN_CHECK_WATCHDOG ) ;
mutex_unlock ( & priv - > mutex ) ;
return ;
done :
2008-10-23 23:48:54 -07:00
/* Cannot perform scan. Make sure we clear scanning
* bits from status so next scan request can be performed .
* If we don ' t clear scanning status bit here all next scan
* will fail
*/
clear_bit ( STATUS_SCAN_HW , & priv - > status ) ;
clear_bit ( STATUS_SCANNING , & priv - > status ) ;
2008-06-12 09:47:10 +08:00
/* inform mac80211 scan aborted */
queue_work ( priv - > workqueue , & priv - > scan_completed ) ;
mutex_unlock ( & priv - > mutex ) ;
}
2009-01-23 13:45:12 -08:00
void iwl_bg_abort_scan ( struct work_struct * work )
2008-06-12 09:47:10 +08:00
{
struct iwl_priv * priv = container_of ( work , struct iwl_priv , abort_scan ) ;
if ( ! iwl_is_ready ( priv ) )
return ;
mutex_lock ( & priv - > mutex ) ;
set_bit ( STATUS_SCAN_ABORTING , & priv - > status ) ;
iwl_send_scan_abort ( priv ) ;
mutex_unlock ( & priv - > mutex ) ;
}
2009-01-23 13:45:12 -08:00
EXPORT_SYMBOL ( iwl_bg_abort_scan ) ;
2008-06-12 09:47:10 +08:00
2009-01-23 13:45:12 -08:00
void iwl_bg_scan_completed ( struct work_struct * work )
2008-10-06 16:05:32 +08:00
{
struct iwl_priv * priv =
container_of ( work , struct iwl_priv , scan_completed ) ;
2009-01-27 14:27:56 -08:00
IWL_DEBUG_SCAN ( priv , " SCAN complete scan \n " ) ;
2008-10-06 16:05:32 +08:00
if ( test_bit ( STATUS_EXIT_PENDING , & priv - > status ) )
return ;
2009-02-10 21:25:55 +01:00
ieee80211_scan_completed ( priv - > hw , false ) ;
2008-10-06 16:05:32 +08:00
/* Since setting the TXPOWER may have been deferred while
* performing the scan , fire one off */
mutex_lock ( & priv - > mutex ) ;
iwl_set_tx_power ( priv , priv - > tx_power_user_lmt , true ) ;
mutex_unlock ( & priv - > mutex ) ;
}
2009-01-23 13:45:12 -08:00
EXPORT_SYMBOL ( iwl_bg_scan_completed ) ;
2008-10-06 16:05:32 +08:00
2008-06-12 09:47:10 +08:00
void iwl_setup_scan_deferred_work ( struct iwl_priv * priv )
{
2008-10-06 16:05:32 +08:00
INIT_WORK ( & priv - > scan_completed , iwl_bg_scan_completed ) ;
2008-06-12 09:47:10 +08:00
INIT_WORK ( & priv - > request_scan , iwl_bg_request_scan ) ;
INIT_WORK ( & priv - > abort_scan , iwl_bg_abort_scan ) ;
INIT_DELAYED_WORK ( & priv - > scan_check , iwl_bg_scan_check ) ;
}
EXPORT_SYMBOL ( iwl_setup_scan_deferred_work ) ;