xref: /openbsd-src/sys/dev/usb/if_rsu.c (revision 81508fe356eb7772a68118f65f91723ce5261d7d)
1*81508fe3Sjsg /*	$OpenBSD: if_rsu.c,v 1.53 2024/05/23 03:21:08 jsg Exp $	*/
25fdd4e61Sdamien 
35fdd4e61Sdamien /*-
45fdd4e61Sdamien  * Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr>
55fdd4e61Sdamien  *
65fdd4e61Sdamien  * Permission to use, copy, modify, and distribute this software for any
75fdd4e61Sdamien  * purpose with or without fee is hereby granted, provided that the above
85fdd4e61Sdamien  * copyright notice and this permission notice appear in all copies.
95fdd4e61Sdamien  *
105fdd4e61Sdamien  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
115fdd4e61Sdamien  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
125fdd4e61Sdamien  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
135fdd4e61Sdamien  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
145fdd4e61Sdamien  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
155fdd4e61Sdamien  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
165fdd4e61Sdamien  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
175fdd4e61Sdamien  */
185fdd4e61Sdamien 
195fdd4e61Sdamien /*
205fdd4e61Sdamien  * Driver for Realtek RTL8188SU/RTL8191SU/RTL8192SU.
215fdd4e61Sdamien  */
225fdd4e61Sdamien 
235fdd4e61Sdamien #include "bpfilter.h"
245fdd4e61Sdamien 
255fdd4e61Sdamien #include <sys/param.h>
265fdd4e61Sdamien #include <sys/sockio.h>
275fdd4e61Sdamien #include <sys/mbuf.h>
285fdd4e61Sdamien #include <sys/systm.h>
295fdd4e61Sdamien #include <sys/timeout.h>
305fdd4e61Sdamien #include <sys/device.h>
319b18ffb8Sguenther #include <sys/endian.h>
325fdd4e61Sdamien 
335fdd4e61Sdamien #include <machine/intr.h>
345fdd4e61Sdamien 
355fdd4e61Sdamien #if NBPFILTER > 0
365fdd4e61Sdamien #include <net/bpf.h>
375fdd4e61Sdamien #endif
385fdd4e61Sdamien #include <net/if.h>
395fdd4e61Sdamien #include <net/if_dl.h>
405fdd4e61Sdamien #include <net/if_media.h>
415fdd4e61Sdamien 
425fdd4e61Sdamien #include <netinet/in.h>
435fdd4e61Sdamien #include <netinet/if_ether.h>
445fdd4e61Sdamien 
455fdd4e61Sdamien #include <net80211/ieee80211_var.h>
465fdd4e61Sdamien #include <net80211/ieee80211_radiotap.h>
475fdd4e61Sdamien 
485fdd4e61Sdamien #include <dev/usb/usb.h>
495fdd4e61Sdamien #include <dev/usb/usbdi.h>
505fdd4e61Sdamien #include <dev/usb/usbdi_util.h>
515fdd4e61Sdamien #include <dev/usb/usbdevs.h>
525fdd4e61Sdamien 
535fdd4e61Sdamien #include <dev/usb/if_rsureg.h>
545fdd4e61Sdamien 
555fdd4e61Sdamien #ifdef RSU_DEBUG
565fdd4e61Sdamien #define DPRINTF(x)	do { if (rsu_debug) printf x; } while (0)
575fdd4e61Sdamien #define DPRINTFN(n, x)	do { if (rsu_debug >= (n)) printf x; } while (0)
585fdd4e61Sdamien int rsu_debug = 4;
595fdd4e61Sdamien #else
605fdd4e61Sdamien #define DPRINTF(x)
615fdd4e61Sdamien #define DPRINTFN(n, x)
625fdd4e61Sdamien #endif
635fdd4e61Sdamien 
645fdd4e61Sdamien /*
655fdd4e61Sdamien  * NB: When updating this list of devices, beware to also update the list
665fdd4e61Sdamien  * of devices that have HT support disabled below, if applicable.
675fdd4e61Sdamien  */
685fdd4e61Sdamien static const struct usb_devno rsu_devs[] = {
695fdd4e61Sdamien 	{ USB_VENDOR_ACCTON,		USB_PRODUCT_ACCTON_RTL8192SU },
705fdd4e61Sdamien 	{ USB_VENDOR_ASUS,		USB_PRODUCT_ASUS_USBN10 },
715fdd4e61Sdamien 	{ USB_VENDOR_ASUS,		USB_PRODUCT_ASUS_RTL8192SU_1 },
725fdd4e61Sdamien 	{ USB_VENDOR_AZUREWAVE,		USB_PRODUCT_AZUREWAVE_RTL8192SU_1 },
735fdd4e61Sdamien 	{ USB_VENDOR_AZUREWAVE,		USB_PRODUCT_AZUREWAVE_RTL8192SU_2 },
745fdd4e61Sdamien 	{ USB_VENDOR_AZUREWAVE,		USB_PRODUCT_AZUREWAVE_RTL8192SU_3 },
755fdd4e61Sdamien 	{ USB_VENDOR_AZUREWAVE,		USB_PRODUCT_AZUREWAVE_RTL8192SU_4 },
765fdd4e61Sdamien 	{ USB_VENDOR_AZUREWAVE,		USB_PRODUCT_AZUREWAVE_RTL8192SU_5 },
775fdd4e61Sdamien 	{ USB_VENDOR_BELKIN,		USB_PRODUCT_BELKIN_RTL8192SU_1 },
785fdd4e61Sdamien 	{ USB_VENDOR_BELKIN,		USB_PRODUCT_BELKIN_RTL8192SU_2 },
795fdd4e61Sdamien 	{ USB_VENDOR_BELKIN,		USB_PRODUCT_BELKIN_RTL8192SU_3 },
805fdd4e61Sdamien 	{ USB_VENDOR_CONCEPTRONIC2,	USB_PRODUCT_CONCEPTRONIC2_RTL8192SU_1 },
815fdd4e61Sdamien 	{ USB_VENDOR_CONCEPTRONIC2,	USB_PRODUCT_CONCEPTRONIC2_RTL8192SU_2 },
825fdd4e61Sdamien 	{ USB_VENDOR_CONCEPTRONIC2,	USB_PRODUCT_CONCEPTRONIC2_RTL8192SU_3 },
835fdd4e61Sdamien 	{ USB_VENDOR_COREGA,		USB_PRODUCT_COREGA_RTL8192SU },
845fdd4e61Sdamien 	{ USB_VENDOR_DLINK2,		USB_PRODUCT_DLINK2_DWA131A1 },
855fdd4e61Sdamien 	{ USB_VENDOR_DLINK2,		USB_PRODUCT_DLINK2_RTL8192SU_1 },
865fdd4e61Sdamien 	{ USB_VENDOR_DLINK2,		USB_PRODUCT_DLINK2_RTL8192SU_2 },
875fdd4e61Sdamien 	{ USB_VENDOR_EDIMAX,		USB_PRODUCT_EDIMAX_RTL8192SU_1 },
885fdd4e61Sdamien 	{ USB_VENDOR_EDIMAX,		USB_PRODUCT_EDIMAX_RTL8192SU_2 },
895fdd4e61Sdamien 	{ USB_VENDOR_EDIMAX,		USB_PRODUCT_EDIMAX_RTL8192SU_3 },
9023105fddSdamien 	{ USB_VENDOR_GUILLEMOT,		USB_PRODUCT_GUILLEMOT_HWGUN54 },
9123105fddSdamien 	{ USB_VENDOR_GUILLEMOT,		USB_PRODUCT_GUILLEMOT_HWNUM300 },
925fdd4e61Sdamien 	{ USB_VENDOR_HAWKING,		USB_PRODUCT_HAWKING_RTL8192SU_1 },
935fdd4e61Sdamien 	{ USB_VENDOR_HAWKING,		USB_PRODUCT_HAWKING_RTL8192SU_2 },
94f010e75bSjsg 	{ USB_VENDOR_PLANEX2,		USB_PRODUCT_PLANEX2_GWUSNANO },
955fdd4e61Sdamien 	{ USB_VENDOR_REALTEK,		USB_PRODUCT_REALTEK_RTL8171 },
965fdd4e61Sdamien 	{ USB_VENDOR_REALTEK,		USB_PRODUCT_REALTEK_RTL8172 },
975fdd4e61Sdamien 	{ USB_VENDOR_REALTEK,		USB_PRODUCT_REALTEK_RTL8173 },
985fdd4e61Sdamien 	{ USB_VENDOR_REALTEK,		USB_PRODUCT_REALTEK_RTL8174 },
995fdd4e61Sdamien 	{ USB_VENDOR_REALTEK,		USB_PRODUCT_REALTEK_RTL8192SU },
1005fdd4e61Sdamien 	{ USB_VENDOR_REALTEK,		USB_PRODUCT_REALTEK_RTL8712 },
1015fdd4e61Sdamien 	{ USB_VENDOR_REALTEK,		USB_PRODUCT_REALTEK_RTL8713 },
1025fdd4e61Sdamien 	{ USB_VENDOR_SENAO,		USB_PRODUCT_SENAO_RTL8192SU_1 },
1035fdd4e61Sdamien 	{ USB_VENDOR_SENAO,		USB_PRODUCT_SENAO_RTL8192SU_2 },
104c352cd82Skettenis 	{ USB_VENDOR_SITECOMEU,		USB_PRODUCT_SITECOMEU_WL349V1 },
1058b191611Sjakemsr 	{ USB_VENDOR_SITECOMEU,		USB_PRODUCT_SITECOMEU_WL353 },
1068b191611Sjakemsr 	{ USB_VENDOR_SWEEX2,		USB_PRODUCT_SWEEX2_LW154 }
1075fdd4e61Sdamien };
1085fdd4e61Sdamien 
1095fdd4e61Sdamien /* List of devices that have HT support disabled. */
1105fdd4e61Sdamien static const struct usb_devno rsu_devs_noht[] = {
1115fdd4e61Sdamien 	{ USB_VENDOR_ASUS,		USB_PRODUCT_ASUS_RTL8192SU_1 },
1125fdd4e61Sdamien 	{ USB_VENDOR_AZUREWAVE,		USB_PRODUCT_AZUREWAVE_RTL8192SU_4 }
1135fdd4e61Sdamien };
1145fdd4e61Sdamien 
1155fdd4e61Sdamien int		rsu_match(struct device *, void *, void *);
1165fdd4e61Sdamien void		rsu_attach(struct device *, struct device *, void *);
1175fdd4e61Sdamien int		rsu_detach(struct device *, int);
1185fdd4e61Sdamien int		rsu_open_pipes(struct rsu_softc *);
1195fdd4e61Sdamien void		rsu_close_pipes(struct rsu_softc *);
1205fdd4e61Sdamien int		rsu_alloc_rx_list(struct rsu_softc *);
1215fdd4e61Sdamien void		rsu_free_rx_list(struct rsu_softc *);
1225fdd4e61Sdamien int		rsu_alloc_tx_list(struct rsu_softc *);
1235fdd4e61Sdamien void		rsu_free_tx_list(struct rsu_softc *);
1245fdd4e61Sdamien void		rsu_task(void *);
1255fdd4e61Sdamien void		rsu_do_async(struct rsu_softc *,
1265fdd4e61Sdamien 		    void (*)(struct rsu_softc *, void *), void *, int);
1275fdd4e61Sdamien void		rsu_wait_async(struct rsu_softc *);
1285fdd4e61Sdamien int		rsu_write_region_1(struct rsu_softc *, uint16_t, uint8_t *,
1295fdd4e61Sdamien 		    int);
1305fdd4e61Sdamien void		rsu_write_1(struct rsu_softc *, uint16_t, uint8_t);
1315fdd4e61Sdamien void		rsu_write_2(struct rsu_softc *, uint16_t, uint16_t);
1325fdd4e61Sdamien void		rsu_write_4(struct rsu_softc *, uint16_t, uint32_t);
1335fdd4e61Sdamien int		rsu_read_region_1(struct rsu_softc *, uint16_t, uint8_t *,
1345fdd4e61Sdamien 		    int);
1355fdd4e61Sdamien uint8_t		rsu_read_1(struct rsu_softc *, uint16_t);
1365fdd4e61Sdamien uint16_t	rsu_read_2(struct rsu_softc *, uint16_t);
1375fdd4e61Sdamien uint32_t	rsu_read_4(struct rsu_softc *, uint16_t);
1385fdd4e61Sdamien int		rsu_fw_iocmd(struct rsu_softc *, uint32_t);
1395fdd4e61Sdamien uint8_t		rsu_efuse_read_1(struct rsu_softc *, uint16_t);
1405fdd4e61Sdamien int		rsu_read_rom(struct rsu_softc *);
1415fdd4e61Sdamien int		rsu_fw_cmd(struct rsu_softc *, uint8_t, void *, int);
1425fdd4e61Sdamien int		rsu_media_change(struct ifnet *);
1435fdd4e61Sdamien void		rsu_calib_to(void *);
1445fdd4e61Sdamien void		rsu_calib_cb(struct rsu_softc *, void *);
1455fdd4e61Sdamien int		rsu_newstate(struct ieee80211com *, enum ieee80211_state, int);
1465fdd4e61Sdamien void		rsu_newstate_cb(struct rsu_softc *, void *);
1475fdd4e61Sdamien int		rsu_set_key(struct ieee80211com *, struct ieee80211_node *,
1485fdd4e61Sdamien 		    struct ieee80211_key *);
1495fdd4e61Sdamien void		rsu_set_key_cb(struct rsu_softc *, void *);
1505fdd4e61Sdamien void		rsu_delete_key(struct ieee80211com *, struct ieee80211_node *,
1515fdd4e61Sdamien 		    struct ieee80211_key *);
152fcc42384Sdamien void		rsu_delete_key_cb(struct rsu_softc *, void *);
1535fdd4e61Sdamien int		rsu_site_survey(struct rsu_softc *);
1545fdd4e61Sdamien int		rsu_join_bss(struct rsu_softc *, struct ieee80211_node *);
1555fdd4e61Sdamien int		rsu_disconnect(struct rsu_softc *);
1565fdd4e61Sdamien void		rsu_event_survey(struct rsu_softc *, uint8_t *, int);
1575fdd4e61Sdamien void		rsu_event_join_bss(struct rsu_softc *, uint8_t *, int);
1585fdd4e61Sdamien void		rsu_rx_event(struct rsu_softc *, uint8_t, uint8_t *, int);
1595fdd4e61Sdamien void		rsu_rx_multi_event(struct rsu_softc *, uint8_t *, int);
1605fdd4e61Sdamien int8_t		rsu_get_rssi(struct rsu_softc *, int, void *);
1618fbaf8a2Sstsp void		rsu_rx_frame(struct rsu_softc *, uint8_t *, int,
1628fbaf8a2Sstsp 		    struct mbuf_list *);
1635fdd4e61Sdamien void		rsu_rx_multi_frame(struct rsu_softc *, uint8_t *, int);
164ab0b1be7Smglocker void		rsu_rxeof(struct usbd_xfer *, void *, usbd_status);
165ab0b1be7Smglocker void		rsu_txeof(struct usbd_xfer *, void *, usbd_status);
1665fdd4e61Sdamien int		rsu_tx(struct rsu_softc *, struct mbuf *,
1675fdd4e61Sdamien 		    struct ieee80211_node *);
1685fdd4e61Sdamien int		rsu_send_mgmt(struct ieee80211com *, struct ieee80211_node *,
1695fdd4e61Sdamien 		    int, int, int);
1705fdd4e61Sdamien void		rsu_start(struct ifnet *);
1715fdd4e61Sdamien void		rsu_watchdog(struct ifnet *);
1725fdd4e61Sdamien int		rsu_ioctl(struct ifnet *, u_long, caddr_t);
1735fdd4e61Sdamien void		rsu_power_on_acut(struct rsu_softc *);
1745fdd4e61Sdamien void		rsu_power_on_bcut(struct rsu_softc *);
1755fdd4e61Sdamien void		rsu_power_off(struct rsu_softc *);
1765fdd4e61Sdamien int		rsu_fw_loadsection(struct rsu_softc *, uint8_t *, int);
1775fdd4e61Sdamien int		rsu_load_firmware(struct rsu_softc *);
1785fdd4e61Sdamien int		rsu_init(struct ifnet *);
1795fdd4e61Sdamien void		rsu_stop(struct ifnet *);
1805fdd4e61Sdamien 
1815fdd4e61Sdamien struct cfdriver rsu_cd = {
1825fdd4e61Sdamien 	NULL, "rsu", DV_IFNET
1835fdd4e61Sdamien };
1845fdd4e61Sdamien 
1855fdd4e61Sdamien const struct cfattach rsu_ca = {
18653c6612dSmpi 	sizeof(struct rsu_softc), rsu_match, rsu_attach, rsu_detach,
1875fdd4e61Sdamien };
1885fdd4e61Sdamien 
1895fdd4e61Sdamien int
rsu_match(struct device * parent,void * match,void * aux)1905fdd4e61Sdamien rsu_match(struct device *parent, void *match, void *aux)
1915fdd4e61Sdamien {
1925fdd4e61Sdamien 	struct usb_attach_arg *uaa = aux;
1935fdd4e61Sdamien 
1947ebc5b51Smpi 	if (uaa->iface == NULL || uaa->configno != 1)
1955fdd4e61Sdamien 		return (UMATCH_NONE);
1965fdd4e61Sdamien 
1975fdd4e61Sdamien 	return ((usb_lookup(rsu_devs, uaa->vendor, uaa->product) != NULL) ?
1987ebc5b51Smpi 	    UMATCH_VENDOR_PRODUCT_CONF_IFACE : UMATCH_NONE);
1995fdd4e61Sdamien }
2005fdd4e61Sdamien 
2015fdd4e61Sdamien void
rsu_attach(struct device * parent,struct device * self,void * aux)2025fdd4e61Sdamien rsu_attach(struct device *parent, struct device *self, void *aux)
2035fdd4e61Sdamien {
2045fdd4e61Sdamien 	struct rsu_softc *sc = (struct rsu_softc *)self;
2055fdd4e61Sdamien 	struct usb_attach_arg *uaa = aux;
2065fdd4e61Sdamien 	struct ieee80211com *ic = &sc->sc_ic;
2075fdd4e61Sdamien 	struct ifnet *ifp = &ic->ic_if;
2085fdd4e61Sdamien 	int i, error;
2095fdd4e61Sdamien 
2105fdd4e61Sdamien 	sc->sc_udev = uaa->device;
2117ebc5b51Smpi 	sc->sc_iface = uaa->iface;
2125fdd4e61Sdamien 
2135fdd4e61Sdamien 	usb_init_task(&sc->sc_task, rsu_task, sc, USB_TASK_TYPE_GENERIC);
2145fdd4e61Sdamien 	timeout_set(&sc->calib_to, rsu_calib_to, sc);
2155fdd4e61Sdamien 
2165fdd4e61Sdamien 	/* Read chip revision. */
2175fdd4e61Sdamien 	sc->cut = MS(rsu_read_4(sc, R92S_PMC_FSM), R92S_PMC_FSM_CUT);
2185fdd4e61Sdamien 	if (sc->cut != 3)
2195fdd4e61Sdamien 		sc->cut = (sc->cut >> 1) + 1;
2205fdd4e61Sdamien 
2215fdd4e61Sdamien 	error = rsu_read_rom(sc);
2225fdd4e61Sdamien 	if (error != 0) {
2235fdd4e61Sdamien 		printf("%s: could not read ROM\n", sc->sc_dev.dv_xname);
2245fdd4e61Sdamien 		return;
2255fdd4e61Sdamien 	}
2265fdd4e61Sdamien 	IEEE80211_ADDR_COPY(ic->ic_myaddr, &sc->rom[0x12]);
2275fdd4e61Sdamien 
2285fdd4e61Sdamien 	printf("%s: MAC/BB RTL8712 cut %d, address %s\n",
2295fdd4e61Sdamien 	    sc->sc_dev.dv_xname, sc->cut, ether_sprintf(ic->ic_myaddr));
2305fdd4e61Sdamien 
2315fdd4e61Sdamien 	if (rsu_open_pipes(sc) != 0)
2325fdd4e61Sdamien 		return;
2335fdd4e61Sdamien 
2345fdd4e61Sdamien 	ic->ic_phytype = IEEE80211_T_OFDM;	/* Not only, but not used. */
2355fdd4e61Sdamien 	ic->ic_opmode = IEEE80211_M_STA;	/* Default to BSS mode. */
2365fdd4e61Sdamien 	ic->ic_state = IEEE80211_S_INIT;
2375fdd4e61Sdamien 
2385fdd4e61Sdamien 	/* Set device capabilities. */
2395fdd4e61Sdamien 	ic->ic_caps =
2405fdd4e61Sdamien 	    IEEE80211_C_SCANALL |	/* Hardware scan. */
2415fdd4e61Sdamien 	    IEEE80211_C_SHPREAMBLE |	/* Short preamble supported. */
2425fdd4e61Sdamien 	    IEEE80211_C_SHSLOT |	/* Short slot time supported. */
2435fdd4e61Sdamien 	    IEEE80211_C_WEP |		/* WEP. */
2445fdd4e61Sdamien 	    IEEE80211_C_RSN;		/* WPA/RSN. */
2455fdd4e61Sdamien 	/* Check if HT support is present. */
2465fdd4e61Sdamien 	if (usb_lookup(rsu_devs_noht, uaa->vendor, uaa->product) == NULL) {
24700fecc41Sstsp #ifdef notyet
2485fdd4e61Sdamien 		/* Set HT capabilities. */
2495fdd4e61Sdamien 		ic->ic_htcaps =
2505fdd4e61Sdamien 		    IEEE80211_HTCAP_CBW20_40 |
2515fdd4e61Sdamien 		    IEEE80211_HTCAP_DSSSCCK40;
2525fdd4e61Sdamien 		/* Set supported HT rates. */
2535fdd4e61Sdamien 		for (i = 0; i < 2; i++)
2545fdd4e61Sdamien 			ic->ic_sup_mcs[i] = 0xff;
25500fecc41Sstsp #endif
2565fdd4e61Sdamien 	}
2575fdd4e61Sdamien 
2585fdd4e61Sdamien 	/* Set supported .11b and .11g rates. */
2595fdd4e61Sdamien 	ic->ic_sup_rates[IEEE80211_MODE_11B] = ieee80211_std_rateset_11b;
2605fdd4e61Sdamien 	ic->ic_sup_rates[IEEE80211_MODE_11G] = ieee80211_std_rateset_11g;
2615fdd4e61Sdamien 
2625fdd4e61Sdamien 	/* Set supported .11b and .11g channels (1 through 14). */
2635fdd4e61Sdamien 	for (i = 1; i <= 14; i++) {
2645fdd4e61Sdamien 		ic->ic_channels[i].ic_freq =
2655fdd4e61Sdamien 		    ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ);
2665fdd4e61Sdamien 		ic->ic_channels[i].ic_flags =
2675fdd4e61Sdamien 		    IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM |
2685fdd4e61Sdamien 		    IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ;
2695fdd4e61Sdamien 	}
2705fdd4e61Sdamien 
2715fdd4e61Sdamien 	ifp->if_softc = sc;
2725fdd4e61Sdamien 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
2735fdd4e61Sdamien 	ifp->if_ioctl = rsu_ioctl;
2745fdd4e61Sdamien 	ifp->if_start = rsu_start;
2755fdd4e61Sdamien 	ifp->if_watchdog = rsu_watchdog;
2765fdd4e61Sdamien 	memcpy(ifp->if_xname, sc->sc_dev.dv_xname, IFNAMSIZ);
2775fdd4e61Sdamien 
2785fdd4e61Sdamien 	if_attach(ifp);
2795fdd4e61Sdamien 	ieee80211_ifattach(ifp);
2805fdd4e61Sdamien #ifdef notyet
2815fdd4e61Sdamien 	ic->ic_set_key = rsu_set_key;
2825fdd4e61Sdamien 	ic->ic_delete_key = rsu_delete_key;
2835fdd4e61Sdamien #endif
2845fdd4e61Sdamien 	/* Override state transition machine. */
2855fdd4e61Sdamien 	sc->sc_newstate = ic->ic_newstate;
2865fdd4e61Sdamien 	ic->ic_newstate = rsu_newstate;
2875fdd4e61Sdamien 	ic->ic_send_mgmt = rsu_send_mgmt;
2885fdd4e61Sdamien 	ieee80211_media_init(ifp, rsu_media_change, ieee80211_media_status);
2895fdd4e61Sdamien 
2905fdd4e61Sdamien #if NBPFILTER > 0
2915fdd4e61Sdamien 	bpfattach(&sc->sc_drvbpf, ifp, DLT_IEEE802_11_RADIO,
2925fdd4e61Sdamien 	    sizeof(struct ieee80211_frame) + IEEE80211_RADIOTAP_HDRLEN);
2935fdd4e61Sdamien 
2945fdd4e61Sdamien 	sc->sc_rxtap_len = sizeof(sc->sc_rxtapu);
2955fdd4e61Sdamien 	sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len);
2965fdd4e61Sdamien 	sc->sc_rxtap.wr_ihdr.it_present = htole32(RSU_RX_RADIOTAP_PRESENT);
2975fdd4e61Sdamien 
2985fdd4e61Sdamien 	sc->sc_txtap_len = sizeof(sc->sc_txtapu);
2995fdd4e61Sdamien 	sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len);
3005fdd4e61Sdamien 	sc->sc_txtap.wt_ihdr.it_present = htole32(RSU_TX_RADIOTAP_PRESENT);
3015fdd4e61Sdamien #endif
3025fdd4e61Sdamien }
3035fdd4e61Sdamien 
3045fdd4e61Sdamien int
rsu_detach(struct device * self,int flags)3055fdd4e61Sdamien rsu_detach(struct device *self, int flags)
3065fdd4e61Sdamien {
3075fdd4e61Sdamien 	struct rsu_softc *sc = (struct rsu_softc *)self;
3085fdd4e61Sdamien 	struct ifnet *ifp = &sc->sc_ic.ic_if;
3095fdd4e61Sdamien 	int s;
3105fdd4e61Sdamien 
3115ad57547Sjakemsr 	s = splusb();
3125fdd4e61Sdamien 
3135fdd4e61Sdamien 	if (timeout_initialized(&sc->calib_to))
3145fdd4e61Sdamien 		timeout_del(&sc->calib_to);
3155ad57547Sjakemsr 
3165ad57547Sjakemsr 	/* Wait for all async commands to complete. */
3175ad57547Sjakemsr 	usb_rem_wait_task(sc->sc_udev, &sc->sc_task);
3185ad57547Sjakemsr 
3195ad57547Sjakemsr 	usbd_ref_wait(sc->sc_udev);
3205ad57547Sjakemsr 
3215fdd4e61Sdamien 	if (ifp->if_softc != NULL) {
3225fdd4e61Sdamien 		ieee80211_ifdetach(ifp);
3235fdd4e61Sdamien 		if_detach(ifp);
3245fdd4e61Sdamien 	}
3255fdd4e61Sdamien 
3265fdd4e61Sdamien 	/* Abort and close Tx/Rx pipes. */
3275fdd4e61Sdamien 	rsu_close_pipes(sc);
3285fdd4e61Sdamien 
3295fdd4e61Sdamien 	/* Free Tx/Rx buffers. */
3305fdd4e61Sdamien 	rsu_free_tx_list(sc);
3315fdd4e61Sdamien 	rsu_free_rx_list(sc);
3325fdd4e61Sdamien 	splx(s);
3335fdd4e61Sdamien 
3345fdd4e61Sdamien 	return (0);
3355fdd4e61Sdamien }
3365fdd4e61Sdamien 
3375fdd4e61Sdamien int
rsu_open_pipes(struct rsu_softc * sc)3385fdd4e61Sdamien rsu_open_pipes(struct rsu_softc *sc)
3395fdd4e61Sdamien {
3405fdd4e61Sdamien 	usb_interface_descriptor_t *id;
3415fdd4e61Sdamien 	int i, error;
3425fdd4e61Sdamien 
3435fdd4e61Sdamien 	/*
3445fdd4e61Sdamien 	 * Determine the number of Tx/Rx endpoints (there are chips with
3455fdd4e61Sdamien 	 * 4, 6 or 11 endpoints).
3465fdd4e61Sdamien 	 */
3475fdd4e61Sdamien 	id = usbd_get_interface_descriptor(sc->sc_iface);
3485fdd4e61Sdamien 	sc->npipes = id->bNumEndpoints;
3495fdd4e61Sdamien 	if (sc->npipes == 4)
3505fdd4e61Sdamien 		sc->qid2idx = rsu_qid2idx_4ep;
3515fdd4e61Sdamien 	else if (sc->npipes == 6)
3525fdd4e61Sdamien 		sc->qid2idx = rsu_qid2idx_6ep;
3535fdd4e61Sdamien 	else	/* Assume npipes==11; will fail below otherwise. */
3545fdd4e61Sdamien 		sc->qid2idx = rsu_qid2idx_11ep;
3555fdd4e61Sdamien 	DPRINTF(("%d endpoints configuration\n", sc->npipes));
3565fdd4e61Sdamien 
3575fdd4e61Sdamien 	/* Open all pipes. */
3585fdd4e61Sdamien 	for (i = 0; i < MIN(sc->npipes, nitems(r92s_epaddr)); i++) {
3595fdd4e61Sdamien 		error = usbd_open_pipe(sc->sc_iface, r92s_epaddr[i], 0,
3605fdd4e61Sdamien 		    &sc->pipe[i]);
3615fdd4e61Sdamien 		if (error != 0) {
3625fdd4e61Sdamien 			printf("%s: could not open bulk pipe 0x%02x\n",
3635fdd4e61Sdamien 			    sc->sc_dev.dv_xname, r92s_epaddr[i]);
3645fdd4e61Sdamien 			break;
3655fdd4e61Sdamien 		}
3665fdd4e61Sdamien 	}
3675fdd4e61Sdamien 	if (error != 0)
3685fdd4e61Sdamien 		rsu_close_pipes(sc);
3695fdd4e61Sdamien 	return (error);
3705fdd4e61Sdamien }
3715fdd4e61Sdamien 
3725fdd4e61Sdamien void
rsu_close_pipes(struct rsu_softc * sc)3735fdd4e61Sdamien rsu_close_pipes(struct rsu_softc *sc)
3745fdd4e61Sdamien {
3755fdd4e61Sdamien 	int i;
3765fdd4e61Sdamien 
3775fdd4e61Sdamien 	/* Close all pipes. */
3785fdd4e61Sdamien 	for (i = 0; i < sc->npipes; i++) {
3795fdd4e61Sdamien 		if (sc->pipe[i] == NULL)
3805fdd4e61Sdamien 			continue;
3815fdd4e61Sdamien 		usbd_close_pipe(sc->pipe[i]);
3825fdd4e61Sdamien 	}
3835fdd4e61Sdamien }
3845fdd4e61Sdamien 
3855fdd4e61Sdamien int
rsu_alloc_rx_list(struct rsu_softc * sc)3865fdd4e61Sdamien rsu_alloc_rx_list(struct rsu_softc *sc)
3875fdd4e61Sdamien {
3885fdd4e61Sdamien 	struct rsu_rx_data *data;
3895fdd4e61Sdamien 	int i, error = 0;
3905fdd4e61Sdamien 
3915fdd4e61Sdamien 	for (i = 0; i < RSU_RX_LIST_COUNT; i++) {
3925fdd4e61Sdamien 		data = &sc->rx_data[i];
3935fdd4e61Sdamien 
3945fdd4e61Sdamien 		data->sc = sc;	/* Backpointer for callbacks. */
3955fdd4e61Sdamien 
3965fdd4e61Sdamien 		data->xfer = usbd_alloc_xfer(sc->sc_udev);
3975fdd4e61Sdamien 		if (data->xfer == NULL) {
3985fdd4e61Sdamien 			printf("%s: could not allocate xfer\n",
3995fdd4e61Sdamien 			    sc->sc_dev.dv_xname);
4005fdd4e61Sdamien 			error = ENOMEM;
4015fdd4e61Sdamien 			break;
4025fdd4e61Sdamien 		}
4035fdd4e61Sdamien 		data->buf = usbd_alloc_buffer(data->xfer, RSU_RXBUFSZ);
4045fdd4e61Sdamien 		if (data->buf == NULL) {
4055fdd4e61Sdamien 			printf("%s: could not allocate xfer buffer\n",
4065fdd4e61Sdamien 			    sc->sc_dev.dv_xname);
4075fdd4e61Sdamien 			error = ENOMEM;
4085fdd4e61Sdamien 			break;
4095fdd4e61Sdamien 		}
4105fdd4e61Sdamien 	}
4115fdd4e61Sdamien 	if (error != 0)
4125fdd4e61Sdamien 		rsu_free_rx_list(sc);
4135fdd4e61Sdamien 	return (error);
4145fdd4e61Sdamien }
4155fdd4e61Sdamien 
4165fdd4e61Sdamien void
rsu_free_rx_list(struct rsu_softc * sc)4175fdd4e61Sdamien rsu_free_rx_list(struct rsu_softc *sc)
4185fdd4e61Sdamien {
4195fdd4e61Sdamien 	int i;
4205fdd4e61Sdamien 
4215fdd4e61Sdamien 	/* NB: Caller must abort pipe first. */
4225fdd4e61Sdamien 	for (i = 0; i < RSU_RX_LIST_COUNT; i++) {
4235fdd4e61Sdamien 		if (sc->rx_data[i].xfer != NULL)
4245fdd4e61Sdamien 			usbd_free_xfer(sc->rx_data[i].xfer);
4255fdd4e61Sdamien 		sc->rx_data[i].xfer = NULL;
4265fdd4e61Sdamien 	}
4275fdd4e61Sdamien }
4285fdd4e61Sdamien 
4295fdd4e61Sdamien int
rsu_alloc_tx_list(struct rsu_softc * sc)4305fdd4e61Sdamien rsu_alloc_tx_list(struct rsu_softc *sc)
4315fdd4e61Sdamien {
4325fdd4e61Sdamien 	struct rsu_tx_data *data;
4335fdd4e61Sdamien 	int i, error = 0;
4345fdd4e61Sdamien 
4355fdd4e61Sdamien 	TAILQ_INIT(&sc->tx_free_list);
4365fdd4e61Sdamien 	for (i = 0; i < RSU_TX_LIST_COUNT; i++) {
4375fdd4e61Sdamien 		data = &sc->tx_data[i];
4385fdd4e61Sdamien 
4395fdd4e61Sdamien 		data->sc = sc;	/* Backpointer for callbacks. */
4405fdd4e61Sdamien 
4415fdd4e61Sdamien 		data->xfer = usbd_alloc_xfer(sc->sc_udev);
4425fdd4e61Sdamien 		if (data->xfer == NULL) {
4435fdd4e61Sdamien 			printf("%s: could not allocate xfer\n",
4445fdd4e61Sdamien 			    sc->sc_dev.dv_xname);
4455fdd4e61Sdamien 			error = ENOMEM;
4465fdd4e61Sdamien 			break;
4475fdd4e61Sdamien 		}
4485fdd4e61Sdamien 		data->buf = usbd_alloc_buffer(data->xfer, RSU_TXBUFSZ);
4495fdd4e61Sdamien 		if (data->buf == NULL) {
4505fdd4e61Sdamien 			printf("%s: could not allocate xfer buffer\n",
4515fdd4e61Sdamien 			    sc->sc_dev.dv_xname);
4525fdd4e61Sdamien 			error = ENOMEM;
4535fdd4e61Sdamien 			break;
4545fdd4e61Sdamien 		}
4555fdd4e61Sdamien 		/* Append this Tx buffer to our free list. */
4565fdd4e61Sdamien 		TAILQ_INSERT_TAIL(&sc->tx_free_list, data, next);
4575fdd4e61Sdamien 	}
4585fdd4e61Sdamien 	if (error != 0)
4595fdd4e61Sdamien 		rsu_free_tx_list(sc);
4605fdd4e61Sdamien 	return (error);
4615fdd4e61Sdamien }
4625fdd4e61Sdamien 
4635fdd4e61Sdamien void
rsu_free_tx_list(struct rsu_softc * sc)4645fdd4e61Sdamien rsu_free_tx_list(struct rsu_softc *sc)
4655fdd4e61Sdamien {
4665fdd4e61Sdamien 	int i;
4675fdd4e61Sdamien 
4685fdd4e61Sdamien 	/* NB: Caller must abort pipe first. */
4695fdd4e61Sdamien 	for (i = 0; i < RSU_TX_LIST_COUNT; i++) {
4705fdd4e61Sdamien 		if (sc->tx_data[i].xfer != NULL)
4715fdd4e61Sdamien 			usbd_free_xfer(sc->tx_data[i].xfer);
4725fdd4e61Sdamien 		sc->tx_data[i].xfer = NULL;
4735fdd4e61Sdamien 	}
4745fdd4e61Sdamien }
4755fdd4e61Sdamien 
4765fdd4e61Sdamien void
rsu_task(void * arg)4775fdd4e61Sdamien rsu_task(void *arg)
4785fdd4e61Sdamien {
4795fdd4e61Sdamien 	struct rsu_softc *sc = arg;
4805fdd4e61Sdamien 	struct rsu_host_cmd_ring *ring = &sc->cmdq;
4815fdd4e61Sdamien 	struct rsu_host_cmd *cmd;
4825fdd4e61Sdamien 	int s;
4835fdd4e61Sdamien 
4845fdd4e61Sdamien 	/* Process host commands. */
4855fdd4e61Sdamien 	s = splusb();
4865fdd4e61Sdamien 	while (ring->next != ring->cur) {
4875fdd4e61Sdamien 		cmd = &ring->cmd[ring->next];
4885fdd4e61Sdamien 		splx(s);
4895fdd4e61Sdamien 		/* Invoke callback. */
4905fdd4e61Sdamien 		cmd->cb(sc, cmd->data);
4915fdd4e61Sdamien 		s = splusb();
4925fdd4e61Sdamien 		ring->queued--;
4935fdd4e61Sdamien 		ring->next = (ring->next + 1) % RSU_HOST_CMD_RING_COUNT;
4945fdd4e61Sdamien 	}
4955fdd4e61Sdamien 	splx(s);
4965fdd4e61Sdamien }
4975fdd4e61Sdamien 
4985fdd4e61Sdamien void
rsu_do_async(struct rsu_softc * sc,void (* cb)(struct rsu_softc *,void *),void * arg,int len)4995fdd4e61Sdamien rsu_do_async(struct rsu_softc *sc,
5005fdd4e61Sdamien     void (*cb)(struct rsu_softc *, void *), void *arg, int len)
5015fdd4e61Sdamien {
5025fdd4e61Sdamien 	struct rsu_host_cmd_ring *ring = &sc->cmdq;
5035fdd4e61Sdamien 	struct rsu_host_cmd *cmd;
5045fdd4e61Sdamien 	int s;
5055fdd4e61Sdamien 
5065fdd4e61Sdamien 	s = splusb();
5075fdd4e61Sdamien 	cmd = &ring->cmd[ring->cur];
5085fdd4e61Sdamien 	cmd->cb = cb;
5095fdd4e61Sdamien 	KASSERT(len <= sizeof(cmd->data));
5105fdd4e61Sdamien 	memcpy(cmd->data, arg, len);
5115fdd4e61Sdamien 	ring->cur = (ring->cur + 1) % RSU_HOST_CMD_RING_COUNT;
5125fdd4e61Sdamien 
5135fdd4e61Sdamien 	/* If there is no pending command already, schedule a task. */
5145fdd4e61Sdamien 	if (++ring->queued == 1)
5155fdd4e61Sdamien 		usb_add_task(sc->sc_udev, &sc->sc_task);
5165fdd4e61Sdamien 	splx(s);
5175fdd4e61Sdamien }
5185fdd4e61Sdamien 
5195fdd4e61Sdamien void
rsu_wait_async(struct rsu_softc * sc)5205fdd4e61Sdamien rsu_wait_async(struct rsu_softc *sc)
5215fdd4e61Sdamien {
5225fdd4e61Sdamien 	/* Wait for all queued asynchronous commands to complete. */
5235ad57547Sjakemsr 	usb_wait_task(sc->sc_udev, &sc->sc_task);
5245fdd4e61Sdamien }
5255fdd4e61Sdamien 
5265fdd4e61Sdamien int
rsu_write_region_1(struct rsu_softc * sc,uint16_t addr,uint8_t * buf,int len)5275fdd4e61Sdamien rsu_write_region_1(struct rsu_softc *sc, uint16_t addr, uint8_t *buf,
5285fdd4e61Sdamien     int len)
5295fdd4e61Sdamien {
5305fdd4e61Sdamien 	usb_device_request_t req;
5315fdd4e61Sdamien 
5325fdd4e61Sdamien 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
5335fdd4e61Sdamien 	req.bRequest = R92S_REQ_REGS;
5345fdd4e61Sdamien 	USETW(req.wValue, addr);
5355fdd4e61Sdamien 	USETW(req.wIndex, 0);
5365fdd4e61Sdamien 	USETW(req.wLength, len);
5375fdd4e61Sdamien 	return (usbd_do_request(sc->sc_udev, &req, buf));
5385fdd4e61Sdamien }
5395fdd4e61Sdamien 
5405fdd4e61Sdamien void
rsu_write_1(struct rsu_softc * sc,uint16_t addr,uint8_t val)5415fdd4e61Sdamien rsu_write_1(struct rsu_softc *sc, uint16_t addr, uint8_t val)
5425fdd4e61Sdamien {
5435fdd4e61Sdamien 	rsu_write_region_1(sc, addr, &val, 1);
5445fdd4e61Sdamien }
5455fdd4e61Sdamien 
5465fdd4e61Sdamien void
rsu_write_2(struct rsu_softc * sc,uint16_t addr,uint16_t val)5475fdd4e61Sdamien rsu_write_2(struct rsu_softc *sc, uint16_t addr, uint16_t val)
5485fdd4e61Sdamien {
5495fdd4e61Sdamien 	val = htole16(val);
5505fdd4e61Sdamien 	rsu_write_region_1(sc, addr, (uint8_t *)&val, 2);
5515fdd4e61Sdamien }
5525fdd4e61Sdamien 
5535fdd4e61Sdamien void
rsu_write_4(struct rsu_softc * sc,uint16_t addr,uint32_t val)5545fdd4e61Sdamien rsu_write_4(struct rsu_softc *sc, uint16_t addr, uint32_t val)
5555fdd4e61Sdamien {
5565fdd4e61Sdamien 	val = htole32(val);
5575fdd4e61Sdamien 	rsu_write_region_1(sc, addr, (uint8_t *)&val, 4);
5585fdd4e61Sdamien }
5595fdd4e61Sdamien 
5605fdd4e61Sdamien int
rsu_read_region_1(struct rsu_softc * sc,uint16_t addr,uint8_t * buf,int len)5615fdd4e61Sdamien rsu_read_region_1(struct rsu_softc *sc, uint16_t addr, uint8_t *buf,
5625fdd4e61Sdamien     int len)
5635fdd4e61Sdamien {
5645fdd4e61Sdamien 	usb_device_request_t req;
5655fdd4e61Sdamien 
5665fdd4e61Sdamien 	req.bmRequestType = UT_READ_VENDOR_DEVICE;
5675fdd4e61Sdamien 	req.bRequest = R92S_REQ_REGS;
5685fdd4e61Sdamien 	USETW(req.wValue, addr);
5695fdd4e61Sdamien 	USETW(req.wIndex, 0);
5705fdd4e61Sdamien 	USETW(req.wLength, len);
5715fdd4e61Sdamien 	return (usbd_do_request(sc->sc_udev, &req, buf));
5725fdd4e61Sdamien }
5735fdd4e61Sdamien 
5745fdd4e61Sdamien uint8_t
rsu_read_1(struct rsu_softc * sc,uint16_t addr)5755fdd4e61Sdamien rsu_read_1(struct rsu_softc *sc, uint16_t addr)
5765fdd4e61Sdamien {
5775fdd4e61Sdamien 	uint8_t val;
5785fdd4e61Sdamien 
5795fdd4e61Sdamien 	if (rsu_read_region_1(sc, addr, &val, 1) != 0)
5805fdd4e61Sdamien 		return (0xff);
5815fdd4e61Sdamien 	return (val);
5825fdd4e61Sdamien }
5835fdd4e61Sdamien 
5845fdd4e61Sdamien uint16_t
rsu_read_2(struct rsu_softc * sc,uint16_t addr)5855fdd4e61Sdamien rsu_read_2(struct rsu_softc *sc, uint16_t addr)
5865fdd4e61Sdamien {
5875fdd4e61Sdamien 	uint16_t val;
5885fdd4e61Sdamien 
5895fdd4e61Sdamien 	if (rsu_read_region_1(sc, addr, (uint8_t *)&val, 2) != 0)
5905fdd4e61Sdamien 		return (0xffff);
5915fdd4e61Sdamien 	return (letoh16(val));
5925fdd4e61Sdamien }
5935fdd4e61Sdamien 
5945fdd4e61Sdamien uint32_t
rsu_read_4(struct rsu_softc * sc,uint16_t addr)5955fdd4e61Sdamien rsu_read_4(struct rsu_softc *sc, uint16_t addr)
5965fdd4e61Sdamien {
5975fdd4e61Sdamien 	uint32_t val;
5985fdd4e61Sdamien 
5995fdd4e61Sdamien 	if (rsu_read_region_1(sc, addr, (uint8_t *)&val, 4) != 0)
6005fdd4e61Sdamien 		return (0xffffffff);
6015fdd4e61Sdamien 	return (letoh32(val));
6025fdd4e61Sdamien }
6035fdd4e61Sdamien 
6045fdd4e61Sdamien int
rsu_fw_iocmd(struct rsu_softc * sc,uint32_t iocmd)6055fdd4e61Sdamien rsu_fw_iocmd(struct rsu_softc *sc, uint32_t iocmd)
6065fdd4e61Sdamien {
6075fdd4e61Sdamien 	int ntries;
6085fdd4e61Sdamien 
6095fdd4e61Sdamien 	rsu_write_4(sc, R92S_IOCMD_CTRL, iocmd);
6105fdd4e61Sdamien 	DELAY(100);
6115fdd4e61Sdamien 	for (ntries = 0; ntries < 50; ntries++) {
6125fdd4e61Sdamien 		if (rsu_read_4(sc, R92S_IOCMD_CTRL) == 0)
6135fdd4e61Sdamien 			return (0);
6145fdd4e61Sdamien 		DELAY(10);
6155fdd4e61Sdamien 	}
6165fdd4e61Sdamien 	return (ETIMEDOUT);
6175fdd4e61Sdamien }
6185fdd4e61Sdamien 
6195fdd4e61Sdamien uint8_t
rsu_efuse_read_1(struct rsu_softc * sc,uint16_t addr)6205fdd4e61Sdamien rsu_efuse_read_1(struct rsu_softc *sc, uint16_t addr)
6215fdd4e61Sdamien {
6225fdd4e61Sdamien 	uint32_t reg;
6235fdd4e61Sdamien 	int ntries;
6245fdd4e61Sdamien 
6255fdd4e61Sdamien 	reg = rsu_read_4(sc, R92S_EFUSE_CTRL);
6265fdd4e61Sdamien 	reg = RW(reg, R92S_EFUSE_CTRL_ADDR, addr);
6275fdd4e61Sdamien 	reg &= ~R92S_EFUSE_CTRL_VALID;
6285fdd4e61Sdamien 	rsu_write_4(sc, R92S_EFUSE_CTRL, reg);
6295fdd4e61Sdamien 	/* Wait for read operation to complete. */
6305fdd4e61Sdamien 	for (ntries = 0; ntries < 100; ntries++) {
6315fdd4e61Sdamien 		reg = rsu_read_4(sc, R92S_EFUSE_CTRL);
6325fdd4e61Sdamien 		if (reg & R92S_EFUSE_CTRL_VALID)
6335fdd4e61Sdamien 			return (MS(reg, R92S_EFUSE_CTRL_DATA));
6345fdd4e61Sdamien 		DELAY(5);
6355fdd4e61Sdamien 	}
6365fdd4e61Sdamien 	printf("%s: could not read efuse byte at address 0x%x\n",
6375fdd4e61Sdamien 	    sc->sc_dev.dv_xname, addr);
6385fdd4e61Sdamien 	return (0xff);
6395fdd4e61Sdamien }
6405fdd4e61Sdamien 
6415fdd4e61Sdamien int
rsu_read_rom(struct rsu_softc * sc)6425fdd4e61Sdamien rsu_read_rom(struct rsu_softc *sc)
6435fdd4e61Sdamien {
6445fdd4e61Sdamien 	uint8_t *rom = sc->rom;
6455fdd4e61Sdamien 	uint16_t addr = 0;
6465fdd4e61Sdamien 	uint32_t reg;
6475fdd4e61Sdamien 	uint8_t off, msk;
6485fdd4e61Sdamien 	int i;
6495fdd4e61Sdamien 
6505fdd4e61Sdamien 	/* Make sure that ROM type is eFuse and that autoload succeeded. */
6515fdd4e61Sdamien 	reg = rsu_read_1(sc, R92S_EE_9346CR);
6525fdd4e61Sdamien 	if ((reg & (R92S_9356SEL | R92S_EEPROM_EN)) != R92S_EEPROM_EN)
6535fdd4e61Sdamien 		return (EIO);
6545fdd4e61Sdamien 
6555fdd4e61Sdamien 	/* Turn on 2.5V to prevent eFuse leakage. */
6565fdd4e61Sdamien 	reg = rsu_read_1(sc, R92S_EFUSE_TEST + 3);
6575fdd4e61Sdamien 	rsu_write_1(sc, R92S_EFUSE_TEST + 3, reg | 0x80);
6585fdd4e61Sdamien 	DELAY(1000);
6595fdd4e61Sdamien 	rsu_write_1(sc, R92S_EFUSE_TEST + 3, reg & ~0x80);
6605fdd4e61Sdamien 
6615fdd4e61Sdamien 	/* Read full ROM image. */
6625fdd4e61Sdamien 	memset(&sc->rom, 0xff, sizeof(sc->rom));
6635fdd4e61Sdamien 	while (addr < 512) {
6645fdd4e61Sdamien 		reg = rsu_efuse_read_1(sc, addr);
6655fdd4e61Sdamien 		if (reg == 0xff)
6665fdd4e61Sdamien 			break;
6675fdd4e61Sdamien 		addr++;
6685fdd4e61Sdamien 		off = reg >> 4;
6695fdd4e61Sdamien 		msk = reg & 0xf;
6705fdd4e61Sdamien 		for (i = 0; i < 4; i++) {
6715fdd4e61Sdamien 			if (msk & (1 << i))
6725fdd4e61Sdamien 				continue;
6735fdd4e61Sdamien 			rom[off * 8 + i * 2 + 0] =
6745fdd4e61Sdamien 			    rsu_efuse_read_1(sc, addr);
6755fdd4e61Sdamien 			addr++;
6765fdd4e61Sdamien 			rom[off * 8 + i * 2 + 1] =
6775fdd4e61Sdamien 			    rsu_efuse_read_1(sc, addr);
6785fdd4e61Sdamien 			addr++;
6795fdd4e61Sdamien 		}
6805fdd4e61Sdamien 	}
6815fdd4e61Sdamien #ifdef RSU_DEBUG
6825fdd4e61Sdamien 	if (rsu_debug >= 5) {
6835fdd4e61Sdamien 		/* Dump ROM content. */
6845fdd4e61Sdamien 		printf("\n");
6855fdd4e61Sdamien 		for (i = 0; i < sizeof(sc->rom); i++)
6865fdd4e61Sdamien 			printf("%02x:", rom[i]);
6875fdd4e61Sdamien 		printf("\n");
6885fdd4e61Sdamien 	}
6895fdd4e61Sdamien #endif
6905fdd4e61Sdamien 	return (0);
6915fdd4e61Sdamien }
6925fdd4e61Sdamien 
6935fdd4e61Sdamien int
rsu_fw_cmd(struct rsu_softc * sc,uint8_t code,void * buf,int len)6945fdd4e61Sdamien rsu_fw_cmd(struct rsu_softc *sc, uint8_t code, void *buf, int len)
6955fdd4e61Sdamien {
6965fdd4e61Sdamien 	struct rsu_tx_data *data;
6975fdd4e61Sdamien 	struct r92s_tx_desc *txd;
6985fdd4e61Sdamien 	struct r92s_fw_cmd_hdr *cmd;
699ab0b1be7Smglocker 	struct usbd_pipe *pipe;
7005fdd4e61Sdamien 	int cmdsz, xferlen;
7015fdd4e61Sdamien 
7025fdd4e61Sdamien 	data = sc->fwcmd_data;
7035fdd4e61Sdamien 
7045fdd4e61Sdamien 	/* Round-up command length to a multiple of 8 bytes. */
7055fdd4e61Sdamien 	cmdsz = (len + 7) & ~7;
7065fdd4e61Sdamien 
7075fdd4e61Sdamien 	xferlen = sizeof(*txd) + sizeof(*cmd) + cmdsz;
7085fdd4e61Sdamien 	KASSERT(xferlen <= RSU_TXBUFSZ);
7095fdd4e61Sdamien 	memset(data->buf, 0, xferlen);
7105fdd4e61Sdamien 
7115fdd4e61Sdamien 	/* Setup Tx descriptor. */
7125fdd4e61Sdamien 	txd = (struct r92s_tx_desc *)data->buf;
7135fdd4e61Sdamien 	txd->txdw0 = htole32(
7145fdd4e61Sdamien 	    SM(R92S_TXDW0_OFFSET, sizeof(*txd)) |
7155fdd4e61Sdamien 	    SM(R92S_TXDW0_PKTLEN, sizeof(*cmd) + cmdsz) |
7165fdd4e61Sdamien 	    R92S_TXDW0_OWN | R92S_TXDW0_FSG | R92S_TXDW0_LSG);
7175fdd4e61Sdamien 	txd->txdw1 = htole32(SM(R92S_TXDW1_QSEL, R92S_TXDW1_QSEL_H2C));
7185fdd4e61Sdamien 
7195fdd4e61Sdamien 	/* Setup command header. */
7205fdd4e61Sdamien 	cmd = (struct r92s_fw_cmd_hdr *)&txd[1];
7215fdd4e61Sdamien 	cmd->len = htole16(cmdsz);
7225fdd4e61Sdamien 	cmd->code = code;
7235fdd4e61Sdamien 	cmd->seq = sc->cmd_seq;
7245fdd4e61Sdamien 	sc->cmd_seq = (sc->cmd_seq + 1) & 0x7f;
7255fdd4e61Sdamien 
7265fdd4e61Sdamien 	/* Copy command payload. */
7275fdd4e61Sdamien 	memcpy(&cmd[1], buf, len);
7285fdd4e61Sdamien 
7295fdd4e61Sdamien 	DPRINTFN(2, ("Tx cmd code=%d len=%d\n", code, cmdsz));
7305fdd4e61Sdamien 	pipe = sc->pipe[sc->qid2idx[RSU_QID_H2C]];
7315fdd4e61Sdamien 	usbd_setup_xfer(data->xfer, pipe, NULL, data->buf, xferlen,
732aa88c704Smpi 	    USBD_SHORT_XFER_OK | USBD_NO_COPY | USBD_SYNCHRONOUS,
733aa88c704Smpi 	    RSU_CMD_TIMEOUT, NULL);
734aa88c704Smpi 	return (usbd_transfer(data->xfer));
7355fdd4e61Sdamien }
7365fdd4e61Sdamien 
7375fdd4e61Sdamien int
rsu_media_change(struct ifnet * ifp)7385fdd4e61Sdamien rsu_media_change(struct ifnet *ifp)
7395fdd4e61Sdamien {
7405fdd4e61Sdamien 	int error;
7415fdd4e61Sdamien 
7425fdd4e61Sdamien 	error = ieee80211_media_change(ifp);
7435fdd4e61Sdamien 	if (error != ENETRESET)
7445fdd4e61Sdamien 		return (error);
7455fdd4e61Sdamien 
7465fdd4e61Sdamien 	if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) ==
7475fdd4e61Sdamien 	    (IFF_UP | IFF_RUNNING)) {
7485fdd4e61Sdamien 		rsu_stop(ifp);
7492e342c84Skevlo 		error = rsu_init(ifp);
7505fdd4e61Sdamien 	}
7512e342c84Skevlo 	return (error);
7525fdd4e61Sdamien }
7535fdd4e61Sdamien 
7545fdd4e61Sdamien void
rsu_calib_to(void * arg)7555fdd4e61Sdamien rsu_calib_to(void *arg)
7565fdd4e61Sdamien {
7575fdd4e61Sdamien 	struct rsu_softc *sc = arg;
7585fdd4e61Sdamien 
7595ad57547Sjakemsr 	if (usbd_is_dying(sc->sc_udev))
7605ad57547Sjakemsr 		return;
7615ad57547Sjakemsr 
7625ad57547Sjakemsr 	usbd_ref_incr(sc->sc_udev);
7635ad57547Sjakemsr 
7645fdd4e61Sdamien 	/* Do it in a process context. */
7655fdd4e61Sdamien 	rsu_do_async(sc, rsu_calib_cb, NULL, 0);
7665ad57547Sjakemsr 
7675ad57547Sjakemsr 	usbd_ref_decr(sc->sc_udev);
7685fdd4e61Sdamien }
7695fdd4e61Sdamien 
7705fdd4e61Sdamien void
rsu_calib_cb(struct rsu_softc * sc,void * arg)7715fdd4e61Sdamien rsu_calib_cb(struct rsu_softc *sc, void *arg)
7725fdd4e61Sdamien {
7735fdd4e61Sdamien 	uint32_t reg;
7745fdd4e61Sdamien 
7755fdd4e61Sdamien #ifdef notyet
7765fdd4e61Sdamien 	/* Read WPS PBC status. */
7775fdd4e61Sdamien 	rsu_write_1(sc, R92S_MAC_PINMUX_CTRL,
7785fdd4e61Sdamien 	    R92S_GPIOMUX_EN | SM(R92S_GPIOSEL_GPIO, R92S_GPIOSEL_GPIO_JTAG));
7795fdd4e61Sdamien 	rsu_write_1(sc, R92S_GPIO_IO_SEL,
7805fdd4e61Sdamien 	    rsu_read_1(sc, R92S_GPIO_IO_SEL) & ~R92S_GPIO_WPS);
7815fdd4e61Sdamien 	reg = rsu_read_1(sc, R92S_GPIO_CTRL);
7825fdd4e61Sdamien 	if (reg != 0xff && (reg & R92S_GPIO_WPS))
7835fdd4e61Sdamien 		DPRINTF(("WPS PBC is pushed\n"));
7845fdd4e61Sdamien #endif
7855fdd4e61Sdamien 	/* Read current signal level. */
7865fdd4e61Sdamien 	if (rsu_fw_iocmd(sc, 0xf4000001) == 0) {
7875fdd4e61Sdamien 		reg = rsu_read_4(sc, R92S_IOCMD_DATA);
7885fdd4e61Sdamien 		DPRINTFN(8, ("RSSI=%d%%\n", reg >> 4));
7895fdd4e61Sdamien 	}
7905fdd4e61Sdamien 
7915ad57547Sjakemsr 	if (!usbd_is_dying(sc->sc_udev))
7925fdd4e61Sdamien 		timeout_add_sec(&sc->calib_to, 2);
7935fdd4e61Sdamien }
7945fdd4e61Sdamien 
7955fdd4e61Sdamien int
rsu_newstate(struct ieee80211com * ic,enum ieee80211_state nstate,int arg)7965fdd4e61Sdamien rsu_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
7975fdd4e61Sdamien {
7985fdd4e61Sdamien 	struct rsu_softc *sc = ic->ic_softc;
7995fdd4e61Sdamien 	struct rsu_cmd_newstate cmd;
8005fdd4e61Sdamien 
8015fdd4e61Sdamien 	/* Do it in a process context. */
8025fdd4e61Sdamien 	cmd.state = nstate;
8035fdd4e61Sdamien 	cmd.arg = arg;
8045fdd4e61Sdamien 	rsu_do_async(sc, rsu_newstate_cb, &cmd, sizeof(cmd));
8055fdd4e61Sdamien 	return (0);
8065fdd4e61Sdamien }
8075fdd4e61Sdamien 
8085fdd4e61Sdamien void
rsu_newstate_cb(struct rsu_softc * sc,void * arg)8095fdd4e61Sdamien rsu_newstate_cb(struct rsu_softc *sc, void *arg)
8105fdd4e61Sdamien {
8115fdd4e61Sdamien 	struct rsu_cmd_newstate *cmd = arg;
8125fdd4e61Sdamien 	struct ieee80211com *ic = &sc->sc_ic;
8135d1a01feSstsp 	struct ifnet *ifp = &ic->ic_if;
8145fdd4e61Sdamien 	enum ieee80211_state ostate;
8155fdd4e61Sdamien 	int error, s;
8165fdd4e61Sdamien 
8175fdd4e61Sdamien 	s = splnet();
8185fdd4e61Sdamien 	ostate = ic->ic_state;
8195fdd4e61Sdamien 
8205fdd4e61Sdamien 	if (ostate == IEEE80211_S_RUN) {
8215fdd4e61Sdamien 		/* Stop calibration. */
8225fdd4e61Sdamien 		timeout_del(&sc->calib_to);
8235fdd4e61Sdamien 		/* Disassociate from our current BSS. */
8245fdd4e61Sdamien 		(void)rsu_disconnect(sc);
8255fdd4e61Sdamien 	}
8265fdd4e61Sdamien 	switch (cmd->state) {
8275fdd4e61Sdamien 	case IEEE80211_S_INIT:
8285fdd4e61Sdamien 		break;
8295fdd4e61Sdamien 	case IEEE80211_S_SCAN:
8305fdd4e61Sdamien 		error = rsu_site_survey(sc);
8315fdd4e61Sdamien 		if (error != 0) {
8325fdd4e61Sdamien 			printf("%s: could not send site survey command\n",
8335fdd4e61Sdamien 			    sc->sc_dev.dv_xname);
8345fdd4e61Sdamien 		}
8355d1a01feSstsp 		if (ifp->if_flags & IFF_DEBUG)
8365d1a01feSstsp 			printf("%s: %s -> %s\n", ifp->if_xname,
8375d1a01feSstsp 			    ieee80211_state_name[ic->ic_state],
8385d1a01feSstsp 			    ieee80211_state_name[cmd->state]);
8395fdd4e61Sdamien 		ic->ic_state = cmd->state;
8405fdd4e61Sdamien 		splx(s);
8415fdd4e61Sdamien 		return;
8425fdd4e61Sdamien 	case IEEE80211_S_AUTH:
843ba76f343Sstsp 		ic->ic_bss->ni_rsn_supp_state = RSNA_SUPP_INITIALIZE;
8445fdd4e61Sdamien 		error = rsu_join_bss(sc, ic->ic_bss);
8455fdd4e61Sdamien 		if (error != 0) {
8465fdd4e61Sdamien 			printf("%s: could not send join command\n",
8475fdd4e61Sdamien 			    sc->sc_dev.dv_xname);
8485fdd4e61Sdamien 			ieee80211_begin_scan(&ic->ic_if);
8495fdd4e61Sdamien 			splx(s);
8505fdd4e61Sdamien 			return;
8515fdd4e61Sdamien 		}
8525d1a01feSstsp 		if (ifp->if_flags & IFF_DEBUG)
8535d1a01feSstsp 			printf("%s: %s -> %s\n", ifp->if_xname,
8545d1a01feSstsp 			    ieee80211_state_name[ic->ic_state],
8555d1a01feSstsp 			    ieee80211_state_name[cmd->state]);
8565fdd4e61Sdamien 		ic->ic_state = cmd->state;
857ba76f343Sstsp 		if (ic->ic_flags & IEEE80211_F_RSNON)
858ba76f343Sstsp 			ic->ic_bss->ni_rsn_supp_state = RSNA_SUPP_PTKSTART;
8595fdd4e61Sdamien 		splx(s);
8605fdd4e61Sdamien 		return;
8615fdd4e61Sdamien 	case IEEE80211_S_ASSOC:
862ba76f343Sstsp 		/* No-op for this driver. See rsu_event_join_bss(). */
8635d1a01feSstsp 		if (ifp->if_flags & IFF_DEBUG)
8645d1a01feSstsp 			printf("%s: %s -> %s\n", ifp->if_xname,
8655d1a01feSstsp 			    ieee80211_state_name[ic->ic_state],
8665d1a01feSstsp 			    ieee80211_state_name[cmd->state]);
8675fdd4e61Sdamien 		ic->ic_state = cmd->state;
8685fdd4e61Sdamien 		splx(s);
8695fdd4e61Sdamien 		return;
8705fdd4e61Sdamien 	case IEEE80211_S_RUN:
8715fdd4e61Sdamien 		/* Indicate highest supported rate. */
8725fdd4e61Sdamien 		ic->ic_bss->ni_txrate = ic->ic_bss->ni_rates.rs_nrates - 1;
8735fdd4e61Sdamien 
8745fdd4e61Sdamien 		/* Start periodic calibration. */
8755ad57547Sjakemsr 		if (!usbd_is_dying(sc->sc_udev))
8765fdd4e61Sdamien 			timeout_add_sec(&sc->calib_to, 2);
8775fdd4e61Sdamien 		break;
8785fdd4e61Sdamien 	}
8795fdd4e61Sdamien 	(void)sc->sc_newstate(ic, cmd->state, cmd->arg);
8805fdd4e61Sdamien 	splx(s);
8815fdd4e61Sdamien }
8825fdd4e61Sdamien 
8835fdd4e61Sdamien int
rsu_set_key(struct ieee80211com * ic,struct ieee80211_node * ni,struct ieee80211_key * k)8845fdd4e61Sdamien rsu_set_key(struct ieee80211com *ic, struct ieee80211_node *ni,
8855fdd4e61Sdamien     struct ieee80211_key *k)
8865fdd4e61Sdamien {
8875fdd4e61Sdamien 	struct rsu_softc *sc = ic->ic_softc;
8885fdd4e61Sdamien 	struct rsu_cmd_key cmd;
8895fdd4e61Sdamien 
8905fdd4e61Sdamien 	/* Defer setting of WEP keys until interface is brought up. */
8915fdd4e61Sdamien 	if ((ic->ic_if.if_flags & (IFF_UP | IFF_RUNNING)) !=
8925fdd4e61Sdamien 	    (IFF_UP | IFF_RUNNING))
8935fdd4e61Sdamien 		return (0);
8945fdd4e61Sdamien 
8955fdd4e61Sdamien 	/* Do it in a process context. */
8965fdd4e61Sdamien 	cmd.key = *k;
8974fac4e76Skrw 	cmd.ni = ni;
8985fdd4e61Sdamien 	rsu_do_async(sc, rsu_set_key_cb, &cmd, sizeof(cmd));
8994fac4e76Skrw 	sc->sc_key_tasks++;
9004fac4e76Skrw 	return EBUSY;
9015fdd4e61Sdamien }
9025fdd4e61Sdamien 
9035fdd4e61Sdamien void
rsu_set_key_cb(struct rsu_softc * sc,void * arg)9045fdd4e61Sdamien rsu_set_key_cb(struct rsu_softc *sc, void *arg)
9055fdd4e61Sdamien {
9065fdd4e61Sdamien 	struct rsu_cmd_key *cmd = arg;
9074fac4e76Skrw 	struct ieee80211com *ic = &sc->sc_ic;
9085fdd4e61Sdamien 	struct ieee80211_key *k = &cmd->key;
9095fdd4e61Sdamien 	struct r92s_fw_cmd_set_key key;
9105fdd4e61Sdamien 
9114fac4e76Skrw 	sc->sc_key_tasks--;
9124fac4e76Skrw 
9135fdd4e61Sdamien 	memset(&key, 0, sizeof(key));
9145fdd4e61Sdamien 	/* Map net80211 cipher to HW crypto algorithm. */
9155fdd4e61Sdamien 	switch (k->k_cipher) {
9165fdd4e61Sdamien 	case IEEE80211_CIPHER_WEP40:
9175fdd4e61Sdamien 		key.algo = R92S_KEY_ALGO_WEP40;
9185fdd4e61Sdamien 		break;
9195fdd4e61Sdamien 	case IEEE80211_CIPHER_WEP104:
9205fdd4e61Sdamien 		key.algo = R92S_KEY_ALGO_WEP104;
9215fdd4e61Sdamien 		break;
9225fdd4e61Sdamien 	case IEEE80211_CIPHER_TKIP:
9235fdd4e61Sdamien 		key.algo = R92S_KEY_ALGO_TKIP;
9245fdd4e61Sdamien 		break;
9255fdd4e61Sdamien 	case IEEE80211_CIPHER_CCMP:
9265fdd4e61Sdamien 		key.algo = R92S_KEY_ALGO_AES;
9275fdd4e61Sdamien 		break;
9285fdd4e61Sdamien 	default:
9294fac4e76Skrw 		IEEE80211_SEND_MGMT(ic, cmd->ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
9304fac4e76Skrw 		    IEEE80211_REASON_AUTH_LEAVE);
9314fac4e76Skrw 		ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
9325fdd4e61Sdamien 		return;
9335fdd4e61Sdamien 	}
9345fdd4e61Sdamien 	key.id = k->k_id;
9355fdd4e61Sdamien 	key.grpkey = (k->k_flags & IEEE80211_KEY_GROUP) != 0;
9365fdd4e61Sdamien 	memcpy(key.key, k->k_key, MIN(k->k_len, sizeof(key.key)));
9375fdd4e61Sdamien 	(void)rsu_fw_cmd(sc, R92S_CMD_SET_KEY, &key, sizeof(key));
9384fac4e76Skrw 
9394fac4e76Skrw 	if (sc->sc_key_tasks == 0) {
9404fac4e76Skrw 		DPRINTF(("marking port %s valid\n",
9414fac4e76Skrw 		    ether_sprintf(cmd->ni->ni_macaddr)));
9424fac4e76Skrw 		cmd->ni->ni_port_valid = 1;
9434fac4e76Skrw 		ieee80211_set_link_state(ic, LINK_STATE_UP);
9444fac4e76Skrw 	}
9455fdd4e61Sdamien }
9465fdd4e61Sdamien 
9475fdd4e61Sdamien void
rsu_delete_key(struct ieee80211com * ic,struct ieee80211_node * ni,struct ieee80211_key * k)9485fdd4e61Sdamien rsu_delete_key(struct ieee80211com *ic, struct ieee80211_node *ni,
9495fdd4e61Sdamien     struct ieee80211_key *k)
9505fdd4e61Sdamien {
951fcc42384Sdamien 	struct rsu_softc *sc = ic->ic_softc;
952fcc42384Sdamien 	struct rsu_cmd_key cmd;
953fcc42384Sdamien 
954fcc42384Sdamien 	if (!(ic->ic_if.if_flags & IFF_RUNNING) ||
955fcc42384Sdamien 	    ic->ic_state != IEEE80211_S_RUN)
956fcc42384Sdamien 		return;	/* Nothing to do. */
957fcc42384Sdamien 
958fcc42384Sdamien 	/* Do it in a process context. */
959fcc42384Sdamien 	cmd.key = *k;
960fcc42384Sdamien 	rsu_do_async(sc, rsu_delete_key_cb, &cmd, sizeof(cmd));
961fcc42384Sdamien }
962fcc42384Sdamien 
963fcc42384Sdamien void
rsu_delete_key_cb(struct rsu_softc * sc,void * arg)964fcc42384Sdamien rsu_delete_key_cb(struct rsu_softc *sc, void *arg)
965fcc42384Sdamien {
966fcc42384Sdamien 	struct rsu_cmd_key *cmd = arg;
967fcc42384Sdamien 	struct ieee80211_key *k = &cmd->key;
968fcc42384Sdamien 	struct r92s_fw_cmd_set_key key;
969fcc42384Sdamien 
970fcc42384Sdamien 	memset(&key, 0, sizeof(key));
971fcc42384Sdamien 	key.id = k->k_id;
972fcc42384Sdamien 	(void)rsu_fw_cmd(sc, R92S_CMD_SET_KEY, &key, sizeof(key));
9735fdd4e61Sdamien }
9745fdd4e61Sdamien 
9755fdd4e61Sdamien int
rsu_site_survey(struct rsu_softc * sc)9765fdd4e61Sdamien rsu_site_survey(struct rsu_softc *sc)
9775fdd4e61Sdamien {
9785fdd4e61Sdamien 	struct ieee80211com *ic = &sc->sc_ic;
9795fdd4e61Sdamien 	struct r92s_fw_cmd_sitesurvey cmd;
9805fdd4e61Sdamien 
9815fdd4e61Sdamien 	memset(&cmd, 0, sizeof(cmd));
9825fdd4e61Sdamien 	if ((ic->ic_flags & IEEE80211_F_ASCAN) || sc->scan_pass == 1)
9835fdd4e61Sdamien 		cmd.active = htole32(1);
9845fdd4e61Sdamien 	cmd.limit = htole32(48);
9855fdd4e61Sdamien 	if (sc->scan_pass == 1) {
9865fdd4e61Sdamien 		/* Do a directed scan for second pass. */
9875fdd4e61Sdamien 		cmd.ssidlen = htole32(ic->ic_des_esslen);
9885fdd4e61Sdamien 		memcpy(cmd.ssid, ic->ic_des_essid, ic->ic_des_esslen);
9895fdd4e61Sdamien 	}
9905fdd4e61Sdamien 	DPRINTF(("sending site survey command, pass=%d\n", sc->scan_pass));
9915fdd4e61Sdamien 	return (rsu_fw_cmd(sc, R92S_CMD_SITE_SURVEY, &cmd, sizeof(cmd)));
9925fdd4e61Sdamien }
9935fdd4e61Sdamien 
9945fdd4e61Sdamien int
rsu_join_bss(struct rsu_softc * sc,struct ieee80211_node * ni)9955fdd4e61Sdamien rsu_join_bss(struct rsu_softc *sc, struct ieee80211_node *ni)
9965fdd4e61Sdamien {
9975fdd4e61Sdamien 	struct ieee80211com *ic = &sc->sc_ic;
9985fdd4e61Sdamien 	struct ndis_wlan_bssid_ex *bss;
9995fdd4e61Sdamien 	struct ndis_802_11_fixed_ies *fixed;
10005fdd4e61Sdamien 	struct r92s_fw_cmd_auth auth;
10015fdd4e61Sdamien 	uint8_t buf[sizeof(*bss) + 128], *frm;
10025fdd4e61Sdamien 	uint8_t opmode;
10035fdd4e61Sdamien 	int error;
10045fdd4e61Sdamien 
10055fdd4e61Sdamien 	/* Let the FW decide the opmode based on the capinfo field. */
10065fdd4e61Sdamien 	opmode = NDIS802_11AUTOUNKNOWN;
10075fdd4e61Sdamien 	DPRINTF(("setting operating mode to %d\n", opmode));
10085fdd4e61Sdamien 	error = rsu_fw_cmd(sc, R92S_CMD_SET_OPMODE, &opmode, sizeof(opmode));
10095fdd4e61Sdamien 	if (error != 0)
10105fdd4e61Sdamien 		return (error);
10115fdd4e61Sdamien 
10125fdd4e61Sdamien 	memset(&auth, 0, sizeof(auth));
10135fdd4e61Sdamien 	if (ic->ic_flags & IEEE80211_F_RSNON) {
10145fdd4e61Sdamien 		auth.mode = R92S_AUTHMODE_WPA;
10155fdd4e61Sdamien 		auth.dot1x = ieee80211_is_8021x_akm(ni->ni_rsnakms);
10165fdd4e61Sdamien 	} else
10175fdd4e61Sdamien 		auth.mode = R92S_AUTHMODE_OPEN;
10185fdd4e61Sdamien 	DPRINTF(("setting auth mode to %d\n", auth.mode));
10195fdd4e61Sdamien 	error = rsu_fw_cmd(sc, R92S_CMD_SET_AUTH, &auth, sizeof(auth));
10205fdd4e61Sdamien 	if (error != 0)
10215fdd4e61Sdamien 		return (error);
10225fdd4e61Sdamien 
10235fdd4e61Sdamien 	memset(buf, 0, sizeof(buf));
10245fdd4e61Sdamien 	bss = (struct ndis_wlan_bssid_ex *)buf;
10255fdd4e61Sdamien 	IEEE80211_ADDR_COPY(bss->macaddr, ni->ni_bssid);
10265fdd4e61Sdamien 	bss->ssid.ssidlen = htole32(ni->ni_esslen);
10275fdd4e61Sdamien 	memcpy(bss->ssid.ssid, ni->ni_essid, ni->ni_esslen);
10285fdd4e61Sdamien 	if (ic->ic_flags & (IEEE80211_F_WEPON | IEEE80211_F_RSNON))
10295fdd4e61Sdamien 		bss->privacy = htole32(1);
10305fdd4e61Sdamien 	bss->rssi = htole32(ni->ni_rssi);
10315fdd4e61Sdamien 	if (ic->ic_curmode == IEEE80211_MODE_11B)
10325fdd4e61Sdamien 		bss->networktype = htole32(NDIS802_11DS);
10335fdd4e61Sdamien 	else
10345fdd4e61Sdamien 		bss->networktype = htole32(NDIS802_11OFDM24);
10355fdd4e61Sdamien 	bss->config.len = htole32(sizeof(bss->config));
10365fdd4e61Sdamien 	bss->config.bintval = htole32(ni->ni_intval);
10375fdd4e61Sdamien 	bss->config.dsconfig = htole32(ieee80211_chan2ieee(ic, ni->ni_chan));
10385fdd4e61Sdamien 	bss->inframode = htole32(NDIS802_11INFRASTRUCTURE);
10395fdd4e61Sdamien 	memcpy(bss->supprates, ni->ni_rates.rs_rates,
10405fdd4e61Sdamien 	    ni->ni_rates.rs_nrates);
10415fdd4e61Sdamien 	/* Write the fixed fields of the beacon frame. */
10425fdd4e61Sdamien 	fixed = (struct ndis_802_11_fixed_ies *)&bss[1];
10435fdd4e61Sdamien 	memcpy(&fixed->tstamp, ni->ni_tstamp, 8);
10445fdd4e61Sdamien 	fixed->bintval = htole16(ni->ni_intval);
10455fdd4e61Sdamien 	fixed->capabilities = htole16(ni->ni_capinfo);
1046fcc42384Sdamien 	/* Write IEs to be included in the association request. */
10475fdd4e61Sdamien 	frm = (uint8_t *)&fixed[1];
10485fdd4e61Sdamien 	if ((ic->ic_flags & IEEE80211_F_RSNON) &&
10495fdd4e61Sdamien 	    (ni->ni_rsnprotos & IEEE80211_PROTO_RSN))
10505fdd4e61Sdamien 		frm = ieee80211_add_rsn(frm, ic, ni);
10515fdd4e61Sdamien 	if (ni->ni_flags & IEEE80211_NODE_QOS)
10525fdd4e61Sdamien 		frm = ieee80211_add_qos_capability(frm, ic);
10535fdd4e61Sdamien 	if (ni->ni_flags & IEEE80211_NODE_HT)
10545fdd4e61Sdamien 		frm = ieee80211_add_htcaps(frm, ic);
10555fdd4e61Sdamien 	if ((ic->ic_flags & IEEE80211_F_RSNON) &&
10565fdd4e61Sdamien 	    (ni->ni_rsnprotos & IEEE80211_PROTO_WPA))
10575fdd4e61Sdamien 		frm = ieee80211_add_wpa(frm, ic, ni);
10585fdd4e61Sdamien 	bss->ieslen = htole32(frm - (uint8_t *)fixed);
10595fdd4e61Sdamien 	bss->len = htole32(((frm - buf) + 3) & ~3);
10605fdd4e61Sdamien 	DPRINTF(("sending join bss command to %s chan %d\n",
10615fdd4e61Sdamien 	    ether_sprintf(bss->macaddr), letoh32(bss->config.dsconfig)));
1062091b4750Skettenis 	return (rsu_fw_cmd(sc, R92S_CMD_JOIN_BSS, buf, sizeof(buf)));
10635fdd4e61Sdamien }
10645fdd4e61Sdamien 
10655fdd4e61Sdamien int
rsu_disconnect(struct rsu_softc * sc)10665fdd4e61Sdamien rsu_disconnect(struct rsu_softc *sc)
10675fdd4e61Sdamien {
10685fdd4e61Sdamien 	uint32_t zero = 0;	/* :-) */
10695fdd4e61Sdamien 
10705fdd4e61Sdamien 	/* Disassociate from our current BSS. */
10715fdd4e61Sdamien 	DPRINTF(("sending disconnect command\n"));
10725fdd4e61Sdamien 	return (rsu_fw_cmd(sc, R92S_CMD_DISCONNECT, &zero, sizeof(zero)));
10735fdd4e61Sdamien }
10745fdd4e61Sdamien 
10755fdd4e61Sdamien void
rsu_event_survey(struct rsu_softc * sc,uint8_t * buf,int len)10765fdd4e61Sdamien rsu_event_survey(struct rsu_softc *sc, uint8_t *buf, int len)
10775fdd4e61Sdamien {
10785fdd4e61Sdamien 	struct ieee80211com *ic = &sc->sc_ic;
10795fdd4e61Sdamien 	struct ifnet *ifp = &ic->ic_if;
10805fdd4e61Sdamien 	struct ieee80211_rxinfo rxi;
10815fdd4e61Sdamien 	struct ieee80211_node *ni;
10825fdd4e61Sdamien 	struct ieee80211_frame *wh;
10835fdd4e61Sdamien 	struct ndis_wlan_bssid_ex *bss;
10845fdd4e61Sdamien 	struct mbuf *m;
1085d5f8d3bdSstsp 	uint32_t pktlen, ieslen;
10865fdd4e61Sdamien 
10875fdd4e61Sdamien 	if (__predict_false(len < sizeof(*bss)))
10885fdd4e61Sdamien 		return;
10895fdd4e61Sdamien 	bss = (struct ndis_wlan_bssid_ex *)buf;
1090d5f8d3bdSstsp 	ieslen = letoh32(bss->ieslen);
1091d5f8d3bdSstsp 	if (ieslen > len - sizeof(*bss))
10925fdd4e61Sdamien 		return;
10935fdd4e61Sdamien 
10945fdd4e61Sdamien 	DPRINTFN(2, ("found BSS %s: len=%d chan=%d inframode=%d "
10955fdd4e61Sdamien 	    "networktype=%d privacy=%d\n",
10965fdd4e61Sdamien 	    ether_sprintf(bss->macaddr), letoh32(bss->len),
10975fdd4e61Sdamien 	    letoh32(bss->config.dsconfig), letoh32(bss->inframode),
10985fdd4e61Sdamien 	    letoh32(bss->networktype), letoh32(bss->privacy)));
10995fdd4e61Sdamien 
11005fdd4e61Sdamien 	/* Build a fake beacon frame to let net80211 do all the parsing. */
1101d5f8d3bdSstsp 	pktlen = sizeof(*wh) + ieslen;
11025fdd4e61Sdamien 	if (__predict_false(pktlen > MCLBYTES))
11035fdd4e61Sdamien 		return;
11045fdd4e61Sdamien 	MGETHDR(m, M_DONTWAIT, MT_DATA);
11055fdd4e61Sdamien 	if (__predict_false(m == NULL))
11065fdd4e61Sdamien 		return;
11075fdd4e61Sdamien 	if (pktlen > MHLEN) {
11085fdd4e61Sdamien 		MCLGET(m, M_DONTWAIT);
11095fdd4e61Sdamien 		if (!(m->m_flags & M_EXT)) {
11105fdd4e61Sdamien 			m_free(m);
11115fdd4e61Sdamien 			return;
11125fdd4e61Sdamien 		}
11135fdd4e61Sdamien 	}
11145fdd4e61Sdamien 	wh = mtod(m, struct ieee80211_frame *);
11155fdd4e61Sdamien 	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
11165fdd4e61Sdamien 	    IEEE80211_FC0_SUBTYPE_BEACON;
11175fdd4e61Sdamien 	wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
11185fdd4e61Sdamien 	*(uint16_t *)wh->i_dur = 0;
11195fdd4e61Sdamien 	IEEE80211_ADDR_COPY(wh->i_addr1, etherbroadcastaddr);
11205fdd4e61Sdamien 	IEEE80211_ADDR_COPY(wh->i_addr2, bss->macaddr);
11215fdd4e61Sdamien 	IEEE80211_ADDR_COPY(wh->i_addr3, bss->macaddr);
11225fdd4e61Sdamien 	*(uint16_t *)wh->i_seq = 0;
1123d5f8d3bdSstsp 	memcpy(&wh[1], (uint8_t *)&bss[1], ieslen);
11245fdd4e61Sdamien 
11255fdd4e61Sdamien 	/* Finalize mbuf. */
11265fdd4e61Sdamien 	m->m_pkthdr.len = m->m_len = pktlen;
11275fdd4e61Sdamien 
11285fdd4e61Sdamien 	ni = ieee80211_find_rxnode(ic, wh);
112952a13037Sstsp 	memset(&rxi, 0, sizeof(rxi));
11305fdd4e61Sdamien 	rxi.rxi_rssi = letoh32(bss->rssi);
11315fdd4e61Sdamien 	ieee80211_input(ifp, m, ni, &rxi);
11325fdd4e61Sdamien 	/* Node is no longer needed. */
11335fdd4e61Sdamien 	ieee80211_release_node(ic, ni);
11345fdd4e61Sdamien }
11355fdd4e61Sdamien 
11365fdd4e61Sdamien void
rsu_event_join_bss(struct rsu_softc * sc,uint8_t * buf,int len)11375fdd4e61Sdamien rsu_event_join_bss(struct rsu_softc *sc, uint8_t *buf, int len)
11385fdd4e61Sdamien {
11395fdd4e61Sdamien 	struct ieee80211com *ic = &sc->sc_ic;
11405fdd4e61Sdamien 	struct ieee80211_node *ni = ic->ic_bss;
11415fdd4e61Sdamien 	struct r92s_event_join_bss *rsp;
11425fdd4e61Sdamien 	int res;
11435fdd4e61Sdamien 
11445fdd4e61Sdamien 	if (__predict_false(len < sizeof(*rsp)))
11455fdd4e61Sdamien 		return;
11465fdd4e61Sdamien 	rsp = (struct r92s_event_join_bss *)buf;
11475fdd4e61Sdamien 	res = (int)letoh32(rsp->join_res);
11485fdd4e61Sdamien 
11495fdd4e61Sdamien 	DPRINTF(("Rx join BSS event len=%d res=%d\n", len, res));
11505fdd4e61Sdamien 	if (res <= 0) {
11515fdd4e61Sdamien 		ic->ic_stats.is_rx_auth_fail++;
11525fdd4e61Sdamien 		ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
11535fdd4e61Sdamien 		return;
11545fdd4e61Sdamien 	}
11555fdd4e61Sdamien 	DPRINTF(("associated with %s associd=%d\n",
11565fdd4e61Sdamien 	    ether_sprintf(rsp->bss.macaddr), letoh32(rsp->associd)));
11575fdd4e61Sdamien 
11585fdd4e61Sdamien 	ni->ni_associd = letoh32(rsp->associd) | 0xc000;
11595fdd4e61Sdamien 	if (ic->ic_flags & IEEE80211_F_WEPON)
11605fdd4e61Sdamien 		ni->ni_flags |= IEEE80211_NODE_TXRXPROT;
11615fdd4e61Sdamien 
1162ba76f343Sstsp 	/* Force an ASSOC->RUN transition. AUTH->RUN is invalid. */
1163ba76f343Sstsp 	ic->ic_state = IEEE80211_S_ASSOC;
11645fdd4e61Sdamien 	ieee80211_new_state(ic, IEEE80211_S_RUN,
11655fdd4e61Sdamien 	    IEEE80211_FC0_SUBTYPE_ASSOC_RESP);
11665fdd4e61Sdamien }
11675fdd4e61Sdamien 
11685fdd4e61Sdamien void
rsu_rx_event(struct rsu_softc * sc,uint8_t code,uint8_t * buf,int len)11695fdd4e61Sdamien rsu_rx_event(struct rsu_softc *sc, uint8_t code, uint8_t *buf, int len)
11705fdd4e61Sdamien {
11715fdd4e61Sdamien 	struct ieee80211com *ic = &sc->sc_ic;
11725fdd4e61Sdamien 	struct ifnet *ifp = &ic->ic_if;
11735fdd4e61Sdamien 
11745fdd4e61Sdamien 	DPRINTFN(4, ("Rx event code=%d len=%d\n", code, len));
11755fdd4e61Sdamien 	switch (code) {
1176fcc42384Sdamien 	case R92S_EVT_SURVEY:
11775fdd4e61Sdamien 		if (ic->ic_state == IEEE80211_S_SCAN)
11785fdd4e61Sdamien 			rsu_event_survey(sc, buf, len);
11795fdd4e61Sdamien 		break;
1180fcc42384Sdamien 	case R92S_EVT_SURVEY_DONE:
11815fdd4e61Sdamien 		DPRINTF(("site survey pass %d done, found %d BSS\n",
11825fdd4e61Sdamien 		    sc->scan_pass, letoh32(*(uint32_t *)buf)));
11835fdd4e61Sdamien 		if (ic->ic_state != IEEE80211_S_SCAN)
11845fdd4e61Sdamien 			break;	/* Ignore if not scanning. */
11855fdd4e61Sdamien 		if (sc->scan_pass == 0 && ic->ic_des_esslen != 0) {
11865fdd4e61Sdamien 			/* Schedule a directed scan for hidden APs. */
11875fdd4e61Sdamien 			sc->scan_pass = 1;
11885fdd4e61Sdamien 			ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
11895fdd4e61Sdamien 			break;
11905fdd4e61Sdamien 		}
11915fdd4e61Sdamien 		ieee80211_end_scan(ifp);
11925fdd4e61Sdamien 		sc->scan_pass = 0;
11935fdd4e61Sdamien 		break;
1194fcc42384Sdamien 	case R92S_EVT_JOIN_BSS:
11955fdd4e61Sdamien 		if (ic->ic_state == IEEE80211_S_AUTH)
11965fdd4e61Sdamien 			rsu_event_join_bss(sc, buf, len);
11975fdd4e61Sdamien 		break;
1198fcc42384Sdamien 	case R92S_EVT_DEL_STA:
11995fdd4e61Sdamien 		DPRINTF(("disassociated from %s\n", ether_sprintf(buf)));
12005fdd4e61Sdamien 		if (ic->ic_state == IEEE80211_S_RUN &&
12015fdd4e61Sdamien 		    IEEE80211_ADDR_EQ(ic->ic_bss->ni_bssid, buf))
12025fdd4e61Sdamien 			ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
12035fdd4e61Sdamien 		break;
1204fcc42384Sdamien 	case R92S_EVT_WPS_PBC:
12055fdd4e61Sdamien 		DPRINTF(("WPS PBC pushed.\n"));
12065fdd4e61Sdamien 		break;
1207fcc42384Sdamien 	case R92S_EVT_FWDBG:
12083423a330Sdamien 		if (ifp->if_flags & IFF_DEBUG) {
12095fdd4e61Sdamien 			buf[60] = '\0';
12105fdd4e61Sdamien 			printf("FWDBG: %s\n", (char *)buf);
12113423a330Sdamien 		}
12125fdd4e61Sdamien 		break;
12135fdd4e61Sdamien 	}
12145fdd4e61Sdamien }
12155fdd4e61Sdamien 
12165fdd4e61Sdamien void
rsu_rx_multi_event(struct rsu_softc * sc,uint8_t * buf,int len)12175fdd4e61Sdamien rsu_rx_multi_event(struct rsu_softc *sc, uint8_t *buf, int len)
12185fdd4e61Sdamien {
12195fdd4e61Sdamien 	struct r92s_fw_cmd_hdr *cmd;
12205fdd4e61Sdamien 	int cmdsz;
12215fdd4e61Sdamien 
12225fdd4e61Sdamien 	DPRINTFN(6, ("Rx events len=%d\n", len));
12235fdd4e61Sdamien 
12245fdd4e61Sdamien 	/* Skip Rx status. */
12255fdd4e61Sdamien 	buf += sizeof(struct r92s_rx_stat);
12265fdd4e61Sdamien 	len -= sizeof(struct r92s_rx_stat);
12275fdd4e61Sdamien 
12285fdd4e61Sdamien 	/* Process all events. */
12295fdd4e61Sdamien 	for (;;) {
12305fdd4e61Sdamien 		/* Check that command header fits. */
12315fdd4e61Sdamien 		if (__predict_false(len < sizeof(*cmd)))
12325fdd4e61Sdamien 			break;
12335fdd4e61Sdamien 		cmd = (struct r92s_fw_cmd_hdr *)buf;
12345fdd4e61Sdamien 		/* Check that command payload fits. */
12355fdd4e61Sdamien 		cmdsz = letoh16(cmd->len);
12365fdd4e61Sdamien 		if (__predict_false(len < sizeof(*cmd) + cmdsz))
12375fdd4e61Sdamien 			break;
1238d5f8d3bdSstsp 		if (cmdsz > len)
1239d5f8d3bdSstsp 			break;
12405fdd4e61Sdamien 
12415fdd4e61Sdamien 		/* Process firmware event. */
12425fdd4e61Sdamien 		rsu_rx_event(sc, cmd->code, (uint8_t *)&cmd[1], cmdsz);
12435fdd4e61Sdamien 
12445fdd4e61Sdamien 		if (!(cmd->seq & R92S_FW_CMD_MORE))
12455fdd4e61Sdamien 			break;
12465fdd4e61Sdamien 		buf += sizeof(*cmd) + cmdsz;
12475fdd4e61Sdamien 		len -= sizeof(*cmd) + cmdsz;
12485fdd4e61Sdamien 	}
12495fdd4e61Sdamien }
12505fdd4e61Sdamien 
12515fdd4e61Sdamien int8_t
rsu_get_rssi(struct rsu_softc * sc,int rate,void * physt)12525fdd4e61Sdamien rsu_get_rssi(struct rsu_softc *sc, int rate, void *physt)
12535fdd4e61Sdamien {
12545fdd4e61Sdamien 	static const int8_t cckoff[] = { 14, -2, -20, -40 };
12555fdd4e61Sdamien 	struct r92s_rx_phystat *phy;
12565fdd4e61Sdamien 	struct r92s_rx_cck *cck;
12575fdd4e61Sdamien 	uint8_t rpt;
12585fdd4e61Sdamien 	int8_t rssi;
12595fdd4e61Sdamien 
12605fdd4e61Sdamien 	if (rate <= 3) {
12615fdd4e61Sdamien 		cck = (struct r92s_rx_cck *)physt;
12625fdd4e61Sdamien 		rpt = (cck->agc_rpt >> 6) & 0x3;
12635fdd4e61Sdamien 		rssi = cck->agc_rpt & 0x3e;
12645fdd4e61Sdamien 		rssi = cckoff[rpt] - rssi;
12655fdd4e61Sdamien 	} else {	/* OFDM/HT. */
12665fdd4e61Sdamien 		phy = (struct r92s_rx_phystat *)physt;
12675fdd4e61Sdamien 		rssi = ((letoh32(phy->phydw1) >> 1) & 0x7f) - 106;
12685fdd4e61Sdamien 	}
12695fdd4e61Sdamien 	return (rssi);
12705fdd4e61Sdamien }
12715fdd4e61Sdamien 
12725fdd4e61Sdamien void
rsu_rx_frame(struct rsu_softc * sc,uint8_t * buf,int pktlen,struct mbuf_list * ml)12738fbaf8a2Sstsp rsu_rx_frame(struct rsu_softc *sc, uint8_t *buf, int pktlen,
12748fbaf8a2Sstsp     struct mbuf_list *ml)
12755fdd4e61Sdamien {
12765fdd4e61Sdamien 	struct ieee80211com *ic = &sc->sc_ic;
12775fdd4e61Sdamien 	struct ifnet *ifp = &ic->ic_if;
12785fdd4e61Sdamien 	struct ieee80211_rxinfo rxi;
12795fdd4e61Sdamien 	struct ieee80211_frame *wh;
12805fdd4e61Sdamien 	struct ieee80211_node *ni;
12815fdd4e61Sdamien 	struct r92s_rx_stat *stat;
12825fdd4e61Sdamien 	uint32_t rxdw0, rxdw3;
12835fdd4e61Sdamien 	struct mbuf *m;
12845fdd4e61Sdamien 	uint8_t rate;
12855fdd4e61Sdamien 	int8_t rssi = 0;
12865fdd4e61Sdamien 	int s, infosz;
12875fdd4e61Sdamien 
12885fdd4e61Sdamien 	stat = (struct r92s_rx_stat *)buf;
12895fdd4e61Sdamien 	rxdw0 = letoh32(stat->rxdw0);
12905fdd4e61Sdamien 	rxdw3 = letoh32(stat->rxdw3);
12915fdd4e61Sdamien 
12925fdd4e61Sdamien 	if (__predict_false(rxdw0 & R92S_RXDW0_CRCERR)) {
12935fdd4e61Sdamien 		ifp->if_ierrors++;
12945fdd4e61Sdamien 		return;
12955fdd4e61Sdamien 	}
12965fdd4e61Sdamien 	if (__predict_false(pktlen < sizeof(*wh) || pktlen > MCLBYTES)) {
12975fdd4e61Sdamien 		ifp->if_ierrors++;
12985fdd4e61Sdamien 		return;
12995fdd4e61Sdamien 	}
13005fdd4e61Sdamien 
13015fdd4e61Sdamien 	rate = MS(rxdw3, R92S_RXDW3_RATE);
13025fdd4e61Sdamien 	infosz = MS(rxdw0, R92S_RXDW0_INFOSZ) * 8;
13035fdd4e61Sdamien 
13045fdd4e61Sdamien 	/* Get RSSI from PHY status descriptor if present. */
13055fdd4e61Sdamien 	if (infosz != 0)
13065fdd4e61Sdamien 		rssi = rsu_get_rssi(sc, rate, &stat[1]);
13075fdd4e61Sdamien 
13085fdd4e61Sdamien 	DPRINTFN(5, ("Rx frame len=%d rate=%d infosz=%d rssi=%d\n",
13095fdd4e61Sdamien 	    pktlen, rate, infosz, rssi));
13105fdd4e61Sdamien 
13115fdd4e61Sdamien 	MGETHDR(m, M_DONTWAIT, MT_DATA);
13125fdd4e61Sdamien 	if (__predict_false(m == NULL)) {
13135fdd4e61Sdamien 		ifp->if_ierrors++;
13145fdd4e61Sdamien 		return;
13155fdd4e61Sdamien 	}
13165fdd4e61Sdamien 	if (pktlen > MHLEN) {
13175fdd4e61Sdamien 		MCLGET(m, M_DONTWAIT);
13185fdd4e61Sdamien 		if (__predict_false(!(m->m_flags & M_EXT))) {
13195fdd4e61Sdamien 			ifp->if_ierrors++;
13205fdd4e61Sdamien 			m_freem(m);
13215fdd4e61Sdamien 			return;
13225fdd4e61Sdamien 		}
13235fdd4e61Sdamien 	}
13245fdd4e61Sdamien 	/* Finalize mbuf. */
13255fdd4e61Sdamien 	/* Hardware does Rx TCP checksum offload. */
13265fdd4e61Sdamien 	if (rxdw3 & R92S_RXDW3_TCPCHKVALID) {
13275fdd4e61Sdamien 		if (__predict_true(rxdw3 & R92S_RXDW3_TCPCHKRPT))
13285fdd4e61Sdamien 			m->m_pkthdr.csum_flags |= M_TCP_CSUM_IN_OK;
13295fdd4e61Sdamien 		else
13305fdd4e61Sdamien 			m->m_pkthdr.csum_flags |= M_TCP_CSUM_IN_BAD;
13315fdd4e61Sdamien 	}
13325fdd4e61Sdamien 	wh = (struct ieee80211_frame *)((uint8_t *)&stat[1] + infosz);
13335fdd4e61Sdamien 	memcpy(mtod(m, uint8_t *), wh, pktlen);
13345fdd4e61Sdamien 	m->m_pkthdr.len = m->m_len = pktlen;
13355fdd4e61Sdamien 
13365fdd4e61Sdamien 	s = splnet();
13375fdd4e61Sdamien #if NBPFILTER > 0
13385fdd4e61Sdamien 	if (__predict_false(sc->sc_drvbpf != NULL)) {
13395fdd4e61Sdamien 		struct rsu_rx_radiotap_header *tap = &sc->sc_rxtap;
13405fdd4e61Sdamien 		struct mbuf mb;
13415fdd4e61Sdamien 
13425fdd4e61Sdamien 		tap->wr_flags = 0;
13435fdd4e61Sdamien 		/* Map HW rate index to 802.11 rate. */
13445fdd4e61Sdamien 		tap->wr_flags = 2;
13455fdd4e61Sdamien 		if (!(rxdw3 & R92S_RXDW3_HTC)) {
13465fdd4e61Sdamien 			switch (rate) {
13475fdd4e61Sdamien 			/* CCK. */
13485fdd4e61Sdamien 			case  0: tap->wr_rate =   2; break;
13495fdd4e61Sdamien 			case  1: tap->wr_rate =   4; break;
13505fdd4e61Sdamien 			case  2: tap->wr_rate =  11; break;
13515fdd4e61Sdamien 			case  3: tap->wr_rate =  22; break;
13525fdd4e61Sdamien 			/* OFDM. */
13535fdd4e61Sdamien 			case  4: tap->wr_rate =  12; break;
13545fdd4e61Sdamien 			case  5: tap->wr_rate =  18; break;
13555fdd4e61Sdamien 			case  6: tap->wr_rate =  24; break;
13565fdd4e61Sdamien 			case  7: tap->wr_rate =  36; break;
13575fdd4e61Sdamien 			case  8: tap->wr_rate =  48; break;
13585fdd4e61Sdamien 			case  9: tap->wr_rate =  72; break;
13595fdd4e61Sdamien 			case 10: tap->wr_rate =  96; break;
13605fdd4e61Sdamien 			case 11: tap->wr_rate = 108; break;
13615fdd4e61Sdamien 			}
13625fdd4e61Sdamien 		} else if (rate >= 12) {	/* MCS0~15. */
13635fdd4e61Sdamien 			/* Bit 7 set means HT MCS instead of rate. */
13645fdd4e61Sdamien 			tap->wr_rate = 0x80 | (rate - 12);
13655fdd4e61Sdamien 		}
13665fdd4e61Sdamien 		tap->wr_dbm_antsignal = rssi;
1367ba76f343Sstsp 		tap->wr_chan_freq = htole16(ic->ic_bss->ni_chan->ic_freq);
1368ba76f343Sstsp 		tap->wr_chan_flags = htole16(ic->ic_bss->ni_chan->ic_flags);
13695fdd4e61Sdamien 
13705fdd4e61Sdamien 		mb.m_data = (caddr_t)tap;
13715fdd4e61Sdamien 		mb.m_len = sc->sc_rxtap_len;
13725fdd4e61Sdamien 		mb.m_next = m;
13735fdd4e61Sdamien 		mb.m_nextpkt = NULL;
13745fdd4e61Sdamien 		mb.m_type = 0;
13755fdd4e61Sdamien 		mb.m_flags = 0;
13765fdd4e61Sdamien 		bpf_mtap(sc->sc_drvbpf, &mb, BPF_DIRECTION_IN);
13775fdd4e61Sdamien 	}
13785fdd4e61Sdamien #endif
13795fdd4e61Sdamien 
13805fdd4e61Sdamien 	ni = ieee80211_find_rxnode(ic, wh);
138152a13037Sstsp 	memset(&rxi, 0, sizeof(rxi));
13825fdd4e61Sdamien 	rxi.rxi_rssi = rssi;
13838fbaf8a2Sstsp 	ieee80211_inputm(ifp, m, ni, &rxi, ml);
13845fdd4e61Sdamien 	/* Node is no longer needed. */
13855fdd4e61Sdamien 	ieee80211_release_node(ic, ni);
13865fdd4e61Sdamien 	splx(s);
13875fdd4e61Sdamien }
13885fdd4e61Sdamien 
13895fdd4e61Sdamien void
rsu_rx_multi_frame(struct rsu_softc * sc,uint8_t * buf,int len)13905fdd4e61Sdamien rsu_rx_multi_frame(struct rsu_softc *sc, uint8_t *buf, int len)
13915fdd4e61Sdamien {
13928fbaf8a2Sstsp 	struct mbuf_list ml = MBUF_LIST_INITIALIZER();
13935fdd4e61Sdamien 	struct r92s_rx_stat *stat;
13945fdd4e61Sdamien 	uint32_t rxdw0;
13955fdd4e61Sdamien 	int totlen, pktlen, infosz, npkts;
13965fdd4e61Sdamien 
13975fdd4e61Sdamien 	/* Get the number of encapsulated frames. */
13985fdd4e61Sdamien 	stat = (struct r92s_rx_stat *)buf;
13995fdd4e61Sdamien 	npkts = MS(letoh32(stat->rxdw2), R92S_RXDW2_PKTCNT);
14005fdd4e61Sdamien 	DPRINTFN(6, ("Rx %d frames in one chunk\n", npkts));
14015fdd4e61Sdamien 
14025fdd4e61Sdamien 	/* Process all of them. */
14035fdd4e61Sdamien 	while (npkts-- > 0) {
14045fdd4e61Sdamien 		if (__predict_false(len < sizeof(*stat)))
14055fdd4e61Sdamien 			break;
14065fdd4e61Sdamien 		stat = (struct r92s_rx_stat *)buf;
14075fdd4e61Sdamien 		rxdw0 = letoh32(stat->rxdw0);
14085fdd4e61Sdamien 
14095fdd4e61Sdamien 		pktlen = MS(rxdw0, R92S_RXDW0_PKTLEN);
14105fdd4e61Sdamien 		if (__predict_false(pktlen == 0))
14115fdd4e61Sdamien 			break;
14125fdd4e61Sdamien 
14135fdd4e61Sdamien 		infosz = MS(rxdw0, R92S_RXDW0_INFOSZ) * 8;
14145fdd4e61Sdamien 
14155fdd4e61Sdamien 		/* Make sure everything fits in xfer. */
14165fdd4e61Sdamien 		totlen = sizeof(*stat) + infosz + pktlen;
14175fdd4e61Sdamien 		if (__predict_false(totlen > len))
14185fdd4e61Sdamien 			break;
14195fdd4e61Sdamien 
14205fdd4e61Sdamien 		/* Process 802.11 frame. */
14218fbaf8a2Sstsp 		rsu_rx_frame(sc, buf, pktlen, &ml);
14225fdd4e61Sdamien 
14235fdd4e61Sdamien 		/* Next chunk is 128-byte aligned. */
14245fdd4e61Sdamien 		totlen = (totlen + 127) & ~127;
14255fdd4e61Sdamien 		buf += totlen;
14265fdd4e61Sdamien 		len -= totlen;
14275fdd4e61Sdamien 	}
14288fbaf8a2Sstsp 	if_input(&sc->sc_ic.ic_if, &ml);
14295fdd4e61Sdamien }
14305fdd4e61Sdamien 
14315fdd4e61Sdamien void
rsu_rxeof(struct usbd_xfer * xfer,void * priv,usbd_status status)1432ab0b1be7Smglocker rsu_rxeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
14335fdd4e61Sdamien {
14345fdd4e61Sdamien 	struct rsu_rx_data *data = priv;
14355fdd4e61Sdamien 	struct rsu_softc *sc = data->sc;
14365fdd4e61Sdamien 	struct r92s_rx_stat *stat;
1437d5f8d3bdSstsp 	struct ifnet *ifp = &sc->sc_ic.ic_if;
14385fdd4e61Sdamien 	int len;
14395fdd4e61Sdamien 
14405fdd4e61Sdamien 	if (__predict_false(status != USBD_NORMAL_COMPLETION)) {
14415fdd4e61Sdamien 		DPRINTF(("RX status=%d\n", status));
14425fdd4e61Sdamien 		if (status == USBD_STALLED)
14435fdd4e61Sdamien 			usbd_clear_endpoint_stall_async(data->pipe);
14445fdd4e61Sdamien 		if (status != USBD_CANCELLED)
14455fdd4e61Sdamien 			goto resubmit;
14465fdd4e61Sdamien 		return;
14475fdd4e61Sdamien 	}
14485fdd4e61Sdamien 	usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL);
14495fdd4e61Sdamien 
14505fdd4e61Sdamien 	if (__predict_false(len < sizeof(*stat))) {
14515fdd4e61Sdamien 		DPRINTF(("xfer too short %d\n", len));
1452d5f8d3bdSstsp 		ifp->if_ierrors++;
14535fdd4e61Sdamien 		goto resubmit;
14545fdd4e61Sdamien 	}
1455d5f8d3bdSstsp 	if (len > RSU_RXBUFSZ) {
1456d5f8d3bdSstsp 		DPRINTF(("xfer too large %d\n", len));
1457d5f8d3bdSstsp 		ifp->if_ierrors++;
1458d5f8d3bdSstsp 		goto resubmit;
1459d5f8d3bdSstsp 	}
1460d5f8d3bdSstsp 
14615fdd4e61Sdamien 	/* Determine if it is a firmware C2H event or an 802.11 frame. */
14625fdd4e61Sdamien 	stat = (struct r92s_rx_stat *)data->buf;
14635fdd4e61Sdamien 	if ((letoh32(stat->rxdw1) & 0x1ff) == 0x1ff)
14645fdd4e61Sdamien 		rsu_rx_multi_event(sc, data->buf, len);
14655fdd4e61Sdamien 	else
14665fdd4e61Sdamien 		rsu_rx_multi_frame(sc, data->buf, len);
14675fdd4e61Sdamien 
14685fdd4e61Sdamien  resubmit:
14695fdd4e61Sdamien 	/* Setup a new transfer. */
14705fdd4e61Sdamien 	usbd_setup_xfer(xfer, data->pipe, data, data->buf, RSU_RXBUFSZ,
14715fdd4e61Sdamien 	    USBD_SHORT_XFER_OK | USBD_NO_COPY, USBD_NO_TIMEOUT, rsu_rxeof);
14725fdd4e61Sdamien 	(void)usbd_transfer(xfer);
14735fdd4e61Sdamien }
14745fdd4e61Sdamien 
14755fdd4e61Sdamien void
rsu_txeof(struct usbd_xfer * xfer,void * priv,usbd_status status)1476ab0b1be7Smglocker rsu_txeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
14775fdd4e61Sdamien {
14785fdd4e61Sdamien 	struct rsu_tx_data *data = priv;
14795fdd4e61Sdamien 	struct rsu_softc *sc = data->sc;
14805fdd4e61Sdamien 	struct ifnet *ifp = &sc->sc_ic.ic_if;
14815fdd4e61Sdamien 	int s;
14825fdd4e61Sdamien 
14835fdd4e61Sdamien 	s = splnet();
14845fdd4e61Sdamien 	/* Put this Tx buffer back to our free list. */
14855fdd4e61Sdamien 	TAILQ_INSERT_TAIL(&sc->tx_free_list, data, next);
14865fdd4e61Sdamien 
14875fdd4e61Sdamien 	if (__predict_false(status != USBD_NORMAL_COMPLETION)) {
14885fdd4e61Sdamien 		DPRINTF(("TX status=%d\n", status));
14895fdd4e61Sdamien 		if (status == USBD_STALLED)
14905fdd4e61Sdamien 			usbd_clear_endpoint_stall_async(data->pipe);
14915fdd4e61Sdamien 		ifp->if_oerrors++;
14925fdd4e61Sdamien 		splx(s);
14935fdd4e61Sdamien 		return;
14945fdd4e61Sdamien 	}
14955fdd4e61Sdamien 	sc->sc_tx_timer = 0;
14965fdd4e61Sdamien 
14975fdd4e61Sdamien 	/* We just released a Tx buffer, notify Tx. */
1498de6cd8fbSdlg 	if (ifq_is_oactive(&ifp->if_snd)) {
1499de6cd8fbSdlg 		ifq_clr_oactive(&ifp->if_snd);
15005fdd4e61Sdamien 		rsu_start(ifp);
15015fdd4e61Sdamien 	}
15025fdd4e61Sdamien 	splx(s);
15035fdd4e61Sdamien }
15045fdd4e61Sdamien 
15055fdd4e61Sdamien int
rsu_tx(struct rsu_softc * sc,struct mbuf * m,struct ieee80211_node * ni)15065fdd4e61Sdamien rsu_tx(struct rsu_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
15075fdd4e61Sdamien {
15085fdd4e61Sdamien 	struct ieee80211com *ic = &sc->sc_ic;
15095fdd4e61Sdamien 	struct ieee80211_frame *wh;
15105fdd4e61Sdamien 	struct ieee80211_key *k = NULL;
15115fdd4e61Sdamien 	struct rsu_tx_data *data;
15125fdd4e61Sdamien 	struct r92s_tx_desc *txd;
1513ab0b1be7Smglocker 	struct usbd_pipe *pipe;
15145fdd4e61Sdamien 	uint16_t qos;
15155fdd4e61Sdamien 	uint8_t type, qid, tid = 0;
15165fdd4e61Sdamien 	int hasqos, xferlen, error;
15175fdd4e61Sdamien 
15185fdd4e61Sdamien 	wh = mtod(m, struct ieee80211_frame *);
15195fdd4e61Sdamien 	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
15205fdd4e61Sdamien 
15215fdd4e61Sdamien 	if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
15225fdd4e61Sdamien 		k = ieee80211_get_txkey(ic, wh, ni);
15235fdd4e61Sdamien 		if ((m = ieee80211_encrypt(ic, m, k)) == NULL)
15245fdd4e61Sdamien 			return (ENOBUFS);
15255fdd4e61Sdamien 		wh = mtod(m, struct ieee80211_frame *);
15265fdd4e61Sdamien 	}
15275fdd4e61Sdamien 	if ((hasqos = ieee80211_has_qos(wh))) {
15285fdd4e61Sdamien 		qos = ieee80211_get_qos(wh);
15295fdd4e61Sdamien 		tid = qos & IEEE80211_QOS_TID;
15305fdd4e61Sdamien 		qid = rsu_ac2qid[ieee80211_up_to_ac(ic, tid)];
15315fdd4e61Sdamien 	} else
15325fdd4e61Sdamien 		qid = RSU_QID_BE;
15335fdd4e61Sdamien 
15345fdd4e61Sdamien 	/* Get the USB pipe to use for this queue id. */
15355fdd4e61Sdamien 	pipe = sc->pipe[sc->qid2idx[qid]];
15365fdd4e61Sdamien 
15375fdd4e61Sdamien 	/* Grab a Tx buffer from our free list. */
15385fdd4e61Sdamien 	data = TAILQ_FIRST(&sc->tx_free_list);
15395fdd4e61Sdamien 	TAILQ_REMOVE(&sc->tx_free_list, data, next);
15405fdd4e61Sdamien 
15415fdd4e61Sdamien 	/* Fill Tx descriptor. */
15425fdd4e61Sdamien 	txd = (struct r92s_tx_desc *)data->buf;
15435fdd4e61Sdamien 	memset(txd, 0, sizeof(*txd));
15445fdd4e61Sdamien 
15455fdd4e61Sdamien 	txd->txdw0 |= htole32(
15465fdd4e61Sdamien 	    SM(R92S_TXDW0_PKTLEN, m->m_pkthdr.len) |
15475fdd4e61Sdamien 	    SM(R92S_TXDW0_OFFSET, sizeof(*txd)) |
15485fdd4e61Sdamien 	    R92S_TXDW0_OWN | R92S_TXDW0_FSG | R92S_TXDW0_LSG);
15495fdd4e61Sdamien 
15505fdd4e61Sdamien 	txd->txdw1 |= htole32(
15515fdd4e61Sdamien 	    SM(R92S_TXDW1_MACID, R92S_MACID_BSS) |
15525fdd4e61Sdamien 	    SM(R92S_TXDW1_QSEL, R92S_TXDW1_QSEL_BE));
15535fdd4e61Sdamien 	if (!hasqos)
15545fdd4e61Sdamien 		txd->txdw1 |= htole32(R92S_TXDW1_NONQOS);
15555fdd4e61Sdamien #ifdef notyet
15565fdd4e61Sdamien 	if (k != NULL) {
15575fdd4e61Sdamien 		switch (k->k_cipher) {
15585fdd4e61Sdamien 		case IEEE80211_CIPHER_WEP40:
15595fdd4e61Sdamien 		case IEEE80211_CIPHER_WEP104:
15605fdd4e61Sdamien 			cipher = R92S_TXDW1_CIPHER_WEP;
15615fdd4e61Sdamien 			break;
15625fdd4e61Sdamien 		case IEEE80211_CIPHER_TKIP:
15635fdd4e61Sdamien 			cipher = R92S_TXDW1_CIPHER_TKIP;
15645fdd4e61Sdamien 			break;
15655fdd4e61Sdamien 		case IEEE80211_CIPHER_CCMP:
15665fdd4e61Sdamien 			cipher = R92S_TXDW1_CIPHER_AES;
15675fdd4e61Sdamien 			break;
15685fdd4e61Sdamien 		default:
15695fdd4e61Sdamien 			cipher = R92S_TXDW1_CIPHER_NONE;
15705fdd4e61Sdamien 		}
15715fdd4e61Sdamien 		txd->txdw1 |= htole32(
15725fdd4e61Sdamien 		    SM(R92S_TXDW1_CIPHER, cipher) |
15735fdd4e61Sdamien 		    SM(R92S_TXDW1_KEYIDX, k->k_id));
15745fdd4e61Sdamien 	}
15755fdd4e61Sdamien #endif
15765fdd4e61Sdamien 	txd->txdw2 |= htole32(R92S_TXDW2_BK);
15775fdd4e61Sdamien 	if (IEEE80211_IS_MULTICAST(wh->i_addr1))
15785fdd4e61Sdamien 		txd->txdw2 |= htole32(R92S_TXDW2_BMCAST);
15795fdd4e61Sdamien 	/*
15805fdd4e61Sdamien 	 * Firmware will use and increment the sequence number for the
15815fdd4e61Sdamien 	 * specified TID.
15825fdd4e61Sdamien 	 */
15835fdd4e61Sdamien 	txd->txdw3 |= htole32(SM(R92S_TXDW3_SEQ, tid));
15845fdd4e61Sdamien 
15855fdd4e61Sdamien #if NBPFILTER > 0
15865fdd4e61Sdamien 	if (__predict_false(sc->sc_drvbpf != NULL)) {
15875fdd4e61Sdamien 		struct rsu_tx_radiotap_header *tap = &sc->sc_txtap;
15885fdd4e61Sdamien 		struct mbuf mb;
15895fdd4e61Sdamien 
15905fdd4e61Sdamien 		tap->wt_flags = 0;
15915fdd4e61Sdamien 		tap->wt_chan_freq = htole16(ic->ic_bss->ni_chan->ic_freq);
15925fdd4e61Sdamien 		tap->wt_chan_flags = htole16(ic->ic_bss->ni_chan->ic_flags);
15935fdd4e61Sdamien 
15945fdd4e61Sdamien 		mb.m_data = (caddr_t)tap;
15955fdd4e61Sdamien 		mb.m_len = sc->sc_txtap_len;
15965fdd4e61Sdamien 		mb.m_next = m;
15975fdd4e61Sdamien 		mb.m_nextpkt = NULL;
15985fdd4e61Sdamien 		mb.m_type = 0;
15995fdd4e61Sdamien 		mb.m_flags = 0;
16005fdd4e61Sdamien 		bpf_mtap(sc->sc_drvbpf, &mb, BPF_DIRECTION_OUT);
16015fdd4e61Sdamien 	}
16025fdd4e61Sdamien #endif
16035fdd4e61Sdamien 
16045fdd4e61Sdamien 	xferlen = sizeof(*txd) + m->m_pkthdr.len;
16055c7fed39Sdlg 	m_copydata(m, 0, m->m_pkthdr.len, &txd[1]);
16065fdd4e61Sdamien 	m_freem(m);
16075fdd4e61Sdamien 
16085fdd4e61Sdamien 	data->pipe = pipe;
16095fdd4e61Sdamien 	usbd_setup_xfer(data->xfer, pipe, data, data->buf, xferlen,
16105fdd4e61Sdamien 	    USBD_FORCE_SHORT_XFER | USBD_NO_COPY, RSU_TX_TIMEOUT,
16115fdd4e61Sdamien 	    rsu_txeof);
16125fdd4e61Sdamien 	error = usbd_transfer(data->xfer);
16135fdd4e61Sdamien 	if (__predict_false(error != USBD_IN_PROGRESS && error != 0)) {
16145fdd4e61Sdamien 		/* Put this Tx buffer back to our free list. */
16155fdd4e61Sdamien 		TAILQ_INSERT_TAIL(&sc->tx_free_list, data, next);
16165fdd4e61Sdamien 		return (error);
16175fdd4e61Sdamien 	}
16185fdd4e61Sdamien 	ieee80211_release_node(ic, ni);
16195fdd4e61Sdamien 	return (0);
16205fdd4e61Sdamien }
16215fdd4e61Sdamien 
16225fdd4e61Sdamien int
rsu_send_mgmt(struct ieee80211com * ic,struct ieee80211_node * ni,int type,int arg1,int arg2)16235fdd4e61Sdamien rsu_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, int type,
16245fdd4e61Sdamien     int arg1, int arg2)
16255fdd4e61Sdamien {
16265fdd4e61Sdamien 	return (EOPNOTSUPP);
16275fdd4e61Sdamien }
16285fdd4e61Sdamien 
16295fdd4e61Sdamien void
rsu_start(struct ifnet * ifp)16305fdd4e61Sdamien rsu_start(struct ifnet *ifp)
16315fdd4e61Sdamien {
16325fdd4e61Sdamien 	struct rsu_softc *sc = ifp->if_softc;
16335fdd4e61Sdamien 	struct ieee80211com *ic = &sc->sc_ic;
16345fdd4e61Sdamien 	struct ieee80211_node *ni;
16355fdd4e61Sdamien 	struct mbuf *m;
16365fdd4e61Sdamien 
1637de6cd8fbSdlg 	if (!(ifp->if_flags & IFF_RUNNING) || ifq_is_oactive(&ifp->if_snd))
16385fdd4e61Sdamien 		return;
16395fdd4e61Sdamien 
16405fdd4e61Sdamien 	for (;;) {
16415fdd4e61Sdamien 		if (TAILQ_EMPTY(&sc->tx_free_list)) {
1642de6cd8fbSdlg 			ifq_set_oactive(&ifp->if_snd);
16435fdd4e61Sdamien 			break;
16445fdd4e61Sdamien 		}
16455fdd4e61Sdamien 		if (ic->ic_state != IEEE80211_S_RUN)
16465fdd4e61Sdamien 			break;
16475fdd4e61Sdamien 
16485fdd4e61Sdamien 		/* Encapsulate and send data frames. */
164963bcfa73Spatrick 		m = ifq_dequeue(&ifp->if_snd);
16505fdd4e61Sdamien 		if (m == NULL)
16515fdd4e61Sdamien 			break;
16525fdd4e61Sdamien #if NBPFILTER > 0
16535fdd4e61Sdamien 		if (ifp->if_bpf != NULL)
16545fdd4e61Sdamien 			bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
16555fdd4e61Sdamien #endif
16565fdd4e61Sdamien 		if ((m = ieee80211_encap(ifp, m, &ni)) == NULL)
16575fdd4e61Sdamien 			continue;
16585fdd4e61Sdamien 
16595fdd4e61Sdamien #if NBPFILTER > 0
16605fdd4e61Sdamien 		if (ic->ic_rawbpf != NULL)
16615fdd4e61Sdamien 			bpf_mtap(ic->ic_rawbpf, m, BPF_DIRECTION_OUT);
16625fdd4e61Sdamien #endif
16635fdd4e61Sdamien 		if (rsu_tx(sc, m, ni) != 0) {
16645fdd4e61Sdamien 			ieee80211_release_node(ic, ni);
16655fdd4e61Sdamien 			ifp->if_oerrors++;
16665fdd4e61Sdamien 			continue;
16675fdd4e61Sdamien 		}
16685fdd4e61Sdamien 
16695fdd4e61Sdamien 		sc->sc_tx_timer = 5;
16705fdd4e61Sdamien 		ifp->if_timer = 1;
16715fdd4e61Sdamien 	}
16725fdd4e61Sdamien }
16735fdd4e61Sdamien 
16745fdd4e61Sdamien void
rsu_watchdog(struct ifnet * ifp)16755fdd4e61Sdamien rsu_watchdog(struct ifnet *ifp)
16765fdd4e61Sdamien {
16775fdd4e61Sdamien 	struct rsu_softc *sc = ifp->if_softc;
16785fdd4e61Sdamien 
16795fdd4e61Sdamien 	ifp->if_timer = 0;
16805fdd4e61Sdamien 
16815fdd4e61Sdamien 	if (sc->sc_tx_timer > 0) {
16825fdd4e61Sdamien 		if (--sc->sc_tx_timer == 0) {
16835fdd4e61Sdamien 			printf("%s: device timeout\n", sc->sc_dev.dv_xname);
16845fdd4e61Sdamien 			/* rsu_init(ifp); XXX needs a process context! */
16855fdd4e61Sdamien 			ifp->if_oerrors++;
16865fdd4e61Sdamien 			return;
16875fdd4e61Sdamien 		}
16885fdd4e61Sdamien 		ifp->if_timer = 1;
16895fdd4e61Sdamien 	}
16905fdd4e61Sdamien 	ieee80211_watchdog(ifp);
16915fdd4e61Sdamien }
16925fdd4e61Sdamien 
16935fdd4e61Sdamien int
rsu_ioctl(struct ifnet * ifp,u_long cmd,caddr_t data)16945fdd4e61Sdamien rsu_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
16955fdd4e61Sdamien {
16965fdd4e61Sdamien 	struct rsu_softc *sc = ifp->if_softc;
16975fdd4e61Sdamien 	int s, error = 0;
16985fdd4e61Sdamien 
16995ad57547Sjakemsr 	if (usbd_is_dying(sc->sc_udev))
17005ad57547Sjakemsr 		return ENXIO;
17015ad57547Sjakemsr 
17025ad57547Sjakemsr 	usbd_ref_incr(sc->sc_udev);
17035ad57547Sjakemsr 
17045fdd4e61Sdamien 	s = splnet();
17055fdd4e61Sdamien 
17065fdd4e61Sdamien 	switch (cmd) {
17075fdd4e61Sdamien 	case SIOCSIFADDR:
17085fdd4e61Sdamien 		ifp->if_flags |= IFF_UP;
17095fdd4e61Sdamien 		/* FALLTHROUGH */
17105fdd4e61Sdamien 	case SIOCSIFFLAGS:
17115fdd4e61Sdamien 		if (ifp->if_flags & IFF_UP) {
17125fdd4e61Sdamien 			if (!(ifp->if_flags & IFF_RUNNING))
17135fdd4e61Sdamien 				rsu_init(ifp);
17145fdd4e61Sdamien 		} else {
17155fdd4e61Sdamien 			if (ifp->if_flags & IFF_RUNNING)
17165fdd4e61Sdamien 				rsu_stop(ifp);
17175fdd4e61Sdamien 		}
17185fdd4e61Sdamien 		break;
17195fdd4e61Sdamien 	default:
17205fdd4e61Sdamien 		error = ieee80211_ioctl(ifp, cmd, data);
17215fdd4e61Sdamien 	}
17225fdd4e61Sdamien 
17235fdd4e61Sdamien 	if (error == ENETRESET) {
17245fdd4e61Sdamien 		if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) ==
17255fdd4e61Sdamien 		    (IFF_UP | IFF_RUNNING)) {
17265fdd4e61Sdamien 			rsu_stop(ifp);
17275fdd4e61Sdamien 			rsu_init(ifp);
17285fdd4e61Sdamien 		}
17295fdd4e61Sdamien 		error = 0;
17305fdd4e61Sdamien 	}
17315fdd4e61Sdamien 	splx(s);
17325ad57547Sjakemsr 
17335ad57547Sjakemsr 	usbd_ref_decr(sc->sc_udev);
17345ad57547Sjakemsr 
17355fdd4e61Sdamien 	return (error);
17365fdd4e61Sdamien }
17375fdd4e61Sdamien 
17385fdd4e61Sdamien /*
17395fdd4e61Sdamien  * Power on sequence for A-cut adapters.
17405fdd4e61Sdamien  */
17415fdd4e61Sdamien void
rsu_power_on_acut(struct rsu_softc * sc)17425fdd4e61Sdamien rsu_power_on_acut(struct rsu_softc *sc)
17435fdd4e61Sdamien {
17445fdd4e61Sdamien 	uint32_t reg;
17455fdd4e61Sdamien 
17465fdd4e61Sdamien 	rsu_write_1(sc, R92S_SPS0_CTRL + 1, 0x53);
17475fdd4e61Sdamien 	rsu_write_1(sc, R92S_SPS0_CTRL + 0, 0x57);
17485fdd4e61Sdamien 
17495fdd4e61Sdamien 	/* Enable AFE macro block's bandgap and Mbias. */
17505fdd4e61Sdamien 	rsu_write_1(sc, R92S_AFE_MISC,
17515fdd4e61Sdamien 	    rsu_read_1(sc, R92S_AFE_MISC) |
17525fdd4e61Sdamien 	    R92S_AFE_MISC_BGEN | R92S_AFE_MISC_MBEN);
17535fdd4e61Sdamien 	/* Enable LDOA15 block. */
17545fdd4e61Sdamien 	rsu_write_1(sc, R92S_LDOA15_CTRL,
17555fdd4e61Sdamien 	    rsu_read_1(sc, R92S_LDOA15_CTRL) | R92S_LDA15_EN);
17565fdd4e61Sdamien 
17575fdd4e61Sdamien 	rsu_write_1(sc, R92S_SPS1_CTRL,
17585fdd4e61Sdamien 	    rsu_read_1(sc, R92S_SPS1_CTRL) | R92S_SPS1_LDEN);
17595fdd4e61Sdamien 	usbd_delay_ms(sc->sc_udev, 2);
17605fdd4e61Sdamien 	/* Enable switch regulator block. */
17615fdd4e61Sdamien 	rsu_write_1(sc, R92S_SPS1_CTRL,
17625fdd4e61Sdamien 	    rsu_read_1(sc, R92S_SPS1_CTRL) | R92S_SPS1_SWEN);
17635fdd4e61Sdamien 
17645fdd4e61Sdamien 	rsu_write_4(sc, R92S_SPS1_CTRL, 0x00a7b267);
17655fdd4e61Sdamien 
17665fdd4e61Sdamien 	rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1,
17675fdd4e61Sdamien 	    rsu_read_1(sc, R92S_SYS_ISO_CTRL + 1) | 0x08);
17685fdd4e61Sdamien 
17695fdd4e61Sdamien 	rsu_write_1(sc, R92S_SYS_FUNC_EN + 1,
17705fdd4e61Sdamien 	    rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x20);
17715fdd4e61Sdamien 
17725fdd4e61Sdamien 	rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1,
17735fdd4e61Sdamien 	    rsu_read_1(sc, R92S_SYS_ISO_CTRL + 1) & ~0x90);
17745fdd4e61Sdamien 
17755fdd4e61Sdamien 	/* Enable AFE clock. */
17765fdd4e61Sdamien 	rsu_write_1(sc, R92S_AFE_XTAL_CTRL + 1,
17775fdd4e61Sdamien 	    rsu_read_1(sc, R92S_AFE_XTAL_CTRL + 1) & ~0x04);
17785fdd4e61Sdamien 	/* Enable AFE PLL macro block. */
17795fdd4e61Sdamien 	rsu_write_1(sc, R92S_AFE_PLL_CTRL,
17805fdd4e61Sdamien 	    rsu_read_1(sc, R92S_AFE_PLL_CTRL) | 0x11);
17815fdd4e61Sdamien 	/* Attach AFE PLL to MACTOP/BB. */
17825fdd4e61Sdamien 	rsu_write_1(sc, R92S_SYS_ISO_CTRL,
17835fdd4e61Sdamien 	    rsu_read_1(sc, R92S_SYS_ISO_CTRL) & ~0x11);
17845fdd4e61Sdamien 
17855fdd4e61Sdamien 	/* Switch to 40MHz clock instead of 80MHz. */
17865fdd4e61Sdamien 	rsu_write_2(sc, R92S_SYS_CLKR,
17875fdd4e61Sdamien 	    rsu_read_2(sc, R92S_SYS_CLKR) & ~R92S_SYS_CLKSEL);
17885fdd4e61Sdamien 
17895fdd4e61Sdamien 	/* Enable MAC clock. */
17905fdd4e61Sdamien 	rsu_write_2(sc, R92S_SYS_CLKR,
17915fdd4e61Sdamien 	    rsu_read_2(sc, R92S_SYS_CLKR) |
17925fdd4e61Sdamien 	    R92S_MAC_CLK_EN | R92S_SYS_CLK_EN);
17935fdd4e61Sdamien 
17945fdd4e61Sdamien 	rsu_write_1(sc, R92S_PMC_FSM, 0x02);
17955fdd4e61Sdamien 
17965fdd4e61Sdamien 	/* Enable digital core and IOREG R/W. */
17975fdd4e61Sdamien 	rsu_write_1(sc, R92S_SYS_FUNC_EN + 1,
17985fdd4e61Sdamien 	    rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x08);
17995fdd4e61Sdamien 
18005fdd4e61Sdamien 	rsu_write_1(sc, R92S_SYS_FUNC_EN + 1,
18015fdd4e61Sdamien 	    rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x80);
18025fdd4e61Sdamien 
18035fdd4e61Sdamien 	/* Switch the control path to firmware. */
18045fdd4e61Sdamien 	reg = rsu_read_2(sc, R92S_SYS_CLKR);
18055fdd4e61Sdamien 	reg = (reg & ~R92S_SWHW_SEL) | R92S_FWHW_SEL;
18065fdd4e61Sdamien 	rsu_write_2(sc, R92S_SYS_CLKR, reg);
18075fdd4e61Sdamien 
18085fdd4e61Sdamien 	rsu_write_2(sc, R92S_CR, 0x37fc);
18095fdd4e61Sdamien 
18105fdd4e61Sdamien 	/* Fix USB RX FIFO issue. */
18115fdd4e61Sdamien 	rsu_write_1(sc, 0xfe5c,
18125fdd4e61Sdamien 	    rsu_read_1(sc, 0xfe5c) | 0x80);
18135fdd4e61Sdamien 	rsu_write_1(sc, 0x00ab,
18145fdd4e61Sdamien 	    rsu_read_1(sc, 0x00ab) | 0xc0);
18155fdd4e61Sdamien 
18165fdd4e61Sdamien 	rsu_write_1(sc, R92S_SYS_CLKR,
18175fdd4e61Sdamien 	    rsu_read_1(sc, R92S_SYS_CLKR) & ~R92S_SYS_CPU_CLKSEL);
18185fdd4e61Sdamien }
18195fdd4e61Sdamien 
18205fdd4e61Sdamien /*
18215fdd4e61Sdamien  * Power on sequence for B-cut and C-cut adapters.
18225fdd4e61Sdamien  */
18235fdd4e61Sdamien void
rsu_power_on_bcut(struct rsu_softc * sc)18245fdd4e61Sdamien rsu_power_on_bcut(struct rsu_softc *sc)
18255fdd4e61Sdamien {
18265fdd4e61Sdamien 	uint32_t reg;
18275fdd4e61Sdamien 	int ntries;
18285fdd4e61Sdamien 
18295fdd4e61Sdamien 	/* Prevent eFuse leakage. */
18305fdd4e61Sdamien 	rsu_write_1(sc, 0x37, 0xb0);
18315fdd4e61Sdamien 	usbd_delay_ms(sc->sc_udev, 10);
18325fdd4e61Sdamien 	rsu_write_1(sc, 0x37, 0x30);
18335fdd4e61Sdamien 
18345fdd4e61Sdamien 	/* Switch the control path to hardware. */
18355fdd4e61Sdamien 	reg = rsu_read_2(sc, R92S_SYS_CLKR);
18365fdd4e61Sdamien 	if (reg & R92S_FWHW_SEL) {
18375fdd4e61Sdamien 		rsu_write_2(sc, R92S_SYS_CLKR,
18385fdd4e61Sdamien 		    reg & ~(R92S_SWHW_SEL | R92S_FWHW_SEL));
18395fdd4e61Sdamien 	}
18405fdd4e61Sdamien 	rsu_write_1(sc, R92S_SYS_FUNC_EN + 1,
18415fdd4e61Sdamien 	    rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) & ~0x8c);
18425fdd4e61Sdamien 	DELAY(1000);
18435fdd4e61Sdamien 
18445fdd4e61Sdamien 	rsu_write_1(sc, R92S_SPS0_CTRL + 1, 0x53);
18455fdd4e61Sdamien 	rsu_write_1(sc, R92S_SPS0_CTRL + 0, 0x57);
18465fdd4e61Sdamien 
18475fdd4e61Sdamien 	reg = rsu_read_1(sc, R92S_AFE_MISC);
18485fdd4e61Sdamien 	rsu_write_1(sc, R92S_AFE_MISC, reg | R92S_AFE_MISC_BGEN);
18495fdd4e61Sdamien 	rsu_write_1(sc, R92S_AFE_MISC, reg | R92S_AFE_MISC_BGEN |
18505fdd4e61Sdamien 	    R92S_AFE_MISC_MBEN | R92S_AFE_MISC_I32_EN);
18515fdd4e61Sdamien 
18525fdd4e61Sdamien 	/* Enable PLL. */
18535fdd4e61Sdamien 	rsu_write_1(sc, R92S_LDOA15_CTRL,
18545fdd4e61Sdamien 	    rsu_read_1(sc, R92S_LDOA15_CTRL) | R92S_LDA15_EN);
18555fdd4e61Sdamien 
18565fdd4e61Sdamien 	rsu_write_1(sc, R92S_LDOV12D_CTRL,
18575fdd4e61Sdamien 	    rsu_read_1(sc, R92S_LDOV12D_CTRL) | R92S_LDV12_EN);
18585fdd4e61Sdamien 
18595fdd4e61Sdamien 	rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1,
18605fdd4e61Sdamien 	    rsu_read_1(sc, R92S_SYS_ISO_CTRL + 1) | 0x08);
18615fdd4e61Sdamien 
18625fdd4e61Sdamien 	rsu_write_1(sc, R92S_SYS_FUNC_EN + 1,
18635fdd4e61Sdamien 	    rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x20);
18645fdd4e61Sdamien 
18655fdd4e61Sdamien 	/* Support 64KB IMEM. */
18665fdd4e61Sdamien 	rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1,
18675fdd4e61Sdamien 	    rsu_read_1(sc, R92S_SYS_ISO_CTRL + 1) & ~0x97);
18685fdd4e61Sdamien 
18695fdd4e61Sdamien 	/* Enable AFE clock. */
18705fdd4e61Sdamien 	rsu_write_1(sc, R92S_AFE_XTAL_CTRL + 1,
18715fdd4e61Sdamien 	    rsu_read_1(sc, R92S_AFE_XTAL_CTRL + 1) & ~0x04);
18725fdd4e61Sdamien 	/* Enable AFE PLL macro block. */
18735fdd4e61Sdamien 	reg = rsu_read_1(sc, R92S_AFE_PLL_CTRL);
18745fdd4e61Sdamien 	rsu_write_1(sc, R92S_AFE_PLL_CTRL, reg | 0x11);
18755fdd4e61Sdamien 	DELAY(500);
18765fdd4e61Sdamien 	rsu_write_1(sc, R92S_AFE_PLL_CTRL, reg | 0x51);
18775fdd4e61Sdamien 	DELAY(500);
18785fdd4e61Sdamien 	rsu_write_1(sc, R92S_AFE_PLL_CTRL, reg | 0x11);
18795fdd4e61Sdamien 	DELAY(500);
18805fdd4e61Sdamien 
18815fdd4e61Sdamien 	/* Attach AFE PLL to MACTOP/BB. */
18825fdd4e61Sdamien 	rsu_write_1(sc, R92S_SYS_ISO_CTRL,
18835fdd4e61Sdamien 	    rsu_read_1(sc, R92S_SYS_ISO_CTRL) & ~0x11);
18845fdd4e61Sdamien 
18855fdd4e61Sdamien 	/* Switch to 40MHz clock. */
18865fdd4e61Sdamien 	rsu_write_1(sc, R92S_SYS_CLKR, 0x00);
18875fdd4e61Sdamien 	/* Disable CPU clock and 80MHz SSC. */
18885fdd4e61Sdamien 	rsu_write_1(sc, R92S_SYS_CLKR,
18895fdd4e61Sdamien 	    rsu_read_1(sc, R92S_SYS_CLKR) | 0xa0);
18905fdd4e61Sdamien 	/* Enable MAC clock. */
18915fdd4e61Sdamien 	rsu_write_2(sc, R92S_SYS_CLKR,
18925fdd4e61Sdamien 	    rsu_read_2(sc, R92S_SYS_CLKR) |
18935fdd4e61Sdamien 	    R92S_MAC_CLK_EN | R92S_SYS_CLK_EN);
18945fdd4e61Sdamien 
18955fdd4e61Sdamien 	rsu_write_1(sc, R92S_PMC_FSM, 0x02);
18965fdd4e61Sdamien 
18975fdd4e61Sdamien 	/* Enable digital core and IOREG R/W. */
18985fdd4e61Sdamien 	rsu_write_1(sc, R92S_SYS_FUNC_EN + 1,
18995fdd4e61Sdamien 	    rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x08);
19005fdd4e61Sdamien 
19015fdd4e61Sdamien 	rsu_write_1(sc, R92S_SYS_FUNC_EN + 1,
19025fdd4e61Sdamien 	    rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x80);
19035fdd4e61Sdamien 
19045fdd4e61Sdamien 	/* Switch the control path to firmware. */
19055fdd4e61Sdamien 	reg = rsu_read_2(sc, R92S_SYS_CLKR);
19065fdd4e61Sdamien 	reg = (reg & ~R92S_SWHW_SEL) | R92S_FWHW_SEL;
19075fdd4e61Sdamien 	rsu_write_2(sc, R92S_SYS_CLKR, reg);
19085fdd4e61Sdamien 
19095fdd4e61Sdamien 	rsu_write_2(sc, R92S_CR, 0x37fc);
19105fdd4e61Sdamien 
19115fdd4e61Sdamien 	/* Fix USB RX FIFO issue. */
19125fdd4e61Sdamien 	rsu_write_1(sc, 0xfe5c,
19135fdd4e61Sdamien 	    rsu_read_1(sc, 0xfe5c) | 0x80);
19145fdd4e61Sdamien 
19155fdd4e61Sdamien 	rsu_write_1(sc, R92S_SYS_CLKR,
19165fdd4e61Sdamien 	    rsu_read_1(sc, R92S_SYS_CLKR) & ~R92S_SYS_CPU_CLKSEL);
19175fdd4e61Sdamien 
19185fdd4e61Sdamien 	rsu_write_1(sc, 0xfe1c, 0x80);
19195fdd4e61Sdamien 
19205fdd4e61Sdamien 	/* Make sure TxDMA is ready to download firmware. */
19215fdd4e61Sdamien 	for (ntries = 0; ntries < 20; ntries++) {
19225fdd4e61Sdamien 		reg = rsu_read_1(sc, R92S_TCR);
19235fdd4e61Sdamien 		if ((reg & (R92S_TCR_IMEM_CHK_RPT | R92S_TCR_EMEM_CHK_RPT)) ==
19245fdd4e61Sdamien 		    (R92S_TCR_IMEM_CHK_RPT | R92S_TCR_EMEM_CHK_RPT))
19255fdd4e61Sdamien 			break;
19265fdd4e61Sdamien 		DELAY(5);
19275fdd4e61Sdamien 	}
19285fdd4e61Sdamien 	if (ntries == 20) {
19295fdd4e61Sdamien 		/* Reset TxDMA. */
19305fdd4e61Sdamien 		reg = rsu_read_1(sc, R92S_CR);
19315fdd4e61Sdamien 		rsu_write_1(sc, R92S_CR, reg & ~R92S_CR_TXDMA_EN);
19325fdd4e61Sdamien 		DELAY(2);
19335fdd4e61Sdamien 		rsu_write_1(sc, R92S_CR, reg | R92S_CR_TXDMA_EN);
19345fdd4e61Sdamien 	}
19355fdd4e61Sdamien }
19365fdd4e61Sdamien 
19375fdd4e61Sdamien void
rsu_power_off(struct rsu_softc * sc)19385fdd4e61Sdamien rsu_power_off(struct rsu_softc *sc)
19395fdd4e61Sdamien {
19405fdd4e61Sdamien 	/* Turn RF off. */
19415fdd4e61Sdamien 	rsu_write_1(sc, R92S_RF_CTRL, 0x00);
19425fdd4e61Sdamien 	usbd_delay_ms(sc->sc_udev, 5);
19435fdd4e61Sdamien 
19445fdd4e61Sdamien 	/* Turn MAC off. */
19455fdd4e61Sdamien 	/* Switch control path. */
19465fdd4e61Sdamien 	rsu_write_1(sc, R92S_SYS_CLKR + 1, 0x38);
19475fdd4e61Sdamien 	/* Reset MACTOP. */
19485fdd4e61Sdamien 	rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, 0x70);
19495fdd4e61Sdamien 	rsu_write_1(sc, R92S_PMC_FSM, 0x06);
19505fdd4e61Sdamien 	rsu_write_1(sc, R92S_SYS_ISO_CTRL + 0, 0xf9);
19515fdd4e61Sdamien 	rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1, 0xe8);
19525fdd4e61Sdamien 
19535fdd4e61Sdamien 	/* Disable AFE PLL. */
19545fdd4e61Sdamien 	rsu_write_1(sc, R92S_AFE_PLL_CTRL, 0x00);
19555fdd4e61Sdamien 	/* Disable A15V. */
19565fdd4e61Sdamien 	rsu_write_1(sc, R92S_LDOA15_CTRL, 0x54);
19575fdd4e61Sdamien 	/* Disable eFuse 1.2V. */
19585fdd4e61Sdamien 	rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, 0x50);
19595fdd4e61Sdamien 	rsu_write_1(sc, R92S_LDOV12D_CTRL, 0x24);
19605fdd4e61Sdamien 	/* Enable AFE macro block's bandgap and Mbias. */
19615fdd4e61Sdamien 	rsu_write_1(sc, R92S_AFE_MISC, 0x30);
19625fdd4e61Sdamien 	/* Disable 1.6V LDO. */
19635fdd4e61Sdamien 	rsu_write_1(sc, R92S_SPS0_CTRL + 0, 0x56);
19645fdd4e61Sdamien 	rsu_write_1(sc, R92S_SPS0_CTRL + 1, 0x43);
19655fdd4e61Sdamien }
19665fdd4e61Sdamien 
19675fdd4e61Sdamien int
rsu_fw_loadsection(struct rsu_softc * sc,uint8_t * buf,int len)19685fdd4e61Sdamien rsu_fw_loadsection(struct rsu_softc *sc, uint8_t *buf, int len)
19695fdd4e61Sdamien {
19705fdd4e61Sdamien 	struct rsu_tx_data *data;
19715fdd4e61Sdamien 	struct r92s_tx_desc *txd;
1972ab0b1be7Smglocker 	struct usbd_pipe *pipe;
19735fdd4e61Sdamien 	int mlen, error;
19745fdd4e61Sdamien 
19755fdd4e61Sdamien 	data = sc->fwcmd_data;
19765fdd4e61Sdamien 	pipe = sc->pipe[sc->qid2idx[RSU_QID_VO]];
19775fdd4e61Sdamien 	txd = (struct r92s_tx_desc *)data->buf;
19785fdd4e61Sdamien 	while (len > 0) {
19795fdd4e61Sdamien 		memset(txd, 0, sizeof(*txd));
19805fdd4e61Sdamien 		if (len <= RSU_TXBUFSZ - sizeof(*txd)) {
19815fdd4e61Sdamien 			/* Last chunk. */
19825fdd4e61Sdamien 			txd->txdw0 |= htole32(R92S_TXDW0_LINIP);
19835fdd4e61Sdamien 			mlen = len;
19845fdd4e61Sdamien 		} else
19855fdd4e61Sdamien 			mlen = RSU_TXBUFSZ - sizeof(*txd);
19865fdd4e61Sdamien 		txd->txdw0 |= htole32(SM(R92S_TXDW0_PKTLEN, mlen));
19875fdd4e61Sdamien 		memcpy(&txd[1], buf, mlen);
19885fdd4e61Sdamien 
19895fdd4e61Sdamien 		usbd_setup_xfer(data->xfer, pipe, NULL, data->buf,
1990aa88c704Smpi 		    sizeof(*txd) + mlen,
1991aa88c704Smpi 		    USBD_SHORT_XFER_OK | USBD_NO_COPY | USBD_SYNCHRONOUS,
19925fdd4e61Sdamien 		    RSU_TX_TIMEOUT, NULL);
1993aa88c704Smpi 		error = usbd_transfer(data->xfer);
19945fdd4e61Sdamien 		if (error != 0)
19955fdd4e61Sdamien 			return (error);
19965fdd4e61Sdamien 		buf += mlen;
19975fdd4e61Sdamien 		len -= mlen;
19985fdd4e61Sdamien 	}
19995fdd4e61Sdamien 	return (0);
20005fdd4e61Sdamien }
20015fdd4e61Sdamien 
20025fdd4e61Sdamien int
rsu_load_firmware(struct rsu_softc * sc)20035fdd4e61Sdamien rsu_load_firmware(struct rsu_softc *sc)
20045fdd4e61Sdamien {
20055fdd4e61Sdamien 	struct ieee80211com *ic = &sc->sc_ic;
20065fdd4e61Sdamien 	struct r92s_fw_hdr *hdr;
20075fdd4e61Sdamien 	struct r92s_fw_priv *dmem;
20085fdd4e61Sdamien 	uint8_t *imem, *emem;
20095fdd4e61Sdamien 	int imemsz, ememsz;
20105fdd4e61Sdamien 	u_char *fw;
20115fdd4e61Sdamien 	size_t size;
20125fdd4e61Sdamien 	uint32_t reg;
20135fdd4e61Sdamien 	int ntries, error;
20145fdd4e61Sdamien 
20155fdd4e61Sdamien 	/* Read firmware image from the filesystem. */
20161e1b982aSkevlo 	if ((error = loadfirmware("rsu-rtl8712", &fw, &size)) != 0) {
20175fdd4e61Sdamien 		printf("%s: failed loadfirmware of file %s (error %d)\n",
20181e1b982aSkevlo 		    sc->sc_dev.dv_xname, "rsu-rtl8712", error);
20195fdd4e61Sdamien 		return (error);
20205fdd4e61Sdamien 	}
20215fdd4e61Sdamien 	if (size < sizeof(*hdr)) {
20225fdd4e61Sdamien 		printf("%s: firmware too short\n", sc->sc_dev.dv_xname);
20235fdd4e61Sdamien 		error = EINVAL;
20245fdd4e61Sdamien 		goto fail;
20255fdd4e61Sdamien 	}
20265fdd4e61Sdamien 	hdr = (struct r92s_fw_hdr *)fw;
20275fdd4e61Sdamien 	if (hdr->signature != htole16(0x8712) &&
20285fdd4e61Sdamien 	    hdr->signature != htole16(0x8192)) {
20295fdd4e61Sdamien 		printf("%s: invalid firmware signature 0x%x\n",
20305fdd4e61Sdamien 		    sc->sc_dev.dv_xname, letoh16(hdr->signature));
20315fdd4e61Sdamien 		error = EINVAL;
20325fdd4e61Sdamien 		goto fail;
20335fdd4e61Sdamien 	}
20345fdd4e61Sdamien 	DPRINTF(("FW V%d %02x-%02x %02x:%02x\n", letoh16(hdr->version),
20355fdd4e61Sdamien 	    hdr->month, hdr->day, hdr->hour, hdr->minute));
20365fdd4e61Sdamien 
20375fdd4e61Sdamien 	/* Make sure that driver and firmware are in sync. */
20385fdd4e61Sdamien 	if (hdr->privsz != htole32(sizeof(*dmem))) {
20395fdd4e61Sdamien 		printf("%s: unsupported firmware image\n",
20405fdd4e61Sdamien 		    sc->sc_dev.dv_xname);
20415fdd4e61Sdamien 		error = EINVAL;
20425fdd4e61Sdamien 		goto fail;
20435fdd4e61Sdamien 	}
20445fdd4e61Sdamien 	/* Get FW sections sizes. */
20455fdd4e61Sdamien 	imemsz = letoh32(hdr->imemsz);
20465fdd4e61Sdamien 	ememsz = letoh32(hdr->sramsz);
20475fdd4e61Sdamien 	/* Check that all FW sections fit in image. */
20485fdd4e61Sdamien 	if (size < sizeof(*hdr) + imemsz + ememsz) {
20495fdd4e61Sdamien 		printf("%s: firmware too short\n", sc->sc_dev.dv_xname);
20505fdd4e61Sdamien 		error = EINVAL;
20515fdd4e61Sdamien 		goto fail;
20525fdd4e61Sdamien 	}
20535fdd4e61Sdamien 	imem = (uint8_t *)&hdr[1];
20545fdd4e61Sdamien 	emem = imem + imemsz;
20555fdd4e61Sdamien 
20565fdd4e61Sdamien 	/* Load IMEM section. */
20575fdd4e61Sdamien 	error = rsu_fw_loadsection(sc, imem, imemsz);
20585fdd4e61Sdamien 	if (error != 0) {
20595fdd4e61Sdamien 		printf("%s: could not load firmware section %s\n",
20605fdd4e61Sdamien 		    sc->sc_dev.dv_xname, "IMEM");
20615fdd4e61Sdamien 		goto fail;
20625fdd4e61Sdamien 	}
20635fdd4e61Sdamien 	/* Wait for load to complete. */
20645fdd4e61Sdamien 	for (ntries = 0; ntries < 10; ntries++) {
20655fdd4e61Sdamien 		reg = rsu_read_2(sc, R92S_TCR);
20665fdd4e61Sdamien 		if (reg & R92S_TCR_IMEM_CODE_DONE)
20675fdd4e61Sdamien 			break;
20685fdd4e61Sdamien 		DELAY(10);
20695fdd4e61Sdamien 	}
20705fdd4e61Sdamien 	if (ntries == 10 || !(reg & R92S_TCR_IMEM_CHK_RPT)) {
20715fdd4e61Sdamien 		printf("%s: timeout waiting for %s transfer\n",
20725fdd4e61Sdamien 		    sc->sc_dev.dv_xname, "IMEM");
20735fdd4e61Sdamien 		error = ETIMEDOUT;
20745fdd4e61Sdamien 		goto fail;
20755fdd4e61Sdamien 	}
20765fdd4e61Sdamien 
20775fdd4e61Sdamien 	/* Load EMEM section. */
20785fdd4e61Sdamien 	error = rsu_fw_loadsection(sc, emem, ememsz);
20795fdd4e61Sdamien 	if (error != 0) {
20805fdd4e61Sdamien 		printf("%s: could not load firmware section %s\n",
20815fdd4e61Sdamien 		    sc->sc_dev.dv_xname, "EMEM");
20825fdd4e61Sdamien 		goto fail;
20835fdd4e61Sdamien 	}
20845fdd4e61Sdamien 	/* Wait for load to complete. */
20855fdd4e61Sdamien 	for (ntries = 0; ntries < 10; ntries++) {
20865fdd4e61Sdamien 		reg = rsu_read_2(sc, R92S_TCR);
20875fdd4e61Sdamien 		if (reg & R92S_TCR_EMEM_CODE_DONE)
20885fdd4e61Sdamien 			break;
20895fdd4e61Sdamien 		DELAY(10);
20905fdd4e61Sdamien 	}
20915fdd4e61Sdamien 	if (ntries == 10 || !(reg & R92S_TCR_EMEM_CHK_RPT)) {
20925fdd4e61Sdamien 		printf("%s: timeout waiting for %s transfer\n",
20935fdd4e61Sdamien 		    sc->sc_dev.dv_xname, "EMEM");
20945fdd4e61Sdamien 		error = ETIMEDOUT;
20955fdd4e61Sdamien 		goto fail;
20965fdd4e61Sdamien 	}
20975fdd4e61Sdamien 
20985fdd4e61Sdamien 	/* Enable CPU. */
20995fdd4e61Sdamien 	rsu_write_1(sc, R92S_SYS_CLKR,
21005fdd4e61Sdamien 	    rsu_read_1(sc, R92S_SYS_CLKR) | R92S_SYS_CPU_CLKSEL);
21015fdd4e61Sdamien 	if (!(rsu_read_1(sc, R92S_SYS_CLKR) & R92S_SYS_CPU_CLKSEL)) {
21025fdd4e61Sdamien 		printf("%s: could not enable system clock\n",
21035fdd4e61Sdamien 		    sc->sc_dev.dv_xname);
21045fdd4e61Sdamien 		error = EIO;
21055fdd4e61Sdamien 		goto fail;
21065fdd4e61Sdamien 	}
21075fdd4e61Sdamien 	rsu_write_2(sc, R92S_SYS_FUNC_EN,
21085fdd4e61Sdamien 	    rsu_read_2(sc, R92S_SYS_FUNC_EN) | R92S_FEN_CPUEN);
21095fdd4e61Sdamien 	if (!(rsu_read_2(sc, R92S_SYS_FUNC_EN) & R92S_FEN_CPUEN)) {
21105fdd4e61Sdamien 		printf("%s: could not enable microcontroller\n",
21115fdd4e61Sdamien 		    sc->sc_dev.dv_xname);
21125fdd4e61Sdamien 		error = EIO;
21135fdd4e61Sdamien 		goto fail;
21145fdd4e61Sdamien 	}
21155fdd4e61Sdamien 	/* Wait for CPU to initialize. */
21165fdd4e61Sdamien 	for (ntries = 0; ntries < 100; ntries++) {
21175fdd4e61Sdamien 		if (rsu_read_2(sc, R92S_TCR) & R92S_TCR_IMEM_RDY)
21185fdd4e61Sdamien 			break;
21195fdd4e61Sdamien 		DELAY(1000);
21205fdd4e61Sdamien 	}
21215fdd4e61Sdamien 	if (ntries == 100) {
21225fdd4e61Sdamien 		printf("%s: timeout waiting for microcontroller\n",
21235fdd4e61Sdamien 		    sc->sc_dev.dv_xname);
21245fdd4e61Sdamien 		error = ETIMEDOUT;
21255fdd4e61Sdamien 		goto fail;
21265fdd4e61Sdamien 	}
21275fdd4e61Sdamien 
21285fdd4e61Sdamien 	/* Update DMEM section before loading. */
21295fdd4e61Sdamien 	dmem = &hdr->priv;
21305fdd4e61Sdamien 	memset(dmem, 0, sizeof(*dmem));
21315fdd4e61Sdamien 	dmem->hci_sel = R92S_HCI_SEL_USB | R92S_HCI_SEL_8172;
21325fdd4e61Sdamien 	dmem->nendpoints = sc->npipes;
21335fdd4e61Sdamien 	dmem->rf_config = 0x12;	/* 1T2R */
21345fdd4e61Sdamien 	dmem->vcs_type = R92S_VCS_TYPE_AUTO;
21355fdd4e61Sdamien 	dmem->vcs_mode = R92S_VCS_MODE_RTS_CTS;
21365fdd4e61Sdamien 	dmem->bw40_en = (ic->ic_htcaps & IEEE80211_HTCAP_CBW20_40) != 0;
21375fdd4e61Sdamien 	dmem->turbo_mode = 1;
21385fdd4e61Sdamien 	/* Load DMEM section. */
21395fdd4e61Sdamien 	error = rsu_fw_loadsection(sc, (uint8_t *)dmem, sizeof(*dmem));
21405fdd4e61Sdamien 	if (error != 0) {
21415fdd4e61Sdamien 		printf("%s: could not load firmware section %s\n",
21425fdd4e61Sdamien 		    sc->sc_dev.dv_xname, "DMEM");
21435fdd4e61Sdamien 		goto fail;
21445fdd4e61Sdamien 	}
21455fdd4e61Sdamien 	/* Wait for load to complete. */
21465fdd4e61Sdamien 	for (ntries = 0; ntries < 100; ntries++) {
21475fdd4e61Sdamien 		if (rsu_read_2(sc, R92S_TCR) & R92S_TCR_DMEM_CODE_DONE)
21485fdd4e61Sdamien 			break;
21495fdd4e61Sdamien 		DELAY(1000);
21505fdd4e61Sdamien 	}
21515fdd4e61Sdamien 	if (ntries == 100) {
21525fdd4e61Sdamien 		printf("%s: timeout waiting for %s transfer\n",
21535fdd4e61Sdamien 		    sc->sc_dev.dv_xname, "DMEM");
21545fdd4e61Sdamien 		error = ETIMEDOUT;
21555fdd4e61Sdamien 		goto fail;
21565fdd4e61Sdamien 	}
21575fdd4e61Sdamien 	/* Wait for firmware readiness. */
21585fdd4e61Sdamien 	for (ntries = 0; ntries < 60; ntries++) {
21595fdd4e61Sdamien 		if (!(rsu_read_2(sc, R92S_TCR) & R92S_TCR_FWRDY))
21605fdd4e61Sdamien 			break;
21615fdd4e61Sdamien 		DELAY(1000);
21625fdd4e61Sdamien 	}
21635fdd4e61Sdamien 	if (ntries == 60) {
21645fdd4e61Sdamien 		printf("%s: timeout waiting for firmware readiness\n",
21655fdd4e61Sdamien 		    sc->sc_dev.dv_xname);
21665fdd4e61Sdamien 		error = ETIMEDOUT;
21675fdd4e61Sdamien 		goto fail;
21685fdd4e61Sdamien 	}
21695fdd4e61Sdamien  fail:
2170c9ee9455Sderaadt 	free(fw, M_DEVBUF, size);
21715fdd4e61Sdamien 	return (error);
21725fdd4e61Sdamien }
21735fdd4e61Sdamien 
21745fdd4e61Sdamien int
rsu_init(struct ifnet * ifp)21755fdd4e61Sdamien rsu_init(struct ifnet *ifp)
21765fdd4e61Sdamien {
21775fdd4e61Sdamien 	struct rsu_softc *sc = ifp->if_softc;
21785fdd4e61Sdamien 	struct ieee80211com *ic = &sc->sc_ic;
21795fdd4e61Sdamien 	struct r92s_set_pwr_mode cmd;
21805fdd4e61Sdamien 	struct rsu_rx_data *data;
21815fdd4e61Sdamien 	int i, error;
21825fdd4e61Sdamien 
21835fdd4e61Sdamien 	/* Init host async commands ring. */
21845fdd4e61Sdamien 	sc->cmdq.cur = sc->cmdq.next = sc->cmdq.queued = 0;
21855fdd4e61Sdamien 
21865fdd4e61Sdamien 	/* Allocate Tx/Rx buffers. */
21875fdd4e61Sdamien 	error = rsu_alloc_rx_list(sc);
21885fdd4e61Sdamien 	if (error != 0) {
21895fdd4e61Sdamien 		printf("%s: could not allocate Rx buffers\n",
21905fdd4e61Sdamien 		    sc->sc_dev.dv_xname);
21915fdd4e61Sdamien 		goto fail;
21925fdd4e61Sdamien 	}
21935fdd4e61Sdamien 	error = rsu_alloc_tx_list(sc);
21945fdd4e61Sdamien 	if (error != 0) {
21955fdd4e61Sdamien 		printf("%s: could not allocate Tx buffers\n",
21965fdd4e61Sdamien 		    sc->sc_dev.dv_xname);
21975fdd4e61Sdamien 		goto fail;
21985fdd4e61Sdamien 	}
21995fdd4e61Sdamien 	/* Reserve one Tx buffer for firmware commands. */
22005fdd4e61Sdamien 	sc->fwcmd_data = TAILQ_FIRST(&sc->tx_free_list);
22015fdd4e61Sdamien 	TAILQ_REMOVE(&sc->tx_free_list, sc->fwcmd_data, next);
22025fdd4e61Sdamien 
22035fdd4e61Sdamien 	/* Power on adapter. */
22045fdd4e61Sdamien 	if (sc->cut == 1)
22055fdd4e61Sdamien 		rsu_power_on_acut(sc);
22065fdd4e61Sdamien 	else
22075fdd4e61Sdamien 		rsu_power_on_bcut(sc);
22085fdd4e61Sdamien 	/* Load firmware. */
22095fdd4e61Sdamien 	error = rsu_load_firmware(sc);
22105fdd4e61Sdamien 	if (error != 0)
22115fdd4e61Sdamien 		goto fail;
22125fdd4e61Sdamien 
22135fdd4e61Sdamien 	/* Enable Rx TCP checksum offload. */
22145fdd4e61Sdamien 	rsu_write_4(sc, R92S_RCR,
22155fdd4e61Sdamien 	    rsu_read_4(sc, R92S_RCR) | 0x04000000);
22165fdd4e61Sdamien 	/* Append PHY status. */
22175fdd4e61Sdamien 	rsu_write_4(sc, R92S_RCR,
22185fdd4e61Sdamien 	    rsu_read_4(sc, R92S_RCR) | 0x02000000);
22195fdd4e61Sdamien 
22205fdd4e61Sdamien 	rsu_write_4(sc, R92S_CR,
22215fdd4e61Sdamien 	    rsu_read_4(sc, R92S_CR) & ~0xff000000);
22225fdd4e61Sdamien 
22235fdd4e61Sdamien 	/* Use 128 bytes pages. */
22245fdd4e61Sdamien 	rsu_write_1(sc, 0x00b5,
22255fdd4e61Sdamien 	    rsu_read_1(sc, 0x00b5) | 0x01);
22265fdd4e61Sdamien 	/* Enable USB Rx aggregation. */
22275fdd4e61Sdamien 	rsu_write_1(sc, 0x00bd,
22285fdd4e61Sdamien 	    rsu_read_1(sc, 0x00bd) | 0x80);
22295fdd4e61Sdamien 	/* Set USB Rx aggregation threshold. */
22305fdd4e61Sdamien 	rsu_write_1(sc, 0x00d9, 0x01);
22315fdd4e61Sdamien 	/* Set USB Rx aggregation timeout (1.7ms/4). */
22325fdd4e61Sdamien 	rsu_write_1(sc, 0xfe5b, 0x04);
22335fdd4e61Sdamien 	/* Fix USB Rx FIFO issue. */
22345fdd4e61Sdamien 	rsu_write_1(sc, 0xfe5c,
22355fdd4e61Sdamien 	    rsu_read_1(sc, 0xfe5c) | 0x80);
22365fdd4e61Sdamien 
22375fdd4e61Sdamien 	/* Set MAC address. */
22385fdd4e61Sdamien 	IEEE80211_ADDR_COPY(ic->ic_myaddr, LLADDR(ifp->if_sadl));
22395fdd4e61Sdamien 	rsu_write_region_1(sc, R92S_MACID, ic->ic_myaddr, IEEE80211_ADDR_LEN);
22405fdd4e61Sdamien 
22415fdd4e61Sdamien 	/* Queue Rx xfers (XXX C2H pipe for 11-pipe configurations?) */
22425fdd4e61Sdamien 	for (i = 0; i < RSU_RX_LIST_COUNT; i++) {
22435fdd4e61Sdamien 		data = &sc->rx_data[i];
22445fdd4e61Sdamien 
22455fdd4e61Sdamien 		data->pipe = sc->pipe[sc->qid2idx[RSU_QID_RXOFF]];
22465fdd4e61Sdamien 		usbd_setup_xfer(data->xfer, data->pipe, data, data->buf,
22475fdd4e61Sdamien 		    RSU_RXBUFSZ, USBD_SHORT_XFER_OK | USBD_NO_COPY,
22485fdd4e61Sdamien 		    USBD_NO_TIMEOUT, rsu_rxeof);
22495fdd4e61Sdamien 		error = usbd_transfer(data->xfer);
22505fdd4e61Sdamien 		if (error != 0 && error != USBD_IN_PROGRESS)
22515fdd4e61Sdamien 			goto fail;
22525fdd4e61Sdamien 	}
22535fdd4e61Sdamien 
22545fdd4e61Sdamien 	/* NB: it really takes that long for firmware to boot. */
22555fdd4e61Sdamien 	usbd_delay_ms(sc->sc_udev, 1500);
22565fdd4e61Sdamien 
22575fdd4e61Sdamien 	DPRINTF(("setting MAC address to %s\n", ether_sprintf(ic->ic_myaddr)));
22585fdd4e61Sdamien 	error = rsu_fw_cmd(sc, R92S_CMD_SET_MAC_ADDRESS, ic->ic_myaddr,
22595fdd4e61Sdamien 	    IEEE80211_ADDR_LEN);
22605fdd4e61Sdamien 	if (error != 0) {
22615fdd4e61Sdamien 		printf("%s: could not set MAC address\n", sc->sc_dev.dv_xname);
22625fdd4e61Sdamien 		goto fail;
22635fdd4e61Sdamien 	}
22645fdd4e61Sdamien 
22655fdd4e61Sdamien 	rsu_write_1(sc, R92S_USB_HRPWM,
22665fdd4e61Sdamien 	    R92S_USB_HRPWM_PS_ST_ACTIVE | R92S_USB_HRPWM_PS_ALL_ON);
22675fdd4e61Sdamien 
22685fdd4e61Sdamien 	memset(&cmd, 0, sizeof(cmd));
22695fdd4e61Sdamien 	cmd.mode = R92S_PS_MODE_ACTIVE;
22705fdd4e61Sdamien 	DPRINTF(("setting ps mode to %d\n", cmd.mode));
22715fdd4e61Sdamien 	error = rsu_fw_cmd(sc, R92S_CMD_SET_PWR_MODE, &cmd, sizeof(cmd));
22725fdd4e61Sdamien 	if (error != 0) {
22735fdd4e61Sdamien 		printf("%s: could not set PS mode\n", sc->sc_dev.dv_xname);
22745fdd4e61Sdamien 		goto fail;
22755fdd4e61Sdamien 	}
22765fdd4e61Sdamien 
22775fdd4e61Sdamien 	if (ic->ic_htcaps & IEEE80211_HTCAP_CBW20_40) {
22785fdd4e61Sdamien 		/* Enable 40MHz mode. */
22795fdd4e61Sdamien 		error = rsu_fw_iocmd(sc,
22805fdd4e61Sdamien 		    SM(R92S_IOCMD_CLASS, 0xf4) |
22815fdd4e61Sdamien 		    SM(R92S_IOCMD_INDEX, 0x00) |
22825fdd4e61Sdamien 		    SM(R92S_IOCMD_VALUE, 0x0007));
22835fdd4e61Sdamien 		if (error != 0) {
22845fdd4e61Sdamien 			printf("%s: could not enable 40MHz mode\n",
22855fdd4e61Sdamien 			    sc->sc_dev.dv_xname);
22865fdd4e61Sdamien 			goto fail;
22875fdd4e61Sdamien 		}
22885fdd4e61Sdamien 	}
22895e32cd22Sstsp 
22905fdd4e61Sdamien 	/* Set default channel. */
22915fdd4e61Sdamien 	ic->ic_bss->ni_chan = ic->ic_ibss_chan;
22925fdd4e61Sdamien 
22935fdd4e61Sdamien 	/* We're ready to go. */
22945fdd4e61Sdamien 	ifp->if_flags |= IFF_RUNNING;
229514fa908aSstsp 	ifq_clr_oactive(&ifp->if_snd);
22965fdd4e61Sdamien 
22975fdd4e61Sdamien #ifdef notyet
22985fdd4e61Sdamien 	if (ic->ic_flags & IEEE80211_F_WEPON) {
22995fdd4e61Sdamien 		/* Install WEP keys. */
23005fdd4e61Sdamien 		for (i = 0; i < IEEE80211_WEP_NKID; i++)
23015fdd4e61Sdamien 			rsu_set_key(ic, NULL, &ic->ic_nw_keys[i]);
23025fdd4e61Sdamien 		rsu_wait_async(sc);
23035fdd4e61Sdamien 	}
23045fdd4e61Sdamien #endif
23055fdd4e61Sdamien 
23065fdd4e61Sdamien 	sc->scan_pass = 0;
23075fdd4e61Sdamien 	ieee80211_begin_scan(ifp);
23085fdd4e61Sdamien 	return (0);
23095fdd4e61Sdamien  fail:
23105fdd4e61Sdamien 	rsu_stop(ifp);
23115fdd4e61Sdamien 	return (error);
23125fdd4e61Sdamien }
23135fdd4e61Sdamien 
23145fdd4e61Sdamien void
rsu_stop(struct ifnet * ifp)23155fdd4e61Sdamien rsu_stop(struct ifnet *ifp)
23165fdd4e61Sdamien {
23175fdd4e61Sdamien 	struct rsu_softc *sc = ifp->if_softc;
23185fdd4e61Sdamien 	struct ieee80211com *ic = &sc->sc_ic;
23195fdd4e61Sdamien 	int i, s;
23205fdd4e61Sdamien 
23215fdd4e61Sdamien 	sc->sc_tx_timer = 0;
23225fdd4e61Sdamien 	ifp->if_timer = 0;
2323de6cd8fbSdlg 	ifp->if_flags &= ~IFF_RUNNING;
2324de6cd8fbSdlg 	ifq_clr_oactive(&ifp->if_snd);
23255fdd4e61Sdamien 
23265fdd4e61Sdamien 	s = splusb();
23275fdd4e61Sdamien 	ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
23285fdd4e61Sdamien 	/* Wait for all async commands to complete. */
23295fdd4e61Sdamien 	rsu_wait_async(sc);
23305fdd4e61Sdamien 	splx(s);
23315fdd4e61Sdamien 
23325fdd4e61Sdamien 	timeout_del(&sc->calib_to);
23335fdd4e61Sdamien 
23345fdd4e61Sdamien 	/* Power off adapter. */
23355fdd4e61Sdamien 	rsu_power_off(sc);
23365fdd4e61Sdamien 
23375fdd4e61Sdamien 	/* Abort Tx/Rx. */
23385fdd4e61Sdamien 	for (i = 0; i < sc->npipes; i++)
23395fdd4e61Sdamien 		usbd_abort_pipe(sc->pipe[i]);
23405fdd4e61Sdamien 
23415fdd4e61Sdamien 	/* Free Tx/Rx buffers. */
23425fdd4e61Sdamien 	rsu_free_tx_list(sc);
23435fdd4e61Sdamien 	rsu_free_rx_list(sc);
23445fdd4e61Sdamien }
2345