2008-08-04 00:16:41 -07:00
/*
2011-05-17 13:36:18 +05:30
* Copyright ( c ) 2008 - 2011 Atheros Communications Inc .
2008-08-04 00:16:41 -07:00
*
* 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 <linux/nl80211.h>
2011-02-19 01:13:42 -08:00
# include <linux/delay.h>
2009-02-09 13:26:54 +05:30
# include "ath9k.h"
2009-09-09 02:33:11 -07:00
# include "btcoex.h"
2008-08-04 00:16:41 -07:00
2012-07-17 17:16:36 +05:30
static void ath9k_set_assoc_state ( struct ath_softc * sc ,
struct ieee80211_vif * vif ) ;
2012-06-25 07:15:22 +02:00
u8 ath9k_parse_mpdudensity ( u8 mpdudensity )
2008-11-24 12:07:55 +05:30
{
/*
* 802.11 n D2 .0 defined values for " Minimum MPDU Start Spacing " :
* 0 for no restriction
* 1 for 1 / 4 us
* 2 for 1 / 2 us
* 3 for 1 us
* 4 for 2 us
* 5 for 4 us
* 6 for 8 us
* 7 for 16 us
*/
switch ( mpdudensity ) {
case 0 :
return 0 ;
case 1 :
case 2 :
case 3 :
/* Our lower layer calculations limit our precision to
1 microsecond */
return 1 ;
case 4 :
return 2 ;
case 5 :
return 4 ;
case 6 :
return 8 ;
case 7 :
return 16 ;
default :
return 0 ;
}
}
2011-02-19 01:13:42 -08:00
static bool ath9k_has_pending_frames ( struct ath_softc * sc , struct ath_txq * txq )
{
bool pending = false ;
spin_lock_bh ( & txq - > axq_lock ) ;
if ( txq - > axq_depth | | ! list_empty ( & txq - > axq_acq ) )
pending = true ;
spin_unlock_bh ( & txq - > axq_lock ) ;
return pending ;
}
2011-05-19 17:40:46 +05:30
static bool ath9k_setpower ( struct ath_softc * sc , enum ath9k_power_mode mode )
2009-09-09 21:02:34 -07:00
{
unsigned long flags ;
bool ret ;
2009-09-09 21:10:09 -07:00
spin_lock_irqsave ( & sc - > sc_pm_lock , flags ) ;
ret = ath9k_hw_setpower ( sc - > sc_ah , mode ) ;
spin_unlock_irqrestore ( & sc - > sc_pm_lock , flags ) ;
2009-09-09 21:02:34 -07:00
return ret ;
}
2009-09-09 20:29:18 -07:00
void ath9k_ps_wakeup ( struct ath_softc * sc )
{
2010-10-12 14:02:53 +02:00
struct ath_common * common = ath9k_hw_common ( sc - > sc_ah ) ;
2009-09-09 20:29:18 -07:00
unsigned long flags ;
2010-11-03 01:36:51 +01:00
enum ath9k_power_mode power_mode ;
2009-09-09 20:29:18 -07:00
spin_lock_irqsave ( & sc - > sc_pm_lock , flags ) ;
if ( + + sc - > ps_usecount ! = 1 )
goto unlock ;
2010-11-03 01:36:51 +01:00
power_mode = sc - > sc_ah - > power_mode ;
2009-09-09 21:10:09 -07:00
ath9k_hw_setpower ( sc - > sc_ah , ATH9K_PM_AWAKE ) ;
2009-09-09 20:29:18 -07:00
2010-10-12 14:02:53 +02:00
/*
* While the hardware is asleep , the cycle counters contain no
* useful data . Better clear them now so that they don ' t mess up
* survey data results .
*/
2010-11-03 01:36:51 +01:00
if ( power_mode ! = ATH9K_PM_AWAKE ) {
spin_lock ( & common - > cc_lock ) ;
ath_hw_cycle_counters_update ( common ) ;
memset ( & common - > cc_survey , 0 , sizeof ( common - > cc_survey ) ) ;
2012-06-04 16:28:41 +05:30
memset ( & common - > cc_ani , 0 , sizeof ( common - > cc_ani ) ) ;
2010-11-03 01:36:51 +01:00
spin_unlock ( & common - > cc_lock ) ;
}
2010-10-12 14:02:53 +02:00
2009-09-09 20:29:18 -07:00
unlock :
spin_unlock_irqrestore ( & sc - > sc_pm_lock , flags ) ;
}
void ath9k_ps_restore ( struct ath_softc * sc )
{
2010-10-12 14:02:53 +02:00
struct ath_common * common = ath9k_hw_common ( sc - > sc_ah ) ;
2011-09-14 21:24:24 +02:00
enum ath9k_power_mode mode ;
2009-09-09 20:29:18 -07:00
unsigned long flags ;
2012-04-24 10:23:20 +05:30
bool reset ;
2009-09-09 20:29:18 -07:00
spin_lock_irqsave ( & sc - > sc_pm_lock , flags ) ;
if ( - - sc - > ps_usecount ! = 0 )
goto unlock ;
2012-04-24 10:23:20 +05:30
if ( sc - > ps_idle ) {
ath9k_hw_setrxabort ( sc - > sc_ah , 1 ) ;
ath9k_hw_stopdmarecv ( sc - > sc_ah , & reset ) ;
2011-09-14 21:24:24 +02:00
mode = ATH9K_PM_FULL_SLEEP ;
2012-04-24 10:23:20 +05:30
} else if ( sc - > ps_enabled & &
! ( sc - > ps_flags & ( PS_WAIT_FOR_BEACON |
PS_WAIT_FOR_CAB |
PS_WAIT_FOR_PSPOLL_DATA |
PS_WAIT_FOR_TX_ACK ) ) ) {
2011-09-14 21:24:24 +02:00
mode = ATH9K_PM_NETWORK_SLEEP ;
2012-07-01 19:53:54 +05:30
if ( ath9k_hw_btcoex_is_enabled ( sc - > sc_ah ) )
ath9k_btcoex_stop_gen_timer ( sc ) ;
2012-04-24 10:23:20 +05:30
} else {
2011-09-14 21:24:24 +02:00
goto unlock ;
2012-04-24 10:23:20 +05:30
}
2011-09-14 21:24:24 +02:00
spin_lock ( & common - > cc_lock ) ;
ath_hw_cycle_counters_update ( common ) ;
spin_unlock ( & common - > cc_lock ) ;
2011-09-22 08:04:32 -06:00
ath9k_hw_setpower ( sc - > sc_ah , mode ) ;
2009-09-09 20:29:18 -07:00
unlock :
spin_unlock_irqrestore ( & sc - > sc_pm_lock , flags ) ;
}
2011-09-03 01:40:26 +02:00
static void __ath_cancel_work ( struct ath_softc * sc )
2008-11-24 12:07:55 +05:30
{
2010-07-31 00:11:59 +02:00
cancel_work_sync ( & sc - > paprd_work ) ;
cancel_work_sync ( & sc - > hw_check_work ) ;
cancel_delayed_work_sync ( & sc - > tx_complete_work ) ;
2011-01-27 14:45:08 +05:30
cancel_delayed_work_sync ( & sc - > hw_pll_work ) ;
2012-06-25 13:54:22 +05:30
2012-06-27 14:15:59 +05:30
# ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
2012-06-25 13:54:22 +05:30
if ( ath9k_hw_mci_is_enabled ( sc - > sc_ah ) )
cancel_work_sync ( & sc - > mci_work ) ;
2012-06-27 14:15:59 +05:30
# endif
2011-09-03 01:40:26 +02:00
}
2010-07-31 00:11:59 +02:00
2011-09-03 01:40:26 +02:00
static void ath_cancel_work ( struct ath_softc * sc )
{
__ath_cancel_work ( sc ) ;
cancel_work_sync ( & sc - > hw_reset_work ) ;
}
2009-01-20 11:17:08 +05:30
2012-06-04 20:23:43 +05:30
static void ath_restart_work ( struct ath_softc * sc )
{
ieee80211_queue_delayed_work ( sc - > hw , & sc - > tx_complete_work , 0 ) ;
2012-07-03 19:13:32 +02:00
if ( AR_SREV_9340 ( sc - > sc_ah ) | | AR_SREV_9485 ( sc - > sc_ah ) | |
AR_SREV_9550 ( sc - > sc_ah ) )
2012-06-04 20:23:43 +05:30
ieee80211_queue_delayed_work ( sc - > hw , & sc - > hw_pll_work ,
msecs_to_jiffies ( ATH_PLL_WORK_INTERVAL ) ) ;
ath_start_rx_poll ( sc , 3 ) ;
2012-07-17 17:16:29 +05:30
ath_start_ani ( sc ) ;
2012-06-04 20:23:43 +05:30
}
2011-09-03 01:40:26 +02:00
static bool ath_prepare_reset ( struct ath_softc * sc , bool retry_tx , bool flush )
{
struct ath_hw * ah = sc - > sc_ah ;
2012-05-24 14:32:19 +02:00
bool ret = true ;
2010-10-26 15:27:25 -07:00
2011-09-03 01:40:26 +02:00
ieee80211_stop_queues ( sc - > hw ) ;
2010-10-20 16:07:06 -07:00
2011-09-03 01:40:26 +02:00
sc - > hw_busy_count = 0 ;
2012-07-17 17:16:29 +05:30
ath_stop_ani ( sc ) ;
2012-03-15 05:34:27 +05:30
del_timer_sync ( & sc - > rx_poll_timer ) ;
2008-11-24 12:07:55 +05:30
2011-09-03 01:40:26 +02:00
ath9k_debug_samp_bb_mac ( sc ) ;
ath9k_hw_disable_interrupts ( ah ) ;
2011-01-10 17:05:50 -07:00
2011-09-03 01:40:26 +02:00
if ( ! ath_stoprecv ( sc ) )
ret = false ;
2008-12-23 15:58:50 -08:00
2012-05-24 14:32:19 +02:00
if ( ! ath_drain_all_txq ( sc , retry_tx ) )
ret = false ;
2011-09-03 01:40:26 +02:00
if ( ! flush ) {
if ( ah - > caps . hw_caps & ATH9K_HW_CAP_EDMA )
2011-09-14 21:23:03 +02:00
ath_rx_tasklet ( sc , 1 , true ) ;
ath_rx_tasklet ( sc , 1 , false ) ;
2011-09-03 01:40:26 +02:00
} else {
ath_flushrecv ( sc ) ;
}
2010-07-31 00:12:00 +02:00
2011-09-03 01:40:26 +02:00
return ret ;
}
2008-11-24 12:07:55 +05:30
2011-09-03 01:40:26 +02:00
static bool ath_complete_reset ( struct ath_softc * sc , bool start )
{
struct ath_hw * ah = sc - > sc_ah ;
struct ath_common * common = ath9k_hw_common ( ah ) ;
2012-06-04 20:24:13 +05:30
unsigned long flags ;
2008-12-23 15:58:50 -08:00
if ( ath_startrecv ( sc ) ! = 0 ) {
2010-12-02 19:12:36 -08:00
ath_err ( common , " Unable to restart recv logic \n " ) ;
2011-09-03 01:40:26 +02:00
return false ;
2008-12-23 15:58:50 -08:00
}
2011-01-31 23:47:44 +05:30
ath9k_cmn_update_txpow ( ah , sc - > curtxpow ,
sc - > config . txpowlimit , & sc - > curtxpow ) ;
2012-06-04 20:24:01 +05:30
clear_bit ( SC_OP_HW_RESET , & sc - > sc_flags ) ;
2011-10-08 20:06:19 +02:00
ath9k_hw_set_interrupts ( ah ) ;
2011-08-05 18:59:40 +05:30
ath9k_hw_enable_interrupts ( ah ) ;
2009-06-15 17:49:09 +02:00
2012-06-04 16:27:52 +05:30
if ( ! ( sc - > hw - > conf . flags & IEEE80211_CONF_OFFCHANNEL ) & & start ) {
2012-06-04 20:24:13 +05:30
if ( ! test_bit ( SC_OP_BEACONS , & sc - > sc_flags ) )
goto work ;
2012-07-17 17:15:56 +05:30
ath9k_set_beacon ( sc ) ;
2012-06-04 20:24:13 +05:30
if ( ah - > opmode = = NL80211_IFTYPE_STATION & &
test_bit ( SC_OP_PRIM_STA_VIF , & sc - > sc_flags ) ) {
spin_lock_irqsave ( & sc - > sc_pm_lock , flags ) ;
sc - > ps_flags | = PS_BEACON_SYNC | PS_WAIT_FOR_BEACON ;
spin_unlock_irqrestore ( & sc - > sc_pm_lock , flags ) ;
}
work :
2012-06-04 20:23:43 +05:30
ath_restart_work ( sc ) ;
2010-07-31 00:11:59 +02:00
}
2012-06-04 20:23:49 +05:30
if ( ( ah - > caps . hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB ) & & sc - > ant_rx ! = 3 )
ath_ant_comb_update ( sc ) ;
2011-09-03 01:40:27 +02:00
2011-09-03 01:40:26 +02:00
ieee80211_wake_queues ( sc - > hw ) ;
return true ;
}
static int ath_reset_internal ( struct ath_softc * sc , struct ath9k_channel * hchan ,
bool retry_tx )
{
struct ath_hw * ah = sc - > sc_ah ;
struct ath_common * common = ath9k_hw_common ( ah ) ;
struct ath9k_hw_cal_data * caldata = NULL ;
bool fastcc = true ;
bool flush = false ;
int r ;
__ath_cancel_work ( sc ) ;
spin_lock_bh ( & sc - > sc_pcu_lock ) ;
2011-01-24 19:23:14 +01:00
2012-06-04 16:27:52 +05:30
if ( ! ( sc - > hw - > conf . flags & IEEE80211_CONF_OFFCHANNEL ) ) {
2011-09-03 01:40:26 +02:00
fastcc = false ;
caldata = & sc - > caldata ;
}
if ( ! hchan ) {
fastcc = false ;
flush = true ;
hchan = ah - > curchan ;
}
if ( ! ath_prepare_reset ( sc , retry_tx , flush ) )
fastcc = false ;
2011-12-15 14:55:53 -08:00
ath_dbg ( common , CONFIG , " Reset to %u MHz, HT40: %d fastcc: %d \n " ,
2012-01-30 14:21:42 +05:30
hchan - > channel , IS_CHAN_HT40 ( hchan ) , fastcc ) ;
2011-09-03 01:40:26 +02:00
r = ath9k_hw_reset ( ah , hchan , caldata , fastcc ) ;
if ( r ) {
ath_err ( common ,
" Unable to reset channel, reset status %d \n " , r ) ;
goto out ;
}
if ( ! ath_complete_reset ( sc , true ) )
r = - EIO ;
out :
2010-10-26 15:27:25 -07:00
spin_unlock_bh ( & sc - > sc_pcu_lock ) ;
2011-09-03 01:40:26 +02:00
return r ;
}
/*
* Set / change channels . If the channel is really being changed , it ' s done
* by reseting the chip . To accomplish this we must first cleanup any pending
* DMA , then restart stuff .
*/
static int ath_set_channel ( struct ath_softc * sc , struct ieee80211_hw * hw ,
struct ath9k_channel * hchan )
{
int r ;
2012-06-04 20:23:55 +05:30
if ( test_bit ( SC_OP_INVALID , & sc - > sc_flags ) )
2011-09-03 01:40:26 +02:00
return - EIO ;
r = ath_reset_internal ( sc , hchan , false ) ;
2010-10-26 15:27:25 -07:00
2009-06-15 17:49:09 +02:00
return r ;
2008-11-24 12:07:55 +05:30
}
2011-11-03 11:33:13 -07:00
static void ath_node_attach ( struct ath_softc * sc , struct ieee80211_sta * sta ,
struct ieee80211_vif * vif )
2008-11-24 12:07:55 +05:30
{
struct ath_node * an ;
2012-06-25 07:15:22 +02:00
u8 density ;
2008-11-24 12:07:55 +05:30
an = ( struct ath_node * ) sta - > drv_priv ;
2011-01-09 23:11:49 -08:00
# ifdef CONFIG_ATH9K_DEBUGFS
spin_lock ( & sc - > nodes_lock ) ;
list_add ( & an - > list , & sc - > nodes ) ;
spin_unlock ( & sc - > nodes_lock ) ;
2011-12-14 22:08:04 +01:00
# endif
2011-01-09 23:11:49 -08:00
an - > sta = sta ;
2011-11-03 11:33:13 -07:00
an - > vif = vif ;
2012-03-14 14:40:58 +05:30
2012-03-27 10:08:55 +05:30
if ( sc - > sc_ah - > caps . hw_caps & ATH9K_HW_CAP_HT ) {
2008-11-24 12:07:55 +05:30
ath_tx_node_init ( sc , an ) ;
2009-07-23 15:32:34 +05:30
an - > maxampdu = 1 < < ( IEEE80211_HT_MAX_AMPDU_FACTOR +
2009-03-30 15:28:48 +05:30
sta - > ht_cap . ampdu_factor ) ;
2012-06-25 07:15:22 +02:00
density = ath9k_parse_mpdudensity ( sta - > ht_cap . ampdu_density ) ;
an - > mpdudensity = density ;
2009-03-30 15:28:48 +05:30
}
2008-11-24 12:07:55 +05:30
}
static void ath_node_detach ( struct ath_softc * sc , struct ieee80211_sta * sta )
{
struct ath_node * an = ( struct ath_node * ) sta - > drv_priv ;
2011-01-09 23:11:49 -08:00
# ifdef CONFIG_ATH9K_DEBUGFS
spin_lock ( & sc - > nodes_lock ) ;
list_del ( & an - > list ) ;
spin_unlock ( & sc - > nodes_lock ) ;
an - > sta = NULL ;
# endif
2012-03-27 10:08:55 +05:30
if ( sc - > sc_ah - > caps . hw_caps & ATH9K_HW_CAP_HT )
2008-11-24 12:07:55 +05:30
ath_tx_node_cleanup ( sc , an ) ;
}
2010-01-08 10:36:02 +05:30
void ath9k_tasklet ( unsigned long data )
2008-11-24 12:07:55 +05:30
{
struct ath_softc * sc = ( struct ath_softc * ) data ;
2009-09-09 02:33:11 -07:00
struct ath_hw * ah = sc - > sc_ah ;
2009-09-13 02:42:02 -07:00
struct ath_common * common = ath9k_hw_common ( ah ) ;
2012-07-17 17:16:42 +05:30
enum ath_reset_type type ;
2012-06-04 20:24:07 +05:30
unsigned long flags ;
2009-02-09 13:27:03 +05:30
u32 status = sc - > intrstatus ;
2010-04-15 17:38:48 -04:00
u32 rxmask ;
2008-11-24 12:07:55 +05:30
2011-09-14 21:23:01 +02:00
ath9k_ps_wakeup ( sc ) ;
spin_lock ( & sc - > sc_pcu_lock ) ;
2011-05-20 17:52:10 +05:30
if ( ( status & ATH9K_INT_FATAL ) | |
( status & ATH9K_INT_BB_WATCHDOG ) ) {
2011-10-07 02:28:13 +02:00
if ( status & ATH9K_INT_FATAL )
type = RESET_TYPE_FATAL_INT ;
else
type = RESET_TYPE_BB_WATCHDOG ;
2012-07-17 17:16:42 +05:30
ath9k_queue_reset ( sc , type ) ;
2011-09-14 21:23:01 +02:00
goto out ;
2009-03-30 15:28:49 +05:30
}
2008-11-24 12:07:55 +05:30
2012-06-04 20:24:07 +05:30
spin_lock_irqsave ( & sc - > sc_pm_lock , flags ) ;
2011-05-06 18:27:47 +05:30
if ( ( status & ATH9K_INT_TSFOOR ) & & sc - > ps_enabled ) {
/*
* TSF sync does not look correct ; remain awake to sync with
* the next Beacon .
*/
2011-12-15 14:55:53 -08:00
ath_dbg ( common , PS , " TSFOOR - Sync with next Beacon \n " ) ;
2011-08-05 18:59:41 +05:30
sc - > ps_flags | = PS_WAIT_FOR_BEACON | PS_BEACON_SYNC ;
2011-05-06 18:27:47 +05:30
}
2012-06-04 20:24:07 +05:30
spin_unlock_irqrestore ( & sc - > sc_pm_lock , flags ) ;
2011-05-06 18:27:47 +05:30
2010-04-15 17:38:48 -04:00
if ( ah - > caps . hw_caps & ATH9K_HW_CAP_EDMA )
rxmask = ( ATH9K_INT_RXHP | ATH9K_INT_RXLP | ATH9K_INT_RXEOL |
ATH9K_INT_RXORN ) ;
else
rxmask = ( ATH9K_INT_RX | ATH9K_INT_RXEOL | ATH9K_INT_RXORN ) ;
if ( status & rxmask ) {
/* Check for high priority Rx first */
if ( ( ah - > caps . hw_caps & ATH9K_HW_CAP_EDMA ) & &
( status & ATH9K_INT_RXHP ) )
ath_rx_tasklet ( sc , 0 , true ) ;
ath_rx_tasklet ( sc , 0 , false ) ;
2008-11-24 12:07:55 +05:30
}
2010-04-15 17:39:36 -04:00
if ( status & ATH9K_INT_TX ) {
if ( ah - > caps . hw_caps & ATH9K_HW_CAP_EDMA )
ath_tx_edma_tasklet ( sc ) ;
else
ath_tx_tasklet ( sc ) ;
}
2009-03-30 15:28:49 +05:30
2012-02-22 12:40:32 +05:30
ath9k_btcoex_handle_interrupt ( sc , status ) ;
2011-11-30 10:41:28 +05:30
2011-09-14 21:23:01 +02:00
out :
2008-11-24 12:07:55 +05:30
/* re-enable hardware interrupt */
2010-11-08 20:54:47 +01:00
ath9k_hw_enable_interrupts ( ah ) ;
2010-10-26 15:27:25 -07:00
2010-12-23 21:06:57 +05:30
spin_unlock ( & sc - > sc_pcu_lock ) ;
2009-05-15 02:47:16 -04:00
ath9k_ps_restore ( sc ) ;
2008-11-24 12:07:55 +05:30
}
2009-01-14 20:17:06 +01:00
irqreturn_t ath_isr ( int irq , void * dev )
2008-11-24 12:07:55 +05:30
{
2009-03-30 15:28:49 +05:30
# define SCHED_INTR ( \
ATH9K_INT_FATAL | \
2011-05-20 17:52:10 +05:30
ATH9K_INT_BB_WATCHDOG | \
2009-03-30 15:28:49 +05:30
ATH9K_INT_RXORN | \
ATH9K_INT_RXEOL | \
ATH9K_INT_RX | \
2010-04-15 17:38:48 -04:00
ATH9K_INT_RXLP | \
ATH9K_INT_RXHP | \
2009-03-30 15:28:49 +05:30
ATH9K_INT_TX | \
ATH9K_INT_BMISS | \
ATH9K_INT_CST | \
2009-09-01 17:46:32 +05:30
ATH9K_INT_TSFOOR | \
2011-11-30 10:41:18 +05:30
ATH9K_INT_GENTIMER | \
ATH9K_INT_MCI )
2009-03-30 15:28:49 +05:30
2008-11-24 12:07:55 +05:30
struct ath_softc * sc = dev ;
2009-02-09 13:27:12 +05:30
struct ath_hw * ah = sc - > sc_ah ;
2010-10-08 22:13:53 +02:00
struct ath_common * common = ath9k_hw_common ( ah ) ;
2008-11-24 12:07:55 +05:30
enum ath9k_int status ;
bool sched = false ;
2009-03-30 15:28:49 +05:30
/*
* The hardware is not ready / present , don ' t
* touch anything . Note this can happen early
* on if the IRQ is shared .
*/
2012-06-04 20:23:55 +05:30
if ( test_bit ( SC_OP_INVALID , & sc - > sc_flags ) )
2009-03-30 15:28:49 +05:30
return IRQ_NONE ;
2008-11-24 12:07:55 +05:30
2009-03-30 15:28:49 +05:30
/* shared irq, not for us */
2009-05-15 02:47:16 -04:00
if ( ! ath9k_hw_intrpend ( ah ) )
2009-03-30 15:28:49 +05:30
return IRQ_NONE ;
2012-06-04 20:24:01 +05:30
if ( test_bit ( SC_OP_HW_RESET , & sc - > sc_flags ) )
return IRQ_HANDLED ;
2009-03-30 15:28:49 +05:30
/*
* Figure out the reason ( s ) for the interrupt . Note
* that the hal returns a pseudo - ISR that may include
* bits we haven ' t explicitly enabled so we mask the
* value to insure we only process bits we requested .
*/
ath9k_hw_getisr ( ah , & status ) ; /* NB: clears ISR too */
2010-03-31 18:05:31 -04:00
status & = ah - > imask ; /* discard unasked-for bits */
2008-11-24 12:07:55 +05:30
2009-03-30 15:28:49 +05:30
/*
* If there are no status bits set , then this interrupt was not
* for me ( should have been caught above ) .
*/
2009-05-15 02:47:16 -04:00
if ( ! status )
2009-03-30 15:28:49 +05:30
return IRQ_NONE ;
2008-11-24 12:07:55 +05:30
2009-03-30 15:28:49 +05:30
/* Cache the status */
sc - > intrstatus = status ;
if ( status & SCHED_INTR )
sched = true ;
2012-07-10 14:56:52 +05:30
# ifdef CONFIG_PM_SLEEP
if ( status & ATH9K_INT_BMISS ) {
if ( atomic_read ( & sc - > wow_sleep_proc_intr ) = = 0 ) {
ath_dbg ( common , ANY , " during WoW we got a BMISS \n " ) ;
atomic_inc ( & sc - > wow_got_bmiss_intr ) ;
atomic_dec ( & sc - > wow_sleep_proc_intr ) ;
}
ath_dbg ( common , INTERRUPT , " beacon miss interrupt \n " ) ;
}
# endif
2009-03-30 15:28:49 +05:30
/*
* If a FATAL or RXORN interrupt is received , we have to reset the
* chip immediately .
*/
2010-04-15 17:38:48 -04:00
if ( ( status & ATH9K_INT_FATAL ) | | ( ( status & ATH9K_INT_RXORN ) & &
! ( ah - > caps . hw_caps & ATH9K_HW_CAP_EDMA ) ) )
2009-03-30 15:28:49 +05:30
goto chip_reset ;
2010-05-13 13:33:44 -04:00
if ( ( ah - > caps . hw_caps & ATH9K_HW_CAP_EDMA ) & &
( status & ATH9K_INT_BB_WATCHDOG ) ) {
2010-10-08 22:13:53 +02:00
spin_lock ( & common - > cc_lock ) ;
ath_hw_cycle_counters_update ( common ) ;
2010-05-13 13:33:44 -04:00
ar9003_hw_bb_watchdog_dbg_info ( ah ) ;
2010-10-08 22:13:53 +02:00
spin_unlock ( & common - > cc_lock ) ;
2010-05-13 13:33:44 -04:00
goto chip_reset ;
}
2009-03-30 15:28:49 +05:30
if ( status & ATH9K_INT_SWBA )
tasklet_schedule ( & sc - > bcon_tasklet ) ;
if ( status & ATH9K_INT_TXURN )
ath9k_hw_updatetxtriglevel ( ah , true ) ;
2011-08-13 10:28:09 +05:30
if ( status & ATH9K_INT_RXEOL ) {
ah - > imask & = ~ ( ATH9K_INT_RXEOL | ATH9K_INT_RXORN ) ;
2011-10-08 20:06:19 +02:00
ath9k_hw_set_interrupts ( ah ) ;
2010-04-15 17:38:48 -04:00
}
2009-05-15 02:47:16 -04:00
if ( ! ( ah - > caps . hw_caps & ATH9K_HW_CAP_AUTOSLEEP ) )
if ( status & ATH9K_INT_TIM_TIMER ) {
2010-12-07 15:13:22 -08:00
if ( ATH_DBG_WARN_ON_ONCE ( sc - > ps_idle ) )
goto chip_reset ;
2009-03-30 15:28:49 +05:30
/* Clear RxAbort bit so that we can
* receive frames */
2009-09-09 21:10:09 -07:00
ath9k_setpower ( sc , ATH9K_PM_AWAKE ) ;
2012-06-04 20:24:07 +05:30
spin_lock ( & sc - > sc_pm_lock ) ;
2009-05-15 02:47:16 -04:00
ath9k_hw_setrxabort ( sc - > sc_ah , 0 ) ;
2010-01-08 10:36:05 +05:30
sc - > ps_flags | = PS_WAIT_FOR_BEACON ;
2012-06-04 20:24:07 +05:30
spin_unlock ( & sc - > sc_pm_lock ) ;
2008-11-24 12:07:55 +05:30
}
2009-03-30 15:28:49 +05:30
chip_reset :
2008-11-24 12:07:55 +05:30
2008-12-07 21:42:44 +05:30
ath_debug_stat_interrupt ( sc , status ) ;
2008-11-24 12:07:55 +05:30
if ( sched ) {
2010-11-08 20:54:47 +01:00
/* turn off every interrupt */
ath9k_hw_disable_interrupts ( ah ) ;
2008-11-24 12:07:55 +05:30
tasklet_schedule ( & sc - > intr_tq ) ;
}
return IRQ_HANDLED ;
2009-03-30 15:28:49 +05:30
# undef SCHED_INTR
2008-11-24 12:07:55 +05:30
}
2011-09-03 01:40:25 +02:00
static int ath_reset ( struct ath_softc * sc , bool retry_tx )
2008-11-24 12:07:55 +05:30
{
2008-12-23 15:58:40 -08:00
int r ;
2008-11-24 12:07:55 +05:30
2011-01-21 18:52:38 +01:00
ath9k_ps_wakeup ( sc ) ;
2010-10-26 15:27:25 -07:00
2011-09-03 01:40:26 +02:00
r = ath_reset_internal ( sc , NULL , retry_tx ) ;
2008-11-24 12:07:55 +05:30
if ( retry_tx ) {
int i ;
for ( i = 0 ; i < ATH9K_NUM_TX_QUEUES ; i + + ) {
if ( ATH_TXQ_SETUP ( sc , i ) ) {
2008-12-07 21:44:03 +05:30
spin_lock_bh ( & sc - > tx . txq [ i ] . axq_lock ) ;
ath_txq_schedule ( sc , & sc - > tx . txq [ i ] ) ;
spin_unlock_bh ( & sc - > tx . txq [ i ] . axq_lock ) ;
2008-11-24 12:07:55 +05:30
}
}
}
2011-01-21 18:52:38 +01:00
ath9k_ps_restore ( sc ) ;
2009-12-14 16:34:56 +05:30
2008-12-23 15:58:40 -08:00
return r ;
2008-11-24 12:07:55 +05:30
}
2012-07-17 17:16:42 +05:30
void ath9k_queue_reset ( struct ath_softc * sc , enum ath_reset_type type )
{
# ifdef CONFIG_ATH9K_DEBUGFS
RESET_STAT_INC ( sc , type ) ;
# endif
set_bit ( SC_OP_HW_RESET , & sc - > sc_flags ) ;
ieee80211_queue_work ( sc - > hw , & sc - > hw_reset_work ) ;
}
2011-09-03 01:40:25 +02:00
void ath_reset_work ( struct work_struct * work )
{
struct ath_softc * sc = container_of ( work , struct ath_softc , hw_reset_work ) ;
ath_reset ( sc , true ) ;
}
2008-11-24 12:07:55 +05:30
/**********************/
/* mac80211 callbacks */
/**********************/
2008-09-10 18:49:27 +05:30
static int ath9k_start ( struct ieee80211_hw * hw )
2008-08-04 00:16:41 -07:00
{
2011-01-24 19:23:18 +01:00
struct ath_softc * sc = hw - > priv ;
2009-09-09 02:33:11 -07:00
struct ath_hw * ah = sc - > sc_ah ;
2009-09-13 02:42:02 -07:00
struct ath_common * common = ath9k_hw_common ( ah ) ;
2008-09-10 18:49:27 +05:30
struct ieee80211_channel * curchan = hw - > conf . channel ;
2008-11-24 12:07:55 +05:30
struct ath9k_channel * init_channel ;
2009-06-13 14:50:24 +05:30
int r ;
2008-08-04 00:16:41 -07:00
2011-12-15 14:55:53 -08:00
ath_dbg ( common , CONFIG ,
2010-12-02 19:12:37 -08:00
" Starting driver with initial channel: %d MHz \n " ,
curchan - > center_freq ) ;
2008-08-04 00:16:41 -07:00
2011-03-25 17:43:41 +01:00
ath9k_ps_wakeup ( sc ) ;
2009-02-04 08:10:07 +05:30
mutex_lock ( & sc - > mutex ) ;
2011-01-31 23:47:43 +05:30
init_channel = ath9k_cmn_get_curchannel ( hw , ah ) ;
2008-11-24 12:07:55 +05:30
/* Reset SERDES registers */
2011-08-05 13:10:32 +02:00
ath9k_hw_configpcipowersave ( ah , false ) ;
2008-11-24 12:07:55 +05:30
/*
* The basic interface to setting the hardware in a good
* state is ` ` reset ' ' . On return the hardware is known to
* be powered up and with interrupts disabled . This must
* be followed by initialization of the appropriate bits
* and then setup of the interrupt mask .
*/
2010-10-26 15:27:24 -07:00
spin_lock_bh ( & sc - > sc_pcu_lock ) ;
2011-11-16 13:08:41 +01:00
atomic_set ( & ah - > intr_ref_cnt , - 1 ) ;
2010-07-31 00:12:00 +02:00
r = ath9k_hw_reset ( ah , init_channel , ah - > caldata , false ) ;
2008-12-23 15:58:40 -08:00
if ( r ) {
2010-12-02 19:12:36 -08:00
ath_err ( common ,
" Unable to reset hardware; reset status %d (freq %u MHz) \n " ,
r , curchan - > center_freq ) ;
2010-10-26 15:27:24 -07:00
spin_unlock_bh ( & sc - > sc_pcu_lock ) ;
2009-02-04 08:10:07 +05:30
goto mutex_unlock ;
2008-11-24 12:07:55 +05:30
}
/* Setup our intr mask. */
2010-04-15 17:38:48 -04:00
ah - > imask = ATH9K_INT_TX | ATH9K_INT_RXEOL |
ATH9K_INT_RXORN | ATH9K_INT_FATAL |
ATH9K_INT_GLOBAL ;
if ( ah - > caps . hw_caps & ATH9K_HW_CAP_EDMA )
2010-05-13 13:33:44 -04:00
ah - > imask | = ATH9K_INT_RXHP |
ATH9K_INT_RXLP |
ATH9K_INT_BB_WATCHDOG ;
2010-04-15 17:38:48 -04:00
else
ah - > imask | = ATH9K_INT_RX ;
2008-11-24 12:07:55 +05:30
2010-09-14 20:22:44 +02:00
ah - > imask | = ATH9K_INT_GTT ;
2008-11-24 12:07:55 +05:30
2009-09-09 02:33:11 -07:00
if ( ah - > caps . hw_caps & ATH9K_HW_CAP_HT )
2010-03-31 18:05:31 -04:00
ah - > imask | = ATH9K_INT_CST ;
2008-11-24 12:07:55 +05:30
2012-06-04 16:27:19 +05:30
ath_mci_enable ( sc ) ;
2011-11-30 10:41:18 +05:30
2012-06-04 20:23:55 +05:30
clear_bit ( SC_OP_INVALID , & sc - > sc_flags ) ;
2010-10-27 18:31:15 +05:30
sc - > sc_ah - > is_monitoring = false ;
2008-11-24 12:07:55 +05:30
2011-09-03 01:40:26 +02:00
if ( ! ath_complete_reset ( sc , false ) ) {
r = - EIO ;
spin_unlock_bh ( & sc - > sc_pcu_lock ) ;
goto mutex_unlock ;
}
2008-11-24 12:07:55 +05:30
2011-11-16 13:08:41 +01:00
if ( ah - > led_pin > = 0 ) {
ath9k_hw_cfg_output ( ah , ah - > led_pin ,
AR_GPIO_OUTPUT_MUX_AS_OUTPUT ) ;
ath9k_hw_set_gpio ( ah , ah - > led_pin , 0 ) ;
}
/*
* Reset key cache to sane defaults ( all entries cleared ) instead of
* semi - random values after suspend / resume .
*/
ath9k_cmn_init_crypto ( sc - > sc_ah ) ;
2011-09-03 01:40:26 +02:00
spin_unlock_bh ( & sc - > sc_pcu_lock ) ;
2009-07-14 20:17:09 -04:00
2010-12-06 04:27:42 -08:00
if ( ah - > caps . pcie_lcr_extsync_en & & common - > bus_ops - > extn_synch_en )
common - > bus_ops - > extn_synch_en ( common ) ;
2009-02-04 08:10:07 +05:30
mutex_unlock :
mutex_unlock ( & sc - > mutex ) ;
2011-03-25 17:43:41 +01:00
ath9k_ps_restore ( sc ) ;
2008-12-23 15:58:40 -08:00
return r ;
2008-08-04 00:16:41 -07:00
}
2011-02-24 14:42:06 +01:00
static void ath9k_tx ( struct ieee80211_hw * hw , struct sk_buff * skb )
2008-08-04 00:16:41 -07:00
{
2011-01-24 19:23:18 +01:00
struct ath_softc * sc = hw - > priv ;
2009-09-13 02:42:02 -07:00
struct ath_common * common = ath9k_hw_common ( sc - > sc_ah ) ;
2008-10-29 10:14:26 +05:30
struct ath_tx_control txctl ;
2009-11-24 15:49:18 +01:00
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) skb - > data ;
2012-06-04 20:24:07 +05:30
unsigned long flags ;
2008-10-29 10:14:26 +05:30
2009-07-24 17:27:21 +02:00
if ( sc - > ps_enabled ) {
2009-05-19 17:01:42 +03:00
/*
* mac80211 does not set PM field for normal data frames , so we
* need to update that based on the current PS mode .
*/
if ( ieee80211_is_data ( hdr - > frame_control ) & &
! ieee80211_is_nullfunc ( hdr - > frame_control ) & &
! ieee80211_has_pm ( hdr - > frame_control ) ) {
2011-12-15 14:55:53 -08:00
ath_dbg ( common , PS ,
2010-12-02 19:12:37 -08:00
" Add PM=1 for a TX frame while in PS mode \n " ) ;
2009-05-19 17:01:42 +03:00
hdr - > frame_control | = cpu_to_le16 ( IEEE80211_FCTL_PM ) ;
}
}
2012-04-24 10:23:20 +05:30
if ( unlikely ( sc - > sc_ah - > power_mode = = ATH9K_PM_NETWORK_SLEEP ) ) {
2009-05-19 17:01:38 +03:00
/*
* We are using PS - Poll and mac80211 can request TX while in
* power save mode . Need to wake up hardware for the TX to be
* completed and if needed , also for RX of buffered frames .
*/
ath9k_ps_wakeup ( sc ) ;
2012-06-04 20:24:07 +05:30
spin_lock_irqsave ( & sc - > sc_pm_lock , flags ) ;
2010-05-17 18:57:55 -07:00
if ( ! ( sc - > sc_ah - > caps . hw_caps & ATH9K_HW_CAP_AUTOSLEEP ) )
ath9k_hw_setrxabort ( sc - > sc_ah , 0 ) ;
2009-05-19 17:01:38 +03:00
if ( ieee80211_is_pspoll ( hdr - > frame_control ) ) {
2011-12-15 14:55:53 -08:00
ath_dbg ( common , PS ,
2010-12-02 19:12:37 -08:00
" Sending PS-Poll to pick a buffered frame \n " ) ;
2010-01-08 10:36:05 +05:30
sc - > ps_flags | = PS_WAIT_FOR_PSPOLL_DATA ;
2009-05-19 17:01:38 +03:00
} else {
2011-12-15 14:55:53 -08:00
ath_dbg ( common , PS , " Wake up to complete TX \n " ) ;
2010-01-08 10:36:05 +05:30
sc - > ps_flags | = PS_WAIT_FOR_TX_ACK ;
2009-05-19 17:01:38 +03:00
}
/*
* The actual restore operation will happen only after
2012-04-24 10:23:20 +05:30
* the ps_flags bit is cleared . We are just dropping
2009-05-19 17:01:38 +03:00
* the ps_usecount here .
*/
2012-06-04 20:24:07 +05:30
spin_unlock_irqrestore ( & sc - > sc_pm_lock , flags ) ;
2009-05-19 17:01:38 +03:00
ath9k_ps_restore ( sc ) ;
}
2012-04-24 10:23:20 +05:30
/*
* Cannot tx while the hardware is in full sleep , it first needs a full
* chip reset to recover from that
*/
if ( unlikely ( sc - > sc_ah - > power_mode = = ATH9K_PM_FULL_SLEEP ) ) {
ath_err ( common , " TX while HW is in FULL_SLEEP mode \n " ) ;
goto exit ;
}
2008-10-29 10:14:26 +05:30
memset ( & txctl , 0 , sizeof ( struct ath_tx_control ) ) ;
2010-11-07 14:59:39 +01:00
txctl . txq = sc - > tx . txq_map [ skb_get_queue_mapping ( skb ) ] ;
2008-10-29 10:14:26 +05:30
2011-12-15 14:55:53 -08:00
ath_dbg ( common , XMIT , " transmitting packet, skb: %p \n " , skb ) ;
2008-09-10 18:49:27 +05:30
2009-03-03 19:23:29 +02:00
if ( ath_tx_start ( hw , skb , & txctl ) ! = 0 ) {
2011-12-15 14:55:53 -08:00
ath_dbg ( common , XMIT , " TX failed \n " ) ;
2012-04-03 09:16:55 -07:00
TX_STAT_INC ( txctl . txq - > axq_qnum , txfailed ) ;
2008-10-29 10:14:26 +05:30
goto exit ;
2008-09-10 18:49:27 +05:30
}
2011-02-24 14:42:06 +01:00
return ;
2008-10-29 10:14:26 +05:30
exit :
dev_kfree_skb_any ( skb ) ;
2008-08-04 00:16:41 -07:00
}
2008-09-10 18:49:27 +05:30
static void ath9k_stop ( struct ieee80211_hw * hw )
2008-08-04 00:16:41 -07:00
{
2011-01-24 19:23:18 +01:00
struct ath_softc * sc = hw - > priv ;
2009-09-09 02:33:11 -07:00
struct ath_hw * ah = sc - > sc_ah ;
2009-09-13 02:42:02 -07:00
struct ath_common * common = ath9k_hw_common ( ah ) ;
2011-11-16 13:08:41 +01:00
bool prev_idle ;
2008-08-04 00:16:41 -07:00
2009-08-18 10:51:52 +05:30
mutex_lock ( & sc - > mutex ) ;
2011-09-03 01:40:26 +02:00
ath_cancel_work ( sc ) ;
2012-03-15 05:34:27 +05:30
del_timer_sync ( & sc - > rx_poll_timer ) ;
2009-07-27 11:53:04 -07:00
2012-06-04 20:23:55 +05:30
if ( test_bit ( SC_OP_INVALID , & sc - > sc_flags ) ) {
2011-12-15 14:55:53 -08:00
ath_dbg ( common , ANY , " Device not present \n " ) ;
2009-08-18 10:51:52 +05:30
mutex_unlock ( & sc - > mutex ) ;
2008-10-29 10:17:13 +05:30
return ;
}
2008-09-10 18:49:27 +05:30
2009-12-23 20:03:27 -05:00
/* Ensure HW is awake when we try to shut it down. */
ath9k_ps_wakeup ( sc ) ;
2010-10-26 15:27:25 -07:00
spin_lock_bh ( & sc - > sc_pcu_lock ) ;
2011-01-25 14:08:40 +01:00
/* prevent tasklets to enable interrupts once we disable them */
ah - > imask & = ~ ATH9K_INT_GLOBAL ;
2008-11-24 12:07:55 +05:30
/* make sure h/w will not generate any interrupt
* before setting the invalid flag . */
2010-11-08 20:54:47 +01:00
ath9k_hw_disable_interrupts ( ah ) ;
2008-11-24 12:07:55 +05:30
2011-11-16 13:08:41 +01:00
spin_unlock_bh ( & sc - > sc_pcu_lock ) ;
/* we can now sync irq and kill any running tasklets, since we already
* disabled interrupts and not holding a spin lock */
synchronize_irq ( sc - > irq ) ;
tasklet_kill ( & sc - > intr_tq ) ;
tasklet_kill ( & sc - > bcon_tasklet ) ;
prev_idle = sc - > ps_idle ;
sc - > ps_idle = true ;
spin_lock_bh ( & sc - > sc_pcu_lock ) ;
if ( ah - > led_pin > = 0 ) {
ath9k_hw_set_gpio ( ah , ah - > led_pin , 1 ) ;
ath9k_hw_cfg_gpio_input ( ah , ah - > led_pin ) ;
}
ath_prepare_reset ( sc , false , true ) ;
2008-11-24 12:07:55 +05:30
2011-01-26 18:23:27 +01:00
if ( sc - > rx . frag ) {
dev_kfree_skb_any ( sc - > rx . frag ) ;
sc - > rx . frag = NULL ;
}
2011-11-16 13:08:41 +01:00
if ( ! ah - > curchan )
ah - > curchan = ath9k_cmn_get_curchannel ( hw , ah ) ;
2010-10-26 15:27:25 -07:00
2011-11-16 13:08:41 +01:00
ath9k_hw_reset ( ah , ah - > curchan , ah - > caldata , false ) ;
ath9k_hw_phy_disable ( ah ) ;
2010-10-26 15:27:25 -07:00
2011-11-16 13:08:41 +01:00
ath9k_hw_configpcipowersave ( ah , true ) ;
2011-01-25 14:08:40 +01:00
2011-11-16 13:08:41 +01:00
spin_unlock_bh ( & sc - > sc_pcu_lock ) ;
2009-12-23 20:03:27 -05:00
2011-11-16 13:08:41 +01:00
ath9k_ps_restore ( sc ) ;
2008-11-24 12:07:55 +05:30
2012-06-04 20:23:55 +05:30
set_bit ( SC_OP_INVALID , & sc - > sc_flags ) ;
2011-11-16 13:08:41 +01:00
sc - > ps_idle = prev_idle ;
2008-09-10 18:50:17 +05:30
2009-02-04 08:10:07 +05:30
mutex_unlock ( & sc - > mutex ) ;
2011-12-15 14:55:53 -08:00
ath_dbg ( common , CONFIG , " Driver halt \n " ) ;
2008-08-04 00:16:41 -07:00
}
2011-01-15 19:13:48 +00:00
bool ath9k_uses_beacons ( int type )
{
switch ( type ) {
case NL80211_IFTYPE_AP :
case NL80211_IFTYPE_ADHOC :
case NL80211_IFTYPE_MESH_POINT :
return true ;
default :
return false ;
}
}
static void ath9k_vif_iter ( void * data , u8 * mac , struct ieee80211_vif * vif )
{
struct ath9k_vif_iter_data * iter_data = data ;
int i ;
if ( iter_data - > hw_macaddr )
for ( i = 0 ; i < ETH_ALEN ; i + + )
iter_data - > mask [ i ] & =
~ ( iter_data - > hw_macaddr [ i ] ^ mac [ i ] ) ;
2009-02-04 08:10:07 +05:30
2009-12-23 13:15:45 +01:00
switch ( vif - > type ) {
2011-01-15 19:13:48 +00:00
case NL80211_IFTYPE_AP :
iter_data - > naps + + ;
2008-08-04 00:16:41 -07:00
break ;
2011-01-15 19:13:48 +00:00
case NL80211_IFTYPE_STATION :
iter_data - > nstations + + ;
2010-10-01 11:20:39 -04:00
break ;
2008-09-11 00:01:58 +02:00
case NL80211_IFTYPE_ADHOC :
2011-01-15 19:13:48 +00:00
iter_data - > nadhocs + + ;
break ;
2009-03-20 22:59:59 -04:00
case NL80211_IFTYPE_MESH_POINT :
2011-01-15 19:13:48 +00:00
iter_data - > nmeshes + + ;
break ;
case NL80211_IFTYPE_WDS :
iter_data - > nwds + + ;
2008-08-04 00:16:41 -07:00
break ;
default :
2011-01-15 19:13:48 +00:00
break ;
2008-08-04 00:16:41 -07:00
}
2011-01-15 19:13:48 +00:00
}
2008-08-04 00:16:41 -07:00
2012-07-17 17:16:36 +05:30
static void ath9k_sta_vif_iter ( void * data , u8 * mac , struct ieee80211_vif * vif )
{
struct ath_softc * sc = data ;
struct ath_vif * avp = ( void * ) vif - > drv_priv ;
if ( vif - > type ! = NL80211_IFTYPE_STATION )
return ;
if ( avp - > primary_sta_vif )
ath9k_set_assoc_state ( sc , vif ) ;
}
2011-01-15 19:13:48 +00:00
/* Called with sc->mutex held. */
void ath9k_calculate_iter_data ( struct ieee80211_hw * hw ,
struct ieee80211_vif * vif ,
struct ath9k_vif_iter_data * iter_data )
{
2011-01-24 19:23:18 +01:00
struct ath_softc * sc = hw - > priv ;
2011-01-15 19:13:48 +00:00
struct ath_hw * ah = sc - > sc_ah ;
struct ath_common * common = ath9k_hw_common ( ah ) ;
2008-09-10 18:49:27 +05:30
2011-01-15 19:13:48 +00:00
/*
* Use the hardware MAC address as reference , the hardware uses it
* together with the BSSID mask when matching addresses .
*/
memset ( iter_data , 0 , sizeof ( * iter_data ) ) ;
iter_data - > hw_macaddr = common - > macaddr ;
memset ( & iter_data - > mask , 0xff , ETH_ALEN ) ;
2008-10-29 10:16:06 +05:30
2011-01-15 19:13:48 +00:00
if ( vif )
ath9k_vif_iter ( iter_data , vif - > addr , vif ) ;
/* Get list of all active MAC addresses */
ieee80211_iterate_active_interfaces_atomic ( sc - > hw , ath9k_vif_iter ,
iter_data ) ;
}
2009-03-03 19:23:27 +02:00
2011-01-15 19:13:48 +00:00
/* Called with sc->mutex held. */
static void ath9k_calculate_summary_state ( struct ieee80211_hw * hw ,
struct ieee80211_vif * vif )
{
2011-01-24 19:23:18 +01:00
struct ath_softc * sc = hw - > priv ;
2011-01-15 19:13:48 +00:00
struct ath_hw * ah = sc - > sc_ah ;
struct ath_common * common = ath9k_hw_common ( ah ) ;
struct ath9k_vif_iter_data iter_data ;
2012-07-17 17:16:36 +05:30
enum nl80211_iftype old_opmode = ah - > opmode ;
2009-03-03 19:23:27 +02:00
2011-01-15 19:13:48 +00:00
ath9k_calculate_iter_data ( hw , vif , & iter_data ) ;
2009-03-03 19:23:26 +02:00
2011-01-15 19:13:48 +00:00
memcpy ( common - > bssidmask , iter_data . mask , ETH_ALEN ) ;
ath_hw_setbssidmask ( common ) ;
if ( iter_data . naps > 0 ) {
2012-07-17 17:15:37 +05:30
ath9k_hw_set_tsfadjust ( ah , true ) ;
2011-01-15 19:13:48 +00:00
ah - > opmode = NL80211_IFTYPE_AP ;
} else {
2012-07-17 17:15:37 +05:30
ath9k_hw_set_tsfadjust ( ah , false ) ;
2008-10-29 10:16:06 +05:30
2011-05-03 16:57:19 -07:00
if ( iter_data . nmeshes )
ah - > opmode = NL80211_IFTYPE_MESH_POINT ;
else if ( iter_data . nwds )
2011-01-15 19:13:48 +00:00
ah - > opmode = NL80211_IFTYPE_AP ;
else if ( iter_data . nadhocs )
ah - > opmode = NL80211_IFTYPE_ADHOC ;
else
ah - > opmode = NL80211_IFTYPE_STATION ;
}
2008-10-29 10:16:06 +05:30
2012-07-17 17:15:43 +05:30
ath9k_hw_setopmode ( ah ) ;
2012-06-15 15:25:25 +02:00
if ( ( iter_data . nstations + iter_data . nadhocs + iter_data . nmeshes ) > 0 )
2010-03-31 18:05:31 -04:00
ah - > imask | = ATH9K_INT_TSFOOR ;
2012-06-15 15:25:25 +02:00
else
2011-01-15 19:13:48 +00:00
ah - > imask & = ~ ATH9K_INT_TSFOOR ;
2009-02-12 10:06:47 +05:30
2011-10-08 20:06:19 +02:00
ath9k_hw_set_interrupts ( ah ) ;
2012-07-17 17:16:36 +05:30
/*
* If we are changing the opmode to STATION ,
* a beacon sync needs to be done .
*/
if ( ah - > opmode = = NL80211_IFTYPE_STATION & &
old_opmode = = NL80211_IFTYPE_AP & &
test_bit ( SC_OP_PRIM_STA_VIF , & sc - > sc_flags ) ) {
ieee80211_iterate_active_interfaces_atomic ( sc - > hw ,
ath9k_sta_vif_iter , sc ) ;
}
2011-01-15 19:13:48 +00:00
}
2008-10-03 15:45:27 -07:00
2011-01-15 19:13:48 +00:00
static int ath9k_add_interface ( struct ieee80211_hw * hw ,
struct ieee80211_vif * vif )
2010-12-08 19:38:55 +05:30
{
2011-01-24 19:23:18 +01:00
struct ath_softc * sc = hw - > priv ;
2011-01-15 19:13:48 +00:00
struct ath_hw * ah = sc - > sc_ah ;
struct ath_common * common = ath9k_hw_common ( ah ) ;
int ret = 0 ;
2010-12-08 19:38:55 +05:30
2011-04-07 19:07:17 +02:00
ath9k_ps_wakeup ( sc ) ;
2011-01-15 19:13:48 +00:00
mutex_lock ( & sc - > mutex ) ;
2010-12-08 19:38:55 +05:30
2011-01-15 19:13:48 +00:00
switch ( vif - > type ) {
case NL80211_IFTYPE_STATION :
case NL80211_IFTYPE_WDS :
case NL80211_IFTYPE_ADHOC :
case NL80211_IFTYPE_AP :
case NL80211_IFTYPE_MESH_POINT :
break ;
default :
ath_err ( common , " Interface type %d not yet supported \n " ,
vif - > type ) ;
ret = - EOPNOTSUPP ;
goto out ;
}
2010-12-08 19:38:55 +05:30
2011-01-15 19:13:48 +00:00
if ( ath9k_uses_beacons ( vif - > type ) ) {
if ( sc - > nbcnvifs > = ATH_BCBUF ) {
ath_err ( common , " Not enough beacon buffers when adding "
" new interface of type: %i \n " ,
vif - > type ) ;
ret = - ENOBUFS ;
goto out ;
}
}
2011-12-15 14:55:53 -08:00
ath_dbg ( common , CONFIG , " Attach a VIF of type: %d \n " , vif - > type ) ;
2011-01-15 19:13:48 +00:00
sc - > nvifs + + ;
2012-07-17 17:15:30 +05:30
ath9k_calculate_summary_state ( hw , vif ) ;
if ( ath9k_uses_beacons ( vif - > type ) )
ath9k_beacon_assign_slot ( sc , vif ) ;
2011-01-15 19:13:48 +00:00
out :
mutex_unlock ( & sc - > mutex ) ;
2011-04-07 19:07:17 +02:00
ath9k_ps_restore ( sc ) ;
2011-01-15 19:13:48 +00:00
return ret ;
2010-12-08 19:38:55 +05:30
}
static int ath9k_change_interface ( struct ieee80211_hw * hw ,
struct ieee80211_vif * vif ,
enum nl80211_iftype new_type ,
bool p2p )
{
2011-01-24 19:23:18 +01:00
struct ath_softc * sc = hw - > priv ;
2010-12-08 19:38:55 +05:30
struct ath_common * common = ath9k_hw_common ( sc - > sc_ah ) ;
2010-12-21 06:59:06 +03:00
int ret = 0 ;
2010-12-08 19:38:55 +05:30
2011-12-15 14:55:53 -08:00
ath_dbg ( common , CONFIG , " Change Interface \n " ) ;
2012-07-17 17:15:30 +05:30
2010-12-08 19:38:55 +05:30
mutex_lock ( & sc - > mutex ) ;
2011-04-07 19:07:17 +02:00
ath9k_ps_wakeup ( sc ) ;
2010-12-08 19:38:55 +05:30
2011-01-15 19:13:48 +00:00
if ( ath9k_uses_beacons ( new_type ) & &
! ath9k_uses_beacons ( vif - > type ) ) {
2010-12-08 19:38:55 +05:30
if ( sc - > nbcnvifs > = ATH_BCBUF ) {
ath_err ( common , " No beacon slot available \n " ) ;
2010-12-21 06:59:06 +03:00
ret = - ENOBUFS ;
goto out ;
2010-12-08 19:38:55 +05:30
}
}
2011-01-15 19:13:48 +00:00
if ( ath9k_uses_beacons ( vif - > type ) )
2012-07-17 17:15:30 +05:30
ath9k_beacon_remove_slot ( sc , vif ) ;
2011-01-15 19:13:48 +00:00
2010-12-08 19:38:55 +05:30
vif - > type = new_type ;
vif - > p2p = p2p ;
2012-07-17 17:15:30 +05:30
ath9k_calculate_summary_state ( hw , vif ) ;
if ( ath9k_uses_beacons ( vif - > type ) )
ath9k_beacon_assign_slot ( sc , vif ) ;
2010-12-21 06:59:06 +03:00
out :
2011-04-07 19:07:17 +02:00
ath9k_ps_restore ( sc ) ;
2010-12-08 19:38:55 +05:30
mutex_unlock ( & sc - > mutex ) ;
2010-12-21 06:59:06 +03:00
return ret ;
2010-12-08 19:38:55 +05:30
}
2008-09-10 18:49:27 +05:30
static void ath9k_remove_interface ( struct ieee80211_hw * hw ,
2009-12-23 13:15:45 +01:00
struct ieee80211_vif * vif )
2008-08-04 00:16:41 -07:00
{
2011-01-24 19:23:18 +01:00
struct ath_softc * sc = hw - > priv ;
2009-09-13 02:42:02 -07:00
struct ath_common * common = ath9k_hw_common ( sc - > sc_ah ) ;
2008-08-04 00:16:41 -07:00
2011-12-15 14:55:53 -08:00
ath_dbg ( common , CONFIG , " Detach Interface \n " ) ;
2008-08-04 00:16:41 -07:00
2011-04-07 19:07:17 +02:00
ath9k_ps_wakeup ( sc ) ;
2009-02-04 08:10:07 +05:30
mutex_lock ( & sc - > mutex ) ;
2011-01-15 19:13:48 +00:00
sc - > nvifs - - ;
2008-08-11 14:01:49 +03:00
2011-01-15 19:13:48 +00:00
if ( ath9k_uses_beacons ( vif - > type ) )
2012-07-17 17:15:30 +05:30
ath9k_beacon_remove_slot ( sc , vif ) ;
2009-03-03 19:23:26 +02:00
2011-01-15 19:13:48 +00:00
ath9k_calculate_summary_state ( hw , NULL ) ;
2009-02-04 08:10:07 +05:30
mutex_unlock ( & sc - > mutex ) ;
2011-04-07 19:07:17 +02:00
ath9k_ps_restore ( sc ) ;
2008-08-04 00:16:41 -07:00
}
2010-10-05 20:36:40 +05:30
static void ath9k_enable_ps ( struct ath_softc * sc )
2010-02-03 22:51:13 +05:30
{
2010-03-31 18:05:31 -04:00
struct ath_hw * ah = sc - > sc_ah ;
2012-04-24 10:23:20 +05:30
struct ath_common * common = ath9k_hw_common ( ah ) ;
2010-03-31 18:05:31 -04:00
2010-02-03 22:51:13 +05:30
sc - > ps_enabled = true ;
2010-03-31 18:05:31 -04:00
if ( ! ( ah - > caps . hw_caps & ATH9K_HW_CAP_AUTOSLEEP ) ) {
if ( ( ah - > imask & ATH9K_INT_TIM_TIMER ) = = 0 ) {
ah - > imask | = ATH9K_INT_TIM_TIMER ;
2011-10-08 20:06:19 +02:00
ath9k_hw_set_interrupts ( ah ) ;
2010-02-03 22:51:13 +05:30
}
2010-05-17 18:57:55 -07:00
ath9k_hw_setrxabort ( ah , 1 ) ;
2010-02-03 22:51:13 +05:30
}
2012-04-24 10:23:20 +05:30
ath_dbg ( common , PS , " PowerSave enabled \n " ) ;
2010-02-03 22:51:13 +05:30
}
2010-10-05 20:36:41 +05:30
static void ath9k_disable_ps ( struct ath_softc * sc )
{
struct ath_hw * ah = sc - > sc_ah ;
2012-04-24 10:23:20 +05:30
struct ath_common * common = ath9k_hw_common ( ah ) ;
2010-10-05 20:36:41 +05:30
sc - > ps_enabled = false ;
ath9k_hw_setpower ( ah , ATH9K_PM_AWAKE ) ;
if ( ! ( ah - > caps . hw_caps & ATH9K_HW_CAP_AUTOSLEEP ) ) {
ath9k_hw_setrxabort ( ah , 0 ) ;
sc - > ps_flags & = ~ ( PS_WAIT_FOR_BEACON |
PS_WAIT_FOR_CAB |
PS_WAIT_FOR_PSPOLL_DATA |
PS_WAIT_FOR_TX_ACK ) ;
if ( ah - > imask & ATH9K_INT_TIM_TIMER ) {
ah - > imask & = ~ ATH9K_INT_TIM_TIMER ;
2011-10-08 20:06:19 +02:00
ath9k_hw_set_interrupts ( ah ) ;
2010-10-05 20:36:41 +05:30
}
}
2012-04-24 10:23:20 +05:30
ath_dbg ( common , PS , " PowerSave disabled \n " ) ;
2010-10-05 20:36:41 +05:30
}
2008-10-09 12:18:51 +02:00
static int ath9k_config ( struct ieee80211_hw * hw , u32 changed )
2008-08-04 00:16:41 -07:00
{
2011-01-24 19:23:18 +01:00
struct ath_softc * sc = hw - > priv ;
2010-10-10 18:21:52 +02:00
struct ath_hw * ah = sc - > sc_ah ;
struct ath_common * common = ath9k_hw_common ( ah ) ;
2008-10-09 12:18:51 +02:00
struct ieee80211_conf * conf = & hw - > conf ;
2012-04-12 20:36:31 +02:00
bool reset_channel = false ;
2008-08-04 00:16:41 -07:00
2011-11-16 13:08:41 +01:00
ath9k_ps_wakeup ( sc ) ;
2008-12-18 11:40:16 +05:30
mutex_lock ( & sc - > mutex ) ;
2009-02-04 08:10:07 +05:30
2011-11-16 13:08:43 +01:00
if ( changed & IEEE80211_CONF_CHANGE_IDLE ) {
2011-01-24 19:23:16 +01:00
sc - > ps_idle = ! ! ( conf - > flags & IEEE80211_CONF_IDLE ) ;
2012-07-01 19:53:53 +05:30
if ( sc - > ps_idle ) {
2011-11-16 13:08:43 +01:00
ath_cancel_work ( sc ) ;
2012-07-01 19:53:53 +05:30
ath9k_stop_btcoex ( sc ) ;
} else {
ath9k_start_btcoex ( sc ) ;
2012-04-12 20:36:31 +02:00
/*
* The chip needs a reset to properly wake up from
* full sleep
*/
reset_channel = ah - > chip_fullsleep ;
2012-07-01 19:53:53 +05:30
}
2011-11-16 13:08:43 +01:00
}
2009-07-14 20:22:53 -04:00
2009-11-24 02:53:25 -05:00
/*
* We just prepare to enable PS . We have to wait until our AP has
* ACK ' d our null data frame to disable RX otherwise we ' ll ignore
* those ACKs and end up retransmitting the same null data frames .
* IEEE80211_CONF_CHANGE_PS is only passed by mac80211 for STA mode .
*/
2009-01-20 11:17:08 +05:30
if ( changed & IEEE80211_CONF_CHANGE_PS ) {
2010-09-16 15:12:26 -04:00
unsigned long flags ;
spin_lock_irqsave ( & sc - > sc_pm_lock , flags ) ;
2010-10-05 20:36:40 +05:30
if ( conf - > flags & IEEE80211_CONF_PS )
ath9k_enable_ps ( sc ) ;
2010-10-05 20:36:41 +05:30
else
ath9k_disable_ps ( sc ) ;
2010-09-16 15:12:26 -04:00
spin_unlock_irqrestore ( & sc - > sc_pm_lock , flags ) ;
2009-01-20 11:17:08 +05:30
}
2010-01-08 10:36:13 +05:30
if ( changed & IEEE80211_CONF_CHANGE_MONITOR ) {
if ( conf - > flags & IEEE80211_CONF_MONITOR ) {
2011-12-15 14:55:53 -08:00
ath_dbg ( common , CONFIG , " Monitor mode is enabled \n " ) ;
2010-10-27 18:31:15 +05:30
sc - > sc_ah - > is_monitoring = true ;
} else {
2011-12-15 14:55:53 -08:00
ath_dbg ( common , CONFIG , " Monitor mode is disabled \n " ) ;
2010-10-27 18:31:15 +05:30
sc - > sc_ah - > is_monitoring = false ;
2010-01-08 10:36:13 +05:30
}
}
2012-04-12 20:36:31 +02:00
if ( ( changed & IEEE80211_CONF_CHANGE_CHANNEL ) | | reset_channel ) {
2008-11-24 12:08:35 +05:30
struct ieee80211_channel * curchan = hw - > conf . channel ;
2009-01-22 15:16:48 -08:00
int pos = curchan - > hw_value ;
2010-10-10 18:21:52 +02:00
int old_pos = - 1 ;
unsigned long flags ;
if ( ah - > curchan )
old_pos = ah - > curchan - & ah - > channels [ 0 ] ;
2008-10-14 16:58:37 +02:00
2011-12-15 14:55:53 -08:00
ath_dbg ( common , CONFIG , " Set channel: %d MHz type: %d \n " ,
2011-02-07 13:44:33 -08:00
curchan - > center_freq , conf - > channel_type ) ;
2008-08-04 00:16:41 -07:00
2010-10-10 18:21:52 +02:00
/* update survey stats for the old channel before switching */
spin_lock_irqsave ( & common - > cc_lock , flags ) ;
ath_update_survey_stats ( sc ) ;
spin_unlock_irqrestore ( & common - > cc_lock , flags ) ;
2011-08-13 10:28:17 +05:30
/*
* Preserve the current channel values , before updating
* the same channel
*/
2012-01-09 15:37:53 +05:30
if ( ah - > curchan & & ( old_pos = = pos ) )
ath9k_hw_getnf ( ah , ah - > curchan ) ;
2011-08-13 10:28:17 +05:30
ath9k_cmn_update_ichannel ( & sc - > sc_ah - > channels [ pos ] ,
curchan , conf - > channel_type ) ;
2010-10-10 18:21:52 +02:00
/*
* If the operating channel changes , change the survey in - use flags
* along with it .
* Reset the survey data for the new channel , unless we ' re switching
* back to the operating channel from an off - channel operation .
*/
if ( ! ( hw - > conf . flags & IEEE80211_CONF_OFFCHANNEL ) & &
sc - > cur_survey ! = & sc - > survey [ pos ] ) {
if ( sc - > cur_survey )
sc - > cur_survey - > filled & = ~ SURVEY_INFO_IN_USE ;
sc - > cur_survey = & sc - > survey [ pos ] ;
memset ( sc - > cur_survey , 0 , sizeof ( struct survey_info ) ) ;
sc - > cur_survey - > filled | = SURVEY_INFO_IN_USE ;
} else if ( ! ( sc - > survey [ pos ] . filled & SURVEY_INFO_IN_USE ) ) {
memset ( & sc - > survey [ pos ] , 0 , sizeof ( struct survey_info ) ) ;
}
2009-03-03 19:23:32 +02:00
if ( ath_set_channel ( sc , hw , & sc - > sc_ah - > channels [ pos ] ) < 0 ) {
2010-12-02 19:12:36 -08:00
ath_err ( common , " Unable to set channel \n " ) ;
2008-12-18 11:40:16 +05:30
mutex_unlock ( & sc - > mutex ) ;
2012-06-11 12:19:30 +05:30
ath9k_ps_restore ( sc ) ;
2008-11-27 09:46:27 +05:30
return - EINVAL ;
}
2010-10-10 18:21:52 +02:00
/*
* The most recent snapshot of channel - > noisefloor for the old
* channel is only available after the hardware reset . Copy it to
* the survey stats now .
*/
if ( old_pos > = 0 )
ath_update_survey_nf ( sc , old_pos ) ;
2008-12-12 11:57:43 +05:30
}
2008-08-04 00:16:41 -07:00
2010-01-19 14:04:19 -05:00
if ( changed & IEEE80211_CONF_CHANGE_POWER ) {
2011-12-15 14:55:53 -08:00
ath_dbg ( common , CONFIG , " Set power: %d \n " , conf - > power_level ) ;
2009-02-09 13:27:03 +05:30
sc - > config . txpowlimit = 2 * conf - > power_level ;
2011-01-31 23:47:44 +05:30
ath9k_cmn_update_txpow ( ah , sc - > curtxpow ,
sc - > config . txpowlimit , & sc - > curtxpow ) ;
2009-07-14 20:22:53 -04:00
}
2008-12-18 11:40:16 +05:30
mutex_unlock ( & sc - > mutex ) ;
2011-11-16 13:08:41 +01:00
ath9k_ps_restore ( sc ) ;
2009-02-04 08:10:07 +05:30
2008-08-04 00:16:41 -07:00
return 0 ;
}
2008-09-10 18:49:27 +05:30
# define SUPPORTED_FILTERS \
( FIF_PROMISC_IN_BSS | \
FIF_ALLMULTI | \
FIF_CONTROL | \
2009-08-08 21:55:16 -04:00
FIF_PSPOLL | \
2008-09-10 18:49:27 +05:30
FIF_OTHER_BSS | \
FIF_BCN_PRBRESP_PROMISC | \
2010-10-13 17:29:31 +03:00
FIF_PROBE_REQ | \
2008-09-10 18:49:27 +05:30
FIF_FCSFAIL )
2008-08-25 20:47:29 +05:30
2008-09-10 18:49:27 +05:30
/* FIXME: sc->sc_full_reset ? */
static void ath9k_configure_filter ( struct ieee80211_hw * hw ,
unsigned int changed_flags ,
unsigned int * total_flags ,
2009-08-17 16:16:53 +02:00
u64 multicast )
2008-09-10 18:49:27 +05:30
{
2011-01-24 19:23:18 +01:00
struct ath_softc * sc = hw - > priv ;
2008-09-10 18:49:27 +05:30
u32 rfilt ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
changed_flags & = SUPPORTED_FILTERS ;
* total_flags & = SUPPORTED_FILTERS ;
2008-08-04 00:16:41 -07:00
2008-12-07 21:44:03 +05:30
sc - > rx . rxfilter = * total_flags ;
2009-05-19 17:01:41 +03:00
ath9k_ps_wakeup ( sc ) ;
2008-09-10 18:49:27 +05:30
rfilt = ath_calcrxfilter ( sc ) ;
ath9k_hw_setrxfilter ( sc - > sc_ah , rfilt ) ;
2009-05-19 17:01:41 +03:00
ath9k_ps_restore ( sc ) ;
2008-08-04 00:16:41 -07:00
2011-12-15 14:55:53 -08:00
ath_dbg ( ath9k_hw_common ( sc - > sc_ah ) , CONFIG , " Set HW RX filter: 0x%x \n " ,
rfilt ) ;
2008-09-10 18:49:27 +05:30
}
2008-08-04 00:16:41 -07:00
2010-02-19 19:06:56 +01:00
static int ath9k_sta_add ( struct ieee80211_hw * hw ,
struct ieee80211_vif * vif ,
struct ieee80211_sta * sta )
2008-09-10 18:49:27 +05:30
{
2011-01-24 19:23:18 +01:00
struct ath_softc * sc = hw - > priv ;
2011-04-17 23:28:10 +02:00
struct ath_common * common = ath9k_hw_common ( sc - > sc_ah ) ;
struct ath_node * an = ( struct ath_node * ) sta - > drv_priv ;
struct ieee80211_key_conf ps_key = { } ;
2008-08-04 00:16:41 -07:00
2011-11-03 11:33:13 -07:00
ath_node_attach ( sc , sta , vif ) ;
2011-05-10 20:52:22 +02:00
if ( vif - > type ! = NL80211_IFTYPE_AP & &
vif - > type ! = NL80211_IFTYPE_AP_VLAN )
return 0 ;
2011-04-17 23:28:10 +02:00
an - > ps_key = ath_key_config ( common , vif , sta , & ps_key ) ;
2010-02-19 19:06:56 +01:00
return 0 ;
}
2011-04-17 23:28:10 +02:00
static void ath9k_del_ps_key ( struct ath_softc * sc ,
struct ieee80211_vif * vif ,
struct ieee80211_sta * sta )
{
struct ath_common * common = ath9k_hw_common ( sc - > sc_ah ) ;
struct ath_node * an = ( struct ath_node * ) sta - > drv_priv ;
struct ieee80211_key_conf ps_key = { . hw_key_idx = an - > ps_key } ;
if ( ! an - > ps_key )
return ;
ath_key_delete ( common , & ps_key ) ;
}
2010-02-19 19:06:56 +01:00
static int ath9k_sta_remove ( struct ieee80211_hw * hw ,
struct ieee80211_vif * vif ,
struct ieee80211_sta * sta )
{
2011-01-24 19:23:18 +01:00
struct ath_softc * sc = hw - > priv ;
2010-02-19 19:06:56 +01:00
2011-04-17 23:28:10 +02:00
ath9k_del_ps_key ( sc , vif , sta ) ;
2010-02-19 19:06:56 +01:00
ath_node_detach ( sc , sta ) ;
return 0 ;
2008-08-04 00:16:41 -07:00
}
2011-04-17 23:28:09 +02:00
static void ath9k_sta_notify ( struct ieee80211_hw * hw ,
struct ieee80211_vif * vif ,
enum sta_notify_cmd cmd ,
struct ieee80211_sta * sta )
{
struct ath_softc * sc = hw - > priv ;
struct ath_node * an = ( struct ath_node * ) sta - > drv_priv ;
2012-03-14 14:40:58 +05:30
if ( ! sta - > ht_cap . ht_supported )
2011-12-26 10:42:15 +05:30
return ;
2011-04-17 23:28:09 +02:00
switch ( cmd ) {
case STA_NOTIFY_SLEEP :
an - > sleeping = true ;
2011-09-29 16:04:26 +02:00
ath_tx_aggr_sleep ( sta , sc , an ) ;
2011-04-17 23:28:09 +02:00
break ;
case STA_NOTIFY_AWAKE :
an - > sleeping = false ;
ath_tx_aggr_wakeup ( sc , an ) ;
break ;
}
}
2011-10-02 10:15:52 +02:00
static int ath9k_conf_tx ( struct ieee80211_hw * hw ,
struct ieee80211_vif * vif , u16 queue ,
2008-09-10 18:49:27 +05:30
const struct ieee80211_tx_queue_params * params )
2008-08-04 00:16:41 -07:00
{
2011-01-24 19:23:18 +01:00
struct ath_softc * sc = hw - > priv ;
2009-09-13 02:42:02 -07:00
struct ath_common * common = ath9k_hw_common ( sc - > sc_ah ) ;
2010-11-07 14:59:39 +01:00
struct ath_txq * txq ;
2008-09-10 18:49:27 +05:30
struct ath9k_tx_queue_info qi ;
2010-11-07 14:59:39 +01:00
int ret = 0 ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
if ( queue > = WME_NUM_AC )
return 0 ;
2008-08-04 00:16:41 -07:00
2010-11-07 14:59:39 +01:00
txq = sc - > tx . txq_map [ queue ] ;
2011-04-07 19:07:17 +02:00
ath9k_ps_wakeup ( sc ) ;
2009-02-04 08:10:07 +05:30
mutex_lock ( & sc - > mutex ) ;
2009-03-30 15:28:46 +05:30
memset ( & qi , 0 , sizeof ( struct ath9k_tx_queue_info ) ) ;
2008-09-10 18:49:27 +05:30
qi . tqi_aifs = params - > aifs ;
qi . tqi_cwmin = params - > cw_min ;
qi . tqi_cwmax = params - > cw_max ;
2012-07-15 19:53:34 +02:00
qi . tqi_burstTime = params - > txop * 32 ;
2008-08-04 00:16:41 -07:00
2011-12-15 14:55:53 -08:00
ath_dbg ( common , CONFIG ,
2010-12-02 19:12:37 -08:00
" Configure tx [queue/halq] [%d/%d], aifs: %d, cw_min: %d, cw_max: %d, txop: %d \n " ,
queue , txq - > axq_qnum , params - > aifs , params - > cw_min ,
params - > cw_max , params - > txop ) ;
2008-08-04 00:16:41 -07:00
2012-07-15 19:53:36 +02:00
ath_update_max_aggr_framelen ( sc , queue , qi . tqi_burstTime ) ;
2010-11-07 14:59:39 +01:00
ret = ath_txq_update ( sc , txq - > axq_qnum , & qi ) ;
2008-09-10 18:49:27 +05:30
if ( ret )
2010-12-02 19:12:36 -08:00
ath_err ( common , " TXQ Update failed \n " ) ;
2008-08-04 00:16:41 -07:00
2009-02-04 08:10:07 +05:30
mutex_unlock ( & sc - > mutex ) ;
2011-04-07 19:07:17 +02:00
ath9k_ps_restore ( sc ) ;
2009-02-04 08:10:07 +05:30
2008-09-10 18:49:27 +05:30
return ret ;
}
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
static int ath9k_set_key ( struct ieee80211_hw * hw ,
enum set_key_cmd cmd ,
2008-12-29 12:55:09 +01:00
struct ieee80211_vif * vif ,
struct ieee80211_sta * sta ,
2008-09-10 18:49:27 +05:30
struct ieee80211_key_conf * key )
{
2011-01-24 19:23:18 +01:00
struct ath_softc * sc = hw - > priv ;
2009-09-13 02:42:02 -07:00
struct ath_common * common = ath9k_hw_common ( sc - > sc_ah ) ;
2008-09-10 18:49:27 +05:30
int ret = 0 ;
2008-08-04 00:16:41 -07:00
2011-01-05 09:39:17 -05:00
if ( ath9k_modparam_nohwcrypt )
2009-02-24 13:42:01 +02:00
return - ENOSPC ;
2011-12-07 12:45:46 -08:00
if ( ( vif - > type = = NL80211_IFTYPE_ADHOC | |
vif - > type = = NL80211_IFTYPE_MESH_POINT ) & &
2011-03-23 14:52:19 +02:00
( key - > cipher = = WLAN_CIPHER_SUITE_TKIP | |
key - > cipher = = WLAN_CIPHER_SUITE_CCMP ) & &
! ( key - > flags & IEEE80211_KEY_FLAG_PAIRWISE ) ) {
/*
* For now , disable hw crypto for the RSN IBSS group keys . This
* could be optimized in the future to use a modified key cache
* design to support per - STA RX GTK , but until that gets
* implemented , use of software crypto for group addressed
* frames is a acceptable to allow RSN IBSS to be used .
*/
return - EOPNOTSUPP ;
}
2009-02-04 08:10:07 +05:30
mutex_lock ( & sc - > mutex ) ;
2009-01-20 11:17:08 +05:30
ath9k_ps_wakeup ( sc ) ;
2011-12-15 14:55:53 -08:00
ath_dbg ( common , CONFIG , " Set HW Key \n " ) ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
switch ( cmd ) {
case SET_KEY :
2011-04-17 23:28:10 +02:00
if ( sta )
ath9k_del_ps_key ( sc , vif , sta ) ;
2010-09-08 16:05:04 +09:00
ret = ath_key_config ( common , vif , sta , key ) ;
2008-12-17 13:32:17 +02:00
if ( ret > = 0 ) {
key - > hw_key_idx = ret ;
2008-09-10 18:49:27 +05:30
/* push IV and Michael MIC generation to stack */
key - > flags | = IEEE80211_KEY_FLAG_GENERATE_IV ;
2010-08-10 09:46:38 +02:00
if ( key - > cipher = = WLAN_CIPHER_SUITE_TKIP )
2008-09-10 18:49:27 +05:30
key - > flags | = IEEE80211_KEY_FLAG_GENERATE_MMIC ;
2010-08-10 09:46:38 +02:00
if ( sc - > sc_ah - > sw_mgmt_crypto & &
key - > cipher = = WLAN_CIPHER_SUITE_CCMP )
2009-01-08 13:32:13 +02:00
key - > flags | = IEEE80211_KEY_FLAG_SW_MGMT ;
2008-12-17 13:32:17 +02:00
ret = 0 ;
2008-09-10 18:49:27 +05:30
}
break ;
case DISABLE_KEY :
2010-09-08 16:05:04 +09:00
ath_key_delete ( common , key ) ;
2008-09-10 18:49:27 +05:30
break ;
default :
ret = - EINVAL ;
}
2008-08-04 00:16:41 -07:00
2009-01-20 11:17:08 +05:30
ath9k_ps_restore ( sc ) ;
2009-02-04 08:10:07 +05:30
mutex_unlock ( & sc - > mutex ) ;
2008-09-10 18:49:27 +05:30
return ret ;
}
2012-07-17 17:15:50 +05:30
static void ath9k_set_assoc_state ( struct ath_softc * sc ,
struct ieee80211_vif * vif )
2011-04-04 22:56:18 +05:30
{
struct ath_common * common = ath9k_hw_common ( sc - > sc_ah ) ;
struct ath_vif * avp = ( void * ) vif - > drv_priv ;
2012-07-17 17:15:50 +05:30
struct ieee80211_bss_conf * bss_conf = & vif - > bss_conf ;
2012-06-04 20:24:07 +05:30
unsigned long flags ;
2012-07-17 17:15:50 +05:30
set_bit ( SC_OP_PRIM_STA_VIF , & sc - > sc_flags ) ;
avp - > primary_sta_vif = true ;
2011-05-20 17:52:12 +05:30
/*
2012-07-17 17:15:50 +05:30
* Set the AID , BSSID and do beacon - sync only when
* the HW opmode is STATION .
*
* But the primary bit is set above in any case .
2011-05-20 17:52:12 +05:30
*/
2012-07-17 17:15:50 +05:30
if ( sc - > sc_ah - > opmode ! = NL80211_IFTYPE_STATION )
2011-05-20 17:52:12 +05:30
return ;
2012-07-17 17:15:50 +05:30
memcpy ( common - > curbssid , bss_conf - > bssid , ETH_ALEN ) ;
common - > curaid = bss_conf - > aid ;
ath9k_hw_write_associd ( sc - > sc_ah ) ;
2012-06-04 20:24:07 +05:30
2012-07-17 17:15:50 +05:30
sc - > last_rssi = ATH_RSSI_DUMMY_MARKER ;
sc - > sc_ah - > stats . avgbrssi = ATH_RSSI_DUMMY_MARKER ;
2011-04-04 22:56:19 +05:30
2012-07-17 17:15:50 +05:30
spin_lock_irqsave ( & sc - > sc_pm_lock , flags ) ;
sc - > ps_flags | = PS_BEACON_SYNC | PS_WAIT_FOR_BEACON ;
spin_unlock_irqrestore ( & sc - > sc_pm_lock , flags ) ;
2011-05-26 10:56:15 +05:30
2012-07-17 17:15:50 +05:30
ath_dbg ( common , CONFIG ,
" Primary Station interface: %pM, BSSID: %pM \n " ,
vif - > addr , common - > curbssid ) ;
2011-04-04 22:56:18 +05:30
}
2012-07-17 17:15:50 +05:30
static void ath9k_bss_assoc_iter ( void * data , u8 * mac , struct ieee80211_vif * vif )
2011-04-04 22:56:18 +05:30
{
2012-07-17 17:15:50 +05:30
struct ath_softc * sc = data ;
2011-04-04 22:56:18 +05:30
struct ieee80211_bss_conf * bss_conf = & vif - > bss_conf ;
2012-07-17 17:15:50 +05:30
if ( test_bit ( SC_OP_PRIM_STA_VIF , & sc - > sc_flags ) )
2011-05-20 17:52:12 +05:30
return ;
2012-07-17 17:15:50 +05:30
if ( bss_conf - > assoc )
ath9k_set_assoc_state ( sc , vif ) ;
2011-04-04 22:56:18 +05:30
}
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
static void ath9k_bss_info_changed ( struct ieee80211_hw * hw ,
struct ieee80211_vif * vif ,
struct ieee80211_bss_conf * bss_conf ,
u32 changed )
{
2012-07-17 17:16:29 +05:30
# define CHECK_ANI \
( BSS_CHANGED_ASSOC | \
BSS_CHANGED_IBSS | \
BSS_CHANGED_BEACON_ENABLED )
2011-01-24 19:23:18 +01:00
struct ath_softc * sc = hw - > priv ;
2009-04-23 16:13:26 +02:00
struct ath_hw * ah = sc - > sc_ah ;
2009-09-10 09:22:37 -07:00
struct ath_common * common = ath9k_hw_common ( ah ) ;
2009-04-23 16:13:26 +02:00
struct ath_vif * avp = ( void * ) vif - > drv_priv ;
2010-01-15 02:33:40 +01:00
int slottime ;
2008-08-04 00:16:41 -07:00
2011-04-07 19:07:17 +02:00
ath9k_ps_wakeup ( sc ) ;
2009-02-04 08:10:07 +05:30
mutex_lock ( & sc - > mutex ) ;
2012-03-10 05:06:49 +05:30
if ( changed & BSS_CHANGED_ASSOC ) {
2012-07-17 17:15:50 +05:30
ath_dbg ( common , CONFIG , " BSSID %pM Changed ASSOC %d \n " ,
bss_conf - > bssid , bss_conf - > assoc ) ;
if ( avp - > primary_sta_vif & & ! bss_conf - > assoc ) {
clear_bit ( SC_OP_PRIM_STA_VIF , & sc - > sc_flags ) ;
avp - > primary_sta_vif = false ;
if ( ah - > opmode = = NL80211_IFTYPE_STATION )
clear_bit ( SC_OP_BEACONS , & sc - > sc_flags ) ;
}
ieee80211_iterate_active_interfaces_atomic ( sc - > hw ,
ath9k_bss_assoc_iter , sc ) ;
2009-04-23 16:13:26 +02:00
2012-07-17 17:15:50 +05:30
if ( ! test_bit ( SC_OP_PRIM_STA_VIF , & sc - > sc_flags ) & &
ah - > opmode = = NL80211_IFTYPE_STATION ) {
memset ( common - > curbssid , 0 , ETH_ALEN ) ;
common - > curaid = 0 ;
ath9k_hw_write_associd ( sc - > sc_ah ) ;
}
2009-11-16 11:40:48 +05:30
}
2009-04-23 16:13:26 +02:00
2011-05-20 17:52:12 +05:30
if ( changed & BSS_CHANGED_IBSS ) {
memcpy ( common - > curbssid , bss_conf - > bssid , ETH_ALEN ) ;
common - > curaid = bss_conf - > aid ;
ath9k_hw_write_associd ( sc - > sc_ah ) ;
}
2012-07-17 17:15:56 +05:30
if ( ( changed & BSS_CHANGED_BEACON_ENABLED ) | |
( changed & BSS_CHANGED_BEACON_INT ) ) {
2012-07-17 17:16:16 +05:30
if ( ah - > opmode = = NL80211_IFTYPE_AP & &
bss_conf - > enable_beacon )
ath9k_set_tsfadjust ( sc , vif ) ;
2012-07-17 17:15:56 +05:30
if ( ath9k_allow_beacon_config ( sc , vif ) )
ath9k_beacon_config ( sc , vif , changed ) ;
2010-01-15 02:33:40 +01:00
}
if ( changed & BSS_CHANGED_ERP_SLOT ) {
if ( bss_conf - > use_short_slot )
slottime = 9 ;
else
slottime = 20 ;
if ( vif - > type = = NL80211_IFTYPE_AP ) {
/*
* Defer update , so that connected stations can adjust
* their settings at the same time .
* See beacon . c for more details
*/
sc - > beacon . slottime = slottime ;
sc - > beacon . updateslot = UPDATE ;
} else {
ah - > slottime = slottime ;
ath9k_hw_init_global_settings ( ah ) ;
}
2009-04-23 16:13:26 +02:00
}
2012-07-17 17:16:29 +05:30
if ( changed & CHECK_ANI )
ath_check_ani ( sc ) ;
2009-02-04 08:10:07 +05:30
mutex_unlock ( & sc - > mutex ) ;
2011-04-07 19:07:17 +02:00
ath9k_ps_restore ( sc ) ;
2012-07-17 17:16:29 +05:30
# undef CHECK_ANI
2008-09-10 18:49:27 +05:30
}
2008-08-04 00:16:41 -07:00
2011-09-21 14:06:11 +03:00
static u64 ath9k_get_tsf ( struct ieee80211_hw * hw , struct ieee80211_vif * vif )
2008-09-10 18:49:27 +05:30
{
2011-01-24 19:23:18 +01:00
struct ath_softc * sc = hw - > priv ;
2008-09-10 18:49:27 +05:30
u64 tsf ;
2008-08-04 00:16:41 -07:00
2009-02-04 08:10:07 +05:30
mutex_lock ( & sc - > mutex ) ;
2010-12-10 11:27:06 +05:30
ath9k_ps_wakeup ( sc ) ;
2009-02-04 08:10:07 +05:30
tsf = ath9k_hw_gettsf64 ( sc - > sc_ah ) ;
2010-12-10 11:27:06 +05:30
ath9k_ps_restore ( sc ) ;
2009-02-04 08:10:07 +05:30
mutex_unlock ( & sc - > mutex ) ;
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
return tsf ;
}
2008-08-04 00:16:41 -07:00
2011-09-21 14:06:11 +03:00
static void ath9k_set_tsf ( struct ieee80211_hw * hw ,
struct ieee80211_vif * vif ,
u64 tsf )
2009-01-24 07:09:59 +01:00
{
2011-01-24 19:23:18 +01:00
struct ath_softc * sc = hw - > priv ;
2009-01-24 07:09:59 +01:00
2009-02-04 08:10:07 +05:30
mutex_lock ( & sc - > mutex ) ;
2010-12-10 11:27:06 +05:30
ath9k_ps_wakeup ( sc ) ;
2009-02-04 08:10:07 +05:30
ath9k_hw_settsf64 ( sc - > sc_ah , tsf ) ;
2010-12-10 11:27:06 +05:30
ath9k_ps_restore ( sc ) ;
2009-02-04 08:10:07 +05:30
mutex_unlock ( & sc - > mutex ) ;
2009-01-24 07:09:59 +01:00
}
2011-09-21 14:06:11 +03:00
static void ath9k_reset_tsf ( struct ieee80211_hw * hw , struct ieee80211_vif * vif )
2008-09-10 18:49:27 +05:30
{
2011-01-24 19:23:18 +01:00
struct ath_softc * sc = hw - > priv ;
2008-08-25 20:47:29 +05:30
2009-02-04 08:10:07 +05:30
mutex_lock ( & sc - > mutex ) ;
2009-09-09 20:05:39 -07:00
ath9k_ps_wakeup ( sc ) ;
2009-02-04 08:10:07 +05:30
ath9k_hw_reset_tsf ( sc - > sc_ah ) ;
2009-09-09 20:05:39 -07:00
ath9k_ps_restore ( sc ) ;
2009-02-04 08:10:07 +05:30
mutex_unlock ( & sc - > mutex ) ;
2008-09-10 18:49:27 +05:30
}
2008-08-04 00:16:41 -07:00
2008-09-10 18:49:27 +05:30
static int ath9k_ampdu_action ( struct ieee80211_hw * hw ,
2009-11-16 12:00:38 +01:00
struct ieee80211_vif * vif ,
2009-02-04 08:10:07 +05:30
enum ieee80211_ampdu_mlme_action action ,
struct ieee80211_sta * sta ,
2011-01-18 13:51:05 +01:00
u16 tid , u16 * ssn , u8 buf_size )
2008-09-10 18:49:27 +05:30
{
2011-01-24 19:23:18 +01:00
struct ath_softc * sc = hw - > priv ;
2008-09-10 18:49:27 +05:30
int ret = 0 ;
2008-08-04 00:16:41 -07:00
2010-06-10 10:21:49 +02:00
local_bh_disable ( ) ;
2008-09-10 18:49:27 +05:30
switch ( action ) {
case IEEE80211_AMPDU_RX_START :
break ;
case IEEE80211_AMPDU_RX_STOP :
break ;
case IEEE80211_AMPDU_TX_START :
2009-12-23 20:03:29 -05:00
ath9k_ps_wakeup ( sc ) ;
2010-09-20 19:35:28 +02:00
ret = ath_tx_aggr_start ( sc , sta , tid , ssn ) ;
if ( ! ret )
ieee80211_start_tx_ba_cb_irqsafe ( vif , sta - > addr , tid ) ;
2009-12-23 20:03:29 -05:00
ath9k_ps_restore ( sc ) ;
2008-09-10 18:49:27 +05:30
break ;
case IEEE80211_AMPDU_TX_STOP :
2009-12-23 20:03:29 -05:00
ath9k_ps_wakeup ( sc ) ;
2009-07-23 15:32:37 +05:30
ath_tx_aggr_stop ( sc , sta , tid ) ;
2009-11-16 12:00:38 +01:00
ieee80211_stop_tx_ba_cb_irqsafe ( vif , sta - > addr , tid ) ;
2009-12-23 20:03:29 -05:00
ath9k_ps_restore ( sc ) ;
2008-09-10 18:49:27 +05:30
break ;
2009-03-23 17:28:39 +01:00
case IEEE80211_AMPDU_TX_OPERATIONAL :
2009-12-23 20:03:29 -05:00
ath9k_ps_wakeup ( sc ) ;
2008-10-29 10:19:28 +05:30
ath_tx_aggr_resume ( sc , sta , tid ) ;
2009-12-23 20:03:29 -05:00
ath9k_ps_restore ( sc ) ;
2008-10-29 10:19:28 +05:30
break ;
2008-09-10 18:49:27 +05:30
default :
2010-12-02 19:12:36 -08:00
ath_err ( ath9k_hw_common ( sc - > sc_ah ) , " Unknown AMPDU action \n " ) ;
2008-09-10 18:49:27 +05:30
}
2010-06-10 10:21:49 +02:00
local_bh_enable ( ) ;
2008-09-10 18:49:27 +05:30
return ret ;
2008-08-04 00:16:41 -07:00
}
2010-04-28 00:08:24 +02:00
static int ath9k_get_survey ( struct ieee80211_hw * hw , int idx ,
struct survey_info * survey )
{
2011-01-24 19:23:18 +01:00
struct ath_softc * sc = hw - > priv ;
2010-10-10 18:21:52 +02:00
struct ath_common * common = ath9k_hw_common ( sc - > sc_ah ) ;
2010-09-29 19:12:06 +02:00
struct ieee80211_supported_band * sband ;
2010-10-10 18:21:52 +02:00
struct ieee80211_channel * chan ;
unsigned long flags ;
int pos ;
spin_lock_irqsave ( & common - > cc_lock , flags ) ;
if ( idx = = 0 )
ath_update_survey_stats ( sc ) ;
2010-09-29 19:12:06 +02:00
sband = hw - > wiphy - > bands [ IEEE80211_BAND_2GHZ ] ;
if ( sband & & idx > = sband - > n_channels ) {
idx - = sband - > n_channels ;
sband = NULL ;
}
2010-04-28 00:08:24 +02:00
2010-09-29 19:12:06 +02:00
if ( ! sband )
sband = hw - > wiphy - > bands [ IEEE80211_BAND_5GHZ ] ;
2010-04-28 00:08:24 +02:00
2010-10-10 18:21:52 +02:00
if ( ! sband | | idx > = sband - > n_channels ) {
spin_unlock_irqrestore ( & common - > cc_lock , flags ) ;
return - ENOENT ;
2010-09-29 17:15:28 +02:00
}
2010-04-28 00:08:24 +02:00
2010-10-10 18:21:52 +02:00
chan = & sband - > channels [ idx ] ;
pos = chan - > hw_value ;
memcpy ( survey , & sc - > survey [ pos ] , sizeof ( * survey ) ) ;
survey - > channel = chan ;
spin_unlock_irqrestore ( & common - > cc_lock , flags ) ;
2010-04-28 00:08:24 +02:00
return 0 ;
}
2010-01-15 02:34:58 +01:00
static void ath9k_set_coverage_class ( struct ieee80211_hw * hw , u8 coverage_class )
{
2011-01-24 19:23:18 +01:00
struct ath_softc * sc = hw - > priv ;
2010-01-15 02:34:58 +01:00
struct ath_hw * ah = sc - > sc_ah ;
mutex_lock ( & sc - > mutex ) ;
ah - > coverage_class = coverage_class ;
2011-08-24 21:38:07 +05:30
ath9k_ps_wakeup ( sc ) ;
2010-01-15 02:34:58 +01:00
ath9k_hw_init_global_settings ( ah ) ;
2011-08-24 21:38:07 +05:30
ath9k_ps_restore ( sc ) ;
2010-01-15 02:34:58 +01:00
mutex_unlock ( & sc - > mutex ) ;
}
2011-02-19 01:13:42 -08:00
static void ath9k_flush ( struct ieee80211_hw * hw , bool drop )
{
struct ath_softc * sc = hw - > priv ;
2011-05-06 20:43:11 +05:30
struct ath_hw * ah = sc - > sc_ah ;
struct ath_common * common = ath9k_hw_common ( ah ) ;
2011-03-11 21:38:19 +01:00
int timeout = 200 ; /* ms */
int i , j ;
2011-04-28 15:31:57 +05:30
bool drain_txq ;
2011-02-19 01:13:42 -08:00
mutex_lock ( & sc - > mutex ) ;
cancel_delayed_work_sync ( & sc - > tx_complete_work ) ;
2011-09-09 10:41:08 +05:30
if ( ah - > ah_flags & AH_UNPLUGGED ) {
2011-12-15 14:55:53 -08:00
ath_dbg ( common , ANY , " Device has been unplugged! \n " ) ;
2011-09-09 10:41:08 +05:30
mutex_unlock ( & sc - > mutex ) ;
return ;
}
2012-06-04 20:23:55 +05:30
if ( test_bit ( SC_OP_INVALID , & sc - > sc_flags ) ) {
2011-12-15 14:55:53 -08:00
ath_dbg ( common , ANY , " Device not present \n " ) ;
2011-05-06 20:43:11 +05:30
mutex_unlock ( & sc - > mutex ) ;
return ;
}
2011-03-11 21:38:19 +01:00
for ( j = 0 ; j < timeout ; j + + ) {
2011-05-13 20:59:42 +05:30
bool npend = false ;
2011-03-11 21:38:19 +01:00
if ( j )
usleep_range ( 1000 , 2000 ) ;
2011-02-19 01:13:42 -08:00
2011-03-11 21:38:19 +01:00
for ( i = 0 ; i < ATH9K_NUM_TX_QUEUES ; i + + ) {
if ( ! ATH_TXQ_SETUP ( sc , i ) )
continue ;
2011-05-13 20:59:42 +05:30
npend = ath9k_has_pending_frames ( sc , & sc - > tx . txq [ i ] ) ;
if ( npend )
break ;
2011-02-19 01:13:42 -08:00
}
2011-03-11 21:38:19 +01:00
if ( ! npend )
2011-11-16 13:08:42 +01:00
break ;
2011-02-19 01:13:42 -08:00
}
2011-11-16 13:08:42 +01:00
if ( drop ) {
ath9k_ps_wakeup ( sc ) ;
spin_lock_bh ( & sc - > sc_pcu_lock ) ;
drain_txq = ath_drain_all_txq ( sc , false ) ;
spin_unlock_bh ( & sc - > sc_pcu_lock ) ;
2011-09-03 01:40:26 +02:00
2011-11-16 13:08:42 +01:00
if ( ! drain_txq )
ath_reset ( sc , false ) ;
2011-09-03 01:40:26 +02:00
2011-11-16 13:08:42 +01:00
ath9k_ps_restore ( sc ) ;
ieee80211_wake_queues ( hw ) ;
}
2011-03-23 23:07:22 +05:30
2011-02-19 01:13:42 -08:00
ieee80211_queue_delayed_work ( hw , & sc - > tx_complete_work , 0 ) ;
mutex_unlock ( & sc - > mutex ) ;
}
2011-04-06 11:41:11 +05:30
static bool ath9k_tx_frames_pending ( struct ieee80211_hw * hw )
{
struct ath_softc * sc = hw - > priv ;
int i ;
for ( i = 0 ; i < ATH9K_NUM_TX_QUEUES ; i + + ) {
if ( ! ATH_TXQ_SETUP ( sc , i ) )
continue ;
if ( ath9k_has_pending_frames ( sc , & sc - > tx . txq [ i ] ) )
return true ;
}
return false ;
}
2011-05-19 18:08:57 +05:30
static int ath9k_tx_last_beacon ( struct ieee80211_hw * hw )
2011-05-17 21:09:54 +02:00
{
struct ath_softc * sc = hw - > priv ;
struct ath_hw * ah = sc - > sc_ah ;
struct ieee80211_vif * vif ;
struct ath_vif * avp ;
struct ath_buf * bf ;
struct ath_tx_status ts ;
2012-02-27 19:58:40 +01:00
bool edma = ! ! ( ah - > caps . hw_caps & ATH9K_HW_CAP_EDMA ) ;
2011-05-17 21:09:54 +02:00
int status ;
vif = sc - > beacon . bslot [ 0 ] ;
if ( ! vif )
return 0 ;
2012-07-17 17:16:03 +05:30
if ( ! vif - > bss_conf . enable_beacon )
2011-05-17 21:09:54 +02:00
return 0 ;
2012-07-17 17:16:03 +05:30
avp = ( void * ) vif - > drv_priv ;
2012-02-27 19:58:40 +01:00
if ( ! sc - > beacon . tx_processed & & ! edma ) {
2011-05-17 21:09:54 +02:00
tasklet_disable ( & sc - > bcon_tasklet ) ;
bf = avp - > av_bcbuf ;
if ( ! bf | | ! bf - > bf_mpdu )
goto skip ;
status = ath9k_hw_txprocdesc ( ah , bf - > bf_desc , & ts ) ;
if ( status = = - EINPROGRESS )
goto skip ;
sc - > beacon . tx_processed = true ;
sc - > beacon . tx_last = ! ( ts . ts_status & ATH9K_TXERR_MASK ) ;
skip :
tasklet_enable ( & sc - > bcon_tasklet ) ;
}
return sc - > beacon . tx_last ;
}
2011-08-20 17:21:42 +05:30
static int ath9k_get_stats ( struct ieee80211_hw * hw ,
struct ieee80211_low_level_stats * stats )
{
struct ath_softc * sc = hw - > priv ;
struct ath_hw * ah = sc - > sc_ah ;
struct ath9k_mib_stats * mib_stats = & ah - > ah_mibStats ;
stats - > dot11ACKFailureCount = mib_stats - > ackrcv_bad ;
stats - > dot11RTSFailureCount = mib_stats - > rts_bad ;
stats - > dot11FCSErrorCount = mib_stats - > fcs_bad ;
stats - > dot11RTSSuccessCount = mib_stats - > rts_good ;
return 0 ;
}
2011-09-03 01:40:27 +02:00
static u32 fill_chainmask ( u32 cap , u32 new )
{
u32 filled = 0 ;
int i ;
for ( i = 0 ; cap & & new ; i + + , cap > > = 1 ) {
if ( ! ( cap & BIT ( 0 ) ) )
continue ;
if ( new & BIT ( 0 ) )
filled | = BIT ( i ) ;
new > > = 1 ;
}
return filled ;
}
2012-07-15 19:53:30 +02:00
static bool validate_antenna_mask ( struct ath_hw * ah , u32 val )
{
switch ( val & 0x7 ) {
case 0x1 :
case 0x3 :
case 0x7 :
return true ;
case 0x2 :
return ( ah - > caps . rx_chainmask = = 1 ) ;
default :
return false ;
}
}
2011-09-03 01:40:27 +02:00
static int ath9k_set_antenna ( struct ieee80211_hw * hw , u32 tx_ant , u32 rx_ant )
{
struct ath_softc * sc = hw - > priv ;
struct ath_hw * ah = sc - > sc_ah ;
2012-07-15 19:53:30 +02:00
if ( ah - > caps . rx_chainmask ! = 1 )
rx_ant | = tx_ant ;
if ( ! validate_antenna_mask ( ah , rx_ant ) | | ! tx_ant )
2011-09-03 01:40:27 +02:00
return - EINVAL ;
sc - > ant_rx = rx_ant ;
sc - > ant_tx = tx_ant ;
if ( ah - > caps . rx_chainmask = = 1 )
return 0 ;
/* AR9100 runs into calibration issues if not all rx chains are enabled */
if ( AR_SREV_9100 ( ah ) )
ah - > rxchainmask = 0x7 ;
else
ah - > rxchainmask = fill_chainmask ( ah - > caps . rx_chainmask , rx_ant ) ;
ah - > txchainmask = fill_chainmask ( ah - > caps . tx_chainmask , tx_ant ) ;
ath9k_reload_chainmask_settings ( sc ) ;
return 0 ;
}
static int ath9k_get_antenna ( struct ieee80211_hw * hw , u32 * tx_ant , u32 * rx_ant )
{
struct ath_softc * sc = hw - > priv ;
* tx_ant = sc - > ant_tx ;
* rx_ant = sc - > ant_rx ;
return 0 ;
}
2012-05-15 15:33:25 -07:00
# ifdef CONFIG_ATH9K_DEBUGFS
/* Ethtool support for get-stats */
# define AMKSTR(nm) #nm "_BE", #nm "_BK", #nm "_VI", #nm "_VO"
static const char ath9k_gstrings_stats [ ] [ ETH_GSTRING_LEN ] = {
" tx_pkts_nic " ,
" tx_bytes_nic " ,
" rx_pkts_nic " ,
" rx_bytes_nic " ,
AMKSTR ( d_tx_pkts ) ,
AMKSTR ( d_tx_bytes ) ,
AMKSTR ( d_tx_mpdus_queued ) ,
AMKSTR ( d_tx_mpdus_completed ) ,
AMKSTR ( d_tx_mpdu_xretries ) ,
AMKSTR ( d_tx_aggregates ) ,
AMKSTR ( d_tx_ampdus_queued_hw ) ,
AMKSTR ( d_tx_ampdus_queued_sw ) ,
AMKSTR ( d_tx_ampdus_completed ) ,
AMKSTR ( d_tx_ampdu_retries ) ,
AMKSTR ( d_tx_ampdu_xretries ) ,
AMKSTR ( d_tx_fifo_underrun ) ,
AMKSTR ( d_tx_op_exceeded ) ,
AMKSTR ( d_tx_timer_expiry ) ,
AMKSTR ( d_tx_desc_cfg_err ) ,
AMKSTR ( d_tx_data_underrun ) ,
AMKSTR ( d_tx_delim_underrun ) ,
" d_rx_decrypt_crc_err " ,
" d_rx_phy_err " ,
" d_rx_mic_err " ,
" d_rx_pre_delim_crc_err " ,
" d_rx_post_delim_crc_err " ,
" d_rx_decrypt_busy_err " ,
" d_rx_phyerr_radar " ,
" d_rx_phyerr_ofdm_timing " ,
" d_rx_phyerr_cck_timing " ,
} ;
# define ATH9K_SSTATS_LEN ARRAY_SIZE(ath9k_gstrings_stats)
static void ath9k_get_et_strings ( struct ieee80211_hw * hw ,
struct ieee80211_vif * vif ,
u32 sset , u8 * data )
{
if ( sset = = ETH_SS_STATS )
memcpy ( data , * ath9k_gstrings_stats ,
sizeof ( ath9k_gstrings_stats ) ) ;
}
static int ath9k_get_et_sset_count ( struct ieee80211_hw * hw ,
struct ieee80211_vif * vif , int sset )
{
if ( sset = = ETH_SS_STATS )
return ATH9K_SSTATS_LEN ;
return 0 ;
}
# define PR_QNUM(_n) (sc->tx.txq_map[_n]->axq_qnum)
# define AWDATA(elem) \
do { \
data [ i + + ] = sc - > debug . stats . txstats [ PR_QNUM ( WME_AC_BE ) ] . elem ; \
data [ i + + ] = sc - > debug . stats . txstats [ PR_QNUM ( WME_AC_BK ) ] . elem ; \
data [ i + + ] = sc - > debug . stats . txstats [ PR_QNUM ( WME_AC_VI ) ] . elem ; \
data [ i + + ] = sc - > debug . stats . txstats [ PR_QNUM ( WME_AC_VO ) ] . elem ; \
} while ( 0 )
# define AWDATA_RX(elem) \
do { \
data [ i + + ] = sc - > debug . stats . rxstats . elem ; \
} while ( 0 )
static void ath9k_get_et_stats ( struct ieee80211_hw * hw ,
struct ieee80211_vif * vif ,
struct ethtool_stats * stats , u64 * data )
{
struct ath_softc * sc = hw - > priv ;
int i = 0 ;
data [ i + + ] = ( sc - > debug . stats . txstats [ PR_QNUM ( WME_AC_BE ) ] . tx_pkts_all +
sc - > debug . stats . txstats [ PR_QNUM ( WME_AC_BK ) ] . tx_pkts_all +
sc - > debug . stats . txstats [ PR_QNUM ( WME_AC_VI ) ] . tx_pkts_all +
sc - > debug . stats . txstats [ PR_QNUM ( WME_AC_VO ) ] . tx_pkts_all ) ;
data [ i + + ] = ( sc - > debug . stats . txstats [ PR_QNUM ( WME_AC_BE ) ] . tx_bytes_all +
sc - > debug . stats . txstats [ PR_QNUM ( WME_AC_BK ) ] . tx_bytes_all +
sc - > debug . stats . txstats [ PR_QNUM ( WME_AC_VI ) ] . tx_bytes_all +
sc - > debug . stats . txstats [ PR_QNUM ( WME_AC_VO ) ] . tx_bytes_all ) ;
AWDATA_RX ( rx_pkts_all ) ;
AWDATA_RX ( rx_bytes_all ) ;
AWDATA ( tx_pkts_all ) ;
AWDATA ( tx_bytes_all ) ;
AWDATA ( queued ) ;
AWDATA ( completed ) ;
AWDATA ( xretries ) ;
AWDATA ( a_aggr ) ;
AWDATA ( a_queued_hw ) ;
AWDATA ( a_queued_sw ) ;
AWDATA ( a_completed ) ;
AWDATA ( a_retries ) ;
AWDATA ( a_xretries ) ;
AWDATA ( fifo_underrun ) ;
AWDATA ( xtxop ) ;
AWDATA ( timer_exp ) ;
AWDATA ( desc_cfg_err ) ;
AWDATA ( data_underrun ) ;
AWDATA ( delim_underrun ) ;
AWDATA_RX ( decrypt_crc_err ) ;
AWDATA_RX ( phy_err ) ;
AWDATA_RX ( mic_err ) ;
AWDATA_RX ( pre_delim_crc_err ) ;
AWDATA_RX ( post_delim_crc_err ) ;
AWDATA_RX ( decrypt_busy_err ) ;
AWDATA_RX ( phy_err_stats [ ATH9K_PHYERR_RADAR ] ) ;
AWDATA_RX ( phy_err_stats [ ATH9K_PHYERR_OFDM_TIMING ] ) ;
AWDATA_RX ( phy_err_stats [ ATH9K_PHYERR_CCK_TIMING ] ) ;
WARN_ON ( i ! = ATH9K_SSTATS_LEN ) ;
}
/* End of ethtool get-stats functions */
# endif
2012-07-10 14:56:52 +05:30
# ifdef CONFIG_PM_SLEEP
static void ath9k_wow_map_triggers ( struct ath_softc * sc ,
struct cfg80211_wowlan * wowlan ,
u32 * wow_triggers )
{
if ( wowlan - > disconnect )
* wow_triggers | = AH_WOW_LINK_CHANGE |
AH_WOW_BEACON_MISS ;
if ( wowlan - > magic_pkt )
* wow_triggers | = AH_WOW_MAGIC_PATTERN_EN ;
if ( wowlan - > n_patterns )
* wow_triggers | = AH_WOW_USER_PATTERN_EN ;
sc - > wow_enabled = * wow_triggers ;
}
static void ath9k_wow_add_disassoc_deauth_pattern ( struct ath_softc * sc )
{
struct ath_hw * ah = sc - > sc_ah ;
struct ath_common * common = ath9k_hw_common ( ah ) ;
struct ath9k_hw_capabilities * pcaps = & ah - > caps ;
int pattern_count = 0 ;
int i , byte_cnt ;
u8 dis_deauth_pattern [ MAX_PATTERN_SIZE ] ;
u8 dis_deauth_mask [ MAX_PATTERN_SIZE ] ;
memset ( dis_deauth_pattern , 0 , MAX_PATTERN_SIZE ) ;
memset ( dis_deauth_mask , 0 , MAX_PATTERN_SIZE ) ;
/*
* Create Dissassociate / Deauthenticate packet filter
*
* 2 bytes 2 byte 6 bytes 6 bytes 6 bytes
* + - - - - - - - - - - - - - - + - - - - - - - - - - + - - - - - - - - - + - - - - - - - - + - - - - - - - - + - - - -
* + Frame Control + Duration + DA + SA + BSSID +
* + - - - - - - - - - - - - - - + - - - - - - - - - - + - - - - - - - - - + - - - - - - - - + - - - - - - - - + - - - -
*
* The above is the management frame format for disassociate /
* deauthenticate pattern , from this we need to match the first byte
* of ' Frame Control ' and DA , SA , and BSSID fields
* ( skipping 2 nd byte of FC and Duration feild .
*
* Disassociate pattern
* - - - - - - - - - - - - - - - - - - - -
* Frame control = 00 00 1010
* DA , SA , BSSID = x : x : x : x : x : x
* Pattern will be A0000000 | x : x : x : x : x : x | x : x : x : x : x : x
* | x : x : x : x : x : x - - 22 bytes
*
* Deauthenticate pattern
* - - - - - - - - - - - - - - - - - - - - - -
* Frame control = 00 00 1100
* DA , SA , BSSID = x : x : x : x : x : x
* Pattern will be C0000000 | x : x : x : x : x : x | x : x : x : x : x : x
* | x : x : x : x : x : x - - 22 bytes
*/
/* Create Disassociate Pattern first */
byte_cnt = 0 ;
/* Fill out the mask with all FF's */
for ( i = 0 ; i < MAX_PATTERN_MASK_SIZE ; i + + )
dis_deauth_mask [ i ] = 0xff ;
/* copy the first byte of frame control field */
dis_deauth_pattern [ byte_cnt ] = 0xa0 ;
byte_cnt + + ;
/* skip 2nd byte of frame control and Duration field */
byte_cnt + = 3 ;
/*
* need not match the destination mac address , it can be a broadcast
* mac address or an unicast to this station
*/
byte_cnt + = 6 ;
/* copy the source mac address */
memcpy ( ( dis_deauth_pattern + byte_cnt ) , common - > curbssid , ETH_ALEN ) ;
byte_cnt + = 6 ;
/* copy the bssid, its same as the source mac address */
memcpy ( ( dis_deauth_pattern + byte_cnt ) , common - > curbssid , ETH_ALEN ) ;
/* Create Disassociate pattern mask */
if ( pcaps - > hw_caps & ATH9K_HW_WOW_PATTERN_MATCH_EXACT ) {
if ( pcaps - > hw_caps & ATH9K_HW_WOW_PATTERN_MATCH_DWORD ) {
/*
* for AR9280 , because of hardware limitation , the
* first 4 bytes have to be matched for all patterns .
* the mask for disassociation and de - auth pattern
* matching need to enable the first 4 bytes .
* also the duration field needs to be filled .
*/
dis_deauth_mask [ 0 ] = 0xf0 ;
/*
* fill in duration field
FIXME : what is the exact value ?
*/
dis_deauth_pattern [ 2 ] = 0xff ;
dis_deauth_pattern [ 3 ] = 0xff ;
} else {
dis_deauth_mask [ 0 ] = 0xfe ;
}
dis_deauth_mask [ 1 ] = 0x03 ;
dis_deauth_mask [ 2 ] = 0xc0 ;
} else {
dis_deauth_mask [ 0 ] = 0xef ;
dis_deauth_mask [ 1 ] = 0x3f ;
dis_deauth_mask [ 2 ] = 0x00 ;
dis_deauth_mask [ 3 ] = 0xfc ;
}
ath_dbg ( common , WOW , " Adding disassoc/deauth patterns for WoW \n " ) ;
ath9k_hw_wow_apply_pattern ( ah , dis_deauth_pattern , dis_deauth_mask ,
pattern_count , byte_cnt ) ;
pattern_count + + ;
/*
* for de - authenticate pattern , only the first byte of the frame
* control field gets changed from 0xA0 to 0xC0
*/
dis_deauth_pattern [ 0 ] = 0xC0 ;
ath9k_hw_wow_apply_pattern ( ah , dis_deauth_pattern , dis_deauth_mask ,
pattern_count , byte_cnt ) ;
}
static void ath9k_wow_add_pattern ( struct ath_softc * sc ,
struct cfg80211_wowlan * wowlan )
{
struct ath_hw * ah = sc - > sc_ah ;
struct ath9k_wow_pattern * wow_pattern = NULL ;
struct cfg80211_wowlan_trig_pkt_pattern * patterns = wowlan - > patterns ;
int mask_len ;
s8 i = 0 ;
if ( ! wowlan - > n_patterns )
return ;
/*
* Add the new user configured patterns
*/
for ( i = 0 ; i < wowlan - > n_patterns ; i + + ) {
wow_pattern = kzalloc ( sizeof ( * wow_pattern ) , GFP_KERNEL ) ;
if ( ! wow_pattern )
return ;
/*
* TODO : convert the generic user space pattern to
* appropriate chip specific / 802.11 pattern .
*/
mask_len = DIV_ROUND_UP ( wowlan - > patterns [ i ] . pattern_len , 8 ) ;
memset ( wow_pattern - > pattern_bytes , 0 , MAX_PATTERN_SIZE ) ;
memset ( wow_pattern - > mask_bytes , 0 , MAX_PATTERN_SIZE ) ;
memcpy ( wow_pattern - > pattern_bytes , patterns [ i ] . pattern ,
patterns [ i ] . pattern_len ) ;
memcpy ( wow_pattern - > mask_bytes , patterns [ i ] . mask , mask_len ) ;
wow_pattern - > pattern_len = patterns [ i ] . pattern_len ;
/*
* just need to take care of deauth and disssoc pattern ,
* make sure we don ' t overwrite them .
*/
ath9k_hw_wow_apply_pattern ( ah , wow_pattern - > pattern_bytes ,
wow_pattern - > mask_bytes ,
i + 2 ,
wow_pattern - > pattern_len ) ;
kfree ( wow_pattern ) ;
}
}
static int ath9k_suspend ( struct ieee80211_hw * hw ,
struct cfg80211_wowlan * wowlan )
{
struct ath_softc * sc = hw - > priv ;
struct ath_hw * ah = sc - > sc_ah ;
struct ath_common * common = ath9k_hw_common ( ah ) ;
u32 wow_triggers_enabled = 0 ;
int ret = 0 ;
mutex_lock ( & sc - > mutex ) ;
ath_cancel_work ( sc ) ;
del_timer_sync ( & common - > ani . timer ) ;
del_timer_sync ( & sc - > rx_poll_timer ) ;
if ( test_bit ( SC_OP_INVALID , & sc - > sc_flags ) ) {
ath_dbg ( common , ANY , " Device not present \n " ) ;
ret = - EINVAL ;
goto fail_wow ;
}
if ( WARN_ON ( ! wowlan ) ) {
ath_dbg ( common , WOW , " None of the WoW triggers enabled \n " ) ;
ret = - EINVAL ;
goto fail_wow ;
}
if ( ! device_can_wakeup ( sc - > dev ) ) {
ath_dbg ( common , WOW , " device_can_wakeup failed, WoW is not enabled \n " ) ;
ret = 1 ;
goto fail_wow ;
}
/*
* none of the sta vifs are associated
* and we are not currently handling multivif
* cases , for instance we have to seperately
* configure ' keep alive frame ' for each
* STA .
*/
if ( ! test_bit ( SC_OP_PRIM_STA_VIF , & sc - > sc_flags ) ) {
ath_dbg ( common , WOW , " None of the STA vifs are associated \n " ) ;
ret = 1 ;
goto fail_wow ;
}
if ( sc - > nvifs > 1 ) {
ath_dbg ( common , WOW , " WoW for multivif is not yet supported \n " ) ;
ret = 1 ;
goto fail_wow ;
}
ath9k_wow_map_triggers ( sc , wowlan , & wow_triggers_enabled ) ;
ath_dbg ( common , WOW , " WoW triggers enabled 0x%x \n " ,
wow_triggers_enabled ) ;
ath9k_ps_wakeup ( sc ) ;
ath9k_stop_btcoex ( sc ) ;
/*
* Enable wake up on recieving disassoc / deauth
* frame by default .
*/
ath9k_wow_add_disassoc_deauth_pattern ( sc ) ;
if ( wow_triggers_enabled & AH_WOW_USER_PATTERN_EN )
ath9k_wow_add_pattern ( sc , wowlan ) ;
spin_lock_bh ( & sc - > sc_pcu_lock ) ;
/*
* To avoid false wake , we enable beacon miss interrupt only
* when we go to sleep . We save the current interrupt mask
* so we can restore it after the system wakes up
*/
sc - > wow_intr_before_sleep = ah - > imask ;
ah - > imask & = ~ ATH9K_INT_GLOBAL ;
ath9k_hw_disable_interrupts ( ah ) ;
ah - > imask = ATH9K_INT_BMISS | ATH9K_INT_GLOBAL ;
ath9k_hw_set_interrupts ( ah ) ;
ath9k_hw_enable_interrupts ( ah ) ;
spin_unlock_bh ( & sc - > sc_pcu_lock ) ;
/*
* we can now sync irq and kill any running tasklets , since we already
* disabled interrupts and not holding a spin lock
*/
synchronize_irq ( sc - > irq ) ;
tasklet_kill ( & sc - > intr_tq ) ;
ath9k_hw_wow_enable ( ah , wow_triggers_enabled ) ;
ath9k_ps_restore ( sc ) ;
ath_dbg ( common , ANY , " WoW enabled in ath9k \n " ) ;
atomic_inc ( & sc - > wow_sleep_proc_intr ) ;
fail_wow :
mutex_unlock ( & sc - > mutex ) ;
return ret ;
}
static int ath9k_resume ( struct ieee80211_hw * hw )
{
struct ath_softc * sc = hw - > priv ;
struct ath_hw * ah = sc - > sc_ah ;
struct ath_common * common = ath9k_hw_common ( ah ) ;
u32 wow_status ;
mutex_lock ( & sc - > mutex ) ;
ath9k_ps_wakeup ( sc ) ;
spin_lock_bh ( & sc - > sc_pcu_lock ) ;
ath9k_hw_disable_interrupts ( ah ) ;
ah - > imask = sc - > wow_intr_before_sleep ;
ath9k_hw_set_interrupts ( ah ) ;
ath9k_hw_enable_interrupts ( ah ) ;
spin_unlock_bh ( & sc - > sc_pcu_lock ) ;
wow_status = ath9k_hw_wow_wakeup ( ah ) ;
if ( atomic_read ( & sc - > wow_got_bmiss_intr ) = = 0 ) {
/*
* some devices may not pick beacon miss
* as the reason they woke up so we add
* that here for that shortcoming .
*/
wow_status | = AH_WOW_BEACON_MISS ;
atomic_dec ( & sc - > wow_got_bmiss_intr ) ;
ath_dbg ( common , ANY , " Beacon miss interrupt picked up during WoW sleep \n " ) ;
}
atomic_dec ( & sc - > wow_sleep_proc_intr ) ;
if ( wow_status ) {
ath_dbg ( common , ANY , " Waking up due to WoW triggers %s with WoW status = %x \n " ,
ath9k_hw_wow_event_to_string ( wow_status ) , wow_status ) ;
}
ath_restart_work ( sc ) ;
ath9k_start_btcoex ( sc ) ;
ath9k_ps_restore ( sc ) ;
mutex_unlock ( & sc - > mutex ) ;
return 0 ;
}
static void ath9k_set_wakeup ( struct ieee80211_hw * hw , bool enabled )
{
struct ath_softc * sc = hw - > priv ;
mutex_lock ( & sc - > mutex ) ;
device_init_wakeup ( sc - > dev , 1 ) ;
device_set_wakeup_enable ( sc - > dev , enabled ) ;
mutex_unlock ( & sc - > mutex ) ;
}
# endif
2009-01-14 20:17:06 +01:00
struct ieee80211_ops ath9k_ops = {
2008-09-10 18:49:27 +05:30
. tx = ath9k_tx ,
. start = ath9k_start ,
. stop = ath9k_stop ,
. add_interface = ath9k_add_interface ,
2010-12-08 19:38:55 +05:30
. change_interface = ath9k_change_interface ,
2008-09-10 18:49:27 +05:30
. remove_interface = ath9k_remove_interface ,
. config = ath9k_config ,
. configure_filter = ath9k_configure_filter ,
2010-02-19 19:06:56 +01:00
. sta_add = ath9k_sta_add ,
. sta_remove = ath9k_sta_remove ,
2011-04-17 23:28:09 +02:00
. sta_notify = ath9k_sta_notify ,
2008-09-10 18:49:27 +05:30
. conf_tx = ath9k_conf_tx ,
. bss_info_changed = ath9k_bss_info_changed ,
. set_key = ath9k_set_key ,
. get_tsf = ath9k_get_tsf ,
2009-01-24 07:09:59 +01:00
. set_tsf = ath9k_set_tsf ,
2008-09-10 18:49:27 +05:30
. reset_tsf = ath9k_reset_tsf ,
2008-10-13 13:35:05 +02:00
. ampdu_action = ath9k_ampdu_action ,
2010-04-28 00:08:24 +02:00
. get_survey = ath9k_get_survey ,
2009-06-13 14:50:26 +05:30
. rfkill_poll = ath9k_rfkill_poll_state ,
2010-01-15 02:34:58 +01:00
. set_coverage_class = ath9k_set_coverage_class ,
2011-02-19 01:13:42 -08:00
. flush = ath9k_flush ,
2011-04-06 11:41:11 +05:30
. tx_frames_pending = ath9k_tx_frames_pending ,
2011-08-20 17:21:42 +05:30
. tx_last_beacon = ath9k_tx_last_beacon ,
. get_stats = ath9k_get_stats ,
2011-09-03 01:40:27 +02:00
. set_antenna = ath9k_set_antenna ,
. get_antenna = ath9k_get_antenna ,
2012-05-15 15:33:25 -07:00
2012-07-10 14:56:52 +05:30
# ifdef CONFIG_PM_SLEEP
. suspend = ath9k_suspend ,
. resume = ath9k_resume ,
. set_wakeup = ath9k_set_wakeup ,
# endif
2012-05-15 15:33:25 -07:00
# ifdef CONFIG_ATH9K_DEBUGFS
. get_et_sset_count = ath9k_get_et_sset_count ,
. get_et_stats = ath9k_get_et_stats ,
. get_et_strings = ath9k_get_et_strings ,
# endif
2008-09-10 18:49:27 +05:30
} ;