xref: /freebsd/sys/dev/rtwn/usb/rtwn_usb_tx.c (revision d99eb8230eb717ab0b2eba948614d0f2f2b5dd2b)
17453645fSAndriy Voskoboinyk /*	$OpenBSD: if_urtwn.c,v 1.16 2011/02/10 17:26:40 jakemsr Exp $	*/
27453645fSAndriy Voskoboinyk 
37453645fSAndriy Voskoboinyk /*-
47453645fSAndriy Voskoboinyk  * Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr>
57453645fSAndriy Voskoboinyk  * Copyright (c) 2014 Kevin Lo <kevlo@FreeBSD.org>
67453645fSAndriy Voskoboinyk  * Copyright (c) 2015-2016 Andriy Voskoboinyk <avos@FreeBSD.org>
77453645fSAndriy Voskoboinyk  *
87453645fSAndriy Voskoboinyk  * Permission to use, copy, modify, and distribute this software for any
97453645fSAndriy Voskoboinyk  * purpose with or without fee is hereby granted, provided that the above
107453645fSAndriy Voskoboinyk  * copyright notice and this permission notice appear in all copies.
117453645fSAndriy Voskoboinyk  *
127453645fSAndriy Voskoboinyk  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
137453645fSAndriy Voskoboinyk  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
147453645fSAndriy Voskoboinyk  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
157453645fSAndriy Voskoboinyk  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
167453645fSAndriy Voskoboinyk  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
177453645fSAndriy Voskoboinyk  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
187453645fSAndriy Voskoboinyk  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
197453645fSAndriy Voskoboinyk  */
207453645fSAndriy Voskoboinyk 
217453645fSAndriy Voskoboinyk #include <sys/cdefs.h>
227453645fSAndriy Voskoboinyk #include "opt_wlan.h"
237453645fSAndriy Voskoboinyk 
247453645fSAndriy Voskoboinyk #include <sys/param.h>
257453645fSAndriy Voskoboinyk #include <sys/lock.h>
267453645fSAndriy Voskoboinyk #include <sys/mutex.h>
277453645fSAndriy Voskoboinyk #include <sys/mbuf.h>
287453645fSAndriy Voskoboinyk #include <sys/kernel.h>
297453645fSAndriy Voskoboinyk #include <sys/socket.h>
307453645fSAndriy Voskoboinyk #include <sys/systm.h>
317453645fSAndriy Voskoboinyk #include <sys/malloc.h>
327453645fSAndriy Voskoboinyk #include <sys/queue.h>
337453645fSAndriy Voskoboinyk #include <sys/taskqueue.h>
347453645fSAndriy Voskoboinyk #include <sys/bus.h>
357453645fSAndriy Voskoboinyk #include <sys/endian.h>
367453645fSAndriy Voskoboinyk 
377453645fSAndriy Voskoboinyk #include <net/if.h>
387453645fSAndriy Voskoboinyk #include <net/if_var.h>
397453645fSAndriy Voskoboinyk #include <net/ethernet.h>
407453645fSAndriy Voskoboinyk #include <net/if_media.h>
417453645fSAndriy Voskoboinyk 
427453645fSAndriy Voskoboinyk #include <net80211/ieee80211_var.h>
437453645fSAndriy Voskoboinyk #include <net80211/ieee80211_radiotap.h>
447453645fSAndriy Voskoboinyk #include <net80211/ieee80211_ratectl.h>
457453645fSAndriy Voskoboinyk 
467453645fSAndriy Voskoboinyk #include <dev/usb/usb.h>
477453645fSAndriy Voskoboinyk #include <dev/usb/usbdi.h>
487453645fSAndriy Voskoboinyk 
497453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwnreg.h>
507453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwnvar.h>
517453645fSAndriy Voskoboinyk 
527453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwn_beacon.h>
537453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwn_debug.h>
547453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwn_ridx.h>
557453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwn_task.h>
567453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwn_tx.h>
577453645fSAndriy Voskoboinyk 
587453645fSAndriy Voskoboinyk #include <dev/rtwn/usb/rtwn_usb_var.h>
597453645fSAndriy Voskoboinyk 
607453645fSAndriy Voskoboinyk #include <dev/rtwn/usb/rtwn_usb_reg.h>
617453645fSAndriy Voskoboinyk #include <dev/rtwn/usb/rtwn_usb_tx.h>
627453645fSAndriy Voskoboinyk 
637453645fSAndriy Voskoboinyk static struct rtwn_data * _rtwn_usb_getbuf(struct rtwn_usb_softc *);
647453645fSAndriy Voskoboinyk static struct rtwn_data * rtwn_usb_getbuf(struct rtwn_usb_softc *);
657453645fSAndriy Voskoboinyk static void		rtwn_usb_txeof(struct rtwn_usb_softc *,
667453645fSAndriy Voskoboinyk 			    struct rtwn_data *, int);
677453645fSAndriy Voskoboinyk 
687453645fSAndriy Voskoboinyk static struct rtwn_data *
_rtwn_usb_getbuf(struct rtwn_usb_softc * uc)697453645fSAndriy Voskoboinyk _rtwn_usb_getbuf(struct rtwn_usb_softc *uc)
707453645fSAndriy Voskoboinyk {
717453645fSAndriy Voskoboinyk 	struct rtwn_softc *sc = &uc->uc_sc;
727453645fSAndriy Voskoboinyk 	struct rtwn_data *bf;
737453645fSAndriy Voskoboinyk 
747453645fSAndriy Voskoboinyk 	bf = STAILQ_FIRST(&uc->uc_tx_inactive);
757453645fSAndriy Voskoboinyk 	if (bf != NULL)
767453645fSAndriy Voskoboinyk 		STAILQ_REMOVE_HEAD(&uc->uc_tx_inactive, next);
777453645fSAndriy Voskoboinyk 	else {
787453645fSAndriy Voskoboinyk 		RTWN_DPRINTF(sc, RTWN_DEBUG_XMIT,
797453645fSAndriy Voskoboinyk 		    "%s: out of xmit buffers\n", __func__);
807453645fSAndriy Voskoboinyk 	}
817453645fSAndriy Voskoboinyk 	return (bf);
827453645fSAndriy Voskoboinyk }
837453645fSAndriy Voskoboinyk 
847453645fSAndriy Voskoboinyk static struct rtwn_data *
rtwn_usb_getbuf(struct rtwn_usb_softc * uc)857453645fSAndriy Voskoboinyk rtwn_usb_getbuf(struct rtwn_usb_softc *uc)
867453645fSAndriy Voskoboinyk {
877453645fSAndriy Voskoboinyk 	struct rtwn_softc *sc = &uc->uc_sc;
887453645fSAndriy Voskoboinyk 	struct rtwn_data *bf;
897453645fSAndriy Voskoboinyk 
907453645fSAndriy Voskoboinyk 	RTWN_ASSERT_LOCKED(sc);
917453645fSAndriy Voskoboinyk 
927453645fSAndriy Voskoboinyk 	bf = _rtwn_usb_getbuf(uc);
937453645fSAndriy Voskoboinyk 	if (bf == NULL) {
947453645fSAndriy Voskoboinyk 		RTWN_DPRINTF(sc, RTWN_DEBUG_XMIT, "%s: stop queue\n",
957453645fSAndriy Voskoboinyk 		    __func__);
967453645fSAndriy Voskoboinyk 	}
977453645fSAndriy Voskoboinyk 	return (bf);
987453645fSAndriy Voskoboinyk }
997453645fSAndriy Voskoboinyk 
1007453645fSAndriy Voskoboinyk static void
rtwn_usb_txeof(struct rtwn_usb_softc * uc,struct rtwn_data * data,int status)1017453645fSAndriy Voskoboinyk rtwn_usb_txeof(struct rtwn_usb_softc *uc, struct rtwn_data *data, int status)
1027453645fSAndriy Voskoboinyk {
1037453645fSAndriy Voskoboinyk 	struct rtwn_softc *sc = &uc->uc_sc;
104*d99eb823SAdrian Chadd 	bool is_empty = true;
1057453645fSAndriy Voskoboinyk 
1067453645fSAndriy Voskoboinyk 	RTWN_ASSERT_LOCKED(sc);
1077453645fSAndriy Voskoboinyk 
1087453645fSAndriy Voskoboinyk 	if (data->ni != NULL)	/* not a beacon frame */
1097453645fSAndriy Voskoboinyk 		ieee80211_tx_complete(data->ni, data->m, status);
1107453645fSAndriy Voskoboinyk 
1117453645fSAndriy Voskoboinyk 	if (sc->sc_ratectl != RTWN_RATECTL_NET80211)
1127453645fSAndriy Voskoboinyk 		if (sc->sc_tx_n_active > 0)
1137453645fSAndriy Voskoboinyk 			sc->sc_tx_n_active--;
1147453645fSAndriy Voskoboinyk 
1157453645fSAndriy Voskoboinyk 	data->ni = NULL;
1167453645fSAndriy Voskoboinyk 	data->m = NULL;
1177453645fSAndriy Voskoboinyk 
1187453645fSAndriy Voskoboinyk 	STAILQ_INSERT_TAIL(&uc->uc_tx_inactive, data, next);
1197453645fSAndriy Voskoboinyk 	sc->qfullmsk = 0;
120*d99eb823SAdrian Chadd 
1217453645fSAndriy Voskoboinyk #ifndef D4054
122*d99eb823SAdrian Chadd 	for (int i = RTWN_BULK_TX_FIRST; i < RTWN_BULK_EP_COUNT; i++) {
123*d99eb823SAdrian Chadd 		if (!STAILQ_EMPTY(&uc->uc_tx_active[i]) ||
124*d99eb823SAdrian Chadd 		    !STAILQ_EMPTY(&uc->uc_tx_pending[i]))
125*d99eb823SAdrian Chadd 			is_empty = false;
126*d99eb823SAdrian Chadd 	}
127*d99eb823SAdrian Chadd 
128*d99eb823SAdrian Chadd 	if (is_empty)
1297453645fSAndriy Voskoboinyk 		sc->sc_tx_timer = 0;
1307453645fSAndriy Voskoboinyk 	else
1317453645fSAndriy Voskoboinyk 		sc->sc_tx_timer = 5;
1327453645fSAndriy Voskoboinyk #endif
1337453645fSAndriy Voskoboinyk }
1347453645fSAndriy Voskoboinyk 
135*d99eb823SAdrian Chadd static void
rtwn_bulk_tx_callback_qid(struct usb_xfer * xfer,usb_error_t error,int qid)136*d99eb823SAdrian Chadd rtwn_bulk_tx_callback_qid(struct usb_xfer *xfer, usb_error_t error, int qid)
1377453645fSAndriy Voskoboinyk {
1387453645fSAndriy Voskoboinyk 	struct rtwn_usb_softc *uc = usbd_xfer_softc(xfer);
1397453645fSAndriy Voskoboinyk 	struct rtwn_softc *sc = &uc->uc_sc;
1407453645fSAndriy Voskoboinyk 	struct rtwn_data *data;
141*d99eb823SAdrian Chadd 	bool do_is_empty_check = false;
142*d99eb823SAdrian Chadd 	int i;
143*d99eb823SAdrian Chadd 
144*d99eb823SAdrian Chadd 	RTWN_DPRINTF(sc, RTWN_DEBUG_XMIT,
145*d99eb823SAdrian Chadd 	    "%s: called, qid=%d\n", __func__, qid);
1467453645fSAndriy Voskoboinyk 
1477453645fSAndriy Voskoboinyk 	RTWN_ASSERT_LOCKED(sc);
1487453645fSAndriy Voskoboinyk 
1497453645fSAndriy Voskoboinyk 	switch (USB_GET_STATE(xfer)){
1507453645fSAndriy Voskoboinyk 	case USB_ST_TRANSFERRED:
151*d99eb823SAdrian Chadd 		data = STAILQ_FIRST(&uc->uc_tx_active[qid]);
1527453645fSAndriy Voskoboinyk 		if (data == NULL)
1537453645fSAndriy Voskoboinyk 			goto tr_setup;
154*d99eb823SAdrian Chadd 		STAILQ_REMOVE_HEAD(&uc->uc_tx_active[qid], next);
1557453645fSAndriy Voskoboinyk 		rtwn_usb_txeof(uc, data, 0);
1567453645fSAndriy Voskoboinyk 		/* FALLTHROUGH */
1577453645fSAndriy Voskoboinyk 	case USB_ST_SETUP:
1587453645fSAndriy Voskoboinyk tr_setup:
159*d99eb823SAdrian Chadd 		data = STAILQ_FIRST(&uc->uc_tx_pending[qid]);
1607453645fSAndriy Voskoboinyk 		if (data == NULL) {
1617453645fSAndriy Voskoboinyk 			RTWN_DPRINTF(sc, RTWN_DEBUG_XMIT,
1627453645fSAndriy Voskoboinyk 			    "%s: empty pending queue\n", __func__);
163*d99eb823SAdrian Chadd 			do_is_empty_check = true;
1647453645fSAndriy Voskoboinyk 			goto finish;
1657453645fSAndriy Voskoboinyk 		}
166*d99eb823SAdrian Chadd 		STAILQ_REMOVE_HEAD(&uc->uc_tx_pending[qid], next);
167*d99eb823SAdrian Chadd 		STAILQ_INSERT_TAIL(&uc->uc_tx_active[qid], data, next);
1687453645fSAndriy Voskoboinyk 
1697453645fSAndriy Voskoboinyk 		/*
1707453645fSAndriy Voskoboinyk 		 * Note: if this is a beacon frame, ensure that it will go
1717453645fSAndriy Voskoboinyk 		 * into appropriate queue.
1727453645fSAndriy Voskoboinyk 		 */
1737453645fSAndriy Voskoboinyk 		if (data->ni == NULL && RTWN_CHIP_HAS_BCNQ1(sc))
1747453645fSAndriy Voskoboinyk 			rtwn_switch_bcnq(sc, data->id);
1757453645fSAndriy Voskoboinyk 		usbd_xfer_set_frame_data(xfer, 0, data->buf, data->buflen);
1767453645fSAndriy Voskoboinyk 		usbd_transfer_submit(xfer);
1777453645fSAndriy Voskoboinyk 		if (sc->sc_ratectl != RTWN_RATECTL_NET80211)
1787453645fSAndriy Voskoboinyk 			sc->sc_tx_n_active++;
1797453645fSAndriy Voskoboinyk 		break;
1807453645fSAndriy Voskoboinyk 	default:
181*d99eb823SAdrian Chadd 		data = STAILQ_FIRST(&uc->uc_tx_active[qid]);
1827453645fSAndriy Voskoboinyk 		if (data == NULL)
1837453645fSAndriy Voskoboinyk 			goto tr_setup;
184*d99eb823SAdrian Chadd 		STAILQ_REMOVE_HEAD(&uc->uc_tx_active[qid], next);
1857453645fSAndriy Voskoboinyk 		rtwn_usb_txeof(uc, data, 1);
186*d99eb823SAdrian Chadd 		if (error != 0)
187*d99eb823SAdrian Chadd 			device_printf(sc->sc_dev,
188*d99eb823SAdrian Chadd 			    "%s: called; txeof qid=%d, error=%s\n",
189*d99eb823SAdrian Chadd 			    __func__,
190*d99eb823SAdrian Chadd 			    qid,
191*d99eb823SAdrian Chadd 			    usbd_errstr(error));
1927453645fSAndriy Voskoboinyk 		if (error != USB_ERR_CANCELLED) {
1937453645fSAndriy Voskoboinyk 			usbd_xfer_set_stall(xfer);
1947453645fSAndriy Voskoboinyk 			goto tr_setup;
1957453645fSAndriy Voskoboinyk 		}
1967453645fSAndriy Voskoboinyk 		break;
1977453645fSAndriy Voskoboinyk 	}
1987453645fSAndriy Voskoboinyk finish:
199*d99eb823SAdrian Chadd 
200*d99eb823SAdrian Chadd 	/*
201*d99eb823SAdrian Chadd 	 * Clear sc_tx_n_active if all the pending transfers are 0.
202*d99eb823SAdrian Chadd 	 *
203*d99eb823SAdrian Chadd 	 * This is currently a crutch because net80211 doesn't provide
204*d99eb823SAdrian Chadd 	 * a way to defer all the FF checks or one of the FF checks.
205*d99eb823SAdrian Chadd 	 * Eventually this should just be tracked per-endpoint.
206*d99eb823SAdrian Chadd 	 */
207*d99eb823SAdrian Chadd 	for (i = RTWN_BULK_TX_FIRST; i < RTWN_BULK_EP_COUNT; i++)
208*d99eb823SAdrian Chadd 		if (STAILQ_FIRST(&uc->uc_tx_pending[i]) != NULL)
209*d99eb823SAdrian Chadd 			do_is_empty_check = false;
210*d99eb823SAdrian Chadd 	if (do_is_empty_check)
211*d99eb823SAdrian Chadd 		sc->sc_tx_n_active = 0;
2127453645fSAndriy Voskoboinyk #ifdef	IEEE80211_SUPPORT_SUPERG
2137453645fSAndriy Voskoboinyk 	/*
2147453645fSAndriy Voskoboinyk 	 * If the TX active queue drops below a certain
2157453645fSAndriy Voskoboinyk 	 * threshold, ensure we age fast-frames out so they're
2167453645fSAndriy Voskoboinyk 	 * transmitted.
2177453645fSAndriy Voskoboinyk 	 */
2187453645fSAndriy Voskoboinyk 	if (sc->sc_ratectl != RTWN_RATECTL_NET80211 &&
2197453645fSAndriy Voskoboinyk 	    sc->sc_tx_n_active <= 1) {
2207453645fSAndriy Voskoboinyk 		/* XXX ew - net80211 should defer this for us! */
2217453645fSAndriy Voskoboinyk 
2227453645fSAndriy Voskoboinyk 		/*
2237453645fSAndriy Voskoboinyk 		 * Note: this sc_tx_n_active currently tracks
2247453645fSAndriy Voskoboinyk 		 * the number of pending transmit submissions
2257453645fSAndriy Voskoboinyk 		 * and not the actual depth of the TX frames
2267453645fSAndriy Voskoboinyk 		 * pending to the hardware.  That means that
2277453645fSAndriy Voskoboinyk 		 * we're going to end up with some sub-optimal
2287453645fSAndriy Voskoboinyk 		 * aggregation behaviour.
2297453645fSAndriy Voskoboinyk 		 */
2307453645fSAndriy Voskoboinyk 		/*
2317453645fSAndriy Voskoboinyk 		 * XXX TODO: just make this a callout timer schedule so we can
2327453645fSAndriy Voskoboinyk 		 * flush the FF staging queue if we're approaching idle.
2337453645fSAndriy Voskoboinyk 		 */
2341a58c704SAndriy Voskoboinyk 		rtwn_cmd_sleepable(sc, NULL, 0, rtwn_ff_flush_all);
2357453645fSAndriy Voskoboinyk 	}
2367453645fSAndriy Voskoboinyk #endif
2377453645fSAndriy Voskoboinyk 	/* Kick-start more transmit */
2387453645fSAndriy Voskoboinyk 	rtwn_start(sc);
2397453645fSAndriy Voskoboinyk }
2407453645fSAndriy Voskoboinyk 
241*d99eb823SAdrian Chadd void
rtwn_bulk_tx_callback_be(struct usb_xfer * xfer,usb_error_t error)242*d99eb823SAdrian Chadd rtwn_bulk_tx_callback_be(struct usb_xfer *xfer, usb_error_t error)
243*d99eb823SAdrian Chadd {
244*d99eb823SAdrian Chadd 
245*d99eb823SAdrian Chadd 	rtwn_bulk_tx_callback_qid(xfer, error, RTWN_BULK_TX_BE);
246*d99eb823SAdrian Chadd }
247*d99eb823SAdrian Chadd 
248*d99eb823SAdrian Chadd void
rtwn_bulk_tx_callback_bk(struct usb_xfer * xfer,usb_error_t error)249*d99eb823SAdrian Chadd rtwn_bulk_tx_callback_bk(struct usb_xfer *xfer, usb_error_t error)
250*d99eb823SAdrian Chadd {
251*d99eb823SAdrian Chadd 
252*d99eb823SAdrian Chadd 	rtwn_bulk_tx_callback_qid(xfer, error, RTWN_BULK_TX_BK);
253*d99eb823SAdrian Chadd }
254*d99eb823SAdrian Chadd 
255*d99eb823SAdrian Chadd void
rtwn_bulk_tx_callback_vi(struct usb_xfer * xfer,usb_error_t error)256*d99eb823SAdrian Chadd rtwn_bulk_tx_callback_vi(struct usb_xfer *xfer, usb_error_t error)
257*d99eb823SAdrian Chadd {
258*d99eb823SAdrian Chadd 
259*d99eb823SAdrian Chadd 	rtwn_bulk_tx_callback_qid(xfer, error, RTWN_BULK_TX_VI);
260*d99eb823SAdrian Chadd }
261*d99eb823SAdrian Chadd 
262*d99eb823SAdrian Chadd void
rtwn_bulk_tx_callback_vo(struct usb_xfer * xfer,usb_error_t error)263*d99eb823SAdrian Chadd rtwn_bulk_tx_callback_vo(struct usb_xfer *xfer, usb_error_t error)
264*d99eb823SAdrian Chadd {
265*d99eb823SAdrian Chadd 
266*d99eb823SAdrian Chadd 	rtwn_bulk_tx_callback_qid(xfer, error, RTWN_BULK_TX_VO);
267*d99eb823SAdrian Chadd }
268*d99eb823SAdrian Chadd 
2697453645fSAndriy Voskoboinyk static void
rtwn_usb_tx_checksum(struct rtwn_tx_desc_common * txd)2707453645fSAndriy Voskoboinyk rtwn_usb_tx_checksum(struct rtwn_tx_desc_common *txd)
2717453645fSAndriy Voskoboinyk {
2727453645fSAndriy Voskoboinyk 	txd->txdw7.usb_checksum = 0;
2737453645fSAndriy Voskoboinyk 	txd->txdw7.usb_checksum = rtwn_usb_calc_tx_checksum(txd);
2747453645fSAndriy Voskoboinyk }
2757453645fSAndriy Voskoboinyk 
2767453645fSAndriy Voskoboinyk int
rtwn_usb_tx_start(struct rtwn_softc * sc,struct ieee80211_node * ni,struct mbuf * m,uint8_t * tx_desc,uint8_t type,int id)2777453645fSAndriy Voskoboinyk rtwn_usb_tx_start(struct rtwn_softc *sc, struct ieee80211_node *ni,
2787453645fSAndriy Voskoboinyk     struct mbuf *m, uint8_t *tx_desc, uint8_t type, int id)
2797453645fSAndriy Voskoboinyk {
2807453645fSAndriy Voskoboinyk 	struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc);
2817453645fSAndriy Voskoboinyk 	struct rtwn_tx_desc_common *txd;
2827453645fSAndriy Voskoboinyk 	struct rtwn_data *data;
2837453645fSAndriy Voskoboinyk 	struct usb_xfer *xfer;
2847453645fSAndriy Voskoboinyk 	uint16_t ac;
285*d99eb823SAdrian Chadd 	int qid = 0;
2867453645fSAndriy Voskoboinyk 
2877453645fSAndriy Voskoboinyk 	RTWN_ASSERT_LOCKED(sc);
2887453645fSAndriy Voskoboinyk 
28937376971SAndriy Voskoboinyk 	if (m->m_pkthdr.len + sc->txdesc_len > RTWN_USB_TXBUFSZ)
29037376971SAndriy Voskoboinyk 		return (EINVAL);
29137376971SAndriy Voskoboinyk 
2927453645fSAndriy Voskoboinyk 	data = rtwn_usb_getbuf(uc);
2937453645fSAndriy Voskoboinyk 	if (data == NULL)
2947453645fSAndriy Voskoboinyk 		return (ENOBUFS);
2957453645fSAndriy Voskoboinyk 
296*d99eb823SAdrian Chadd 	/* TODO: should really get a consistent AC/TID, ath(4) style */
2977453645fSAndriy Voskoboinyk 	ac = M_WME_GETAC(m);
2987453645fSAndriy Voskoboinyk 
2997453645fSAndriy Voskoboinyk 	switch (type) {
3007453645fSAndriy Voskoboinyk 	case IEEE80211_FC0_TYPE_CTL:
3017453645fSAndriy Voskoboinyk 	case IEEE80211_FC0_TYPE_MGT:
302*d99eb823SAdrian Chadd 		qid = RTWN_BULK_TX_VO;
3037453645fSAndriy Voskoboinyk 		break;
3047453645fSAndriy Voskoboinyk 	default:
305*d99eb823SAdrian Chadd 		qid = uc->wme2qid[ac];
3067453645fSAndriy Voskoboinyk 		break;
3077453645fSAndriy Voskoboinyk 	}
308*d99eb823SAdrian Chadd 	xfer = uc->uc_xfer[qid];
309*d99eb823SAdrian Chadd 
310*d99eb823SAdrian Chadd 	RTWN_DPRINTF(sc, RTWN_DEBUG_XMIT,
311*d99eb823SAdrian Chadd 	    "%s: called, ac=%d, qid=%d, xfer=%p\n",
312*d99eb823SAdrian Chadd 	    __func__, ac, qid, xfer);
3137453645fSAndriy Voskoboinyk 
3147453645fSAndriy Voskoboinyk 	txd = (struct rtwn_tx_desc_common *)tx_desc;
3157453645fSAndriy Voskoboinyk 	txd->pktlen = htole16(m->m_pkthdr.len);
3167453645fSAndriy Voskoboinyk 	txd->offset = sc->txdesc_len;
3177453645fSAndriy Voskoboinyk 	txd->flags0 |= RTWN_FLAGS0_OWN;
3187453645fSAndriy Voskoboinyk 	rtwn_usb_tx_checksum(txd);
3197453645fSAndriy Voskoboinyk 
3207453645fSAndriy Voskoboinyk 	/* Dump Tx descriptor. */
3217453645fSAndriy Voskoboinyk 	rtwn_dump_tx_desc(sc, tx_desc);
3227453645fSAndriy Voskoboinyk 
3237453645fSAndriy Voskoboinyk 	memcpy(data->buf, tx_desc, sc->txdesc_len);
3247453645fSAndriy Voskoboinyk 	m_copydata(m, 0, m->m_pkthdr.len,
3257453645fSAndriy Voskoboinyk 	    (caddr_t)(data->buf + sc->txdesc_len));
3267453645fSAndriy Voskoboinyk 
3277453645fSAndriy Voskoboinyk 	data->buflen = m->m_pkthdr.len + sc->txdesc_len;
3287453645fSAndriy Voskoboinyk 	data->id = id;
3297453645fSAndriy Voskoboinyk 	data->ni = ni;
330*d99eb823SAdrian Chadd 	data->qid = qid;
3317453645fSAndriy Voskoboinyk 	if (data->ni != NULL) {
3327453645fSAndriy Voskoboinyk 		data->m = m;
3337453645fSAndriy Voskoboinyk #ifndef D4054
3347453645fSAndriy Voskoboinyk 		sc->sc_tx_timer = 5;
3357453645fSAndriy Voskoboinyk #endif
3367453645fSAndriy Voskoboinyk 	}
3377453645fSAndriy Voskoboinyk 
338*d99eb823SAdrian Chadd 	STAILQ_INSERT_TAIL(&uc->uc_tx_pending[qid], data, next);
3397453645fSAndriy Voskoboinyk 	if (STAILQ_EMPTY(&uc->uc_tx_inactive))
3407453645fSAndriy Voskoboinyk 		sc->qfullmsk = 1;
3417453645fSAndriy Voskoboinyk 
3427453645fSAndriy Voskoboinyk 	usbd_transfer_start(xfer);
3437453645fSAndriy Voskoboinyk 
3447453645fSAndriy Voskoboinyk 	return (0);
3457453645fSAndriy Voskoboinyk }
346