xref: /freebsd/sys/dev/rtwn/usb/rtwn_usb_attach.c (revision 9dba612805f65ef396e2950568a6213dfe13aacd)
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 __FBSDID("$FreeBSD$");
237453645fSAndriy Voskoboinyk 
247453645fSAndriy Voskoboinyk #include <sys/param.h>
257453645fSAndriy Voskoboinyk #include <sys/sysctl.h>
267453645fSAndriy Voskoboinyk #include <sys/lock.h>
277453645fSAndriy Voskoboinyk #include <sys/mutex.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/module.h>
347453645fSAndriy Voskoboinyk #include <sys/bus.h>
357453645fSAndriy Voskoboinyk #include <sys/endian.h>
367453645fSAndriy Voskoboinyk #include <sys/linker.h>
377453645fSAndriy Voskoboinyk #include <sys/kdb.h>
387453645fSAndriy Voskoboinyk 
397453645fSAndriy Voskoboinyk #include <net/if.h>
407453645fSAndriy Voskoboinyk #include <net/if_var.h>
417453645fSAndriy Voskoboinyk #include <net/ethernet.h>
427453645fSAndriy Voskoboinyk #include <net/if_media.h>
437453645fSAndriy Voskoboinyk 
447453645fSAndriy Voskoboinyk #include <net80211/ieee80211_var.h>
457453645fSAndriy Voskoboinyk 
467453645fSAndriy Voskoboinyk #include <dev/usb/usb.h>
477453645fSAndriy Voskoboinyk #include <dev/usb/usbdi.h>
487453645fSAndriy Voskoboinyk #include "usbdevs.h"
497453645fSAndriy Voskoboinyk 
507453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwnvar.h>
517453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwn_nop.h>
527453645fSAndriy Voskoboinyk 
537453645fSAndriy Voskoboinyk #include <dev/rtwn/usb/rtwn_usb_var.h>
547453645fSAndriy Voskoboinyk 
557453645fSAndriy Voskoboinyk #include <dev/rtwn/usb/rtwn_usb_attach.h>
567453645fSAndriy Voskoboinyk #include <dev/rtwn/usb/rtwn_usb_ep.h>
577453645fSAndriy Voskoboinyk #include <dev/rtwn/usb/rtwn_usb_reg.h>
587453645fSAndriy Voskoboinyk #include <dev/rtwn/usb/rtwn_usb_tx.h>
597453645fSAndriy Voskoboinyk 
607453645fSAndriy Voskoboinyk #include <dev/rtwn/rtl8192c/r92c_reg.h>
617453645fSAndriy Voskoboinyk 
627453645fSAndriy Voskoboinyk static device_probe_t	rtwn_usb_match;
637453645fSAndriy Voskoboinyk static device_attach_t	rtwn_usb_attach;
647453645fSAndriy Voskoboinyk static device_detach_t	rtwn_usb_detach;
657453645fSAndriy Voskoboinyk static device_suspend_t	rtwn_usb_suspend;
667453645fSAndriy Voskoboinyk static device_resume_t	rtwn_usb_resume;
677453645fSAndriy Voskoboinyk 
687453645fSAndriy Voskoboinyk static int	rtwn_usb_alloc_list(struct rtwn_softc *,
697453645fSAndriy Voskoboinyk 		    struct rtwn_data[], int, int);
707453645fSAndriy Voskoboinyk static int	rtwn_usb_alloc_rx_list(struct rtwn_softc *);
717453645fSAndriy Voskoboinyk static int	rtwn_usb_alloc_tx_list(struct rtwn_softc *);
727453645fSAndriy Voskoboinyk static void	rtwn_usb_free_list(struct rtwn_softc *,
737453645fSAndriy Voskoboinyk 		    struct rtwn_data data[], int);
747453645fSAndriy Voskoboinyk static void	rtwn_usb_free_rx_list(struct rtwn_softc *);
757453645fSAndriy Voskoboinyk static void	rtwn_usb_free_tx_list(struct rtwn_softc *);
767453645fSAndriy Voskoboinyk static void	rtwn_usb_reset_lists(struct rtwn_softc *,
777453645fSAndriy Voskoboinyk 		    struct ieee80211vap *);
787453645fSAndriy Voskoboinyk static void	rtwn_usb_reset_tx_list(struct rtwn_usb_softc *,
797453645fSAndriy Voskoboinyk 		    rtwn_datahead *, struct ieee80211vap *);
80*9dba6128SAndriy Voskoboinyk static void	rtwn_usb_reset_rx_list(struct rtwn_usb_softc *);
817453645fSAndriy Voskoboinyk static void	rtwn_usb_start_xfers(struct rtwn_softc *);
827453645fSAndriy Voskoboinyk static void	rtwn_usb_abort_xfers(struct rtwn_softc *);
837453645fSAndriy Voskoboinyk static int	rtwn_usb_fw_write_block(struct rtwn_softc *,
847453645fSAndriy Voskoboinyk 		    const uint8_t *, uint16_t, int);
85d067ef0fSAndriy Voskoboinyk static void	rtwn_usb_drop_incorrect_tx(struct rtwn_softc *);
867453645fSAndriy Voskoboinyk static void	rtwn_usb_attach_methods(struct rtwn_softc *);
87*9dba6128SAndriy Voskoboinyk static void	rtwn_usb_sysctlattach(struct rtwn_softc *);
887453645fSAndriy Voskoboinyk 
897453645fSAndriy Voskoboinyk #define RTWN_CONFIG_INDEX	0
907453645fSAndriy Voskoboinyk 
917453645fSAndriy Voskoboinyk 
927453645fSAndriy Voskoboinyk static int
937453645fSAndriy Voskoboinyk rtwn_usb_match(device_t self)
947453645fSAndriy Voskoboinyk {
957453645fSAndriy Voskoboinyk 	struct usb_attach_arg *uaa = device_get_ivars(self);
967453645fSAndriy Voskoboinyk 
977453645fSAndriy Voskoboinyk 	if (uaa->usb_mode != USB_MODE_HOST)
987453645fSAndriy Voskoboinyk 		return (ENXIO);
997453645fSAndriy Voskoboinyk 	if (uaa->info.bConfigIndex != RTWN_CONFIG_INDEX)
1007453645fSAndriy Voskoboinyk 		return (ENXIO);
1017453645fSAndriy Voskoboinyk 	if (uaa->info.bIfaceIndex != RTWN_IFACE_INDEX)
1027453645fSAndriy Voskoboinyk 		return (ENXIO);
1037453645fSAndriy Voskoboinyk 
1047453645fSAndriy Voskoboinyk 	return (usbd_lookup_id_by_uaa(rtwn_devs, sizeof(rtwn_devs), uaa));
1057453645fSAndriy Voskoboinyk }
1067453645fSAndriy Voskoboinyk 
1077453645fSAndriy Voskoboinyk static int
1087453645fSAndriy Voskoboinyk rtwn_usb_alloc_list(struct rtwn_softc *sc, struct rtwn_data data[],
1097453645fSAndriy Voskoboinyk     int ndata, int maxsz)
1107453645fSAndriy Voskoboinyk {
1117453645fSAndriy Voskoboinyk 	int i, error;
1127453645fSAndriy Voskoboinyk 
1137453645fSAndriy Voskoboinyk 	for (i = 0; i < ndata; i++) {
1147453645fSAndriy Voskoboinyk 		struct rtwn_data *dp = &data[i];
1157453645fSAndriy Voskoboinyk 		dp->m = NULL;
1167453645fSAndriy Voskoboinyk 		dp->buf = malloc(maxsz, M_USBDEV, M_NOWAIT);
1177453645fSAndriy Voskoboinyk 		if (dp->buf == NULL) {
1187453645fSAndriy Voskoboinyk 			device_printf(sc->sc_dev,
1197453645fSAndriy Voskoboinyk 			    "could not allocate buffer\n");
1207453645fSAndriy Voskoboinyk 			error = ENOMEM;
1217453645fSAndriy Voskoboinyk 			goto fail;
1227453645fSAndriy Voskoboinyk 		}
1237453645fSAndriy Voskoboinyk 		dp->ni = NULL;
1247453645fSAndriy Voskoboinyk 	}
1257453645fSAndriy Voskoboinyk 
1267453645fSAndriy Voskoboinyk 	return (0);
1277453645fSAndriy Voskoboinyk fail:
1287453645fSAndriy Voskoboinyk 	rtwn_usb_free_list(sc, data, ndata);
1297453645fSAndriy Voskoboinyk 	return (error);
1307453645fSAndriy Voskoboinyk }
1317453645fSAndriy Voskoboinyk 
1327453645fSAndriy Voskoboinyk static int
1337453645fSAndriy Voskoboinyk rtwn_usb_alloc_rx_list(struct rtwn_softc *sc)
1347453645fSAndriy Voskoboinyk {
1357453645fSAndriy Voskoboinyk 	struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc);
1367453645fSAndriy Voskoboinyk 	int error, i;
1377453645fSAndriy Voskoboinyk 
1387453645fSAndriy Voskoboinyk 	error = rtwn_usb_alloc_list(sc, uc->uc_rx, RTWN_USB_RX_LIST_COUNT,
139*9dba6128SAndriy Voskoboinyk 	    uc->uc_rx_buf_size * RTWN_USB_RXBUFSZ_UNIT);
1407453645fSAndriy Voskoboinyk 	if (error != 0)
1417453645fSAndriy Voskoboinyk 		return (error);
1427453645fSAndriy Voskoboinyk 
1437453645fSAndriy Voskoboinyk 	STAILQ_INIT(&uc->uc_rx_active);
1447453645fSAndriy Voskoboinyk 	STAILQ_INIT(&uc->uc_rx_inactive);
1457453645fSAndriy Voskoboinyk 
1467453645fSAndriy Voskoboinyk 	for (i = 0; i < RTWN_USB_RX_LIST_COUNT; i++)
1477453645fSAndriy Voskoboinyk 		STAILQ_INSERT_HEAD(&uc->uc_rx_inactive, &uc->uc_rx[i], next);
1487453645fSAndriy Voskoboinyk 
1497453645fSAndriy Voskoboinyk 	return (0);
1507453645fSAndriy Voskoboinyk }
1517453645fSAndriy Voskoboinyk 
1527453645fSAndriy Voskoboinyk static int
1537453645fSAndriy Voskoboinyk rtwn_usb_alloc_tx_list(struct rtwn_softc *sc)
1547453645fSAndriy Voskoboinyk {
1557453645fSAndriy Voskoboinyk 	struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc);
1567453645fSAndriy Voskoboinyk 	int error, i;
1577453645fSAndriy Voskoboinyk 
1587453645fSAndriy Voskoboinyk 	error = rtwn_usb_alloc_list(sc, uc->uc_tx, RTWN_USB_TX_LIST_COUNT,
15937376971SAndriy Voskoboinyk 	    RTWN_USB_TXBUFSZ);
1607453645fSAndriy Voskoboinyk 	if (error != 0)
1617453645fSAndriy Voskoboinyk 		return (error);
1627453645fSAndriy Voskoboinyk 
1637453645fSAndriy Voskoboinyk 	STAILQ_INIT(&uc->uc_tx_active);
1647453645fSAndriy Voskoboinyk 	STAILQ_INIT(&uc->uc_tx_inactive);
1657453645fSAndriy Voskoboinyk 	STAILQ_INIT(&uc->uc_tx_pending);
1667453645fSAndriy Voskoboinyk 
1677453645fSAndriy Voskoboinyk 	for (i = 0; i < RTWN_USB_TX_LIST_COUNT; i++)
1687453645fSAndriy Voskoboinyk 		STAILQ_INSERT_HEAD(&uc->uc_tx_inactive, &uc->uc_tx[i], next);
1697453645fSAndriy Voskoboinyk 
1707453645fSAndriy Voskoboinyk 	return (0);
1717453645fSAndriy Voskoboinyk }
1727453645fSAndriy Voskoboinyk 
1737453645fSAndriy Voskoboinyk static void
1747453645fSAndriy Voskoboinyk rtwn_usb_free_list(struct rtwn_softc *sc, struct rtwn_data data[], int ndata)
1757453645fSAndriy Voskoboinyk {
1767453645fSAndriy Voskoboinyk 	int i;
1777453645fSAndriy Voskoboinyk 
1787453645fSAndriy Voskoboinyk 	for (i = 0; i < ndata; i++) {
1797453645fSAndriy Voskoboinyk 		struct rtwn_data *dp = &data[i];
1807453645fSAndriy Voskoboinyk 
1817453645fSAndriy Voskoboinyk 		if (dp->buf != NULL) {
1827453645fSAndriy Voskoboinyk 			free(dp->buf, M_USBDEV);
1837453645fSAndriy Voskoboinyk 			dp->buf = NULL;
1847453645fSAndriy Voskoboinyk 		}
1857453645fSAndriy Voskoboinyk 		if (dp->ni != NULL) {
1867453645fSAndriy Voskoboinyk 			ieee80211_free_node(dp->ni);
1877453645fSAndriy Voskoboinyk 			dp->ni = NULL;
1887453645fSAndriy Voskoboinyk 		}
1897453645fSAndriy Voskoboinyk 		if (dp->m != NULL) {
1907453645fSAndriy Voskoboinyk 			m_freem(dp->m);
1917453645fSAndriy Voskoboinyk 			dp->m = NULL;
1927453645fSAndriy Voskoboinyk 		}
1937453645fSAndriy Voskoboinyk 	}
1947453645fSAndriy Voskoboinyk }
1957453645fSAndriy Voskoboinyk 
1967453645fSAndriy Voskoboinyk static void
1977453645fSAndriy Voskoboinyk rtwn_usb_free_rx_list(struct rtwn_softc *sc)
1987453645fSAndriy Voskoboinyk {
1997453645fSAndriy Voskoboinyk 	struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc);
2007453645fSAndriy Voskoboinyk 
2017453645fSAndriy Voskoboinyk 	rtwn_usb_free_list(sc, uc->uc_rx, RTWN_USB_RX_LIST_COUNT);
2027453645fSAndriy Voskoboinyk 
203*9dba6128SAndriy Voskoboinyk 	uc->uc_rx_stat_len = 0;
204*9dba6128SAndriy Voskoboinyk 	uc->uc_rx_off = 0;
205*9dba6128SAndriy Voskoboinyk 
2067453645fSAndriy Voskoboinyk 	STAILQ_INIT(&uc->uc_rx_active);
2077453645fSAndriy Voskoboinyk 	STAILQ_INIT(&uc->uc_rx_inactive);
2087453645fSAndriy Voskoboinyk }
2097453645fSAndriy Voskoboinyk 
2107453645fSAndriy Voskoboinyk static void
2117453645fSAndriy Voskoboinyk rtwn_usb_free_tx_list(struct rtwn_softc *sc)
2127453645fSAndriy Voskoboinyk {
2137453645fSAndriy Voskoboinyk 	struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc);
2147453645fSAndriy Voskoboinyk 
2157453645fSAndriy Voskoboinyk 	rtwn_usb_free_list(sc, uc->uc_tx, RTWN_USB_TX_LIST_COUNT);
2167453645fSAndriy Voskoboinyk 
2177453645fSAndriy Voskoboinyk 	STAILQ_INIT(&uc->uc_tx_active);
2187453645fSAndriy Voskoboinyk 	STAILQ_INIT(&uc->uc_tx_inactive);
2197453645fSAndriy Voskoboinyk 	STAILQ_INIT(&uc->uc_tx_pending);
2207453645fSAndriy Voskoboinyk }
2217453645fSAndriy Voskoboinyk 
2227453645fSAndriy Voskoboinyk static void
2237453645fSAndriy Voskoboinyk rtwn_usb_reset_lists(struct rtwn_softc *sc, struct ieee80211vap *vap)
2247453645fSAndriy Voskoboinyk {
2257453645fSAndriy Voskoboinyk 	struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc);
2267453645fSAndriy Voskoboinyk 
2277453645fSAndriy Voskoboinyk 	RTWN_ASSERT_LOCKED(sc);
2287453645fSAndriy Voskoboinyk 
2297453645fSAndriy Voskoboinyk 	rtwn_usb_reset_tx_list(uc, &uc->uc_tx_active, vap);
2307453645fSAndriy Voskoboinyk 	rtwn_usb_reset_tx_list(uc, &uc->uc_tx_pending, vap);
231*9dba6128SAndriy Voskoboinyk 	if (vap == NULL) {
232*9dba6128SAndriy Voskoboinyk 		rtwn_usb_reset_rx_list(uc);
2337453645fSAndriy Voskoboinyk 		sc->qfullmsk = 0;
2347453645fSAndriy Voskoboinyk 	}
235*9dba6128SAndriy Voskoboinyk }
2367453645fSAndriy Voskoboinyk 
2377453645fSAndriy Voskoboinyk static void
2387453645fSAndriy Voskoboinyk rtwn_usb_reset_tx_list(struct rtwn_usb_softc *uc,
2397453645fSAndriy Voskoboinyk     rtwn_datahead *head, struct ieee80211vap *vap)
2407453645fSAndriy Voskoboinyk {
2417453645fSAndriy Voskoboinyk 	struct rtwn_vap *uvp = RTWN_VAP(vap);
2427453645fSAndriy Voskoboinyk 	struct rtwn_data *dp, *tmp;
2437453645fSAndriy Voskoboinyk 	int id;
2447453645fSAndriy Voskoboinyk 
2457453645fSAndriy Voskoboinyk 	id = (uvp != NULL ? uvp->id : RTWN_VAP_ID_INVALID);
2467453645fSAndriy Voskoboinyk 
2477453645fSAndriy Voskoboinyk 	STAILQ_FOREACH_SAFE(dp, head, next, tmp) {
2487453645fSAndriy Voskoboinyk 		if (vap == NULL || (dp->ni == NULL &&
2497453645fSAndriy Voskoboinyk 		    (dp->id == id || id == RTWN_VAP_ID_INVALID)) ||
2507453645fSAndriy Voskoboinyk 		    (dp->ni != NULL && dp->ni->ni_vap == vap)) {
2517453645fSAndriy Voskoboinyk 			if (dp->ni != NULL) {
2527453645fSAndriy Voskoboinyk 				ieee80211_free_node(dp->ni);
2537453645fSAndriy Voskoboinyk 				dp->ni = NULL;
2547453645fSAndriy Voskoboinyk 			}
2557453645fSAndriy Voskoboinyk 
2567453645fSAndriy Voskoboinyk 			if (dp->m != NULL) {
2577453645fSAndriy Voskoboinyk 				m_freem(dp->m);
2587453645fSAndriy Voskoboinyk 				dp->m = NULL;
2597453645fSAndriy Voskoboinyk 			}
2607453645fSAndriy Voskoboinyk 
2617453645fSAndriy Voskoboinyk 			STAILQ_REMOVE(head, dp, rtwn_data, next);
2627453645fSAndriy Voskoboinyk 			STAILQ_INSERT_TAIL(&uc->uc_tx_inactive, dp, next);
2637453645fSAndriy Voskoboinyk 		}
2647453645fSAndriy Voskoboinyk 	}
2657453645fSAndriy Voskoboinyk }
2667453645fSAndriy Voskoboinyk 
2677453645fSAndriy Voskoboinyk static void
268*9dba6128SAndriy Voskoboinyk rtwn_usb_reset_rx_list(struct rtwn_usb_softc *uc)
269*9dba6128SAndriy Voskoboinyk {
270*9dba6128SAndriy Voskoboinyk 	int i;
271*9dba6128SAndriy Voskoboinyk 
272*9dba6128SAndriy Voskoboinyk 	for (i = 0; i < RTWN_USB_RX_LIST_COUNT; i++) {
273*9dba6128SAndriy Voskoboinyk 		struct rtwn_data *dp = &uc->uc_rx[i];
274*9dba6128SAndriy Voskoboinyk 
275*9dba6128SAndriy Voskoboinyk 		if (dp->m != NULL) {
276*9dba6128SAndriy Voskoboinyk 			m_freem(dp->m);
277*9dba6128SAndriy Voskoboinyk 			dp->m = NULL;
278*9dba6128SAndriy Voskoboinyk 		}
279*9dba6128SAndriy Voskoboinyk 	}
280*9dba6128SAndriy Voskoboinyk 	uc->uc_rx_stat_len = 0;
281*9dba6128SAndriy Voskoboinyk 	uc->uc_rx_off = 0;
282*9dba6128SAndriy Voskoboinyk }
283*9dba6128SAndriy Voskoboinyk 
284*9dba6128SAndriy Voskoboinyk static void
2857453645fSAndriy Voskoboinyk rtwn_usb_start_xfers(struct rtwn_softc *sc)
2867453645fSAndriy Voskoboinyk {
2877453645fSAndriy Voskoboinyk 	struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc);
2887453645fSAndriy Voskoboinyk 
2897453645fSAndriy Voskoboinyk 	usbd_transfer_start(uc->uc_xfer[RTWN_BULK_RX]);
2907453645fSAndriy Voskoboinyk }
2917453645fSAndriy Voskoboinyk 
2927453645fSAndriy Voskoboinyk static void
2937453645fSAndriy Voskoboinyk rtwn_usb_abort_xfers(struct rtwn_softc *sc)
2947453645fSAndriy Voskoboinyk {
2957453645fSAndriy Voskoboinyk 	struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc);
2967453645fSAndriy Voskoboinyk 	int i;
2977453645fSAndriy Voskoboinyk 
2987453645fSAndriy Voskoboinyk 	RTWN_ASSERT_LOCKED(sc);
2997453645fSAndriy Voskoboinyk 
3007453645fSAndriy Voskoboinyk 	/* abort any pending transfers */
30177cbc479SAndriy Voskoboinyk 	RTWN_UNLOCK(sc);
3027453645fSAndriy Voskoboinyk 	for (i = 0; i < RTWN_N_TRANSFER; i++)
30377cbc479SAndriy Voskoboinyk 		usbd_transfer_drain(uc->uc_xfer[i]);
30477cbc479SAndriy Voskoboinyk 	RTWN_LOCK(sc);
3057453645fSAndriy Voskoboinyk }
3067453645fSAndriy Voskoboinyk 
3077453645fSAndriy Voskoboinyk static int
3087453645fSAndriy Voskoboinyk rtwn_usb_fw_write_block(struct rtwn_softc *sc, const uint8_t *buf,
3097453645fSAndriy Voskoboinyk     uint16_t reg, int mlen)
3107453645fSAndriy Voskoboinyk {
3117453645fSAndriy Voskoboinyk 	int error;
3127453645fSAndriy Voskoboinyk 
3137453645fSAndriy Voskoboinyk 	/* XXX fix this deconst */
3147453645fSAndriy Voskoboinyk 	error = rtwn_usb_write_region_1(sc, reg, __DECONST(uint8_t *, buf),
3157453645fSAndriy Voskoboinyk 	    mlen);
3167453645fSAndriy Voskoboinyk 
3177453645fSAndriy Voskoboinyk 	return (error);
3187453645fSAndriy Voskoboinyk }
3197453645fSAndriy Voskoboinyk 
3207453645fSAndriy Voskoboinyk static void
3217453645fSAndriy Voskoboinyk rtwn_usb_drop_incorrect_tx(struct rtwn_softc *sc)
3227453645fSAndriy Voskoboinyk {
3237453645fSAndriy Voskoboinyk 
3247453645fSAndriy Voskoboinyk 	rtwn_setbits_1_shift(sc, R92C_TXDMA_OFFSET_CHK, 0,
3257453645fSAndriy Voskoboinyk 	    R92C_TXDMA_OFFSET_DROP_DATA_EN, 1);
3267453645fSAndriy Voskoboinyk }
3277453645fSAndriy Voskoboinyk 
3287453645fSAndriy Voskoboinyk static void
3297453645fSAndriy Voskoboinyk rtwn_usb_attach_methods(struct rtwn_softc *sc)
3307453645fSAndriy Voskoboinyk {
3317453645fSAndriy Voskoboinyk 	sc->sc_write_1		= rtwn_usb_write_1;
3327453645fSAndriy Voskoboinyk 	sc->sc_write_2		= rtwn_usb_write_2;
3337453645fSAndriy Voskoboinyk 	sc->sc_write_4		= rtwn_usb_write_4;
3347453645fSAndriy Voskoboinyk 	sc->sc_read_1		= rtwn_usb_read_1;
3357453645fSAndriy Voskoboinyk 	sc->sc_read_2		= rtwn_usb_read_2;
3367453645fSAndriy Voskoboinyk 	sc->sc_read_4		= rtwn_usb_read_4;
3377453645fSAndriy Voskoboinyk 	sc->sc_delay		= rtwn_usb_delay;
3387453645fSAndriy Voskoboinyk 	sc->sc_tx_start		= rtwn_usb_tx_start;
3397453645fSAndriy Voskoboinyk 	sc->sc_start_xfers	= rtwn_usb_start_xfers;
3407453645fSAndriy Voskoboinyk 	sc->sc_reset_lists	= rtwn_usb_reset_lists;
3417453645fSAndriy Voskoboinyk 	sc->sc_abort_xfers	= rtwn_usb_abort_xfers;
3427453645fSAndriy Voskoboinyk 	sc->sc_fw_write_block	= rtwn_usb_fw_write_block;
3437453645fSAndriy Voskoboinyk 	sc->sc_get_qmap		= rtwn_usb_get_qmap;
3447453645fSAndriy Voskoboinyk 	sc->sc_set_desc_addr	= rtwn_nop_softc;
3457453645fSAndriy Voskoboinyk 	sc->sc_drop_incorrect_tx = rtwn_usb_drop_incorrect_tx;
346d067ef0fSAndriy Voskoboinyk 	sc->sc_beacon_update_begin = rtwn_nop_softc_vap;
347d067ef0fSAndriy Voskoboinyk 	sc->sc_beacon_update_end = rtwn_nop_softc_vap;
348d067ef0fSAndriy Voskoboinyk 	sc->sc_beacon_unload	= rtwn_nop_softc_int;
349d067ef0fSAndriy Voskoboinyk 
350d067ef0fSAndriy Voskoboinyk 	sc->bcn_check_interval	= 100;
3517453645fSAndriy Voskoboinyk }
3527453645fSAndriy Voskoboinyk 
353*9dba6128SAndriy Voskoboinyk static void
354*9dba6128SAndriy Voskoboinyk rtwn_usb_sysctlattach(struct rtwn_softc *sc)
355*9dba6128SAndriy Voskoboinyk {
356*9dba6128SAndriy Voskoboinyk 	struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc);
357*9dba6128SAndriy Voskoboinyk 	struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev);
358*9dba6128SAndriy Voskoboinyk 	struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev);
359*9dba6128SAndriy Voskoboinyk 	char str[64];
360*9dba6128SAndriy Voskoboinyk 	int ret;
361*9dba6128SAndriy Voskoboinyk 
362*9dba6128SAndriy Voskoboinyk 	ret = snprintf(str, sizeof(str),
363*9dba6128SAndriy Voskoboinyk 	    "Rx buffer size, 512-byte units [%d...%d]",
364*9dba6128SAndriy Voskoboinyk 	    RTWN_USB_RXBUFSZ_MIN, RTWN_USB_RXBUFSZ_MAX);
365*9dba6128SAndriy Voskoboinyk 	KASSERT(ret > 0, ("ret (%d) <= 0!\n", ret));
366*9dba6128SAndriy Voskoboinyk 	(void) ret;
367*9dba6128SAndriy Voskoboinyk 
368*9dba6128SAndriy Voskoboinyk 	uc->uc_rx_buf_size = RTWN_USB_RXBUFSZ_DEF;
369*9dba6128SAndriy Voskoboinyk 	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
370*9dba6128SAndriy Voskoboinyk 	    "rx_buf_size", CTLFLAG_RDTUN, &uc->uc_rx_buf_size,
371*9dba6128SAndriy Voskoboinyk 	    uc->uc_rx_buf_size, str);
372*9dba6128SAndriy Voskoboinyk 	if (uc->uc_rx_buf_size < RTWN_USB_RXBUFSZ_MIN)
373*9dba6128SAndriy Voskoboinyk 		uc->uc_rx_buf_size = RTWN_USB_RXBUFSZ_MIN;
374*9dba6128SAndriy Voskoboinyk 	if (uc->uc_rx_buf_size > RTWN_USB_RXBUFSZ_MAX)
375*9dba6128SAndriy Voskoboinyk 		uc->uc_rx_buf_size = RTWN_USB_RXBUFSZ_MAX;
376*9dba6128SAndriy Voskoboinyk }
377*9dba6128SAndriy Voskoboinyk 
3787453645fSAndriy Voskoboinyk static int
3797453645fSAndriy Voskoboinyk rtwn_usb_attach(device_t self)
3807453645fSAndriy Voskoboinyk {
3817453645fSAndriy Voskoboinyk 	struct usb_attach_arg *uaa = device_get_ivars(self);
3827453645fSAndriy Voskoboinyk 	struct rtwn_usb_softc *uc = device_get_softc(self);
3837453645fSAndriy Voskoboinyk 	struct rtwn_softc *sc = &uc->uc_sc;
3847453645fSAndriy Voskoboinyk 	struct ieee80211com *ic = &sc->sc_ic;
3857453645fSAndriy Voskoboinyk 	int error;
3867453645fSAndriy Voskoboinyk 
3877453645fSAndriy Voskoboinyk 	device_set_usb_desc(self);
3887453645fSAndriy Voskoboinyk 	uc->uc_udev = uaa->device;
3897453645fSAndriy Voskoboinyk 	sc->sc_dev = self;
3907453645fSAndriy Voskoboinyk 	ic->ic_name = device_get_nameunit(self);
3917453645fSAndriy Voskoboinyk 
3927453645fSAndriy Voskoboinyk 	/* Need to be initialized early. */
3937453645fSAndriy Voskoboinyk 	rtwn_sysctlattach(sc);
394*9dba6128SAndriy Voskoboinyk 	rtwn_usb_sysctlattach(sc);
3957453645fSAndriy Voskoboinyk 	mtx_init(&sc->sc_mtx, ic->ic_name, MTX_NETWORK_LOCK, MTX_DEF);
3967453645fSAndriy Voskoboinyk 
3977453645fSAndriy Voskoboinyk 	rtwn_usb_attach_methods(sc);
3987453645fSAndriy Voskoboinyk 	rtwn_usb_attach_private(uc, USB_GET_DRIVER_INFO(uaa));
3997453645fSAndriy Voskoboinyk 
4007453645fSAndriy Voskoboinyk 	error = rtwn_usb_setup_endpoints(uc);
4017453645fSAndriy Voskoboinyk 	if (error != 0)
4027453645fSAndriy Voskoboinyk 		goto detach;
4037453645fSAndriy Voskoboinyk 
4047453645fSAndriy Voskoboinyk 	/* Allocate Tx/Rx buffers. */
4057453645fSAndriy Voskoboinyk 	error = rtwn_usb_alloc_rx_list(sc);
4067453645fSAndriy Voskoboinyk 	if (error != 0)
4077453645fSAndriy Voskoboinyk 		goto detach;
4087453645fSAndriy Voskoboinyk 
4097453645fSAndriy Voskoboinyk 	error = rtwn_usb_alloc_tx_list(sc);
4107453645fSAndriy Voskoboinyk 	if (error != 0)
4117453645fSAndriy Voskoboinyk 		goto detach;
4127453645fSAndriy Voskoboinyk 
4137453645fSAndriy Voskoboinyk 	/* Generic attach. */
4147453645fSAndriy Voskoboinyk 	error = rtwn_attach(sc);
4157453645fSAndriy Voskoboinyk 	if (error != 0)
4167453645fSAndriy Voskoboinyk 		goto detach;
4177453645fSAndriy Voskoboinyk 
4187453645fSAndriy Voskoboinyk 	return (0);
4197453645fSAndriy Voskoboinyk 
4207453645fSAndriy Voskoboinyk detach:
4217453645fSAndriy Voskoboinyk 	rtwn_usb_detach(self);		/* failure */
4227453645fSAndriy Voskoboinyk 	return (ENXIO);
4237453645fSAndriy Voskoboinyk }
4247453645fSAndriy Voskoboinyk 
4257453645fSAndriy Voskoboinyk static int
4267453645fSAndriy Voskoboinyk rtwn_usb_detach(device_t self)
4277453645fSAndriy Voskoboinyk {
4287453645fSAndriy Voskoboinyk 	struct rtwn_usb_softc *uc = device_get_softc(self);
4297453645fSAndriy Voskoboinyk 	struct rtwn_softc *sc = &uc->uc_sc;
4307453645fSAndriy Voskoboinyk 
4317453645fSAndriy Voskoboinyk 	/* Generic detach. */
4327453645fSAndriy Voskoboinyk 	rtwn_detach(sc);
4337453645fSAndriy Voskoboinyk 
4347453645fSAndriy Voskoboinyk 	/* Free Tx/Rx buffers. */
4357453645fSAndriy Voskoboinyk 	rtwn_usb_free_tx_list(sc);
4367453645fSAndriy Voskoboinyk 	rtwn_usb_free_rx_list(sc);
4377453645fSAndriy Voskoboinyk 
4387453645fSAndriy Voskoboinyk 	/* Detach all USB transfers. */
4397453645fSAndriy Voskoboinyk 	usbd_transfer_unsetup(uc->uc_xfer, RTWN_N_TRANSFER);
4407453645fSAndriy Voskoboinyk 
4417453645fSAndriy Voskoboinyk 	rtwn_detach_private(sc);
4427453645fSAndriy Voskoboinyk 	mtx_destroy(&sc->sc_mtx);
4437453645fSAndriy Voskoboinyk 
4447453645fSAndriy Voskoboinyk 	return (0);
4457453645fSAndriy Voskoboinyk }
4467453645fSAndriy Voskoboinyk 
4477453645fSAndriy Voskoboinyk static int
4487453645fSAndriy Voskoboinyk rtwn_usb_suspend(device_t self)
4497453645fSAndriy Voskoboinyk {
4507453645fSAndriy Voskoboinyk 	struct rtwn_usb_softc *uc = device_get_softc(self);
4517453645fSAndriy Voskoboinyk 
4527453645fSAndriy Voskoboinyk 	rtwn_suspend(&uc->uc_sc);
4537453645fSAndriy Voskoboinyk 
4547453645fSAndriy Voskoboinyk 	return (0);
4557453645fSAndriy Voskoboinyk }
4567453645fSAndriy Voskoboinyk 
4577453645fSAndriy Voskoboinyk static int
4587453645fSAndriy Voskoboinyk rtwn_usb_resume(device_t self)
4597453645fSAndriy Voskoboinyk {
4607453645fSAndriy Voskoboinyk 	struct rtwn_usb_softc *uc = device_get_softc(self);
4617453645fSAndriy Voskoboinyk 
4627453645fSAndriy Voskoboinyk 	rtwn_resume(&uc->uc_sc);
4637453645fSAndriy Voskoboinyk 
4647453645fSAndriy Voskoboinyk 	return (0);
4657453645fSAndriy Voskoboinyk }
4667453645fSAndriy Voskoboinyk 
4677453645fSAndriy Voskoboinyk static device_method_t rtwn_usb_methods[] = {
4687453645fSAndriy Voskoboinyk 	/* Device interface */
4697453645fSAndriy Voskoboinyk 	DEVMETHOD(device_probe,		rtwn_usb_match),
4707453645fSAndriy Voskoboinyk 	DEVMETHOD(device_attach,	rtwn_usb_attach),
4717453645fSAndriy Voskoboinyk 	DEVMETHOD(device_detach,	rtwn_usb_detach),
4727453645fSAndriy Voskoboinyk 	DEVMETHOD(device_suspend,	rtwn_usb_suspend),
4737453645fSAndriy Voskoboinyk 	DEVMETHOD(device_resume,	rtwn_usb_resume),
4747453645fSAndriy Voskoboinyk 
4757453645fSAndriy Voskoboinyk 	DEVMETHOD_END
4767453645fSAndriy Voskoboinyk };
4777453645fSAndriy Voskoboinyk 
4787453645fSAndriy Voskoboinyk static driver_t rtwn_usb_driver = {
4797453645fSAndriy Voskoboinyk 	"rtwn",
4807453645fSAndriy Voskoboinyk 	rtwn_usb_methods,
4817453645fSAndriy Voskoboinyk 	sizeof(struct rtwn_usb_softc)
4827453645fSAndriy Voskoboinyk };
4837453645fSAndriy Voskoboinyk 
4847453645fSAndriy Voskoboinyk static devclass_t rtwn_usb_devclass;
4857453645fSAndriy Voskoboinyk 
4867453645fSAndriy Voskoboinyk DRIVER_MODULE(rtwn_usb, uhub, rtwn_usb_driver, rtwn_usb_devclass, NULL, NULL);
4877453645fSAndriy Voskoboinyk MODULE_VERSION(rtwn_usb, 1);
4887453645fSAndriy Voskoboinyk MODULE_DEPEND(rtwn_usb, usb, 1, 1, 1);
4897453645fSAndriy Voskoboinyk MODULE_DEPEND(rtwn_usb, wlan, 1, 1, 1);
4907453645fSAndriy Voskoboinyk MODULE_DEPEND(rtwn_usb, rtwn, 2, 2, 2);
4917453645fSAndriy Voskoboinyk USB_PNP_HOST_INFO(rtwn_devs);
492