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 #include <sys/param.h>
229999SWang.Lin@Sun.COM #include <sys/types.h>
239999SWang.Lin@Sun.COM #include <sys/signal.h>
249999SWang.Lin@Sun.COM #include <sys/stream.h>
259999SWang.Lin@Sun.COM #include <sys/termio.h>
269999SWang.Lin@Sun.COM #include <sys/errno.h>
279999SWang.Lin@Sun.COM #include <sys/file.h>
289999SWang.Lin@Sun.COM #include <sys/cmn_err.h>
299999SWang.Lin@Sun.COM #include <sys/stropts.h>
309999SWang.Lin@Sun.COM #include <sys/strsubr.h>
319999SWang.Lin@Sun.COM #include <sys/strtty.h>
329999SWang.Lin@Sun.COM #include <sys/kbio.h>
339999SWang.Lin@Sun.COM #include <sys/cred.h>
349999SWang.Lin@Sun.COM #include <sys/stat.h>
359999SWang.Lin@Sun.COM #include <sys/consdev.h>
369999SWang.Lin@Sun.COM #include <sys/kmem.h>
379999SWang.Lin@Sun.COM #include <sys/modctl.h>
389999SWang.Lin@Sun.COM #include <sys/ddi.h>
399999SWang.Lin@Sun.COM #include <sys/sunddi.h>
409999SWang.Lin@Sun.COM #include <sys/pci.h>
419999SWang.Lin@Sun.COM #include <sys/errno.h>
429999SWang.Lin@Sun.COM #include <sys/mac_provider.h>
439999SWang.Lin@Sun.COM #include <sys/dlpi.h>
449999SWang.Lin@Sun.COM #include <sys/ethernet.h>
459999SWang.Lin@Sun.COM #include <sys/list.h>
469999SWang.Lin@Sun.COM #include <sys/byteorder.h>
479999SWang.Lin@Sun.COM #include <sys/strsun.h>
489999SWang.Lin@Sun.COM #include <sys/policy.h>
499999SWang.Lin@Sun.COM #include <inet/common.h>
509999SWang.Lin@Sun.COM #include <inet/nd.h>
519999SWang.Lin@Sun.COM #include <inet/mi.h>
529999SWang.Lin@Sun.COM #include <inet/wifi_ioctl.h>
539999SWang.Lin@Sun.COM #include <sys/mac_wifi.h>
549999SWang.Lin@Sun.COM
559999SWang.Lin@Sun.COM #include "arn_core.h"
569999SWang.Lin@Sun.COM
579999SWang.Lin@Sun.COM #define BITS_PER_BYTE 8
589999SWang.Lin@Sun.COM #define OFDM_PLCP_BITS 22
599999SWang.Lin@Sun.COM #define HT_RC_2_MCS(_rc) ((_rc) & 0x0f)
609999SWang.Lin@Sun.COM #define HT_RC_2_STREAMS(_rc) ((((_rc) & 0x78) >> 3) + 1)
619999SWang.Lin@Sun.COM #define L_STF 8
629999SWang.Lin@Sun.COM #define L_LTF 8
639999SWang.Lin@Sun.COM #define L_SIG 4
649999SWang.Lin@Sun.COM #define HT_SIG 8
659999SWang.Lin@Sun.COM #define HT_STF 4
669999SWang.Lin@Sun.COM #define HT_LTF(_ns) (4 * (_ns))
679999SWang.Lin@Sun.COM #define SYMBOL_TIME(_ns) ((_ns) << 2) /* ns * 4 us */
689999SWang.Lin@Sun.COM #define SYMBOL_TIME_HALFGI(_ns) (((_ns) * 18 + 4) / 5) /* ns * 3.6 us */
69*11729SWang.Lin@Sun.COM #define NUM_SYMBOLS_PER_USEC(_usec) (_usec >> 2)
70*11729SWang.Lin@Sun.COM #define NUM_SYMBOLS_PER_USEC_HALFGI(_usec) (((_usec*5)-4)/18)
719999SWang.Lin@Sun.COM
729999SWang.Lin@Sun.COM #define OFDM_SIFS_TIME 16
739999SWang.Lin@Sun.COM
74*11729SWang.Lin@Sun.COM static uint32_t bits_per_symbol[][2] = {
75*11729SWang.Lin@Sun.COM /* 20MHz 40MHz */
76*11729SWang.Lin@Sun.COM { 26, 54 }, /* 0: BPSK */
77*11729SWang.Lin@Sun.COM { 52, 108 }, /* 1: QPSK 1/2 */
78*11729SWang.Lin@Sun.COM { 78, 162 }, /* 2: QPSK 3/4 */
79*11729SWang.Lin@Sun.COM { 104, 216 }, /* 3: 16-QAM 1/2 */
80*11729SWang.Lin@Sun.COM { 156, 324 }, /* 4: 16-QAM 3/4 */
81*11729SWang.Lin@Sun.COM { 208, 432 }, /* 5: 64-QAM 2/3 */
82*11729SWang.Lin@Sun.COM { 234, 486 }, /* 6: 64-QAM 3/4 */
83*11729SWang.Lin@Sun.COM { 260, 540 }, /* 7: 64-QAM 5/6 */
84*11729SWang.Lin@Sun.COM { 52, 108 }, /* 8: BPSK */
85*11729SWang.Lin@Sun.COM { 104, 216 }, /* 9: QPSK 1/2 */
86*11729SWang.Lin@Sun.COM { 156, 324 }, /* 10: QPSK 3/4 */
87*11729SWang.Lin@Sun.COM { 208, 432 }, /* 11: 16-QAM 1/2 */
88*11729SWang.Lin@Sun.COM { 312, 648 }, /* 12: 16-QAM 3/4 */
89*11729SWang.Lin@Sun.COM { 416, 864 }, /* 13: 64-QAM 2/3 */
90*11729SWang.Lin@Sun.COM { 468, 972 }, /* 14: 64-QAM 3/4 */
91*11729SWang.Lin@Sun.COM { 520, 1080 }, /* 15: 64-QAM 5/6 */
92*11729SWang.Lin@Sun.COM };
93*11729SWang.Lin@Sun.COM
949999SWang.Lin@Sun.COM #define IS_HT_RATE(_rate) ((_rate) & 0x80)
959999SWang.Lin@Sun.COM
96*11729SWang.Lin@Sun.COM #ifdef ARN_TX_AGGREGRATION
97*11729SWang.Lin@Sun.COM static void arn_tx_send_ht_normal(struct arn_softc *sc, struct ath_txq *txq,
98*11729SWang.Lin@Sun.COM struct ath_atx_tid *tid, list_t *bf_list);
99*11729SWang.Lin@Sun.COM static void arn_tx_complete_buf(struct arn_softc *sc, struct ath_buf *bf,
100*11729SWang.Lin@Sun.COM list_t *bf_q, int txok, int sendbar);
101*11729SWang.Lin@Sun.COM static void arn_tx_txqaddbuf(struct arn_softc *sc, struct ath_txq *txq,
102*11729SWang.Lin@Sun.COM list_t *buf_list);
103*11729SWang.Lin@Sun.COM static void arn_buf_set_rate(struct arn_softc *sc, struct ath_buf *bf);
104*11729SWang.Lin@Sun.COM static int arn_tx_num_badfrms(struct arn_softc *sc,
105*11729SWang.Lin@Sun.COM struct ath_buf *bf, int txok);
106*11729SWang.Lin@Sun.COM static void arn_tx_rc_status(struct ath_buf *bf, struct ath_desc *ds,
107*11729SWang.Lin@Sun.COM int nbad, int txok, boolean_t update_rc);
108*11729SWang.Lin@Sun.COM #endif
109*11729SWang.Lin@Sun.COM
1109999SWang.Lin@Sun.COM static void
arn_get_beaconconfig(struct arn_softc * sc,struct ath_beacon_config * conf)1119999SWang.Lin@Sun.COM arn_get_beaconconfig(struct arn_softc *sc, struct ath_beacon_config *conf)
1129999SWang.Lin@Sun.COM {
1139999SWang.Lin@Sun.COM ieee80211com_t *ic = (ieee80211com_t *)sc;
1149999SWang.Lin@Sun.COM struct ieee80211_node *in = ic->ic_bss;
1159999SWang.Lin@Sun.COM
1169999SWang.Lin@Sun.COM /* fill in beacon config data */
1179999SWang.Lin@Sun.COM
1189999SWang.Lin@Sun.COM conf->beacon_interval = in->in_intval ?
1199999SWang.Lin@Sun.COM in->in_intval : ATH_DEFAULT_BINTVAL;
1209999SWang.Lin@Sun.COM conf->listen_interval = 100;
1219999SWang.Lin@Sun.COM conf->dtim_count = 1;
1229999SWang.Lin@Sun.COM conf->bmiss_timeout = ATH_DEFAULT_BMISS_LIMIT * conf->listen_interval;
1239999SWang.Lin@Sun.COM }
1249999SWang.Lin@Sun.COM
125*11729SWang.Lin@Sun.COM /* Aggregation logic */
126*11729SWang.Lin@Sun.COM
127*11729SWang.Lin@Sun.COM #ifdef ARN_TX_AGGREGATION
128*11729SWang.Lin@Sun.COM
129*11729SWang.Lin@Sun.COM /* Check if it's okay to send out aggregates */
130*11729SWang.Lin@Sun.COM static int
arn_aggr_query(struct arn_softc * sc,struct ath_node * an,uint8_t tidno)131*11729SWang.Lin@Sun.COM arn_aggr_query(struct arn_softc *sc, struct ath_node *an, uint8_t tidno)
132*11729SWang.Lin@Sun.COM {
133*11729SWang.Lin@Sun.COM struct ath_atx_tid *tid;
134*11729SWang.Lin@Sun.COM tid = ATH_AN_2_TID(an, tidno);
135*11729SWang.Lin@Sun.COM
136*11729SWang.Lin@Sun.COM if (tid->state & AGGR_ADDBA_COMPLETE ||
137*11729SWang.Lin@Sun.COM tid->state & AGGR_ADDBA_PROGRESS)
138*11729SWang.Lin@Sun.COM return (1);
139*11729SWang.Lin@Sun.COM else
140*11729SWang.Lin@Sun.COM return (0);
141*11729SWang.Lin@Sun.COM }
142*11729SWang.Lin@Sun.COM
143*11729SWang.Lin@Sun.COM /*
144*11729SWang.Lin@Sun.COM * queue up a dest/ac pair for tx scheduling
145*11729SWang.Lin@Sun.COM * NB: must be called with txq lock held
146*11729SWang.Lin@Sun.COM */
147*11729SWang.Lin@Sun.COM static void
arn_tx_queue_tid(struct ath_txq * txq,struct ath_atx_tid * tid)148*11729SWang.Lin@Sun.COM arn_tx_queue_tid(struct ath_txq *txq, struct ath_atx_tid *tid)
149*11729SWang.Lin@Sun.COM {
150*11729SWang.Lin@Sun.COM struct ath_atx_ac *ac = tid->ac;
151*11729SWang.Lin@Sun.COM
152*11729SWang.Lin@Sun.COM /* if tid is paused, hold off */
153*11729SWang.Lin@Sun.COM if (tid->paused)
154*11729SWang.Lin@Sun.COM return;
155*11729SWang.Lin@Sun.COM
156*11729SWang.Lin@Sun.COM /* add tid to ac atmost once */
157*11729SWang.Lin@Sun.COM if (tid->sched)
158*11729SWang.Lin@Sun.COM return;
159*11729SWang.Lin@Sun.COM
160*11729SWang.Lin@Sun.COM tid->sched = B_TRUE;
161*11729SWang.Lin@Sun.COM list_insert_tail(&ac->tid_q, &tid->list);
162*11729SWang.Lin@Sun.COM
163*11729SWang.Lin@Sun.COM /* add node ac to txq atmost once */
164*11729SWang.Lin@Sun.COM if (ac->sched)
165*11729SWang.Lin@Sun.COM return;
166*11729SWang.Lin@Sun.COM
167*11729SWang.Lin@Sun.COM ac->sched = B_TRUE;
168*11729SWang.Lin@Sun.COM list_insert_tail(&txq->axq_acq, &ac->list);
169*11729SWang.Lin@Sun.COM }
170*11729SWang.Lin@Sun.COM
171*11729SWang.Lin@Sun.COM /* pause a tid */
172*11729SWang.Lin@Sun.COM static void
arn_tx_pause_tid(struct arn_softc * sc,struct ath_atx_tid * tid)173*11729SWang.Lin@Sun.COM arn_tx_pause_tid(struct arn_softc *sc, struct ath_atx_tid *tid)
174*11729SWang.Lin@Sun.COM {
175*11729SWang.Lin@Sun.COM struct ath_txq *txq = &sc->sc_txq[tid->ac->qnum];
176*11729SWang.Lin@Sun.COM
177*11729SWang.Lin@Sun.COM mutex_enter(&txq->axq_lock);
178*11729SWang.Lin@Sun.COM
179*11729SWang.Lin@Sun.COM tid->paused++;
180*11729SWang.Lin@Sun.COM
181*11729SWang.Lin@Sun.COM mutex_exit(&txq->axq_lock);
182*11729SWang.Lin@Sun.COM }
183*11729SWang.Lin@Sun.COM
184*11729SWang.Lin@Sun.COM /* resume a tid and schedule aggregate */
185*11729SWang.Lin@Sun.COM void
arn_tx_resume_tid(struct arn_softc * sc,struct ath_atx_tid * tid)186*11729SWang.Lin@Sun.COM arn_tx_resume_tid(struct arn_softc *sc, struct ath_atx_tid *tid)
187*11729SWang.Lin@Sun.COM {
188*11729SWang.Lin@Sun.COM struct ath_txq *txq = &sc->sc_txq[tid->ac->qnum];
189*11729SWang.Lin@Sun.COM
190*11729SWang.Lin@Sun.COM ASSERT(tid->paused > 0);
191*11729SWang.Lin@Sun.COM mutex_enter(&txq->axq_lock);
192*11729SWang.Lin@Sun.COM
193*11729SWang.Lin@Sun.COM tid->paused--;
194*11729SWang.Lin@Sun.COM
195*11729SWang.Lin@Sun.COM if (tid->paused > 0)
196*11729SWang.Lin@Sun.COM goto unlock;
197*11729SWang.Lin@Sun.COM
198*11729SWang.Lin@Sun.COM if (list_empty(&tid->buf_q))
199*11729SWang.Lin@Sun.COM goto unlock;
200*11729SWang.Lin@Sun.COM
201*11729SWang.Lin@Sun.COM /*
202*11729SWang.Lin@Sun.COM * Add this TID to scheduler and try to send out aggregates
203*11729SWang.Lin@Sun.COM */
204*11729SWang.Lin@Sun.COM arn_tx_queue_tid(txq, tid);
205*11729SWang.Lin@Sun.COM arn_txq_schedule(sc, txq);
206*11729SWang.Lin@Sun.COM unlock:
207*11729SWang.Lin@Sun.COM mutex_exit(&txq->axq_lock);
208*11729SWang.Lin@Sun.COM }
209*11729SWang.Lin@Sun.COM
210*11729SWang.Lin@Sun.COM /* flush tid's software queue and send frames as non-ampdu's */
211*11729SWang.Lin@Sun.COM static void
arn_tx_flush_tid(struct arn_softc * sc,struct ath_atx_tid * tid)212*11729SWang.Lin@Sun.COM arn_tx_flush_tid(struct arn_softc *sc, struct ath_atx_tid *tid)
213*11729SWang.Lin@Sun.COM {
214*11729SWang.Lin@Sun.COM struct ath_txq *txq = &sc->sc_txq[tid->ac->qnum];
215*11729SWang.Lin@Sun.COM struct ath_buf *bf;
216*11729SWang.Lin@Sun.COM
217*11729SWang.Lin@Sun.COM list_t list;
218*11729SWang.Lin@Sun.COM list_create(&list, sizeof (struct ath_buf),
219*11729SWang.Lin@Sun.COM offsetof(struct ath_buf, bf_node));
220*11729SWang.Lin@Sun.COM
221*11729SWang.Lin@Sun.COM ASSERT(tid->paused > 0);
222*11729SWang.Lin@Sun.COM mutex_enter(&txq->axq_lock);
223*11729SWang.Lin@Sun.COM
224*11729SWang.Lin@Sun.COM tid->paused--;
225*11729SWang.Lin@Sun.COM
226*11729SWang.Lin@Sun.COM if (tid->paused > 0) {
227*11729SWang.Lin@Sun.COM mutex_exit(&txq->axq_lock);
228*11729SWang.Lin@Sun.COM return;
229*11729SWang.Lin@Sun.COM }
230*11729SWang.Lin@Sun.COM
231*11729SWang.Lin@Sun.COM while (!list_empty(&tid->buf_q)) {
232*11729SWang.Lin@Sun.COM bf = list_head(&tid->buf_q);
233*11729SWang.Lin@Sun.COM ASSERT(!bf_isretried(bf));
234*11729SWang.Lin@Sun.COM list_remove(&tid->buf_q, bf);
235*11729SWang.Lin@Sun.COM list_insert_tail(&list, bf);
236*11729SWang.Lin@Sun.COM arn_tx_send_ht_normal(sc, txq, tid, &list);
237*11729SWang.Lin@Sun.COM }
238*11729SWang.Lin@Sun.COM
239*11729SWang.Lin@Sun.COM mutex_exit(&txq->axq_lock);
240*11729SWang.Lin@Sun.COM }
241*11729SWang.Lin@Sun.COM
242*11729SWang.Lin@Sun.COM /* Update block ack window */
243*11729SWang.Lin@Sun.COM static void
arn_tx_update_baw(struct arn_softc * sc,struct ath_atx_tid * tid,int seqno)244*11729SWang.Lin@Sun.COM arn_tx_update_baw(struct arn_softc *sc, struct ath_atx_tid *tid, int seqno)
245*11729SWang.Lin@Sun.COM {
246*11729SWang.Lin@Sun.COM int index, cindex;
247*11729SWang.Lin@Sun.COM
248*11729SWang.Lin@Sun.COM index = ATH_BA_INDEX(tid->seq_start, seqno);
249*11729SWang.Lin@Sun.COM cindex = (tid->baw_head + index) & (ATH_TID_MAX_BUFS - 1);
250*11729SWang.Lin@Sun.COM
251*11729SWang.Lin@Sun.COM tid->tx_buf[cindex] = NULL;
252*11729SWang.Lin@Sun.COM
253*11729SWang.Lin@Sun.COM while (tid->baw_head != tid->baw_tail && !tid->tx_buf[tid->baw_head]) {
254*11729SWang.Lin@Sun.COM INCR(tid->seq_start, IEEE80211_SEQ_MAX);
255*11729SWang.Lin@Sun.COM INCR(tid->baw_head, ATH_TID_MAX_BUFS);
256*11729SWang.Lin@Sun.COM }
257*11729SWang.Lin@Sun.COM }
258*11729SWang.Lin@Sun.COM
259*11729SWang.Lin@Sun.COM /* Add a sub-frame to block ack window */
260*11729SWang.Lin@Sun.COM static void
arn_tx_addto_baw(struct arn_softc * sc,struct ath_atx_tid * tid,struct ath_buf * bf)261*11729SWang.Lin@Sun.COM arn_tx_addto_baw(struct arn_softc *sc, struct ath_atx_tid *tid,
262*11729SWang.Lin@Sun.COM struct ath_buf *bf)
263*11729SWang.Lin@Sun.COM {
264*11729SWang.Lin@Sun.COM int index, cindex;
265*11729SWang.Lin@Sun.COM
266*11729SWang.Lin@Sun.COM if (bf_isretried(bf))
267*11729SWang.Lin@Sun.COM return;
268*11729SWang.Lin@Sun.COM
269*11729SWang.Lin@Sun.COM index = ATH_BA_INDEX(tid->seq_start, bf->bf_seqno);
270*11729SWang.Lin@Sun.COM cindex = (tid->baw_head + index) & (ATH_TID_MAX_BUFS - 1);
271*11729SWang.Lin@Sun.COM
272*11729SWang.Lin@Sun.COM ASSERT(tid->tx_buf[cindex] == NULL);
273*11729SWang.Lin@Sun.COM tid->tx_buf[cindex] = bf;
274*11729SWang.Lin@Sun.COM
275*11729SWang.Lin@Sun.COM if (index >= ((tid->baw_tail - tid->baw_head) &
276*11729SWang.Lin@Sun.COM (ATH_TID_MAX_BUFS - 1))) {
277*11729SWang.Lin@Sun.COM tid->baw_tail = cindex;
278*11729SWang.Lin@Sun.COM INCR(tid->baw_tail, ATH_TID_MAX_BUFS);
279*11729SWang.Lin@Sun.COM }
280*11729SWang.Lin@Sun.COM }
281*11729SWang.Lin@Sun.COM
282*11729SWang.Lin@Sun.COM /*
283*11729SWang.Lin@Sun.COM * TODO: For frame(s) that are in the retry state, we will reuse the
284*11729SWang.Lin@Sun.COM * sequence number(s) without setting the retry bit. The
285*11729SWang.Lin@Sun.COM * alternative is to give up on these and BAR the receiver's window
286*11729SWang.Lin@Sun.COM * forward.
287*11729SWang.Lin@Sun.COM */
288*11729SWang.Lin@Sun.COM static void
arn_tid_drain(struct arn_softc * sc,struct ath_txq * txq,struct ath_atx_tid * tid)289*11729SWang.Lin@Sun.COM arn_tid_drain(struct arn_softc *sc,
290*11729SWang.Lin@Sun.COM struct ath_txq *txq,
291*11729SWang.Lin@Sun.COM struct ath_atx_tid *tid)
292*11729SWang.Lin@Sun.COM
293*11729SWang.Lin@Sun.COM {
294*11729SWang.Lin@Sun.COM struct ath_buf *bf;
295*11729SWang.Lin@Sun.COM
296*11729SWang.Lin@Sun.COM list_t list;
297*11729SWang.Lin@Sun.COM list_create(&list, sizeof (struct ath_buf),
298*11729SWang.Lin@Sun.COM offsetof(struct ath_buf, bf_node));
299*11729SWang.Lin@Sun.COM
300*11729SWang.Lin@Sun.COM for (;;) {
301*11729SWang.Lin@Sun.COM if (list_empty(&tid->buf_q))
302*11729SWang.Lin@Sun.COM break;
303*11729SWang.Lin@Sun.COM
304*11729SWang.Lin@Sun.COM bf = list_head(&tid->buf_q);
305*11729SWang.Lin@Sun.COM list_remove(&tid->buf_q, bf);
306*11729SWang.Lin@Sun.COM list_insert_tail(&list, bf);
307*11729SWang.Lin@Sun.COM
308*11729SWang.Lin@Sun.COM if (bf_isretried(bf))
309*11729SWang.Lin@Sun.COM arn_tx_update_baw(sc, tid, bf->bf_seqno);
310*11729SWang.Lin@Sun.COM
311*11729SWang.Lin@Sun.COM mutex_enter(&txq->axq_lock);
312*11729SWang.Lin@Sun.COM arn_tx_complete_buf(sc, bf, &list, 0, 0);
313*11729SWang.Lin@Sun.COM mutex_exit(&txq->axq_lock);
314*11729SWang.Lin@Sun.COM }
315*11729SWang.Lin@Sun.COM
316*11729SWang.Lin@Sun.COM tid->seq_next = tid->seq_start;
317*11729SWang.Lin@Sun.COM tid->baw_tail = tid->baw_head;
318*11729SWang.Lin@Sun.COM }
319*11729SWang.Lin@Sun.COM
320*11729SWang.Lin@Sun.COM static void
arn_tx_set_retry(struct arn_softc * sc,struct ath_buf * bf)321*11729SWang.Lin@Sun.COM arn_tx_set_retry(struct arn_softc *sc, struct ath_buf *bf)
322*11729SWang.Lin@Sun.COM {
323*11729SWang.Lin@Sun.COM struct ieee80211_frame *wh;
324*11729SWang.Lin@Sun.COM wh = (struct ieee80211_frame *)bf->bf_dma.mem_va;
325*11729SWang.Lin@Sun.COM
326*11729SWang.Lin@Sun.COM bf->bf_state.bf_type |= BUF_RETRY;
327*11729SWang.Lin@Sun.COM bf->bf_retries++;
328*11729SWang.Lin@Sun.COM
329*11729SWang.Lin@Sun.COM *(uint16_t *)&wh->i_seq[0] |= LE_16(0x0800); /* ??? */
330*11729SWang.Lin@Sun.COM }
331*11729SWang.Lin@Sun.COM
332*11729SWang.Lin@Sun.COM static struct ath_buf *
arn_clone_txbuf(struct arn_softc * sc,struct ath_buf * bf)333*11729SWang.Lin@Sun.COM arn_clone_txbuf(struct arn_softc *sc, struct ath_buf *bf)
334*11729SWang.Lin@Sun.COM {
335*11729SWang.Lin@Sun.COM struct ath_buf *tbf;
336*11729SWang.Lin@Sun.COM
337*11729SWang.Lin@Sun.COM mutex_enter(&sc->sc_txbuflock);
338*11729SWang.Lin@Sun.COM ASSERT(!list_empty((&sc->sc_txbuf_list)));
339*11729SWang.Lin@Sun.COM
340*11729SWang.Lin@Sun.COM tbf = list_head(&sc->sc_txbuf_list);
341*11729SWang.Lin@Sun.COM list_remove(&sc->sc_txbuf_list, tbf);
342*11729SWang.Lin@Sun.COM mutex_exit(&sc->sc_txbuflock);
343*11729SWang.Lin@Sun.COM
344*11729SWang.Lin@Sun.COM ATH_TXBUF_RESET(tbf);
345*11729SWang.Lin@Sun.COM
346*11729SWang.Lin@Sun.COM tbf->bf_daddr = bf->bf_daddr; /* physical addr of desc */
347*11729SWang.Lin@Sun.COM tbf->bf_dma = bf->bf_dma; /* dma area for buf */
348*11729SWang.Lin@Sun.COM *(tbf->bf_desc) = *(bf->bf_desc); /* virtual addr of desc */
349*11729SWang.Lin@Sun.COM tbf->bf_state = bf->bf_state; /* buffer state */
350*11729SWang.Lin@Sun.COM
351*11729SWang.Lin@Sun.COM return (tbf);
352*11729SWang.Lin@Sun.COM }
353*11729SWang.Lin@Sun.COM
354*11729SWang.Lin@Sun.COM static void
arn_tx_complete_aggr(struct arn_softc * sc,struct ath_txq * txq,struct ath_buf * bf,list_t * bf_q,int txok)355*11729SWang.Lin@Sun.COM arn_tx_complete_aggr(struct arn_softc *sc, struct ath_txq *txq,
356*11729SWang.Lin@Sun.COM struct ath_buf *bf, list_t *bf_q, int txok)
357*11729SWang.Lin@Sun.COM {
358*11729SWang.Lin@Sun.COM struct ieee80211_node *in;
359*11729SWang.Lin@Sun.COM struct ath_node *an = NULL;
360*11729SWang.Lin@Sun.COM struct ath_atx_tid *tid = NULL;
361*11729SWang.Lin@Sun.COM struct ath_buf *bf_next, *bf_last = bf->bf_lastbf;
362*11729SWang.Lin@Sun.COM struct ath_desc *ds = bf_last->bf_desc;
363*11729SWang.Lin@Sun.COM
364*11729SWang.Lin@Sun.COM list_t list, list_pending;
365*11729SWang.Lin@Sun.COM uint16_t seq_st = 0, acked_cnt = 0, txfail_cnt = 0;
366*11729SWang.Lin@Sun.COM uint32_t ba[WME_BA_BMP_SIZE >> 5];
367*11729SWang.Lin@Sun.COM int isaggr, txfail, txpending, sendbar = 0, needreset = 0, nbad = 0;
368*11729SWang.Lin@Sun.COM boolean_t rc_update = B_TRUE;
369*11729SWang.Lin@Sun.COM
370*11729SWang.Lin@Sun.COM an = ATH_NODE(in); /* Be sure in != NULL */
371*11729SWang.Lin@Sun.COM tid = ATH_AN_2_TID(an, bf->bf_tidno);
372*11729SWang.Lin@Sun.COM
373*11729SWang.Lin@Sun.COM isaggr = bf_isaggr(bf);
374*11729SWang.Lin@Sun.COM memset(ba, 0, WME_BA_BMP_SIZE >> 3);
375*11729SWang.Lin@Sun.COM
376*11729SWang.Lin@Sun.COM if (isaggr && txok) {
377*11729SWang.Lin@Sun.COM if (ATH_DS_TX_BA(ds)) {
378*11729SWang.Lin@Sun.COM seq_st = ATH_DS_BA_SEQ(ds);
379*11729SWang.Lin@Sun.COM memcpy(ba, ATH_DS_BA_BITMAP(ds),
380*11729SWang.Lin@Sun.COM WME_BA_BMP_SIZE >> 3);
381*11729SWang.Lin@Sun.COM } else {
382*11729SWang.Lin@Sun.COM /*
383*11729SWang.Lin@Sun.COM * AR5416 can become deaf/mute when BA
384*11729SWang.Lin@Sun.COM * issue happens. Chip needs to be reset.
385*11729SWang.Lin@Sun.COM * But AP code may have sychronization issues
386*11729SWang.Lin@Sun.COM * when perform internal reset in this routine.
387*11729SWang.Lin@Sun.COM * Only enable reset in STA mode for now.
388*11729SWang.Lin@Sun.COM */
389*11729SWang.Lin@Sun.COM if (sc->sc_ah->ah_opmode == ATH9K_M_STA)
390*11729SWang.Lin@Sun.COM needreset = 1;
391*11729SWang.Lin@Sun.COM }
392*11729SWang.Lin@Sun.COM }
393*11729SWang.Lin@Sun.COM
394*11729SWang.Lin@Sun.COM list_create(&list_pending, sizeof (struct ath_buf),
395*11729SWang.Lin@Sun.COM offsetof(struct ath_buf, bf_node));
396*11729SWang.Lin@Sun.COM list_create(&list, sizeof (struct ath_buf),
397*11729SWang.Lin@Sun.COM offsetof(struct ath_buf, bf_node));
398*11729SWang.Lin@Sun.COM
399*11729SWang.Lin@Sun.COM nbad = arn_tx_num_badfrms(sc, bf, txok);
400*11729SWang.Lin@Sun.COM while (bf) {
401*11729SWang.Lin@Sun.COM txfail = txpending = 0;
402*11729SWang.Lin@Sun.COM bf_next = bf->bf_next;
403*11729SWang.Lin@Sun.COM
404*11729SWang.Lin@Sun.COM if (ATH_BA_ISSET(ba, ATH_BA_INDEX(seq_st, bf->bf_seqno))) {
405*11729SWang.Lin@Sun.COM /*
406*11729SWang.Lin@Sun.COM * transmit completion, subframe is
407*11729SWang.Lin@Sun.COM * acked by block ack
408*11729SWang.Lin@Sun.COM */
409*11729SWang.Lin@Sun.COM acked_cnt++;
410*11729SWang.Lin@Sun.COM } else if (!isaggr && txok) {
411*11729SWang.Lin@Sun.COM /* transmit completion */
412*11729SWang.Lin@Sun.COM acked_cnt++;
413*11729SWang.Lin@Sun.COM } else {
414*11729SWang.Lin@Sun.COM if (!(tid->state & AGGR_CLEANUP) &&
415*11729SWang.Lin@Sun.COM ds->ds_txstat.ts_flags != ATH9K_TX_SW_ABORTED) {
416*11729SWang.Lin@Sun.COM if (bf->bf_retries < ATH_MAX_SW_RETRIES) {
417*11729SWang.Lin@Sun.COM arn_tx_set_retry(sc, bf);
418*11729SWang.Lin@Sun.COM txpending = 1;
419*11729SWang.Lin@Sun.COM } else {
420*11729SWang.Lin@Sun.COM bf->bf_state.bf_type |= BUF_XRETRY;
421*11729SWang.Lin@Sun.COM txfail = 1;
422*11729SWang.Lin@Sun.COM sendbar = 1;
423*11729SWang.Lin@Sun.COM txfail_cnt++;
424*11729SWang.Lin@Sun.COM }
425*11729SWang.Lin@Sun.COM } else {
426*11729SWang.Lin@Sun.COM /*
427*11729SWang.Lin@Sun.COM * cleanup in progress, just fail
428*11729SWang.Lin@Sun.COM * the un-acked sub-frames
429*11729SWang.Lin@Sun.COM */
430*11729SWang.Lin@Sun.COM txfail = 1;
431*11729SWang.Lin@Sun.COM }
432*11729SWang.Lin@Sun.COM }
433*11729SWang.Lin@Sun.COM
434*11729SWang.Lin@Sun.COM if (bf_next == NULL) {
435*11729SWang.Lin@Sun.COM /* INIT_LIST_HEAD */
436*11729SWang.Lin@Sun.COM list_create(&list, sizeof (struct ath_buf),
437*11729SWang.Lin@Sun.COM offsetof(struct ath_buf, bf_node));
438*11729SWang.Lin@Sun.COM } else {
439*11729SWang.Lin@Sun.COM ASSERT(!list_empty(bf_q));
440*11729SWang.Lin@Sun.COM list_remove(bf_q, bf);
441*11729SWang.Lin@Sun.COM list_insert_tail(&list, bf);
442*11729SWang.Lin@Sun.COM }
443*11729SWang.Lin@Sun.COM
444*11729SWang.Lin@Sun.COM if (!txpending) {
445*11729SWang.Lin@Sun.COM /*
446*11729SWang.Lin@Sun.COM * complete the acked-ones/xretried ones; update
447*11729SWang.Lin@Sun.COM * block-ack window
448*11729SWang.Lin@Sun.COM */
449*11729SWang.Lin@Sun.COM mutex_enter(&txq->axq_lock);
450*11729SWang.Lin@Sun.COM arn_tx_update_baw(sc, tid, bf->bf_seqno);
451*11729SWang.Lin@Sun.COM mutex_exit(&txq->axq_lock);
452*11729SWang.Lin@Sun.COM
453*11729SWang.Lin@Sun.COM if (rc_update && (acked_cnt == 1 || txfail_cnt == 1)) {
454*11729SWang.Lin@Sun.COM ath_tx_rc_status(bf, ds, nbad, txok, B_TRUE);
455*11729SWang.Lin@Sun.COM rc_update = B_FALSE;
456*11729SWang.Lin@Sun.COM } else {
457*11729SWang.Lin@Sun.COM ath_tx_rc_status(bf, ds, nbad, txok, B_FALSE);
458*11729SWang.Lin@Sun.COM }
459*11729SWang.Lin@Sun.COM
460*11729SWang.Lin@Sun.COM ath_tx_complete_buf(sc, bf, list, !txfail, sendbar);
461*11729SWang.Lin@Sun.COM } else {
462*11729SWang.Lin@Sun.COM /* retry the un-acked ones */
463*11729SWang.Lin@Sun.COM if (bf->bf_next == NULL &&
464*11729SWang.Lin@Sun.COM bf_last->bf_status & ATH_BUFSTATUS_STALE) {
465*11729SWang.Lin@Sun.COM struct ath_buf *tbf;
466*11729SWang.Lin@Sun.COM
467*11729SWang.Lin@Sun.COM tbf = arn_clone_txbuf(sc, bf_last);
468*11729SWang.Lin@Sun.COM ath9k_hw_cleartxdesc(sc->sc_ah, tbf->bf_desc);
469*11729SWang.Lin@Sun.COM list_insert_tail(&list, tbf);
470*11729SWang.Lin@Sun.COM } else {
471*11729SWang.Lin@Sun.COM /*
472*11729SWang.Lin@Sun.COM * Clear descriptor status words for
473*11729SWang.Lin@Sun.COM * software retry
474*11729SWang.Lin@Sun.COM */
475*11729SWang.Lin@Sun.COM ath9k_hw_cleartxdesc(sc->sc_ah, bf->bf_desc);
476*11729SWang.Lin@Sun.COM }
477*11729SWang.Lin@Sun.COM
478*11729SWang.Lin@Sun.COM /*
479*11729SWang.Lin@Sun.COM * Put this buffer to the temporary pending
480*11729SWang.Lin@Sun.COM * queue to retain ordering
481*11729SWang.Lin@Sun.COM */
482*11729SWang.Lin@Sun.COM list_splice_tail_init(&list, &list_pending);
483*11729SWang.Lin@Sun.COM /*
484*11729SWang.Lin@Sun.COM * Insert src list after dst list.
485*11729SWang.Lin@Sun.COM * Empty src list thereafter
486*11729SWang.Lin@Sun.COM */
487*11729SWang.Lin@Sun.COM list_move_tail(&list_pending, &list);
488*11729SWang.Lin@Sun.COM /* should re-initialize list here??? */
489*11729SWang.Lin@Sun.COM }
490*11729SWang.Lin@Sun.COM
491*11729SWang.Lin@Sun.COM bf = bf_next;
492*11729SWang.Lin@Sun.COM }
493*11729SWang.Lin@Sun.COM
494*11729SWang.Lin@Sun.COM if (tid->state & AGGR_CLEANUP) {
495*11729SWang.Lin@Sun.COM if (tid->baw_head == tid->baw_tail) {
496*11729SWang.Lin@Sun.COM tid->state &= ~AGGR_ADDBA_COMPLETE;
497*11729SWang.Lin@Sun.COM tid->addba_exchangeattempts = 0;
498*11729SWang.Lin@Sun.COM tid->state &= ~AGGR_CLEANUP;
499*11729SWang.Lin@Sun.COM
500*11729SWang.Lin@Sun.COM /* send buffered frames as singles */
501*11729SWang.Lin@Sun.COM arn_tx_flush_tid(sc, tid);
502*11729SWang.Lin@Sun.COM }
503*11729SWang.Lin@Sun.COM return;
504*11729SWang.Lin@Sun.COM }
505*11729SWang.Lin@Sun.COM
506*11729SWang.Lin@Sun.COM /*
507*11729SWang.Lin@Sun.COM * prepend un-acked frames to the beginning of
508*11729SWang.Lin@Sun.COM * the pending frame queue
509*11729SWang.Lin@Sun.COM */
510*11729SWang.Lin@Sun.COM
511*11729SWang.Lin@Sun.COM if (!list_empty(&list_pending)) {
512*11729SWang.Lin@Sun.COM mutex_enter(&txq->axq_lock);
513*11729SWang.Lin@Sun.COM list_move_tail(&list_pending, &tid->buf_q);
514*11729SWang.Lin@Sun.COM arn_tx_queue_tid(txq, tid);
515*11729SWang.Lin@Sun.COM mutex_exit(&txq->axq_lock);
516*11729SWang.Lin@Sun.COM }
517*11729SWang.Lin@Sun.COM }
518*11729SWang.Lin@Sun.COM
519*11729SWang.Lin@Sun.COM static uint32_t
arn_lookup_rate(struct arn_softc * sc,struct ath_buf * bf,struct ath_atx_tid * tid)520*11729SWang.Lin@Sun.COM arn_lookup_rate(struct arn_softc *sc, struct ath_buf *bf,
521*11729SWang.Lin@Sun.COM struct ath_atx_tid *tid)
522*11729SWang.Lin@Sun.COM {
523*11729SWang.Lin@Sun.COM struct ath_rate_table *rate_table = sc->sc_currates;
524*11729SWang.Lin@Sun.COM struct ath9k_tx_rate *rates;
525*11729SWang.Lin@Sun.COM struct ath_tx_info_priv *tx_info_priv;
526*11729SWang.Lin@Sun.COM uint32_t max_4ms_framelen, frmlen;
527*11729SWang.Lin@Sun.COM uint16_t aggr_limit, legacy = 0, maxampdu;
528*11729SWang.Lin@Sun.COM int i;
529*11729SWang.Lin@Sun.COM
530*11729SWang.Lin@Sun.COM /* ??? */
531*11729SWang.Lin@Sun.COM rates = (struct ath9k_tx_rate *)bf->rates;
532*11729SWang.Lin@Sun.COM tx_info_priv = (struct ath_tx_info_priv *)&bf->tx_info_priv;
533*11729SWang.Lin@Sun.COM
534*11729SWang.Lin@Sun.COM /*
535*11729SWang.Lin@Sun.COM * Find the lowest frame length among the rate series that will have a
536*11729SWang.Lin@Sun.COM * 4ms transmit duration.
537*11729SWang.Lin@Sun.COM * TODO - TXOP limit needs to be considered.
538*11729SWang.Lin@Sun.COM */
539*11729SWang.Lin@Sun.COM max_4ms_framelen = ATH_AMPDU_LIMIT_MAX;
540*11729SWang.Lin@Sun.COM
541*11729SWang.Lin@Sun.COM for (i = 0; i < 4; i++) {
542*11729SWang.Lin@Sun.COM if (rates[i].count) {
543*11729SWang.Lin@Sun.COM if (!WLAN_RC_PHY_HT
544*11729SWang.Lin@Sun.COM (rate_table->info[rates[i].idx].phy)) {
545*11729SWang.Lin@Sun.COM legacy = 1;
546*11729SWang.Lin@Sun.COM break;
547*11729SWang.Lin@Sun.COM }
548*11729SWang.Lin@Sun.COM
549*11729SWang.Lin@Sun.COM frmlen =
550*11729SWang.Lin@Sun.COM rate_table->info[rates[i].idx].max_4ms_framelen;
551*11729SWang.Lin@Sun.COM max_4ms_framelen = min(max_4ms_framelen, frmlen);
552*11729SWang.Lin@Sun.COM }
553*11729SWang.Lin@Sun.COM }
554*11729SWang.Lin@Sun.COM
555*11729SWang.Lin@Sun.COM /*
556*11729SWang.Lin@Sun.COM * limit aggregate size by the minimum rate if rate selected is
557*11729SWang.Lin@Sun.COM * not a probe rate, if rate selected is a probe rate then
558*11729SWang.Lin@Sun.COM * avoid aggregation of this packet.
559*11729SWang.Lin@Sun.COM */
560*11729SWang.Lin@Sun.COM if (legacy)
561*11729SWang.Lin@Sun.COM return (0);
562*11729SWang.Lin@Sun.COM
563*11729SWang.Lin@Sun.COM aggr_limit = min(max_4ms_framelen, (uint32_t)ATH_AMPDU_LIMIT_DEFAULT);
564*11729SWang.Lin@Sun.COM
565*11729SWang.Lin@Sun.COM /*
566*11729SWang.Lin@Sun.COM * h/w can accept aggregates upto 16 bit lengths (65535).
567*11729SWang.Lin@Sun.COM * The IE, however can hold upto 65536, which shows up here
568*11729SWang.Lin@Sun.COM * as zero. Ignore 65536 since we are constrained by hw.
569*11729SWang.Lin@Sun.COM */
570*11729SWang.Lin@Sun.COM maxampdu = tid->an->maxampdu;
571*11729SWang.Lin@Sun.COM if (maxampdu)
572*11729SWang.Lin@Sun.COM aggr_limit = min(aggr_limit, maxampdu);
573*11729SWang.Lin@Sun.COM
574*11729SWang.Lin@Sun.COM return (aggr_limit);
575*11729SWang.Lin@Sun.COM }
576*11729SWang.Lin@Sun.COM
577*11729SWang.Lin@Sun.COM /*
578*11729SWang.Lin@Sun.COM * Returns the number of delimiters to be added to
579*11729SWang.Lin@Sun.COM * meet the minimum required mpdudensity.
580*11729SWang.Lin@Sun.COM * caller should make sure that the rate is HT rate .
581*11729SWang.Lin@Sun.COM */
582*11729SWang.Lin@Sun.COM static int
arn_compute_num_delims(struct arn_softc * sc,struct ath_atx_tid * tid,struct ath_buf * bf,uint16_t frmlen)583*11729SWang.Lin@Sun.COM arn_compute_num_delims(struct arn_softc *sc, struct ath_atx_tid *tid,
584*11729SWang.Lin@Sun.COM struct ath_buf *bf, uint16_t frmlen)
585*11729SWang.Lin@Sun.COM {
586*11729SWang.Lin@Sun.COM struct ath_rate_table *rt = sc->sc_currates;
587*11729SWang.Lin@Sun.COM struct ath9k_tx_rate *rates = (struct ath9k_tx_rate *)bf->rates;
588*11729SWang.Lin@Sun.COM uint32_t nsymbits, nsymbols, mpdudensity;
589*11729SWang.Lin@Sun.COM uint16_t minlen;
590*11729SWang.Lin@Sun.COM uint8_t rc, flags, rix;
591*11729SWang.Lin@Sun.COM int width, half_gi, ndelim, mindelim;
592*11729SWang.Lin@Sun.COM
593*11729SWang.Lin@Sun.COM /* Select standard number of delimiters based on frame length alone */
594*11729SWang.Lin@Sun.COM ndelim = ATH_AGGR_GET_NDELIM(frmlen);
595*11729SWang.Lin@Sun.COM
596*11729SWang.Lin@Sun.COM /*
597*11729SWang.Lin@Sun.COM * If encryption enabled, hardware requires some more padding between
598*11729SWang.Lin@Sun.COM * subframes.
599*11729SWang.Lin@Sun.COM * TODO - this could be improved to be dependent on the rate.
600*11729SWang.Lin@Sun.COM * The hardware can keep up at lower rates, but not higher rates
601*11729SWang.Lin@Sun.COM */
602*11729SWang.Lin@Sun.COM if (bf->bf_keytype != ATH9K_KEY_TYPE_CLEAR)
603*11729SWang.Lin@Sun.COM ndelim += ATH_AGGR_ENCRYPTDELIM;
604*11729SWang.Lin@Sun.COM
605*11729SWang.Lin@Sun.COM /*
606*11729SWang.Lin@Sun.COM * Convert desired mpdu density from microeconds to bytes based
607*11729SWang.Lin@Sun.COM * on highest rate in rate series (i.e. first rate) to determine
608*11729SWang.Lin@Sun.COM * required minimum length for subframe. Take into account
609*11729SWang.Lin@Sun.COM * whether high rate is 20 or 40Mhz and half or full GI.
610*11729SWang.Lin@Sun.COM */
611*11729SWang.Lin@Sun.COM mpdudensity = tid->an->mpdudensity;
612*11729SWang.Lin@Sun.COM
613*11729SWang.Lin@Sun.COM /*
614*11729SWang.Lin@Sun.COM * If there is no mpdu density restriction, no further calculation
615*11729SWang.Lin@Sun.COM * is needed.
616*11729SWang.Lin@Sun.COM */
617*11729SWang.Lin@Sun.COM if (mpdudensity == 0)
618*11729SWang.Lin@Sun.COM return (ndelim);
619*11729SWang.Lin@Sun.COM
620*11729SWang.Lin@Sun.COM rix = rates[0].idx;
621*11729SWang.Lin@Sun.COM flags = rates[0].flags;
622*11729SWang.Lin@Sun.COM rc = rt->info[rix].ratecode;
623*11729SWang.Lin@Sun.COM width = (flags & ATH9K_TX_RC_40_MHZ_WIDTH) ? 1 : 0;
624*11729SWang.Lin@Sun.COM half_gi = (flags & ATH9K_TX_RC_SHORT_GI) ? 1 : 0;
625*11729SWang.Lin@Sun.COM
626*11729SWang.Lin@Sun.COM if (half_gi)
627*11729SWang.Lin@Sun.COM nsymbols = NUM_SYMBOLS_PER_USEC_HALFGI(mpdudensity);
628*11729SWang.Lin@Sun.COM else
629*11729SWang.Lin@Sun.COM nsymbols = NUM_SYMBOLS_PER_USEC(mpdudensity);
630*11729SWang.Lin@Sun.COM
631*11729SWang.Lin@Sun.COM if (nsymbols == 0)
632*11729SWang.Lin@Sun.COM nsymbols = 1;
633*11729SWang.Lin@Sun.COM
634*11729SWang.Lin@Sun.COM nsymbits = bits_per_symbol[HT_RC_2_MCS(rc)][width];
635*11729SWang.Lin@Sun.COM minlen = (nsymbols * nsymbits) / BITS_PER_BYTE;
636*11729SWang.Lin@Sun.COM
637*11729SWang.Lin@Sun.COM if (frmlen < minlen) {
638*11729SWang.Lin@Sun.COM mindelim = (minlen - frmlen) / ATH_AGGR_DELIM_SZ;
639*11729SWang.Lin@Sun.COM ndelim = max(mindelim, ndelim);
640*11729SWang.Lin@Sun.COM }
641*11729SWang.Lin@Sun.COM
642*11729SWang.Lin@Sun.COM return (ndelim);
643*11729SWang.Lin@Sun.COM }
644*11729SWang.Lin@Sun.COM
645*11729SWang.Lin@Sun.COM static enum ATH_AGGR_STATUS
arn_tx_form_aggr(struct arn_softc * sc,struct ath_atx_tid * tid,list_t * bf_q)646*11729SWang.Lin@Sun.COM arn_tx_form_aggr(struct arn_softc *sc, struct ath_atx_tid *tid,
647*11729SWang.Lin@Sun.COM list_t *bf_q)
648*11729SWang.Lin@Sun.COM {
649*11729SWang.Lin@Sun.COM #define PADBYTES(_len) ((4 - ((_len) % 4)) % 4)
650*11729SWang.Lin@Sun.COM struct ath_buf *bf, *bf_first, *bf_prev = NULL;
651*11729SWang.Lin@Sun.COM int rl = 0, nframes = 0, ndelim, prev_al = 0;
652*11729SWang.Lin@Sun.COM uint16_t aggr_limit = 0, al = 0, bpad = 0,
653*11729SWang.Lin@Sun.COM al_delta, h_baw = tid->baw_size / 2;
654*11729SWang.Lin@Sun.COM enum ATH_AGGR_STATUS status = ATH_AGGR_DONE;
655*11729SWang.Lin@Sun.COM
656*11729SWang.Lin@Sun.COM bf_first = list_head(&tid->buf_q);
657*11729SWang.Lin@Sun.COM
658*11729SWang.Lin@Sun.COM do {
659*11729SWang.Lin@Sun.COM bf = list_head(&tid->buf_q);
660*11729SWang.Lin@Sun.COM
661*11729SWang.Lin@Sun.COM /* do not step over block-ack window */
662*11729SWang.Lin@Sun.COM if (!BAW_WITHIN(tid->seq_start, tid->baw_size, bf->bf_seqno)) {
663*11729SWang.Lin@Sun.COM status = ATH_AGGR_BAW_CLOSED;
664*11729SWang.Lin@Sun.COM break;
665*11729SWang.Lin@Sun.COM }
666*11729SWang.Lin@Sun.COM
667*11729SWang.Lin@Sun.COM if (!rl) {
668*11729SWang.Lin@Sun.COM aggr_limit = arn_lookup_rate(sc, bf, tid);
669*11729SWang.Lin@Sun.COM rl = 1;
670*11729SWang.Lin@Sun.COM }
671*11729SWang.Lin@Sun.COM
672*11729SWang.Lin@Sun.COM /* do not exceed aggregation limit */
673*11729SWang.Lin@Sun.COM al_delta = ATH_AGGR_DELIM_SZ + bf->bf_frmlen;
674*11729SWang.Lin@Sun.COM
675*11729SWang.Lin@Sun.COM if (nframes &&
676*11729SWang.Lin@Sun.COM (aggr_limit < (al + bpad + al_delta + prev_al))) {
677*11729SWang.Lin@Sun.COM status = ATH_AGGR_LIMITED;
678*11729SWang.Lin@Sun.COM break;
679*11729SWang.Lin@Sun.COM }
680*11729SWang.Lin@Sun.COM
681*11729SWang.Lin@Sun.COM /* do not exceed subframe limit */
682*11729SWang.Lin@Sun.COM if (nframes >= min((int)h_baw, ATH_AMPDU_SUBFRAME_DEFAULT)) {
683*11729SWang.Lin@Sun.COM status = ATH_AGGR_LIMITED;
684*11729SWang.Lin@Sun.COM break;
685*11729SWang.Lin@Sun.COM }
686*11729SWang.Lin@Sun.COM nframes++;
687*11729SWang.Lin@Sun.COM
688*11729SWang.Lin@Sun.COM /* add padding for previous frame to aggregation length */
689*11729SWang.Lin@Sun.COM al += bpad + al_delta;
690*11729SWang.Lin@Sun.COM
691*11729SWang.Lin@Sun.COM /*
692*11729SWang.Lin@Sun.COM * Get the delimiters needed to meet the MPDU
693*11729SWang.Lin@Sun.COM * density for this node.
694*11729SWang.Lin@Sun.COM */
695*11729SWang.Lin@Sun.COM ndelim =
696*11729SWang.Lin@Sun.COM arn_compute_num_delims(sc, tid, bf_first, bf->bf_frmlen);
697*11729SWang.Lin@Sun.COM bpad = PADBYTES(al_delta) + (ndelim << 2);
698*11729SWang.Lin@Sun.COM
699*11729SWang.Lin@Sun.COM bf->bf_next = NULL;
700*11729SWang.Lin@Sun.COM bf->bf_desc->ds_link = 0;
701*11729SWang.Lin@Sun.COM
702*11729SWang.Lin@Sun.COM /* link buffers of this frame to the aggregate */
703*11729SWang.Lin@Sun.COM arn_tx_addto_baw(sc, tid, bf);
704*11729SWang.Lin@Sun.COM ath9k_hw_set11n_aggr_middle(sc->sc_ah, bf->bf_desc, ndelim);
705*11729SWang.Lin@Sun.COM list_remove(&tid->buf_q, bf);
706*11729SWang.Lin@Sun.COM list_insert_tail(bf_q, bf);
707*11729SWang.Lin@Sun.COM if (bf_prev) {
708*11729SWang.Lin@Sun.COM bf_prev->bf_next = bf;
709*11729SWang.Lin@Sun.COM bf_prev->bf_desc->ds_link = bf->bf_daddr;
710*11729SWang.Lin@Sun.COM }
711*11729SWang.Lin@Sun.COM bf_prev = bf;
712*11729SWang.Lin@Sun.COM } while (!list_empty(&tid->buf_q));
713*11729SWang.Lin@Sun.COM
714*11729SWang.Lin@Sun.COM bf_first->bf_al = al;
715*11729SWang.Lin@Sun.COM bf_first->bf_nframes = nframes;
716*11729SWang.Lin@Sun.COM
717*11729SWang.Lin@Sun.COM return (status);
718*11729SWang.Lin@Sun.COM #undef PADBYTES
719*11729SWang.Lin@Sun.COM }
720*11729SWang.Lin@Sun.COM
721*11729SWang.Lin@Sun.COM static void
arn_tx_sched_aggr(struct arn_softc * sc,struct ath_txq * txq,struct ath_atx_tid * tid)722*11729SWang.Lin@Sun.COM arn_tx_sched_aggr(struct arn_softc *sc, struct ath_txq *txq,
723*11729SWang.Lin@Sun.COM struct ath_atx_tid *tid)
724*11729SWang.Lin@Sun.COM {
725*11729SWang.Lin@Sun.COM struct ath_buf *bf;
726*11729SWang.Lin@Sun.COM enum ATH_AGGR_STATUS status;
727*11729SWang.Lin@Sun.COM list_t bf_q;
728*11729SWang.Lin@Sun.COM
729*11729SWang.Lin@Sun.COM do {
730*11729SWang.Lin@Sun.COM if (list_empty(&tid->buf_q))
731*11729SWang.Lin@Sun.COM return;
732*11729SWang.Lin@Sun.COM
733*11729SWang.Lin@Sun.COM /* INIT_LIST_HEAD */
734*11729SWang.Lin@Sun.COM list_create(&bf_q, sizeof (struct ath_buf),
735*11729SWang.Lin@Sun.COM offsetof(struct ath_buf, bf_node));
736*11729SWang.Lin@Sun.COM
737*11729SWang.Lin@Sun.COM status = arn_tx_form_aggr(sc, tid, &bf_q);
738*11729SWang.Lin@Sun.COM
739*11729SWang.Lin@Sun.COM /*
740*11729SWang.Lin@Sun.COM * no frames picked up to be aggregated;
741*11729SWang.Lin@Sun.COM * block-ack window is not open.
742*11729SWang.Lin@Sun.COM */
743*11729SWang.Lin@Sun.COM if (list_empty(&bf_q))
744*11729SWang.Lin@Sun.COM break;
745*11729SWang.Lin@Sun.COM
746*11729SWang.Lin@Sun.COM bf = list_head(&bf_q);
747*11729SWang.Lin@Sun.COM bf->bf_lastbf = list_object(&bf_q, bf->bf_node.list_prev);
748*11729SWang.Lin@Sun.COM
749*11729SWang.Lin@Sun.COM /* if only one frame, send as non-aggregate */
750*11729SWang.Lin@Sun.COM if (bf->bf_nframes == 1) {
751*11729SWang.Lin@Sun.COM bf->bf_state.bf_type &= ~BUF_AGGR;
752*11729SWang.Lin@Sun.COM ath9k_hw_clr11n_aggr(sc->sc_ah, bf->bf_desc);
753*11729SWang.Lin@Sun.COM ath_buf_set_rate(sc, bf);
754*11729SWang.Lin@Sun.COM arn_tx_txqaddbuf(sc, txq, &bf_q);
755*11729SWang.Lin@Sun.COM continue;
756*11729SWang.Lin@Sun.COM }
757*11729SWang.Lin@Sun.COM
758*11729SWang.Lin@Sun.COM /* setup first desc of aggregate */
759*11729SWang.Lin@Sun.COM bf->bf_state.bf_type |= BUF_AGGR;
760*11729SWang.Lin@Sun.COM ath_buf_set_rate(sc, bf);
761*11729SWang.Lin@Sun.COM ath9k_hw_set11n_aggr_first(sc->sc_ah, bf->bf_desc, bf->bf_al);
762*11729SWang.Lin@Sun.COM
763*11729SWang.Lin@Sun.COM /* anchor last desc of aggregate */
764*11729SWang.Lin@Sun.COM ath9k_hw_set11n_aggr_last(sc->sc_ah, bf->bf_lastbf->bf_desc);
765*11729SWang.Lin@Sun.COM
766*11729SWang.Lin@Sun.COM txq->axq_aggr_depth++;
767*11729SWang.Lin@Sun.COM arn_tx_txqaddbuf(sc, txq, &bf_q);
768*11729SWang.Lin@Sun.COM
769*11729SWang.Lin@Sun.COM } while (txq->axq_depth < ATH_AGGR_MIN_QDEPTH &&
770*11729SWang.Lin@Sun.COM status != ATH_AGGR_BAW_CLOSED);
771*11729SWang.Lin@Sun.COM }
772*11729SWang.Lin@Sun.COM
773*11729SWang.Lin@Sun.COM int
arn_tx_aggr_start(struct arn_softc * sc,struct ieee80211_node * in,uint16_t tid,uint16_t * ssn)774*11729SWang.Lin@Sun.COM arn_tx_aggr_start(struct arn_softc *sc, struct ieee80211_node *in,
775*11729SWang.Lin@Sun.COM uint16_t tid, uint16_t *ssn)
776*11729SWang.Lin@Sun.COM {
777*11729SWang.Lin@Sun.COM struct ath_atx_tid *txtid;
778*11729SWang.Lin@Sun.COM struct ath_node *an;
779*11729SWang.Lin@Sun.COM
780*11729SWang.Lin@Sun.COM an = ATH_NODE(in);
781*11729SWang.Lin@Sun.COM
782*11729SWang.Lin@Sun.COM if (sc->sc_flags & SC_OP_TXAGGR) {
783*11729SWang.Lin@Sun.COM txtid = ATH_AN_2_TID(an, tid);
784*11729SWang.Lin@Sun.COM txtid->state |= AGGR_ADDBA_PROGRESS;
785*11729SWang.Lin@Sun.COM arn_tx_pause_tid(sc, txtid);
786*11729SWang.Lin@Sun.COM *ssn = txtid->seq_start;
787*11729SWang.Lin@Sun.COM }
788*11729SWang.Lin@Sun.COM
789*11729SWang.Lin@Sun.COM return (0);
790*11729SWang.Lin@Sun.COM }
791*11729SWang.Lin@Sun.COM
792*11729SWang.Lin@Sun.COM int
arn_tx_aggr_stop(struct arn_softc * sc,struct ieee80211_node * in,uint16_t tid)793*11729SWang.Lin@Sun.COM arn_tx_aggr_stop(struct arn_softc *sc, struct ieee80211_node *in, uint16_t tid)
794*11729SWang.Lin@Sun.COM {
795*11729SWang.Lin@Sun.COM struct ath_node *an = ATH_NODE(in);
796*11729SWang.Lin@Sun.COM struct ath_atx_tid *txtid = ATH_AN_2_TID(an, tid);
797*11729SWang.Lin@Sun.COM struct ath_txq *txq = &sc->sc_txq[txtid->ac->qnum];
798*11729SWang.Lin@Sun.COM struct ath_buf *bf;
799*11729SWang.Lin@Sun.COM
800*11729SWang.Lin@Sun.COM list_t list;
801*11729SWang.Lin@Sun.COM list_create(&list, sizeof (struct ath_buf),
802*11729SWang.Lin@Sun.COM offsetof(struct ath_buf, bf_node));
803*11729SWang.Lin@Sun.COM
804*11729SWang.Lin@Sun.COM if (txtid->state & AGGR_CLEANUP)
805*11729SWang.Lin@Sun.COM return (0);
806*11729SWang.Lin@Sun.COM
807*11729SWang.Lin@Sun.COM if (!(txtid->state & AGGR_ADDBA_COMPLETE)) {
808*11729SWang.Lin@Sun.COM txtid->addba_exchangeattempts = 0;
809*11729SWang.Lin@Sun.COM return (0);
810*11729SWang.Lin@Sun.COM }
811*11729SWang.Lin@Sun.COM
812*11729SWang.Lin@Sun.COM arn_tx_pause_tid(sc, txtid);
813*11729SWang.Lin@Sun.COM
814*11729SWang.Lin@Sun.COM /* drop all software retried frames and mark this TID */
815*11729SWang.Lin@Sun.COM mutex_enter(&txq->axq_lock);
816*11729SWang.Lin@Sun.COM while (!list_empty(&txtid->buf_q)) {
817*11729SWang.Lin@Sun.COM /* list_first_entry */
818*11729SWang.Lin@Sun.COM bf = list_head(&txtid->buf_q);
819*11729SWang.Lin@Sun.COM if (!bf_isretried(bf)) {
820*11729SWang.Lin@Sun.COM /*
821*11729SWang.Lin@Sun.COM * NB: it's based on the assumption that
822*11729SWang.Lin@Sun.COM * software retried frame will always stay
823*11729SWang.Lin@Sun.COM * at the head of software queue.
824*11729SWang.Lin@Sun.COM */
825*11729SWang.Lin@Sun.COM break;
826*11729SWang.Lin@Sun.COM }
827*11729SWang.Lin@Sun.COM list_remove(&txtid->buf_q, bf);
828*11729SWang.Lin@Sun.COM list_insert_tail(&list, bf);
829*11729SWang.Lin@Sun.COM arn_tx_update_baw(sc, txtid, bf->bf_seqno);
830*11729SWang.Lin@Sun.COM // ath_tx_complete_buf(sc, bf, &list, 0, 0); /* to do */
831*11729SWang.Lin@Sun.COM }
832*11729SWang.Lin@Sun.COM mutex_exit(&txq->axq_lock);
833*11729SWang.Lin@Sun.COM
834*11729SWang.Lin@Sun.COM if (txtid->baw_head != txtid->baw_tail) {
835*11729SWang.Lin@Sun.COM txtid->state |= AGGR_CLEANUP;
836*11729SWang.Lin@Sun.COM } else {
837*11729SWang.Lin@Sun.COM txtid->state &= ~AGGR_ADDBA_COMPLETE;
838*11729SWang.Lin@Sun.COM txtid->addba_exchangeattempts = 0;
839*11729SWang.Lin@Sun.COM arn_tx_flush_tid(sc, txtid);
840*11729SWang.Lin@Sun.COM }
841*11729SWang.Lin@Sun.COM
842*11729SWang.Lin@Sun.COM return (0);
843*11729SWang.Lin@Sun.COM }
844*11729SWang.Lin@Sun.COM
845*11729SWang.Lin@Sun.COM void
arn_tx_aggr_resume(struct arn_softc * sc,struct ieee80211_node * in,uint16_t tid)846*11729SWang.Lin@Sun.COM arn_tx_aggr_resume(struct arn_softc *sc,
847*11729SWang.Lin@Sun.COM struct ieee80211_node *in,
848*11729SWang.Lin@Sun.COM uint16_t tid)
849*11729SWang.Lin@Sun.COM {
850*11729SWang.Lin@Sun.COM struct ath_atx_tid *txtid;
851*11729SWang.Lin@Sun.COM struct ath_node *an;
852*11729SWang.Lin@Sun.COM
853*11729SWang.Lin@Sun.COM an = ATH_NODE(in);
854*11729SWang.Lin@Sun.COM
855*11729SWang.Lin@Sun.COM if (sc->sc_flags & SC_OP_TXAGGR) {
856*11729SWang.Lin@Sun.COM txtid = ATH_AN_2_TID(an, tid);
857*11729SWang.Lin@Sun.COM txtid->baw_size = (0x8) << sc->sc_ht_conf.ampdu_factor;
858*11729SWang.Lin@Sun.COM txtid->state |= AGGR_ADDBA_COMPLETE;
859*11729SWang.Lin@Sun.COM txtid->state &= ~AGGR_ADDBA_PROGRESS;
860*11729SWang.Lin@Sun.COM arn_tx_resume_tid(sc, txtid);
861*11729SWang.Lin@Sun.COM }
862*11729SWang.Lin@Sun.COM }
863*11729SWang.Lin@Sun.COM
864*11729SWang.Lin@Sun.COM boolean_t
arn_tx_aggr_check(struct arn_softc * sc,struct ath_node * an,uint8_t tidno)865*11729SWang.Lin@Sun.COM arn_tx_aggr_check(struct arn_softc *sc, struct ath_node *an, uint8_t tidno)
866*11729SWang.Lin@Sun.COM {
867*11729SWang.Lin@Sun.COM struct ath_atx_tid *txtid;
868*11729SWang.Lin@Sun.COM
869*11729SWang.Lin@Sun.COM if (!(sc->sc_flags & SC_OP_TXAGGR))
870*11729SWang.Lin@Sun.COM return (B_FALSE);
871*11729SWang.Lin@Sun.COM
872*11729SWang.Lin@Sun.COM txtid = ATH_AN_2_TID(an, tidno);
873*11729SWang.Lin@Sun.COM
874*11729SWang.Lin@Sun.COM if (!(txtid->state & AGGR_ADDBA_COMPLETE)) {
875*11729SWang.Lin@Sun.COM if (!(txtid->state & AGGR_ADDBA_PROGRESS) &&
876*11729SWang.Lin@Sun.COM (txtid->addba_exchangeattempts < ADDBA_EXCHANGE_ATTEMPTS)) {
877*11729SWang.Lin@Sun.COM txtid->addba_exchangeattempts++;
878*11729SWang.Lin@Sun.COM return (B_TRUE);
879*11729SWang.Lin@Sun.COM }
880*11729SWang.Lin@Sun.COM }
881*11729SWang.Lin@Sun.COM
882*11729SWang.Lin@Sun.COM return (B_FALSE);
883*11729SWang.Lin@Sun.COM }
884*11729SWang.Lin@Sun.COM
885*11729SWang.Lin@Sun.COM /* Queue Management */
886*11729SWang.Lin@Sun.COM
887*11729SWang.Lin@Sun.COM static void
arn_txq_drain_pending_buffers(struct arn_softc * sc,struct ath_txq * txq)888*11729SWang.Lin@Sun.COM arn_txq_drain_pending_buffers(struct arn_softc *sc, struct ath_txq *txq)
889*11729SWang.Lin@Sun.COM {
890*11729SWang.Lin@Sun.COM struct ath_atx_ac *ac, *ac_tmp;
891*11729SWang.Lin@Sun.COM struct ath_atx_tid *tid, *tid_tmp;
892*11729SWang.Lin@Sun.COM
893*11729SWang.Lin@Sun.COM list_for_each_entry_safe(ac, ac_tmp, &txq->axq_acq) {
894*11729SWang.Lin@Sun.COM list_remove(&txq->axq_acq, ac);
895*11729SWang.Lin@Sun.COM ac->sched = B_FALSE;
896*11729SWang.Lin@Sun.COM list_for_each_entry_safe(tid, tid_tmp, &ac->tid_q) {
897*11729SWang.Lin@Sun.COM list_remove(&ac->tid_q, tid);
898*11729SWang.Lin@Sun.COM tid->sched = B_FALSE;
899*11729SWang.Lin@Sun.COM arn_tid_drain(sc, txq, tid);
900*11729SWang.Lin@Sun.COM }
901*11729SWang.Lin@Sun.COM }
902*11729SWang.Lin@Sun.COM }
903*11729SWang.Lin@Sun.COM
904*11729SWang.Lin@Sun.COM int
arn_tx_get_qnum(struct arn_softc * sc,int qtype,int haltype)905*11729SWang.Lin@Sun.COM arn_tx_get_qnum(struct arn_softc *sc, int qtype, int haltype)
906*11729SWang.Lin@Sun.COM {
907*11729SWang.Lin@Sun.COM int qnum;
908*11729SWang.Lin@Sun.COM
909*11729SWang.Lin@Sun.COM switch (qtype) {
910*11729SWang.Lin@Sun.COM case ATH9K_TX_QUEUE_DATA:
911*11729SWang.Lin@Sun.COM if (haltype >= ARRAY_SIZE(sc->sc_haltype2q)) {
912*11729SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_FATAL, "arn: arn_tx_get_qnum(): "
913*11729SWang.Lin@Sun.COM "HAL AC %u out of range, max %zu!\n",
914*11729SWang.Lin@Sun.COM haltype, ARRAY_SIZE(sc->sc_haltype2q)));
915*11729SWang.Lin@Sun.COM return (-1);
916*11729SWang.Lin@Sun.COM }
917*11729SWang.Lin@Sun.COM qnum = sc->sc_haltype2q[haltype];
918*11729SWang.Lin@Sun.COM break;
919*11729SWang.Lin@Sun.COM case ATH9K_TX_QUEUE_BEACON:
920*11729SWang.Lin@Sun.COM qnum = sc->sc_beaconq;
921*11729SWang.Lin@Sun.COM break;
922*11729SWang.Lin@Sun.COM case ATH9K_TX_QUEUE_CAB:
923*11729SWang.Lin@Sun.COM qnum = sc->sc_cabq->axq_qnum;
924*11729SWang.Lin@Sun.COM break;
925*11729SWang.Lin@Sun.COM default:
926*11729SWang.Lin@Sun.COM qnum = -1;
927*11729SWang.Lin@Sun.COM }
928*11729SWang.Lin@Sun.COM return (qnum);
929*11729SWang.Lin@Sun.COM }
930*11729SWang.Lin@Sun.COM
931*11729SWang.Lin@Sun.COM struct ath_txq *
arn_test_get_txq(struct arn_softc * sc,struct ieee80211_node * in,struct ieee80211_frame * wh,uint8_t type)932*11729SWang.Lin@Sun.COM arn_test_get_txq(struct arn_softc *sc, struct ieee80211_node *in,
933*11729SWang.Lin@Sun.COM struct ieee80211_frame *wh, uint8_t type)
934*11729SWang.Lin@Sun.COM {
935*11729SWang.Lin@Sun.COM struct ieee80211_qosframe *qwh = NULL;
936*11729SWang.Lin@Sun.COM struct ath_txq *txq = NULL;
937*11729SWang.Lin@Sun.COM int tid = -1;
938*11729SWang.Lin@Sun.COM int qos_ac;
939*11729SWang.Lin@Sun.COM int qnum;
940*11729SWang.Lin@Sun.COM
941*11729SWang.Lin@Sun.COM if (in->in_flags & IEEE80211_NODE_QOS) {
942*11729SWang.Lin@Sun.COM
943*11729SWang.Lin@Sun.COM if ((type & IEEE80211_FC0_TYPE_MASK) ==
944*11729SWang.Lin@Sun.COM IEEE80211_FC0_TYPE_DATA) {
945*11729SWang.Lin@Sun.COM
946*11729SWang.Lin@Sun.COM if (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_QOS) {
947*11729SWang.Lin@Sun.COM qwh = (struct ieee80211_qosframe *)wh;
948*11729SWang.Lin@Sun.COM
949*11729SWang.Lin@Sun.COM tid = qwh->i_qos[0] & IEEE80211_QOS_TID;
950*11729SWang.Lin@Sun.COM switch (tid) {
951*11729SWang.Lin@Sun.COM case 1:
952*11729SWang.Lin@Sun.COM case 2:
953*11729SWang.Lin@Sun.COM qos_ac = WME_AC_BK;
954*11729SWang.Lin@Sun.COM case 0:
955*11729SWang.Lin@Sun.COM case 3:
956*11729SWang.Lin@Sun.COM qos_ac = WME_AC_BE;
957*11729SWang.Lin@Sun.COM case 4:
958*11729SWang.Lin@Sun.COM case 5:
959*11729SWang.Lin@Sun.COM qos_ac = WME_AC_VI;
960*11729SWang.Lin@Sun.COM case 6:
961*11729SWang.Lin@Sun.COM case 7:
962*11729SWang.Lin@Sun.COM qos_ac = WME_AC_VO;
963*11729SWang.Lin@Sun.COM }
964*11729SWang.Lin@Sun.COM }
965*11729SWang.Lin@Sun.COM } else {
966*11729SWang.Lin@Sun.COM qos_ac = WME_AC_VO;
967*11729SWang.Lin@Sun.COM }
968*11729SWang.Lin@Sun.COM } else if ((type & IEEE80211_FC0_TYPE_MASK) ==
969*11729SWang.Lin@Sun.COM IEEE80211_FC0_TYPE_MGT) {
970*11729SWang.Lin@Sun.COM qos_ac = WME_AC_VO;
971*11729SWang.Lin@Sun.COM } else if ((type & IEEE80211_FC0_TYPE_MASK) ==
972*11729SWang.Lin@Sun.COM IEEE80211_FC0_TYPE_CTL) {
973*11729SWang.Lin@Sun.COM qos_ac = WME_AC_VO;
974*11729SWang.Lin@Sun.COM } else {
975*11729SWang.Lin@Sun.COM qos_ac = WME_AC_BK;
976*11729SWang.Lin@Sun.COM }
977*11729SWang.Lin@Sun.COM qnum = arn_get_hal_qnum(qos_ac, sc);
978*11729SWang.Lin@Sun.COM txq = &sc->sc_txq[qnum];
979*11729SWang.Lin@Sun.COM
980*11729SWang.Lin@Sun.COM mutex_enter(&txq->axq_lock);
981*11729SWang.Lin@Sun.COM
982*11729SWang.Lin@Sun.COM if (txq->axq_depth >= (ATH_TXBUF - 20)) {
983*11729SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_XMIT,
984*11729SWang.Lin@Sun.COM "TX queue: %d is full, depth: %d\n",
985*11729SWang.Lin@Sun.COM qnum, txq->axq_depth));
986*11729SWang.Lin@Sun.COM /* stop th queue */
987*11729SWang.Lin@Sun.COM sc->sc_resched_needed = B_TRUE;
988*11729SWang.Lin@Sun.COM txq->stopped = 1;
989*11729SWang.Lin@Sun.COM mutex_exit(&txq->axq_lock);
990*11729SWang.Lin@Sun.COM return (NULL);
991*11729SWang.Lin@Sun.COM }
992*11729SWang.Lin@Sun.COM
993*11729SWang.Lin@Sun.COM mutex_exit(&txq->axq_lock);
994*11729SWang.Lin@Sun.COM
995*11729SWang.Lin@Sun.COM return (txq);
996*11729SWang.Lin@Sun.COM }
997*11729SWang.Lin@Sun.COM
998*11729SWang.Lin@Sun.COM /* Called only when tx aggregation is enabled and HT is supported */
999*11729SWang.Lin@Sun.COM static void
assign_aggr_tid_seqno(struct arn_softc * sc,struct ath_buf * bf,struct ieee80211_frame * wh)1000*11729SWang.Lin@Sun.COM assign_aggr_tid_seqno(struct arn_softc *sc,
1001*11729SWang.Lin@Sun.COM struct ath_buf *bf,
1002*11729SWang.Lin@Sun.COM struct ieee80211_frame *wh)
1003*11729SWang.Lin@Sun.COM {
1004*11729SWang.Lin@Sun.COM struct ath_node *an;
1005*11729SWang.Lin@Sun.COM struct ath_atx_tid *tid;
1006*11729SWang.Lin@Sun.COM struct ieee80211_node *in;
1007*11729SWang.Lin@Sun.COM struct ieee80211_qosframe *qwh = NULL;
1008*11729SWang.Lin@Sun.COM ieee80211com_t *ic = (ieee80211com_t *)sc;
1009*11729SWang.Lin@Sun.COM
1010*11729SWang.Lin@Sun.COM in = ieee80211_find_txnode(ic, wh->i_addr1);
1011*11729SWang.Lin@Sun.COM if (in == NULL) {
1012*11729SWang.Lin@Sun.COM arn_problem("assign_aggr_tid_seqno():"
1013*11729SWang.Lin@Sun.COM "failed to find tx node\n");
1014*11729SWang.Lin@Sun.COM return;
1015*11729SWang.Lin@Sun.COM }
1016*11729SWang.Lin@Sun.COM an = ATH_NODE(in);
1017*11729SWang.Lin@Sun.COM
1018*11729SWang.Lin@Sun.COM /* Get tidno */
1019*11729SWang.Lin@Sun.COM if (in->in_flags & IEEE80211_NODE_QOS) {
1020*11729SWang.Lin@Sun.COM if (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_QOS) {
1021*11729SWang.Lin@Sun.COM qwh = (struct ieee80211_qosframe *)wh;
1022*11729SWang.Lin@Sun.COM bf->bf_tidno = qwh->i_qos[0] & IEEE80211_QOS_TID;
1023*11729SWang.Lin@Sun.COM }
1024*11729SWang.Lin@Sun.COM }
1025*11729SWang.Lin@Sun.COM
1026*11729SWang.Lin@Sun.COM /* Get seqno */
1027*11729SWang.Lin@Sun.COM /*
1028*11729SWang.Lin@Sun.COM * For HT capable stations, we save tidno for later use.
1029*11729SWang.Lin@Sun.COM * We also override seqno set by upper layer with the one
1030*11729SWang.Lin@Sun.COM * in tx aggregation state.
1031*11729SWang.Lin@Sun.COM *
1032*11729SWang.Lin@Sun.COM * If fragmentation is on, the sequence number is
1033*11729SWang.Lin@Sun.COM * not overridden, since it has been
1034*11729SWang.Lin@Sun.COM * incremented by the fragmentation routine.
1035*11729SWang.Lin@Sun.COM *
1036*11729SWang.Lin@Sun.COM * FIXME: check if the fragmentation threshold exceeds
1037*11729SWang.Lin@Sun.COM * IEEE80211 max.
1038*11729SWang.Lin@Sun.COM */
1039*11729SWang.Lin@Sun.COM tid = ATH_AN_2_TID(an, bf->bf_tidno);
1040*11729SWang.Lin@Sun.COM
1041*11729SWang.Lin@Sun.COM *(uint16_t *)&wh->i_seq[0] =
1042*11729SWang.Lin@Sun.COM LE_16(tid->seq_next << IEEE80211_SEQ_SEQ_SHIFT);
1043*11729SWang.Lin@Sun.COM bf->bf_seqno = tid->seq_next;
1044*11729SWang.Lin@Sun.COM /* LINTED E_CONSTANT_CONDITION */
1045*11729SWang.Lin@Sun.COM INCR(tid->seq_next, IEEE80211_SEQ_MAX);
1046*11729SWang.Lin@Sun.COM
1047*11729SWang.Lin@Sun.COM /* release node */
1048*11729SWang.Lin@Sun.COM ieee80211_free_node(in);
1049*11729SWang.Lin@Sun.COM }
1050*11729SWang.Lin@Sun.COM
1051*11729SWang.Lin@Sun.COM /* Compute the number of bad frames */
1052*11729SWang.Lin@Sun.COM /* ARGSUSED */
1053*11729SWang.Lin@Sun.COM static int
arn_tx_num_badfrms(struct arn_softc * sc,struct ath_buf * bf,int txok)1054*11729SWang.Lin@Sun.COM arn_tx_num_badfrms(struct arn_softc *sc, struct ath_buf *bf, int txok)
1055*11729SWang.Lin@Sun.COM {
1056*11729SWang.Lin@Sun.COM struct ath_buf *bf_last = bf->bf_lastbf;
1057*11729SWang.Lin@Sun.COM struct ath_desc *ds = bf_last->bf_desc;
1058*11729SWang.Lin@Sun.COM uint16_t seq_st = 0;
1059*11729SWang.Lin@Sun.COM uint32_t ba[WME_BA_BMP_SIZE >> 5];
1060*11729SWang.Lin@Sun.COM int ba_index;
1061*11729SWang.Lin@Sun.COM int nbad = 0;
1062*11729SWang.Lin@Sun.COM int isaggr = 0;
1063*11729SWang.Lin@Sun.COM
1064*11729SWang.Lin@Sun.COM if (ds->ds_txstat.ts_flags == ATH9K_TX_SW_ABORTED)
1065*11729SWang.Lin@Sun.COM return (0);
1066*11729SWang.Lin@Sun.COM
1067*11729SWang.Lin@Sun.COM isaggr = bf_isaggr(bf);
1068*11729SWang.Lin@Sun.COM if (isaggr) {
1069*11729SWang.Lin@Sun.COM seq_st = ATH_DS_BA_SEQ(ds);
1070*11729SWang.Lin@Sun.COM memcpy(ba, ATH_DS_BA_BITMAP(ds), WME_BA_BMP_SIZE >> 3);
1071*11729SWang.Lin@Sun.COM }
1072*11729SWang.Lin@Sun.COM
1073*11729SWang.Lin@Sun.COM while (bf) {
1074*11729SWang.Lin@Sun.COM ba_index = ATH_BA_INDEX(seq_st, bf->bf_seqno);
1075*11729SWang.Lin@Sun.COM if (!txok || (isaggr && !ATH_BA_ISSET(ba, ba_index)))
1076*11729SWang.Lin@Sun.COM nbad++;
1077*11729SWang.Lin@Sun.COM
1078*11729SWang.Lin@Sun.COM bf = bf->bf_next;
1079*11729SWang.Lin@Sun.COM }
1080*11729SWang.Lin@Sun.COM
1081*11729SWang.Lin@Sun.COM return (nbad);
1082*11729SWang.Lin@Sun.COM }
1083*11729SWang.Lin@Sun.COM
1084*11729SWang.Lin@Sun.COM static void
arn_tx_send_ht_normal(struct arn_softc * sc,struct ath_txq * txq,struct ath_atx_tid * tid,list_t * list)1085*11729SWang.Lin@Sun.COM arn_tx_send_ht_normal(struct arn_softc *sc,
1086*11729SWang.Lin@Sun.COM struct ath_txq *txq,
1087*11729SWang.Lin@Sun.COM struct ath_atx_tid *tid,
1088*11729SWang.Lin@Sun.COM list_t *list)
1089*11729SWang.Lin@Sun.COM {
1090*11729SWang.Lin@Sun.COM struct ath_buf *bf;
1091*11729SWang.Lin@Sun.COM
1092*11729SWang.Lin@Sun.COM bf = list_head(list);
1093*11729SWang.Lin@Sun.COM bf->bf_state.bf_type &= ~BUF_AMPDU;
1094*11729SWang.Lin@Sun.COM
1095*11729SWang.Lin@Sun.COM /* update starting sequence number for subsequent ADDBA request */
1096*11729SWang.Lin@Sun.COM INCR(tid->seq_start, IEEE80211_SEQ_MAX);
1097*11729SWang.Lin@Sun.COM
1098*11729SWang.Lin@Sun.COM bf->bf_nframes = 1;
1099*11729SWang.Lin@Sun.COM bf->bf_lastbf = bf;
1100*11729SWang.Lin@Sun.COM ath_buf_set_rate(sc, bf);
1101*11729SWang.Lin@Sun.COM arn_tx_txqaddbuf(sc, txq, list);
1102*11729SWang.Lin@Sun.COM }
1103*11729SWang.Lin@Sun.COM
1104*11729SWang.Lin@Sun.COM /*
1105*11729SWang.Lin@Sun.COM * Insert a chain of ath_buf (descriptors) on a txq and
1106*11729SWang.Lin@Sun.COM * assume the descriptors are already chained together by caller.
1107*11729SWang.Lin@Sun.COM */
1108*11729SWang.Lin@Sun.COM static void
arn_tx_txqaddbuf(struct arn_softc * sc,struct ath_txq * txq,list_t * list)1109*11729SWang.Lin@Sun.COM arn_tx_txqaddbuf(struct arn_softc *sc,
1110*11729SWang.Lin@Sun.COM struct ath_txq *txq,
1111*11729SWang.Lin@Sun.COM list_t *list)
1112*11729SWang.Lin@Sun.COM {
1113*11729SWang.Lin@Sun.COM struct ath_buf *bf;
1114*11729SWang.Lin@Sun.COM
1115*11729SWang.Lin@Sun.COM /*
1116*11729SWang.Lin@Sun.COM * Insert the frame on the outbound list and
1117*11729SWang.Lin@Sun.COM * pass it on to the hardware.
1118*11729SWang.Lin@Sun.COM */
1119*11729SWang.Lin@Sun.COM
1120*11729SWang.Lin@Sun.COM if (list_empty(list))
1121*11729SWang.Lin@Sun.COM return;
1122*11729SWang.Lin@Sun.COM
1123*11729SWang.Lin@Sun.COM bf = list_head(list);
1124*11729SWang.Lin@Sun.COM
1125*11729SWang.Lin@Sun.COM list_splice_tail_init(list, &txq->axq_q);
1126*11729SWang.Lin@Sun.COM
1127*11729SWang.Lin@Sun.COM txq->axq_depth++;
1128*11729SWang.Lin@Sun.COM txq->axq_totalqueued++;
1129*11729SWang.Lin@Sun.COM txq->axq_linkbuf = list_object(list, txq->axq_q.prev);
1130*11729SWang.Lin@Sun.COM
1131*11729SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_QUEUE,
1132*11729SWang.Lin@Sun.COM "qnum: %d, txq depth: %d\n", txq->axq_qnum, txq->axq_depth));
1133*11729SWang.Lin@Sun.COM
1134*11729SWang.Lin@Sun.COM if (txq->axq_link == NULL) {
1135*11729SWang.Lin@Sun.COM ath9k_hw_puttxbuf(sc->sc_ah, txq->axq_qnum, bf->bf_daddr);
1136*11729SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_XMIT,
1137*11729SWang.Lin@Sun.COM "TXDP[%u] = %llx (%p)\n",
1138*11729SWang.Lin@Sun.COM txq->axq_qnum, ito64(bf->bf_daddr), bf->bf_desc));
1139*11729SWang.Lin@Sun.COM } else {
1140*11729SWang.Lin@Sun.COM *txq->axq_link = bf->bf_daddr;
1141*11729SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_XMIT, "link[%u] (%p)=%llx (%p)\n",
1142*11729SWang.Lin@Sun.COM txq->axq_qnum, txq->axq_link,
1143*11729SWang.Lin@Sun.COM ito64(bf->bf_daddr), bf->bf_desc));
1144*11729SWang.Lin@Sun.COM }
1145*11729SWang.Lin@Sun.COM txq->axq_link = &(bf->bf_lastbf->bf_desc->ds_link);
1146*11729SWang.Lin@Sun.COM ath9k_hw_txstart(sc->sc_ah, txq->axq_qnum);
1147*11729SWang.Lin@Sun.COM }
1148*11729SWang.Lin@Sun.COM #endif /* ARN_TX_AGGREGATION */
1149*11729SWang.Lin@Sun.COM
1150*11729SWang.Lin@Sun.COM /*
1151*11729SWang.Lin@Sun.COM * ath_pkt_dur - compute packet duration (NB: not NAV)
1152*11729SWang.Lin@Sun.COM * rix - rate index
1153*11729SWang.Lin@Sun.COM * pktlen - total bytes (delims + data + fcs + pads + pad delims)
1154*11729SWang.Lin@Sun.COM * width - 0 for 20 MHz, 1 for 40 MHz
1155*11729SWang.Lin@Sun.COM * half_gi - to use 4us v/s 3.6 us for symbol time
1156*11729SWang.Lin@Sun.COM */
1157*11729SWang.Lin@Sun.COM
1158*11729SWang.Lin@Sun.COM static uint32_t
1159*11729SWang.Lin@Sun.COM /* LINTED E_STATIC_UNUSED */
arn_pkt_duration(struct arn_softc * sc,uint8_t rix,struct ath_buf * bf,int width,int half_gi,boolean_t shortPreamble)1160*11729SWang.Lin@Sun.COM arn_pkt_duration(struct arn_softc *sc, uint8_t rix, struct ath_buf *bf,
1161*11729SWang.Lin@Sun.COM int width, int half_gi, boolean_t shortPreamble)
1162*11729SWang.Lin@Sun.COM {
1163*11729SWang.Lin@Sun.COM struct ath_rate_table *rate_table = sc->sc_currates;
1164*11729SWang.Lin@Sun.COM uint32_t nbits, nsymbits, duration, nsymbols;
1165*11729SWang.Lin@Sun.COM uint8_t rc;
1166*11729SWang.Lin@Sun.COM int streams, pktlen;
1167*11729SWang.Lin@Sun.COM
1168*11729SWang.Lin@Sun.COM pktlen = bf_isaggr(bf) ? bf->bf_al : bf->bf_frmlen;
1169*11729SWang.Lin@Sun.COM rc = rate_table->info[rix].ratecode;
1170*11729SWang.Lin@Sun.COM
1171*11729SWang.Lin@Sun.COM /* for legacy rates, use old function to compute packet duration */
1172*11729SWang.Lin@Sun.COM if (!IS_HT_RATE(rc))
1173*11729SWang.Lin@Sun.COM return (ath9k_hw_computetxtime(sc->sc_ah, rate_table, pktlen,
1174*11729SWang.Lin@Sun.COM rix, shortPreamble));
1175*11729SWang.Lin@Sun.COM
1176*11729SWang.Lin@Sun.COM /* find number of symbols: PLCP + data */
1177*11729SWang.Lin@Sun.COM nbits = (pktlen << 3) + OFDM_PLCP_BITS;
1178*11729SWang.Lin@Sun.COM nsymbits = bits_per_symbol[HT_RC_2_MCS(rc)][width];
1179*11729SWang.Lin@Sun.COM nsymbols = (nbits + nsymbits - 1) / nsymbits;
1180*11729SWang.Lin@Sun.COM
1181*11729SWang.Lin@Sun.COM if (!half_gi)
1182*11729SWang.Lin@Sun.COM duration = SYMBOL_TIME(nsymbols);
1183*11729SWang.Lin@Sun.COM else
1184*11729SWang.Lin@Sun.COM duration = SYMBOL_TIME_HALFGI(nsymbols);
1185*11729SWang.Lin@Sun.COM
1186*11729SWang.Lin@Sun.COM /* addup duration for legacy/ht training and signal fields */
1187*11729SWang.Lin@Sun.COM streams = HT_RC_2_STREAMS(rc);
1188*11729SWang.Lin@Sun.COM duration += L_STF + L_LTF + L_SIG + HT_SIG + HT_STF + HT_LTF(streams);
1189*11729SWang.Lin@Sun.COM
1190*11729SWang.Lin@Sun.COM return (duration);
1191*11729SWang.Lin@Sun.COM }
1192*11729SWang.Lin@Sun.COM
1193*11729SWang.Lin@Sun.COM static struct ath_buf *
arn_tx_get_buffer(struct arn_softc * sc)1194*11729SWang.Lin@Sun.COM arn_tx_get_buffer(struct arn_softc *sc)
1195*11729SWang.Lin@Sun.COM {
1196*11729SWang.Lin@Sun.COM struct ath_buf *bf = NULL;
1197*11729SWang.Lin@Sun.COM
1198*11729SWang.Lin@Sun.COM mutex_enter(&sc->sc_txbuflock);
1199*11729SWang.Lin@Sun.COM bf = list_head(&sc->sc_txbuf_list);
1200*11729SWang.Lin@Sun.COM /* Check if a tx buffer is available */
1201*11729SWang.Lin@Sun.COM if (bf != NULL)
1202*11729SWang.Lin@Sun.COM list_remove(&sc->sc_txbuf_list, bf);
1203*11729SWang.Lin@Sun.COM if (list_empty(&sc->sc_txbuf_list)) {
1204*11729SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_XMIT, "arn: arn_tx(): "
1205*11729SWang.Lin@Sun.COM "stop queue\n"));
1206*11729SWang.Lin@Sun.COM sc->sc_stats.ast_tx_qstop++;
1207*11729SWang.Lin@Sun.COM }
1208*11729SWang.Lin@Sun.COM mutex_exit(&sc->sc_txbuflock);
1209*11729SWang.Lin@Sun.COM
1210*11729SWang.Lin@Sun.COM return (bf);
1211*11729SWang.Lin@Sun.COM }
1212*11729SWang.Lin@Sun.COM
1213*11729SWang.Lin@Sun.COM static uint32_t
setup_tx_flags(struct arn_softc * sc,struct ieee80211_frame * wh,uint32_t pktlen)1214*11729SWang.Lin@Sun.COM setup_tx_flags(struct arn_softc *sc,
1215*11729SWang.Lin@Sun.COM struct ieee80211_frame *wh,
1216*11729SWang.Lin@Sun.COM uint32_t pktlen)
1217*11729SWang.Lin@Sun.COM {
1218*11729SWang.Lin@Sun.COM int flags = 0;
1219*11729SWang.Lin@Sun.COM ieee80211com_t *ic = (ieee80211com_t *)sc;
1220*11729SWang.Lin@Sun.COM
1221*11729SWang.Lin@Sun.COM flags |= ATH9K_TXDESC_CLRDMASK; /* needed for crypto errors */
1222*11729SWang.Lin@Sun.COM flags |= ATH9K_TXDESC_INTREQ;
1223*11729SWang.Lin@Sun.COM
1224*11729SWang.Lin@Sun.COM if (IEEE80211_IS_MULTICAST(wh->i_addr1)) {
1225*11729SWang.Lin@Sun.COM flags |= ATH9K_TXDESC_NOACK; /* no ack on broad/multicast */
1226*11729SWang.Lin@Sun.COM sc->sc_stats.ast_tx_noack++;
1227*11729SWang.Lin@Sun.COM }
1228*11729SWang.Lin@Sun.COM if (pktlen > ic->ic_rtsthreshold) {
1229*11729SWang.Lin@Sun.COM flags |= ATH9K_TXDESC_RTSENA; /* RTS based on frame length */
1230*11729SWang.Lin@Sun.COM sc->sc_stats.ast_tx_rts++;
1231*11729SWang.Lin@Sun.COM }
1232*11729SWang.Lin@Sun.COM
1233*11729SWang.Lin@Sun.COM return (flags);
1234*11729SWang.Lin@Sun.COM }
1235*11729SWang.Lin@Sun.COM
1236*11729SWang.Lin@Sun.COM static void
ath_tx_setup_buffer(struct arn_softc * sc,struct ath_buf * bf,struct ieee80211_node * in,struct ieee80211_frame * wh,uint32_t pktlen,uint32_t keytype)1237*11729SWang.Lin@Sun.COM ath_tx_setup_buffer(struct arn_softc *sc, struct ath_buf *bf,
1238*11729SWang.Lin@Sun.COM struct ieee80211_node *in, struct ieee80211_frame *wh,
1239*11729SWang.Lin@Sun.COM uint32_t pktlen, uint32_t keytype)
1240*11729SWang.Lin@Sun.COM {
1241*11729SWang.Lin@Sun.COM ieee80211com_t *ic = (ieee80211com_t *)sc;
1242*11729SWang.Lin@Sun.COM int i;
1243*11729SWang.Lin@Sun.COM
1244*11729SWang.Lin@Sun.COM /* Buf reset */
1245*11729SWang.Lin@Sun.COM ATH_TXBUF_RESET(bf);
1246*11729SWang.Lin@Sun.COM for (i = 0; i < 4; i++) {
1247*11729SWang.Lin@Sun.COM bf->rates[i].idx = -1;
1248*11729SWang.Lin@Sun.COM bf->rates[i].flags = 0;
1249*11729SWang.Lin@Sun.COM bf->rates[i].count = 1;
1250*11729SWang.Lin@Sun.COM }
1251*11729SWang.Lin@Sun.COM
1252*11729SWang.Lin@Sun.COM bf->bf_in = in;
1253*11729SWang.Lin@Sun.COM /* LINTED E_ASSIGN_NARROW_CONV */
1254*11729SWang.Lin@Sun.COM bf->bf_frmlen = pktlen;
1255*11729SWang.Lin@Sun.COM
1256*11729SWang.Lin@Sun.COM /* Frame type */
1257*11729SWang.Lin@Sun.COM IEEE80211_IS_DATA(wh) ?
1258*11729SWang.Lin@Sun.COM (bf->bf_state.bf_type |= BUF_DATA) :
1259*11729SWang.Lin@Sun.COM (bf->bf_state.bf_type &= ~BUF_DATA);
1260*11729SWang.Lin@Sun.COM IEEE80211_IS_BACK_REQ(wh) ?
1261*11729SWang.Lin@Sun.COM (bf->bf_state.bf_type |= BUF_BAR) :
1262*11729SWang.Lin@Sun.COM (bf->bf_state.bf_type &= ~BUF_BAR);
1263*11729SWang.Lin@Sun.COM IEEE80211_IS_PSPOLL(wh) ?
1264*11729SWang.Lin@Sun.COM (bf->bf_state.bf_type |= BUF_PSPOLL) :
1265*11729SWang.Lin@Sun.COM (bf->bf_state.bf_type &= ~BUF_PSPOLL);
1266*11729SWang.Lin@Sun.COM /*
1267*11729SWang.Lin@Sun.COM * The 802.11 layer marks whether or not we should
1268*11729SWang.Lin@Sun.COM * use short preamble based on the current mode and
1269*11729SWang.Lin@Sun.COM * negotiated parameters.
1270*11729SWang.Lin@Sun.COM */
1271*11729SWang.Lin@Sun.COM ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
1272*11729SWang.Lin@Sun.COM (in->in_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) ?
1273*11729SWang.Lin@Sun.COM (bf->bf_state.bf_type |= BUF_SHORT_PREAMBLE) :
1274*11729SWang.Lin@Sun.COM (bf->bf_state.bf_type &= ~BUF_SHORT_PREAMBLE);
1275*11729SWang.Lin@Sun.COM
1276*11729SWang.Lin@Sun.COM bf->bf_flags = setup_tx_flags(sc, wh, pktlen);
1277*11729SWang.Lin@Sun.COM
1278*11729SWang.Lin@Sun.COM /* Crypto */
1279*11729SWang.Lin@Sun.COM bf->bf_keytype = keytype;
1280*11729SWang.Lin@Sun.COM
1281*11729SWang.Lin@Sun.COM /* Assign seqno, tidno for tx aggrefation */
1282*11729SWang.Lin@Sun.COM
1283*11729SWang.Lin@Sun.COM #ifdef ARN_TX_AGGREGATION
1284*11729SWang.Lin@Sun.COM if (ieee80211_is_data_qos(wh) && (sc->sc_flags & SC_OP_TXAGGR))
1285*11729SWang.Lin@Sun.COM assign_aggr_tid_seqno(sc, bf, wh);
1286*11729SWang.Lin@Sun.COM #endif /* ARN_TX_AGGREGATION */
1287*11729SWang.Lin@Sun.COM
1288*11729SWang.Lin@Sun.COM }
1289*11729SWang.Lin@Sun.COM
1290*11729SWang.Lin@Sun.COM /*
1291*11729SWang.Lin@Sun.COM * ath_pkt_dur - compute packet duration (NB: not NAV)
1292*11729SWang.Lin@Sun.COM *
1293*11729SWang.Lin@Sun.COM * rix - rate index
1294*11729SWang.Lin@Sun.COM * pktlen - total bytes (delims + data + fcs + pads + pad delims)
1295*11729SWang.Lin@Sun.COM * width - 0 for 20 MHz, 1 for 40 MHz
1296*11729SWang.Lin@Sun.COM * half_gi - to use 4us v/s 3.6 us for symbol time
1297*11729SWang.Lin@Sun.COM */
1298*11729SWang.Lin@Sun.COM static uint32_t
ath_pkt_duration(struct arn_softc * sc,uint8_t rix,struct ath_buf * bf,int width,int half_gi,boolean_t shortPreamble)1299*11729SWang.Lin@Sun.COM ath_pkt_duration(struct arn_softc *sc, uint8_t rix, struct ath_buf *bf,
1300*11729SWang.Lin@Sun.COM int width, int half_gi, boolean_t shortPreamble)
1301*11729SWang.Lin@Sun.COM {
1302*11729SWang.Lin@Sun.COM struct ath_rate_table *rate_table = sc->sc_currates;
1303*11729SWang.Lin@Sun.COM uint32_t nbits, nsymbits, duration, nsymbols;
1304*11729SWang.Lin@Sun.COM uint8_t rc;
1305*11729SWang.Lin@Sun.COM int streams, pktlen;
1306*11729SWang.Lin@Sun.COM
1307*11729SWang.Lin@Sun.COM pktlen = bf_isaggr(bf) ? bf->bf_al : bf->bf_frmlen;
1308*11729SWang.Lin@Sun.COM rc = rate_table->info[rix].ratecode;
1309*11729SWang.Lin@Sun.COM
1310*11729SWang.Lin@Sun.COM /* for legacy rates, use old function to compute packet duration */
1311*11729SWang.Lin@Sun.COM if (!IS_HT_RATE(rc))
1312*11729SWang.Lin@Sun.COM return (ath9k_hw_computetxtime(sc->sc_ah, rate_table, pktlen,
1313*11729SWang.Lin@Sun.COM rix, shortPreamble));
1314*11729SWang.Lin@Sun.COM
1315*11729SWang.Lin@Sun.COM /* find number of symbols: PLCP + data */
1316*11729SWang.Lin@Sun.COM nbits = (pktlen << 3) + OFDM_PLCP_BITS;
1317*11729SWang.Lin@Sun.COM nsymbits = bits_per_symbol[HT_RC_2_MCS(rc)][width];
1318*11729SWang.Lin@Sun.COM nsymbols = (nbits + nsymbits - 1) / nsymbits;
1319*11729SWang.Lin@Sun.COM
1320*11729SWang.Lin@Sun.COM if (!half_gi)
1321*11729SWang.Lin@Sun.COM duration = SYMBOL_TIME(nsymbols);
1322*11729SWang.Lin@Sun.COM else
1323*11729SWang.Lin@Sun.COM duration = SYMBOL_TIME_HALFGI(nsymbols);
1324*11729SWang.Lin@Sun.COM
1325*11729SWang.Lin@Sun.COM /* addup duration for legacy/ht training and signal fields */
1326*11729SWang.Lin@Sun.COM streams = HT_RC_2_STREAMS(rc);
1327*11729SWang.Lin@Sun.COM duration += L_STF + L_LTF + L_SIG + HT_SIG + HT_STF + HT_LTF(streams);
1328*11729SWang.Lin@Sun.COM
1329*11729SWang.Lin@Sun.COM return (duration);
1330*11729SWang.Lin@Sun.COM }
1331*11729SWang.Lin@Sun.COM
1332*11729SWang.Lin@Sun.COM /* Rate module function to set rate related fields in tx descriptor */
1333*11729SWang.Lin@Sun.COM static void
ath_buf_set_rate(struct arn_softc * sc,struct ath_buf * bf,struct ieee80211_frame * wh)1334*11729SWang.Lin@Sun.COM ath_buf_set_rate(struct arn_softc *sc,
1335*11729SWang.Lin@Sun.COM struct ath_buf *bf,
1336*11729SWang.Lin@Sun.COM struct ieee80211_frame *wh)
1337*11729SWang.Lin@Sun.COM {
1338*11729SWang.Lin@Sun.COM struct ath_hal *ah = sc->sc_ah;
1339*11729SWang.Lin@Sun.COM struct ath_rate_table *rt;
1340*11729SWang.Lin@Sun.COM struct ath_desc *ds = bf->bf_desc;
1341*11729SWang.Lin@Sun.COM struct ath_desc *lastds = bf->bf_desc; /* temp workground */
1342*11729SWang.Lin@Sun.COM struct ath9k_11n_rate_series series[4];
1343*11729SWang.Lin@Sun.COM struct ath9k_tx_rate *rates;
1344*11729SWang.Lin@Sun.COM int i, flags, rtsctsena = 0;
1345*11729SWang.Lin@Sun.COM uint32_t ctsduration = 0;
1346*11729SWang.Lin@Sun.COM uint8_t rix = 0, cix, ctsrate = 0;
1347*11729SWang.Lin@Sun.COM
1348*11729SWang.Lin@Sun.COM (void) memset(series, 0, sizeof (struct ath9k_11n_rate_series) * 4);
1349*11729SWang.Lin@Sun.COM
1350*11729SWang.Lin@Sun.COM rates = bf->rates;
1351*11729SWang.Lin@Sun.COM
1352*11729SWang.Lin@Sun.COM if (IEEE80211_HAS_MOREFRAGS(wh) ||
1353*11729SWang.Lin@Sun.COM wh->i_seq[0] & IEEE80211_SEQ_FRAG_MASK) {
1354*11729SWang.Lin@Sun.COM rates[1].count = rates[2].count = rates[3].count = 0;
1355*11729SWang.Lin@Sun.COM rates[1].idx = rates[2].idx = rates[3].idx = 0;
1356*11729SWang.Lin@Sun.COM rates[0].count = ATH_TXMAXTRY;
1357*11729SWang.Lin@Sun.COM }
1358*11729SWang.Lin@Sun.COM
1359*11729SWang.Lin@Sun.COM /* get the cix for the lowest valid rix */
1360*11729SWang.Lin@Sun.COM rt = sc->sc_currates;
1361*11729SWang.Lin@Sun.COM for (i = 3; i >= 0; i--) {
1362*11729SWang.Lin@Sun.COM if (rates[i].count && (rates[i].idx >= 0)) {
1363*11729SWang.Lin@Sun.COM rix = rates[i].idx;
1364*11729SWang.Lin@Sun.COM break;
1365*11729SWang.Lin@Sun.COM }
1366*11729SWang.Lin@Sun.COM }
1367*11729SWang.Lin@Sun.COM
1368*11729SWang.Lin@Sun.COM flags = (bf->bf_flags & (ATH9K_TXDESC_RTSENA | ATH9K_TXDESC_CTSENA));
1369*11729SWang.Lin@Sun.COM cix = rt->info[rix].ctrl_rate;
1370*11729SWang.Lin@Sun.COM
1371*11729SWang.Lin@Sun.COM /*
1372*11729SWang.Lin@Sun.COM * If 802.11g protection is enabled, determine whether to use RTS/CTS or
1373*11729SWang.Lin@Sun.COM * just CTS. Note that this is only done for OFDM/HT unicast frames.
1374*11729SWang.Lin@Sun.COM */
1375*11729SWang.Lin@Sun.COM if (sc->sc_protmode != PROT_M_NONE &&
1376*11729SWang.Lin@Sun.COM !(bf->bf_flags & ATH9K_TXDESC_NOACK) &&
1377*11729SWang.Lin@Sun.COM (rt->info[rix].phy == WLAN_RC_PHY_OFDM ||
1378*11729SWang.Lin@Sun.COM WLAN_RC_PHY_HT(rt->info[rix].phy))) {
1379*11729SWang.Lin@Sun.COM if (sc->sc_protmode == PROT_M_RTSCTS)
1380*11729SWang.Lin@Sun.COM flags = ATH9K_TXDESC_RTSENA;
1381*11729SWang.Lin@Sun.COM else if (sc->sc_protmode == PROT_M_CTSONLY)
1382*11729SWang.Lin@Sun.COM flags = ATH9K_TXDESC_CTSENA;
1383*11729SWang.Lin@Sun.COM
1384*11729SWang.Lin@Sun.COM cix = rt->info[sc->sc_protrix].ctrl_rate;
1385*11729SWang.Lin@Sun.COM rtsctsena = 1;
1386*11729SWang.Lin@Sun.COM }
1387*11729SWang.Lin@Sun.COM
1388*11729SWang.Lin@Sun.COM /*
1389*11729SWang.Lin@Sun.COM * For 11n, the default behavior is to enable RTS for hw retried frames.
1390*11729SWang.Lin@Sun.COM * We enable the global flag here and let rate series flags determine
1391*11729SWang.Lin@Sun.COM * which rates will actually use RTS.
1392*11729SWang.Lin@Sun.COM */
1393*11729SWang.Lin@Sun.COM if ((ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT) && bf_isdata(bf)) {
1394*11729SWang.Lin@Sun.COM /* 802.11g protection not needed, use our default behavior */
1395*11729SWang.Lin@Sun.COM if (!rtsctsena)
1396*11729SWang.Lin@Sun.COM flags = ATH9K_TXDESC_RTSENA;
1397*11729SWang.Lin@Sun.COM }
1398*11729SWang.Lin@Sun.COM
1399*11729SWang.Lin@Sun.COM /* Set protection if aggregate protection on */
1400*11729SWang.Lin@Sun.COM if (sc->sc_config.ath_aggr_prot &&
1401*11729SWang.Lin@Sun.COM (!bf_isaggr(bf) || (bf_isaggr(bf) && bf->bf_al < 8192))) {
1402*11729SWang.Lin@Sun.COM flags = ATH9K_TXDESC_RTSENA;
1403*11729SWang.Lin@Sun.COM cix = rt->info[sc->sc_protrix].ctrl_rate;
1404*11729SWang.Lin@Sun.COM rtsctsena = 1;
1405*11729SWang.Lin@Sun.COM }
1406*11729SWang.Lin@Sun.COM
1407*11729SWang.Lin@Sun.COM /* For AR5416 - RTS cannot be followed by a frame larger than 8K */
1408*11729SWang.Lin@Sun.COM if (bf_isaggr(bf) && (bf->bf_al > ah->ah_caps.rts_aggr_limit))
1409*11729SWang.Lin@Sun.COM flags &= ~(ATH9K_TXDESC_RTSENA);
1410*11729SWang.Lin@Sun.COM
1411*11729SWang.Lin@Sun.COM /*
1412*11729SWang.Lin@Sun.COM * CTS transmit rate is derived from the transmit rate by looking in the
1413*11729SWang.Lin@Sun.COM * h/w rate table. We must also factor in whether or not a short
1414*11729SWang.Lin@Sun.COM * preamble is to be used. NB: cix is set above where RTS/CTS is enabled
1415*11729SWang.Lin@Sun.COM */
1416*11729SWang.Lin@Sun.COM ctsrate = rt->info[cix].ratecode |
1417*11729SWang.Lin@Sun.COM (bf_isshpreamble(bf) ? rt->info[cix].short_preamble : 0);
1418*11729SWang.Lin@Sun.COM
1419*11729SWang.Lin@Sun.COM for (i = 0; i < 4; i++) {
1420*11729SWang.Lin@Sun.COM if (!rates[i].count || (rates[i].idx < 0))
1421*11729SWang.Lin@Sun.COM continue;
1422*11729SWang.Lin@Sun.COM
1423*11729SWang.Lin@Sun.COM rix = rates[i].idx;
1424*11729SWang.Lin@Sun.COM
1425*11729SWang.Lin@Sun.COM series[i].Rate = rt->info[rix].ratecode |
1426*11729SWang.Lin@Sun.COM (bf_isshpreamble(bf) ?
1427*11729SWang.Lin@Sun.COM rt->info[rix].short_preamble : 0);
1428*11729SWang.Lin@Sun.COM
1429*11729SWang.Lin@Sun.COM series[i].Tries = rates[i].count;
1430*11729SWang.Lin@Sun.COM
1431*11729SWang.Lin@Sun.COM series[i].RateFlags =
1432*11729SWang.Lin@Sun.COM ((rates[i].flags & ATH9K_TX_RC_USE_RTS_CTS) ?
1433*11729SWang.Lin@Sun.COM ATH9K_RATESERIES_RTS_CTS : 0) |
1434*11729SWang.Lin@Sun.COM ((rates[i].flags & ATH9K_TX_RC_40_MHZ_WIDTH) ?
1435*11729SWang.Lin@Sun.COM ATH9K_RATESERIES_2040 : 0) |
1436*11729SWang.Lin@Sun.COM ((rates[i].flags & ATH9K_TX_RC_SHORT_GI) ?
1437*11729SWang.Lin@Sun.COM ATH9K_RATESERIES_HALFGI : 0);
1438*11729SWang.Lin@Sun.COM
1439*11729SWang.Lin@Sun.COM series[i].PktDuration = ath_pkt_duration(sc, rix, bf,
1440*11729SWang.Lin@Sun.COM (rates[i].flags & ATH9K_TX_RC_40_MHZ_WIDTH) != 0,
1441*11729SWang.Lin@Sun.COM (rates[i].flags & ATH9K_TX_RC_SHORT_GI),
1442*11729SWang.Lin@Sun.COM bf_isshpreamble(bf));
1443*11729SWang.Lin@Sun.COM
1444*11729SWang.Lin@Sun.COM series[i].ChSel = sc->sc_tx_chainmask;
1445*11729SWang.Lin@Sun.COM
1446*11729SWang.Lin@Sun.COM if (rtsctsena)
1447*11729SWang.Lin@Sun.COM series[i].RateFlags |= ATH9K_RATESERIES_RTS_CTS;
1448*11729SWang.Lin@Sun.COM
1449*11729SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_RATE,
1450*11729SWang.Lin@Sun.COM "series[%d]--flags & ATH9K_TX_RC_USE_RTS_CTS = %08x"
1451*11729SWang.Lin@Sun.COM "--flags & ATH9K_TX_RC_40_MHZ_WIDTH = %08x"
1452*11729SWang.Lin@Sun.COM "--flags & ATH9K_TX_RC_SHORT_GI = %08x\n",
1453*11729SWang.Lin@Sun.COM rates[i].flags & ATH9K_TX_RC_USE_RTS_CTS,
1454*11729SWang.Lin@Sun.COM rates[i].flags & ATH9K_TX_RC_40_MHZ_WIDTH,
1455*11729SWang.Lin@Sun.COM rates[i].flags & ATH9K_TX_RC_SHORT_GI));
1456*11729SWang.Lin@Sun.COM
1457*11729SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_RATE,
1458*11729SWang.Lin@Sun.COM "series[%d]:"
1459*11729SWang.Lin@Sun.COM "dot11rate:%d"
1460*11729SWang.Lin@Sun.COM "index:%d"
1461*11729SWang.Lin@Sun.COM "retry count:%d\n",
1462*11729SWang.Lin@Sun.COM i,
1463*11729SWang.Lin@Sun.COM (rt->info[rates[i].idx].ratekbps)/1000,
1464*11729SWang.Lin@Sun.COM rates[i].idx,
1465*11729SWang.Lin@Sun.COM rates[i].count));
1466*11729SWang.Lin@Sun.COM }
1467*11729SWang.Lin@Sun.COM
1468*11729SWang.Lin@Sun.COM /* set dur_update_en for l-sig computation except for PS-Poll frames */
1469*11729SWang.Lin@Sun.COM ath9k_hw_set11n_ratescenario(ah, ds, lastds, !bf_ispspoll(bf),
1470*11729SWang.Lin@Sun.COM ctsrate, ctsduration,
1471*11729SWang.Lin@Sun.COM series, 4, flags);
1472*11729SWang.Lin@Sun.COM
1473*11729SWang.Lin@Sun.COM if (sc->sc_config.ath_aggr_prot && flags)
1474*11729SWang.Lin@Sun.COM ath9k_hw_set11n_burstduration(ah, ds, 8192);
1475*11729SWang.Lin@Sun.COM }
1476*11729SWang.Lin@Sun.COM
1477*11729SWang.Lin@Sun.COM static void
ath_tx_complete(struct arn_softc * sc,struct ath_buf * bf,struct ath_xmit_status * tx_status)1478*11729SWang.Lin@Sun.COM ath_tx_complete(struct arn_softc *sc, struct ath_buf *bf,
1479*11729SWang.Lin@Sun.COM struct ath_xmit_status *tx_status)
1480*11729SWang.Lin@Sun.COM {
1481*11729SWang.Lin@Sun.COM boolean_t is_data = bf_isdata(bf);
1482*11729SWang.Lin@Sun.COM
1483*11729SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_XMIT, "TX complete\n"));
1484*11729SWang.Lin@Sun.COM
1485*11729SWang.Lin@Sun.COM if (tx_status->flags & ATH_TX_BAR)
1486*11729SWang.Lin@Sun.COM tx_status->flags &= ~ATH_TX_BAR;
1487*11729SWang.Lin@Sun.COM
1488*11729SWang.Lin@Sun.COM bf->rates[0].count = tx_status->retries + 1;
1489*11729SWang.Lin@Sun.COM
1490*11729SWang.Lin@Sun.COM arn_tx_status(sc, bf, is_data);
1491*11729SWang.Lin@Sun.COM }
1492*11729SWang.Lin@Sun.COM
1493*11729SWang.Lin@Sun.COM /* To complete a chain of buffers associated a frame */
1494*11729SWang.Lin@Sun.COM static void
ath_tx_complete_buf(struct arn_softc * sc,struct ath_buf * bf,int txok,int sendbar)1495*11729SWang.Lin@Sun.COM ath_tx_complete_buf(struct arn_softc *sc, struct ath_buf *bf,
1496*11729SWang.Lin@Sun.COM int txok, int sendbar)
1497*11729SWang.Lin@Sun.COM {
1498*11729SWang.Lin@Sun.COM struct ath_xmit_status tx_status;
1499*11729SWang.Lin@Sun.COM
1500*11729SWang.Lin@Sun.COM /*
1501*11729SWang.Lin@Sun.COM * Set retry information.
1502*11729SWang.Lin@Sun.COM * NB: Don't use the information in the descriptor, because the frame
1503*11729SWang.Lin@Sun.COM * could be software retried.
1504*11729SWang.Lin@Sun.COM */
1505*11729SWang.Lin@Sun.COM tx_status.retries = bf->bf_retries;
1506*11729SWang.Lin@Sun.COM tx_status.flags = 0;
1507*11729SWang.Lin@Sun.COM
1508*11729SWang.Lin@Sun.COM if (sendbar)
1509*11729SWang.Lin@Sun.COM tx_status.flags = ATH_TX_BAR;
1510*11729SWang.Lin@Sun.COM
1511*11729SWang.Lin@Sun.COM if (!txok) {
1512*11729SWang.Lin@Sun.COM tx_status.flags |= ATH_TX_ERROR;
1513*11729SWang.Lin@Sun.COM
1514*11729SWang.Lin@Sun.COM if (bf_isxretried(bf))
1515*11729SWang.Lin@Sun.COM tx_status.flags |= ATH_TX_XRETRY;
1516*11729SWang.Lin@Sun.COM }
1517*11729SWang.Lin@Sun.COM
1518*11729SWang.Lin@Sun.COM /* complete this frame */
1519*11729SWang.Lin@Sun.COM ath_tx_complete(sc, bf, &tx_status);
1520*11729SWang.Lin@Sun.COM
1521*11729SWang.Lin@Sun.COM /*
1522*11729SWang.Lin@Sun.COM * Return the list of ath_buf of this mpdu to free queue
1523*11729SWang.Lin@Sun.COM */
1524*11729SWang.Lin@Sun.COM }
1525*11729SWang.Lin@Sun.COM
15269999SWang.Lin@Sun.COM static void
arn_tx_stopdma(struct arn_softc * sc,struct ath_txq * txq)15279999SWang.Lin@Sun.COM arn_tx_stopdma(struct arn_softc *sc, struct ath_txq *txq)
15289999SWang.Lin@Sun.COM {
15299999SWang.Lin@Sun.COM struct ath_hal *ah = sc->sc_ah;
15309999SWang.Lin@Sun.COM
15319999SWang.Lin@Sun.COM (void) ath9k_hw_stoptxdma(ah, txq->axq_qnum);
15329999SWang.Lin@Sun.COM
15339999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_XMIT, "arn: arn_drain_txdataq(): "
15349999SWang.Lin@Sun.COM "tx queue [%u] %x, link %p\n",
15359999SWang.Lin@Sun.COM txq->axq_qnum,
15369999SWang.Lin@Sun.COM ath9k_hw_gettxbuf(ah, txq->axq_qnum), txq->axq_link));
15379999SWang.Lin@Sun.COM
15389999SWang.Lin@Sun.COM }
15399999SWang.Lin@Sun.COM
15409999SWang.Lin@Sun.COM /* Drain only the data queues */
15419999SWang.Lin@Sun.COM /* ARGSUSED */
15429999SWang.Lin@Sun.COM static void
arn_drain_txdataq(struct arn_softc * sc,boolean_t retry_tx)15439999SWang.Lin@Sun.COM arn_drain_txdataq(struct arn_softc *sc, boolean_t retry_tx)
15449999SWang.Lin@Sun.COM {
15459999SWang.Lin@Sun.COM struct ath_hal *ah = sc->sc_ah;
15469999SWang.Lin@Sun.COM int i, status, npend = 0;
15479999SWang.Lin@Sun.COM
15489999SWang.Lin@Sun.COM if (!(sc->sc_flags & SC_OP_INVALID)) {
15499999SWang.Lin@Sun.COM for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
15509999SWang.Lin@Sun.COM if (ARN_TXQ_SETUP(sc, i)) {
15519999SWang.Lin@Sun.COM arn_tx_stopdma(sc, &sc->sc_txq[i]);
15529999SWang.Lin@Sun.COM /*
15539999SWang.Lin@Sun.COM * The TxDMA may not really be stopped.
15549999SWang.Lin@Sun.COM * Double check the hal tx pending count
15559999SWang.Lin@Sun.COM */
15569999SWang.Lin@Sun.COM npend += ath9k_hw_numtxpending(ah,
15579999SWang.Lin@Sun.COM sc->sc_txq[i].axq_qnum);
15589999SWang.Lin@Sun.COM }
15599999SWang.Lin@Sun.COM }
15609999SWang.Lin@Sun.COM }
15619999SWang.Lin@Sun.COM
15629999SWang.Lin@Sun.COM if (npend) {
15639999SWang.Lin@Sun.COM /* TxDMA not stopped, reset the hal */
15649999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_XMIT, "arn: arn_drain_txdataq(): "
15659999SWang.Lin@Sun.COM "Unable to stop TxDMA. Reset HAL!\n"));
15669999SWang.Lin@Sun.COM
15679999SWang.Lin@Sun.COM if (!ath9k_hw_reset(ah,
15689999SWang.Lin@Sun.COM sc->sc_ah->ah_curchan,
15699999SWang.Lin@Sun.COM sc->tx_chan_width,
15709999SWang.Lin@Sun.COM sc->sc_tx_chainmask, sc->sc_rx_chainmask,
15719999SWang.Lin@Sun.COM sc->sc_ht_extprotspacing, B_TRUE, &status)) {
15729999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_FATAL, "arn: arn_drain_txdataq(): "
15739999SWang.Lin@Sun.COM "unable to reset hardware; hal status %u\n",
15749999SWang.Lin@Sun.COM status));
15759999SWang.Lin@Sun.COM }
15769999SWang.Lin@Sun.COM }
15779999SWang.Lin@Sun.COM
15789999SWang.Lin@Sun.COM for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
15799999SWang.Lin@Sun.COM if (ARN_TXQ_SETUP(sc, i))
15809999SWang.Lin@Sun.COM arn_tx_draintxq(sc, &sc->sc_txq[i]);
15819999SWang.Lin@Sun.COM }
15829999SWang.Lin@Sun.COM }
15839999SWang.Lin@Sun.COM
15849999SWang.Lin@Sun.COM /* Setup a h/w transmit queue */
15859999SWang.Lin@Sun.COM struct ath_txq *
arn_txq_setup(struct arn_softc * sc,int qtype,int subtype)15869999SWang.Lin@Sun.COM arn_txq_setup(struct arn_softc *sc, int qtype, int subtype)
15879999SWang.Lin@Sun.COM {
15889999SWang.Lin@Sun.COM struct ath_hal *ah = sc->sc_ah;
15899999SWang.Lin@Sun.COM struct ath9k_tx_queue_info qi;
15909999SWang.Lin@Sun.COM int qnum;
15919999SWang.Lin@Sun.COM
15929999SWang.Lin@Sun.COM (void) memset(&qi, 0, sizeof (qi));
15939999SWang.Lin@Sun.COM qi.tqi_subtype = subtype;
15949999SWang.Lin@Sun.COM qi.tqi_aifs = ATH9K_TXQ_USEDEFAULT;
15959999SWang.Lin@Sun.COM qi.tqi_cwmin = ATH9K_TXQ_USEDEFAULT;
15969999SWang.Lin@Sun.COM qi.tqi_cwmax = ATH9K_TXQ_USEDEFAULT;
15979999SWang.Lin@Sun.COM qi.tqi_physCompBuf = 0;
15989999SWang.Lin@Sun.COM
15999999SWang.Lin@Sun.COM /*
16009999SWang.Lin@Sun.COM * Enable interrupts only for EOL and DESC conditions.
16019999SWang.Lin@Sun.COM * We mark tx descriptors to receive a DESC interrupt
16029999SWang.Lin@Sun.COM * when a tx queue gets deep; otherwise waiting for the
16039999SWang.Lin@Sun.COM * EOL to reap descriptors. Note that this is done to
16049999SWang.Lin@Sun.COM * reduce interrupt load and this only defers reaping
16059999SWang.Lin@Sun.COM * descriptors, never transmitting frames. Aside from
16069999SWang.Lin@Sun.COM * reducing interrupts this also permits more concurrency.
16079999SWang.Lin@Sun.COM * The only potential downside is if the tx queue backs
16089999SWang.Lin@Sun.COM * up in which case the top half of the kernel may backup
16099999SWang.Lin@Sun.COM * due to a lack of tx descriptors.
16109999SWang.Lin@Sun.COM *
16119999SWang.Lin@Sun.COM * The UAPSD queue is an exception, since we take a desc-
16129999SWang.Lin@Sun.COM * based intr on the EOSP frames.
16139999SWang.Lin@Sun.COM */
16149999SWang.Lin@Sun.COM if (qtype == ATH9K_TX_QUEUE_UAPSD)
16159999SWang.Lin@Sun.COM qi.tqi_qflags = TXQ_FLAG_TXDESCINT_ENABLE;
16169999SWang.Lin@Sun.COM else
16179999SWang.Lin@Sun.COM qi.tqi_qflags = TXQ_FLAG_TXEOLINT_ENABLE |
16189999SWang.Lin@Sun.COM TXQ_FLAG_TXDESCINT_ENABLE;
16199999SWang.Lin@Sun.COM qnum = ath9k_hw_setuptxqueue(ah, qtype, &qi);
16209999SWang.Lin@Sun.COM if (qnum == -1) {
16219999SWang.Lin@Sun.COM /*
16229999SWang.Lin@Sun.COM * NB: don't print a message, this happens
16239999SWang.Lin@Sun.COM * normally on parts with too few tx queues
16249999SWang.Lin@Sun.COM */
16259999SWang.Lin@Sun.COM return (NULL);
16269999SWang.Lin@Sun.COM }
16279999SWang.Lin@Sun.COM if (qnum >= ARRAY_SIZE(sc->sc_txq)) {
16289999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_FATAL, "arn: arn_txq_setup(): "
16299999SWang.Lin@Sun.COM "hal qnum %u out of range, max %u!\n",
16309999SWang.Lin@Sun.COM qnum, (unsigned int)ARRAY_SIZE(sc->sc_txq)));
16319999SWang.Lin@Sun.COM (void) ath9k_hw_releasetxqueue(ah, qnum);
16329999SWang.Lin@Sun.COM return (NULL);
16339999SWang.Lin@Sun.COM }
16349999SWang.Lin@Sun.COM if (!ARN_TXQ_SETUP(sc, qnum)) {
16359999SWang.Lin@Sun.COM struct ath_txq *txq = &sc->sc_txq[qnum];
16369999SWang.Lin@Sun.COM
16379999SWang.Lin@Sun.COM txq->axq_qnum = qnum;
1638*11729SWang.Lin@Sun.COM txq->axq_intrcnt = 0; /* legacy */
16399999SWang.Lin@Sun.COM txq->axq_link = NULL;
16409999SWang.Lin@Sun.COM
16419999SWang.Lin@Sun.COM list_create(&txq->axq_list, sizeof (struct ath_buf),
16429999SWang.Lin@Sun.COM offsetof(struct ath_buf, bf_node));
1643*11729SWang.Lin@Sun.COM list_create(&txq->axq_acq, sizeof (struct ath_buf),
1644*11729SWang.Lin@Sun.COM offsetof(struct ath_buf, bf_node));
16459999SWang.Lin@Sun.COM mutex_init(&txq->axq_lock, NULL, MUTEX_DRIVER, NULL);
16469999SWang.Lin@Sun.COM
16479999SWang.Lin@Sun.COM txq->axq_depth = 0;
16489999SWang.Lin@Sun.COM txq->axq_aggr_depth = 0;
16499999SWang.Lin@Sun.COM txq->axq_totalqueued = 0;
1650*11729SWang.Lin@Sun.COM txq->axq_linkbuf = NULL;
16519999SWang.Lin@Sun.COM sc->sc_txqsetup |= 1<<qnum;
16529999SWang.Lin@Sun.COM }
16539999SWang.Lin@Sun.COM return (&sc->sc_txq[qnum]);
16549999SWang.Lin@Sun.COM }
16559999SWang.Lin@Sun.COM
16569999SWang.Lin@Sun.COM /* Reclaim resources for a setup queue */
16579999SWang.Lin@Sun.COM
16589999SWang.Lin@Sun.COM void
arn_tx_cleanupq(struct arn_softc * sc,struct ath_txq * txq)16599999SWang.Lin@Sun.COM arn_tx_cleanupq(struct arn_softc *sc, struct ath_txq *txq)
16609999SWang.Lin@Sun.COM {
16619999SWang.Lin@Sun.COM (void) ath9k_hw_releasetxqueue(sc->sc_ah, txq->axq_qnum);
16629999SWang.Lin@Sun.COM sc->sc_txqsetup &= ~(1<<txq->axq_qnum);
16639999SWang.Lin@Sun.COM }
16649999SWang.Lin@Sun.COM
16659999SWang.Lin@Sun.COM /*
16669999SWang.Lin@Sun.COM * Setup a hardware data transmit queue for the specified
16679999SWang.Lin@Sun.COM * access control. The hal may not support all requested
16689999SWang.Lin@Sun.COM * queues in which case it will return a reference to a
16699999SWang.Lin@Sun.COM * previously setup queue. We record the mapping from ac's
16709999SWang.Lin@Sun.COM * to h/w queues for use by arn_tx_start and also track
16719999SWang.Lin@Sun.COM * the set of h/w queues being used to optimize work in the
16729999SWang.Lin@Sun.COM * transmit interrupt handler and related routines.
16739999SWang.Lin@Sun.COM */
16749999SWang.Lin@Sun.COM
16759999SWang.Lin@Sun.COM int
arn_tx_setup(struct arn_softc * sc,int haltype)16769999SWang.Lin@Sun.COM arn_tx_setup(struct arn_softc *sc, int haltype)
16779999SWang.Lin@Sun.COM {
16789999SWang.Lin@Sun.COM struct ath_txq *txq;
16799999SWang.Lin@Sun.COM
16809999SWang.Lin@Sun.COM if (haltype >= ARRAY_SIZE(sc->sc_haltype2q)) {
16819999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_FATAL, "arn: arn_tx_setup(): "
16829999SWang.Lin@Sun.COM "HAL AC %u out of range, max %zu!\n",
16839999SWang.Lin@Sun.COM haltype, ARRAY_SIZE(sc->sc_haltype2q)));
16849999SWang.Lin@Sun.COM return (0);
16859999SWang.Lin@Sun.COM }
16869999SWang.Lin@Sun.COM txq = arn_txq_setup(sc, ATH9K_TX_QUEUE_DATA, haltype);
16879999SWang.Lin@Sun.COM if (txq != NULL) {
16889999SWang.Lin@Sun.COM sc->sc_haltype2q[haltype] = txq->axq_qnum;
16899999SWang.Lin@Sun.COM return (1);
16909999SWang.Lin@Sun.COM } else
16919999SWang.Lin@Sun.COM return (0);
16929999SWang.Lin@Sun.COM }
16939999SWang.Lin@Sun.COM
16949999SWang.Lin@Sun.COM void
arn_tx_draintxq(struct arn_softc * sc,struct ath_txq * txq)16959999SWang.Lin@Sun.COM arn_tx_draintxq(struct arn_softc *sc, struct ath_txq *txq)
16969999SWang.Lin@Sun.COM {
16979999SWang.Lin@Sun.COM struct ath_buf *bf;
16989999SWang.Lin@Sun.COM
16999999SWang.Lin@Sun.COM /*
17009999SWang.Lin@Sun.COM * This assumes output has been stopped.
17019999SWang.Lin@Sun.COM */
17029999SWang.Lin@Sun.COM for (;;) {
17039999SWang.Lin@Sun.COM mutex_enter(&txq->axq_lock);
17049999SWang.Lin@Sun.COM bf = list_head(&txq->axq_list);
17059999SWang.Lin@Sun.COM if (bf == NULL) {
17069999SWang.Lin@Sun.COM txq->axq_link = NULL;
17079999SWang.Lin@Sun.COM mutex_exit(&txq->axq_lock);
17089999SWang.Lin@Sun.COM break;
17099999SWang.Lin@Sun.COM }
17109999SWang.Lin@Sun.COM list_remove(&txq->axq_list, bf);
17119999SWang.Lin@Sun.COM mutex_exit(&txq->axq_lock);
17129999SWang.Lin@Sun.COM bf->bf_in = NULL;
17139999SWang.Lin@Sun.COM mutex_enter(&sc->sc_txbuflock);
17149999SWang.Lin@Sun.COM list_insert_tail(&sc->sc_txbuf_list, bf);
17159999SWang.Lin@Sun.COM mutex_exit(&sc->sc_txbuflock);
17169999SWang.Lin@Sun.COM }
17179999SWang.Lin@Sun.COM }
17189999SWang.Lin@Sun.COM
17199999SWang.Lin@Sun.COM /* Drain the transmit queues and reclaim resources */
17209999SWang.Lin@Sun.COM
17219999SWang.Lin@Sun.COM void
arn_draintxq(struct arn_softc * sc,boolean_t retry_tx)17229999SWang.Lin@Sun.COM arn_draintxq(struct arn_softc *sc, boolean_t retry_tx)
17239999SWang.Lin@Sun.COM {
17249999SWang.Lin@Sun.COM /*
17259999SWang.Lin@Sun.COM * stop beacon queue. The beacon will be freed when
17269999SWang.Lin@Sun.COM * we go to INIT state
17279999SWang.Lin@Sun.COM */
17289999SWang.Lin@Sun.COM if (!(sc->sc_flags & SC_OP_INVALID)) {
17299999SWang.Lin@Sun.COM (void) ath9k_hw_stoptxdma(sc->sc_ah, sc->sc_beaconq);
17309999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_XMIT, "arn: arn_draintxq(): "
17319999SWang.Lin@Sun.COM "beacon queue %x\n",
17329999SWang.Lin@Sun.COM ath9k_hw_gettxbuf(sc->sc_ah, sc->sc_beaconq)));
17339999SWang.Lin@Sun.COM }
17349999SWang.Lin@Sun.COM
17359999SWang.Lin@Sun.COM arn_drain_txdataq(sc, retry_tx);
17369999SWang.Lin@Sun.COM }
17379999SWang.Lin@Sun.COM
17389999SWang.Lin@Sun.COM uint32_t
arn_txq_depth(struct arn_softc * sc,int qnum)17399999SWang.Lin@Sun.COM arn_txq_depth(struct arn_softc *sc, int qnum)
17409999SWang.Lin@Sun.COM {
17419999SWang.Lin@Sun.COM return (sc->sc_txq[qnum].axq_depth);
17429999SWang.Lin@Sun.COM }
17439999SWang.Lin@Sun.COM
17449999SWang.Lin@Sun.COM uint32_t
arn_txq_aggr_depth(struct arn_softc * sc,int qnum)17459999SWang.Lin@Sun.COM arn_txq_aggr_depth(struct arn_softc *sc, int qnum)
17469999SWang.Lin@Sun.COM {
17479999SWang.Lin@Sun.COM return (sc->sc_txq[qnum].axq_aggr_depth);
17489999SWang.Lin@Sun.COM }
17499999SWang.Lin@Sun.COM
17509999SWang.Lin@Sun.COM /* Update parameters for a transmit queue */
17519999SWang.Lin@Sun.COM int
arn_txq_update(struct arn_softc * sc,int qnum,struct ath9k_tx_queue_info * qinfo)17529999SWang.Lin@Sun.COM arn_txq_update(struct arn_softc *sc, int qnum,
17539999SWang.Lin@Sun.COM struct ath9k_tx_queue_info *qinfo)
17549999SWang.Lin@Sun.COM {
17559999SWang.Lin@Sun.COM struct ath_hal *ah = sc->sc_ah;
17569999SWang.Lin@Sun.COM int error = 0;
17579999SWang.Lin@Sun.COM struct ath9k_tx_queue_info qi;
17589999SWang.Lin@Sun.COM
17599999SWang.Lin@Sun.COM if (qnum == sc->sc_beaconq) {
17609999SWang.Lin@Sun.COM /*
17619999SWang.Lin@Sun.COM * XXX: for beacon queue, we just save the parameter.
17629999SWang.Lin@Sun.COM * It will be picked up by arn_beaconq_config() when
17639999SWang.Lin@Sun.COM * it's necessary.
17649999SWang.Lin@Sun.COM */
17659999SWang.Lin@Sun.COM sc->sc_beacon_qi = *qinfo;
17669999SWang.Lin@Sun.COM return (0);
17679999SWang.Lin@Sun.COM }
17689999SWang.Lin@Sun.COM
17699999SWang.Lin@Sun.COM ASSERT(sc->sc_txq[qnum].axq_qnum == qnum);
17709999SWang.Lin@Sun.COM
17719999SWang.Lin@Sun.COM (void) ath9k_hw_get_txq_props(ah, qnum, &qi);
17729999SWang.Lin@Sun.COM qi.tqi_aifs = qinfo->tqi_aifs;
17739999SWang.Lin@Sun.COM qi.tqi_cwmin = qinfo->tqi_cwmin;
17749999SWang.Lin@Sun.COM qi.tqi_cwmax = qinfo->tqi_cwmax;
17759999SWang.Lin@Sun.COM qi.tqi_burstTime = qinfo->tqi_burstTime;
17769999SWang.Lin@Sun.COM qi.tqi_readyTime = qinfo->tqi_readyTime;
17779999SWang.Lin@Sun.COM
17789999SWang.Lin@Sun.COM if (!ath9k_hw_set_txq_props(ah, qnum, &qi)) {
17799999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_FATAL,
17809999SWang.Lin@Sun.COM "Unable to update hardware queue %u!\n", qnum));
17819999SWang.Lin@Sun.COM error = -EIO;
17829999SWang.Lin@Sun.COM } else {
17839999SWang.Lin@Sun.COM (void) ath9k_hw_resettxqueue(ah, qnum); /* push to h/w */
17849999SWang.Lin@Sun.COM }
17859999SWang.Lin@Sun.COM
17869999SWang.Lin@Sun.COM return (error);
17879999SWang.Lin@Sun.COM }
17889999SWang.Lin@Sun.COM
17899999SWang.Lin@Sun.COM int
ath_cabq_update(struct arn_softc * sc)17909999SWang.Lin@Sun.COM ath_cabq_update(struct arn_softc *sc)
17919999SWang.Lin@Sun.COM {
17929999SWang.Lin@Sun.COM struct ath9k_tx_queue_info qi;
17939999SWang.Lin@Sun.COM int qnum = sc->sc_cabq->axq_qnum;
17949999SWang.Lin@Sun.COM struct ath_beacon_config conf;
17959999SWang.Lin@Sun.COM
17969999SWang.Lin@Sun.COM (void) ath9k_hw_get_txq_props(sc->sc_ah, qnum, &qi);
17979999SWang.Lin@Sun.COM /*
17989999SWang.Lin@Sun.COM * Ensure the readytime % is within the bounds.
17999999SWang.Lin@Sun.COM */
18009999SWang.Lin@Sun.COM if (sc->sc_config.cabqReadytime < ATH9K_READY_TIME_LO_BOUND)
18019999SWang.Lin@Sun.COM sc->sc_config.cabqReadytime = ATH9K_READY_TIME_LO_BOUND;
18029999SWang.Lin@Sun.COM else if (sc->sc_config.cabqReadytime > ATH9K_READY_TIME_HI_BOUND)
18039999SWang.Lin@Sun.COM sc->sc_config.cabqReadytime = ATH9K_READY_TIME_HI_BOUND;
18049999SWang.Lin@Sun.COM
18059999SWang.Lin@Sun.COM arn_get_beaconconfig(sc, &conf);
18069999SWang.Lin@Sun.COM qi.tqi_readyTime =
18079999SWang.Lin@Sun.COM (conf.beacon_interval * sc->sc_config.cabqReadytime) / 100;
18089999SWang.Lin@Sun.COM (void) arn_txq_update(sc, qnum, &qi);
18099999SWang.Lin@Sun.COM
18109999SWang.Lin@Sun.COM return (0);
18119999SWang.Lin@Sun.COM }
18129999SWang.Lin@Sun.COM
18139999SWang.Lin@Sun.COM static uint32_t
arn_tx_get_keytype(const struct ieee80211_cipher * cip)18149999SWang.Lin@Sun.COM arn_tx_get_keytype(const struct ieee80211_cipher *cip)
18159999SWang.Lin@Sun.COM {
18169999SWang.Lin@Sun.COM uint32_t index;
18179999SWang.Lin@Sun.COM static const uint8_t ciphermap[] = {
18189999SWang.Lin@Sun.COM ATH9K_CIPHER_WEP, /* IEEE80211_CIPHER_WEP */
18199999SWang.Lin@Sun.COM ATH9K_CIPHER_TKIP, /* IEEE80211_CIPHER_TKIP */
18209999SWang.Lin@Sun.COM ATH9K_CIPHER_AES_OCB, /* IEEE80211_CIPHER_AES_OCB */
18219999SWang.Lin@Sun.COM ATH9K_CIPHER_AES_CCM, /* IEEE80211_CIPHER_AES_CCM */
18229999SWang.Lin@Sun.COM ATH9K_CIPHER_CKIP, /* IEEE80211_CIPHER_CKIP */
18239999SWang.Lin@Sun.COM ATH9K_CIPHER_CLR, /* IEEE80211_CIPHER_NONE */
18249999SWang.Lin@Sun.COM };
18259999SWang.Lin@Sun.COM
18269999SWang.Lin@Sun.COM ASSERT(cip->ic_cipher < ARRAY_SIZE(ciphermap));
18279999SWang.Lin@Sun.COM index = cip->ic_cipher;
18289999SWang.Lin@Sun.COM
18299999SWang.Lin@Sun.COM if (ciphermap[index] == ATH9K_CIPHER_WEP)
18309999SWang.Lin@Sun.COM return (ATH9K_KEY_TYPE_WEP);
18319999SWang.Lin@Sun.COM else if (ciphermap[index] == ATH9K_CIPHER_TKIP)
18329999SWang.Lin@Sun.COM return (ATH9K_KEY_TYPE_TKIP);
18339999SWang.Lin@Sun.COM else if (ciphermap[index] == ATH9K_CIPHER_AES_CCM)
18349999SWang.Lin@Sun.COM return (ATH9K_KEY_TYPE_AES);
18359999SWang.Lin@Sun.COM
18369999SWang.Lin@Sun.COM return (ATH9K_KEY_TYPE_CLEAR);
18379999SWang.Lin@Sun.COM
18389999SWang.Lin@Sun.COM }
18399999SWang.Lin@Sun.COM
1840*11729SWang.Lin@Sun.COM /* Display buffer */
1841*11729SWang.Lin@Sun.COM void
arn_dump_line(unsigned char * p,uint32_t len,boolean_t isaddress,uint32_t group)1842*11729SWang.Lin@Sun.COM arn_dump_line(unsigned char *p, uint32_t len, boolean_t isaddress,
1843*11729SWang.Lin@Sun.COM uint32_t group)
1844*11729SWang.Lin@Sun.COM {
1845*11729SWang.Lin@Sun.COM char *pnumeric = "0123456789ABCDEF";
1846*11729SWang.Lin@Sun.COM char hex[((2 + 1) * 16) + 1];
1847*11729SWang.Lin@Sun.COM char *phex = hex;
1848*11729SWang.Lin@Sun.COM char ascii[16 + 1];
1849*11729SWang.Lin@Sun.COM char *pascii = ascii;
1850*11729SWang.Lin@Sun.COM uint32_t grouped = 0;
1851*11729SWang.Lin@Sun.COM
1852*11729SWang.Lin@Sun.COM if (isaddress) {
1853*11729SWang.Lin@Sun.COM arn_problem("arn: %08x: ", p);
1854*11729SWang.Lin@Sun.COM } else {
1855*11729SWang.Lin@Sun.COM arn_problem("arn: ");
1856*11729SWang.Lin@Sun.COM }
1857*11729SWang.Lin@Sun.COM
1858*11729SWang.Lin@Sun.COM while (len) {
1859*11729SWang.Lin@Sun.COM *phex++ = pnumeric[((uint8_t)*p) / 16];
1860*11729SWang.Lin@Sun.COM *phex++ = pnumeric[((uint8_t)*p) % 16];
1861*11729SWang.Lin@Sun.COM if (++grouped >= group) {
1862*11729SWang.Lin@Sun.COM *phex++ = ' ';
1863*11729SWang.Lin@Sun.COM grouped = 0;
1864*11729SWang.Lin@Sun.COM }
1865*11729SWang.Lin@Sun.COM
1866*11729SWang.Lin@Sun.COM *pascii++ = (*p >= 32 && *p < 128) ? *p : '.';
1867*11729SWang.Lin@Sun.COM
1868*11729SWang.Lin@Sun.COM ++p;
1869*11729SWang.Lin@Sun.COM --len;
1870*11729SWang.Lin@Sun.COM }
1871*11729SWang.Lin@Sun.COM
1872*11729SWang.Lin@Sun.COM *phex = '\0';
1873*11729SWang.Lin@Sun.COM *pascii = '\0';
1874*11729SWang.Lin@Sun.COM
1875*11729SWang.Lin@Sun.COM arn_problem("%-*s|%-*s|\n", (2 * 16) +
1876*11729SWang.Lin@Sun.COM (16 / group), hex, 16, ascii);
1877*11729SWang.Lin@Sun.COM }
1878*11729SWang.Lin@Sun.COM
1879*11729SWang.Lin@Sun.COM void
arn_dump_pkg(unsigned char * p,uint32_t len,boolean_t isaddress,uint32_t group)1880*11729SWang.Lin@Sun.COM arn_dump_pkg(unsigned char *p, uint32_t len, boolean_t isaddress,
1881*11729SWang.Lin@Sun.COM uint32_t group)
1882*11729SWang.Lin@Sun.COM {
1883*11729SWang.Lin@Sun.COM uint32_t perline;
1884*11729SWang.Lin@Sun.COM while (len) {
1885*11729SWang.Lin@Sun.COM perline = (len < 16) ? len : 16;
1886*11729SWang.Lin@Sun.COM arn_dump_line(p, perline, isaddress, group);
1887*11729SWang.Lin@Sun.COM len -= perline;
1888*11729SWang.Lin@Sun.COM p += perline;
1889*11729SWang.Lin@Sun.COM }
1890*11729SWang.Lin@Sun.COM }
1891*11729SWang.Lin@Sun.COM
18929999SWang.Lin@Sun.COM /*
18939999SWang.Lin@Sun.COM * The input parameter mp has following assumption:
18949999SWang.Lin@Sun.COM * For data packets, GLDv3 mac_wifi plugin allocates and fills the
18959999SWang.Lin@Sun.COM * ieee80211 header. For management packets, net80211 allocates and
18969999SWang.Lin@Sun.COM * fills the ieee80211 header. In both cases, enough spaces in the
18979999SWang.Lin@Sun.COM * header are left for encryption option.
18989999SWang.Lin@Sun.COM */
18999999SWang.Lin@Sun.COM static int32_t
arn_tx_start(struct arn_softc * sc,struct ieee80211_node * in,struct ath_buf * bf,mblk_t * mp)19009999SWang.Lin@Sun.COM arn_tx_start(struct arn_softc *sc, struct ieee80211_node *in,
19019999SWang.Lin@Sun.COM struct ath_buf *bf, mblk_t *mp)
19029999SWang.Lin@Sun.COM {
19039999SWang.Lin@Sun.COM ieee80211com_t *ic = (ieee80211com_t *)sc;
1904*11729SWang.Lin@Sun.COM struct ieee80211_frame *wh = (struct ieee80211_frame *)mp->b_rptr;
19059999SWang.Lin@Sun.COM struct ath_hal *ah = sc->sc_ah;
1906*11729SWang.Lin@Sun.COM struct ath_node *an;
19079999SWang.Lin@Sun.COM struct ath_desc *ds;
19089999SWang.Lin@Sun.COM struct ath_txq *txq;
1909*11729SWang.Lin@Sun.COM struct ath_rate_table *rt;
19109999SWang.Lin@Sun.COM enum ath9k_pkt_type atype;
1911*11729SWang.Lin@Sun.COM boolean_t shortPreamble, is_padding = B_FALSE;
1912*11729SWang.Lin@Sun.COM uint32_t subtype, keytype = ATH9K_KEY_TYPE_CLEAR;
1913*11729SWang.Lin@Sun.COM int32_t keyix, iswep, hdrlen, pktlen, mblen, mbslen;
19149999SWang.Lin@Sun.COM caddr_t dest;
19159999SWang.Lin@Sun.COM
19169999SWang.Lin@Sun.COM /*
19179999SWang.Lin@Sun.COM * CRC are added by H/W, not encaped by driver,
19189999SWang.Lin@Sun.COM * but we must count it in pkt length.
19199999SWang.Lin@Sun.COM */
19209999SWang.Lin@Sun.COM pktlen = IEEE80211_CRC_LEN;
19219999SWang.Lin@Sun.COM iswep = wh->i_fc[1] & IEEE80211_FC1_WEP;
19229999SWang.Lin@Sun.COM keyix = ATH9K_TXKEYIX_INVALID;
1923*11729SWang.Lin@Sun.COM hdrlen = ieee80211_hdrspace(ic, mp->b_rptr);
1924*11729SWang.Lin@Sun.COM if (hdrlen == 28)
1925*11729SWang.Lin@Sun.COM is_padding = B_TRUE;
1926*11729SWang.Lin@Sun.COM
19279999SWang.Lin@Sun.COM if (iswep != 0) {
19289999SWang.Lin@Sun.COM const struct ieee80211_cipher *cip;
19299999SWang.Lin@Sun.COM struct ieee80211_key *k;
19309999SWang.Lin@Sun.COM
19319999SWang.Lin@Sun.COM /*
19329999SWang.Lin@Sun.COM * Construct the 802.11 header+trailer for an encrypted
19339999SWang.Lin@Sun.COM * frame. The only reason this can fail is because of an
19349999SWang.Lin@Sun.COM * unknown or unsupported cipher/key type.
19359999SWang.Lin@Sun.COM */
19369999SWang.Lin@Sun.COM k = ieee80211_crypto_encap(ic, mp);
19379999SWang.Lin@Sun.COM if (k == NULL) {
19389999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_XMIT, "arn: arn_tx_start "
19399999SWang.Lin@Sun.COM "crypto_encap failed\n"));
19409999SWang.Lin@Sun.COM /*
19419999SWang.Lin@Sun.COM * This can happen when the key is yanked after the
19429999SWang.Lin@Sun.COM * frame was queued. Just discard the frame; the
19439999SWang.Lin@Sun.COM * 802.11 layer counts failures and provides
19449999SWang.Lin@Sun.COM * debugging/diagnostics.
19459999SWang.Lin@Sun.COM */
19469999SWang.Lin@Sun.COM return (EIO);
19479999SWang.Lin@Sun.COM }
19489999SWang.Lin@Sun.COM cip = k->wk_cipher;
19499999SWang.Lin@Sun.COM
19509999SWang.Lin@Sun.COM keytype = arn_tx_get_keytype(cip);
19519999SWang.Lin@Sun.COM
19529999SWang.Lin@Sun.COM /*
19539999SWang.Lin@Sun.COM * Adjust the packet + header lengths for the crypto
19549999SWang.Lin@Sun.COM * additions and calculate the h/w key index. When
19559999SWang.Lin@Sun.COM * a s/w mic is done the frame will have had any mic
19569999SWang.Lin@Sun.COM * added to it prior to entry so m0->m_pkthdr.len above will
19579999SWang.Lin@Sun.COM * account for it. Otherwise we need to add it to the
19589999SWang.Lin@Sun.COM * packet length.
19599999SWang.Lin@Sun.COM */
19609999SWang.Lin@Sun.COM hdrlen += cip->ic_header;
19619999SWang.Lin@Sun.COM pktlen += cip->ic_trailer;
19629999SWang.Lin@Sun.COM if ((k->wk_flags & IEEE80211_KEY_SWMIC) == 0)
19639999SWang.Lin@Sun.COM pktlen += cip->ic_miclen;
19649999SWang.Lin@Sun.COM
19659999SWang.Lin@Sun.COM keyix = k->wk_keyix;
19669999SWang.Lin@Sun.COM
19679999SWang.Lin@Sun.COM /* packet header may have moved, reset our local pointer */
19689999SWang.Lin@Sun.COM wh = (struct ieee80211_frame *)mp->b_rptr;
19699999SWang.Lin@Sun.COM }
19709999SWang.Lin@Sun.COM
19719999SWang.Lin@Sun.COM dest = bf->bf_dma.mem_va;
19729999SWang.Lin@Sun.COM for (; mp != NULL; mp = mp->b_cont) {
19739999SWang.Lin@Sun.COM mblen = MBLKL(mp);
19749999SWang.Lin@Sun.COM bcopy(mp->b_rptr, dest, mblen);
19759999SWang.Lin@Sun.COM dest += mblen;
19769999SWang.Lin@Sun.COM }
19779999SWang.Lin@Sun.COM mbslen = (uintptr_t)dest - (uintptr_t)bf->bf_dma.mem_va;
19789999SWang.Lin@Sun.COM pktlen += mbslen;
1979*11729SWang.Lin@Sun.COM if (is_padding && (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
1980*11729SWang.Lin@Sun.COM IEEE80211_FC0_TYPE_DATA)
1981*11729SWang.Lin@Sun.COM pktlen -= 2; /* real pkg len */
19829999SWang.Lin@Sun.COM
1983*11729SWang.Lin@Sun.COM /* buf setup */
1984*11729SWang.Lin@Sun.COM ath_tx_setup_buffer(sc, bf, in, wh, pktlen, keytype);
19859999SWang.Lin@Sun.COM
19869999SWang.Lin@Sun.COM /* setup descriptors */
19879999SWang.Lin@Sun.COM ds = bf->bf_desc;
19889999SWang.Lin@Sun.COM rt = sc->sc_currates;
19899999SWang.Lin@Sun.COM ASSERT(rt != NULL);
19909999SWang.Lin@Sun.COM
1991*11729SWang.Lin@Sun.COM arn_get_rate(sc, bf, wh);
19929999SWang.Lin@Sun.COM an = (struct ath_node *)(in);
19939999SWang.Lin@Sun.COM
19949999SWang.Lin@Sun.COM /*
19959999SWang.Lin@Sun.COM * Calculate Atheros packet type from IEEE80211 packet header
19969999SWang.Lin@Sun.COM * and setup for rate calculations.
19979999SWang.Lin@Sun.COM */
19989999SWang.Lin@Sun.COM switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) {
19999999SWang.Lin@Sun.COM case IEEE80211_FC0_TYPE_MGT:
20009999SWang.Lin@Sun.COM subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
20019999SWang.Lin@Sun.COM if (subtype == IEEE80211_FC0_SUBTYPE_BEACON)
20029999SWang.Lin@Sun.COM atype = ATH9K_PKT_TYPE_BEACON;
20039999SWang.Lin@Sun.COM else if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
20049999SWang.Lin@Sun.COM atype = ATH9K_PKT_TYPE_PROBE_RESP;
20059999SWang.Lin@Sun.COM else if (subtype == IEEE80211_FC0_SUBTYPE_ATIM)
20069999SWang.Lin@Sun.COM atype = ATH9K_PKT_TYPE_ATIM;
20079999SWang.Lin@Sun.COM else
20089999SWang.Lin@Sun.COM atype = ATH9K_PKT_TYPE_NORMAL;
2009*11729SWang.Lin@Sun.COM
20109999SWang.Lin@Sun.COM /* force all ctl frames to highest queue */
20119999SWang.Lin@Sun.COM txq = &sc->sc_txq[arn_get_hal_qnum(WME_AC_VO, sc)];
20129999SWang.Lin@Sun.COM break;
20139999SWang.Lin@Sun.COM case IEEE80211_FC0_TYPE_CTL:
20149999SWang.Lin@Sun.COM atype = ATH9K_PKT_TYPE_PSPOLL;
20159999SWang.Lin@Sun.COM subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
2016*11729SWang.Lin@Sun.COM
20179999SWang.Lin@Sun.COM /* force all ctl frames to highest queue */
20189999SWang.Lin@Sun.COM txq = &sc->sc_txq[arn_get_hal_qnum(WME_AC_VO, sc)];
20199999SWang.Lin@Sun.COM break;
20209999SWang.Lin@Sun.COM case IEEE80211_FC0_TYPE_DATA:
2021*11729SWang.Lin@Sun.COM // arn_dump_pkg((unsigned char *)bf->bf_dma.mem_va,
2022*11729SWang.Lin@Sun.COM // pktlen, 1, 1);
20239999SWang.Lin@Sun.COM atype = ATH9K_PKT_TYPE_NORMAL;
2024*11729SWang.Lin@Sun.COM
20259999SWang.Lin@Sun.COM /* Always use background queue */
2026*11729SWang.Lin@Sun.COM txq = &sc->sc_txq[arn_get_hal_qnum(WME_AC_BE, sc)];
20279999SWang.Lin@Sun.COM break;
20289999SWang.Lin@Sun.COM default:
20299999SWang.Lin@Sun.COM /* Unknown 802.11 frame */
20309999SWang.Lin@Sun.COM sc->sc_stats.ast_tx_invalid++;
20319999SWang.Lin@Sun.COM return (1);
20329999SWang.Lin@Sun.COM }
20339999SWang.Lin@Sun.COM
20349999SWang.Lin@Sun.COM /* setup descriptor */
20359999SWang.Lin@Sun.COM ds->ds_link = 0;
20369999SWang.Lin@Sun.COM ds->ds_data = bf->bf_dma.cookie.dmac_address;
20379999SWang.Lin@Sun.COM
20389999SWang.Lin@Sun.COM /*
20399999SWang.Lin@Sun.COM * Formulate first tx descriptor with tx controls.
20409999SWang.Lin@Sun.COM */
20419999SWang.Lin@Sun.COM ath9k_hw_set11n_txdesc(ah, ds,
2042*11729SWang.Lin@Sun.COM (pktlen), /* packet length */
20439999SWang.Lin@Sun.COM atype, /* Atheros packet type */
20449999SWang.Lin@Sun.COM MAX_RATE_POWER /* MAX_RATE_POWER */,
20459999SWang.Lin@Sun.COM keyix /* ATH9K_TXKEYIX_INVALID */,
20469999SWang.Lin@Sun.COM keytype /* ATH9K_KEY_TYPE_CLEAR */,
2047*11729SWang.Lin@Sun.COM bf->bf_flags /* flags */);
20489999SWang.Lin@Sun.COM
20499999SWang.Lin@Sun.COM /* LINTED E_BAD_PTR_CAST_ALIGN */
20509999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_XMIT, "arn: arn_tx_start(): to %s totlen=%d "
20519999SWang.Lin@Sun.COM "an->an_tx_rate1sp=%d tx_rate2sp=%d tx_rate3sp=%d "
2052*11729SWang.Lin@Sun.COM "qnum=%d sht=%d dur = %d\n",
20539999SWang.Lin@Sun.COM ieee80211_macaddr_sprintf(wh->i_addr1), mbslen, an->an_tx_rate1sp,
20549999SWang.Lin@Sun.COM an->an_tx_rate2sp, an->an_tx_rate3sp,
2055*11729SWang.Lin@Sun.COM txq->axq_qnum, shortPreamble, *(uint16_t *)wh->i_dur));
20569999SWang.Lin@Sun.COM
20579999SWang.Lin@Sun.COM (void) ath9k_hw_filltxdesc(ah, ds,
20589999SWang.Lin@Sun.COM mbslen, /* segment length */
20599999SWang.Lin@Sun.COM B_TRUE, /* first segment */
20609999SWang.Lin@Sun.COM B_TRUE, /* last segment */
20619999SWang.Lin@Sun.COM ds); /* first descriptor */
20629999SWang.Lin@Sun.COM
20639999SWang.Lin@Sun.COM /* set rate related fields in tx descriptor */
2064*11729SWang.Lin@Sun.COM ath_buf_set_rate(sc, bf, wh);
20659999SWang.Lin@Sun.COM
20669999SWang.Lin@Sun.COM ARN_DMA_SYNC(bf->bf_dma, DDI_DMA_SYNC_FORDEV);
20679999SWang.Lin@Sun.COM
20689999SWang.Lin@Sun.COM mutex_enter(&txq->axq_lock);
20699999SWang.Lin@Sun.COM list_insert_tail(&txq->axq_list, bf);
20709999SWang.Lin@Sun.COM if (txq->axq_link == NULL) {
20719999SWang.Lin@Sun.COM (void) ath9k_hw_puttxbuf(ah, txq->axq_qnum, bf->bf_daddr);
20729999SWang.Lin@Sun.COM } else {
20739999SWang.Lin@Sun.COM *txq->axq_link = bf->bf_daddr;
20749999SWang.Lin@Sun.COM }
20759999SWang.Lin@Sun.COM txq->axq_link = &ds->ds_link;
20769999SWang.Lin@Sun.COM mutex_exit(&txq->axq_lock);
20779999SWang.Lin@Sun.COM
2078*11729SWang.Lin@Sun.COM // arn_dump_pkg((unsigned char *)bf->bf_dma.mem_va, pktlen, 1, 1);
2079*11729SWang.Lin@Sun.COM
20809999SWang.Lin@Sun.COM (void) ath9k_hw_txstart(ah, txq->axq_qnum);
20819999SWang.Lin@Sun.COM
20829999SWang.Lin@Sun.COM ic->ic_stats.is_tx_frags++;
20839999SWang.Lin@Sun.COM ic->ic_stats.is_tx_bytes += pktlen;
20849999SWang.Lin@Sun.COM
20859999SWang.Lin@Sun.COM return (0);
20869999SWang.Lin@Sun.COM }
20879999SWang.Lin@Sun.COM
20889999SWang.Lin@Sun.COM /*
20899999SWang.Lin@Sun.COM * Transmit a management frame.
20909999SWang.Lin@Sun.COM * Note that management frames come directly from the 802.11 layer
20919999SWang.Lin@Sun.COM * and do not honor the send queue flow control.
20929999SWang.Lin@Sun.COM */
20939999SWang.Lin@Sun.COM /* Upon failure caller should free mp */
20949999SWang.Lin@Sun.COM int
arn_tx(ieee80211com_t * ic,mblk_t * mp,uint8_t type)20959999SWang.Lin@Sun.COM arn_tx(ieee80211com_t *ic, mblk_t *mp, uint8_t type)
20969999SWang.Lin@Sun.COM {
20979999SWang.Lin@Sun.COM struct arn_softc *sc = (struct arn_softc *)ic;
20989999SWang.Lin@Sun.COM struct ath_hal *ah = sc->sc_ah;
20999999SWang.Lin@Sun.COM struct ieee80211_node *in = NULL;
21009999SWang.Lin@Sun.COM struct ath_buf *bf = NULL;
21019999SWang.Lin@Sun.COM struct ieee80211_frame *wh;
21029999SWang.Lin@Sun.COM int error = 0;
21039999SWang.Lin@Sun.COM
21049999SWang.Lin@Sun.COM ASSERT(mp->b_next == NULL);
21059999SWang.Lin@Sun.COM /* should check later */
21069999SWang.Lin@Sun.COM if (sc->sc_flags & SC_OP_INVALID) {
21079999SWang.Lin@Sun.COM if ((type & IEEE80211_FC0_TYPE_MASK) !=
21089999SWang.Lin@Sun.COM IEEE80211_FC0_TYPE_DATA) {
21099999SWang.Lin@Sun.COM freemsg(mp);
21109999SWang.Lin@Sun.COM }
21119999SWang.Lin@Sun.COM return (ENXIO);
21129999SWang.Lin@Sun.COM }
21139999SWang.Lin@Sun.COM
21149999SWang.Lin@Sun.COM /* Grab a TX buffer */
2115*11729SWang.Lin@Sun.COM bf = arn_tx_get_buffer(sc);
21169999SWang.Lin@Sun.COM if (bf == NULL) {
21179999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_XMIT, "arn: arn_tx(): discard, "
21189999SWang.Lin@Sun.COM "no xmit buf\n"));
21199999SWang.Lin@Sun.COM ic->ic_stats.is_tx_nobuf++;
21209999SWang.Lin@Sun.COM if ((type & IEEE80211_FC0_TYPE_MASK) ==
21219999SWang.Lin@Sun.COM IEEE80211_FC0_TYPE_DATA) {
21229999SWang.Lin@Sun.COM sc->sc_stats.ast_tx_nobuf++;
21239999SWang.Lin@Sun.COM mutex_enter(&sc->sc_resched_lock);
21249999SWang.Lin@Sun.COM sc->sc_resched_needed = B_TRUE;
21259999SWang.Lin@Sun.COM mutex_exit(&sc->sc_resched_lock);
21269999SWang.Lin@Sun.COM } else {
21279999SWang.Lin@Sun.COM sc->sc_stats.ast_tx_nobufmgt++;
21289999SWang.Lin@Sun.COM freemsg(mp);
21299999SWang.Lin@Sun.COM }
21309999SWang.Lin@Sun.COM return (ENOMEM);
21319999SWang.Lin@Sun.COM }
21329999SWang.Lin@Sun.COM
21339999SWang.Lin@Sun.COM wh = (struct ieee80211_frame *)mp->b_rptr;
21349999SWang.Lin@Sun.COM
21359999SWang.Lin@Sun.COM /* Locate node */
21369999SWang.Lin@Sun.COM in = ieee80211_find_txnode(ic, wh->i_addr1);
21379999SWang.Lin@Sun.COM if (in == NULL) {
21389999SWang.Lin@Sun.COM error = EIO;
21399999SWang.Lin@Sun.COM goto bad;
21409999SWang.Lin@Sun.COM }
21419999SWang.Lin@Sun.COM
21429999SWang.Lin@Sun.COM in->in_inact = 0;
21439999SWang.Lin@Sun.COM switch (type & IEEE80211_FC0_TYPE_MASK) {
21449999SWang.Lin@Sun.COM case IEEE80211_FC0_TYPE_DATA:
21459999SWang.Lin@Sun.COM (void) ieee80211_encap(ic, mp, in);
21469999SWang.Lin@Sun.COM break;
21479999SWang.Lin@Sun.COM default:
21489999SWang.Lin@Sun.COM if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
21499999SWang.Lin@Sun.COM IEEE80211_FC0_SUBTYPE_PROBE_RESP) {
21509999SWang.Lin@Sun.COM /* fill time stamp */
21519999SWang.Lin@Sun.COM uint64_t tsf;
21529999SWang.Lin@Sun.COM uint32_t *tstamp;
21539999SWang.Lin@Sun.COM
21549999SWang.Lin@Sun.COM tsf = ath9k_hw_gettsf64(ah);
21559999SWang.Lin@Sun.COM /* adjust 100us delay to xmit */
21569999SWang.Lin@Sun.COM tsf += 100;
21579999SWang.Lin@Sun.COM /* LINTED E_BAD_PTR_CAST_ALIGN */
21589999SWang.Lin@Sun.COM tstamp = (uint32_t *)&wh[1];
21599999SWang.Lin@Sun.COM tstamp[0] = LE_32(tsf & 0xffffffff);
21609999SWang.Lin@Sun.COM tstamp[1] = LE_32(tsf >> 32);
21619999SWang.Lin@Sun.COM }
21629999SWang.Lin@Sun.COM sc->sc_stats.ast_tx_mgmt++;
21639999SWang.Lin@Sun.COM break;
21649999SWang.Lin@Sun.COM }
21659999SWang.Lin@Sun.COM
21669999SWang.Lin@Sun.COM error = arn_tx_start(sc, in, bf, mp);
21679999SWang.Lin@Sun.COM
21689999SWang.Lin@Sun.COM if (error != 0) {
21699999SWang.Lin@Sun.COM bad:
21709999SWang.Lin@Sun.COM ic->ic_stats.is_tx_failed++;
21719999SWang.Lin@Sun.COM if (bf != NULL) {
21729999SWang.Lin@Sun.COM mutex_enter(&sc->sc_txbuflock);
21739999SWang.Lin@Sun.COM list_insert_tail(&sc->sc_txbuf_list, bf);
21749999SWang.Lin@Sun.COM mutex_exit(&sc->sc_txbuflock);
21759999SWang.Lin@Sun.COM }
21769999SWang.Lin@Sun.COM }
21779999SWang.Lin@Sun.COM if (in != NULL)
21789999SWang.Lin@Sun.COM ieee80211_free_node(in);
21799999SWang.Lin@Sun.COM if ((type & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_DATA ||
21809999SWang.Lin@Sun.COM error == 0) {
21819999SWang.Lin@Sun.COM freemsg(mp);
21829999SWang.Lin@Sun.COM }
21839999SWang.Lin@Sun.COM
21849999SWang.Lin@Sun.COM return (error);
21859999SWang.Lin@Sun.COM }
21869999SWang.Lin@Sun.COM
21879999SWang.Lin@Sun.COM static void
arn_printtxbuf(struct ath_buf * bf,int done)21889999SWang.Lin@Sun.COM arn_printtxbuf(struct ath_buf *bf, int done)
21899999SWang.Lin@Sun.COM {
21909999SWang.Lin@Sun.COM struct ath_desc *ds = bf->bf_desc;
21919999SWang.Lin@Sun.COM const struct ath_tx_status *ts = &ds->ds_txstat;
21929999SWang.Lin@Sun.COM
21939999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_XMIT, "arn: T(%p %p) %08x %08x %08x %08x %08x"
21949999SWang.Lin@Sun.COM " %08x %08x %08x %c\n",
21959999SWang.Lin@Sun.COM ds, bf->bf_daddr,
21969999SWang.Lin@Sun.COM ds->ds_link, ds->ds_data,
21979999SWang.Lin@Sun.COM ds->ds_ctl0, ds->ds_ctl1,
21989999SWang.Lin@Sun.COM ds->ds_hw[0], ds->ds_hw[1], ds->ds_hw[2], ds->ds_hw[3],
21999999SWang.Lin@Sun.COM !done ? ' ' : (ts->ts_status == 0) ? '*' : '!'));
22009999SWang.Lin@Sun.COM }
22019999SWang.Lin@Sun.COM
2202*11729SWang.Lin@Sun.COM /* ARGSUSED */
2203*11729SWang.Lin@Sun.COM static void
ath_tx_rc_status(struct ath_buf * bf,struct ath_desc * ds,int nbad,int txok,boolean_t update_rc)2204*11729SWang.Lin@Sun.COM ath_tx_rc_status(struct ath_buf *bf,
2205*11729SWang.Lin@Sun.COM struct ath_desc *ds,
2206*11729SWang.Lin@Sun.COM int nbad,
2207*11729SWang.Lin@Sun.COM int txok,
2208*11729SWang.Lin@Sun.COM boolean_t update_rc)
2209*11729SWang.Lin@Sun.COM {
2210*11729SWang.Lin@Sun.COM struct ath_tx_info_priv *tx_info_priv =
2211*11729SWang.Lin@Sun.COM (struct ath_tx_info_priv *)&bf->tx_info_priv;
2212*11729SWang.Lin@Sun.COM
2213*11729SWang.Lin@Sun.COM tx_info_priv->update_rc = B_FALSE;
2214*11729SWang.Lin@Sun.COM
2215*11729SWang.Lin@Sun.COM if ((ds->ds_txstat.ts_status & ATH9K_TXERR_FILT) == 0 &&
2216*11729SWang.Lin@Sun.COM (bf->bf_flags & ATH9K_TXDESC_NOACK) == 0) {
2217*11729SWang.Lin@Sun.COM if (bf_isdata(bf)) {
2218*11729SWang.Lin@Sun.COM (void) memcpy(&tx_info_priv->tx, &ds->ds_txstat,
2219*11729SWang.Lin@Sun.COM sizeof (tx_info_priv->tx));
2220*11729SWang.Lin@Sun.COM tx_info_priv->n_frames = bf->bf_nframes;
2221*11729SWang.Lin@Sun.COM tx_info_priv->n_bad_frames = nbad;
2222*11729SWang.Lin@Sun.COM tx_info_priv->update_rc = B_TRUE;
2223*11729SWang.Lin@Sun.COM }
2224*11729SWang.Lin@Sun.COM }
2225*11729SWang.Lin@Sun.COM }
2226*11729SWang.Lin@Sun.COM
22279999SWang.Lin@Sun.COM /* Process completed xmit descriptors from the specified queue */
22289999SWang.Lin@Sun.COM static int
arn_tx_processq(struct arn_softc * sc,struct ath_txq * txq)22299999SWang.Lin@Sun.COM arn_tx_processq(struct arn_softc *sc, struct ath_txq *txq)
22309999SWang.Lin@Sun.COM {
22319999SWang.Lin@Sun.COM ieee80211com_t *ic = (ieee80211com_t *)sc;
22329999SWang.Lin@Sun.COM struct ath_hal *ah = sc->sc_ah;
22339999SWang.Lin@Sun.COM struct ath_buf *bf;
22349999SWang.Lin@Sun.COM struct ath_desc *ds;
22359999SWang.Lin@Sun.COM struct ieee80211_node *in;
2236*11729SWang.Lin@Sun.COM struct ath_tx_status *ts;
2237*11729SWang.Lin@Sun.COM struct ath_node *an;
22389999SWang.Lin@Sun.COM int32_t sr, lr, nacked = 0;
2239*11729SWang.Lin@Sun.COM int txok, nbad = 0;
22409999SWang.Lin@Sun.COM int status;
22419999SWang.Lin@Sun.COM
22429999SWang.Lin@Sun.COM for (;;) {
22439999SWang.Lin@Sun.COM mutex_enter(&txq->axq_lock);
22449999SWang.Lin@Sun.COM bf = list_head(&txq->axq_list);
22459999SWang.Lin@Sun.COM if (bf == NULL) {
22469999SWang.Lin@Sun.COM txq->axq_link = NULL;
2247*11729SWang.Lin@Sun.COM /* txq->axq_linkbuf = NULL; */
22489999SWang.Lin@Sun.COM mutex_exit(&txq->axq_lock);
22499999SWang.Lin@Sun.COM break;
22509999SWang.Lin@Sun.COM }
22519999SWang.Lin@Sun.COM ds = bf->bf_desc; /* last decriptor */
22529999SWang.Lin@Sun.COM ts = &ds->ds_txstat;
22539999SWang.Lin@Sun.COM status = ath9k_hw_txprocdesc(ah, ds);
22549999SWang.Lin@Sun.COM
22559999SWang.Lin@Sun.COM #ifdef DEBUG
22569999SWang.Lin@Sun.COM arn_printtxbuf(bf, status == 0);
22579999SWang.Lin@Sun.COM #endif
22589999SWang.Lin@Sun.COM
22599999SWang.Lin@Sun.COM if (status == EINPROGRESS) {
22609999SWang.Lin@Sun.COM mutex_exit(&txq->axq_lock);
22619999SWang.Lin@Sun.COM break;
22629999SWang.Lin@Sun.COM }
22639999SWang.Lin@Sun.COM list_remove(&txq->axq_list, bf);
22649999SWang.Lin@Sun.COM mutex_exit(&txq->axq_lock);
22659999SWang.Lin@Sun.COM in = bf->bf_in;
22669999SWang.Lin@Sun.COM if (in != NULL) {
22679999SWang.Lin@Sun.COM an = ATH_NODE(in);
22689999SWang.Lin@Sun.COM /* Successful transmition */
22699999SWang.Lin@Sun.COM if (ts->ts_status == 0) {
22709999SWang.Lin@Sun.COM an->an_tx_ok++;
22719999SWang.Lin@Sun.COM an->an_tx_antenna = ts->ts_antenna;
22729999SWang.Lin@Sun.COM sc->sc_stats.ast_tx_rssidelta =
22739999SWang.Lin@Sun.COM ts->ts_rssi - sc->sc_stats.ast_tx_rssi;
22749999SWang.Lin@Sun.COM sc->sc_stats.ast_tx_rssi = ts->ts_rssi;
22759999SWang.Lin@Sun.COM } else {
22769999SWang.Lin@Sun.COM an->an_tx_err++;
22779999SWang.Lin@Sun.COM if (ts->ts_status & ATH9K_TXERR_XRETRY) {
22789999SWang.Lin@Sun.COM sc->sc_stats.ast_tx_xretries++;
22799999SWang.Lin@Sun.COM }
22809999SWang.Lin@Sun.COM if (ts->ts_status & ATH9K_TXERR_FIFO) {
22819999SWang.Lin@Sun.COM sc->sc_stats.ast_tx_fifoerr++;
22829999SWang.Lin@Sun.COM }
22839999SWang.Lin@Sun.COM if (ts->ts_status & ATH9K_TXERR_FILT) {
22849999SWang.Lin@Sun.COM sc->sc_stats.ast_tx_filtered++;
22859999SWang.Lin@Sun.COM }
22869999SWang.Lin@Sun.COM an->an_tx_antenna = 0; /* invalidate */
22879999SWang.Lin@Sun.COM }
22889999SWang.Lin@Sun.COM sr = ts->ts_shortretry;
22899999SWang.Lin@Sun.COM lr = ts->ts_longretry;
22909999SWang.Lin@Sun.COM sc->sc_stats.ast_tx_shortretry += sr;
22919999SWang.Lin@Sun.COM sc->sc_stats.ast_tx_longretry += lr;
22929999SWang.Lin@Sun.COM /*
22939999SWang.Lin@Sun.COM * Hand the descriptor to the rate control algorithm.
22949999SWang.Lin@Sun.COM */
22959999SWang.Lin@Sun.COM if ((ts->ts_status & ATH9K_TXERR_FILT) == 0 &&
22969999SWang.Lin@Sun.COM (bf->bf_flags & ATH9K_TXDESC_NOACK) == 0) {
22979999SWang.Lin@Sun.COM /*
22989999SWang.Lin@Sun.COM * If frame was ack'd update the last rx time
22999999SWang.Lin@Sun.COM * used to workaround phantom bmiss interrupts.
23009999SWang.Lin@Sun.COM */
23019999SWang.Lin@Sun.COM if (ts->ts_status == 0) {
23029999SWang.Lin@Sun.COM nacked++;
23039999SWang.Lin@Sun.COM an->an_tx_ok++;
23049999SWang.Lin@Sun.COM } else {
23059999SWang.Lin@Sun.COM an->an_tx_err++;
23069999SWang.Lin@Sun.COM }
23079999SWang.Lin@Sun.COM an->an_tx_retr += sr + lr;
23089999SWang.Lin@Sun.COM }
23099999SWang.Lin@Sun.COM }
2310*11729SWang.Lin@Sun.COM
2311*11729SWang.Lin@Sun.COM txok = (ds->ds_txstat.ts_status == 0);
2312*11729SWang.Lin@Sun.COM if (!bf_isampdu(bf)) {
2313*11729SWang.Lin@Sun.COM /*
2314*11729SWang.Lin@Sun.COM * This frame is sent out as a single frame.
2315*11729SWang.Lin@Sun.COM * Use hardware retry status for this frame.
2316*11729SWang.Lin@Sun.COM */
2317*11729SWang.Lin@Sun.COM bf->bf_retries = ds->ds_txstat.ts_longretry;
2318*11729SWang.Lin@Sun.COM if (ds->ds_txstat.ts_status & ATH9K_TXERR_XRETRY)
2319*11729SWang.Lin@Sun.COM bf->bf_state.bf_type |= BUF_XRETRY;
2320*11729SWang.Lin@Sun.COM nbad = 0;
2321*11729SWang.Lin@Sun.COM }
2322*11729SWang.Lin@Sun.COM ath_tx_rc_status(bf, ds, nbad, B_TRUE, txok);
2323*11729SWang.Lin@Sun.COM
2324*11729SWang.Lin@Sun.COM ath_tx_complete_buf(sc, bf, txok, 0);
2325*11729SWang.Lin@Sun.COM
2326*11729SWang.Lin@Sun.COM // arn_dump_pkg((unsigned char *)bf->bf_dma.mem_va,
2327*11729SWang.Lin@Sun.COM // bf->bf_frmlen, 1, 1);
2328*11729SWang.Lin@Sun.COM
23299999SWang.Lin@Sun.COM bf->bf_in = NULL;
23309999SWang.Lin@Sun.COM mutex_enter(&sc->sc_txbuflock);
23319999SWang.Lin@Sun.COM list_insert_tail(&sc->sc_txbuf_list, bf);
23329999SWang.Lin@Sun.COM mutex_exit(&sc->sc_txbuflock);
23339999SWang.Lin@Sun.COM
23349999SWang.Lin@Sun.COM /*
23359999SWang.Lin@Sun.COM * Reschedule stalled outbound packets
23369999SWang.Lin@Sun.COM */
23379999SWang.Lin@Sun.COM mutex_enter(&sc->sc_resched_lock);
23389999SWang.Lin@Sun.COM if (sc->sc_resched_needed) {
23399999SWang.Lin@Sun.COM sc->sc_resched_needed = B_FALSE;
23409999SWang.Lin@Sun.COM mac_tx_update(ic->ic_mach);
23419999SWang.Lin@Sun.COM }
23429999SWang.Lin@Sun.COM mutex_exit(&sc->sc_resched_lock);
23439999SWang.Lin@Sun.COM }
23449999SWang.Lin@Sun.COM
23459999SWang.Lin@Sun.COM return (nacked);
23469999SWang.Lin@Sun.COM }
23479999SWang.Lin@Sun.COM
23489999SWang.Lin@Sun.COM static void
arn_tx_handler(struct arn_softc * sc)23499999SWang.Lin@Sun.COM arn_tx_handler(struct arn_softc *sc)
23509999SWang.Lin@Sun.COM {
23519999SWang.Lin@Sun.COM int i;
23529999SWang.Lin@Sun.COM int nacked = 0;
23539999SWang.Lin@Sun.COM uint32_t qcumask = ((1 << ATH9K_NUM_TX_QUEUES) - 1);
23549999SWang.Lin@Sun.COM ath9k_hw_gettxintrtxqs(sc->sc_ah, &qcumask);
23559999SWang.Lin@Sun.COM
23569999SWang.Lin@Sun.COM /*
23579999SWang.Lin@Sun.COM * Process each active queue.
23589999SWang.Lin@Sun.COM */
23599999SWang.Lin@Sun.COM for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
23609999SWang.Lin@Sun.COM if (ARN_TXQ_SETUP(sc, i) && (qcumask & (1 << i))) {
23619999SWang.Lin@Sun.COM nacked += arn_tx_processq(sc, &sc->sc_txq[i]);
23629999SWang.Lin@Sun.COM }
23639999SWang.Lin@Sun.COM }
23649999SWang.Lin@Sun.COM
23659999SWang.Lin@Sun.COM if (nacked)
23669999SWang.Lin@Sun.COM sc->sc_lastrx = ath9k_hw_gettsf64(sc->sc_ah);
23679999SWang.Lin@Sun.COM }
23689999SWang.Lin@Sun.COM
23699999SWang.Lin@Sun.COM /* Deferred processing of transmit interrupt */
23709999SWang.Lin@Sun.COM
23719999SWang.Lin@Sun.COM void
arn_tx_int_proc(void * arg)23729999SWang.Lin@Sun.COM arn_tx_int_proc(void *arg)
23739999SWang.Lin@Sun.COM {
23749999SWang.Lin@Sun.COM struct arn_softc *sc = arg;
23759999SWang.Lin@Sun.COM arn_tx_handler(sc);
23769999SWang.Lin@Sun.COM }
2377*11729SWang.Lin@Sun.COM
2378*11729SWang.Lin@Sun.COM /* Node init & cleanup functions */
2379*11729SWang.Lin@Sun.COM
2380*11729SWang.Lin@Sun.COM #ifdef ARN_TX_AGGREGATION
2381*11729SWang.Lin@Sun.COM void
arn_tx_node_init(struct arn_softc * sc,struct ath_node * an)2382*11729SWang.Lin@Sun.COM arn_tx_node_init(struct arn_softc *sc, struct ath_node *an)
2383*11729SWang.Lin@Sun.COM {
2384*11729SWang.Lin@Sun.COM struct ath_atx_tid *tid;
2385*11729SWang.Lin@Sun.COM struct ath_atx_ac *ac;
2386*11729SWang.Lin@Sun.COM int tidno, acno;
2387*11729SWang.Lin@Sun.COM
2388*11729SWang.Lin@Sun.COM for (tidno = 0, tid = &an->tid[tidno]; tidno < WME_NUM_TID;
2389*11729SWang.Lin@Sun.COM tidno++, tid++) {
2390*11729SWang.Lin@Sun.COM tid->an = an;
2391*11729SWang.Lin@Sun.COM tid->tidno = tidno;
2392*11729SWang.Lin@Sun.COM tid->seq_start = tid->seq_next = 0;
2393*11729SWang.Lin@Sun.COM tid->baw_size = WME_MAX_BA;
2394*11729SWang.Lin@Sun.COM tid->baw_head = tid->baw_tail = 0;
2395*11729SWang.Lin@Sun.COM tid->sched = B_FALSE;
2396*11729SWang.Lin@Sun.COM tid->paused = B_FALSE;
2397*11729SWang.Lin@Sun.COM tid->state &= ~AGGR_CLEANUP;
2398*11729SWang.Lin@Sun.COM list_create(&tid->buf_q, sizeof (struct ath_buf),
2399*11729SWang.Lin@Sun.COM offsetof(struct ath_buf, bf_node));
2400*11729SWang.Lin@Sun.COM acno = TID_TO_WME_AC(tidno);
2401*11729SWang.Lin@Sun.COM tid->ac = &an->ac[acno];
2402*11729SWang.Lin@Sun.COM tid->state &= ~AGGR_ADDBA_COMPLETE;
2403*11729SWang.Lin@Sun.COM tid->state &= ~AGGR_ADDBA_PROGRESS;
2404*11729SWang.Lin@Sun.COM tid->addba_exchangeattempts = 0;
2405*11729SWang.Lin@Sun.COM }
2406*11729SWang.Lin@Sun.COM
2407*11729SWang.Lin@Sun.COM for (acno = 0, ac = &an->ac[acno]; acno < WME_NUM_AC; acno++, ac++) {
2408*11729SWang.Lin@Sun.COM ac->sched = B_FALSE;
2409*11729SWang.Lin@Sun.COM list_create(&ac->tid_q, sizeof (struct ath_atx_tid),
2410*11729SWang.Lin@Sun.COM offsetof(struct ath_atx_tid, list));
2411*11729SWang.Lin@Sun.COM
2412*11729SWang.Lin@Sun.COM switch (acno) {
2413*11729SWang.Lin@Sun.COM case WME_AC_BE:
2414*11729SWang.Lin@Sun.COM ac->qnum = arn_tx_get_qnum(sc,
2415*11729SWang.Lin@Sun.COM ATH9K_TX_QUEUE_DATA, ATH9K_WME_AC_BE);
2416*11729SWang.Lin@Sun.COM break;
2417*11729SWang.Lin@Sun.COM case WME_AC_BK:
2418*11729SWang.Lin@Sun.COM ac->qnum = arn_tx_get_qnum(sc,
2419*11729SWang.Lin@Sun.COM ATH9K_TX_QUEUE_DATA, ATH9K_WME_AC_BK);
2420*11729SWang.Lin@Sun.COM break;
2421*11729SWang.Lin@Sun.COM case WME_AC_VI:
2422*11729SWang.Lin@Sun.COM ac->qnum = arn_tx_get_qnum(sc,
2423*11729SWang.Lin@Sun.COM ATH9K_TX_QUEUE_DATA, ATH9K_WME_AC_VI);
2424*11729SWang.Lin@Sun.COM break;
2425*11729SWang.Lin@Sun.COM case WME_AC_VO:
2426*11729SWang.Lin@Sun.COM ac->qnum = arn_tx_get_qnum(sc,
2427*11729SWang.Lin@Sun.COM ATH9K_TX_QUEUE_DATA, ATH9K_WME_AC_VO);
2428*11729SWang.Lin@Sun.COM break;
2429*11729SWang.Lin@Sun.COM }
2430*11729SWang.Lin@Sun.COM }
2431*11729SWang.Lin@Sun.COM }
2432*11729SWang.Lin@Sun.COM
2433*11729SWang.Lin@Sun.COM void
arn_tx_node_cleanup(struct arn_softc * sc,struct ieee80211_node * in)2434*11729SWang.Lin@Sun.COM arn_tx_node_cleanup(struct arn_softc *sc, struct ieee80211_node *in)
2435*11729SWang.Lin@Sun.COM {
2436*11729SWang.Lin@Sun.COM int i;
2437*11729SWang.Lin@Sun.COM struct ath_atx_ac *ac, *ac_tmp;
2438*11729SWang.Lin@Sun.COM struct ath_atx_tid *tid, *tid_tmp;
2439*11729SWang.Lin@Sun.COM struct ath_txq *txq;
2440*11729SWang.Lin@Sun.COM struct ath_node *an = ATH_NODE(in);
2441*11729SWang.Lin@Sun.COM
2442*11729SWang.Lin@Sun.COM for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
2443*11729SWang.Lin@Sun.COM if (ARN_TXQ_SETUP(sc, i)) {
2444*11729SWang.Lin@Sun.COM txq = &sc->sc_txq[i];
2445*11729SWang.Lin@Sun.COM
2446*11729SWang.Lin@Sun.COM mutex_enter(&txq->axq_lock);
2447*11729SWang.Lin@Sun.COM
2448*11729SWang.Lin@Sun.COM list_for_each_entry_safe(ac, ac_tmp, &txq->axq_acq) {
2449*11729SWang.Lin@Sun.COM tid = list_head(&ac->tid_q);
2450*11729SWang.Lin@Sun.COM if (tid && tid->an != an)
2451*11729SWang.Lin@Sun.COM continue;
2452*11729SWang.Lin@Sun.COM list_remove(&txq->axq_acq, ac);
2453*11729SWang.Lin@Sun.COM ac->sched = B_FALSE;
2454*11729SWang.Lin@Sun.COM
2455*11729SWang.Lin@Sun.COM list_for_each_entry_safe(tid, tid_tmp,
2456*11729SWang.Lin@Sun.COM &ac->tid_q) {
2457*11729SWang.Lin@Sun.COM list_remove(&ac->tid_q, tid);
2458*11729SWang.Lin@Sun.COM bf = list_head(&tid->buf_q);
2459*11729SWang.Lin@Sun.COM while (bf != NULL) {
2460*11729SWang.Lin@Sun.COM if (bf->bf_in == in)
2461*11729SWang.Lin@Sun.COM bf->bf_in = NULL;
2462*11729SWang.Lin@Sun.COM }
2463*11729SWang.Lin@Sun.COM bf = list_next(&txq->axq_list, bf);
2464*11729SWang.Lin@Sun.COM tid->sched = B_FALSE;
2465*11729SWang.Lin@Sun.COM arn_tid_drain(sc, txq, tid);
2466*11729SWang.Lin@Sun.COM tid->state &= ~AGGR_ADDBA_COMPLETE;
2467*11729SWang.Lin@Sun.COM tid->addba_exchangeattempts = 0;
2468*11729SWang.Lin@Sun.COM tid->state &= ~AGGR_CLEANUP;
2469*11729SWang.Lin@Sun.COM }
2470*11729SWang.Lin@Sun.COM }
2471*11729SWang.Lin@Sun.COM
2472*11729SWang.Lin@Sun.COM mutex_exit(&txq->axq_lock);
2473*11729SWang.Lin@Sun.COM }
2474*11729SWang.Lin@Sun.COM }
2475*11729SWang.Lin@Sun.COM }
2476*11729SWang.Lin@Sun.COM #endif /* ARN_TX_AGGREGATION */
2477