2011-07-18 00:22:30 +03:00
/*
* Copyright ( c ) 2004 - 2011 Atheros Communications Inc .
*
* Permission to use , copy , modify , and / or distribute this software for any
* purpose with or without fee is hereby granted , provided that the above
* copyright notice and this permission notice appear in all copies .
*
* THE SOFTWARE IS PROVIDED " AS IS " AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS . IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL , DIRECT , INDIRECT , OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE , DATA OR PROFITS , WHETHER IN AN
* ACTION OF CONTRACT , NEGLIGENCE OR OTHER TORTIOUS ACTION , ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE .
*/
# include "core.h"
# include "hif-ops.h"
# include "cfg80211.h"
# include "target.h"
# include "debug.h"
2011-10-25 19:34:17 +05:30
struct ath6kl_sta * ath6kl_find_sta ( struct ath6kl_vif * vif , u8 * node_addr )
2011-07-18 00:22:30 +03:00
{
2011-10-25 19:34:17 +05:30
struct ath6kl * ar = vif - > ar ;
2011-07-18 00:22:30 +03:00
struct ath6kl_sta * conn = NULL ;
u8 i , max_conn ;
2011-10-25 19:34:03 +05:30
max_conn = ( vif - > nw_type = = AP_NETWORK ) ? AP_MAX_NUM_STA : 0 ;
2011-07-18 00:22:30 +03:00
for ( i = 0 ; i < max_conn ; i + + ) {
if ( memcmp ( node_addr , ar - > sta_list [ i ] . mac , ETH_ALEN ) = = 0 ) {
conn = & ar - > sta_list [ i ] ;
break ;
}
}
return conn ;
}
struct ath6kl_sta * ath6kl_find_sta_by_aid ( struct ath6kl * ar , u8 aid )
{
struct ath6kl_sta * conn = NULL ;
u8 ctr ;
for ( ctr = 0 ; ctr < AP_MAX_NUM_STA ; ctr + + ) {
if ( ar - > sta_list [ ctr ] . aid = = aid ) {
conn = & ar - > sta_list [ ctr ] ;
break ;
}
}
return conn ;
}
static void ath6kl_add_new_sta ( struct ath6kl * ar , u8 * mac , u16 aid , u8 * wpaie ,
2012-01-03 15:28:53 +05:30
size_t ielen , u8 keymgmt , u8 ucipher , u8 auth )
2011-07-18 00:22:30 +03:00
{
struct ath6kl_sta * sta ;
u8 free_slot ;
free_slot = aid - 1 ;
sta = & ar - > sta_list [ free_slot ] ;
memcpy ( sta - > mac , mac , ETH_ALEN ) ;
2011-08-30 21:57:51 +03:00
if ( ielen < = ATH6KL_MAX_IE )
memcpy ( sta - > wpa_ie , wpaie , ielen ) ;
2011-07-18 00:22:30 +03:00
sta - > aid = aid ;
sta - > keymgmt = keymgmt ;
sta - > ucipher = ucipher ;
sta - > auth = auth ;
ar - > sta_list_index = ar - > sta_list_index | ( 1 < < free_slot ) ;
ar - > ap_stats . sta [ free_slot ] . aid = cpu_to_le32 ( aid ) ;
}
static void ath6kl_sta_cleanup ( struct ath6kl * ar , u8 i )
{
struct ath6kl_sta * sta = & ar - > sta_list [ i ] ;
/* empty the queued pkts in the PS queue if any */
spin_lock_bh ( & sta - > psq_lock ) ;
skb_queue_purge ( & sta - > psq ) ;
spin_unlock_bh ( & sta - > psq_lock ) ;
memset ( & ar - > ap_stats . sta [ sta - > aid - 1 ] , 0 ,
sizeof ( struct wmi_per_sta_stat ) ) ;
memset ( sta - > mac , 0 , ETH_ALEN ) ;
memset ( sta - > wpa_ie , 0 , ATH6KL_MAX_IE ) ;
sta - > aid = 0 ;
sta - > sta_flags = 0 ;
ar - > sta_list_index = ar - > sta_list_index & ~ ( 1 < < i ) ;
}
static u8 ath6kl_remove_sta ( struct ath6kl * ar , u8 * mac , u16 reason )
{
u8 i , removed = 0 ;
if ( is_zero_ether_addr ( mac ) )
return removed ;
if ( is_broadcast_ether_addr ( mac ) ) {
ath6kl_dbg ( ATH6KL_DBG_TRC , " deleting all station \n " ) ;
for ( i = 0 ; i < AP_MAX_NUM_STA ; i + + ) {
if ( ! is_zero_ether_addr ( ar - > sta_list [ i ] . mac ) ) {
ath6kl_sta_cleanup ( ar , i ) ;
removed = 1 ;
}
}
} else {
for ( i = 0 ; i < AP_MAX_NUM_STA ; i + + ) {
if ( memcmp ( ar - > sta_list [ i ] . mac , mac , ETH_ALEN ) = = 0 ) {
ath6kl_dbg ( ATH6KL_DBG_TRC ,
" deleting station %pM aid=%d reason=%d \n " ,
mac , ar - > sta_list [ i ] . aid , reason ) ;
ath6kl_sta_cleanup ( ar , i ) ;
removed = 1 ;
break ;
}
}
}
return removed ;
}
enum htc_endpoint_id ath6kl_ac2_endpoint_id ( void * devt , u8 ac )
{
struct ath6kl * ar = devt ;
return ar - > ac2ep_map [ ac ] ;
}
struct ath6kl_cookie * ath6kl_alloc_cookie ( struct ath6kl * ar )
{
struct ath6kl_cookie * cookie ;
cookie = ar - > cookie_list ;
if ( cookie ! = NULL ) {
ar - > cookie_list = cookie - > arc_list_next ;
ar - > cookie_count - - ;
}
return cookie ;
}
void ath6kl_cookie_init ( struct ath6kl * ar )
{
u32 i ;
ar - > cookie_list = NULL ;
ar - > cookie_count = 0 ;
memset ( ar - > cookie_mem , 0 , sizeof ( ar - > cookie_mem ) ) ;
for ( i = 0 ; i < MAX_COOKIE_NUM ; i + + )
ath6kl_free_cookie ( ar , & ar - > cookie_mem [ i ] ) ;
}
void ath6kl_cookie_cleanup ( struct ath6kl * ar )
{
ar - > cookie_list = NULL ;
ar - > cookie_count = 0 ;
}
void ath6kl_free_cookie ( struct ath6kl * ar , struct ath6kl_cookie * cookie )
{
/* Insert first */
if ( ! ar | | ! cookie )
return ;
cookie - > arc_list_next = ar - > cookie_list ;
ar - > cookie_list = cookie ;
ar - > cookie_count + + ;
}
/*
2011-09-02 10:32:05 +03:00
* Read from the hardware through its diagnostic window . No cooperation
* from the firmware is required for this .
2011-07-18 00:22:30 +03:00
*/
2011-09-02 10:32:05 +03:00
int ath6kl_diag_read32 ( struct ath6kl * ar , u32 address , u32 * value )
2011-07-18 00:22:30 +03:00
{
2011-09-02 10:32:05 +03:00
int ret ;
2011-07-18 00:22:30 +03:00
2011-11-11 12:17:51 +02:00
ret = ath6kl_hif_diag_read32 ( ar , address , value ) ;
2011-09-02 10:32:05 +03:00
if ( ret ) {
ath6kl_warn ( " failed to read32 through diagnose window: %d \n " ,
ret ) ;
return ret ;
2011-07-18 00:22:30 +03:00
}
2011-09-02 10:32:05 +03:00
return 0 ;
2011-07-18 00:22:30 +03:00
}
/*
* Write to the ATH6KL through its diagnostic window . No cooperation from
* the Target is required for this .
*/
2011-09-05 11:19:46 +03:00
int ath6kl_diag_write32 ( struct ath6kl * ar , u32 address , __le32 value )
2011-07-18 00:22:30 +03:00
{
2011-09-02 10:32:05 +03:00
int ret ;
2011-07-18 00:22:30 +03:00
2011-11-11 12:17:51 +02:00
ret = ath6kl_hif_diag_write32 ( ar , address , value ) ;
2011-09-02 10:32:05 +03:00
if ( ret ) {
ath6kl_err ( " failed to write 0x%x during diagnose window to 0x%d \n " ,
address , value ) ;
return ret ;
2011-07-18 00:22:30 +03:00
}
2011-11-11 12:17:51 +02:00
return 0 ;
2011-07-18 00:22:30 +03:00
}
2011-09-02 10:32:05 +03:00
int ath6kl_diag_read ( struct ath6kl * ar , u32 address , void * data , u32 length )
2011-07-18 00:22:30 +03:00
{
2011-09-02 10:32:05 +03:00
u32 count , * buf = data ;
int ret ;
2011-07-18 00:22:30 +03:00
2011-09-02 10:32:05 +03:00
if ( WARN_ON ( length % 4 ) )
return - EINVAL ;
for ( count = 0 ; count < length / 4 ; count + + , address + = 4 ) {
ret = ath6kl_diag_read32 ( ar , address , & buf [ count ] ) ;
if ( ret )
return ret ;
2011-07-18 00:22:30 +03:00
}
2011-09-02 10:32:05 +03:00
return 0 ;
}
int ath6kl_diag_write ( struct ath6kl * ar , u32 address , void * data , u32 length )
{
2011-09-05 11:19:46 +03:00
u32 count ;
__le32 * buf = data ;
2011-09-02 10:32:05 +03:00
int ret ;
if ( WARN_ON ( length % 4 ) )
return - EINVAL ;
for ( count = 0 ; count < length / 4 ; count + + , address + = 4 ) {
ret = ath6kl_diag_write32 ( ar , address , buf [ count ] ) ;
if ( ret )
return ret ;
}
return 0 ;
2011-07-18 00:22:30 +03:00
}
2011-09-02 10:32:05 +03:00
int ath6kl_read_fwlogs ( struct ath6kl * ar )
{
struct ath6kl_dbglog_hdr debug_hdr ;
struct ath6kl_dbglog_buf debug_buf ;
u32 address , length , dropped , firstbuf , debug_hdr_addr ;
int ret = 0 , loop ;
u8 * buf ;
buf = kmalloc ( ATH6KL_FWLOG_PAYLOAD_SIZE , GFP_KERNEL ) ;
if ( ! buf )
return - ENOMEM ;
address = TARG_VTOP ( ar - > target_type ,
ath6kl_get_hi_item_addr ( ar ,
HI_ITEM ( hi_dbglog_hdr ) ) ) ;
ret = ath6kl_diag_read32 ( ar , address , & debug_hdr_addr ) ;
if ( ret )
goto out ;
/* Get the contents of the ring buffer */
if ( debug_hdr_addr = = 0 ) {
ath6kl_warn ( " Invalid address for debug_hdr_addr \n " ) ;
ret = - EINVAL ;
goto out ;
}
address = TARG_VTOP ( ar - > target_type , debug_hdr_addr ) ;
ath6kl_diag_read ( ar , address , & debug_hdr , sizeof ( debug_hdr ) ) ;
address = TARG_VTOP ( ar - > target_type ,
le32_to_cpu ( debug_hdr . dbuf_addr ) ) ;
firstbuf = address ;
dropped = le32_to_cpu ( debug_hdr . dropped ) ;
ath6kl_diag_read ( ar , address , & debug_buf , sizeof ( debug_buf ) ) ;
loop = 100 ;
do {
address = TARG_VTOP ( ar - > target_type ,
le32_to_cpu ( debug_buf . buffer_addr ) ) ;
length = le32_to_cpu ( debug_buf . length ) ;
if ( length ! = 0 & & ( le32_to_cpu ( debug_buf . length ) < =
le32_to_cpu ( debug_buf . bufsize ) ) ) {
length = ALIGN ( length , 4 ) ;
ret = ath6kl_diag_read ( ar , address ,
buf , length ) ;
if ( ret )
goto out ;
ath6kl_debug_fwlog_event ( ar , buf , length ) ;
}
address = TARG_VTOP ( ar - > target_type ,
le32_to_cpu ( debug_buf . next ) ) ;
ath6kl_diag_read ( ar , address , & debug_buf , sizeof ( debug_buf ) ) ;
if ( ret )
goto out ;
loop - - ;
if ( WARN_ON ( loop = = 0 ) ) {
ret = - ETIMEDOUT ;
goto out ;
}
} while ( address ! = firstbuf ) ;
out :
kfree ( buf ) ;
return ret ;
}
2011-07-11 17:14:13 +08:00
/* FIXME: move to a better place, target.h? */
# define AR6003_RESET_CONTROL_ADDRESS 0x00004000
# define AR6004_RESET_CONTROL_ADDRESS 0x00004000
2011-10-25 19:34:16 +05:30
void ath6kl_reset_device ( struct ath6kl * ar , u32 target_type ,
bool wait_fot_compltn , bool cold_reset )
2011-07-18 00:22:30 +03:00
{
int status = 0 ;
u32 address ;
2011-09-05 11:19:46 +03:00
__le32 data ;
2011-07-18 00:22:30 +03:00
2011-07-11 17:14:13 +08:00
if ( target_type ! = TARGET_TYPE_AR6003 & &
target_type ! = TARGET_TYPE_AR6004 )
2011-07-18 00:22:30 +03:00
return ;
2011-09-05 11:19:46 +03:00
data = cold_reset ? cpu_to_le32 ( RESET_CONTROL_COLD_RST ) :
cpu_to_le32 ( RESET_CONTROL_MBOX_RST ) ;
2011-07-18 00:22:30 +03:00
2011-07-11 17:14:13 +08:00
switch ( target_type ) {
case TARGET_TYPE_AR6003 :
address = AR6003_RESET_CONTROL_ADDRESS ;
break ;
case TARGET_TYPE_AR6004 :
address = AR6004_RESET_CONTROL_ADDRESS ;
break ;
}
2011-09-02 10:32:05 +03:00
status = ath6kl_diag_write32 ( ar , address , data ) ;
2011-07-18 00:22:30 +03:00
if ( status )
ath6kl_err ( " failed to reset target \n " ) ;
}
2011-10-25 19:34:13 +05:30
static void ath6kl_install_static_wep_keys ( struct ath6kl_vif * vif )
2011-07-18 00:22:30 +03:00
{
u8 index ;
u8 keyusage ;
2011-12-29 16:18:39 +05:30
for ( index = 0 ; index < = WMI_MAX_KEY_INDEX ; index + + ) {
2011-10-25 19:34:06 +05:30
if ( vif - > wep_key_list [ index ] . key_len ) {
2011-07-18 00:22:30 +03:00
keyusage = GROUP_USAGE ;
2011-10-25 19:34:02 +05:30
if ( index = = vif - > def_txkey_index )
2011-07-18 00:22:30 +03:00
keyusage | = TX_USAGE ;
2011-10-25 19:34:13 +05:30
ath6kl_wmi_addkey_cmd ( vif - > ar - > wmi , vif - > fw_vif_idx ,
2011-07-18 00:22:30 +03:00
index ,
WEP_CRYPT ,
keyusage ,
2011-10-25 19:34:06 +05:30
vif - > wep_key_list [ index ] . key_len ,
2011-11-02 23:45:55 +02:00
NULL , 0 ,
2011-10-25 19:34:06 +05:30
vif - > wep_key_list [ index ] . key ,
2011-07-18 00:22:30 +03:00
KEY_OP_INIT_VAL , NULL ,
NO_SYNC_WMIFLAG ) ;
}
}
}
2011-10-25 19:34:13 +05:30
void ath6kl_connect_ap_mode_bss ( struct ath6kl_vif * vif , u16 channel )
2011-07-18 00:22:30 +03:00
{
2011-10-25 19:34:13 +05:30
struct ath6kl * ar = vif - > ar ;
2011-07-18 00:22:30 +03:00
struct ath6kl_req_key * ik ;
2011-08-30 21:57:52 +03:00
int res ;
u8 key_rsc [ ATH6KL_KEY_SEQ_LEN ] ;
2011-07-18 00:22:30 +03:00
2011-09-05 17:38:45 +03:00
ik = & ar - > ap_mode_bkey ;
2011-07-18 00:22:30 +03:00
2011-09-05 17:38:45 +03:00
ath6kl_dbg ( ATH6KL_DBG_WLAN_CFG , " AP mode started on %u MHz \n " , channel ) ;
2011-08-30 21:57:52 +03:00
2011-10-25 19:34:02 +05:30
switch ( vif - > auth_mode ) {
2011-09-05 17:38:45 +03:00
case NONE_AUTH :
2011-10-25 19:34:02 +05:30
if ( vif - > prwise_crypto = = WEP_CRYPT )
2011-10-25 19:34:13 +05:30
ath6kl_install_static_wep_keys ( vif ) ;
2011-12-08 16:50:30 +02:00
if ( ! ik - > valid | | ik - > key_type ! = WAPI_CRYPT )
break ;
/* for WAPI, we need to set the delayed group key, continue: */
2011-09-05 17:38:45 +03:00
case WPA_PSK_AUTH :
case WPA2_PSK_AUTH :
case ( WPA_PSK_AUTH | WPA2_PSK_AUTH ) :
if ( ! ik - > valid )
2011-07-18 00:22:30 +03:00
break ;
2011-08-30 21:57:52 +03:00
2011-09-05 17:38:45 +03:00
ath6kl_dbg ( ATH6KL_DBG_WLAN_CFG , " Delayed addkey for "
" the initial group key for AP mode \n " ) ;
memset ( key_rsc , 0 , sizeof ( key_rsc ) ) ;
res = ath6kl_wmi_addkey_cmd (
2011-10-25 19:34:12 +05:30
ar - > wmi , vif - > fw_vif_idx , ik - > key_index , ik - > key_type ,
2011-11-02 23:45:55 +02:00
GROUP_USAGE , ik - > key_len , key_rsc , ATH6KL_KEY_SEQ_LEN ,
ik - > key ,
2011-09-05 17:38:45 +03:00
KEY_OP_INIT_VAL , NULL , SYNC_BOTH_WMIFLAG ) ;
if ( res ) {
ath6kl_dbg ( ATH6KL_DBG_WLAN_CFG , " Delayed "
" addkey failed: %d \n " , res ) ;
2011-07-18 00:22:30 +03:00
}
2011-09-05 17:38:45 +03:00
break ;
2011-07-18 00:22:30 +03:00
}
2011-10-25 19:34:13 +05:30
ath6kl_wmi_bssfilter_cmd ( ar - > wmi , vif - > fw_vif_idx , NONE_BSS_FILTER , 0 ) ;
2011-10-25 19:34:01 +05:30
set_bit ( CONNECTED , & vif - > flags ) ;
2011-10-25 19:34:13 +05:30
netif_carrier_on ( vif - > ndev ) ;
2011-09-05 17:38:45 +03:00
}
2011-10-25 19:34:13 +05:30
void ath6kl_connect_ap_mode_sta ( struct ath6kl_vif * vif , u16 aid , u8 * mac_addr ,
2011-09-05 17:38:45 +03:00
u8 keymgmt , u8 ucipher , u8 auth ,
u8 assoc_req_len , u8 * assoc_info )
{
2011-10-25 19:34:13 +05:30
struct ath6kl * ar = vif - > ar ;
2011-09-05 17:38:45 +03:00
u8 * ies = NULL , * wpa_ie = NULL , * pos ;
size_t ies_len = 0 ;
struct station_info sinfo ;
ath6kl_dbg ( ATH6KL_DBG_TRC , " new station %pM aid=%d \n " , mac_addr , aid ) ;
2011-07-18 00:22:30 +03:00
2011-08-30 21:57:51 +03:00
if ( assoc_req_len > sizeof ( struct ieee80211_hdr_3addr ) ) {
struct ieee80211_mgmt * mgmt =
( struct ieee80211_mgmt * ) assoc_info ;
if ( ieee80211_is_assoc_req ( mgmt - > frame_control ) & &
assoc_req_len > = sizeof ( struct ieee80211_hdr_3addr ) +
sizeof ( mgmt - > u . assoc_req ) ) {
ies = mgmt - > u . assoc_req . variable ;
ies_len = assoc_info + assoc_req_len - ies ;
} else if ( ieee80211_is_reassoc_req ( mgmt - > frame_control ) & &
assoc_req_len > = sizeof ( struct ieee80211_hdr_3addr )
+ sizeof ( mgmt - > u . reassoc_req ) ) {
ies = mgmt - > u . reassoc_req . variable ;
ies_len = assoc_info + assoc_req_len - ies ;
}
}
pos = ies ;
while ( pos & & pos + 1 < ies + ies_len ) {
if ( pos + 2 + pos [ 1 ] > ies + ies_len )
break ;
if ( pos [ 0 ] = = WLAN_EID_RSN )
wpa_ie = pos ; /* RSN IE */
else if ( pos [ 0 ] = = WLAN_EID_VENDOR_SPECIFIC & &
pos [ 1 ] > = 4 & &
pos [ 2 ] = = 0x00 & & pos [ 3 ] = = 0x50 & & pos [ 4 ] = = 0xf2 ) {
if ( pos [ 5 ] = = 0x01 )
wpa_ie = pos ; /* WPA IE */
else if ( pos [ 5 ] = = 0x04 ) {
wpa_ie = pos ; /* WPS IE */
break ; /* overrides WPA/RSN IE */
}
2011-11-03 11:39:39 +02:00
} else if ( pos [ 0 ] = = 0x44 & & wpa_ie = = NULL ) {
/*
* Note : WAPI Parameter Set IE re - uses Element ID that
* was officially allocated for BSS AC Access Delay . As
* such , we need to be a bit more careful on when
* parsing the frame . However , BSS AC Access Delay
* element is not supposed to be included in
* ( Re ) Association Request frames , so this should not
* cause problems .
*/
wpa_ie = pos ; /* WAPI IE */
break ;
2011-08-30 21:57:51 +03:00
}
pos + = 2 + pos [ 1 ] ;
}
2011-09-05 17:38:45 +03:00
ath6kl_add_new_sta ( ar , mac_addr , aid , wpa_ie ,
2011-08-30 21:57:51 +03:00
wpa_ie ? 2 + wpa_ie [ 1 ] : 0 ,
2011-09-05 17:38:45 +03:00
keymgmt , ucipher , auth ) ;
2011-07-18 00:22:30 +03:00
/* send event to application */
memset ( & sinfo , 0 , sizeof ( sinfo ) ) ;
/* TODO: sinfo.generation */
2011-08-30 21:57:51 +03:00
sinfo . assoc_req_ies = ies ;
sinfo . assoc_req_ies_len = ies_len ;
sinfo . filled | = STATION_INFO_ASSOC_REQ_IES ;
2011-10-25 19:34:13 +05:30
cfg80211_new_sta ( vif - > ndev , mac_addr , & sinfo , GFP_KERNEL ) ;
2011-07-18 00:22:30 +03:00
2011-10-25 19:34:13 +05:30
netif_wake_queue ( vif - > ndev ) ;
2011-07-18 00:22:30 +03:00
}
void disconnect_timer_handler ( unsigned long ptr )
{
struct net_device * dev = ( struct net_device * ) ptr ;
2011-10-25 19:34:12 +05:30
struct ath6kl_vif * vif = netdev_priv ( dev ) ;
2011-07-18 00:22:30 +03:00
2011-10-25 19:34:15 +05:30
ath6kl_init_profile_info ( vif ) ;
2011-10-25 19:34:13 +05:30
ath6kl_disconnect ( vif ) ;
2011-07-18 00:22:30 +03:00
}
2011-10-25 19:34:13 +05:30
void ath6kl_disconnect ( struct ath6kl_vif * vif )
2011-07-18 00:22:30 +03:00
{
2011-10-25 19:34:01 +05:30
if ( test_bit ( CONNECTED , & vif - > flags ) | |
test_bit ( CONNECT_PEND , & vif - > flags ) ) {
2011-10-25 19:34:13 +05:30
ath6kl_wmi_disconnect_cmd ( vif - > ar - > wmi , vif - > fw_vif_idx ) ;
2011-07-18 00:22:30 +03:00
/*
* Disconnect command is issued , clear the connect pending
* flag . The connected flag will be cleared in
* disconnect event notification .
*/
2011-10-25 19:34:01 +05:30
clear_bit ( CONNECT_PEND , & vif - > flags ) ;
2011-07-18 00:22:30 +03:00
}
}
/* WMI Event handlers */
void ath6kl_ready_event ( void * devt , u8 * datap , u32 sw_ver , u32 abi_ver )
{
struct ath6kl * ar = devt ;
2011-10-25 19:34:18 +05:30
memcpy ( ar - > mac_addr , datap , ETH_ALEN ) ;
2011-07-18 00:22:30 +03:00
ath6kl_dbg ( ATH6KL_DBG_TRC , " %s: mac addr = %pM \n " ,
2011-10-25 19:34:18 +05:30
__func__ , ar - > mac_addr ) ;
2011-07-18 00:22:30 +03:00
ar - > version . wlan_ver = sw_ver ;
ar - > version . abi_ver = abi_ver ;
2011-10-25 19:33:57 +05:30
snprintf ( ar - > wiphy - > fw_version ,
sizeof ( ar - > wiphy - > fw_version ) ,
2011-07-18 00:22:30 +03:00
" %u.%u.%u.%u " ,
( ar - > version . wlan_ver & 0xf0000000 ) > > 28 ,
( ar - > version . wlan_ver & 0x0f000000 ) > > 24 ,
( ar - > version . wlan_ver & 0x00ff0000 ) > > 16 ,
( ar - > version . wlan_ver & 0x0000ffff ) ) ;
/* indicate to the waiting thread that the ready event was received */
set_bit ( WMI_READY , & ar - > flag ) ;
wake_up ( & ar - > event_wq ) ;
}
2011-10-25 19:34:13 +05:30
void ath6kl_scan_complete_evt ( struct ath6kl_vif * vif , int status )
2011-07-18 00:22:30 +03:00
{
2011-10-25 19:34:13 +05:30
struct ath6kl * ar = vif - > ar ;
2011-11-01 08:43:56 +02:00
bool aborted = false ;
2011-10-25 19:34:01 +05:30
2011-11-01 08:43:56 +02:00
if ( status ! = WMI_SCAN_STATUS_SUCCESS )
aborted = true ;
ath6kl_cfg80211_scan_complete_event ( vif , aborted ) ;
2011-07-18 00:22:30 +03:00
2011-09-19 19:15:06 +03:00
if ( ! ar - > usr_bss_filter ) {
2011-10-25 19:34:01 +05:30
clear_bit ( CLEAR_BSSFILTER_ON_BEACON , & vif - > flags ) ;
2011-10-25 19:34:13 +05:30
ath6kl_wmi_bssfilter_cmd ( ar - > wmi , vif - > fw_vif_idx ,
NONE_BSS_FILTER , 0 ) ;
2011-09-19 19:15:06 +03:00
}
2011-07-18 00:22:30 +03:00
2011-10-24 12:17:51 +03:00
ath6kl_dbg ( ATH6KL_DBG_WLAN_CFG , " scan complete: %d \n " , status ) ;
2011-07-18 00:22:30 +03:00
}
2011-10-25 19:34:13 +05:30
void ath6kl_connect_event ( struct ath6kl_vif * vif , u16 channel , u8 * bssid ,
2011-07-18 00:22:30 +03:00
u16 listen_int , u16 beacon_int ,
enum network_type net_type , u8 beacon_ie_len ,
u8 assoc_req_len , u8 assoc_resp_len ,
u8 * assoc_info )
{
2011-10-25 19:34:13 +05:30
struct ath6kl * ar = vif - > ar ;
2011-10-25 19:34:01 +05:30
2011-10-25 19:34:13 +05:30
ath6kl_cfg80211_connect_event ( vif , channel , bssid ,
2011-07-18 00:22:30 +03:00
listen_int , beacon_int ,
net_type , beacon_ie_len ,
assoc_req_len , assoc_resp_len ,
assoc_info ) ;
2011-10-25 19:34:04 +05:30
memcpy ( vif - > bssid , bssid , sizeof ( vif - > bssid ) ) ;
2011-10-25 19:34:05 +05:30
vif - > bss_ch = channel ;
2011-07-18 00:22:30 +03:00
2011-10-25 19:34:03 +05:30
if ( ( vif - > nw_type = = INFRA_NETWORK ) )
2011-10-25 19:34:12 +05:30
ath6kl_wmi_listeninterval_cmd ( ar - > wmi , vif - > fw_vif_idx ,
ar - > listen_intvl_t ,
2011-07-18 00:22:30 +03:00
ar - > listen_intvl_b ) ;
2011-10-25 19:34:13 +05:30
netif_wake_queue ( vif - > ndev ) ;
2011-07-18 00:22:30 +03:00
/* Update connect & link status atomically */
2011-10-25 19:34:19 +05:30
spin_lock_bh ( & vif - > if_lock ) ;
2011-10-25 19:34:01 +05:30
set_bit ( CONNECTED , & vif - > flags ) ;
clear_bit ( CONNECT_PEND , & vif - > flags ) ;
2011-10-25 19:34:13 +05:30
netif_carrier_on ( vif - > ndev ) ;
2011-10-25 19:34:19 +05:30
spin_unlock_bh ( & vif - > if_lock ) ;
2011-07-18 00:22:30 +03:00
2011-10-25 19:34:07 +05:30
aggr_reset_state ( vif - > aggr_cntxt ) ;
2011-10-25 19:34:10 +05:30
vif - > reconnect_flag = 0 ;
2011-07-18 00:22:30 +03:00
2011-10-25 19:34:03 +05:30
if ( ( vif - > nw_type = = ADHOC_NETWORK ) & & ar - > ibss_ps_enable ) {
2011-07-18 00:22:30 +03:00
memset ( ar - > node_map , 0 , sizeof ( ar - > node_map ) ) ;
ar - > node_num = 0 ;
ar - > next_ep_id = ENDPOINT_2 ;
}
2011-09-19 19:15:06 +03:00
if ( ! ar - > usr_bss_filter ) {
2011-10-25 19:34:01 +05:30
set_bit ( CLEAR_BSSFILTER_ON_BEACON , & vif - > flags ) ;
2011-10-25 19:34:13 +05:30
ath6kl_wmi_bssfilter_cmd ( ar - > wmi , vif - > fw_vif_idx ,
CURRENT_BSS_FILTER , 0 ) ;
2011-09-19 19:15:06 +03:00
}
2011-07-18 00:22:30 +03:00
}
2011-10-25 19:34:13 +05:30
void ath6kl_tkip_micerr_event ( struct ath6kl_vif * vif , u8 keyid , bool ismcast )
2011-07-18 00:22:30 +03:00
{
struct ath6kl_sta * sta ;
2011-10-25 19:34:13 +05:30
struct ath6kl * ar = vif - > ar ;
2011-07-18 00:22:30 +03:00
u8 tsc [ 6 ] ;
2011-10-25 19:34:13 +05:30
2011-07-18 00:22:30 +03:00
/*
* For AP case , keyid will have aid of STA which sent pkt with
* MIC error . Use this aid to get MAC & send it to hostapd .
*/
2011-10-25 19:34:03 +05:30
if ( vif - > nw_type = = AP_NETWORK ) {
2011-07-18 00:22:30 +03:00
sta = ath6kl_find_sta_by_aid ( ar , ( keyid > > 2 ) ) ;
if ( ! sta )
return ;
ath6kl_dbg ( ATH6KL_DBG_TRC ,
" ap tkip mic error received from aid=%d \n " , keyid ) ;
memset ( tsc , 0 , sizeof ( tsc ) ) ; /* FIX: get correct TSC */
2011-10-25 19:34:13 +05:30
cfg80211_michael_mic_failure ( vif - > ndev , sta - > mac ,
2011-07-18 00:22:30 +03:00
NL80211_KEYTYPE_PAIRWISE , keyid ,
tsc , GFP_KERNEL ) ;
} else
2011-10-25 19:34:13 +05:30
ath6kl_cfg80211_tkip_micerr_event ( vif , keyid , ismcast ) ;
2011-07-18 00:22:30 +03:00
}
2011-10-25 19:34:13 +05:30
static void ath6kl_update_target_stats ( struct ath6kl_vif * vif , u8 * ptr , u32 len )
2011-07-18 00:22:30 +03:00
{
struct wmi_target_stats * tgt_stats =
( struct wmi_target_stats * ) ptr ;
2011-10-25 19:34:13 +05:30
struct ath6kl * ar = vif - > ar ;
2011-10-25 19:34:11 +05:30
struct target_stats * stats = & vif - > target_stats ;
2011-07-18 00:22:30 +03:00
struct tkip_ccmp_stats * ccmp_stats ;
u8 ac ;
if ( len < sizeof ( * tgt_stats ) )
return ;
ath6kl_dbg ( ATH6KL_DBG_TRC , " updating target stats \n " ) ;
stats - > tx_pkt + = le32_to_cpu ( tgt_stats - > stats . tx . pkt ) ;
stats - > tx_byte + = le32_to_cpu ( tgt_stats - > stats . tx . byte ) ;
stats - > tx_ucast_pkt + = le32_to_cpu ( tgt_stats - > stats . tx . ucast_pkt ) ;
stats - > tx_ucast_byte + = le32_to_cpu ( tgt_stats - > stats . tx . ucast_byte ) ;
stats - > tx_mcast_pkt + = le32_to_cpu ( tgt_stats - > stats . tx . mcast_pkt ) ;
stats - > tx_mcast_byte + = le32_to_cpu ( tgt_stats - > stats . tx . mcast_byte ) ;
stats - > tx_bcast_pkt + = le32_to_cpu ( tgt_stats - > stats . tx . bcast_pkt ) ;
stats - > tx_bcast_byte + = le32_to_cpu ( tgt_stats - > stats . tx . bcast_byte ) ;
stats - > tx_rts_success_cnt + =
le32_to_cpu ( tgt_stats - > stats . tx . rts_success_cnt ) ;
for ( ac = 0 ; ac < WMM_NUM_AC ; ac + + )
stats - > tx_pkt_per_ac [ ac ] + =
le32_to_cpu ( tgt_stats - > stats . tx . pkt_per_ac [ ac ] ) ;
stats - > tx_err + = le32_to_cpu ( tgt_stats - > stats . tx . err ) ;
stats - > tx_fail_cnt + = le32_to_cpu ( tgt_stats - > stats . tx . fail_cnt ) ;
stats - > tx_retry_cnt + = le32_to_cpu ( tgt_stats - > stats . tx . retry_cnt ) ;
stats - > tx_mult_retry_cnt + =
le32_to_cpu ( tgt_stats - > stats . tx . mult_retry_cnt ) ;
stats - > tx_rts_fail_cnt + =
le32_to_cpu ( tgt_stats - > stats . tx . rts_fail_cnt ) ;
stats - > tx_ucast_rate =
ath6kl_wmi_get_rate ( a_sle32_to_cpu ( tgt_stats - > stats . tx . ucast_rate ) ) ;
stats - > rx_pkt + = le32_to_cpu ( tgt_stats - > stats . rx . pkt ) ;
stats - > rx_byte + = le32_to_cpu ( tgt_stats - > stats . rx . byte ) ;
stats - > rx_ucast_pkt + = le32_to_cpu ( tgt_stats - > stats . rx . ucast_pkt ) ;
stats - > rx_ucast_byte + = le32_to_cpu ( tgt_stats - > stats . rx . ucast_byte ) ;
stats - > rx_mcast_pkt + = le32_to_cpu ( tgt_stats - > stats . rx . mcast_pkt ) ;
stats - > rx_mcast_byte + = le32_to_cpu ( tgt_stats - > stats . rx . mcast_byte ) ;
stats - > rx_bcast_pkt + = le32_to_cpu ( tgt_stats - > stats . rx . bcast_pkt ) ;
stats - > rx_bcast_byte + = le32_to_cpu ( tgt_stats - > stats . rx . bcast_byte ) ;
stats - > rx_frgment_pkt + = le32_to_cpu ( tgt_stats - > stats . rx . frgment_pkt ) ;
stats - > rx_err + = le32_to_cpu ( tgt_stats - > stats . rx . err ) ;
stats - > rx_crc_err + = le32_to_cpu ( tgt_stats - > stats . rx . crc_err ) ;
stats - > rx_key_cache_miss + =
le32_to_cpu ( tgt_stats - > stats . rx . key_cache_miss ) ;
stats - > rx_decrypt_err + = le32_to_cpu ( tgt_stats - > stats . rx . decrypt_err ) ;
stats - > rx_dupl_frame + = le32_to_cpu ( tgt_stats - > stats . rx . dupl_frame ) ;
stats - > rx_ucast_rate =
ath6kl_wmi_get_rate ( a_sle32_to_cpu ( tgt_stats - > stats . rx . ucast_rate ) ) ;
ccmp_stats = & tgt_stats - > stats . tkip_ccmp_stats ;
stats - > tkip_local_mic_fail + =
le32_to_cpu ( ccmp_stats - > tkip_local_mic_fail ) ;
stats - > tkip_cnter_measures_invoked + =
le32_to_cpu ( ccmp_stats - > tkip_cnter_measures_invoked ) ;
stats - > tkip_fmt_err + = le32_to_cpu ( ccmp_stats - > tkip_fmt_err ) ;
stats - > ccmp_fmt_err + = le32_to_cpu ( ccmp_stats - > ccmp_fmt_err ) ;
stats - > ccmp_replays + = le32_to_cpu ( ccmp_stats - > ccmp_replays ) ;
stats - > pwr_save_fail_cnt + =
le32_to_cpu ( tgt_stats - > pm_stats . pwr_save_failure_cnt ) ;
stats - > noise_floor_calib =
a_sle32_to_cpu ( tgt_stats - > noise_floor_calib ) ;
stats - > cs_bmiss_cnt + =
le32_to_cpu ( tgt_stats - > cserv_stats . cs_bmiss_cnt ) ;
stats - > cs_low_rssi_cnt + =
le32_to_cpu ( tgt_stats - > cserv_stats . cs_low_rssi_cnt ) ;
stats - > cs_connect_cnt + =
le16_to_cpu ( tgt_stats - > cserv_stats . cs_connect_cnt ) ;
stats - > cs_discon_cnt + =
le16_to_cpu ( tgt_stats - > cserv_stats . cs_discon_cnt ) ;
stats - > cs_ave_beacon_rssi =
a_sle16_to_cpu ( tgt_stats - > cserv_stats . cs_ave_beacon_rssi ) ;
stats - > cs_last_roam_msec =
tgt_stats - > cserv_stats . cs_last_roam_msec ;
stats - > cs_snr = tgt_stats - > cserv_stats . cs_snr ;
stats - > cs_rssi = a_sle16_to_cpu ( tgt_stats - > cserv_stats . cs_rssi ) ;
stats - > lq_val = le32_to_cpu ( tgt_stats - > lq_val ) ;
stats - > wow_pkt_dropped + =
le32_to_cpu ( tgt_stats - > wow_stats . wow_pkt_dropped ) ;
stats - > wow_host_pkt_wakeups + =
tgt_stats - > wow_stats . wow_host_pkt_wakeups ;
stats - > wow_host_evt_wakeups + =
tgt_stats - > wow_stats . wow_host_evt_wakeups ;
stats - > wow_evt_discarded + =
le16_to_cpu ( tgt_stats - > wow_stats . wow_evt_discarded ) ;
2011-10-25 19:34:11 +05:30
if ( test_bit ( STATS_UPDATE_PEND , & vif - > flags ) ) {
clear_bit ( STATS_UPDATE_PEND , & vif - > flags ) ;
2011-07-18 00:22:30 +03:00
wake_up ( & ar - > event_wq ) ;
}
}
static void ath6kl_add_le32 ( __le32 * var , __le32 val )
{
* var = cpu_to_le32 ( le32_to_cpu ( * var ) + le32_to_cpu ( val ) ) ;
}
2011-10-25 19:34:13 +05:30
void ath6kl_tgt_stats_event ( struct ath6kl_vif * vif , u8 * ptr , u32 len )
2011-07-18 00:22:30 +03:00
{
struct wmi_ap_mode_stat * p = ( struct wmi_ap_mode_stat * ) ptr ;
2011-10-25 19:34:13 +05:30
struct ath6kl * ar = vif - > ar ;
2011-07-18 00:22:30 +03:00
struct wmi_ap_mode_stat * ap = & ar - > ap_stats ;
struct wmi_per_sta_stat * st_ap , * st_p ;
u8 ac ;
2011-10-25 19:34:03 +05:30
if ( vif - > nw_type = = AP_NETWORK ) {
2011-07-18 00:22:30 +03:00
if ( len < sizeof ( * p ) )
return ;
for ( ac = 0 ; ac < AP_MAX_NUM_STA ; ac + + ) {
st_ap = & ap - > sta [ ac ] ;
st_p = & p - > sta [ ac ] ;
ath6kl_add_le32 ( & st_ap - > tx_bytes , st_p - > tx_bytes ) ;
ath6kl_add_le32 ( & st_ap - > tx_pkts , st_p - > tx_pkts ) ;
ath6kl_add_le32 ( & st_ap - > tx_error , st_p - > tx_error ) ;
ath6kl_add_le32 ( & st_ap - > tx_discard , st_p - > tx_discard ) ;
ath6kl_add_le32 ( & st_ap - > rx_bytes , st_p - > rx_bytes ) ;
ath6kl_add_le32 ( & st_ap - > rx_pkts , st_p - > rx_pkts ) ;
ath6kl_add_le32 ( & st_ap - > rx_error , st_p - > rx_error ) ;
ath6kl_add_le32 ( & st_ap - > rx_discard , st_p - > rx_discard ) ;
}
} else {
2011-10-25 19:34:13 +05:30
ath6kl_update_target_stats ( vif , ptr , len ) ;
2011-07-18 00:22:30 +03:00
}
}
void ath6kl_wakeup_event ( void * dev )
{
struct ath6kl * ar = ( struct ath6kl * ) dev ;
wake_up ( & ar - > event_wq ) ;
}
void ath6kl_txpwr_rx_evt ( void * devt , u8 tx_pwr )
{
struct ath6kl * ar = ( struct ath6kl * ) devt ;
ar - > tx_pwr = tx_pwr ;
wake_up ( & ar - > event_wq ) ;
}
2011-10-25 19:34:13 +05:30
void ath6kl_pspoll_event ( struct ath6kl_vif * vif , u8 aid )
2011-07-18 00:22:30 +03:00
{
struct ath6kl_sta * conn ;
struct sk_buff * skb ;
bool psq_empty = false ;
2011-10-25 19:34:13 +05:30
struct ath6kl * ar = vif - > ar ;
2011-07-18 00:22:30 +03:00
conn = ath6kl_find_sta_by_aid ( ar , aid ) ;
if ( ! conn )
return ;
/*
* Send out a packet queued on ps queue . When the ps queue
* becomes empty update the PVB for this station .
*/
spin_lock_bh ( & conn - > psq_lock ) ;
psq_empty = skb_queue_empty ( & conn - > psq ) ;
spin_unlock_bh ( & conn - > psq_lock ) ;
if ( psq_empty )
/* TODO: Send out a NULL data frame */
return ;
spin_lock_bh ( & conn - > psq_lock ) ;
skb = skb_dequeue ( & conn - > psq ) ;
spin_unlock_bh ( & conn - > psq_lock ) ;
conn - > sta_flags | = STA_PS_POLLED ;
2011-10-25 19:34:13 +05:30
ath6kl_data_tx ( skb , vif - > ndev ) ;
2011-07-18 00:22:30 +03:00
conn - > sta_flags & = ~ STA_PS_POLLED ;
spin_lock_bh ( & conn - > psq_lock ) ;
psq_empty = skb_queue_empty ( & conn - > psq ) ;
spin_unlock_bh ( & conn - > psq_lock ) ;
if ( psq_empty )
2011-10-25 19:34:12 +05:30
ath6kl_wmi_set_pvb_cmd ( ar - > wmi , vif - > fw_vif_idx , conn - > aid , 0 ) ;
2011-07-18 00:22:30 +03:00
}
2011-10-25 19:34:13 +05:30
void ath6kl_dtimexpiry_event ( struct ath6kl_vif * vif )
2011-07-18 00:22:30 +03:00
{
bool mcastq_empty = false ;
struct sk_buff * skb ;
2011-10-25 19:34:13 +05:30
struct ath6kl * ar = vif - > ar ;
2011-07-18 00:22:30 +03:00
/*
* If there are no associated STAs , ignore the DTIM expiry event .
* There can be potential race conditions where the last associated
* STA may disconnect & before the host could clear the ' Indicate
* DTIM ' request to the firmware , the firmware would have just
* indicated a DTIM expiry event . The race is between ' clear DTIM
* expiry cmd ' going from the host to the firmware & the DTIM
* expiry event happening from the firmware to the host .
*/
if ( ! ar - > sta_list_index )
return ;
spin_lock_bh ( & ar - > mcastpsq_lock ) ;
mcastq_empty = skb_queue_empty ( & ar - > mcastpsq ) ;
spin_unlock_bh ( & ar - > mcastpsq_lock ) ;
if ( mcastq_empty )
return ;
/* set the STA flag to dtim_expired for the frame to go out */
2011-10-25 19:34:01 +05:30
set_bit ( DTIM_EXPIRED , & vif - > flags ) ;
2011-07-18 00:22:30 +03:00
spin_lock_bh ( & ar - > mcastpsq_lock ) ;
while ( ( skb = skb_dequeue ( & ar - > mcastpsq ) ) ! = NULL ) {
spin_unlock_bh ( & ar - > mcastpsq_lock ) ;
2011-10-25 19:34:13 +05:30
ath6kl_data_tx ( skb , vif - > ndev ) ;
2011-07-18 00:22:30 +03:00
spin_lock_bh ( & ar - > mcastpsq_lock ) ;
}
spin_unlock_bh ( & ar - > mcastpsq_lock ) ;
2011-10-25 19:34:01 +05:30
clear_bit ( DTIM_EXPIRED , & vif - > flags ) ;
2011-07-18 00:22:30 +03:00
/* clear the LSB of the BitMapCtl field of the TIM IE */
2011-10-25 19:34:12 +05:30
ath6kl_wmi_set_pvb_cmd ( ar - > wmi , vif - > fw_vif_idx , MCAST_AID , 0 ) ;
2011-07-18 00:22:30 +03:00
}
2011-10-25 19:34:13 +05:30
void ath6kl_disconnect_event ( struct ath6kl_vif * vif , u8 reason , u8 * bssid ,
2011-07-18 00:22:30 +03:00
u8 assoc_resp_len , u8 * assoc_info ,
u16 prot_reason_status )
{
2011-10-25 19:34:13 +05:30
struct ath6kl * ar = vif - > ar ;
2011-10-25 19:34:01 +05:30
2011-10-25 19:34:03 +05:30
if ( vif - > nw_type = = AP_NETWORK ) {
2011-07-18 00:22:30 +03:00
if ( ! ath6kl_remove_sta ( ar , bssid , prot_reason_status ) )
return ;
/* if no more associated STAs, empty the mcast PS q */
if ( ar - > sta_list_index = = 0 ) {
spin_lock_bh ( & ar - > mcastpsq_lock ) ;
skb_queue_purge ( & ar - > mcastpsq ) ;
spin_unlock_bh ( & ar - > mcastpsq_lock ) ;
/* clear the LSB of the TIM IE's BitMapCtl field */
if ( test_bit ( WMI_READY , & ar - > flag ) )
2011-10-25 19:34:12 +05:30
ath6kl_wmi_set_pvb_cmd ( ar - > wmi , vif - > fw_vif_idx ,
MCAST_AID , 0 ) ;
2011-07-18 00:22:30 +03:00
}
if ( ! is_broadcast_ether_addr ( bssid ) ) {
/* send event to application */
2011-10-25 19:34:13 +05:30
cfg80211_del_sta ( vif - > ndev , bssid , GFP_KERNEL ) ;
2011-07-18 00:22:30 +03:00
}
2011-10-25 19:34:13 +05:30
if ( memcmp ( vif - > ndev - > dev_addr , bssid , ETH_ALEN ) = = 0 ) {
2011-10-25 19:34:06 +05:30
memset ( vif - > wep_key_list , 0 , sizeof ( vif - > wep_key_list ) ) ;
2011-10-25 19:34:01 +05:30
clear_bit ( CONNECTED , & vif - > flags ) ;
2011-09-15 15:10:16 +03:00
}
2011-07-18 00:22:30 +03:00
return ;
}
2011-10-25 19:34:13 +05:30
ath6kl_cfg80211_disconnect_event ( vif , reason , bssid ,
2011-07-18 00:22:30 +03:00
assoc_resp_len , assoc_info ,
prot_reason_status ) ;
2011-10-25 19:34:07 +05:30
aggr_reset_state ( vif - > aggr_cntxt ) ;
2011-07-18 00:22:30 +03:00
2011-10-25 19:34:08 +05:30
del_timer ( & vif - > disconnect_timer ) ;
2011-07-18 00:22:30 +03:00
2011-10-24 12:17:51 +03:00
ath6kl_dbg ( ATH6KL_DBG_WLAN_CFG , " disconnect reason is %d \n " , reason ) ;
2011-07-18 00:22:30 +03:00
/*
* If the event is due to disconnect cmd from the host , only they
* the target would stop trying to connect . Under any other
* condition , target would keep trying to connect .
*/
if ( reason = = DISCONNECT_CMD ) {
if ( ! ar - > usr_bss_filter & & test_bit ( WMI_READY , & ar - > flag ) )
2011-10-25 19:34:13 +05:30
ath6kl_wmi_bssfilter_cmd ( ar - > wmi , vif - > fw_vif_idx ,
NONE_BSS_FILTER , 0 ) ;
2011-07-18 00:22:30 +03:00
} else {
2011-10-25 19:34:01 +05:30
set_bit ( CONNECT_PEND , & vif - > flags ) ;
2011-07-18 00:22:30 +03:00
if ( ( ( reason = = ASSOC_FAILED ) & &
( prot_reason_status = = 0x11 ) ) | |
( ( reason = = ASSOC_FAILED ) & & ( prot_reason_status = = 0x0 )
2011-10-25 19:34:10 +05:30
& & ( vif - > reconnect_flag = = 1 ) ) ) {
2011-10-25 19:34:01 +05:30
set_bit ( CONNECTED , & vif - > flags ) ;
2011-07-18 00:22:30 +03:00
return ;
}
}
/* update connect & link status atomically */
2011-10-25 19:34:19 +05:30
spin_lock_bh ( & vif - > if_lock ) ;
2011-10-25 19:34:01 +05:30
clear_bit ( CONNECTED , & vif - > flags ) ;
2011-10-25 19:34:13 +05:30
netif_carrier_off ( vif - > ndev ) ;
2011-10-25 19:34:19 +05:30
spin_unlock_bh ( & vif - > if_lock ) ;
2011-07-18 00:22:30 +03:00
2011-10-25 19:34:10 +05:30
if ( ( reason ! = CSERV_DISCONNECT ) | | ( vif - > reconnect_flag ! = 1 ) )
vif - > reconnect_flag = 0 ;
2011-07-18 00:22:30 +03:00
if ( reason ! = CSERV_DISCONNECT )
ar - > user_key_ctrl = 0 ;
2011-10-25 19:34:13 +05:30
netif_stop_queue ( vif - > ndev ) ;
2011-10-25 19:34:04 +05:30
memset ( vif - > bssid , 0 , sizeof ( vif - > bssid ) ) ;
2011-10-25 19:34:05 +05:30
vif - > bss_ch = 0 ;
2011-07-18 00:22:30 +03:00
ath6kl_tx_data_cleanup ( ar ) ;
}
2011-10-25 19:34:20 +05:30
struct ath6kl_vif * ath6kl_vif_first ( struct ath6kl * ar )
{
struct ath6kl_vif * vif ;
2011-11-01 16:38:50 +05:30
spin_lock_bh ( & ar - > list_lock ) ;
2011-10-25 19:34:20 +05:30
if ( list_empty ( & ar - > vif_list ) ) {
2011-11-01 16:38:50 +05:30
spin_unlock_bh ( & ar - > list_lock ) ;
2011-10-25 19:34:20 +05:30
return NULL ;
}
vif = list_first_entry ( & ar - > vif_list , struct ath6kl_vif , list ) ;
2011-11-01 16:38:50 +05:30
spin_unlock_bh ( & ar - > list_lock ) ;
2011-10-25 19:34:20 +05:30
return vif ;
}
2011-07-18 00:22:30 +03:00
static int ath6kl_open ( struct net_device * dev )
{
2011-10-25 19:34:01 +05:30
struct ath6kl_vif * vif = netdev_priv ( dev ) ;
2011-07-18 00:22:30 +03:00
2011-10-25 19:34:01 +05:30
set_bit ( WLAN_ENABLED , & vif - > flags ) ;
2011-07-18 00:22:30 +03:00
2011-10-25 19:34:01 +05:30
if ( test_bit ( CONNECTED , & vif - > flags ) ) {
2011-07-18 00:22:30 +03:00
netif_carrier_on ( dev ) ;
netif_wake_queue ( dev ) ;
} else
netif_carrier_off ( dev ) ;
return 0 ;
}
static int ath6kl_close ( struct net_device * dev )
{
2011-10-25 19:34:01 +05:30
struct ath6kl_vif * vif = netdev_priv ( dev ) ;
2011-07-18 00:22:30 +03:00
netif_stop_queue ( dev ) ;
2011-12-13 14:51:37 +02:00
ath6kl_cfg80211_stop ( vif ) ;
2011-07-18 00:22:30 +03:00
2011-10-30 21:16:33 +02:00
clear_bit ( WLAN_ENABLED , & vif - > flags ) ;
2011-07-18 00:22:30 +03:00
return 0 ;
}
static struct net_device_stats * ath6kl_get_stats ( struct net_device * dev )
{
2011-10-25 19:34:11 +05:30
struct ath6kl_vif * vif = netdev_priv ( dev ) ;
2011-07-18 00:22:30 +03:00
2011-10-25 19:34:11 +05:30
return & vif - > net_stats ;
2011-07-18 00:22:30 +03:00
}
2011-12-27 14:28:00 -08:00
static int ath6kl_set_features ( struct net_device * dev , u32 features )
{
struct ath6kl_vif * vif = netdev_priv ( dev ) ;
struct ath6kl * ar = vif - > ar ;
int err = 0 ;
if ( ( features & NETIF_F_RXCSUM ) & &
( ar - > rx_meta_ver ! = WMI_META_VERSION_2 ) ) {
ar - > rx_meta_ver = WMI_META_VERSION_2 ;
err = ath6kl_wmi_set_rx_frame_format_cmd ( ar - > wmi ,
vif - > fw_vif_idx ,
ar - > rx_meta_ver , 0 , 0 ) ;
if ( err ) {
dev - > features = features & ~ NETIF_F_RXCSUM ;
return err ;
}
} else if ( ! ( features & NETIF_F_RXCSUM ) & &
( ar - > rx_meta_ver = = WMI_META_VERSION_2 ) ) {
ar - > rx_meta_ver = 0 ;
err = ath6kl_wmi_set_rx_frame_format_cmd ( ar - > wmi ,
vif - > fw_vif_idx ,
ar - > rx_meta_ver , 0 , 0 ) ;
if ( err ) {
dev - > features = features | NETIF_F_RXCSUM ;
return err ;
}
}
return err ;
}
2012-01-03 14:42:01 +05:30
static void ath6kl_set_multicast_list ( struct net_device * ndev )
{
struct ath6kl_vif * vif = netdev_priv ( ndev ) ;
bool mc_all_on = false , mc_all_off = false ;
int mc_count = netdev_mc_count ( ndev ) ;
struct netdev_hw_addr * ha ;
bool found ;
struct ath6kl_mc_filter * mc_filter , * tmp ;
struct list_head mc_filter_new ;
int ret ;
if ( ! test_bit ( WMI_READY , & vif - > ar - > flag ) | |
! test_bit ( WLAN_ENABLED , & vif - > flags ) )
return ;
mc_all_on = ! ! ( ndev - > flags & IFF_PROMISC ) | |
! ! ( ndev - > flags & IFF_ALLMULTI ) | |
! ! ( mc_count > ATH6K_MAX_MC_FILTERS_PER_LIST ) ;
mc_all_off = ! ( ndev - > flags & IFF_MULTICAST ) | | mc_count = = 0 ;
if ( mc_all_on | | mc_all_off ) {
/* Enable/disable all multicast */
ath6kl_dbg ( ATH6KL_DBG_TRC , " %s multicast filter \n " ,
mc_all_on ? " enabling " : " disabling " ) ;
ret = ath6kl_wmi_mcast_filter_cmd ( vif - > ar - > wmi , vif - > fw_vif_idx ,
mc_all_on ) ;
if ( ret )
ath6kl_warn ( " Failed to %s multicast receive \n " ,
mc_all_on ? " enable " : " disable " ) ;
return ;
}
list_for_each_entry_safe ( mc_filter , tmp , & vif - > mc_filter , list ) {
found = false ;
netdev_for_each_mc_addr ( ha , ndev ) {
if ( memcmp ( ha - > addr , mc_filter - > hw_addr ,
ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE ) = = 0 ) {
found = true ;
break ;
}
}
if ( ! found ) {
/*
* Delete the filter which was previously set
* but not in the new request .
*/
ath6kl_dbg ( ATH6KL_DBG_TRC ,
" Removing %pM from multicast filter \n " ,
mc_filter - > hw_addr ) ;
ret = ath6kl_wmi_add_del_mcast_filter_cmd ( vif - > ar - > wmi ,
vif - > fw_vif_idx , mc_filter - > hw_addr ,
false ) ;
if ( ret ) {
ath6kl_warn ( " Failed to remove multicast filter:%pM \n " ,
mc_filter - > hw_addr ) ;
return ;
}
list_del ( & mc_filter - > list ) ;
kfree ( mc_filter ) ;
}
}
INIT_LIST_HEAD ( & mc_filter_new ) ;
netdev_for_each_mc_addr ( ha , ndev ) {
found = false ;
list_for_each_entry ( mc_filter , & vif - > mc_filter , list ) {
if ( memcmp ( ha - > addr , mc_filter - > hw_addr ,
ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE ) = = 0 ) {
found = true ;
break ;
}
}
if ( ! found ) {
mc_filter = kzalloc ( sizeof ( struct ath6kl_mc_filter ) ,
GFP_ATOMIC ) ;
if ( ! mc_filter ) {
WARN_ON ( 1 ) ;
goto out ;
}
memcpy ( mc_filter - > hw_addr , ha - > addr ,
ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE ) ;
/* Set the multicast filter */
ath6kl_dbg ( ATH6KL_DBG_TRC ,
" Adding %pM to multicast filter list \n " ,
mc_filter - > hw_addr ) ;
ret = ath6kl_wmi_add_del_mcast_filter_cmd ( vif - > ar - > wmi ,
vif - > fw_vif_idx , mc_filter - > hw_addr ,
true ) ;
if ( ret ) {
ath6kl_warn ( " Failed to add multicast filter :%pM \n " ,
mc_filter - > hw_addr ) ;
kfree ( mc_filter ) ;
goto out ;
}
list_add_tail ( & mc_filter - > list , & mc_filter_new ) ;
}
}
out :
list_splice_tail ( & mc_filter_new , & vif - > mc_filter ) ;
}
2011-07-18 00:22:30 +03:00
static struct net_device_ops ath6kl_netdev_ops = {
. ndo_open = ath6kl_open ,
. ndo_stop = ath6kl_close ,
. ndo_start_xmit = ath6kl_data_tx ,
. ndo_get_stats = ath6kl_get_stats ,
2011-12-27 14:28:00 -08:00
. ndo_set_features = ath6kl_set_features ,
2012-01-03 14:42:01 +05:30
. ndo_set_rx_mode = ath6kl_set_multicast_list ,
2011-07-18 00:22:30 +03:00
} ;
void init_netdev ( struct net_device * dev )
{
dev - > netdev_ops = & ath6kl_netdev_ops ;
2011-10-25 19:34:21 +05:30
dev - > destructor = free_netdev ;
2011-07-18 00:22:30 +03:00
dev - > watchdog_timeo = ATH6KL_TX_TIMEOUT ;
dev - > needed_headroom = ETH_HLEN ;
dev - > needed_headroom + = sizeof ( struct ath6kl_llc_snap_hdr ) +
sizeof ( struct wmi_data_hdr ) + HTC_HDR_LENGTH
2011-08-17 18:45:10 +05:30
+ WMI_MAX_TX_META_SZ + ATH6KL_HTC_ALIGN_BYTES ;
2011-07-18 00:22:30 +03:00
return ;
}