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