2008-04-14 21:16:06 -07:00
/******************************************************************************
*
* Copyright ( c ) 2003 - 2008 Intel Corporation . All rights reserved .
*
* 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 :
* James P . Ketrenos < ipw2100 - admin @ linux . intel . com >
* 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"
# include "iwl-helpers.h"
2008-04-14 21:16:08 -07:00
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 ;
DECLARE_MAC_BUF ( mac ) ;
if ( ( priv - > iw_mode = = IEEE80211_IF_TYPE_IBSS ) | |
( priv - > iw_mode = = IEEE80211_IF_TYPE_AP ) )
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 ;
}
IWL_DEBUG_ASSOC_LIMIT ( " can not find STA %s total %d \n " ,
print_mac ( mac , addr ) , priv - > num_stations ) ;
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 )
{
if ( priv - > iw_mode = = IEEE80211_IF_TYPE_STA ) {
return IWL_AP_ID ;
} else {
u8 * da = ieee80211_get_DA ( hdr ) ;
return iwl_find_station ( priv , da ) ;
}
}
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 ;
DECLARE_MAC_BUF ( mac ) ;
spin_lock_irqsave ( & priv - > sta_lock , flags ) ;
if ( ! ( priv - > stations [ sta_id ] . used & IWL_STA_DRIVER_ACTIVE ) )
IWL_ERROR ( " ACTIVATE a non DRIVER active station %d \n " , sta_id ) ;
priv - > stations [ sta_id ] . used | = IWL_STA_UCODE_ACTIVE ;
IWL_DEBUG_ASSOC ( " Added STA to Ucode: %s \n " ,
print_mac ( mac , priv - > stations [ sta_id ] . sta . sta . addr ) ) ;
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 ,
struct iwl_cmd * cmd , struct sk_buff * skb )
{
struct iwl_rx_packet * res = NULL ;
2008-06-30 17:23:03 +08:00
u8 sta_id = cmd - > cmd . addsta . sta . sta_id ;
2008-05-29 16:35:03 +08:00
if ( ! skb ) {
IWL_ERROR ( " Error: Response NULL in REPLY_ADD_STA. \n " ) ;
return 1 ;
}
res = ( struct iwl_rx_packet * ) skb - > data ;
if ( res - > hdr . flags & IWL_CMD_FAILED_MSK ) {
IWL_ERROR ( " Bad return from REPLY_ADD_STA (0x%08X) \n " ,
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 :
IWL_DEBUG_HC ( " Received REPLY_ADD_STA:(0x%08X) \n " ,
res - > u . add_sta . status ) ;
break ;
}
/* We didn't cache the SKB; let the caller free it */
return 1 ;
}
2008-05-05 10:22:34 +08:00
int iwl_send_add_sta ( struct iwl_priv * priv ,
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 ,
. meta . flags = flags ,
. data = data ,
} ;
2008-05-29 16:35:03 +08:00
if ( flags & CMD_ASYNC )
cmd . meta . u . callback = iwl_add_sta_callback ;
else
2008-05-05 10:22:34 +08:00
cmd . meta . flags | = CMD_WANT_SKB ;
cmd . len = priv - > cfg - > ops - > utils - > build_addsta_hcmd ( sta , data ) ;
ret = iwl_send_cmd ( priv , & cmd ) ;
if ( ret | | ( flags & CMD_ASYNC ) )
return ret ;
res = ( struct iwl_rx_packet * ) cmd . meta . u . skb - > data ;
if ( res - > hdr . flags & IWL_CMD_FAILED_MSK ) {
IWL_ERROR ( " Bad return from REPLY_ADD_STA (0x%08X) \n " ,
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 ) ;
2008-05-05 10:22:34 +08:00
IWL_DEBUG_INFO ( " REPLY_ADD_STA PASSED \n " ) ;
break ;
default :
ret = - EIO ;
IWL_WARNING ( " REPLY_ADD_STA failed \n " ) ;
break ;
}
}
priv - > alloc_rxb_skb - - ;
dev_kfree_skb_any ( cmd . meta . u . skb ) ;
return ret ;
}
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 ,
struct ieee80211_ht_info * sta_ht_inf )
{
__le32 sta_flags ;
u8 mimo_ps_mode ;
if ( ! sta_ht_inf | | ! sta_ht_inf - > ht_supported )
goto done ;
mimo_ps_mode = ( sta_ht_inf - > cap & IEEE80211_HT_CAP_MIMO_PS ) > > 2 ;
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 ) {
case WLAN_HT_CAP_MIMO_PS_STATIC :
sta_flags | = STA_FLG_MIMO_DIS_MSK ;
break ;
case WLAN_HT_CAP_MIMO_PS_DYNAMIC :
sta_flags | = STA_FLG_RTS_MIMO_PROT_MSK ;
break ;
case WLAN_HT_CAP_MIMO_PS_DISABLED :
break ;
default :
IWL_WARNING ( " Invalid MIMO PS mode %d " , mimo_ps_mode ) ;
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 ;
}
/**
* iwl_add_station_flags - Add station to tables in driver and device
*/
u8 iwl_add_station_flags ( struct iwl_priv * priv , const u8 * addr , int is_ap ,
u8 flags , struct ieee80211_ht_info * ht_info )
{
int i ;
2008-06-30 17:23:03 +08:00
int sta_id = IWL_INVALID_STATION ;
2008-05-15 13:54:04 +08:00
struct iwl_station_entry * station ;
unsigned long flags_spin ;
DECLARE_MAC_BUF ( mac ) ;
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 ;
IWL_DEBUG_ASSOC ( " Add STA to driver ID %d: %s \n " ,
sta_id , print_mac ( mac , 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-05-15 13:54:04 +08:00
priv - > iw_mode ! = IEEE80211_IF_TYPE_IBSS )
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
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
}
EXPORT_SYMBOL ( iwl_add_station_flags ) ;
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 ;
DECLARE_MAC_BUF ( mac ) ;
2008-06-30 17:23:03 +08:00
u8 sta_id = iwl_find_station ( priv , addr ) ;
BUG_ON ( sta_id = = IWL_INVALID_STATION ) ;
IWL_DEBUG_ASSOC ( " Removed STA from Ucode: %s \n " ,
print_mac ( mac , addr ) ) ;
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 )
IWL_ERROR ( " removed non active STA %d \n " , sta_id ) ;
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 ,
struct iwl_cmd * cmd , struct sk_buff * skb )
{
struct iwl_rx_packet * res = NULL ;
const char * addr = cmd - > cmd . rm_sta . addr ;
if ( ! skb ) {
IWL_ERROR ( " Error: Response NULL in REPLY_REMOVE_STA. \n " ) ;
return 1 ;
}
res = ( struct iwl_rx_packet * ) skb - > data ;
if ( res - > hdr . flags & IWL_CMD_FAILED_MSK ) {
IWL_ERROR ( " Bad return from REPLY_REMOVE_STA (0x%08X) \n " ,
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-06-30 17:23:03 +08:00
IWL_ERROR ( " 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 ) ,
. meta . flags = flags ,
. 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 )
cmd . meta . u . callback = iwl_remove_sta_callback ;
else
cmd . meta . flags | = CMD_WANT_SKB ;
ret = iwl_send_cmd ( priv , & cmd ) ;
if ( ret | | ( flags & CMD_ASYNC ) )
return ret ;
res = ( struct iwl_rx_packet * ) cmd . meta . u . skb - > data ;
if ( res - > hdr . flags & IWL_CMD_FAILED_MSK ) {
IWL_ERROR ( " Bad return from REPLY_REMOVE_STA (0x%08X) \n " ,
res - > hdr . flags ) ;
ret = - EIO ;
}
if ( ! ret ) {
switch ( res - > u . rem_sta . status ) {
case REM_STA_SUCCESS_MSK :
iwl_sta_ucode_deactivate ( priv , addr ) ;
IWL_DEBUG_ASSOC ( " REPLY_REMOVE_STA PASSED \n " ) ;
break ;
default :
ret = - EIO ;
IWL_ERROR ( " REPLY_REMOVE_STA failed \n " ) ;
break ;
}
}
priv - > alloc_rxb_skb - - ;
dev_kfree_skb_any ( cmd . meta . u . skb ) ;
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 .
*/
2008-06-30 17:23:03 +08:00
int iwl_remove_station ( struct iwl_priv * priv , const u8 * addr , int 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 ;
2008-06-30 17:23:03 +08:00
DECLARE_MAC_BUF ( mac ) ;
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 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 ;
2008-06-30 17:23:03 +08:00
IWL_DEBUG_ASSOC ( " Removing STA from driver:%d %s \n " ,
sta_id , print_mac ( mac , addr ) ) ;
if ( ! ( priv - > stations [ sta_id ] . used & IWL_STA_DRIVER_ACTIVE ) ) {
IWL_ERROR ( " Removing %s but non DRIVER active \n " ,
print_mac ( mac , addr ) ) ;
goto out ;
}
if ( ! ( priv - > stations [ sta_id ] . used & IWL_STA_UCODE_ACTIVE ) ) {
IWL_ERROR ( " Removing %s but non UCODE active \n " ,
print_mac ( mac , addr ) ) ;
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-06-12 09:47:18 +08:00
static 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 ;
return - 1 ;
}
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 ,
. meta . flags = CMD_ASYNC ,
} ;
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 ) ;
2008-04-14 21:16:08 -07:00
if ( ! test_and_clear_bit ( keyconf - > keyidx , & priv - > ucode_key_table ) )
IWL_ERROR ( " index %d not used in uCode key table. \n " ,
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 ] ) ) ;
2008-04-14 21:16:06 -07:00
ret = iwl_send_static_wepkey_cmd ( priv , 1 ) ;
2008-06-12 09:47:09 +08:00
IWL_DEBUG_WEP ( " Remove default WEP key: idx=%d ret=%d \n " ,
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 ) {
IWL_DEBUG_WEP ( " Bad WEP key length %d \n " , keyconf - > keylen ) ;
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 ) )
IWL_ERROR ( " index %d already used in uCode key table. \n " ,
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 ) ;
2008-06-12 09:47:09 +08:00
IWL_DEBUG_WEP ( " Set default WEP key: len=%d idx=%d ret=%d \n " ,
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-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 ;
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-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 ;
spin_unlock_irqrestore ( & priv - > sta_lock , flags ) ;
IWL_DEBUG_INFO ( " hwcrypto: modify ucode station key info \n " ) ;
2008-05-05 10:22:34 +08:00
return iwl_send_add_sta ( priv , & priv - > stations [ sta_id ] . sta , CMD_ASYNC ) ;
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 ;
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
/* 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-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 ;
2008-06-12 09:47:09 +08:00
IWL_DEBUG_WEP ( " Remove dynamic key: idx=%d sta=%d \n " ,
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-04-14 21:16:09 -07:00
if ( ! test_and_clear_bit ( priv - > stations [ sta_id ] . sta . key . key_offset ,
& priv - > ucode_key_table ) )
IWL_ERROR ( " index %d not used in uCode key table. \n " ,
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 ;
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-05-15 13:54:09 +08:00
IWL_ERROR ( " Unknown alg: %s alg = %d \n " , __func__ , keyconf - > alg ) ;
2008-04-14 21:16:09 -07:00
ret = - EINVAL ;
}
2008-06-12 09:47:09 +08:00
IWL_DEBUG_WEP ( " Set dynamic key: alg= %d len=%d idx=%d sta=%d ret=%d \n " ,
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 ;
IWL_DEBUG_RATE ( " lq station id 0x%x \n " , lq - > sta_id ) ;
IWL_DEBUG_RATE ( " lq dta 0x%X 0x%X \n " ,
lq - > general_params . single_stream_ant_msk ,
lq - > general_params . dual_stream_ant_msk ) ;
for ( i = 0 ; i < LINK_QUAL_MAX_RETRY_NUM ; i + + )
IWL_DEBUG_RATE ( " lq index %d 0x%X \n " ,
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 ) ,
. meta . flags = flags ,
. data = lq ,
} ;
if ( ( lq - > sta_id = = 0xFF ) & &
( priv - > iw_mode = = IEEE80211_IF_TYPE_IBSS ) )
return - EINVAL ;
if ( lq - > sta_id = = 0xFF )
lq - > sta_id = IWL_AP_ID ;
iwl_dump_lq_cmd ( priv , lq ) ;
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
* if the driver ' s iwl - 4965 - rs rate scaling algorithm is used , instead of
* 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 ) .
*/
static void iwl_sta_init_lq ( struct iwl_priv * priv , const u8 * addr , int is_ap )
{
int i , r ;
struct iwl_link_quality_cmd link_cmd = {
. reserved1 = 0 ,
} ;
u16 rate_flags ;
/* 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 ;
/* Use Tx antenna B only */
rate_flags | = RATE_MCS_ANT_B_MSK ; /*FIXME:RS*/
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-05-15 13:54:04 +08:00
r = iwl4965_get_prev_ieee_rate ( r ) ;
}
link_cmd . general_params . single_stream_ant_msk = 2 ;
link_cmd . general_params . dual_stream_ant_msk = 3 ;
link_cmd . agg_params . agg_dis_start_th = 3 ;
link_cmd . agg_params . agg_time_limit = cpu_to_le16 ( 4000 ) ;
/* 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
* NOTE : mutex must be held before calling this fnction
*/
int iwl_rxon_add_station ( struct iwl_priv * priv , const u8 * addr , int is_ap )
{
u8 sta_id ;
/* Add station to device's station table */
struct ieee80211_conf * conf = & priv - > hw - > conf ;
struct ieee80211_ht_info * cur_ht_config = & conf - > ht_conf ;
if ( ( is_ap ) & &
( conf - > flags & IEEE80211_CONF_SUPPORT_HT_MODE ) & &
( priv - > iw_mode = = IEEE80211_IF_TYPE_STA ) )
sta_id = iwl_add_station_flags ( priv , addr , is_ap ,
0 , cur_ht_config ) ;
else
sta_id = iwl_add_station_flags ( priv , addr , is_ap ,
0 , NULL ) ;
/* 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 ;
u16 fc = le16_to_cpu ( hdr - > frame_control ) ;
DECLARE_MAC_BUF ( mac ) ;
/* If this frame is broadcast or management, use broadcast station id */
if ( ( ( fc & IEEE80211_FCTL_FTYPE ) ! = IEEE80211_FTYPE_DATA ) | |
is_multicast_ether_addr ( hdr - > addr1 ) )
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 ) */
case IEEE80211_IF_TYPE_STA :
return IWL_AP_ID ;
/* If we are an AP, then find the station, or use BCAST */
case IEEE80211_IF_TYPE_AP :
sta_id = iwl_find_station ( priv , hdr - > addr1 ) ;
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 */
case IEEE80211_IF_TYPE_IBSS :
sta_id = iwl_find_station ( priv , hdr - > addr1 ) ;
if ( sta_id ! = IWL_INVALID_STATION )
return sta_id ;
/* Create new station table entry */
sta_id = iwl_add_station_flags ( priv , hdr - > addr1 ,
0 , CMD_ASYNC , NULL ) ;
if ( sta_id ! = IWL_INVALID_STATION )
return sta_id ;
IWL_DEBUG_DROP ( " Station %s not in station map. "
" Defaulting to broadcast... \n " ,
print_mac ( mac , hdr - > addr1 ) ) ;
iwl_print_hex_dump ( priv , IWL_DL_DROP , ( u8 * ) hdr , sizeof ( * hdr ) ) ;
return priv - > hw_params . bcast_sta_id ;
default :
IWL_WARNING ( " Unknown mode of operation: %d " , priv - > iw_mode ) ;
return priv - > hw_params . bcast_sta_id ;
}
}
EXPORT_SYMBOL ( iwl_get_sta_id ) ;
2008-05-29 16:35:15 +08:00
/**
* iwl_sta_modify_enable_tid_tx - Enable Tx for this TID in station table
*/
void iwl_sta_modify_enable_tid_tx ( struct iwl_priv * priv , int sta_id , int tid )
{
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 ) ;
}
EXPORT_SYMBOL ( iwl_sta_modify_enable_tid_tx ) ;