2008-04-14 21:16:06 -07:00
/******************************************************************************
*
2010-01-15 13:43:41 -08:00
* Copyright ( c ) 2003 - 2010 Intel Corporation . All rights reserved .
2008-04-14 21:16:06 -07:00
*
* Portions of this file are derived from the ipw3945 project , as well
* as portions of the ieee80211 subsystem header files .
*
* 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 .
*
* Contact Information :
2008-12-09 11:28:58 -08:00
* Intel Linux Wireless < ilw @ linux . intel . com >
2008-04-14 21:16:06 -07:00
* Intel Corporation , 5200 N . E . Elam Young Parkway , Hillsboro , OR 97124 - 6497
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include <net/mac80211.h>
2008-04-16 16:34:48 -07:00
# include <linux/etherdevice.h>
2010-02-22 16:24:47 -08:00
# include <linux/sched.h>
2008-04-14 21:16:06 -07:00
2008-04-24 11:55:38 -07:00
# include "iwl-dev.h"
2008-04-14 21:16:06 -07:00
# include "iwl-core.h"
# include "iwl-sta.h"
2008-05-29 16:35:02 +08:00
2008-04-16 16:34:48 -07:00
u8 iwl_find_station ( struct iwl_priv * priv , const u8 * addr )
{
int i ;
int start = 0 ;
int ret = IWL_INVALID_STATION ;
unsigned long flags ;
2008-09-11 00:01:58 +02:00
if ( ( priv - > iw_mode = = NL80211_IFTYPE_ADHOC ) | |
( priv - > iw_mode = = NL80211_IFTYPE_AP ) )
2008-04-16 16:34:48 -07:00
start = IWL_STA_ID ;
if ( is_broadcast_ether_addr ( addr ) )
return priv - > hw_params . bcast_sta_id ;
spin_lock_irqsave ( & priv - > sta_lock , flags ) ;
for ( i = start ; i < priv - > hw_params . max_stations ; i + + )
if ( priv - > stations [ i ] . used & &
( ! compare_ether_addr ( priv - > stations [ i ] . sta . sta . addr ,
addr ) ) ) {
ret = i ;
goto out ;
}
2009-01-27 14:27:56 -08:00
IWL_DEBUG_ASSOC_LIMIT ( priv , " can not find STA %pM total %d \n " ,
2008-10-27 15:59:26 -07:00
addr , priv - > num_stations ) ;
2008-04-16 16:34:48 -07:00
out :
2010-02-22 16:24:47 -08:00
/*
* It may be possible that more commands interacting with stations
* arrive before we completed processing the adding of
* station
*/
if ( ret ! = IWL_INVALID_STATION & &
( ! ( priv - > stations [ ret ] . used & IWL_STA_UCODE_ACTIVE ) | |
( ( priv - > stations [ ret ] . used & IWL_STA_UCODE_ACTIVE ) & &
( priv - > stations [ ret ] . used & IWL_STA_UCODE_INPROGRESS ) ) ) ) {
2010-03-24 14:19:58 -07:00
IWL_ERR ( priv , " Requested station info for sta %d before ready. \n " ,
2010-02-22 16:24:47 -08:00
ret ) ;
ret = IWL_INVALID_STATION ;
}
2008-04-16 16:34:48 -07:00
spin_unlock_irqrestore ( & priv - > sta_lock , flags ) ;
return ret ;
}
EXPORT_SYMBOL ( iwl_find_station ) ;
2008-06-12 09:47:18 +08:00
int iwl_get_ra_sta_id ( struct iwl_priv * priv , struct ieee80211_hdr * hdr )
{
2008-09-11 00:01:58 +02:00
if ( priv - > iw_mode = = NL80211_IFTYPE_STATION ) {
2008-06-12 09:47:18 +08:00
return IWL_AP_ID ;
} else {
u8 * da = ieee80211_get_DA ( hdr ) ;
2009-06-03 11:44:07 -07:00
return iwl_find_station ( priv , da ) ;
2008-06-12 09:47:18 +08:00
}
}
EXPORT_SYMBOL ( iwl_get_ra_sta_id ) ;
2010-01-22 14:22:48 -08:00
/* priv->sta_lock must be held */
2008-06-30 17:23:03 +08:00
static void iwl_sta_ucode_activate ( struct iwl_priv * priv , u8 sta_id )
{
2009-06-03 11:44:07 -07:00
if ( ! ( priv - > stations [ sta_id ] . used & IWL_STA_DRIVER_ACTIVE ) )
2010-01-22 14:22:48 -08:00
IWL_ERR ( priv , " ACTIVATE a non DRIVER active station id %u addr %pM \n " ,
sta_id , priv - > stations [ sta_id ] . sta . sta . addr ) ;
2008-06-30 17:23:03 +08:00
2010-01-22 14:22:48 -08:00
if ( priv - > stations [ sta_id ] . used & IWL_STA_UCODE_ACTIVE ) {
IWL_DEBUG_ASSOC ( priv ,
" STA id %u addr %pM already present in uCode (according to driver) \n " ,
sta_id , priv - > stations [ sta_id ] . sta . sta . addr ) ;
} else {
priv - > stations [ sta_id ] . used | = IWL_STA_UCODE_ACTIVE ;
IWL_DEBUG_ASSOC ( priv , " Added STA id %u addr %pM to uCode \n " ,
sta_id , priv - > stations [ sta_id ] . sta . sta . addr ) ;
}
2008-06-30 17:23:03 +08:00
}
2010-01-22 14:22:48 -08:00
static void iwl_process_add_sta_resp ( struct iwl_priv * priv ,
struct iwl_addsta_cmd * addsta ,
struct iwl_rx_packet * pkt ,
bool sync )
2008-05-29 16:35:03 +08:00
{
2008-10-14 12:32:43 -07:00
u8 sta_id = addsta - > sta . sta_id ;
2010-01-22 14:22:48 -08:00
unsigned long flags ;
2008-05-29 16:35:03 +08:00
2009-10-09 17:19:45 +08:00
if ( pkt - > hdr . flags & IWL_CMD_FAILED_MSK ) {
2008-12-19 10:37:33 +08:00
IWL_ERR ( priv , " Bad return from REPLY_ADD_STA (0x%08X) \n " ,
2010-01-22 14:22:48 -08:00
pkt - > hdr . flags ) ;
2009-07-24 11:13:06 -07:00
return ;
2008-05-29 16:35:03 +08:00
}
2010-01-22 14:22:48 -08:00
IWL_DEBUG_INFO ( priv , " Processing response for adding station %u \n " ,
sta_id ) ;
spin_lock_irqsave ( & priv - > sta_lock , flags ) ;
2009-10-09 17:19:45 +08:00
switch ( pkt - > u . add_sta . status ) {
2008-05-29 16:35:03 +08:00
case ADD_STA_SUCCESS_MSK :
2010-01-22 14:22:48 -08:00
IWL_DEBUG_INFO ( priv , " REPLY_ADD_STA PASSED \n " ) ;
2008-06-30 17:23:03 +08:00
iwl_sta_ucode_activate ( priv , sta_id ) ;
2010-01-22 14:22:48 -08:00
break ;
case ADD_STA_NO_ROOM_IN_TABLE :
IWL_ERR ( priv , " Adding station %d failed, no room in table. \n " ,
sta_id ) ;
break ;
case ADD_STA_NO_BLOCK_ACK_RESOURCE :
IWL_ERR ( priv , " Adding station %d failed, no block ack resource. \n " ,
sta_id ) ;
break ;
case ADD_STA_MODIFY_NON_EXIST_STA :
2010-03-24 14:19:58 -07:00
IWL_ERR ( priv , " Attempting to modify non-existing station %d \n " ,
2010-01-22 14:22:48 -08:00
sta_id ) ;
break ;
2008-05-29 16:35:03 +08:00
default :
2010-01-22 14:22:48 -08:00
IWL_DEBUG_ASSOC ( priv , " Received REPLY_ADD_STA:(0x%08X) \n " ,
pkt - > u . add_sta . status ) ;
2008-05-29 16:35:03 +08:00
break ;
}
2010-01-22 14:22:48 -08:00
IWL_DEBUG_INFO ( priv , " %s station id %u addr %pM \n " ,
priv - > stations [ sta_id ] . sta . mode = =
STA_CONTROL_MODIFY_MSK ? " Modified " : " Added " ,
sta_id , priv - > stations [ sta_id ] . sta . sta . addr ) ;
/*
* XXX : The MAC address in the command buffer is often changed from
* the original sent to the device . That is , the MAC address
* written to the command buffer often is not the same MAC adress
* read from the command buffer when the command returns . This
* issue has not yet been resolved and this debugging is left to
* observe the problem .
*/
IWL_DEBUG_INFO ( priv , " %s station according to cmd buffer %pM \n " ,
priv - > stations [ sta_id ] . sta . mode = =
STA_CONTROL_MODIFY_MSK ? " Modified " : " Added " ,
addsta - > sta . addr ) ;
spin_unlock_irqrestore ( & priv - > sta_lock , flags ) ;
}
static void iwl_add_sta_callback ( struct iwl_priv * priv ,
struct iwl_device_cmd * cmd ,
struct iwl_rx_packet * pkt )
{
struct iwl_addsta_cmd * addsta =
( struct iwl_addsta_cmd * ) cmd - > cmd . payload ;
iwl_process_add_sta_resp ( priv , addsta , pkt , false ) ;
2008-05-29 16:35:03 +08:00
}
2009-01-23 13:45:20 -08:00
int iwl_send_add_sta ( struct iwl_priv * priv ,
2008-05-05 10:22:34 +08:00
struct iwl_addsta_cmd * sta , u8 flags )
{
2009-10-09 17:19:45 +08:00
struct iwl_rx_packet * pkt = NULL ;
2008-05-05 10:22:34 +08:00
int ret = 0 ;
u8 data [ sizeof ( * sta ) ] ;
struct iwl_host_cmd cmd = {
. id = REPLY_ADD_STA ,
2009-07-24 11:13:05 -07:00
. flags = flags ,
2008-05-05 10:22:34 +08:00
. data = data ,
} ;
2010-04-05 10:43:10 -07:00
u8 sta_id __maybe_unused = sta - > sta . sta_id ;
2010-02-22 16:24:47 -08:00
IWL_DEBUG_INFO ( priv , " Adding sta %u (%pM) %ssynchronously \n " ,
sta_id , sta - > sta . addr , flags & CMD_ASYNC ? " a " : " " ) ;
2008-05-05 10:22:34 +08:00
2008-05-29 16:35:03 +08:00
if ( flags & CMD_ASYNC )
2009-07-24 11:13:05 -07:00
cmd . callback = iwl_add_sta_callback ;
2008-05-29 16:35:03 +08:00
else
2009-07-24 11:13:05 -07:00
cmd . flags | = CMD_WANT_SKB ;
2008-05-05 10:22:34 +08:00
cmd . len = priv - > cfg - > ops - > utils - > build_addsta_hcmd ( sta , data ) ;
ret = iwl_send_cmd ( priv , & cmd ) ;
if ( ret | | ( flags & CMD_ASYNC ) )
return ret ;
if ( ret = = 0 ) {
2010-01-22 14:22:48 -08:00
pkt = ( struct iwl_rx_packet * ) cmd . reply_page ;
iwl_process_add_sta_resp ( priv , sta , pkt , true ) ;
2008-05-05 10:22:34 +08:00
}
2009-12-10 14:37:21 -08:00
iwl_free_pages ( priv , cmd . reply_page ) ;
2008-05-05 10:22:34 +08:00
return ret ;
}
2009-01-23 13:45:20 -08:00
EXPORT_SYMBOL ( iwl_send_add_sta ) ;
2008-04-16 16:34:48 -07:00
2008-05-15 13:54:04 +08:00
static void iwl_set_ht_add_station ( struct iwl_priv * priv , u8 index ,
2008-10-09 12:13:49 +02:00
struct ieee80211_sta_ht_cap * sta_ht_inf )
2008-05-15 13:54:04 +08:00
{
__le32 sta_flags ;
u8 mimo_ps_mode ;
if ( ! sta_ht_inf | | ! sta_ht_inf - > ht_supported )
goto done ;
2008-09-03 11:26:42 +08:00
mimo_ps_mode = ( sta_ht_inf - > cap & IEEE80211_HT_CAP_SM_PS ) > > 2 ;
2009-10-30 14:36:17 -07:00
IWL_DEBUG_ASSOC ( priv , " spatial multiplexing power save mode: %s \n " ,
( mimo_ps_mode = = WLAN_HT_CAP_SM_PS_STATIC ) ?
" static " :
( mimo_ps_mode = = WLAN_HT_CAP_SM_PS_DYNAMIC ) ?
" dynamic " : " disabled " ) ;
2008-05-15 13:54:04 +08:00
sta_flags = priv - > stations [ index ] . sta . station_flags ;
sta_flags & = ~ ( STA_FLG_RTS_MIMO_PROT_MSK | STA_FLG_MIMO_DIS_MSK ) ;
switch ( mimo_ps_mode ) {
2008-09-03 11:26:42 +08:00
case WLAN_HT_CAP_SM_PS_STATIC :
2008-05-15 13:54:04 +08:00
sta_flags | = STA_FLG_MIMO_DIS_MSK ;
break ;
2008-09-03 11:26:42 +08:00
case WLAN_HT_CAP_SM_PS_DYNAMIC :
2008-05-15 13:54:04 +08:00
sta_flags | = STA_FLG_RTS_MIMO_PROT_MSK ;
break ;
2008-09-03 11:26:42 +08:00
case WLAN_HT_CAP_SM_PS_DISABLED :
2008-05-15 13:54:04 +08:00
break ;
default :
2008-12-19 10:37:32 +08:00
IWL_WARN ( priv , " Invalid MIMO PS mode %d \n " , mimo_ps_mode ) ;
2008-05-15 13:54:04 +08:00
break ;
}
sta_flags | = cpu_to_le32 (
( u32 ) sta_ht_inf - > ampdu_factor < < STA_FLG_MAX_AGG_SIZE_POS ) ;
sta_flags | = cpu_to_le32 (
( u32 ) sta_ht_inf - > ampdu_density < < STA_FLG_AGG_MPDU_DENSITY_POS ) ;
2009-08-07 15:41:38 -07:00
if ( iwl_is_ht40_tx_allowed ( priv , sta_ht_inf ) )
sta_flags | = STA_FLG_HT40_EN_MSK ;
2008-05-15 13:54:04 +08:00
else
2009-08-07 15:41:38 -07:00
sta_flags & = ~ STA_FLG_HT40_EN_MSK ;
2008-05-15 13:54:04 +08:00
priv - > stations [ index ] . sta . station_flags = sta_flags ;
done :
return ;
}
/**
2010-02-22 16:24:47 -08:00
* iwl_prep_station - Prepare station information for addition
*
* should be called with sta_lock held
2008-05-15 13:54:04 +08:00
*/
2010-02-22 16:24:47 -08:00
static u8 iwl_prep_station ( struct iwl_priv * priv , const u8 * addr ,
bool is_ap ,
struct ieee80211_sta_ht_cap * ht_info )
2008-05-15 13:54:04 +08:00
{
struct iwl_station_entry * station ;
2009-06-03 11:44:07 -07:00
int i ;
2010-02-22 16:24:47 -08:00
u8 sta_id = IWL_INVALID_STATION ;
2009-06-03 11:44:07 -07:00
u16 rate ;
2008-05-15 13:54:04 +08:00
if ( is_ap )
2008-06-30 17:23:03 +08:00
sta_id = IWL_AP_ID ;
2008-05-15 13:54:04 +08:00
else if ( is_broadcast_ether_addr ( addr ) )
2008-06-30 17:23:03 +08:00
sta_id = priv - > hw_params . bcast_sta_id ;
2008-05-15 13:54:04 +08:00
else
for ( i = IWL_STA_ID ; i < priv - > hw_params . max_stations ; i + + ) {
if ( ! compare_ether_addr ( priv - > stations [ i ] . sta . sta . addr ,
addr ) ) {
2008-06-30 17:23:03 +08:00
sta_id = i ;
2008-05-15 13:54:04 +08:00
break ;
}
if ( ! priv - > stations [ i ] . used & &
2008-06-30 17:23:03 +08:00
sta_id = = IWL_INVALID_STATION )
sta_id = i ;
2008-05-15 13:54:04 +08:00
}
2010-02-22 16:24:47 -08:00
/*
* These two conditions have the same outcome , but keep them
* separate
*/
if ( unlikely ( sta_id = = IWL_INVALID_STATION ) )
return sta_id ;
/*
* uCode is not able to deal with multiple requests to add a
* station . Keep track if one is in progress so that we do not send
* another .
*/
if ( priv - > stations [ sta_id ] . used & IWL_STA_UCODE_INPROGRESS ) {
IWL_DEBUG_INFO ( priv , " STA %d already in process of being added. \n " ,
sta_id ) ;
2008-06-30 17:23:03 +08:00
return sta_id ;
2008-05-15 13:54:04 +08:00
}
2010-02-22 16:24:47 -08:00
if ( ( priv - > stations [ sta_id ] . used & IWL_STA_DRIVER_ACTIVE ) & &
( priv - > stations [ sta_id ] . used & IWL_STA_UCODE_ACTIVE ) & &
2008-06-30 17:23:03 +08:00
! compare_ether_addr ( priv - > stations [ sta_id ] . sta . sta . addr , addr ) ) {
2010-02-22 16:24:47 -08:00
IWL_DEBUG_ASSOC ( priv , " STA %d (%pM) already added, not adding again. \n " ,
sta_id , addr ) ;
2008-06-30 17:23:03 +08:00
return sta_id ;
2008-05-15 13:54:04 +08:00
}
2008-06-30 17:23:03 +08:00
station = & priv - > stations [ sta_id ] ;
station - > used = IWL_STA_DRIVER_ACTIVE ;
2009-01-27 14:27:56 -08:00
IWL_DEBUG_ASSOC ( priv , " Add STA to driver ID %d: %pM \n " ,
2008-10-27 15:59:26 -07:00
sta_id , addr ) ;
2008-05-15 13:54:04 +08:00
priv - > num_stations + + ;
/* Set up the REPLY_ADD_STA command to send to device */
memset ( & station - > sta , 0 , sizeof ( struct iwl_addsta_cmd ) ) ;
memcpy ( station - > sta . sta . addr , addr , ETH_ALEN ) ;
station - > sta . mode = 0 ;
2008-06-30 17:23:03 +08:00
station - > sta . sta . sta_id = sta_id ;
2008-05-15 13:54:04 +08:00
station - > sta . station_flags = 0 ;
/* BCAST station and IBSS stations do not work in HT mode */
2008-06-30 17:23:03 +08:00
if ( sta_id ! = priv - > hw_params . bcast_sta_id & &
2008-09-11 00:01:58 +02:00
priv - > iw_mode ! = NL80211_IFTYPE_ADHOC )
2008-06-30 17:23:03 +08:00
iwl_set_ht_add_station ( priv , sta_id , ht_info ) ;
2008-05-15 13:54:04 +08:00
2009-06-03 11:44:07 -07:00
/* 3945 only */
rate = ( priv - > band = = IEEE80211_BAND_5GHZ ) ?
IWL_RATE_6M_PLCP : IWL_RATE_1M_PLCP ;
/* Turn on both antennas for the station... */
station - > sta . rate_n_flags = cpu_to_le16 ( rate | RATE_MCS_ANT_AB_MSK ) ;
2010-02-22 16:24:47 -08:00
return sta_id ;
}
# define STA_WAIT_TIMEOUT (HZ / 2)
/**
* iwl_add_station_common -
*/
int iwl_add_station_common ( struct iwl_priv * priv , const u8 * addr ,
bool is_ap ,
struct ieee80211_sta_ht_cap * ht_info ,
u8 * sta_id_r )
{
struct iwl_station_entry * station ;
unsigned long flags_spin ;
int ret = 0 ;
u8 sta_id ;
* sta_id_r = 0 ;
spin_lock_irqsave ( & priv - > sta_lock , flags_spin ) ;
sta_id = iwl_prep_station ( priv , addr , is_ap , ht_info ) ;
if ( sta_id = = IWL_INVALID_STATION ) {
IWL_ERR ( priv , " Unable to prepare station %pM for addition \n " ,
addr ) ;
spin_unlock_irqrestore ( & priv - > sta_lock , flags_spin ) ;
return - EINVAL ;
}
/*
* uCode is not able to deal with multiple requests to add a
* station . Keep track if one is in progress so that we do not send
* another .
*/
if ( priv - > stations [ sta_id ] . used & IWL_STA_UCODE_INPROGRESS ) {
IWL_DEBUG_INFO ( priv , " STA %d already in process of being added. \n " ,
sta_id ) ;
spin_unlock_irqrestore ( & priv - > sta_lock , flags_spin ) ;
return - EEXIST ;
}
if ( ( priv - > stations [ sta_id ] . used & IWL_STA_DRIVER_ACTIVE ) & &
( priv - > stations [ sta_id ] . used & IWL_STA_UCODE_ACTIVE ) ) {
IWL_DEBUG_ASSOC ( priv , " STA %d (%pM) already added, not adding again. \n " ,
sta_id , addr ) ;
spin_unlock_irqrestore ( & priv - > sta_lock , flags_spin ) ;
return - EEXIST ;
}
priv - > stations [ sta_id ] . used | = IWL_STA_UCODE_INPROGRESS ;
station = & priv - > stations [ sta_id ] ;
2008-05-15 13:54:04 +08:00
spin_unlock_irqrestore ( & priv - > sta_lock , flags_spin ) ;
/* Add station to device's station table */
2010-02-22 16:24:47 -08:00
ret = iwl_send_add_sta ( priv , & station - > sta , CMD_SYNC ) ;
if ( ret ) {
IWL_ERR ( priv , " Adding station %pM failed. \n " , station - > sta . sta . addr ) ;
spin_lock_irqsave ( & priv - > sta_lock , flags_spin ) ;
priv - > stations [ sta_id ] . used & = ~ IWL_STA_DRIVER_ACTIVE ;
priv - > stations [ sta_id ] . used & = ~ IWL_STA_UCODE_INPROGRESS ;
spin_unlock_irqrestore ( & priv - > sta_lock , flags_spin ) ;
}
* sta_id_r = sta_id ;
return ret ;
2008-05-15 13:54:04 +08:00
}
2010-02-22 16:24:47 -08:00
EXPORT_SYMBOL ( iwl_add_station_common ) ;
2008-05-15 13:54:04 +08:00
2010-02-22 16:24:47 -08:00
static void iwl_sta_init_lq ( struct iwl_priv * priv , const u8 * addr , bool is_ap )
2008-05-29 16:35:02 +08:00
{
2010-02-22 16:24:47 -08:00
int i , r ;
struct iwl_link_quality_cmd link_cmd = {
. reserved1 = 0 ,
} ;
u32 rate_flags ;
2010-03-09 15:36:10 -08:00
int ret = 0 ;
2010-02-22 16:24:47 -08:00
/* Set up the rate scaling to start at selected rate, fall back
* all the way down to 1 M in IEEE order , and then spin on 1 M */
if ( is_ap )
r = IWL_RATE_54M_INDEX ;
else if ( priv - > band = = IEEE80211_BAND_5GHZ )
r = IWL_RATE_6M_INDEX ;
else
r = IWL_RATE_1M_INDEX ;
2008-06-30 17:23:03 +08:00
2010-02-22 16:24:47 -08:00
for ( i = 0 ; i < LINK_QUAL_MAX_RETRY_NUM ; i + + ) {
rate_flags = 0 ;
if ( r > = IWL_FIRST_CCK_RATE & & r < = IWL_LAST_CCK_RATE )
rate_flags | = RATE_MCS_CCK_MSK ;
2008-06-30 17:23:03 +08:00
2010-02-22 16:24:47 -08:00
rate_flags | = first_antenna ( priv - > hw_params . valid_tx_ant ) < <
RATE_MCS_ANT_POS ;
2008-06-30 17:23:03 +08:00
2010-02-22 16:24:47 -08:00
link_cmd . rs_table [ i ] . rate_n_flags =
iwl_hw_set_rate_n_flags ( iwl_rates [ r ] . plcp , rate_flags ) ;
r = iwl_get_prev_ieee_rate ( r ) ;
}
2008-06-30 17:23:03 +08:00
2010-02-22 16:24:47 -08:00
link_cmd . general_params . single_stream_ant_msk =
first_antenna ( priv - > hw_params . valid_tx_ant ) ;
link_cmd . general_params . dual_stream_ant_msk = 3 ;
link_cmd . agg_params . agg_dis_start_th = LINK_QUAL_AGG_DISABLE_START_DEF ;
link_cmd . agg_params . agg_time_limit =
cpu_to_le16 ( LINK_QUAL_AGG_TIME_LIMIT_DEF ) ;
2008-06-30 17:23:03 +08:00
2010-02-22 16:24:47 -08:00
/* Update the rate scaling for control frame Tx to AP */
link_cmd . sta_id = is_ap ? IWL_AP_ID : priv - > hw_params . bcast_sta_id ;
2008-06-30 17:23:03 +08:00
2010-03-09 15:36:10 -08:00
ret = iwl_send_cmd_pdu ( priv , REPLY_TX_LINK_QUALITY_CMD ,
2010-02-22 16:24:47 -08:00
sizeof ( link_cmd ) , & link_cmd ) ;
2010-03-09 15:36:10 -08:00
if ( ret )
IWL_ERR ( priv , " REPLY_TX_LINK_QUALITY_CMD failed (%d) \n " , ret ) ;
2008-05-29 16:35:02 +08:00
}
2010-02-22 16:24:47 -08:00
/*
* iwl_add_local_stations - Add stations not requested by mac80211
*
* This will be either the broadcast station or the bssid station needed by
* ad - hoc .
*
* Function sleeps .
*/
int iwl_add_local_station ( struct iwl_priv * priv , const u8 * addr , bool init_rs )
2008-05-29 16:35:02 +08:00
{
2010-02-22 16:24:47 -08:00
int ret ;
u8 sta_id ;
2008-05-29 16:35:02 +08:00
2010-02-22 16:24:47 -08:00
ret = iwl_add_station_common ( priv , addr , 0 , NULL , & sta_id ) ;
if ( ret ) {
IWL_ERR ( priv , " Unable to add station %pM \n " , addr ) ;
return ret ;
2008-05-29 16:35:02 +08:00
}
2010-02-22 16:24:47 -08:00
if ( init_rs )
/* Set up default rate scaling table in device's station table */
iwl_sta_init_lq ( priv , addr , false ) ;
return 0 ;
}
EXPORT_SYMBOL ( iwl_add_local_station ) ;
/**
* iwl_sta_ucode_deactivate - deactivate ucode status for a station
*
* priv - > sta_lock must be held
*/
static void iwl_sta_ucode_deactivate ( struct iwl_priv * priv , u8 sta_id )
{
/* Ucode must be active and driver must be non active */
if ( priv - > stations [ sta_id ] . used ! = IWL_STA_UCODE_ACTIVE )
IWL_ERR ( priv , " removed non active STA %u \n " , sta_id ) ;
priv - > stations [ sta_id ] . used & = ~ IWL_STA_UCODE_ACTIVE ;
memset ( & priv - > stations [ sta_id ] , 0 , sizeof ( struct iwl_station_entry ) ) ;
IWL_DEBUG_ASSOC ( priv , " Removed STA %u \n " , sta_id ) ;
2008-05-29 16:35:02 +08:00
}
2010-02-22 16:24:47 -08:00
static int iwl_send_remove_station ( struct iwl_priv * priv ,
struct iwl_station_entry * station )
2008-05-29 16:35:02 +08:00
{
2009-10-09 17:19:45 +08:00
struct iwl_rx_packet * pkt ;
2008-05-29 16:35:02 +08:00
int ret ;
2010-02-22 16:24:47 -08:00
unsigned long flags_spin ;
2008-05-29 16:35:02 +08:00
struct iwl_rem_sta_cmd rm_sta_cmd ;
struct iwl_host_cmd cmd = {
. id = REPLY_REMOVE_STA ,
. len = sizeof ( struct iwl_rem_sta_cmd ) ,
2010-02-22 16:24:47 -08:00
. flags = CMD_SYNC ,
2008-05-29 16:35:02 +08:00
. data = & rm_sta_cmd ,
} ;
memset ( & rm_sta_cmd , 0 , sizeof ( rm_sta_cmd ) ) ;
rm_sta_cmd . num_sta = 1 ;
2010-02-22 16:24:47 -08:00
memcpy ( & rm_sta_cmd . addr , & station - > sta . sta . addr , ETH_ALEN ) ;
cmd . flags | = CMD_WANT_SKB ;
2008-05-29 16:35:02 +08:00
ret = iwl_send_cmd ( priv , & cmd ) ;
2010-02-22 16:24:47 -08:00
if ( ret )
2008-05-29 16:35:02 +08:00
return ret ;
2009-10-09 17:19:45 +08:00
pkt = ( struct iwl_rx_packet * ) cmd . reply_page ;
if ( pkt - > hdr . flags & IWL_CMD_FAILED_MSK ) {
2008-12-19 10:37:33 +08:00
IWL_ERR ( priv , " Bad return from REPLY_REMOVE_STA (0x%08X) \n " ,
2009-10-09 17:19:45 +08:00
pkt - > hdr . flags ) ;
2008-05-29 16:35:02 +08:00
ret = - EIO ;
}
if ( ! ret ) {
2009-10-09 17:19:45 +08:00
switch ( pkt - > u . rem_sta . status ) {
2008-05-29 16:35:02 +08:00
case REM_STA_SUCCESS_MSK :
2010-02-22 16:24:47 -08:00
spin_lock_irqsave ( & priv - > sta_lock , flags_spin ) ;
iwl_sta_ucode_deactivate ( priv , station - > sta . sta . sta_id ) ;
spin_unlock_irqrestore ( & priv - > sta_lock , flags_spin ) ;
2009-01-27 14:27:56 -08:00
IWL_DEBUG_ASSOC ( priv , " REPLY_REMOVE_STA PASSED \n " ) ;
2008-05-29 16:35:02 +08:00
break ;
default :
ret = - EIO ;
2008-12-19 10:37:33 +08:00
IWL_ERR ( priv , " REPLY_REMOVE_STA failed \n " ) ;
2008-05-29 16:35:02 +08:00
break ;
}
}
2009-12-10 14:37:21 -08:00
iwl_free_pages ( priv , cmd . reply_page ) ;
2008-05-29 16:35:02 +08:00
return ret ;
}
2008-06-12 09:47:18 +08:00
2008-05-29 16:35:02 +08:00
/**
* iwl_remove_station - Remove driver ' s knowledge of station .
*/
2010-02-22 16:24:47 -08:00
static int iwl_remove_station ( struct iwl_priv * priv , struct ieee80211_sta * sta )
2008-05-29 16:35:02 +08:00
{
2008-06-30 17:23:03 +08:00
int sta_id = IWL_INVALID_STATION ;
int i , ret = - EINVAL ;
2008-05-29 16:35:02 +08:00
unsigned long flags ;
2010-02-22 16:24:47 -08:00
bool is_ap = priv - > iw_mode = = NL80211_IFTYPE_STATION ;
struct iwl_station_entry * station ;
if ( ! iwl_is_ready ( priv ) ) {
IWL_DEBUG_INFO ( priv ,
2010-03-24 14:19:58 -07:00
" Unable to remove station %pM, device not ready. \n " ,
2010-02-22 16:24:47 -08:00
sta - > addr ) ;
/*
* It is typical for stations to be removed when we are
* going down . Return success since device will be down
* soon anyway
*/
return 0 ;
}
2008-05-29 16:35:02 +08:00
spin_lock_irqsave ( & priv - > sta_lock , flags ) ;
if ( is_ap )
2008-06-30 17:23:03 +08:00
sta_id = IWL_AP_ID ;
2008-05-29 16:35:02 +08:00
else
for ( i = IWL_STA_ID ; i < priv - > hw_params . max_stations ; i + + )
if ( priv - > stations [ i ] . used & &
! compare_ether_addr ( priv - > stations [ i ] . sta . sta . addr ,
2010-02-22 16:24:47 -08:00
sta - > addr ) ) {
2008-06-30 17:23:03 +08:00
sta_id = i ;
2008-05-29 16:35:02 +08:00
break ;
}
2008-06-30 17:23:03 +08:00
if ( unlikely ( sta_id = = IWL_INVALID_STATION ) )
2008-05-29 16:35:02 +08:00
goto out ;
2009-01-27 14:27:56 -08:00
IWL_DEBUG_ASSOC ( priv , " Removing STA from driver:%d %pM \n " ,
2010-02-22 16:24:47 -08:00
sta_id , sta - > addr ) ;
2008-06-30 17:23:03 +08:00
if ( ! ( priv - > stations [ sta_id ] . used & IWL_STA_DRIVER_ACTIVE ) ) {
2010-02-22 16:24:47 -08:00
IWL_DEBUG_INFO ( priv , " Removing %pM but non DRIVER active \n " ,
sta - > addr ) ;
2008-06-30 17:23:03 +08:00
goto out ;
}
if ( ! ( priv - > stations [ sta_id ] . used & IWL_STA_UCODE_ACTIVE ) ) {
2010-02-22 16:24:47 -08:00
IWL_DEBUG_INFO ( priv , " Removing %pM but non UCODE active \n " ,
sta - > addr ) ;
2008-06-30 17:23:03 +08:00
goto out ;
2008-05-29 16:35:02 +08:00
}
2008-06-30 17:23:03 +08:00
priv - > stations [ sta_id ] . used & = ~ IWL_STA_DRIVER_ACTIVE ;
priv - > num_stations - - ;
2008-05-29 16:35:02 +08:00
BUG_ON ( priv - > num_stations < 0 ) ;
2008-06-30 17:23:03 +08:00
2010-02-22 16:24:47 -08:00
station = & priv - > stations [ sta_id ] ;
2008-05-29 16:35:02 +08:00
spin_unlock_irqrestore ( & priv - > sta_lock , flags ) ;
2008-06-30 17:23:03 +08:00
2010-02-22 16:24:47 -08:00
ret = iwl_send_remove_station ( priv , station ) ;
2008-06-30 17:23:03 +08:00
return ret ;
2008-05-29 16:35:02 +08:00
out :
spin_unlock_irqrestore ( & priv - > sta_lock , flags ) ;
2008-06-30 17:23:03 +08:00
return ret ;
2008-05-29 16:35:02 +08:00
}
2008-06-30 17:23:03 +08:00
2008-11-19 15:32:23 -08:00
/**
2010-02-18 22:58:32 -08:00
* iwl_clear_ucode_stations ( ) - clear entire station table driver and / or ucode
* @ priv :
* @ force : If set then the uCode station table needs to be cleared here . If
* not set then the uCode station table has already been cleared ,
* for example after sending it a RXON command without ASSOC bit
* set , and we just need to change driver state here .
2008-11-19 15:32:23 -08:00
*/
2010-02-18 22:58:32 -08:00
void iwl_clear_ucode_stations ( struct iwl_priv * priv , bool force )
2008-11-19 15:32:23 -08:00
{
2009-03-11 11:17:59 -07:00
int i ;
2010-02-18 22:58:32 -08:00
unsigned long flags_spin ;
bool cleared = false ;
IWL_DEBUG_INFO ( priv , " Clearing ucode stations in driver%s \n " ,
force ? " and ucode " : " " ) ;
if ( force ) {
if ( ! iwl_is_ready ( priv ) ) {
/*
* If device is not ready at this point the station
* table is likely already empty ( uCode not ready
* to receive station requests ) or will soon be
* due to interface going down .
*/
IWL_DEBUG_INFO ( priv , " Unable to remove stations from device - device not ready \n " ) ;
} else {
iwl_send_cmd_pdu_async ( priv , REPLY_REMOVE_ALL_STA , 0 , NULL , NULL ) ;
}
}
2008-11-19 15:32:23 -08:00
2010-02-18 22:58:32 -08:00
spin_lock_irqsave ( & priv - > sta_lock , flags_spin ) ;
if ( force ) {
IWL_DEBUG_INFO ( priv , " Clearing all station information in driver \n " ) ;
priv - > num_stations = 0 ;
memset ( priv - > stations , 0 , sizeof ( priv - > stations ) ) ;
} else {
for ( i = 0 ; i < priv - > hw_params . max_stations ; i + + ) {
if ( priv - > stations [ i ] . used & IWL_STA_UCODE_ACTIVE ) {
2010-03-24 14:19:58 -07:00
IWL_DEBUG_INFO ( priv , " Clearing ucode active for station %d \n " , i ) ;
2010-02-18 22:58:32 -08:00
priv - > stations [ i ] . used & = ~ IWL_STA_UCODE_ACTIVE ;
cleared = true ;
}
}
}
spin_unlock_irqrestore ( & priv - > sta_lock , flags_spin ) ;
if ( ! cleared )
IWL_DEBUG_INFO ( priv , " No active stations found to be cleared \n " ) ;
}
EXPORT_SYMBOL ( iwl_clear_ucode_stations ) ;
2008-11-19 15:32:23 -08:00
2010-02-18 22:58:32 -08:00
/**
* iwl_restore_stations ( ) - Restore driver known stations to device
*
* All stations considered active by driver , but not present in ucode , is
* restored .
2010-02-22 16:24:47 -08:00
*
* Function sleeps .
2010-02-18 22:58:32 -08:00
*/
void iwl_restore_stations ( struct iwl_priv * priv )
{
2010-02-22 16:24:47 -08:00
struct iwl_station_entry * station ;
2010-02-18 22:58:32 -08:00
unsigned long flags_spin ;
int i ;
bool found = false ;
2010-02-22 16:24:47 -08:00
int ret ;
2008-11-19 15:32:23 -08:00
2010-02-18 22:58:32 -08:00
if ( ! iwl_is_ready ( priv ) ) {
IWL_DEBUG_INFO ( priv , " Not ready yet, not restoring any stations. \n " ) ;
return ;
}
2008-11-19 15:32:23 -08:00
2010-02-18 22:58:32 -08:00
IWL_DEBUG_ASSOC ( priv , " Restoring all known stations ... start. \n " ) ;
spin_lock_irqsave ( & priv - > sta_lock , flags_spin ) ;
for ( i = 0 ; i < priv - > hw_params . max_stations ; i + + ) {
if ( ( priv - > stations [ i ] . used & IWL_STA_DRIVER_ACTIVE ) & &
! ( priv - > stations [ i ] . used & IWL_STA_UCODE_ACTIVE ) ) {
IWL_DEBUG_ASSOC ( priv , " Restoring sta %pM \n " ,
priv - > stations [ i ] . sta . sta . addr ) ;
priv - > stations [ i ] . sta . mode = 0 ;
priv - > stations [ i ] . used | = IWL_STA_UCODE_INPROGRESS ;
found = true ;
}
}
2009-01-28 09:38:30 -08:00
2010-02-18 22:58:32 -08:00
for ( i = 0 ; i < priv - > hw_params . max_stations ; i + + ) {
if ( ( priv - > stations [ i ] . used & IWL_STA_UCODE_INPROGRESS ) ) {
2010-02-22 16:24:47 -08:00
spin_unlock_irqrestore ( & priv - > sta_lock , flags_spin ) ;
station = & priv - > stations [ i ] ;
ret = iwl_send_add_sta ( priv , & priv - > stations [ i ] . sta , CMD_SYNC ) ;
if ( ret ) {
IWL_ERR ( priv , " Adding station %pM failed. \n " ,
station - > sta . sta . addr ) ;
spin_lock_irqsave ( & priv - > sta_lock , flags_spin ) ;
priv - > stations [ i ] . used & = ~ IWL_STA_DRIVER_ACTIVE ;
priv - > stations [ i ] . used & = ~ IWL_STA_UCODE_INPROGRESS ;
spin_unlock_irqrestore ( & priv - > sta_lock , flags_spin ) ;
}
/*
* Rate scaling has already been initialized , send
* current LQ command
*/
if ( station - > lq )
iwl_send_lq_cmd ( priv , station - > lq , CMD_SYNC , true ) ;
spin_lock_irqsave ( & priv - > sta_lock , flags_spin ) ;
2010-02-18 22:58:32 -08:00
priv - > stations [ i ] . used & = ~ IWL_STA_UCODE_INPROGRESS ;
}
2009-03-11 11:17:59 -07:00
}
2010-02-18 22:58:32 -08:00
spin_unlock_irqrestore ( & priv - > sta_lock , flags_spin ) ;
if ( ! found )
IWL_DEBUG_INFO ( priv , " Restoring all known stations .... no stations to be restored. \n " ) ;
else
2010-02-22 16:24:47 -08:00
IWL_DEBUG_INFO ( priv , " Restoring all known stations .... complete. \n " ) ;
2008-11-19 15:32:23 -08:00
}
2010-02-18 22:58:32 -08:00
EXPORT_SYMBOL ( iwl_restore_stations ) ;
2008-11-19 15:32:23 -08:00
2009-02-27 16:21:21 -08:00
int iwl_get_free_ucode_key_index ( struct iwl_priv * priv )
2008-04-14 21:16:08 -07:00
{
int i ;
for ( i = 0 ; i < STA_KEY_MAX_NUM ; i + + )
2008-04-15 16:01:44 -07:00
if ( ! test_and_set_bit ( i , & priv - > ucode_key_table ) )
2008-04-14 21:16:08 -07:00
return i ;
2008-11-25 23:29:03 +02:00
return WEP_INVALID_OFFSET ;
2008-04-14 21:16:08 -07:00
}
2009-02-27 16:21:21 -08:00
EXPORT_SYMBOL ( iwl_get_free_ucode_key_index ) ;
2008-04-14 21:16:06 -07:00
2010-03-30 10:11:46 -07:00
static int iwl_send_static_wepkey_cmd ( struct iwl_priv * priv , u8 send_if_empty )
2008-04-14 21:16:06 -07:00
{
int i , not_empty = 0 ;
u8 buff [ sizeof ( struct iwl_wep_cmd ) +
sizeof ( struct iwl_wep_key ) * WEP_KEYS_MAX ] ;
struct iwl_wep_cmd * wep_cmd = ( struct iwl_wep_cmd * ) buff ;
size_t cmd_size = sizeof ( struct iwl_wep_cmd ) ;
struct iwl_host_cmd cmd = {
. id = REPLY_WEPKEY ,
. data = wep_cmd ,
2010-02-19 11:42:32 -08:00
. flags = CMD_SYNC ,
2008-04-14 21:16:06 -07:00
} ;
2010-02-19 11:42:32 -08:00
might_sleep ( ) ;
2008-04-14 21:16:06 -07:00
memset ( wep_cmd , 0 , cmd_size +
( sizeof ( struct iwl_wep_key ) * WEP_KEYS_MAX ) ) ;
for ( i = 0 ; i < WEP_KEYS_MAX ; i + + ) {
wep_cmd - > key [ i ] . key_index = i ;
if ( priv - > wep_keys [ i ] . key_size ) {
wep_cmd - > key [ i ] . key_offset = i ;
not_empty = 1 ;
} else {
wep_cmd - > key [ i ] . key_offset = WEP_INVALID_OFFSET ;
}
wep_cmd - > key [ i ] . key_size = priv - > wep_keys [ i ] . key_size ;
memcpy ( & wep_cmd - > key [ i ] . key [ 3 ] , priv - > wep_keys [ i ] . key ,
priv - > wep_keys [ i ] . key_size ) ;
}
wep_cmd - > global_key_type = WEP_KEY_WEP_TYPE ;
wep_cmd - > num_keys = WEP_KEYS_MAX ;
cmd_size + = sizeof ( struct iwl_wep_key ) * WEP_KEYS_MAX ;
cmd . len = cmd_size ;
if ( not_empty | | send_if_empty )
return iwl_send_cmd ( priv , & cmd ) ;
else
return 0 ;
}
2010-03-30 10:11:46 -07:00
int iwl_restore_default_wep_keys ( struct iwl_priv * priv )
{
WARN_ON ( ! mutex_is_locked ( & priv - > mutex ) ) ;
return iwl_send_static_wepkey_cmd ( priv , 0 ) ;
}
EXPORT_SYMBOL ( iwl_restore_default_wep_keys ) ;
2008-04-14 21:16:06 -07:00
int iwl_remove_default_wep_key ( struct iwl_priv * priv ,
2008-04-14 21:16:08 -07:00
struct ieee80211_key_conf * keyconf )
2008-04-14 21:16:06 -07:00
{
int ret ;
2010-02-19 11:42:32 -08:00
WARN_ON ( ! mutex_is_locked ( & priv - > mutex ) ) ;
2009-07-17 09:30:18 -07:00
IWL_DEBUG_WEP ( priv , " Removing default WEP key: idx=%d \n " ,
keyconf - > keyidx ) ;
2008-04-14 21:16:08 -07:00
memset ( & priv - > wep_keys [ keyconf - > keyidx ] , 0 , sizeof ( priv - > wep_keys [ 0 ] ) ) ;
2009-07-17 09:30:18 -07:00
if ( iwl_is_rfkill ( priv ) ) {
IWL_DEBUG_WEP ( priv , " Not sending REPLY_WEPKEY command due to RFKILL. \n " ) ;
2010-02-19 11:42:32 -08:00
/* but keys in device are clear anyway so return success */
2009-07-17 09:30:18 -07:00
return 0 ;
}
2008-04-14 21:16:06 -07:00
ret = iwl_send_static_wepkey_cmd ( priv , 1 ) ;
2009-01-27 14:27:56 -08:00
IWL_DEBUG_WEP ( priv , " Remove default WEP key: idx=%d ret=%d \n " ,
2008-06-12 09:47:09 +08:00
keyconf - > keyidx , ret ) ;
2008-04-14 21:16:06 -07:00
return ret ;
}
2008-05-05 10:22:44 +08:00
EXPORT_SYMBOL ( iwl_remove_default_wep_key ) ;
2008-04-14 21:16:06 -07:00
int iwl_set_default_wep_key ( struct iwl_priv * priv ,
struct ieee80211_key_conf * keyconf )
{
int ret ;
2010-02-19 11:42:32 -08:00
WARN_ON ( ! mutex_is_locked ( & priv - > mutex ) ) ;
2008-04-14 21:16:06 -07:00
2008-06-12 09:47:09 +08:00
if ( keyconf - > keylen ! = WEP_KEY_LEN_128 & &
keyconf - > keylen ! = WEP_KEY_LEN_64 ) {
2009-01-27 14:27:56 -08:00
IWL_DEBUG_WEP ( priv , " Bad WEP key length %d \n " , keyconf - > keylen ) ;
2008-06-12 09:47:09 +08:00
return - EINVAL ;
}
2008-04-14 21:16:06 -07:00
keyconf - > flags & = ~ IEEE80211_KEY_FLAG_GENERATE_IV ;
2008-05-15 13:54:09 +08:00
keyconf - > hw_key_idx = HW_KEY_DEFAULT ;
2008-04-14 21:16:06 -07:00
priv - > stations [ IWL_AP_ID ] . keyinfo . alg = ALG_WEP ;
priv - > wep_keys [ keyconf - > keyidx ] . key_size = keyconf - > keylen ;
memcpy ( & priv - > wep_keys [ keyconf - > keyidx ] . key , & keyconf - > key ,
keyconf - > keylen ) ;
ret = iwl_send_static_wepkey_cmd ( priv , 0 ) ;
2009-01-27 14:27:56 -08:00
IWL_DEBUG_WEP ( priv , " Set default WEP key: len=%d idx=%d ret=%d \n " ,
2008-06-12 09:47:09 +08:00
keyconf - > keylen , keyconf - > keyidx , ret ) ;
2008-04-14 21:16:06 -07:00
return ret ;
}
2008-05-05 10:22:44 +08:00
EXPORT_SYMBOL ( iwl_set_default_wep_key ) ;
2008-04-14 21:16:06 -07:00
2008-04-14 21:16:09 -07:00
static int iwl_set_wep_dynamic_key_info ( struct iwl_priv * priv ,
2008-04-14 21:16:07 -07:00
struct ieee80211_key_conf * keyconf ,
u8 sta_id )
{
unsigned long flags ;
__le16 key_flags = 0 ;
int ret ;
keyconf - > flags & = ~ IEEE80211_KEY_FLAG_GENERATE_IV ;
key_flags | = ( STA_KEY_FLG_WEP | STA_KEY_FLG_MAP_KEY_MSK ) ;
key_flags | = cpu_to_le16 ( keyconf - > keyidx < < STA_KEY_FLG_KEYID_POS ) ;
key_flags & = ~ STA_KEY_FLG_INVALID ;
if ( keyconf - > keylen = = WEP_KEY_LEN_128 )
key_flags | = STA_KEY_FLG_KEY_SIZE_MSK ;
2008-04-15 16:01:38 -07:00
if ( sta_id = = priv - > hw_params . bcast_sta_id )
2008-04-14 21:16:07 -07:00
key_flags | = STA_KEY_MULTICAST_MSK ;
spin_lock_irqsave ( & priv - > sta_lock , flags ) ;
priv - > stations [ sta_id ] . keyinfo . alg = keyconf - > alg ;
priv - > stations [ sta_id ] . keyinfo . keylen = keyconf - > keylen ;
priv - > stations [ sta_id ] . keyinfo . keyidx = keyconf - > keyidx ;
memcpy ( priv - > stations [ sta_id ] . keyinfo . key ,
keyconf - > key , keyconf - > keylen ) ;
memcpy ( & priv - > stations [ sta_id ] . sta . key . key [ 3 ] ,
keyconf - > key , keyconf - > keylen ) ;
2008-04-17 16:03:36 -07:00
if ( ( priv - > stations [ sta_id ] . sta . key . key_flags & STA_KEY_FLG_ENCRYPT_MSK )
= = STA_KEY_FLG_NO_ENC )
priv - > stations [ sta_id ] . sta . key . key_offset =
2008-04-14 21:16:08 -07:00
iwl_get_free_ucode_key_index ( priv ) ;
2008-04-17 16:03:36 -07:00
/* else, we are overriding an existing key => no need to allocated room
* in uCode . */
2008-04-14 21:16:07 -07:00
2008-11-25 23:29:03 +02:00
WARN ( priv - > stations [ sta_id ] . sta . key . key_offset = = WEP_INVALID_OFFSET ,
2009-01-05 17:06:06 +01:00
" no space for a new key " ) ;
2008-11-25 23:29:03 +02:00
2008-04-17 16:03:36 -07:00
priv - > stations [ sta_id ] . sta . key . key_flags = key_flags ;
2008-04-14 21:16:07 -07:00
priv - > stations [ sta_id ] . sta . sta . modify_mask = STA_MODIFY_KEY_MASK ;
priv - > stations [ sta_id ] . sta . mode = STA_CONTROL_MODIFY_MSK ;
2008-05-05 10:22:34 +08:00
ret = iwl_send_add_sta ( priv , & priv - > stations [ sta_id ] . sta , CMD_ASYNC ) ;
2008-04-14 21:16:07 -07:00
spin_unlock_irqrestore ( & priv - > sta_lock , flags ) ;
return ret ;
}
2008-04-14 21:16:09 -07:00
static int iwl_set_ccmp_dynamic_key_info ( struct iwl_priv * priv ,
struct ieee80211_key_conf * keyconf ,
u8 sta_id )
{
unsigned long flags ;
__le16 key_flags = 0 ;
2008-11-25 23:29:03 +02:00
int ret ;
2008-04-14 21:16:09 -07:00
key_flags | = ( STA_KEY_FLG_CCMP | STA_KEY_FLG_MAP_KEY_MSK ) ;
key_flags | = cpu_to_le16 ( keyconf - > keyidx < < STA_KEY_FLG_KEYID_POS ) ;
key_flags & = ~ STA_KEY_FLG_INVALID ;
2008-04-15 16:01:38 -07:00
if ( sta_id = = priv - > hw_params . bcast_sta_id )
2008-04-14 21:16:09 -07:00
key_flags | = STA_KEY_MULTICAST_MSK ;
keyconf - > flags | = IEEE80211_KEY_FLAG_GENERATE_IV ;
spin_lock_irqsave ( & priv - > sta_lock , flags ) ;
priv - > stations [ sta_id ] . keyinfo . alg = keyconf - > alg ;
priv - > stations [ sta_id ] . keyinfo . keylen = keyconf - > keylen ;
memcpy ( priv - > stations [ sta_id ] . keyinfo . key , keyconf - > key ,
keyconf - > keylen ) ;
memcpy ( priv - > stations [ sta_id ] . sta . key . key , keyconf - > key ,
keyconf - > keylen ) ;
2008-04-17 16:03:36 -07:00
if ( ( priv - > stations [ sta_id ] . sta . key . key_flags & STA_KEY_FLG_ENCRYPT_MSK )
= = STA_KEY_FLG_NO_ENC )
priv - > stations [ sta_id ] . sta . key . key_offset =
iwl_get_free_ucode_key_index ( priv ) ;
/* else, we are overriding an existing key => no need to allocated room
* in uCode . */
2008-11-25 23:29:03 +02:00
WARN ( priv - > stations [ sta_id ] . sta . key . key_offset = = WEP_INVALID_OFFSET ,
2009-01-05 17:06:06 +01:00
" no space for a new key " ) ;
2008-11-25 23:29:03 +02:00
2008-04-14 21:16:09 -07:00
priv - > stations [ sta_id ] . sta . key . key_flags = key_flags ;
priv - > stations [ sta_id ] . sta . sta . modify_mask = STA_MODIFY_KEY_MASK ;
priv - > stations [ sta_id ] . sta . mode = STA_CONTROL_MODIFY_MSK ;
2008-11-25 23:29:03 +02:00
ret = iwl_send_add_sta ( priv , & priv - > stations [ sta_id ] . sta , CMD_ASYNC ) ;
2008-04-14 21:16:09 -07:00
spin_unlock_irqrestore ( & priv - > sta_lock , flags ) ;
2008-11-25 23:29:03 +02:00
return ret ;
2008-04-14 21:16:09 -07:00
}
static int iwl_set_tkip_dynamic_key_info ( struct iwl_priv * priv ,
struct ieee80211_key_conf * keyconf ,
u8 sta_id )
{
unsigned long flags ;
int ret = 0 ;
2009-04-30 13:56:31 -07:00
__le16 key_flags = 0 ;
key_flags | = ( STA_KEY_FLG_TKIP | STA_KEY_FLG_MAP_KEY_MSK ) ;
key_flags | = cpu_to_le16 ( keyconf - > keyidx < < STA_KEY_FLG_KEYID_POS ) ;
key_flags & = ~ STA_KEY_FLG_INVALID ;
if ( sta_id = = priv - > hw_params . bcast_sta_id )
key_flags | = STA_KEY_MULTICAST_MSK ;
2008-04-14 21:16:09 -07:00
keyconf - > flags | = IEEE80211_KEY_FLAG_GENERATE_IV ;
keyconf - > flags | = IEEE80211_KEY_FLAG_GENERATE_MMIC ;
spin_lock_irqsave ( & priv - > sta_lock , flags ) ;
priv - > stations [ sta_id ] . keyinfo . alg = keyconf - > alg ;
priv - > stations [ sta_id ] . keyinfo . keylen = 16 ;
2008-04-17 16:03:36 -07:00
if ( ( priv - > stations [ sta_id ] . sta . key . key_flags & STA_KEY_FLG_ENCRYPT_MSK )
= = STA_KEY_FLG_NO_ENC )
priv - > stations [ sta_id ] . sta . key . key_offset =
2008-04-15 16:01:44 -07:00
iwl_get_free_ucode_key_index ( priv ) ;
2008-04-17 16:03:36 -07:00
/* else, we are overriding an existing key => no need to allocated room
* in uCode . */
2008-04-14 21:16:09 -07:00
2008-11-25 23:29:03 +02:00
WARN ( priv - > stations [ sta_id ] . sta . key . key_offset = = WEP_INVALID_OFFSET ,
2009-01-05 17:06:06 +01:00
" no space for a new key " ) ;
2008-11-25 23:29:03 +02:00
2009-04-30 13:56:31 -07:00
priv - > stations [ sta_id ] . sta . key . key_flags = key_flags ;
2008-04-14 21:16:09 -07:00
/* This copy is acutally not needed: we get the key with each TX */
memcpy ( priv - > stations [ sta_id ] . keyinfo . key , keyconf - > key , 16 ) ;
memcpy ( priv - > stations [ sta_id ] . sta . key . key , keyconf - > key , 16 ) ;
spin_unlock_irqrestore ( & priv - > sta_lock , flags ) ;
return ret ;
}
2008-11-12 13:14:05 -08:00
void iwl_update_tkip_key ( struct iwl_priv * priv ,
struct ieee80211_key_conf * keyconf ,
const u8 * addr , u32 iv32 , u16 * phase1key )
{
u8 sta_id = IWL_INVALID_STATION ;
unsigned long flags ;
int i ;
2009-06-03 11:44:07 -07:00
sta_id = iwl_find_station ( priv , addr ) ;
2008-11-12 13:14:05 -08:00
if ( sta_id = = IWL_INVALID_STATION ) {
2009-01-27 14:27:56 -08:00
IWL_DEBUG_MAC80211 ( priv , " leave - %pM not in station map. \n " ,
2008-11-12 13:14:05 -08:00
addr ) ;
return ;
}
if ( iwl_scan_cancel ( priv ) ) {
/* cancel scan failed, just live w/ bad key and rely
briefly on SW decryption */
return ;
}
spin_lock_irqsave ( & priv - > sta_lock , flags ) ;
priv - > stations [ sta_id ] . sta . key . tkip_rx_tsc_byte2 = ( u8 ) iv32 ;
for ( i = 0 ; i < 5 ; i + + )
priv - > stations [ sta_id ] . sta . key . tkip_rx_ttak [ i ] =
cpu_to_le16 ( phase1key [ i ] ) ;
priv - > stations [ sta_id ] . sta . sta . modify_mask = STA_MODIFY_KEY_MASK ;
priv - > stations [ sta_id ] . sta . mode = STA_CONTROL_MODIFY_MSK ;
iwl_send_add_sta ( priv , & priv - > stations [ sta_id ] . sta , CMD_ASYNC ) ;
spin_unlock_irqrestore ( & priv - > sta_lock , flags ) ;
}
EXPORT_SYMBOL ( iwl_update_tkip_key ) ;
2008-04-17 16:03:36 -07:00
int iwl_remove_dynamic_key ( struct iwl_priv * priv ,
struct ieee80211_key_conf * keyconf ,
u8 sta_id )
2008-04-14 21:16:09 -07:00
{
unsigned long flags ;
2008-04-17 16:03:36 -07:00
int ret = 0 ;
u16 key_flags ;
u8 keyidx ;
2008-04-14 21:16:09 -07:00
2008-05-15 13:54:09 +08:00
priv - > key_mapping_key - - ;
2008-04-14 21:16:09 -07:00
spin_lock_irqsave ( & priv - > sta_lock , flags ) ;
2008-04-17 16:03:36 -07:00
key_flags = le16_to_cpu ( priv - > stations [ sta_id ] . sta . key . key_flags ) ;
keyidx = ( key_flags > > STA_KEY_FLG_KEYID_POS ) & 0x3 ;
2009-01-27 14:27:56 -08:00
IWL_DEBUG_WEP ( priv , " Remove dynamic key: idx=%d sta=%d \n " ,
2008-06-12 09:47:09 +08:00
keyconf - > keyidx , sta_id ) ;
2008-04-17 16:03:36 -07:00
if ( keyconf - > keyidx ! = keyidx ) {
/* We need to remove a key with index different that the one
* in the uCode . This means that the key we need to remove has
* been replaced by another one with different index .
* Don ' t do anything and return ok
*/
spin_unlock_irqrestore ( & priv - > sta_lock , flags ) ;
return 0 ;
}
2008-11-25 23:29:03 +02:00
if ( priv - > stations [ sta_id ] . sta . key . key_offset = = WEP_INVALID_OFFSET ) {
2008-12-19 10:37:32 +08:00
IWL_WARN ( priv , " Removing wrong key %d 0x%x \n " ,
2008-11-25 23:29:03 +02:00
keyconf - > keyidx , key_flags ) ;
spin_unlock_irqrestore ( & priv - > sta_lock , flags ) ;
return 0 ;
}
2008-04-14 21:16:09 -07:00
if ( ! test_and_clear_bit ( priv - > stations [ sta_id ] . sta . key . key_offset ,
& priv - > ucode_key_table ) )
2008-12-19 10:37:33 +08:00
IWL_ERR ( priv , " index %d not used in uCode key table. \n " ,
2008-04-14 21:16:09 -07:00
priv - > stations [ sta_id ] . sta . key . key_offset ) ;
memset ( & priv - > stations [ sta_id ] . keyinfo , 0 ,
2008-05-05 10:22:31 +08:00
sizeof ( struct iwl_hw_key ) ) ;
2008-04-14 21:16:09 -07:00
memset ( & priv - > stations [ sta_id ] . sta . key , 0 ,
sizeof ( struct iwl4965_keyinfo ) ) ;
2008-04-17 16:03:36 -07:00
priv - > stations [ sta_id ] . sta . key . key_flags =
STA_KEY_FLG_NO_ENC | STA_KEY_FLG_INVALID ;
priv - > stations [ sta_id ] . sta . key . key_offset = WEP_INVALID_OFFSET ;
2008-04-14 21:16:09 -07:00
priv - > stations [ sta_id ] . sta . sta . modify_mask = STA_MODIFY_KEY_MASK ;
priv - > stations [ sta_id ] . sta . mode = STA_CONTROL_MODIFY_MSK ;
2009-07-17 09:30:18 -07:00
if ( iwl_is_rfkill ( priv ) ) {
2010-03-24 14:19:58 -07:00
IWL_DEBUG_WEP ( priv , " Not sending REPLY_ADD_STA command because RFKILL enabled. \n " ) ;
2009-07-17 09:30:18 -07:00
spin_unlock_irqrestore ( & priv - > sta_lock , flags ) ;
return 0 ;
}
2008-05-15 13:54:09 +08:00
ret = iwl_send_add_sta ( priv , & priv - > stations [ sta_id ] . sta , CMD_ASYNC ) ;
2008-04-17 16:03:36 -07:00
spin_unlock_irqrestore ( & priv - > sta_lock , flags ) ;
return ret ;
2008-04-14 21:16:09 -07:00
}
2008-05-05 10:22:44 +08:00
EXPORT_SYMBOL ( iwl_remove_dynamic_key ) ;
2008-04-14 21:16:09 -07:00
int iwl_set_dynamic_key ( struct iwl_priv * priv ,
2008-05-15 13:54:09 +08:00
struct ieee80211_key_conf * keyconf , u8 sta_id )
2008-04-14 21:16:09 -07:00
{
int ret ;
2008-05-15 13:54:09 +08:00
priv - > key_mapping_key + + ;
keyconf - > hw_key_idx = HW_KEY_DYNAMIC ;
2008-04-14 21:16:09 -07:00
2008-05-15 13:54:09 +08:00
switch ( keyconf - > alg ) {
2008-04-14 21:16:09 -07:00
case ALG_CCMP :
2008-05-15 13:54:09 +08:00
ret = iwl_set_ccmp_dynamic_key_info ( priv , keyconf , sta_id ) ;
2008-04-14 21:16:09 -07:00
break ;
case ALG_TKIP :
2008-05-15 13:54:09 +08:00
ret = iwl_set_tkip_dynamic_key_info ( priv , keyconf , sta_id ) ;
2008-04-14 21:16:09 -07:00
break ;
case ALG_WEP :
2008-05-15 13:54:09 +08:00
ret = iwl_set_wep_dynamic_key_info ( priv , keyconf , sta_id ) ;
2008-04-14 21:16:09 -07:00
break ;
default :
2008-12-19 10:37:33 +08:00
IWL_ERR ( priv ,
" Unknown alg: %s alg = %d \n " , __func__ , keyconf - > alg ) ;
2008-04-14 21:16:09 -07:00
ret = - EINVAL ;
}
2009-01-27 14:27:56 -08:00
IWL_DEBUG_WEP ( priv , " Set dynamic key: alg= %d len=%d idx=%d sta=%d ret=%d \n " ,
2008-06-12 09:47:09 +08:00
keyconf - > alg , keyconf - > keylen , keyconf - > keyidx ,
sta_id , ret ) ;
2008-04-14 21:16:09 -07:00
return ret ;
}
2008-05-05 10:22:44 +08:00
EXPORT_SYMBOL ( iwl_set_dynamic_key ) ;
2008-04-14 21:16:09 -07:00
2008-04-15 16:01:40 -07:00
# ifdef CONFIG_IWLWIFI_DEBUG
static void iwl_dump_lq_cmd ( struct iwl_priv * priv ,
struct iwl_link_quality_cmd * lq )
{
int i ;
2009-01-27 14:27:56 -08:00
IWL_DEBUG_RATE ( priv , " lq station id 0x%x \n " , lq - > sta_id ) ;
IWL_DEBUG_RATE ( priv , " lq ant 0x%X 0x%X \n " ,
2008-04-15 16:01:40 -07:00
lq - > general_params . single_stream_ant_msk ,
lq - > general_params . dual_stream_ant_msk ) ;
for ( i = 0 ; i < LINK_QUAL_MAX_RETRY_NUM ; i + + )
2009-01-27 14:27:56 -08:00
IWL_DEBUG_RATE ( priv , " lq index %d 0x%X \n " ,
2008-04-15 16:01:40 -07:00
i , lq - > rs_table [ i ] . rate_n_flags ) ;
}
# else
static inline void iwl_dump_lq_cmd ( struct iwl_priv * priv ,
struct iwl_link_quality_cmd * lq )
{
}
# endif
2010-02-22 16:24:47 -08:00
/**
* iwl_send_lq_cmd ( ) - Send link quality command
* @ init : This command is sent as part of station initialization right
* after station has been added .
*
* The link quality command is sent as the last step of station creation .
* This is the special case in which init is set and we call a callback in
* this case to clear the state indicating that station creation is in
* progress .
*/
2008-04-15 16:01:40 -07:00
int iwl_send_lq_cmd ( struct iwl_priv * priv ,
2010-02-22 16:24:47 -08:00
struct iwl_link_quality_cmd * lq , u8 flags , bool init )
2008-04-15 16:01:40 -07:00
{
2010-02-22 16:24:47 -08:00
int ret = 0 ;
unsigned long flags_spin ;
2008-04-15 16:01:40 -07:00
struct iwl_host_cmd cmd = {
. id = REPLY_TX_LINK_QUALITY_CMD ,
. len = sizeof ( struct iwl_link_quality_cmd ) ,
2009-07-24 11:13:05 -07:00
. flags = flags ,
2008-04-15 16:01:40 -07:00
. data = lq ,
} ;
2010-03-30 02:46:29 -07:00
if ( WARN_ON ( lq - > sta_id = = IWL_INVALID_STATION ) )
2008-04-15 16:01:40 -07:00
return - EINVAL ;
2008-07-21 02:40:14 +03:00
iwl_dump_lq_cmd ( priv , lq ) ;
2010-02-22 16:24:47 -08:00
BUG_ON ( init & & ( cmd . flags & CMD_ASYNC ) ) ;
2008-04-15 16:01:40 -07:00
2010-02-22 16:24:47 -08:00
iwl_dump_lq_cmd ( priv , lq ) ;
ret = iwl_send_cmd ( priv , & cmd ) ;
if ( ret | | ( cmd . flags & CMD_ASYNC ) )
return ret ;
2008-04-15 16:01:40 -07:00
2010-02-22 16:24:47 -08:00
if ( init ) {
2010-03-24 14:19:58 -07:00
IWL_DEBUG_INFO ( priv , " init LQ command complete, clearing sta addition status for sta %d \n " ,
2010-02-22 16:24:47 -08:00
lq - > sta_id ) ;
spin_lock_irqsave ( & priv - > sta_lock , flags_spin ) ;
priv - > stations [ lq - > sta_id ] . used & = ~ IWL_STA_UCODE_INPROGRESS ;
spin_unlock_irqrestore ( & priv - > sta_lock , flags_spin ) ;
}
2008-04-15 16:01:40 -07:00
return 0 ;
}
EXPORT_SYMBOL ( iwl_send_lq_cmd ) ;
2009-10-30 14:36:12 -07:00
/**
* iwl_add_bcast_station - add broadcast station into station table .
*/
void iwl_add_bcast_station ( struct iwl_priv * priv )
{
2010-01-22 14:22:49 -08:00
IWL_DEBUG_INFO ( priv , " Adding broadcast station to station table \n " ) ;
2010-02-22 16:24:47 -08:00
iwl_add_local_station ( priv , iwl_bcast_addr , true ) ;
2009-10-30 14:36:12 -07:00
}
EXPORT_SYMBOL ( iwl_add_bcast_station ) ;
2010-01-22 14:22:49 -08:00
/**
* iwl3945_add_bcast_station - add broadcast station into station table .
*/
void iwl3945_add_bcast_station ( struct iwl_priv * priv )
{
IWL_DEBUG_INFO ( priv , " Adding broadcast station to station table \n " ) ;
2010-02-22 16:24:47 -08:00
iwl_add_local_station ( priv , iwl_bcast_addr , false ) ;
/*
* It is assumed that when station is added more initialization
* needs to be done , but for 3945 it is not the case and we can
* just release station table access right here .
*/
priv - > stations [ priv - > hw_params . bcast_sta_id ] . used & = ~ IWL_STA_UCODE_INPROGRESS ;
2010-01-22 14:22:49 -08:00
}
EXPORT_SYMBOL ( iwl3945_add_bcast_station ) ;
2008-05-15 13:54:04 +08:00
/**
* iwl_get_sta_id - Find station ' s index within station table
*
* If new IBSS station , create new entry in station table
*/
int iwl_get_sta_id ( struct iwl_priv * priv , struct ieee80211_hdr * hdr )
{
int sta_id ;
2009-07-14 20:14:07 -04:00
__le16 fc = hdr - > frame_control ;
2008-05-15 13:54:04 +08:00
/* If this frame is broadcast or management, use broadcast station id */
2009-07-14 20:14:07 -04:00
if ( ! ieee80211_is_data ( fc ) | | is_multicast_ether_addr ( hdr - > addr1 ) )
2008-05-15 13:54:04 +08:00
return priv - > hw_params . bcast_sta_id ;
switch ( priv - > iw_mode ) {
/* If we are a client station in a BSS network, use the special
* AP station entry ( that ' s the only station we communicate with ) */
2008-09-11 00:01:58 +02:00
case NL80211_IFTYPE_STATION :
2010-02-22 16:24:47 -08:00
/*
* If addition of station not complete yet , which means
* that rate scaling has not been initialized , then return
* the broadcast station .
*/
if ( ! ( priv - > stations [ IWL_AP_ID ] . used & IWL_STA_UCODE_ACTIVE ) )
return priv - > hw_params . bcast_sta_id ;
2008-05-15 13:54:04 +08:00
return IWL_AP_ID ;
/* If we are an AP, then find the station, or use BCAST */
2008-09-11 00:01:58 +02:00
case NL80211_IFTYPE_AP :
2009-06-03 11:44:07 -07:00
sta_id = iwl_find_station ( priv , hdr - > addr1 ) ;
2008-05-15 13:54:04 +08:00
if ( sta_id ! = IWL_INVALID_STATION )
return sta_id ;
return priv - > hw_params . bcast_sta_id ;
/* If this frame is going out to an IBSS network, find the station,
* or create a new station table entry */
2008-09-11 00:01:58 +02:00
case NL80211_IFTYPE_ADHOC :
2009-06-03 11:44:07 -07:00
sta_id = iwl_find_station ( priv , hdr - > addr1 ) ;
2008-05-15 13:54:04 +08:00
if ( sta_id ! = IWL_INVALID_STATION )
return sta_id ;
2009-01-27 14:27:56 -08:00
IWL_DEBUG_DROP ( priv , " Station %pM not in station map. "
2008-05-15 13:54:04 +08:00
" Defaulting to broadcast... \n " ,
2008-10-27 15:59:26 -07:00
hdr - > addr1 ) ;
2009-08-07 15:41:37 -07:00
iwl_print_hex_dump ( priv , IWL_DL_DROP , ( u8 * ) hdr , sizeof ( * hdr ) ) ;
2008-05-15 13:54:04 +08:00
return priv - > hw_params . bcast_sta_id ;
default :
2008-12-19 10:37:32 +08:00
IWL_WARN ( priv , " Unknown mode of operation: %d \n " ,
priv - > iw_mode ) ;
2008-05-15 13:54:04 +08:00
return priv - > hw_params . bcast_sta_id ;
}
}
EXPORT_SYMBOL ( iwl_get_sta_id ) ;
2008-05-29 16:35:15 +08:00
/**
2008-11-12 13:14:05 -08:00
* iwl_sta_tx_modify_enable_tid - Enable Tx for this TID in station table
2008-05-29 16:35:15 +08:00
*/
2008-11-12 13:14:05 -08:00
void iwl_sta_tx_modify_enable_tid ( struct iwl_priv * priv , int sta_id , int tid )
2008-05-29 16:35:15 +08:00
{
unsigned long flags ;
/* Remove "disable" flag, to enable Tx for this TID */
spin_lock_irqsave ( & priv - > sta_lock , flags ) ;
priv - > stations [ sta_id ] . sta . sta . modify_mask = STA_MODIFY_TID_DISABLE_TX ;
priv - > stations [ sta_id ] . sta . tid_disable_tx & = cpu_to_le16 ( ~ ( 1 < < tid ) ) ;
priv - > stations [ sta_id ] . sta . mode = STA_CONTROL_MODIFY_MSK ;
spin_unlock_irqrestore ( & priv - > sta_lock , flags ) ;
iwl_send_add_sta ( priv , & priv - > stations [ sta_id ] . sta , CMD_ASYNC ) ;
}
2008-11-12 13:14:05 -08:00
EXPORT_SYMBOL ( iwl_sta_tx_modify_enable_tid ) ;
int iwl_sta_rx_agg_start ( struct iwl_priv * priv ,
const u8 * addr , int tid , u16 ssn )
{
unsigned long flags ;
int sta_id ;
2009-06-03 11:44:07 -07:00
sta_id = iwl_find_station ( priv , addr ) ;
2008-11-12 13:14:05 -08:00
if ( sta_id = = IWL_INVALID_STATION )
return - ENXIO ;
spin_lock_irqsave ( & priv - > sta_lock , flags ) ;
priv - > stations [ sta_id ] . sta . station_flags_msk = 0 ;
priv - > stations [ sta_id ] . sta . sta . modify_mask = STA_MODIFY_ADDBA_TID_MSK ;
priv - > stations [ sta_id ] . sta . add_immediate_ba_tid = ( u8 ) tid ;
priv - > stations [ sta_id ] . sta . add_immediate_ba_ssn = cpu_to_le16 ( ssn ) ;
priv - > stations [ sta_id ] . sta . mode = STA_CONTROL_MODIFY_MSK ;
spin_unlock_irqrestore ( & priv - > sta_lock , flags ) ;
return iwl_send_add_sta ( priv , & priv - > stations [ sta_id ] . sta ,
CMD_ASYNC ) ;
}
EXPORT_SYMBOL ( iwl_sta_rx_agg_start ) ;
int iwl_sta_rx_agg_stop ( struct iwl_priv * priv , const u8 * addr , int tid )
{
unsigned long flags ;
int sta_id ;
2009-06-03 11:44:07 -07:00
sta_id = iwl_find_station ( priv , addr ) ;
2009-03-17 21:51:52 -07:00
if ( sta_id = = IWL_INVALID_STATION ) {
IWL_ERR ( priv , " Invalid station for AGG tid %d \n " , tid ) ;
2008-11-12 13:14:05 -08:00
return - ENXIO ;
2009-03-17 21:51:52 -07:00
}
2008-11-12 13:14:05 -08:00
spin_lock_irqsave ( & priv - > sta_lock , flags ) ;
priv - > stations [ sta_id ] . sta . station_flags_msk = 0 ;
priv - > stations [ sta_id ] . sta . sta . modify_mask = STA_MODIFY_DELBA_TID_MSK ;
priv - > stations [ sta_id ] . sta . remove_immediate_ba_tid = ( u8 ) tid ;
priv - > stations [ sta_id ] . sta . mode = STA_CONTROL_MODIFY_MSK ;
spin_unlock_irqrestore ( & priv - > sta_lock , flags ) ;
return iwl_send_add_sta ( priv , & priv - > stations [ sta_id ] . sta ,
CMD_ASYNC ) ;
}
EXPORT_SYMBOL ( iwl_sta_rx_agg_stop ) ;
2009-11-13 11:56:37 -08:00
void iwl_sta_modify_ps_wake ( struct iwl_priv * priv , int sta_id )
2008-11-12 13:14:05 -08:00
{
unsigned long flags ;
spin_lock_irqsave ( & priv - > sta_lock , flags ) ;
priv - > stations [ sta_id ] . sta . station_flags & = ~ STA_FLG_PWR_SAVE_MSK ;
priv - > stations [ sta_id ] . sta . station_flags_msk = STA_FLG_PWR_SAVE_MSK ;
priv - > stations [ sta_id ] . sta . sta . modify_mask = 0 ;
2009-11-13 11:56:37 -08:00
priv - > stations [ sta_id ] . sta . sleep_tx_count = 0 ;
2008-11-12 13:14:05 -08:00
priv - > stations [ sta_id ] . sta . mode = STA_CONTROL_MODIFY_MSK ;
spin_unlock_irqrestore ( & priv - > sta_lock , flags ) ;
iwl_send_add_sta ( priv , & priv - > stations [ sta_id ] . sta , CMD_ASYNC ) ;
}
2009-11-13 11:56:37 -08:00
EXPORT_SYMBOL ( iwl_sta_modify_ps_wake ) ;
2008-11-12 13:14:05 -08:00
2009-11-13 11:56:37 -08:00
void iwl_sta_modify_sleep_tx_count ( struct iwl_priv * priv , int sta_id , int cnt )
2008-11-12 13:14:05 -08:00
{
2009-11-13 11:56:37 -08:00
unsigned long flags ;
2008-11-12 13:14:05 -08:00
2009-11-13 11:56:37 -08:00
spin_lock_irqsave ( & priv - > sta_lock , flags ) ;
priv - > stations [ sta_id ] . sta . station_flags | = STA_FLG_PWR_SAVE_MSK ;
priv - > stations [ sta_id ] . sta . station_flags_msk = STA_FLG_PWR_SAVE_MSK ;
priv - > stations [ sta_id ] . sta . sta . modify_mask =
STA_MODIFY_SLEEP_TX_COUNT_MSK ;
priv - > stations [ sta_id ] . sta . sleep_tx_count = cpu_to_le16 ( cnt ) ;
priv - > stations [ sta_id ] . sta . mode = STA_CONTROL_MODIFY_MSK ;
spin_unlock_irqrestore ( & priv - > sta_lock , flags ) ;
2008-11-12 13:14:05 -08:00
2009-11-13 11:56:37 -08:00
iwl_send_add_sta ( priv , & priv - > stations [ sta_id ] . sta , CMD_ASYNC ) ;
2008-11-12 13:14:05 -08:00
}
2010-03-17 13:34:34 -07:00
EXPORT_SYMBOL ( iwl_sta_modify_sleep_tx_count ) ;
2010-02-22 16:24:47 -08:00
int iwl_mac_sta_remove ( struct ieee80211_hw * hw ,
struct ieee80211_vif * vif ,
struct ieee80211_sta * sta )
{
int ret ;
struct iwl_priv * priv = hw - > priv ;
IWL_DEBUG_INFO ( priv , " received request to remove station %pM \n " ,
sta - > addr ) ;
ret = iwl_remove_station ( priv , sta ) ;
if ( ret )
IWL_ERR ( priv , " Error removing station %pM \n " ,
sta - > addr ) ;
return ret ;
}
EXPORT_SYMBOL ( iwl_mac_sta_remove ) ;