xref: /freebsd/sys/dev/rtwn/pci/rtwn_pci_tx.c (revision 685dc743dc3b5645e34836464128e1c0558b404b)
17453645fSAndriy Voskoboinyk /*	$OpenBSD: if_rtwn.c,v 1.6 2015/08/28 00:03:53 deraadt Exp $	*/
27453645fSAndriy Voskoboinyk 
37453645fSAndriy Voskoboinyk /*-
47453645fSAndriy Voskoboinyk  * Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr>
57453645fSAndriy Voskoboinyk  * Copyright (c) 2015 Stefan Sperling <stsp@openbsd.org>
67453645fSAndriy Voskoboinyk  * Copyright (c) 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 <machine/bus.h>
387453645fSAndriy Voskoboinyk #include <machine/resource.h>
397453645fSAndriy Voskoboinyk #include <sys/rman.h>
407453645fSAndriy Voskoboinyk 
417453645fSAndriy Voskoboinyk #include <net/if.h>
427453645fSAndriy Voskoboinyk #include <net/if_var.h>
437453645fSAndriy Voskoboinyk #include <net/ethernet.h>
447453645fSAndriy Voskoboinyk #include <net/if_media.h>
457453645fSAndriy Voskoboinyk 
467453645fSAndriy Voskoboinyk #include <net80211/ieee80211_var.h>
477453645fSAndriy Voskoboinyk #include <net80211/ieee80211_radiotap.h>
487453645fSAndriy Voskoboinyk 
497453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwnreg.h>
507453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwnvar.h>
517453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwn_debug.h>
527453645fSAndriy Voskoboinyk 
537453645fSAndriy Voskoboinyk #include <dev/rtwn/pci/rtwn_pci_var.h>
547453645fSAndriy Voskoboinyk #include <dev/rtwn/pci/rtwn_pci_tx.h>
557453645fSAndriy Voskoboinyk 
567453645fSAndriy Voskoboinyk #include <dev/rtwn/rtl8192c/pci/r92ce_reg.h>
577453645fSAndriy Voskoboinyk 
58a14f9888SAndriy Voskoboinyk static struct mbuf *
rtwn_mbuf_defrag(struct mbuf * m0,int how)59a14f9888SAndriy Voskoboinyk rtwn_mbuf_defrag(struct mbuf *m0, int how)
60a14f9888SAndriy Voskoboinyk {
61a14f9888SAndriy Voskoboinyk 	struct mbuf *m = NULL;
62a14f9888SAndriy Voskoboinyk 
63a14f9888SAndriy Voskoboinyk 	KASSERT(m0->m_flags & M_PKTHDR,
64a14f9888SAndriy Voskoboinyk 	    ("M_PKTHDR flag is absent (m %p)!", m0));
65a14f9888SAndriy Voskoboinyk 
66a14f9888SAndriy Voskoboinyk 	/* NB: we need _exactly_ one mbuf (no less, no more). */
67a14f9888SAndriy Voskoboinyk 	if (m0->m_pkthdr.len > MJUMPAGESIZE) {
68a14f9888SAndriy Voskoboinyk 		/* XXX MJUM9BYTES? */
69a14f9888SAndriy Voskoboinyk 		return (NULL);
70a14f9888SAndriy Voskoboinyk 	} else if (m0->m_pkthdr.len > MCLBYTES) {
71a14f9888SAndriy Voskoboinyk 		m = m_getjcl(how, MT_DATA, M_PKTHDR, MJUMPAGESIZE);
72a14f9888SAndriy Voskoboinyk 		if (m == NULL)
73a14f9888SAndriy Voskoboinyk 			return (NULL);
74a14f9888SAndriy Voskoboinyk 
75a14f9888SAndriy Voskoboinyk 		if (m_dup_pkthdr(m, m0, how) == 0) {
76a14f9888SAndriy Voskoboinyk 			m_freem(m);
77a14f9888SAndriy Voskoboinyk 			return (NULL);
78a14f9888SAndriy Voskoboinyk 		}
79a14f9888SAndriy Voskoboinyk 
80a14f9888SAndriy Voskoboinyk 		m_copydata(m0, 0, m0->m_pkthdr.len, mtod(m, caddr_t));
81a14f9888SAndriy Voskoboinyk 		m->m_len = m->m_pkthdr.len;
82a14f9888SAndriy Voskoboinyk 		m_freem(m0);
83a14f9888SAndriy Voskoboinyk 
84a14f9888SAndriy Voskoboinyk 		return (m);
85a14f9888SAndriy Voskoboinyk 	} else
86a14f9888SAndriy Voskoboinyk 		return (m_defrag(m0, how));
87a14f9888SAndriy Voskoboinyk }
88a14f9888SAndriy Voskoboinyk 
897453645fSAndriy Voskoboinyk static int
rtwn_pci_tx_start_frame(struct rtwn_softc * sc,struct ieee80211_node * ni,struct mbuf * m,uint8_t * tx_desc,uint8_t type)90d067ef0fSAndriy Voskoboinyk rtwn_pci_tx_start_frame(struct rtwn_softc *sc, struct ieee80211_node *ni,
91d067ef0fSAndriy Voskoboinyk     struct mbuf *m, uint8_t *tx_desc, uint8_t type)
927453645fSAndriy Voskoboinyk {
937453645fSAndriy Voskoboinyk 	struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc);
947453645fSAndriy Voskoboinyk 	struct rtwn_tx_ring *ring;
957453645fSAndriy Voskoboinyk 	struct rtwn_tx_data *data;
967453645fSAndriy Voskoboinyk 	struct rtwn_tx_desc_common *txd;
977453645fSAndriy Voskoboinyk 	bus_dma_segment_t segs[1];
987453645fSAndriy Voskoboinyk 	uint8_t qid;
997453645fSAndriy Voskoboinyk 	int nsegs, error;
1007453645fSAndriy Voskoboinyk 
1017453645fSAndriy Voskoboinyk 	RTWN_ASSERT_LOCKED(sc);
1027453645fSAndriy Voskoboinyk 
1037453645fSAndriy Voskoboinyk 	switch (type) {
1047453645fSAndriy Voskoboinyk 	case IEEE80211_FC0_TYPE_CTL:
1057453645fSAndriy Voskoboinyk 	case IEEE80211_FC0_TYPE_MGT:
106d067ef0fSAndriy Voskoboinyk 		qid = RTWN_PCI_MGNT_QUEUE;
1077453645fSAndriy Voskoboinyk 		break;
1087453645fSAndriy Voskoboinyk 	default:
1097453645fSAndriy Voskoboinyk 		qid = M_WME_GETAC(m);
1107453645fSAndriy Voskoboinyk 		break;
1117453645fSAndriy Voskoboinyk 	}
1127453645fSAndriy Voskoboinyk 
1137453645fSAndriy Voskoboinyk 	ring = &pc->tx_ring[qid];
1147453645fSAndriy Voskoboinyk 	data = &ring->tx_data[ring->cur];
1157453645fSAndriy Voskoboinyk 	if (data->m != NULL) {
1167453645fSAndriy Voskoboinyk 		RTWN_DPRINTF(sc, RTWN_DEBUG_XMIT,
1177453645fSAndriy Voskoboinyk 		    "%s: ring #%u is full (m %p)\n", __func__, qid, data->m);
1187453645fSAndriy Voskoboinyk 		return (ENOBUFS);
1197453645fSAndriy Voskoboinyk 	}
1207453645fSAndriy Voskoboinyk 
1217453645fSAndriy Voskoboinyk 	txd = (struct rtwn_tx_desc_common *)
1227453645fSAndriy Voskoboinyk 	    ((uint8_t *)ring->desc + sc->txdesc_len * ring->cur);
1237453645fSAndriy Voskoboinyk 	if (txd->flags0 & RTWN_FLAGS0_OWN) {
1247453645fSAndriy Voskoboinyk 		device_printf(sc->sc_dev,
1257453645fSAndriy Voskoboinyk 		    "%s: OWN bit is set (tx desc %d, ring %u)!\n",
1267453645fSAndriy Voskoboinyk 		    __func__, ring->cur, qid);
1277453645fSAndriy Voskoboinyk 		return (ENOBUFS);
1287453645fSAndriy Voskoboinyk 	}
1297453645fSAndriy Voskoboinyk 
1307453645fSAndriy Voskoboinyk 	/* Copy Tx descriptor. */
1317453645fSAndriy Voskoboinyk 	rtwn_pci_copy_tx_desc(pc, txd, tx_desc);
1327453645fSAndriy Voskoboinyk 	txd->pktlen = htole16(m->m_pkthdr.len);
1337453645fSAndriy Voskoboinyk 	txd->offset = sc->txdesc_len;
1347453645fSAndriy Voskoboinyk 
1357453645fSAndriy Voskoboinyk 	error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m, segs,
1367453645fSAndriy Voskoboinyk 	    &nsegs, BUS_DMA_NOWAIT);
1377453645fSAndriy Voskoboinyk 	if (error != 0 && error != EFBIG) {
1387453645fSAndriy Voskoboinyk 		device_printf(sc->sc_dev, "can't map mbuf (error %d)\n",
1397453645fSAndriy Voskoboinyk 		    error);
1407453645fSAndriy Voskoboinyk 		return (error);
1417453645fSAndriy Voskoboinyk 	}
1427453645fSAndriy Voskoboinyk 	if (error != 0) {
1437453645fSAndriy Voskoboinyk 		struct mbuf *mnew;
1447453645fSAndriy Voskoboinyk 
145a14f9888SAndriy Voskoboinyk 		mnew = rtwn_mbuf_defrag(m, M_NOWAIT);
1467453645fSAndriy Voskoboinyk 		if (mnew == NULL) {
1477453645fSAndriy Voskoboinyk 			device_printf(sc->sc_dev, "can't defragment mbuf\n");
1487453645fSAndriy Voskoboinyk 			return (ENOBUFS);
1497453645fSAndriy Voskoboinyk 		}
1507453645fSAndriy Voskoboinyk 		m = mnew;
1517453645fSAndriy Voskoboinyk 
1527453645fSAndriy Voskoboinyk 		error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m,
1537453645fSAndriy Voskoboinyk 		    segs, &nsegs, BUS_DMA_NOWAIT);
1547453645fSAndriy Voskoboinyk 		if (error != 0) {
1557453645fSAndriy Voskoboinyk 			device_printf(sc->sc_dev,
1567453645fSAndriy Voskoboinyk 			    "can't map mbuf (error %d)\n", error);
1577453645fSAndriy Voskoboinyk 			if (ni != NULL) {
1587453645fSAndriy Voskoboinyk 				if_inc_counter(ni->ni_vap->iv_ifp,
1597453645fSAndriy Voskoboinyk 				    IFCOUNTER_OERRORS, 1);
1607453645fSAndriy Voskoboinyk 				ieee80211_free_node(ni);
1617453645fSAndriy Voskoboinyk 			}
1627453645fSAndriy Voskoboinyk 			m_freem(m);
1637453645fSAndriy Voskoboinyk 			return (0);	/* XXX */
1647453645fSAndriy Voskoboinyk 		}
1657453645fSAndriy Voskoboinyk 	}
1667453645fSAndriy Voskoboinyk 
1677453645fSAndriy Voskoboinyk 	rtwn_pci_tx_postsetup(pc, txd, segs);
1687453645fSAndriy Voskoboinyk 	txd->flags0 |= RTWN_FLAGS0_OWN;
1697453645fSAndriy Voskoboinyk 
1707453645fSAndriy Voskoboinyk 	/* Dump Tx descriptor. */
1717453645fSAndriy Voskoboinyk 	rtwn_dump_tx_desc(sc, txd);
1727453645fSAndriy Voskoboinyk 
1737453645fSAndriy Voskoboinyk 	bus_dmamap_sync(ring->desc_dmat, ring->desc_map,
174*387c3f14SAndriy Voskoboinyk 	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
175*387c3f14SAndriy Voskoboinyk 	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE);
1767453645fSAndriy Voskoboinyk 
1777453645fSAndriy Voskoboinyk 	data->m = m;
1787453645fSAndriy Voskoboinyk 	data->ni = ni;
1797453645fSAndriy Voskoboinyk 
1807453645fSAndriy Voskoboinyk 	ring->cur = (ring->cur + 1) % RTWN_PCI_TX_LIST_COUNT;
1817453645fSAndriy Voskoboinyk 
1827453645fSAndriy Voskoboinyk 	ring->queued++;
1837453645fSAndriy Voskoboinyk 	if (ring->queued >= (RTWN_PCI_TX_LIST_COUNT - 1))
1847453645fSAndriy Voskoboinyk 		sc->qfullmsk |= (1 << qid);
1857453645fSAndriy Voskoboinyk 
1867453645fSAndriy Voskoboinyk #ifndef D4054
1877453645fSAndriy Voskoboinyk 	sc->sc_tx_timer = 5;
1887453645fSAndriy Voskoboinyk #endif
1897453645fSAndriy Voskoboinyk 
1907453645fSAndriy Voskoboinyk 	/* Kick TX. */
1917453645fSAndriy Voskoboinyk 	rtwn_write_2(sc, R92C_PCIE_CTRL_REG, (1 << qid));
1927453645fSAndriy Voskoboinyk 
1937453645fSAndriy Voskoboinyk 	return (0);
1947453645fSAndriy Voskoboinyk }
1957453645fSAndriy Voskoboinyk 
196d067ef0fSAndriy Voskoboinyk static int
rtwn_pci_tx_start_beacon(struct rtwn_softc * sc,struct mbuf * m,uint8_t * tx_desc,int id)197d067ef0fSAndriy Voskoboinyk rtwn_pci_tx_start_beacon(struct rtwn_softc *sc, struct mbuf *m,
198d067ef0fSAndriy Voskoboinyk     uint8_t *tx_desc, int id)
199d067ef0fSAndriy Voskoboinyk {
200d067ef0fSAndriy Voskoboinyk 	struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc);
201d067ef0fSAndriy Voskoboinyk 	struct rtwn_tx_ring *ring;
202d067ef0fSAndriy Voskoboinyk 	struct rtwn_tx_data *data;
203d067ef0fSAndriy Voskoboinyk 	struct rtwn_tx_desc_common *txd;
204d067ef0fSAndriy Voskoboinyk 	bus_dma_segment_t segs[1];
205d067ef0fSAndriy Voskoboinyk 	int nsegs, error, own;
206d067ef0fSAndriy Voskoboinyk 
207d067ef0fSAndriy Voskoboinyk 	RTWN_ASSERT_LOCKED(sc);
208d067ef0fSAndriy Voskoboinyk 
209d067ef0fSAndriy Voskoboinyk 	KASSERT(id == 0 || id == 1, ("bogus vap id %d\n", id));
210d067ef0fSAndriy Voskoboinyk 
211d067ef0fSAndriy Voskoboinyk 	ring = &pc->tx_ring[RTWN_PCI_BEACON_QUEUE];
212d067ef0fSAndriy Voskoboinyk 	data = &ring->tx_data[id];
213d067ef0fSAndriy Voskoboinyk 	txd = (struct rtwn_tx_desc_common *)
214d067ef0fSAndriy Voskoboinyk 	    ((uint8_t *)ring->desc + id * sc->txdesc_len);
215d067ef0fSAndriy Voskoboinyk 
216d067ef0fSAndriy Voskoboinyk 	bus_dmamap_sync(ring->desc_dmat, ring->desc_map,
217d067ef0fSAndriy Voskoboinyk 	    BUS_DMASYNC_POSTREAD);
218d067ef0fSAndriy Voskoboinyk 	own = !!(txd->flags0 & RTWN_FLAGS0_OWN);
219d067ef0fSAndriy Voskoboinyk 	error = 0;
220d067ef0fSAndriy Voskoboinyk 	if (!own || txd->pktlen != htole16(m->m_pkthdr.len)) {
221d067ef0fSAndriy Voskoboinyk 		if (!own) {
222d067ef0fSAndriy Voskoboinyk 			/* Copy Tx descriptor. */
223d067ef0fSAndriy Voskoboinyk 			rtwn_pci_copy_tx_desc(pc, txd, tx_desc);
224d067ef0fSAndriy Voskoboinyk 			txd->offset = sc->txdesc_len;
225d067ef0fSAndriy Voskoboinyk 		} else {
226d067ef0fSAndriy Voskoboinyk 			/* Reload mbuf. */
227d067ef0fSAndriy Voskoboinyk 			bus_dmamap_unload(ring->data_dmat, data->map);
228d067ef0fSAndriy Voskoboinyk 		}
229d067ef0fSAndriy Voskoboinyk 
230d067ef0fSAndriy Voskoboinyk 		error = bus_dmamap_load_mbuf_sg(ring->data_dmat,
231d067ef0fSAndriy Voskoboinyk 		    data->map, m, segs, &nsegs, BUS_DMA_NOWAIT);
232d067ef0fSAndriy Voskoboinyk 		if (error != 0) {
233d067ef0fSAndriy Voskoboinyk 			device_printf(sc->sc_dev,
234d067ef0fSAndriy Voskoboinyk 			    "can't map beacon (error %d)\n", error);
235d067ef0fSAndriy Voskoboinyk 			txd->flags0 &= ~RTWN_FLAGS0_OWN;
236d067ef0fSAndriy Voskoboinyk 			goto end;
237d067ef0fSAndriy Voskoboinyk 		}
238d067ef0fSAndriy Voskoboinyk 
239d067ef0fSAndriy Voskoboinyk 		txd->pktlen = htole16(m->m_pkthdr.len);
240d067ef0fSAndriy Voskoboinyk 		rtwn_pci_tx_postsetup(pc, txd, segs);
241d067ef0fSAndriy Voskoboinyk 		txd->flags0 |= RTWN_FLAGS0_OWN;
242d067ef0fSAndriy Voskoboinyk end:
243d067ef0fSAndriy Voskoboinyk 		bus_dmamap_sync(ring->desc_dmat, ring->desc_map,
244d067ef0fSAndriy Voskoboinyk 		    BUS_DMASYNC_PREWRITE);
245d067ef0fSAndriy Voskoboinyk 	}
246d067ef0fSAndriy Voskoboinyk 
247d067ef0fSAndriy Voskoboinyk 	/* Dump Tx descriptor. */
248d067ef0fSAndriy Voskoboinyk 	rtwn_dump_tx_desc(sc, txd);
249d067ef0fSAndriy Voskoboinyk 
250d067ef0fSAndriy Voskoboinyk 	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE);
251d067ef0fSAndriy Voskoboinyk 
252d067ef0fSAndriy Voskoboinyk 	return (0);
253d067ef0fSAndriy Voskoboinyk }
254d067ef0fSAndriy Voskoboinyk 
2557453645fSAndriy Voskoboinyk int
rtwn_pci_tx_start(struct rtwn_softc * sc,struct ieee80211_node * ni,struct mbuf * m,uint8_t * tx_desc,uint8_t type,int id)2567453645fSAndriy Voskoboinyk rtwn_pci_tx_start(struct rtwn_softc *sc, struct ieee80211_node *ni,
2577453645fSAndriy Voskoboinyk     struct mbuf *m, uint8_t *tx_desc, uint8_t type, int id)
2587453645fSAndriy Voskoboinyk {
2597453645fSAndriy Voskoboinyk 	int error = 0;
2607453645fSAndriy Voskoboinyk 
261d067ef0fSAndriy Voskoboinyk 	RTWN_ASSERT_LOCKED(sc);
2627453645fSAndriy Voskoboinyk 
263d067ef0fSAndriy Voskoboinyk 	if (ni == NULL)		/* beacon frame */
264d067ef0fSAndriy Voskoboinyk 		error = rtwn_pci_tx_start_beacon(sc, m, tx_desc, id);
265d067ef0fSAndriy Voskoboinyk 	else
266d067ef0fSAndriy Voskoboinyk 		error = rtwn_pci_tx_start_frame(sc, ni, m, tx_desc, type);
2677453645fSAndriy Voskoboinyk 
2687453645fSAndriy Voskoboinyk 	return (error);
2697453645fSAndriy Voskoboinyk }
270