xref: /freebsd/sys/dev/usb/wlan/if_rsu.c (revision 4914fa0fd65d00c2b2d935189a1146043cd8b8cb)
131d98677SRui Paulo /*	$OpenBSD: if_rsu.c,v 1.17 2013/04/15 09:23:01 mglocker Exp $	*/
231d98677SRui Paulo 
331d98677SRui Paulo /*-
431d98677SRui Paulo  * Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr>
531d98677SRui Paulo  *
631d98677SRui Paulo  * Permission to use, copy, modify, and distribute this software for any
731d98677SRui Paulo  * purpose with or without fee is hereby granted, provided that the above
831d98677SRui Paulo  * copyright notice and this permission notice appear in all copies.
931d98677SRui Paulo  *
1031d98677SRui Paulo  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1131d98677SRui Paulo  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1231d98677SRui Paulo  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1331d98677SRui Paulo  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1431d98677SRui Paulo  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1531d98677SRui Paulo  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1631d98677SRui Paulo  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1731d98677SRui Paulo  */
1831d98677SRui Paulo #include <sys/cdefs.h>
1931d98677SRui Paulo __FBSDID("$FreeBSD$");
2031d98677SRui Paulo 
2131d98677SRui Paulo /*
2231d98677SRui Paulo  * Driver for Realtek RTL8188SU/RTL8191SU/RTL8192SU.
2331d98677SRui Paulo  *
2431d98677SRui Paulo  * TODO:
2531d98677SRui Paulo  *   o 11n support
2631d98677SRui Paulo  *   o h/w crypto
2731d98677SRui Paulo  *   o hostap / ibss / mesh
2831d98677SRui Paulo  */
29*4914fa0fSAdrian Chadd 
3031d98677SRui Paulo #include <sys/param.h>
3131d98677SRui Paulo #include <sys/endian.h>
3231d98677SRui Paulo #include <sys/sockio.h>
3331d98677SRui Paulo #include <sys/mbuf.h>
3431d98677SRui Paulo #include <sys/kernel.h>
3531d98677SRui Paulo #include <sys/socket.h>
3631d98677SRui Paulo #include <sys/systm.h>
3731d98677SRui Paulo #include <sys/conf.h>
3831d98677SRui Paulo #include <sys/bus.h>
3931d98677SRui Paulo #include <sys/rman.h>
4031d98677SRui Paulo #include <sys/firmware.h>
4131d98677SRui Paulo #include <sys/module.h>
4231d98677SRui Paulo 
4331d98677SRui Paulo #include <machine/bus.h>
4431d98677SRui Paulo #include <machine/resource.h>
4531d98677SRui Paulo 
4631d98677SRui Paulo #include <net/bpf.h>
4731d98677SRui Paulo #include <net/if.h>
4876039bc8SGleb Smirnoff #include <net/if_var.h>
4931d98677SRui Paulo #include <net/if_arp.h>
5031d98677SRui Paulo #include <net/if_dl.h>
5131d98677SRui Paulo #include <net/if_media.h>
5231d98677SRui Paulo #include <net/if_types.h>
5331d98677SRui Paulo 
5431d98677SRui Paulo #include <netinet/in.h>
5531d98677SRui Paulo #include <netinet/in_systm.h>
5631d98677SRui Paulo #include <netinet/in_var.h>
5731d98677SRui Paulo #include <netinet/if_ether.h>
5831d98677SRui Paulo #include <netinet/ip.h>
5931d98677SRui Paulo 
6031d98677SRui Paulo #include <net80211/ieee80211_var.h>
6131d98677SRui Paulo #include <net80211/ieee80211_regdomain.h>
6231d98677SRui Paulo #include <net80211/ieee80211_radiotap.h>
6331d98677SRui Paulo 
6431d98677SRui Paulo #include <dev/usb/usb.h>
6531d98677SRui Paulo #include <dev/usb/usbdi.h>
6631d98677SRui Paulo #include "usbdevs.h"
6731d98677SRui Paulo 
6831d98677SRui Paulo #define USB_DEBUG_VAR rsu_debug
6931d98677SRui Paulo #include <dev/usb/usb_debug.h>
7031d98677SRui Paulo 
7131d98677SRui Paulo #include <dev/usb/wlan/if_rsureg.h>
7231d98677SRui Paulo 
7331d98677SRui Paulo #ifdef USB_DEBUG
7431d98677SRui Paulo static int rsu_debug = 0;
7531d98677SRui Paulo SYSCTL_NODE(_hw_usb, OID_AUTO, rsu, CTLFLAG_RW, 0, "USB rsu");
76ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb_rsu, OID_AUTO, debug, CTLFLAG_RWTUN, &rsu_debug, 0,
7731d98677SRui Paulo     "Debug level");
78*4914fa0fSAdrian Chadd #define	RSU_DPRINTF(_sc, _flg, ...)					\
79*4914fa0fSAdrian Chadd 	do								\
80*4914fa0fSAdrian Chadd 		if (((_flg) == (RSU_DEBUG_ANY)) || (rsu_debug & (_flg))) \
81*4914fa0fSAdrian Chadd 			device_printf((_sc)->sc_dev, __VA_ARGS__);	\
82*4914fa0fSAdrian Chadd 	while (0)
83*4914fa0fSAdrian Chadd #else
84*4914fa0fSAdrian Chadd #define	RSU_DPRINTF(_sc, _flg, ...)
8531d98677SRui Paulo #endif
8631d98677SRui Paulo 
87*4914fa0fSAdrian Chadd #define	RSU_DEBUG_ANY		0xffffffff
88*4914fa0fSAdrian Chadd #define	RSU_DEBUG_TX		0x00000001
89*4914fa0fSAdrian Chadd #define	RSU_DEBUG_RX		0x00000002
90*4914fa0fSAdrian Chadd #define	RSU_DEBUG_RESET		0x00000004
91*4914fa0fSAdrian Chadd #define	RSU_DEBUG_CALIB		0x00000008
92*4914fa0fSAdrian Chadd #define	RSU_DEBUG_STATE		0x00000010
93*4914fa0fSAdrian Chadd #define	RSU_DEBUG_SCAN		0x00000020
94*4914fa0fSAdrian Chadd #define	RSU_DEBUG_FWCMD		0x00000040
95*4914fa0fSAdrian Chadd #define	RSU_DEBUG_TXDONE	0x00000080
96*4914fa0fSAdrian Chadd #define	RSU_DEBUG_FW		0x00000100
97*4914fa0fSAdrian Chadd #define	RSU_DEBUG_FWDBG		0x00000200
98*4914fa0fSAdrian Chadd 
9931d98677SRui Paulo static const STRUCT_USB_HOST_ID rsu_devs[] = {
10031d98677SRui Paulo #define	RSU_HT_NOT_SUPPORTED 0
10131d98677SRui Paulo #define	RSU_HT_SUPPORTED 1
10231d98677SRui Paulo #define RSU_DEV_HT(v,p)  { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, \
10331d98677SRui Paulo 				   RSU_HT_SUPPORTED) }
10431d98677SRui Paulo #define RSU_DEV(v,p)     { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, \
10531d98677SRui Paulo 				   RSU_HT_NOT_SUPPORTED) }
10631d98677SRui Paulo 	RSU_DEV(ASUS,			RTL8192SU),
10731d98677SRui Paulo 	RSU_DEV(AZUREWAVE,		RTL8192SU_4),
10831d98677SRui Paulo 	RSU_DEV_HT(ACCTON,		RTL8192SU),
10931d98677SRui Paulo 	RSU_DEV_HT(ASUS,		USBN10),
11031d98677SRui Paulo 	RSU_DEV_HT(AZUREWAVE,		RTL8192SU_1),
11131d98677SRui Paulo 	RSU_DEV_HT(AZUREWAVE,		RTL8192SU_2),
11231d98677SRui Paulo 	RSU_DEV_HT(AZUREWAVE,		RTL8192SU_3),
11331d98677SRui Paulo 	RSU_DEV_HT(AZUREWAVE,		RTL8192SU_5),
11431d98677SRui Paulo 	RSU_DEV_HT(BELKIN,		RTL8192SU_1),
11531d98677SRui Paulo 	RSU_DEV_HT(BELKIN,		RTL8192SU_2),
11631d98677SRui Paulo 	RSU_DEV_HT(BELKIN,		RTL8192SU_3),
11731d98677SRui Paulo 	RSU_DEV_HT(CONCEPTRONIC2,	RTL8192SU_1),
11831d98677SRui Paulo 	RSU_DEV_HT(CONCEPTRONIC2,	RTL8192SU_2),
11931d98677SRui Paulo 	RSU_DEV_HT(CONCEPTRONIC2,	RTL8192SU_3),
12031d98677SRui Paulo 	RSU_DEV_HT(COREGA,		RTL8192SU),
12131d98677SRui Paulo 	RSU_DEV_HT(DLINK2,		DWA131A1),
12231d98677SRui Paulo 	RSU_DEV_HT(DLINK2,		RTL8192SU_1),
12331d98677SRui Paulo 	RSU_DEV_HT(DLINK2,		RTL8192SU_2),
12431d98677SRui Paulo 	RSU_DEV_HT(EDIMAX,		RTL8192SU_1),
12531d98677SRui Paulo 	RSU_DEV_HT(EDIMAX,		RTL8192SU_2),
126bf124fcfSKevin Lo 	RSU_DEV_HT(EDIMAX,		EW7622UMN),
12731d98677SRui Paulo 	RSU_DEV_HT(GUILLEMOT,		HWGUN54),
12831d98677SRui Paulo 	RSU_DEV_HT(GUILLEMOT,		HWNUM300),
12931d98677SRui Paulo 	RSU_DEV_HT(HAWKING,		RTL8192SU_1),
13031d98677SRui Paulo 	RSU_DEV_HT(HAWKING,		RTL8192SU_2),
13131d98677SRui Paulo 	RSU_DEV_HT(PLANEX2,		GWUSNANO),
13231d98677SRui Paulo 	RSU_DEV_HT(REALTEK,		RTL8171),
13331d98677SRui Paulo 	RSU_DEV_HT(REALTEK,		RTL8172),
13431d98677SRui Paulo 	RSU_DEV_HT(REALTEK,		RTL8173),
13531d98677SRui Paulo 	RSU_DEV_HT(REALTEK,		RTL8174),
13631d98677SRui Paulo 	RSU_DEV_HT(REALTEK,		RTL8192SU),
13731d98677SRui Paulo 	RSU_DEV_HT(REALTEK,		RTL8712),
13831d98677SRui Paulo 	RSU_DEV_HT(REALTEK,		RTL8713),
13931d98677SRui Paulo 	RSU_DEV_HT(SENAO,		RTL8192SU_1),
14031d98677SRui Paulo 	RSU_DEV_HT(SENAO,		RTL8192SU_2),
14131d98677SRui Paulo 	RSU_DEV_HT(SITECOMEU,		WL349V1),
14231d98677SRui Paulo 	RSU_DEV_HT(SITECOMEU,		WL353),
14331d98677SRui Paulo 	RSU_DEV_HT(SWEEX2,		LW154),
144fa20eb98SKevin Lo 	RSU_DEV_HT(TRENDNET,		TEW646UBH),
14531d98677SRui Paulo #undef RSU_DEV_HT
14631d98677SRui Paulo #undef RSU_DEV
14731d98677SRui Paulo };
14831d98677SRui Paulo 
14931d98677SRui Paulo static device_probe_t   rsu_match;
15031d98677SRui Paulo static device_attach_t  rsu_attach;
15131d98677SRui Paulo static device_detach_t  rsu_detach;
152910593b5SHans Petter Selasky static usb_callback_t   rsu_bulk_tx_callback_be_bk;
153910593b5SHans Petter Selasky static usb_callback_t   rsu_bulk_tx_callback_vi_vo;
15431d98677SRui Paulo static usb_callback_t   rsu_bulk_rx_callback;
15531d98677SRui Paulo static usb_error_t	rsu_do_request(struct rsu_softc *,
15631d98677SRui Paulo 			    struct usb_device_request *, void *);
15731d98677SRui Paulo static struct ieee80211vap *
15831d98677SRui Paulo 		rsu_vap_create(struct ieee80211com *, const char name[],
15931d98677SRui Paulo 		    int, enum ieee80211_opmode, int, const uint8_t bssid[],
16031d98677SRui Paulo 		    const uint8_t mac[]);
16131d98677SRui Paulo static void	rsu_vap_delete(struct ieee80211vap *);
16231d98677SRui Paulo static void	rsu_scan_start(struct ieee80211com *);
16331d98677SRui Paulo static void	rsu_scan_end(struct ieee80211com *);
16431d98677SRui Paulo static void	rsu_set_channel(struct ieee80211com *);
165272f6adeSGleb Smirnoff static void	rsu_update_mcast(struct ieee80211com *);
16631d98677SRui Paulo static int	rsu_alloc_rx_list(struct rsu_softc *);
16731d98677SRui Paulo static void	rsu_free_rx_list(struct rsu_softc *);
16831d98677SRui Paulo static int	rsu_alloc_tx_list(struct rsu_softc *);
16931d98677SRui Paulo static void	rsu_free_tx_list(struct rsu_softc *);
17031d98677SRui Paulo static void	rsu_free_list(struct rsu_softc *, struct rsu_data [], int);
17131d98677SRui Paulo static struct rsu_data *_rsu_getbuf(struct rsu_softc *);
17231d98677SRui Paulo static struct rsu_data *rsu_getbuf(struct rsu_softc *);
17331d98677SRui Paulo static int	rsu_write_region_1(struct rsu_softc *, uint16_t, uint8_t *,
17431d98677SRui Paulo 		    int);
17531d98677SRui Paulo static void	rsu_write_1(struct rsu_softc *, uint16_t, uint8_t);
17631d98677SRui Paulo static void	rsu_write_2(struct rsu_softc *, uint16_t, uint16_t);
17731d98677SRui Paulo static void	rsu_write_4(struct rsu_softc *, uint16_t, uint32_t);
17831d98677SRui Paulo static int	rsu_read_region_1(struct rsu_softc *, uint16_t, uint8_t *,
17931d98677SRui Paulo 		    int);
18031d98677SRui Paulo static uint8_t	rsu_read_1(struct rsu_softc *, uint16_t);
18131d98677SRui Paulo static uint16_t	rsu_read_2(struct rsu_softc *, uint16_t);
18231d98677SRui Paulo static uint32_t	rsu_read_4(struct rsu_softc *, uint16_t);
18331d98677SRui Paulo static int	rsu_fw_iocmd(struct rsu_softc *, uint32_t);
18431d98677SRui Paulo static uint8_t	rsu_efuse_read_1(struct rsu_softc *, uint16_t);
18531d98677SRui Paulo static int	rsu_read_rom(struct rsu_softc *);
18631d98677SRui Paulo static int	rsu_fw_cmd(struct rsu_softc *, uint8_t, void *, int);
18731d98677SRui Paulo static void	rsu_calib_task(void *, int);
18831d98677SRui Paulo static int	rsu_newstate(struct ieee80211vap *, enum ieee80211_state, int);
18931d98677SRui Paulo #ifdef notyet
19031d98677SRui Paulo static void	rsu_set_key(struct rsu_softc *, const struct ieee80211_key *);
19131d98677SRui Paulo static void	rsu_delete_key(struct rsu_softc *, const struct ieee80211_key *);
19231d98677SRui Paulo #endif
19331d98677SRui Paulo static int	rsu_site_survey(struct rsu_softc *, struct ieee80211vap *);
19431d98677SRui Paulo static int	rsu_join_bss(struct rsu_softc *, struct ieee80211_node *);
19531d98677SRui Paulo static int	rsu_disconnect(struct rsu_softc *);
19631d98677SRui Paulo static void	rsu_event_survey(struct rsu_softc *, uint8_t *, int);
19731d98677SRui Paulo static void	rsu_event_join_bss(struct rsu_softc *, uint8_t *, int);
19831d98677SRui Paulo static void	rsu_rx_event(struct rsu_softc *, uint8_t, uint8_t *, int);
19931d98677SRui Paulo static void	rsu_rx_multi_event(struct rsu_softc *, uint8_t *, int);
20031d98677SRui Paulo static int8_t	rsu_get_rssi(struct rsu_softc *, int, void *);
20131d98677SRui Paulo static struct mbuf *
20231d98677SRui Paulo 		rsu_rx_frame(struct rsu_softc *, uint8_t *, int, int *);
20331d98677SRui Paulo static struct mbuf *
20431d98677SRui Paulo 		rsu_rx_multi_frame(struct rsu_softc *, uint8_t *, int, int *);
20531d98677SRui Paulo static struct mbuf *
20631d98677SRui Paulo 		rsu_rxeof(struct usb_xfer *, struct rsu_data *, int *);
20731d98677SRui Paulo static void	rsu_txeof(struct usb_xfer *, struct rsu_data *);
20831d98677SRui Paulo static int	rsu_raw_xmit(struct ieee80211_node *, struct mbuf *,
20931d98677SRui Paulo 		    const struct ieee80211_bpf_params *);
2107a79cebfSGleb Smirnoff static void	rsu_init(struct rsu_softc *);
21131d98677SRui Paulo static int	rsu_tx_start(struct rsu_softc *, struct ieee80211_node *,
212400b4e53SHans Petter Selasky 		    struct mbuf *, struct rsu_data *);
2137a79cebfSGleb Smirnoff static int	rsu_transmit(struct ieee80211com *, struct mbuf *);
2147a79cebfSGleb Smirnoff static void	rsu_start(struct rsu_softc *);
2157a79cebfSGleb Smirnoff static void	rsu_parent(struct ieee80211com *);
2167a79cebfSGleb Smirnoff static void	rsu_stop(struct rsu_softc *);
217b41381aeSHans Petter Selasky static void	rsu_ms_delay(struct rsu_softc *);
21831d98677SRui Paulo 
21931d98677SRui Paulo static device_method_t rsu_methods[] = {
22031d98677SRui Paulo 	DEVMETHOD(device_probe,		rsu_match),
22131d98677SRui Paulo 	DEVMETHOD(device_attach,	rsu_attach),
22231d98677SRui Paulo 	DEVMETHOD(device_detach,	rsu_detach),
22331d98677SRui Paulo 
22431d98677SRui Paulo 	DEVMETHOD_END
22531d98677SRui Paulo };
22631d98677SRui Paulo 
22731d98677SRui Paulo static driver_t rsu_driver = {
22831d98677SRui Paulo 	.name = "rsu",
22931d98677SRui Paulo 	.methods = rsu_methods,
23031d98677SRui Paulo 	.size = sizeof(struct rsu_softc)
23131d98677SRui Paulo };
23231d98677SRui Paulo 
23331d98677SRui Paulo static devclass_t rsu_devclass;
23431d98677SRui Paulo 
23531d98677SRui Paulo DRIVER_MODULE(rsu, uhub, rsu_driver, rsu_devclass, NULL, 0);
23631d98677SRui Paulo MODULE_DEPEND(rsu, wlan, 1, 1, 1);
23731d98677SRui Paulo MODULE_DEPEND(rsu, usb, 1, 1, 1);
23831d98677SRui Paulo MODULE_DEPEND(rsu, firmware, 1, 1, 1);
23931d98677SRui Paulo MODULE_VERSION(rsu, 1);
24031d98677SRui Paulo 
241910593b5SHans Petter Selasky static uint8_t rsu_wme_ac_xfer_map[4] = {
242910593b5SHans Petter Selasky 	[WME_AC_BE] = RSU_BULK_TX_BE_BK,
243910593b5SHans Petter Selasky 	[WME_AC_BK] = RSU_BULK_TX_BE_BK,
244910593b5SHans Petter Selasky 	[WME_AC_VI] = RSU_BULK_TX_VI_VO,
245910593b5SHans Petter Selasky 	[WME_AC_VO] = RSU_BULK_TX_VI_VO,
246910593b5SHans Petter Selasky };
247910593b5SHans Petter Selasky 
24831d98677SRui Paulo static const struct usb_config rsu_config[RSU_N_TRANSFER] = {
24931d98677SRui Paulo 	[RSU_BULK_RX] = {
25031d98677SRui Paulo 		.type = UE_BULK,
25131d98677SRui Paulo 		.endpoint = UE_ADDR_ANY,
25231d98677SRui Paulo 		.direction = UE_DIR_IN,
25331d98677SRui Paulo 		.bufsize = RSU_RXBUFSZ,
25431d98677SRui Paulo 		.flags = {
25531d98677SRui Paulo 			.pipe_bof = 1,
25631d98677SRui Paulo 			.short_xfer_ok = 1
25731d98677SRui Paulo 		},
25831d98677SRui Paulo 		.callback = rsu_bulk_rx_callback
25931d98677SRui Paulo 	},
260910593b5SHans Petter Selasky 	[RSU_BULK_TX_BE_BK] = {
26131d98677SRui Paulo 		.type = UE_BULK,
26231d98677SRui Paulo 		.endpoint = 0x06,
26331d98677SRui Paulo 		.direction = UE_DIR_OUT,
26431d98677SRui Paulo 		.bufsize = RSU_TXBUFSZ,
26531d98677SRui Paulo 		.flags = {
26631d98677SRui Paulo 			.ext_buffer = 1,
26731d98677SRui Paulo 			.pipe_bof = 1,
26831d98677SRui Paulo 			.force_short_xfer = 1
26931d98677SRui Paulo 		},
270910593b5SHans Petter Selasky 		.callback = rsu_bulk_tx_callback_be_bk,
27131d98677SRui Paulo 		.timeout = RSU_TX_TIMEOUT
27231d98677SRui Paulo 	},
273910593b5SHans Petter Selasky 	[RSU_BULK_TX_VI_VO] = {
27431d98677SRui Paulo 		.type = UE_BULK,
27531d98677SRui Paulo 		.endpoint = 0x04,
27631d98677SRui Paulo 		.direction = UE_DIR_OUT,
27731d98677SRui Paulo 		.bufsize = RSU_TXBUFSZ,
27831d98677SRui Paulo 		.flags = {
27931d98677SRui Paulo 			.ext_buffer = 1,
28031d98677SRui Paulo 			.pipe_bof = 1,
28131d98677SRui Paulo 			.force_short_xfer = 1
28231d98677SRui Paulo 		},
283910593b5SHans Petter Selasky 		.callback = rsu_bulk_tx_callback_vi_vo,
28431d98677SRui Paulo 		.timeout = RSU_TX_TIMEOUT
28531d98677SRui Paulo 	},
28631d98677SRui Paulo };
28731d98677SRui Paulo 
28831d98677SRui Paulo static int
28931d98677SRui Paulo rsu_match(device_t self)
29031d98677SRui Paulo {
29131d98677SRui Paulo 	struct usb_attach_arg *uaa = device_get_ivars(self);
29231d98677SRui Paulo 
29331d98677SRui Paulo 	if (uaa->usb_mode != USB_MODE_HOST ||
29431d98677SRui Paulo 	    uaa->info.bIfaceIndex != 0 ||
29531d98677SRui Paulo 	    uaa->info.bConfigIndex != 0)
29631d98677SRui Paulo 		return (ENXIO);
29731d98677SRui Paulo 
29831d98677SRui Paulo 	return (usbd_lookup_id_by_uaa(rsu_devs, sizeof(rsu_devs), uaa));
29931d98677SRui Paulo }
30031d98677SRui Paulo 
30131d98677SRui Paulo static int
30231d98677SRui Paulo rsu_attach(device_t self)
30331d98677SRui Paulo {
30431d98677SRui Paulo 	struct usb_attach_arg *uaa = device_get_ivars(self);
30531d98677SRui Paulo 	struct rsu_softc *sc = device_get_softc(self);
3067a79cebfSGleb Smirnoff 	struct ieee80211com *ic = &sc->sc_ic;
30731d98677SRui Paulo 	int error;
30831d98677SRui Paulo 	uint8_t iface_index, bands;
30931d98677SRui Paulo 
31031d98677SRui Paulo 	device_set_usb_desc(self);
31131d98677SRui Paulo 	sc->sc_udev = uaa->device;
31231d98677SRui Paulo 	sc->sc_dev = self;
31331d98677SRui Paulo 
31431d98677SRui Paulo 	mtx_init(&sc->sc_mtx, device_get_nameunit(self), MTX_NETWORK_LOCK,
31531d98677SRui Paulo 	    MTX_DEF);
31631d98677SRui Paulo 	TIMEOUT_TASK_INIT(taskqueue_thread, &sc->calib_task, 0,
31731d98677SRui Paulo 	    rsu_calib_task, sc);
3187a79cebfSGleb Smirnoff 	mbufq_init(&sc->sc_snd, ifqmaxlen);
31931d98677SRui Paulo 
320885476cbSHans Petter Selasky 	/* Allocate Tx/Rx buffers. */
321885476cbSHans Petter Selasky 	error = rsu_alloc_rx_list(sc);
322885476cbSHans Petter Selasky 	if (error != 0) {
323885476cbSHans Petter Selasky 		device_printf(sc->sc_dev, "could not allocate Rx buffers\n");
324885476cbSHans Petter Selasky 		goto fail_usb;
325885476cbSHans Petter Selasky 	}
326885476cbSHans Petter Selasky 
327885476cbSHans Petter Selasky 	error = rsu_alloc_tx_list(sc);
328885476cbSHans Petter Selasky 	if (error != 0) {
329885476cbSHans Petter Selasky 		device_printf(sc->sc_dev, "could not allocate Tx buffers\n");
330885476cbSHans Petter Selasky 		rsu_free_rx_list(sc);
331885476cbSHans Petter Selasky 		goto fail_usb;
332885476cbSHans Petter Selasky 	}
333885476cbSHans Petter Selasky 
33431d98677SRui Paulo 	iface_index = 0;
33531d98677SRui Paulo 	error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer,
33631d98677SRui Paulo 	    rsu_config, RSU_N_TRANSFER, sc, &sc->sc_mtx);
33731d98677SRui Paulo 	if (error) {
33831d98677SRui Paulo 		device_printf(sc->sc_dev,
33931d98677SRui Paulo 		    "could not allocate USB transfers, err=%s\n",
34031d98677SRui Paulo 		    usbd_errstr(error));
34153dfd5c1SRui Paulo 		goto fail_usb;
34231d98677SRui Paulo 	}
34331d98677SRui Paulo 	RSU_LOCK(sc);
34431d98677SRui Paulo 	/* Read chip revision. */
34531d98677SRui Paulo 	sc->cut = MS(rsu_read_4(sc, R92S_PMC_FSM), R92S_PMC_FSM_CUT);
34631d98677SRui Paulo 	if (sc->cut != 3)
34731d98677SRui Paulo 		sc->cut = (sc->cut >> 1) + 1;
34831d98677SRui Paulo 	error = rsu_read_rom(sc);
349abfa11d6SHans Petter Selasky 	RSU_UNLOCK(sc);
35031d98677SRui Paulo 	if (error != 0) {
35131d98677SRui Paulo 		device_printf(self, "could not read ROM\n");
35253dfd5c1SRui Paulo 		goto fail_rom;
35331d98677SRui Paulo 	}
3547a79cebfSGleb Smirnoff 	IEEE80211_ADDR_COPY(ic->ic_macaddr, &sc->rom[0x12]);
35531d98677SRui Paulo 	device_printf(self, "MAC/BB RTL8712 cut %d\n", sc->cut);
35631d98677SRui Paulo 
35759686fe9SGleb Smirnoff 	ic->ic_softc = sc;
358c8550c02SGleb Smirnoff 	ic->ic_name = device_get_nameunit(self);
35931d98677SRui Paulo 	ic->ic_phytype = IEEE80211_T_OFDM;	/* Not only, but not used. */
36031d98677SRui Paulo 	ic->ic_opmode = IEEE80211_M_STA;	/* Default to BSS mode. */
36131d98677SRui Paulo 
36231d98677SRui Paulo 	/* Set device capabilities. */
36331d98677SRui Paulo 	ic->ic_caps =
36431d98677SRui Paulo 	    IEEE80211_C_STA |		/* station mode */
365*4914fa0fSAdrian Chadd #if 0
36631d98677SRui Paulo 	    IEEE80211_C_BGSCAN |	/* Background scan. */
367*4914fa0fSAdrian Chadd #endif
36831d98677SRui Paulo 	    IEEE80211_C_SHPREAMBLE |	/* Short preamble supported. */
36931d98677SRui Paulo 	    IEEE80211_C_SHSLOT |	/* Short slot time supported. */
37031d98677SRui Paulo 	    IEEE80211_C_WPA;		/* WPA/RSN. */
37131d98677SRui Paulo 
37231d98677SRui Paulo #if 0
37331d98677SRui Paulo 	/* Check if HT support is present. */
37431d98677SRui Paulo 	if (usb_lookup(rsu_devs_noht, uaa->vendor, uaa->product) == NULL) {
37531d98677SRui Paulo 		/* Set HT capabilities. */
37631d98677SRui Paulo 		ic->ic_htcaps =
37731d98677SRui Paulo 		    IEEE80211_HTCAP_CBW20_40 |
37831d98677SRui Paulo 		    IEEE80211_HTCAP_DSSSCCK40;
37931d98677SRui Paulo 		/* Set supported HT rates. */
38031d98677SRui Paulo 		for (i = 0; i < 2; i++)
38131d98677SRui Paulo 			ic->ic_sup_mcs[i] = 0xff;
38231d98677SRui Paulo 	}
38331d98677SRui Paulo #endif
38431d98677SRui Paulo 
38531d98677SRui Paulo 	/* Set supported .11b and .11g rates. */
38631d98677SRui Paulo 	bands = 0;
38731d98677SRui Paulo 	setbit(&bands, IEEE80211_MODE_11B);
38831d98677SRui Paulo 	setbit(&bands, IEEE80211_MODE_11G);
38931d98677SRui Paulo 	ieee80211_init_channels(ic, NULL, &bands);
39031d98677SRui Paulo 
3917a79cebfSGleb Smirnoff 	ieee80211_ifattach(ic);
39231d98677SRui Paulo 	ic->ic_raw_xmit = rsu_raw_xmit;
39331d98677SRui Paulo 	ic->ic_scan_start = rsu_scan_start;
39431d98677SRui Paulo 	ic->ic_scan_end = rsu_scan_end;
39531d98677SRui Paulo 	ic->ic_set_channel = rsu_set_channel;
39631d98677SRui Paulo 	ic->ic_vap_create = rsu_vap_create;
39731d98677SRui Paulo 	ic->ic_vap_delete = rsu_vap_delete;
39831d98677SRui Paulo 	ic->ic_update_mcast = rsu_update_mcast;
3997a79cebfSGleb Smirnoff 	ic->ic_parent = rsu_parent;
4007a79cebfSGleb Smirnoff 	ic->ic_transmit = rsu_transmit;
40131d98677SRui Paulo 
40231d98677SRui Paulo 	ieee80211_radiotap_attach(ic, &sc->sc_txtap.wt_ihdr,
40331d98677SRui Paulo 	    sizeof(sc->sc_txtap), RSU_TX_RADIOTAP_PRESENT,
40431d98677SRui Paulo 	    &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap),
40531d98677SRui Paulo 	    RSU_RX_RADIOTAP_PRESENT);
40631d98677SRui Paulo 
40731d98677SRui Paulo 	if (bootverbose)
40831d98677SRui Paulo 		ieee80211_announce(ic);
40931d98677SRui Paulo 
41031d98677SRui Paulo 	return (0);
41131d98677SRui Paulo 
41253dfd5c1SRui Paulo fail_rom:
41353dfd5c1SRui Paulo 	usbd_transfer_unsetup(sc->sc_xfer, RSU_N_TRANSFER);
41453dfd5c1SRui Paulo fail_usb:
41553dfd5c1SRui Paulo 	mtx_destroy(&sc->sc_mtx);
41631d98677SRui Paulo 	return (ENXIO);
41731d98677SRui Paulo }
41831d98677SRui Paulo 
41931d98677SRui Paulo static int
42031d98677SRui Paulo rsu_detach(device_t self)
42131d98677SRui Paulo {
42231d98677SRui Paulo 	struct rsu_softc *sc = device_get_softc(self);
4237a79cebfSGleb Smirnoff 	struct ieee80211com *ic = &sc->sc_ic;
42431d98677SRui Paulo 
4257a79cebfSGleb Smirnoff 	RSU_LOCK(sc);
4267a79cebfSGleb Smirnoff 	rsu_stop(sc);
4277a79cebfSGleb Smirnoff 	RSU_UNLOCK(sc);
42831d98677SRui Paulo 	usbd_transfer_unsetup(sc->sc_xfer, RSU_N_TRANSFER);
42931d98677SRui Paulo 	ieee80211_ifdetach(ic);
43031d98677SRui Paulo 
43131d98677SRui Paulo 	taskqueue_drain_timeout(taskqueue_thread, &sc->calib_task);
43231d98677SRui Paulo 
43331d98677SRui Paulo 	/* Free Tx/Rx buffers. */
43431d98677SRui Paulo 	rsu_free_tx_list(sc);
43531d98677SRui Paulo 	rsu_free_rx_list(sc);
43631d98677SRui Paulo 
4377a79cebfSGleb Smirnoff 	mbufq_drain(&sc->sc_snd);
43831d98677SRui Paulo 	mtx_destroy(&sc->sc_mtx);
43931d98677SRui Paulo 
44031d98677SRui Paulo 	return (0);
44131d98677SRui Paulo }
44231d98677SRui Paulo 
44331d98677SRui Paulo static usb_error_t
44431d98677SRui Paulo rsu_do_request(struct rsu_softc *sc, struct usb_device_request *req,
44531d98677SRui Paulo     void *data)
44631d98677SRui Paulo {
44731d98677SRui Paulo 	usb_error_t err;
44831d98677SRui Paulo 	int ntries = 10;
44931d98677SRui Paulo 
45031d98677SRui Paulo 	RSU_ASSERT_LOCKED(sc);
45131d98677SRui Paulo 
45231d98677SRui Paulo 	while (ntries--) {
45331d98677SRui Paulo 		err = usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx,
45431d98677SRui Paulo 		    req, data, 0, NULL, 250 /* ms */);
4557243077cSHans Petter Selasky 		if (err == 0 || err == USB_ERR_NOT_CONFIGURED)
45631d98677SRui Paulo 			break;
45731d98677SRui Paulo 		DPRINTFN(1, "Control request failed, %s (retrying)\n",
45831d98677SRui Paulo 		    usbd_errstr(err));
45931d98677SRui Paulo 		usb_pause_mtx(&sc->sc_mtx, hz / 100);
46031d98677SRui Paulo         }
46131d98677SRui Paulo 
46231d98677SRui Paulo         return (err);
46331d98677SRui Paulo }
46431d98677SRui Paulo 
46531d98677SRui Paulo static struct ieee80211vap *
46631d98677SRui Paulo rsu_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit,
46731d98677SRui Paulo     enum ieee80211_opmode opmode, int flags,
46831d98677SRui Paulo     const uint8_t bssid[IEEE80211_ADDR_LEN],
46931d98677SRui Paulo     const uint8_t mac[IEEE80211_ADDR_LEN])
47031d98677SRui Paulo {
47131d98677SRui Paulo 	struct rsu_vap *uvp;
47231d98677SRui Paulo 	struct ieee80211vap *vap;
47331d98677SRui Paulo 
47431d98677SRui Paulo 	if (!TAILQ_EMPTY(&ic->ic_vaps))         /* only one at a time */
47531d98677SRui Paulo 		return (NULL);
47631d98677SRui Paulo 
4777a79cebfSGleb Smirnoff 	uvp =  malloc(sizeof(struct rsu_vap), M_80211_VAP, M_WAITOK | M_ZERO);
47831d98677SRui Paulo 	vap = &uvp->vap;
479bb2f69e8SHans Petter Selasky 
480bb2f69e8SHans Petter Selasky 	if (ieee80211_vap_setup(ic, vap, name, unit, opmode,
4817a79cebfSGleb Smirnoff 	    flags, bssid) != 0) {
482bb2f69e8SHans Petter Selasky 		/* out of memory */
483bb2f69e8SHans Petter Selasky 		free(uvp, M_80211_VAP);
484bb2f69e8SHans Petter Selasky 		return (NULL);
485bb2f69e8SHans Petter Selasky 	}
48631d98677SRui Paulo 
48731d98677SRui Paulo 	/* override state transition machine */
48831d98677SRui Paulo 	uvp->newstate = vap->iv_newstate;
48931d98677SRui Paulo 	vap->iv_newstate = rsu_newstate;
49031d98677SRui Paulo 
49131d98677SRui Paulo 	/* complete setup */
49231d98677SRui Paulo 	ieee80211_vap_attach(vap, ieee80211_media_change,
4937a79cebfSGleb Smirnoff 	    ieee80211_media_status, mac);
49431d98677SRui Paulo 	ic->ic_opmode = opmode;
49531d98677SRui Paulo 
49631d98677SRui Paulo 	return (vap);
49731d98677SRui Paulo }
49831d98677SRui Paulo 
49931d98677SRui Paulo static void
50031d98677SRui Paulo rsu_vap_delete(struct ieee80211vap *vap)
50131d98677SRui Paulo {
50231d98677SRui Paulo 	struct rsu_vap *uvp = RSU_VAP(vap);
50331d98677SRui Paulo 
50431d98677SRui Paulo 	ieee80211_vap_detach(vap);
50531d98677SRui Paulo 	free(uvp, M_80211_VAP);
50631d98677SRui Paulo }
50731d98677SRui Paulo 
50831d98677SRui Paulo static void
50931d98677SRui Paulo rsu_scan_start(struct ieee80211com *ic)
51031d98677SRui Paulo {
511d3fdd08cSAdrian Chadd 	struct rsu_softc *sc = ic->ic_softc;
5127a79cebfSGleb Smirnoff 	int error;
51331d98677SRui Paulo 
51431d98677SRui Paulo 	/* Scanning is done by the firmware. */
51531d98677SRui Paulo 	RSU_LOCK(sc);
51631d98677SRui Paulo 	error = rsu_site_survey(sc, TAILQ_FIRST(&ic->ic_vaps));
51731d98677SRui Paulo 	RSU_UNLOCK(sc);
51831d98677SRui Paulo 	if (error != 0)
51931d98677SRui Paulo 		device_printf(sc->sc_dev,
52031d98677SRui Paulo 		    "could not send site survey command\n");
52131d98677SRui Paulo }
52231d98677SRui Paulo 
52331d98677SRui Paulo static void
52431d98677SRui Paulo rsu_scan_end(struct ieee80211com *ic)
52531d98677SRui Paulo {
52631d98677SRui Paulo 	/* Nothing to do here. */
52731d98677SRui Paulo }
52831d98677SRui Paulo 
52931d98677SRui Paulo static void
53031d98677SRui Paulo rsu_set_channel(struct ieee80211com *ic __unused)
53131d98677SRui Paulo {
53231d98677SRui Paulo 	/* We are unable to switch channels, yet. */
53331d98677SRui Paulo }
53431d98677SRui Paulo 
53531d98677SRui Paulo static void
536272f6adeSGleb Smirnoff rsu_update_mcast(struct ieee80211com *ic)
53731d98677SRui Paulo {
53831d98677SRui Paulo         /* XXX do nothing?  */
53931d98677SRui Paulo }
54031d98677SRui Paulo 
54131d98677SRui Paulo static int
54231d98677SRui Paulo rsu_alloc_list(struct rsu_softc *sc, struct rsu_data data[],
54331d98677SRui Paulo     int ndata, int maxsz)
54431d98677SRui Paulo {
54531d98677SRui Paulo 	int i, error;
54631d98677SRui Paulo 
54731d98677SRui Paulo 	for (i = 0; i < ndata; i++) {
54831d98677SRui Paulo 		struct rsu_data *dp = &data[i];
54931d98677SRui Paulo 		dp->sc = sc;
55031d98677SRui Paulo 		dp->m = NULL;
55131d98677SRui Paulo 		dp->buf = malloc(maxsz, M_USBDEV, M_NOWAIT);
55231d98677SRui Paulo 		if (dp->buf == NULL) {
55331d98677SRui Paulo 			device_printf(sc->sc_dev,
55431d98677SRui Paulo 			    "could not allocate buffer\n");
55531d98677SRui Paulo 			error = ENOMEM;
55631d98677SRui Paulo 			goto fail;
55731d98677SRui Paulo 		}
55831d98677SRui Paulo 		dp->ni = NULL;
55931d98677SRui Paulo 	}
56031d98677SRui Paulo 
56131d98677SRui Paulo 	return (0);
56231d98677SRui Paulo fail:
56331d98677SRui Paulo 	rsu_free_list(sc, data, ndata);
56431d98677SRui Paulo 	return (error);
56531d98677SRui Paulo }
56631d98677SRui Paulo 
56731d98677SRui Paulo static int
56831d98677SRui Paulo rsu_alloc_rx_list(struct rsu_softc *sc)
56931d98677SRui Paulo {
57031d98677SRui Paulo         int error, i;
57131d98677SRui Paulo 
57231d98677SRui Paulo 	error = rsu_alloc_list(sc, sc->sc_rx, RSU_RX_LIST_COUNT,
57331d98677SRui Paulo 	    RSU_RXBUFSZ);
57431d98677SRui Paulo 	if (error != 0)
57531d98677SRui Paulo 		return (error);
57631d98677SRui Paulo 
57731d98677SRui Paulo 	STAILQ_INIT(&sc->sc_rx_active);
57831d98677SRui Paulo 	STAILQ_INIT(&sc->sc_rx_inactive);
57931d98677SRui Paulo 
58031d98677SRui Paulo 	for (i = 0; i < RSU_RX_LIST_COUNT; i++)
58131d98677SRui Paulo 		STAILQ_INSERT_HEAD(&sc->sc_rx_inactive, &sc->sc_rx[i], next);
58231d98677SRui Paulo 
58331d98677SRui Paulo 	return (0);
58431d98677SRui Paulo }
58531d98677SRui Paulo 
58631d98677SRui Paulo static int
58731d98677SRui Paulo rsu_alloc_tx_list(struct rsu_softc *sc)
58831d98677SRui Paulo {
58931d98677SRui Paulo 	int error, i;
59031d98677SRui Paulo 
59131d98677SRui Paulo 	error = rsu_alloc_list(sc, sc->sc_tx, RSU_TX_LIST_COUNT,
59231d98677SRui Paulo 	    RSU_TXBUFSZ);
59331d98677SRui Paulo 	if (error != 0)
59431d98677SRui Paulo 		return (error);
59531d98677SRui Paulo 
59631d98677SRui Paulo 	STAILQ_INIT(&sc->sc_tx_inactive);
597400b4e53SHans Petter Selasky 
598910593b5SHans Petter Selasky 	for (i = 0; i != RSU_N_TRANSFER; i++) {
599400b4e53SHans Petter Selasky 		STAILQ_INIT(&sc->sc_tx_active[i]);
600400b4e53SHans Petter Selasky 		STAILQ_INIT(&sc->sc_tx_pending[i]);
601400b4e53SHans Petter Selasky 	}
60231d98677SRui Paulo 
60331d98677SRui Paulo 	for (i = 0; i < RSU_TX_LIST_COUNT; i++) {
60431d98677SRui Paulo 		STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, &sc->sc_tx[i], next);
60531d98677SRui Paulo 	}
60631d98677SRui Paulo 
60731d98677SRui Paulo 	return (0);
60831d98677SRui Paulo }
60931d98677SRui Paulo 
61031d98677SRui Paulo static void
61131d98677SRui Paulo rsu_free_tx_list(struct rsu_softc *sc)
61231d98677SRui Paulo {
613885476cbSHans Petter Selasky 	int i;
614885476cbSHans Petter Selasky 
615885476cbSHans Petter Selasky 	/* prevent further allocations from TX list(s) */
616885476cbSHans Petter Selasky 	STAILQ_INIT(&sc->sc_tx_inactive);
617885476cbSHans Petter Selasky 
618910593b5SHans Petter Selasky 	for (i = 0; i != RSU_N_TRANSFER; i++) {
619885476cbSHans Petter Selasky 		STAILQ_INIT(&sc->sc_tx_active[i]);
620885476cbSHans Petter Selasky 		STAILQ_INIT(&sc->sc_tx_pending[i]);
621885476cbSHans Petter Selasky 	}
622885476cbSHans Petter Selasky 
62331d98677SRui Paulo 	rsu_free_list(sc, sc->sc_tx, RSU_TX_LIST_COUNT);
62431d98677SRui Paulo }
62531d98677SRui Paulo 
62631d98677SRui Paulo static void
62731d98677SRui Paulo rsu_free_rx_list(struct rsu_softc *sc)
62831d98677SRui Paulo {
629885476cbSHans Petter Selasky 	/* prevent further allocations from RX list(s) */
630885476cbSHans Petter Selasky 	STAILQ_INIT(&sc->sc_rx_inactive);
631885476cbSHans Petter Selasky 	STAILQ_INIT(&sc->sc_rx_active);
632885476cbSHans Petter Selasky 
63331d98677SRui Paulo 	rsu_free_list(sc, sc->sc_rx, RSU_RX_LIST_COUNT);
63431d98677SRui Paulo }
63531d98677SRui Paulo 
63631d98677SRui Paulo static void
63731d98677SRui Paulo rsu_free_list(struct rsu_softc *sc, struct rsu_data data[], int ndata)
63831d98677SRui Paulo {
63931d98677SRui Paulo 	int i;
64031d98677SRui Paulo 
64131d98677SRui Paulo 	for (i = 0; i < ndata; i++) {
64231d98677SRui Paulo 		struct rsu_data *dp = &data[i];
64331d98677SRui Paulo 
64431d98677SRui Paulo 		if (dp->buf != NULL) {
64531d98677SRui Paulo 			free(dp->buf, M_USBDEV);
64631d98677SRui Paulo 			dp->buf = NULL;
64731d98677SRui Paulo 		}
64831d98677SRui Paulo 		if (dp->ni != NULL) {
64931d98677SRui Paulo 			ieee80211_free_node(dp->ni);
65031d98677SRui Paulo 			dp->ni = NULL;
65131d98677SRui Paulo 		}
65231d98677SRui Paulo 	}
65331d98677SRui Paulo }
65431d98677SRui Paulo 
65531d98677SRui Paulo static struct rsu_data *
65631d98677SRui Paulo _rsu_getbuf(struct rsu_softc *sc)
65731d98677SRui Paulo {
65831d98677SRui Paulo 	struct rsu_data *bf;
65931d98677SRui Paulo 
66031d98677SRui Paulo 	bf = STAILQ_FIRST(&sc->sc_tx_inactive);
66131d98677SRui Paulo 	if (bf != NULL)
66231d98677SRui Paulo 		STAILQ_REMOVE_HEAD(&sc->sc_tx_inactive, next);
66331d98677SRui Paulo 	else
66431d98677SRui Paulo 		bf = NULL;
66531d98677SRui Paulo 	if (bf == NULL)
66631d98677SRui Paulo 		DPRINTF("out of xmit buffers\n");
66731d98677SRui Paulo         return (bf);
66831d98677SRui Paulo }
66931d98677SRui Paulo 
67031d98677SRui Paulo static struct rsu_data *
67131d98677SRui Paulo rsu_getbuf(struct rsu_softc *sc)
67231d98677SRui Paulo {
67331d98677SRui Paulo 	struct rsu_data *bf;
67431d98677SRui Paulo 
67531d98677SRui Paulo 	RSU_ASSERT_LOCKED(sc);
67631d98677SRui Paulo 
67731d98677SRui Paulo 	bf = _rsu_getbuf(sc);
6787a79cebfSGleb Smirnoff 	if (bf == NULL)
67931d98677SRui Paulo 		DPRINTF("stop queue\n");
68031d98677SRui Paulo 	return (bf);
68131d98677SRui Paulo }
68231d98677SRui Paulo 
68331d98677SRui Paulo static int
68431d98677SRui Paulo rsu_write_region_1(struct rsu_softc *sc, uint16_t addr, uint8_t *buf,
68531d98677SRui Paulo     int len)
68631d98677SRui Paulo {
68731d98677SRui Paulo 	usb_device_request_t req;
68831d98677SRui Paulo 
68931d98677SRui Paulo 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
69031d98677SRui Paulo 	req.bRequest = R92S_REQ_REGS;
69131d98677SRui Paulo 	USETW(req.wValue, addr);
69231d98677SRui Paulo 	USETW(req.wIndex, 0);
69331d98677SRui Paulo 	USETW(req.wLength, len);
69431d98677SRui Paulo 
69531d98677SRui Paulo 	return (rsu_do_request(sc, &req, buf));
69631d98677SRui Paulo }
69731d98677SRui Paulo 
69831d98677SRui Paulo static void
69931d98677SRui Paulo rsu_write_1(struct rsu_softc *sc, uint16_t addr, uint8_t val)
70031d98677SRui Paulo {
70131d98677SRui Paulo 	rsu_write_region_1(sc, addr, &val, 1);
70231d98677SRui Paulo }
70331d98677SRui Paulo 
70431d98677SRui Paulo static void
70531d98677SRui Paulo rsu_write_2(struct rsu_softc *sc, uint16_t addr, uint16_t val)
70631d98677SRui Paulo {
70731d98677SRui Paulo 	val = htole16(val);
70831d98677SRui Paulo 	rsu_write_region_1(sc, addr, (uint8_t *)&val, 2);
70931d98677SRui Paulo }
71031d98677SRui Paulo 
71131d98677SRui Paulo static void
71231d98677SRui Paulo rsu_write_4(struct rsu_softc *sc, uint16_t addr, uint32_t val)
71331d98677SRui Paulo {
71431d98677SRui Paulo 	val = htole32(val);
71531d98677SRui Paulo 	rsu_write_region_1(sc, addr, (uint8_t *)&val, 4);
71631d98677SRui Paulo }
71731d98677SRui Paulo 
71831d98677SRui Paulo static int
71931d98677SRui Paulo rsu_read_region_1(struct rsu_softc *sc, uint16_t addr, uint8_t *buf,
72031d98677SRui Paulo     int len)
72131d98677SRui Paulo {
72231d98677SRui Paulo 	usb_device_request_t req;
72331d98677SRui Paulo 
72431d98677SRui Paulo 	req.bmRequestType = UT_READ_VENDOR_DEVICE;
72531d98677SRui Paulo 	req.bRequest = R92S_REQ_REGS;
72631d98677SRui Paulo 	USETW(req.wValue, addr);
72731d98677SRui Paulo 	USETW(req.wIndex, 0);
72831d98677SRui Paulo 	USETW(req.wLength, len);
72931d98677SRui Paulo 
73031d98677SRui Paulo 	return (rsu_do_request(sc, &req, buf));
73131d98677SRui Paulo }
73231d98677SRui Paulo 
73331d98677SRui Paulo static uint8_t
73431d98677SRui Paulo rsu_read_1(struct rsu_softc *sc, uint16_t addr)
73531d98677SRui Paulo {
73631d98677SRui Paulo 	uint8_t val;
73731d98677SRui Paulo 
73831d98677SRui Paulo 	if (rsu_read_region_1(sc, addr, &val, 1) != 0)
73931d98677SRui Paulo 		return (0xff);
74031d98677SRui Paulo 	return (val);
74131d98677SRui Paulo }
74231d98677SRui Paulo 
74331d98677SRui Paulo static uint16_t
74431d98677SRui Paulo rsu_read_2(struct rsu_softc *sc, uint16_t addr)
74531d98677SRui Paulo {
74631d98677SRui Paulo 	uint16_t val;
74731d98677SRui Paulo 
74831d98677SRui Paulo 	if (rsu_read_region_1(sc, addr, (uint8_t *)&val, 2) != 0)
74931d98677SRui Paulo 		return (0xffff);
75031d98677SRui Paulo 	return (le16toh(val));
75131d98677SRui Paulo }
75231d98677SRui Paulo 
75331d98677SRui Paulo static uint32_t
75431d98677SRui Paulo rsu_read_4(struct rsu_softc *sc, uint16_t addr)
75531d98677SRui Paulo {
75631d98677SRui Paulo 	uint32_t val;
75731d98677SRui Paulo 
75831d98677SRui Paulo 	if (rsu_read_region_1(sc, addr, (uint8_t *)&val, 4) != 0)
75931d98677SRui Paulo 		return (0xffffffff);
76031d98677SRui Paulo 	return (le32toh(val));
76131d98677SRui Paulo }
76231d98677SRui Paulo 
76331d98677SRui Paulo static int
76431d98677SRui Paulo rsu_fw_iocmd(struct rsu_softc *sc, uint32_t iocmd)
76531d98677SRui Paulo {
76631d98677SRui Paulo 	int ntries;
76731d98677SRui Paulo 
76831d98677SRui Paulo 	rsu_write_4(sc, R92S_IOCMD_CTRL, iocmd);
769b41381aeSHans Petter Selasky 	rsu_ms_delay(sc);
77031d98677SRui Paulo 	for (ntries = 0; ntries < 50; ntries++) {
77131d98677SRui Paulo 		if (rsu_read_4(sc, R92S_IOCMD_CTRL) == 0)
77231d98677SRui Paulo 			return (0);
773b41381aeSHans Petter Selasky 		rsu_ms_delay(sc);
77431d98677SRui Paulo 	}
77531d98677SRui Paulo 	return (ETIMEDOUT);
77631d98677SRui Paulo }
77731d98677SRui Paulo 
77831d98677SRui Paulo static uint8_t
77931d98677SRui Paulo rsu_efuse_read_1(struct rsu_softc *sc, uint16_t addr)
78031d98677SRui Paulo {
78131d98677SRui Paulo 	uint32_t reg;
78231d98677SRui Paulo 	int ntries;
78331d98677SRui Paulo 
78431d98677SRui Paulo 	reg = rsu_read_4(sc, R92S_EFUSE_CTRL);
78531d98677SRui Paulo 	reg = RW(reg, R92S_EFUSE_CTRL_ADDR, addr);
78631d98677SRui Paulo 	reg &= ~R92S_EFUSE_CTRL_VALID;
78731d98677SRui Paulo 	rsu_write_4(sc, R92S_EFUSE_CTRL, reg);
78831d98677SRui Paulo 	/* Wait for read operation to complete. */
78931d98677SRui Paulo 	for (ntries = 0; ntries < 100; ntries++) {
79031d98677SRui Paulo 		reg = rsu_read_4(sc, R92S_EFUSE_CTRL);
79131d98677SRui Paulo 		if (reg & R92S_EFUSE_CTRL_VALID)
79231d98677SRui Paulo 			return (MS(reg, R92S_EFUSE_CTRL_DATA));
793b41381aeSHans Petter Selasky 		rsu_ms_delay(sc);
79431d98677SRui Paulo 	}
79531d98677SRui Paulo 	device_printf(sc->sc_dev,
79631d98677SRui Paulo 	    "could not read efuse byte at address 0x%x\n", addr);
79731d98677SRui Paulo 	return (0xff);
79831d98677SRui Paulo }
79931d98677SRui Paulo 
80031d98677SRui Paulo static int
80131d98677SRui Paulo rsu_read_rom(struct rsu_softc *sc)
80231d98677SRui Paulo {
80331d98677SRui Paulo 	uint8_t *rom = sc->rom;
80431d98677SRui Paulo 	uint16_t addr = 0;
80531d98677SRui Paulo 	uint32_t reg;
80631d98677SRui Paulo 	uint8_t off, msk;
80731d98677SRui Paulo 	int i;
80831d98677SRui Paulo 
80931d98677SRui Paulo 	/* Make sure that ROM type is eFuse and that autoload succeeded. */
81031d98677SRui Paulo 	reg = rsu_read_1(sc, R92S_EE_9346CR);
81131d98677SRui Paulo 	if ((reg & (R92S_9356SEL | R92S_EEPROM_EN)) != R92S_EEPROM_EN)
81231d98677SRui Paulo 		return (EIO);
81331d98677SRui Paulo 
81431d98677SRui Paulo 	/* Turn on 2.5V to prevent eFuse leakage. */
81531d98677SRui Paulo 	reg = rsu_read_1(sc, R92S_EFUSE_TEST + 3);
81631d98677SRui Paulo 	rsu_write_1(sc, R92S_EFUSE_TEST + 3, reg | 0x80);
817b41381aeSHans Petter Selasky 	rsu_ms_delay(sc);
81831d98677SRui Paulo 	rsu_write_1(sc, R92S_EFUSE_TEST + 3, reg & ~0x80);
81931d98677SRui Paulo 
82031d98677SRui Paulo 	/* Read full ROM image. */
82131d98677SRui Paulo 	memset(&sc->rom, 0xff, sizeof(sc->rom));
82231d98677SRui Paulo 	while (addr < 512) {
82331d98677SRui Paulo 		reg = rsu_efuse_read_1(sc, addr);
82431d98677SRui Paulo 		if (reg == 0xff)
82531d98677SRui Paulo 			break;
82631d98677SRui Paulo 		addr++;
82731d98677SRui Paulo 		off = reg >> 4;
82831d98677SRui Paulo 		msk = reg & 0xf;
82931d98677SRui Paulo 		for (i = 0; i < 4; i++) {
83031d98677SRui Paulo 			if (msk & (1 << i))
83131d98677SRui Paulo 				continue;
83231d98677SRui Paulo 			rom[off * 8 + i * 2 + 0] =
83331d98677SRui Paulo 			    rsu_efuse_read_1(sc, addr);
83431d98677SRui Paulo 			addr++;
83531d98677SRui Paulo 			rom[off * 8 + i * 2 + 1] =
83631d98677SRui Paulo 			    rsu_efuse_read_1(sc, addr);
83731d98677SRui Paulo 			addr++;
83831d98677SRui Paulo 		}
83931d98677SRui Paulo 	}
84031d98677SRui Paulo #ifdef USB_DEBUG
84131d98677SRui Paulo 	if (rsu_debug >= 5) {
84231d98677SRui Paulo 		/* Dump ROM content. */
84331d98677SRui Paulo 		printf("\n");
84431d98677SRui Paulo 		for (i = 0; i < sizeof(sc->rom); i++)
84531d98677SRui Paulo 			printf("%02x:", rom[i]);
84631d98677SRui Paulo 		printf("\n");
84731d98677SRui Paulo 	}
84831d98677SRui Paulo #endif
84931d98677SRui Paulo 	return (0);
85031d98677SRui Paulo }
85131d98677SRui Paulo 
85231d98677SRui Paulo static int
85331d98677SRui Paulo rsu_fw_cmd(struct rsu_softc *sc, uint8_t code, void *buf, int len)
85431d98677SRui Paulo {
855910593b5SHans Petter Selasky 	const uint8_t which = rsu_wme_ac_xfer_map[WME_AC_VO];
85631d98677SRui Paulo 	struct rsu_data *data;
85731d98677SRui Paulo 	struct r92s_tx_desc *txd;
85831d98677SRui Paulo 	struct r92s_fw_cmd_hdr *cmd;
859400b4e53SHans Petter Selasky 	int cmdsz;
860400b4e53SHans Petter Selasky 	int xferlen;
86131d98677SRui Paulo 
86231d98677SRui Paulo 	data = rsu_getbuf(sc);
86331d98677SRui Paulo 	if (data == NULL)
86431d98677SRui Paulo 		return (ENOMEM);
86531d98677SRui Paulo 
86631d98677SRui Paulo 	/* Round-up command length to a multiple of 8 bytes. */
86731d98677SRui Paulo 	cmdsz = (len + 7) & ~7;
86831d98677SRui Paulo 
86931d98677SRui Paulo 	xferlen = sizeof(*txd) + sizeof(*cmd) + cmdsz;
87031d98677SRui Paulo 	KASSERT(xferlen <= RSU_TXBUFSZ, ("%s: invalid length", __func__));
87131d98677SRui Paulo 	memset(data->buf, 0, xferlen);
87231d98677SRui Paulo 
87331d98677SRui Paulo 	/* Setup Tx descriptor. */
87431d98677SRui Paulo 	txd = (struct r92s_tx_desc *)data->buf;
87531d98677SRui Paulo 	txd->txdw0 = htole32(
87631d98677SRui Paulo 	    SM(R92S_TXDW0_OFFSET, sizeof(*txd)) |
87731d98677SRui Paulo 	    SM(R92S_TXDW0_PKTLEN, sizeof(*cmd) + cmdsz) |
87831d98677SRui Paulo 	    R92S_TXDW0_OWN | R92S_TXDW0_FSG | R92S_TXDW0_LSG);
87931d98677SRui Paulo 	txd->txdw1 = htole32(SM(R92S_TXDW1_QSEL, R92S_TXDW1_QSEL_H2C));
88031d98677SRui Paulo 
88131d98677SRui Paulo 	/* Setup command header. */
88231d98677SRui Paulo 	cmd = (struct r92s_fw_cmd_hdr *)&txd[1];
88331d98677SRui Paulo 	cmd->len = htole16(cmdsz);
88431d98677SRui Paulo 	cmd->code = code;
88531d98677SRui Paulo 	cmd->seq = sc->cmd_seq;
88631d98677SRui Paulo 	sc->cmd_seq = (sc->cmd_seq + 1) & 0x7f;
88731d98677SRui Paulo 
88831d98677SRui Paulo 	/* Copy command payload. */
88931d98677SRui Paulo 	memcpy(&cmd[1], buf, len);
89031d98677SRui Paulo 
891*4914fa0fSAdrian Chadd 	RSU_DPRINTF(sc, RSU_DEBUG_TX,
892*4914fa0fSAdrian Chadd 	    "%s: Tx cmd code=0x%x len=0x%x\n",
893*4914fa0fSAdrian Chadd 	    __func__, code, cmdsz);
89431d98677SRui Paulo 	data->buflen = xferlen;
895400b4e53SHans Petter Selasky 	STAILQ_INSERT_TAIL(&sc->sc_tx_pending[which], data, next);
896910593b5SHans Petter Selasky 	usbd_transfer_start(sc->sc_xfer[which]);
89731d98677SRui Paulo 
89831d98677SRui Paulo 	return (0);
89931d98677SRui Paulo }
90031d98677SRui Paulo 
90131d98677SRui Paulo /* ARGSUSED */
90231d98677SRui Paulo static void
90331d98677SRui Paulo rsu_calib_task(void *arg, int pending __unused)
90431d98677SRui Paulo {
90531d98677SRui Paulo 	struct rsu_softc *sc = arg;
90631d98677SRui Paulo 	uint32_t reg;
90731d98677SRui Paulo 
908*4914fa0fSAdrian Chadd 	RSU_DPRINTF(sc, RSU_DEBUG_CALIB, "%s: running calibration task\n",
909*4914fa0fSAdrian Chadd 	    __func__);
910910593b5SHans Petter Selasky 
91131d98677SRui Paulo 	RSU_LOCK(sc);
91231d98677SRui Paulo #ifdef notyet
91331d98677SRui Paulo 	/* Read WPS PBC status. */
91431d98677SRui Paulo 	rsu_write_1(sc, R92S_MAC_PINMUX_CTRL,
91531d98677SRui Paulo 	    R92S_GPIOMUX_EN | SM(R92S_GPIOSEL_GPIO, R92S_GPIOSEL_GPIO_JTAG));
91631d98677SRui Paulo 	rsu_write_1(sc, R92S_GPIO_IO_SEL,
91731d98677SRui Paulo 	    rsu_read_1(sc, R92S_GPIO_IO_SEL) & ~R92S_GPIO_WPS);
91831d98677SRui Paulo 	reg = rsu_read_1(sc, R92S_GPIO_CTRL);
91931d98677SRui Paulo 	if (reg != 0xff && (reg & R92S_GPIO_WPS))
92031d98677SRui Paulo 		DPRINTF(("WPS PBC is pushed\n"));
92131d98677SRui Paulo #endif
92231d98677SRui Paulo 	/* Read current signal level. */
92331d98677SRui Paulo 	if (rsu_fw_iocmd(sc, 0xf4000001) == 0) {
92431d98677SRui Paulo 		reg = rsu_read_4(sc, R92S_IOCMD_DATA);
925*4914fa0fSAdrian Chadd 		RSU_DPRINTF(sc, RSU_DEBUG_CALIB, "%s: RSSI=%d%%\n",
926*4914fa0fSAdrian Chadd 		    __func__, reg >> 4);
92731d98677SRui Paulo 	}
928910593b5SHans Petter Selasky 	if (sc->sc_calibrating)
929910593b5SHans Petter Selasky 		taskqueue_enqueue_timeout(taskqueue_thread, &sc->calib_task, hz);
93031d98677SRui Paulo 	RSU_UNLOCK(sc);
93131d98677SRui Paulo }
93231d98677SRui Paulo 
93331d98677SRui Paulo static int
93431d98677SRui Paulo rsu_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
93531d98677SRui Paulo {
93631d98677SRui Paulo 	struct rsu_vap *uvp = RSU_VAP(vap);
93731d98677SRui Paulo 	struct ieee80211com *ic = vap->iv_ic;
938d3fdd08cSAdrian Chadd 	struct rsu_softc *sc = ic->ic_softc;
93931d98677SRui Paulo 	struct ieee80211_node *ni;
94031d98677SRui Paulo 	struct ieee80211_rateset *rs;
94131d98677SRui Paulo 	enum ieee80211_state ostate;
94231d98677SRui Paulo 	int error, startcal = 0;
94331d98677SRui Paulo 
94431d98677SRui Paulo 	ostate = vap->iv_state;
945*4914fa0fSAdrian Chadd 	RSU_DPRINTF(sc, RSU_DEBUG_STATE, "%s: %s -> %s\n",
946*4914fa0fSAdrian Chadd 	    __func__,
947*4914fa0fSAdrian Chadd 	    ieee80211_state_name[ostate],
94831d98677SRui Paulo 	    ieee80211_state_name[nstate]);
94931d98677SRui Paulo 
95031d98677SRui Paulo 	IEEE80211_UNLOCK(ic);
95131d98677SRui Paulo 	if (ostate == IEEE80211_S_RUN) {
95231d98677SRui Paulo 		RSU_LOCK(sc);
95331d98677SRui Paulo 		/* Stop calibration. */
95431d98677SRui Paulo 		sc->sc_calibrating = 0;
95531d98677SRui Paulo 		RSU_UNLOCK(sc);
95631d98677SRui Paulo 		taskqueue_drain_timeout(taskqueue_thread, &sc->calib_task);
95731d98677SRui Paulo 		/* Disassociate from our current BSS. */
95831d98677SRui Paulo 		RSU_LOCK(sc);
95931d98677SRui Paulo 		rsu_disconnect(sc);
96031d98677SRui Paulo 	} else
96131d98677SRui Paulo 		RSU_LOCK(sc);
96231d98677SRui Paulo 	switch (nstate) {
96331d98677SRui Paulo 	case IEEE80211_S_INIT:
96431d98677SRui Paulo 		break;
96531d98677SRui Paulo 	case IEEE80211_S_AUTH:
96631d98677SRui Paulo 		ni = ieee80211_ref_node(vap->iv_bss);
96731d98677SRui Paulo 		error = rsu_join_bss(sc, ni);
96831d98677SRui Paulo 		ieee80211_free_node(ni);
96931d98677SRui Paulo 		if (error != 0) {
97031d98677SRui Paulo 			device_printf(sc->sc_dev,
97131d98677SRui Paulo 			    "could not send join command\n");
97231d98677SRui Paulo 		}
97331d98677SRui Paulo 		break;
97431d98677SRui Paulo 	case IEEE80211_S_RUN:
97531d98677SRui Paulo 		ni = ieee80211_ref_node(vap->iv_bss);
97631d98677SRui Paulo 		rs = &ni->ni_rates;
97731d98677SRui Paulo 		/* Indicate highest supported rate. */
97831d98677SRui Paulo 		ni->ni_txrate = rs->rs_rates[rs->rs_nrates - 1];
97931d98677SRui Paulo 		ieee80211_free_node(ni);
98031d98677SRui Paulo 		startcal = 1;
98131d98677SRui Paulo 		break;
98231d98677SRui Paulo 	default:
98331d98677SRui Paulo 		break;
98431d98677SRui Paulo 	}
98531d98677SRui Paulo 	sc->sc_calibrating = 1;
986910593b5SHans Petter Selasky 	/* Start periodic calibration. */
987910593b5SHans Petter Selasky 	taskqueue_enqueue_timeout(taskqueue_thread, &sc->calib_task, hz);
98831d98677SRui Paulo 	RSU_UNLOCK(sc);
98931d98677SRui Paulo 	IEEE80211_LOCK(ic);
99031d98677SRui Paulo 	return (uvp->newstate(vap, nstate, arg));
99131d98677SRui Paulo }
99231d98677SRui Paulo 
99331d98677SRui Paulo #ifdef notyet
99431d98677SRui Paulo static void
99531d98677SRui Paulo rsu_set_key(struct rsu_softc *sc, const struct ieee80211_key *k)
99631d98677SRui Paulo {
99731d98677SRui Paulo 	struct r92s_fw_cmd_set_key key;
99831d98677SRui Paulo 
99931d98677SRui Paulo 	memset(&key, 0, sizeof(key));
100031d98677SRui Paulo 	/* Map net80211 cipher to HW crypto algorithm. */
100131d98677SRui Paulo 	switch (k->wk_cipher->ic_cipher) {
100231d98677SRui Paulo 	case IEEE80211_CIPHER_WEP:
100331d98677SRui Paulo 		if (k->wk_keylen < 8)
100431d98677SRui Paulo 			key.algo = R92S_KEY_ALGO_WEP40;
100531d98677SRui Paulo 		else
100631d98677SRui Paulo 			key.algo = R92S_KEY_ALGO_WEP104;
100731d98677SRui Paulo 		break;
100831d98677SRui Paulo 	case IEEE80211_CIPHER_TKIP:
100931d98677SRui Paulo 		key.algo = R92S_KEY_ALGO_TKIP;
101031d98677SRui Paulo 		break;
101131d98677SRui Paulo 	case IEEE80211_CIPHER_AES_CCM:
101231d98677SRui Paulo 		key.algo = R92S_KEY_ALGO_AES;
101331d98677SRui Paulo 		break;
101431d98677SRui Paulo 	default:
101531d98677SRui Paulo 		return;
101631d98677SRui Paulo 	}
101731d98677SRui Paulo 	key.id = k->wk_keyix;
101831d98677SRui Paulo 	key.grpkey = (k->wk_flags & IEEE80211_KEY_GROUP) != 0;
101931d98677SRui Paulo 	memcpy(key.key, k->wk_key, MIN(k->wk_keylen, sizeof(key.key)));
102031d98677SRui Paulo 	(void)rsu_fw_cmd(sc, R92S_CMD_SET_KEY, &key, sizeof(key));
102131d98677SRui Paulo }
102231d98677SRui Paulo 
102331d98677SRui Paulo static void
102431d98677SRui Paulo rsu_delete_key(struct rsu_softc *sc, const struct ieee80211_key *k)
102531d98677SRui Paulo {
102631d98677SRui Paulo 	struct r92s_fw_cmd_set_key key;
102731d98677SRui Paulo 
102831d98677SRui Paulo 	memset(&key, 0, sizeof(key));
102931d98677SRui Paulo 	key.id = k->wk_keyix;
103031d98677SRui Paulo 	(void)rsu_fw_cmd(sc, R92S_CMD_SET_KEY, &key, sizeof(key));
103131d98677SRui Paulo }
103231d98677SRui Paulo #endif
103331d98677SRui Paulo 
103431d98677SRui Paulo static int
103531d98677SRui Paulo rsu_site_survey(struct rsu_softc *sc, struct ieee80211vap *vap)
103631d98677SRui Paulo {
103731d98677SRui Paulo 	struct r92s_fw_cmd_sitesurvey cmd;
10387a79cebfSGleb Smirnoff 	struct ieee80211com *ic = &sc->sc_ic;
103931d98677SRui Paulo 
104031d98677SRui Paulo 	memset(&cmd, 0, sizeof(cmd));
10417a79cebfSGleb Smirnoff 	if ((ic->ic_flags & IEEE80211_F_ASCAN) || sc->sc_scan_pass == 1)
104231d98677SRui Paulo 		cmd.active = htole32(1);
104331d98677SRui Paulo 	cmd.limit = htole32(48);
10447a79cebfSGleb Smirnoff 	if (sc->sc_scan_pass == 1 && vap->iv_des_nssid > 0) {
104531d98677SRui Paulo 		/* Do a directed scan for second pass. */
104631d98677SRui Paulo 		cmd.ssidlen = htole32(vap->iv_des_ssid[0].len);
104731d98677SRui Paulo 		memcpy(cmd.ssid, vap->iv_des_ssid[0].ssid,
104831d98677SRui Paulo 		    vap->iv_des_ssid[0].len);
104931d98677SRui Paulo 
105031d98677SRui Paulo 	}
10517a79cebfSGleb Smirnoff 	DPRINTF("sending site survey command, pass=%d\n", sc->sc_scan_pass);
105231d98677SRui Paulo 	return (rsu_fw_cmd(sc, R92S_CMD_SITE_SURVEY, &cmd, sizeof(cmd)));
105331d98677SRui Paulo }
105431d98677SRui Paulo 
105531d98677SRui Paulo static int
105631d98677SRui Paulo rsu_join_bss(struct rsu_softc *sc, struct ieee80211_node *ni)
105731d98677SRui Paulo {
10587a79cebfSGleb Smirnoff 	struct ieee80211com *ic = &sc->sc_ic;
105931d98677SRui Paulo 	struct ieee80211vap *vap = ni->ni_vap;
106031d98677SRui Paulo 	struct ndis_wlan_bssid_ex *bss;
106131d98677SRui Paulo 	struct ndis_802_11_fixed_ies *fixed;
106231d98677SRui Paulo 	struct r92s_fw_cmd_auth auth;
1063bb03cd6fSHans Petter Selasky 	uint8_t buf[sizeof(*bss) + 128] __aligned(4);
1064bb03cd6fSHans Petter Selasky 	uint8_t *frm;
106531d98677SRui Paulo 	uint8_t opmode;
106631d98677SRui Paulo 	int error;
106731d98677SRui Paulo 
106831d98677SRui Paulo 	/* Let the FW decide the opmode based on the capinfo field. */
106931d98677SRui Paulo 	opmode = NDIS802_11AUTOUNKNOWN;
1070*4914fa0fSAdrian Chadd 	RSU_DPRINTF(sc, RSU_DEBUG_RESET,
1071*4914fa0fSAdrian Chadd 	    "%s: setting operating mode to %d\n",
1072*4914fa0fSAdrian Chadd 	    __func__, opmode);
107331d98677SRui Paulo 	error = rsu_fw_cmd(sc, R92S_CMD_SET_OPMODE, &opmode, sizeof(opmode));
107431d98677SRui Paulo 	if (error != 0)
107531d98677SRui Paulo 		return (error);
107631d98677SRui Paulo 
107731d98677SRui Paulo 	memset(&auth, 0, sizeof(auth));
107831d98677SRui Paulo 	if (vap->iv_flags & IEEE80211_F_WPA) {
107931d98677SRui Paulo 		auth.mode = R92S_AUTHMODE_WPA;
1080bb03cd6fSHans Petter Selasky 		auth.dot1x = (ni->ni_authmode == IEEE80211_AUTH_8021X);
108131d98677SRui Paulo 	} else
108231d98677SRui Paulo 		auth.mode = R92S_AUTHMODE_OPEN;
1083*4914fa0fSAdrian Chadd 	RSU_DPRINTF(sc, RSU_DEBUG_RESET,
1084*4914fa0fSAdrian Chadd 	    "%s: setting auth mode to %d\n",
1085*4914fa0fSAdrian Chadd 	    __func__, auth.mode);
108631d98677SRui Paulo 	error = rsu_fw_cmd(sc, R92S_CMD_SET_AUTH, &auth, sizeof(auth));
108731d98677SRui Paulo 	if (error != 0)
108831d98677SRui Paulo 		return (error);
108931d98677SRui Paulo 
109031d98677SRui Paulo 	memset(buf, 0, sizeof(buf));
109131d98677SRui Paulo 	bss = (struct ndis_wlan_bssid_ex *)buf;
109231d98677SRui Paulo 	IEEE80211_ADDR_COPY(bss->macaddr, ni->ni_bssid);
109331d98677SRui Paulo 	bss->ssid.ssidlen = htole32(ni->ni_esslen);
109431d98677SRui Paulo 	memcpy(bss->ssid.ssid, ni->ni_essid, ni->ni_esslen);
109531d98677SRui Paulo 	if (vap->iv_flags & (IEEE80211_F_PRIVACY | IEEE80211_F_WPA))
109631d98677SRui Paulo 		bss->privacy = htole32(1);
109731d98677SRui Paulo 	bss->rssi = htole32(ni->ni_avgrssi);
109831d98677SRui Paulo 	if (ic->ic_curmode == IEEE80211_MODE_11B)
109931d98677SRui Paulo 		bss->networktype = htole32(NDIS802_11DS);
110031d98677SRui Paulo 	else
110131d98677SRui Paulo 		bss->networktype = htole32(NDIS802_11OFDM24);
110231d98677SRui Paulo 	bss->config.len = htole32(sizeof(bss->config));
110331d98677SRui Paulo 	bss->config.bintval = htole32(ni->ni_intval);
110431d98677SRui Paulo 	bss->config.dsconfig = htole32(ieee80211_chan2ieee(ic, ni->ni_chan));
110531d98677SRui Paulo 	bss->inframode = htole32(NDIS802_11INFRASTRUCTURE);
1106*4914fa0fSAdrian Chadd 	/* XXX verify how this is supposed to look! */
110731d98677SRui Paulo 	memcpy(bss->supprates, ni->ni_rates.rs_rates,
110831d98677SRui Paulo 	    ni->ni_rates.rs_nrates);
110931d98677SRui Paulo 	/* Write the fixed fields of the beacon frame. */
111031d98677SRui Paulo 	fixed = (struct ndis_802_11_fixed_ies *)&bss[1];
111131d98677SRui Paulo 	memcpy(&fixed->tstamp, ni->ni_tstamp.data, 8);
111231d98677SRui Paulo 	fixed->bintval = htole16(ni->ni_intval);
111331d98677SRui Paulo 	fixed->capabilities = htole16(ni->ni_capinfo);
111431d98677SRui Paulo 	/* Write IEs to be included in the association request. */
111531d98677SRui Paulo 	frm = (uint8_t *)&fixed[1];
111631d98677SRui Paulo 	frm = ieee80211_add_rsn(frm, vap);
111731d98677SRui Paulo 	frm = ieee80211_add_wpa(frm, vap);
111831d98677SRui Paulo 	frm = ieee80211_add_qos(frm, ni);
111931d98677SRui Paulo 	if (ni->ni_flags & IEEE80211_NODE_HT)
112031d98677SRui Paulo 		frm = ieee80211_add_htcap(frm, ni);
112131d98677SRui Paulo 	bss->ieslen = htole32(frm - (uint8_t *)fixed);
112231d98677SRui Paulo 	bss->len = htole32(((frm - buf) + 3) & ~3);
1123*4914fa0fSAdrian Chadd 	RSU_DPRINTF(sc, RSU_DEBUG_RESET | RSU_DEBUG_FWCMD,
1124*4914fa0fSAdrian Chadd 	    "%s: sending join bss command to %s chan %d\n",
1125*4914fa0fSAdrian Chadd 	    __func__,
112631d98677SRui Paulo 	    ether_sprintf(bss->macaddr), le32toh(bss->config.dsconfig));
112731d98677SRui Paulo 	return (rsu_fw_cmd(sc, R92S_CMD_JOIN_BSS, buf, sizeof(buf)));
112831d98677SRui Paulo }
112931d98677SRui Paulo 
113031d98677SRui Paulo static int
113131d98677SRui Paulo rsu_disconnect(struct rsu_softc *sc)
113231d98677SRui Paulo {
113331d98677SRui Paulo 	uint32_t zero = 0;	/* :-) */
113431d98677SRui Paulo 
113531d98677SRui Paulo 	/* Disassociate from our current BSS. */
1136*4914fa0fSAdrian Chadd 	RSU_DPRINTF(sc, RSU_DEBUG_STATE | RSU_DEBUG_FWCMD,
1137*4914fa0fSAdrian Chadd 	    "%s: sending disconnect command\n", __func__);
113831d98677SRui Paulo 	return (rsu_fw_cmd(sc, R92S_CMD_DISCONNECT, &zero, sizeof(zero)));
113931d98677SRui Paulo }
114031d98677SRui Paulo 
114131d98677SRui Paulo static void
114231d98677SRui Paulo rsu_event_survey(struct rsu_softc *sc, uint8_t *buf, int len)
114331d98677SRui Paulo {
11447a79cebfSGleb Smirnoff 	struct ieee80211com *ic = &sc->sc_ic;
114531d98677SRui Paulo 	struct ieee80211_frame *wh;
114631d98677SRui Paulo 	struct ieee80211_channel *c;
114731d98677SRui Paulo 	struct ndis_wlan_bssid_ex *bss;
114831d98677SRui Paulo 	struct mbuf *m;
114931d98677SRui Paulo 	int pktlen;
115031d98677SRui Paulo 
115131d98677SRui Paulo 	if (__predict_false(len < sizeof(*bss)))
115231d98677SRui Paulo 		return;
115331d98677SRui Paulo 	bss = (struct ndis_wlan_bssid_ex *)buf;
115431d98677SRui Paulo 	if (__predict_false(len < sizeof(*bss) + le32toh(bss->ieslen)))
115531d98677SRui Paulo 		return;
115631d98677SRui Paulo 
1157*4914fa0fSAdrian Chadd 	RSU_DPRINTF(sc, RSU_DEBUG_SCAN,
1158*4914fa0fSAdrian Chadd 	    "%s: found BSS %s: len=%d chan=%d inframode=%d "
115931d98677SRui Paulo 	    "networktype=%d privacy=%d\n",
1160*4914fa0fSAdrian Chadd 	    __func__,
116131d98677SRui Paulo 	    ether_sprintf(bss->macaddr), le32toh(bss->len),
116231d98677SRui Paulo 	    le32toh(bss->config.dsconfig), le32toh(bss->inframode),
116331d98677SRui Paulo 	    le32toh(bss->networktype), le32toh(bss->privacy));
116431d98677SRui Paulo 
116531d98677SRui Paulo 	/* Build a fake beacon frame to let net80211 do all the parsing. */
1166*4914fa0fSAdrian Chadd 	/* XXX TODO: just call the new scan API methods! */
116731d98677SRui Paulo 	pktlen = sizeof(*wh) + le32toh(bss->ieslen);
116831d98677SRui Paulo 	if (__predict_false(pktlen > MCLBYTES))
116931d98677SRui Paulo 		return;
1170107f00c0SKevin Lo 	m = m_get2(pktlen, M_NOWAIT, MT_DATA, M_PKTHDR);
117131d98677SRui Paulo 	if (__predict_false(m == NULL))
117231d98677SRui Paulo 		return;
117331d98677SRui Paulo 	wh = mtod(m, struct ieee80211_frame *);
117431d98677SRui Paulo 	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
117531d98677SRui Paulo 	    IEEE80211_FC0_SUBTYPE_BEACON;
117631d98677SRui Paulo 	wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
11778cfe5440SHans Petter Selasky 	USETW(wh->i_dur, 0);
11787a79cebfSGleb Smirnoff 	IEEE80211_ADDR_COPY(wh->i_addr1, ieee80211broadcastaddr);
117931d98677SRui Paulo 	IEEE80211_ADDR_COPY(wh->i_addr2, bss->macaddr);
118031d98677SRui Paulo 	IEEE80211_ADDR_COPY(wh->i_addr3, bss->macaddr);
118131d98677SRui Paulo 	*(uint16_t *)wh->i_seq = 0;
118231d98677SRui Paulo 	memcpy(&wh[1], (uint8_t *)&bss[1], le32toh(bss->ieslen));
118331d98677SRui Paulo 
118431d98677SRui Paulo 	/* Finalize mbuf. */
118531d98677SRui Paulo 	m->m_pkthdr.len = m->m_len = pktlen;
118631d98677SRui Paulo 	/* Fix the channel. */
118731d98677SRui Paulo 	c = ieee80211_find_channel_byieee(ic,
118831d98677SRui Paulo 	    le32toh(bss->config.dsconfig),
118931d98677SRui Paulo 	    IEEE80211_CHAN_G);
119031d98677SRui Paulo 	if (c) {
119131d98677SRui Paulo 		ic->ic_curchan = c;
119231d98677SRui Paulo 		ieee80211_radiotap_chan_change(ic);
119331d98677SRui Paulo 	}
119431d98677SRui Paulo 	/* XXX avoid a LOR */
119531d98677SRui Paulo 	RSU_UNLOCK(sc);
119631d98677SRui Paulo 	ieee80211_input_all(ic, m, le32toh(bss->rssi), 0);
119731d98677SRui Paulo 	RSU_LOCK(sc);
119831d98677SRui Paulo }
119931d98677SRui Paulo 
120031d98677SRui Paulo static void
120131d98677SRui Paulo rsu_event_join_bss(struct rsu_softc *sc, uint8_t *buf, int len)
120231d98677SRui Paulo {
12037a79cebfSGleb Smirnoff 	struct ieee80211com *ic = &sc->sc_ic;
120431d98677SRui Paulo 	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
120531d98677SRui Paulo 	struct ieee80211_node *ni = vap->iv_bss;
120631d98677SRui Paulo 	struct r92s_event_join_bss *rsp;
1207bb03cd6fSHans Petter Selasky 	uint32_t tmp;
120831d98677SRui Paulo 	int res;
120931d98677SRui Paulo 
121031d98677SRui Paulo 	if (__predict_false(len < sizeof(*rsp)))
121131d98677SRui Paulo 		return;
121231d98677SRui Paulo 	rsp = (struct r92s_event_join_bss *)buf;
121331d98677SRui Paulo 	res = (int)le32toh(rsp->join_res);
121431d98677SRui Paulo 
121531d98677SRui Paulo 	DPRINTF("Rx join BSS event len=%d res=%d\n", len, res);
121631d98677SRui Paulo 	if (res <= 0) {
121731d98677SRui Paulo 		RSU_UNLOCK(sc);
121831d98677SRui Paulo 		ieee80211_new_state(vap, IEEE80211_S_SCAN, -1);
121931d98677SRui Paulo 		RSU_LOCK(sc);
122031d98677SRui Paulo 		return;
122131d98677SRui Paulo 	}
1222bb03cd6fSHans Petter Selasky 	tmp = le32toh(rsp->associd);
1223bb03cd6fSHans Petter Selasky 	if (tmp >= vap->iv_max_aid) {
1224bb03cd6fSHans Petter Selasky 		DPRINTF("Assoc ID overflow\n");
1225bb03cd6fSHans Petter Selasky 		tmp = 1;
1226bb03cd6fSHans Petter Selasky 	}
122731d98677SRui Paulo 	DPRINTF("associated with %s associd=%d\n",
1228bb03cd6fSHans Petter Selasky 	    ether_sprintf(rsp->bss.macaddr), tmp);
1229bb03cd6fSHans Petter Selasky 	ni->ni_associd = tmp | 0xc000;
123031d98677SRui Paulo 	RSU_UNLOCK(sc);
123131d98677SRui Paulo 	ieee80211_new_state(vap, IEEE80211_S_RUN,
123231d98677SRui Paulo 	    IEEE80211_FC0_SUBTYPE_ASSOC_RESP);
123331d98677SRui Paulo 	RSU_LOCK(sc);
123431d98677SRui Paulo }
123531d98677SRui Paulo 
123631d98677SRui Paulo static void
123731d98677SRui Paulo rsu_rx_event(struct rsu_softc *sc, uint8_t code, uint8_t *buf, int len)
123831d98677SRui Paulo {
12397a79cebfSGleb Smirnoff 	struct ieee80211com *ic = &sc->sc_ic;
124031d98677SRui Paulo 	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
124131d98677SRui Paulo 
1242*4914fa0fSAdrian Chadd 	RSU_DPRINTF(sc, RSU_DEBUG_RX,
1243*4914fa0fSAdrian Chadd 	    "%s: Rx event code=%d len=%d\n", __func__, code, len);
124431d98677SRui Paulo 	switch (code) {
124531d98677SRui Paulo 	case R92S_EVT_SURVEY:
124631d98677SRui Paulo 		if (vap->iv_state == IEEE80211_S_SCAN)
124731d98677SRui Paulo 			rsu_event_survey(sc, buf, len);
124831d98677SRui Paulo 		break;
124931d98677SRui Paulo 	case R92S_EVT_SURVEY_DONE:
1250*4914fa0fSAdrian Chadd 		RSU_DPRINTF(sc, RSU_DEBUG_SCAN,
1251*4914fa0fSAdrian Chadd 		    "%s: site survey pass %d done, found %d BSS\n",
1252*4914fa0fSAdrian Chadd 		    __func__, sc->sc_scan_pass, le32toh(*(uint32_t *)buf));
125331d98677SRui Paulo 		if (vap->iv_state != IEEE80211_S_SCAN)
125431d98677SRui Paulo 			break;	/* Ignore if not scanning. */
12557a79cebfSGleb Smirnoff 		if (sc->sc_scan_pass == 0 && vap->iv_des_nssid != 0) {
125631d98677SRui Paulo 			/* Schedule a directed scan for hidden APs. */
12577a79cebfSGleb Smirnoff 			sc->sc_scan_pass = 1;
125831d98677SRui Paulo 			RSU_UNLOCK(sc);
125931d98677SRui Paulo 			ieee80211_new_state(vap, IEEE80211_S_SCAN, -1);
126031d98677SRui Paulo 			RSU_LOCK(sc);
126131d98677SRui Paulo 			break;
126231d98677SRui Paulo 		}
12637a79cebfSGleb Smirnoff 		sc->sc_scan_pass = 0;
126431d98677SRui Paulo 		break;
126531d98677SRui Paulo 	case R92S_EVT_JOIN_BSS:
126631d98677SRui Paulo 		if (vap->iv_state == IEEE80211_S_AUTH)
126731d98677SRui Paulo 			rsu_event_join_bss(sc, buf, len);
126831d98677SRui Paulo 		break;
1269910593b5SHans Petter Selasky #if 0
1270910593b5SHans Petter Selasky XXX This event is occurring regularly, possibly due to some power saving event
1271910593b5SHans Petter Selasky XXX and disrupts the WLAN traffic. Disable for now.
127231d98677SRui Paulo 	case R92S_EVT_DEL_STA:
127331d98677SRui Paulo 		DPRINTF("disassociated from %s\n", ether_sprintf(buf));
127431d98677SRui Paulo 		if (vap->iv_state == IEEE80211_S_RUN &&
127531d98677SRui Paulo 		    IEEE80211_ADDR_EQ(vap->iv_bss->ni_bssid, buf)) {
127631d98677SRui Paulo 			RSU_UNLOCK(sc);
127731d98677SRui Paulo 			ieee80211_new_state(vap, IEEE80211_S_SCAN, -1);
127831d98677SRui Paulo 			RSU_LOCK(sc);
127931d98677SRui Paulo 		}
128031d98677SRui Paulo 		break;
1281910593b5SHans Petter Selasky #endif
128231d98677SRui Paulo 	case R92S_EVT_WPS_PBC:
1283*4914fa0fSAdrian Chadd 		RSU_DPRINTF(sc, RSU_DEBUG_RX | RSU_DEBUG_FWCMD,
1284*4914fa0fSAdrian Chadd 		    "%s: WPS PBC pushed.\n", __func__);
128531d98677SRui Paulo 		break;
128631d98677SRui Paulo 	case R92S_EVT_FWDBG:
128731d98677SRui Paulo 		buf[60] = '\0';
1288*4914fa0fSAdrian Chadd 		RSU_DPRINTF(sc, RSU_DEBUG_FWDBG, "FWDBG: %s\n", (char *)buf);
128931d98677SRui Paulo 		break;
1290910593b5SHans Petter Selasky 	default:
1291*4914fa0fSAdrian Chadd 		RSU_DPRINTF(sc, RSU_DEBUG_ANY, "%s: unhandled code (%d)\n",
1292*4914fa0fSAdrian Chadd 		    __func__, code);
1293910593b5SHans Petter Selasky 		break;
129431d98677SRui Paulo 	}
129531d98677SRui Paulo }
129631d98677SRui Paulo 
129731d98677SRui Paulo static void
129831d98677SRui Paulo rsu_rx_multi_event(struct rsu_softc *sc, uint8_t *buf, int len)
129931d98677SRui Paulo {
130031d98677SRui Paulo 	struct r92s_fw_cmd_hdr *cmd;
130131d98677SRui Paulo 	int cmdsz;
130231d98677SRui Paulo 
1303*4914fa0fSAdrian Chadd 	RSU_DPRINTF(sc, RSU_DEBUG_RX, "%s: Rx events len=%d\n", __func__, len);
130431d98677SRui Paulo 
130531d98677SRui Paulo 	/* Skip Rx status. */
130631d98677SRui Paulo 	buf += sizeof(struct r92s_rx_stat);
130731d98677SRui Paulo 	len -= sizeof(struct r92s_rx_stat);
130831d98677SRui Paulo 
130931d98677SRui Paulo 	/* Process all events. */
131031d98677SRui Paulo 	for (;;) {
131131d98677SRui Paulo 		/* Check that command header fits. */
131231d98677SRui Paulo 		if (__predict_false(len < sizeof(*cmd)))
131331d98677SRui Paulo 			break;
131431d98677SRui Paulo 		cmd = (struct r92s_fw_cmd_hdr *)buf;
131531d98677SRui Paulo 		/* Check that command payload fits. */
131631d98677SRui Paulo 		cmdsz = le16toh(cmd->len);
131731d98677SRui Paulo 		if (__predict_false(len < sizeof(*cmd) + cmdsz))
131831d98677SRui Paulo 			break;
131931d98677SRui Paulo 
132031d98677SRui Paulo 		/* Process firmware event. */
132131d98677SRui Paulo 		rsu_rx_event(sc, cmd->code, (uint8_t *)&cmd[1], cmdsz);
132231d98677SRui Paulo 
132331d98677SRui Paulo 		if (!(cmd->seq & R92S_FW_CMD_MORE))
132431d98677SRui Paulo 			break;
132531d98677SRui Paulo 		buf += sizeof(*cmd) + cmdsz;
132631d98677SRui Paulo 		len -= sizeof(*cmd) + cmdsz;
132731d98677SRui Paulo 	}
132831d98677SRui Paulo }
132931d98677SRui Paulo 
133031d98677SRui Paulo static int8_t
133131d98677SRui Paulo rsu_get_rssi(struct rsu_softc *sc, int rate, void *physt)
133231d98677SRui Paulo {
133331d98677SRui Paulo 	static const int8_t cckoff[] = { 14, -2, -20, -40 };
133431d98677SRui Paulo 	struct r92s_rx_phystat *phy;
133531d98677SRui Paulo 	struct r92s_rx_cck *cck;
133631d98677SRui Paulo 	uint8_t rpt;
133731d98677SRui Paulo 	int8_t rssi;
133831d98677SRui Paulo 
133931d98677SRui Paulo 	if (rate <= 3) {
134031d98677SRui Paulo 		cck = (struct r92s_rx_cck *)physt;
134131d98677SRui Paulo 		rpt = (cck->agc_rpt >> 6) & 0x3;
134231d98677SRui Paulo 		rssi = cck->agc_rpt & 0x3e;
134331d98677SRui Paulo 		rssi = cckoff[rpt] - rssi;
134431d98677SRui Paulo 	} else {	/* OFDM/HT. */
134531d98677SRui Paulo 		phy = (struct r92s_rx_phystat *)physt;
134631d98677SRui Paulo 		rssi = ((le32toh(phy->phydw1) >> 1) & 0x7f) - 106;
134731d98677SRui Paulo 	}
134831d98677SRui Paulo 	return (rssi);
134931d98677SRui Paulo }
135031d98677SRui Paulo 
135131d98677SRui Paulo static struct mbuf *
135231d98677SRui Paulo rsu_rx_frame(struct rsu_softc *sc, uint8_t *buf, int pktlen, int *rssi)
135331d98677SRui Paulo {
13547a79cebfSGleb Smirnoff 	struct ieee80211com *ic = &sc->sc_ic;
135531d98677SRui Paulo 	struct ieee80211_frame *wh;
135631d98677SRui Paulo 	struct r92s_rx_stat *stat;
135731d98677SRui Paulo 	uint32_t rxdw0, rxdw3;
135831d98677SRui Paulo 	struct mbuf *m;
135931d98677SRui Paulo 	uint8_t rate;
136031d98677SRui Paulo 	int infosz;
136131d98677SRui Paulo 
136231d98677SRui Paulo 	stat = (struct r92s_rx_stat *)buf;
136331d98677SRui Paulo 	rxdw0 = le32toh(stat->rxdw0);
136431d98677SRui Paulo 	rxdw3 = le32toh(stat->rxdw3);
136531d98677SRui Paulo 
136631d98677SRui Paulo 	if (__predict_false(rxdw0 & R92S_RXDW0_CRCERR)) {
13677a79cebfSGleb Smirnoff 		counter_u64_add(ic->ic_ierrors, 1);
136831d98677SRui Paulo 		return NULL;
136931d98677SRui Paulo 	}
137031d98677SRui Paulo 	if (__predict_false(pktlen < sizeof(*wh) || pktlen > MCLBYTES)) {
13717a79cebfSGleb Smirnoff 		counter_u64_add(ic->ic_ierrors, 1);
137231d98677SRui Paulo 		return NULL;
137331d98677SRui Paulo 	}
137431d98677SRui Paulo 
137531d98677SRui Paulo 	rate = MS(rxdw3, R92S_RXDW3_RATE);
137631d98677SRui Paulo 	infosz = MS(rxdw0, R92S_RXDW0_INFOSZ) * 8;
137731d98677SRui Paulo 
137831d98677SRui Paulo 	/* Get RSSI from PHY status descriptor if present. */
137931d98677SRui Paulo 	if (infosz != 0)
138031d98677SRui Paulo 		*rssi = rsu_get_rssi(sc, rate, &stat[1]);
138131d98677SRui Paulo 	else
138231d98677SRui Paulo 		*rssi = 0;
138331d98677SRui Paulo 
1384*4914fa0fSAdrian Chadd 	RSU_DPRINTF(sc, RSU_DEBUG_RX,
1385*4914fa0fSAdrian Chadd 	    "%s: Rx frame len=%d rate=%d infosz=%d rssi=%d\n",
1386*4914fa0fSAdrian Chadd 	    __func__,
138731d98677SRui Paulo 	    pktlen, rate, infosz, *rssi);
138831d98677SRui Paulo 
1389107f00c0SKevin Lo 	m = m_get2(pktlen, M_NOWAIT, MT_DATA, M_PKTHDR);
139031d98677SRui Paulo 	if (__predict_false(m == NULL)) {
13917a79cebfSGleb Smirnoff 		counter_u64_add(ic->ic_ierrors, 1);
139231d98677SRui Paulo 		return NULL;
139331d98677SRui Paulo 	}
139431d98677SRui Paulo 	/* Hardware does Rx TCP checksum offload. */
139531d98677SRui Paulo 	if (rxdw3 & R92S_RXDW3_TCPCHKVALID) {
139631d98677SRui Paulo 		if (__predict_true(rxdw3 & R92S_RXDW3_TCPCHKRPT))
139731d98677SRui Paulo 			m->m_pkthdr.csum_flags |= CSUM_DATA_VALID;
139831d98677SRui Paulo 	}
139931d98677SRui Paulo 	wh = (struct ieee80211_frame *)((uint8_t *)&stat[1] + infosz);
140031d98677SRui Paulo 	memcpy(mtod(m, uint8_t *), wh, pktlen);
140131d98677SRui Paulo 	m->m_pkthdr.len = m->m_len = pktlen;
140231d98677SRui Paulo 
140331d98677SRui Paulo 	if (ieee80211_radiotap_active(ic)) {
140431d98677SRui Paulo 		struct rsu_rx_radiotap_header *tap = &sc->sc_rxtap;
140531d98677SRui Paulo 
140631d98677SRui Paulo 		/* Map HW rate index to 802.11 rate. */
140731d98677SRui Paulo 		tap->wr_flags = 2;
140831d98677SRui Paulo 		if (!(rxdw3 & R92S_RXDW3_HTC)) {
140931d98677SRui Paulo 			switch (rate) {
141031d98677SRui Paulo 			/* CCK. */
141131d98677SRui Paulo 			case  0: tap->wr_rate =   2; break;
141231d98677SRui Paulo 			case  1: tap->wr_rate =   4; break;
141331d98677SRui Paulo 			case  2: tap->wr_rate =  11; break;
141431d98677SRui Paulo 			case  3: tap->wr_rate =  22; break;
141531d98677SRui Paulo 			/* OFDM. */
141631d98677SRui Paulo 			case  4: tap->wr_rate =  12; break;
141731d98677SRui Paulo 			case  5: tap->wr_rate =  18; break;
141831d98677SRui Paulo 			case  6: tap->wr_rate =  24; break;
141931d98677SRui Paulo 			case  7: tap->wr_rate =  36; break;
142031d98677SRui Paulo 			case  8: tap->wr_rate =  48; break;
142131d98677SRui Paulo 			case  9: tap->wr_rate =  72; break;
142231d98677SRui Paulo 			case 10: tap->wr_rate =  96; break;
142331d98677SRui Paulo 			case 11: tap->wr_rate = 108; break;
142431d98677SRui Paulo 			}
142531d98677SRui Paulo 		} else if (rate >= 12) {	/* MCS0~15. */
142631d98677SRui Paulo 			/* Bit 7 set means HT MCS instead of rate. */
142731d98677SRui Paulo 			tap->wr_rate = 0x80 | (rate - 12);
142831d98677SRui Paulo 		}
142931d98677SRui Paulo 		tap->wr_dbm_antsignal = *rssi;
143031d98677SRui Paulo 		tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq);
143131d98677SRui Paulo 		tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags);
143231d98677SRui Paulo 	}
143331d98677SRui Paulo 
143431d98677SRui Paulo 	return (m);
143531d98677SRui Paulo }
143631d98677SRui Paulo 
143731d98677SRui Paulo static struct mbuf *
143831d98677SRui Paulo rsu_rx_multi_frame(struct rsu_softc *sc, uint8_t *buf, int len, int *rssi)
143931d98677SRui Paulo {
144031d98677SRui Paulo 	struct r92s_rx_stat *stat;
144131d98677SRui Paulo 	uint32_t rxdw0;
144231d98677SRui Paulo 	int totlen, pktlen, infosz, npkts;
144331d98677SRui Paulo 	struct mbuf *m, *m0 = NULL, *prevm = NULL;
144431d98677SRui Paulo 
144531d98677SRui Paulo 	/* Get the number of encapsulated frames. */
144631d98677SRui Paulo 	stat = (struct r92s_rx_stat *)buf;
144731d98677SRui Paulo 	npkts = MS(le32toh(stat->rxdw2), R92S_RXDW2_PKTCNT);
1448*4914fa0fSAdrian Chadd 	RSU_DPRINTF(sc, RSU_DEBUG_RX,
1449*4914fa0fSAdrian Chadd 	    "%s: Rx %d frames in one chunk\n", __func__, npkts);
145031d98677SRui Paulo 
145131d98677SRui Paulo 	/* Process all of them. */
145231d98677SRui Paulo 	while (npkts-- > 0) {
145331d98677SRui Paulo 		if (__predict_false(len < sizeof(*stat)))
145431d98677SRui Paulo 			break;
145531d98677SRui Paulo 		stat = (struct r92s_rx_stat *)buf;
145631d98677SRui Paulo 		rxdw0 = le32toh(stat->rxdw0);
145731d98677SRui Paulo 
145831d98677SRui Paulo 		pktlen = MS(rxdw0, R92S_RXDW0_PKTLEN);
145931d98677SRui Paulo 		if (__predict_false(pktlen == 0))
146031d98677SRui Paulo 			break;
146131d98677SRui Paulo 
146231d98677SRui Paulo 		infosz = MS(rxdw0, R92S_RXDW0_INFOSZ) * 8;
146331d98677SRui Paulo 
146431d98677SRui Paulo 		/* Make sure everything fits in xfer. */
146531d98677SRui Paulo 		totlen = sizeof(*stat) + infosz + pktlen;
146631d98677SRui Paulo 		if (__predict_false(totlen > len))
146731d98677SRui Paulo 			break;
146831d98677SRui Paulo 
146931d98677SRui Paulo 		/* Process 802.11 frame. */
147031d98677SRui Paulo 		m = rsu_rx_frame(sc, buf, pktlen, rssi);
147131d98677SRui Paulo 		if (m0 == NULL)
147231d98677SRui Paulo 			m0 = m;
147331d98677SRui Paulo 		if (prevm == NULL)
147431d98677SRui Paulo 			prevm = m;
147531d98677SRui Paulo 		else {
147631d98677SRui Paulo 			prevm->m_next = m;
147731d98677SRui Paulo 			prevm = m;
147831d98677SRui Paulo 		}
147931d98677SRui Paulo 		/* Next chunk is 128-byte aligned. */
148031d98677SRui Paulo 		totlen = (totlen + 127) & ~127;
148131d98677SRui Paulo 		buf += totlen;
148231d98677SRui Paulo 		len -= totlen;
148331d98677SRui Paulo 	}
148431d98677SRui Paulo 
148531d98677SRui Paulo 	return (m0);
148631d98677SRui Paulo }
148731d98677SRui Paulo 
148831d98677SRui Paulo static struct mbuf *
148931d98677SRui Paulo rsu_rxeof(struct usb_xfer *xfer, struct rsu_data *data, int *rssi)
149031d98677SRui Paulo {
149131d98677SRui Paulo 	struct rsu_softc *sc = data->sc;
14927a79cebfSGleb Smirnoff 	struct ieee80211com *ic = &sc->sc_ic;
149331d98677SRui Paulo 	struct r92s_rx_stat *stat;
149431d98677SRui Paulo 	int len;
149531d98677SRui Paulo 
149631d98677SRui Paulo 	usbd_xfer_status(xfer, &len, NULL, NULL, NULL);
149731d98677SRui Paulo 
149831d98677SRui Paulo 	if (__predict_false(len < sizeof(*stat))) {
149931d98677SRui Paulo 		DPRINTF("xfer too short %d\n", len);
15007a79cebfSGleb Smirnoff 		counter_u64_add(ic->ic_ierrors, 1);
150131d98677SRui Paulo 		return (NULL);
150231d98677SRui Paulo 	}
150331d98677SRui Paulo 	/* Determine if it is a firmware C2H event or an 802.11 frame. */
150431d98677SRui Paulo 	stat = (struct r92s_rx_stat *)data->buf;
150531d98677SRui Paulo 	if ((le32toh(stat->rxdw1) & 0x1ff) == 0x1ff) {
150631d98677SRui Paulo 		rsu_rx_multi_event(sc, data->buf, len);
150731d98677SRui Paulo 		/* No packets to process. */
150831d98677SRui Paulo 		return (NULL);
150931d98677SRui Paulo 	} else
151031d98677SRui Paulo 		return (rsu_rx_multi_frame(sc, data->buf, len, rssi));
151131d98677SRui Paulo }
151231d98677SRui Paulo 
151331d98677SRui Paulo static void
151431d98677SRui Paulo rsu_bulk_rx_callback(struct usb_xfer *xfer, usb_error_t error)
151531d98677SRui Paulo {
151631d98677SRui Paulo 	struct rsu_softc *sc = usbd_xfer_softc(xfer);
15177a79cebfSGleb Smirnoff 	struct ieee80211com *ic = &sc->sc_ic;
151831d98677SRui Paulo 	struct ieee80211_frame *wh;
151931d98677SRui Paulo 	struct ieee80211_node *ni;
152031d98677SRui Paulo 	struct mbuf *m = NULL, *next;
152131d98677SRui Paulo 	struct rsu_data *data;
152231d98677SRui Paulo 	int rssi = 1;
152331d98677SRui Paulo 
152431d98677SRui Paulo 	RSU_ASSERT_LOCKED(sc);
152531d98677SRui Paulo 
152631d98677SRui Paulo 	switch (USB_GET_STATE(xfer)) {
152731d98677SRui Paulo 	case USB_ST_TRANSFERRED:
152831d98677SRui Paulo 		data = STAILQ_FIRST(&sc->sc_rx_active);
152931d98677SRui Paulo 		if (data == NULL)
153031d98677SRui Paulo 			goto tr_setup;
153131d98677SRui Paulo 		STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next);
153231d98677SRui Paulo 		m = rsu_rxeof(xfer, data, &rssi);
153331d98677SRui Paulo 		STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next);
153431d98677SRui Paulo 		/* FALLTHROUGH */
153531d98677SRui Paulo 	case USB_ST_SETUP:
153631d98677SRui Paulo tr_setup:
153731d98677SRui Paulo 		data = STAILQ_FIRST(&sc->sc_rx_inactive);
153831d98677SRui Paulo 		if (data == NULL) {
153931d98677SRui Paulo 			KASSERT(m == NULL, ("mbuf isn't NULL"));
154031d98677SRui Paulo 			return;
154131d98677SRui Paulo 		}
154231d98677SRui Paulo 		STAILQ_REMOVE_HEAD(&sc->sc_rx_inactive, next);
154331d98677SRui Paulo 		STAILQ_INSERT_TAIL(&sc->sc_rx_active, data, next);
154431d98677SRui Paulo 		usbd_xfer_set_frame_data(xfer, 0, data->buf,
154531d98677SRui Paulo 		    usbd_xfer_max_len(xfer));
154631d98677SRui Paulo 		usbd_transfer_submit(xfer);
154731d98677SRui Paulo 		/*
154831d98677SRui Paulo 		 * To avoid LOR we should unlock our private mutex here to call
154931d98677SRui Paulo 		 * ieee80211_input() because here is at the end of a USB
155031d98677SRui Paulo 		 * callback and safe to unlock.
155131d98677SRui Paulo 		 */
155231d98677SRui Paulo 		RSU_UNLOCK(sc);
155331d98677SRui Paulo 		while (m != NULL) {
155431d98677SRui Paulo 			next = m->m_next;
155531d98677SRui Paulo 			m->m_next = NULL;
155631d98677SRui Paulo 			wh = mtod(m, struct ieee80211_frame *);
155731d98677SRui Paulo 			ni = ieee80211_find_rxnode(ic,
155831d98677SRui Paulo 			    (struct ieee80211_frame_min *)wh);
155931d98677SRui Paulo 			if (ni != NULL) {
156031d98677SRui Paulo 				(void)ieee80211_input(ni, m, rssi, 0);
156131d98677SRui Paulo 				ieee80211_free_node(ni);
156231d98677SRui Paulo 			} else
156331d98677SRui Paulo 				(void)ieee80211_input_all(ic, m, rssi, 0);
156431d98677SRui Paulo 			m = next;
156531d98677SRui Paulo 		}
156631d98677SRui Paulo 		RSU_LOCK(sc);
156731d98677SRui Paulo 		break;
156831d98677SRui Paulo 	default:
156931d98677SRui Paulo 		/* needs it to the inactive queue due to a error. */
157031d98677SRui Paulo 		data = STAILQ_FIRST(&sc->sc_rx_active);
157131d98677SRui Paulo 		if (data != NULL) {
157231d98677SRui Paulo 			STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next);
157331d98677SRui Paulo 			STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next);
157431d98677SRui Paulo 		}
157531d98677SRui Paulo 		if (error != USB_ERR_CANCELLED) {
157631d98677SRui Paulo 			usbd_xfer_set_stall(xfer);
15777a79cebfSGleb Smirnoff 			counter_u64_add(ic->ic_ierrors, 1);
157831d98677SRui Paulo 			goto tr_setup;
157931d98677SRui Paulo 		}
158031d98677SRui Paulo 		break;
158131d98677SRui Paulo 	}
158231d98677SRui Paulo 
158331d98677SRui Paulo }
158431d98677SRui Paulo 
158531d98677SRui Paulo static void
158631d98677SRui Paulo rsu_txeof(struct usb_xfer *xfer, struct rsu_data *data)
158731d98677SRui Paulo {
1588*4914fa0fSAdrian Chadd #ifdef	USB_DEBUG
1589*4914fa0fSAdrian Chadd 	struct rsu_softc *sc = usbd_xfer_softc(xfer);
1590*4914fa0fSAdrian Chadd #endif
1591*4914fa0fSAdrian Chadd 
1592*4914fa0fSAdrian Chadd 	RSU_DPRINTF(sc, RSU_DEBUG_TXDONE, "%s: called; data=%p\n",
1593*4914fa0fSAdrian Chadd 	    __func__,
1594*4914fa0fSAdrian Chadd 	    data);
159531d98677SRui Paulo 
159631d98677SRui Paulo 	if (data->m) {
159731d98677SRui Paulo 		/* XXX status? */
15987a79cebfSGleb Smirnoff 		ieee80211_tx_complete(data->ni, data->m, 0);
159931d98677SRui Paulo 		data->m = NULL;
160031d98677SRui Paulo 		data->ni = NULL;
160131d98677SRui Paulo 	}
160231d98677SRui Paulo }
160331d98677SRui Paulo 
160431d98677SRui Paulo static void
1605400b4e53SHans Petter Selasky rsu_bulk_tx_callback_sub(struct usb_xfer *xfer, usb_error_t error,
1606400b4e53SHans Petter Selasky     uint8_t which)
160731d98677SRui Paulo {
160831d98677SRui Paulo 	struct rsu_softc *sc = usbd_xfer_softc(xfer);
16097a79cebfSGleb Smirnoff 	struct ieee80211com *ic = &sc->sc_ic;
161031d98677SRui Paulo 	struct rsu_data *data;
161131d98677SRui Paulo 
161231d98677SRui Paulo 	RSU_ASSERT_LOCKED(sc);
161331d98677SRui Paulo 
161431d98677SRui Paulo 	switch (USB_GET_STATE(xfer)) {
161531d98677SRui Paulo 	case USB_ST_TRANSFERRED:
1616400b4e53SHans Petter Selasky 		data = STAILQ_FIRST(&sc->sc_tx_active[which]);
161731d98677SRui Paulo 		if (data == NULL)
161831d98677SRui Paulo 			goto tr_setup;
1619*4914fa0fSAdrian Chadd 		RSU_DPRINTF(sc, RSU_DEBUG_TXDONE, "%s: transfer done %p\n",
1620*4914fa0fSAdrian Chadd 		    __func__, data);
1621400b4e53SHans Petter Selasky 		STAILQ_REMOVE_HEAD(&sc->sc_tx_active[which], next);
162231d98677SRui Paulo 		rsu_txeof(xfer, data);
162331d98677SRui Paulo 		STAILQ_INSERT_TAIL(&sc->sc_tx_inactive, data, next);
162431d98677SRui Paulo 		/* FALLTHROUGH */
162531d98677SRui Paulo 	case USB_ST_SETUP:
162631d98677SRui Paulo tr_setup:
1627400b4e53SHans Petter Selasky 		data = STAILQ_FIRST(&sc->sc_tx_pending[which]);
162831d98677SRui Paulo 		if (data == NULL) {
1629*4914fa0fSAdrian Chadd 			RSU_DPRINTF(sc, RSU_DEBUG_TXDONE,
1630*4914fa0fSAdrian Chadd 			    "%s: empty pending queue sc %p\n", __func__, sc);
163131d98677SRui Paulo 			return;
163231d98677SRui Paulo 		}
1633400b4e53SHans Petter Selasky 		STAILQ_REMOVE_HEAD(&sc->sc_tx_pending[which], next);
1634400b4e53SHans Petter Selasky 		STAILQ_INSERT_TAIL(&sc->sc_tx_active[which], data, next);
163531d98677SRui Paulo 		usbd_xfer_set_frame_data(xfer, 0, data->buf, data->buflen);
1636*4914fa0fSAdrian Chadd 		RSU_DPRINTF(sc, RSU_DEBUG_TXDONE,
1637*4914fa0fSAdrian Chadd 		    "%s: submitting transfer %p\n",
1638*4914fa0fSAdrian Chadd 		    __func__,
1639*4914fa0fSAdrian Chadd 		    data);
164031d98677SRui Paulo 		usbd_transfer_submit(xfer);
164131d98677SRui Paulo 		break;
164231d98677SRui Paulo 	default:
1643400b4e53SHans Petter Selasky 		data = STAILQ_FIRST(&sc->sc_tx_active[which]);
1644400b4e53SHans Petter Selasky 		if (data != NULL) {
1645400b4e53SHans Petter Selasky 			STAILQ_REMOVE_HEAD(&sc->sc_tx_active[which], next);
1646400b4e53SHans Petter Selasky 			rsu_txeof(xfer, data);
1647400b4e53SHans Petter Selasky 			STAILQ_INSERT_TAIL(&sc->sc_tx_inactive, data, next);
164831d98677SRui Paulo 		}
16497a79cebfSGleb Smirnoff 		counter_u64_add(ic->ic_oerrors, 1);
1650400b4e53SHans Petter Selasky 
165131d98677SRui Paulo 		if (error != USB_ERR_CANCELLED) {
165231d98677SRui Paulo 			usbd_xfer_set_stall(xfer);
165331d98677SRui Paulo 			goto tr_setup;
165431d98677SRui Paulo 		}
165531d98677SRui Paulo 		break;
165631d98677SRui Paulo 	}
165731d98677SRui Paulo }
165831d98677SRui Paulo 
1659400b4e53SHans Petter Selasky static void
1660910593b5SHans Petter Selasky rsu_bulk_tx_callback_be_bk(struct usb_xfer *xfer, usb_error_t error)
1661400b4e53SHans Petter Selasky {
1662910593b5SHans Petter Selasky 	rsu_bulk_tx_callback_sub(xfer, error, RSU_BULK_TX_BE_BK);
1663400b4e53SHans Petter Selasky }
1664400b4e53SHans Petter Selasky 
1665400b4e53SHans Petter Selasky static void
1666910593b5SHans Petter Selasky rsu_bulk_tx_callback_vi_vo(struct usb_xfer *xfer, usb_error_t error)
1667400b4e53SHans Petter Selasky {
1668910593b5SHans Petter Selasky 	rsu_bulk_tx_callback_sub(xfer, error, RSU_BULK_TX_VI_VO);
1669400b4e53SHans Petter Selasky }
1670400b4e53SHans Petter Selasky 
167131d98677SRui Paulo static int
167231d98677SRui Paulo rsu_tx_start(struct rsu_softc *sc, struct ieee80211_node *ni,
1673400b4e53SHans Petter Selasky     struct mbuf *m0, struct rsu_data *data)
167431d98677SRui Paulo {
16757a79cebfSGleb Smirnoff 	struct ieee80211com *ic = &sc->sc_ic;
167631d98677SRui Paulo         struct ieee80211vap *vap = ni->ni_vap;
167731d98677SRui Paulo 	struct ieee80211_frame *wh;
167831d98677SRui Paulo 	struct ieee80211_key *k = NULL;
167931d98677SRui Paulo 	struct r92s_tx_desc *txd;
1680400b4e53SHans Petter Selasky 	uint8_t type;
1681400b4e53SHans Petter Selasky 	uint8_t tid = 0;
1682400b4e53SHans Petter Selasky 	uint8_t which;
1683400b4e53SHans Petter Selasky 	int hasqos;
1684400b4e53SHans Petter Selasky 	int xferlen;
168531d98677SRui Paulo 
168631d98677SRui Paulo 	RSU_ASSERT_LOCKED(sc);
168731d98677SRui Paulo 
168831d98677SRui Paulo 	wh = mtod(m0, struct ieee80211_frame *);
168931d98677SRui Paulo 	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
169031d98677SRui Paulo 
1691*4914fa0fSAdrian Chadd 	RSU_DPRINTF(sc, RSU_DEBUG_TX, "%s: data=%p, m=%p\n",
1692*4914fa0fSAdrian Chadd 	    __func__, data, m0);
1693*4914fa0fSAdrian Chadd 
16945945b5f5SKevin Lo 	if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
169531d98677SRui Paulo 		k = ieee80211_crypto_encap(ni, m0);
169631d98677SRui Paulo 		if (k == NULL) {
169731d98677SRui Paulo 			device_printf(sc->sc_dev,
169831d98677SRui Paulo 			    "ieee80211_crypto_encap returns NULL.\n");
169931d98677SRui Paulo 			/* XXX we don't expect the fragmented frames */
170031d98677SRui Paulo 			m_freem(m0);
170131d98677SRui Paulo 			return (ENOBUFS);
170231d98677SRui Paulo 		}
170331d98677SRui Paulo 		wh = mtod(m0, struct ieee80211_frame *);
170431d98677SRui Paulo 	}
170531d98677SRui Paulo 	switch (type) {
170631d98677SRui Paulo 	case IEEE80211_FC0_TYPE_CTL:
170731d98677SRui Paulo 	case IEEE80211_FC0_TYPE_MGT:
1708910593b5SHans Petter Selasky 		which = rsu_wme_ac_xfer_map[WME_AC_VO];
170931d98677SRui Paulo 		break;
171031d98677SRui Paulo 	default:
1711910593b5SHans Petter Selasky 		which = rsu_wme_ac_xfer_map[M_WME_GETAC(m0)];
171231d98677SRui Paulo 		break;
171331d98677SRui Paulo 	}
171431d98677SRui Paulo 	hasqos = 0;
171531d98677SRui Paulo 
171631d98677SRui Paulo 	/* Fill Tx descriptor. */
171731d98677SRui Paulo 	txd = (struct r92s_tx_desc *)data->buf;
171831d98677SRui Paulo 	memset(txd, 0, sizeof(*txd));
171931d98677SRui Paulo 
172031d98677SRui Paulo 	txd->txdw0 |= htole32(
172131d98677SRui Paulo 	    SM(R92S_TXDW0_PKTLEN, m0->m_pkthdr.len) |
172231d98677SRui Paulo 	    SM(R92S_TXDW0_OFFSET, sizeof(*txd)) |
172331d98677SRui Paulo 	    R92S_TXDW0_OWN | R92S_TXDW0_FSG | R92S_TXDW0_LSG);
172431d98677SRui Paulo 
172531d98677SRui Paulo 	txd->txdw1 |= htole32(
172631d98677SRui Paulo 	    SM(R92S_TXDW1_MACID, R92S_MACID_BSS) |
172731d98677SRui Paulo 	    SM(R92S_TXDW1_QSEL, R92S_TXDW1_QSEL_BE));
172831d98677SRui Paulo 	if (!hasqos)
172931d98677SRui Paulo 		txd->txdw1 |= htole32(R92S_TXDW1_NONQOS);
173031d98677SRui Paulo #ifdef notyet
173131d98677SRui Paulo 	if (k != NULL) {
173231d98677SRui Paulo 		switch (k->wk_cipher->ic_cipher) {
173331d98677SRui Paulo 		case IEEE80211_CIPHER_WEP:
173431d98677SRui Paulo 			cipher = R92S_TXDW1_CIPHER_WEP;
173531d98677SRui Paulo 			break;
173631d98677SRui Paulo 		case IEEE80211_CIPHER_TKIP:
173731d98677SRui Paulo 			cipher = R92S_TXDW1_CIPHER_TKIP;
173831d98677SRui Paulo 			break;
173931d98677SRui Paulo 		case IEEE80211_CIPHER_AES_CCM:
174031d98677SRui Paulo 			cipher = R92S_TXDW1_CIPHER_AES;
174131d98677SRui Paulo 			break;
174231d98677SRui Paulo 		default:
174331d98677SRui Paulo 			cipher = R92S_TXDW1_CIPHER_NONE;
174431d98677SRui Paulo 		}
174531d98677SRui Paulo 		txd->txdw1 |= htole32(
174631d98677SRui Paulo 		    SM(R92S_TXDW1_CIPHER, cipher) |
174731d98677SRui Paulo 		    SM(R92S_TXDW1_KEYIDX, k->k_id));
174831d98677SRui Paulo 	}
174931d98677SRui Paulo #endif
175031d98677SRui Paulo 	txd->txdw2 |= htole32(R92S_TXDW2_BK);
175131d98677SRui Paulo 	if (IEEE80211_IS_MULTICAST(wh->i_addr1))
175231d98677SRui Paulo 		txd->txdw2 |= htole32(R92S_TXDW2_BMCAST);
175331d98677SRui Paulo 	/*
175431d98677SRui Paulo 	 * Firmware will use and increment the sequence number for the
175531d98677SRui Paulo 	 * specified TID.
175631d98677SRui Paulo 	 */
175731d98677SRui Paulo 	txd->txdw3 |= htole32(SM(R92S_TXDW3_SEQ, tid));
175831d98677SRui Paulo 
175931d98677SRui Paulo 	if (ieee80211_radiotap_active_vap(vap)) {
176031d98677SRui Paulo 		struct rsu_tx_radiotap_header *tap = &sc->sc_txtap;
176131d98677SRui Paulo 
176231d98677SRui Paulo 		tap->wt_flags = 0;
176331d98677SRui Paulo 		tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq);
176431d98677SRui Paulo 		tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
176531d98677SRui Paulo 		ieee80211_radiotap_tx(vap, m0);
176631d98677SRui Paulo 	}
1767*4914fa0fSAdrian Chadd 
176831d98677SRui Paulo 	xferlen = sizeof(*txd) + m0->m_pkthdr.len;
176931d98677SRui Paulo 	m_copydata(m0, 0, m0->m_pkthdr.len, (caddr_t)&txd[1]);
177031d98677SRui Paulo 
177131d98677SRui Paulo 	data->buflen = xferlen;
177231d98677SRui Paulo 	data->ni = ni;
177331d98677SRui Paulo 	data->m = m0;
1774400b4e53SHans Petter Selasky 	STAILQ_INSERT_TAIL(&sc->sc_tx_pending[which], data, next);
177531d98677SRui Paulo 
1776400b4e53SHans Petter Selasky 	/* start transfer, if any */
1777910593b5SHans Petter Selasky 	usbd_transfer_start(sc->sc_xfer[which]);
177831d98677SRui Paulo 	return (0);
177931d98677SRui Paulo }
178031d98677SRui Paulo 
17817a79cebfSGleb Smirnoff static int
17827a79cebfSGleb Smirnoff rsu_transmit(struct ieee80211com *ic, struct mbuf *m)
178331d98677SRui Paulo {
17847a79cebfSGleb Smirnoff 	struct rsu_softc *sc = ic->ic_softc;
17857a79cebfSGleb Smirnoff 	int error;
178631d98677SRui Paulo 
178731d98677SRui Paulo 	RSU_LOCK(sc);
17887a79cebfSGleb Smirnoff 	if (!sc->sc_running) {
178931d98677SRui Paulo 		RSU_UNLOCK(sc);
17907a79cebfSGleb Smirnoff 		return (ENXIO);
17917a79cebfSGleb Smirnoff 	}
17927a79cebfSGleb Smirnoff 	error = mbufq_enqueue(&sc->sc_snd, m);
17937a79cebfSGleb Smirnoff 	if (error) {
17947a79cebfSGleb Smirnoff 		RSU_UNLOCK(sc);
17957a79cebfSGleb Smirnoff 		return (error);
17967a79cebfSGleb Smirnoff 	}
17977a79cebfSGleb Smirnoff 	rsu_start(sc);
17987a79cebfSGleb Smirnoff 	RSU_UNLOCK(sc);
17997a79cebfSGleb Smirnoff 
18007a79cebfSGleb Smirnoff 	return (0);
180131d98677SRui Paulo }
180231d98677SRui Paulo 
180331d98677SRui Paulo static void
18047a79cebfSGleb Smirnoff rsu_start(struct rsu_softc *sc)
180531d98677SRui Paulo {
180631d98677SRui Paulo 	struct ieee80211_node *ni;
180731d98677SRui Paulo 	struct rsu_data *bf;
1808400b4e53SHans Petter Selasky 	struct mbuf *m;
180931d98677SRui Paulo 
181031d98677SRui Paulo 	RSU_ASSERT_LOCKED(sc);
181131d98677SRui Paulo 
18127a79cebfSGleb Smirnoff 	while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) {
18137a79cebfSGleb Smirnoff 		bf = rsu_getbuf(sc);
18147a79cebfSGleb Smirnoff 		if (bf == NULL) {
18157a79cebfSGleb Smirnoff 			mbufq_prepend(&sc->sc_snd, m);
181631d98677SRui Paulo 			break;
18177a79cebfSGleb Smirnoff 		}
18187a79cebfSGleb Smirnoff 
181931d98677SRui Paulo 		ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
182031d98677SRui Paulo 		m->m_pkthdr.rcvif = NULL;
182131d98677SRui Paulo 
18227a79cebfSGleb Smirnoff 		if (rsu_tx_start(sc, ni, m, bf) != 0) {
18237a79cebfSGleb Smirnoff 			if_inc_counter(ni->ni_vap->iv_ifp,
18247a79cebfSGleb Smirnoff 			    IFCOUNTER_OERRORS, 1);
182531d98677SRui Paulo 			STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, bf, next);
182631d98677SRui Paulo 			ieee80211_free_node(ni);
18277a79cebfSGleb Smirnoff 			break;
182831d98677SRui Paulo 		}
182931d98677SRui Paulo 	}
183031d98677SRui Paulo }
183131d98677SRui Paulo 
18327a79cebfSGleb Smirnoff static void
18337a79cebfSGleb Smirnoff rsu_parent(struct ieee80211com *ic)
183431d98677SRui Paulo {
1835d3fdd08cSAdrian Chadd 	struct rsu_softc *sc = ic->ic_softc;
18367a79cebfSGleb Smirnoff 	int startall = 0;
183731d98677SRui Paulo 
18387a79cebfSGleb Smirnoff 	RSU_LOCK(sc);
18397a79cebfSGleb Smirnoff 	if (ic->ic_nrunning > 0) {
18407a79cebfSGleb Smirnoff 		if (!sc->sc_running) {
1841d3fdd08cSAdrian Chadd 			rsu_init(sc);
184231d98677SRui Paulo 			startall = 1;
184331d98677SRui Paulo 		}
18447a79cebfSGleb Smirnoff 	} else if (sc->sc_running)
18457a79cebfSGleb Smirnoff 		rsu_stop(sc);
18467a79cebfSGleb Smirnoff 	RSU_UNLOCK(sc);
18477a79cebfSGleb Smirnoff 
184831d98677SRui Paulo 	if (startall)
184931d98677SRui Paulo 		ieee80211_start_all(ic);
185031d98677SRui Paulo }
185131d98677SRui Paulo 
185231d98677SRui Paulo /*
185331d98677SRui Paulo  * Power on sequence for A-cut adapters.
185431d98677SRui Paulo  */
185531d98677SRui Paulo static void
185631d98677SRui Paulo rsu_power_on_acut(struct rsu_softc *sc)
185731d98677SRui Paulo {
185831d98677SRui Paulo 	uint32_t reg;
185931d98677SRui Paulo 
186031d98677SRui Paulo 	rsu_write_1(sc, R92S_SPS0_CTRL + 1, 0x53);
186131d98677SRui Paulo 	rsu_write_1(sc, R92S_SPS0_CTRL + 0, 0x57);
186231d98677SRui Paulo 
186331d98677SRui Paulo 	/* Enable AFE macro block's bandgap and Mbias. */
186431d98677SRui Paulo 	rsu_write_1(sc, R92S_AFE_MISC,
186531d98677SRui Paulo 	    rsu_read_1(sc, R92S_AFE_MISC) |
186631d98677SRui Paulo 	    R92S_AFE_MISC_BGEN | R92S_AFE_MISC_MBEN);
186731d98677SRui Paulo 	/* Enable LDOA15 block. */
186831d98677SRui Paulo 	rsu_write_1(sc, R92S_LDOA15_CTRL,
186931d98677SRui Paulo 	    rsu_read_1(sc, R92S_LDOA15_CTRL) | R92S_LDA15_EN);
187031d98677SRui Paulo 
187131d98677SRui Paulo 	rsu_write_1(sc, R92S_SPS1_CTRL,
187231d98677SRui Paulo 	    rsu_read_1(sc, R92S_SPS1_CTRL) | R92S_SPS1_LDEN);
187331d98677SRui Paulo 	usb_pause_mtx(&sc->sc_mtx, 2 * hz);
187431d98677SRui Paulo 	/* Enable switch regulator block. */
187531d98677SRui Paulo 	rsu_write_1(sc, R92S_SPS1_CTRL,
187631d98677SRui Paulo 	    rsu_read_1(sc, R92S_SPS1_CTRL) | R92S_SPS1_SWEN);
187731d98677SRui Paulo 
187831d98677SRui Paulo 	rsu_write_4(sc, R92S_SPS1_CTRL, 0x00a7b267);
187931d98677SRui Paulo 
188031d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1,
188131d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_ISO_CTRL + 1) | 0x08);
188231d98677SRui Paulo 
188331d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_FUNC_EN + 1,
188431d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x20);
188531d98677SRui Paulo 
188631d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1,
188731d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_ISO_CTRL + 1) & ~0x90);
188831d98677SRui Paulo 
188931d98677SRui Paulo 	/* Enable AFE clock. */
189031d98677SRui Paulo 	rsu_write_1(sc, R92S_AFE_XTAL_CTRL + 1,
189131d98677SRui Paulo 	    rsu_read_1(sc, R92S_AFE_XTAL_CTRL + 1) & ~0x04);
189231d98677SRui Paulo 	/* Enable AFE PLL macro block. */
189331d98677SRui Paulo 	rsu_write_1(sc, R92S_AFE_PLL_CTRL,
189431d98677SRui Paulo 	    rsu_read_1(sc, R92S_AFE_PLL_CTRL) | 0x11);
189531d98677SRui Paulo 	/* Attach AFE PLL to MACTOP/BB. */
189631d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_ISO_CTRL,
189731d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_ISO_CTRL) & ~0x11);
189831d98677SRui Paulo 
189931d98677SRui Paulo 	/* Switch to 40MHz clock instead of 80MHz. */
190031d98677SRui Paulo 	rsu_write_2(sc, R92S_SYS_CLKR,
190131d98677SRui Paulo 	    rsu_read_2(sc, R92S_SYS_CLKR) & ~R92S_SYS_CLKSEL);
190231d98677SRui Paulo 
190331d98677SRui Paulo 	/* Enable MAC clock. */
190431d98677SRui Paulo 	rsu_write_2(sc, R92S_SYS_CLKR,
190531d98677SRui Paulo 	    rsu_read_2(sc, R92S_SYS_CLKR) |
190631d98677SRui Paulo 	    R92S_MAC_CLK_EN | R92S_SYS_CLK_EN);
190731d98677SRui Paulo 
190831d98677SRui Paulo 	rsu_write_1(sc, R92S_PMC_FSM, 0x02);
190931d98677SRui Paulo 
191031d98677SRui Paulo 	/* Enable digital core and IOREG R/W. */
191131d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_FUNC_EN + 1,
191231d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x08);
191331d98677SRui Paulo 
191431d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_FUNC_EN + 1,
191531d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x80);
191631d98677SRui Paulo 
191731d98677SRui Paulo 	/* Switch the control path to firmware. */
191831d98677SRui Paulo 	reg = rsu_read_2(sc, R92S_SYS_CLKR);
191931d98677SRui Paulo 	reg = (reg & ~R92S_SWHW_SEL) | R92S_FWHW_SEL;
192031d98677SRui Paulo 	rsu_write_2(sc, R92S_SYS_CLKR, reg);
192131d98677SRui Paulo 
192231d98677SRui Paulo 	rsu_write_2(sc, R92S_CR, 0x37fc);
192331d98677SRui Paulo 
192431d98677SRui Paulo 	/* Fix USB RX FIFO issue. */
192531d98677SRui Paulo 	rsu_write_1(sc, 0xfe5c,
192631d98677SRui Paulo 	    rsu_read_1(sc, 0xfe5c) | 0x80);
192731d98677SRui Paulo 	rsu_write_1(sc, 0x00ab,
192831d98677SRui Paulo 	    rsu_read_1(sc, 0x00ab) | 0xc0);
192931d98677SRui Paulo 
193031d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_CLKR,
193131d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_CLKR) & ~R92S_SYS_CPU_CLKSEL);
193231d98677SRui Paulo }
193331d98677SRui Paulo 
193431d98677SRui Paulo /*
193531d98677SRui Paulo  * Power on sequence for B-cut and C-cut adapters.
193631d98677SRui Paulo  */
193731d98677SRui Paulo static void
193831d98677SRui Paulo rsu_power_on_bcut(struct rsu_softc *sc)
193931d98677SRui Paulo {
194031d98677SRui Paulo 	uint32_t reg;
194131d98677SRui Paulo 	int ntries;
194231d98677SRui Paulo 
194331d98677SRui Paulo 	/* Prevent eFuse leakage. */
194431d98677SRui Paulo 	rsu_write_1(sc, 0x37, 0xb0);
1945400b4e53SHans Petter Selasky 	usb_pause_mtx(&sc->sc_mtx, hz / 100);
194631d98677SRui Paulo 	rsu_write_1(sc, 0x37, 0x30);
194731d98677SRui Paulo 
194831d98677SRui Paulo 	/* Switch the control path to hardware. */
194931d98677SRui Paulo 	reg = rsu_read_2(sc, R92S_SYS_CLKR);
195031d98677SRui Paulo 	if (reg & R92S_FWHW_SEL) {
195131d98677SRui Paulo 		rsu_write_2(sc, R92S_SYS_CLKR,
195231d98677SRui Paulo 		    reg & ~(R92S_SWHW_SEL | R92S_FWHW_SEL));
195331d98677SRui Paulo 	}
195431d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_FUNC_EN + 1,
195531d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) & ~0x8c);
1956b41381aeSHans Petter Selasky 	rsu_ms_delay(sc);
195731d98677SRui Paulo 
195831d98677SRui Paulo 	rsu_write_1(sc, R92S_SPS0_CTRL + 1, 0x53);
195931d98677SRui Paulo 	rsu_write_1(sc, R92S_SPS0_CTRL + 0, 0x57);
196031d98677SRui Paulo 
196131d98677SRui Paulo 	reg = rsu_read_1(sc, R92S_AFE_MISC);
196231d98677SRui Paulo 	rsu_write_1(sc, R92S_AFE_MISC, reg | R92S_AFE_MISC_BGEN);
196331d98677SRui Paulo 	rsu_write_1(sc, R92S_AFE_MISC, reg | R92S_AFE_MISC_BGEN |
196431d98677SRui Paulo 	    R92S_AFE_MISC_MBEN | R92S_AFE_MISC_I32_EN);
196531d98677SRui Paulo 
196631d98677SRui Paulo 	/* Enable PLL. */
196731d98677SRui Paulo 	rsu_write_1(sc, R92S_LDOA15_CTRL,
196831d98677SRui Paulo 	    rsu_read_1(sc, R92S_LDOA15_CTRL) | R92S_LDA15_EN);
196931d98677SRui Paulo 
197031d98677SRui Paulo 	rsu_write_1(sc, R92S_LDOV12D_CTRL,
197131d98677SRui Paulo 	    rsu_read_1(sc, R92S_LDOV12D_CTRL) | R92S_LDV12_EN);
197231d98677SRui Paulo 
197331d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1,
197431d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_ISO_CTRL + 1) | 0x08);
197531d98677SRui Paulo 
197631d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_FUNC_EN + 1,
197731d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x20);
197831d98677SRui Paulo 
197931d98677SRui Paulo 	/* Support 64KB IMEM. */
198031d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1,
198131d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_ISO_CTRL + 1) & ~0x97);
198231d98677SRui Paulo 
198331d98677SRui Paulo 	/* Enable AFE clock. */
198431d98677SRui Paulo 	rsu_write_1(sc, R92S_AFE_XTAL_CTRL + 1,
198531d98677SRui Paulo 	    rsu_read_1(sc, R92S_AFE_XTAL_CTRL + 1) & ~0x04);
198631d98677SRui Paulo 	/* Enable AFE PLL macro block. */
198731d98677SRui Paulo 	reg = rsu_read_1(sc, R92S_AFE_PLL_CTRL);
198831d98677SRui Paulo 	rsu_write_1(sc, R92S_AFE_PLL_CTRL, reg | 0x11);
1989b41381aeSHans Petter Selasky 	rsu_ms_delay(sc);
199031d98677SRui Paulo 	rsu_write_1(sc, R92S_AFE_PLL_CTRL, reg | 0x51);
1991b41381aeSHans Petter Selasky 	rsu_ms_delay(sc);
199231d98677SRui Paulo 	rsu_write_1(sc, R92S_AFE_PLL_CTRL, reg | 0x11);
1993b41381aeSHans Petter Selasky 	rsu_ms_delay(sc);
199431d98677SRui Paulo 
199531d98677SRui Paulo 	/* Attach AFE PLL to MACTOP/BB. */
199631d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_ISO_CTRL,
199731d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_ISO_CTRL) & ~0x11);
199831d98677SRui Paulo 
199931d98677SRui Paulo 	/* Switch to 40MHz clock. */
200031d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_CLKR, 0x00);
200131d98677SRui Paulo 	/* Disable CPU clock and 80MHz SSC. */
200231d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_CLKR,
200331d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_CLKR) | 0xa0);
200431d98677SRui Paulo 	/* Enable MAC clock. */
200531d98677SRui Paulo 	rsu_write_2(sc, R92S_SYS_CLKR,
200631d98677SRui Paulo 	    rsu_read_2(sc, R92S_SYS_CLKR) |
200731d98677SRui Paulo 	    R92S_MAC_CLK_EN | R92S_SYS_CLK_EN);
200831d98677SRui Paulo 
200931d98677SRui Paulo 	rsu_write_1(sc, R92S_PMC_FSM, 0x02);
201031d98677SRui Paulo 
201131d98677SRui Paulo 	/* Enable digital core and IOREG R/W. */
201231d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_FUNC_EN + 1,
201331d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x08);
201431d98677SRui Paulo 
201531d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_FUNC_EN + 1,
201631d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x80);
201731d98677SRui Paulo 
201831d98677SRui Paulo 	/* Switch the control path to firmware. */
201931d98677SRui Paulo 	reg = rsu_read_2(sc, R92S_SYS_CLKR);
202031d98677SRui Paulo 	reg = (reg & ~R92S_SWHW_SEL) | R92S_FWHW_SEL;
202131d98677SRui Paulo 	rsu_write_2(sc, R92S_SYS_CLKR, reg);
202231d98677SRui Paulo 
202331d98677SRui Paulo 	rsu_write_2(sc, R92S_CR, 0x37fc);
202431d98677SRui Paulo 
202531d98677SRui Paulo 	/* Fix USB RX FIFO issue. */
202631d98677SRui Paulo 	rsu_write_1(sc, 0xfe5c,
202731d98677SRui Paulo 	    rsu_read_1(sc, 0xfe5c) | 0x80);
202831d98677SRui Paulo 
202931d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_CLKR,
203031d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_CLKR) & ~R92S_SYS_CPU_CLKSEL);
203131d98677SRui Paulo 
203231d98677SRui Paulo 	rsu_write_1(sc, 0xfe1c, 0x80);
203331d98677SRui Paulo 
203431d98677SRui Paulo 	/* Make sure TxDMA is ready to download firmware. */
203531d98677SRui Paulo 	for (ntries = 0; ntries < 20; ntries++) {
203631d98677SRui Paulo 		reg = rsu_read_1(sc, R92S_TCR);
203731d98677SRui Paulo 		if ((reg & (R92S_TCR_IMEM_CHK_RPT | R92S_TCR_EMEM_CHK_RPT)) ==
203831d98677SRui Paulo 		    (R92S_TCR_IMEM_CHK_RPT | R92S_TCR_EMEM_CHK_RPT))
203931d98677SRui Paulo 			break;
2040b41381aeSHans Petter Selasky 		rsu_ms_delay(sc);
204131d98677SRui Paulo 	}
204231d98677SRui Paulo 	if (ntries == 20) {
2043*4914fa0fSAdrian Chadd 		RSU_DPRINTF(sc, RSU_DEBUG_RESET | RSU_DEBUG_TX,
2044*4914fa0fSAdrian Chadd 		    "%s: TxDMA is not ready\n",
2045*4914fa0fSAdrian Chadd 		    __func__);
204631d98677SRui Paulo 		/* Reset TxDMA. */
204731d98677SRui Paulo 		reg = rsu_read_1(sc, R92S_CR);
204831d98677SRui Paulo 		rsu_write_1(sc, R92S_CR, reg & ~R92S_CR_TXDMA_EN);
2049b41381aeSHans Petter Selasky 		rsu_ms_delay(sc);
205031d98677SRui Paulo 		rsu_write_1(sc, R92S_CR, reg | R92S_CR_TXDMA_EN);
205131d98677SRui Paulo 	}
205231d98677SRui Paulo }
205331d98677SRui Paulo 
205431d98677SRui Paulo static void
205531d98677SRui Paulo rsu_power_off(struct rsu_softc *sc)
205631d98677SRui Paulo {
205731d98677SRui Paulo 	/* Turn RF off. */
205831d98677SRui Paulo 	rsu_write_1(sc, R92S_RF_CTRL, 0x00);
2059400b4e53SHans Petter Selasky 	usb_pause_mtx(&sc->sc_mtx, hz / 200);
206031d98677SRui Paulo 
206131d98677SRui Paulo 	/* Turn MAC off. */
206231d98677SRui Paulo 	/* Switch control path. */
206331d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_CLKR + 1, 0x38);
206431d98677SRui Paulo 	/* Reset MACTOP. */
206531d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, 0x70);
206631d98677SRui Paulo 	rsu_write_1(sc, R92S_PMC_FSM, 0x06);
206731d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_ISO_CTRL + 0, 0xf9);
206831d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1, 0xe8);
206931d98677SRui Paulo 
207031d98677SRui Paulo 	/* Disable AFE PLL. */
207131d98677SRui Paulo 	rsu_write_1(sc, R92S_AFE_PLL_CTRL, 0x00);
207231d98677SRui Paulo 	/* Disable A15V. */
207331d98677SRui Paulo 	rsu_write_1(sc, R92S_LDOA15_CTRL, 0x54);
207431d98677SRui Paulo 	/* Disable eFuse 1.2V. */
207531d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, 0x50);
207631d98677SRui Paulo 	rsu_write_1(sc, R92S_LDOV12D_CTRL, 0x24);
207731d98677SRui Paulo 	/* Enable AFE macro block's bandgap and Mbias. */
207831d98677SRui Paulo 	rsu_write_1(sc, R92S_AFE_MISC, 0x30);
207931d98677SRui Paulo 	/* Disable 1.6V LDO. */
208031d98677SRui Paulo 	rsu_write_1(sc, R92S_SPS0_CTRL + 0, 0x56);
208131d98677SRui Paulo 	rsu_write_1(sc, R92S_SPS0_CTRL + 1, 0x43);
208231d98677SRui Paulo }
208331d98677SRui Paulo 
208431d98677SRui Paulo static int
20856d9b2f85SRui Paulo rsu_fw_loadsection(struct rsu_softc *sc, const uint8_t *buf, int len)
208631d98677SRui Paulo {
2087910593b5SHans Petter Selasky 	const uint8_t which = rsu_wme_ac_xfer_map[WME_AC_VO];
208831d98677SRui Paulo 	struct rsu_data *data;
208931d98677SRui Paulo 	struct r92s_tx_desc *txd;
209031d98677SRui Paulo 	int mlen;
209131d98677SRui Paulo 
209231d98677SRui Paulo 	while (len > 0) {
209331d98677SRui Paulo 		data = rsu_getbuf(sc);
209431d98677SRui Paulo 		if (data == NULL)
209531d98677SRui Paulo 			return (ENOMEM);
209631d98677SRui Paulo 		txd = (struct r92s_tx_desc *)data->buf;
209731d98677SRui Paulo 		memset(txd, 0, sizeof(*txd));
209831d98677SRui Paulo 		if (len <= RSU_TXBUFSZ - sizeof(*txd)) {
209931d98677SRui Paulo 			/* Last chunk. */
210031d98677SRui Paulo 			txd->txdw0 |= htole32(R92S_TXDW0_LINIP);
210131d98677SRui Paulo 			mlen = len;
210231d98677SRui Paulo 		} else
210331d98677SRui Paulo 			mlen = RSU_TXBUFSZ - sizeof(*txd);
210431d98677SRui Paulo 		txd->txdw0 |= htole32(SM(R92S_TXDW0_PKTLEN, mlen));
210531d98677SRui Paulo 		memcpy(&txd[1], buf, mlen);
210631d98677SRui Paulo 		data->buflen = sizeof(*txd) + mlen;
2107*4914fa0fSAdrian Chadd 		RSU_DPRINTF(sc, RSU_DEBUG_TX | RSU_DEBUG_FW | RSU_DEBUG_RESET,
2108*4914fa0fSAdrian Chadd 		    "%s: starting transfer %p\n",
2109*4914fa0fSAdrian Chadd 		    __func__, data);
2110400b4e53SHans Petter Selasky 		STAILQ_INSERT_TAIL(&sc->sc_tx_pending[which], data, next);
211131d98677SRui Paulo 		buf += mlen;
211231d98677SRui Paulo 		len -= mlen;
211331d98677SRui Paulo 	}
2114910593b5SHans Petter Selasky 	usbd_transfer_start(sc->sc_xfer[which]);
211531d98677SRui Paulo 	return (0);
211631d98677SRui Paulo }
211731d98677SRui Paulo 
211831d98677SRui Paulo static int
211931d98677SRui Paulo rsu_load_firmware(struct rsu_softc *sc)
212031d98677SRui Paulo {
21216d9b2f85SRui Paulo 	const struct r92s_fw_hdr *hdr;
212231d98677SRui Paulo 	struct r92s_fw_priv *dmem;
21236d9b2f85SRui Paulo 	const uint8_t *imem, *emem;
212431d98677SRui Paulo 	int imemsz, ememsz;
212531d98677SRui Paulo 	const struct firmware *fw;
212631d98677SRui Paulo 	size_t size;
212731d98677SRui Paulo 	uint32_t reg;
212831d98677SRui Paulo 	int ntries, error;
212931d98677SRui Paulo 
2130910593b5SHans Petter Selasky 	if (rsu_read_1(sc, R92S_TCR) & R92S_TCR_FWRDY) {
2131*4914fa0fSAdrian Chadd 		RSU_DPRINTF(sc, RSU_DEBUG_FW | RSU_DEBUG_RESET,
2132*4914fa0fSAdrian Chadd 		    "%s: Firmware already loaded\n",
2133*4914fa0fSAdrian Chadd 		    __func__);
2134910593b5SHans Petter Selasky 		return (0);
2135910593b5SHans Petter Selasky 	}
2136910593b5SHans Petter Selasky 
213731d98677SRui Paulo 	RSU_UNLOCK(sc);
213831d98677SRui Paulo 	/* Read firmware image from the filesystem. */
213931d98677SRui Paulo 	if ((fw = firmware_get("rsu-rtl8712fw")) == NULL) {
214031d98677SRui Paulo 		device_printf(sc->sc_dev,
214131d98677SRui Paulo 		    "%s: failed load firmware of file rsu-rtl8712fw\n",
214231d98677SRui Paulo 		    __func__);
214331d98677SRui Paulo 		RSU_LOCK(sc);
214431d98677SRui Paulo 		return (ENXIO);
214531d98677SRui Paulo 	}
214631d98677SRui Paulo 	RSU_LOCK(sc);
214731d98677SRui Paulo 	size = fw->datasize;
214831d98677SRui Paulo 	if (size < sizeof(*hdr)) {
214931d98677SRui Paulo 		device_printf(sc->sc_dev, "firmware too short\n");
215031d98677SRui Paulo 		error = EINVAL;
215131d98677SRui Paulo 		goto fail;
215231d98677SRui Paulo 	}
21536d9b2f85SRui Paulo 	hdr = (const struct r92s_fw_hdr *)fw->data;
215431d98677SRui Paulo 	if (hdr->signature != htole16(0x8712) &&
215531d98677SRui Paulo 	    hdr->signature != htole16(0x8192)) {
215631d98677SRui Paulo 		device_printf(sc->sc_dev,
215731d98677SRui Paulo 		    "invalid firmware signature 0x%x\n",
215831d98677SRui Paulo 		    le16toh(hdr->signature));
215931d98677SRui Paulo 		error = EINVAL;
216031d98677SRui Paulo 		goto fail;
216131d98677SRui Paulo 	}
216231d98677SRui Paulo 	DPRINTF("FW V%d %02x-%02x %02x:%02x\n", le16toh(hdr->version),
216331d98677SRui Paulo 	    hdr->month, hdr->day, hdr->hour, hdr->minute);
216431d98677SRui Paulo 
216531d98677SRui Paulo 	/* Make sure that driver and firmware are in sync. */
216631d98677SRui Paulo 	if (hdr->privsz != htole32(sizeof(*dmem))) {
216731d98677SRui Paulo 		device_printf(sc->sc_dev, "unsupported firmware image\n");
216831d98677SRui Paulo 		error = EINVAL;
216931d98677SRui Paulo 		goto fail;
217031d98677SRui Paulo 	}
217131d98677SRui Paulo 	/* Get FW sections sizes. */
217231d98677SRui Paulo 	imemsz = le32toh(hdr->imemsz);
217331d98677SRui Paulo 	ememsz = le32toh(hdr->sramsz);
217431d98677SRui Paulo 	/* Check that all FW sections fit in image. */
217531d98677SRui Paulo 	if (size < sizeof(*hdr) + imemsz + ememsz) {
217631d98677SRui Paulo 		device_printf(sc->sc_dev, "firmware too short\n");
217731d98677SRui Paulo 		error = EINVAL;
217831d98677SRui Paulo 		goto fail;
217931d98677SRui Paulo 	}
21806d9b2f85SRui Paulo 	imem = (const uint8_t *)&hdr[1];
218131d98677SRui Paulo 	emem = imem + imemsz;
218231d98677SRui Paulo 
218331d98677SRui Paulo 	/* Load IMEM section. */
218431d98677SRui Paulo 	error = rsu_fw_loadsection(sc, imem, imemsz);
218531d98677SRui Paulo 	if (error != 0) {
218631d98677SRui Paulo 		device_printf(sc->sc_dev,
218731d98677SRui Paulo 		    "could not load firmware section %s\n", "IMEM");
218831d98677SRui Paulo 		goto fail;
218931d98677SRui Paulo 	}
219031d98677SRui Paulo 	/* Wait for load to complete. */
2191885476cbSHans Petter Selasky 	for (ntries = 0; ntries != 50; ntries++) {
2192400b4e53SHans Petter Selasky 		usb_pause_mtx(&sc->sc_mtx, hz / 100);
2193910593b5SHans Petter Selasky 		reg = rsu_read_1(sc, R92S_TCR);
219431d98677SRui Paulo 		if (reg & R92S_TCR_IMEM_CODE_DONE)
219531d98677SRui Paulo 			break;
219631d98677SRui Paulo 	}
2197885476cbSHans Petter Selasky 	if (ntries == 50) {
2198885476cbSHans Petter Selasky 		device_printf(sc->sc_dev, "timeout waiting for IMEM transfer\n");
219931d98677SRui Paulo 		error = ETIMEDOUT;
220031d98677SRui Paulo 		goto fail;
220131d98677SRui Paulo 	}
220231d98677SRui Paulo 	/* Load EMEM section. */
220331d98677SRui Paulo 	error = rsu_fw_loadsection(sc, emem, ememsz);
220431d98677SRui Paulo 	if (error != 0) {
220531d98677SRui Paulo 		device_printf(sc->sc_dev,
220631d98677SRui Paulo 		    "could not load firmware section %s\n", "EMEM");
220731d98677SRui Paulo 		goto fail;
220831d98677SRui Paulo 	}
220931d98677SRui Paulo 	/* Wait for load to complete. */
22101593e875SHans Petter Selasky 	for (ntries = 0; ntries != 50; ntries++) {
2211400b4e53SHans Petter Selasky 		usb_pause_mtx(&sc->sc_mtx, hz / 100);
221231d98677SRui Paulo 		reg = rsu_read_2(sc, R92S_TCR);
221331d98677SRui Paulo 		if (reg & R92S_TCR_EMEM_CODE_DONE)
221431d98677SRui Paulo 			break;
221531d98677SRui Paulo 	}
22161593e875SHans Petter Selasky 	if (ntries == 50) {
2217885476cbSHans Petter Selasky 		device_printf(sc->sc_dev, "timeout waiting for EMEM transfer\n");
221831d98677SRui Paulo 		error = ETIMEDOUT;
221931d98677SRui Paulo 		goto fail;
222031d98677SRui Paulo 	}
222131d98677SRui Paulo 	/* Enable CPU. */
222231d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_CLKR,
222331d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_CLKR) | R92S_SYS_CPU_CLKSEL);
222431d98677SRui Paulo 	if (!(rsu_read_1(sc, R92S_SYS_CLKR) & R92S_SYS_CPU_CLKSEL)) {
222531d98677SRui Paulo 		device_printf(sc->sc_dev, "could not enable system clock\n");
222631d98677SRui Paulo 		error = EIO;
222731d98677SRui Paulo 		goto fail;
222831d98677SRui Paulo 	}
222931d98677SRui Paulo 	rsu_write_2(sc, R92S_SYS_FUNC_EN,
223031d98677SRui Paulo 	    rsu_read_2(sc, R92S_SYS_FUNC_EN) | R92S_FEN_CPUEN);
223131d98677SRui Paulo 	if (!(rsu_read_2(sc, R92S_SYS_FUNC_EN) & R92S_FEN_CPUEN)) {
223231d98677SRui Paulo 		device_printf(sc->sc_dev,
223331d98677SRui Paulo 		    "could not enable microcontroller\n");
223431d98677SRui Paulo 		error = EIO;
223531d98677SRui Paulo 		goto fail;
223631d98677SRui Paulo 	}
223731d98677SRui Paulo 	/* Wait for CPU to initialize. */
223831d98677SRui Paulo 	for (ntries = 0; ntries < 100; ntries++) {
2239910593b5SHans Petter Selasky 		if (rsu_read_1(sc, R92S_TCR) & R92S_TCR_IMEM_RDY)
224031d98677SRui Paulo 			break;
2241b41381aeSHans Petter Selasky 		rsu_ms_delay(sc);
224231d98677SRui Paulo 	}
224331d98677SRui Paulo 	if (ntries == 100) {
224431d98677SRui Paulo 		device_printf(sc->sc_dev,
224531d98677SRui Paulo 		    "timeout waiting for microcontroller\n");
224631d98677SRui Paulo 		error = ETIMEDOUT;
224731d98677SRui Paulo 		goto fail;
224831d98677SRui Paulo 	}
224931d98677SRui Paulo 
225031d98677SRui Paulo 	/* Update DMEM section before loading. */
22516d9b2f85SRui Paulo 	dmem = __DECONST(struct r92s_fw_priv *, &hdr->priv);
225231d98677SRui Paulo 	memset(dmem, 0, sizeof(*dmem));
225331d98677SRui Paulo 	dmem->hci_sel = R92S_HCI_SEL_USB | R92S_HCI_SEL_8172;
2254400b4e53SHans Petter Selasky 	dmem->nendpoints = 0;
225531d98677SRui Paulo 	dmem->rf_config = 0x12;	/* 1T2R */
225631d98677SRui Paulo 	dmem->vcs_type = R92S_VCS_TYPE_AUTO;
225731d98677SRui Paulo 	dmem->vcs_mode = R92S_VCS_MODE_RTS_CTS;
225831d98677SRui Paulo #ifdef notyet
225931d98677SRui Paulo 	dmem->bw40_en = (ic->ic_htcaps & IEEE80211_HTCAP_CBW20_40) != 0;
226031d98677SRui Paulo #endif
226131d98677SRui Paulo 	dmem->turbo_mode = 1;
226231d98677SRui Paulo 	/* Load DMEM section. */
226331d98677SRui Paulo 	error = rsu_fw_loadsection(sc, (uint8_t *)dmem, sizeof(*dmem));
226431d98677SRui Paulo 	if (error != 0) {
226531d98677SRui Paulo 		device_printf(sc->sc_dev,
226631d98677SRui Paulo 		    "could not load firmware section %s\n", "DMEM");
226731d98677SRui Paulo 		goto fail;
226831d98677SRui Paulo 	}
226931d98677SRui Paulo 	/* Wait for load to complete. */
227031d98677SRui Paulo 	for (ntries = 0; ntries < 100; ntries++) {
2271910593b5SHans Petter Selasky 		if (rsu_read_1(sc, R92S_TCR) & R92S_TCR_DMEM_CODE_DONE)
227231d98677SRui Paulo 			break;
2273b41381aeSHans Petter Selasky 		rsu_ms_delay(sc);
227431d98677SRui Paulo 	}
227531d98677SRui Paulo 	if (ntries == 100) {
227631d98677SRui Paulo 		device_printf(sc->sc_dev, "timeout waiting for %s transfer\n",
227731d98677SRui Paulo 		    "DMEM");
227831d98677SRui Paulo 		error = ETIMEDOUT;
227931d98677SRui Paulo 		goto fail;
228031d98677SRui Paulo 	}
228131d98677SRui Paulo 	/* Wait for firmware readiness. */
228231d98677SRui Paulo 	for (ntries = 0; ntries < 60; ntries++) {
2283910593b5SHans Petter Selasky 		if (!(rsu_read_1(sc, R92S_TCR) & R92S_TCR_FWRDY))
228431d98677SRui Paulo 			break;
2285b41381aeSHans Petter Selasky 		rsu_ms_delay(sc);
228631d98677SRui Paulo 	}
228731d98677SRui Paulo 	if (ntries == 60) {
228831d98677SRui Paulo 		device_printf(sc->sc_dev,
228931d98677SRui Paulo 		    "timeout waiting for firmware readiness\n");
229031d98677SRui Paulo 		error = ETIMEDOUT;
229131d98677SRui Paulo 		goto fail;
229231d98677SRui Paulo 	}
229331d98677SRui Paulo  fail:
229431d98677SRui Paulo 	firmware_put(fw, FIRMWARE_UNLOAD);
229531d98677SRui Paulo 	return (error);
229631d98677SRui Paulo }
229731d98677SRui Paulo 
229831d98677SRui Paulo 
229931d98677SRui Paulo static int
230031d98677SRui Paulo rsu_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
230131d98677SRui Paulo     const struct ieee80211_bpf_params *params)
230231d98677SRui Paulo {
230331d98677SRui Paulo 	struct ieee80211com *ic = ni->ni_ic;
2304d3fdd08cSAdrian Chadd 	struct rsu_softc *sc = ic->ic_softc;
230531d98677SRui Paulo 	struct rsu_data *bf;
230631d98677SRui Paulo 
230731d98677SRui Paulo 	/* prevent management frames from being sent if we're not ready */
23087a79cebfSGleb Smirnoff 	if (!sc->sc_running) {
230931d98677SRui Paulo 		m_freem(m);
231031d98677SRui Paulo 		ieee80211_free_node(ni);
231131d98677SRui Paulo 		return (ENETDOWN);
231231d98677SRui Paulo 	}
231331d98677SRui Paulo 	RSU_LOCK(sc);
231431d98677SRui Paulo 	bf = rsu_getbuf(sc);
231531d98677SRui Paulo 	if (bf == NULL) {
231631d98677SRui Paulo 		ieee80211_free_node(ni);
231731d98677SRui Paulo 		m_freem(m);
231831d98677SRui Paulo 		RSU_UNLOCK(sc);
231931d98677SRui Paulo 		return (ENOBUFS);
232031d98677SRui Paulo 	}
2321400b4e53SHans Petter Selasky 	if (rsu_tx_start(sc, ni, m, bf) != 0) {
232231d98677SRui Paulo 		ieee80211_free_node(ni);
232331d98677SRui Paulo 		STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, bf, next);
232431d98677SRui Paulo 		RSU_UNLOCK(sc);
232531d98677SRui Paulo 		return (EIO);
232631d98677SRui Paulo 	}
232731d98677SRui Paulo 	RSU_UNLOCK(sc);
232831d98677SRui Paulo 
232931d98677SRui Paulo 	return (0);
233031d98677SRui Paulo }
233131d98677SRui Paulo 
233231d98677SRui Paulo static void
23337a79cebfSGleb Smirnoff rsu_init(struct rsu_softc *sc)
233431d98677SRui Paulo {
23357a79cebfSGleb Smirnoff 	struct ieee80211com *ic = &sc->sc_ic;
23367a79cebfSGleb Smirnoff 	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
23377a79cebfSGleb Smirnoff 	uint8_t macaddr[IEEE80211_ADDR_LEN];
233831d98677SRui Paulo 	struct r92s_set_pwr_mode cmd;
233931d98677SRui Paulo 	int error;
2340885476cbSHans Petter Selasky 	int i;
234131d98677SRui Paulo 
23427a79cebfSGleb Smirnoff 	RSU_ASSERT_LOCKED(sc);
23437a79cebfSGleb Smirnoff 
234431d98677SRui Paulo 	/* Init host async commands ring. */
234531d98677SRui Paulo 	sc->cmdq.cur = sc->cmdq.next = sc->cmdq.queued = 0;
234631d98677SRui Paulo 
234731d98677SRui Paulo 	/* Power on adapter. */
234831d98677SRui Paulo 	if (sc->cut == 1)
234931d98677SRui Paulo 		rsu_power_on_acut(sc);
235031d98677SRui Paulo 	else
235131d98677SRui Paulo 		rsu_power_on_bcut(sc);
2352910593b5SHans Petter Selasky 
235331d98677SRui Paulo 	/* Load firmware. */
235431d98677SRui Paulo 	error = rsu_load_firmware(sc);
235531d98677SRui Paulo 	if (error != 0)
235631d98677SRui Paulo 		goto fail;
235731d98677SRui Paulo 
235831d98677SRui Paulo 	/* Enable Rx TCP checksum offload. */
235931d98677SRui Paulo 	rsu_write_4(sc, R92S_RCR,
236031d98677SRui Paulo 	    rsu_read_4(sc, R92S_RCR) | 0x04000000);
236131d98677SRui Paulo 	/* Append PHY status. */
236231d98677SRui Paulo 	rsu_write_4(sc, R92S_RCR,
236331d98677SRui Paulo 	    rsu_read_4(sc, R92S_RCR) | 0x02000000);
236431d98677SRui Paulo 
236531d98677SRui Paulo 	rsu_write_4(sc, R92S_CR,
236631d98677SRui Paulo 	    rsu_read_4(sc, R92S_CR) & ~0xff000000);
236731d98677SRui Paulo 
236831d98677SRui Paulo 	/* Use 128 bytes pages. */
236931d98677SRui Paulo 	rsu_write_1(sc, 0x00b5,
237031d98677SRui Paulo 	    rsu_read_1(sc, 0x00b5) | 0x01);
237131d98677SRui Paulo 	/* Enable USB Rx aggregation. */
237231d98677SRui Paulo 	rsu_write_1(sc, 0x00bd,
237331d98677SRui Paulo 	    rsu_read_1(sc, 0x00bd) | 0x80);
237431d98677SRui Paulo 	/* Set USB Rx aggregation threshold. */
237531d98677SRui Paulo 	rsu_write_1(sc, 0x00d9, 0x01);
237631d98677SRui Paulo 	/* Set USB Rx aggregation timeout (1.7ms/4). */
237731d98677SRui Paulo 	rsu_write_1(sc, 0xfe5b, 0x04);
237831d98677SRui Paulo 	/* Fix USB Rx FIFO issue. */
237931d98677SRui Paulo 	rsu_write_1(sc, 0xfe5c,
238031d98677SRui Paulo 	    rsu_read_1(sc, 0xfe5c) | 0x80);
238131d98677SRui Paulo 
238231d98677SRui Paulo 	/* Set MAC address. */
23837a79cebfSGleb Smirnoff 	IEEE80211_ADDR_COPY(macaddr, vap ? vap->iv_myaddr : ic->ic_macaddr);
23847a79cebfSGleb Smirnoff 	rsu_write_region_1(sc, R92S_MACID, macaddr, IEEE80211_ADDR_LEN);
238531d98677SRui Paulo 
2386b41381aeSHans Petter Selasky 	/* It really takes 1.5 seconds for the firmware to boot: */
2387b41381aeSHans Petter Selasky 	usb_pause_mtx(&sc->sc_mtx, (3 * hz) / 2);
238831d98677SRui Paulo 
2389*4914fa0fSAdrian Chadd 	RSU_DPRINTF(sc, RSU_DEBUG_RESET, "%s: setting MAC address to %s\n",
2390*4914fa0fSAdrian Chadd 	    __func__,
2391*4914fa0fSAdrian Chadd 	    ether_sprintf(macaddr));
23927a79cebfSGleb Smirnoff 	error = rsu_fw_cmd(sc, R92S_CMD_SET_MAC_ADDRESS, macaddr,
239331d98677SRui Paulo 	    IEEE80211_ADDR_LEN);
239431d98677SRui Paulo 	if (error != 0) {
239531d98677SRui Paulo 		device_printf(sc->sc_dev, "could not set MAC address\n");
239631d98677SRui Paulo 		goto fail;
239731d98677SRui Paulo 	}
239831d98677SRui Paulo 
239931d98677SRui Paulo 	rsu_write_1(sc, R92S_USB_HRPWM,
240031d98677SRui Paulo 	    R92S_USB_HRPWM_PS_ST_ACTIVE | R92S_USB_HRPWM_PS_ALL_ON);
240131d98677SRui Paulo 
2402*4914fa0fSAdrian Chadd 	/* XXX non-configurable psmode? */
240331d98677SRui Paulo 	memset(&cmd, 0, sizeof(cmd));
240431d98677SRui Paulo 	cmd.mode = R92S_PS_MODE_ACTIVE;
2405*4914fa0fSAdrian Chadd 	RSU_DPRINTF(sc, RSU_DEBUG_RESET, "%s: setting ps mode to %d\n",
2406*4914fa0fSAdrian Chadd 	    __func__, cmd.mode);
240731d98677SRui Paulo 	error = rsu_fw_cmd(sc, R92S_CMD_SET_PWR_MODE, &cmd, sizeof(cmd));
240831d98677SRui Paulo 	if (error != 0) {
240931d98677SRui Paulo 		device_printf(sc->sc_dev, "could not set PS mode\n");
241031d98677SRui Paulo 		goto fail;
241131d98677SRui Paulo 	}
241231d98677SRui Paulo 
241331d98677SRui Paulo #if 0
241431d98677SRui Paulo 	if (ic->ic_htcaps & IEEE80211_HTCAP_CBW20_40) {
241531d98677SRui Paulo 		/* Enable 40MHz mode. */
241631d98677SRui Paulo 		error = rsu_fw_iocmd(sc,
241731d98677SRui Paulo 		    SM(R92S_IOCMD_CLASS, 0xf4) |
241831d98677SRui Paulo 		    SM(R92S_IOCMD_INDEX, 0x00) |
241931d98677SRui Paulo 		    SM(R92S_IOCMD_VALUE, 0x0007));
242031d98677SRui Paulo 		if (error != 0) {
242131d98677SRui Paulo 			device_printf(sc->sc_dev,
242231d98677SRui Paulo 			    "could not enable 40MHz mode\n");
242331d98677SRui Paulo 			goto fail;
242431d98677SRui Paulo 		}
242531d98677SRui Paulo 	}
242631d98677SRui Paulo 
242731d98677SRui Paulo 	/* Set default channel. */
242831d98677SRui Paulo 	ic->ic_bss->ni_chan = ic->ic_ibss_chan;
242931d98677SRui Paulo #endif
24307a79cebfSGleb Smirnoff 	sc->sc_scan_pass = 0;
243131d98677SRui Paulo 	usbd_transfer_start(sc->sc_xfer[RSU_BULK_RX]);
243231d98677SRui Paulo 
243331d98677SRui Paulo 	/* We're ready to go. */
24347a79cebfSGleb Smirnoff 	sc->sc_running = 1;
243531d98677SRui Paulo 	return;
243631d98677SRui Paulo fail:
2437885476cbSHans Petter Selasky 	/* Need to stop all failed transfers, if any */
2438885476cbSHans Petter Selasky 	for (i = 0; i != RSU_N_TRANSFER; i++)
2439885476cbSHans Petter Selasky 		usbd_transfer_stop(sc->sc_xfer[i]);
244031d98677SRui Paulo }
244131d98677SRui Paulo 
244231d98677SRui Paulo static void
24437a79cebfSGleb Smirnoff rsu_stop(struct rsu_softc *sc)
244431d98677SRui Paulo {
244531d98677SRui Paulo 	int i;
244631d98677SRui Paulo 
24477a79cebfSGleb Smirnoff 	sc->sc_running = 0;
244831d98677SRui Paulo 	sc->sc_calibrating = 0;
244931d98677SRui Paulo 	taskqueue_cancel_timeout(taskqueue_thread, &sc->calib_task, NULL);
245031d98677SRui Paulo 
245131d98677SRui Paulo 	/* Power off adapter. */
245231d98677SRui Paulo 	rsu_power_off(sc);
245331d98677SRui Paulo 
245431d98677SRui Paulo 	for (i = 0; i < RSU_N_TRANSFER; i++)
245531d98677SRui Paulo 		usbd_transfer_stop(sc->sc_xfer[i]);
245631d98677SRui Paulo }
245731d98677SRui Paulo 
2458b41381aeSHans Petter Selasky static void
2459b41381aeSHans Petter Selasky rsu_ms_delay(struct rsu_softc *sc)
2460b41381aeSHans Petter Selasky {
2461b41381aeSHans Petter Selasky 	usb_pause_mtx(&sc->sc_mtx, hz / 1000);
2462b41381aeSHans Petter Selasky }
2463