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 ;
u32 txpow ;
if ( priv - > curtxpow ! = priv - > txpowlimit ) {
ath9k_hw_set_txpowerlimit ( ah , priv - > txpowlimit ) ;
/* read back in case value is clamped */
ath9k_hw_getcapability ( ah , ATH9K_CAP_TXPOW , 1 , & txpow ) ;
priv - > curtxpow = txpow ;
}
}
/* 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 ;
if ( priv - > ps_enabled )
ath9k_hw_setpower ( priv - > ah , ATH9K_PM_NETWORK_SLEEP ) ;
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 ;
u16 htc_mode ;
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 ) ;
2010-04-05 14:48:05 +05:30
ath9k_htc_ps_restore ( priv ) ;
2010-03-17 14:25:25 +05:30
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 ;
tcap . tx_chainmask_legacy = 1 ;
tcap . protmode = 1 ;
tcap . tx_chainmask = 1 ;
WMI_CMD_BUF ( WMI_TARGET_IC_UPDATE_CMDID , & tcap ) ;
return ret ;
}
static int ath9k_htc_init_rate ( 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 = ( struct ath9k_htc_sta * ) sta - > drv_priv ;
struct ieee80211_supported_band * sband ;
struct ath9k_htc_target_rate trate ;
u32 caps = 0 ;
u8 cmd_rsp ;
int i , j , ret ;
memset ( & trate , 0 , sizeof ( trate ) ) ;
/* Only 2GHz is supported */
sband = priv - > hw - > wiphy - > bands [ IEEE80211_BAND_2GHZ ] ;
for ( i = 0 , j = 0 ; i < sband - > n_bitrates ; i + + ) {
if ( sta - > supp_rates [ sband - > band ] & BIT ( i ) ) {
priv - > tgt_rate . rates . legacy_rates . rs_rates [ j ]
= ( sband - > bitrates [ i ] . bitrate * 2 ) / 10 ;
j + + ;
}
}
priv - > tgt_rate . rates . legacy_rates . rs_nrates = j ;
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 ) ) )
priv - > tgt_rate . rates . ht_rates . rs_rates [ j + + ] = i ;
if ( j = = ATH_HTC_RATE_MAX )
break ;
}
priv - > tgt_rate . rates . ht_rates . rs_nrates = j ;
caps = WLAN_RC_HT_FLAG ;
if ( sta - > ht_cap . cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 )
caps | = WLAN_RC_40_FLAG ;
if ( sta - > ht_cap . cap & IEEE80211_HT_CAP_SGI_40 )
caps | = WLAN_RC_SGI_FLAG ;
}
priv - > tgt_rate . sta_index = ista - > index ;
priv - > tgt_rate . isnew = 1 ;
trate = priv - > tgt_rate ;
priv - > tgt_rate . capflags = caps ;
trate . capflags = cpu_to_be32 ( caps ) ;
WMI_CMD_BUF ( WMI_RC_RATE_UPDATE_CMDID , & trate ) ;
if ( ret ) {
ath_print ( common , ATH_DBG_FATAL ,
" Unable to initialize Rate information on target \n " ) ;
return ret ;
}
ath_print ( common , ATH_DBG_CONFIG ,
" Updated target STA: %pM (caps: 0x%x) \n " , sta - > addr , caps ) ;
return 0 ;
}
static bool check_rc_update ( struct ieee80211_hw * hw , bool * cw40 )
{
struct ath9k_htc_priv * priv = hw - > priv ;
struct ieee80211_conf * conf = & hw - > conf ;
if ( ! conf_is_ht ( conf ) )
return false ;
if ( ! ( priv - > op_flags & OP_ASSOCIATED ) | |
( priv - > op_flags & OP_SCANNING ) )
return false ;
if ( conf_is_ht40 ( conf ) ) {
if ( priv - > ah - > curchan - > chanmode &
( CHANNEL_HT40PLUS | CHANNEL_HT40MINUS ) ) {
return false ;
} else {
* cw40 = true ;
return true ;
}
} else { /* ht20 */
if ( priv - > ah - > curchan - > chanmode & CHANNEL_HT20 )
return false ;
else
return true ;
}
}
static void ath9k_htc_rc_update ( struct ath9k_htc_priv * priv , bool is_cw40 )
{
struct ath9k_htc_target_rate trate ;
struct ath_common * common = ath9k_hw_common ( priv - > ah ) ;
int ret ;
u8 cmd_rsp ;
memset ( & trate , 0 , sizeof ( trate ) ) ;
trate = priv - > tgt_rate ;
if ( is_cw40 )
priv - > tgt_rate . capflags | = WLAN_RC_40_FLAG ;
else
priv - > tgt_rate . capflags & = ~ WLAN_RC_40_FLAG ;
trate . capflags = cpu_to_be32 ( priv - > tgt_rate . capflags ) ;
WMI_CMD_BUF ( WMI_RC_RATE_UPDATE_CMDID , & trate ) ;
if ( ret ) {
ath_print ( common , ATH_DBG_FATAL ,
" Unable to update Rate information on target \n " ) ;
return ;
}
ath_print ( common , ATH_DBG_CONFIG , " Rate control updated with "
" caps:0x%x on target \n " , priv - > tgt_rate . capflags ) ;
}
static int ath9k_htc_aggr_oper ( struct ath9k_htc_priv * priv ,
struct ieee80211_vif * vif ,
u8 * sta_addr , u8 tid , bool oper )
{
struct ath_common * common = ath9k_hw_common ( priv - > ah ) ;
struct ath9k_htc_target_aggr aggr ;
struct ieee80211_sta * sta = NULL ;
struct ath9k_htc_sta * ista = ( struct ath9k_htc_sta * ) sta - > drv_priv ;
int ret = 0 ;
u8 cmd_rsp ;
if ( tid > ATH9K_HTC_MAX_TID )
return - EINVAL ;
2010-03-29 16:07:11 +05:30
memset ( & aggr , 0 , sizeof ( struct ath9k_htc_target_aggr ) ) ;
2010-03-17 14:25:25 +05:30
rcu_read_lock ( ) ;
2010-03-29 16:07:11 +05:30
/* Check if we are able to retrieve the station */
2010-03-17 14:25:25 +05:30
sta = ieee80211_find_sta ( vif , sta_addr ) ;
2010-03-29 16:07:11 +05:30
if ( ! sta ) {
2010-03-17 14:25:25 +05:30
rcu_read_unlock ( ) ;
return - EINVAL ;
}
2010-03-29 16:07:11 +05:30
ista = ( struct ath9k_htc_sta * ) sta - > drv_priv ;
2010-03-17 14:25:25 +05:30
2010-03-29 16:07:11 +05:30
if ( oper )
ista - > tid_state [ tid ] = AGGR_START ;
else
ista - > tid_state [ tid ] = AGGR_STOP ;
2010-03-17 14:25:25 +05:30
aggr . sta_index = ista - > index ;
2010-03-29 16:07:11 +05:30
2010-03-17 14:25:25 +05:30
rcu_read_unlock ( ) ;
2010-03-29 16:07:11 +05:30
2010-03-17 14:25:25 +05:30
aggr . tidno = tid ;
aggr . aggr_enable = oper ;
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 " ,
( oper ) ? " start " : " stop " , sta - > addr , tid ) ;
else
ath_print ( common , ATH_DBG_CONFIG ,
" %s aggregation for (%pM, %d) \n " ,
( oper ) ? " Starting " : " Stopping " , sta - > addr , tid ) ;
return ret ;
}
void ath9k_htc_aggr_work ( struct work_struct * work )
{
int ret = 0 ;
struct ath9k_htc_priv * priv =
container_of ( work , struct ath9k_htc_priv ,
ath9k_aggr_work . work ) ;
struct ath9k_htc_aggr_work * wk = & priv - > aggr_work ;
mutex_lock ( & wk - > mutex ) ;
switch ( wk - > action ) {
case IEEE80211_AMPDU_TX_START :
ret = ath9k_htc_aggr_oper ( priv , wk - > vif , wk - > sta_addr ,
wk - > tid , true ) ;
if ( ! ret )
ieee80211_start_tx_ba_cb ( wk - > vif , wk - > sta_addr ,
wk - > tid ) ;
break ;
case IEEE80211_AMPDU_TX_STOP :
ath9k_htc_aggr_oper ( priv , wk - > vif , wk - > sta_addr ,
wk - > tid , false ) ;
ieee80211_stop_tx_ba_cb ( wk - > vif , wk - > sta_addr , wk - > tid ) ;
break ;
default :
ath_print ( ath9k_hw_common ( priv - > ah ) , ATH_DBG_FATAL ,
" Unknown AMPDU action \n " ) ;
}
mutex_unlock ( & wk - > mutex ) ;
}
/*********/
/* 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 )
{
struct ath9k_htc_priv * priv =
( struct ath9k_htc_priv * ) file - > private_data ;
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 )
{
struct ath9k_htc_priv * priv =
( struct ath9k_htc_priv * ) file - > private_data ;
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
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 )
{
struct ath9k_htc_priv * priv =
( struct ath9k_htc_priv * ) file - > private_data ;
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 ;
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 ) ;
}
/**********************/
/* 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 ;
}
static int ath9k_htc_start ( 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 ) ;
struct ieee80211_channel * curchan = hw - > conf . channel ;
struct ath9k_channel * init_channel ;
int ret = 0 ;
enum htc_phymode mode ;
u16 htc_mode ;
u8 cmd_rsp ;
ath_print ( common , ATH_DBG_CONFIG ,
" Starting driver with initial channel: %d MHz \n " ,
curchan - > center_freq ) ;
mutex_lock ( & priv - > mutex ) ;
/* 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 ) ;
goto mutex_unlock ;
}
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 ) ;
if ( ret )
goto mutex_unlock ;
WMI_CMD ( WMI_ATH_INIT_CMDID ) ;
if ( ret )
goto mutex_unlock ;
WMI_CMD ( WMI_START_RECV_CMDID ) ;
if ( ret )
goto mutex_unlock ;
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 :
mutex_unlock ( & priv - > mutex ) ;
return ret ;
}
static void ath9k_htc_stop ( 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 = 0 ;
u8 cmd_rsp ;
mutex_lock ( & priv - > mutex ) ;
if ( priv - > op_flags & OP_INVALID ) {
ath_print ( common , ATH_DBG_ANY , " Device not present \n " ) ;
mutex_unlock ( & priv - > mutex ) ;
return ;
}
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 ) ;
ath9k_hw_phy_disable ( ah ) ;
ath9k_hw_disable ( ah ) ;
ath9k_hw_configpcipowersave ( ah , 1 , 1 ) ;
2010-04-05 14:48:05 +05:30
ath9k_htc_ps_restore ( priv ) ;
ath9k_htc_setpower ( priv , ATH9K_PM_FULL_SLEEP ) ;
2010-03-17 14:25:25 +05:30
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 ) ;
cancel_delayed_work_sync ( & priv - > ath9k_aggr_work ) ;
cancel_delayed_work_sync ( & priv - > ath9k_led_blink_work ) ;
ath9k_led_stop_brightness ( priv ) ;
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 " ) ;
}
priv - > op_flags | = OP_INVALID ;
mutex_unlock ( & priv - > mutex ) ;
ath_print ( common , ATH_DBG_CONFIG , " Driver halt \n " ) ;
}
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 ) ;
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 ) ;
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 ) ;
if ( vif - > type = = NL80211_IFTYPE_ADHOC ) {
spin_lock_bh ( & priv - > beacon_lock ) ;
if ( priv - > beacon )
dev_kfree_skb_any ( priv - > beacon ) ;
priv - > beacon = NULL ;
spin_unlock_bh ( & priv - > beacon_lock ) ;
}
priv - > vif = NULL ;
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 ) ;
if ( changed & IEEE80211_CONF_CHANGE_CHANNEL ) {
struct ieee80211_channel * curchan = hw - > conf . channel ;
int pos = curchan - > hw_value ;
bool is_cw40 = false ;
ath_print ( common , ATH_DBG_CONFIG , " Set channel: %d MHz \n " ,
curchan - > center_freq ) ;
if ( check_rc_update ( hw , & is_cw40 ) )
ath9k_htc_rc_update ( priv , is_cw40 ) ;
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 " ) ;
}
}
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-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 ) ;
}
static void ath9k_htc_sta_notify ( struct ieee80211_hw * hw ,
struct ieee80211_vif * vif ,
enum sta_notify_cmd cmd ,
struct ieee80211_sta * sta )
{
struct ath9k_htc_priv * priv = hw - > priv ;
int ret ;
switch ( cmd ) {
case STA_NOTIFY_ADD :
ret = ath9k_htc_add_station ( priv , vif , sta ) ;
if ( ! ret )
ath9k_htc_init_rate ( priv , vif , sta ) ;
break ;
case STA_NOTIFY_REMOVE :
ath9k_htc_remove_station ( priv , vif , sta ) ;
break ;
default :
break ;
}
}
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 ) ;
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-03-17 14:25:25 +05:30
if ( ret )
ath_print ( common , ATH_DBG_FATAL , " TXQ Update failed \n " ) ;
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 ;
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 ) ;
}
}
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 )
ath9k_htc_beacon_update ( priv , vif ) ;
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-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 ) ;
tsf = ath9k_hw_gettsf64 ( priv - > ah ) ;
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 ) ;
ath9k_hw_settsf64 ( priv - > ah , tsf ) ;
mutex_unlock ( & priv - > mutex ) ;
}
static void ath9k_htc_reset_tsf ( struct ieee80211_hw * hw )
{
struct ath9k_htc_priv * priv = hw - > priv ;
2010-04-05 14:48:05 +05:30
ath9k_htc_ps_wakeup ( priv ) ;
2010-03-17 14:25:25 +05:30
mutex_lock ( & priv - > mutex ) ;
ath9k_hw_reset_tsf ( priv - > ah ) ;
mutex_unlock ( & priv - > mutex ) ;
2010-04-05 14:48:05 +05:30
ath9k_htc_ps_restore ( priv ) ;
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_aggr_work * work = & priv - > aggr_work ;
struct ath9k_htc_sta * ista ;
switch ( action ) {
case IEEE80211_AMPDU_RX_START :
break ;
case IEEE80211_AMPDU_RX_STOP :
break ;
case IEEE80211_AMPDU_TX_START :
case IEEE80211_AMPDU_TX_STOP :
if ( ! ( priv - > op_flags & OP_TXAGGR ) )
return - ENOTSUPP ;
memcpy ( work - > sta_addr , sta - > addr , ETH_ALEN ) ;
work - > hw = hw ;
work - > vif = vif ;
work - > action = action ;
work - > tid = tid ;
ieee80211_queue_delayed_work ( hw , & priv - > ath9k_aggr_work , 0 ) ;
break ;
case IEEE80211_AMPDU_TX_OPERATIONAL :
ista = ( struct ath9k_htc_sta * ) sta - > drv_priv ;
ista - > tid_state [ tid ] = AGGR_OPERATIONAL ;
break ;
default :
ath_print ( ath9k_hw_common ( priv - > ah ) , ATH_DBG_FATAL ,
" Unknown AMPDU action \n " ) ;
}
return 0 ;
}
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 ;
2010-04-05 14:48:05 +05:30
ath9k_htc_ps_wakeup ( priv ) ;
2010-03-17 14:25:25 +05:30
mutex_lock ( & priv - > mutex ) ;
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 ) ;
mutex_unlock ( & priv - > mutex ) ;
2010-04-05 14:48:05 +05:30
ath9k_htc_ps_restore ( priv ) ;
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 ) ;
priv - > ah - > coverage_class = coverage_class ;
ath9k_hw_init_global_settings ( priv - > ah ) ;
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 ,
. sta_notify = ath9k_htc_sta_notify ,
. 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 ,
} ;