2008-08-04 00:16:41 -07:00
/*
2009-03-13 09:07:23 +05:30
* Copyright ( c ) 2008 - 2009 Atheros Communications Inc .
2008-08-04 00:16:41 -07:00
*
* Permission to use , copy , modify , and / or distribute this software for any
* purpose with or without fee is hereby granted , provided that the above
* copyright notice and this permission notice appear in all copies .
*
* THE SOFTWARE IS PROVIDED " AS IS " AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS . IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL , DIRECT , INDIRECT , OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE , DATA OR PROFITS , WHETHER IN AN
* ACTION OF CONTRACT , NEGLIGENCE OR OTHER TORTIOUS ACTION , ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE .
*/
2009-02-09 13:26:54 +05:30
# include "ath9k.h"
2008-08-04 00:16:41 -07:00
2009-03-03 10:16:54 +05:30
# define FUDGE 2
2008-08-04 00:16:41 -07:00
/*
* This function will modify certain transmit queue properties depending on
* the operating mode of the station ( AP or AdHoc ) . Parameters are AIFS
* settings and channel width min / max
*/
static int ath_beaconq_config ( struct ath_softc * sc )
{
2009-02-09 13:27:12 +05:30
struct ath_hw * ah = sc - > sc_ah ;
2009-09-13 02:42:02 -07:00
struct ath_common * common = ath9k_hw_common ( ah ) ;
2008-08-07 10:53:10 +05:30
struct ath9k_tx_queue_info qi ;
2008-08-04 00:16:41 -07:00
2008-12-07 21:44:03 +05:30
ath9k_hw_get_txq_props ( ah , sc - > beacon . beaconq , & qi ) ;
2009-02-09 13:27:26 +05:30
if ( sc - > sc_ah - > opmode = = NL80211_IFTYPE_AP ) {
2008-08-04 00:16:41 -07:00
/* Always burst out beacon and CAB traffic. */
qi . tqi_aifs = 1 ;
qi . tqi_cwmin = 0 ;
qi . tqi_cwmax = 0 ;
} else {
/* Adhoc mode; important thing is to use 2x cwmin. */
2008-12-07 21:44:03 +05:30
qi . tqi_aifs = sc - > beacon . beacon_qi . tqi_aifs ;
qi . tqi_cwmin = 2 * sc - > beacon . beacon_qi . tqi_cwmin ;
qi . tqi_cwmax = sc - > beacon . beacon_qi . tqi_cwmax ;
2008-08-04 00:16:41 -07:00
}
2008-12-07 21:44:03 +05:30
if ( ! ath9k_hw_set_txq_props ( ah , sc - > beacon . beaconq , & qi ) ) {
2009-09-13 02:42:02 -07:00
ath_print ( common , ATH_DBG_FATAL ,
" Unable to update h/w beacon queue parameters \n " ) ;
2008-08-04 00:16:41 -07:00
return 0 ;
} else {
2009-03-03 10:16:51 +05:30
ath9k_hw_resettxqueue ( ah , sc - > beacon . beaconq ) ;
2008-08-04 00:16:41 -07:00
return 1 ;
}
}
/*
* Associates the beacon frame buffer with a transmit descriptor . Will set
* up all required antenna switch parameters , rate codes , and channel flags .
* Beacons are always sent out at the lowest rate , and are not retried .
*/
2009-03-03 10:16:51 +05:30
static void ath_beacon_setup ( struct ath_softc * sc , struct ath_vif * avp ,
struct ath_buf * bf )
2008-08-04 00:16:41 -07:00
{
2009-03-30 15:28:36 +05:30
struct sk_buff * skb = bf - > bf_mpdu ;
2009-02-09 13:27:12 +05:30
struct ath_hw * ah = sc - > sc_ah ;
2009-09-13 21:07:07 -07:00
struct ath_common * common = ath9k_hw_common ( ah ) ;
2008-08-04 00:16:41 -07:00
struct ath_desc * ds ;
2008-09-17 10:15:09 +05:30
struct ath9k_11n_rate_series series [ 4 ] ;
2009-03-03 10:16:51 +05:30
int flags , antenna , ctsrate = 0 , ctsduration = 0 ;
2009-11-23 22:21:01 +01:00
struct ieee80211_supported_band * sband ;
u8 rate = 0 ;
2008-08-04 00:16:41 -07:00
ds = bf - > bf_desc ;
flags = ATH9K_TXDESC_NOACK ;
2009-03-20 22:59:59 -04:00
if ( ( ( sc - > sc_ah - > opmode = = NL80211_IFTYPE_ADHOC ) | |
( sc - > sc_ah - > opmode = = NL80211_IFTYPE_MESH_POINT ) ) & &
2009-02-09 13:27:26 +05:30
( ah - > caps . hw_caps & ATH9K_HW_CAP_VEOL ) ) {
2008-08-04 00:16:41 -07:00
ds - > ds_link = bf - > bf_daddr ; /* self-linked */
flags | = ATH9K_TXDESC_VEOL ;
/* Let hardware handle antenna switching. */
antenna = 0 ;
} else {
ds - > ds_link = 0 ;
/*
* Switch antenna every beacon .
2009-03-03 10:16:51 +05:30
* Should only switch every beacon period , not for every SWBA
* XXX assumes two antennae
2008-08-04 00:16:41 -07:00
*/
2009-02-09 13:27:03 +05:30
antenna = ( ( sc - > beacon . ast_be_xmit / sc - > nbcnvifs ) & 1 ? 2 : 1 ) ;
2008-08-04 00:16:41 -07:00
}
ds - > ds_data = bf - > bf_buf_addr ;
2009-11-23 22:21:01 +01:00
sband = & sc - > sbands [ common - > hw - > conf . channel - > band ] ;
rate = sband - > bitrates [ 0 ] . hw_value ;
2008-08-11 14:05:08 +05:30
if ( sc - > sc_flags & SC_OP_PREAMBLE_SHORT )
2009-11-23 22:21:01 +01:00
rate | = sband - > bitrates [ 0 ] . hw_value_short ;
2009-03-03 10:16:51 +05:30
ath9k_hw_set11n_txdesc ( ah , ds , skb - > len + FCS_LEN ,
ATH9K_PKT_TYPE_BEACON ,
MAX_RATE_POWER ,
ATH9K_TXKEYIX_INVALID ,
ATH9K_KEY_TYPE_CLEAR ,
flags ) ;
2008-08-04 00:16:41 -07:00
/* NB: beacon's BufLen must be a multiple of 4 bytes */
2009-03-03 10:16:51 +05:30
ath9k_hw_filltxdesc ( ah , ds , roundup ( skb - > len , 4 ) ,
true , true , ds ) ;
2008-08-04 00:16:41 -07:00
2008-10-03 15:45:25 -07:00
memset ( series , 0 , sizeof ( struct ath9k_11n_rate_series ) * 4 ) ;
2008-08-04 00:16:41 -07:00
series [ 0 ] . Tries = 1 ;
series [ 0 ] . Rate = rate ;
2009-09-13 21:07:07 -07:00
series [ 0 ] . ChSel = common - > tx_chainmask ;
2008-08-04 00:16:41 -07:00
series [ 0 ] . RateFlags = ( ctsrate ) ? ATH9K_RATESERIES_RTS_CTS : 0 ;
2009-03-03 10:16:51 +05:30
ath9k_hw_set11n_ratescenario ( ah , ds , ds , 0 , ctsrate , ctsduration ,
series , 4 , 0 ) ;
2008-08-04 00:16:41 -07:00
}
2009-03-03 19:23:29 +02:00
static struct ath_buf * ath_beacon_generate ( struct ieee80211_hw * hw ,
2009-03-03 19:23:26 +02:00
struct ieee80211_vif * vif )
2008-08-04 00:16:41 -07:00
{
2009-03-03 19:23:29 +02:00
struct ath_wiphy * aphy = hw - > priv ;
struct ath_softc * sc = aphy - > sc ;
2009-09-13 02:42:02 -07:00
struct ath_common * common = ath9k_hw_common ( sc - > sc_ah ) ;
2008-08-04 00:16:41 -07:00
struct ath_buf * bf ;
2009-02-09 13:27:03 +05:30
struct ath_vif * avp ;
2008-08-04 00:16:41 -07:00
struct sk_buff * skb ;
struct ath_txq * cabq ;
2008-08-11 14:01:50 +03:00
struct ieee80211_tx_info * info ;
2008-09-17 10:15:09 +05:30
int cabq_depth ;
2009-03-03 19:23:31 +02:00
if ( aphy - > state ! = ATH_WIPHY_ACTIVE )
return NULL ;
2008-10-29 10:16:06 +05:30
avp = ( void * ) vif - > drv_priv ;
2008-12-07 21:44:03 +05:30
cabq = sc - > beacon . cabq ;
2008-08-04 00:16:41 -07:00
2009-03-30 15:28:25 +05:30
if ( avp - > av_bcbuf = = NULL )
2008-08-04 00:16:41 -07:00
return NULL ;
2008-09-17 10:15:09 +05:30
2009-03-03 10:16:51 +05:30
/* Release the old beacon first */
2008-08-04 00:16:41 -07:00
bf = avp - > av_bcbuf ;
2009-03-30 15:28:36 +05:30
skb = bf - > bf_mpdu ;
2008-08-11 14:01:48 +03:00
if ( skb ) {
2009-01-14 20:17:03 +01:00
dma_unmap_single ( sc - > dev , bf - > bf_dmacontext ,
2009-03-03 10:16:51 +05:30
skb - > len , DMA_TO_DEVICE ) ;
2008-12-05 20:42:45 +02:00
dev_kfree_skb_any ( skb ) ;
2008-08-11 14:01:48 +03:00
}
2008-08-04 00:16:41 -07:00
2009-03-03 10:16:51 +05:30
/* Get a new beacon from mac80211 */
2009-03-03 19:23:29 +02:00
skb = ieee80211_beacon_get ( hw , vif ) ;
2008-08-11 14:01:48 +03:00
bf - > bf_mpdu = skb ;
if ( skb = = NULL )
return NULL ;
2009-03-12 21:53:23 +02:00
( ( struct ieee80211_mgmt * ) skb - > data ) - > u . beacon . timestamp =
avp - > tsf_adjust ;
2008-09-17 10:15:09 +05:30
2008-08-11 14:01:50 +03:00
info = IEEE80211_SKB_CB ( skb ) ;
if ( info - > flags & IEEE80211_TX_CTL_ASSIGN_SEQ ) {
/*
* TODO : make sure the seq # gets assigned properly ( vs . other
* TX frames )
*/
2008-09-17 10:15:09 +05:30
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) skb - > data ;
2008-12-07 21:44:03 +05:30
sc - > tx . seq_no + = 0x10 ;
2008-08-11 14:01:50 +03:00
hdr - > seq_ctrl & = cpu_to_le16 ( IEEE80211_SCTL_FRAG ) ;
2008-12-07 21:44:03 +05:30
hdr - > seq_ctrl | = cpu_to_le16 ( sc - > tx . seq_no ) ;
2008-08-11 14:01:50 +03:00
}
2008-09-17 10:15:09 +05:30
2008-08-11 14:01:48 +03:00
bf - > bf_buf_addr = bf - > bf_dmacontext =
2009-01-14 20:17:03 +01:00
dma_map_single ( sc - > dev , skb - > data ,
2009-03-03 10:16:51 +05:30
skb - > len , DMA_TO_DEVICE ) ;
2009-01-14 20:17:03 +01:00
if ( unlikely ( dma_mapping_error ( sc - > dev , bf - > bf_buf_addr ) ) ) {
2008-12-03 03:35:29 -08:00
dev_kfree_skb_any ( skb ) ;
bf - > bf_mpdu = NULL ;
2009-09-13 02:42:02 -07:00
ath_print ( common , ATH_DBG_FATAL ,
" dma_mapping_error on beaconing \n " ) ;
2008-12-03 03:35:29 -08:00
return NULL ;
}
2008-08-04 00:16:41 -07:00
2009-03-03 19:23:29 +02:00
skb = ieee80211_get_buffered_bc ( hw , vif ) ;
2008-08-04 00:16:41 -07:00
/*
* if the CABQ traffic from previous DTIM is pending and the current
* beacon is also a DTIM .
2009-02-09 13:27:03 +05:30
* 1 ) if there is only one vif let the cab traffic continue .
* 2 ) if there are more than one vif and we are using staggered
2008-08-04 00:16:41 -07:00
* beacons , then drain the cabq by dropping all the frames in
2009-02-09 13:27:03 +05:30
* the cabq so that the current vifs cab traffic can be scheduled .
2008-08-04 00:16:41 -07:00
*/
spin_lock_bh ( & cabq - > axq_lock ) ;
cabq_depth = cabq - > axq_depth ;
spin_unlock_bh ( & cabq - > axq_lock ) ;
2008-08-22 17:31:33 +03:00
if ( skb & & cabq_depth ) {
2009-02-09 13:27:03 +05:30
if ( sc - > nvifs > 1 ) {
2009-09-13 02:42:02 -07:00
ath_print ( common , ATH_DBG_BEACON ,
" Flushing previous cabq traffic \n " ) ;
2009-03-03 10:16:51 +05:30
ath_draintxq ( sc , cabq , false ) ;
2008-08-04 00:16:41 -07:00
}
}
ath_beacon_setup ( sc , avp , bf ) ;
2008-08-22 17:31:33 +03:00
while ( skb ) {
2009-03-03 19:23:29 +02:00
ath_tx_cabq ( hw , skb ) ;
skb = ieee80211_get_buffered_bc ( hw , vif ) ;
2008-08-22 17:31:33 +03:00
}
2008-08-04 00:16:41 -07:00
return bf ;
}
/*
* Startup beacon transmission for adhoc mode when they are sent entirely
* by the hardware using the self - linked descriptor + veol trick .
*/
2009-03-03 19:23:26 +02:00
static void ath_beacon_start_adhoc ( struct ath_softc * sc ,
struct ieee80211_vif * vif )
2008-08-04 00:16:41 -07:00
{
2009-02-09 13:27:12 +05:30
struct ath_hw * ah = sc - > sc_ah ;
2009-09-13 02:42:02 -07:00
struct ath_common * common = ath9k_hw_common ( ah ) ;
2008-08-04 00:16:41 -07:00
struct ath_buf * bf ;
2009-02-09 13:27:03 +05:30
struct ath_vif * avp ;
2008-08-04 00:16:41 -07:00
struct sk_buff * skb ;
2008-10-29 10:16:06 +05:30
avp = ( void * ) vif - > drv_priv ;
2008-08-04 00:16:41 -07:00
2009-03-03 10:16:51 +05:30
if ( avp - > av_bcbuf = = NULL )
2008-08-04 00:16:41 -07:00
return ;
2009-03-03 10:16:51 +05:30
2008-08-04 00:16:41 -07:00
bf = avp - > av_bcbuf ;
2009-03-30 15:28:36 +05:30
skb = bf - > bf_mpdu ;
2008-08-04 00:16:41 -07:00
ath_beacon_setup ( sc , avp , bf ) ;
/* NB: caller is known to have already stopped tx dma */
2008-12-07 21:44:03 +05:30
ath9k_hw_puttxbuf ( ah , sc - > beacon . beaconq , bf - > bf_daddr ) ;
ath9k_hw_txstart ( ah , sc - > beacon . beaconq ) ;
2009-09-13 02:42:02 -07:00
ath_print ( common , ATH_DBG_BEACON , " TXDP%u = %llx (%p) \n " ,
sc - > beacon . beaconq , ito64 ( bf - > bf_daddr ) , bf - > bf_desc ) ;
2008-08-04 00:16:41 -07:00
}
2009-03-03 19:23:29 +02:00
int ath_beacon_alloc ( struct ath_wiphy * aphy , struct ieee80211_vif * vif )
2008-08-04 00:16:41 -07:00
{
2009-03-03 19:23:29 +02:00
struct ath_softc * sc = aphy - > sc ;
2009-09-13 02:42:02 -07:00
struct ath_common * common = ath9k_hw_common ( sc - > sc_ah ) ;
2009-02-09 13:27:03 +05:30
struct ath_vif * avp ;
2008-08-04 00:16:41 -07:00
struct ath_buf * bf ;
struct sk_buff * skb ;
2008-09-17 10:15:36 +05:30
__le64 tstamp ;
2008-08-04 00:16:41 -07:00
2008-10-29 10:16:06 +05:30
avp = ( void * ) vif - > drv_priv ;
2008-08-04 00:16:41 -07:00
/* Allocate a beacon descriptor if we haven't done so. */
if ( ! avp - > av_bcbuf ) {
2008-09-17 10:15:09 +05:30
/* Allocate beacon state for hostap/ibss. We know
* a buffer is available . */
2008-12-07 21:44:03 +05:30
avp - > av_bcbuf = list_first_entry ( & sc - > beacon . bbuf ,
2008-09-17 10:15:09 +05:30
struct ath_buf , list ) ;
2008-08-04 00:16:41 -07:00
list_del ( & avp - > av_bcbuf - > list ) ;
2009-02-09 13:27:26 +05:30
if ( sc - > sc_ah - > opmode = = NL80211_IFTYPE_AP | |
! ( sc - > sc_ah - > caps . hw_caps & ATH9K_HW_CAP_VEOL ) ) {
2008-08-04 00:16:41 -07:00
int slot ;
/*
2009-02-09 13:27:03 +05:30
* Assign the vif to a beacon xmit slot . As
2008-08-04 00:16:41 -07:00
* above , this cannot fail to find one .
*/
avp - > av_bslot = 0 ;
for ( slot = 0 ; slot < ATH_BCBUF ; slot + + )
2009-03-03 19:23:26 +02:00
if ( sc - > beacon . bslot [ slot ] = = NULL ) {
2008-08-04 00:16:41 -07:00
/*
* XXX hack , space out slots to better
* deal with misses
*/
if ( slot + 1 < ATH_BCBUF & &
2009-03-03 19:23:26 +02:00
sc - > beacon . bslot [ slot + 1 ] = = NULL ) {
2008-08-04 00:16:41 -07:00
avp - > av_bslot = slot + 1 ;
break ;
}
avp - > av_bslot = slot ;
/* NB: keep looking for a double slot */
}
2009-03-03 19:23:26 +02:00
BUG_ON ( sc - > beacon . bslot [ avp - > av_bslot ] ! = NULL ) ;
sc - > beacon . bslot [ avp - > av_bslot ] = vif ;
2009-03-03 19:23:29 +02:00
sc - > beacon . bslot_aphy [ avp - > av_bslot ] = aphy ;
2009-02-09 13:27:03 +05:30
sc - > nbcnvifs + + ;
2008-08-04 00:16:41 -07:00
}
}
2009-03-03 10:16:51 +05:30
/* release the previous beacon frame, if it already exists. */
2008-08-04 00:16:41 -07:00
bf = avp - > av_bcbuf ;
if ( bf - > bf_mpdu ! = NULL ) {
2009-03-30 15:28:36 +05:30
skb = bf - > bf_mpdu ;
2009-01-14 20:17:03 +01:00
dma_unmap_single ( sc - > dev , bf - > bf_dmacontext ,
2009-03-03 10:16:51 +05:30
skb - > len , DMA_TO_DEVICE ) ;
2008-08-04 00:16:41 -07:00
dev_kfree_skb_any ( skb ) ;
bf - > bf_mpdu = NULL ;
}
2009-03-03 10:16:51 +05:30
/* NB: the beacon data buffer must be 32-bit aligned. */
2008-10-29 10:16:06 +05:30
skb = ieee80211_beacon_get ( sc - > hw , vif ) ;
2008-08-04 00:16:41 -07:00
if ( skb = = NULL ) {
2009-09-13 02:42:02 -07:00
ath_print ( common , ATH_DBG_BEACON , " cannot get skb \n " ) ;
2008-08-04 00:16:41 -07:00
return - ENOMEM ;
}
2008-09-17 10:15:36 +05:30
tstamp = ( ( struct ieee80211_mgmt * ) skb - > data ) - > u . beacon . timestamp ;
2008-12-07 21:44:03 +05:30
sc - > beacon . bc_tstamp = le64_to_cpu ( tstamp ) ;
2009-03-12 21:53:23 +02:00
/* Calculate a TSF adjustment factor required for staggered beacons. */
2008-08-04 00:16:41 -07:00
if ( avp - > av_bslot > 0 ) {
u64 tsfadjust ;
int intval ;
2009-04-23 16:10:04 +02:00
intval = sc - > beacon_interval ? : ATH_DEFAULT_BINTVAL ;
2008-08-04 00:16:41 -07:00
/*
2009-03-12 21:53:23 +02:00
* Calculate the TSF offset for this beacon slot , i . e . , the
* number of usecs that need to be added to the timestamp field
* in Beacon and Probe Response frames . Beacon slot 0 is
* processed at the correct offset , so it does not require TSF
* adjustment . Other slots are adjusted to get the timestamp
* close to the TBTT for the BSS .
2008-08-04 00:16:41 -07:00
*/
2009-03-12 21:53:23 +02:00
tsfadjust = intval * avp - > av_bslot / ATH_BCBUF ;
avp - > tsf_adjust = cpu_to_le64 ( TU_TO_USEC ( tsfadjust ) ) ;
2008-08-04 00:16:41 -07:00
2009-09-13 02:42:02 -07:00
ath_print ( common , ATH_DBG_BEACON ,
" stagger beacons, bslot %d intval "
" %u tsfadjust %llu \n " ,
avp - > av_bslot , intval , ( unsigned long long ) tsfadjust ) ;
2008-08-04 00:16:41 -07:00
2009-03-12 21:53:23 +02:00
( ( struct ieee80211_mgmt * ) skb - > data ) - > u . beacon . timestamp =
avp - > tsf_adjust ;
} else
avp - > tsf_adjust = cpu_to_le64 ( 0 ) ;
2008-08-04 00:16:41 -07:00
2008-12-03 03:35:29 -08:00
bf - > bf_mpdu = skb ;
2008-08-11 14:01:48 +03:00
bf - > bf_buf_addr = bf - > bf_dmacontext =
2009-01-14 20:17:03 +01:00
dma_map_single ( sc - > dev , skb - > data ,
2009-03-03 10:16:51 +05:30
skb - > len , DMA_TO_DEVICE ) ;
2009-01-14 20:17:03 +01:00
if ( unlikely ( dma_mapping_error ( sc - > dev , bf - > bf_buf_addr ) ) ) {
2008-12-03 03:35:29 -08:00
dev_kfree_skb_any ( skb ) ;
bf - > bf_mpdu = NULL ;
2009-09-13 02:42:02 -07:00
ath_print ( common , ATH_DBG_FATAL ,
" dma_mapping_error on beacon alloc \n " ) ;
2008-12-03 03:35:29 -08:00
return - ENOMEM ;
}
2008-08-04 00:16:41 -07:00
return 0 ;
}
2009-02-09 13:27:03 +05:30
void ath_beacon_return ( struct ath_softc * sc , struct ath_vif * avp )
2008-08-04 00:16:41 -07:00
{
if ( avp - > av_bcbuf ! = NULL ) {
struct ath_buf * bf ;
if ( avp - > av_bslot ! = - 1 ) {
2009-03-03 19:23:26 +02:00
sc - > beacon . bslot [ avp - > av_bslot ] = NULL ;
2009-03-03 19:23:29 +02:00
sc - > beacon . bslot_aphy [ avp - > av_bslot ] = NULL ;
2009-02-09 13:27:03 +05:30
sc - > nbcnvifs - - ;
2008-08-04 00:16:41 -07:00
}
bf = avp - > av_bcbuf ;
if ( bf - > bf_mpdu ! = NULL ) {
2009-03-30 15:28:36 +05:30
struct sk_buff * skb = bf - > bf_mpdu ;
2009-01-14 20:17:03 +01:00
dma_unmap_single ( sc - > dev , bf - > bf_dmacontext ,
2009-03-03 10:16:51 +05:30
skb - > len , DMA_TO_DEVICE ) ;
2008-08-04 00:16:41 -07:00
dev_kfree_skb_any ( skb ) ;
bf - > bf_mpdu = NULL ;
}
2008-12-07 21:44:03 +05:30
list_add_tail ( & bf - > list , & sc - > beacon . bbuf ) ;
2008-08-04 00:16:41 -07:00
avp - > av_bcbuf = NULL ;
}
}
2009-03-03 10:16:51 +05:30
void ath_beacon_tasklet ( unsigned long data )
2008-08-04 00:16:41 -07:00
{
struct ath_softc * sc = ( struct ath_softc * ) data ;
2009-02-09 13:27:12 +05:30
struct ath_hw * ah = sc - > sc_ah ;
2009-09-13 02:42:02 -07:00
struct ath_common * common = ath9k_hw_common ( ah ) ;
2008-08-04 00:16:41 -07:00
struct ath_buf * bf = NULL ;
2009-03-03 19:23:26 +02:00
struct ieee80211_vif * vif ;
2009-03-03 19:23:29 +02:00
struct ath_wiphy * aphy ;
2009-03-03 19:23:26 +02:00
int slot ;
2009-03-03 10:16:53 +05:30
u32 bfaddr , bc = 0 , tsftu ;
2008-08-04 00:16:41 -07:00
u64 tsf ;
u16 intval ;
/*
* Check if the previous beacon has gone out . If
* not don ' t try to post another , skip this period
* and wait for the next . Missed beacons indicate
* a problem and should not occur . If we miss too
* many consecutive beacons reset the device .
*/
2008-12-07 21:44:03 +05:30
if ( ath9k_hw_numtxpending ( ah , sc - > beacon . beaconq ) ! = 0 ) {
sc - > beacon . bmisscnt + + ;
2009-03-03 10:16:53 +05:30
2008-12-07 21:44:03 +05:30
if ( sc - > beacon . bmisscnt < BSTUCK_THRESH ) {
2009-09-13 02:42:02 -07:00
ath_print ( common , ATH_DBG_BEACON ,
" missed %u consecutive beacons \n " ,
sc - > beacon . bmisscnt ) ;
2008-12-07 21:44:03 +05:30
} else if ( sc - > beacon . bmisscnt > = BSTUCK_THRESH ) {
2009-09-13 02:42:02 -07:00
ath_print ( common , ATH_DBG_BEACON ,
" beacon is officially stuck \n " ) ;
2009-05-27 12:48:27 +00:00
sc - > sc_flags | = SC_OP_TSF_RESET ;
2009-03-03 10:16:53 +05:30
ath_reset ( sc , false ) ;
2008-08-04 00:16:41 -07:00
}
2009-03-03 10:16:53 +05:30
2008-08-04 00:16:41 -07:00
return ;
}
2008-09-17 10:15:09 +05:30
2008-12-07 21:44:03 +05:30
if ( sc - > beacon . bmisscnt ! = 0 ) {
2009-09-13 02:42:02 -07:00
ath_print ( common , ATH_DBG_BEACON ,
" resume beacon xmit after %u misses \n " ,
sc - > beacon . bmisscnt ) ;
2008-12-07 21:44:03 +05:30
sc - > beacon . bmisscnt = 0 ;
2008-08-04 00:16:41 -07:00
}
/*
* Generate beacon frames . we are sending frames
* staggered so calculate the slot for this frame based
* on the tsf to safeguard against missing an swba .
*/
2009-04-23 16:10:04 +02:00
intval = sc - > beacon_interval ? : ATH_DEFAULT_BINTVAL ;
2008-08-04 00:16:41 -07:00
tsf = ath9k_hw_gettsf64 ( ah ) ;
tsftu = TSF_TO_TU ( tsf > > 32 , tsf ) ;
slot = ( ( tsftu % intval ) * ATH_BCBUF ) / intval ;
2009-03-12 21:53:23 +02:00
/*
* Reverse the slot order to get slot 0 on the TBTT offset that does
* not require TSF adjustment and other slots adding
* slot / ATH_BCBUF * beacon_int to timestamp . For example , with
* ATH_BCBUF = 4 , we process beacon slots as follows : 3 2 1 0 3 2 1 . .
* and slot 0 is at correct offset to TBTT .
*/
slot = ATH_BCBUF - slot - 1 ;
vif = sc - > beacon . bslot [ slot ] ;
aphy = sc - > beacon . bslot_aphy [ slot ] ;
2008-09-17 10:15:09 +05:30
2009-09-13 02:42:02 -07:00
ath_print ( common , ATH_DBG_BEACON ,
" slot %d [tsf %llu tsftu %u intval %u] vif %p \n " ,
slot , tsf , tsftu , intval , vif ) ;
2008-09-17 10:15:09 +05:30
2008-08-04 00:16:41 -07:00
bfaddr = 0 ;
2009-03-03 19:23:26 +02:00
if ( vif ) {
2009-03-03 19:23:29 +02:00
bf = ath_beacon_generate ( aphy - > hw , vif ) ;
2008-08-04 00:16:41 -07:00
if ( bf ! = NULL ) {
bfaddr = bf - > bf_daddr ;
bc = 1 ;
}
}
2009-03-03 10:16:53 +05:30
2008-08-04 00:16:41 -07:00
/*
* Handle slot time change when a non - ERP station joins / leaves
* an 11 g network . The 802.11 layer notifies us via callback ,
* we mark updateslot , then wait one beacon before effecting
* the change . This gives associated stations at least one
* beacon interval to note the state change .
*
* NB : The slot time change state machine is clocked according
* to whether we are bursting or staggering beacons . We
* recognize the request to update and record the current
* slot then don ' t transition until that slot is reached
* again . If we miss a beacon for that slot then we ' ll be
* slow to transition but we ' ll be sure at least one beacon
* interval has passed . When bursting slot is always left
* set to ATH_BCBUF so this check is a noop .
*/
2008-12-07 21:44:03 +05:30
if ( sc - > beacon . updateslot = = UPDATE ) {
sc - > beacon . updateslot = COMMIT ; /* commit next beacon */
sc - > beacon . slotupdate = slot ;
} else if ( sc - > beacon . updateslot = = COMMIT & & sc - > beacon . slotupdate = = slot ) {
ath9k_hw_setslottime ( sc - > sc_ah , sc - > beacon . slottime ) ;
sc - > beacon . updateslot = OK ;
2008-11-24 12:07:55 +05:30
}
2008-08-04 00:16:41 -07:00
if ( bfaddr ! = 0 ) {
/*
* Stop any current dma and put the new frame ( s ) on the queue .
* This should never fail since we check above that no frames
* are still pending on the queue .
*/
2008-12-07 21:44:03 +05:30
if ( ! ath9k_hw_stoptxdma ( ah , sc - > beacon . beaconq ) ) {
2009-09-13 02:42:02 -07:00
ath_print ( common , ATH_DBG_FATAL ,
2008-12-07 21:44:03 +05:30
" beacon queue %u did not stop? \n " , sc - > beacon . beaconq ) ;
2008-08-04 00:16:41 -07:00
}
/* NB: cabq traffic should already be queued and primed */
2008-12-07 21:44:03 +05:30
ath9k_hw_puttxbuf ( ah , sc - > beacon . beaconq , bfaddr ) ;
ath9k_hw_txstart ( ah , sc - > beacon . beaconq ) ;
2008-08-04 00:16:41 -07:00
2009-02-09 13:27:03 +05:30
sc - > beacon . ast_be_xmit + = bc ; /* XXX per-vif? */
2008-08-04 00:16:41 -07:00
}
}
2009-09-09 20:05:39 -07:00
static void ath9k_beacon_init ( struct ath_softc * sc ,
u32 next_beacon ,
u32 beacon_period )
{
if ( beacon_period & ATH9K_BEACON_RESET_TSF )
ath9k_ps_wakeup ( sc ) ;
ath9k_hw_beaconinit ( sc - > sc_ah , next_beacon , beacon_period ) ;
if ( beacon_period & ATH9K_BEACON_RESET_TSF )
ath9k_ps_restore ( sc ) ;
}
2008-08-04 00:16:41 -07:00
/*
2009-03-03 10:16:54 +05:30
* For multi - bss ap support beacons are either staggered evenly over N slots or
* burst together . For the former arrange for the SWBA to be delivered for each
* slot . Slots that are not occupied will generate nothing .
2008-08-04 00:16:41 -07:00
*/
2009-03-03 10:16:54 +05:30
static void ath_beacon_config_ap ( struct ath_softc * sc ,
2009-05-15 18:59:21 +05:30
struct ath_beacon_config * conf )
2008-08-04 00:16:41 -07:00
{
2008-09-17 10:15:09 +05:30
u32 nexttbtt , intval ;
2008-08-04 00:16:41 -07:00
2009-03-03 10:16:56 +05:30
/* Configure the timers only when the TSF has to be reset */
if ( ! ( sc - > sc_flags & SC_OP_TSF_RESET ) )
return ;
2009-03-03 10:16:54 +05:30
/* NB: the beacon interval is kept internally in TU's */
intval = conf - > beacon_interval & ATH9K_BEACON_PERIOD ;
intval / = ATH_BCBUF ; /* for staggered beacons */
nexttbtt = intval ;
intval | = ATH9K_BEACON_RESET_TSF ;
2008-08-04 00:16:41 -07:00
2009-03-03 10:16:54 +05:30
/*
* In AP mode we enable the beacon timers and SWBA interrupts to
* prepare beacon frames .
*/
intval | = ATH9K_BEACON_ENA ;
sc - > imask | = ATH9K_INT_SWBA ;
ath_beaconq_config ( sc ) ;
2008-08-04 00:16:41 -07:00
2009-03-03 10:16:54 +05:30
/* Set the computed AP beacon timers */
2008-08-04 00:16:41 -07:00
2009-03-03 10:16:54 +05:30
ath9k_hw_set_interrupts ( sc - > sc_ah , 0 ) ;
2009-09-09 20:05:39 -07:00
ath9k_beacon_init ( sc , nexttbtt , intval ) ;
2009-03-03 10:16:54 +05:30
sc - > beacon . bmisscnt = 0 ;
ath9k_hw_set_interrupts ( sc - > sc_ah , sc - > imask ) ;
2009-03-03 10:16:56 +05:30
/* Clear the reset TSF flag, so that subsequent beacon updation
will not reset the HW TSF . */
sc - > sc_flags & = ~ SC_OP_TSF_RESET ;
2009-03-03 10:16:54 +05:30
}
2008-09-17 10:15:36 +05:30
2009-03-03 10:16:54 +05:30
/*
* This sets up the beacon timers according to the timestamp of the last
* received beacon and the current TSF , configures PCF and DTIM
* handling , programs the sleep registers so the hardware will wakeup in
* time to receive beacons , and configures the beacon miss handling so
* we ' ll receive a BMISS interrupt when we stop seeing beacons from the AP
* we ' ve associated with .
*/
static void ath_beacon_config_sta ( struct ath_softc * sc ,
2009-05-15 18:59:21 +05:30
struct ath_beacon_config * conf )
2009-03-03 10:16:54 +05:30
{
2009-09-13 02:42:02 -07:00
struct ath_common * common = ath9k_hw_common ( sc - > sc_ah ) ;
2009-03-03 10:16:54 +05:30
struct ath9k_beacon_state bs ;
int dtimperiod , dtimcount , sleepduration ;
int cfpperiod , cfpcount ;
u32 nexttbtt = 0 , intval , tsftu ;
u64 tsf ;
2009-05-20 21:56:39 +03:00
int num_beacons , offset , dtim_dec_count , cfp_dec_count ;
2009-03-03 10:16:54 +05:30
memset ( & bs , 0 , sizeof ( bs ) ) ;
intval = conf - > beacon_interval & ATH9K_BEACON_PERIOD ;
/*
* Setup dtim and cfp parameters according to
* last beacon we received ( which may be none ) .
*/
dtimperiod = conf - > dtim_period ;
if ( dtimperiod < = 0 ) /* NB: 0 if not known */
dtimperiod = 1 ;
dtimcount = conf - > dtim_count ;
if ( dtimcount > = dtimperiod ) /* NB: sanity check */
dtimcount = 0 ;
cfpperiod = 1 ; /* NB: no PCF support yet */
cfpcount = 0 ;
sleepduration = conf - > listen_interval * intval ;
if ( sleepduration < = 0 )
sleepduration = intval ;
/*
* Pull nexttbtt forward to reflect the current
* TSF and calculate dtim + cfp state for the result .
*/
tsf = ath9k_hw_gettsf64 ( sc - > sc_ah ) ;
tsftu = TSF_TO_TU ( tsf > > 32 , tsf ) + FUDGE ;
2009-05-20 21:56:39 +03:00
num_beacons = tsftu / intval + 1 ;
offset = tsftu % intval ;
nexttbtt = tsftu - offset ;
if ( offset )
2009-03-03 10:16:54 +05:30
nexttbtt + = intval ;
2009-05-20 21:56:39 +03:00
/* DTIM Beacon every dtimperiod Beacon */
dtim_dec_count = num_beacons % dtimperiod ;
/* CFP every cfpperiod DTIM Beacon */
cfp_dec_count = ( num_beacons / dtimperiod ) % cfpperiod ;
if ( dtim_dec_count )
cfp_dec_count + + ;
dtimcount - = dtim_dec_count ;
if ( dtimcount < 0 )
dtimcount + = dtimperiod ;
cfpcount - = cfp_dec_count ;
if ( cfpcount < 0 )
cfpcount + = cfpperiod ;
2009-03-03 10:16:54 +05:30
bs . bs_intval = intval ;
bs . bs_nexttbtt = nexttbtt ;
bs . bs_dtimperiod = dtimperiod * intval ;
bs . bs_nextdtim = bs . bs_nexttbtt + dtimcount * intval ;
bs . bs_cfpperiod = cfpperiod * bs . bs_dtimperiod ;
bs . bs_cfpnext = bs . bs_nextdtim + cfpcount * bs . bs_dtimperiod ;
bs . bs_cfpmaxduration = 0 ;
/*
* Calculate the number of consecutive beacons to miss * before taking
* a BMISS interrupt . The configuration is specified in TU so we only
* need calculate based on the beacon interval . Note that we clamp the
* result to at most 15 beacons .
*/
if ( sleepduration > intval ) {
bs . bs_bmissthreshold = conf - > listen_interval *
ATH_DEFAULT_BMISS_LIMIT / 2 ;
2008-08-04 00:16:41 -07:00
} else {
2009-03-03 10:16:54 +05:30
bs . bs_bmissthreshold = DIV_ROUND_UP ( conf - > bmiss_timeout , intval ) ;
if ( bs . bs_bmissthreshold > 15 )
bs . bs_bmissthreshold = 15 ;
else if ( bs . bs_bmissthreshold < = 0 )
bs . bs_bmissthreshold = 1 ;
2008-08-04 00:16:41 -07:00
}
2009-03-03 10:16:54 +05:30
/*
* Calculate sleep duration . The configuration is given in ms .
* We ensure a multiple of the beacon period is used . Also , if the sleep
* duration is greater than the DTIM period then it makes senses
* to make it a multiple of that .
*
* XXX fixed at 100 ms
*/
2008-09-17 10:15:09 +05:30
2009-03-03 10:16:54 +05:30
bs . bs_sleepduration = roundup ( IEEE80211_MS_TO_TU ( 100 ) , sleepduration ) ;
if ( bs . bs_sleepduration > bs . bs_dtimperiod )
bs . bs_sleepduration = bs . bs_dtimperiod ;
2008-09-17 10:15:09 +05:30
2009-03-03 10:16:54 +05:30
/* TSF out of range threshold fixed at 1 second */
bs . bs_tsfoor_threshold = ATH9K_TSFOOR_THRESHOLD ;
2008-08-04 00:16:41 -07:00
2009-09-13 02:42:02 -07:00
ath_print ( common , ATH_DBG_BEACON , " tsf: %llu tsftu: %u \n " , tsf , tsftu ) ;
ath_print ( common , ATH_DBG_BEACON ,
" bmiss: %u sleep: %u cfp-period: %u maxdur: %u next: %u \n " ,
bs . bs_bmissthreshold , bs . bs_sleepduration ,
bs . bs_cfpperiod , bs . bs_cfpmaxduration , bs . bs_cfpnext ) ;
2008-08-04 00:16:41 -07:00
2009-03-03 10:16:54 +05:30
/* Set the computed STA beacon timers */
2008-09-17 10:15:09 +05:30
2009-03-03 10:16:54 +05:30
ath9k_hw_set_interrupts ( sc - > sc_ah , 0 ) ;
ath9k_hw_set_sta_beacon_timers ( sc - > sc_ah , & bs ) ;
sc - > imask | = ATH9K_INT_BMISS ;
ath9k_hw_set_interrupts ( sc - > sc_ah , sc - > imask ) ;
}
2008-08-04 00:16:41 -07:00
2009-03-03 10:16:54 +05:30
static void ath_beacon_config_adhoc ( struct ath_softc * sc ,
struct ath_beacon_config * conf ,
2009-03-03 19:23:26 +02:00
struct ieee80211_vif * vif )
2009-03-03 10:16:54 +05:30
{
2009-09-13 02:42:02 -07:00
struct ath_common * common = ath9k_hw_common ( sc - > sc_ah ) ;
2009-03-03 10:16:54 +05:30
u64 tsf ;
u32 tsftu , intval , nexttbtt ;
2008-08-04 00:16:41 -07:00
2009-03-03 10:16:54 +05:30
intval = conf - > beacon_interval & ATH9K_BEACON_PERIOD ;
2008-08-04 00:16:41 -07:00
2009-05-28 19:25:28 +03:00
2009-03-03 10:16:54 +05:30
/* Pull nexttbtt forward to reflect the current TSF */
2009-02-12 10:06:47 +05:30
2009-03-03 10:16:54 +05:30
nexttbtt = TSF_TO_TU ( sc - > beacon . bc_tstamp > > 32 , sc - > beacon . bc_tstamp ) ;
if ( nexttbtt = = 0 )
nexttbtt = intval ;
else if ( intval )
nexttbtt = roundup ( nexttbtt , intval ) ;
2009-03-03 10:16:51 +05:30
2009-03-03 10:16:54 +05:30
tsf = ath9k_hw_gettsf64 ( sc - > sc_ah ) ;
tsftu = TSF_TO_TU ( ( u32 ) ( tsf > > 32 ) , ( u32 ) tsf ) + FUDGE ;
do {
nexttbtt + = intval ;
} while ( nexttbtt < tsftu ) ;
2008-08-04 00:16:41 -07:00
2009-09-13 02:42:02 -07:00
ath_print ( common , ATH_DBG_BEACON ,
" IBSS nexttbtt %u intval %u (%u) \n " ,
nexttbtt , intval , conf - > beacon_interval ) ;
2009-03-03 10:16:51 +05:30
2009-03-03 10:16:54 +05:30
/*
* In IBSS mode enable the beacon timers but only enable SWBA interrupts
* if we need to manually prepare beacon frames . Otherwise we use a
* self - linked tx descriptor and let the hardware deal with things .
*/
intval | = ATH9K_BEACON_ENA ;
if ( ! ( sc - > sc_ah - > caps . hw_caps & ATH9K_HW_CAP_VEOL ) )
sc - > imask | = ATH9K_INT_SWBA ;
2009-03-03 10:16:51 +05:30
2009-03-03 10:16:54 +05:30
ath_beaconq_config ( sc ) ;
/* Set the computed ADHOC beacon timers */
ath9k_hw_set_interrupts ( sc - > sc_ah , 0 ) ;
2009-09-09 20:05:39 -07:00
ath9k_beacon_init ( sc , nexttbtt , intval ) ;
2009-03-03 10:16:54 +05:30
sc - > beacon . bmisscnt = 0 ;
ath9k_hw_set_interrupts ( sc - > sc_ah , sc - > imask ) ;
2009-05-15 18:59:22 +05:30
/* FIXME: Handle properly when vif is NULL */
if ( vif & & sc - > sc_ah - > caps . hw_caps & ATH9K_HW_CAP_VEOL )
2009-03-03 19:23:26 +02:00
ath_beacon_start_adhoc ( sc , vif ) ;
2008-08-04 00:16:41 -07:00
}
2009-03-03 19:23:26 +02:00
void ath_beacon_config ( struct ath_softc * sc , struct ieee80211_vif * vif )
2008-08-04 00:16:41 -07:00
{
2009-05-15 18:59:22 +05:30
struct ath_beacon_config * cur_conf = & sc - > cur_beacon_conf ;
2009-09-13 02:42:02 -07:00
struct ath_common * common = ath9k_hw_common ( sc - > sc_ah ) ;
2009-05-15 18:59:22 +05:30
enum nl80211_iftype iftype ;
2009-03-03 10:16:54 +05:30
/* Setup the beacon configuration parameters */
2009-03-03 19:23:26 +02:00
if ( vif ) {
2009-05-15 18:59:22 +05:30
struct ieee80211_bss_conf * bss_conf = & vif - > bss_conf ;
iftype = vif - > type ;
2009-03-03 10:16:54 +05:30
2009-05-15 18:59:22 +05:30
cur_conf - > beacon_interval = bss_conf - > beacon_int ;
cur_conf - > dtim_period = bss_conf - > dtim_period ;
cur_conf - > listen_interval = 1 ;
cur_conf - > dtim_count = 1 ;
cur_conf - > bmiss_timeout =
ATH_DEFAULT_BMISS_LIMIT * cur_conf - > beacon_interval ;
} else {
iftype = sc - > sc_ah - > opmode ;
2009-03-03 10:16:54 +05:30
}
2009-05-15 18:59:22 +05:30
2009-06-12 10:55:55 +05:30
/*
* It looks like mac80211 may end up using beacon interval of zero in
* some cases ( at least for mesh point ) . Avoid getting into an
* infinite loop by using a bit safer value instead . To be safe ,
* do sanity check on beacon interval for all operating modes .
*/
if ( cur_conf - > beacon_interval = = 0 )
cur_conf - > beacon_interval = 100 ;
2009-05-15 18:59:22 +05:30
switch ( iftype ) {
case NL80211_IFTYPE_AP :
ath_beacon_config_ap ( sc , cur_conf ) ;
break ;
case NL80211_IFTYPE_ADHOC :
case NL80211_IFTYPE_MESH_POINT :
ath_beacon_config_adhoc ( sc , cur_conf , vif ) ;
break ;
case NL80211_IFTYPE_STATION :
ath_beacon_config_sta ( sc , cur_conf ) ;
break ;
default :
2009-09-13 02:42:02 -07:00
ath_print ( common , ATH_DBG_CONFIG ,
" Unsupported beaconing mode \n " ) ;
2009-05-15 18:59:22 +05:30
return ;
}
sc - > sc_flags | = SC_OP_BEACONS ;
2008-08-04 00:16:41 -07:00
}