1 /*
   2  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
   3  * Use is subject to license terms.
   4  */
   5 
   6 /*
   7  * Copyright (c) 2008 Atheros Communications Inc.
   8  *
   9  * Permission to use, copy, modify, and/or distribute this software for any
  10  * purpose with or without fee is hereby granted, provided that the above
  11  * copyright notice and this permission notice appear in all copies.
  12  *
  13  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  14  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  15  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  16  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  17  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  18  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  19  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  20  */
  21 
  22 #include <sys/param.h>
  23 #include <sys/strsun.h>
  24 #include <inet/common.h>
  25 #include <inet/nd.h>
  26 #include <inet/mi.h>
  27 #include <inet/wifi_ioctl.h>
  28 
  29 #include "arn_core.h"
  30 
  31 /*
  32  * This function will modify certain transmit queue properties depending on
  33  * the operating mode of the station (AP or AdHoc).  Parameters are AIFS
  34  * settings and channel width min/max
  35  */
  36 
  37 static int
  38 /* LINTED E_STATIC_UNUSED */
  39 arn_beaconq_config(struct arn_softc *sc)
  40 {
  41         struct ath_hal *ah = sc->sc_ah;
  42         struct ath9k_tx_queue_info qi;
  43 
  44         (void) ath9k_hw_get_txq_props(ah, sc->sc_beaconq, &qi);
  45         if (sc->sc_ah->ah_opmode == ATH9K_M_HOSTAP) {
  46                 /* Always burst out beacon and CAB traffic. */
  47                 qi.tqi_aifs = 1;
  48                 qi.tqi_cwmin = 0;
  49                 qi.tqi_cwmax = 0;
  50         } else {
  51                 /* Adhoc mode; important thing is to use 2x cwmin. */
  52                 qi.tqi_aifs = sc->sc_beacon_qi.tqi_aifs;
  53                 qi.tqi_cwmin = 2*sc->sc_beacon_qi.tqi_cwmin;
  54                 qi.tqi_cwmax = sc->sc_beacon_qi.tqi_cwmax;
  55         }
  56 
  57         if (!ath9k_hw_set_txq_props(ah, sc->sc_beaconq, &qi)) {
  58                 arn_problem("unable to update h/w beacon queue parameters\n");
  59                 return (0);
  60         } else {
  61                 /* push to h/w */
  62                 (void) ath9k_hw_resettxqueue(ah, sc->sc_beaconq);
  63                 return (1);
  64         }
  65 }
  66 
  67 /*
  68  * Associates the beacon frame buffer with a transmit descriptor.  Will set
  69  * up all required antenna switch parameters, rate codes, and channel flags.
  70  * Beacons are always sent out at the lowest rate, and are not retried.
  71  */
  72 #ifdef ARN_IBSS
  73 static void
  74 arn_beacon_setup(struct arn_softc *sc, struct ath_buf *bf)
  75 {
  76 #define USE_SHPREAMBLE(_ic) \
  77         (((_ic)->ic_flags & (IEEE80211_F_SHPREAMBLE | IEEE80211_F_USEBARKER))\
  78             == IEEE80211_F_SHPREAMBLE)
  79         mblk_t *mp = bf->bf_m;
  80         struct ath_hal *ah = sc->sc_ah;
  81         struct ath_desc *ds;
  82         /* LINTED E_FUNC_SET_NOT_USED */
  83         int flags, antenna = 0;
  84         struct ath_rate_table *rt;
  85         uint8_t rix, rate;
  86         struct ath9k_11n_rate_series series[4];
  87         int ctsrate = 0;
  88         int ctsduration = 0;
  89 
  90         /* set up descriptors */
  91         ds = bf->bf_desc;
  92 
  93         flags = ATH9K_TXDESC_NOACK;
  94         if (sc->sc_ah->ah_opmode == ATH9K_M_IBSS &&
  95             (ah->ah_caps.hw_caps & ATH9K_HW_CAP_VEOL)) {
  96                 ds->ds_link = bf->bf_daddr;       /* self-linked */
  97                 flags |= ATH9K_TXDESC_VEOL;
  98                 /*
  99                  * Let hardware handle antenna switching.
 100                  */
 101                 antenna = 0;
 102         } else {
 103                 ds->ds_link = 0;
 104                 /*
 105                  * Switch antenna every 4 beacons.
 106                  * NB: assumes two antenna
 107                  */
 108                 antenna = ((sc->ast_be_xmit / sc->sc_nbcnvaps) & 1 ? 2 : 1);
 109         }
 110 
 111         ds->ds_data = bf->bf_dma.cookie.dmac_address;
 112         /*
 113          * Calculate rate code.
 114          * XXX everything at min xmit rate
 115          */
 116         rix = 0;
 117         rt = sc->hw_rate_table[sc->sc_curmode];
 118         rate = rt->info[rix].ratecode;
 119         if (sc->sc_flags & SC_OP_PREAMBLE_SHORT)
 120                 rate |= rt->info[rix].short_preamble;
 121 
 122         ath9k_hw_set11n_txdesc(ah, ds,
 123             MBLKL(mp) + IEEE80211_CRC_LEN, /* frame length */
 124             ATH9K_PKT_TYPE_BEACON,      /* Atheros packet type */
 125             MAX_RATE_POWER,             /* FIXME */
 126             ATH9K_TXKEYIX_INVALID,      /* no encryption */
 127             ATH9K_KEY_TYPE_CLEAR,       /* no encryption */
 128             flags);                     /* no ack, veol for beacons */
 129 
 130         /* NB: beacon's BufLen must be a multiple of 4 bytes */
 131         (void) ath9k_hw_filltxdesc(ah, ds,
 132             roundup(MBLKL(mp), 4),      /* buffer length */
 133             B_TRUE,                     /* first segment */
 134             B_TRUE,                     /* last segment */
 135             ds);                        /* first descriptor */
 136 
 137         (void) memset(series, 0, sizeof (struct ath9k_11n_rate_series) * 4);
 138         series[0].Tries = 1;
 139         series[0].Rate = rate;
 140         series[0].ChSel = sc->sc_tx_chainmask;
 141         series[0].RateFlags = (ctsrate) ? ATH9K_RATESERIES_RTS_CTS : 0;
 142         ath9k_hw_set11n_ratescenario(ah, ds, ds, 0,
 143             ctsrate, ctsduration, series, 4, 0);
 144 #undef  USE_SHPREAMBLE
 145 }
 146 #endif
 147 
 148 /*
 149  * Startup beacon transmission for adhoc mode when they are sent entirely
 150  * by the hardware using the self-linked descriptor + veol trick.
 151  */
 152 #ifdef ARN_IBSS
 153 static void
 154 arn_beacon_start_adhoc(struct arn_softc *sc)
 155 
 156 {
 157         struct ath_buf *bf = list_head(&sc->sc_bcbuf_list);
 158         struct ieee80211_node *in = bf->bf_in;
 159         struct ieee80211com *ic = in->in_ic;
 160         struct ath_hal *ah = sc->sc_ah;
 161         mblk_t *mp;
 162 
 163         mp = bf->bf_m;
 164         if (ieee80211_beacon_update(ic, bf->bf_in, &sc->asc_boff, mp, 0))
 165                 bcopy(mp->b_rptr, bf->bf_dma.mem_va, MBLKL(mp));
 166 
 167         /* Construct tx descriptor. */
 168         arn_beacon_setup(sc, bf);
 169 
 170         /*
 171          * Stop any current dma and put the new frame on the queue.
 172          * This should never fail since we check above that no frames
 173          * are still pending on the queue.
 174          */
 175         if (!ath9k_hw_stoptxdma(ah, sc->sc_beaconq)) {
 176                 arn_problem("ath: beacon queue %d did not stop?\n",
 177                     sc->sc_beaconq);
 178         }
 179         ARN_DMA_SYNC(bf->bf_dma, DDI_DMA_SYNC_FORDEV);
 180 
 181         /* NB: caller is known to have already stopped tx dma */
 182         (void) ath9k_hw_puttxbuf(ah, sc->sc_beaconq, bf->bf_daddr);
 183         (void) ath9k_hw_txstart(ah, sc->sc_beaconq);
 184 
 185         ARN_DBG((ARN_DBG_BEACON, "arn: arn_bstuck_process(): "
 186             "TXDP%u = %llx (%p)\n", sc->sc_beaconq,
 187             ito64(bf->bf_daddr), bf->bf_desc));
 188 }
 189 #endif /* ARN_IBSS */
 190 
 191 uint32_t
 192 arn_beaconq_setup(struct ath_hal *ah)
 193 {
 194         struct ath9k_tx_queue_info qi;
 195 
 196         (void) memset(&qi, 0, sizeof (qi));
 197         qi.tqi_aifs = 1;
 198         qi.tqi_cwmin = 0;
 199         qi.tqi_cwmax = 0;
 200         /* NB: don't enable any interrupts */
 201         return (ath9k_hw_setuptxqueue(ah, ATH9K_TX_QUEUE_BEACON, &qi));
 202 }
 203 
 204 int
 205 arn_beacon_alloc(struct arn_softc *sc, struct ieee80211_node *in)
 206 {
 207         ieee80211com_t  *ic = in->in_ic;
 208         struct ath_buf *bf;
 209         mblk_t *mp;
 210 
 211         mutex_enter(&sc->sc_bcbuflock);
 212         bf = list_head(&sc->sc_bcbuf_list);
 213         if (bf == NULL) {
 214                 arn_problem("arn: arn_beacon_alloc():"
 215                     "no dma buffers");
 216                 mutex_exit(&sc->sc_bcbuflock);
 217                 return (ENOMEM);
 218         }
 219 
 220         mp = ieee80211_beacon_alloc(ic, in, &sc->asc_boff);
 221         if (mp == NULL) {
 222                 arn_problem("ath: arn_beacon_alloc():"
 223                     "cannot get mbuf\n");
 224                 mutex_exit(&sc->sc_bcbuflock);
 225                 return (ENOMEM);
 226         }
 227         ASSERT(mp->b_cont == NULL);
 228         bf->bf_m = mp;
 229         bcopy(mp->b_rptr, bf->bf_dma.mem_va, MBLKL(mp));
 230         bf->bf_in = ieee80211_ref_node(in);
 231         mutex_exit(&sc->sc_bcbuflock);
 232 
 233         return (0);
 234 }
 235 
 236 
 237 void
 238 arn_beacon_return(struct arn_softc *sc)
 239 {
 240         struct ath_buf *bf;
 241 
 242         mutex_enter(&sc->sc_bcbuflock);
 243         bf = list_head(&sc->sc_bcbuf_list);
 244         while (bf != NULL) {
 245                 if (bf->bf_m != NULL) {
 246                         freemsg(bf->bf_m);
 247                         bf->bf_m = NULL;
 248                 }
 249                 if (bf->bf_in != NULL) {
 250                         ieee80211_free_node(bf->bf_in);
 251                         bf->bf_in = NULL;
 252                 }
 253                 bf = list_next(&sc->sc_bcbuf_list, bf);
 254         }
 255         mutex_exit(&sc->sc_bcbuflock);
 256 }
 257 
 258 void
 259 arn_beacon_config(struct arn_softc *sc)
 260 
 261 {
 262         struct ath_beacon_config conf;
 263         ieee80211com_t *ic = (ieee80211com_t *)sc;
 264         struct ieee80211_node *in = ic->ic_bss;
 265 
 266         /* New added */
 267         struct ath9k_beacon_state bs;
 268         int dtimperiod, dtimcount, sleepduration;
 269         int cfpperiod, cfpcount;
 270         uint32_t nexttbtt = 0, intval, tsftu;
 271         uint64_t tsf;
 272 
 273         (void) memset(&conf, 0, sizeof (struct ath_beacon_config));
 274 
 275         /* XXX fix me */
 276         conf.beacon_interval = in->in_intval ?
 277             in->in_intval : ATH_DEFAULT_BINTVAL;
 278         ARN_DBG((ARN_DBG_BEACON, "arn: arn_beacon_config():"
 279             "conf.beacon_interval = %d\n", conf.beacon_interval));
 280         conf.listen_interval = 1;
 281         conf.dtim_period = conf.beacon_interval;
 282         conf.dtim_count = 1;
 283         conf.bmiss_timeout = ATH_DEFAULT_BMISS_LIMIT * conf.beacon_interval;
 284 
 285         (void) memset(&bs, 0, sizeof (bs));
 286         intval = conf.beacon_interval & ATH9K_BEACON_PERIOD;
 287 
 288         /*
 289          * Setup dtim and cfp parameters according to
 290          * last beacon we received (which may be none).
 291          */
 292         dtimperiod = conf.dtim_period;
 293         if (dtimperiod <= 0)         /* NB: 0 if not known */
 294                 dtimperiod = 1;
 295         dtimcount = conf.dtim_count;
 296         if (dtimcount >= dtimperiod) /* NB: sanity check */
 297                 dtimcount = 0;
 298         cfpperiod = 1;                  /* NB: no PCF support yet */
 299         cfpcount = 0;
 300 
 301         sleepduration = conf.listen_interval * intval;
 302         if (sleepduration <= 0)
 303                 sleepduration = intval;
 304 
 305         /*
 306          * Pull nexttbtt forward to reflect the current
 307          * TSF and calculate dtim+cfp state for the result.
 308          */
 309         tsf = ath9k_hw_gettsf64(sc->sc_ah);
 310         tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE;
 311         do {
 312                 nexttbtt += intval;
 313                 if (--dtimcount < 0) {
 314                         dtimcount = dtimperiod - 1;
 315                         if (--cfpcount < 0)
 316                                 cfpcount = cfpperiod - 1;
 317                 }
 318         } while (nexttbtt < tsftu);
 319 
 320         bs.bs_intval = intval;
 321         bs.bs_nexttbtt = nexttbtt;
 322         bs.bs_dtimperiod = dtimperiod*intval;
 323         bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount*intval;
 324         bs.bs_cfpperiod = cfpperiod*bs.bs_dtimperiod;
 325         bs.bs_cfpnext = bs.bs_nextdtim + cfpcount*bs.bs_dtimperiod;
 326         bs.bs_cfpmaxduration = 0;
 327 
 328         /*
 329          * Calculate the number of consecutive beacons to miss* before taking
 330          * a BMISS interrupt. The configuration is specified in TU so we only
 331          * need calculate based on the beacon interval.  Note that we clamp the
 332          * result to at most 15 beacons.
 333          */
 334         if (sleepduration > intval) {
 335                 bs.bs_bmissthreshold = conf.listen_interval *
 336                         ATH_DEFAULT_BMISS_LIMIT / 2;
 337         } else {
 338                 bs.bs_bmissthreshold = DIV_ROUND_UP(conf.bmiss_timeout, intval);
 339                 if (bs.bs_bmissthreshold > 15)
 340                         bs.bs_bmissthreshold = 15;
 341                 else if (bs.bs_bmissthreshold == 0)
 342                         bs.bs_bmissthreshold = 1;
 343         }
 344 
 345         /*
 346          * Calculate sleep duration. The configuration is given in ms.
 347          * We ensure a multiple of the beacon period is used. Also, if the sleep
 348          * duration is greater than the DTIM period then it makes senses
 349          * to make it a multiple of that.
 350          *
 351          * XXX fixed at 100ms
 352          */
 353 
 354         bs.bs_sleepduration = roundup(IEEE80211_MS_TO_TU(100), sleepduration);
 355         if (bs.bs_sleepduration > bs.bs_dtimperiod)
 356                 bs.bs_sleepduration = bs.bs_dtimperiod;
 357 
 358         /* TSF out of range threshold fixed at 1 second */
 359         bs.bs_tsfoor_threshold = ATH9K_TSFOOR_THRESHOLD;
 360 
 361         ARN_DBG((ARN_DBG_BEACON, "arn: arn_beacon_config(): "
 362             "tsf %llu "
 363             "tsf:tu %u "
 364             "intval %u "
 365             "nexttbtt %u "
 366             "dtim %u "
 367             "nextdtim %u "
 368             "bmiss %u "
 369             "sleep %u "
 370             "cfp:period %u "
 371             "maxdur %u "
 372             "next %u "
 373             "timoffset %u\n",
 374             (unsigned long long)tsf, tsftu,
 375             bs.bs_intval,
 376             bs.bs_nexttbtt,
 377             bs.bs_dtimperiod,
 378             bs.bs_nextdtim,
 379             bs.bs_bmissthreshold,
 380             bs.bs_sleepduration,
 381             bs.bs_cfpperiod,
 382             bs.bs_cfpmaxduration,
 383             bs.bs_cfpnext,
 384             bs.bs_timoffset));
 385 
 386         /* Set the computed STA beacon timers */
 387 
 388         (void) ath9k_hw_set_interrupts(sc->sc_ah, 0);
 389         ath9k_hw_set_sta_beacon_timers(sc->sc_ah, &bs);
 390         sc->sc_imask |= ATH9K_INT_BMISS;
 391         (void) ath9k_hw_set_interrupts(sc->sc_ah, sc->sc_imask);
 392 }
 393 
 394 void
 395 ath_beacon_sync(struct arn_softc *sc)
 396 {
 397         /*
 398          * Resync beacon timers using the tsf of the
 399          * beacon frame we just received.
 400          */
 401         arn_beacon_config(sc);
 402         sc->sc_flags |= SC_OP_BEACONS;
 403 }
 404 
 405 void
 406 arn_bmiss_proc(void *arg)
 407 {
 408         struct arn_softc *sc = (struct arn_softc *)arg;
 409         ieee80211com_t *ic = (ieee80211com_t *)sc;
 410         uint64_t tsf, lastrx;
 411         uint_t  bmisstimeout;
 412 
 413         if (ic->ic_opmode != IEEE80211_M_STA ||
 414             ic->ic_state != IEEE80211_S_RUN) {
 415                 return;
 416         }
 417 
 418         ARN_LOCK(sc);
 419         lastrx = sc->sc_lastrx;
 420         tsf = ath9k_hw_gettsf64(sc->sc_ah);
 421         bmisstimeout = ic->ic_bmissthreshold * ic->ic_bss->in_intval * 1024;
 422 
 423         ARN_DBG((ARN_DBG_BEACON, "arn_bmiss_proc():"
 424             " tsf %llu, lastrx %llu (%lld), bmiss %u\n",
 425             (unsigned long long)tsf, (unsigned long long)sc->sc_lastrx,
 426             (long long)(tsf - lastrx), bmisstimeout));
 427         ARN_UNLOCK(sc);
 428 
 429         /* temp workaround */
 430         if ((tsf - lastrx) > bmisstimeout)
 431                 ieee80211_beacon_miss(ic);
 432 }