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>
2009-02-09 10:56:54 +03:00
# include "ath9k.h"
2008-08-04 11:16:41 +04:00
static char * dev_info = " ath9k " ;
MODULE_AUTHOR ( " Atheros Communications " ) ;
MODULE_DESCRIPTION ( " Support for Atheros 802.11n wireless LAN cards. " ) ;
MODULE_SUPPORTED_DEVICE ( " Atheros 802.11n WLAN cards " ) ;
MODULE_LICENSE ( " Dual BSD/GPL " ) ;
2009-02-24 14:42:01 +03:00
static int modparam_nohwcrypt ;
module_param_named ( nohwcrypt , modparam_nohwcrypt , int , 0444 ) ;
MODULE_PARM_DESC ( nohwcrypt , " Disable hardware encryption " ) ;
2009-01-23 02:16:48 +03:00
/* We use the hw_value as an index into our private channel structure */
# define CHAN2G(_freq, _idx) { \
. center_freq = ( _freq ) , \
. hw_value = ( _idx ) , \
2009-05-20 01:49:46 +04:00
. max_power = 20 , \
2009-01-23 02:16:48 +03:00
}
# define CHAN5G(_freq, _idx) { \
. band = IEEE80211_BAND_5GHZ , \
. center_freq = ( _freq ) , \
. hw_value = ( _idx ) , \
2009-05-20 01:49:46 +04:00
. max_power = 20 , \
2009-01-23 02:16:48 +03:00
}
/* Some 2 GHz radios are actually tunable on 2312-2732
* on 5 MHz steps , we support the channels which we know
* we have calibration data for all cards though to make
* this static */
static struct ieee80211_channel ath9k_2ghz_chantable [ ] = {
CHAN2G ( 2412 , 0 ) , /* Channel 1 */
CHAN2G ( 2417 , 1 ) , /* Channel 2 */
CHAN2G ( 2422 , 2 ) , /* Channel 3 */
CHAN2G ( 2427 , 3 ) , /* Channel 4 */
CHAN2G ( 2432 , 4 ) , /* Channel 5 */
CHAN2G ( 2437 , 5 ) , /* Channel 6 */
CHAN2G ( 2442 , 6 ) , /* Channel 7 */
CHAN2G ( 2447 , 7 ) , /* Channel 8 */
CHAN2G ( 2452 , 8 ) , /* Channel 9 */
CHAN2G ( 2457 , 9 ) , /* Channel 10 */
CHAN2G ( 2462 , 10 ) , /* Channel 11 */
CHAN2G ( 2467 , 11 ) , /* Channel 12 */
CHAN2G ( 2472 , 12 ) , /* Channel 13 */
CHAN2G ( 2484 , 13 ) , /* Channel 14 */
} ;
/* Some 5 GHz radios are actually tunable on XXXX-YYYY
* on 5 MHz steps , we support the channels which we know
* we have calibration data for all cards though to make
* this static */
static struct ieee80211_channel ath9k_5ghz_chantable [ ] = {
/* _We_ call this UNII 1 */
CHAN5G ( 5180 , 14 ) , /* Channel 36 */
CHAN5G ( 5200 , 15 ) , /* Channel 40 */
CHAN5G ( 5220 , 16 ) , /* Channel 44 */
CHAN5G ( 5240 , 17 ) , /* Channel 48 */
/* _We_ call this UNII 2 */
CHAN5G ( 5260 , 18 ) , /* Channel 52 */
CHAN5G ( 5280 , 19 ) , /* Channel 56 */
CHAN5G ( 5300 , 20 ) , /* Channel 60 */
CHAN5G ( 5320 , 21 ) , /* Channel 64 */
/* _We_ call this "Middle band" */
CHAN5G ( 5500 , 22 ) , /* Channel 100 */
CHAN5G ( 5520 , 23 ) , /* Channel 104 */
CHAN5G ( 5540 , 24 ) , /* Channel 108 */
CHAN5G ( 5560 , 25 ) , /* Channel 112 */
CHAN5G ( 5580 , 26 ) , /* Channel 116 */
CHAN5G ( 5600 , 27 ) , /* Channel 120 */
CHAN5G ( 5620 , 28 ) , /* Channel 124 */
CHAN5G ( 5640 , 29 ) , /* Channel 128 */
CHAN5G ( 5660 , 30 ) , /* Channel 132 */
CHAN5G ( 5680 , 31 ) , /* Channel 136 */
CHAN5G ( 5700 , 32 ) , /* Channel 140 */
/* _We_ call this UNII 3 */
CHAN5G ( 5745 , 33 ) , /* Channel 149 */
CHAN5G ( 5765 , 34 ) , /* Channel 153 */
CHAN5G ( 5785 , 35 ) , /* Channel 157 */
CHAN5G ( 5805 , 36 ) , /* Channel 161 */
CHAN5G ( 5825 , 37 ) , /* Channel 165 */
} ;
2008-12-24 02:58:39 +03:00
static void ath_cache_conf_rate ( struct ath_softc * sc ,
struct ieee80211_conf * conf )
2008-11-24 09:37:55 +03:00
{
2008-12-24 02:58:37 +03:00
switch ( conf - > channel - > band ) {
case IEEE80211_BAND_2GHZ :
if ( conf_is_ht20 ( conf ) )
sc - > cur_rate_table =
sc - > hw_rate_table [ ATH9K_MODE_11NG_HT20 ] ;
else if ( conf_is_ht40_minus ( conf ) )
sc - > cur_rate_table =
sc - > hw_rate_table [ ATH9K_MODE_11NG_HT40MINUS ] ;
else if ( conf_is_ht40_plus ( conf ) )
sc - > cur_rate_table =
sc - > hw_rate_table [ ATH9K_MODE_11NG_HT40PLUS ] ;
2008-12-24 02:58:38 +03:00
else
2008-12-24 02:58:37 +03:00
sc - > cur_rate_table =
sc - > hw_rate_table [ ATH9K_MODE_11G ] ;
break ;
case IEEE80211_BAND_5GHZ :
if ( conf_is_ht20 ( conf ) )
sc - > cur_rate_table =
sc - > hw_rate_table [ ATH9K_MODE_11NA_HT20 ] ;
else if ( conf_is_ht40_minus ( conf ) )
sc - > cur_rate_table =
sc - > hw_rate_table [ ATH9K_MODE_11NA_HT40MINUS ] ;
else if ( conf_is_ht40_plus ( conf ) )
sc - > cur_rate_table =
sc - > hw_rate_table [ ATH9K_MODE_11NA_HT40PLUS ] ;
else
2008-12-24 02:58:38 +03:00
sc - > cur_rate_table =
sc - > hw_rate_table [ ATH9K_MODE_11A ] ;
2008-12-24 02:58:37 +03:00
break ;
default :
2008-12-24 02:58:39 +03:00
BUG_ON ( 1 ) ;
2008-12-24 02:58:37 +03:00
break ;
}
2008-11-24 09:37:55 +03:00
}
static void ath_update_txpow ( struct ath_softc * sc )
{
2009-02-09 10:57:12 +03:00
struct ath_hw * ah = sc - > sc_ah ;
2008-11-24 09:37:55 +03:00
u32 txpow ;
2009-02-09 10:57:03 +03:00
if ( sc - > curtxpow ! = sc - > config . txpowlimit ) {
ath9k_hw_set_txpowerlimit ( ah , sc - > config . txpowlimit ) ;
2008-11-24 09:37:55 +03:00
/* read back in case value is clamped */
ath9k_hw_getcapability ( ah , ATH9K_CAP_TXPOW , 1 , & txpow ) ;
2009-02-09 10:57:03 +03:00
sc - > curtxpow = txpow ;
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 ;
}
}
static void ath_setup_rates ( struct ath_softc * sc , enum ieee80211_band band )
{
2009-05-06 10:20:00 +04:00
const struct ath_rate_table * rate_table = NULL ;
2008-11-24 09:37:55 +03:00
struct ieee80211_supported_band * sband ;
struct ieee80211_rate * rate ;
int i , maxrates ;
switch ( band ) {
case IEEE80211_BAND_2GHZ :
rate_table = sc - > hw_rate_table [ ATH9K_MODE_11G ] ;
break ;
case IEEE80211_BAND_5GHZ :
rate_table = sc - > hw_rate_table [ ATH9K_MODE_11A ] ;
break ;
default :
break ;
}
if ( rate_table = = NULL )
return ;
sband = & sc - > sbands [ band ] ;
rate = sc - > rates [ band ] ;
if ( rate_table - > rate_cnt > ATH_RATE_MAX )
maxrates = ATH_RATE_MAX ;
else
maxrates = rate_table - > rate_cnt ;
for ( i = 0 ; i < maxrates ; i + + ) {
rate [ i ] . bitrate = rate_table - > info [ i ] . ratekbps / 100 ;
rate [ i ] . hw_value = rate_table - > info [ i ] . ratecode ;
2009-01-27 11:21:03 +03:00
if ( rate_table - > info [ i ] . short_preamble ) {
rate [ i ] . hw_value_short = rate_table - > info [ i ] . ratecode |
rate_table - > info [ i ] . short_preamble ;
rate [ i ] . flags = IEEE80211_RATE_SHORT_PREAMBLE ;
}
2008-11-24 09:37:55 +03:00
sband - > n_bitrates + + ;
2009-01-27 11:21:03 +03:00
2009-09-07 15:52:26 +04:00
DPRINTF ( sc - > sc_ah , ATH_DBG_CONFIG , " Rate: %2dMbps, ratecode: %2d \n " ,
2008-11-28 19:48:05 +03:00
rate [ i ] . bitrate / 10 , rate [ i ] . hw_value ) ;
2008-11-24 09:37:55 +03:00
}
}
2009-06-13 13:20:24 +04:00
static struct ath9k_channel * ath_get_curchannel ( struct ath_softc * sc ,
struct ieee80211_hw * hw )
{
struct ieee80211_channel * curchan = hw - > conf . channel ;
struct ath9k_channel * channel ;
u8 chan_idx ;
chan_idx = curchan - > hw_value ;
channel = & sc - > sc_ah - > channels [ chan_idx ] ;
ath9k_update_ichannel ( sc , hw , channel ) ;
return channel ;
}
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 ;
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 ;
int r ;
2008-11-24 09:37:55 +03:00
if ( sc - > sc_flags & SC_OP_INVALID )
return - EIO ;
2009-01-20 08:47:08 +03:00
ath9k_ps_wakeup ( sc ) ;
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 .
*/
ath9k_hw_set_interrupts ( ah , 0 ) ;
2009-01-16 19:08:47 +03:00
ath_drain_all_txq ( sc , false ) ;
2008-12-24 02:58:50 +03:00
stopped = ath_stoprecv ( sc ) ;
2008-11-24 09:37:55 +03:00
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
2008-12-24 02:58:50 +03:00
if ( ! stopped | | ( sc - > sc_flags & SC_OP_FULL_RESET ) )
fastcc = false ;
2009-09-07 15:52:26 +04:00
DPRINTF ( sc - > sc_ah , ATH_DBG_CONFIG ,
2008-12-24 02:58:50 +03:00
" (%u MHz) -> (%u MHz), chanwidth: %d \n " ,
2009-02-09 10:57:26 +03:00
sc - > sc_ah - > curchan - > channel ,
2008-12-24 02:58:50 +03:00
channel - > center_freq , sc - > tx_chan_width ) ;
2008-11-24 09:37:55 +03:00
2008-12-24 02:58:50 +03:00
spin_lock_bh ( & sc - > sc_resetlock ) ;
r = ath9k_hw_reset ( ah , hchan , fastcc ) ;
if ( r ) {
2009-09-07 15:52:26 +04:00
DPRINTF ( sc - > sc_ah , ATH_DBG_FATAL ,
2008-12-24 02:58:50 +03:00
" Unable to reset channel (%u Mhz) "
2009-05-15 17:29:20 +04:00
" reset status %d \n " ,
2008-12-24 02:58:50 +03:00
channel - > center_freq , r ) ;
spin_unlock_bh ( & sc - > sc_resetlock ) ;
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
spin_unlock_bh ( & sc - > sc_resetlock ) ;
sc - > sc_flags & = ~ SC_OP_FULL_RESET ;
if ( ath_startrecv ( sc ) ! = 0 ) {
2009-09-07 15:52:26 +04:00
DPRINTF ( sc - > sc_ah , ATH_DBG_FATAL ,
2008-12-24 02:58:50 +03:00
" 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
}
ath_cache_conf_rate ( sc , & hw - > conf ) ;
ath_update_txpow ( sc ) ;
2009-02-09 10:57:03 +03:00
ath9k_hw_set_interrupts ( ah , sc - > imask ) ;
2009-06-15 19:49:09 +04:00
ps_restore :
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
}
/*
* 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 .
*/
static void ath_ani_calibrate ( unsigned long data )
{
2009-02-20 12:43:28 +03:00
struct ath_softc * sc = ( struct ath_softc * ) data ;
struct ath_hw * ah = sc - > sc_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 ) ;
2009-02-20 12:43:28 +03:00
u32 cal_interval , short_cal_interval ;
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
/*
* don ' t calibrate when we ' re scanning .
* we are most likely not on our home channel .
*/
2009-06-24 17:26:41 +04:00
spin_lock ( & sc - > ani_lock ) ;
2009-03-03 07:46:45 +03:00
if ( sc - > sc_flags & SC_OP_SCANNING )
2009-02-20 12:43:28 +03:00
goto set_timer ;
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. */
2009-02-09 10:57:03 +03:00
if ( ( timestamp - sc - > ani . longcal_timer ) > = ATH_LONG_CALINTERVAL ) {
2008-11-24 09:37:55 +03:00
longcal = true ;
2009-09-07 15:52:26 +04:00
DPRINTF ( sc - > sc_ah , ATH_DBG_ANI , " longcal @%lu \n " , jiffies ) ;
2009-02-09 10:57:03 +03:00
sc - > 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 */
if ( ! sc - > ani . caldone ) {
2009-02-20 12:43:28 +03:00
if ( ( timestamp - sc - > ani . shortcal_timer ) > = short_cal_interval ) {
2008-11-24 09:37:55 +03:00
shortcal = true ;
2009-09-07 15:52:26 +04:00
DPRINTF ( sc - > sc_ah , ATH_DBG_ANI , " shortcal @%lu \n " , jiffies ) ;
2009-02-09 10:57:03 +03:00
sc - > ani . shortcal_timer = timestamp ;
sc - > ani . resetcal_timer = timestamp ;
2008-11-24 09:37:55 +03:00
}
} else {
2009-02-09 10:57:03 +03:00
if ( ( timestamp - sc - > ani . resetcal_timer ) > =
2008-11-24 09:37:55 +03:00
ATH_RESTART_CALINTERVAL ) {
2009-02-09 10:57:03 +03:00
sc - > ani . caldone = ath9k_hw_reset_calvalid ( ah ) ;
if ( sc - > ani . caldone )
sc - > ani . resetcal_timer = timestamp ;
2008-11-24 09:37:55 +03:00
}
}
/* Verify whether we must check ANI */
2009-02-20 12:43:28 +03:00
if ( ( timestamp - sc - > ani . checkani_timer ) > = ATH_ANI_POLLINTERVAL ) {
2008-11-24 09:37:55 +03:00
aniflag = true ;
2009-02-09 10:57:03 +03:00
sc - > 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 */
if ( aniflag )
2009-08-19 14:53:40 +04:00
ath9k_hw_ani_monitor ( ah , ah - > curchan ) ;
2008-11-24 09:37:55 +03:00
/* Perform calibration if necessary */
if ( longcal | | shortcal ) {
2009-04-13 20:26:48 +04:00
sc - > ani . caldone = ath9k_hw_calibrate ( ah , ah - > curchan ,
sc - > rx_chainmask , longcal ) ;
if ( longcal )
sc - > ani . noise_floor = ath9k_hw_getchan_noise ( ah ,
ah - > curchan ) ;
2009-09-07 15:52:26 +04:00
DPRINTF ( sc - > sc_ah , ATH_DBG_ANI , " calibrate chan %u/%x nf: %d \n " ,
2009-04-13 20:26:48 +04:00
ah - > curchan - > channel , ah - > curchan - > channelFlags ,
sc - > ani . noise_floor ) ;
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 :
2009-06-24 17:26:41 +04:00
spin_unlock ( & sc - > ani_lock ) ;
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 )
2008-12-02 16:07:54 +03:00
cal_interval = min ( cal_interval , ( u32 ) ATH_ANI_POLLINTERVAL ) ;
2009-02-09 10:57:03 +03:00
if ( ! sc - > 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-02-09 10:57:03 +03:00
mod_timer ( & sc - > ani . timer , jiffies + msecs_to_jiffies ( cal_interval ) ) ;
2008-11-24 09:37:55 +03:00
}
2009-04-13 20:26:46 +04:00
static void ath_start_ani ( struct ath_softc * sc )
{
unsigned long timestamp = jiffies_to_msecs ( jiffies ) ;
sc - > ani . longcal_timer = timestamp ;
sc - > ani . shortcal_timer = timestamp ;
sc - > ani . checkani_timer = timestamp ;
mod_timer ( & sc - > ani . timer ,
jiffies + msecs_to_jiffies ( ATH_ANI_POLLINTERVAL ) ) ;
}
2008-11-24 09:37:55 +03:00
/*
* Update tx / rx chainmask . For legacy association ,
* hard code chainmask to 1 x1 , for 11 n association , use
2009-01-02 13:05:46 +03:00
* the chainmask configuration , for bt coexistence , use
* the chainmask configuration even in legacy mode .
2008-11-24 09:37:55 +03:00
*/
2009-03-03 20:23:32 +03:00
void ath_update_chainmask ( struct ath_softc * sc , int is_ht )
2008-11-24 09:37:55 +03:00
{
2009-08-21 10:30:28 +04:00
if ( ( sc - > sc_flags & SC_OP_SCANNING ) | | is_ht | |
2009-08-26 19:38:48 +04:00
( sc - > btcoex_info . btcoex_scheme ! = ATH_BTCOEX_CFG_NONE ) ) {
2009-02-09 10:57:26 +03:00
sc - > tx_chainmask = sc - > sc_ah - > caps . tx_chainmask ;
sc - > rx_chainmask = sc - > sc_ah - > caps . rx_chainmask ;
2008-11-24 09:37:55 +03:00
} else {
2009-02-09 10:57:03 +03:00
sc - > tx_chainmask = 1 ;
sc - > rx_chainmask = 1 ;
2008-11-24 09:37:55 +03:00
}
2009-09-07 15:52:26 +04:00
DPRINTF ( sc - > sc_ah , ATH_DBG_CONFIG , " tx chmask: %d, rx chmask: %d \n " ,
2009-02-09 10:57:03 +03:00
sc - > tx_chainmask , sc - > rx_chainmask ) ;
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 ;
an = ( struct ath_node * ) sta - > drv_priv ;
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 ) ;
2009-07-15 04:17:07 +04:00
an - > last_rssi = ATH_RSSI_DUMMY_MARKER ;
2009-03-30 13:58:48 +04:00
}
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 ;
if ( sc - > sc_flags & SC_OP_TXAGGR )
ath_tx_node_cleanup ( sc , an ) ;
}
static void ath9k_tasklet ( unsigned long data )
{
struct ath_softc * sc = ( struct ath_softc * ) data ;
2009-02-09 10:57:03 +03:00
u32 status = sc - > intrstatus ;
2008-11-24 09:37:55 +03:00
2009-05-15 10:47:16 +04:00
ath9k_ps_wakeup ( sc ) ;
2008-11-24 09:37:55 +03:00
if ( status & ATH9K_INT_FATAL ) {
ath_reset ( sc , false ) ;
2009-05-15 10:47:16 +04:00
ath9k_ps_restore ( sc ) ;
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
2009-03-30 13:58:49 +04:00
if ( status & ( ATH9K_INT_RX | ATH9K_INT_RXEOL | ATH9K_INT_RXORN ) ) {
spin_lock_bh ( & sc - > rx . rxflushlock ) ;
ath_rx_tasklet ( sc , 0 ) ;
spin_unlock_bh ( & sc - > rx . rxflushlock ) ;
2008-11-24 09:37:55 +03:00
}
2009-03-30 13:58:49 +04:00
if ( status & ATH9K_INT_TX )
ath_tx_tasklet ( sc ) ;
2009-07-24 19:27:21 +04:00
if ( ( status & ATH9K_INT_TSFOOR ) & & sc - > ps_enabled ) {
2009-05-19 18:01:40 +04:00
/*
* TSF sync does not look correct ; remain awake to sync with
* the next Beacon .
*/
2009-09-07 15:52:26 +04:00
DPRINTF ( sc - > sc_ah , ATH_DBG_PS , " TSFOOR - Sync with next Beacon \n " ) ;
2009-05-20 22:59:08 +04:00
sc - > sc_flags | = SC_OP_WAIT_FOR_BEACON | SC_OP_BEACON_SYNC ;
2009-05-19 18:01:40 +04:00
}
2009-09-01 16:16:32 +04:00
if ( sc - > btcoex_info . btcoex_scheme = = ATH_BTCOEX_CFG_3WIRE )
if ( status & ATH9K_INT_GENTIMER )
ath_gen_timer_isr ( sc - > sc_ah ) ;
2008-11-24 09:37:55 +03:00
/* re-enable hardware interrupt */
2009-02-09 10:57:03 +03:00
ath9k_hw_set_interrupts ( sc - > sc_ah , sc - > imask ) ;
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 | \
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 ;
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 */
status & = sc - > 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 .
*/
if ( status & ( ATH9K_INT_FATAL | ATH9K_INT_RXORN ) )
goto chip_reset ;
if ( status & ATH9K_INT_SWBA )
tasklet_schedule ( & sc - > bcon_tasklet ) ;
if ( status & ATH9K_INT_TXURN )
ath9k_hw_updatetxtriglevel ( ah , true ) ;
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
*/
2009-03-30 13:58:49 +04:00
ath9k_hw_set_interrupts ( ah , 0 ) ;
/*
* Let the hal handle the event . We assume
* it will clear whatever condition caused
* the interrupt .
*/
2009-08-19 14:53:40 +04:00
ath9k_hw_procmibevent ( ah ) ;
2009-03-30 13:58:49 +04:00
ath9k_hw_set_interrupts ( ah , sc - > imask ) ;
}
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 ) {
2009-03-30 13:58:49 +04:00
/* Clear RxAbort bit so that we can
* receive frames */
ath9k_hw_setpower ( ah , ATH9K_PM_AWAKE ) ;
2009-05-15 10:47:16 +04:00
ath9k_hw_setrxabort ( sc - > sc_ah , 0 ) ;
2009-03-30 13:58:49 +04:00
sc - > sc_flags | = SC_OP_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 ) {
/* turn off every interrupt except SWBA */
2009-02-09 10:57:03 +03:00
ath9k_hw_set_interrupts ( ah , ( sc - > imask & ATH9K_INT_SWBA ) ) ;
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
}
2008-08-04 11:16:41 +04:00
static u32 ath_get_extchanmode ( struct ath_softc * sc ,
2008-11-24 09:38:35 +03:00
struct ieee80211_channel * chan ,
2008-12-12 09:27:43 +03:00
enum nl80211_channel_type channel_type )
2008-08-04 11:16:41 +04:00
{
u32 chanmode = 0 ;
switch ( chan - > band ) {
case IEEE80211_BAND_2GHZ :
2008-12-12 09:27:43 +03:00
switch ( channel_type ) {
case NL80211_CHAN_NO_HT :
case NL80211_CHAN_HT20 :
2008-08-04 11:16:41 +04:00
chanmode = CHANNEL_G_HT20 ;
2008-12-12 09:27:43 +03:00
break ;
case NL80211_CHAN_HT40PLUS :
2008-08-04 11:16:41 +04:00
chanmode = CHANNEL_G_HT40PLUS ;
2008-12-12 09:27:43 +03:00
break ;
case NL80211_CHAN_HT40MINUS :
2008-08-04 11:16:41 +04:00
chanmode = CHANNEL_G_HT40MINUS ;
2008-12-12 09:27:43 +03:00
break ;
}
2008-08-04 11:16:41 +04:00
break ;
case IEEE80211_BAND_5GHZ :
2008-12-12 09:27:43 +03:00
switch ( channel_type ) {
case NL80211_CHAN_NO_HT :
case NL80211_CHAN_HT20 :
2008-08-04 11:16:41 +04:00
chanmode = CHANNEL_A_HT20 ;
2008-12-12 09:27:43 +03:00
break ;
case NL80211_CHAN_HT40PLUS :
2008-08-04 11:16:41 +04:00
chanmode = CHANNEL_A_HT40PLUS ;
2008-12-12 09:27:43 +03:00
break ;
case NL80211_CHAN_HT40MINUS :
2008-08-04 11:16:41 +04:00
chanmode = CHANNEL_A_HT40MINUS ;
2008-12-12 09:27:43 +03:00
break ;
}
2008-08-04 11:16:41 +04:00
break ;
default :
break ;
}
return chanmode ;
}
2008-12-17 14:32:17 +03:00
static int ath_setkey_tkip ( struct ath_softc * sc , u16 keyix , const u8 * key ,
2009-02-26 12:18:46 +03:00
struct ath9k_keyval * hk , const u8 * addr ,
bool authenticator )
2008-08-04 11:16:41 +04:00
{
2008-12-17 14:32:17 +03:00
const u8 * key_rxmic ;
const u8 * key_txmic ;
2008-08-04 11:16:41 +04:00
2008-12-17 14:32:17 +03:00
key_txmic = key + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY ;
key_rxmic = key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY ;
2008-08-04 11:16:41 +04:00
if ( addr = = NULL ) {
2009-03-03 14:11:53 +03:00
/*
* Group key installation - only two key cache entries are used
* regardless of splitmic capability since group key is only
* used either for TX or RX .
*/
2009-02-26 12:18:46 +03:00
if ( authenticator ) {
memcpy ( hk - > kv_mic , key_txmic , sizeof ( hk - > kv_mic ) ) ;
memcpy ( hk - > kv_txmic , key_txmic , sizeof ( hk - > kv_mic ) ) ;
} else {
memcpy ( hk - > kv_mic , key_rxmic , sizeof ( hk - > kv_mic ) ) ;
memcpy ( hk - > kv_txmic , key_rxmic , sizeof ( hk - > kv_mic ) ) ;
}
2009-03-03 14:11:53 +03:00
return ath9k_hw_set_keycache_entry ( sc - > sc_ah , keyix , hk , addr ) ;
2008-08-04 11:16:41 +04:00
}
2009-02-09 10:57:03 +03:00
if ( ! sc - > splitmic ) {
2009-03-03 14:11:53 +03:00
/* TX and RX keys share the same key cache entry. */
2008-08-04 11:16:41 +04:00
memcpy ( hk - > kv_mic , key_rxmic , sizeof ( hk - > kv_mic ) ) ;
memcpy ( hk - > kv_txmic , key_txmic , sizeof ( hk - > kv_txmic ) ) ;
2009-03-03 14:11:53 +03:00
return ath9k_hw_set_keycache_entry ( sc - > sc_ah , keyix , hk , addr ) ;
2008-08-04 11:16:41 +04:00
}
2009-03-03 14:11:53 +03:00
/* Separate key cache entries for TX and RX */
/* TX key goes at first index, RX key at +32. */
2008-08-04 11:16:41 +04:00
memcpy ( hk - > kv_mic , key_txmic , sizeof ( hk - > kv_mic ) ) ;
2009-03-03 14:11:53 +03:00
if ( ! ath9k_hw_set_keycache_entry ( sc - > sc_ah , keyix , hk , NULL ) ) {
/* TX MIC entry failed. No need to proceed further */
2009-09-07 15:52:26 +04:00
DPRINTF ( sc - > sc_ah , ATH_DBG_FATAL ,
2008-11-28 19:48:05 +03:00
" Setting TX MIC Key Failed \n " ) ;
2008-08-04 11:16:41 +04:00
return 0 ;
}
memcpy ( hk - > kv_mic , key_rxmic , sizeof ( hk - > kv_mic ) ) ;
/* XXX delete tx key on failure? */
2009-03-03 14:11:53 +03:00
return ath9k_hw_set_keycache_entry ( sc - > sc_ah , keyix + 32 , hk , addr ) ;
2008-12-17 14:32:17 +03:00
}
static int ath_reserve_key_cache_slot_tkip ( struct ath_softc * sc )
{
int i ;
2009-02-09 10:57:03 +03:00
for ( i = IEEE80211_WEP_NKID ; i < sc - > keymax / 2 ; i + + ) {
if ( test_bit ( i , sc - > keymap ) | |
test_bit ( i + 64 , sc - > keymap ) )
2008-12-17 14:32:17 +03:00
continue ; /* At least one part of TKIP key allocated */
2009-02-09 10:57:03 +03:00
if ( sc - > splitmic & &
( test_bit ( i + 32 , sc - > keymap ) | |
test_bit ( i + 64 + 32 , sc - > keymap ) ) )
2008-12-17 14:32:17 +03:00
continue ; /* At least one part of TKIP key allocated */
/* Found a free slot for a TKIP key */
return i ;
}
return - 1 ;
}
static int ath_reserve_key_cache_slot ( struct ath_softc * sc )
{
int i ;
/* First, try to find slots that would not be available for TKIP. */
2009-02-09 10:57:03 +03:00
if ( sc - > splitmic ) {
for ( i = IEEE80211_WEP_NKID ; i < sc - > keymax / 4 ; i + + ) {
if ( ! test_bit ( i , sc - > keymap ) & &
( test_bit ( i + 32 , sc - > keymap ) | |
test_bit ( i + 64 , sc - > keymap ) | |
test_bit ( i + 64 + 32 , sc - > keymap ) ) )
2008-12-17 14:32:17 +03:00
return i ;
2009-02-09 10:57:03 +03:00
if ( ! test_bit ( i + 32 , sc - > keymap ) & &
( test_bit ( i , sc - > keymap ) | |
test_bit ( i + 64 , sc - > keymap ) | |
test_bit ( i + 64 + 32 , sc - > keymap ) ) )
2008-12-17 14:32:17 +03:00
return i + 32 ;
2009-02-09 10:57:03 +03:00
if ( ! test_bit ( i + 64 , sc - > keymap ) & &
( test_bit ( i , sc - > keymap ) | |
test_bit ( i + 32 , sc - > keymap ) | |
test_bit ( i + 64 + 32 , sc - > keymap ) ) )
2008-12-18 15:31:10 +03:00
return i + 64 ;
2009-02-09 10:57:03 +03:00
if ( ! test_bit ( i + 64 + 32 , sc - > keymap ) & &
( test_bit ( i , sc - > keymap ) | |
test_bit ( i + 32 , sc - > keymap ) | |
test_bit ( i + 64 , sc - > keymap ) ) )
2008-12-18 15:31:10 +03:00
return i + 64 + 32 ;
2008-12-17 14:32:17 +03:00
}
} else {
2009-02-09 10:57:03 +03:00
for ( i = IEEE80211_WEP_NKID ; i < sc - > keymax / 2 ; i + + ) {
if ( ! test_bit ( i , sc - > keymap ) & &
test_bit ( i + 64 , sc - > keymap ) )
2008-12-17 14:32:17 +03:00
return i ;
2009-02-09 10:57:03 +03:00
if ( test_bit ( i , sc - > keymap ) & &
! test_bit ( i + 64 , sc - > keymap ) )
2008-12-17 14:32:17 +03:00
return i + 64 ;
}
}
/* No partially used TKIP slots, pick any available slot */
2009-02-09 10:57:03 +03:00
for ( i = IEEE80211_WEP_NKID ; i < sc - > keymax ; i + + ) {
2008-12-18 15:33:00 +03:00
/* Do not allow slots that could be needed for TKIP group keys
* to be used . This limitation could be removed if we know that
* TKIP will not be used . */
if ( i > = 64 & & i < 64 + IEEE80211_WEP_NKID )
continue ;
2009-02-09 10:57:03 +03:00
if ( sc - > splitmic ) {
2008-12-18 15:33:00 +03:00
if ( i > = 32 & & i < 32 + IEEE80211_WEP_NKID )
continue ;
if ( i > = 64 + 32 & & i < 64 + 32 + IEEE80211_WEP_NKID )
continue ;
}
2009-02-09 10:57:03 +03:00
if ( ! test_bit ( i , sc - > keymap ) )
2008-12-17 14:32:17 +03:00
return i ; /* Found a free slot for a key */
}
/* No free slot found */
return - 1 ;
2008-08-04 11:16:41 +04:00
}
static int ath_key_config ( struct ath_softc * sc ,
2009-02-26 12:18:46 +03:00
struct ieee80211_vif * vif ,
2008-12-29 14:55:09 +03:00
struct ieee80211_sta * sta ,
2008-08-04 11:16:41 +04:00
struct ieee80211_key_conf * key )
{
struct ath9k_keyval hk ;
const u8 * mac = NULL ;
int ret = 0 ;
2008-12-17 14:32:17 +03:00
int idx ;
2008-08-04 11:16:41 +04:00
memset ( & hk , 0 , sizeof ( hk ) ) ;
switch ( key - > alg ) {
case ALG_WEP :
hk . kv_type = ATH9K_CIPHER_WEP ;
break ;
case ALG_TKIP :
hk . kv_type = ATH9K_CIPHER_TKIP ;
break ;
case ALG_CCMP :
hk . kv_type = ATH9K_CIPHER_AES_CCM ;
break ;
default :
2009-01-08 14:32:12 +03:00
return - EOPNOTSUPP ;
2008-08-04 11:16:41 +04:00
}
2008-12-17 14:32:17 +03:00
hk . kv_len = key - > keylen ;
2008-08-04 11:16:41 +04:00
memcpy ( hk . kv_val , key - > key , key - > keylen ) ;
2008-12-17 14:32:17 +03:00
if ( ! ( key - > flags & IEEE80211_KEY_FLAG_PAIRWISE ) ) {
/* For now, use the default keys for broadcast keys. This may
* need to change with virtual interfaces . */
idx = key - > keyidx ;
} else if ( key - > keyidx ) {
2008-12-29 14:55:09 +03:00
if ( WARN_ON ( ! sta ) )
return - EOPNOTSUPP ;
mac = sta - > addr ;
2008-12-17 14:32:17 +03:00
if ( vif - > type ! = NL80211_IFTYPE_AP ) {
/* Only keyidx 0 should be used with unicast key, but
* allow this for client mode for now . */
idx = key - > keyidx ;
} else
return - EIO ;
2008-08-04 11:16:41 +04:00
} else {
2008-12-29 14:55:09 +03:00
if ( WARN_ON ( ! sta ) )
return - EOPNOTSUPP ;
mac = sta - > addr ;
2008-12-17 14:32:17 +03:00
if ( key - > alg = = ALG_TKIP )
idx = ath_reserve_key_cache_slot_tkip ( sc ) ;
else
idx = ath_reserve_key_cache_slot ( sc ) ;
if ( idx < 0 )
2009-01-08 14:32:12 +03:00
return - ENOSPC ; /* no free key cache entries */
2008-08-04 11:16:41 +04:00
}
if ( key - > alg = = ALG_TKIP )
2009-02-26 12:18:46 +03:00
ret = ath_setkey_tkip ( sc , idx , key - > key , & hk , mac ,
vif - > type = = NL80211_IFTYPE_AP ) ;
2008-08-04 11:16:41 +04:00
else
2009-03-03 14:11:53 +03:00
ret = ath9k_hw_set_keycache_entry ( sc - > sc_ah , idx , & hk , mac ) ;
2008-08-04 11:16:41 +04:00
if ( ! ret )
return - EIO ;
2009-02-09 10:57:03 +03:00
set_bit ( idx , sc - > keymap ) ;
2008-12-17 14:32:17 +03:00
if ( key - > alg = = ALG_TKIP ) {
2009-02-09 10:57:03 +03:00
set_bit ( idx + 64 , sc - > keymap ) ;
if ( sc - > splitmic ) {
set_bit ( idx + 32 , sc - > keymap ) ;
set_bit ( idx + 64 + 32 , sc - > keymap ) ;
2008-12-17 14:32:17 +03:00
}
}
return idx ;
2008-08-04 11:16:41 +04:00
}
static void ath_key_delete ( struct ath_softc * sc , struct ieee80211_key_conf * key )
{
2008-12-17 14:32:17 +03:00
ath9k_hw_keyreset ( sc - > sc_ah , key - > hw_key_idx ) ;
if ( key - > hw_key_idx < IEEE80211_WEP_NKID )
return ;
2009-02-09 10:57:03 +03:00
clear_bit ( key - > hw_key_idx , sc - > keymap ) ;
2008-12-17 14:32:17 +03:00
if ( key - > alg ! = ALG_TKIP )
return ;
2008-08-04 11:16:41 +04:00
2009-02-09 10:57:03 +03:00
clear_bit ( key - > hw_key_idx + 64 , sc - > keymap ) ;
if ( sc - > splitmic ) {
clear_bit ( key - > hw_key_idx + 32 , sc - > keymap ) ;
clear_bit ( key - > hw_key_idx + 64 + 32 , sc - > keymap ) ;
2008-12-17 14:32:17 +03:00
}
2008-08-04 11:16:41 +04:00
}
2009-01-23 08:50:44 +03:00
static void setup_ht_cap ( struct ath_softc * sc ,
struct ieee80211_sta_ht_cap * ht_info )
2008-08-04 11:16:41 +04:00
{
2009-06-24 17:26:42 +04:00
u8 tx_streams , rx_streams ;
2008-08-04 11:16:41 +04:00
2008-10-09 14:13:49 +04:00
ht_info - > ht_supported = true ;
ht_info - > cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
IEEE80211_HT_CAP_SM_PS |
IEEE80211_HT_CAP_SGI_40 |
IEEE80211_HT_CAP_DSSSCCK40 ;
2008-08-04 11:16:41 +04:00
2009-07-23 14:02:34 +04:00
ht_info - > ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K ;
ht_info - > ampdu_density = IEEE80211_HT_MPDU_DENSITY_8 ;
2009-01-23 08:50:44 +03:00
2008-10-09 14:13:49 +04:00
/* set up supported mcs set */
memset ( & ht_info - > mcs , 0 , sizeof ( ht_info - > mcs ) ) ;
2009-06-24 17:26:42 +04:00
tx_streams = ! ( sc - > tx_chainmask & ( sc - > tx_chainmask - 1 ) ) ? 1 : 2 ;
rx_streams = ! ( sc - > rx_chainmask & ( sc - > rx_chainmask - 1 ) ) ? 1 : 2 ;
if ( tx_streams ! = rx_streams ) {
2009-09-07 15:52:26 +04:00
DPRINTF ( sc - > sc_ah , ATH_DBG_CONFIG , " TX streams %d, RX streams: %d \n " ,
2009-06-24 17:26:42 +04:00
tx_streams , rx_streams ) ;
ht_info - > mcs . tx_params | = IEEE80211_HT_MCS_TX_RX_DIFF ;
ht_info - > mcs . tx_params | = ( ( tx_streams - 1 ) < <
IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT ) ;
}
2009-01-23 08:50:44 +03:00
2009-06-24 17:26:42 +04:00
ht_info - > mcs . rx_mask [ 0 ] = 0xff ;
if ( rx_streams > = 2 )
2009-01-23 08:50:44 +03:00
ht_info - > mcs . rx_mask [ 1 ] = 0xff ;
2009-06-24 17:26:42 +04:00
ht_info - > mcs . tx_params | = IEEE80211_HT_MCS_TX_DEFINED ;
2008-08-04 11:16:41 +04:00
}
2008-09-10 17:19:27 +04:00
static void ath9k_bss_assoc_info ( struct ath_softc * sc ,
2008-10-29 07:46:06 +03:00
struct ieee80211_vif * vif ,
2008-09-10 17:19:27 +04:00
struct ieee80211_bss_conf * bss_conf )
2008-08-04 11:16:41 +04:00
{
2008-09-10 17:19:27 +04:00
if ( bss_conf - > assoc ) {
2009-09-07 15:52:26 +04:00
DPRINTF ( sc - > sc_ah , ATH_DBG_CONFIG , " Bss Info ASSOC %d, bssid: %pM \n " ,
2009-02-09 10:57:03 +03:00
bss_conf - > aid , sc - > curbssid ) ;
2008-08-04 11:16:41 +04:00
2008-09-10 17:19:27 +04:00
/* New association, store aid */
2009-06-24 17:26:39 +04:00
sc - > curaid = bss_conf - > aid ;
ath9k_hw_write_associd ( sc ) ;
/*
* 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 - > sc_flags | = SC_OP_BEACON_SYNC ;
2008-08-04 11:16:41 +04:00
2008-09-10 17:19:27 +04:00
/* Configure the beacon */
2009-03-03 20:23:26 +03:00
ath_beacon_config ( sc , vif ) ;
2008-08-04 11:16:41 +04:00
2008-09-10 17:19:27 +04:00
/* Reset rssi stats */
2009-08-19 14:53:40 +04:00
sc - > sc_ah - > stats . avgbrssi = ATH_RSSI_DUMMY_MARKER ;
2008-08-04 11:16:41 +04:00
2009-04-13 20:26:46 +04:00
ath_start_ani ( sc ) ;
2008-09-10 17:19:27 +04:00
} else {
2009-09-07 15:52:26 +04:00
DPRINTF ( sc - > sc_ah , ATH_DBG_CONFIG , " Bss Info DISASSOC \n " ) ;
2009-02-09 10:57:03 +03:00
sc - > curaid = 0 ;
2009-06-24 17:26:40 +04:00
/* Stop ANI */
del_timer_sync ( & sc - > ani . timer ) ;
2008-08-04 11:16:41 +04:00
}
2008-09-10 17:19:27 +04:00
}
2008-08-04 11:16:41 +04:00
2008-09-10 17:19:27 +04:00
/********************************/
/* LED functions */
/********************************/
2008-08-04 11:16:41 +04:00
2009-01-29 15:22:19 +03:00
static void ath_led_blink_work ( struct work_struct * work )
{
struct ath_softc * sc = container_of ( work , struct ath_softc ,
ath_led_blink_work . work ) ;
if ( ! ( sc - > sc_flags & SC_OP_LED_ASSOCIATED ) )
return ;
2009-03-14 17:29:41 +03:00
if ( ( sc - > led_on_duration = = ATH_LED_ON_DURATION_IDLE ) | |
( sc - > led_off_duration = = ATH_LED_OFF_DURATION_IDLE ) )
2009-08-14 10:00:52 +04:00
ath9k_hw_set_gpio ( sc - > sc_ah , sc - > sc_ah - > led_pin , 0 ) ;
2009-03-14 17:29:41 +03:00
else
2009-08-14 10:00:52 +04:00
ath9k_hw_set_gpio ( sc - > sc_ah , sc - > sc_ah - > led_pin ,
2009-03-14 17:29:41 +03:00
( sc - > sc_flags & SC_OP_LED_ON ) ? 1 : 0 ) ;
2009-01-29 15:22:19 +03:00
2009-07-30 04:08:07 +04:00
ieee80211_queue_delayed_work ( sc - > hw ,
& sc - > ath_led_blink_work ,
( sc - > sc_flags & SC_OP_LED_ON ) ?
msecs_to_jiffies ( sc - > led_off_duration ) :
msecs_to_jiffies ( sc - > led_on_duration ) ) ;
2009-01-29 15:22:19 +03:00
2009-03-14 17:29:41 +03:00
sc - > led_on_duration = sc - > led_on_cnt ?
max ( ( ATH_LED_ON_DURATION_IDLE - sc - > led_on_cnt ) , 25 ) :
ATH_LED_ON_DURATION_IDLE ;
sc - > led_off_duration = sc - > led_off_cnt ?
max ( ( ATH_LED_OFF_DURATION_IDLE - sc - > led_off_cnt ) , 10 ) :
ATH_LED_OFF_DURATION_IDLE ;
2009-01-29 15:22:19 +03:00
sc - > led_on_cnt = sc - > led_off_cnt = 0 ;
if ( sc - > sc_flags & SC_OP_LED_ON )
sc - > sc_flags & = ~ SC_OP_LED_ON ;
else
sc - > sc_flags | = SC_OP_LED_ON ;
}
2008-09-10 17:19:27 +04:00
static void ath_led_brightness ( struct led_classdev * led_cdev ,
enum led_brightness brightness )
{
struct ath_led * led = container_of ( led_cdev , struct ath_led , led_cdev ) ;
struct ath_softc * sc = led - > sc ;
2008-08-04 11:16:41 +04:00
2008-09-10 17:19:27 +04:00
switch ( brightness ) {
case LED_OFF :
if ( led - > led_type = = ATH_LED_ASSOC | |
2009-01-29 15:22:19 +03:00
led - > led_type = = ATH_LED_RADIO ) {
2009-08-14 10:00:52 +04:00
ath9k_hw_set_gpio ( sc - > sc_ah , sc - > sc_ah - > led_pin ,
2009-01-29 15:22:19 +03:00
( led - > led_type = = ATH_LED_RADIO ) ) ;
2008-09-10 17:19:27 +04:00
sc - > sc_flags & = ~ SC_OP_LED_ASSOCIATED ;
2009-01-29 15:22:19 +03:00
if ( led - > led_type = = ATH_LED_RADIO )
sc - > sc_flags & = ~ SC_OP_LED_ON ;
} else {
sc - > led_off_cnt + + ;
}
2008-09-10 17:19:27 +04:00
break ;
case LED_FULL :
2009-01-29 15:22:19 +03:00
if ( led - > led_type = = ATH_LED_ASSOC ) {
2008-09-10 17:19:27 +04:00
sc - > sc_flags | = SC_OP_LED_ASSOCIATED ;
2009-07-30 04:08:07 +04:00
ieee80211_queue_delayed_work ( sc - > hw ,
& sc - > ath_led_blink_work , 0 ) ;
2009-01-29 15:22:19 +03:00
} else if ( led - > led_type = = ATH_LED_RADIO ) {
2009-08-14 10:00:52 +04:00
ath9k_hw_set_gpio ( sc - > sc_ah , sc - > sc_ah - > led_pin , 0 ) ;
2009-01-29 15:22:19 +03:00
sc - > sc_flags | = SC_OP_LED_ON ;
} else {
sc - > led_on_cnt + + ;
}
2008-09-10 17:19:27 +04:00
break ;
default :
break ;
2008-08-04 11:16:41 +04:00
}
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 ath_register_led ( struct ath_softc * sc , struct ath_led * led ,
char * trigger )
{
int ret ;
2008-08-04 11:16:41 +04:00
2008-09-10 17:19:27 +04:00
led - > sc = sc ;
led - > led_cdev . name = led - > name ;
led - > led_cdev . default_trigger = trigger ;
led - > led_cdev . brightness_set = ath_led_brightness ;
2008-08-04 11:16:41 +04:00
2008-09-10 17:19:27 +04:00
ret = led_classdev_register ( wiphy_dev ( sc - > hw - > wiphy ) , & led - > led_cdev ) ;
if ( ret )
2009-09-07 15:52:26 +04:00
DPRINTF ( sc - > sc_ah , ATH_DBG_FATAL ,
2008-09-10 17:19:27 +04:00
" Failed to register led:%s " , led - > name ) ;
else
led - > registered = 1 ;
return ret ;
}
2008-08-04 11:16:41 +04:00
2008-09-10 17:19:27 +04:00
static void ath_unregister_led ( struct ath_led * led )
{
if ( led - > registered ) {
led_classdev_unregister ( & led - > led_cdev ) ;
led - > registered = 0 ;
2008-08-04 11:16:41 +04:00
}
}
2008-09-10 17:19:27 +04:00
static void ath_deinit_leds ( struct ath_softc * sc )
2008-08-04 11:16:41 +04:00
{
2008-09-10 17:19:27 +04:00
ath_unregister_led ( & sc - > assoc_led ) ;
sc - > sc_flags & = ~ SC_OP_LED_ASSOCIATED ;
ath_unregister_led ( & sc - > tx_led ) ;
ath_unregister_led ( & sc - > rx_led ) ;
ath_unregister_led ( & sc - > radio_led ) ;
2009-08-14 10:00:52 +04:00
ath9k_hw_set_gpio ( sc - > sc_ah , sc - > sc_ah - > led_pin , 1 ) ;
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 void ath_init_leds ( struct ath_softc * sc )
{
char * trigger ;
int ret ;
2008-08-04 11:16:41 +04:00
2009-08-14 10:00:52 +04:00
if ( AR_SREV_9287 ( sc - > sc_ah ) )
sc - > sc_ah - > led_pin = ATH_LED_PIN_9287 ;
else
sc - > sc_ah - > led_pin = ATH_LED_PIN_DEF ;
2008-09-10 17:19:27 +04:00
/* Configure gpio 1 for output */
2009-08-14 10:00:52 +04:00
ath9k_hw_cfg_output ( sc - > sc_ah , sc - > sc_ah - > led_pin ,
2008-09-10 17:19:27 +04:00
AR_GPIO_OUTPUT_MUX_AS_OUTPUT ) ;
/* LED off, active low */
2009-08-14 10:00:52 +04:00
ath9k_hw_set_gpio ( sc - > sc_ah , sc - > sc_ah - > led_pin , 1 ) ;
2008-08-11 12:33:13 +04:00
2009-01-29 15:22:19 +03:00
INIT_DELAYED_WORK ( & sc - > ath_led_blink_work , ath_led_blink_work ) ;
2008-09-10 17:19:27 +04:00
trigger = ieee80211_get_radio_led_name ( sc - > hw ) ;
snprintf ( sc - > radio_led . name , sizeof ( sc - > radio_led . name ) ,
2009-01-31 17:52:09 +03:00
" ath9k-%s::radio " , wiphy_name ( sc - > hw - > wiphy ) ) ;
2008-09-10 17:19:27 +04:00
ret = ath_register_led ( sc , & sc - > radio_led , trigger ) ;
sc - > radio_led . led_type = ATH_LED_RADIO ;
if ( ret )
goto fail ;
2008-08-11 12:33:13 +04:00
2008-09-10 17:19:27 +04:00
trigger = ieee80211_get_assoc_led_name ( sc - > hw ) ;
snprintf ( sc - > assoc_led . name , sizeof ( sc - > assoc_led . name ) ,
2009-01-31 17:52:09 +03:00
" ath9k-%s::assoc " , wiphy_name ( sc - > hw - > wiphy ) ) ;
2008-09-10 17:19:27 +04:00
ret = ath_register_led ( sc , & sc - > assoc_led , trigger ) ;
sc - > assoc_led . led_type = ATH_LED_ASSOC ;
if ( ret )
goto fail ;
2008-08-04 11:16:41 +04:00
2008-09-10 17:19:27 +04:00
trigger = ieee80211_get_tx_led_name ( sc - > hw ) ;
snprintf ( sc - > tx_led . name , sizeof ( sc - > tx_led . name ) ,
2009-01-31 17:52:09 +03:00
" ath9k-%s::tx " , wiphy_name ( sc - > hw - > wiphy ) ) ;
2008-09-10 17:19:27 +04:00
ret = ath_register_led ( sc , & sc - > tx_led , trigger ) ;
sc - > tx_led . led_type = ATH_LED_TX ;
if ( ret )
goto fail ;
2008-08-04 11:16:41 +04:00
2008-09-10 17:19:27 +04:00
trigger = ieee80211_get_rx_led_name ( sc - > hw ) ;
snprintf ( sc - > rx_led . name , sizeof ( sc - > rx_led . name ) ,
2009-01-31 17:52:09 +03:00
" ath9k-%s::rx " , wiphy_name ( sc - > hw - > wiphy ) ) ;
2008-09-10 17:19:27 +04:00
ret = ath_register_led ( sc , & sc - > rx_led , trigger ) ;
sc - > rx_led . led_type = ATH_LED_RX ;
if ( ret )
goto fail ;
2008-08-04 11:16:41 +04:00
2008-09-10 17:19:27 +04:00
return ;
fail :
2009-07-27 22:53:03 +04:00
cancel_delayed_work_sync ( & sc - > ath_led_blink_work ) ;
2008-09-10 17:19:27 +04:00
ath_deinit_leds ( sc ) ;
2008-08-04 11:16:41 +04:00
}
2009-03-03 20:23:37 +03:00
void ath_radio_enable ( struct ath_softc * sc )
2008-09-10 17:20:17 +04:00
{
2009-02-09 10:57:12 +03:00
struct ath_hw * ah = sc - > sc_ah ;
2008-12-24 02:58:40 +03:00
struct ieee80211_channel * channel = sc - > hw - > conf . channel ;
int r ;
2008-09-10 17:20:17 +04:00
2009-01-20 08:47:08 +03:00
ath9k_ps_wakeup ( sc ) ;
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 )
ah - > curchan = ath_get_curchannel ( sc , sc - > hw ) ;
2009-04-13 20:26:25 +04:00
spin_lock_bh ( & sc - > sc_resetlock ) ;
2009-02-09 10:57:26 +03:00
r = ath9k_hw_reset ( ah , ah - > curchan , false ) ;
2008-12-24 02:58:40 +03:00
if ( r ) {
2009-09-07 15:52:26 +04:00
DPRINTF ( sc - > sc_ah , ATH_DBG_FATAL ,
2008-12-24 02:58:40 +03:00
" Unable to reset channel %u (%uMhz) " ,
2009-05-15 17:29:20 +04:00
" reset status %d \n " ,
2008-12-24 02:58:40 +03:00
channel - > center_freq , r ) ;
2008-09-10 17:20:17 +04:00
}
spin_unlock_bh ( & sc - > sc_resetlock ) ;
ath_update_txpow ( sc ) ;
if ( ath_startrecv ( sc ) ! = 0 ) {
2009-09-07 15:52:26 +04:00
DPRINTF ( sc - > sc_ah , ATH_DBG_FATAL ,
2008-11-28 19:48:05 +03:00
" Unable to restart recv logic \n " ) ;
2008-09-10 17:20:17 +04:00
return ;
}
if ( sc - > sc_flags & SC_OP_BEACONS )
2009-03-03 20:23:26 +03:00
ath_beacon_config ( sc , NULL ) ; /* restart beacons */
2008-09-10 17:20:17 +04:00
/* Re-Enable interrupts */
2009-02-09 10:57:03 +03:00
ath9k_hw_set_interrupts ( ah , sc - > 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
ieee80211_wake_queues ( sc - > hw ) ;
2009-01-20 08:47:08 +03:00
ath9k_ps_restore ( sc ) ;
2008-09-10 17:20:17 +04:00
}
2009-03-03 20:23:37 +03:00
void ath_radio_disable ( struct ath_softc * sc )
2008-09-10 17:20:17 +04:00
{
2009-02-09 10:57:12 +03:00
struct ath_hw * ah = sc - > sc_ah ;
2008-12-24 02:58:40 +03:00
struct ieee80211_channel * channel = sc - > hw - > conf . channel ;
int r ;
2008-09-10 17:20:17 +04:00
2009-01-20 08:47:08 +03:00
ath9k_ps_wakeup ( sc ) ;
2008-09-10 17:20:17 +04:00
ieee80211_stop_queues ( sc - > hw ) ;
/* Disable LED */
2009-08-14 10:00:52 +04:00
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 */
ath9k_hw_set_interrupts ( ah , 0 ) ;
2009-01-16 19:08:47 +03:00
ath_drain_all_txq ( sc , false ) ; /* clear pending tx frames */
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 )
ah - > curchan = ath_get_curchannel ( sc , sc - > hw ) ;
2008-09-10 17:20:17 +04:00
spin_lock_bh ( & sc - > sc_resetlock ) ;
2009-02-09 10:57:26 +03:00
r = ath9k_hw_reset ( ah , ah - > curchan , false ) ;
2008-12-24 02:58:40 +03:00
if ( r ) {
2009-09-07 15:52:26 +04:00
DPRINTF ( sc - > sc_ah , ATH_DBG_FATAL ,
2008-11-28 19:48:05 +03:00
" Unable to reset channel %u (%uMhz) "
2009-05-15 17:29:20 +04:00
" reset status %d \n " ,
2008-12-24 02:58:40 +03:00
channel - > center_freq , r ) ;
2008-09-10 17:20:17 +04:00
}
spin_unlock_bh ( & sc - > sc_resetlock ) ;
ath9k_hw_phy_disable ( ah ) ;
2009-09-17 07:54:58 +04:00
ath9k_hw_configpcipowersave ( ah , 1 , 1 ) ;
2009-01-20 08:47:08 +03:00
ath9k_ps_restore ( sc ) ;
2009-06-17 22:53:21 +04:00
ath9k_hw_setpower ( ah , ATH9K_PM_FULL_SLEEP ) ;
2008-09-10 17:20:17 +04:00
}
2009-03-06 13:17:55 +03:00
/*******************/
/* Rfkill */
/*******************/
2008-09-10 17:20:17 +04:00
static bool ath_is_rfkill_set ( struct ath_softc * sc )
{
2009-02-09 10:57:12 +03:00
struct ath_hw * ah = sc - > sc_ah ;
2008-09-10 17:20:17 +04:00
2009-02-09 10:57:26 +03:00
return ath9k_hw_gpio_get ( ah , ah - > rfkill_gpio ) = =
ah - > rfkill_polarity ;
2008-09-10 17:20:17 +04:00
}
2009-06-13 13:20:26 +04:00
static void ath9k_rfkill_poll_state ( struct ieee80211_hw * hw )
2008-09-10 17:20:17 +04:00
{
2009-06-13 13:20:26 +04:00
struct ath_wiphy * aphy = hw - > priv ;
struct ath_softc * sc = aphy - > sc ;
2009-06-02 15:01:37 +04:00
bool blocked = ! ! ath_is_rfkill_set ( sc ) ;
2008-09-10 17:20:17 +04:00
2009-06-13 13:20:26 +04:00
wiphy_rfkill_set_hw_state ( hw - > wiphy , blocked ) ;
2008-09-10 17:20:17 +04:00
}
2009-06-13 13:20:26 +04:00
static void ath_start_rfkill_poll ( struct ath_softc * sc )
2008-09-10 17:20:17 +04:00
{
2009-06-13 13:20:26 +04:00
struct ath_hw * ah = sc - > sc_ah ;
2008-10-29 07:47:13 +03:00
2009-06-13 13:20:26 +04:00
if ( ah - > caps . hw_caps & ATH9K_HW_CAP_RFSILENT )
wiphy_rfkill_start_polling ( sc - > hw - > wiphy ) ;
2008-10-29 07:47:13 +03:00
}
2008-09-10 17:20:17 +04:00
2009-01-14 22:17:06 +03:00
void ath_cleanup ( struct ath_softc * sc )
2009-01-14 22:17:05 +03:00
{
ath_detach ( sc ) ;
free_irq ( sc - > irq , sc ) ;
ath_bus_cleanup ( sc ) ;
2009-03-03 20:23:29 +03:00
kfree ( sc - > sec_wiphy ) ;
2009-01-14 22:17:05 +03:00
ieee80211_free_hw ( sc - > hw ) ;
}
2009-01-14 22:17:06 +03:00
void ath_detach ( struct ath_softc * sc )
2008-08-04 11:16:41 +04:00
{
2008-09-10 17:19:27 +04:00
struct ieee80211_hw * hw = sc - > hw ;
2009-09-07 15:52:26 +04:00
struct ath_hw * ah = sc - > sc_ah ;
2008-10-29 07:47:13 +03:00
int i = 0 ;
2008-08-04 11:16:41 +04:00
2009-01-20 08:47:08 +03:00
ath9k_ps_wakeup ( sc ) ;
2009-09-07 15:52:26 +04:00
dev_dbg ( sc - > dev , " Detach ATH hw \n " ) ;
2008-08-04 11:16:41 +04:00
2009-07-27 22:53:03 +04:00
ath_deinit_leds ( sc ) ;
2009-09-23 12:19:12 +04:00
wiphy_rfkill_stop_polling ( sc - > hw - > wiphy ) ;
2009-07-27 22:53:03 +04:00
2009-03-03 20:23:29 +03:00
for ( i = 0 ; i < sc - > num_sec_wiphy ; i + + ) {
struct ath_wiphy * aphy = sc - > sec_wiphy [ i ] ;
if ( aphy = = NULL )
continue ;
sc - > sec_wiphy [ i ] = NULL ;
ieee80211_unregister_hw ( aphy - > hw ) ;
ieee80211_free_hw ( aphy - > hw ) ;
}
2008-11-17 22:49:56 +03:00
ieee80211_unregister_hw ( hw ) ;
2008-09-10 17:19:27 +04:00
ath_rx_cleanup ( sc ) ;
ath_tx_cleanup ( sc ) ;
2008-08-04 11:16:41 +04:00
2008-10-29 07:47:13 +03:00
tasklet_kill ( & sc - > intr_tq ) ;
tasklet_kill ( & sc - > bcon_tasklet ) ;
2008-08-04 11:16:41 +04:00
2008-10-29 07:47:13 +03:00
if ( ! ( sc - > sc_flags & SC_OP_INVALID ) )
2009-09-07 15:52:26 +04:00
ath9k_hw_setpower ( ah , ATH9K_PM_AWAKE ) ;
2008-09-10 17:19:27 +04:00
2008-10-29 07:47:13 +03:00
/* cleanup tx queues */
for ( i = 0 ; i < ATH9K_NUM_TX_QUEUES ; i + + )
if ( ATH_TXQ_SETUP ( sc , i ) )
2008-12-07 19:14:03 +03:00
ath_tx_cleanupq ( sc , & sc - > tx . txq [ i ] ) ;
2008-10-29 07:47:13 +03:00
2009-08-26 19:38:50 +04:00
if ( ( sc - > btcoex_info . no_stomp_timer ) & &
sc - > btcoex_info . btcoex_scheme = = ATH_BTCOEX_CFG_3WIRE )
2009-09-07 15:52:26 +04:00
ath_gen_timer_free ( ah , sc - > btcoex_info . no_stomp_timer ) ;
2009-08-26 19:38:50 +04:00
2009-09-07 15:52:26 +04:00
ath9k_hw_detach ( ah ) ;
ath9k_exit_debug ( sc - > sc_ah ) ;
2009-08-03 23:24:53 +04:00
sc - > sc_ah = NULL ;
2008-08-04 11:16:41 +04:00
}
2009-03-31 06:30:30 +04:00
static int ath9k_reg_notifier ( struct wiphy * wiphy ,
struct regulatory_request * request )
{
struct ieee80211_hw * hw = wiphy_to_ieee80211_hw ( wiphy ) ;
struct ath_wiphy * aphy = hw - > priv ;
struct ath_softc * sc = aphy - > sc ;
2009-08-18 05:07:23 +04:00
struct ath_regulatory * reg = & sc - > common . regulatory ;
2009-03-31 06:30:30 +04:00
return ath_reg_notifier_apply ( wiphy , request , reg ) ;
}
2009-08-03 23:24:47 +04:00
/*
* Initialize and fill ath_softc , ath_sofct is the
* " Software Carrier " struct . Historically it has existed
* to allow the separation between hardware specific
* variables ( now in ath_hw ) and driver specific variables .
*/
2009-09-09 13:55:49 +04:00
static int ath_init_softc ( u16 devid , struct ath_softc * sc , u16 subsysid )
2008-11-24 09:37:55 +03:00
{
2009-02-09 10:57:12 +03:00
struct ath_hw * ah = NULL ;
2009-08-03 23:24:36 +04:00
int r = 0 , i ;
2008-11-24 09:37:55 +03:00
int csz = 0 ;
/* XXX: hardware will not be ready until ath_open() being called */
sc - > sc_flags | = SC_OP_INVALID ;
2008-11-28 19:49:02 +03:00
2009-03-03 20:23:29 +03:00
spin_lock_init ( & sc - > wiphy_lock ) ;
2008-11-24 09:37:55 +03:00
spin_lock_init ( & sc - > sc_resetlock ) ;
2009-03-13 01:18:49 +03:00
spin_lock_init ( & sc - > sc_serial_rw ) ;
2009-06-24 17:26:41 +04:00
spin_lock_init ( & sc - > ani_lock ) ;
2009-07-15 04:17:13 +04:00
spin_lock_init ( & sc - > sc_pm_lock ) ;
2008-12-18 09:10:16 +03:00
mutex_init ( & sc - > mutex ) ;
2008-11-24 09:37:55 +03:00
tasklet_init ( & sc - > intr_tq , ath9k_tasklet , ( unsigned long ) sc ) ;
2009-03-03 07:46:51 +03:00
tasklet_init ( & sc - > bcon_tasklet , ath_beacon_tasklet ,
2008-11-24 09:37:55 +03:00
( unsigned long ) sc ) ;
/*
* Cache line size is used to size and align various
* structures used to communicate with the hardware .
*/
2009-01-14 22:17:04 +03:00
ath_read_cachesize ( sc , & csz ) ;
2008-11-24 09:37:55 +03:00
/* XXX assert csz is non-zero */
2009-08-12 20:56:59 +04:00
sc - > common . cachelsz = csz < < 2 ; /* convert to bytes */
2008-11-24 09:37:55 +03:00
2009-08-03 23:24:36 +04:00
ah = kzalloc ( sizeof ( struct ath_hw ) , GFP_KERNEL ) ;
if ( ! ah ) {
r = - ENOMEM ;
goto bad_no_ah ;
}
ah - > ah_sc = sc ;
2009-08-03 23:24:37 +04:00
ah - > hw_version . devid = devid ;
2009-09-09 13:55:49 +04:00
ah - > hw_version . subsysid = subsysid ;
2009-08-03 23:24:38 +04:00
sc - > sc_ah = ah ;
2009-08-03 23:24:36 +04:00
2009-09-07 15:52:26 +04:00
if ( ath9k_init_debug ( ah ) < 0 )
dev_err ( sc - > dev , " Unable to create debugfs files \n " ) ;
2009-08-03 23:24:46 +04:00
r = ath9k_hw_init ( ah ) ;
2009-08-03 23:24:36 +04:00
if ( r ) {
2009-09-07 15:52:26 +04:00
DPRINTF ( ah , ATH_DBG_FATAL ,
2009-08-03 23:24:46 +04:00
" Unable to initialize hardware; "
2009-08-03 23:24:36 +04:00
" initialization status: %d \n " , r ) ;
2008-11-24 09:37:55 +03:00
goto bad ;
}
/* Get the hardware key cache size. */
2009-02-09 10:57:26 +03:00
sc - > keymax = ah - > caps . keycache_size ;
2009-02-09 10:57:03 +03:00
if ( sc - > keymax > ATH_KEYMAX ) {
2009-09-07 15:52:26 +04:00
DPRINTF ( ah , ATH_DBG_ANY ,
2008-11-28 19:48:05 +03:00
" Warning, using only %u entries in %u key cache \n " ,
2009-02-09 10:57:03 +03:00
ATH_KEYMAX , sc - > keymax ) ;
sc - > keymax = ATH_KEYMAX ;
2008-11-24 09:37:55 +03:00
}
/*
* Reset the key cache since some parts do not
* reset the contents on initial power up .
*/
2009-02-09 10:57:03 +03:00
for ( i = 0 ; i < sc - > keymax ; i + + )
2008-11-24 09:37:55 +03:00
ath9k_hw_keyreset ( ah , ( u16 ) i ) ;
/* default to MONITOR mode */
2009-02-09 10:57:26 +03:00
sc - > sc_ah - > opmode = NL80211_IFTYPE_MONITOR ;
2008-12-02 00:38:55 +03:00
2008-11-24 09:37:55 +03:00
/* Setup rate tables */
ath_rate_attach ( sc ) ;
ath_setup_rates ( sc , IEEE80211_BAND_2GHZ ) ;
ath_setup_rates ( sc , IEEE80211_BAND_5GHZ ) ;
/*
* Allocate hardware transmit queues : one queue for
* beacon frames and one data queue for each QoS
* priority . Note that the hal handles reseting
* these queues at the needed time .
*/
2008-12-07 19:14:03 +03:00
sc - > beacon . beaconq = ath_beaconq_setup ( ah ) ;
if ( sc - > beacon . beaconq = = - 1 ) {
2009-09-07 15:52:26 +04:00
DPRINTF ( ah , ATH_DBG_FATAL ,
2008-11-28 19:48:05 +03:00
" Unable to setup a beacon xmit queue \n " ) ;
2009-08-03 23:24:36 +04:00
r = - EIO ;
2008-11-24 09:37:55 +03:00
goto bad2 ;
}
2008-12-07 19:14:03 +03:00
sc - > beacon . cabq = ath_txq_setup ( sc , ATH9K_TX_QUEUE_CAB , 0 ) ;
if ( sc - > beacon . cabq = = NULL ) {
2009-09-07 15:52:26 +04:00
DPRINTF ( ah , ATH_DBG_FATAL ,
2008-11-28 19:48:05 +03:00
" Unable to setup CAB xmit queue \n " ) ;
2009-08-03 23:24:36 +04:00
r = - EIO ;
2008-11-24 09:37:55 +03:00
goto bad2 ;
}
2009-02-09 10:57:03 +03:00
sc - > config . cabqReadytime = ATH_CABQ_READY_TIME ;
2008-11-24 09:37:55 +03:00
ath_cabq_update ( sc ) ;
2008-12-07 19:14:03 +03:00
for ( i = 0 ; i < ARRAY_SIZE ( sc - > tx . hwq_map ) ; i + + )
sc - > tx . hwq_map [ i ] = - 1 ;
2008-11-24 09:37:55 +03:00
/* Setup data queues */
/* NB: ensure BK queue is the lowest priority h/w queue */
if ( ! ath_tx_setup ( sc , ATH9K_WME_AC_BK ) ) {
2009-09-07 15:52:26 +04:00
DPRINTF ( ah , ATH_DBG_FATAL ,
2008-11-28 19:48:05 +03:00
" Unable to setup xmit queue for BK traffic \n " ) ;
2009-08-03 23:24:36 +04:00
r = - EIO ;
2008-11-24 09:37:55 +03:00
goto bad2 ;
}
if ( ! ath_tx_setup ( sc , ATH9K_WME_AC_BE ) ) {
2009-09-07 15:52:26 +04:00
DPRINTF ( ah , ATH_DBG_FATAL ,
2008-11-28 19:48:05 +03:00
" Unable to setup xmit queue for BE traffic \n " ) ;
2009-08-03 23:24:36 +04:00
r = - EIO ;
2008-11-24 09:37:55 +03:00
goto bad2 ;
}
if ( ! ath_tx_setup ( sc , ATH9K_WME_AC_VI ) ) {
2009-09-07 15:52:26 +04:00
DPRINTF ( ah , ATH_DBG_FATAL ,
2008-11-28 19:48:05 +03:00
" Unable to setup xmit queue for VI traffic \n " ) ;
2009-08-03 23:24:36 +04:00
r = - EIO ;
2008-11-24 09:37:55 +03:00
goto bad2 ;
}
if ( ! ath_tx_setup ( sc , ATH9K_WME_AC_VO ) ) {
2009-09-07 15:52:26 +04:00
DPRINTF ( ah , ATH_DBG_FATAL ,
2008-11-28 19:48:05 +03:00
" Unable to setup xmit queue for VO traffic \n " ) ;
2009-08-03 23:24:36 +04:00
r = - EIO ;
2008-11-24 09:37:55 +03:00
goto bad2 ;
}
/* Initializes the noise floor to a reasonable default value.
* Later on this will be updated during ANI processing . */
2009-02-09 10:57:03 +03:00
sc - > ani . noise_floor = ATH_DEFAULT_NOISE_FLOOR ;
setup_timer ( & sc - > ani . timer , ath_ani_calibrate , ( unsigned long ) sc ) ;
2008-11-24 09:37:55 +03:00
if ( ath9k_hw_getcapability ( ah , ATH9K_CAP_CIPHER ,
ATH9K_CIPHER_TKIP , NULL ) ) {
/*
* Whether we should enable h / w TKIP MIC .
* XXX : if we don ' t support WME TKIP MIC , then we wouldn ' t
* report WMM capable , so it ' s always safe to turn on
* TKIP MIC in this case .
*/
ath9k_hw_setcapability ( sc - > sc_ah , ATH9K_CAP_TKIP_MIC ,
0 , 1 , NULL ) ;
}
/*
* Check whether the separate key cache entries
* are required to handle both tx + rx MIC keys .
* With split mic keys the number of stations is limited
* to 27 otherwise 59.
*/
if ( ath9k_hw_getcapability ( ah , ATH9K_CAP_CIPHER ,
ATH9K_CIPHER_TKIP , NULL )
& & ath9k_hw_getcapability ( ah , ATH9K_CAP_CIPHER ,
ATH9K_CIPHER_MIC , NULL )
& & ath9k_hw_getcapability ( ah , ATH9K_CAP_TKIP_SPLIT ,
0 , NULL ) )
2009-02-09 10:57:03 +03:00
sc - > splitmic = 1 ;
2008-11-24 09:37:55 +03:00
/* turn on mcast key search if possible */
if ( ! ath9k_hw_getcapability ( ah , ATH9K_CAP_MCAST_KEYSRCH , 0 , NULL ) )
( void ) ath9k_hw_setcapability ( ah , ATH9K_CAP_MCAST_KEYSRCH , 1 ,
1 , NULL ) ;
2009-02-09 10:57:03 +03:00
sc - > config . txpowlimit = ATH_TXPOWER_MAX ;
2008-11-24 09:37:55 +03:00
/* 11n Capabilities */
2009-02-09 10:57:26 +03:00
if ( ah - > caps . hw_caps & ATH9K_HW_CAP_HT ) {
2008-11-24 09:37:55 +03:00
sc - > sc_flags | = SC_OP_TXAGGR ;
sc - > sc_flags | = SC_OP_RXAGGR ;
}
2009-02-09 10:57:26 +03:00
sc - > tx_chainmask = ah - > caps . tx_chainmask ;
sc - > rx_chainmask = ah - > caps . rx_chainmask ;
2008-11-24 09:37:55 +03:00
ath9k_hw_setcapability ( ah , ATH9K_CAP_DIVERSITY , 1 , true , NULL ) ;
2008-12-07 19:14:03 +03:00
sc - > rx . defant = ath9k_hw_getdefantenna ( ah ) ;
2008-11-24 09:37:55 +03:00
2009-03-03 20:23:27 +03:00
if ( ah - > caps . hw_caps & ATH9K_HW_CAP_BSSIDMASK )
2009-02-09 10:57:10 +03:00
memcpy ( sc - > bssidmask , ath_bcast_mac , ETH_ALEN ) ;
2008-11-24 09:37:55 +03:00
2008-12-07 19:14:03 +03:00
sc - > beacon . slottime = ATH9K_SLOT_TIME_9 ; /* default to short slot time */
2008-11-24 09:37:55 +03:00
/* initialize beacon slots */
2009-03-03 20:23:29 +03:00
for ( i = 0 ; i < ARRAY_SIZE ( sc - > beacon . bslot ) ; i + + ) {
2009-03-03 20:23:26 +03:00
sc - > beacon . bslot [ i ] = NULL ;
2009-03-03 20:23:29 +03:00
sc - > beacon . bslot_aphy [ i ] = NULL ;
}
2008-11-24 09:37:55 +03:00
/* setup channels and rates */
2009-01-23 02:16:48 +03:00
sc - > sbands [ IEEE80211_BAND_2GHZ ] . channels = ath9k_2ghz_chantable ;
2008-11-24 09:37:55 +03:00
sc - > sbands [ IEEE80211_BAND_2GHZ ] . bitrates =
sc - > rates [ IEEE80211_BAND_2GHZ ] ;
sc - > sbands [ IEEE80211_BAND_2GHZ ] . band = IEEE80211_BAND_2GHZ ;
2009-01-23 02:16:48 +03:00
sc - > sbands [ IEEE80211_BAND_2GHZ ] . n_channels =
ARRAY_SIZE ( ath9k_2ghz_chantable ) ;
2008-11-24 09:37:55 +03:00
2009-02-09 10:57:26 +03:00
if ( test_bit ( ATH9K_MODE_11A , sc - > sc_ah - > caps . wireless_modes ) ) {
2009-01-23 02:16:48 +03:00
sc - > sbands [ IEEE80211_BAND_5GHZ ] . channels = ath9k_5ghz_chantable ;
2008-11-24 09:37:55 +03:00
sc - > sbands [ IEEE80211_BAND_5GHZ ] . bitrates =
sc - > rates [ IEEE80211_BAND_5GHZ ] ;
sc - > sbands [ IEEE80211_BAND_5GHZ ] . band = IEEE80211_BAND_5GHZ ;
2009-01-23 02:16:48 +03:00
sc - > sbands [ IEEE80211_BAND_5GHZ ] . n_channels =
ARRAY_SIZE ( ath9k_5ghz_chantable ) ;
2008-11-24 09:37:55 +03:00
}
2009-08-26 19:38:50 +04:00
if ( sc - > btcoex_info . btcoex_scheme ! = ATH_BTCOEX_CFG_NONE ) {
r = ath9k_hw_btcoex_init ( ah ) ;
if ( r )
goto bad2 ;
}
2009-01-02 13:05:46 +03:00
2008-11-24 09:37:55 +03:00
return 0 ;
bad2 :
/* cleanup tx queues */
for ( i = 0 ; i < ATH9K_NUM_TX_QUEUES ; i + + )
if ( ATH_TXQ_SETUP ( sc , i ) )
2008-12-07 19:14:03 +03:00
ath_tx_cleanupq ( sc , & sc - > tx . txq [ i ] ) ;
2008-11-24 09:37:55 +03:00
bad :
2009-08-03 23:24:54 +04:00
ath9k_hw_detach ( ah ) ;
2009-08-03 23:24:36 +04:00
bad_no_ah :
2009-09-07 15:52:26 +04:00
ath9k_exit_debug ( sc - > sc_ah ) ;
sc - > sc_ah = NULL ;
2008-11-24 09:37:55 +03:00
2009-08-03 23:24:36 +04:00
return r ;
2008-11-24 09:37:55 +03:00
}
2009-03-03 20:23:29 +03:00
void ath_set_hw_capab ( struct ath_softc * sc , struct ieee80211_hw * hw )
2008-08-04 11:16:41 +04:00
{
2008-10-29 07:47:13 +03:00
hw - > flags = IEEE80211_HW_RX_INCLUDES_FCS |
IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
IEEE80211_HW_SIGNAL_DBM |
2009-01-20 08:47:08 +03:00
IEEE80211_HW_AMPDU_AGGREGATION |
IEEE80211_HW_SUPPORTS_PS |
2009-03-10 08:09:53 +03:00
IEEE80211_HW_PS_NULLFUNC_STACK |
IEEE80211_HW_SPECTRUM_MGMT ;
2008-08-04 11:16:41 +04:00
2009-02-24 14:42:01 +03:00
if ( AR_SREV_9160_10_OR_LATER ( sc - > sc_ah ) | | modparam_nohwcrypt )
2009-01-08 14:32:13 +03:00
hw - > flags | = IEEE80211_HW_MFP_CAPABLE ;
2008-10-29 07:47:13 +03:00
hw - > wiphy - > interface_modes =
BIT ( NL80211_IFTYPE_AP ) |
BIT ( NL80211_IFTYPE_STATION ) |
2009-03-21 05:59:59 +03:00
BIT ( NL80211_IFTYPE_ADHOC ) |
BIT ( NL80211_IFTYPE_MESH_POINT ) ;
2008-08-04 11:16:41 +04:00
2008-09-10 17:19:27 +04:00
hw - > queues = 4 ;
2008-11-18 06:37:53 +03:00
hw - > max_rates = 4 ;
2009-02-17 13:06:25 +03:00
hw - > channel_change_time = 5000 ;
2009-03-03 20:23:34 +03:00
hw - > max_listen_interval = 10 ;
2009-07-15 04:13:56 +04:00
/* Hardware supports 10 but we use 4 */
hw - > max_rate_tries = 4 ;
2008-10-29 07:44:26 +03:00
hw - > sta_data_size = sizeof ( struct ath_node ) ;
2009-02-09 10:57:03 +03:00
hw - > vif_data_size = sizeof ( struct ath_vif ) ;
2008-08-04 11:16:41 +04:00
2008-09-10 17:19:27 +04:00
hw - > rate_control_algorithm = " ath9k_rate_control " ;
2008-08-04 11:16:41 +04:00
2009-03-03 20:23:29 +03:00
hw - > wiphy - > bands [ IEEE80211_BAND_2GHZ ] =
& sc - > sbands [ IEEE80211_BAND_2GHZ ] ;
if ( test_bit ( ATH9K_MODE_11A , sc - > sc_ah - > caps . wireless_modes ) )
hw - > wiphy - > bands [ IEEE80211_BAND_5GHZ ] =
& sc - > sbands [ IEEE80211_BAND_5GHZ ] ;
}
2009-08-03 23:24:47 +04:00
/* Device driver core initialization */
2009-09-09 13:55:49 +04:00
int ath_init_device ( u16 devid , struct ath_softc * sc , u16 subsysid )
2009-03-03 20:23:29 +03:00
{
struct ieee80211_hw * hw = sc - > hw ;
2009-09-07 15:52:26 +04:00
struct ath_hw * ah ;
2009-03-03 20:23:29 +03:00
int error = 0 , i ;
2009-03-31 06:30:29 +04:00
struct ath_regulatory * reg ;
2009-03-03 20:23:29 +03:00
2009-09-07 15:52:26 +04:00
dev_dbg ( sc - > dev , " Attach ATH hw \n " ) ;
2009-03-03 20:23:29 +03:00
2009-09-09 13:55:49 +04:00
error = ath_init_softc ( devid , sc , subsysid ) ;
2009-03-03 20:23:29 +03:00
if ( error ! = 0 )
return error ;
2009-09-07 15:52:26 +04:00
ah = sc - > sc_ah ;
2009-03-03 20:23:29 +03:00
/* get mac address from hardware and set in mac80211 */
2009-09-07 15:52:26 +04:00
SET_IEEE80211_PERM_ADDR ( hw , ah - > macaddr ) ;
2009-03-03 20:23:29 +03:00
ath_set_hw_capab ( sc , hw ) ;
2009-08-18 05:07:23 +04:00
error = ath_regd_init ( & sc - > common . regulatory , sc - > hw - > wiphy ,
2009-05-20 02:27:11 +04:00
ath9k_reg_notifier ) ;
if ( error )
return error ;
2009-08-18 05:07:23 +04:00
reg = & sc - > common . regulatory ;
2009-05-20 02:27:11 +04:00
2009-09-07 15:52:26 +04:00
if ( ah - > caps . hw_caps & ATH9K_HW_CAP_HT ) {
2009-01-23 08:50:44 +03:00
setup_ht_cap ( sc , & sc - > sbands [ IEEE80211_BAND_2GHZ ] . ht_cap ) ;
2009-09-07 15:52:26 +04:00
if ( test_bit ( ATH9K_MODE_11A , ah - > caps . wireless_modes ) )
2009-01-23 08:50:44 +03:00
setup_ht_cap ( sc , & sc - > sbands [ IEEE80211_BAND_5GHZ ] . ht_cap ) ;
2008-10-29 07:47:13 +03:00
}
2008-11-13 15:31:08 +03:00
/* initialize tx/rx engine */
error = ath_tx_init ( sc , ATH_TXBUF ) ;
if ( error ! = 0 )
2009-02-16 11:25:07 +03:00
goto error_attach ;
2008-09-10 17:19:27 +04:00
2008-11-13 15:31:08 +03:00
error = ath_rx_init ( sc , ATH_RXBUF ) ;
if ( error ! = 0 )
2009-02-16 11:25:07 +03:00
goto error_attach ;
2008-09-10 17:19:27 +04:00
2009-03-03 20:23:32 +03:00
INIT_WORK ( & sc - > chan_work , ath9k_wiphy_chan_work ) ;
2009-03-03 20:23:39 +03:00
INIT_DELAYED_WORK ( & sc - > wiphy_work , ath9k_wiphy_work ) ;
sc - > wiphy_scheduler_int = msecs_to_jiffies ( 500 ) ;
2009-03-03 20:23:32 +03:00
2008-11-13 15:31:08 +03:00
error = ieee80211_register_hw ( hw ) ;
2008-09-10 17:19:27 +04:00
2009-03-31 06:30:29 +04:00
if ( ! ath_is_world_regd ( reg ) ) {
2009-03-31 06:30:28 +04:00
error = regulatory_hint ( hw - > wiphy , reg - > alpha2 ) ;
2009-02-21 08:04:30 +03:00
if ( error )
goto error_attach ;
}
2009-01-23 02:16:48 +03:00
2008-11-13 15:31:08 +03:00
/* Initialize LED control */
ath_init_leds ( sc ) ;
2008-09-10 17:19:27 +04:00
2009-06-13 13:20:26 +04:00
ath_start_rfkill_poll ( sc ) ;
2009-01-23 02:16:48 +03:00
2008-09-10 17:19:27 +04:00
return 0 ;
2009-02-16 11:25:07 +03:00
error_attach :
/* cleanup tx queues */
for ( i = 0 ; i < ATH9K_NUM_TX_QUEUES ; i + + )
if ( ATH_TXQ_SETUP ( sc , i ) )
ath_tx_cleanupq ( sc , & sc - > tx . txq [ i ] ) ;
2009-09-07 15:52:26 +04:00
ath9k_hw_detach ( ah ) ;
ath9k_exit_debug ( ah ) ;
2009-08-03 23:24:53 +04:00
sc - > sc_ah = NULL ;
2009-02-16 11:25:07 +03:00
2008-09-10 17:19:27 +04:00
return error ;
2008-08-04 11:16:41 +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 ;
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
ath9k_hw_set_interrupts ( ah , 0 ) ;
2009-01-16 19:08:47 +03:00
ath_drain_all_txq ( sc , retry_tx ) ;
2008-11-24 09:37:55 +03:00
ath_stoprecv ( sc ) ;
ath_flushrecv ( sc ) ;
spin_lock_bh ( & sc - > sc_resetlock ) ;
2009-02-09 10:57:26 +03:00
r = ath9k_hw_reset ( ah , sc - > sc_ah - > curchan , false ) ;
2008-12-24 02:58:40 +03:00
if ( r )
2009-09-07 15:52:26 +04:00
DPRINTF ( ah , ATH_DBG_FATAL ,
2009-05-15 17:29:20 +04:00
" Unable to reset hardware; reset status %d \n " , r ) ;
2008-11-24 09:37:55 +03:00
spin_unlock_bh ( & sc - > sc_resetlock ) ;
if ( ath_startrecv ( sc ) ! = 0 )
2009-09-07 15:52:26 +04:00
DPRINTF ( ah , ATH_DBG_FATAL , " 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 .
*/
2008-12-24 02:58:39 +03:00
ath_cache_conf_rate ( sc , & hw - > conf ) ;
2008-11-24 09:37:55 +03:00
ath_update_txpow ( sc ) ;
if ( sc - > sc_flags & SC_OP_BEACONS )
2009-03-03 20:23:26 +03:00
ath_beacon_config ( sc , NULL ) ; /* restart beacons */
2008-11-24 09:37:55 +03:00
2009-02-09 10:57:03 +03:00
ath9k_hw_set_interrupts ( ah , sc - > 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
}
}
}
2008-12-24 02:58:40 +03:00
return r ;
2008-11-24 09:37:55 +03:00
}
/*
* This function will allocate both the DMA descriptor structure , and the
* buffers it contains . These are used to contain the descriptors used
* by the system .
*/
int ath_descdma_setup ( struct ath_softc * sc , struct ath_descdma * dd ,
struct list_head * head , const char * name ,
int nbuf , int ndesc )
{
# define DS2PHYS(_dd, _ds) \
( ( _dd ) - > dd_desc_paddr + ( ( caddr_t ) ( _ds ) - ( caddr_t ) ( _dd ) - > dd_desc ) )
# define ATH_DESC_4KB_BOUND_CHECK(_daddr) ((((_daddr) & 0xFFF) > 0xF7F) ? 1 : 0)
# define ATH_DESC_4KB_BOUND_NUM_SKIPPED(_len) ((_len) / 4096)
struct ath_desc * ds ;
struct ath_buf * bf ;
int i , bsize , error ;
2009-09-07 15:52:26 +04:00
DPRINTF ( sc - > sc_ah , ATH_DBG_CONFIG , " %s DMA: %u buffers %u desc/buf \n " ,
2008-11-28 19:48:05 +03:00
name , nbuf , ndesc ) ;
2008-11-24 09:37:55 +03:00
2009-03-06 08:54:09 +03:00
INIT_LIST_HEAD ( head ) ;
2008-11-24 09:37:55 +03:00
/* ath_desc must be a multiple of DWORDs */
if ( ( sizeof ( struct ath_desc ) % 4 ) ! = 0 ) {
2009-09-07 15:52:26 +04:00
DPRINTF ( sc - > sc_ah , ATH_DBG_FATAL , " ath_desc not DWORD aligned \n " ) ;
2008-11-24 09:37:55 +03:00
ASSERT ( ( sizeof ( struct ath_desc ) % 4 ) = = 0 ) ;
error = - ENOMEM ;
goto fail ;
}
dd - > dd_desc_len = sizeof ( struct ath_desc ) * nbuf * ndesc ;
/*
* Need additional DMA memory because we can ' t use
* descriptors that cross the 4 K page boundary . Assume
* one skipped descriptor per 4 K page .
*/
2009-02-09 10:57:26 +03:00
if ( ! ( sc - > sc_ah - > caps . hw_caps & ATH9K_HW_CAP_4KB_SPLITTRANS ) ) {
2008-11-24 09:37:55 +03:00
u32 ndesc_skipped =
ATH_DESC_4KB_BOUND_NUM_SKIPPED ( dd - > dd_desc_len ) ;
u32 dma_len ;
while ( ndesc_skipped ) {
dma_len = ndesc_skipped * sizeof ( struct ath_desc ) ;
dd - > dd_desc_len + = dma_len ;
ndesc_skipped = ATH_DESC_4KB_BOUND_NUM_SKIPPED ( dma_len ) ;
} ;
}
/* allocate descriptors */
2009-01-14 22:17:03 +03:00
dd - > dd_desc = dma_alloc_coherent ( sc - > dev , dd - > dd_desc_len ,
2009-03-06 08:54:08 +03:00
& dd - > dd_desc_paddr , GFP_KERNEL ) ;
2008-11-24 09:37:55 +03:00
if ( dd - > dd_desc = = NULL ) {
error = - ENOMEM ;
goto fail ;
}
ds = dd - > dd_desc ;
2009-09-07 15:52:26 +04:00
DPRINTF ( sc - > sc_ah , ATH_DBG_CONFIG , " %s DMA map: %p (%u) -> %llx (%u) \n " ,
2009-03-30 13:58:40 +04:00
name , ds , ( u32 ) dd - > dd_desc_len ,
2008-11-24 09:37:55 +03:00
ito64 ( dd - > dd_desc_paddr ) , /*XXX*/ ( u32 ) dd - > dd_desc_len ) ;
/* allocate buffers */
bsize = sizeof ( struct ath_buf ) * nbuf ;
2009-03-06 08:54:08 +03:00
bf = kzalloc ( bsize , GFP_KERNEL ) ;
2008-11-24 09:37:55 +03:00
if ( bf = = NULL ) {
error = - ENOMEM ;
goto fail2 ;
}
dd - > dd_bufptr = bf ;
for ( i = 0 ; i < nbuf ; i + + , bf + + , ds + = ndesc ) {
bf - > bf_desc = ds ;
bf - > bf_daddr = DS2PHYS ( dd , ds ) ;
2009-02-09 10:57:26 +03:00
if ( ! ( sc - > sc_ah - > caps . hw_caps &
2008-11-24 09:37:55 +03:00
ATH9K_HW_CAP_4KB_SPLITTRANS ) ) {
/*
* Skip descriptor addresses which can cause 4 KB
* boundary crossing ( addr + length ) with a 32 dword
* descriptor fetch .
*/
while ( ATH_DESC_4KB_BOUND_CHECK ( bf - > bf_daddr ) ) {
ASSERT ( ( caddr_t ) bf - > bf_desc <
( ( caddr_t ) dd - > dd_desc +
dd - > dd_desc_len ) ) ;
ds + = ndesc ;
bf - > bf_desc = ds ;
bf - > bf_daddr = DS2PHYS ( dd , ds ) ;
}
}
list_add_tail ( & bf - > list , head ) ;
}
return 0 ;
fail2 :
2009-01-14 22:17:03 +03:00
dma_free_coherent ( sc - > dev , dd - > dd_desc_len , dd - > dd_desc ,
dd - > dd_desc_paddr ) ;
2008-11-24 09:37:55 +03:00
fail :
memset ( dd , 0 , sizeof ( * dd ) ) ;
return error ;
# undef ATH_DESC_4KB_BOUND_CHECK
# undef ATH_DESC_4KB_BOUND_NUM_SKIPPED
# undef DS2PHYS
}
void ath_descdma_cleanup ( struct ath_softc * sc ,
struct ath_descdma * dd ,
struct list_head * head )
{
2009-01-14 22:17:03 +03:00
dma_free_coherent ( sc - > dev , dd - > dd_desc_len , dd - > dd_desc ,
dd - > dd_desc_paddr ) ;
2008-11-24 09:37:55 +03:00
INIT_LIST_HEAD ( head ) ;
kfree ( dd - > dd_bufptr ) ;
memset ( dd , 0 , sizeof ( * dd ) ) ;
}
int ath_get_hal_qnum ( u16 queue , struct ath_softc * sc )
{
int qnum ;
switch ( queue ) {
case 0 :
2008-12-07 19:14:03 +03:00
qnum = sc - > tx . hwq_map [ ATH9K_WME_AC_VO ] ;
2008-11-24 09:37:55 +03:00
break ;
case 1 :
2008-12-07 19:14:03 +03:00
qnum = sc - > tx . hwq_map [ ATH9K_WME_AC_VI ] ;
2008-11-24 09:37:55 +03:00
break ;
case 2 :
2008-12-07 19:14:03 +03:00
qnum = sc - > tx . hwq_map [ ATH9K_WME_AC_BE ] ;
2008-11-24 09:37:55 +03:00
break ;
case 3 :
2008-12-07 19:14:03 +03:00
qnum = sc - > tx . hwq_map [ ATH9K_WME_AC_BK ] ;
2008-11-24 09:37:55 +03:00
break ;
default :
2008-12-07 19:14:03 +03:00
qnum = sc - > tx . hwq_map [ ATH9K_WME_AC_BE ] ;
2008-11-24 09:37:55 +03:00
break ;
}
return qnum ;
}
int ath_get_mac80211_qnum ( u32 queue , struct ath_softc * sc )
{
int qnum ;
switch ( queue ) {
case ATH9K_WME_AC_VO :
qnum = 0 ;
break ;
case ATH9K_WME_AC_VI :
qnum = 1 ;
break ;
case ATH9K_WME_AC_BE :
qnum = 2 ;
break ;
case ATH9K_WME_AC_BK :
qnum = 3 ;
break ;
default :
qnum = - 1 ;
break ;
}
return qnum ;
}
2009-01-23 02:16:48 +03:00
/* XXX: Remove me once we don't depend on ath9k_channel for all
* this redundant data */
2009-03-03 20:23:32 +03:00
void ath9k_update_ichannel ( struct ath_softc * sc , struct ieee80211_hw * hw ,
struct ath9k_channel * ichan )
2009-01-23 02:16:48 +03:00
{
struct ieee80211_channel * chan = hw - > conf . channel ;
struct ieee80211_conf * conf = & hw - > conf ;
ichan - > channel = chan - > center_freq ;
ichan - > chan = chan ;
if ( chan - > band = = IEEE80211_BAND_2GHZ ) {
ichan - > chanmode = CHANNEL_G ;
2009-09-03 10:38:53 +04:00
ichan - > channelFlags = CHANNEL_2GHZ | CHANNEL_OFDM | CHANNEL_G ;
2009-01-23 02:16:48 +03:00
} else {
ichan - > chanmode = CHANNEL_A ;
ichan - > channelFlags = CHANNEL_5GHZ | CHANNEL_OFDM ;
}
sc - > tx_chan_width = ATH9K_HT_MACMODE_20 ;
if ( conf_is_ht ( conf ) ) {
if ( conf_is_ht40 ( conf ) )
sc - > tx_chan_width = ATH9K_HT_MACMODE_2040 ;
ichan - > chanmode = ath_get_extchanmode ( sc , chan ,
conf - > channel_type ) ;
}
}
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
{
2009-03-03 20:23:28 +03:00
struct ath_wiphy * aphy = hw - > priv ;
struct ath_softc * sc = aphy - > sc ;
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
2009-09-07 15:52:26 +04:00
DPRINTF ( sc - > sc_ah , ATH_DBG_CONFIG , " Starting driver with "
2008-11-28 19:48:05 +03:00
" initial channel: %d MHz \n " , curchan - > center_freq ) ;
2008-08-04 11:16:41 +04:00
2009-02-04 05:40:07 +03:00
mutex_lock ( & sc - > mutex ) ;
2009-03-03 20:23:33 +03:00
if ( ath9k_wiphy_started ( sc ) ) {
if ( sc - > chan_idx = = curchan - > hw_value ) {
/*
* Already on the operational channel , the new wiphy
* can be marked active .
*/
aphy - > state = ATH_WIPHY_ACTIVE ;
ieee80211_wake_queues ( hw ) ;
} else {
/*
* Another wiphy is on another channel , start the new
* wiphy in paused state .
*/
aphy - > state = ATH_WIPHY_PAUSED ;
ieee80211_stop_queues ( hw ) ;
}
mutex_unlock ( & sc - > mutex ) ;
return 0 ;
}
aphy - > state = ATH_WIPHY_ACTIVE ;
2008-09-10 17:19:27 +04:00
/* setup initial channel */
2008-08-04 11:16:41 +04:00
2009-06-13 13:20:24 +04:00
sc - > chan_idx = curchan - > hw_value ;
2008-08-04 11:16:41 +04:00
2009-06-13 13:20:24 +04:00
init_channel = ath_get_curchannel ( sc , hw ) ;
2008-11-24 09:37:55 +03:00
/* Reset SERDES registers */
2009-09-17 07:54:58 +04:00
ath9k_hw_configpcipowersave ( sc - > sc_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 .
*/
spin_lock_bh ( & sc - > sc_resetlock ) ;
2008-12-24 02:58:40 +03:00
r = ath9k_hw_reset ( sc - > sc_ah , init_channel , false ) ;
if ( r ) {
2009-09-07 15:52:26 +04:00
DPRINTF ( sc - > sc_ah , ATH_DBG_FATAL ,
2009-05-15 17:29:20 +04:00
" Unable to reset hardware; reset status %d "
2008-12-24 02:58:40 +03:00
" (freq %u MHz) \n " , r ,
curchan - > center_freq ) ;
2008-11-24 09:37:55 +03:00
spin_unlock_bh ( & sc - > sc_resetlock ) ;
2009-02-04 05:40:07 +03:00
goto mutex_unlock ;
2008-11-24 09:37:55 +03:00
}
spin_unlock_bh ( & sc - > sc_resetlock ) ;
/*
* This is needed only to setup initial state
* but it ' s best done after a reset .
*/
ath_update_txpow ( sc ) ;
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 ) {
2009-09-07 15:52:26 +04:00
DPRINTF ( sc - > sc_ah , ATH_DBG_FATAL , " Unable to start recv logic \n " ) ;
2009-02-04 05:40:07 +03:00
r = - EIO ;
goto mutex_unlock ;
2008-08-04 11:16:41 +04:00
}
2008-09-10 17:19:27 +04:00
2008-11-24 09:37:55 +03:00
/* Setup our intr mask. */
2009-02-09 10:57:03 +03:00
sc - > imask = ATH9K_INT_RX | ATH9K_INT_TX
2008-11-24 09:37:55 +03:00
| ATH9K_INT_RXEOL | ATH9K_INT_RXORN
| ATH9K_INT_FATAL | ATH9K_INT_GLOBAL ;
2009-02-09 10:57:26 +03:00
if ( sc - > sc_ah - > caps . hw_caps & ATH9K_HW_CAP_GTT )
2009-02-09 10:57:03 +03:00
sc - > imask | = ATH9K_INT_GTT ;
2008-11-24 09:37:55 +03:00
2009-02-09 10:57:26 +03:00
if ( sc - > sc_ah - > caps . hw_caps & ATH9K_HW_CAP_HT )
2009-02-09 10:57:03 +03:00
sc - > imask | = ATH9K_INT_CST ;
2008-11-24 09:37:55 +03:00
2008-12-24 02:58:39 +03:00
ath_cache_conf_rate ( sc , & hw - > conf ) ;
2008-11-24 09:37:55 +03:00
sc - > sc_flags & = ~ SC_OP_INVALID ;
/* Disable BMISS interrupt when we're not associated */
2009-02-09 10:57:03 +03:00
sc - > imask & = ~ ( ATH9K_INT_SWBA | ATH9K_INT_BMISS ) ;
ath9k_hw_set_interrupts ( sc - > sc_ah , sc - > 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-08-26 19:38:50 +04:00
if ( ( sc - > btcoex_info . btcoex_scheme ! = ATH_BTCOEX_CFG_NONE ) & &
! ( sc - > sc_flags & SC_OP_BTCOEX_ENABLED ) ) {
ath_btcoex_set_weight ( & sc - > btcoex_info , AR_BT_COEX_WGHT ,
AR_STOMP_LOW_WLAN_WGHT ) ;
2009-08-26 19:38:43 +04:00
ath9k_hw_btcoex_enable ( sc - > sc_ah ) ;
2009-09-07 16:16:49 +04:00
ath_pcie_aspm_disable ( sc ) ;
2009-08-26 19:38:50 +04:00
if ( sc - > btcoex_info . btcoex_scheme = = ATH_BTCOEX_CFG_3WIRE )
2009-09-09 12:18:09 +04:00
ath_btcoex_timer_resume ( sc ) ;
2009-08-26 19:38:50 +04:00
}
2009-02-04 05:40:07 +03:00
mutex_unlock :
mutex_unlock ( & sc - > mutex ) ;
2008-12-24 02:58:40 +03:00
return r ;
2008-08-04 11:16:41 +04:00
}
2008-09-10 17:19:27 +04:00
static int ath9k_tx ( struct ieee80211_hw * hw ,
struct sk_buff * skb )
2008-08-04 11:16:41 +04:00
{
2008-10-29 07:44:26 +03:00
struct ieee80211_tx_info * info = IEEE80211_SKB_CB ( skb ) ;
2009-03-03 20:23:28 +03:00
struct ath_wiphy * aphy = hw - > priv ;
struct ath_softc * sc = aphy - > sc ;
2008-10-29 07:44:26 +03:00
struct ath_tx_control txctl ;
2008-09-10 17:19:27 +04:00
int hdrlen , padsize ;
2008-10-29 07:44:26 +03:00
2009-03-03 20:23:38 +03:00
if ( aphy - > state ! = ATH_WIPHY_ACTIVE & & aphy - > state ! = ATH_WIPHY_SCAN ) {
2009-03-03 20:23:36 +03:00
printk ( KERN_DEBUG " ath9k: %s: TX in unexpected wiphy state "
" %d \n " , wiphy_name ( hw - > wiphy ) , aphy - > state ) ;
goto exit ;
}
2009-07-24 19:27:21 +04:00
if ( sc - > ps_enabled ) {
2009-05-19 18:01:42 +04:00
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) skb - > data ;
/*
* 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 ) ) {
2009-09-07 15:52:26 +04:00
DPRINTF ( sc - > sc_ah , ATH_DBG_PS , " Add PM=1 for a TX frame "
2009-05-19 18:01:42 +04:00
" while in PS mode \n " ) ;
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 .
*/
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) skb - > data ;
ath9k_ps_wakeup ( sc ) ;
ath9k_hw_setrxabort ( sc - > sc_ah , 0 ) ;
if ( ieee80211_is_pspoll ( hdr - > frame_control ) ) {
2009-09-07 15:52:26 +04:00
DPRINTF ( sc - > sc_ah , ATH_DBG_PS , " Sending PS-Poll to pick a "
2009-05-19 18:01:38 +04:00
" buffered frame \n " ) ;
sc - > sc_flags | = SC_OP_WAIT_FOR_PSPOLL_DATA ;
} else {
2009-09-07 15:52:26 +04:00
DPRINTF ( sc - > sc_ah , ATH_DBG_PS , " Wake up to complete TX \n " ) ;
2009-05-19 18:01:38 +04:00
sc - > sc_flags | = SC_OP_WAIT_FOR_TX_ACK ;
}
/*
* 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 ) ) ;
2008-08-04 11:16:41 +04:00
2008-09-10 17:19:27 +04:00
/*
* As a temporary workaround , assign seq # here ; this will likely need
* to be cleaned up to work better with Beacon transmission and virtual
* BSSes .
*/
if ( info - > flags & IEEE80211_TX_CTL_ASSIGN_SEQ ) {
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) skb - > data ;
if ( info - > flags & IEEE80211_TX_CTL_FIRST_FRAGMENT )
2008-12-07 19:14:03 +03:00
sc - > tx . seq_no + = 0x10 ;
2008-09-10 17:19:27 +04:00
hdr - > seq_ctrl & = cpu_to_le16 ( IEEE80211_SCTL_FRAG ) ;
2008-12-07 19:14:03 +03:00
hdr - > seq_ctrl | = cpu_to_le16 ( sc - > tx . seq_no ) ;
2008-09-10 17:19:27 +04:00
}
2008-08-04 11:16:41 +04:00
2008-09-10 17:19:27 +04:00
/* Add the padding after the header if this is not already done */
hdrlen = ieee80211_get_hdrlen_from_skb ( skb ) ;
if ( hdrlen & 3 ) {
padsize = hdrlen % 4 ;
if ( skb_headroom ( skb ) < padsize )
return - 1 ;
skb_push ( skb , padsize ) ;
memmove ( skb - > data , skb - > data + padsize , hdrlen ) ;
}
2008-10-29 07:44:26 +03:00
/* Check if a tx queue is available */
txctl . txq = ath_test_get_txq ( sc , skb ) ;
if ( ! txctl . txq )
goto exit ;
2009-09-07 15:52:26 +04:00
DPRINTF ( sc - > sc_ah , 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 ) {
2009-09-07 15:52:26 +04:00
DPRINTF ( sc - > sc_ah , ATH_DBG_XMIT , " TX failed \n " ) ;
2008-10-29 07:44:26 +03:00
goto exit ;
2008-09-10 17:19:27 +04:00
}
2008-10-29 07:44:26 +03:00
return 0 ;
exit :
dev_kfree_skb_any ( skb ) ;
2008-09-10 17:19:27 +04:00
return 0 ;
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
{
2009-03-03 20:23:28 +03:00
struct ath_wiphy * aphy = hw - > priv ;
struct ath_softc * sc = aphy - > sc ;
2008-08-04 11:16:41 +04:00
2009-08-18 09:21:52 +04:00
mutex_lock ( & sc - > mutex ) ;
2009-03-03 20:23:33 +03:00
aphy - > state = ATH_WIPHY_INACTIVE ;
2009-07-27 22:53:04 +04:00
cancel_delayed_work_sync ( & sc - > ath_led_blink_work ) ;
cancel_delayed_work_sync ( & sc - > tx_complete_work ) ;
if ( ! sc - > num_sec_wiphy ) {
cancel_delayed_work_sync ( & sc - > wiphy_work ) ;
cancel_work_sync ( & sc - > chan_work ) ;
}
2008-10-29 07:47:13 +03:00
if ( sc - > sc_flags & SC_OP_INVALID ) {
2009-09-07 15:52:26 +04:00
DPRINTF ( sc - > sc_ah , 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-03-03 20:23:33 +03:00
if ( ath9k_wiphy_started ( sc ) ) {
mutex_unlock ( & sc - > mutex ) ;
return ; /* another wiphy still in use */
}
2009-08-26 19:38:50 +04:00
if ( sc - > sc_flags & SC_OP_BTCOEX_ENABLED ) {
ath9k_hw_btcoex_disable ( sc - > sc_ah ) ;
if ( sc - > btcoex_info . btcoex_scheme = = ATH_BTCOEX_CFG_3WIRE )
2009-09-09 12:18:09 +04:00
ath_btcoex_timer_pause ( sc ) ;
2009-08-26 19:38:50 +04:00
}
2008-11-24 09:37:55 +03:00
/* make sure h/w will not generate any interrupt
* before setting the invalid flag . */
ath9k_hw_set_interrupts ( sc - > sc_ah , 0 ) ;
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 ) ;
ath9k_hw_phy_disable ( sc - > sc_ah ) ;
} else
2008-12-07 19:14:03 +03:00
sc - > rx . rxlink = NULL ;
2008-11-24 09:37:55 +03:00
/* disable HAL and put h/w to sleep */
ath9k_hw_disable ( sc - > sc_ah ) ;
2009-09-17 07:54:58 +04:00
ath9k_hw_configpcipowersave ( sc - > sc_ah , 1 , 1 ) ;
2009-08-13 08:04:37 +04:00
ath9k_hw_setpower ( sc - > sc_ah , ATH9K_PM_FULL_SLEEP ) ;
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 ) ;
2009-09-07 15:52:26 +04:00
DPRINTF ( sc - > sc_ah , ATH_DBG_CONFIG , " Driver halt \n " ) ;
2008-08-04 11:16:41 +04:00
}
2008-09-10 17:19:27 +04:00
static int ath9k_add_interface ( struct ieee80211_hw * hw ,
struct ieee80211_if_init_conf * conf )
2008-08-04 11:16:41 +04:00
{
2009-03-03 20:23:28 +03:00
struct ath_wiphy * aphy = hw - > priv ;
struct ath_softc * sc = aphy - > sc ;
2009-02-09 10:57:03 +03:00
struct ath_vif * avp = ( void * ) conf - > vif - > drv_priv ;
2008-12-02 00:38:55 +03:00
enum nl80211_iftype ic_opmode = NL80211_IFTYPE_UNSPECIFIED ;
2009-03-03 20:23:26 +03:00
int ret = 0 ;
2008-09-10 17:19:27 +04:00
2009-02-04 05:40:07 +03:00
mutex_lock ( & sc - > mutex ) ;
2009-03-03 20:23:27 +03:00
if ( ! ( sc - > sc_ah - > caps . hw_caps & ATH9K_HW_CAP_BSSIDMASK ) & &
sc - > nvifs > 0 ) {
ret = - ENOBUFS ;
goto out ;
}
2008-09-10 17:19:27 +04:00
switch ( conf - > type ) {
2008-09-11 02:01:58 +04:00
case NL80211_IFTYPE_STATION :
2008-12-02 00:38:55 +03:00
ic_opmode = NL80211_IFTYPE_STATION ;
2008-08-04 11:16:41 +04:00
break ;
2008-09-11 02:01:58 +04:00
case NL80211_IFTYPE_ADHOC :
case NL80211_IFTYPE_AP :
2009-03-21 05:59:59 +03:00
case NL80211_IFTYPE_MESH_POINT :
2009-03-03 20:23:26 +03:00
if ( sc - > nbcnvifs > = ATH_BCBUF ) {
ret = - ENOBUFS ;
goto out ;
}
2009-03-21 05:59:59 +03:00
ic_opmode = conf - > type ;
2008-08-04 11:16:41 +04:00
break ;
default :
2009-09-07 15:52:26 +04:00
DPRINTF ( sc - > sc_ah , ATH_DBG_FATAL ,
2008-11-28 19:48:05 +03:00
" Interface type %d not yet supported \n " , conf - > type ) ;
2009-03-03 20:23:26 +03:00
ret = - EOPNOTSUPP ;
goto out ;
2008-08-04 11:16:41 +04:00
}
2009-09-07 15:52:26 +04:00
DPRINTF ( sc - > sc_ah , ATH_DBG_CONFIG , " Attach a VIF of type: %d \n " , ic_opmode ) ;
2008-09-10 17:19:27 +04:00
2009-02-09 10:57:03 +03:00
/* Set the VIF opmode */
2008-10-29 07:46:06 +03:00
avp - > av_opmode = ic_opmode ;
avp - > av_bslot = - 1 ;
2009-03-03 20:23:26 +03:00
sc - > nvifs + + ;
2009-03-03 20:23:27 +03:00
if ( sc - > sc_ah - > caps . hw_caps & ATH9K_HW_CAP_BSSIDMASK )
ath9k_set_bssid_mask ( hw ) ;
2009-03-03 20:23:26 +03:00
if ( sc - > nvifs > 1 )
goto out ; /* skip global settings for secondary vif */
2009-03-03 07:46:56 +03:00
if ( ic_opmode = = NL80211_IFTYPE_AP ) {
2008-10-29 07:46:06 +03:00
ath9k_hw_set_tsfadjust ( sc - > sc_ah , 1 ) ;
2009-03-03 07:46:56 +03:00
sc - > sc_flags | = SC_OP_TSF_RESET ;
}
2008-10-29 07:46:06 +03:00
/* Set the device opmode */
2009-02-09 10:57:26 +03:00
sc - > sc_ah - > opmode = ic_opmode ;
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 .
* Note we only do this ( at the moment ) for station mode .
*/
2009-02-12 07:36:47 +03:00
if ( ( conf - > type = = NL80211_IFTYPE_STATION ) | |
2009-03-21 05:59:59 +03:00
( conf - > type = = NL80211_IFTYPE_ADHOC ) | |
( conf - > type = = NL80211_IFTYPE_MESH_POINT ) ) {
2009-08-13 08:04:25 +04:00
sc - > imask | = ATH9K_INT_MIB ;
2009-02-12 07:36:47 +03:00
sc - > imask | = ATH9K_INT_TSFOOR ;
}
2009-02-09 10:57:03 +03:00
ath9k_hw_set_interrupts ( sc - > sc_ah , sc - > imask ) ;
2009-01-28 18:23:27 +03:00
2009-06-24 17:26:40 +04:00
if ( conf - > type = = NL80211_IFTYPE_AP | |
conf - > type = = NL80211_IFTYPE_ADHOC | |
conf - > type = = NL80211_IFTYPE_MONITOR )
2009-04-13 20:26:46 +04:00
ath_start_ani ( sc ) ;
2008-10-04 02:45:27 +04:00
2009-03-03 20:23:26 +03:00
out :
2009-02-04 05:40:07 +03:00
mutex_unlock ( & sc - > mutex ) ;
2009-03-03 20:23:26 +03:00
return ret ;
2008-08-04 11:16:41 +04:00
}
2008-09-10 17:19:27 +04:00
static void ath9k_remove_interface ( struct ieee80211_hw * hw ,
struct ieee80211_if_init_conf * conf )
2008-08-04 11:16:41 +04:00
{
2009-03-03 20:23:28 +03:00
struct ath_wiphy * aphy = hw - > priv ;
struct ath_softc * sc = aphy - > sc ;
2009-02-09 10:57:03 +03:00
struct ath_vif * avp = ( void * ) conf - > vif - > drv_priv ;
2009-03-03 20:23:26 +03:00
int i ;
2008-08-04 11:16:41 +04:00
2009-09-07 15:52:26 +04:00
DPRINTF ( sc - > sc_ah , ATH_DBG_CONFIG , " Detach Interface \n " ) ;
2008-08-04 11:16:41 +04:00
2009-02-04 05:40:07 +03:00
mutex_lock ( & sc - > mutex ) ;
2008-10-04 02:45:27 +04:00
/* Stop ANI */
2009-02-09 10:57:03 +03:00
del_timer_sync ( & sc - > ani . timer ) ;
2008-08-11 15:01:49 +04:00
2008-09-10 17:19:27 +04:00
/* Reclaim beacon resources */
2009-03-21 05:59:59 +03:00
if ( ( sc - > sc_ah - > opmode = = NL80211_IFTYPE_AP ) | |
( sc - > sc_ah - > opmode = = NL80211_IFTYPE_ADHOC ) | |
( sc - > sc_ah - > opmode = = NL80211_IFTYPE_MESH_POINT ) ) {
2008-12-07 19:14:03 +03:00
ath9k_hw_stoptxdma ( sc - > sc_ah , sc - > beacon . beaconq ) ;
2008-09-10 17:19:27 +04:00
ath_beacon_return ( sc , avp ) ;
2008-08-11 15:01:49 +04:00
}
2008-08-04 11:16:41 +04:00
2008-09-10 17:19:27 +04:00
sc - > sc_flags & = ~ SC_OP_BEACONS ;
2008-08-04 11:16:41 +04:00
2009-03-03 20:23:26 +03:00
for ( i = 0 ; i < ARRAY_SIZE ( sc - > beacon . bslot ) ; i + + ) {
if ( sc - > beacon . bslot [ i ] = = conf - > vif ) {
printk ( KERN_DEBUG " %s: vif had allocated beacon "
" slot \n " , __func__ ) ;
sc - > beacon . bslot [ i ] = NULL ;
2009-03-03 20:23:29 +03:00
sc - > beacon . bslot_aphy [ i ] = NULL ;
2009-03-03 20:23:26 +03:00
}
}
2009-02-09 10:57:03 +03:00
sc - > nvifs - - ;
2009-02-04 05:40:07 +03:00
mutex_unlock ( & sc - > mutex ) ;
2008-08-04 11:16:41 +04:00
}
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
{
2009-03-03 20:23:28 +03:00
struct ath_wiphy * aphy = hw - > priv ;
struct ath_softc * sc = aphy - > sc ;
2008-10-09 14:18:51 +04:00
struct ieee80211_conf * conf = & hw - > conf ;
2009-03-30 12:47:00 +04:00
struct ath_hw * ah = sc - > sc_ah ;
2009-07-15 04:22:53 +04:00
bool all_wiphys_idle = false , 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-07-15 04:22:53 +04:00
/* Leave this as the first check */
if ( changed & IEEE80211_CONF_CHANGE_IDLE ) {
spin_lock_bh ( & sc - > wiphy_lock ) ;
all_wiphys_idle = ath9k_all_wiphys_idle ( sc ) ;
spin_unlock_bh ( & sc - > wiphy_lock ) ;
if ( conf - > flags & IEEE80211_CONF_IDLE ) {
if ( all_wiphys_idle )
disable_radio = true ;
}
else if ( all_wiphys_idle ) {
ath_radio_enable ( sc ) ;
2009-09-07 15:52:26 +04:00
DPRINTF ( sc - > sc_ah , ATH_DBG_CONFIG ,
2009-07-15 04:22:53 +04:00
" not-idle: enabling radio \n " ) ;
}
}
2009-01-20 08:47:08 +03:00
if ( changed & IEEE80211_CONF_CHANGE_PS ) {
if ( conf - > flags & IEEE80211_CONF_PS ) {
2009-03-30 12:47:00 +04:00
if ( ! ( ah - > caps . hw_caps &
ATH9K_HW_CAP_AUTOSLEEP ) ) {
if ( ( sc - > imask & ATH9K_INT_TIM_TIMER ) = = 0 ) {
sc - > imask | = ATH9K_INT_TIM_TIMER ;
ath9k_hw_set_interrupts ( sc - > sc_ah ,
sc - > imask ) ;
}
ath9k_hw_setrxabort ( sc - > sc_ah , 1 ) ;
2009-01-20 08:47:08 +03:00
}
2009-07-24 19:27:21 +04:00
sc - > ps_enabled = true ;
2009-01-20 08:47:08 +03:00
} else {
2009-07-24 19:27:21 +04:00
sc - > ps_enabled = false ;
2009-01-20 08:47:08 +03:00
ath9k_hw_setpower ( sc - > sc_ah , ATH9K_PM_AWAKE ) ;
2009-03-30 12:47:00 +04:00
if ( ! ( ah - > caps . hw_caps &
ATH9K_HW_CAP_AUTOSLEEP ) ) {
ath9k_hw_setrxabort ( sc - > sc_ah , 0 ) ;
2009-05-19 18:01:38 +04:00
sc - > sc_flags & = ~ ( SC_OP_WAIT_FOR_BEACON |
SC_OP_WAIT_FOR_CAB |
SC_OP_WAIT_FOR_PSPOLL_DATA |
SC_OP_WAIT_FOR_TX_ACK ) ;
2009-03-30 12:47:00 +04:00
if ( sc - > imask & ATH9K_INT_TIM_TIMER ) {
sc - > imask & = ~ ATH9K_INT_TIM_TIMER ;
ath9k_hw_set_interrupts ( sc - > sc_ah ,
sc - > imask ) ;
}
2009-01-20 08:47:08 +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 ;
2008-10-14 18:58:37 +04:00
2009-03-03 20:23:32 +03:00
aphy - > chan_idx = pos ;
aphy - > chan_is_ht = conf_is_ht ( conf ) ;
2009-03-03 20:23:38 +03:00
if ( aphy - > state = = ATH_WIPHY_SCAN | |
aphy - > state = = ATH_WIPHY_ACTIVE )
ath9k_wiphy_pause_all_forced ( sc , aphy ) ;
else {
/*
* Do not change operational channel based on a paused
* wiphy changes .
*/
goto skip_chan_change ;
}
2009-03-03 20:23:32 +03:00
2009-09-07 15:52:26 +04:00
DPRINTF ( sc - > sc_ah , ATH_DBG_CONFIG , " Set channel: %d MHz \n " ,
2008-11-28 19:48:05 +03:00
curchan - > center_freq ) ;
2008-08-04 11:16:41 +04:00
2009-01-23 02:16:48 +03:00
/* XXX: remove me eventualy */
2009-03-03 20:23:32 +03:00
ath9k_update_ichannel ( sc , hw , & sc - > sc_ah - > channels [ pos ] ) ;
2008-11-27 07:16:27 +03:00
2008-12-24 02:58:43 +03:00
ath_update_chainmask ( sc , conf_is_ht ( conf ) ) ;
2009-01-07 11:55:29 +03:00
2009-03-03 20:23:32 +03:00
if ( ath_set_channel ( sc , hw , & sc - > sc_ah - > channels [ pos ] ) < 0 ) {
2009-09-07 15:52:26 +04:00
DPRINTF ( sc - > sc_ah , ATH_DBG_FATAL , " 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 ;
}
2008-12-12 09:27:43 +03:00
}
2008-08-04 11:16:41 +04:00
2009-03-03 20:23:38 +03:00
skip_chan_change :
2008-10-23 00:28:45 +04:00
if ( changed & IEEE80211_CONF_CHANGE_POWER )
2009-02-09 10:57:03 +03:00
sc - > config . txpowlimit = 2 * conf - > power_level ;
2008-08-04 11:16:41 +04:00
2009-07-15 04:22:53 +04:00
if ( disable_radio ) {
2009-09-07 15:52:26 +04:00
DPRINTF ( sc - > sc_ah , ATH_DBG_CONFIG , " idle: disabling radio \n " ) ;
2009-07-15 04:22:53 +04:00
ath_radio_disable ( sc ) ;
}
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 | \
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
{
2009-03-03 20:23:28 +03:00
struct ath_wiphy * aphy = hw - > priv ;
struct ath_softc * sc = aphy - > sc ;
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
2009-09-07 15:52:26 +04:00
DPRINTF ( 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
2008-09-10 17:19:27 +04:00
static void ath9k_sta_notify ( struct ieee80211_hw * hw ,
struct ieee80211_vif * vif ,
enum sta_notify_cmd cmd ,
2008-09-11 02:02:02 +04:00
struct ieee80211_sta * sta )
2008-09-10 17:19:27 +04:00
{
2009-03-03 20:23:28 +03:00
struct ath_wiphy * aphy = hw - > priv ;
struct ath_softc * sc = aphy - > sc ;
2008-08-04 11:16:41 +04:00
2008-09-10 17:19:27 +04:00
switch ( cmd ) {
case STA_NOTIFY_ADD :
2008-10-29 07:46:06 +03:00
ath_node_attach ( sc , sta ) ;
2008-09-10 17:19:27 +04:00
break ;
case STA_NOTIFY_REMOVE :
2008-10-29 07:43:31 +03:00
ath_node_detach ( sc , sta ) ;
2008-09-10 17:19:27 +04:00
break ;
default :
break ;
}
2008-08-04 11:16:41 +04:00
}
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
{
2009-03-03 20:23:28 +03:00
struct ath_wiphy * aphy = hw - > priv ;
struct ath_softc * sc = aphy - > sc ;
2008-09-10 17:19:27 +04:00
struct ath9k_tx_queue_info qi ;
int ret = 0 , qnum ;
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
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 ;
qnum = ath_get_hal_qnum ( queue , sc ) ;
2008-08-04 11:16:41 +04:00
2009-09-07 15:52:26 +04:00
DPRINTF ( sc - > sc_ah , ATH_DBG_CONFIG ,
2008-11-28 19:48:05 +03:00
" Configure tx [queue/halq] [%d/%d], "
2008-09-10 17:19:27 +04:00
" aifs: %d, cw_min: %d, cw_max: %d, txop: %d \n " ,
2008-11-28 19:48:05 +03:00
queue , qnum , params - > aifs , params - > cw_min ,
params - > cw_max , params - > txop ) ;
2008-08-04 11:16:41 +04:00
2008-09-10 17:19:27 +04:00
ret = ath_txq_update ( sc , qnum , & qi ) ;
if ( ret )
2009-09-07 15:52:26 +04:00
DPRINTF ( sc - > sc_ah , ATH_DBG_FATAL , " TXQ Update failed \n " ) ;
2008-08-04 11:16:41 +04:00
2009-02-04 05:40:07 +03:00
mutex_unlock ( & sc - > mutex ) ;
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 )
{
2009-03-03 20:23:28 +03:00
struct ath_wiphy * aphy = hw - > priv ;
struct ath_softc * sc = aphy - > sc ;
2008-09-10 17:19:27 +04:00
int ret = 0 ;
2008-08-04 11:16:41 +04:00
2009-02-24 14:42:01 +03:00
if ( modparam_nohwcrypt )
return - ENOSPC ;
2009-02-04 05:40:07 +03:00
mutex_lock ( & sc - > mutex ) ;
2009-01-20 08:47:08 +03:00
ath9k_ps_wakeup ( sc ) ;
2009-09-07 15:52:26 +04:00
DPRINTF ( sc - > sc_ah , 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 :
2009-02-26 12:18:46 +03:00
ret = ath_key_config ( sc , 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 ;
if ( key - > alg = = ALG_TKIP )
key - > flags | = IEEE80211_KEY_FLAG_GENERATE_MMIC ;
2009-01-08 14:32:13 +03:00
if ( sc - > sc_ah - > sw_mgmt_crypto & & key - > alg = = ALG_CCMP )
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 :
ath_key_delete ( sc , key ) ;
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 ;
}
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 )
{
2009-03-03 20:23:28 +03:00
struct ath_wiphy * aphy = hw - > priv ;
struct ath_softc * sc = aphy - > sc ;
2009-04-23 18:13:26 +04:00
struct ath_hw * ah = sc - > sc_ah ;
struct ath_vif * avp = ( void * ) vif - > drv_priv ;
u32 rfilt = 0 ;
int error , i ;
2008-08-04 11:16:41 +04:00
2009-02-04 05:40:07 +03:00
mutex_lock ( & sc - > mutex ) ;
2009-04-23 18:13:26 +04:00
/*
* TODO : Need to decide which hw opmode to use for
* multi - interface cases
* XXX : This belongs into add_interface !
*/
if ( vif - > type = = NL80211_IFTYPE_AP & &
ah - > opmode ! = NL80211_IFTYPE_AP ) {
ah - > opmode = NL80211_IFTYPE_STATION ;
ath9k_hw_setopmode ( ah ) ;
memcpy ( sc - > curbssid , sc - > sc_ah - > macaddr , ETH_ALEN ) ;
sc - > curaid = 0 ;
ath9k_hw_write_associd ( sc ) ;
/* Request full reset to get hw opmode changed properly */
sc - > sc_flags | = SC_OP_FULL_RESET ;
}
if ( ( changed & BSS_CHANGED_BSSID ) & &
! is_zero_ether_addr ( bss_conf - > bssid ) ) {
switch ( vif - > type ) {
case NL80211_IFTYPE_STATION :
case NL80211_IFTYPE_ADHOC :
case NL80211_IFTYPE_MESH_POINT :
/* Set BSSID */
memcpy ( sc - > curbssid , bss_conf - > bssid , ETH_ALEN ) ;
memcpy ( avp - > bssid , bss_conf - > bssid , ETH_ALEN ) ;
sc - > curaid = 0 ;
ath9k_hw_write_associd ( sc ) ;
/* Set aggregation protection mode parameters */
sc - > config . ath_aggr_prot = 0 ;
2009-09-07 15:52:26 +04:00
DPRINTF ( sc - > sc_ah , ATH_DBG_CONFIG ,
2009-04-23 18:13:26 +04:00
" RX filter 0x%x bssid %pM aid 0x%x \n " ,
rfilt , sc - > curbssid , sc - > curaid ) ;
/* need to reconfigure the beacon */
sc - > sc_flags & = ~ SC_OP_BEACONS ;
break ;
default :
break ;
}
}
if ( ( vif - > type = = NL80211_IFTYPE_ADHOC ) | |
( vif - > type = = NL80211_IFTYPE_AP ) | |
( vif - > type = = NL80211_IFTYPE_MESH_POINT ) ) {
if ( ( changed & BSS_CHANGED_BEACON ) | |
( changed & BSS_CHANGED_BEACON_ENABLED & &
bss_conf - > enable_beacon ) ) {
/*
* Allocate and setup the beacon frame .
*
* Stop any previous beacon DMA . This may be
* necessary , for example , when an ibss merge
* causes reconfiguration ; we may be called
* with beacon transmission active .
*/
ath9k_hw_stoptxdma ( sc - > sc_ah , sc - > beacon . beaconq ) ;
error = ath_beacon_alloc ( aphy , vif ) ;
if ( ! error )
ath_beacon_config ( sc , vif ) ;
}
}
/* Check for WLAN_CAPABILITY_PRIVACY ? */
if ( ( avp - > av_opmode ! = NL80211_IFTYPE_STATION ) ) {
for ( i = 0 ; i < IEEE80211_WEP_NKID ; i + + )
if ( ath9k_hw_keyisvalid ( sc - > sc_ah , ( u16 ) i ) )
ath9k_hw_keysetmac ( sc - > sc_ah ,
( u16 ) i ,
sc - > curbssid ) ;
}
/* Only legacy IBSS for now */
if ( vif - > type = = NL80211_IFTYPE_ADHOC )
ath_update_chainmask ( sc , 0 ) ;
2008-09-10 17:19:27 +04:00
if ( changed & BSS_CHANGED_ERP_PREAMBLE ) {
2009-09-07 15:52:26 +04:00
DPRINTF ( sc - > sc_ah , ATH_DBG_CONFIG , " BSS Changed PREAMBLE %d \n " ,
2008-09-10 17:19:27 +04:00
bss_conf - > use_short_preamble ) ;
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 ) {
2009-09-07 15:52:26 +04:00
DPRINTF ( sc - > sc_ah , ATH_DBG_CONFIG , " BSS Changed CTS PROT %d \n " ,
2008-09-10 17:19:27 +04:00
bss_conf - > use_cts_prot ) ;
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
2008-09-10 17:19:27 +04:00
if ( changed & BSS_CHANGED_ASSOC ) {
2009-09-07 15:52:26 +04:00
DPRINTF ( sc - > sc_ah , ATH_DBG_CONFIG , " BSS Changed ASSOC %d \n " ,
2008-09-10 17:19:27 +04:00
bss_conf - > assoc ) ;
2008-10-29 07:46:06 +03:00
ath9k_bss_assoc_info ( sc , vif , bss_conf ) ;
2008-09-10 17:19:27 +04:00
}
2009-02-04 05:40:07 +03:00
2009-04-23 18:10:04 +04:00
/*
* The HW TSF has to be reset when the beacon interval changes .
* We set the flag here , and ath_beacon_config_ap ( ) would take this
* into account when it gets called through the subsequent
* config_interface ( ) call - with IFCC_BEACON in the changed field .
*/
if ( changed & BSS_CHANGED_BEACON_INT ) {
sc - > sc_flags | = SC_OP_TSF_RESET ;
sc - > beacon_interval = bss_conf - > beacon_int ;
}
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 u64 ath9k_get_tsf ( struct ieee80211_hw * hw )
{
u64 tsf ;
2009-03-03 20:23:28 +03:00
struct ath_wiphy * aphy = hw - > priv ;
struct ath_softc * sc = aphy - > sc ;
2008-08-04 11:16:41 +04:00
2009-02-04 05:40:07 +03:00
mutex_lock ( & sc - > mutex ) ;
tsf = ath9k_hw_gettsf64 ( sc - > sc_ah ) ;
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 )
{
2009-03-03 20:23:28 +03:00
struct ath_wiphy * aphy = hw - > priv ;
struct ath_softc * sc = aphy - > sc ;
2009-01-24 09:09:59 +03:00
2009-02-04 05:40:07 +03:00
mutex_lock ( & sc - > mutex ) ;
ath9k_hw_settsf64 ( sc - > sc_ah , tsf ) ;
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 )
{
2009-03-03 20:23:28 +03:00
struct ath_wiphy * aphy = hw - > priv ;
struct ath_softc * sc = aphy - > sc ;
2008-08-25 19:17:29 +04:00
2009-02-04 05:40:07 +03:00
mutex_lock ( & sc - > mutex ) ;
ath9k_hw_reset_tsf ( sc - > sc_ah ) ;
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-02-04 05:40:07 +03:00
enum ieee80211_ampdu_mlme_action action ,
struct ieee80211_sta * sta ,
u16 tid , u16 * ssn )
2008-09-10 17:19:27 +04:00
{
2009-03-03 20:23:28 +03:00
struct ath_wiphy * aphy = hw - > priv ;
struct ath_softc * sc = aphy - > sc ;
2008-09-10 17:19:27 +04:00
int ret = 0 ;
2008-08-04 11:16:41 +04:00
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 :
2009-07-23 14:02:37 +04:00
ath_tx_aggr_start ( sc , sta , tid , ssn ) ;
ieee80211_start_tx_ba_cb_irqsafe ( hw , sta - > addr , tid ) ;
2008-09-10 17:19:27 +04:00
break ;
case IEEE80211_AMPDU_TX_STOP :
2009-07-23 14:02:37 +04:00
ath_tx_aggr_stop ( sc , sta , tid ) ;
2008-09-11 02:02:02 +04:00
ieee80211_stop_tx_ba_cb_irqsafe ( hw , sta - > addr , tid ) ;
2008-09-10 17:19:27 +04:00
break ;
2009-03-23 19:28:39 +03:00
case IEEE80211_AMPDU_TX_OPERATIONAL :
2008-10-29 07:49:28 +03:00
ath_tx_aggr_resume ( sc , sta , tid ) ;
break ;
2008-09-10 17:19:27 +04:00
default :
2009-09-07 15:52:26 +04:00
DPRINTF ( sc - > sc_ah , ATH_DBG_FATAL , " Unknown AMPDU action \n " ) ;
2008-09-10 17:19:27 +04:00
}
return ret ;
2008-08-04 11:16:41 +04:00
}
2009-03-03 07:46:45 +03:00
static void ath9k_sw_scan_start ( struct ieee80211_hw * hw )
{
2009-03-03 20:23:28 +03:00
struct ath_wiphy * aphy = hw - > priv ;
struct ath_softc * sc = aphy - > sc ;
2009-03-03 07:46:45 +03:00
2009-08-21 10:30:28 +04:00
mutex_lock ( & sc - > mutex ) ;
2009-03-03 20:23:38 +03:00
if ( ath9k_wiphy_scanning ( sc ) ) {
printk ( KERN_DEBUG " ath9k: Two wiphys trying to scan at the "
" same time \n " ) ;
/*
* Do not allow the concurrent scanning state for now . This
* could be improved with scanning control moved into ath9k .
*/
2009-08-21 10:30:28 +04:00
mutex_unlock ( & sc - > mutex ) ;
2009-03-03 20:23:38 +03:00
return ;
}
aphy - > state = ATH_WIPHY_SCAN ;
ath9k_wiphy_pause_all_forced ( sc , aphy ) ;
2009-06-24 17:26:41 +04:00
spin_lock_bh ( & sc - > ani_lock ) ;
2009-03-03 07:46:45 +03:00
sc - > sc_flags | = SC_OP_SCANNING ;
2009-06-24 17:26:41 +04:00
spin_unlock_bh ( & sc - > ani_lock ) ;
2009-08-21 10:30:28 +04:00
mutex_unlock ( & sc - > mutex ) ;
2009-03-03 07:46:45 +03:00
}
static void ath9k_sw_scan_complete ( struct ieee80211_hw * hw )
{
2009-03-03 20:23:28 +03:00
struct ath_wiphy * aphy = hw - > priv ;
struct ath_softc * sc = aphy - > sc ;
2009-03-03 07:46:45 +03:00
2009-08-21 10:30:28 +04:00
mutex_lock ( & sc - > mutex ) ;
2009-06-24 17:26:41 +04:00
spin_lock_bh ( & sc - > ani_lock ) ;
2009-03-03 20:23:38 +03:00
aphy - > state = ATH_WIPHY_ACTIVE ;
2009-03-03 07:46:45 +03:00
sc - > sc_flags & = ~ SC_OP_SCANNING ;
2009-04-13 20:26:36 +04:00
sc - > sc_flags | = SC_OP_FULL_RESET ;
2009-06-24 17:26:41 +04:00
spin_unlock_bh ( & sc - > ani_lock ) ;
2009-09-02 14:20:55 +04:00
ath_beacon_config ( sc , NULL ) ;
2009-08-21 10:30:28 +04:00
mutex_unlock ( & sc - > mutex ) ;
2009-03-03 07:46:45 +03:00
}
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 ,
. remove_interface = ath9k_remove_interface ,
. config = ath9k_config ,
. configure_filter = ath9k_configure_filter ,
. sta_notify = ath9k_sta_notify ,
. 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 ,
2009-03-03 07:46:45 +03:00
. sw_scan_start = ath9k_sw_scan_start ,
. sw_scan_complete = ath9k_sw_scan_complete ,
2009-06-13 13:20:26 +04:00
. rfkill_poll = ath9k_rfkill_poll_state ,
2008-09-10 17:19:27 +04:00
} ;
2008-11-07 00:26:49 +03:00
static struct {
u32 version ;
const char * name ;
} ath_mac_bb_names [ ] = {
{ AR_SREV_VERSION_5416_PCI , " 5416 " } ,
{ AR_SREV_VERSION_5416_PCIE , " 5418 " } ,
{ AR_SREV_VERSION_9100 , " 9100 " } ,
{ AR_SREV_VERSION_9160 , " 9160 " } ,
{ AR_SREV_VERSION_9280 , " 9280 " } ,
2009-07-23 09:29:57 +04:00
{ AR_SREV_VERSION_9285 , " 9285 " } ,
{ AR_SREV_VERSION_9287 , " 9287 " }
2008-11-07 00:26:49 +03:00
} ;
static struct {
u16 version ;
const char * name ;
} ath_rf_names [ ] = {
{ 0 , " 5133 " } ,
{ AR_RAD5133_SREV_MAJOR , " 5133 " } ,
{ AR_RAD5122_SREV_MAJOR , " 5122 " } ,
{ AR_RAD2133_SREV_MAJOR , " 2133 " } ,
{ AR_RAD2122_SREV_MAJOR , " 2122 " }
} ;
/*
* Return the MAC / BB name . " ???? " is returned if the MAC / BB is unknown .
*/
2009-01-14 22:17:06 +03:00
const char *
2008-11-07 00:26:49 +03:00
ath_mac_bb_name ( u32 mac_bb_version )
{
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( ath_mac_bb_names ) ; i + + ) {
if ( ath_mac_bb_names [ i ] . version = = mac_bb_version ) {
return ath_mac_bb_names [ i ] . name ;
}
}
return " ???? " ;
}
/*
* Return the RF name . " ???? " is returned if the RF is unknown .
*/
2009-01-14 22:17:06 +03:00
const char *
2008-11-07 00:26:49 +03:00
ath_rf_name ( u16 rf_version )
{
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( ath_rf_names ) ; i + + ) {
if ( ath_rf_names [ i ] . version = = rf_version ) {
return ath_rf_names [ i ] . name ;
}
}
return " ???? " ;
}
2009-01-14 22:17:06 +03:00
static int __init ath9k_init ( void )
2008-08-04 11:16:41 +04:00
{
2008-12-16 10:07:38 +03:00
int error ;
/* Register rate control algorithm */
error = ath_rate_control_register ( ) ;
if ( error ! = 0 ) {
printk ( KERN_ERR
2009-01-26 18:30:03 +03:00
" ath9k: Unable to register rate control "
" algorithm: %d \n " ,
2008-12-16 10:07:38 +03:00
error ) ;
2009-01-14 22:17:06 +03:00
goto err_out ;
2008-12-16 10:07:38 +03:00
}
2009-03-05 18:55:18 +03:00
error = ath9k_debug_create_root ( ) ;
if ( error ) {
printk ( KERN_ERR
" ath9k: Unable to create debugfs root: %d \n " ,
error ) ;
goto err_rate_unregister ;
}
2009-01-14 22:17:06 +03:00
error = ath_pci_init ( ) ;
if ( error < 0 ) {
2008-08-04 11:16:41 +04:00
printk ( KERN_ERR
2009-01-26 18:30:03 +03:00
" ath9k: No PCI devices found, driver not installed. \n " ) ;
2009-01-14 22:17:06 +03:00
error = - ENODEV ;
2009-03-05 18:55:18 +03:00
goto err_remove_root ;
2008-08-04 11:16:41 +04:00
}
2009-01-14 22:17:07 +03:00
error = ath_ahb_init ( ) ;
if ( error < 0 ) {
error = - ENODEV ;
goto err_pci_exit ;
}
2008-08-04 11:16:41 +04:00
return 0 ;
2009-01-14 22:17:06 +03:00
2009-01-14 22:17:07 +03:00
err_pci_exit :
ath_pci_exit ( ) ;
2009-03-05 18:55:18 +03:00
err_remove_root :
ath9k_debug_remove_root ( ) ;
2009-01-14 22:17:06 +03:00
err_rate_unregister :
ath_rate_control_unregister ( ) ;
err_out :
return error ;
2008-08-04 11:16:41 +04:00
}
2009-01-14 22:17:06 +03:00
module_init ( ath9k_init ) ;
2008-08-04 11:16:41 +04:00
2009-01-14 22:17:06 +03:00
static void __exit ath9k_exit ( void )
2008-08-04 11:16:41 +04:00
{
2009-01-14 22:17:07 +03:00
ath_ahb_exit ( ) ;
2009-01-14 22:17:06 +03:00
ath_pci_exit ( ) ;
2009-03-05 18:55:18 +03:00
ath9k_debug_remove_root ( ) ;
2008-12-16 10:07:38 +03:00
ath_rate_control_unregister ( ) ;
2008-11-28 19:48:05 +03:00
printk ( KERN_INFO " %s: Driver unloaded \n " , dev_info ) ;
2008-08-04 11:16:41 +04:00
}
2009-01-14 22:17:06 +03:00
module_exit ( ath9k_exit ) ;