2008-04-14 21:16:06 -07:00
/******************************************************************************
*
2009-01-08 10:20:02 -08:00
* Copyright ( c ) 2003 - 2009 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>
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-06-30 17:23:03 +08:00
# define IWL_STA_DRIVER_ACTIVE BIT(0) /* driver entry is active */
# define IWL_STA_UCODE_ACTIVE BIT(1) /* ucode entry is active */
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 :
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 ) ;
2008-06-30 17:23:03 +08:00
static void iwl_sta_ucode_activate ( struct iwl_priv * priv , u8 sta_id )
{
unsigned long flags ;
spin_lock_irqsave ( & priv - > sta_lock , flags ) ;
2009-06-03 11:44:07 -07:00
if ( ! ( priv - > stations [ sta_id ] . used & IWL_STA_DRIVER_ACTIVE ) )
2008-12-19 10:37:33 +08:00
IWL_ERR ( priv , " ACTIVATE a non DRIVER active station %d \n " ,
sta_id ) ;
2008-06-30 17:23:03 +08:00
priv - > stations [ sta_id ] . used | = IWL_STA_UCODE_ACTIVE ;
2009-01-27 14:27:56 -08:00
IWL_DEBUG_ASSOC ( priv , " Added STA to Ucode: %pM \n " ,
2008-10-27 15:59:26 -07:00
priv - > stations [ sta_id ] . sta . sta . addr ) ;
2008-06-30 17:23:03 +08:00
spin_unlock_irqrestore ( & priv - > sta_lock , flags ) ;
}
2008-05-29 16:35:03 +08:00
static int iwl_add_sta_callback ( struct iwl_priv * priv ,
2009-07-24 11:13:05 -07:00
struct iwl_device_cmd * cmd ,
struct sk_buff * skb )
2008-05-29 16:35:03 +08:00
{
struct iwl_rx_packet * res = NULL ;
2008-10-14 12:32:43 -07:00
struct iwl_addsta_cmd * addsta =
( struct iwl_addsta_cmd * ) cmd - > cmd . payload ;
u8 sta_id = addsta - > sta . sta_id ;
2008-05-29 16:35:03 +08:00
if ( ! skb ) {
2008-12-19 10:37:33 +08:00
IWL_ERR ( priv , " Error: Response NULL in REPLY_ADD_STA. \n " ) ;
2008-05-29 16:35:03 +08:00
return 1 ;
}
res = ( struct iwl_rx_packet * ) skb - > data ;
if ( res - > 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 " ,
2008-05-29 16:35:03 +08:00
res - > hdr . flags ) ;
return 1 ;
}
switch ( res - > u . add_sta . status ) {
case ADD_STA_SUCCESS_MSK :
2008-06-30 17:23:03 +08:00
iwl_sta_ucode_activate ( priv , sta_id ) ;
/* fall through */
2008-05-29 16:35:03 +08:00
default :
2009-01-27 14:27:56 -08:00
IWL_DEBUG_HC ( priv , " Received REPLY_ADD_STA:(0x%08X) \n " ,
2008-05-29 16:35:03 +08:00
res - > u . add_sta . status ) ;
break ;
}
/* We didn't cache the SKB; let the caller free it */
return 1 ;
}
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 )
{
struct iwl_rx_packet * res = NULL ;
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 ,
} ;
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 ;
2009-07-24 11:13:05 -07:00
res = ( struct iwl_rx_packet * ) cmd . reply_skb - > data ;
2008-05-05 10:22:34 +08:00
if ( res - > 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 " ,
2008-05-05 10:22:34 +08:00
res - > hdr . flags ) ;
ret = - EIO ;
}
if ( ret = = 0 ) {
switch ( res - > u . add_sta . status ) {
case ADD_STA_SUCCESS_MSK :
2008-06-30 17:23:03 +08:00
iwl_sta_ucode_activate ( priv , sta - > sta . sta_id ) ;
2009-01-27 14:27:56 -08:00
IWL_DEBUG_INFO ( priv , " REPLY_ADD_STA PASSED \n " ) ;
2008-05-05 10:22:34 +08:00
break ;
default :
ret = - EIO ;
2008-12-19 10:37:32 +08:00
IWL_WARN ( priv , " REPLY_ADD_STA failed \n " ) ;
2008-05-05 10:22:34 +08:00
break ;
}
}
priv - > alloc_rxb_skb - - ;
2009-07-24 11:13:05 -07:00
dev_kfree_skb_any ( cmd . reply_skb ) ;
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 ;
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 ) ;
if ( iwl_is_fat_tx_allowed ( priv , sta_ht_inf ) )
sta_flags | = STA_FLG_FAT_EN_MSK ;
else
sta_flags & = ~ STA_FLG_FAT_EN_MSK ;
priv - > stations [ index ] . sta . station_flags = sta_flags ;
done :
return ;
}
/**
2009-06-03 11:44:07 -07:00
* iwl_add_station - Add station to tables in driver and device
2008-05-15 13:54:04 +08:00
*/
2009-06-03 11:44:07 -07:00
u8 iwl_add_station ( struct iwl_priv * priv , const u8 * addr , bool is_ap , u8 flags ,
struct ieee80211_sta_ht_cap * ht_info )
2008-05-15 13:54:04 +08:00
{
struct iwl_station_entry * station ;
unsigned long flags_spin ;
2009-06-03 11:44:07 -07:00
int i ;
int sta_id = IWL_INVALID_STATION ;
u16 rate ;
2008-05-15 13:54:04 +08:00
spin_lock_irqsave ( & priv - > sta_lock , flags_spin ) ;
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
}
/* These two conditions have the same outcome, but keep them separate
2008-06-30 17:23:03 +08:00
since they have different meanings */
if ( unlikely ( sta_id = = IWL_INVALID_STATION ) ) {
2008-05-15 13:54:04 +08:00
spin_unlock_irqrestore ( & priv - > sta_lock , flags_spin ) ;
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
if ( priv - > stations [ sta_id ] . used & &
! compare_ether_addr ( priv - > stations [ sta_id ] . sta . sta . addr , addr ) ) {
2008-05-15 13:54:04 +08:00
spin_unlock_irqrestore ( & priv - > sta_lock , flags_spin ) ;
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 ) ;
2008-05-15 13:54:04 +08:00
spin_unlock_irqrestore ( & priv - > sta_lock , flags_spin ) ;
/* Add station to device's station table */
iwl_send_add_sta ( priv , & station - > sta , flags ) ;
2008-06-30 17:23:03 +08:00
return sta_id ;
2008-05-15 13:54:04 +08:00
}
2009-06-03 11:44:07 -07:00
EXPORT_SYMBOL ( iwl_add_station ) ;
2008-05-15 13:54:04 +08:00
2008-06-30 17:23:03 +08:00
static void iwl_sta_ucode_deactivate ( struct iwl_priv * priv , const char * addr )
2008-05-29 16:35:02 +08:00
{
unsigned long flags ;
2009-06-03 11:44:07 -07:00
u8 sta_id = iwl_find_station ( priv , addr ) ;
2008-06-30 17:23:03 +08:00
BUG_ON ( sta_id = = IWL_INVALID_STATION ) ;
2009-01-27 14:27:56 -08:00
IWL_DEBUG_ASSOC ( priv , " Removed STA from Ucode: %pM \n " , addr ) ;
2008-06-30 17:23:03 +08:00
spin_lock_irqsave ( & priv - > sta_lock , flags ) ;
/* Ucode must be active and driver must be non active */
if ( priv - > stations [ sta_id ] . used ! = IWL_STA_UCODE_ACTIVE )
2008-12-19 10:37:33 +08:00
IWL_ERR ( priv , " removed non active STA %d \n " , sta_id ) ;
2008-06-30 17:23:03 +08:00
priv - > stations [ sta_id ] . used & = ~ IWL_STA_UCODE_ACTIVE ;
memset ( & priv - > stations [ sta_id ] , 0 , sizeof ( struct iwl_station_entry ) ) ;
spin_unlock_irqrestore ( & priv - > sta_lock , flags ) ;
2008-05-29 16:35:02 +08:00
}
static int iwl_remove_sta_callback ( struct iwl_priv * priv ,
2009-07-24 11:13:05 -07:00
struct iwl_device_cmd * cmd ,
struct sk_buff * skb )
2008-05-29 16:35:02 +08:00
{
struct iwl_rx_packet * res = NULL ;
2008-10-14 12:32:43 -07:00
struct iwl_rem_sta_cmd * rm_sta =
( struct iwl_rem_sta_cmd * ) cmd - > cmd . payload ;
const char * addr = rm_sta - > addr ;
2008-05-29 16:35:02 +08:00
if ( ! skb ) {
2008-12-19 10:37:33 +08:00
IWL_ERR ( priv , " Error: Response NULL in REPLY_REMOVE_STA. \n " ) ;
2008-05-29 16:35:02 +08:00
return 1 ;
}
res = ( struct iwl_rx_packet * ) skb - > data ;
if ( res - > 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 " ,
2008-05-29 16:35:02 +08:00
res - > hdr . flags ) ;
return 1 ;
}
switch ( res - > u . rem_sta . status ) {
case REM_STA_SUCCESS_MSK :
iwl_sta_ucode_deactivate ( priv , addr ) ;
break ;
default :
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 ;
}
/* We didn't cache the SKB; let the caller free it */
return 1 ;
}
static int iwl_send_remove_station ( struct iwl_priv * priv , const u8 * addr ,
u8 flags )
{
struct iwl_rx_packet * res = NULL ;
int ret ;
struct iwl_rem_sta_cmd rm_sta_cmd ;
struct iwl_host_cmd cmd = {
. id = REPLY_REMOVE_STA ,
. len = sizeof ( struct iwl_rem_sta_cmd ) ,
2009-07-24 11:13:05 -07:00
. flags = flags ,
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 ;
memcpy ( & rm_sta_cmd . addr , addr , ETH_ALEN ) ;
if ( flags & CMD_ASYNC )
2009-07-24 11:13:05 -07:00
cmd . callback = iwl_remove_sta_callback ;
2008-05-29 16:35:02 +08:00
else
2009-07-24 11:13:05 -07:00
cmd . flags | = CMD_WANT_SKB ;
2008-05-29 16:35:02 +08:00
ret = iwl_send_cmd ( priv , & cmd ) ;
if ( ret | | ( flags & CMD_ASYNC ) )
return ret ;
2009-07-24 11:13:05 -07:00
res = ( struct iwl_rx_packet * ) cmd . reply_skb - > data ;
2008-05-29 16:35:02 +08:00
if ( res - > 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 " ,
2008-05-29 16:35:02 +08:00
res - > hdr . flags ) ;
ret = - EIO ;
}
if ( ! ret ) {
switch ( res - > u . rem_sta . status ) {
case REM_STA_SUCCESS_MSK :
iwl_sta_ucode_deactivate ( priv , addr ) ;
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 ;
}
}
priv - > alloc_rxb_skb - - ;
2009-07-24 11:13:05 -07:00
dev_kfree_skb_any ( cmd . reply_skb ) ;
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 .
*/
2009-06-03 11:44:07 -07:00
int iwl_remove_station ( struct iwl_priv * priv , const u8 * addr , bool is_ap )
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 ;
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 if ( is_broadcast_ether_addr ( addr ) )
2008-06-30 17:23:03 +08:00
sta_id = priv - > hw_params . bcast_sta_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 ,
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 " ,
2008-10-27 15:59:26 -07:00
sta_id , addr ) ;
2008-06-30 17:23:03 +08:00
if ( ! ( priv - > stations [ sta_id ] . used & IWL_STA_DRIVER_ACTIVE ) ) {
2008-12-19 10:37:33 +08:00
IWL_ERR ( priv , " Removing %pM but non DRIVER active \n " ,
2008-10-27 15:59:26 -07:00
addr ) ;
2008-06-30 17:23:03 +08:00
goto out ;
}
if ( ! ( priv - > stations [ sta_id ] . used & IWL_STA_UCODE_ACTIVE ) ) {
2008-12-19 10:37:33 +08:00
IWL_ERR ( priv , " Removing %pM but non UCODE active \n " ,
2008-10-27 15:59:26 -07:00
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
2008-05-29 16:35:02 +08:00
spin_unlock_irqrestore ( & priv - > sta_lock , flags ) ;
2008-06-30 17:23:03 +08:00
ret = iwl_send_remove_station ( priv , addr , CMD_ASYNC ) ;
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
}
EXPORT_SYMBOL ( iwl_remove_station ) ;
2008-06-30 17:23:03 +08:00
2008-11-19 15:32:23 -08:00
/**
* iwl_clear_stations_table - Clear the driver ' s station table
*
* NOTE : This does not clear or otherwise alter the device ' s station table .
*/
void iwl_clear_stations_table ( struct iwl_priv * priv )
{
unsigned long flags ;
2009-03-11 11:17:59 -07:00
int i ;
2008-11-19 15:32:23 -08:00
spin_lock_irqsave ( & priv - > sta_lock , flags ) ;
if ( iwl_is_alive ( priv ) & &
! test_bit ( STATUS_EXIT_PENDING , & priv - > status ) & &
iwl_send_cmd_pdu_async ( priv , REPLY_REMOVE_ALL_STA , 0 , NULL , NULL ) )
2008-12-19 10:37:33 +08:00
IWL_ERR ( priv , " Couldn't clear the station table \n " ) ;
2008-11-19 15:32:23 -08:00
priv - > num_stations = 0 ;
memset ( priv - > stations , 0 , sizeof ( priv - > stations ) ) ;
2009-01-28 09:38:30 -08:00
/* clean ucode key table bit map */
priv - > ucode_key_table = 0 ;
2009-03-11 11:17:59 -07:00
/* keep track of static keys */
for ( i = 0 ; i < WEP_KEYS_MAX ; i + + ) {
if ( priv - > wep_keys [ i ] . key_size )
2009-04-30 13:56:29 -07:00
set_bit ( i , & priv - > ucode_key_table ) ;
2009-03-11 11:17:59 -07:00
}
2008-11-19 15:32:23 -08:00
spin_unlock_irqrestore ( & priv - > sta_lock , flags ) ;
}
EXPORT_SYMBOL ( iwl_clear_stations_table ) ;
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
int iwl_send_static_wepkey_cmd ( struct iwl_priv * priv , u8 send_if_empty )
{
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 ,
2009-07-24 11:13:05 -07:00
. flags = CMD_ASYNC ,
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 ;
}
2008-05-05 10:22:44 +08:00
EXPORT_SYMBOL ( iwl_send_static_wepkey_cmd ) ;
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 ;
unsigned long flags ;
spin_lock_irqsave ( & priv - > sta_lock , flags ) ;
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
if ( ! test_and_clear_bit ( keyconf - > keyidx , & 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:08 -07:00
keyconf - > keyidx ) ;
2008-04-14 21:16:06 -07:00
priv - > default_wep_key - - ;
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 " ) ;
spin_unlock_irqrestore ( & priv - > sta_lock , flags ) ;
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
spin_unlock_irqrestore ( & priv - > sta_lock , flags ) ;
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 ;
unsigned long flags ;
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 ;
spin_lock_irqsave ( & priv - > sta_lock , flags ) ;
priv - > default_wep_key + + ;
2008-04-14 21:16:08 -07:00
if ( test_and_set_bit ( keyconf - > keyidx , & priv - > ucode_key_table ) )
2008-12-19 10:37:33 +08:00
IWL_ERR ( priv , " index %d already used in uCode key table. \n " ,
2008-04-14 21:16:08 -07:00
keyconf - > keyidx ) ;
2008-04-14 21:16:06 -07:00
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
spin_unlock_irqrestore ( & priv - > sta_lock , flags ) ;
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 ) ) {
IWL_DEBUG_WEP ( priv , " Not sending REPLY_ADD_STA command because RFKILL enabled. \n " ) ;
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
int iwl_send_lq_cmd ( struct iwl_priv * priv ,
struct iwl_link_quality_cmd * lq , u8 flags )
{
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 ,
} ;
if ( ( lq - > sta_id = = 0xFF ) & &
2008-09-11 00:01:58 +02:00
( priv - > iw_mode = = NL80211_IFTYPE_ADHOC ) )
2008-04-15 16:01:40 -07:00
return - EINVAL ;
if ( lq - > sta_id = = 0xFF )
lq - > sta_id = IWL_AP_ID ;
2008-07-21 02:40:14 +03:00
iwl_dump_lq_cmd ( priv , lq ) ;
2008-04-15 16:01:40 -07:00
2008-06-30 17:23:13 +08:00
if ( iwl_is_associated ( priv ) & & priv - > assoc_station_added )
2008-04-15 16:01:40 -07:00
return iwl_send_cmd ( priv , & cmd ) ;
return 0 ;
}
EXPORT_SYMBOL ( iwl_send_lq_cmd ) ;
2008-05-15 13:54:04 +08:00
/**
* iwl_sta_init_lq - Initialize a station ' s hardware rate table
*
* The uCode ' s station table contains a table of fallback rates
* for automatic fallback during transmission .
*
* NOTE : This sets up a default set of values . These will be replaced later
2008-07-18 13:53:05 +08:00
* if the driver ' s iwl - agn - rs rate scaling algorithm is used , instead of
2008-05-15 13:54:04 +08:00
* rc80211_simple .
*
* NOTE : Run REPLY_ADD_STA command to set up station table entry , before
* calling this function ( which runs REPLY_TX_LINK_QUALITY_CMD ,
* which requires station table entry to exist ) .
*/
2009-06-03 11:44:07 -07:00
static void iwl_sta_init_lq ( struct iwl_priv * priv , const u8 * addr , bool is_ap )
2008-05-15 13:54:04 +08:00
{
int i , r ;
struct iwl_link_quality_cmd link_cmd = {
. reserved1 = 0 ,
} ;
2008-10-08 09:37:29 +08:00
u32 rate_flags ;
2008-05-15 13:54:04 +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 ;
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-10-08 09:37:29 +08:00
rate_flags | = first_antenna ( priv - > hw_params . valid_tx_ant ) < <
RATE_MCS_ANT_POS ;
2008-05-15 13:54:04 +08:00
link_cmd . rs_table [ i ] . rate_n_flags =
2008-06-12 09:47:11 +08:00
iwl_hw_set_rate_n_flags ( iwl_rates [ r ] . plcp , rate_flags ) ;
2008-10-29 14:05:44 -07:00
r = iwl_get_prev_ieee_rate ( r ) ;
2008-05-15 13:54:04 +08:00
}
2008-10-08 09:37:29 +08:00
link_cmd . general_params . single_stream_ant_msk =
first_antenna ( priv - > hw_params . valid_tx_ant ) ;
2008-05-15 13:54:04 +08:00
link_cmd . general_params . dual_stream_ant_msk = 3 ;
2009-06-03 11:44:11 -07:00
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-05-15 13:54:04 +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 ;
iwl_send_cmd_pdu_async ( priv , REPLY_TX_LINK_QUALITY_CMD ,
sizeof ( link_cmd ) , & link_cmd , NULL ) ;
}
2008-06-30 17:23:03 +08:00
2008-05-15 13:54:04 +08:00
/**
* iwl_rxon_add_station - add station into station table .
*
* there is only one AP station with id = IWL_AP_ID
2008-10-23 23:48:56 -07:00
* NOTE : mutex must be held before calling this function
2008-05-15 13:54:04 +08:00
*/
2009-06-03 11:44:07 -07:00
int iwl_rxon_add_station ( struct iwl_priv * priv , const u8 * addr , bool is_ap )
2008-05-15 13:54:04 +08:00
{
2008-10-14 16:58:37 +02:00
struct ieee80211_sta * sta ;
struct ieee80211_sta_ht_cap ht_config ;
struct ieee80211_sta_ht_cap * cur_ht_config = NULL ;
2008-05-15 13:54:04 +08:00
u8 sta_id ;
/* Add station to device's station table */
2008-10-14 16:58:37 +02:00
/*
* XXX : This check is definitely not correct , if we ' re an AP
* it ' ll always be false which is not what we want , but
* it doesn ' t look like iwlagn is prepared to be an HT
* AP anyway .
*/
if ( priv - > current_ht_config . is_ht ) {
rcu_read_lock ( ) ;
sta = ieee80211_find_sta ( priv - > hw , addr ) ;
if ( sta ) {
memcpy ( & ht_config , & sta - > ht_cap , sizeof ( ht_config ) ) ;
cur_ht_config = & ht_config ;
}
rcu_read_unlock ( ) ;
}
2009-06-03 11:44:07 -07:00
sta_id = iwl_add_station ( priv , addr , is_ap , CMD_SYNC , cur_ht_config ) ;
2008-05-15 13:54:04 +08:00
/* Set up default rate scaling table in device's station table */
iwl_sta_init_lq ( priv , addr , is_ap ) ;
return sta_id ;
}
EXPORT_SYMBOL ( iwl_rxon_add_station ) ;
/**
* 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 :
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 ;
/* Create new station table entry */
2009-06-03 11:44:07 -07:00
sta_id = iwl_add_station ( priv , hdr - > addr1 , false ,
CMD_ASYNC , NULL ) ;
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-07-17 09:30:24 -07:00
iwl_print_hex_dump ( 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 ) ;
static void iwl_sta_modify_ps_wake ( struct iwl_priv * priv , int sta_id )
{
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 ;
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 ) ;
}
void iwl_update_ps_mode ( struct iwl_priv * priv , u16 ps_bit , u8 * addr )
{
/* FIXME: need locking over ps_status ??? */
2009-06-03 11:44:07 -07:00
u8 sta_id = iwl_find_station ( priv , addr ) ;
2008-11-12 13:14:05 -08:00
if ( sta_id ! = IWL_INVALID_STATION ) {
u8 sta_awake = priv - > stations [ sta_id ] .
ps_status = = STA_PS_STATUS_WAKE ;
if ( sta_awake & & ps_bit )
priv - > stations [ sta_id ] . ps_status = STA_PS_STATUS_SLEEP ;
else if ( ! sta_awake & & ! ps_bit ) {
iwl_sta_modify_ps_wake ( priv , sta_id ) ;
priv - > stations [ sta_id ] . ps_status = STA_PS_STATUS_WAKE ;
}
}
}
2008-05-29 16:35:15 +08:00