2010-03-17 14:25:25 +05:30
/*
* Copyright ( c ) 2010 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 "htc.h"
# ifdef CONFIG_ATH9K_HTC_DEBUGFS
static struct dentry * ath9k_debugfs_root ;
# endif
/*************/
/* Utilities */
/*************/
static void ath_update_txpow ( struct ath9k_htc_priv * priv )
{
struct ath_hw * ah = priv - > ah ;
if ( priv - > curtxpow ! = priv - > txpowlimit ) {
ath9k_hw_set_txpowerlimit ( ah , priv - > txpowlimit ) ;
/* read back in case value is clamped */
2010-06-12 17:22:29 +02:00
priv - > curtxpow = ath9k_hw_regulatory ( ah ) - > power_limit ;
2010-03-17 14:25:25 +05:30
}
}
/* HACK Alert: Use 11NG for 2.4, use 11NA for 5 */
static enum htc_phymode ath9k_htc_get_curmode ( struct ath9k_htc_priv * priv ,
struct ath9k_channel * ichan )
{
enum htc_phymode mode ;
mode = HTC_MODE_AUTO ;
switch ( ichan - > chanmode ) {
case CHANNEL_G :
case CHANNEL_G_HT20 :
case CHANNEL_G_HT40PLUS :
case CHANNEL_G_HT40MINUS :
mode = HTC_MODE_11NG ;
break ;
case CHANNEL_A :
case CHANNEL_A_HT20 :
case CHANNEL_A_HT40PLUS :
case CHANNEL_A_HT40MINUS :
mode = HTC_MODE_11NA ;
break ;
default :
break ;
}
return mode ;
}
2010-04-05 14:48:05 +05:30
static bool ath9k_htc_setpower ( struct ath9k_htc_priv * priv ,
enum ath9k_power_mode mode )
{
bool ret ;
mutex_lock ( & priv - > htc_pm_lock ) ;
ret = ath9k_hw_setpower ( priv - > ah , mode ) ;
mutex_unlock ( & priv - > htc_pm_lock ) ;
return ret ;
}
void ath9k_htc_ps_wakeup ( struct ath9k_htc_priv * priv )
{
mutex_lock ( & priv - > htc_pm_lock ) ;
if ( + + priv - > ps_usecount ! = 1 )
goto unlock ;
ath9k_hw_setpower ( priv - > ah , ATH9K_PM_AWAKE ) ;
unlock :
mutex_unlock ( & priv - > htc_pm_lock ) ;
}
void ath9k_htc_ps_restore ( struct ath9k_htc_priv * priv )
{
mutex_lock ( & priv - > htc_pm_lock ) ;
if ( - - priv - > ps_usecount ! = 0 )
goto unlock ;
2010-04-27 13:05:37 +05:30
if ( priv - > ps_idle )
ath9k_hw_setpower ( priv - > ah , ATH9K_PM_FULL_SLEEP ) ;
else if ( priv - > ps_enabled )
2010-04-05 14:48:05 +05:30
ath9k_hw_setpower ( priv - > ah , ATH9K_PM_NETWORK_SLEEP ) ;
2010-04-27 13:05:37 +05:30
2010-04-05 14:48:05 +05:30
unlock :
mutex_unlock ( & priv - > htc_pm_lock ) ;
}
void ath9k_ps_work ( struct work_struct * work )
{
struct ath9k_htc_priv * priv =
container_of ( work , struct ath9k_htc_priv ,
ps_work ) ;
ath9k_htc_setpower ( priv , ATH9K_PM_AWAKE ) ;
/* The chip wakes up after receiving the first beacon
while network sleep is enabled . For the driver to
be in sync with the hw , set the chip to awake and
only then set it to sleep .
*/
ath9k_htc_setpower ( priv , ATH9K_PM_NETWORK_SLEEP ) ;
}
2010-03-17 14:25:25 +05:30
static int ath9k_htc_set_channel ( struct ath9k_htc_priv * priv ,
struct ieee80211_hw * hw ,
struct ath9k_channel * hchan )
{
struct ath_hw * ah = priv - > ah ;
struct ath_common * common = ath9k_hw_common ( ah ) ;
struct ieee80211_conf * conf = & common - > hw - > conf ;
bool fastcc = true ;
struct ieee80211_channel * channel = hw - > conf . channel ;
enum htc_phymode mode ;
2010-04-16 11:54:03 +05:30
__be16 htc_mode ;
2010-03-17 14:25:25 +05:30
u8 cmd_rsp ;
int ret ;
if ( priv - > op_flags & OP_INVALID )
return - EIO ;
if ( priv - > op_flags & OP_FULL_RESET )
fastcc = false ;
/* Fiddle around with fastcc later on, for now just use full reset */
fastcc = false ;
2010-04-05 14:48:05 +05:30
ath9k_htc_ps_wakeup ( priv ) ;
2010-03-17 14:25:25 +05:30
htc_stop ( priv - > htc ) ;
WMI_CMD ( WMI_DISABLE_INTR_CMDID ) ;
WMI_CMD ( WMI_DRAIN_TXQ_ALL_CMDID ) ;
WMI_CMD ( WMI_STOP_RECV_CMDID ) ;
ath_print ( common , ATH_DBG_CONFIG ,
" (%u MHz) -> (%u MHz), HT: %d, HT40: %d \n " ,
priv - > ah - > curchan - > channel ,
channel - > center_freq , conf_is_ht ( conf ) , conf_is_ht40 ( conf ) ) ;
ret = ath9k_hw_reset ( ah , hchan , fastcc ) ;
if ( ret ) {
ath_print ( common , ATH_DBG_FATAL ,
" Unable to reset channel (%u Mhz) "
" reset status %d \n " , channel - > center_freq , ret ) ;
goto err ;
}
ath_update_txpow ( priv ) ;
WMI_CMD ( WMI_START_RECV_CMDID ) ;
if ( ret )
goto err ;
ath9k_host_rx_init ( priv ) ;
mode = ath9k_htc_get_curmode ( priv , hchan ) ;
htc_mode = cpu_to_be16 ( mode ) ;
WMI_CMD_BUF ( WMI_SET_MODE_CMDID , & htc_mode ) ;
if ( ret )
goto err ;
WMI_CMD ( WMI_ENABLE_INTR_CMDID ) ;
if ( ret )
goto err ;
htc_start ( priv - > htc ) ;
priv - > op_flags & = ~ OP_FULL_RESET ;
err :
2010-04-05 14:48:05 +05:30
ath9k_htc_ps_restore ( priv ) ;
2010-03-17 14:25:25 +05:30
return ret ;
}
static int ath9k_htc_add_monitor_interface ( struct ath9k_htc_priv * priv )
{
struct ath_common * common = ath9k_hw_common ( priv - > ah ) ;
struct ath9k_htc_target_vif hvif ;
int ret = 0 ;
u8 cmd_rsp ;
if ( priv - > nvifs > 0 )
return - ENOBUFS ;
memset ( & hvif , 0 , sizeof ( struct ath9k_htc_target_vif ) ) ;
memcpy ( & hvif . myaddr , common - > macaddr , ETH_ALEN ) ;
hvif . opmode = cpu_to_be32 ( HTC_M_MONITOR ) ;
priv - > ah - > opmode = NL80211_IFTYPE_MONITOR ;
hvif . index = priv - > nvifs ;
WMI_CMD_BUF ( WMI_VAP_CREATE_CMDID , & hvif ) ;
if ( ret )
return ret ;
priv - > nvifs + + ;
return 0 ;
}
static int ath9k_htc_remove_monitor_interface ( struct ath9k_htc_priv * priv )
{
struct ath_common * common = ath9k_hw_common ( priv - > ah ) ;
struct ath9k_htc_target_vif hvif ;
int ret = 0 ;
u8 cmd_rsp ;
memset ( & hvif , 0 , sizeof ( struct ath9k_htc_target_vif ) ) ;
memcpy ( & hvif . myaddr , common - > macaddr , ETH_ALEN ) ;
hvif . index = 0 ; /* Should do for now */
WMI_CMD_BUF ( WMI_VAP_REMOVE_CMDID , & hvif ) ;
priv - > nvifs - - ;
return ret ;
}
static int ath9k_htc_add_station ( struct ath9k_htc_priv * priv ,
struct ieee80211_vif * vif ,
struct ieee80211_sta * sta )
{
struct ath_common * common = ath9k_hw_common ( priv - > ah ) ;
struct ath9k_htc_target_sta tsta ;
struct ath9k_htc_vif * avp = ( struct ath9k_htc_vif * ) vif - > drv_priv ;
struct ath9k_htc_sta * ista ;
int ret ;
u8 cmd_rsp ;
if ( priv - > nstations > = ATH9K_HTC_MAX_STA )
return - ENOBUFS ;
memset ( & tsta , 0 , sizeof ( struct ath9k_htc_target_sta ) ) ;
if ( sta ) {
ista = ( struct ath9k_htc_sta * ) sta - > drv_priv ;
memcpy ( & tsta . macaddr , sta - > addr , ETH_ALEN ) ;
memcpy ( & tsta . bssid , common - > curbssid , ETH_ALEN ) ;
tsta . associd = common - > curaid ;
tsta . is_vif_sta = 0 ;
tsta . valid = true ;
ista - > index = priv - > nstations ;
} else {
memcpy ( & tsta . macaddr , vif - > addr , ETH_ALEN ) ;
tsta . is_vif_sta = 1 ;
}
tsta . sta_index = priv - > nstations ;
tsta . vif_index = avp - > index ;
tsta . maxampdu = 0xffff ;
if ( sta & & sta - > ht_cap . ht_supported )
tsta . flags = cpu_to_be16 ( ATH_HTC_STA_HT ) ;
WMI_CMD_BUF ( WMI_NODE_CREATE_CMDID , & tsta ) ;
if ( ret ) {
if ( sta )
ath_print ( common , ATH_DBG_FATAL ,
" Unable to add station entry for: %pM \n " , sta - > addr ) ;
return ret ;
}
if ( sta )
ath_print ( common , ATH_DBG_CONFIG ,
" Added a station entry for: %pM (idx: %d) \n " ,
sta - > addr , tsta . sta_index ) ;
priv - > nstations + + ;
return 0 ;
}
static int ath9k_htc_remove_station ( struct ath9k_htc_priv * priv ,
struct ieee80211_vif * vif ,
struct ieee80211_sta * sta )
{
struct ath_common * common = ath9k_hw_common ( priv - > ah ) ;
struct ath9k_htc_sta * ista ;
int ret ;
u8 cmd_rsp , sta_idx ;
if ( sta ) {
ista = ( struct ath9k_htc_sta * ) sta - > drv_priv ;
sta_idx = ista - > index ;
} else {
sta_idx = 0 ;
}
WMI_CMD_BUF ( WMI_NODE_REMOVE_CMDID , & sta_idx ) ;
if ( ret ) {
if ( sta )
ath_print ( common , ATH_DBG_FATAL ,
" Unable to remove station entry for: %pM \n " ,
sta - > addr ) ;
return ret ;
}
if ( sta )
ath_print ( common , ATH_DBG_CONFIG ,
" Removed a station entry for: %pM (idx: %d) \n " ,
sta - > addr , sta_idx ) ;
priv - > nstations - - ;
return 0 ;
}
static int ath9k_htc_update_cap_target ( struct ath9k_htc_priv * priv )
{
struct ath9k_htc_cap_target tcap ;
int ret ;
u8 cmd_rsp ;
memset ( & tcap , 0 , sizeof ( struct ath9k_htc_cap_target ) ) ;
/* FIXME: Values are hardcoded */
tcap . flags = 0x240c40 ;
tcap . flags_ext = 0x80601000 ;
tcap . ampdu_limit = 0xffff0000 ;
tcap . ampdu_subframes = 20 ;
2010-06-02 15:53:43 +05:30
tcap . tx_chainmask_legacy = priv - > ah - > caps . tx_chainmask ;
2010-03-17 14:25:25 +05:30
tcap . protmode = 1 ;
2010-06-02 15:53:43 +05:30
tcap . tx_chainmask = priv - > ah - > caps . tx_chainmask ;
2010-03-17 14:25:25 +05:30
WMI_CMD_BUF ( WMI_TARGET_IC_UPDATE_CMDID , & tcap ) ;
return ret ;
}
2010-05-17 12:01:16 +05:30
static void ath9k_htc_setup_rate ( struct ath9k_htc_priv * priv ,
struct ieee80211_sta * sta ,
struct ath9k_htc_target_rate * trate )
2010-03-17 14:25:25 +05:30
{
struct ath9k_htc_sta * ista = ( struct ath9k_htc_sta * ) sta - > drv_priv ;
struct ieee80211_supported_band * sband ;
u32 caps = 0 ;
2010-05-17 12:01:16 +05:30
int i , j ;
2010-03-17 14:25:25 +05:30
2010-06-02 15:53:50 +05:30
sband = priv - > hw - > wiphy - > bands [ priv - > hw - > conf . channel - > band ] ;
2010-03-17 14:25:25 +05:30
for ( i = 0 , j = 0 ; i < sband - > n_bitrates ; i + + ) {
if ( sta - > supp_rates [ sband - > band ] & BIT ( i ) ) {
2010-05-17 12:01:16 +05:30
trate - > rates . legacy_rates . rs_rates [ j ]
2010-03-17 14:25:25 +05:30
= ( sband - > bitrates [ i ] . bitrate * 2 ) / 10 ;
j + + ;
}
}
2010-05-17 12:01:16 +05:30
trate - > rates . legacy_rates . rs_nrates = j ;
2010-03-17 14:25:25 +05:30
if ( sta - > ht_cap . ht_supported ) {
for ( i = 0 , j = 0 ; i < 77 ; i + + ) {
if ( sta - > ht_cap . mcs . rx_mask [ i / 8 ] & ( 1 < < ( i % 8 ) ) )
2010-05-17 12:01:16 +05:30
trate - > rates . ht_rates . rs_rates [ j + + ] = i ;
2010-03-17 14:25:25 +05:30
if ( j = = ATH_HTC_RATE_MAX )
break ;
}
2010-05-17 12:01:16 +05:30
trate - > rates . ht_rates . rs_nrates = j ;
2010-03-17 14:25:25 +05:30
caps = WLAN_RC_HT_FLAG ;
2010-06-12 17:22:33 +02:00
if ( sta - > ht_cap . mcs . rx_mask [ 1 ] )
caps | = WLAN_RC_DS_FLAG ;
2010-03-17 14:25:25 +05:30
if ( sta - > ht_cap . cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 )
caps | = WLAN_RC_40_FLAG ;
2010-05-17 12:01:19 +05:30
if ( conf_is_ht40 ( & priv - > hw - > conf ) & &
( sta - > ht_cap . cap & IEEE80211_HT_CAP_SGI_40 ) )
caps | = WLAN_RC_SGI_FLAG ;
else if ( conf_is_ht20 ( & priv - > hw - > conf ) & &
( sta - > ht_cap . cap & IEEE80211_HT_CAP_SGI_20 ) )
2010-03-17 14:25:25 +05:30
caps | = WLAN_RC_SGI_FLAG ;
}
2010-05-17 12:01:16 +05:30
trate - > sta_index = ista - > index ;
trate - > isnew = 1 ;
trate - > capflags = cpu_to_be32 ( caps ) ;
}
static int ath9k_htc_send_rate_cmd ( struct ath9k_htc_priv * priv ,
struct ath9k_htc_target_rate * trate )
{
struct ath_common * common = ath9k_hw_common ( priv - > ah ) ;
int ret ;
u8 cmd_rsp ;
2010-03-17 14:25:25 +05:30
2010-05-17 12:01:16 +05:30
WMI_CMD_BUF ( WMI_RC_RATE_UPDATE_CMDID , trate ) ;
2010-03-17 14:25:25 +05:30
if ( ret ) {
ath_print ( common , ATH_DBG_FATAL ,
" Unable to initialize Rate information on target \n " ) ;
}
2010-05-17 12:01:16 +05:30
return ret ;
2010-03-17 14:25:25 +05:30
}
2010-05-17 12:01:16 +05:30
static void ath9k_htc_init_rate ( struct ath9k_htc_priv * priv ,
struct ieee80211_sta * sta )
2010-03-17 14:25:25 +05:30
{
struct ath_common * common = ath9k_hw_common ( priv - > ah ) ;
2010-05-17 12:01:16 +05:30
struct ath9k_htc_target_rate trate ;
2010-03-17 14:25:25 +05:30
int ret ;
2010-05-17 12:01:16 +05:30
memset ( & trate , 0 , sizeof ( struct ath9k_htc_target_rate ) ) ;
ath9k_htc_setup_rate ( priv , sta , & trate ) ;
ret = ath9k_htc_send_rate_cmd ( priv , & trate ) ;
if ( ! ret )
ath_print ( common , ATH_DBG_CONFIG ,
" Updated target sta: %pM, rate caps: 0x%X \n " ,
sta - > addr , be32_to_cpu ( trate . capflags ) ) ;
2010-03-17 14:25:25 +05:30
}
2010-05-17 12:01:18 +05:30
static void ath9k_htc_update_rate ( struct ath9k_htc_priv * priv ,
struct ieee80211_vif * vif ,
struct ieee80211_bss_conf * bss_conf )
{
struct ath_common * common = ath9k_hw_common ( priv - > ah ) ;
struct ath9k_htc_target_rate trate ;
struct ieee80211_sta * sta ;
int ret ;
memset ( & trate , 0 , sizeof ( struct ath9k_htc_target_rate ) ) ;
rcu_read_lock ( ) ;
sta = ieee80211_find_sta ( vif , bss_conf - > bssid ) ;
if ( ! sta ) {
rcu_read_unlock ( ) ;
return ;
}
ath9k_htc_setup_rate ( priv , sta , & trate ) ;
rcu_read_unlock ( ) ;
ret = ath9k_htc_send_rate_cmd ( priv , & trate ) ;
if ( ! ret )
ath_print ( common , ATH_DBG_CONFIG ,
" Updated target sta: %pM, rate caps: 0x%X \n " ,
bss_conf - > bssid , be32_to_cpu ( trate . capflags ) ) ;
}
2010-06-15 10:24:37 +05:30
int ath9k_htc_tx_aggr_oper ( struct ath9k_htc_priv * priv ,
struct ieee80211_vif * vif ,
struct ieee80211_sta * sta ,
enum ieee80211_ampdu_mlme_action action , u16 tid )
2010-03-17 14:25:25 +05:30
{
struct ath_common * common = ath9k_hw_common ( priv - > ah ) ;
struct ath9k_htc_target_aggr aggr ;
2010-05-08 18:23:20 +02:00
struct ath9k_htc_sta * ista ;
2010-03-17 14:25:25 +05:30
int ret = 0 ;
u8 cmd_rsp ;
2010-05-08 18:24:02 +02:00
if ( tid > = ATH9K_HTC_MAX_TID )
2010-03-17 14:25:25 +05:30
return - EINVAL ;
2010-03-29 16:07:11 +05:30
memset ( & aggr , 0 , sizeof ( struct ath9k_htc_target_aggr ) ) ;
ista = ( struct ath9k_htc_sta * ) sta - > drv_priv ;
2010-03-17 14:25:25 +05:30
aggr . sta_index = ista - > index ;
2010-06-15 10:24:37 +05:30
aggr . tidno = tid & 0xf ;
aggr . aggr_enable = ( action = = IEEE80211_AMPDU_TX_START ) ? true : false ;
2010-03-17 14:25:25 +05:30
WMI_CMD_BUF ( WMI_TX_AGGR_ENABLE_CMDID , & aggr ) ;
if ( ret )
ath_print ( common , ATH_DBG_CONFIG ,
" Unable to %s TX aggregation for (%pM, %d) \n " ,
2010-06-15 10:24:37 +05:30
( aggr . aggr_enable ) ? " start " : " stop " , sta - > addr , tid ) ;
2010-03-17 14:25:25 +05:30
else
ath_print ( common , ATH_DBG_CONFIG ,
2010-06-15 10:24:37 +05:30
" %s TX aggregation for (%pM, %d) \n " ,
( aggr . aggr_enable ) ? " Starting " : " Stopping " ,
sta - > addr , tid ) ;
2010-03-17 14:25:25 +05:30
2010-06-15 10:24:37 +05:30
spin_lock_bh ( & priv - > tx_lock ) ;
ista - > tid_state [ tid ] = ( aggr . aggr_enable & & ! ret ) ? AGGR_START : AGGR_STOP ;
spin_unlock_bh ( & priv - > tx_lock ) ;
2010-03-17 14:25:25 +05:30
2010-06-15 10:24:37 +05:30
return ret ;
2010-03-17 14:25:25 +05:30
}
/*********/
/* DEBUG */
/*********/
# ifdef CONFIG_ATH9K_HTC_DEBUGFS
static int ath9k_debugfs_open ( struct inode * inode , struct file * file )
{
file - > private_data = inode - > i_private ;
return 0 ;
}
static ssize_t read_file_tgt_stats ( struct file * file , char __user * user_buf ,
size_t count , loff_t * ppos )
{
2010-07-12 13:50:06 -07:00
struct ath9k_htc_priv * priv = file - > private_data ;
2010-03-17 14:25:25 +05:30
struct ath9k_htc_target_stats cmd_rsp ;
char buf [ 512 ] ;
unsigned int len = 0 ;
int ret = 0 ;
memset ( & cmd_rsp , 0 , sizeof ( cmd_rsp ) ) ;
WMI_CMD ( WMI_TGT_STATS_CMDID ) ;
if ( ret )
return - EINVAL ;
len + = snprintf ( buf + len , sizeof ( buf ) - len ,
" %19s : %10u \n " , " TX Short Retries " ,
be32_to_cpu ( cmd_rsp . tx_shortretry ) ) ;
len + = snprintf ( buf + len , sizeof ( buf ) - len ,
" %19s : %10u \n " , " TX Long Retries " ,
be32_to_cpu ( cmd_rsp . tx_longretry ) ) ;
len + = snprintf ( buf + len , sizeof ( buf ) - len ,
" %19s : %10u \n " , " TX Xretries " ,
be32_to_cpu ( cmd_rsp . tx_xretries ) ) ;
len + = snprintf ( buf + len , sizeof ( buf ) - len ,
" %19s : %10u \n " , " TX Unaggr. Xretries " ,
be32_to_cpu ( cmd_rsp . ht_txunaggr_xretry ) ) ;
len + = snprintf ( buf + len , sizeof ( buf ) - len ,
" %19s : %10u \n " , " TX Xretries (HT) " ,
be32_to_cpu ( cmd_rsp . ht_tx_xretries ) ) ;
len + = snprintf ( buf + len , sizeof ( buf ) - len ,
" %19s : %10u \n " , " TX Rate " , priv - > debug . txrate ) ;
return simple_read_from_buffer ( user_buf , count , ppos , buf , len ) ;
}
static const struct file_operations fops_tgt_stats = {
. read = read_file_tgt_stats ,
. open = ath9k_debugfs_open ,
. owner = THIS_MODULE
} ;
static ssize_t read_file_xmit ( struct file * file , char __user * user_buf ,
size_t count , loff_t * ppos )
{
2010-07-12 13:50:06 -07:00
struct ath9k_htc_priv * priv = file - > private_data ;
2010-03-17 14:25:25 +05:30
char buf [ 512 ] ;
unsigned int len = 0 ;
len + = snprintf ( buf + len , sizeof ( buf ) - len ,
" %20s : %10u \n " , " Buffers queued " ,
priv - > debug . tx_stats . buf_queued ) ;
len + = snprintf ( buf + len , sizeof ( buf ) - len ,
" %20s : %10u \n " , " Buffers completed " ,
priv - > debug . tx_stats . buf_completed ) ;
len + = snprintf ( buf + len , sizeof ( buf ) - len ,
" %20s : %10u \n " , " SKBs queued " ,
priv - > debug . tx_stats . skb_queued ) ;
len + = snprintf ( buf + len , sizeof ( buf ) - len ,
" %20s : %10u \n " , " SKBs completed " ,
priv - > debug . tx_stats . skb_completed ) ;
2010-04-16 11:54:00 +05:30
len + = snprintf ( buf + len , sizeof ( buf ) - len ,
" %20s : %10u \n " , " SKBs dropped " ,
priv - > debug . tx_stats . skb_dropped ) ;
2010-03-17 14:25:25 +05:30
2010-05-14 11:18:54 +05:30
len + = snprintf ( buf + len , sizeof ( buf ) - len ,
" %20s : %10u \n " , " BE queued " ,
priv - > debug . tx_stats . queue_stats [ WME_AC_BE ] ) ;
len + = snprintf ( buf + len , sizeof ( buf ) - len ,
" %20s : %10u \n " , " BK queued " ,
priv - > debug . tx_stats . queue_stats [ WME_AC_BK ] ) ;
len + = snprintf ( buf + len , sizeof ( buf ) - len ,
" %20s : %10u \n " , " VI queued " ,
priv - > debug . tx_stats . queue_stats [ WME_AC_VI ] ) ;
len + = snprintf ( buf + len , sizeof ( buf ) - len ,
" %20s : %10u \n " , " VO queued " ,
priv - > debug . tx_stats . queue_stats [ WME_AC_VO ] ) ;
2010-03-17 14:25:25 +05:30
return simple_read_from_buffer ( user_buf , count , ppos , buf , len ) ;
}
static const struct file_operations fops_xmit = {
. read = read_file_xmit ,
. open = ath9k_debugfs_open ,
. owner = THIS_MODULE
} ;
static ssize_t read_file_recv ( struct file * file , char __user * user_buf ,
size_t count , loff_t * ppos )
{
2010-07-12 13:50:06 -07:00
struct ath9k_htc_priv * priv = file - > private_data ;
2010-03-17 14:25:25 +05:30
char buf [ 512 ] ;
unsigned int len = 0 ;
len + = snprintf ( buf + len , sizeof ( buf ) - len ,
" %20s : %10u \n " , " SKBs allocated " ,
priv - > debug . rx_stats . skb_allocated ) ;
len + = snprintf ( buf + len , sizeof ( buf ) - len ,
" %20s : %10u \n " , " SKBs completed " ,
priv - > debug . rx_stats . skb_completed ) ;
len + = snprintf ( buf + len , sizeof ( buf ) - len ,
" %20s : %10u \n " , " SKBs Dropped " ,
priv - > debug . rx_stats . skb_dropped ) ;
return simple_read_from_buffer ( user_buf , count , ppos , buf , len ) ;
}
static const struct file_operations fops_recv = {
. read = read_file_recv ,
. open = ath9k_debugfs_open ,
. owner = THIS_MODULE
} ;
2010-03-24 13:42:13 +05:30
int ath9k_htc_init_debug ( struct ath_hw * ah )
2010-03-17 14:25:25 +05:30
{
struct ath_common * common = ath9k_hw_common ( ah ) ;
struct ath9k_htc_priv * priv = ( struct ath9k_htc_priv * ) common - > priv ;
if ( ! ath9k_debugfs_root )
return - ENOENT ;
priv - > debug . debugfs_phy = debugfs_create_dir ( wiphy_name ( priv - > hw - > wiphy ) ,
ath9k_debugfs_root ) ;
if ( ! priv - > debug . debugfs_phy )
goto err ;
priv - > debug . debugfs_tgt_stats = debugfs_create_file ( " tgt_stats " , S_IRUSR ,
priv - > debug . debugfs_phy ,
priv , & fops_tgt_stats ) ;
if ( ! priv - > debug . debugfs_tgt_stats )
goto err ;
priv - > debug . debugfs_xmit = debugfs_create_file ( " xmit " , S_IRUSR ,
priv - > debug . debugfs_phy ,
priv , & fops_xmit ) ;
if ( ! priv - > debug . debugfs_xmit )
goto err ;
priv - > debug . debugfs_recv = debugfs_create_file ( " recv " , S_IRUSR ,
priv - > debug . debugfs_phy ,
priv , & fops_recv ) ;
if ( ! priv - > debug . debugfs_recv )
goto err ;
return 0 ;
err :
2010-03-24 13:42:13 +05:30
ath9k_htc_exit_debug ( ah ) ;
2010-03-17 14:25:25 +05:30
return - ENOMEM ;
}
2010-03-24 13:42:13 +05:30
void ath9k_htc_exit_debug ( struct ath_hw * ah )
2010-03-17 14:25:25 +05:30
{
struct ath_common * common = ath9k_hw_common ( ah ) ;
struct ath9k_htc_priv * priv = ( struct ath9k_htc_priv * ) common - > priv ;
debugfs_remove ( priv - > debug . debugfs_recv ) ;
debugfs_remove ( priv - > debug . debugfs_xmit ) ;
debugfs_remove ( priv - > debug . debugfs_tgt_stats ) ;
debugfs_remove ( priv - > debug . debugfs_phy ) ;
}
2010-03-24 13:42:13 +05:30
int ath9k_htc_debug_create_root ( void )
2010-03-17 14:25:25 +05:30
{
ath9k_debugfs_root = debugfs_create_dir ( KBUILD_MODNAME , NULL ) ;
if ( ! ath9k_debugfs_root )
return - ENOENT ;
return 0 ;
}
2010-03-24 13:42:13 +05:30
void ath9k_htc_debug_remove_root ( void )
2010-03-17 14:25:25 +05:30
{
debugfs_remove ( ath9k_debugfs_root ) ;
ath9k_debugfs_root = NULL ;
}
# endif /* CONFIG_ATH9K_HTC_DEBUGFS */
/*******/
/* ANI */
/*******/
static void ath_start_ani ( struct ath9k_htc_priv * priv )
{
struct ath_common * common = ath9k_hw_common ( priv - > ah ) ;
unsigned long timestamp = jiffies_to_msecs ( jiffies ) ;
common - > ani . longcal_timer = timestamp ;
common - > ani . shortcal_timer = timestamp ;
common - > ani . checkani_timer = timestamp ;
ieee80211_queue_delayed_work ( common - > hw , & priv - > ath9k_ani_work ,
msecs_to_jiffies ( ATH_ANI_POLLINTERVAL ) ) ;
}
void ath9k_ani_work ( struct work_struct * work )
{
struct ath9k_htc_priv * priv =
container_of ( work , struct ath9k_htc_priv ,
ath9k_ani_work . work ) ;
struct ath_hw * ah = priv - > ah ;
struct ath_common * common = ath9k_hw_common ( ah ) ;
bool longcal = false ;
bool shortcal = false ;
bool aniflag = false ;
unsigned int timestamp = jiffies_to_msecs ( jiffies ) ;
u32 cal_interval , short_cal_interval ;
short_cal_interval = ATH_STA_SHORT_CALINTERVAL ;
2010-04-05 14:48:05 +05:30
/* Only calibrate if awake */
if ( ah - > power_mode ! = ATH9K_PM_AWAKE )
goto set_timer ;
2010-03-17 14:25:25 +05:30
/* Long calibration runs independently of short calibration. */
if ( ( timestamp - common - > ani . longcal_timer ) > = ATH_LONG_CALINTERVAL ) {
longcal = true ;
ath_print ( common , ATH_DBG_ANI , " longcal @%lu \n " , jiffies ) ;
common - > ani . longcal_timer = timestamp ;
}
/* Short calibration applies only while caldone is false */
if ( ! common - > ani . caldone ) {
if ( ( timestamp - common - > ani . shortcal_timer ) > =
short_cal_interval ) {
shortcal = true ;
ath_print ( common , ATH_DBG_ANI ,
" shortcal @%lu \n " , jiffies ) ;
common - > ani . shortcal_timer = timestamp ;
common - > ani . resetcal_timer = timestamp ;
}
} else {
if ( ( timestamp - common - > ani . resetcal_timer ) > =
ATH_RESTART_CALINTERVAL ) {
common - > ani . caldone = ath9k_hw_reset_calvalid ( ah ) ;
if ( common - > ani . caldone )
common - > ani . resetcal_timer = timestamp ;
}
}
/* Verify whether we must check ANI */
if ( ( timestamp - common - > ani . checkani_timer ) > = ATH_ANI_POLLINTERVAL ) {
aniflag = true ;
common - > ani . checkani_timer = timestamp ;
}
/* Skip all processing if there's nothing to do. */
if ( longcal | | shortcal | | aniflag ) {
2010-04-05 14:48:05 +05:30
ath9k_htc_ps_wakeup ( priv ) ;
2010-03-17 14:25:25 +05:30
/* Call ANI routine if necessary */
if ( aniflag )
ath9k_hw_ani_monitor ( ah , ah - > curchan ) ;
/* Perform calibration if necessary */
if ( longcal | | shortcal ) {
common - > ani . caldone =
ath9k_hw_calibrate ( ah , ah - > curchan ,
common - > rx_chainmask ,
longcal ) ;
if ( longcal )
common - > ani . noise_floor =
ath9k_hw_getchan_noise ( ah , ah - > curchan ) ;
ath_print ( common , ATH_DBG_ANI ,
" calibrate chan %u/%x nf: %d \n " ,
ah - > curchan - > channel ,
ah - > curchan - > channelFlags ,
common - > ani . noise_floor ) ;
}
2010-04-05 14:48:05 +05:30
ath9k_htc_ps_restore ( priv ) ;
2010-03-17 14:25:25 +05:30
}
2010-04-05 14:48:05 +05:30
set_timer :
2010-03-17 14:25:25 +05:30
/*
* Set timer interval based on previous results .
* The interval must be the shortest necessary to satisfy ANI ,
* short calibration and long calibration .
*/
cal_interval = ATH_LONG_CALINTERVAL ;
if ( priv - > ah - > config . enable_ani )
cal_interval = min ( cal_interval , ( u32 ) ATH_ANI_POLLINTERVAL ) ;
if ( ! common - > ani . caldone )
cal_interval = min ( cal_interval , ( u32 ) short_cal_interval ) ;
ieee80211_queue_delayed_work ( common - > hw , & priv - > ath9k_ani_work ,
msecs_to_jiffies ( cal_interval ) ) ;
}
/*******/
/* LED */
/*******/
static void ath9k_led_blink_work ( struct work_struct * work )
{
struct ath9k_htc_priv * priv = container_of ( work , struct ath9k_htc_priv ,
ath9k_led_blink_work . work ) ;
if ( ! ( priv - > op_flags & OP_LED_ASSOCIATED ) )
return ;
if ( ( priv - > led_on_duration = = ATH_LED_ON_DURATION_IDLE ) | |
( priv - > led_off_duration = = ATH_LED_OFF_DURATION_IDLE ) )
ath9k_hw_set_gpio ( priv - > ah , priv - > ah - > led_pin , 0 ) ;
else
ath9k_hw_set_gpio ( priv - > ah , priv - > ah - > led_pin ,
( priv - > op_flags & OP_LED_ON ) ? 1 : 0 ) ;
ieee80211_queue_delayed_work ( priv - > hw ,
& priv - > ath9k_led_blink_work ,
( priv - > op_flags & OP_LED_ON ) ?
msecs_to_jiffies ( priv - > led_off_duration ) :
msecs_to_jiffies ( priv - > led_on_duration ) ) ;
priv - > led_on_duration = priv - > led_on_cnt ?
max ( ( ATH_LED_ON_DURATION_IDLE - priv - > led_on_cnt ) , 25 ) :
ATH_LED_ON_DURATION_IDLE ;
priv - > led_off_duration = priv - > led_off_cnt ?
max ( ( ATH_LED_OFF_DURATION_IDLE - priv - > led_off_cnt ) , 10 ) :
ATH_LED_OFF_DURATION_IDLE ;
priv - > led_on_cnt = priv - > led_off_cnt = 0 ;
if ( priv - > op_flags & OP_LED_ON )
priv - > op_flags & = ~ OP_LED_ON ;
else
priv - > op_flags | = OP_LED_ON ;
}
static void ath9k_led_brightness_work ( struct work_struct * work )
{
struct ath_led * led = container_of ( work , struct ath_led ,
brightness_work . work ) ;
struct ath9k_htc_priv * priv = led - > priv ;
switch ( led - > brightness ) {
case LED_OFF :
if ( led - > led_type = = ATH_LED_ASSOC | |
led - > led_type = = ATH_LED_RADIO ) {
ath9k_hw_set_gpio ( priv - > ah , priv - > ah - > led_pin ,
( led - > led_type = = ATH_LED_RADIO ) ) ;
priv - > op_flags & = ~ OP_LED_ASSOCIATED ;
if ( led - > led_type = = ATH_LED_RADIO )
priv - > op_flags & = ~ OP_LED_ON ;
} else {
priv - > led_off_cnt + + ;
}
break ;
case LED_FULL :
if ( led - > led_type = = ATH_LED_ASSOC ) {
priv - > op_flags | = OP_LED_ASSOCIATED ;
ieee80211_queue_delayed_work ( priv - > hw ,
& priv - > ath9k_led_blink_work , 0 ) ;
} else if ( led - > led_type = = ATH_LED_RADIO ) {
ath9k_hw_set_gpio ( priv - > ah , priv - > ah - > led_pin , 0 ) ;
priv - > op_flags | = OP_LED_ON ;
} else {
priv - > led_on_cnt + + ;
}
break ;
default :
break ;
}
}
static void ath9k_led_brightness ( struct led_classdev * led_cdev ,
enum led_brightness brightness )
{
struct ath_led * led = container_of ( led_cdev , struct ath_led , led_cdev ) ;
struct ath9k_htc_priv * priv = led - > priv ;
led - > brightness = brightness ;
if ( ! ( priv - > op_flags & OP_LED_DEINIT ) )
ieee80211_queue_delayed_work ( priv - > hw ,
& led - > brightness_work , 0 ) ;
}
static void ath9k_led_stop_brightness ( struct ath9k_htc_priv * priv )
{
cancel_delayed_work_sync ( & priv - > radio_led . brightness_work ) ;
cancel_delayed_work_sync ( & priv - > assoc_led . brightness_work ) ;
cancel_delayed_work_sync ( & priv - > tx_led . brightness_work ) ;
cancel_delayed_work_sync ( & priv - > rx_led . brightness_work ) ;
}
static int ath9k_register_led ( struct ath9k_htc_priv * priv , struct ath_led * led ,
char * trigger )
{
int ret ;
led - > priv = priv ;
led - > led_cdev . name = led - > name ;
led - > led_cdev . default_trigger = trigger ;
led - > led_cdev . brightness_set = ath9k_led_brightness ;
ret = led_classdev_register ( wiphy_dev ( priv - > hw - > wiphy ) , & led - > led_cdev ) ;
if ( ret )
ath_print ( ath9k_hw_common ( priv - > ah ) , ATH_DBG_FATAL ,
" Failed to register led:%s " , led - > name ) ;
else
led - > registered = 1 ;
INIT_DELAYED_WORK ( & led - > brightness_work , ath9k_led_brightness_work ) ;
return ret ;
}
static void ath9k_unregister_led ( struct ath_led * led )
{
if ( led - > registered ) {
led_classdev_unregister ( & led - > led_cdev ) ;
led - > registered = 0 ;
}
}
void ath9k_deinit_leds ( struct ath9k_htc_priv * priv )
{
priv - > op_flags | = OP_LED_DEINIT ;
ath9k_unregister_led ( & priv - > assoc_led ) ;
priv - > op_flags & = ~ OP_LED_ASSOCIATED ;
ath9k_unregister_led ( & priv - > tx_led ) ;
ath9k_unregister_led ( & priv - > rx_led ) ;
ath9k_unregister_led ( & priv - > radio_led ) ;
}
void ath9k_init_leds ( struct ath9k_htc_priv * priv )
{
char * trigger ;
int ret ;
if ( AR_SREV_9287 ( priv - > ah ) )
priv - > ah - > led_pin = ATH_LED_PIN_9287 ;
else if ( AR_SREV_9271 ( priv - > ah ) )
priv - > ah - > led_pin = ATH_LED_PIN_9271 ;
2010-06-30 14:46:31 +05:30
else if ( AR_DEVID_7010 ( priv - > ah ) )
priv - > ah - > led_pin = ATH_LED_PIN_7010 ;
2010-03-17 14:25:25 +05:30
else
priv - > ah - > led_pin = ATH_LED_PIN_DEF ;
/* Configure gpio 1 for output */
ath9k_hw_cfg_output ( priv - > ah , priv - > ah - > led_pin ,
AR_GPIO_OUTPUT_MUX_AS_OUTPUT ) ;
/* LED off, active low */
ath9k_hw_set_gpio ( priv - > ah , priv - > ah - > led_pin , 1 ) ;
INIT_DELAYED_WORK ( & priv - > ath9k_led_blink_work , ath9k_led_blink_work ) ;
trigger = ieee80211_get_radio_led_name ( priv - > hw ) ;
snprintf ( priv - > radio_led . name , sizeof ( priv - > radio_led . name ) ,
" ath9k-%s::radio " , wiphy_name ( priv - > hw - > wiphy ) ) ;
ret = ath9k_register_led ( priv , & priv - > radio_led , trigger ) ;
priv - > radio_led . led_type = ATH_LED_RADIO ;
if ( ret )
goto fail ;
trigger = ieee80211_get_assoc_led_name ( priv - > hw ) ;
snprintf ( priv - > assoc_led . name , sizeof ( priv - > assoc_led . name ) ,
" ath9k-%s::assoc " , wiphy_name ( priv - > hw - > wiphy ) ) ;
ret = ath9k_register_led ( priv , & priv - > assoc_led , trigger ) ;
priv - > assoc_led . led_type = ATH_LED_ASSOC ;
if ( ret )
goto fail ;
trigger = ieee80211_get_tx_led_name ( priv - > hw ) ;
snprintf ( priv - > tx_led . name , sizeof ( priv - > tx_led . name ) ,
" ath9k-%s::tx " , wiphy_name ( priv - > hw - > wiphy ) ) ;
ret = ath9k_register_led ( priv , & priv - > tx_led , trigger ) ;
priv - > tx_led . led_type = ATH_LED_TX ;
if ( ret )
goto fail ;
trigger = ieee80211_get_rx_led_name ( priv - > hw ) ;
snprintf ( priv - > rx_led . name , sizeof ( priv - > rx_led . name ) ,
" ath9k-%s::rx " , wiphy_name ( priv - > hw - > wiphy ) ) ;
ret = ath9k_register_led ( priv , & priv - > rx_led , trigger ) ;
priv - > rx_led . led_type = ATH_LED_RX ;
if ( ret )
goto fail ;
priv - > op_flags & = ~ OP_LED_DEINIT ;
return ;
fail :
cancel_delayed_work_sync ( & priv - > ath9k_led_blink_work ) ;
ath9k_deinit_leds ( priv ) ;
}
/*******************/
/* Rfkill */
/*******************/
static bool ath_is_rfkill_set ( struct ath9k_htc_priv * priv )
{
return ath9k_hw_gpio_get ( priv - > ah , priv - > ah - > rfkill_gpio ) = =
priv - > ah - > rfkill_polarity ;
}
static void ath9k_htc_rfkill_poll_state ( struct ieee80211_hw * hw )
{
struct ath9k_htc_priv * priv = hw - > priv ;
bool blocked = ! ! ath_is_rfkill_set ( priv ) ;
wiphy_rfkill_set_hw_state ( hw - > wiphy , blocked ) ;
}
void ath9k_start_rfkill_poll ( struct ath9k_htc_priv * priv )
{
if ( priv - > ah - > caps . hw_caps & ATH9K_HW_CAP_RFSILENT )
wiphy_rfkill_start_polling ( priv - > hw - > wiphy ) ;
}
2010-06-01 15:14:11 +05:30
static void ath9k_htc_radio_enable ( struct ieee80211_hw * hw )
{
struct ath9k_htc_priv * priv = hw - > priv ;
struct ath_hw * ah = priv - > ah ;
struct ath_common * common = ath9k_hw_common ( ah ) ;
int ret ;
u8 cmd_rsp ;
if ( ! ah - > curchan )
ah - > curchan = ath9k_cmn_get_curchannel ( hw , ah ) ;
/* Reset the HW */
ret = ath9k_hw_reset ( ah , ah - > curchan , false ) ;
if ( ret ) {
ath_print ( common , ATH_DBG_FATAL ,
" Unable to reset hardware; reset status %d "
" (freq %u MHz) \n " , ret , ah - > curchan - > channel ) ;
}
ath_update_txpow ( priv ) ;
/* Start RX */
WMI_CMD ( WMI_START_RECV_CMDID ) ;
ath9k_host_rx_init ( priv ) ;
/* Start TX */
htc_start ( priv - > htc ) ;
spin_lock_bh ( & priv - > tx_lock ) ;
priv - > tx_queues_stop = false ;
spin_unlock_bh ( & priv - > tx_lock ) ;
ieee80211_wake_queues ( hw ) ;
WMI_CMD ( WMI_ENABLE_INTR_CMDID ) ;
/* Enable LED */
ath9k_hw_cfg_output ( ah , ah - > led_pin ,
AR_GPIO_OUTPUT_MUX_AS_OUTPUT ) ;
ath9k_hw_set_gpio ( ah , ah - > led_pin , 0 ) ;
}
static void ath9k_htc_radio_disable ( struct ieee80211_hw * hw )
{
struct ath9k_htc_priv * priv = hw - > priv ;
struct ath_hw * ah = priv - > ah ;
struct ath_common * common = ath9k_hw_common ( ah ) ;
int ret ;
u8 cmd_rsp ;
ath9k_htc_ps_wakeup ( priv ) ;
/* Disable LED */
ath9k_hw_set_gpio ( ah , ah - > led_pin , 1 ) ;
ath9k_hw_cfg_gpio_input ( ah , ah - > led_pin ) ;
WMI_CMD ( WMI_DISABLE_INTR_CMDID ) ;
/* Stop TX */
ieee80211_stop_queues ( hw ) ;
htc_stop ( priv - > htc ) ;
WMI_CMD ( WMI_DRAIN_TXQ_ALL_CMDID ) ;
skb_queue_purge ( & priv - > tx_queue ) ;
/* Stop RX */
WMI_CMD ( WMI_STOP_RECV_CMDID ) ;
2010-06-01 15:14:18 +05:30
/*
* The MIB counters have to be disabled here ,
* since the target doesn ' t do it .
*/
ath9k_hw_disable_mib_counters ( ah ) ;
2010-06-01 15:14:11 +05:30
if ( ! ah - > curchan )
ah - > curchan = ath9k_cmn_get_curchannel ( hw , ah ) ;
/* Reset the HW */
ret = ath9k_hw_reset ( ah , ah - > curchan , false ) ;
if ( ret ) {
ath_print ( common , ATH_DBG_FATAL ,
" Unable to reset hardware; reset status %d "
" (freq %u MHz) \n " , ret , ah - > curchan - > channel ) ;
}
/* Disable the PHY */
ath9k_hw_phy_disable ( ah ) ;
ath9k_htc_ps_restore ( priv ) ;
ath9k_htc_setpower ( priv , ATH9K_PM_FULL_SLEEP ) ;
}
2010-03-17 14:25:25 +05:30
/**********************/
/* mac80211 Callbacks */
/**********************/
static int ath9k_htc_tx ( struct ieee80211_hw * hw , struct sk_buff * skb )
{
struct ieee80211_hdr * hdr ;
struct ath9k_htc_priv * priv = hw - > priv ;
2010-03-29 16:07:17 +05:30
int padpos , padsize , ret ;
2010-03-17 14:25:25 +05:30
hdr = ( struct ieee80211_hdr * ) skb - > data ;
/* Add the padding after the header if this is not already done */
padpos = ath9k_cmn_padpos ( hdr - > frame_control ) ;
padsize = padpos & 3 ;
if ( padsize & & skb - > len > padpos ) {
if ( skb_headroom ( skb ) < padsize )
return - 1 ;
skb_push ( skb , padsize ) ;
memmove ( skb - > data , skb - > data + padsize , padpos ) ;
}
2010-03-29 16:07:17 +05:30
ret = ath9k_htc_tx_start ( priv , skb ) ;
if ( ret ! = 0 ) {
if ( ret = = - ENOMEM ) {
ath_print ( ath9k_hw_common ( priv - > ah ) , ATH_DBG_XMIT ,
" Stopping TX queues \n " ) ;
ieee80211_stop_queues ( hw ) ;
spin_lock_bh ( & priv - > tx_lock ) ;
priv - > tx_queues_stop = true ;
spin_unlock_bh ( & priv - > tx_lock ) ;
} else {
ath_print ( ath9k_hw_common ( priv - > ah ) , ATH_DBG_XMIT ,
" Tx failed " ) ;
}
2010-03-17 14:25:25 +05:30
goto fail_tx ;
}
return 0 ;
fail_tx :
dev_kfree_skb_any ( skb ) ;
return 0 ;
}
2010-06-01 15:14:11 +05:30
static int ath9k_htc_start ( struct ieee80211_hw * hw )
2010-03-17 14:25:25 +05:30
{
struct ath9k_htc_priv * priv = hw - > priv ;
struct ath_hw * ah = priv - > ah ;
struct ath_common * common = ath9k_hw_common ( ah ) ;
struct ieee80211_channel * curchan = hw - > conf . channel ;
struct ath9k_channel * init_channel ;
int ret = 0 ;
enum htc_phymode mode ;
2010-04-16 11:54:03 +05:30
__be16 htc_mode ;
2010-03-17 14:25:25 +05:30
u8 cmd_rsp ;
2010-06-01 15:14:11 +05:30
mutex_lock ( & priv - > mutex ) ;
2010-03-17 14:25:25 +05:30
ath_print ( common , ATH_DBG_CONFIG ,
" Starting driver with initial channel: %d MHz \n " ,
curchan - > center_freq ) ;
2010-06-01 15:14:18 +05:30
/* Ensure that HW is awake before flushing RX */
ath9k_htc_setpower ( priv , ATH9K_PM_AWAKE ) ;
WMI_CMD ( WMI_FLUSH_RECV_CMDID ) ;
2010-03-17 14:25:25 +05:30
/* setup initial channel */
init_channel = ath9k_cmn_get_curchannel ( hw , ah ) ;
/* Reset SERDES registers */
ath9k_hw_configpcipowersave ( ah , 0 , 0 ) ;
ath9k_hw_htc_resetinit ( ah ) ;
ret = ath9k_hw_reset ( ah , init_channel , false ) ;
if ( ret ) {
ath_print ( common , ATH_DBG_FATAL ,
" Unable to reset hardware; reset status %d "
" (freq %u MHz) \n " , ret , curchan - > center_freq ) ;
2010-06-01 15:14:11 +05:30
mutex_unlock ( & priv - > mutex ) ;
2010-04-27 13:05:37 +05:30
return ret ;
2010-03-17 14:25:25 +05:30
}
ath_update_txpow ( priv ) ;
mode = ath9k_htc_get_curmode ( priv , init_channel ) ;
htc_mode = cpu_to_be16 ( mode ) ;
WMI_CMD_BUF ( WMI_SET_MODE_CMDID , & htc_mode ) ;
WMI_CMD ( WMI_ATH_INIT_CMDID ) ;
WMI_CMD ( WMI_START_RECV_CMDID ) ;
ath9k_host_rx_init ( priv ) ;
priv - > op_flags & = ~ OP_INVALID ;
htc_start ( priv - > htc ) ;
2010-03-29 16:07:17 +05:30
spin_lock_bh ( & priv - > tx_lock ) ;
priv - > tx_queues_stop = false ;
spin_unlock_bh ( & priv - > tx_lock ) ;
ieee80211_wake_queues ( hw ) ;
2010-03-17 14:25:25 +05:30
mutex_unlock ( & priv - > mutex ) ;
2010-04-27 13:05:37 +05:30
2010-03-17 14:25:25 +05:30
return ret ;
}
2010-06-01 15:14:11 +05:30
static void ath9k_htc_stop ( struct ieee80211_hw * hw )
2010-03-17 14:25:25 +05:30
{
struct ath9k_htc_priv * priv = hw - > priv ;
struct ath_hw * ah = priv - > ah ;
struct ath_common * common = ath9k_hw_common ( ah ) ;
int ret = 0 ;
u8 cmd_rsp ;
2010-06-01 15:14:11 +05:30
mutex_lock ( & priv - > mutex ) ;
2010-03-17 14:25:25 +05:30
if ( priv - > op_flags & OP_INVALID ) {
ath_print ( common , ATH_DBG_ANY , " Device not present \n " ) ;
2010-06-01 15:14:11 +05:30
mutex_unlock ( & priv - > mutex ) ;
2010-03-17 14:25:25 +05:30
return ;
}
2010-04-23 10:28:13 +05:30
/* Cancel all the running timers/work .. */
cancel_work_sync ( & priv - > ps_work ) ;
cancel_delayed_work_sync ( & priv - > ath9k_ani_work ) ;
cancel_delayed_work_sync ( & priv - > ath9k_led_blink_work ) ;
ath9k_led_stop_brightness ( priv ) ;
2010-04-05 14:48:05 +05:30
ath9k_htc_ps_wakeup ( priv ) ;
2010-03-17 14:25:25 +05:30
htc_stop ( priv - > htc ) ;
WMI_CMD ( WMI_DISABLE_INTR_CMDID ) ;
WMI_CMD ( WMI_DRAIN_TXQ_ALL_CMDID ) ;
WMI_CMD ( WMI_STOP_RECV_CMDID ) ;
skb_queue_purge ( & priv - > tx_queue ) ;
/* Remove monitor interface here */
if ( ah - > opmode = = NL80211_IFTYPE_MONITOR ) {
if ( ath9k_htc_remove_monitor_interface ( priv ) )
ath_print ( common , ATH_DBG_FATAL ,
" Unable to remove monitor interface \n " ) ;
else
ath_print ( common , ATH_DBG_CONFIG ,
" Monitor interface removed \n " ) ;
}
2010-06-01 15:14:17 +05:30
ath9k_hw_phy_disable ( ah ) ;
ath9k_hw_disable ( ah ) ;
ath9k_hw_configpcipowersave ( ah , 1 , 1 ) ;
ath9k_htc_ps_restore ( priv ) ;
ath9k_htc_setpower ( priv , ATH9K_PM_FULL_SLEEP ) ;
2010-03-17 14:25:25 +05:30
priv - > op_flags | = OP_INVALID ;
ath_print ( common , ATH_DBG_CONFIG , " Driver halt \n " ) ;
2010-04-27 13:05:37 +05:30
mutex_unlock ( & priv - > mutex ) ;
}
2010-03-17 14:25:25 +05:30
static int ath9k_htc_add_interface ( struct ieee80211_hw * hw ,
struct ieee80211_vif * vif )
{
struct ath9k_htc_priv * priv = hw - > priv ;
struct ath9k_htc_vif * avp = ( void * ) vif - > drv_priv ;
struct ath_common * common = ath9k_hw_common ( priv - > ah ) ;
struct ath9k_htc_target_vif hvif ;
int ret = 0 ;
u8 cmd_rsp ;
mutex_lock ( & priv - > mutex ) ;
/* Only one interface for now */
if ( priv - > nvifs > 0 ) {
ret = - ENOBUFS ;
goto out ;
}
2010-04-05 14:48:05 +05:30
ath9k_htc_ps_wakeup ( priv ) ;
2010-03-17 14:25:25 +05:30
memset ( & hvif , 0 , sizeof ( struct ath9k_htc_target_vif ) ) ;
memcpy ( & hvif . myaddr , vif - > addr , ETH_ALEN ) ;
switch ( vif - > type ) {
case NL80211_IFTYPE_STATION :
hvif . opmode = cpu_to_be32 ( HTC_M_STA ) ;
break ;
case NL80211_IFTYPE_ADHOC :
hvif . opmode = cpu_to_be32 ( HTC_M_IBSS ) ;
break ;
default :
ath_print ( common , ATH_DBG_FATAL ,
" Interface type %d not yet supported \n " , vif - > type ) ;
ret = - EOPNOTSUPP ;
goto out ;
}
ath_print ( common , ATH_DBG_CONFIG ,
" Attach a VIF of type: %d \n " , vif - > type ) ;
priv - > ah - > opmode = vif - > type ;
/* Index starts from zero on the target */
avp - > index = hvif . index = priv - > nvifs ;
hvif . rtsthreshold = cpu_to_be16 ( 2304 ) ;
WMI_CMD_BUF ( WMI_VAP_CREATE_CMDID , & hvif ) ;
if ( ret )
goto out ;
priv - > nvifs + + ;
/*
* We need a node in target to tx mgmt frames
* before association .
*/
ret = ath9k_htc_add_station ( priv , vif , NULL ) ;
if ( ret )
goto out ;
ret = ath9k_htc_update_cap_target ( priv ) ;
if ( ret )
ath_print ( common , ATH_DBG_CONFIG , " Failed to update "
" capability in target \n " ) ;
priv - > vif = vif ;
out :
2010-04-05 14:48:05 +05:30
ath9k_htc_ps_restore ( priv ) ;
2010-03-17 14:25:25 +05:30
mutex_unlock ( & priv - > mutex ) ;
2010-06-01 15:14:12 +05:30
2010-03-17 14:25:25 +05:30
return ret ;
}
static void ath9k_htc_remove_interface ( struct ieee80211_hw * hw ,
struct ieee80211_vif * vif )
{
struct ath9k_htc_priv * priv = hw - > priv ;
struct ath_common * common = ath9k_hw_common ( priv - > ah ) ;
struct ath9k_htc_vif * avp = ( void * ) vif - > drv_priv ;
struct ath9k_htc_target_vif hvif ;
int ret = 0 ;
u8 cmd_rsp ;
ath_print ( common , ATH_DBG_CONFIG , " Detach Interface \n " ) ;
mutex_lock ( & priv - > mutex ) ;
2010-06-01 15:14:12 +05:30
ath9k_htc_ps_wakeup ( priv ) ;
2010-03-17 14:25:25 +05:30
memset ( & hvif , 0 , sizeof ( struct ath9k_htc_target_vif ) ) ;
memcpy ( & hvif . myaddr , vif - > addr , ETH_ALEN ) ;
hvif . index = avp - > index ;
WMI_CMD_BUF ( WMI_VAP_REMOVE_CMDID , & hvif ) ;
priv - > nvifs - - ;
ath9k_htc_remove_station ( priv , vif , NULL ) ;
priv - > vif = NULL ;
2010-06-01 15:14:12 +05:30
ath9k_htc_ps_restore ( priv ) ;
2010-03-17 14:25:25 +05:30
mutex_unlock ( & priv - > mutex ) ;
}
static int ath9k_htc_config ( struct ieee80211_hw * hw , u32 changed )
{
struct ath9k_htc_priv * priv = hw - > priv ;
struct ath_common * common = ath9k_hw_common ( priv - > ah ) ;
struct ieee80211_conf * conf = & hw - > conf ;
mutex_lock ( & priv - > mutex ) ;
2010-04-27 13:05:37 +05:30
if ( changed & IEEE80211_CONF_CHANGE_IDLE ) {
bool enable_radio = false ;
bool idle = ! ! ( conf - > flags & IEEE80211_CONF_IDLE ) ;
2010-06-01 15:14:16 +05:30
mutex_lock ( & priv - > htc_pm_lock ) ;
2010-04-27 13:05:37 +05:30
if ( ! idle & & priv - > ps_idle )
enable_radio = true ;
priv - > ps_idle = idle ;
2010-06-01 15:14:16 +05:30
mutex_unlock ( & priv - > htc_pm_lock ) ;
2010-04-27 13:05:37 +05:30
if ( enable_radio ) {
ath_print ( common , ATH_DBG_CONFIG ,
" not-idle: enabling radio \n " ) ;
2010-06-01 15:14:16 +05:30
ath9k_htc_setpower ( priv , ATH9K_PM_AWAKE ) ;
ath9k_htc_radio_enable ( hw ) ;
2010-04-27 13:05:37 +05:30
}
}
2010-03-17 14:25:25 +05:30
if ( changed & IEEE80211_CONF_CHANGE_CHANNEL ) {
struct ieee80211_channel * curchan = hw - > conf . channel ;
int pos = curchan - > hw_value ;
ath_print ( common , ATH_DBG_CONFIG , " Set channel: %d MHz \n " ,
curchan - > center_freq ) ;
ath9k_cmn_update_ichannel ( hw , & priv - > ah - > channels [ pos ] ) ;
if ( ath9k_htc_set_channel ( priv , hw , & priv - > ah - > channels [ pos ] ) < 0 ) {
ath_print ( common , ATH_DBG_FATAL ,
" Unable to set channel \n " ) ;
mutex_unlock ( & priv - > mutex ) ;
return - EINVAL ;
}
}
2010-04-05 14:48:05 +05:30
if ( changed & IEEE80211_CONF_CHANGE_PS ) {
if ( conf - > flags & IEEE80211_CONF_PS ) {
ath9k_htc_setpower ( priv , ATH9K_PM_NETWORK_SLEEP ) ;
priv - > ps_enabled = true ;
} else {
priv - > ps_enabled = false ;
cancel_work_sync ( & priv - > ps_work ) ;
ath9k_htc_setpower ( priv , ATH9K_PM_AWAKE ) ;
}
}
2010-03-17 14:25:25 +05:30
if ( changed & IEEE80211_CONF_CHANGE_MONITOR ) {
if ( conf - > flags & IEEE80211_CONF_MONITOR ) {
if ( ath9k_htc_add_monitor_interface ( priv ) )
ath_print ( common , ATH_DBG_FATAL ,
" Failed to set monitor mode \n " ) ;
else
ath_print ( common , ATH_DBG_CONFIG ,
" HW opmode set to Monitor mode \n " ) ;
}
}
2010-06-01 15:14:16 +05:30
if ( changed & IEEE80211_CONF_CHANGE_IDLE ) {
mutex_lock ( & priv - > htc_pm_lock ) ;
if ( ! priv - > ps_idle ) {
mutex_unlock ( & priv - > htc_pm_lock ) ;
goto out ;
}
mutex_unlock ( & priv - > htc_pm_lock ) ;
2010-04-27 13:05:37 +05:30
ath_print ( common , ATH_DBG_CONFIG ,
" idle: disabling radio \n " ) ;
2010-06-01 15:14:11 +05:30
ath9k_htc_radio_disable ( hw ) ;
2010-04-27 13:05:37 +05:30
}
2010-06-01 15:14:16 +05:30
out :
2010-03-17 14:25:25 +05:30
mutex_unlock ( & priv - > mutex ) ;
return 0 ;
}
# define SUPPORTED_FILTERS \
( FIF_PROMISC_IN_BSS | \
FIF_ALLMULTI | \
FIF_CONTROL | \
FIF_PSPOLL | \
FIF_OTHER_BSS | \
FIF_BCN_PRBRESP_PROMISC | \
FIF_FCSFAIL )
static void ath9k_htc_configure_filter ( struct ieee80211_hw * hw ,
unsigned int changed_flags ,
unsigned int * total_flags ,
u64 multicast )
{
struct ath9k_htc_priv * priv = hw - > priv ;
u32 rfilt ;
mutex_lock ( & priv - > mutex ) ;
2010-04-05 14:48:05 +05:30
ath9k_htc_ps_wakeup ( priv ) ;
2010-06-01 15:14:12 +05:30
2010-03-17 14:25:25 +05:30
changed_flags & = SUPPORTED_FILTERS ;
* total_flags & = SUPPORTED_FILTERS ;
priv - > rxfilter = * total_flags ;
2010-03-29 16:07:09 +05:30
rfilt = ath9k_htc_calcrxfilter ( priv ) ;
2010-03-17 14:25:25 +05:30
ath9k_hw_setrxfilter ( priv - > ah , rfilt ) ;
ath_print ( ath9k_hw_common ( priv - > ah ) , ATH_DBG_CONFIG ,
" Set HW RX filter: 0x%x \n " , rfilt ) ;
2010-04-05 14:48:05 +05:30
ath9k_htc_ps_restore ( priv ) ;
2010-03-17 14:25:25 +05:30
mutex_unlock ( & priv - > mutex ) ;
}
2010-05-18 15:26:04 +05:30
static int ath9k_htc_sta_add ( struct ieee80211_hw * hw ,
struct ieee80211_vif * vif ,
struct ieee80211_sta * sta )
2010-03-17 14:25:25 +05:30
{
struct ath9k_htc_priv * priv = hw - > priv ;
int ret ;
2010-05-11 16:24:38 +05:30
mutex_lock ( & priv - > mutex ) ;
2010-06-01 15:14:12 +05:30
ath9k_htc_ps_wakeup ( priv ) ;
2010-05-18 15:26:04 +05:30
ret = ath9k_htc_add_station ( priv , vif , sta ) ;
if ( ! ret )
ath9k_htc_init_rate ( priv , sta ) ;
ath9k_htc_ps_restore ( priv ) ;
mutex_unlock ( & priv - > mutex ) ;
2010-05-11 16:24:38 +05:30
2010-05-18 15:26:04 +05:30
return ret ;
}
static int ath9k_htc_sta_remove ( struct ieee80211_hw * hw ,
struct ieee80211_vif * vif ,
struct ieee80211_sta * sta )
{
struct ath9k_htc_priv * priv = hw - > priv ;
int ret ;
2010-05-11 16:24:38 +05:30
2010-05-18 15:26:04 +05:30
mutex_lock ( & priv - > mutex ) ;
ath9k_htc_ps_wakeup ( priv ) ;
ret = ath9k_htc_remove_station ( priv , vif , sta ) ;
2010-06-01 15:14:12 +05:30
ath9k_htc_ps_restore ( priv ) ;
2010-05-11 16:24:38 +05:30
mutex_unlock ( & priv - > mutex ) ;
2010-05-18 15:26:04 +05:30
return ret ;
2010-03-17 14:25:25 +05:30
}
static int ath9k_htc_conf_tx ( struct ieee80211_hw * hw , u16 queue ,
const struct ieee80211_tx_queue_params * params )
{
struct ath9k_htc_priv * priv = hw - > priv ;
struct ath_common * common = ath9k_hw_common ( priv - > ah ) ;
struct ath9k_tx_queue_info qi ;
int ret = 0 , qnum ;
if ( queue > = WME_NUM_AC )
return 0 ;
mutex_lock ( & priv - > mutex ) ;
2010-06-01 15:14:12 +05:30
ath9k_htc_ps_wakeup ( priv ) ;
2010-03-17 14:25:25 +05:30
memset ( & qi , 0 , sizeof ( struct ath9k_tx_queue_info ) ) ;
qi . tqi_aifs = params - > aifs ;
qi . tqi_cwmin = params - > cw_min ;
qi . tqi_cwmax = params - > cw_max ;
qi . tqi_burstTime = params - > txop ;
qnum = get_hw_qnum ( queue , priv - > hwq_map ) ;
ath_print ( common , ATH_DBG_CONFIG ,
" Configure tx [queue/hwq] [%d/%d], "
" aifs: %d, cw_min: %d, cw_max: %d, txop: %d \n " ,
queue , qnum , params - > aifs , params - > cw_min ,
params - > cw_max , params - > txop ) ;
2010-03-24 13:42:13 +05:30
ret = ath_htc_txq_update ( priv , qnum , & qi ) ;
2010-06-01 15:14:19 +05:30
if ( ret ) {
2010-03-17 14:25:25 +05:30
ath_print ( common , ATH_DBG_FATAL , " TXQ Update failed \n " ) ;
2010-06-01 15:14:19 +05:30
goto out ;
}
2010-03-17 14:25:25 +05:30
2010-06-01 15:14:19 +05:30
if ( ( priv - > ah - > opmode = = NL80211_IFTYPE_ADHOC ) & &
2010-06-12 00:33:50 -04:00
( qnum = = priv - > hwq_map [ WME_AC_BE ] ) )
2010-06-01 15:14:19 +05:30
ath9k_htc_beaconq_config ( priv ) ;
out :
2010-06-01 15:14:12 +05:30
ath9k_htc_ps_restore ( priv ) ;
2010-03-17 14:25:25 +05:30
mutex_unlock ( & priv - > mutex ) ;
return ret ;
}
static int ath9k_htc_set_key ( struct ieee80211_hw * hw ,
enum set_key_cmd cmd ,
struct ieee80211_vif * vif ,
struct ieee80211_sta * sta ,
struct ieee80211_key_conf * key )
{
struct ath9k_htc_priv * priv = hw - > priv ;
struct ath_common * common = ath9k_hw_common ( priv - > ah ) ;
int ret = 0 ;
2010-03-24 13:42:13 +05:30
if ( htc_modparam_nohwcrypt )
2010-03-17 14:25:25 +05:30
return - ENOSPC ;
mutex_lock ( & priv - > mutex ) ;
ath_print ( common , ATH_DBG_CONFIG , " Set HW Key \n " ) ;
2010-04-05 14:48:05 +05:30
ath9k_htc_ps_wakeup ( priv ) ;
2010-03-17 14:25:25 +05:30
switch ( cmd ) {
case SET_KEY :
ret = ath9k_cmn_key_config ( common , vif , sta , key ) ;
if ( ret > = 0 ) {
key - > hw_key_idx = ret ;
/* push IV and Michael MIC generation to stack */
key - > flags | = IEEE80211_KEY_FLAG_GENERATE_IV ;
if ( key - > alg = = ALG_TKIP )
key - > flags | = IEEE80211_KEY_FLAG_GENERATE_MMIC ;
if ( priv - > ah - > sw_mgmt_crypto & & key - > alg = = ALG_CCMP )
key - > flags | = IEEE80211_KEY_FLAG_SW_MGMT ;
ret = 0 ;
}
break ;
case DISABLE_KEY :
ath9k_cmn_key_delete ( common , key ) ;
break ;
default :
ret = - EINVAL ;
}
2010-04-05 14:48:05 +05:30
ath9k_htc_ps_restore ( priv ) ;
2010-03-17 14:25:25 +05:30
mutex_unlock ( & priv - > mutex ) ;
return ret ;
}
static void ath9k_htc_bss_info_changed ( struct ieee80211_hw * hw ,
struct ieee80211_vif * vif ,
struct ieee80211_bss_conf * bss_conf ,
u32 changed )
{
struct ath9k_htc_priv * priv = hw - > priv ;
struct ath_hw * ah = priv - > ah ;
struct ath_common * common = ath9k_hw_common ( ah ) ;
mutex_lock ( & priv - > mutex ) ;
2010-04-05 14:48:05 +05:30
ath9k_htc_ps_wakeup ( priv ) ;
2010-03-17 14:25:25 +05:30
if ( changed & BSS_CHANGED_ASSOC ) {
common - > curaid = bss_conf - > assoc ?
bss_conf - > aid : 0 ;
ath_print ( common , ATH_DBG_CONFIG , " BSS Changed ASSOC %d \n " ,
bss_conf - > assoc ) ;
if ( bss_conf - > assoc ) {
priv - > op_flags | = OP_ASSOCIATED ;
ath_start_ani ( priv ) ;
} else {
priv - > op_flags & = ~ OP_ASSOCIATED ;
cancel_delayed_work_sync ( & priv - > ath9k_ani_work ) ;
}
}
if ( changed & BSS_CHANGED_BSSID ) {
/* Set BSSID */
memcpy ( common - > curbssid , bss_conf - > bssid , ETH_ALEN ) ;
ath9k_hw_write_associd ( ah ) ;
ath_print ( common , ATH_DBG_CONFIG ,
" BSSID: %pM aid: 0x%x \n " ,
common - > curbssid , common - > curaid ) ;
}
if ( ( changed & BSS_CHANGED_BEACON_INT ) | |
( changed & BSS_CHANGED_BEACON ) | |
( ( changed & BSS_CHANGED_BEACON_ENABLED ) & &
bss_conf - > enable_beacon ) ) {
priv - > op_flags | = OP_ENABLE_BEACON ;
2010-04-05 14:48:06 +05:30
ath9k_htc_beacon_config ( priv , vif ) ;
2010-03-17 14:25:25 +05:30
}
if ( ( changed & BSS_CHANGED_BEACON_ENABLED ) & &
! bss_conf - > enable_beacon ) {
priv - > op_flags & = ~ OP_ENABLE_BEACON ;
2010-04-05 14:48:06 +05:30
ath9k_htc_beacon_config ( priv , vif ) ;
2010-03-17 14:25:25 +05:30
}
if ( changed & BSS_CHANGED_ERP_PREAMBLE ) {
ath_print ( common , ATH_DBG_CONFIG , " BSS Changed PREAMBLE %d \n " ,
bss_conf - > use_short_preamble ) ;
if ( bss_conf - > use_short_preamble )
priv - > op_flags | = OP_PREAMBLE_SHORT ;
else
priv - > op_flags & = ~ OP_PREAMBLE_SHORT ;
}
if ( changed & BSS_CHANGED_ERP_CTS_PROT ) {
ath_print ( common , ATH_DBG_CONFIG , " BSS Changed CTS PROT %d \n " ,
bss_conf - > use_cts_prot ) ;
if ( bss_conf - > use_cts_prot & &
hw - > conf . channel - > band ! = IEEE80211_BAND_5GHZ )
priv - > op_flags | = OP_PROTECT_ENABLE ;
else
priv - > op_flags & = ~ OP_PROTECT_ENABLE ;
}
if ( changed & BSS_CHANGED_ERP_SLOT ) {
if ( bss_conf - > use_short_slot )
ah - > slottime = 9 ;
else
ah - > slottime = 20 ;
ath9k_hw_init_global_settings ( ah ) ;
}
2010-05-17 12:01:18 +05:30
if ( changed & BSS_CHANGED_HT )
ath9k_htc_update_rate ( priv , vif , bss_conf ) ;
2010-04-05 14:48:05 +05:30
ath9k_htc_ps_restore ( priv ) ;
2010-03-17 14:25:25 +05:30
mutex_unlock ( & priv - > mutex ) ;
}
static u64 ath9k_htc_get_tsf ( struct ieee80211_hw * hw )
{
struct ath9k_htc_priv * priv = hw - > priv ;
u64 tsf ;
mutex_lock ( & priv - > mutex ) ;
2010-06-01 15:14:12 +05:30
ath9k_htc_ps_wakeup ( priv ) ;
2010-03-17 14:25:25 +05:30
tsf = ath9k_hw_gettsf64 ( priv - > ah ) ;
2010-06-01 15:14:12 +05:30
ath9k_htc_ps_restore ( priv ) ;
2010-03-17 14:25:25 +05:30
mutex_unlock ( & priv - > mutex ) ;
return tsf ;
}
static void ath9k_htc_set_tsf ( struct ieee80211_hw * hw , u64 tsf )
{
struct ath9k_htc_priv * priv = hw - > priv ;
mutex_lock ( & priv - > mutex ) ;
2010-06-01 15:14:12 +05:30
ath9k_htc_ps_wakeup ( priv ) ;
2010-03-17 14:25:25 +05:30
ath9k_hw_settsf64 ( priv - > ah , tsf ) ;
2010-06-01 15:14:12 +05:30
ath9k_htc_ps_restore ( priv ) ;
2010-03-17 14:25:25 +05:30
mutex_unlock ( & priv - > mutex ) ;
}
static void ath9k_htc_reset_tsf ( struct ieee80211_hw * hw )
{
struct ath9k_htc_priv * priv = hw - > priv ;
mutex_lock ( & priv - > mutex ) ;
2010-06-01 15:14:12 +05:30
ath9k_htc_ps_wakeup ( priv ) ;
2010-03-17 14:25:25 +05:30
ath9k_hw_reset_tsf ( priv - > ah ) ;
2010-04-05 14:48:05 +05:30
ath9k_htc_ps_restore ( priv ) ;
2010-06-01 15:14:12 +05:30
mutex_unlock ( & priv - > mutex ) ;
2010-03-17 14:25:25 +05:30
}
static int ath9k_htc_ampdu_action ( struct ieee80211_hw * hw ,
struct ieee80211_vif * vif ,
enum ieee80211_ampdu_mlme_action action ,
struct ieee80211_sta * sta ,
u16 tid , u16 * ssn )
{
struct ath9k_htc_priv * priv = hw - > priv ;
struct ath9k_htc_sta * ista ;
2010-06-15 10:24:37 +05:30
int ret = 0 ;
2010-03-17 14:25:25 +05:30
switch ( action ) {
case IEEE80211_AMPDU_RX_START :
break ;
case IEEE80211_AMPDU_RX_STOP :
break ;
case IEEE80211_AMPDU_TX_START :
2010-06-15 10:24:37 +05:30
ret = ath9k_htc_tx_aggr_oper ( priv , vif , sta , action , tid ) ;
if ( ! ret )
ieee80211_start_tx_ba_cb_irqsafe ( vif , sta - > addr , tid ) ;
break ;
2010-03-17 14:25:25 +05:30
case IEEE80211_AMPDU_TX_STOP :
2010-06-15 10:24:37 +05:30
ath9k_htc_tx_aggr_oper ( priv , vif , sta , action , tid ) ;
ieee80211_stop_tx_ba_cb_irqsafe ( vif , sta - > addr , tid ) ;
2010-03-17 14:25:25 +05:30
break ;
case IEEE80211_AMPDU_TX_OPERATIONAL :
ista = ( struct ath9k_htc_sta * ) sta - > drv_priv ;
2010-06-15 10:24:37 +05:30
spin_lock_bh ( & priv - > tx_lock ) ;
2010-03-17 14:25:25 +05:30
ista - > tid_state [ tid ] = AGGR_OPERATIONAL ;
2010-06-15 10:24:37 +05:30
spin_unlock_bh ( & priv - > tx_lock ) ;
2010-03-17 14:25:25 +05:30
break ;
default :
ath_print ( ath9k_hw_common ( priv - > ah ) , ATH_DBG_FATAL ,
" Unknown AMPDU action \n " ) ;
}
2010-06-15 10:24:37 +05:30
return ret ;
2010-03-17 14:25:25 +05:30
}
static void ath9k_htc_sw_scan_start ( struct ieee80211_hw * hw )
{
struct ath9k_htc_priv * priv = hw - > priv ;
mutex_lock ( & priv - > mutex ) ;
spin_lock_bh ( & priv - > beacon_lock ) ;
priv - > op_flags | = OP_SCANNING ;
spin_unlock_bh ( & priv - > beacon_lock ) ;
2010-04-05 14:48:05 +05:30
cancel_work_sync ( & priv - > ps_work ) ;
2010-03-17 14:25:25 +05:30
cancel_delayed_work_sync ( & priv - > ath9k_ani_work ) ;
mutex_unlock ( & priv - > mutex ) ;
}
static void ath9k_htc_sw_scan_complete ( struct ieee80211_hw * hw )
{
struct ath9k_htc_priv * priv = hw - > priv ;
mutex_lock ( & priv - > mutex ) ;
2010-06-01 15:14:12 +05:30
ath9k_htc_ps_wakeup ( priv ) ;
2010-03-17 14:25:25 +05:30
spin_lock_bh ( & priv - > beacon_lock ) ;
priv - > op_flags & = ~ OP_SCANNING ;
spin_unlock_bh ( & priv - > beacon_lock ) ;
priv - > op_flags | = OP_FULL_RESET ;
2010-04-05 14:48:06 +05:30
if ( priv - > op_flags & OP_ASSOCIATED )
2010-04-16 11:53:48 +05:30
ath9k_htc_beacon_config ( priv , priv - > vif ) ;
2010-03-17 14:25:25 +05:30
ath_start_ani ( priv ) ;
2010-04-05 14:48:05 +05:30
ath9k_htc_ps_restore ( priv ) ;
2010-06-01 15:14:12 +05:30
mutex_unlock ( & priv - > mutex ) ;
2010-03-17 14:25:25 +05:30
}
static int ath9k_htc_set_rts_threshold ( struct ieee80211_hw * hw , u32 value )
{
return 0 ;
}
static void ath9k_htc_set_coverage_class ( struct ieee80211_hw * hw ,
u8 coverage_class )
{
struct ath9k_htc_priv * priv = hw - > priv ;
mutex_lock ( & priv - > mutex ) ;
2010-06-01 15:14:12 +05:30
ath9k_htc_ps_wakeup ( priv ) ;
2010-03-17 14:25:25 +05:30
priv - > ah - > coverage_class = coverage_class ;
ath9k_hw_init_global_settings ( priv - > ah ) ;
2010-06-01 15:14:12 +05:30
ath9k_htc_ps_restore ( priv ) ;
2010-03-17 14:25:25 +05:30
mutex_unlock ( & priv - > mutex ) ;
}
struct ieee80211_ops ath9k_htc_ops = {
. tx = ath9k_htc_tx ,
. start = ath9k_htc_start ,
. stop = ath9k_htc_stop ,
. add_interface = ath9k_htc_add_interface ,
. remove_interface = ath9k_htc_remove_interface ,
. config = ath9k_htc_config ,
. configure_filter = ath9k_htc_configure_filter ,
2010-05-18 15:26:04 +05:30
. sta_add = ath9k_htc_sta_add ,
. sta_remove = ath9k_htc_sta_remove ,
2010-03-17 14:25:25 +05:30
. conf_tx = ath9k_htc_conf_tx ,
. bss_info_changed = ath9k_htc_bss_info_changed ,
. set_key = ath9k_htc_set_key ,
. get_tsf = ath9k_htc_get_tsf ,
. set_tsf = ath9k_htc_set_tsf ,
. reset_tsf = ath9k_htc_reset_tsf ,
. ampdu_action = ath9k_htc_ampdu_action ,
. sw_scan_start = ath9k_htc_sw_scan_start ,
. sw_scan_complete = ath9k_htc_sw_scan_complete ,
. set_rts_threshold = ath9k_htc_set_rts_threshold ,
. rfkill_poll = ath9k_htc_rfkill_poll_state ,
. set_coverage_class = ath9k_htc_set_coverage_class ,
} ;