2008-08-04 11:16:41 +04:00
/*
2009-03-13 06:37:23 +03:00
* Copyright ( c ) 2008 - 2009 Atheros Communications Inc .
2008-08-04 11:16:41 +04: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 12:13:42 +03:00
# include <linux/delay.h>
2009-02-09 10:56:54 +03:00
# include "ath9k.h"
2009-09-09 13:33:11 +04:00
# include "btcoex.h"
2008-08-04 11:16:41 +04:00
2008-11-24 09:37:55 +03:00
static u8 parse_mpdudensity ( u8 mpdudensity )
{
/*
* 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 12:13:42 +03: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 ;
else if ( sc - > sc_ah - > caps . hw_caps & ATH9K_HW_CAP_EDMA )
pending = ! list_empty ( & txq - > txq_fifo_pending ) ;
spin_unlock_bh ( & txq - > axq_lock ) ;
return pending ;
}
2010-01-08 08:06:02 +03:00
bool ath9k_setpower ( struct ath_softc * sc , enum ath9k_power_mode mode )
2009-09-10 08:02:34 +04:00
{
unsigned long flags ;
bool ret ;
2009-09-10 08:10:09 +04: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-10 08:02:34 +04:00
return ret ;
}
2009-09-10 07:29:18 +04:00
void ath9k_ps_wakeup ( struct ath_softc * sc )
{
2010-10-12 16:02:53 +04:00
struct ath_common * common = ath9k_hw_common ( sc - > sc_ah ) ;
2009-09-10 07:29:18 +04:00
unsigned long flags ;
2010-11-03 03:36:51 +03:00
enum ath9k_power_mode power_mode ;
2009-09-10 07:29:18 +04:00
spin_lock_irqsave ( & sc - > sc_pm_lock , flags ) ;
if ( + + sc - > ps_usecount ! = 1 )
goto unlock ;
2010-11-03 03:36:51 +03:00
power_mode = sc - > sc_ah - > power_mode ;
2009-09-10 08:10:09 +04:00
ath9k_hw_setpower ( sc - > sc_ah , ATH9K_PM_AWAKE ) ;
2009-09-10 07:29:18 +04:00
2010-10-12 16:02:53 +04: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 03:36:51 +03: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 ) ) ;
spin_unlock ( & common - > cc_lock ) ;
}
2010-10-12 16:02:53 +04:00
2009-09-10 07:29:18 +04:00
unlock :
spin_unlock_irqrestore ( & sc - > sc_pm_lock , flags ) ;
}
void ath9k_ps_restore ( struct ath_softc * sc )
{
2010-10-12 16:02:53 +04:00
struct ath_common * common = ath9k_hw_common ( sc - > sc_ah ) ;
2009-09-10 07:29:18 +04:00
unsigned long flags ;
spin_lock_irqsave ( & sc - > sc_pm_lock , flags ) ;
if ( - - sc - > ps_usecount ! = 0 )
goto unlock ;
2010-10-12 16:02:53 +04:00
spin_lock ( & common - > cc_lock ) ;
ath_hw_cycle_counters_update ( common ) ;
spin_unlock ( & common - > cc_lock ) ;
2010-01-29 14:26:51 +03:00
if ( sc - > ps_idle )
ath9k_hw_setpower ( sc - > sc_ah , ATH9K_PM_FULL_SLEEP ) ;
else if ( sc - > ps_enabled & &
! ( sc - > ps_flags & ( PS_WAIT_FOR_BEACON |
2010-01-08 08:06:05 +03:00
PS_WAIT_FOR_CAB |
PS_WAIT_FOR_PSPOLL_DATA |
PS_WAIT_FOR_TX_ACK ) ) )
2009-09-10 08:10:09 +04:00
ath9k_hw_setpower ( sc - > sc_ah , ATH9K_PM_NETWORK_SLEEP ) ;
2009-09-10 07:29:18 +04:00
unlock :
spin_unlock_irqrestore ( & sc - > sc_pm_lock , flags ) ;
}
2010-07-31 02:11:59 +04:00
static void ath_start_ani ( struct ath_common * common )
{
struct ath_hw * ah = common - > ah ;
unsigned long timestamp = jiffies_to_msecs ( jiffies ) ;
struct ath_softc * sc = ( struct ath_softc * ) common - > priv ;
if ( ! ( sc - > sc_flags & SC_OP_ANI_RUN ) )
return ;
if ( sc - > sc_flags & SC_OP_OFFCHANNEL )
return ;
common - > ani . longcal_timer = timestamp ;
common - > ani . shortcal_timer = timestamp ;
common - > ani . checkani_timer = timestamp ;
mod_timer ( & common - > ani . timer ,
jiffies +
msecs_to_jiffies ( ( u32 ) ah - > config . ani_poll_interval ) ) ;
}
2010-10-10 20:21:52 +04:00
static void ath_update_survey_nf ( struct ath_softc * sc , int channel )
{
struct ath_hw * ah = sc - > sc_ah ;
struct ath9k_channel * chan = & ah - > channels [ channel ] ;
struct survey_info * survey = & sc - > survey [ channel ] ;
if ( chan - > noisefloor ) {
survey - > filled | = SURVEY_INFO_NOISE_DBM ;
survey - > noise = chan - > noisefloor ;
}
}
2011-02-04 22:09:25 +03:00
/*
* Updates the survey statistics and returns the busy time since last
* update in % , if the measurement duration was long enough for the
* result to be useful , - 1 otherwise .
*/
static int ath_update_survey_stats ( struct ath_softc * sc )
2010-10-10 20:21:52 +04:00
{
struct ath_hw * ah = sc - > sc_ah ;
struct ath_common * common = ath9k_hw_common ( ah ) ;
int pos = ah - > curchan - & ah - > channels [ 0 ] ;
struct survey_info * survey = & sc - > survey [ pos ] ;
struct ath_cycle_counters * cc = & common - > cc_survey ;
unsigned int div = common - > clockrate * 1000 ;
2011-02-04 22:09:25 +03:00
int ret = 0 ;
2010-10-10 20:21:52 +04:00
2010-10-20 17:59:28 +04:00
if ( ! ah - > curchan )
2011-02-04 22:09:25 +03:00
return - 1 ;
2010-10-20 17:59:28 +04:00
2010-10-12 16:02:53 +04:00
if ( ah - > power_mode = = ATH9K_PM_AWAKE )
ath_hw_cycle_counters_update ( common ) ;
2010-10-10 20:21:52 +04:00
if ( cc - > cycles > 0 ) {
survey - > filled | = SURVEY_INFO_CHANNEL_TIME |
SURVEY_INFO_CHANNEL_TIME_BUSY |
SURVEY_INFO_CHANNEL_TIME_RX |
SURVEY_INFO_CHANNEL_TIME_TX ;
survey - > channel_time + = cc - > cycles / div ;
survey - > channel_time_busy + = cc - > rx_busy / div ;
survey - > channel_time_rx + = cc - > rx_frame / div ;
survey - > channel_time_tx + = cc - > tx_frame / div ;
}
2011-02-04 22:09:25 +03:00
if ( cc - > cycles < div )
return - 1 ;
if ( cc - > cycles > 0 )
ret = cc - > rx_busy * 100 / cc - > cycles ;
2010-10-10 20:21:52 +04:00
memset ( cc , 0 , sizeof ( * cc ) ) ;
ath_update_survey_nf ( sc , pos ) ;
2011-02-04 22:09:25 +03:00
return ret ;
2010-10-10 20:21:52 +04:00
}
2008-11-24 09:37:55 +03:00
/*
* 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 .
*/
2009-03-03 20:23:32 +03:00
int ath_set_channel ( struct ath_softc * sc , struct ieee80211_hw * hw ,
struct ath9k_channel * hchan )
2008-11-24 09:37:55 +03:00
{
2009-02-09 10:57:12 +03:00
struct ath_hw * ah = sc - > sc_ah ;
2009-09-13 13:42:02 +04:00
struct ath_common * common = ath9k_hw_common ( ah ) ;
2009-09-14 10:04:44 +04:00
struct ieee80211_conf * conf = & common - > hw - > conf ;
2008-11-24 09:37:55 +03:00
bool fastcc = true , stopped ;
2008-12-24 02:58:40 +03:00
struct ieee80211_channel * channel = hw - > conf . channel ;
2010-07-31 02:12:00 +04:00
struct ath9k_hw_cal_data * caldata = NULL ;
2008-12-24 02:58:40 +03:00
int r ;
2008-11-24 09:37:55 +03:00
if ( sc - > sc_flags & SC_OP_INVALID )
return - EIO ;
2011-02-04 22:09:25 +03:00
sc - > hw_busy_count = 0 ;
2010-07-31 02:11:59 +04:00
del_timer_sync ( & common - > ani . timer ) ;
cancel_work_sync ( & sc - > paprd_work ) ;
cancel_work_sync ( & sc - > hw_check_work ) ;
cancel_delayed_work_sync ( & sc - > tx_complete_work ) ;
2011-01-27 12:15:08 +03:00
cancel_delayed_work_sync ( & sc - > hw_pll_work ) ;
2010-07-31 02:11:59 +04:00
2009-01-20 08:47:08 +03:00
ath9k_ps_wakeup ( sc ) ;
2010-10-27 02:27:25 +04:00
spin_lock_bh ( & sc - > sc_pcu_lock ) ;
2008-12-24 02:58:50 +03:00
/*
* This is only performed if the channel settings have
* actually changed .
*
* To switch channels clear any pending DMA operations ;
* wait long enough for the RX fifo to drain , reset the
* hardware at the new frequency , and then re - enable
* the relevant bits of the h / w .
*/
2010-11-08 22:54:47 +03:00
ath9k_hw_disable_interrupts ( ah ) ;
2010-12-05 22:17:53 +03:00
stopped = ath_drain_all_txq ( sc , false ) ;
2010-10-21 03:07:06 +04:00
2010-12-05 22:17:53 +03:00
if ( ! ath_stoprecv ( sc ) )
stopped = false ;
2008-11-24 09:37:55 +03:00
2011-01-11 03:05:50 +03:00
if ( ! ath9k_hw_check_alive ( ah ) )
stopped = false ;
2008-12-24 02:58:50 +03:00
/* XXX: do not flush receive queue here. We don't want
* to flush data frames already in queue because of
* changing channel . */
2008-11-24 09:37:55 +03:00
2010-07-31 02:11:59 +04:00
if ( ! stopped | | ! ( sc - > sc_flags & SC_OP_OFFCHANNEL ) )
2008-12-24 02:58:50 +03:00
fastcc = false ;
2010-07-31 02:12:00 +04:00
if ( ! ( sc - > sc_flags & SC_OP_OFFCHANNEL ) )
2011-01-24 21:23:18 +03:00
caldata = & sc - > caldata ;
2010-07-31 02:12:00 +04:00
2010-12-03 06:12:37 +03:00
ath_dbg ( common , ATH_DBG_CONFIG ,
" (%u MHz) -> (%u MHz), conf_is_ht40: %d fastcc: %d \n " ,
sc - > sc_ah - > curchan - > channel ,
channel - > center_freq , conf_is_ht40 ( conf ) ,
fastcc ) ;
2008-11-24 09:37:55 +03:00
2010-07-31 02:12:00 +04:00
r = ath9k_hw_reset ( ah , hchan , caldata , fastcc ) ;
2008-12-24 02:58:50 +03:00
if ( r ) {
2010-12-03 06:12:36 +03:00
ath_err ( common ,
" Unable to reset channel (%u MHz), reset status %d \n " ,
channel - > center_freq , r ) ;
2009-06-15 19:49:09 +04:00
goto ps_restore ;
2008-11-24 09:37:55 +03:00
}
2008-12-24 02:58:50 +03:00
if ( ath_startrecv ( sc ) ! = 0 ) {
2010-12-03 06:12:36 +03:00
ath_err ( common , " Unable to restart recv logic \n " ) ;
2009-06-15 19:49:09 +04:00
r = - EIO ;
goto ps_restore ;
2008-12-24 02:58:50 +03:00
}
2011-01-31 21:17:44 +03:00
ath9k_cmn_update_txpow ( ah , sc - > curtxpow ,
sc - > config . txpowlimit , & sc - > curtxpow ) ;
2010-04-01 02:05:31 +04:00
ath9k_hw_set_interrupts ( ah , ah - > imask ) ;
2009-06-15 19:49:09 +04:00
2010-09-16 23:12:28 +04:00
if ( ! ( sc - > sc_flags & ( SC_OP_OFFCHANNEL ) ) ) {
2010-12-30 16:37:44 +03:00
if ( sc - > sc_flags & SC_OP_BEACONS )
2011-04-04 21:26:19 +04:00
ath_set_beacon ( sc ) ;
2010-07-31 02:11:59 +04:00
ieee80211_queue_delayed_work ( sc - > hw , & sc - > tx_complete_work , 0 ) ;
2011-01-27 12:15:08 +03:00
ieee80211_queue_delayed_work ( sc - > hw , & sc - > hw_pll_work , HZ / 2 ) ;
2010-09-16 23:12:28 +04:00
ath_start_ani ( common ) ;
2010-07-31 02:11:59 +04:00
}
2009-06-15 19:49:09 +04:00
ps_restore :
2011-01-24 21:23:14 +03:00
ieee80211_wake_queues ( hw ) ;
2010-10-27 02:27:25 +04:00
spin_unlock_bh ( & sc - > sc_pcu_lock ) ;
2009-01-20 08:47:08 +03:00
ath9k_ps_restore ( sc ) ;
2009-06-15 19:49:09 +04:00
return r ;
2008-11-24 09:37:55 +03:00
}
2010-06-12 08:34:01 +04:00
static void ath_paprd_activate ( struct ath_softc * sc )
{
struct ath_hw * ah = sc - > sc_ah ;
2010-07-31 02:12:00 +04:00
struct ath9k_hw_cal_data * caldata = ah - > caldata ;
2010-09-21 09:54:46 +04:00
struct ath_common * common = ath9k_hw_common ( ah ) ;
2010-06-12 08:34:01 +04:00
int chain ;
2010-07-31 02:12:00 +04:00
if ( ! caldata | | ! caldata - > paprd_done )
2010-06-12 08:34:01 +04:00
return ;
ath9k_ps_wakeup ( sc ) ;
2010-07-30 23:02:11 +04:00
ar9003_paprd_enable ( ah , false ) ;
2010-06-12 08:34:01 +04:00
for ( chain = 0 ; chain < AR9300_MAX_CHAINS ; chain + + ) {
2010-09-21 09:54:46 +04:00
if ( ! ( common - > tx_chainmask & BIT ( chain ) ) )
2010-06-12 08:34:01 +04:00
continue ;
2010-07-31 02:12:00 +04:00
ar9003_paprd_populate_single_table ( ah , caldata , chain ) ;
2010-06-12 08:34:01 +04:00
}
ar9003_paprd_enable ( ah , true ) ;
ath9k_ps_restore ( sc ) ;
}
2010-12-13 10:40:52 +03:00
static bool ath_paprd_send_frame ( struct ath_softc * sc , struct sk_buff * skb , int chain )
{
struct ieee80211_hw * hw = sc - > hw ;
struct ieee80211_tx_info * tx_info = IEEE80211_SKB_CB ( skb ) ;
2011-01-31 10:55:29 +03:00
struct ath_hw * ah = sc - > sc_ah ;
struct ath_common * common = ath9k_hw_common ( ah ) ;
2010-12-13 10:40:52 +03:00
struct ath_tx_control txctl ;
int time_left ;
memset ( & txctl , 0 , sizeof ( txctl ) ) ;
txctl . txq = sc - > tx . txq_map [ WME_AC_BE ] ;
memset ( tx_info , 0 , sizeof ( * tx_info ) ) ;
tx_info - > band = hw - > conf . channel - > band ;
tx_info - > flags | = IEEE80211_TX_CTL_NO_ACK ;
tx_info - > control . rates [ 0 ] . idx = 0 ;
tx_info - > control . rates [ 0 ] . count = 1 ;
tx_info - > control . rates [ 0 ] . flags = IEEE80211_TX_RC_MCS ;
tx_info - > control . rates [ 1 ] . idx = - 1 ;
init_completion ( & sc - > paprd_complete ) ;
txctl . paprd = BIT ( chain ) ;
2011-01-31 10:55:29 +03:00
if ( ath_tx_start ( hw , skb , & txctl ) ! = 0 ) {
ath_dbg ( common , ATH_DBG_XMIT , " PAPRD TX failed \n " ) ;
dev_kfree_skb_any ( skb ) ;
2010-12-13 10:40:52 +03:00
return false ;
2011-01-31 10:55:29 +03:00
}
2010-12-13 10:40:52 +03:00
time_left = wait_for_completion_timeout ( & sc - > paprd_complete ,
msecs_to_jiffies ( ATH_PAPRD_TIMEOUT ) ) ;
if ( ! time_left )
ath_dbg ( ath9k_hw_common ( sc - > sc_ah ) , ATH_DBG_CALIBRATE ,
" Timeout waiting for paprd training on TX chain %d \n " ,
chain ) ;
return ! ! time_left ;
}
2010-06-12 08:34:01 +04:00
void ath_paprd_calibrate ( struct work_struct * work )
{
struct ath_softc * sc = container_of ( work , struct ath_softc , paprd_work ) ;
struct ieee80211_hw * hw = sc - > hw ;
struct ath_hw * ah = sc - > sc_ah ;
struct ieee80211_hdr * hdr ;
struct sk_buff * skb = NULL ;
2010-07-31 02:12:00 +04:00
struct ath9k_hw_cal_data * caldata = ah - > caldata ;
2010-09-21 09:54:46 +04:00
struct ath_common * common = ath9k_hw_common ( ah ) ;
2010-11-07 16:59:39 +03:00
int ftype ;
2010-06-12 08:34:01 +04:00
int chain_ok = 0 ;
int chain ;
int len = 1800 ;
2010-07-31 02:12:00 +04:00
if ( ! caldata )
return ;
2010-12-13 10:40:54 +03:00
if ( ar9003_paprd_init_table ( ah ) < 0 )
return ;
2010-06-12 08:34:01 +04:00
skb = alloc_skb ( len , GFP_KERNEL ) ;
if ( ! skb )
return ;
skb_put ( skb , len ) ;
memset ( skb - > data , 0 , len ) ;
hdr = ( struct ieee80211_hdr * ) skb - > data ;
ftype = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC ;
hdr - > frame_control = cpu_to_le16 ( ftype ) ;
2010-07-20 21:15:31 +04:00
hdr - > duration_id = cpu_to_le16 ( 10 ) ;
2010-06-12 08:34:01 +04:00
memcpy ( hdr - > addr1 , hw - > wiphy - > perm_addr , ETH_ALEN ) ;
memcpy ( hdr - > addr2 , hw - > wiphy - > perm_addr , ETH_ALEN ) ;
memcpy ( hdr - > addr3 , hw - > wiphy - > perm_addr , ETH_ALEN ) ;
2010-06-24 15:09:27 +04:00
ath9k_ps_wakeup ( sc ) ;
2010-06-12 08:34:01 +04:00
for ( chain = 0 ; chain < AR9300_MAX_CHAINS ; chain + + ) {
2010-09-21 09:54:46 +04:00
if ( ! ( common - > tx_chainmask & BIT ( chain ) ) )
2010-06-12 08:34:01 +04:00
continue ;
chain_ok = 0 ;
2010-12-13 10:40:52 +03:00
ath_dbg ( common , ATH_DBG_CALIBRATE ,
" Sending PAPRD frame for thermal measurement "
" on chain %d \n " , chain ) ;
if ( ! ath_paprd_send_frame ( sc , skb , chain ) )
goto fail_paprd ;
2010-06-12 08:34:01 +04:00
ar9003_paprd_setup_gain_table ( ah , chain ) ;
2010-12-13 10:40:52 +03:00
ath_dbg ( common , ATH_DBG_CALIBRATE ,
" Sending PAPRD training frame on chain %d \n " , chain ) ;
if ( ! ath_paprd_send_frame ( sc , skb , chain ) )
2010-06-24 13:42:44 +04:00
goto fail_paprd ;
2010-06-12 08:34:01 +04:00
if ( ! ar9003_paprd_is_done ( ah ) )
break ;
2010-07-31 02:12:00 +04:00
if ( ar9003_paprd_create_curve ( ah , caldata , chain ) ! = 0 )
2010-06-12 08:34:01 +04:00
break ;
chain_ok = 1 ;
}
kfree_skb ( skb ) ;
if ( chain_ok ) {
2010-07-31 02:12:00 +04:00
caldata - > paprd_done = true ;
2010-06-12 08:34:01 +04:00
ath_paprd_activate ( sc ) ;
}
2010-06-24 13:42:44 +04:00
fail_paprd :
2010-06-12 08:34:01 +04:00
ath9k_ps_restore ( sc ) ;
}
2008-11-24 09:37:55 +03:00
/*
* This routine performs the periodic noise floor calibration function
* that is used to adjust and optimize the chip performance . This
* takes environmental changes ( location , temperature ) into account .
* When the task is complete , it reschedules itself depending on the
* appropriate interval that was calculated .
*/
2010-01-08 08:06:02 +03:00
void ath_ani_calibrate ( unsigned long data )
2008-11-24 09:37:55 +03:00
{
2009-02-20 12:43:28 +03:00
struct ath_softc * sc = ( struct ath_softc * ) data ;
struct ath_hw * ah = sc - > sc_ah ;
2009-09-13 13:42:02 +04:00
struct ath_common * common = ath9k_hw_common ( ah ) ;
2008-11-24 09:37:55 +03:00
bool longcal = false ;
bool shortcal = false ;
bool aniflag = false ;
unsigned int timestamp = jiffies_to_msecs ( jiffies ) ;
2010-08-02 17:53:15 +04:00
u32 cal_interval , short_cal_interval , long_cal_interval ;
2010-10-09 00:13:53 +04:00
unsigned long flags ;
2010-08-02 17:53:15 +04:00
if ( ah - > caldata & & ah - > caldata - > nfcal_interference )
long_cal_interval = ATH_LONG_CALINTERVAL_INT ;
else
long_cal_interval = ATH_LONG_CALINTERVAL ;
2008-11-24 09:37:55 +03:00
2009-02-20 12:43:28 +03:00
short_cal_interval = ( ah - > opmode = = NL80211_IFTYPE_AP ) ?
ATH_AP_SHORT_CALINTERVAL : ATH_STA_SHORT_CALINTERVAL ;
2008-11-24 09:37:55 +03:00
2009-05-19 18:01:39 +04:00
/* Only calibrate if awake */
if ( sc - > sc_ah - > power_mode ! = ATH9K_PM_AWAKE )
goto set_timer ;
ath9k_ps_wakeup ( sc ) ;
2008-11-24 09:37:55 +03:00
/* Long calibration runs independently of short calibration. */
2010-08-02 17:53:15 +04:00
if ( ( timestamp - common - > ani . longcal_timer ) > = long_cal_interval ) {
2008-11-24 09:37:55 +03:00
longcal = true ;
2010-12-03 06:12:37 +03:00
ath_dbg ( common , ATH_DBG_ANI , " longcal @%lu \n " , jiffies ) ;
2009-11-04 04:07:04 +03:00
common - > ani . longcal_timer = timestamp ;
2008-11-24 09:37:55 +03:00
}
2009-02-09 10:57:03 +03:00
/* Short calibration applies only while caldone is false */
2009-11-04 04:07:04 +03:00
if ( ! common - > ani . caldone ) {
if ( ( timestamp - common - > ani . shortcal_timer ) > = short_cal_interval ) {
2008-11-24 09:37:55 +03:00
shortcal = true ;
2010-12-03 06:12:37 +03:00
ath_dbg ( common , ATH_DBG_ANI ,
" shortcal @%lu \n " , jiffies ) ;
2009-11-04 04:07:04 +03:00
common - > ani . shortcal_timer = timestamp ;
common - > ani . resetcal_timer = timestamp ;
2008-11-24 09:37:55 +03:00
}
} else {
2009-11-04 04:07:04 +03:00
if ( ( timestamp - common - > ani . resetcal_timer ) > =
2008-11-24 09:37:55 +03:00
ATH_RESTART_CALINTERVAL ) {
2009-11-04 04:07:04 +03:00
common - > ani . caldone = ath9k_hw_reset_calvalid ( ah ) ;
if ( common - > ani . caldone )
common - > ani . resetcal_timer = timestamp ;
2008-11-24 09:37:55 +03:00
}
}
/* Verify whether we must check ANI */
2010-06-12 08:33:45 +04:00
if ( ( timestamp - common - > ani . checkani_timer ) > =
ah - > config . ani_poll_interval ) {
2008-11-24 09:37:55 +03:00
aniflag = true ;
2009-11-04 04:07:04 +03:00
common - > ani . checkani_timer = timestamp ;
2008-11-24 09:37:55 +03:00
}
/* Skip all processing if there's nothing to do. */
if ( longcal | | shortcal | | aniflag ) {
/* Call ANI routine if necessary */
2010-10-09 00:13:53 +04:00
if ( aniflag ) {
spin_lock_irqsave ( & common - > cc_lock , flags ) ;
2009-08-19 14:53:40 +04:00
ath9k_hw_ani_monitor ( ah , ah - > curchan ) ;
2010-10-10 20:21:52 +04:00
ath_update_survey_stats ( sc ) ;
2010-10-09 00:13:53 +04:00
spin_unlock_irqrestore ( & common - > cc_lock , flags ) ;
}
2008-11-24 09:37:55 +03:00
/* Perform calibration if necessary */
if ( longcal | | shortcal ) {
2009-11-04 04:07:04 +03:00
common - > ani . caldone =
2009-09-14 08:07:07 +04:00
ath9k_hw_calibrate ( ah ,
ah - > curchan ,
common - > rx_chainmask ,
longcal ) ;
2008-11-24 09:37:55 +03:00
}
}
2009-05-19 18:01:39 +04:00
ath9k_ps_restore ( sc ) ;
2009-02-20 12:43:28 +03:00
set_timer :
2008-11-24 09:37:55 +03:00
/*
* Set timer interval based on previous results .
* The interval must be the shortest necessary to satisfy ANI ,
* short calibration and long calibration .
*/
2008-12-02 16:07:54 +03:00
cal_interval = ATH_LONG_CALINTERVAL ;
2009-02-09 10:57:26 +03:00
if ( sc - > sc_ah - > config . enable_ani )
2010-06-12 08:33:45 +04:00
cal_interval = min ( cal_interval ,
( u32 ) ah - > config . ani_poll_interval ) ;
2009-11-04 04:07:04 +03:00
if ( ! common - > ani . caldone )
2009-02-20 12:43:28 +03:00
cal_interval = min ( cal_interval , ( u32 ) short_cal_interval ) ;
2008-11-24 09:37:55 +03:00
2009-11-04 04:07:04 +03:00
mod_timer ( & common - > ani . timer , jiffies + msecs_to_jiffies ( cal_interval ) ) ;
2010-07-31 02:12:00 +04:00
if ( ( sc - > sc_ah - > caps . hw_caps & ATH9K_HW_CAP_PAPRD ) & & ah - > caldata ) {
if ( ! ah - > caldata - > paprd_done )
2010-06-12 08:34:01 +04:00
ieee80211_queue_work ( sc - > hw , & sc - > paprd_work ) ;
2010-12-15 18:30:53 +03:00
else if ( ! ah - > paprd_table_write_done )
2010-06-12 08:34:01 +04:00
ath_paprd_activate ( sc ) ;
}
2008-11-24 09:37:55 +03:00
}
static void ath_node_attach ( struct ath_softc * sc , struct ieee80211_sta * sta )
{
struct ath_node * an ;
2010-11-23 18:12:27 +03:00
struct ath_hw * ah = sc - > sc_ah ;
2008-11-24 09:37:55 +03:00
an = ( struct ath_node * ) sta - > drv_priv ;
2011-01-10 10:11:49 +03:00
# ifdef CONFIG_ATH9K_DEBUGFS
spin_lock ( & sc - > nodes_lock ) ;
list_add ( & an - > list , & sc - > nodes ) ;
spin_unlock ( & sc - > nodes_lock ) ;
an - > sta = sta ;
# endif
2010-11-23 18:12:27 +03:00
if ( ( ah - > caps . hw_caps ) & ATH9K_HW_CAP_APM )
sc - > sc_flags | = SC_OP_ENABLE_APM ;
2009-03-30 13:58:48 +04:00
if ( sc - > sc_flags & SC_OP_TXAGGR ) {
2008-11-24 09:37:55 +03:00
ath_tx_node_init ( sc , an ) ;
2009-07-23 14:02:34 +04:00
an - > maxampdu = 1 < < ( IEEE80211_HT_MAX_AMPDU_FACTOR +
2009-03-30 13:58:48 +04:00
sta - > ht_cap . ampdu_factor ) ;
an - > mpdudensity = parse_mpdudensity ( sta - > ht_cap . ampdu_density ) ;
}
2008-11-24 09:37:55 +03:00
}
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-10 10:11:49 +03:00
# ifdef CONFIG_ATH9K_DEBUGFS
spin_lock ( & sc - > nodes_lock ) ;
list_del ( & an - > list ) ;
spin_unlock ( & sc - > nodes_lock ) ;
an - > sta = NULL ;
# endif
2008-11-24 09:37:55 +03:00
if ( sc - > sc_flags & SC_OP_TXAGGR )
ath_tx_node_cleanup ( sc , an ) ;
}
2010-07-02 02:09:52 +04:00
void ath_hw_check ( struct work_struct * work )
{
struct ath_softc * sc = container_of ( work , struct ath_softc , hw_check_work ) ;
2011-02-04 22:09:25 +03:00
struct ath_common * common = ath9k_hw_common ( sc - > sc_ah ) ;
unsigned long flags ;
int busy ;
2010-07-02 02:09:52 +04:00
ath9k_ps_wakeup ( sc ) ;
2011-02-04 22:09:25 +03:00
if ( ath9k_hw_check_alive ( sc - > sc_ah ) )
goto out ;
2010-07-02 02:09:52 +04:00
2011-02-04 22:09:25 +03:00
spin_lock_irqsave ( & common - > cc_lock , flags ) ;
busy = ath_update_survey_stats ( sc ) ;
spin_unlock_irqrestore ( & common - > cc_lock , flags ) ;
2010-07-02 02:09:52 +04:00
2011-02-04 22:09:25 +03:00
ath_dbg ( common , ATH_DBG_RESET , " Possible baseband hang, "
" busy=%d (try %d) \n " , busy , sc - > hw_busy_count + 1 ) ;
if ( busy > = 99 ) {
if ( + + sc - > hw_busy_count > = 3 )
ath_reset ( sc , true ) ;
} else if ( busy > = 0 )
sc - > hw_busy_count = 0 ;
2010-07-02 02:09:52 +04:00
out :
ath9k_ps_restore ( sc ) ;
}
2011-04-22 10:02:12 +04:00
static void ath_hw_pll_rx_hang_check ( struct ath_softc * sc , u32 pll_sqsum )
{
static int count ;
struct ath_common * common = ath9k_hw_common ( sc - > sc_ah ) ;
if ( pll_sqsum > = 0x40000 ) {
count + + ;
if ( count = = 3 ) {
/* Rx is hung for more than 500ms. Reset it */
ath_dbg ( common , ATH_DBG_RESET ,
" Possible RX hang, resetting " ) ;
ath_reset ( sc , true ) ;
count = 0 ;
}
} else
count = 0 ;
}
2011-04-22 10:02:11 +04:00
void ath_hw_pll_work ( struct work_struct * work )
{
struct ath_softc * sc = container_of ( work , struct ath_softc ,
hw_pll_work . work ) ;
2011-04-22 10:02:12 +04:00
u32 pll_sqsum ;
2011-04-22 10:02:11 +04:00
if ( AR_SREV_9485 ( sc - > sc_ah ) ) {
2011-04-22 10:02:12 +04:00
ath9k_ps_wakeup ( sc ) ;
pll_sqsum = ar9003_get_pll_sqsum_dvc ( sc - > sc_ah ) ;
ath9k_ps_restore ( sc ) ;
ath_hw_pll_rx_hang_check ( sc , pll_sqsum ) ;
2011-04-22 10:02:11 +04:00
ieee80211_queue_delayed_work ( sc - > hw , & sc - > hw_pll_work , HZ / 5 ) ;
}
}
2010-01-08 08:06:02 +03:00
void ath9k_tasklet ( unsigned long data )
2008-11-24 09:37:55 +03:00
{
struct ath_softc * sc = ( struct ath_softc * ) data ;
2009-09-09 13:33:11 +04:00
struct ath_hw * ah = sc - > sc_ah ;
2009-09-13 13:42:02 +04:00
struct ath_common * common = ath9k_hw_common ( ah ) ;
2009-09-09 13:33:11 +04:00
2009-02-09 10:57:03 +03:00
u32 status = sc - > intrstatus ;
2010-04-16 01:38:48 +04:00
u32 rxmask ;
2008-11-24 09:37:55 +03:00
2010-07-02 02:09:52 +04:00
if ( status & ATH9K_INT_FATAL ) {
2010-10-23 19:45:38 +04:00
ath_reset ( sc , true ) ;
2008-11-24 09:37:55 +03:00
return ;
2009-03-30 13:58:49 +04:00
}
2008-11-24 09:37:55 +03:00
2011-01-21 20:52:38 +03:00
ath9k_ps_wakeup ( sc ) ;
2010-12-23 18:36:57 +03:00
spin_lock ( & sc - > sc_pcu_lock ) ;
2010-10-27 02:27:25 +04:00
2011-01-11 03:05:50 +03:00
/*
* Only run the baseband hang check if beacons stop working in AP or
* IBSS mode , because it has a high false positive rate . For station
* mode it should not be necessary , since the upper layers will detect
* this through a beacon miss automatically and the following channel
* change will trigger a hardware reset anyway
*/
if ( ath9k_hw_numtxpending ( ah , sc - > beacon . beaconq ) ! = 0 & &
! ath9k_hw_check_alive ( ah ) )
2010-07-02 02:09:52 +04:00
ieee80211_queue_work ( sc - > hw , & sc - > hw_check_work ) ;
2011-05-06 16:57:47 +04:00
if ( ( status & ATH9K_INT_TSFOOR ) & & sc - > ps_enabled ) {
/*
* TSF sync does not look correct ; remain awake to sync with
* the next Beacon .
*/
ath_dbg ( common , ATH_DBG_PS ,
" TSFOOR - Sync with next Beacon \n " ) ;
sc - > ps_flags | = PS_WAIT_FOR_BEACON | PS_BEACON_SYNC |
PS_TSFOOR_SYNC ;
}
2010-04-16 01: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 09:37:55 +03:00
}
2010-04-16 01: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 13:58:49 +04:00
2009-09-10 01:52:02 +04:00
if ( ah - > btcoex_hw . scheme = = ATH_BTCOEX_CFG_3WIRE )
2009-09-01 16:16:32 +04:00
if ( status & ATH9K_INT_GENTIMER )
ath_gen_timer_isr ( sc - > sc_ah ) ;
2008-11-24 09:37:55 +03:00
/* re-enable hardware interrupt */
2010-11-08 22:54:47 +03:00
ath9k_hw_enable_interrupts ( ah ) ;
2010-10-27 02:27:25 +04:00
2010-12-23 18:36:57 +03:00
spin_unlock ( & sc - > sc_pcu_lock ) ;
2009-05-15 10:47:16 +04:00
ath9k_ps_restore ( sc ) ;
2008-11-24 09:37:55 +03:00
}
2009-01-14 22:17:06 +03:00
irqreturn_t ath_isr ( int irq , void * dev )
2008-11-24 09:37:55 +03:00
{
2009-03-30 13:58:49 +04:00
# define SCHED_INTR ( \
ATH9K_INT_FATAL | \
ATH9K_INT_RXORN | \
ATH9K_INT_RXEOL | \
ATH9K_INT_RX | \
2010-04-16 01:38:48 +04:00
ATH9K_INT_RXLP | \
ATH9K_INT_RXHP | \
2009-03-30 13:58:49 +04:00
ATH9K_INT_TX | \
ATH9K_INT_BMISS | \
ATH9K_INT_CST | \
2009-09-01 16:16:32 +04:00
ATH9K_INT_TSFOOR | \
ATH9K_INT_GENTIMER )
2009-03-30 13:58:49 +04:00
2008-11-24 09:37:55 +03:00
struct ath_softc * sc = dev ;
2009-02-09 10:57:12 +03:00
struct ath_hw * ah = sc - > sc_ah ;
2010-10-09 00:13:53 +04:00
struct ath_common * common = ath9k_hw_common ( ah ) ;
2008-11-24 09:37:55 +03:00
enum ath9k_int status ;
bool sched = false ;
2009-03-30 13:58:49 +04:00
/*
* The hardware is not ready / present , don ' t
* touch anything . Note this can happen early
* on if the IRQ is shared .
*/
if ( sc - > sc_flags & SC_OP_INVALID )
return IRQ_NONE ;
2008-11-24 09:37:55 +03:00
2009-03-30 13:58:49 +04:00
/* shared irq, not for us */
2009-05-15 10:47:16 +04:00
if ( ! ath9k_hw_intrpend ( ah ) )
2009-03-30 13:58:49 +04:00
return IRQ_NONE ;
/*
* 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-04-01 02:05:31 +04:00
status & = ah - > imask ; /* discard unasked-for bits */
2008-11-24 09:37:55 +03:00
2009-03-30 13:58:49 +04:00
/*
* If there are no status bits set , then this interrupt was not
* for me ( should have been caught above ) .
*/
2009-05-15 10:47:16 +04:00
if ( ! status )
2009-03-30 13:58:49 +04:00
return IRQ_NONE ;
2008-11-24 09:37:55 +03:00
2009-03-30 13:58:49 +04:00
/* Cache the status */
sc - > intrstatus = status ;
if ( status & SCHED_INTR )
sched = true ;
/*
* If a FATAL or RXORN interrupt is received , we have to reset the
* chip immediately .
*/
2010-04-16 01:38:48 +04:00
if ( ( status & ATH9K_INT_FATAL ) | | ( ( status & ATH9K_INT_RXORN ) & &
! ( ah - > caps . hw_caps & ATH9K_HW_CAP_EDMA ) ) )
2009-03-30 13:58:49 +04:00
goto chip_reset ;
2010-05-13 21:33:44 +04:00
if ( ( ah - > caps . hw_caps & ATH9K_HW_CAP_EDMA ) & &
( status & ATH9K_INT_BB_WATCHDOG ) ) {
2010-10-09 00:13:53 +04:00
spin_lock ( & common - > cc_lock ) ;
ath_hw_cycle_counters_update ( common ) ;
2010-05-13 21:33:44 +04:00
ar9003_hw_bb_watchdog_dbg_info ( ah ) ;
2010-10-09 00:13:53 +04:00
spin_unlock ( & common - > cc_lock ) ;
2010-05-13 21:33:44 +04:00
goto chip_reset ;
}
2009-03-30 13:58:49 +04:00
if ( status & ATH9K_INT_SWBA )
tasklet_schedule ( & sc - > bcon_tasklet ) ;
if ( status & ATH9K_INT_TXURN )
ath9k_hw_updatetxtriglevel ( ah , true ) ;
2010-04-16 01:38:48 +04:00
if ( ah - > caps . hw_caps & ATH9K_HW_CAP_EDMA ) {
if ( status & ATH9K_INT_RXEOL ) {
ah - > imask & = ~ ( ATH9K_INT_RXEOL | ATH9K_INT_RXORN ) ;
ath9k_hw_set_interrupts ( ah , ah - > imask ) ;
}
}
2009-03-30 13:58:49 +04:00
if ( status & ATH9K_INT_MIB ) {
2008-11-24 09:37:55 +03:00
/*
2009-03-30 13:58:49 +04:00
* Disable interrupts until we service the MIB
* interrupt ; otherwise it will continue to
* fire .
2008-11-24 09:37:55 +03:00
*/
2010-11-08 22:54:47 +03:00
ath9k_hw_disable_interrupts ( ah ) ;
2009-03-30 13:58:49 +04:00
/*
* Let the hal handle the event . We assume
* it will clear whatever condition caused
* the interrupt .
*/
2010-10-12 18:08:03 +04:00
spin_lock ( & common - > cc_lock ) ;
2010-10-04 22:09:48 +04:00
ath9k_hw_proc_mib_event ( ah ) ;
2010-10-12 18:08:03 +04:00
spin_unlock ( & common - > cc_lock ) ;
2010-11-08 22:54:47 +03:00
ath9k_hw_enable_interrupts ( ah ) ;
2009-03-30 13:58:49 +04:00
}
2008-11-24 09:37:55 +03:00
2009-05-15 10:47:16 +04:00
if ( ! ( ah - > caps . hw_caps & ATH9K_HW_CAP_AUTOSLEEP ) )
if ( status & ATH9K_INT_TIM_TIMER ) {
2010-12-08 02:13:22 +03:00
if ( ATH_DBG_WARN_ON_ONCE ( sc - > ps_idle ) )
goto chip_reset ;
2009-03-30 13:58:49 +04:00
/* Clear RxAbort bit so that we can
* receive frames */
2009-09-10 08:10:09 +04:00
ath9k_setpower ( sc , ATH9K_PM_AWAKE ) ;
2009-05-15 10:47:16 +04:00
ath9k_hw_setrxabort ( sc - > sc_ah , 0 ) ;
2010-01-08 08:06:05 +03:00
sc - > ps_flags | = PS_WAIT_FOR_BEACON ;
2008-11-24 09:37:55 +03:00
}
2009-03-30 13:58:49 +04:00
chip_reset :
2008-11-24 09:37:55 +03:00
2008-12-07 19:12:44 +03:00
ath_debug_stat_interrupt ( sc , status ) ;
2008-11-24 09:37:55 +03:00
if ( sched ) {
2010-11-08 22:54:47 +03:00
/* turn off every interrupt */
ath9k_hw_disable_interrupts ( ah ) ;
2008-11-24 09:37:55 +03:00
tasklet_schedule ( & sc - > intr_tq ) ;
}
return IRQ_HANDLED ;
2009-03-30 13:58:49 +04:00
# undef SCHED_INTR
2008-11-24 09:37:55 +03:00
}
2009-11-03 01:35:42 +03:00
void ath_radio_enable ( struct ath_softc * sc , struct ieee80211_hw * hw )
2008-09-10 17:20:17 +04:00
{
2009-02-09 10:57:12 +03:00
struct ath_hw * ah = sc - > sc_ah ;
2009-09-13 13:42:02 +04:00
struct ath_common * common = ath9k_hw_common ( ah ) ;
2009-11-03 01:35:42 +03:00
struct ieee80211_channel * channel = hw - > conf . channel ;
2008-12-24 02:58:40 +03:00
int r ;
2008-09-10 17:20:17 +04:00
2009-01-20 08:47:08 +03:00
ath9k_ps_wakeup ( sc ) ;
2010-10-27 02:27:25 +04:00
spin_lock_bh ( & sc - > sc_pcu_lock ) ;
2009-09-17 07:54:58 +04:00
ath9k_hw_configpcipowersave ( ah , 0 , 0 ) ;
2008-12-24 02:58:40 +03:00
2009-06-13 13:20:25 +04:00
if ( ! ah - > curchan )
2011-01-31 21:17:43 +03:00
ah - > curchan = ath9k_cmn_get_curchannel ( sc - > hw , ah ) ;
2009-06-13 13:20:25 +04:00
2010-07-31 02:12:00 +04:00
r = ath9k_hw_reset ( ah , ah - > curchan , ah - > caldata , false ) ;
2008-12-24 02:58:40 +03:00
if ( r ) {
2010-12-03 06:12:36 +03:00
ath_err ( common ,
" Unable to reset channel (%u MHz), reset status %d \n " ,
channel - > center_freq , r ) ;
2008-09-10 17:20:17 +04:00
}
2011-01-31 21:17:44 +03:00
ath9k_cmn_update_txpow ( ah , sc - > curtxpow ,
sc - > config . txpowlimit , & sc - > curtxpow ) ;
2008-09-10 17:20:17 +04:00
if ( ath_startrecv ( sc ) ! = 0 ) {
2010-12-03 06:12:36 +03:00
ath_err ( common , " Unable to restart recv logic \n " ) ;
2010-12-08 02:13:19 +03:00
goto out ;
2008-09-10 17:20:17 +04:00
}
if ( sc - > sc_flags & SC_OP_BEACONS )
2011-04-04 21:26:19 +04:00
ath_set_beacon ( sc ) ; /* restart beacons */
2008-09-10 17:20:17 +04:00
/* Re-Enable interrupts */
2010-04-01 02:05:31 +04:00
ath9k_hw_set_interrupts ( ah , ah - > imask ) ;
2008-09-10 17:20:17 +04:00
/* Enable LED */
2009-08-14 10:00:52 +04:00
ath9k_hw_cfg_output ( ah , ah - > led_pin ,
2008-09-10 17:20:17 +04:00
AR_GPIO_OUTPUT_MUX_AS_OUTPUT ) ;
2009-08-14 10:00:52 +04:00
ath9k_hw_set_gpio ( ah , ah - > led_pin , 0 ) ;
2008-09-10 17:20:17 +04:00
2009-11-03 01:35:42 +03:00
ieee80211_wake_queues ( hw ) ;
2011-02-25 15:01:02 +03:00
ieee80211_queue_delayed_work ( hw , & sc - > hw_pll_work , HZ / 2 ) ;
2010-12-08 02:13:19 +03:00
out :
2010-10-27 02:27:25 +04:00
spin_unlock_bh ( & sc - > sc_pcu_lock ) ;
2009-01-20 08:47:08 +03:00
ath9k_ps_restore ( sc ) ;
2008-09-10 17:20:17 +04:00
}
2009-11-03 01:35:42 +03:00
void ath_radio_disable ( struct ath_softc * sc , struct ieee80211_hw * hw )
2008-09-10 17:20:17 +04:00
{
2009-02-09 10:57:12 +03:00
struct ath_hw * ah = sc - > sc_ah ;
2009-11-03 01:35:42 +03:00
struct ieee80211_channel * channel = hw - > conf . channel ;
2008-12-24 02:58:40 +03:00
int r ;
2008-09-10 17:20:17 +04:00
2009-01-20 08:47:08 +03:00
ath9k_ps_wakeup ( sc ) ;
2011-02-25 15:01:02 +03:00
cancel_delayed_work_sync ( & sc - > hw_pll_work ) ;
2010-10-27 02:27:25 +04:00
spin_lock_bh ( & sc - > sc_pcu_lock ) ;
2009-11-03 01:35:42 +03:00
ieee80211_stop_queues ( hw ) ;
2008-09-10 17:20:17 +04:00
2010-06-23 10:38:29 +04:00
/*
* Keep the LED on when the radio is disabled
* during idle unassociated state .
*/
if ( ! sc - > ps_idle ) {
ath9k_hw_set_gpio ( ah , ah - > led_pin , 1 ) ;
ath9k_hw_cfg_gpio_input ( ah , ah - > led_pin ) ;
}
2008-09-10 17:20:17 +04:00
/* Disable interrupts */
2010-11-08 22:54:47 +03:00
ath9k_hw_disable_interrupts ( ah ) ;
2008-09-10 17:20:17 +04:00
2009-01-16 19:08:47 +03:00
ath_drain_all_txq ( sc , false ) ; /* clear pending tx frames */
2010-10-21 03:07:06 +04:00
2008-09-10 17:20:17 +04:00
ath_stoprecv ( sc ) ; /* turn off frame recv */
ath_flushrecv ( sc ) ; /* flush recv queue */
2009-06-13 13:20:25 +04:00
if ( ! ah - > curchan )
2011-01-31 21:17:43 +03:00
ah - > curchan = ath9k_cmn_get_curchannel ( hw , ah ) ;
2009-06-13 13:20:25 +04:00
2010-07-31 02:12:00 +04:00
r = ath9k_hw_reset ( ah , ah - > curchan , ah - > caldata , false ) ;
2008-12-24 02:58:40 +03:00
if ( r ) {
2010-12-03 06:12:36 +03:00
ath_err ( ath9k_hw_common ( sc - > sc_ah ) ,
" Unable to reset channel (%u MHz), reset status %d \n " ,
channel - > center_freq , r ) ;
2008-09-10 17:20:17 +04:00
}
ath9k_hw_phy_disable ( ah ) ;
2010-10-21 03:07:06 +04:00
2009-09-17 07:54:58 +04:00
ath9k_hw_configpcipowersave ( ah , 1 , 1 ) ;
2010-10-27 02:27:25 +04:00
spin_unlock_bh ( & sc - > sc_pcu_lock ) ;
2009-01-20 08:47:08 +03:00
ath9k_ps_restore ( sc ) ;
2008-09-10 17:20:17 +04:00
}
2008-11-24 09:37:55 +03:00
int ath_reset ( struct ath_softc * sc , bool retry_tx )
{
2009-02-09 10:57:12 +03:00
struct ath_hw * ah = sc - > sc_ah ;
2009-09-13 13:42:02 +04:00
struct ath_common * common = ath9k_hw_common ( ah ) ;
2008-12-24 02:58:37 +03:00
struct ieee80211_hw * hw = sc - > hw ;
2008-12-24 02:58:40 +03:00
int r ;
2008-11-24 09:37:55 +03:00
2011-02-04 22:09:25 +03:00
sc - > hw_busy_count = 0 ;
2009-12-14 14:04:56 +03:00
/* Stop ANI */
del_timer_sync ( & common - > ani . timer ) ;
2011-01-21 20:52:38 +03:00
ath9k_ps_wakeup ( sc ) ;
2010-10-27 02:27:25 +04:00
spin_lock_bh ( & sc - > sc_pcu_lock ) ;
2010-01-08 08:06:09 +03:00
ieee80211_stop_queues ( hw ) ;
2010-11-08 22:54:47 +03:00
ath9k_hw_disable_interrupts ( ah ) ;
2009-01-16 19:08:47 +03:00
ath_drain_all_txq ( sc , retry_tx ) ;
2010-10-21 03:07:06 +04:00
2008-11-24 09:37:55 +03:00
ath_stoprecv ( sc ) ;
ath_flushrecv ( sc ) ;
2010-07-31 02:12:00 +04:00
r = ath9k_hw_reset ( ah , sc - > sc_ah - > curchan , ah - > caldata , false ) ;
2008-12-24 02:58:40 +03:00
if ( r )
2010-12-03 06:12:36 +03:00
ath_err ( common ,
" Unable to reset hardware; reset status %d \n " , r ) ;
2008-11-24 09:37:55 +03:00
if ( ath_startrecv ( sc ) ! = 0 )
2010-12-03 06:12:36 +03:00
ath_err ( common , " Unable to start recv logic \n " ) ;
2008-11-24 09:37:55 +03:00
/*
* We may be doing a reset in response to a request
* that changes the channel so update any state that
* might change as a result .
*/
2011-01-31 21:17:44 +03:00
ath9k_cmn_update_txpow ( ah , sc - > curtxpow ,
sc - > config . txpowlimit , & sc - > curtxpow ) ;
2008-11-24 09:37:55 +03:00
2010-09-16 23:12:27 +04:00
if ( ( sc - > sc_flags & SC_OP_BEACONS ) | | ! ( sc - > sc_flags & ( SC_OP_OFFCHANNEL ) ) )
2011-04-04 21:26:19 +04:00
ath_set_beacon ( sc ) ; /* restart beacons */
2008-11-24 09:37:55 +03:00
2010-04-01 02:05:31 +04:00
ath9k_hw_set_interrupts ( ah , ah - > imask ) ;
2008-11-24 09:37:55 +03:00
if ( retry_tx ) {
int i ;
for ( i = 0 ; i < ATH9K_NUM_TX_QUEUES ; i + + ) {
if ( ATH_TXQ_SETUP ( sc , i ) ) {
2008-12-07 19:14:03 +03:00
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 09:37:55 +03:00
}
}
}
2010-01-08 08:06:09 +03:00
ieee80211_wake_queues ( hw ) ;
2010-10-27 02:27:25 +04:00
spin_unlock_bh ( & sc - > sc_pcu_lock ) ;
2010-01-08 08:06:09 +03:00
2009-12-14 14:04:56 +03:00
/* Start ANI */
ath_start_ani ( common ) ;
2011-01-21 20:52:38 +03:00
ath9k_ps_restore ( sc ) ;
2009-12-14 14:04:56 +03:00
2008-12-24 02:58:40 +03:00
return r ;
2008-11-24 09:37:55 +03:00
}
/**********************/
/* mac80211 callbacks */
/**********************/
2008-09-10 17:19:27 +04:00
static int ath9k_start ( struct ieee80211_hw * hw )
2008-08-04 11:16:41 +04:00
{
2011-01-24 21:23:18 +03:00
struct ath_softc * sc = hw - > priv ;
2009-09-09 13:33:11 +04:00
struct ath_hw * ah = sc - > sc_ah ;
2009-09-13 13:42:02 +04:00
struct ath_common * common = ath9k_hw_common ( ah ) ;
2008-09-10 17:19:27 +04:00
struct ieee80211_channel * curchan = hw - > conf . channel ;
2008-11-24 09:37:55 +03:00
struct ath9k_channel * init_channel ;
2009-06-13 13:20:24 +04:00
int r ;
2008-08-04 11:16:41 +04:00
2010-12-03 06:12:37 +03:00
ath_dbg ( common , ATH_DBG_CONFIG ,
" Starting driver with initial channel: %d MHz \n " ,
curchan - > center_freq ) ;
2008-08-04 11:16:41 +04:00
2011-03-25 19:43:41 +03:00
ath9k_ps_wakeup ( sc ) ;
2009-02-04 05:40:07 +03:00
mutex_lock ( & sc - > mutex ) ;
2008-09-10 17:19:27 +04:00
/* setup initial channel */
2009-06-13 13:20:24 +04:00
sc - > chan_idx = curchan - > hw_value ;
2008-08-04 11:16:41 +04:00
2011-01-31 21:17:43 +03:00
init_channel = ath9k_cmn_get_curchannel ( hw , ah ) ;
2008-11-24 09:37:55 +03:00
/* Reset SERDES registers */
2009-09-09 13:33:11 +04:00
ath9k_hw_configpcipowersave ( ah , 0 , 0 ) ;
2008-11-24 09:37:55 +03:00
/*
* 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-27 02:27:24 +04:00
spin_lock_bh ( & sc - > sc_pcu_lock ) ;
2010-07-31 02:12:00 +04:00
r = ath9k_hw_reset ( ah , init_channel , ah - > caldata , false ) ;
2008-12-24 02:58:40 +03:00
if ( r ) {
2010-12-03 06:12:36 +03:00
ath_err ( common ,
" Unable to reset hardware; reset status %d (freq %u MHz) \n " ,
r , curchan - > center_freq ) ;
2010-10-27 02:27:24 +04:00
spin_unlock_bh ( & sc - > sc_pcu_lock ) ;
2009-02-04 05:40:07 +03:00
goto mutex_unlock ;
2008-11-24 09:37:55 +03:00
}
/*
* This is needed only to setup initial state
* but it ' s best done after a reset .
*/
2011-01-31 21:17:44 +03:00
ath9k_cmn_update_txpow ( ah , sc - > curtxpow ,
sc - > config . txpowlimit , & sc - > curtxpow ) ;
2008-09-10 17:19:27 +04:00
2008-11-24 09:37:55 +03:00
/*
* Setup the hardware after reset :
* The receive engine is set going .
* Frame transmit is handled entirely
* in the frame output path ; there ' s nothing to do
* here except setup the interrupt mask .
*/
if ( ath_startrecv ( sc ) ! = 0 ) {
2010-12-03 06:12:36 +03:00
ath_err ( common , " Unable to start recv logic \n " ) ;
2009-02-04 05:40:07 +03:00
r = - EIO ;
2010-10-27 02:27:24 +04:00
spin_unlock_bh ( & sc - > sc_pcu_lock ) ;
2009-02-04 05:40:07 +03:00
goto mutex_unlock ;
2008-08-04 11:16:41 +04:00
}
2010-10-27 02:27:24 +04:00
spin_unlock_bh ( & sc - > sc_pcu_lock ) ;
2008-09-10 17:19:27 +04:00
2008-11-24 09:37:55 +03:00
/* Setup our intr mask. */
2010-04-16 01: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 21:33:44 +04:00
ah - > imask | = ATH9K_INT_RXHP |
ATH9K_INT_RXLP |
ATH9K_INT_BB_WATCHDOG ;
2010-04-16 01:38:48 +04:00
else
ah - > imask | = ATH9K_INT_RX ;
2008-11-24 09:37:55 +03:00
2010-09-14 22:22:44 +04:00
ah - > imask | = ATH9K_INT_GTT ;
2008-11-24 09:37:55 +03:00
2009-09-09 13:33:11 +04:00
if ( ah - > caps . hw_caps & ATH9K_HW_CAP_HT )
2010-04-01 02:05:31 +04:00
ah - > imask | = ATH9K_INT_CST ;
2008-11-24 09:37:55 +03:00
sc - > sc_flags & = ~ SC_OP_INVALID ;
2010-10-27 17:01:15 +04:00
sc - > sc_ah - > is_monitoring = false ;
2008-11-24 09:37:55 +03:00
/* Disable BMISS interrupt when we're not associated */
2010-04-01 02:05:31 +04:00
ah - > imask & = ~ ( ATH9K_INT_SWBA | ATH9K_INT_BMISS ) ;
ath9k_hw_set_interrupts ( ah , ah - > imask ) ;
2008-11-24 09:37:55 +03:00
2009-03-03 20:23:28 +03:00
ieee80211_wake_queues ( hw ) ;
2008-11-24 09:37:55 +03:00
2009-07-30 04:08:07 +04:00
ieee80211_queue_delayed_work ( sc - > hw , & sc - > tx_complete_work , 0 ) ;
2009-07-15 04:17:09 +04:00
2009-09-10 01:52:02 +04:00
if ( ( ah - > btcoex_hw . scheme ! = ATH_BTCOEX_CFG_NONE ) & &
! ah - > btcoex_hw . enabled ) {
2009-09-10 02:15:55 +04:00
ath9k_hw_btcoex_set_weight ( ah , AR_BT_COEX_WGHT ,
AR_STOMP_LOW_WLAN_WGHT ) ;
2009-09-09 13:33:11 +04:00
ath9k_hw_btcoex_enable ( ah ) ;
2009-08-26 19:38:43 +04:00
2009-09-14 11:55:09 +04:00
if ( common - > bus_ops - > bt_coex_prep )
common - > bus_ops - > bt_coex_prep ( common ) ;
2009-09-10 01:52:02 +04:00
if ( ah - > btcoex_hw . scheme = = ATH_BTCOEX_CFG_3WIRE )
2009-09-09 15:00:10 +04:00
ath9k_btcoex_timer_resume ( sc ) ;
2009-08-26 19:38:50 +04:00
}
2010-12-06 15:27:42 +03:00
if ( ah - > caps . pcie_lcr_extsync_en & & common - > bus_ops - > extn_synch_en )
common - > bus_ops - > extn_synch_en ( common ) ;
2009-02-04 05:40:07 +03:00
mutex_unlock :
mutex_unlock ( & sc - > mutex ) ;
2011-03-25 19:43:41 +03:00
ath9k_ps_restore ( sc ) ;
2008-12-24 02:58:40 +03:00
return r ;
2008-08-04 11:16:41 +04:00
}
2011-02-24 16:42:06 +03:00
static void ath9k_tx ( struct ieee80211_hw * hw , struct sk_buff * skb )
2008-08-04 11:16:41 +04:00
{
2011-01-24 21:23:18 +03:00
struct ath_softc * sc = hw - > priv ;
2009-09-13 13:42:02 +04:00
struct ath_common * common = ath9k_hw_common ( sc - > sc_ah ) ;
2008-10-29 07:44:26 +03:00
struct ath_tx_control txctl ;
2009-11-24 17:49:18 +03:00
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) skb - > data ;
2008-10-29 07:44:26 +03:00
2009-07-24 19:27:21 +04:00
if ( sc - > ps_enabled ) {
2009-05-19 18:01:42 +04: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 ) ) {
2010-12-03 06:12:37 +03:00
ath_dbg ( common , ATH_DBG_PS ,
" Add PM=1 for a TX frame while in PS mode \n " ) ;
2009-05-19 18:01:42 +04:00
hdr - > frame_control | = cpu_to_le16 ( IEEE80211_FCTL_PM ) ;
}
}
2009-05-19 18:01:38 +04:00
if ( unlikely ( sc - > sc_ah - > power_mode ! = ATH9K_PM_AWAKE ) ) {
/*
* 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 ) ;
2010-05-18 05:57:55 +04:00
if ( ! ( sc - > sc_ah - > caps . hw_caps & ATH9K_HW_CAP_AUTOSLEEP ) )
ath9k_hw_setrxabort ( sc - > sc_ah , 0 ) ;
2009-05-19 18:01:38 +04:00
if ( ieee80211_is_pspoll ( hdr - > frame_control ) ) {
2010-12-03 06:12:37 +03:00
ath_dbg ( common , ATH_DBG_PS ,
" Sending PS-Poll to pick a buffered frame \n " ) ;
2010-01-08 08:06:05 +03:00
sc - > ps_flags | = PS_WAIT_FOR_PSPOLL_DATA ;
2009-05-19 18:01:38 +04:00
} else {
2010-12-03 06:12:37 +03:00
ath_dbg ( common , ATH_DBG_PS ,
" Wake up to complete TX \n " ) ;
2010-01-08 08:06:05 +03:00
sc - > ps_flags | = PS_WAIT_FOR_TX_ACK ;
2009-05-19 18:01:38 +04:00
}
/*
* The actual restore operation will happen only after
* the sc_flags bit is cleared . We are just dropping
* the ps_usecount here .
*/
ath9k_ps_restore ( sc ) ;
}
2008-10-29 07:44:26 +03:00
memset ( & txctl , 0 , sizeof ( struct ath_tx_control ) ) ;
2010-11-07 16:59:39 +03:00
txctl . txq = sc - > tx . txq_map [ skb_get_queue_mapping ( skb ) ] ;
2008-10-29 07:44:26 +03:00
2010-12-03 06:12:37 +03:00
ath_dbg ( common , ATH_DBG_XMIT , " transmitting packet, skb: %p \n " , skb ) ;
2008-09-10 17:19:27 +04:00
2009-03-03 20:23:29 +03:00
if ( ath_tx_start ( hw , skb , & txctl ) ! = 0 ) {
2010-12-03 06:12:37 +03:00
ath_dbg ( common , ATH_DBG_XMIT , " TX failed \n " ) ;
2008-10-29 07:44:26 +03:00
goto exit ;
2008-09-10 17:19:27 +04:00
}
2011-02-24 16:42:06 +03:00
return ;
2008-10-29 07:44:26 +03:00
exit :
dev_kfree_skb_any ( skb ) ;
2008-08-04 11:16:41 +04:00
}
2008-09-10 17:19:27 +04:00
static void ath9k_stop ( struct ieee80211_hw * hw )
2008-08-04 11:16:41 +04:00
{
2011-01-24 21:23:18 +03:00
struct ath_softc * sc = hw - > priv ;
2009-09-09 13:33:11 +04:00
struct ath_hw * ah = sc - > sc_ah ;
2009-09-13 13:42:02 +04:00
struct ath_common * common = ath9k_hw_common ( ah ) ;
2008-08-04 11:16:41 +04:00
2009-08-18 09:21:52 +04:00
mutex_lock ( & sc - > mutex ) ;
2009-07-27 22:53:04 +04:00
cancel_delayed_work_sync ( & sc - > tx_complete_work ) ;
2011-01-27 12:15:08 +03:00
cancel_delayed_work_sync ( & sc - > hw_pll_work ) ;
2010-06-12 08:34:01 +04:00
cancel_work_sync ( & sc - > paprd_work ) ;
2010-07-02 02:09:52 +04:00
cancel_work_sync ( & sc - > hw_check_work ) ;
2009-07-27 22:53:04 +04:00
2008-10-29 07:47:13 +03:00
if ( sc - > sc_flags & SC_OP_INVALID ) {
2010-12-03 06:12:37 +03:00
ath_dbg ( common , ATH_DBG_ANY , " Device not present \n " ) ;
2009-08-18 09:21:52 +04:00
mutex_unlock ( & sc - > mutex ) ;
2008-10-29 07:47:13 +03:00
return ;
}
2008-09-10 17:19:27 +04:00
2009-12-24 04:03:27 +03:00
/* Ensure HW is awake when we try to shut it down. */
ath9k_ps_wakeup ( sc ) ;
2009-09-10 01:52:02 +04:00
if ( ah - > btcoex_hw . enabled ) {
2009-09-09 13:33:11 +04:00
ath9k_hw_btcoex_disable ( ah ) ;
2009-09-10 01:52:02 +04:00
if ( ah - > btcoex_hw . scheme = = ATH_BTCOEX_CFG_3WIRE )
2009-09-09 15:00:10 +04:00
ath9k_btcoex_timer_pause ( sc ) ;
2009-08-26 19:38:50 +04:00
}
2010-10-27 02:27:25 +04:00
spin_lock_bh ( & sc - > sc_pcu_lock ) ;
2011-01-25 16:08:40 +03:00
/* prevent tasklets to enable interrupts once we disable them */
ah - > imask & = ~ ATH9K_INT_GLOBAL ;
2008-11-24 09:37:55 +03:00
/* make sure h/w will not generate any interrupt
* before setting the invalid flag . */
2010-11-08 22:54:47 +03:00
ath9k_hw_disable_interrupts ( ah ) ;
2008-11-24 09:37:55 +03:00
if ( ! ( sc - > sc_flags & SC_OP_INVALID ) ) {
2009-01-16 19:08:47 +03:00
ath_drain_all_txq ( sc , false ) ;
2008-11-24 09:37:55 +03:00
ath_stoprecv ( sc ) ;
2009-09-09 13:33:11 +04:00
ath9k_hw_phy_disable ( ah ) ;
2010-10-27 02:27:25 +04:00
} else
2008-12-07 19:14:03 +03:00
sc - > rx . rxlink = NULL ;
2008-11-24 09:37:55 +03:00
2011-01-26 20:23:27 +03:00
if ( sc - > rx . frag ) {
dev_kfree_skb_any ( sc - > rx . frag ) ;
sc - > rx . frag = NULL ;
}
2008-11-24 09:37:55 +03:00
/* disable HAL and put h/w to sleep */
2009-09-09 13:33:11 +04:00
ath9k_hw_disable ( ah ) ;
ath9k_hw_configpcipowersave ( ah , 1 , 1 ) ;
2010-10-27 02:27:25 +04:00
spin_unlock_bh ( & sc - > sc_pcu_lock ) ;
2011-01-25 16:08:40 +03:00
/* 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 ) ;
2009-12-24 04:03:27 +03:00
ath9k_ps_restore ( sc ) ;
2010-12-08 02:13:20 +03:00
sc - > ps_idle = true ;
ath_radio_disable ( sc , hw ) ;
2008-11-24 09:37:55 +03:00
sc - > sc_flags | = SC_OP_INVALID ;
2008-09-10 17:20:17 +04:00
2009-02-04 05:40:07 +03:00
mutex_unlock ( & sc - > mutex ) ;
2010-12-03 06:12:37 +03:00
ath_dbg ( common , ATH_DBG_CONFIG , " Driver halt \n " ) ;
2008-08-04 11:16:41 +04:00
}
2011-01-15 22:13:48 +03: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_reclaim_beacon ( struct ath_softc * sc ,
struct ieee80211_vif * vif )
2008-08-04 11:16:41 +04:00
{
2009-12-23 15:15:45 +03:00
struct ath_vif * avp = ( void * ) vif - > drv_priv ;
2008-09-10 17:19:27 +04:00
2011-02-09 15:16:39 +03:00
ath9k_set_beaconing_status ( sc , false ) ;
2011-01-15 22:13:48 +03:00
ath_beacon_return ( sc , avp ) ;
2011-02-09 15:16:39 +03:00
ath9k_set_beaconing_status ( sc , true ) ;
2011-01-15 22:13:48 +03:00
sc - > sc_flags & = ~ SC_OP_BEACONS ;
}
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 05:40:07 +03:00
2009-12-23 15:15:45 +03:00
switch ( vif - > type ) {
2011-01-15 22:13:48 +03:00
case NL80211_IFTYPE_AP :
iter_data - > naps + + ;
2008-08-04 11:16:41 +04:00
break ;
2011-01-15 22:13:48 +03:00
case NL80211_IFTYPE_STATION :
iter_data - > nstations + + ;
2010-10-01 19:20:39 +04:00
break ;
2008-09-11 02:01:58 +04:00
case NL80211_IFTYPE_ADHOC :
2011-01-15 22:13:48 +03:00
iter_data - > nadhocs + + ;
break ;
2009-03-21 05:59:59 +03:00
case NL80211_IFTYPE_MESH_POINT :
2011-01-15 22:13:48 +03:00
iter_data - > nmeshes + + ;
break ;
case NL80211_IFTYPE_WDS :
iter_data - > nwds + + ;
2008-08-04 11:16:41 +04:00
break ;
default :
2011-01-15 22:13:48 +03:00
iter_data - > nothers + + ;
break ;
2008-08-04 11:16:41 +04:00
}
2011-01-15 22:13:48 +03:00
}
2008-08-04 11:16:41 +04:00
2011-01-15 22:13:48 +03: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 21:23:18 +03:00
struct ath_softc * sc = hw - > priv ;
2011-01-15 22:13:48 +03:00
struct ath_hw * ah = sc - > sc_ah ;
struct ath_common * common = ath9k_hw_common ( ah ) ;
2008-09-10 17:19:27 +04:00
2011-01-15 22:13:48 +03: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 07:46:06 +03:00
2011-01-15 22:13:48 +03: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 20:23:27 +03:00
2011-01-15 22:13:48 +03:00
/* Called with sc->mutex held. */
static void ath9k_calculate_summary_state ( struct ieee80211_hw * hw ,
struct ieee80211_vif * vif )
{
2011-01-24 21:23:18 +03:00
struct ath_softc * sc = hw - > priv ;
2011-01-15 22:13:48 +03:00
struct ath_hw * ah = sc - > sc_ah ;
struct ath_common * common = ath9k_hw_common ( ah ) ;
struct ath9k_vif_iter_data iter_data ;
2009-03-03 20:23:27 +03:00
2011-01-15 22:13:48 +03:00
ath9k_calculate_iter_data ( hw , vif , & iter_data ) ;
2009-03-03 20:23:26 +03:00
2011-01-15 22:13:48 +03:00
/* Set BSSID mask. */
memcpy ( common - > bssidmask , iter_data . mask , ETH_ALEN ) ;
ath_hw_setbssidmask ( common ) ;
/* Set op-mode & TSF */
if ( iter_data . naps > 0 ) {
2010-04-01 02:05:31 +04:00
ath9k_hw_set_tsfadjust ( ah , 1 ) ;
2009-03-03 07:46:56 +03:00
sc - > sc_flags | = SC_OP_TSF_RESET ;
2011-01-15 22:13:48 +03:00
ah - > opmode = NL80211_IFTYPE_AP ;
} else {
ath9k_hw_set_tsfadjust ( ah , 0 ) ;
sc - > sc_flags & = ~ SC_OP_TSF_RESET ;
2008-10-29 07:46:06 +03:00
2011-05-04 03:57:19 +04:00
if ( iter_data . nmeshes )
ah - > opmode = NL80211_IFTYPE_MESH_POINT ;
else if ( iter_data . nwds )
2011-01-15 22:13:48 +03: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 07:46:06 +03:00
2009-01-28 18:23:27 +03:00
/*
* Enable MIB interrupts when there are hardware phy counters .
*/
2011-01-15 22:13:48 +03:00
if ( ( iter_data . nstations + iter_data . nadhocs + iter_data . nmeshes ) > 0 ) {
2010-04-16 01:38:23 +04:00
if ( ah - > config . enable_ani )
ah - > imask | = ATH9K_INT_MIB ;
2010-04-01 02:05:31 +04:00
ah - > imask | = ATH9K_INT_TSFOOR ;
2011-01-15 22:13:48 +03:00
} else {
ah - > imask & = ~ ATH9K_INT_MIB ;
ah - > imask & = ~ ATH9K_INT_TSFOOR ;
2009-02-12 07:36:47 +03:00
}
2010-04-01 02:05:31 +04:00
ath9k_hw_set_interrupts ( ah , ah - > imask ) ;
2009-01-28 18:23:27 +03:00
2011-01-15 22:13:48 +03:00
/* Set up ANI */
if ( ( iter_data . naps + iter_data . nadhocs ) > 0 ) {
2011-05-09 17:41:29 +04:00
sc - > sc_ah - > stats . avgbrssi = ATH_RSSI_DUMMY_MARKER ;
2010-06-23 17:49:21 +04:00
sc - > sc_flags | = SC_OP_ANI_RUN ;
2009-11-04 04:07:04 +03:00
ath_start_ani ( common ) ;
2011-04-08 15:36:25 +04:00
} else {
sc - > sc_flags & = ~ SC_OP_ANI_RUN ;
del_timer_sync ( & common - > ani . timer ) ;
2010-06-23 17:49:21 +04:00
}
2011-01-15 22:13:48 +03:00
}
2008-10-04 02:45:27 +04:00
2011-01-15 22:13:48 +03:00
/* Called with sc->mutex held, vif counts set up properly. */
static void ath9k_do_vif_add_setup ( struct ieee80211_hw * hw ,
struct ieee80211_vif * vif )
{
2011-01-24 21:23:18 +03:00
struct ath_softc * sc = hw - > priv ;
2011-01-15 22:13:48 +03:00
ath9k_calculate_summary_state ( hw , vif ) ;
if ( ath9k_uses_beacons ( vif - > type ) ) {
int error ;
/* This may fail because upper levels do not have beacons
* properly configured yet . That ' s OK , we assume it
* will be properly configured and then we will be notified
* in the info_changed method and set up beacons properly
* there .
*/
2011-02-09 15:16:39 +03:00
ath9k_set_beaconing_status ( sc , false ) ;
2011-01-24 21:23:18 +03:00
error = ath_beacon_alloc ( sc , vif ) ;
2011-02-01 20:35:36 +03:00
if ( ! error )
2011-01-15 22:13:48 +03:00
ath_beacon_config ( sc , vif ) ;
2011-02-09 15:16:39 +03:00
ath9k_set_beaconing_status ( sc , true ) ;
2011-01-15 22:13:48 +03:00
}
2008-08-04 11:16:41 +04:00
}
2011-01-15 22:13:48 +03:00
static int ath9k_add_interface ( struct ieee80211_hw * hw ,
struct ieee80211_vif * vif )
2010-12-08 17:08:55 +03:00
{
2011-01-24 21:23:18 +03:00
struct ath_softc * sc = hw - > priv ;
2011-01-15 22:13:48 +03:00
struct ath_hw * ah = sc - > sc_ah ;
struct ath_common * common = ath9k_hw_common ( ah ) ;
int ret = 0 ;
2010-12-08 17:08:55 +03:00
2011-04-07 21:07:17 +04:00
ath9k_ps_wakeup ( sc ) ;
2011-01-15 22:13:48 +03:00
mutex_lock ( & sc - > mutex ) ;
2010-12-08 17:08:55 +03:00
2011-01-15 22:13:48 +03: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 17:08:55 +03:00
2011-01-15 22:13:48 +03: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-04-04 21:26:16 +04:00
if ( ( ah - > opmode = = NL80211_IFTYPE_ADHOC ) | |
( ( vif - > type = = NL80211_IFTYPE_ADHOC ) & &
sc - > nvifs > 0 ) ) {
2011-01-15 22:13:48 +03:00
ath_err ( common , " Cannot create ADHOC interface when other "
" interfaces already exist. \n " ) ;
ret = - EINVAL ;
goto out ;
2010-12-08 17:08:55 +03:00
}
2011-01-15 22:13:48 +03:00
ath_dbg ( common , ATH_DBG_CONFIG ,
" Attach a VIF of type: %d \n " , vif - > type ) ;
sc - > nvifs + + ;
ath9k_do_vif_add_setup ( hw , vif ) ;
out :
mutex_unlock ( & sc - > mutex ) ;
2011-04-07 21:07:17 +04:00
ath9k_ps_restore ( sc ) ;
2011-01-15 22:13:48 +03:00
return ret ;
2010-12-08 17:08:55 +03:00
}
static int ath9k_change_interface ( struct ieee80211_hw * hw ,
struct ieee80211_vif * vif ,
enum nl80211_iftype new_type ,
bool p2p )
{
2011-01-24 21:23:18 +03:00
struct ath_softc * sc = hw - > priv ;
2010-12-08 17:08:55 +03:00
struct ath_common * common = ath9k_hw_common ( sc - > sc_ah ) ;
2010-12-21 06:59:06 +03:00
int ret = 0 ;
2010-12-08 17:08:55 +03:00
ath_dbg ( common , ATH_DBG_CONFIG , " Change Interface \n " ) ;
mutex_lock ( & sc - > mutex ) ;
2011-04-07 21:07:17 +04:00
ath9k_ps_wakeup ( sc ) ;
2010-12-08 17:08:55 +03:00
2011-01-15 22:13:48 +03:00
/* See if new interface type is valid. */
if ( ( new_type = = NL80211_IFTYPE_ADHOC ) & &
( sc - > nvifs > 1 ) ) {
ath_err ( common , " When using ADHOC, it must be the only "
" interface. \n " ) ;
ret = - EINVAL ;
goto out ;
}
if ( ath9k_uses_beacons ( new_type ) & &
! ath9k_uses_beacons ( vif - > type ) ) {
2010-12-08 17:08:55 +03:00
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 17:08:55 +03:00
}
}
2011-01-15 22:13:48 +03:00
/* Clean up old vif stuff */
if ( ath9k_uses_beacons ( vif - > type ) )
ath9k_reclaim_beacon ( sc , vif ) ;
/* Add new settings */
2010-12-08 17:08:55 +03:00
vif - > type = new_type ;
vif - > p2p = p2p ;
2011-01-15 22:13:48 +03:00
ath9k_do_vif_add_setup ( hw , vif ) ;
2010-12-21 06:59:06 +03:00
out :
2011-04-07 21:07:17 +04:00
ath9k_ps_restore ( sc ) ;
2010-12-08 17:08:55 +03:00
mutex_unlock ( & sc - > mutex ) ;
2010-12-21 06:59:06 +03:00
return ret ;
2010-12-08 17:08:55 +03:00
}
2008-09-10 17:19:27 +04:00
static void ath9k_remove_interface ( struct ieee80211_hw * hw ,
2009-12-23 15:15:45 +03:00
struct ieee80211_vif * vif )
2008-08-04 11:16:41 +04:00
{
2011-01-24 21:23:18 +03:00
struct ath_softc * sc = hw - > priv ;
2009-09-13 13:42:02 +04:00
struct ath_common * common = ath9k_hw_common ( sc - > sc_ah ) ;
2008-08-04 11:16:41 +04:00
2010-12-03 06:12:37 +03:00
ath_dbg ( common , ATH_DBG_CONFIG , " Detach Interface \n " ) ;
2008-08-04 11:16:41 +04:00
2011-04-07 21:07:17 +04:00
ath9k_ps_wakeup ( sc ) ;
2009-02-04 05:40:07 +03:00
mutex_lock ( & sc - > mutex ) ;
2011-01-15 22:13:48 +03:00
sc - > nvifs - - ;
2008-08-11 15:01:49 +04:00
2008-09-10 17:19:27 +04:00
/* Reclaim beacon resources */
2011-01-15 22:13:48 +03:00
if ( ath9k_uses_beacons ( vif - > type ) )
2010-12-08 17:08:55 +03:00
ath9k_reclaim_beacon ( sc , vif ) ;
2009-03-03 20:23:26 +03:00
2011-01-15 22:13:48 +03:00
ath9k_calculate_summary_state ( hw , NULL ) ;
2009-02-04 05:40:07 +03:00
mutex_unlock ( & sc - > mutex ) ;
2011-04-07 21:07:17 +04:00
ath9k_ps_restore ( sc ) ;
2008-08-04 11:16:41 +04:00
}
2010-10-05 19:06:40 +04:00
static void ath9k_enable_ps ( struct ath_softc * sc )
2010-02-03 20:21:13 +03:00
{
2010-04-01 02:05:31 +04:00
struct ath_hw * ah = sc - > sc_ah ;
2010-02-03 20:21:13 +03:00
sc - > ps_enabled = true ;
2010-04-01 02: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 ;
ath9k_hw_set_interrupts ( ah , ah - > imask ) ;
2010-02-03 20:21:13 +03:00
}
2010-05-18 05:57:55 +04:00
ath9k_hw_setrxabort ( ah , 1 ) ;
2010-02-03 20:21:13 +03:00
}
}
2010-10-05 19:06:41 +04:00
static void ath9k_disable_ps ( struct ath_softc * sc )
{
struct ath_hw * ah = sc - > sc_ah ;
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 ;
ath9k_hw_set_interrupts ( ah , ah - > imask ) ;
}
}
}
2008-10-09 14:18:51 +04:00
static int ath9k_config ( struct ieee80211_hw * hw , u32 changed )
2008-08-04 11:16:41 +04:00
{
2011-01-24 21:23:18 +03:00
struct ath_softc * sc = hw - > priv ;
2010-10-10 20:21:52 +04:00
struct ath_hw * ah = sc - > sc_ah ;
struct ath_common * common = ath9k_hw_common ( ah ) ;
2008-10-09 14:18:51 +04:00
struct ieee80211_conf * conf = & hw - > conf ;
2011-01-24 21:23:16 +03:00
bool disable_radio = false ;
2008-08-04 11:16:41 +04:00
2008-12-18 09:10:16 +03:00
mutex_lock ( & sc - > mutex ) ;
2009-02-04 05:40:07 +03:00
2009-10-29 20:41:15 +03:00
/*
* Leave this as the first check because we need to turn on the
* radio if it was disabled before prior to processing the rest
* of the changes . Likewise we must only disable the radio towards
* the end .
*/
2009-07-15 04:22:53 +04:00
if ( changed & IEEE80211_CONF_CHANGE_IDLE ) {
2011-01-24 21:23:16 +03:00
sc - > ps_idle = ! ! ( conf - > flags & IEEE80211_CONF_IDLE ) ;
if ( ! sc - > ps_idle ) {
2009-11-03 01:35:42 +03:00
ath_radio_enable ( sc , hw ) ;
2010-12-03 06:12:37 +03:00
ath_dbg ( common , ATH_DBG_CONFIG ,
" not-idle: enabling radio \n " ) ;
2011-01-24 21:23:16 +03:00
} else {
disable_radio = true ;
2009-07-15 04:22:53 +04:00
}
}
2009-11-24 10:53:25 +03: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 08:47:08 +03:00
if ( changed & IEEE80211_CONF_CHANGE_PS ) {
2010-09-16 23:12:26 +04:00
unsigned long flags ;
spin_lock_irqsave ( & sc - > sc_pm_lock , flags ) ;
2010-10-05 19:06:40 +04:00
if ( conf - > flags & IEEE80211_CONF_PS )
ath9k_enable_ps ( sc ) ;
2010-10-05 19:06:41 +04:00
else
ath9k_disable_ps ( sc ) ;
2010-09-16 23:12:26 +04:00
spin_unlock_irqrestore ( & sc - > sc_pm_lock , flags ) ;
2009-01-20 08:47:08 +03:00
}
2010-01-08 08:06:13 +03:00
if ( changed & IEEE80211_CONF_CHANGE_MONITOR ) {
if ( conf - > flags & IEEE80211_CONF_MONITOR ) {
2010-12-03 06:12:37 +03:00
ath_dbg ( common , ATH_DBG_CONFIG ,
" Monitor mode is enabled \n " ) ;
2010-10-27 17:01:15 +04:00
sc - > sc_ah - > is_monitoring = true ;
} else {
2010-12-03 06:12:37 +03:00
ath_dbg ( common , ATH_DBG_CONFIG ,
" Monitor mode is disabled \n " ) ;
2010-10-27 17:01:15 +04:00
sc - > sc_ah - > is_monitoring = false ;
2010-01-08 08:06:13 +03:00
}
}
2009-01-07 12:13:27 +03:00
if ( changed & IEEE80211_CONF_CHANGE_CHANNEL ) {
2008-11-24 09:38:35 +03:00
struct ieee80211_channel * curchan = hw - > conf . channel ;
2009-01-23 02:16:48 +03:00
int pos = curchan - > hw_value ;
2010-10-10 20:21:52 +04:00
int old_pos = - 1 ;
unsigned long flags ;
if ( ah - > curchan )
old_pos = ah - > curchan - & ah - > channels [ 0 ] ;
2008-10-14 18:58:37 +04:00
2010-07-31 02:11:59 +04:00
if ( hw - > conf . flags & IEEE80211_CONF_OFFCHANNEL )
sc - > sc_flags | = SC_OP_OFFCHANNEL ;
else
sc - > sc_flags & = ~ SC_OP_OFFCHANNEL ;
2009-03-03 20:23:32 +03:00
2011-02-08 00:44:33 +03:00
ath_dbg ( common , ATH_DBG_CONFIG ,
" Set channel: %d MHz type: %d \n " ,
curchan - > center_freq , conf - > channel_type ) ;
2008-08-04 11:16:41 +04:00
2011-01-28 09:05:43 +03:00
ath9k_cmn_update_ichannel ( & sc - > sc_ah - > channels [ pos ] ,
curchan , conf - > channel_type ) ;
2008-11-27 07:16:27 +03:00
2010-10-10 20:21:52 +04: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 ) ;
/*
* 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 20:23:32 +03:00
if ( ath_set_channel ( sc , hw , & sc - > sc_ah - > channels [ pos ] ) < 0 ) {
2010-12-03 06:12:36 +03:00
ath_err ( common , " Unable to set channel \n " ) ;
2008-12-18 09:10:16 +03:00
mutex_unlock ( & sc - > mutex ) ;
2008-11-27 07:16:27 +03:00
return - EINVAL ;
}
2010-10-10 20:21:52 +04: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 09:27:43 +03:00
}
2008-08-04 11:16:41 +04:00
2010-01-19 22:04:19 +03:00
if ( changed & IEEE80211_CONF_CHANGE_POWER ) {
2011-02-08 00:44:37 +03:00
ath_dbg ( common , ATH_DBG_CONFIG ,
" Set power: %d \n " , conf - > power_level ) ;
2009-02-09 10:57:03 +03:00
sc - > config . txpowlimit = 2 * conf - > power_level ;
2011-01-21 20:52:38 +03:00
ath9k_ps_wakeup ( sc ) ;
2011-01-31 21:17:44 +03:00
ath9k_cmn_update_txpow ( ah , sc - > curtxpow ,
sc - > config . txpowlimit , & sc - > curtxpow ) ;
2011-01-21 20:52:38 +03:00
ath9k_ps_restore ( sc ) ;
2010-01-19 22:04:19 +03:00
}
2008-08-04 11:16:41 +04:00
2009-07-15 04:22:53 +04:00
if ( disable_radio ) {
2010-12-03 06:12:37 +03:00
ath_dbg ( common , ATH_DBG_CONFIG , " idle: disabling radio \n " ) ;
2009-11-03 01:35:42 +03:00
ath_radio_disable ( sc , hw ) ;
2009-07-15 04:22:53 +04:00
}
2008-12-18 09:10:16 +03:00
mutex_unlock ( & sc - > mutex ) ;
2009-02-04 05:40:07 +03:00
2008-08-04 11:16:41 +04:00
return 0 ;
}
2008-09-10 17:19:27 +04:00
# define SUPPORTED_FILTERS \
( FIF_PROMISC_IN_BSS | \
FIF_ALLMULTI | \
FIF_CONTROL | \
2009-08-09 05:55:16 +04:00
FIF_PSPOLL | \
2008-09-10 17:19:27 +04:00
FIF_OTHER_BSS | \
FIF_BCN_PRBRESP_PROMISC | \
2010-10-13 18:29:31 +04:00
FIF_PROBE_REQ | \
2008-09-10 17:19:27 +04:00
FIF_FCSFAIL )
2008-08-25 19:17:29 +04:00
2008-09-10 17:19:27 +04:00
/* 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 18:16:53 +04:00
u64 multicast )
2008-09-10 17:19:27 +04:00
{
2011-01-24 21:23:18 +03:00
struct ath_softc * sc = hw - > priv ;
2008-09-10 17:19:27 +04:00
u32 rfilt ;
2008-08-04 11:16:41 +04:00
2008-09-10 17:19:27 +04:00
changed_flags & = SUPPORTED_FILTERS ;
* total_flags & = SUPPORTED_FILTERS ;
2008-08-04 11:16:41 +04:00
2008-12-07 19:14:03 +03:00
sc - > rx . rxfilter = * total_flags ;
2009-05-19 18:01:41 +04:00
ath9k_ps_wakeup ( sc ) ;
2008-09-10 17:19:27 +04:00
rfilt = ath_calcrxfilter ( sc ) ;
ath9k_hw_setrxfilter ( sc - > sc_ah , rfilt ) ;
2009-05-19 18:01:41 +04:00
ath9k_ps_restore ( sc ) ;
2008-08-04 11:16:41 +04:00
2010-12-03 06:12:37 +03:00
ath_dbg ( ath9k_hw_common ( sc - > sc_ah ) , ATH_DBG_CONFIG ,
" Set HW RX filter: 0x%x \n " , rfilt ) ;
2008-09-10 17:19:27 +04:00
}
2008-08-04 11:16:41 +04:00
2010-02-19 21:06:56 +03:00
static int ath9k_sta_add ( struct ieee80211_hw * hw ,
struct ieee80211_vif * vif ,
struct ieee80211_sta * sta )
2008-09-10 17:19:27 +04:00
{
2011-01-24 21:23:18 +03:00
struct ath_softc * sc = hw - > priv ;
2011-04-18 01:28:10 +04: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 11:16:41 +04:00
2010-02-19 21:06:56 +03:00
ath_node_attach ( sc , sta ) ;
2011-05-10 22:52:22 +04:00
if ( vif - > type ! = NL80211_IFTYPE_AP & &
vif - > type ! = NL80211_IFTYPE_AP_VLAN )
return 0 ;
2011-04-18 01:28:10 +04:00
an - > ps_key = ath_key_config ( common , vif , sta , & ps_key ) ;
2010-02-19 21:06:56 +03:00
return 0 ;
}
2011-04-18 01:28:10 +04: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 21:06:56 +03:00
static int ath9k_sta_remove ( struct ieee80211_hw * hw ,
struct ieee80211_vif * vif ,
struct ieee80211_sta * sta )
{
2011-01-24 21:23:18 +03:00
struct ath_softc * sc = hw - > priv ;
2010-02-19 21:06:56 +03:00
2011-04-18 01:28:10 +04:00
ath9k_del_ps_key ( sc , vif , sta ) ;
2010-02-19 21:06:56 +03:00
ath_node_detach ( sc , sta ) ;
return 0 ;
2008-08-04 11:16:41 +04:00
}
2011-04-18 01:28:09 +04: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 ;
switch ( cmd ) {
case STA_NOTIFY_SLEEP :
an - > sleeping = true ;
if ( ath_tx_aggr_sleep ( sc , an ) )
ieee80211_sta_set_tim ( sta ) ;
break ;
case STA_NOTIFY_AWAKE :
an - > sleeping = false ;
ath_tx_aggr_wakeup ( sc , an ) ;
break ;
}
}
2009-02-04 05:40:07 +03:00
static int ath9k_conf_tx ( struct ieee80211_hw * hw , u16 queue ,
2008-09-10 17:19:27 +04:00
const struct ieee80211_tx_queue_params * params )
2008-08-04 11:16:41 +04:00
{
2011-01-24 21:23:18 +03:00
struct ath_softc * sc = hw - > priv ;
2009-09-13 13:42:02 +04:00
struct ath_common * common = ath9k_hw_common ( sc - > sc_ah ) ;
2010-11-07 16:59:39 +03:00
struct ath_txq * txq ;
2008-09-10 17:19:27 +04:00
struct ath9k_tx_queue_info qi ;
2010-11-07 16:59:39 +03:00
int ret = 0 ;
2008-08-04 11:16:41 +04:00
2008-09-10 17:19:27 +04:00
if ( queue > = WME_NUM_AC )
return 0 ;
2008-08-04 11:16:41 +04:00
2010-11-07 16:59:39 +03:00
txq = sc - > tx . txq_map [ queue ] ;
2011-04-07 21:07:17 +04:00
ath9k_ps_wakeup ( sc ) ;
2009-02-04 05:40:07 +03:00
mutex_lock ( & sc - > mutex ) ;
2009-03-30 13:58:46 +04:00
memset ( & qi , 0 , sizeof ( struct ath9k_tx_queue_info ) ) ;
2008-09-10 17:19:27 +04:00
qi . tqi_aifs = params - > aifs ;
qi . tqi_cwmin = params - > cw_min ;
qi . tqi_cwmax = params - > cw_max ;
qi . tqi_burstTime = params - > txop ;
2008-08-04 11:16:41 +04:00
2010-12-03 06:12:37 +03:00
ath_dbg ( common , ATH_DBG_CONFIG ,
" 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 11:16:41 +04:00
2010-11-07 16:59:39 +03:00
ret = ath_txq_update ( sc , txq - > axq_qnum , & qi ) ;
2008-09-10 17:19:27 +04:00
if ( ret )
2010-12-03 06:12:36 +03:00
ath_err ( common , " TXQ Update failed \n " ) ;
2008-08-04 11:16:41 +04:00
2009-11-25 09:31:54 +03:00
if ( sc - > sc_ah - > opmode = = NL80211_IFTYPE_ADHOC )
2010-11-07 16:59:39 +03:00
if ( queue = = WME_AC_BE & & ! ret )
2009-11-25 09:31:54 +03:00
ath_beaconq_config ( sc ) ;
2009-02-04 05:40:07 +03:00
mutex_unlock ( & sc - > mutex ) ;
2011-04-07 21:07:17 +04:00
ath9k_ps_restore ( sc ) ;
2009-02-04 05:40:07 +03:00
2008-09-10 17:19:27 +04:00
return ret ;
}
2008-08-04 11:16:41 +04:00
2008-09-10 17:19:27 +04:00
static int ath9k_set_key ( struct ieee80211_hw * hw ,
enum set_key_cmd cmd ,
2008-12-29 14:55:09 +03:00
struct ieee80211_vif * vif ,
struct ieee80211_sta * sta ,
2008-09-10 17:19:27 +04:00
struct ieee80211_key_conf * key )
{
2011-01-24 21:23:18 +03:00
struct ath_softc * sc = hw - > priv ;
2009-09-13 13:42:02 +04:00
struct ath_common * common = ath9k_hw_common ( sc - > sc_ah ) ;
2008-09-10 17:19:27 +04:00
int ret = 0 ;
2008-08-04 11:16:41 +04:00
2011-01-05 17:39:17 +03:00
if ( ath9k_modparam_nohwcrypt )
2009-02-24 14:42:01 +03:00
return - ENOSPC ;
2011-03-23 15:52:19 +03:00
if ( vif - > type = = NL80211_IFTYPE_ADHOC & &
( 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 05:40:07 +03:00
mutex_lock ( & sc - > mutex ) ;
2009-01-20 08:47:08 +03:00
ath9k_ps_wakeup ( sc ) ;
2010-12-03 06:12:37 +03:00
ath_dbg ( common , ATH_DBG_CONFIG , " Set HW Key \n " ) ;
2008-08-04 11:16:41 +04:00
2008-09-10 17:19:27 +04:00
switch ( cmd ) {
case SET_KEY :
2011-04-18 01:28:10 +04:00
if ( sta )
ath9k_del_ps_key ( sc , vif , sta ) ;
2010-09-08 11:05:04 +04:00
ret = ath_key_config ( common , vif , sta , key ) ;
2008-12-17 14:32:17 +03:00
if ( ret > = 0 ) {
key - > hw_key_idx = ret ;
2008-09-10 17:19:27 +04:00
/* push IV and Michael MIC generation to stack */
key - > flags | = IEEE80211_KEY_FLAG_GENERATE_IV ;
2010-08-10 11:46:38 +04:00
if ( key - > cipher = = WLAN_CIPHER_SUITE_TKIP )
2008-09-10 17:19:27 +04:00
key - > flags | = IEEE80211_KEY_FLAG_GENERATE_MMIC ;
2010-08-10 11:46:38 +04:00
if ( sc - > sc_ah - > sw_mgmt_crypto & &
key - > cipher = = WLAN_CIPHER_SUITE_CCMP )
2009-01-08 14:32:13 +03:00
key - > flags | = IEEE80211_KEY_FLAG_SW_MGMT ;
2008-12-17 14:32:17 +03:00
ret = 0 ;
2008-09-10 17:19:27 +04:00
}
break ;
case DISABLE_KEY :
2010-09-08 11:05:04 +04:00
ath_key_delete ( common , key ) ;
2008-09-10 17:19:27 +04:00
break ;
default :
ret = - EINVAL ;
}
2008-08-04 11:16:41 +04:00
2009-01-20 08:47:08 +03:00
ath9k_ps_restore ( sc ) ;
2009-02-04 05:40:07 +03:00
mutex_unlock ( & sc - > mutex ) ;
2008-09-10 17:19:27 +04:00
return ret ;
}
2011-04-04 21:26:18 +04:00
static void ath9k_bss_iter ( void * data , u8 * mac , struct ieee80211_vif * vif )
{
struct ath_softc * sc = data ;
struct ath_common * common = ath9k_hw_common ( sc - > sc_ah ) ;
struct ieee80211_bss_conf * bss_conf = & vif - > bss_conf ;
struct ath_vif * avp = ( void * ) vif - > drv_priv ;
switch ( sc - > sc_ah - > opmode ) {
case NL80211_IFTYPE_ADHOC :
/* There can be only one vif available */
memcpy ( common - > curbssid , bss_conf - > bssid , ETH_ALEN ) ;
common - > curaid = bss_conf - > aid ;
ath9k_hw_write_associd ( sc - > sc_ah ) ;
2011-04-04 21:26:19 +04:00
/* configure beacon */
if ( bss_conf - > enable_beacon )
ath_beacon_config ( sc , vif ) ;
2011-04-04 21:26:18 +04:00
break ;
case NL80211_IFTYPE_STATION :
/*
* Skip iteration if primary station vif ' s bss info
* was not changed
*/
if ( sc - > sc_flags & SC_OP_PRIM_STA_VIF )
break ;
if ( bss_conf - > assoc ) {
sc - > sc_flags | = SC_OP_PRIM_STA_VIF ;
avp - > primary_sta_vif = true ;
memcpy ( common - > curbssid , bss_conf - > bssid , ETH_ALEN ) ;
common - > curaid = bss_conf - > aid ;
ath9k_hw_write_associd ( sc - > sc_ah ) ;
2011-04-04 21:26:19 +04:00
ath_dbg ( common , ATH_DBG_CONFIG ,
" Bss Info ASSOC %d, bssid: %pM \n " ,
bss_conf - > aid , common - > curbssid ) ;
ath_beacon_config ( sc , vif ) ;
2011-04-22 13:20:39 +04:00
/*
* Request a re - configuration of Beacon related timers
* on the receipt of the first Beacon frame ( i . e . ,
* after time sync with the AP ) .
*/
sc - > ps_flags | = PS_BEACON_SYNC | PS_WAIT_FOR_BEACON ;
2011-04-04 21:26:19 +04:00
/* Reset rssi stats */
sc - > last_rssi = ATH_RSSI_DUMMY_MARKER ;
sc - > sc_ah - > stats . avgbrssi = ATH_RSSI_DUMMY_MARKER ;
sc - > sc_flags | = SC_OP_ANI_RUN ;
ath_start_ani ( common ) ;
2011-04-04 21:26:18 +04:00
}
break ;
default :
break ;
}
}
static void ath9k_config_bss ( struct ath_softc * sc , struct ieee80211_vif * vif )
{
struct ath_common * common = ath9k_hw_common ( sc - > sc_ah ) ;
struct ieee80211_bss_conf * bss_conf = & vif - > bss_conf ;
struct ath_vif * avp = ( void * ) vif - > drv_priv ;
/* Reconfigure bss info */
if ( avp - > primary_sta_vif & & ! bss_conf - > assoc ) {
2011-04-04 21:26:19 +04:00
ath_dbg ( common , ATH_DBG_CONFIG ,
" Bss Info DISASSOC %d, bssid %pM \n " ,
common - > curaid , common - > curbssid ) ;
sc - > sc_flags & = ~ ( SC_OP_PRIM_STA_VIF | SC_OP_BEACONS ) ;
2011-04-04 21:26:18 +04:00
avp - > primary_sta_vif = false ;
memset ( common - > curbssid , 0 , ETH_ALEN ) ;
common - > curaid = 0 ;
}
ieee80211_iterate_active_interfaces_atomic (
sc - > hw , ath9k_bss_iter , sc ) ;
/*
* None of station vifs are associated .
* Clear bssid & aid
*/
if ( ( sc - > sc_ah - > opmode = = NL80211_IFTYPE_STATION ) & &
2011-04-04 21:26:19 +04:00
! ( sc - > sc_flags & SC_OP_PRIM_STA_VIF ) ) {
2011-04-04 21:26:18 +04:00
ath9k_hw_write_associd ( sc - > sc_ah ) ;
2011-04-04 21:26:19 +04:00
/* Stop ANI */
sc - > sc_flags & = ~ SC_OP_ANI_RUN ;
del_timer_sync ( & common - > ani . timer ) ;
}
2011-04-04 21:26:18 +04:00
}
2008-08-04 11:16:41 +04:00
2008-09-10 17:19:27 +04:00
static void ath9k_bss_info_changed ( struct ieee80211_hw * hw ,
struct ieee80211_vif * vif ,
struct ieee80211_bss_conf * bss_conf ,
u32 changed )
{
2011-01-24 21:23:18 +03:00
struct ath_softc * sc = hw - > priv ;
2009-04-23 18:13:26 +04:00
struct ath_hw * ah = sc - > sc_ah ;
2009-09-10 20:22:37 +04:00
struct ath_common * common = ath9k_hw_common ( ah ) ;
2009-04-23 18:13:26 +04:00
struct ath_vif * avp = ( void * ) vif - > drv_priv ;
2010-01-15 04:33:40 +03:00
int slottime ;
2009-11-16 09:10:48 +03:00
int error ;
2008-08-04 11:16:41 +04:00
2011-04-07 21:07:17 +04:00
ath9k_ps_wakeup ( sc ) ;
2009-02-04 05:40:07 +03:00
mutex_lock ( & sc - > mutex ) ;
2009-11-16 09:10:48 +03:00
if ( changed & BSS_CHANGED_BSSID ) {
2011-04-04 21:26:18 +04:00
ath9k_config_bss ( sc , vif ) ;
2009-04-23 18:13:26 +04:00
2010-12-03 06:12:37 +03:00
ath_dbg ( common , ATH_DBG_CONFIG , " BSSID: %pM aid: 0x%x \n " ,
common - > curbssid , common - > curaid ) ;
2009-11-16 09:10:48 +03:00
}
2009-04-23 18:13:26 +04:00
2009-11-16 09:10:48 +03:00
/* Enable transmission of beacons (AP, IBSS, MESH) */
if ( ( changed & BSS_CHANGED_BEACON ) | |
( ( changed & BSS_CHANGED_BEACON_ENABLED ) & & bss_conf - > enable_beacon ) ) {
2011-02-09 15:16:39 +03:00
ath9k_set_beaconing_status ( sc , false ) ;
2011-01-24 21:23:18 +03:00
error = ath_beacon_alloc ( sc , vif ) ;
2009-11-16 09:10:48 +03:00
if ( ! error )
ath_beacon_config ( sc , vif ) ;
2011-02-09 15:16:39 +03:00
ath9k_set_beaconing_status ( sc , true ) ;
2010-01-15 04:33:40 +03: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 18:13:26 +04:00
}
2009-11-16 09:10:48 +03:00
/* Disable transmission of beacons */
2011-02-09 15:16:39 +03:00
if ( ( changed & BSS_CHANGED_BEACON_ENABLED ) & &
! bss_conf - > enable_beacon ) {
ath9k_set_beaconing_status ( sc , false ) ;
avp - > is_bslot_active = false ;
ath9k_set_beaconing_status ( sc , true ) ;
}
2009-04-23 18:13:26 +04:00
2009-11-16 09:10:48 +03:00
if ( changed & BSS_CHANGED_BEACON_INT ) {
/*
* In case of AP mode , the HW TSF has to be reset
* when the beacon interval changes .
*/
if ( vif - > type = = NL80211_IFTYPE_AP ) {
sc - > sc_flags | = SC_OP_TSF_RESET ;
2011-02-09 15:16:39 +03:00
ath9k_set_beaconing_status ( sc , false ) ;
2011-01-24 21:23:18 +03:00
error = ath_beacon_alloc ( sc , vif ) ;
2009-04-23 18:13:26 +04:00
if ( ! error )
ath_beacon_config ( sc , vif ) ;
2011-02-09 15:16:39 +03:00
ath9k_set_beaconing_status ( sc , true ) ;
2011-04-04 21:26:19 +04:00
} else
2009-11-16 09:10:48 +03:00
ath_beacon_config ( sc , vif ) ;
2009-04-23 18:13:26 +04:00
}
2008-09-10 17:19:27 +04:00
if ( changed & BSS_CHANGED_ERP_PREAMBLE ) {
2010-12-03 06:12:37 +03:00
ath_dbg ( common , ATH_DBG_CONFIG , " BSS Changed PREAMBLE %d \n " ,
bss_conf - > use_short_preamble ) ;
2008-09-10 17:19:27 +04:00
if ( bss_conf - > use_short_preamble )
sc - > sc_flags | = SC_OP_PREAMBLE_SHORT ;
else
sc - > sc_flags & = ~ SC_OP_PREAMBLE_SHORT ;
}
2008-08-04 11:16:41 +04:00
2008-09-10 17:19:27 +04:00
if ( changed & BSS_CHANGED_ERP_CTS_PROT ) {
2010-12-03 06:12:37 +03:00
ath_dbg ( common , ATH_DBG_CONFIG , " BSS Changed CTS PROT %d \n " ,
bss_conf - > use_cts_prot ) ;
2008-09-10 17:19:27 +04:00
if ( bss_conf - > use_cts_prot & &
hw - > conf . channel - > band ! = IEEE80211_BAND_5GHZ )
sc - > sc_flags | = SC_OP_PROTECT_ENABLE ;
else
sc - > sc_flags & = ~ SC_OP_PROTECT_ENABLE ;
}
2008-08-04 11:16:41 +04:00
2009-02-04 05:40:07 +03:00
mutex_unlock ( & sc - > mutex ) ;
2011-04-07 21:07:17 +04:00
ath9k_ps_restore ( sc ) ;
2008-09-10 17:19:27 +04:00
}
2008-08-04 11:16:41 +04:00
2008-09-10 17:19:27 +04:00
static u64 ath9k_get_tsf ( struct ieee80211_hw * hw )
{
2011-01-24 21:23:18 +03:00
struct ath_softc * sc = hw - > priv ;
2008-09-10 17:19:27 +04:00
u64 tsf ;
2008-08-04 11:16:41 +04:00
2009-02-04 05:40:07 +03:00
mutex_lock ( & sc - > mutex ) ;
2010-12-10 08:57:06 +03:00
ath9k_ps_wakeup ( sc ) ;
2009-02-04 05:40:07 +03:00
tsf = ath9k_hw_gettsf64 ( sc - > sc_ah ) ;
2010-12-10 08:57:06 +03:00
ath9k_ps_restore ( sc ) ;
2009-02-04 05:40:07 +03:00
mutex_unlock ( & sc - > mutex ) ;
2008-08-04 11:16:41 +04:00
2008-09-10 17:19:27 +04:00
return tsf ;
}
2008-08-04 11:16:41 +04:00
2009-01-24 09:09:59 +03:00
static void ath9k_set_tsf ( struct ieee80211_hw * hw , u64 tsf )
{
2011-01-24 21:23:18 +03:00
struct ath_softc * sc = hw - > priv ;
2009-01-24 09:09:59 +03:00
2009-02-04 05:40:07 +03:00
mutex_lock ( & sc - > mutex ) ;
2010-12-10 08:57:06 +03:00
ath9k_ps_wakeup ( sc ) ;
2009-02-04 05:40:07 +03:00
ath9k_hw_settsf64 ( sc - > sc_ah , tsf ) ;
2010-12-10 08:57:06 +03:00
ath9k_ps_restore ( sc ) ;
2009-02-04 05:40:07 +03:00
mutex_unlock ( & sc - > mutex ) ;
2009-01-24 09:09:59 +03:00
}
2008-09-10 17:19:27 +04:00
static void ath9k_reset_tsf ( struct ieee80211_hw * hw )
{
2011-01-24 21:23:18 +03:00
struct ath_softc * sc = hw - > priv ;
2008-08-25 19:17:29 +04:00
2009-02-04 05:40:07 +03:00
mutex_lock ( & sc - > mutex ) ;
2009-09-10 07:05:39 +04:00
ath9k_ps_wakeup ( sc ) ;
2009-02-04 05:40:07 +03:00
ath9k_hw_reset_tsf ( sc - > sc_ah ) ;
2009-09-10 07:05:39 +04:00
ath9k_ps_restore ( sc ) ;
2009-02-04 05:40:07 +03:00
mutex_unlock ( & sc - > mutex ) ;
2008-09-10 17:19:27 +04:00
}
2008-08-04 11:16:41 +04:00
2008-09-10 17:19:27 +04:00
static int ath9k_ampdu_action ( struct ieee80211_hw * hw ,
2009-11-16 14:00:38 +03:00
struct ieee80211_vif * vif ,
2009-02-04 05:40:07 +03:00
enum ieee80211_ampdu_mlme_action action ,
struct ieee80211_sta * sta ,
2011-01-18 15:51:05 +03:00
u16 tid , u16 * ssn , u8 buf_size )
2008-09-10 17:19:27 +04:00
{
2011-01-24 21:23:18 +03:00
struct ath_softc * sc = hw - > priv ;
2008-09-10 17:19:27 +04:00
int ret = 0 ;
2008-08-04 11:16:41 +04:00
2010-06-10 12:21:49 +04:00
local_bh_disable ( ) ;
2008-09-10 17:19:27 +04:00
switch ( action ) {
case IEEE80211_AMPDU_RX_START :
2008-10-29 07:49:01 +03:00
if ( ! ( sc - > sc_flags & SC_OP_RXAGGR ) )
ret = - ENOTSUPP ;
2008-09-10 17:19:27 +04:00
break ;
case IEEE80211_AMPDU_RX_STOP :
break ;
case IEEE80211_AMPDU_TX_START :
2010-11-14 17:20:11 +03:00
if ( ! ( sc - > sc_flags & SC_OP_TXAGGR ) )
return - EOPNOTSUPP ;
2009-12-24 04:03:29 +03:00
ath9k_ps_wakeup ( sc ) ;
2010-09-20 21:35:28 +04:00
ret = ath_tx_aggr_start ( sc , sta , tid , ssn ) ;
if ( ! ret )
ieee80211_start_tx_ba_cb_irqsafe ( vif , sta - > addr , tid ) ;
2009-12-24 04:03:29 +03:00
ath9k_ps_restore ( sc ) ;
2008-09-10 17:19:27 +04:00
break ;
case IEEE80211_AMPDU_TX_STOP :
2009-12-24 04:03:29 +03:00
ath9k_ps_wakeup ( sc ) ;
2009-07-23 14:02:37 +04:00
ath_tx_aggr_stop ( sc , sta , tid ) ;
2009-11-16 14:00:38 +03:00
ieee80211_stop_tx_ba_cb_irqsafe ( vif , sta - > addr , tid ) ;
2009-12-24 04:03:29 +03:00
ath9k_ps_restore ( sc ) ;
2008-09-10 17:19:27 +04:00
break ;
2009-03-23 19:28:39 +03:00
case IEEE80211_AMPDU_TX_OPERATIONAL :
2009-12-24 04:03:29 +03:00
ath9k_ps_wakeup ( sc ) ;
2008-10-29 07:49:28 +03:00
ath_tx_aggr_resume ( sc , sta , tid ) ;
2009-12-24 04:03:29 +03:00
ath9k_ps_restore ( sc ) ;
2008-10-29 07:49:28 +03:00
break ;
2008-09-10 17:19:27 +04:00
default :
2010-12-03 06:12:36 +03:00
ath_err ( ath9k_hw_common ( sc - > sc_ah ) , " Unknown AMPDU action \n " ) ;
2008-09-10 17:19:27 +04:00
}
2010-06-10 12:21:49 +04:00
local_bh_enable ( ) ;
2008-09-10 17:19:27 +04:00
return ret ;
2008-08-04 11:16:41 +04:00
}
2010-04-28 02:08:24 +04:00
static int ath9k_get_survey ( struct ieee80211_hw * hw , int idx ,
struct survey_info * survey )
{
2011-01-24 21:23:18 +03:00
struct ath_softc * sc = hw - > priv ;
2010-10-10 20:21:52 +04:00
struct ath_common * common = ath9k_hw_common ( sc - > sc_ah ) ;
2010-09-29 21:12:06 +04:00
struct ieee80211_supported_band * sband ;
2010-10-10 20:21:52 +04: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 21:12:06 +04:00
sband = hw - > wiphy - > bands [ IEEE80211_BAND_2GHZ ] ;
if ( sband & & idx > = sband - > n_channels ) {
idx - = sband - > n_channels ;
sband = NULL ;
}
2010-04-28 02:08:24 +04:00
2010-09-29 21:12:06 +04:00
if ( ! sband )
sband = hw - > wiphy - > bands [ IEEE80211_BAND_5GHZ ] ;
2010-04-28 02:08:24 +04:00
2010-10-10 20:21:52 +04:00
if ( ! sband | | idx > = sband - > n_channels ) {
spin_unlock_irqrestore ( & common - > cc_lock , flags ) ;
return - ENOENT ;
2010-09-29 19:15:28 +04:00
}
2010-04-28 02:08:24 +04:00
2010-10-10 20:21:52 +04: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 02:08:24 +04:00
return 0 ;
}
2010-01-15 04:34:58 +03:00
static void ath9k_set_coverage_class ( struct ieee80211_hw * hw , u8 coverage_class )
{
2011-01-24 21:23:18 +03:00
struct ath_softc * sc = hw - > priv ;
2010-01-15 04:34:58 +03:00
struct ath_hw * ah = sc - > sc_ah ;
mutex_lock ( & sc - > mutex ) ;
ah - > coverage_class = coverage_class ;
ath9k_hw_init_global_settings ( ah ) ;
mutex_unlock ( & sc - > mutex ) ;
}
2011-02-19 12:13:42 +03:00
static void ath9k_flush ( struct ieee80211_hw * hw , bool drop )
{
struct ath_softc * sc = hw - > priv ;
2011-03-11 23:38:19 +03:00
int timeout = 200 ; /* ms */
int i , j ;
2011-04-28 14:01:57 +04:00
bool drain_txq ;
2011-02-19 12:13:42 +03:00
mutex_lock ( & sc - > mutex ) ;
cancel_delayed_work_sync ( & sc - > tx_complete_work ) ;
2011-03-11 23:38:19 +03:00
if ( drop )
timeout = 1 ;
2011-02-19 12:13:42 +03:00
2011-03-11 23:38:19 +03:00
for ( j = 0 ; j < timeout ; j + + ) {
int npend = 0 ;
if ( j )
usleep_range ( 1000 , 2000 ) ;
2011-02-19 12:13:42 +03:00
2011-03-11 23:38:19 +03:00
for ( i = 0 ; i < ATH9K_NUM_TX_QUEUES ; i + + ) {
if ( ! ATH_TXQ_SETUP ( sc , i ) )
continue ;
npend + = ath9k_has_pending_frames ( sc , & sc - > tx . txq [ i ] ) ;
2011-02-19 12:13:42 +03:00
}
2011-03-11 23:38:19 +03:00
if ( ! npend )
goto out ;
2011-02-19 12:13:42 +03:00
}
2011-04-22 10:02:10 +04:00
ath9k_ps_wakeup ( sc ) ;
2011-04-28 14:01:57 +04:00
spin_lock_bh ( & sc - > sc_pcu_lock ) ;
drain_txq = ath_drain_all_txq ( sc , false ) ;
spin_unlock_bh ( & sc - > sc_pcu_lock ) ;
if ( ! drain_txq )
2011-02-19 12:13:42 +03:00
ath_reset ( sc , false ) ;
2011-04-22 10:02:10 +04:00
ath9k_ps_restore ( sc ) ;
2011-03-23 20:37:22 +03:00
ieee80211_wake_queues ( hw ) ;
2011-03-11 23:38:19 +03:00
out :
2011-02-19 12:13:42 +03:00
ieee80211_queue_delayed_work ( hw , & sc - > tx_complete_work , 0 ) ;
mutex_unlock ( & sc - > mutex ) ;
}
2011-04-06 10:11:11 +04:00
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 ;
}
2009-01-14 22:17:06 +03:00
struct ieee80211_ops ath9k_ops = {
2008-09-10 17:19:27 +04:00
. tx = ath9k_tx ,
. start = ath9k_start ,
. stop = ath9k_stop ,
. add_interface = ath9k_add_interface ,
2010-12-08 17:08:55 +03:00
. change_interface = ath9k_change_interface ,
2008-09-10 17:19:27 +04:00
. remove_interface = ath9k_remove_interface ,
. config = ath9k_config ,
. configure_filter = ath9k_configure_filter ,
2010-02-19 21:06:56 +03:00
. sta_add = ath9k_sta_add ,
. sta_remove = ath9k_sta_remove ,
2011-04-18 01:28:09 +04:00
. sta_notify = ath9k_sta_notify ,
2008-09-10 17:19:27 +04:00
. 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 09:09:59 +03:00
. set_tsf = ath9k_set_tsf ,
2008-09-10 17:19:27 +04:00
. reset_tsf = ath9k_reset_tsf ,
2008-10-13 15:35:05 +04:00
. ampdu_action = ath9k_ampdu_action ,
2010-04-28 02:08:24 +04:00
. get_survey = ath9k_get_survey ,
2009-06-13 13:20:26 +04:00
. rfkill_poll = ath9k_rfkill_poll_state ,
2010-01-15 04:34:58 +03:00
. set_coverage_class = ath9k_set_coverage_class ,
2011-02-19 12:13:42 +03:00
. flush = ath9k_flush ,
2011-04-06 10:11:11 +04:00
. tx_frames_pending = ath9k_tx_frames_pending ,
2008-09-10 17:19:27 +04:00
} ;