xref: /freebsd-src/sys/dev/rtwn/usb/rtwn_usb_ep.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/condvar.h>
287453645fSAndriy Voskoboinyk #include <sys/mbuf.h>
297453645fSAndriy Voskoboinyk #include <sys/kernel.h>
307453645fSAndriy Voskoboinyk #include <sys/socket.h>
317453645fSAndriy Voskoboinyk #include <sys/systm.h>
327453645fSAndriy Voskoboinyk #include <sys/malloc.h>
337453645fSAndriy Voskoboinyk #include <sys/queue.h>
347453645fSAndriy Voskoboinyk #include <sys/taskqueue.h>
357453645fSAndriy Voskoboinyk #include <sys/bus.h>
367453645fSAndriy Voskoboinyk #include <sys/endian.h>
377453645fSAndriy Voskoboinyk 
387453645fSAndriy Voskoboinyk #include <dev/usb/usb.h>
397453645fSAndriy Voskoboinyk #include <dev/usb/usbdi.h>
407453645fSAndriy Voskoboinyk #include <dev/usb/usb_device.h>
417453645fSAndriy Voskoboinyk 
427453645fSAndriy Voskoboinyk #include <net/if.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 
487453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwnvar.h>
497453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwn_debug.h>
507453645fSAndriy Voskoboinyk 
517453645fSAndriy Voskoboinyk #include <dev/rtwn/usb/rtwn_usb_var.h>
527453645fSAndriy Voskoboinyk #include <dev/rtwn/usb/rtwn_usb_ep.h>
537453645fSAndriy Voskoboinyk #include <dev/rtwn/usb/rtwn_usb_rx.h>
547453645fSAndriy Voskoboinyk #include <dev/rtwn/usb/rtwn_usb_tx.h>
557453645fSAndriy Voskoboinyk 
567453645fSAndriy Voskoboinyk #include <dev/rtwn/rtl8192c/usb/r92cu_reg.h>
577453645fSAndriy Voskoboinyk 
58*d99eb823SAdrian Chadd static const struct usb_config rtwn_config_common[RTWN_BULK_EP_COUNT] = {
597453645fSAndriy Voskoboinyk 	[RTWN_BULK_RX] = {
607453645fSAndriy Voskoboinyk 		.type = UE_BULK,
617453645fSAndriy Voskoboinyk 		.endpoint = UE_ADDR_ANY,
627453645fSAndriy Voskoboinyk 		.direction = UE_DIR_IN,
637453645fSAndriy Voskoboinyk 		.flags = {
647453645fSAndriy Voskoboinyk 			.pipe_bof = 1,
657453645fSAndriy Voskoboinyk 			.short_xfer_ok = 1
667453645fSAndriy Voskoboinyk 		},
677453645fSAndriy Voskoboinyk 		.callback = rtwn_bulk_rx_callback,
687453645fSAndriy Voskoboinyk 	},
697453645fSAndriy Voskoboinyk 	[RTWN_BULK_TX_BE] = {
707453645fSAndriy Voskoboinyk 		.type = UE_BULK,
717453645fSAndriy Voskoboinyk 		.endpoint = UE_ADDR_ANY,
727453645fSAndriy Voskoboinyk 		.direction = UE_DIR_OUT,
7337376971SAndriy Voskoboinyk 		.bufsize = RTWN_USB_TXBUFSZ,
747453645fSAndriy Voskoboinyk 		.flags = {
757453645fSAndriy Voskoboinyk 			.ext_buffer = 1,
767453645fSAndriy Voskoboinyk 			.pipe_bof = 1,
777453645fSAndriy Voskoboinyk 			.force_short_xfer = 1,
787453645fSAndriy Voskoboinyk 		},
79*d99eb823SAdrian Chadd 		.callback = rtwn_bulk_tx_callback_be,
807453645fSAndriy Voskoboinyk 		.timeout = RTWN_TX_TIMEOUT,	/* ms */
817453645fSAndriy Voskoboinyk 	},
827453645fSAndriy Voskoboinyk 	[RTWN_BULK_TX_BK] = {
837453645fSAndriy Voskoboinyk 		.type = UE_BULK,
847453645fSAndriy Voskoboinyk 		.endpoint = UE_ADDR_ANY,
857453645fSAndriy Voskoboinyk 		.direction = UE_DIR_OUT,
8637376971SAndriy Voskoboinyk 		.bufsize = RTWN_USB_TXBUFSZ,
877453645fSAndriy Voskoboinyk 		.flags = {
887453645fSAndriy Voskoboinyk 			.ext_buffer = 1,
897453645fSAndriy Voskoboinyk 			.pipe_bof = 1,
907453645fSAndriy Voskoboinyk 			.force_short_xfer = 1,
917453645fSAndriy Voskoboinyk 		},
92*d99eb823SAdrian Chadd 		.callback = rtwn_bulk_tx_callback_bk,
937453645fSAndriy Voskoboinyk 		.timeout = RTWN_TX_TIMEOUT,	/* ms */
947453645fSAndriy Voskoboinyk 	},
957453645fSAndriy Voskoboinyk 	[RTWN_BULK_TX_VI] = {
967453645fSAndriy Voskoboinyk 		.type = UE_BULK,
977453645fSAndriy Voskoboinyk 		.endpoint = UE_ADDR_ANY,
987453645fSAndriy Voskoboinyk 		.direction = UE_DIR_OUT,
9937376971SAndriy Voskoboinyk 		.bufsize = RTWN_USB_TXBUFSZ,
1007453645fSAndriy Voskoboinyk 		.flags = {
1017453645fSAndriy Voskoboinyk 			.ext_buffer = 1,
1027453645fSAndriy Voskoboinyk 			.pipe_bof = 1,
1037453645fSAndriy Voskoboinyk 			.force_short_xfer = 1
1047453645fSAndriy Voskoboinyk 		},
105*d99eb823SAdrian Chadd 		.callback = rtwn_bulk_tx_callback_vi,
1067453645fSAndriy Voskoboinyk 		.timeout = RTWN_TX_TIMEOUT,	/* ms */
1077453645fSAndriy Voskoboinyk 	},
1087453645fSAndriy Voskoboinyk 	[RTWN_BULK_TX_VO] = {
1097453645fSAndriy Voskoboinyk 		.type = UE_BULK,
1107453645fSAndriy Voskoboinyk 		.endpoint = UE_ADDR_ANY,
1117453645fSAndriy Voskoboinyk 		.direction = UE_DIR_OUT,
11237376971SAndriy Voskoboinyk 		.bufsize = RTWN_USB_TXBUFSZ,
1137453645fSAndriy Voskoboinyk 		.flags = {
1147453645fSAndriy Voskoboinyk 			.ext_buffer = 1,
1157453645fSAndriy Voskoboinyk 			.pipe_bof = 1,
1167453645fSAndriy Voskoboinyk 			.force_short_xfer = 1
1177453645fSAndriy Voskoboinyk 		},
118*d99eb823SAdrian Chadd 		.callback = rtwn_bulk_tx_callback_vo,
1197453645fSAndriy Voskoboinyk 		.timeout = RTWN_TX_TIMEOUT,	/* ms */
1207453645fSAndriy Voskoboinyk 	},
1217453645fSAndriy Voskoboinyk };
1227453645fSAndriy Voskoboinyk 
1237453645fSAndriy Voskoboinyk static void
1247453645fSAndriy Voskoboinyk rtwn_usb_setup_queues(struct rtwn_usb_softc *uc)
1257453645fSAndriy Voskoboinyk {
1267453645fSAndriy Voskoboinyk 	struct rtwn_softc *sc = &uc->uc_sc;
1277453645fSAndriy Voskoboinyk 	int hasnq, haslq, nqueues, nqpages, nrempages;
1287453645fSAndriy Voskoboinyk 
1297453645fSAndriy Voskoboinyk 	/* Get Tx queues to USB endpoints mapping. */
1307453645fSAndriy Voskoboinyk 	hasnq = haslq = 0;
1317453645fSAndriy Voskoboinyk 	switch (uc->ntx) {
1327453645fSAndriy Voskoboinyk 	case 4:
1337453645fSAndriy Voskoboinyk 	case 3:
1347453645fSAndriy Voskoboinyk 		haslq = 1;
1357453645fSAndriy Voskoboinyk 		/* FALLTHROUGH */
1367453645fSAndriy Voskoboinyk 	case 2:
1377453645fSAndriy Voskoboinyk 		hasnq = 1;
1387453645fSAndriy Voskoboinyk 		/* FALLTHROUGH */
1397453645fSAndriy Voskoboinyk 	default:
1407453645fSAndriy Voskoboinyk 		break;
1417453645fSAndriy Voskoboinyk 	}
1427453645fSAndriy Voskoboinyk 	nqueues = 1 + hasnq + haslq;
1437453645fSAndriy Voskoboinyk 
1447453645fSAndriy Voskoboinyk 	/* Get the number of pages for each queue. */
1457453645fSAndriy Voskoboinyk 	nqpages = (sc->page_count - sc->npubqpages) / nqueues;
1467453645fSAndriy Voskoboinyk 
1477453645fSAndriy Voskoboinyk 	/*
1487453645fSAndriy Voskoboinyk 	 * The remaining pages are assigned to the high priority
1497453645fSAndriy Voskoboinyk 	 * queue.
1507453645fSAndriy Voskoboinyk 	 */
1517453645fSAndriy Voskoboinyk 	nrempages = (sc->page_count - sc->npubqpages) % nqueues;
1527453645fSAndriy Voskoboinyk 
1537453645fSAndriy Voskoboinyk 	sc->nhqpages = nqpages + nrempages;
1547453645fSAndriy Voskoboinyk 	sc->nnqpages = (hasnq ? nqpages : 0);
1557453645fSAndriy Voskoboinyk 	sc->nlqpages = (haslq ? nqpages : 0);
1567453645fSAndriy Voskoboinyk }
1577453645fSAndriy Voskoboinyk 
1587453645fSAndriy Voskoboinyk int
1597453645fSAndriy Voskoboinyk rtwn_usb_setup_endpoints(struct rtwn_usb_softc *uc)
1607453645fSAndriy Voskoboinyk {
1615c06728cSAndriy Voskoboinyk 	struct usb_config *rtwn_config;
1627453645fSAndriy Voskoboinyk 	struct rtwn_softc *sc = &uc->uc_sc;
1637453645fSAndriy Voskoboinyk 	const uint8_t iface_index = RTWN_IFACE_INDEX;
1647453645fSAndriy Voskoboinyk 	struct usb_endpoint *ep, *ep_end;
1657453645fSAndriy Voskoboinyk 	uint8_t addr[RTWN_MAX_EPOUT];
1667453645fSAndriy Voskoboinyk 	int error;
1677453645fSAndriy Voskoboinyk 
1687453645fSAndriy Voskoboinyk 	/* Determine the number of bulk-out pipes. */
1697453645fSAndriy Voskoboinyk 	uc->ntx = 0;
1707453645fSAndriy Voskoboinyk 	ep = uc->uc_udev->endpoints;
1717453645fSAndriy Voskoboinyk 	ep_end = uc->uc_udev->endpoints + uc->uc_udev->endpoints_max;
1727453645fSAndriy Voskoboinyk 	for (; ep != ep_end; ep++) {
1737453645fSAndriy Voskoboinyk 		uint8_t eaddr;
1747453645fSAndriy Voskoboinyk 
1757453645fSAndriy Voskoboinyk 		if ((ep->edesc == NULL) || (ep->iface_index != iface_index))
1767453645fSAndriy Voskoboinyk 			continue;
1777453645fSAndriy Voskoboinyk 
1787453645fSAndriy Voskoboinyk 		eaddr = ep->edesc->bEndpointAddress;
1797453645fSAndriy Voskoboinyk 		RTWN_DPRINTF(sc, RTWN_DEBUG_USB,
1807453645fSAndriy Voskoboinyk 		    "%s: endpoint: addr %u, direction %s\n", __func__,
1817453645fSAndriy Voskoboinyk 		    UE_GET_ADDR(eaddr), UE_GET_DIR(eaddr) == UE_DIR_OUT ?
1827453645fSAndriy Voskoboinyk 		    "output" : "input");
1837453645fSAndriy Voskoboinyk 
1847453645fSAndriy Voskoboinyk 		if (UE_GET_DIR(eaddr) == UE_DIR_OUT) {
1857453645fSAndriy Voskoboinyk 			if (uc->ntx == RTWN_MAX_EPOUT)
1867453645fSAndriy Voskoboinyk 				break;
1877453645fSAndriy Voskoboinyk 
1887453645fSAndriy Voskoboinyk 			addr[uc->ntx++] = UE_GET_ADDR(eaddr);
1897453645fSAndriy Voskoboinyk 		}
1907453645fSAndriy Voskoboinyk 	}
1917453645fSAndriy Voskoboinyk 	if (uc->ntx == 0 || uc->ntx > RTWN_MAX_EPOUT) {
1927453645fSAndriy Voskoboinyk 		device_printf(sc->sc_dev,
1937453645fSAndriy Voskoboinyk 		    "%s: invalid number of Tx bulk pipes (%d)\n", __func__,
1947453645fSAndriy Voskoboinyk 		    uc->ntx);
1957453645fSAndriy Voskoboinyk 		return (EINVAL);
1967453645fSAndriy Voskoboinyk 	}
1977453645fSAndriy Voskoboinyk 
1985c06728cSAndriy Voskoboinyk 	rtwn_config = malloc(sizeof(rtwn_config_common), M_TEMP, M_WAITOK);
1995c06728cSAndriy Voskoboinyk 	memcpy(rtwn_config, rtwn_config_common, sizeof(rtwn_config_common));
2005c06728cSAndriy Voskoboinyk 
2017453645fSAndriy Voskoboinyk 	/* NB: keep in sync with rtwn_dma_init(). */
2027453645fSAndriy Voskoboinyk 	rtwn_config[RTWN_BULK_TX_VO].endpoint = addr[0];
203*d99eb823SAdrian Chadd 	uc->wme2qid[WME_AC_VO] = RTWN_BULK_TX_VO;
2047453645fSAndriy Voskoboinyk 	switch (uc->ntx) {
2057453645fSAndriy Voskoboinyk 	case 4:
2067453645fSAndriy Voskoboinyk 	case 3:
2077453645fSAndriy Voskoboinyk 		rtwn_config[RTWN_BULK_TX_BE].endpoint = addr[2];
2087453645fSAndriy Voskoboinyk 		rtwn_config[RTWN_BULK_TX_BK].endpoint = addr[2];
2097453645fSAndriy Voskoboinyk 		rtwn_config[RTWN_BULK_TX_VI].endpoint = addr[1];
210*d99eb823SAdrian Chadd 		uc->wme2qid[WME_AC_BE] = RTWN_BULK_TX_BE;
211*d99eb823SAdrian Chadd 		uc->wme2qid[WME_AC_BK] = RTWN_BULK_TX_BE;
212*d99eb823SAdrian Chadd 		uc->wme2qid[WME_AC_VI] = RTWN_BULK_TX_VI;
2137453645fSAndriy Voskoboinyk 		break;
2147453645fSAndriy Voskoboinyk 	case 2:
2157453645fSAndriy Voskoboinyk 		rtwn_config[RTWN_BULK_TX_BE].endpoint = addr[1];
2167453645fSAndriy Voskoboinyk 		rtwn_config[RTWN_BULK_TX_BK].endpoint = addr[1];
2177453645fSAndriy Voskoboinyk 		rtwn_config[RTWN_BULK_TX_VI].endpoint = addr[0];
218*d99eb823SAdrian Chadd 		uc->wme2qid[WME_AC_BE] = RTWN_BULK_TX_VI;
219*d99eb823SAdrian Chadd 		uc->wme2qid[WME_AC_BK] = RTWN_BULK_TX_VI;
220*d99eb823SAdrian Chadd 		uc->wme2qid[WME_AC_VI] = RTWN_BULK_TX_VO;
2217453645fSAndriy Voskoboinyk 		break;
2227453645fSAndriy Voskoboinyk 	case 1:
2237453645fSAndriy Voskoboinyk 		rtwn_config[RTWN_BULK_TX_BE].endpoint = addr[0];
2247453645fSAndriy Voskoboinyk 		rtwn_config[RTWN_BULK_TX_BK].endpoint = addr[0];
2257453645fSAndriy Voskoboinyk 		rtwn_config[RTWN_BULK_TX_VI].endpoint = addr[0];
226*d99eb823SAdrian Chadd 
227*d99eb823SAdrian Chadd 		uc->wme2qid[WME_AC_BE] = RTWN_BULK_TX_VO;
228*d99eb823SAdrian Chadd 		uc->wme2qid[WME_AC_BK] = RTWN_BULK_TX_VO;
229*d99eb823SAdrian Chadd 		uc->wme2qid[WME_AC_VI] = RTWN_BULK_TX_VO;
2307453645fSAndriy Voskoboinyk 		break;
2317453645fSAndriy Voskoboinyk 	default:
2327453645fSAndriy Voskoboinyk 		KASSERT(0, ("unhandled number of endpoints %d\n", uc->ntx));
2337453645fSAndriy Voskoboinyk 		break;
2347453645fSAndriy Voskoboinyk 	}
2357453645fSAndriy Voskoboinyk 
2369dba6128SAndriy Voskoboinyk 	rtwn_config[RTWN_BULK_RX].bufsize =
2379dba6128SAndriy Voskoboinyk 	    uc->uc_rx_buf_size * RTWN_USB_RXBUFSZ_UNIT;
2387453645fSAndriy Voskoboinyk 	error = usbd_transfer_setup(uc->uc_udev, &iface_index,
239*d99eb823SAdrian Chadd 	    uc->uc_xfer, rtwn_config, RTWN_BULK_EP_COUNT, uc, &sc->sc_mtx);
2405c06728cSAndriy Voskoboinyk 	free(rtwn_config, M_TEMP);
2415c06728cSAndriy Voskoboinyk 
2427453645fSAndriy Voskoboinyk 	if (error) {
2437453645fSAndriy Voskoboinyk 		device_printf(sc->sc_dev, "could not allocate USB transfers, "
2447453645fSAndriy Voskoboinyk 		    "err=%s\n", usbd_errstr(error));
2457453645fSAndriy Voskoboinyk 		return (error);
2467453645fSAndriy Voskoboinyk 	}
2477453645fSAndriy Voskoboinyk 
2487453645fSAndriy Voskoboinyk 	/* Assign pages for each queue (if not done). */
2497453645fSAndriy Voskoboinyk 	if (sc->nhqpages == 0 && sc->nnqpages == 0 && sc->nlqpages == 0)
2507453645fSAndriy Voskoboinyk 		rtwn_usb_setup_queues(uc);
2517453645fSAndriy Voskoboinyk 
2527453645fSAndriy Voskoboinyk 	return (0);
2537453645fSAndriy Voskoboinyk }
2547453645fSAndriy Voskoboinyk 
2557453645fSAndriy Voskoboinyk uint16_t
2567453645fSAndriy Voskoboinyk rtwn_usb_get_qmap(struct rtwn_softc *sc)
2577453645fSAndriy Voskoboinyk {
2587453645fSAndriy Voskoboinyk 	struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc);
2597453645fSAndriy Voskoboinyk 
2607453645fSAndriy Voskoboinyk 	switch (uc->ntx) {
2617453645fSAndriy Voskoboinyk 	case 1:
2627453645fSAndriy Voskoboinyk 		return (R92C_TRXDMA_CTRL_QMAP_HQ);
2637453645fSAndriy Voskoboinyk 	case 2:
2647453645fSAndriy Voskoboinyk 		return (R92C_TRXDMA_CTRL_QMAP_HQ_NQ);
2657453645fSAndriy Voskoboinyk 	default:
2667453645fSAndriy Voskoboinyk 		return (R92C_TRXDMA_CTRL_QMAP_3EP);
2677453645fSAndriy Voskoboinyk 	}
2687453645fSAndriy Voskoboinyk }
269