19999SWang.Lin@Sun.COM /*
2*11729SWang.Lin@Sun.COM * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
39999SWang.Lin@Sun.COM * Use is subject to license terms.
49999SWang.Lin@Sun.COM */
59999SWang.Lin@Sun.COM
69999SWang.Lin@Sun.COM /*
79999SWang.Lin@Sun.COM * Copyright (c) 2008 Atheros Communications Inc.
89999SWang.Lin@Sun.COM *
99999SWang.Lin@Sun.COM * Permission to use, copy, modify, and/or distribute this software for any
109999SWang.Lin@Sun.COM * purpose with or without fee is hereby granted, provided that the above
119999SWang.Lin@Sun.COM * copyright notice and this permission notice appear in all copies.
129999SWang.Lin@Sun.COM *
139999SWang.Lin@Sun.COM * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
149999SWang.Lin@Sun.COM * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
159999SWang.Lin@Sun.COM * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
169999SWang.Lin@Sun.COM * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
179999SWang.Lin@Sun.COM * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
189999SWang.Lin@Sun.COM * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
199999SWang.Lin@Sun.COM * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
209999SWang.Lin@Sun.COM */
219999SWang.Lin@Sun.COM
229999SWang.Lin@Sun.COM #include <sys/param.h>
239999SWang.Lin@Sun.COM #include <sys/strsun.h>
249999SWang.Lin@Sun.COM #include <inet/common.h>
259999SWang.Lin@Sun.COM #include <inet/nd.h>
269999SWang.Lin@Sun.COM #include <inet/mi.h>
279999SWang.Lin@Sun.COM #include <inet/wifi_ioctl.h>
289999SWang.Lin@Sun.COM
299999SWang.Lin@Sun.COM #include "arn_core.h"
309999SWang.Lin@Sun.COM
319999SWang.Lin@Sun.COM /*
329999SWang.Lin@Sun.COM * This function will modify certain transmit queue properties depending on
339999SWang.Lin@Sun.COM * the operating mode of the station (AP or AdHoc). Parameters are AIFS
349999SWang.Lin@Sun.COM * settings and channel width min/max
359999SWang.Lin@Sun.COM */
36*11729SWang.Lin@Sun.COM
379999SWang.Lin@Sun.COM static int
3811377SWang.Lin@Sun.COM /* LINTED E_STATIC_UNUSED */
arn_beaconq_config(struct arn_softc * sc)399999SWang.Lin@Sun.COM arn_beaconq_config(struct arn_softc *sc)
409999SWang.Lin@Sun.COM {
419999SWang.Lin@Sun.COM struct ath_hal *ah = sc->sc_ah;
429999SWang.Lin@Sun.COM struct ath9k_tx_queue_info qi;
439999SWang.Lin@Sun.COM
449999SWang.Lin@Sun.COM (void) ath9k_hw_get_txq_props(ah, sc->sc_beaconq, &qi);
459999SWang.Lin@Sun.COM if (sc->sc_ah->ah_opmode == ATH9K_M_HOSTAP) {
469999SWang.Lin@Sun.COM /* Always burst out beacon and CAB traffic. */
479999SWang.Lin@Sun.COM qi.tqi_aifs = 1;
489999SWang.Lin@Sun.COM qi.tqi_cwmin = 0;
499999SWang.Lin@Sun.COM qi.tqi_cwmax = 0;
509999SWang.Lin@Sun.COM } else {
519999SWang.Lin@Sun.COM /* Adhoc mode; important thing is to use 2x cwmin. */
529999SWang.Lin@Sun.COM qi.tqi_aifs = sc->sc_beacon_qi.tqi_aifs;
539999SWang.Lin@Sun.COM qi.tqi_cwmin = 2*sc->sc_beacon_qi.tqi_cwmin;
549999SWang.Lin@Sun.COM qi.tqi_cwmax = sc->sc_beacon_qi.tqi_cwmax;
559999SWang.Lin@Sun.COM }
569999SWang.Lin@Sun.COM
579999SWang.Lin@Sun.COM if (!ath9k_hw_set_txq_props(ah, sc->sc_beaconq, &qi)) {
589999SWang.Lin@Sun.COM arn_problem("unable to update h/w beacon queue parameters\n");
599999SWang.Lin@Sun.COM return (0);
609999SWang.Lin@Sun.COM } else {
619999SWang.Lin@Sun.COM /* push to h/w */
629999SWang.Lin@Sun.COM (void) ath9k_hw_resettxqueue(ah, sc->sc_beaconq);
639999SWang.Lin@Sun.COM return (1);
649999SWang.Lin@Sun.COM }
659999SWang.Lin@Sun.COM }
669999SWang.Lin@Sun.COM
679999SWang.Lin@Sun.COM /*
689999SWang.Lin@Sun.COM * Associates the beacon frame buffer with a transmit descriptor. Will set
699999SWang.Lin@Sun.COM * up all required antenna switch parameters, rate codes, and channel flags.
709999SWang.Lin@Sun.COM * Beacons are always sent out at the lowest rate, and are not retried.
719999SWang.Lin@Sun.COM */
72*11729SWang.Lin@Sun.COM #ifdef ARN_IBSS
739999SWang.Lin@Sun.COM static void
arn_beacon_setup(struct arn_softc * sc,struct ath_buf * bf)749999SWang.Lin@Sun.COM arn_beacon_setup(struct arn_softc *sc, struct ath_buf *bf)
759999SWang.Lin@Sun.COM {
769999SWang.Lin@Sun.COM #define USE_SHPREAMBLE(_ic) \
779999SWang.Lin@Sun.COM (((_ic)->ic_flags & (IEEE80211_F_SHPREAMBLE | IEEE80211_F_USEBARKER))\
789999SWang.Lin@Sun.COM == IEEE80211_F_SHPREAMBLE)
799999SWang.Lin@Sun.COM mblk_t *mp = bf->bf_m;
809999SWang.Lin@Sun.COM struct ath_hal *ah = sc->sc_ah;
819999SWang.Lin@Sun.COM struct ath_desc *ds;
829999SWang.Lin@Sun.COM /* LINTED E_FUNC_SET_NOT_USED */
839999SWang.Lin@Sun.COM int flags, antenna = 0;
849999SWang.Lin@Sun.COM struct ath_rate_table *rt;
859999SWang.Lin@Sun.COM uint8_t rix, rate;
869999SWang.Lin@Sun.COM struct ath9k_11n_rate_series series[4];
879999SWang.Lin@Sun.COM int ctsrate = 0;
889999SWang.Lin@Sun.COM int ctsduration = 0;
899999SWang.Lin@Sun.COM
909999SWang.Lin@Sun.COM /* set up descriptors */
919999SWang.Lin@Sun.COM ds = bf->bf_desc;
929999SWang.Lin@Sun.COM
939999SWang.Lin@Sun.COM flags = ATH9K_TXDESC_NOACK;
949999SWang.Lin@Sun.COM if (sc->sc_ah->ah_opmode == ATH9K_M_IBSS &&
959999SWang.Lin@Sun.COM (ah->ah_caps.hw_caps & ATH9K_HW_CAP_VEOL)) {
969999SWang.Lin@Sun.COM ds->ds_link = bf->bf_daddr; /* self-linked */
979999SWang.Lin@Sun.COM flags |= ATH9K_TXDESC_VEOL;
989999SWang.Lin@Sun.COM /*
999999SWang.Lin@Sun.COM * Let hardware handle antenna switching.
1009999SWang.Lin@Sun.COM */
1019999SWang.Lin@Sun.COM antenna = 0;
1029999SWang.Lin@Sun.COM } else {
1039999SWang.Lin@Sun.COM ds->ds_link = 0;
1049999SWang.Lin@Sun.COM /*
1059999SWang.Lin@Sun.COM * Switch antenna every 4 beacons.
1069999SWang.Lin@Sun.COM * NB: assumes two antenna
1079999SWang.Lin@Sun.COM */
1089999SWang.Lin@Sun.COM antenna = ((sc->ast_be_xmit / sc->sc_nbcnvaps) & 1 ? 2 : 1);
1099999SWang.Lin@Sun.COM }
1109999SWang.Lin@Sun.COM
1119999SWang.Lin@Sun.COM ds->ds_data = bf->bf_dma.cookie.dmac_address;
1129999SWang.Lin@Sun.COM /*
1139999SWang.Lin@Sun.COM * Calculate rate code.
1149999SWang.Lin@Sun.COM * XXX everything at min xmit rate
1159999SWang.Lin@Sun.COM */
1169999SWang.Lin@Sun.COM rix = 0;
1179999SWang.Lin@Sun.COM rt = sc->hw_rate_table[sc->sc_curmode];
1189999SWang.Lin@Sun.COM rate = rt->info[rix].ratecode;
1199999SWang.Lin@Sun.COM if (sc->sc_flags & SC_OP_PREAMBLE_SHORT)
1209999SWang.Lin@Sun.COM rate |= rt->info[rix].short_preamble;
1219999SWang.Lin@Sun.COM
1229999SWang.Lin@Sun.COM ath9k_hw_set11n_txdesc(ah, ds,
1239999SWang.Lin@Sun.COM MBLKL(mp) + IEEE80211_CRC_LEN, /* frame length */
1249999SWang.Lin@Sun.COM ATH9K_PKT_TYPE_BEACON, /* Atheros packet type */
1259999SWang.Lin@Sun.COM MAX_RATE_POWER, /* FIXME */
1269999SWang.Lin@Sun.COM ATH9K_TXKEYIX_INVALID, /* no encryption */
1279999SWang.Lin@Sun.COM ATH9K_KEY_TYPE_CLEAR, /* no encryption */
1289999SWang.Lin@Sun.COM flags); /* no ack, veol for beacons */
1299999SWang.Lin@Sun.COM
1309999SWang.Lin@Sun.COM /* NB: beacon's BufLen must be a multiple of 4 bytes */
1319999SWang.Lin@Sun.COM (void) ath9k_hw_filltxdesc(ah, ds,
1329999SWang.Lin@Sun.COM roundup(MBLKL(mp), 4), /* buffer length */
1339999SWang.Lin@Sun.COM B_TRUE, /* first segment */
1349999SWang.Lin@Sun.COM B_TRUE, /* last segment */
1359999SWang.Lin@Sun.COM ds); /* first descriptor */
1369999SWang.Lin@Sun.COM
1379999SWang.Lin@Sun.COM (void) memset(series, 0, sizeof (struct ath9k_11n_rate_series) * 4);
1389999SWang.Lin@Sun.COM series[0].Tries = 1;
1399999SWang.Lin@Sun.COM series[0].Rate = rate;
1409999SWang.Lin@Sun.COM series[0].ChSel = sc->sc_tx_chainmask;
1419999SWang.Lin@Sun.COM series[0].RateFlags = (ctsrate) ? ATH9K_RATESERIES_RTS_CTS : 0;
1429999SWang.Lin@Sun.COM ath9k_hw_set11n_ratescenario(ah, ds, ds, 0,
1439999SWang.Lin@Sun.COM ctsrate, ctsduration, series, 4, 0);
1449999SWang.Lin@Sun.COM #undef USE_SHPREAMBLE
1459999SWang.Lin@Sun.COM }
146*11729SWang.Lin@Sun.COM #endif
1479999SWang.Lin@Sun.COM
1489999SWang.Lin@Sun.COM /*
1499999SWang.Lin@Sun.COM * Startup beacon transmission for adhoc mode when they are sent entirely
1509999SWang.Lin@Sun.COM * by the hardware using the self-linked descriptor + veol trick.
1519999SWang.Lin@Sun.COM */
152*11729SWang.Lin@Sun.COM #ifdef ARN_IBSS
1539999SWang.Lin@Sun.COM static void
arn_beacon_start_adhoc(struct arn_softc * sc)1549999SWang.Lin@Sun.COM arn_beacon_start_adhoc(struct arn_softc *sc)
1559999SWang.Lin@Sun.COM
1569999SWang.Lin@Sun.COM {
1579999SWang.Lin@Sun.COM struct ath_buf *bf = list_head(&sc->sc_bcbuf_list);
1589999SWang.Lin@Sun.COM struct ieee80211_node *in = bf->bf_in;
1599999SWang.Lin@Sun.COM struct ieee80211com *ic = in->in_ic;
1609999SWang.Lin@Sun.COM struct ath_hal *ah = sc->sc_ah;
1619999SWang.Lin@Sun.COM mblk_t *mp;
1629999SWang.Lin@Sun.COM
1639999SWang.Lin@Sun.COM mp = bf->bf_m;
1649999SWang.Lin@Sun.COM if (ieee80211_beacon_update(ic, bf->bf_in, &sc->asc_boff, mp, 0))
1659999SWang.Lin@Sun.COM bcopy(mp->b_rptr, bf->bf_dma.mem_va, MBLKL(mp));
1669999SWang.Lin@Sun.COM
1679999SWang.Lin@Sun.COM /* Construct tx descriptor. */
1689999SWang.Lin@Sun.COM arn_beacon_setup(sc, bf);
1699999SWang.Lin@Sun.COM
1709999SWang.Lin@Sun.COM /*
1719999SWang.Lin@Sun.COM * Stop any current dma and put the new frame on the queue.
1729999SWang.Lin@Sun.COM * This should never fail since we check above that no frames
1739999SWang.Lin@Sun.COM * are still pending on the queue.
1749999SWang.Lin@Sun.COM */
1759999SWang.Lin@Sun.COM if (!ath9k_hw_stoptxdma(ah, sc->sc_beaconq)) {
1769999SWang.Lin@Sun.COM arn_problem("ath: beacon queue %d did not stop?\n",
1779999SWang.Lin@Sun.COM sc->sc_beaconq);
1789999SWang.Lin@Sun.COM }
1799999SWang.Lin@Sun.COM ARN_DMA_SYNC(bf->bf_dma, DDI_DMA_SYNC_FORDEV);
1809999SWang.Lin@Sun.COM
1819999SWang.Lin@Sun.COM /* NB: caller is known to have already stopped tx dma */
1829999SWang.Lin@Sun.COM (void) ath9k_hw_puttxbuf(ah, sc->sc_beaconq, bf->bf_daddr);
1839999SWang.Lin@Sun.COM (void) ath9k_hw_txstart(ah, sc->sc_beaconq);
1849999SWang.Lin@Sun.COM
1859999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_BEACON, "arn: arn_bstuck_process(): "
1869999SWang.Lin@Sun.COM "TXDP%u = %llx (%p)\n", sc->sc_beaconq,
1879999SWang.Lin@Sun.COM ito64(bf->bf_daddr), bf->bf_desc));
1889999SWang.Lin@Sun.COM }
189*11729SWang.Lin@Sun.COM #endif /* ARN_IBSS */
1909999SWang.Lin@Sun.COM
1919999SWang.Lin@Sun.COM uint32_t
arn_beaconq_setup(struct ath_hal * ah)1929999SWang.Lin@Sun.COM arn_beaconq_setup(struct ath_hal *ah)
1939999SWang.Lin@Sun.COM {
1949999SWang.Lin@Sun.COM struct ath9k_tx_queue_info qi;
1959999SWang.Lin@Sun.COM
1969999SWang.Lin@Sun.COM (void) memset(&qi, 0, sizeof (qi));
1979999SWang.Lin@Sun.COM qi.tqi_aifs = 1;
1989999SWang.Lin@Sun.COM qi.tqi_cwmin = 0;
1999999SWang.Lin@Sun.COM qi.tqi_cwmax = 0;
2009999SWang.Lin@Sun.COM /* NB: don't enable any interrupts */
2019999SWang.Lin@Sun.COM return (ath9k_hw_setuptxqueue(ah, ATH9K_TX_QUEUE_BEACON, &qi));
2029999SWang.Lin@Sun.COM }
2039999SWang.Lin@Sun.COM
2049999SWang.Lin@Sun.COM int
arn_beacon_alloc(struct arn_softc * sc,struct ieee80211_node * in)2059999SWang.Lin@Sun.COM arn_beacon_alloc(struct arn_softc *sc, struct ieee80211_node *in)
2069999SWang.Lin@Sun.COM {
2079999SWang.Lin@Sun.COM ieee80211com_t *ic = in->in_ic;
2089999SWang.Lin@Sun.COM struct ath_buf *bf;
2099999SWang.Lin@Sun.COM mblk_t *mp;
2109999SWang.Lin@Sun.COM
2119999SWang.Lin@Sun.COM mutex_enter(&sc->sc_bcbuflock);
2129999SWang.Lin@Sun.COM bf = list_head(&sc->sc_bcbuf_list);
2139999SWang.Lin@Sun.COM if (bf == NULL) {
2149999SWang.Lin@Sun.COM arn_problem("arn: arn_beacon_alloc():"
2159999SWang.Lin@Sun.COM "no dma buffers");
2169999SWang.Lin@Sun.COM mutex_exit(&sc->sc_bcbuflock);
2179999SWang.Lin@Sun.COM return (ENOMEM);
2189999SWang.Lin@Sun.COM }
2199999SWang.Lin@Sun.COM
2209999SWang.Lin@Sun.COM mp = ieee80211_beacon_alloc(ic, in, &sc->asc_boff);
2219999SWang.Lin@Sun.COM if (mp == NULL) {
2229999SWang.Lin@Sun.COM arn_problem("ath: arn_beacon_alloc():"
2239999SWang.Lin@Sun.COM "cannot get mbuf\n");
2249999SWang.Lin@Sun.COM mutex_exit(&sc->sc_bcbuflock);
2259999SWang.Lin@Sun.COM return (ENOMEM);
2269999SWang.Lin@Sun.COM }
2279999SWang.Lin@Sun.COM ASSERT(mp->b_cont == NULL);
2289999SWang.Lin@Sun.COM bf->bf_m = mp;
2299999SWang.Lin@Sun.COM bcopy(mp->b_rptr, bf->bf_dma.mem_va, MBLKL(mp));
2309999SWang.Lin@Sun.COM bf->bf_in = ieee80211_ref_node(in);
2319999SWang.Lin@Sun.COM mutex_exit(&sc->sc_bcbuflock);
2329999SWang.Lin@Sun.COM
2339999SWang.Lin@Sun.COM return (0);
2349999SWang.Lin@Sun.COM }
2359999SWang.Lin@Sun.COM
2369999SWang.Lin@Sun.COM
2379999SWang.Lin@Sun.COM void
arn_beacon_return(struct arn_softc * sc)2389999SWang.Lin@Sun.COM arn_beacon_return(struct arn_softc *sc)
2399999SWang.Lin@Sun.COM {
2409999SWang.Lin@Sun.COM struct ath_buf *bf;
2419999SWang.Lin@Sun.COM
2429999SWang.Lin@Sun.COM mutex_enter(&sc->sc_bcbuflock);
2439999SWang.Lin@Sun.COM bf = list_head(&sc->sc_bcbuf_list);
2449999SWang.Lin@Sun.COM while (bf != NULL) {
2459999SWang.Lin@Sun.COM if (bf->bf_m != NULL) {
2469999SWang.Lin@Sun.COM freemsg(bf->bf_m);
2479999SWang.Lin@Sun.COM bf->bf_m = NULL;
2489999SWang.Lin@Sun.COM }
2499999SWang.Lin@Sun.COM if (bf->bf_in != NULL) {
2509999SWang.Lin@Sun.COM ieee80211_free_node(bf->bf_in);
2519999SWang.Lin@Sun.COM bf->bf_in = NULL;
2529999SWang.Lin@Sun.COM }
2539999SWang.Lin@Sun.COM bf = list_next(&sc->sc_bcbuf_list, bf);
2549999SWang.Lin@Sun.COM }
2559999SWang.Lin@Sun.COM mutex_exit(&sc->sc_bcbuflock);
2569999SWang.Lin@Sun.COM }
2579999SWang.Lin@Sun.COM
2589999SWang.Lin@Sun.COM void
arn_beacon_config(struct arn_softc * sc)2599999SWang.Lin@Sun.COM arn_beacon_config(struct arn_softc *sc)
260*11729SWang.Lin@Sun.COM
2619999SWang.Lin@Sun.COM {
2629999SWang.Lin@Sun.COM struct ath_beacon_config conf;
2639999SWang.Lin@Sun.COM ieee80211com_t *ic = (ieee80211com_t *)sc;
2649999SWang.Lin@Sun.COM struct ieee80211_node *in = ic->ic_bss;
26511377SWang.Lin@Sun.COM
266*11729SWang.Lin@Sun.COM /* New added */
26711377SWang.Lin@Sun.COM struct ath9k_beacon_state bs;
26811377SWang.Lin@Sun.COM int dtimperiod, dtimcount, sleepduration;
26911377SWang.Lin@Sun.COM int cfpperiod, cfpcount;
27011377SWang.Lin@Sun.COM uint32_t nexttbtt = 0, intval, tsftu;
27111377SWang.Lin@Sun.COM uint64_t tsf;
2729999SWang.Lin@Sun.COM
2739999SWang.Lin@Sun.COM (void) memset(&conf, 0, sizeof (struct ath_beacon_config));
2749999SWang.Lin@Sun.COM
2759999SWang.Lin@Sun.COM /* XXX fix me */
2769999SWang.Lin@Sun.COM conf.beacon_interval = in->in_intval ?
2779999SWang.Lin@Sun.COM in->in_intval : ATH_DEFAULT_BINTVAL;
278*11729SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_BEACON, "arn: arn_beacon_config():"
279*11729SWang.Lin@Sun.COM "conf.beacon_interval = %d\n", conf.beacon_interval));
2809999SWang.Lin@Sun.COM conf.listen_interval = 1;
2819999SWang.Lin@Sun.COM conf.dtim_period = conf.beacon_interval;
2829999SWang.Lin@Sun.COM conf.dtim_count = 1;
2839999SWang.Lin@Sun.COM conf.bmiss_timeout = ATH_DEFAULT_BMISS_LIMIT * conf.beacon_interval;
2849999SWang.Lin@Sun.COM
28511377SWang.Lin@Sun.COM (void) memset(&bs, 0, sizeof (bs));
28611377SWang.Lin@Sun.COM intval = conf.beacon_interval & ATH9K_BEACON_PERIOD;
28711377SWang.Lin@Sun.COM
28811377SWang.Lin@Sun.COM /*
28911377SWang.Lin@Sun.COM * Setup dtim and cfp parameters according to
29011377SWang.Lin@Sun.COM * last beacon we received (which may be none).
29111377SWang.Lin@Sun.COM */
29211377SWang.Lin@Sun.COM dtimperiod = conf.dtim_period;
29311377SWang.Lin@Sun.COM if (dtimperiod <= 0) /* NB: 0 if not known */
29411377SWang.Lin@Sun.COM dtimperiod = 1;
29511377SWang.Lin@Sun.COM dtimcount = conf.dtim_count;
29611377SWang.Lin@Sun.COM if (dtimcount >= dtimperiod) /* NB: sanity check */
29711377SWang.Lin@Sun.COM dtimcount = 0;
29811377SWang.Lin@Sun.COM cfpperiod = 1; /* NB: no PCF support yet */
29911377SWang.Lin@Sun.COM cfpcount = 0;
30011377SWang.Lin@Sun.COM
30111377SWang.Lin@Sun.COM sleepduration = conf.listen_interval * intval;
30211377SWang.Lin@Sun.COM if (sleepduration <= 0)
30311377SWang.Lin@Sun.COM sleepduration = intval;
3049999SWang.Lin@Sun.COM
30511377SWang.Lin@Sun.COM /*
30611377SWang.Lin@Sun.COM * Pull nexttbtt forward to reflect the current
30711377SWang.Lin@Sun.COM * TSF and calculate dtim+cfp state for the result.
30811377SWang.Lin@Sun.COM */
30911377SWang.Lin@Sun.COM tsf = ath9k_hw_gettsf64(sc->sc_ah);
31011377SWang.Lin@Sun.COM tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE;
31111377SWang.Lin@Sun.COM do {
31211377SWang.Lin@Sun.COM nexttbtt += intval;
31311377SWang.Lin@Sun.COM if (--dtimcount < 0) {
31411377SWang.Lin@Sun.COM dtimcount = dtimperiod - 1;
31511377SWang.Lin@Sun.COM if (--cfpcount < 0)
31611377SWang.Lin@Sun.COM cfpcount = cfpperiod - 1;
31711377SWang.Lin@Sun.COM }
31811377SWang.Lin@Sun.COM } while (nexttbtt < tsftu);
31911377SWang.Lin@Sun.COM
32011377SWang.Lin@Sun.COM bs.bs_intval = intval;
32111377SWang.Lin@Sun.COM bs.bs_nexttbtt = nexttbtt;
32211377SWang.Lin@Sun.COM bs.bs_dtimperiod = dtimperiod*intval;
32311377SWang.Lin@Sun.COM bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount*intval;
32411377SWang.Lin@Sun.COM bs.bs_cfpperiod = cfpperiod*bs.bs_dtimperiod;
32511377SWang.Lin@Sun.COM bs.bs_cfpnext = bs.bs_nextdtim + cfpcount*bs.bs_dtimperiod;
32611377SWang.Lin@Sun.COM bs.bs_cfpmaxduration = 0;
32711377SWang.Lin@Sun.COM
32811377SWang.Lin@Sun.COM /*
32911377SWang.Lin@Sun.COM * Calculate the number of consecutive beacons to miss* before taking
33011377SWang.Lin@Sun.COM * a BMISS interrupt. The configuration is specified in TU so we only
331*11729SWang.Lin@Sun.COM * need calculate based on the beacon interval. Note that we clamp the
33211377SWang.Lin@Sun.COM * result to at most 15 beacons.
33311377SWang.Lin@Sun.COM */
33411377SWang.Lin@Sun.COM if (sleepduration > intval) {
33511377SWang.Lin@Sun.COM bs.bs_bmissthreshold = conf.listen_interval *
336*11729SWang.Lin@Sun.COM ATH_DEFAULT_BMISS_LIMIT / 2;
3379999SWang.Lin@Sun.COM } else {
33811377SWang.Lin@Sun.COM bs.bs_bmissthreshold = DIV_ROUND_UP(conf.bmiss_timeout, intval);
33911377SWang.Lin@Sun.COM if (bs.bs_bmissthreshold > 15)
34011377SWang.Lin@Sun.COM bs.bs_bmissthreshold = 15;
34111377SWang.Lin@Sun.COM else if (bs.bs_bmissthreshold == 0)
34211377SWang.Lin@Sun.COM bs.bs_bmissthreshold = 1;
3439999SWang.Lin@Sun.COM }
3449999SWang.Lin@Sun.COM
34511377SWang.Lin@Sun.COM /*
34611377SWang.Lin@Sun.COM * Calculate sleep duration. The configuration is given in ms.
34711377SWang.Lin@Sun.COM * We ensure a multiple of the beacon period is used. Also, if the sleep
34811377SWang.Lin@Sun.COM * duration is greater than the DTIM period then it makes senses
34911377SWang.Lin@Sun.COM * to make it a multiple of that.
35011377SWang.Lin@Sun.COM *
35111377SWang.Lin@Sun.COM * XXX fixed at 100ms
35211377SWang.Lin@Sun.COM */
35311377SWang.Lin@Sun.COM
35411377SWang.Lin@Sun.COM bs.bs_sleepduration = roundup(IEEE80211_MS_TO_TU(100), sleepduration);
35511377SWang.Lin@Sun.COM if (bs.bs_sleepduration > bs.bs_dtimperiod)
35611377SWang.Lin@Sun.COM bs.bs_sleepduration = bs.bs_dtimperiod;
35711377SWang.Lin@Sun.COM
35811377SWang.Lin@Sun.COM /* TSF out of range threshold fixed at 1 second */
35911377SWang.Lin@Sun.COM bs.bs_tsfoor_threshold = ATH9K_TSFOOR_THRESHOLD;
3609999SWang.Lin@Sun.COM
3619999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_BEACON, "arn: arn_beacon_config(): "
362*11729SWang.Lin@Sun.COM "tsf %llu "
363*11729SWang.Lin@Sun.COM "tsf:tu %u "
364*11729SWang.Lin@Sun.COM "intval %u "
365*11729SWang.Lin@Sun.COM "nexttbtt %u "
366*11729SWang.Lin@Sun.COM "dtim %u "
367*11729SWang.Lin@Sun.COM "nextdtim %u "
368*11729SWang.Lin@Sun.COM "bmiss %u "
369*11729SWang.Lin@Sun.COM "sleep %u "
370*11729SWang.Lin@Sun.COM "cfp:period %u "
371*11729SWang.Lin@Sun.COM "maxdur %u "
372*11729SWang.Lin@Sun.COM "next %u "
373*11729SWang.Lin@Sun.COM "timoffset %u\n",
374*11729SWang.Lin@Sun.COM (unsigned long long)tsf, tsftu,
375*11729SWang.Lin@Sun.COM bs.bs_intval,
376*11729SWang.Lin@Sun.COM bs.bs_nexttbtt,
377*11729SWang.Lin@Sun.COM bs.bs_dtimperiod,
378*11729SWang.Lin@Sun.COM bs.bs_nextdtim,
379*11729SWang.Lin@Sun.COM bs.bs_bmissthreshold,
380*11729SWang.Lin@Sun.COM bs.bs_sleepduration,
381*11729SWang.Lin@Sun.COM bs.bs_cfpperiod,
382*11729SWang.Lin@Sun.COM bs.bs_cfpmaxduration,
383*11729SWang.Lin@Sun.COM bs.bs_cfpnext,
384*11729SWang.Lin@Sun.COM bs.bs_timoffset));
3859999SWang.Lin@Sun.COM
38611377SWang.Lin@Sun.COM /* Set the computed STA beacon timers */
3879999SWang.Lin@Sun.COM
38811377SWang.Lin@Sun.COM (void) ath9k_hw_set_interrupts(sc->sc_ah, 0);
38911377SWang.Lin@Sun.COM ath9k_hw_set_sta_beacon_timers(sc->sc_ah, &bs);
39011377SWang.Lin@Sun.COM sc->sc_imask |= ATH9K_INT_BMISS;
39111377SWang.Lin@Sun.COM (void) ath9k_hw_set_interrupts(sc->sc_ah, sc->sc_imask);
3929999SWang.Lin@Sun.COM }
3939999SWang.Lin@Sun.COM
3949999SWang.Lin@Sun.COM void
ath_beacon_sync(struct arn_softc * sc)3959999SWang.Lin@Sun.COM ath_beacon_sync(struct arn_softc *sc)
3969999SWang.Lin@Sun.COM {
3979999SWang.Lin@Sun.COM /*
3989999SWang.Lin@Sun.COM * Resync beacon timers using the tsf of the
3999999SWang.Lin@Sun.COM * beacon frame we just received.
4009999SWang.Lin@Sun.COM */
4019999SWang.Lin@Sun.COM arn_beacon_config(sc);
4029999SWang.Lin@Sun.COM sc->sc_flags |= SC_OP_BEACONS;
4039999SWang.Lin@Sun.COM }
4049999SWang.Lin@Sun.COM
4059999SWang.Lin@Sun.COM void
arn_bmiss_proc(void * arg)4069999SWang.Lin@Sun.COM arn_bmiss_proc(void *arg)
4079999SWang.Lin@Sun.COM {
4089999SWang.Lin@Sun.COM struct arn_softc *sc = (struct arn_softc *)arg;
4099999SWang.Lin@Sun.COM ieee80211com_t *ic = (ieee80211com_t *)sc;
4109999SWang.Lin@Sun.COM uint64_t tsf, lastrx;
4119999SWang.Lin@Sun.COM uint_t bmisstimeout;
4129999SWang.Lin@Sun.COM
4139999SWang.Lin@Sun.COM if (ic->ic_opmode != IEEE80211_M_STA ||
4149999SWang.Lin@Sun.COM ic->ic_state != IEEE80211_S_RUN) {
4159999SWang.Lin@Sun.COM return;
4169999SWang.Lin@Sun.COM }
4179999SWang.Lin@Sun.COM
4189999SWang.Lin@Sun.COM ARN_LOCK(sc);
4199999SWang.Lin@Sun.COM lastrx = sc->sc_lastrx;
4209999SWang.Lin@Sun.COM tsf = ath9k_hw_gettsf64(sc->sc_ah);
4219999SWang.Lin@Sun.COM bmisstimeout = ic->ic_bmissthreshold * ic->ic_bss->in_intval * 1024;
4229999SWang.Lin@Sun.COM
4239999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_BEACON, "arn_bmiss_proc():"
4249999SWang.Lin@Sun.COM " tsf %llu, lastrx %llu (%lld), bmiss %u\n",
4259999SWang.Lin@Sun.COM (unsigned long long)tsf, (unsigned long long)sc->sc_lastrx,
4269999SWang.Lin@Sun.COM (long long)(tsf - lastrx), bmisstimeout));
4279999SWang.Lin@Sun.COM ARN_UNLOCK(sc);
4289999SWang.Lin@Sun.COM
4299999SWang.Lin@Sun.COM /* temp workaround */
430*11729SWang.Lin@Sun.COM if ((tsf - lastrx) > bmisstimeout)
4319999SWang.Lin@Sun.COM ieee80211_beacon_miss(ic);
4329999SWang.Lin@Sun.COM }
433