xref: /freebsd/sys/dev/usb/wlan/if_rsu.c (revision 7a79cebfbac5e1de4648e0385b828bd161e05874)
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  */
2931d98677SRui Paulo #include <sys/param.h>
3031d98677SRui Paulo #include <sys/endian.h>
3131d98677SRui Paulo #include <sys/sockio.h>
3231d98677SRui Paulo #include <sys/mbuf.h>
3331d98677SRui Paulo #include <sys/kernel.h>
3431d98677SRui Paulo #include <sys/socket.h>
3531d98677SRui Paulo #include <sys/systm.h>
3631d98677SRui Paulo #include <sys/conf.h>
3731d98677SRui Paulo #include <sys/bus.h>
3831d98677SRui Paulo #include <sys/rman.h>
3931d98677SRui Paulo #include <sys/firmware.h>
4031d98677SRui Paulo #include <sys/module.h>
4131d98677SRui Paulo 
4231d98677SRui Paulo #include <machine/bus.h>
4331d98677SRui Paulo #include <machine/resource.h>
4431d98677SRui Paulo 
4531d98677SRui Paulo #include <net/bpf.h>
4631d98677SRui Paulo #include <net/if.h>
4776039bc8SGleb Smirnoff #include <net/if_var.h>
4831d98677SRui Paulo #include <net/if_arp.h>
4931d98677SRui Paulo #include <net/if_dl.h>
5031d98677SRui Paulo #include <net/if_media.h>
5131d98677SRui Paulo #include <net/if_types.h>
5231d98677SRui Paulo 
5331d98677SRui Paulo #include <netinet/in.h>
5431d98677SRui Paulo #include <netinet/in_systm.h>
5531d98677SRui Paulo #include <netinet/in_var.h>
5631d98677SRui Paulo #include <netinet/if_ether.h>
5731d98677SRui Paulo #include <netinet/ip.h>
5831d98677SRui Paulo 
5931d98677SRui Paulo #include <net80211/ieee80211_var.h>
6031d98677SRui Paulo #include <net80211/ieee80211_regdomain.h>
6131d98677SRui Paulo #include <net80211/ieee80211_radiotap.h>
6231d98677SRui Paulo 
6331d98677SRui Paulo #include <dev/usb/usb.h>
6431d98677SRui Paulo #include <dev/usb/usbdi.h>
6531d98677SRui Paulo #include "usbdevs.h"
6631d98677SRui Paulo 
6731d98677SRui Paulo #define USB_DEBUG_VAR rsu_debug
6831d98677SRui Paulo #include <dev/usb/usb_debug.h>
6931d98677SRui Paulo 
7031d98677SRui Paulo #include <dev/usb/wlan/if_rsureg.h>
7131d98677SRui Paulo 
7231d98677SRui Paulo #ifdef USB_DEBUG
7331d98677SRui Paulo static int rsu_debug = 0;
7431d98677SRui Paulo SYSCTL_NODE(_hw_usb, OID_AUTO, rsu, CTLFLAG_RW, 0, "USB rsu");
75ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb_rsu, OID_AUTO, debug, CTLFLAG_RWTUN, &rsu_debug, 0,
7631d98677SRui Paulo     "Debug level");
7731d98677SRui Paulo #endif
7831d98677SRui Paulo 
7931d98677SRui Paulo static const STRUCT_USB_HOST_ID rsu_devs[] = {
8031d98677SRui Paulo #define	RSU_HT_NOT_SUPPORTED 0
8131d98677SRui Paulo #define	RSU_HT_SUPPORTED 1
8231d98677SRui Paulo #define RSU_DEV_HT(v,p)  { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, \
8331d98677SRui Paulo 				   RSU_HT_SUPPORTED) }
8431d98677SRui Paulo #define RSU_DEV(v,p)     { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, \
8531d98677SRui Paulo 				   RSU_HT_NOT_SUPPORTED) }
8631d98677SRui Paulo 	RSU_DEV(ASUS,			RTL8192SU),
8731d98677SRui Paulo 	RSU_DEV(AZUREWAVE,		RTL8192SU_4),
8831d98677SRui Paulo 	RSU_DEV_HT(ACCTON,		RTL8192SU),
8931d98677SRui Paulo 	RSU_DEV_HT(ASUS,		USBN10),
9031d98677SRui Paulo 	RSU_DEV_HT(AZUREWAVE,		RTL8192SU_1),
9131d98677SRui Paulo 	RSU_DEV_HT(AZUREWAVE,		RTL8192SU_2),
9231d98677SRui Paulo 	RSU_DEV_HT(AZUREWAVE,		RTL8192SU_3),
9331d98677SRui Paulo 	RSU_DEV_HT(AZUREWAVE,		RTL8192SU_5),
9431d98677SRui Paulo 	RSU_DEV_HT(BELKIN,		RTL8192SU_1),
9531d98677SRui Paulo 	RSU_DEV_HT(BELKIN,		RTL8192SU_2),
9631d98677SRui Paulo 	RSU_DEV_HT(BELKIN,		RTL8192SU_3),
9731d98677SRui Paulo 	RSU_DEV_HT(CONCEPTRONIC2,	RTL8192SU_1),
9831d98677SRui Paulo 	RSU_DEV_HT(CONCEPTRONIC2,	RTL8192SU_2),
9931d98677SRui Paulo 	RSU_DEV_HT(CONCEPTRONIC2,	RTL8192SU_3),
10031d98677SRui Paulo 	RSU_DEV_HT(COREGA,		RTL8192SU),
10131d98677SRui Paulo 	RSU_DEV_HT(DLINK2,		DWA131A1),
10231d98677SRui Paulo 	RSU_DEV_HT(DLINK2,		RTL8192SU_1),
10331d98677SRui Paulo 	RSU_DEV_HT(DLINK2,		RTL8192SU_2),
10431d98677SRui Paulo 	RSU_DEV_HT(EDIMAX,		RTL8192SU_1),
10531d98677SRui Paulo 	RSU_DEV_HT(EDIMAX,		RTL8192SU_2),
106bf124fcfSKevin Lo 	RSU_DEV_HT(EDIMAX,		EW7622UMN),
10731d98677SRui Paulo 	RSU_DEV_HT(GUILLEMOT,		HWGUN54),
10831d98677SRui Paulo 	RSU_DEV_HT(GUILLEMOT,		HWNUM300),
10931d98677SRui Paulo 	RSU_DEV_HT(HAWKING,		RTL8192SU_1),
11031d98677SRui Paulo 	RSU_DEV_HT(HAWKING,		RTL8192SU_2),
11131d98677SRui Paulo 	RSU_DEV_HT(PLANEX2,		GWUSNANO),
11231d98677SRui Paulo 	RSU_DEV_HT(REALTEK,		RTL8171),
11331d98677SRui Paulo 	RSU_DEV_HT(REALTEK,		RTL8172),
11431d98677SRui Paulo 	RSU_DEV_HT(REALTEK,		RTL8173),
11531d98677SRui Paulo 	RSU_DEV_HT(REALTEK,		RTL8174),
11631d98677SRui Paulo 	RSU_DEV_HT(REALTEK,		RTL8192SU),
11731d98677SRui Paulo 	RSU_DEV_HT(REALTEK,		RTL8712),
11831d98677SRui Paulo 	RSU_DEV_HT(REALTEK,		RTL8713),
11931d98677SRui Paulo 	RSU_DEV_HT(SENAO,		RTL8192SU_1),
12031d98677SRui Paulo 	RSU_DEV_HT(SENAO,		RTL8192SU_2),
12131d98677SRui Paulo 	RSU_DEV_HT(SITECOMEU,		WL349V1),
12231d98677SRui Paulo 	RSU_DEV_HT(SITECOMEU,		WL353),
12331d98677SRui Paulo 	RSU_DEV_HT(SWEEX2,		LW154),
124fa20eb98SKevin Lo 	RSU_DEV_HT(TRENDNET,		TEW646UBH),
12531d98677SRui Paulo #undef RSU_DEV_HT
12631d98677SRui Paulo #undef RSU_DEV
12731d98677SRui Paulo };
12831d98677SRui Paulo 
12931d98677SRui Paulo static device_probe_t   rsu_match;
13031d98677SRui Paulo static device_attach_t  rsu_attach;
13131d98677SRui Paulo static device_detach_t  rsu_detach;
132910593b5SHans Petter Selasky static usb_callback_t   rsu_bulk_tx_callback_be_bk;
133910593b5SHans Petter Selasky static usb_callback_t   rsu_bulk_tx_callback_vi_vo;
13431d98677SRui Paulo static usb_callback_t   rsu_bulk_rx_callback;
13531d98677SRui Paulo static usb_error_t	rsu_do_request(struct rsu_softc *,
13631d98677SRui Paulo 			    struct usb_device_request *, void *);
13731d98677SRui Paulo static struct ieee80211vap *
13831d98677SRui Paulo 		rsu_vap_create(struct ieee80211com *, const char name[],
13931d98677SRui Paulo 		    int, enum ieee80211_opmode, int, const uint8_t bssid[],
14031d98677SRui Paulo 		    const uint8_t mac[]);
14131d98677SRui Paulo static void	rsu_vap_delete(struct ieee80211vap *);
14231d98677SRui Paulo static void	rsu_scan_start(struct ieee80211com *);
14331d98677SRui Paulo static void	rsu_scan_end(struct ieee80211com *);
14431d98677SRui Paulo static void	rsu_set_channel(struct ieee80211com *);
145272f6adeSGleb Smirnoff static void	rsu_update_mcast(struct ieee80211com *);
14631d98677SRui Paulo static int	rsu_alloc_rx_list(struct rsu_softc *);
14731d98677SRui Paulo static void	rsu_free_rx_list(struct rsu_softc *);
14831d98677SRui Paulo static int	rsu_alloc_tx_list(struct rsu_softc *);
14931d98677SRui Paulo static void	rsu_free_tx_list(struct rsu_softc *);
15031d98677SRui Paulo static void	rsu_free_list(struct rsu_softc *, struct rsu_data [], int);
15131d98677SRui Paulo static struct rsu_data *_rsu_getbuf(struct rsu_softc *);
15231d98677SRui Paulo static struct rsu_data *rsu_getbuf(struct rsu_softc *);
15331d98677SRui Paulo static int	rsu_write_region_1(struct rsu_softc *, uint16_t, uint8_t *,
15431d98677SRui Paulo 		    int);
15531d98677SRui Paulo static void	rsu_write_1(struct rsu_softc *, uint16_t, uint8_t);
15631d98677SRui Paulo static void	rsu_write_2(struct rsu_softc *, uint16_t, uint16_t);
15731d98677SRui Paulo static void	rsu_write_4(struct rsu_softc *, uint16_t, uint32_t);
15831d98677SRui Paulo static int	rsu_read_region_1(struct rsu_softc *, uint16_t, uint8_t *,
15931d98677SRui Paulo 		    int);
16031d98677SRui Paulo static uint8_t	rsu_read_1(struct rsu_softc *, uint16_t);
16131d98677SRui Paulo static uint16_t	rsu_read_2(struct rsu_softc *, uint16_t);
16231d98677SRui Paulo static uint32_t	rsu_read_4(struct rsu_softc *, uint16_t);
16331d98677SRui Paulo static int	rsu_fw_iocmd(struct rsu_softc *, uint32_t);
16431d98677SRui Paulo static uint8_t	rsu_efuse_read_1(struct rsu_softc *, uint16_t);
16531d98677SRui Paulo static int	rsu_read_rom(struct rsu_softc *);
16631d98677SRui Paulo static int	rsu_fw_cmd(struct rsu_softc *, uint8_t, void *, int);
16731d98677SRui Paulo static void	rsu_calib_task(void *, int);
16831d98677SRui Paulo static int	rsu_newstate(struct ieee80211vap *, enum ieee80211_state, int);
16931d98677SRui Paulo #ifdef notyet
17031d98677SRui Paulo static void	rsu_set_key(struct rsu_softc *, const struct ieee80211_key *);
17131d98677SRui Paulo static void	rsu_delete_key(struct rsu_softc *, const struct ieee80211_key *);
17231d98677SRui Paulo #endif
17331d98677SRui Paulo static int	rsu_site_survey(struct rsu_softc *, struct ieee80211vap *);
17431d98677SRui Paulo static int	rsu_join_bss(struct rsu_softc *, struct ieee80211_node *);
17531d98677SRui Paulo static int	rsu_disconnect(struct rsu_softc *);
17631d98677SRui Paulo static void	rsu_event_survey(struct rsu_softc *, uint8_t *, int);
17731d98677SRui Paulo static void	rsu_event_join_bss(struct rsu_softc *, uint8_t *, int);
17831d98677SRui Paulo static void	rsu_rx_event(struct rsu_softc *, uint8_t, uint8_t *, int);
17931d98677SRui Paulo static void	rsu_rx_multi_event(struct rsu_softc *, uint8_t *, int);
18031d98677SRui Paulo static int8_t	rsu_get_rssi(struct rsu_softc *, int, void *);
18131d98677SRui Paulo static struct mbuf *
18231d98677SRui Paulo 		rsu_rx_frame(struct rsu_softc *, uint8_t *, int, int *);
18331d98677SRui Paulo static struct mbuf *
18431d98677SRui Paulo 		rsu_rx_multi_frame(struct rsu_softc *, uint8_t *, int, int *);
18531d98677SRui Paulo static struct mbuf *
18631d98677SRui Paulo 		rsu_rxeof(struct usb_xfer *, struct rsu_data *, int *);
18731d98677SRui Paulo static void	rsu_txeof(struct usb_xfer *, struct rsu_data *);
18831d98677SRui Paulo static int	rsu_raw_xmit(struct ieee80211_node *, struct mbuf *,
18931d98677SRui Paulo 		    const struct ieee80211_bpf_params *);
190*7a79cebfSGleb Smirnoff static void	rsu_init(struct rsu_softc *);
19131d98677SRui Paulo static int	rsu_tx_start(struct rsu_softc *, struct ieee80211_node *,
192400b4e53SHans Petter Selasky 		    struct mbuf *, struct rsu_data *);
193*7a79cebfSGleb Smirnoff static int	rsu_transmit(struct ieee80211com *, struct mbuf *);
194*7a79cebfSGleb Smirnoff static void	rsu_start(struct rsu_softc *);
195*7a79cebfSGleb Smirnoff static void	rsu_parent(struct ieee80211com *);
196*7a79cebfSGleb Smirnoff static void	rsu_stop(struct rsu_softc *);
197b41381aeSHans Petter Selasky static void	rsu_ms_delay(struct rsu_softc *);
19831d98677SRui Paulo 
19931d98677SRui Paulo static device_method_t rsu_methods[] = {
20031d98677SRui Paulo 	DEVMETHOD(device_probe,		rsu_match),
20131d98677SRui Paulo 	DEVMETHOD(device_attach,	rsu_attach),
20231d98677SRui Paulo 	DEVMETHOD(device_detach,	rsu_detach),
20331d98677SRui Paulo 
20431d98677SRui Paulo 	DEVMETHOD_END
20531d98677SRui Paulo };
20631d98677SRui Paulo 
20731d98677SRui Paulo static driver_t rsu_driver = {
20831d98677SRui Paulo 	.name = "rsu",
20931d98677SRui Paulo 	.methods = rsu_methods,
21031d98677SRui Paulo 	.size = sizeof(struct rsu_softc)
21131d98677SRui Paulo };
21231d98677SRui Paulo 
21331d98677SRui Paulo static devclass_t rsu_devclass;
21431d98677SRui Paulo 
21531d98677SRui Paulo DRIVER_MODULE(rsu, uhub, rsu_driver, rsu_devclass, NULL, 0);
21631d98677SRui Paulo MODULE_DEPEND(rsu, wlan, 1, 1, 1);
21731d98677SRui Paulo MODULE_DEPEND(rsu, usb, 1, 1, 1);
21831d98677SRui Paulo MODULE_DEPEND(rsu, firmware, 1, 1, 1);
21931d98677SRui Paulo MODULE_VERSION(rsu, 1);
22031d98677SRui Paulo 
221910593b5SHans Petter Selasky static uint8_t rsu_wme_ac_xfer_map[4] = {
222910593b5SHans Petter Selasky 	[WME_AC_BE] = RSU_BULK_TX_BE_BK,
223910593b5SHans Petter Selasky 	[WME_AC_BK] = RSU_BULK_TX_BE_BK,
224910593b5SHans Petter Selasky 	[WME_AC_VI] = RSU_BULK_TX_VI_VO,
225910593b5SHans Petter Selasky 	[WME_AC_VO] = RSU_BULK_TX_VI_VO,
226910593b5SHans Petter Selasky };
227910593b5SHans Petter Selasky 
22831d98677SRui Paulo static const struct usb_config rsu_config[RSU_N_TRANSFER] = {
22931d98677SRui Paulo 	[RSU_BULK_RX] = {
23031d98677SRui Paulo 		.type = UE_BULK,
23131d98677SRui Paulo 		.endpoint = UE_ADDR_ANY,
23231d98677SRui Paulo 		.direction = UE_DIR_IN,
23331d98677SRui Paulo 		.bufsize = RSU_RXBUFSZ,
23431d98677SRui Paulo 		.flags = {
23531d98677SRui Paulo 			.pipe_bof = 1,
23631d98677SRui Paulo 			.short_xfer_ok = 1
23731d98677SRui Paulo 		},
23831d98677SRui Paulo 		.callback = rsu_bulk_rx_callback
23931d98677SRui Paulo 	},
240910593b5SHans Petter Selasky 	[RSU_BULK_TX_BE_BK] = {
24131d98677SRui Paulo 		.type = UE_BULK,
24231d98677SRui Paulo 		.endpoint = 0x06,
24331d98677SRui Paulo 		.direction = UE_DIR_OUT,
24431d98677SRui Paulo 		.bufsize = RSU_TXBUFSZ,
24531d98677SRui Paulo 		.flags = {
24631d98677SRui Paulo 			.ext_buffer = 1,
24731d98677SRui Paulo 			.pipe_bof = 1,
24831d98677SRui Paulo 			.force_short_xfer = 1
24931d98677SRui Paulo 		},
250910593b5SHans Petter Selasky 		.callback = rsu_bulk_tx_callback_be_bk,
25131d98677SRui Paulo 		.timeout = RSU_TX_TIMEOUT
25231d98677SRui Paulo 	},
253910593b5SHans Petter Selasky 	[RSU_BULK_TX_VI_VO] = {
25431d98677SRui Paulo 		.type = UE_BULK,
25531d98677SRui Paulo 		.endpoint = 0x04,
25631d98677SRui Paulo 		.direction = UE_DIR_OUT,
25731d98677SRui Paulo 		.bufsize = RSU_TXBUFSZ,
25831d98677SRui Paulo 		.flags = {
25931d98677SRui Paulo 			.ext_buffer = 1,
26031d98677SRui Paulo 			.pipe_bof = 1,
26131d98677SRui Paulo 			.force_short_xfer = 1
26231d98677SRui Paulo 		},
263910593b5SHans Petter Selasky 		.callback = rsu_bulk_tx_callback_vi_vo,
26431d98677SRui Paulo 		.timeout = RSU_TX_TIMEOUT
26531d98677SRui Paulo 	},
26631d98677SRui Paulo };
26731d98677SRui Paulo 
26831d98677SRui Paulo static int
26931d98677SRui Paulo rsu_match(device_t self)
27031d98677SRui Paulo {
27131d98677SRui Paulo 	struct usb_attach_arg *uaa = device_get_ivars(self);
27231d98677SRui Paulo 
27331d98677SRui Paulo 	if (uaa->usb_mode != USB_MODE_HOST ||
27431d98677SRui Paulo 	    uaa->info.bIfaceIndex != 0 ||
27531d98677SRui Paulo 	    uaa->info.bConfigIndex != 0)
27631d98677SRui Paulo 		return (ENXIO);
27731d98677SRui Paulo 
27831d98677SRui Paulo 	return (usbd_lookup_id_by_uaa(rsu_devs, sizeof(rsu_devs), uaa));
27931d98677SRui Paulo }
28031d98677SRui Paulo 
28131d98677SRui Paulo static int
28231d98677SRui Paulo rsu_attach(device_t self)
28331d98677SRui Paulo {
28431d98677SRui Paulo 	struct usb_attach_arg *uaa = device_get_ivars(self);
28531d98677SRui Paulo 	struct rsu_softc *sc = device_get_softc(self);
286*7a79cebfSGleb Smirnoff 	struct ieee80211com *ic = &sc->sc_ic;
28731d98677SRui Paulo 	int error;
28831d98677SRui Paulo 	uint8_t iface_index, bands;
28931d98677SRui Paulo 
29031d98677SRui Paulo 	device_set_usb_desc(self);
29131d98677SRui Paulo 	sc->sc_udev = uaa->device;
29231d98677SRui Paulo 	sc->sc_dev = self;
29331d98677SRui Paulo 
29431d98677SRui Paulo 	mtx_init(&sc->sc_mtx, device_get_nameunit(self), MTX_NETWORK_LOCK,
29531d98677SRui Paulo 	    MTX_DEF);
29631d98677SRui Paulo 	TIMEOUT_TASK_INIT(taskqueue_thread, &sc->calib_task, 0,
29731d98677SRui Paulo 	    rsu_calib_task, sc);
298*7a79cebfSGleb Smirnoff 	mbufq_init(&sc->sc_snd, ifqmaxlen);
29931d98677SRui Paulo 
300885476cbSHans Petter Selasky 	/* Allocate Tx/Rx buffers. */
301885476cbSHans Petter Selasky 	error = rsu_alloc_rx_list(sc);
302885476cbSHans Petter Selasky 	if (error != 0) {
303885476cbSHans Petter Selasky 		device_printf(sc->sc_dev, "could not allocate Rx buffers\n");
304885476cbSHans Petter Selasky 		goto fail_usb;
305885476cbSHans Petter Selasky 	}
306885476cbSHans Petter Selasky 
307885476cbSHans Petter Selasky 	error = rsu_alloc_tx_list(sc);
308885476cbSHans Petter Selasky 	if (error != 0) {
309885476cbSHans Petter Selasky 		device_printf(sc->sc_dev, "could not allocate Tx buffers\n");
310885476cbSHans Petter Selasky 		rsu_free_rx_list(sc);
311885476cbSHans Petter Selasky 		goto fail_usb;
312885476cbSHans Petter Selasky 	}
313885476cbSHans Petter Selasky 
31431d98677SRui Paulo 	iface_index = 0;
31531d98677SRui Paulo 	error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer,
31631d98677SRui Paulo 	    rsu_config, RSU_N_TRANSFER, sc, &sc->sc_mtx);
31731d98677SRui Paulo 	if (error) {
31831d98677SRui Paulo 		device_printf(sc->sc_dev,
31931d98677SRui Paulo 		    "could not allocate USB transfers, err=%s\n",
32031d98677SRui Paulo 		    usbd_errstr(error));
32153dfd5c1SRui Paulo 		goto fail_usb;
32231d98677SRui Paulo 	}
32331d98677SRui Paulo 	RSU_LOCK(sc);
32431d98677SRui Paulo 	/* Read chip revision. */
32531d98677SRui Paulo 	sc->cut = MS(rsu_read_4(sc, R92S_PMC_FSM), R92S_PMC_FSM_CUT);
32631d98677SRui Paulo 	if (sc->cut != 3)
32731d98677SRui Paulo 		sc->cut = (sc->cut >> 1) + 1;
32831d98677SRui Paulo 	error = rsu_read_rom(sc);
329abfa11d6SHans Petter Selasky 	RSU_UNLOCK(sc);
33031d98677SRui Paulo 	if (error != 0) {
33131d98677SRui Paulo 		device_printf(self, "could not read ROM\n");
33253dfd5c1SRui Paulo 		goto fail_rom;
33331d98677SRui Paulo 	}
334*7a79cebfSGleb Smirnoff 	IEEE80211_ADDR_COPY(ic->ic_macaddr, &sc->rom[0x12]);
33531d98677SRui Paulo 	device_printf(self, "MAC/BB RTL8712 cut %d\n", sc->cut);
33631d98677SRui Paulo 
33759686fe9SGleb Smirnoff 	ic->ic_softc = sc;
338c8550c02SGleb Smirnoff 	ic->ic_name = device_get_nameunit(self);
33931d98677SRui Paulo 	ic->ic_phytype = IEEE80211_T_OFDM;	/* Not only, but not used. */
34031d98677SRui Paulo 	ic->ic_opmode = IEEE80211_M_STA;	/* Default to BSS mode. */
34131d98677SRui Paulo 
34231d98677SRui Paulo 	/* Set device capabilities. */
34331d98677SRui Paulo 	ic->ic_caps =
34431d98677SRui Paulo 	    IEEE80211_C_STA |		/* station mode */
34531d98677SRui Paulo 	    IEEE80211_C_BGSCAN |	/* Background scan. */
34631d98677SRui Paulo 	    IEEE80211_C_SHPREAMBLE |	/* Short preamble supported. */
34731d98677SRui Paulo 	    IEEE80211_C_SHSLOT |	/* Short slot time supported. */
34831d98677SRui Paulo 	    IEEE80211_C_WPA;		/* WPA/RSN. */
34931d98677SRui Paulo 
35031d98677SRui Paulo #if 0
35131d98677SRui Paulo 	/* Check if HT support is present. */
35231d98677SRui Paulo 	if (usb_lookup(rsu_devs_noht, uaa->vendor, uaa->product) == NULL) {
35331d98677SRui Paulo 		/* Set HT capabilities. */
35431d98677SRui Paulo 		ic->ic_htcaps =
35531d98677SRui Paulo 		    IEEE80211_HTCAP_CBW20_40 |
35631d98677SRui Paulo 		    IEEE80211_HTCAP_DSSSCCK40;
35731d98677SRui Paulo 		/* Set supported HT rates. */
35831d98677SRui Paulo 		for (i = 0; i < 2; i++)
35931d98677SRui Paulo 			ic->ic_sup_mcs[i] = 0xff;
36031d98677SRui Paulo 	}
36131d98677SRui Paulo #endif
36231d98677SRui Paulo 
36331d98677SRui Paulo 	/* Set supported .11b and .11g rates. */
36431d98677SRui Paulo 	bands = 0;
36531d98677SRui Paulo 	setbit(&bands, IEEE80211_MODE_11B);
36631d98677SRui Paulo 	setbit(&bands, IEEE80211_MODE_11G);
36731d98677SRui Paulo 	ieee80211_init_channels(ic, NULL, &bands);
36831d98677SRui Paulo 
369*7a79cebfSGleb Smirnoff 	ieee80211_ifattach(ic);
37031d98677SRui Paulo 	ic->ic_raw_xmit = rsu_raw_xmit;
37131d98677SRui Paulo 	ic->ic_scan_start = rsu_scan_start;
37231d98677SRui Paulo 	ic->ic_scan_end = rsu_scan_end;
37331d98677SRui Paulo 	ic->ic_set_channel = rsu_set_channel;
37431d98677SRui Paulo 	ic->ic_vap_create = rsu_vap_create;
37531d98677SRui Paulo 	ic->ic_vap_delete = rsu_vap_delete;
37631d98677SRui Paulo 	ic->ic_update_mcast = rsu_update_mcast;
377*7a79cebfSGleb Smirnoff 	ic->ic_parent = rsu_parent;
378*7a79cebfSGleb Smirnoff 	ic->ic_transmit = rsu_transmit;
37931d98677SRui Paulo 
38031d98677SRui Paulo 	ieee80211_radiotap_attach(ic, &sc->sc_txtap.wt_ihdr,
38131d98677SRui Paulo 	    sizeof(sc->sc_txtap), RSU_TX_RADIOTAP_PRESENT,
38231d98677SRui Paulo 	    &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap),
38331d98677SRui Paulo 	    RSU_RX_RADIOTAP_PRESENT);
38431d98677SRui Paulo 
38531d98677SRui Paulo 	if (bootverbose)
38631d98677SRui Paulo 		ieee80211_announce(ic);
38731d98677SRui Paulo 
38831d98677SRui Paulo 	return (0);
38931d98677SRui Paulo 
39053dfd5c1SRui Paulo fail_rom:
39153dfd5c1SRui Paulo 	usbd_transfer_unsetup(sc->sc_xfer, RSU_N_TRANSFER);
39253dfd5c1SRui Paulo fail_usb:
39353dfd5c1SRui Paulo 	mtx_destroy(&sc->sc_mtx);
39431d98677SRui Paulo 	return (ENXIO);
39531d98677SRui Paulo }
39631d98677SRui Paulo 
39731d98677SRui Paulo static int
39831d98677SRui Paulo rsu_detach(device_t self)
39931d98677SRui Paulo {
40031d98677SRui Paulo 	struct rsu_softc *sc = device_get_softc(self);
401*7a79cebfSGleb Smirnoff 	struct ieee80211com *ic = &sc->sc_ic;
40231d98677SRui Paulo 
403*7a79cebfSGleb Smirnoff 	RSU_LOCK(sc);
404*7a79cebfSGleb Smirnoff 	rsu_stop(sc);
405*7a79cebfSGleb Smirnoff 	RSU_UNLOCK(sc);
40631d98677SRui Paulo 	usbd_transfer_unsetup(sc->sc_xfer, RSU_N_TRANSFER);
40731d98677SRui Paulo 	ieee80211_ifdetach(ic);
40831d98677SRui Paulo 
40931d98677SRui Paulo 	taskqueue_drain_timeout(taskqueue_thread, &sc->calib_task);
41031d98677SRui Paulo 
41131d98677SRui Paulo 	/* Free Tx/Rx buffers. */
41231d98677SRui Paulo 	rsu_free_tx_list(sc);
41331d98677SRui Paulo 	rsu_free_rx_list(sc);
41431d98677SRui Paulo 
415*7a79cebfSGleb Smirnoff 	mbufq_drain(&sc->sc_snd);
41631d98677SRui Paulo 	mtx_destroy(&sc->sc_mtx);
41731d98677SRui Paulo 
41831d98677SRui Paulo 	return (0);
41931d98677SRui Paulo }
42031d98677SRui Paulo 
42131d98677SRui Paulo static usb_error_t
42231d98677SRui Paulo rsu_do_request(struct rsu_softc *sc, struct usb_device_request *req,
42331d98677SRui Paulo     void *data)
42431d98677SRui Paulo {
42531d98677SRui Paulo 	usb_error_t err;
42631d98677SRui Paulo 	int ntries = 10;
42731d98677SRui Paulo 
42831d98677SRui Paulo 	RSU_ASSERT_LOCKED(sc);
42931d98677SRui Paulo 
43031d98677SRui Paulo 	while (ntries--) {
43131d98677SRui Paulo 		err = usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx,
43231d98677SRui Paulo 		    req, data, 0, NULL, 250 /* ms */);
4337243077cSHans Petter Selasky 		if (err == 0 || err == USB_ERR_NOT_CONFIGURED)
43431d98677SRui Paulo 			break;
43531d98677SRui Paulo 		DPRINTFN(1, "Control request failed, %s (retrying)\n",
43631d98677SRui Paulo 		    usbd_errstr(err));
43731d98677SRui Paulo 		usb_pause_mtx(&sc->sc_mtx, hz / 100);
43831d98677SRui Paulo         }
43931d98677SRui Paulo 
44031d98677SRui Paulo         return (err);
44131d98677SRui Paulo }
44231d98677SRui Paulo 
44331d98677SRui Paulo static struct ieee80211vap *
44431d98677SRui Paulo rsu_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit,
44531d98677SRui Paulo     enum ieee80211_opmode opmode, int flags,
44631d98677SRui Paulo     const uint8_t bssid[IEEE80211_ADDR_LEN],
44731d98677SRui Paulo     const uint8_t mac[IEEE80211_ADDR_LEN])
44831d98677SRui Paulo {
44931d98677SRui Paulo 	struct rsu_vap *uvp;
45031d98677SRui Paulo 	struct ieee80211vap *vap;
45131d98677SRui Paulo 
45231d98677SRui Paulo 	if (!TAILQ_EMPTY(&ic->ic_vaps))         /* only one at a time */
45331d98677SRui Paulo 		return (NULL);
45431d98677SRui Paulo 
455*7a79cebfSGleb Smirnoff 	uvp =  malloc(sizeof(struct rsu_vap), M_80211_VAP, M_WAITOK | M_ZERO);
45631d98677SRui Paulo 	vap = &uvp->vap;
457bb2f69e8SHans Petter Selasky 
458bb2f69e8SHans Petter Selasky 	if (ieee80211_vap_setup(ic, vap, name, unit, opmode,
459*7a79cebfSGleb Smirnoff 	    flags, bssid) != 0) {
460bb2f69e8SHans Petter Selasky 		/* out of memory */
461bb2f69e8SHans Petter Selasky 		free(uvp, M_80211_VAP);
462bb2f69e8SHans Petter Selasky 		return (NULL);
463bb2f69e8SHans Petter Selasky 	}
46431d98677SRui Paulo 
46531d98677SRui Paulo 	/* override state transition machine */
46631d98677SRui Paulo 	uvp->newstate = vap->iv_newstate;
46731d98677SRui Paulo 	vap->iv_newstate = rsu_newstate;
46831d98677SRui Paulo 
46931d98677SRui Paulo 	/* complete setup */
47031d98677SRui Paulo 	ieee80211_vap_attach(vap, ieee80211_media_change,
471*7a79cebfSGleb Smirnoff 	    ieee80211_media_status, mac);
47231d98677SRui Paulo 	ic->ic_opmode = opmode;
47331d98677SRui Paulo 
47431d98677SRui Paulo 	return (vap);
47531d98677SRui Paulo }
47631d98677SRui Paulo 
47731d98677SRui Paulo static void
47831d98677SRui Paulo rsu_vap_delete(struct ieee80211vap *vap)
47931d98677SRui Paulo {
48031d98677SRui Paulo 	struct rsu_vap *uvp = RSU_VAP(vap);
48131d98677SRui Paulo 
48231d98677SRui Paulo 	ieee80211_vap_detach(vap);
48331d98677SRui Paulo 	free(uvp, M_80211_VAP);
48431d98677SRui Paulo }
48531d98677SRui Paulo 
48631d98677SRui Paulo static void
48731d98677SRui Paulo rsu_scan_start(struct ieee80211com *ic)
48831d98677SRui Paulo {
489d3fdd08cSAdrian Chadd 	struct rsu_softc *sc = ic->ic_softc;
490*7a79cebfSGleb Smirnoff 	int error;
49131d98677SRui Paulo 
49231d98677SRui Paulo 	/* Scanning is done by the firmware. */
49331d98677SRui Paulo 	RSU_LOCK(sc);
49431d98677SRui Paulo 	error = rsu_site_survey(sc, TAILQ_FIRST(&ic->ic_vaps));
49531d98677SRui Paulo 	RSU_UNLOCK(sc);
49631d98677SRui Paulo 	if (error != 0)
49731d98677SRui Paulo 		device_printf(sc->sc_dev,
49831d98677SRui Paulo 		    "could not send site survey command\n");
49931d98677SRui Paulo }
50031d98677SRui Paulo 
50131d98677SRui Paulo static void
50231d98677SRui Paulo rsu_scan_end(struct ieee80211com *ic)
50331d98677SRui Paulo {
50431d98677SRui Paulo 	/* Nothing to do here. */
50531d98677SRui Paulo }
50631d98677SRui Paulo 
50731d98677SRui Paulo static void
50831d98677SRui Paulo rsu_set_channel(struct ieee80211com *ic __unused)
50931d98677SRui Paulo {
51031d98677SRui Paulo 	/* We are unable to switch channels, yet. */
51131d98677SRui Paulo }
51231d98677SRui Paulo 
51331d98677SRui Paulo static void
514272f6adeSGleb Smirnoff rsu_update_mcast(struct ieee80211com *ic)
51531d98677SRui Paulo {
51631d98677SRui Paulo         /* XXX do nothing?  */
51731d98677SRui Paulo }
51831d98677SRui Paulo 
51931d98677SRui Paulo static int
52031d98677SRui Paulo rsu_alloc_list(struct rsu_softc *sc, struct rsu_data data[],
52131d98677SRui Paulo     int ndata, int maxsz)
52231d98677SRui Paulo {
52331d98677SRui Paulo 	int i, error;
52431d98677SRui Paulo 
52531d98677SRui Paulo 	for (i = 0; i < ndata; i++) {
52631d98677SRui Paulo 		struct rsu_data *dp = &data[i];
52731d98677SRui Paulo 		dp->sc = sc;
52831d98677SRui Paulo 		dp->m = NULL;
52931d98677SRui Paulo 		dp->buf = malloc(maxsz, M_USBDEV, M_NOWAIT);
53031d98677SRui Paulo 		if (dp->buf == NULL) {
53131d98677SRui Paulo 			device_printf(sc->sc_dev,
53231d98677SRui Paulo 			    "could not allocate buffer\n");
53331d98677SRui Paulo 			error = ENOMEM;
53431d98677SRui Paulo 			goto fail;
53531d98677SRui Paulo 		}
53631d98677SRui Paulo 		dp->ni = NULL;
53731d98677SRui Paulo 	}
53831d98677SRui Paulo 
53931d98677SRui Paulo 	return (0);
54031d98677SRui Paulo fail:
54131d98677SRui Paulo 	rsu_free_list(sc, data, ndata);
54231d98677SRui Paulo 	return (error);
54331d98677SRui Paulo }
54431d98677SRui Paulo 
54531d98677SRui Paulo static int
54631d98677SRui Paulo rsu_alloc_rx_list(struct rsu_softc *sc)
54731d98677SRui Paulo {
54831d98677SRui Paulo         int error, i;
54931d98677SRui Paulo 
55031d98677SRui Paulo 	error = rsu_alloc_list(sc, sc->sc_rx, RSU_RX_LIST_COUNT,
55131d98677SRui Paulo 	    RSU_RXBUFSZ);
55231d98677SRui Paulo 	if (error != 0)
55331d98677SRui Paulo 		return (error);
55431d98677SRui Paulo 
55531d98677SRui Paulo 	STAILQ_INIT(&sc->sc_rx_active);
55631d98677SRui Paulo 	STAILQ_INIT(&sc->sc_rx_inactive);
55731d98677SRui Paulo 
55831d98677SRui Paulo 	for (i = 0; i < RSU_RX_LIST_COUNT; i++)
55931d98677SRui Paulo 		STAILQ_INSERT_HEAD(&sc->sc_rx_inactive, &sc->sc_rx[i], next);
56031d98677SRui Paulo 
56131d98677SRui Paulo 	return (0);
56231d98677SRui Paulo }
56331d98677SRui Paulo 
56431d98677SRui Paulo static int
56531d98677SRui Paulo rsu_alloc_tx_list(struct rsu_softc *sc)
56631d98677SRui Paulo {
56731d98677SRui Paulo 	int error, i;
56831d98677SRui Paulo 
56931d98677SRui Paulo 	error = rsu_alloc_list(sc, sc->sc_tx, RSU_TX_LIST_COUNT,
57031d98677SRui Paulo 	    RSU_TXBUFSZ);
57131d98677SRui Paulo 	if (error != 0)
57231d98677SRui Paulo 		return (error);
57331d98677SRui Paulo 
57431d98677SRui Paulo 	STAILQ_INIT(&sc->sc_tx_inactive);
575400b4e53SHans Petter Selasky 
576910593b5SHans Petter Selasky 	for (i = 0; i != RSU_N_TRANSFER; i++) {
577400b4e53SHans Petter Selasky 		STAILQ_INIT(&sc->sc_tx_active[i]);
578400b4e53SHans Petter Selasky 		STAILQ_INIT(&sc->sc_tx_pending[i]);
579400b4e53SHans Petter Selasky 	}
58031d98677SRui Paulo 
58131d98677SRui Paulo 	for (i = 0; i < RSU_TX_LIST_COUNT; i++) {
58231d98677SRui Paulo 		STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, &sc->sc_tx[i], next);
58331d98677SRui Paulo 	}
58431d98677SRui Paulo 
58531d98677SRui Paulo 	return (0);
58631d98677SRui Paulo }
58731d98677SRui Paulo 
58831d98677SRui Paulo static void
58931d98677SRui Paulo rsu_free_tx_list(struct rsu_softc *sc)
59031d98677SRui Paulo {
591885476cbSHans Petter Selasky 	int i;
592885476cbSHans Petter Selasky 
593885476cbSHans Petter Selasky 	/* prevent further allocations from TX list(s) */
594885476cbSHans Petter Selasky 	STAILQ_INIT(&sc->sc_tx_inactive);
595885476cbSHans Petter Selasky 
596910593b5SHans Petter Selasky 	for (i = 0; i != RSU_N_TRANSFER; i++) {
597885476cbSHans Petter Selasky 		STAILQ_INIT(&sc->sc_tx_active[i]);
598885476cbSHans Petter Selasky 		STAILQ_INIT(&sc->sc_tx_pending[i]);
599885476cbSHans Petter Selasky 	}
600885476cbSHans Petter Selasky 
60131d98677SRui Paulo 	rsu_free_list(sc, sc->sc_tx, RSU_TX_LIST_COUNT);
60231d98677SRui Paulo }
60331d98677SRui Paulo 
60431d98677SRui Paulo static void
60531d98677SRui Paulo rsu_free_rx_list(struct rsu_softc *sc)
60631d98677SRui Paulo {
607885476cbSHans Petter Selasky 	/* prevent further allocations from RX list(s) */
608885476cbSHans Petter Selasky 	STAILQ_INIT(&sc->sc_rx_inactive);
609885476cbSHans Petter Selasky 	STAILQ_INIT(&sc->sc_rx_active);
610885476cbSHans Petter Selasky 
61131d98677SRui Paulo 	rsu_free_list(sc, sc->sc_rx, RSU_RX_LIST_COUNT);
61231d98677SRui Paulo }
61331d98677SRui Paulo 
61431d98677SRui Paulo static void
61531d98677SRui Paulo rsu_free_list(struct rsu_softc *sc, struct rsu_data data[], int ndata)
61631d98677SRui Paulo {
61731d98677SRui Paulo 	int i;
61831d98677SRui Paulo 
61931d98677SRui Paulo 	for (i = 0; i < ndata; i++) {
62031d98677SRui Paulo 		struct rsu_data *dp = &data[i];
62131d98677SRui Paulo 
62231d98677SRui Paulo 		if (dp->buf != NULL) {
62331d98677SRui Paulo 			free(dp->buf, M_USBDEV);
62431d98677SRui Paulo 			dp->buf = NULL;
62531d98677SRui Paulo 		}
62631d98677SRui Paulo 		if (dp->ni != NULL) {
62731d98677SRui Paulo 			ieee80211_free_node(dp->ni);
62831d98677SRui Paulo 			dp->ni = NULL;
62931d98677SRui Paulo 		}
63031d98677SRui Paulo 	}
63131d98677SRui Paulo }
63231d98677SRui Paulo 
63331d98677SRui Paulo static struct rsu_data *
63431d98677SRui Paulo _rsu_getbuf(struct rsu_softc *sc)
63531d98677SRui Paulo {
63631d98677SRui Paulo 	struct rsu_data *bf;
63731d98677SRui Paulo 
63831d98677SRui Paulo 	bf = STAILQ_FIRST(&sc->sc_tx_inactive);
63931d98677SRui Paulo 	if (bf != NULL)
64031d98677SRui Paulo 		STAILQ_REMOVE_HEAD(&sc->sc_tx_inactive, next);
64131d98677SRui Paulo 	else
64231d98677SRui Paulo 		bf = NULL;
64331d98677SRui Paulo 	if (bf == NULL)
64431d98677SRui Paulo 		DPRINTF("out of xmit buffers\n");
64531d98677SRui Paulo         return (bf);
64631d98677SRui Paulo }
64731d98677SRui Paulo 
64831d98677SRui Paulo static struct rsu_data *
64931d98677SRui Paulo rsu_getbuf(struct rsu_softc *sc)
65031d98677SRui Paulo {
65131d98677SRui Paulo 	struct rsu_data *bf;
65231d98677SRui Paulo 
65331d98677SRui Paulo 	RSU_ASSERT_LOCKED(sc);
65431d98677SRui Paulo 
65531d98677SRui Paulo 	bf = _rsu_getbuf(sc);
656*7a79cebfSGleb Smirnoff 	if (bf == NULL)
65731d98677SRui Paulo 		DPRINTF("stop queue\n");
65831d98677SRui Paulo 	return (bf);
65931d98677SRui Paulo }
66031d98677SRui Paulo 
66131d98677SRui Paulo static int
66231d98677SRui Paulo rsu_write_region_1(struct rsu_softc *sc, uint16_t addr, uint8_t *buf,
66331d98677SRui Paulo     int len)
66431d98677SRui Paulo {
66531d98677SRui Paulo 	usb_device_request_t req;
66631d98677SRui Paulo 
66731d98677SRui Paulo 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
66831d98677SRui Paulo 	req.bRequest = R92S_REQ_REGS;
66931d98677SRui Paulo 	USETW(req.wValue, addr);
67031d98677SRui Paulo 	USETW(req.wIndex, 0);
67131d98677SRui Paulo 	USETW(req.wLength, len);
67231d98677SRui Paulo 
67331d98677SRui Paulo 	return (rsu_do_request(sc, &req, buf));
67431d98677SRui Paulo }
67531d98677SRui Paulo 
67631d98677SRui Paulo static void
67731d98677SRui Paulo rsu_write_1(struct rsu_softc *sc, uint16_t addr, uint8_t val)
67831d98677SRui Paulo {
67931d98677SRui Paulo 	rsu_write_region_1(sc, addr, &val, 1);
68031d98677SRui Paulo }
68131d98677SRui Paulo 
68231d98677SRui Paulo static void
68331d98677SRui Paulo rsu_write_2(struct rsu_softc *sc, uint16_t addr, uint16_t val)
68431d98677SRui Paulo {
68531d98677SRui Paulo 	val = htole16(val);
68631d98677SRui Paulo 	rsu_write_region_1(sc, addr, (uint8_t *)&val, 2);
68731d98677SRui Paulo }
68831d98677SRui Paulo 
68931d98677SRui Paulo static void
69031d98677SRui Paulo rsu_write_4(struct rsu_softc *sc, uint16_t addr, uint32_t val)
69131d98677SRui Paulo {
69231d98677SRui Paulo 	val = htole32(val);
69331d98677SRui Paulo 	rsu_write_region_1(sc, addr, (uint8_t *)&val, 4);
69431d98677SRui Paulo }
69531d98677SRui Paulo 
69631d98677SRui Paulo static int
69731d98677SRui Paulo rsu_read_region_1(struct rsu_softc *sc, uint16_t addr, uint8_t *buf,
69831d98677SRui Paulo     int len)
69931d98677SRui Paulo {
70031d98677SRui Paulo 	usb_device_request_t req;
70131d98677SRui Paulo 
70231d98677SRui Paulo 	req.bmRequestType = UT_READ_VENDOR_DEVICE;
70331d98677SRui Paulo 	req.bRequest = R92S_REQ_REGS;
70431d98677SRui Paulo 	USETW(req.wValue, addr);
70531d98677SRui Paulo 	USETW(req.wIndex, 0);
70631d98677SRui Paulo 	USETW(req.wLength, len);
70731d98677SRui Paulo 
70831d98677SRui Paulo 	return (rsu_do_request(sc, &req, buf));
70931d98677SRui Paulo }
71031d98677SRui Paulo 
71131d98677SRui Paulo static uint8_t
71231d98677SRui Paulo rsu_read_1(struct rsu_softc *sc, uint16_t addr)
71331d98677SRui Paulo {
71431d98677SRui Paulo 	uint8_t val;
71531d98677SRui Paulo 
71631d98677SRui Paulo 	if (rsu_read_region_1(sc, addr, &val, 1) != 0)
71731d98677SRui Paulo 		return (0xff);
71831d98677SRui Paulo 	return (val);
71931d98677SRui Paulo }
72031d98677SRui Paulo 
72131d98677SRui Paulo static uint16_t
72231d98677SRui Paulo rsu_read_2(struct rsu_softc *sc, uint16_t addr)
72331d98677SRui Paulo {
72431d98677SRui Paulo 	uint16_t val;
72531d98677SRui Paulo 
72631d98677SRui Paulo 	if (rsu_read_region_1(sc, addr, (uint8_t *)&val, 2) != 0)
72731d98677SRui Paulo 		return (0xffff);
72831d98677SRui Paulo 	return (le16toh(val));
72931d98677SRui Paulo }
73031d98677SRui Paulo 
73131d98677SRui Paulo static uint32_t
73231d98677SRui Paulo rsu_read_4(struct rsu_softc *sc, uint16_t addr)
73331d98677SRui Paulo {
73431d98677SRui Paulo 	uint32_t val;
73531d98677SRui Paulo 
73631d98677SRui Paulo 	if (rsu_read_region_1(sc, addr, (uint8_t *)&val, 4) != 0)
73731d98677SRui Paulo 		return (0xffffffff);
73831d98677SRui Paulo 	return (le32toh(val));
73931d98677SRui Paulo }
74031d98677SRui Paulo 
74131d98677SRui Paulo static int
74231d98677SRui Paulo rsu_fw_iocmd(struct rsu_softc *sc, uint32_t iocmd)
74331d98677SRui Paulo {
74431d98677SRui Paulo 	int ntries;
74531d98677SRui Paulo 
74631d98677SRui Paulo 	rsu_write_4(sc, R92S_IOCMD_CTRL, iocmd);
747b41381aeSHans Petter Selasky 	rsu_ms_delay(sc);
74831d98677SRui Paulo 	for (ntries = 0; ntries < 50; ntries++) {
74931d98677SRui Paulo 		if (rsu_read_4(sc, R92S_IOCMD_CTRL) == 0)
75031d98677SRui Paulo 			return (0);
751b41381aeSHans Petter Selasky 		rsu_ms_delay(sc);
75231d98677SRui Paulo 	}
75331d98677SRui Paulo 	return (ETIMEDOUT);
75431d98677SRui Paulo }
75531d98677SRui Paulo 
75631d98677SRui Paulo static uint8_t
75731d98677SRui Paulo rsu_efuse_read_1(struct rsu_softc *sc, uint16_t addr)
75831d98677SRui Paulo {
75931d98677SRui Paulo 	uint32_t reg;
76031d98677SRui Paulo 	int ntries;
76131d98677SRui Paulo 
76231d98677SRui Paulo 	reg = rsu_read_4(sc, R92S_EFUSE_CTRL);
76331d98677SRui Paulo 	reg = RW(reg, R92S_EFUSE_CTRL_ADDR, addr);
76431d98677SRui Paulo 	reg &= ~R92S_EFUSE_CTRL_VALID;
76531d98677SRui Paulo 	rsu_write_4(sc, R92S_EFUSE_CTRL, reg);
76631d98677SRui Paulo 	/* Wait for read operation to complete. */
76731d98677SRui Paulo 	for (ntries = 0; ntries < 100; ntries++) {
76831d98677SRui Paulo 		reg = rsu_read_4(sc, R92S_EFUSE_CTRL);
76931d98677SRui Paulo 		if (reg & R92S_EFUSE_CTRL_VALID)
77031d98677SRui Paulo 			return (MS(reg, R92S_EFUSE_CTRL_DATA));
771b41381aeSHans Petter Selasky 		rsu_ms_delay(sc);
77231d98677SRui Paulo 	}
77331d98677SRui Paulo 	device_printf(sc->sc_dev,
77431d98677SRui Paulo 	    "could not read efuse byte at address 0x%x\n", addr);
77531d98677SRui Paulo 	return (0xff);
77631d98677SRui Paulo }
77731d98677SRui Paulo 
77831d98677SRui Paulo static int
77931d98677SRui Paulo rsu_read_rom(struct rsu_softc *sc)
78031d98677SRui Paulo {
78131d98677SRui Paulo 	uint8_t *rom = sc->rom;
78231d98677SRui Paulo 	uint16_t addr = 0;
78331d98677SRui Paulo 	uint32_t reg;
78431d98677SRui Paulo 	uint8_t off, msk;
78531d98677SRui Paulo 	int i;
78631d98677SRui Paulo 
78731d98677SRui Paulo 	/* Make sure that ROM type is eFuse and that autoload succeeded. */
78831d98677SRui Paulo 	reg = rsu_read_1(sc, R92S_EE_9346CR);
78931d98677SRui Paulo 	if ((reg & (R92S_9356SEL | R92S_EEPROM_EN)) != R92S_EEPROM_EN)
79031d98677SRui Paulo 		return (EIO);
79131d98677SRui Paulo 
79231d98677SRui Paulo 	/* Turn on 2.5V to prevent eFuse leakage. */
79331d98677SRui Paulo 	reg = rsu_read_1(sc, R92S_EFUSE_TEST + 3);
79431d98677SRui Paulo 	rsu_write_1(sc, R92S_EFUSE_TEST + 3, reg | 0x80);
795b41381aeSHans Petter Selasky 	rsu_ms_delay(sc);
79631d98677SRui Paulo 	rsu_write_1(sc, R92S_EFUSE_TEST + 3, reg & ~0x80);
79731d98677SRui Paulo 
79831d98677SRui Paulo 	/* Read full ROM image. */
79931d98677SRui Paulo 	memset(&sc->rom, 0xff, sizeof(sc->rom));
80031d98677SRui Paulo 	while (addr < 512) {
80131d98677SRui Paulo 		reg = rsu_efuse_read_1(sc, addr);
80231d98677SRui Paulo 		if (reg == 0xff)
80331d98677SRui Paulo 			break;
80431d98677SRui Paulo 		addr++;
80531d98677SRui Paulo 		off = reg >> 4;
80631d98677SRui Paulo 		msk = reg & 0xf;
80731d98677SRui Paulo 		for (i = 0; i < 4; i++) {
80831d98677SRui Paulo 			if (msk & (1 << i))
80931d98677SRui Paulo 				continue;
81031d98677SRui Paulo 			rom[off * 8 + i * 2 + 0] =
81131d98677SRui Paulo 			    rsu_efuse_read_1(sc, addr);
81231d98677SRui Paulo 			addr++;
81331d98677SRui Paulo 			rom[off * 8 + i * 2 + 1] =
81431d98677SRui Paulo 			    rsu_efuse_read_1(sc, addr);
81531d98677SRui Paulo 			addr++;
81631d98677SRui Paulo 		}
81731d98677SRui Paulo 	}
81831d98677SRui Paulo #ifdef USB_DEBUG
81931d98677SRui Paulo 	if (rsu_debug >= 5) {
82031d98677SRui Paulo 		/* Dump ROM content. */
82131d98677SRui Paulo 		printf("\n");
82231d98677SRui Paulo 		for (i = 0; i < sizeof(sc->rom); i++)
82331d98677SRui Paulo 			printf("%02x:", rom[i]);
82431d98677SRui Paulo 		printf("\n");
82531d98677SRui Paulo 	}
82631d98677SRui Paulo #endif
82731d98677SRui Paulo 	return (0);
82831d98677SRui Paulo }
82931d98677SRui Paulo 
83031d98677SRui Paulo static int
83131d98677SRui Paulo rsu_fw_cmd(struct rsu_softc *sc, uint8_t code, void *buf, int len)
83231d98677SRui Paulo {
833910593b5SHans Petter Selasky 	const uint8_t which = rsu_wme_ac_xfer_map[WME_AC_VO];
83431d98677SRui Paulo 	struct rsu_data *data;
83531d98677SRui Paulo 	struct r92s_tx_desc *txd;
83631d98677SRui Paulo 	struct r92s_fw_cmd_hdr *cmd;
837400b4e53SHans Petter Selasky 	int cmdsz;
838400b4e53SHans Petter Selasky 	int xferlen;
83931d98677SRui Paulo 
84031d98677SRui Paulo 	data = rsu_getbuf(sc);
84131d98677SRui Paulo 	if (data == NULL)
84231d98677SRui Paulo 		return (ENOMEM);
84331d98677SRui Paulo 
84431d98677SRui Paulo 	/* Round-up command length to a multiple of 8 bytes. */
84531d98677SRui Paulo 	cmdsz = (len + 7) & ~7;
84631d98677SRui Paulo 
84731d98677SRui Paulo 	xferlen = sizeof(*txd) + sizeof(*cmd) + cmdsz;
84831d98677SRui Paulo 	KASSERT(xferlen <= RSU_TXBUFSZ, ("%s: invalid length", __func__));
84931d98677SRui Paulo 	memset(data->buf, 0, xferlen);
85031d98677SRui Paulo 
85131d98677SRui Paulo 	/* Setup Tx descriptor. */
85231d98677SRui Paulo 	txd = (struct r92s_tx_desc *)data->buf;
85331d98677SRui Paulo 	txd->txdw0 = htole32(
85431d98677SRui Paulo 	    SM(R92S_TXDW0_OFFSET, sizeof(*txd)) |
85531d98677SRui Paulo 	    SM(R92S_TXDW0_PKTLEN, sizeof(*cmd) + cmdsz) |
85631d98677SRui Paulo 	    R92S_TXDW0_OWN | R92S_TXDW0_FSG | R92S_TXDW0_LSG);
85731d98677SRui Paulo 	txd->txdw1 = htole32(SM(R92S_TXDW1_QSEL, R92S_TXDW1_QSEL_H2C));
85831d98677SRui Paulo 
85931d98677SRui Paulo 	/* Setup command header. */
86031d98677SRui Paulo 	cmd = (struct r92s_fw_cmd_hdr *)&txd[1];
86131d98677SRui Paulo 	cmd->len = htole16(cmdsz);
86231d98677SRui Paulo 	cmd->code = code;
86331d98677SRui Paulo 	cmd->seq = sc->cmd_seq;
86431d98677SRui Paulo 	sc->cmd_seq = (sc->cmd_seq + 1) & 0x7f;
86531d98677SRui Paulo 
86631d98677SRui Paulo 	/* Copy command payload. */
86731d98677SRui Paulo 	memcpy(&cmd[1], buf, len);
86831d98677SRui Paulo 
86931d98677SRui Paulo 	DPRINTFN(2, "Tx cmd code=0x%x len=0x%x\n", code, cmdsz);
87031d98677SRui Paulo 	data->buflen = xferlen;
871400b4e53SHans Petter Selasky 	STAILQ_INSERT_TAIL(&sc->sc_tx_pending[which], data, next);
872910593b5SHans Petter Selasky 	usbd_transfer_start(sc->sc_xfer[which]);
87331d98677SRui Paulo 
87431d98677SRui Paulo 	return (0);
87531d98677SRui Paulo }
87631d98677SRui Paulo 
87731d98677SRui Paulo /* ARGSUSED */
87831d98677SRui Paulo static void
87931d98677SRui Paulo rsu_calib_task(void *arg, int pending __unused)
88031d98677SRui Paulo {
88131d98677SRui Paulo 	struct rsu_softc *sc = arg;
88231d98677SRui Paulo 	uint32_t reg;
88331d98677SRui Paulo 
88431d98677SRui Paulo 	DPRINTFN(6, "running calibration task\n");
885910593b5SHans Petter Selasky 
88631d98677SRui Paulo 	RSU_LOCK(sc);
88731d98677SRui Paulo #ifdef notyet
88831d98677SRui Paulo 	/* Read WPS PBC status. */
88931d98677SRui Paulo 	rsu_write_1(sc, R92S_MAC_PINMUX_CTRL,
89031d98677SRui Paulo 	    R92S_GPIOMUX_EN | SM(R92S_GPIOSEL_GPIO, R92S_GPIOSEL_GPIO_JTAG));
89131d98677SRui Paulo 	rsu_write_1(sc, R92S_GPIO_IO_SEL,
89231d98677SRui Paulo 	    rsu_read_1(sc, R92S_GPIO_IO_SEL) & ~R92S_GPIO_WPS);
89331d98677SRui Paulo 	reg = rsu_read_1(sc, R92S_GPIO_CTRL);
89431d98677SRui Paulo 	if (reg != 0xff && (reg & R92S_GPIO_WPS))
89531d98677SRui Paulo 		DPRINTF(("WPS PBC is pushed\n"));
89631d98677SRui Paulo #endif
89731d98677SRui Paulo 	/* Read current signal level. */
89831d98677SRui Paulo 	if (rsu_fw_iocmd(sc, 0xf4000001) == 0) {
89931d98677SRui Paulo 		reg = rsu_read_4(sc, R92S_IOCMD_DATA);
90031d98677SRui Paulo 		DPRINTFN(8, "RSSI=%d%%\n", reg >> 4);
90131d98677SRui Paulo 	}
902910593b5SHans Petter Selasky 	if (sc->sc_calibrating)
903910593b5SHans Petter Selasky 		taskqueue_enqueue_timeout(taskqueue_thread, &sc->calib_task, hz);
90431d98677SRui Paulo 	RSU_UNLOCK(sc);
90531d98677SRui Paulo }
90631d98677SRui Paulo 
90731d98677SRui Paulo static int
90831d98677SRui Paulo rsu_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
90931d98677SRui Paulo {
91031d98677SRui Paulo 	struct rsu_vap *uvp = RSU_VAP(vap);
91131d98677SRui Paulo 	struct ieee80211com *ic = vap->iv_ic;
912d3fdd08cSAdrian Chadd 	struct rsu_softc *sc = ic->ic_softc;
91331d98677SRui Paulo 	struct ieee80211_node *ni;
91431d98677SRui Paulo 	struct ieee80211_rateset *rs;
91531d98677SRui Paulo 	enum ieee80211_state ostate;
91631d98677SRui Paulo 	int error, startcal = 0;
91731d98677SRui Paulo 
91831d98677SRui Paulo 	ostate = vap->iv_state;
91931d98677SRui Paulo 	DPRINTF("%s -> %s\n", ieee80211_state_name[ostate],
92031d98677SRui Paulo 	    ieee80211_state_name[nstate]);
92131d98677SRui Paulo 
92231d98677SRui Paulo 	IEEE80211_UNLOCK(ic);
92331d98677SRui Paulo 	if (ostate == IEEE80211_S_RUN) {
92431d98677SRui Paulo 		RSU_LOCK(sc);
92531d98677SRui Paulo 		/* Stop calibration. */
92631d98677SRui Paulo 		sc->sc_calibrating = 0;
92731d98677SRui Paulo 		RSU_UNLOCK(sc);
92831d98677SRui Paulo 		taskqueue_drain_timeout(taskqueue_thread, &sc->calib_task);
92931d98677SRui Paulo 		/* Disassociate from our current BSS. */
93031d98677SRui Paulo 		RSU_LOCK(sc);
93131d98677SRui Paulo 		rsu_disconnect(sc);
93231d98677SRui Paulo 	} else
93331d98677SRui Paulo 		RSU_LOCK(sc);
93431d98677SRui Paulo 	switch (nstate) {
93531d98677SRui Paulo 	case IEEE80211_S_INIT:
93631d98677SRui Paulo 		break;
93731d98677SRui Paulo 	case IEEE80211_S_AUTH:
93831d98677SRui Paulo 		ni = ieee80211_ref_node(vap->iv_bss);
93931d98677SRui Paulo 		error = rsu_join_bss(sc, ni);
94031d98677SRui Paulo 		ieee80211_free_node(ni);
94131d98677SRui Paulo 		if (error != 0) {
94231d98677SRui Paulo 			device_printf(sc->sc_dev,
94331d98677SRui Paulo 			    "could not send join command\n");
94431d98677SRui Paulo 		}
94531d98677SRui Paulo 		break;
94631d98677SRui Paulo 	case IEEE80211_S_RUN:
94731d98677SRui Paulo 		ni = ieee80211_ref_node(vap->iv_bss);
94831d98677SRui Paulo 		rs = &ni->ni_rates;
94931d98677SRui Paulo 		/* Indicate highest supported rate. */
95031d98677SRui Paulo 		ni->ni_txrate = rs->rs_rates[rs->rs_nrates - 1];
95131d98677SRui Paulo 		ieee80211_free_node(ni);
95231d98677SRui Paulo 		startcal = 1;
95331d98677SRui Paulo 		break;
95431d98677SRui Paulo 	default:
95531d98677SRui Paulo 		break;
95631d98677SRui Paulo 	}
95731d98677SRui Paulo 	sc->sc_calibrating = 1;
958910593b5SHans Petter Selasky 	/* Start periodic calibration. */
959910593b5SHans Petter Selasky 	taskqueue_enqueue_timeout(taskqueue_thread, &sc->calib_task, hz);
96031d98677SRui Paulo 	RSU_UNLOCK(sc);
96131d98677SRui Paulo 	IEEE80211_LOCK(ic);
96231d98677SRui Paulo 	return (uvp->newstate(vap, nstate, arg));
96331d98677SRui Paulo }
96431d98677SRui Paulo 
96531d98677SRui Paulo #ifdef notyet
96631d98677SRui Paulo static void
96731d98677SRui Paulo rsu_set_key(struct rsu_softc *sc, const struct ieee80211_key *k)
96831d98677SRui Paulo {
96931d98677SRui Paulo 	struct r92s_fw_cmd_set_key key;
97031d98677SRui Paulo 
97131d98677SRui Paulo 	memset(&key, 0, sizeof(key));
97231d98677SRui Paulo 	/* Map net80211 cipher to HW crypto algorithm. */
97331d98677SRui Paulo 	switch (k->wk_cipher->ic_cipher) {
97431d98677SRui Paulo 	case IEEE80211_CIPHER_WEP:
97531d98677SRui Paulo 		if (k->wk_keylen < 8)
97631d98677SRui Paulo 			key.algo = R92S_KEY_ALGO_WEP40;
97731d98677SRui Paulo 		else
97831d98677SRui Paulo 			key.algo = R92S_KEY_ALGO_WEP104;
97931d98677SRui Paulo 		break;
98031d98677SRui Paulo 	case IEEE80211_CIPHER_TKIP:
98131d98677SRui Paulo 		key.algo = R92S_KEY_ALGO_TKIP;
98231d98677SRui Paulo 		break;
98331d98677SRui Paulo 	case IEEE80211_CIPHER_AES_CCM:
98431d98677SRui Paulo 		key.algo = R92S_KEY_ALGO_AES;
98531d98677SRui Paulo 		break;
98631d98677SRui Paulo 	default:
98731d98677SRui Paulo 		return;
98831d98677SRui Paulo 	}
98931d98677SRui Paulo 	key.id = k->wk_keyix;
99031d98677SRui Paulo 	key.grpkey = (k->wk_flags & IEEE80211_KEY_GROUP) != 0;
99131d98677SRui Paulo 	memcpy(key.key, k->wk_key, MIN(k->wk_keylen, sizeof(key.key)));
99231d98677SRui Paulo 	(void)rsu_fw_cmd(sc, R92S_CMD_SET_KEY, &key, sizeof(key));
99331d98677SRui Paulo }
99431d98677SRui Paulo 
99531d98677SRui Paulo static void
99631d98677SRui Paulo rsu_delete_key(struct rsu_softc *sc, const struct ieee80211_key *k)
99731d98677SRui Paulo {
99831d98677SRui Paulo 	struct r92s_fw_cmd_set_key key;
99931d98677SRui Paulo 
100031d98677SRui Paulo 	memset(&key, 0, sizeof(key));
100131d98677SRui Paulo 	key.id = k->wk_keyix;
100231d98677SRui Paulo 	(void)rsu_fw_cmd(sc, R92S_CMD_SET_KEY, &key, sizeof(key));
100331d98677SRui Paulo }
100431d98677SRui Paulo #endif
100531d98677SRui Paulo 
100631d98677SRui Paulo static int
100731d98677SRui Paulo rsu_site_survey(struct rsu_softc *sc, struct ieee80211vap *vap)
100831d98677SRui Paulo {
100931d98677SRui Paulo 	struct r92s_fw_cmd_sitesurvey cmd;
1010*7a79cebfSGleb Smirnoff 	struct ieee80211com *ic = &sc->sc_ic;
101131d98677SRui Paulo 
101231d98677SRui Paulo 	memset(&cmd, 0, sizeof(cmd));
1013*7a79cebfSGleb Smirnoff 	if ((ic->ic_flags & IEEE80211_F_ASCAN) || sc->sc_scan_pass == 1)
101431d98677SRui Paulo 		cmd.active = htole32(1);
101531d98677SRui Paulo 	cmd.limit = htole32(48);
1016*7a79cebfSGleb Smirnoff 	if (sc->sc_scan_pass == 1 && vap->iv_des_nssid > 0) {
101731d98677SRui Paulo 		/* Do a directed scan for second pass. */
101831d98677SRui Paulo 		cmd.ssidlen = htole32(vap->iv_des_ssid[0].len);
101931d98677SRui Paulo 		memcpy(cmd.ssid, vap->iv_des_ssid[0].ssid,
102031d98677SRui Paulo 		    vap->iv_des_ssid[0].len);
102131d98677SRui Paulo 
102231d98677SRui Paulo 	}
1023*7a79cebfSGleb Smirnoff 	DPRINTF("sending site survey command, pass=%d\n", sc->sc_scan_pass);
102431d98677SRui Paulo 	return (rsu_fw_cmd(sc, R92S_CMD_SITE_SURVEY, &cmd, sizeof(cmd)));
102531d98677SRui Paulo }
102631d98677SRui Paulo 
102731d98677SRui Paulo static int
102831d98677SRui Paulo rsu_join_bss(struct rsu_softc *sc, struct ieee80211_node *ni)
102931d98677SRui Paulo {
1030*7a79cebfSGleb Smirnoff 	struct ieee80211com *ic = &sc->sc_ic;
103131d98677SRui Paulo 	struct ieee80211vap *vap = ni->ni_vap;
103231d98677SRui Paulo 	struct ndis_wlan_bssid_ex *bss;
103331d98677SRui Paulo 	struct ndis_802_11_fixed_ies *fixed;
103431d98677SRui Paulo 	struct r92s_fw_cmd_auth auth;
1035bb03cd6fSHans Petter Selasky 	uint8_t buf[sizeof(*bss) + 128] __aligned(4);
1036bb03cd6fSHans Petter Selasky 	uint8_t *frm;
103731d98677SRui Paulo 	uint8_t opmode;
103831d98677SRui Paulo 	int error;
103931d98677SRui Paulo 
104031d98677SRui Paulo 	/* Let the FW decide the opmode based on the capinfo field. */
104131d98677SRui Paulo 	opmode = NDIS802_11AUTOUNKNOWN;
104231d98677SRui Paulo 	DPRINTF("setting operating mode to %d\n", opmode);
104331d98677SRui Paulo 	error = rsu_fw_cmd(sc, R92S_CMD_SET_OPMODE, &opmode, sizeof(opmode));
104431d98677SRui Paulo 	if (error != 0)
104531d98677SRui Paulo 		return (error);
104631d98677SRui Paulo 
104731d98677SRui Paulo 	memset(&auth, 0, sizeof(auth));
104831d98677SRui Paulo 	if (vap->iv_flags & IEEE80211_F_WPA) {
104931d98677SRui Paulo 		auth.mode = R92S_AUTHMODE_WPA;
1050bb03cd6fSHans Petter Selasky 		auth.dot1x = (ni->ni_authmode == IEEE80211_AUTH_8021X);
105131d98677SRui Paulo 	} else
105231d98677SRui Paulo 		auth.mode = R92S_AUTHMODE_OPEN;
105331d98677SRui Paulo 	DPRINTF("setting auth mode to %d\n", auth.mode);
105431d98677SRui Paulo 	error = rsu_fw_cmd(sc, R92S_CMD_SET_AUTH, &auth, sizeof(auth));
105531d98677SRui Paulo 	if (error != 0)
105631d98677SRui Paulo 		return (error);
105731d98677SRui Paulo 
105831d98677SRui Paulo 	memset(buf, 0, sizeof(buf));
105931d98677SRui Paulo 	bss = (struct ndis_wlan_bssid_ex *)buf;
106031d98677SRui Paulo 	IEEE80211_ADDR_COPY(bss->macaddr, ni->ni_bssid);
106131d98677SRui Paulo 	bss->ssid.ssidlen = htole32(ni->ni_esslen);
106231d98677SRui Paulo 	memcpy(bss->ssid.ssid, ni->ni_essid, ni->ni_esslen);
106331d98677SRui Paulo 	if (vap->iv_flags & (IEEE80211_F_PRIVACY | IEEE80211_F_WPA))
106431d98677SRui Paulo 		bss->privacy = htole32(1);
106531d98677SRui Paulo 	bss->rssi = htole32(ni->ni_avgrssi);
106631d98677SRui Paulo 	if (ic->ic_curmode == IEEE80211_MODE_11B)
106731d98677SRui Paulo 		bss->networktype = htole32(NDIS802_11DS);
106831d98677SRui Paulo 	else
106931d98677SRui Paulo 		bss->networktype = htole32(NDIS802_11OFDM24);
107031d98677SRui Paulo 	bss->config.len = htole32(sizeof(bss->config));
107131d98677SRui Paulo 	bss->config.bintval = htole32(ni->ni_intval);
107231d98677SRui Paulo 	bss->config.dsconfig = htole32(ieee80211_chan2ieee(ic, ni->ni_chan));
107331d98677SRui Paulo 	bss->inframode = htole32(NDIS802_11INFRASTRUCTURE);
107431d98677SRui Paulo 	memcpy(bss->supprates, ni->ni_rates.rs_rates,
107531d98677SRui Paulo 	    ni->ni_rates.rs_nrates);
107631d98677SRui Paulo 	/* Write the fixed fields of the beacon frame. */
107731d98677SRui Paulo 	fixed = (struct ndis_802_11_fixed_ies *)&bss[1];
107831d98677SRui Paulo 	memcpy(&fixed->tstamp, ni->ni_tstamp.data, 8);
107931d98677SRui Paulo 	fixed->bintval = htole16(ni->ni_intval);
108031d98677SRui Paulo 	fixed->capabilities = htole16(ni->ni_capinfo);
108131d98677SRui Paulo 	/* Write IEs to be included in the association request. */
108231d98677SRui Paulo 	frm = (uint8_t *)&fixed[1];
108331d98677SRui Paulo 	frm = ieee80211_add_rsn(frm, vap);
108431d98677SRui Paulo 	frm = ieee80211_add_wpa(frm, vap);
108531d98677SRui Paulo 	frm = ieee80211_add_qos(frm, ni);
108631d98677SRui Paulo 	if (ni->ni_flags & IEEE80211_NODE_HT)
108731d98677SRui Paulo 		frm = ieee80211_add_htcap(frm, ni);
108831d98677SRui Paulo 	bss->ieslen = htole32(frm - (uint8_t *)fixed);
108931d98677SRui Paulo 	bss->len = htole32(((frm - buf) + 3) & ~3);
109031d98677SRui Paulo 	DPRINTF("sending join bss command to %s chan %d\n",
109131d98677SRui Paulo 	    ether_sprintf(bss->macaddr), le32toh(bss->config.dsconfig));
109231d98677SRui Paulo 	return (rsu_fw_cmd(sc, R92S_CMD_JOIN_BSS, buf, sizeof(buf)));
109331d98677SRui Paulo }
109431d98677SRui Paulo 
109531d98677SRui Paulo static int
109631d98677SRui Paulo rsu_disconnect(struct rsu_softc *sc)
109731d98677SRui Paulo {
109831d98677SRui Paulo 	uint32_t zero = 0;	/* :-) */
109931d98677SRui Paulo 
110031d98677SRui Paulo 	/* Disassociate from our current BSS. */
110131d98677SRui Paulo 	DPRINTF("sending disconnect command\n");
110231d98677SRui Paulo 	return (rsu_fw_cmd(sc, R92S_CMD_DISCONNECT, &zero, sizeof(zero)));
110331d98677SRui Paulo }
110431d98677SRui Paulo 
110531d98677SRui Paulo static void
110631d98677SRui Paulo rsu_event_survey(struct rsu_softc *sc, uint8_t *buf, int len)
110731d98677SRui Paulo {
1108*7a79cebfSGleb Smirnoff 	struct ieee80211com *ic = &sc->sc_ic;
110931d98677SRui Paulo 	struct ieee80211_frame *wh;
111031d98677SRui Paulo 	struct ieee80211_channel *c;
111131d98677SRui Paulo 	struct ndis_wlan_bssid_ex *bss;
111231d98677SRui Paulo 	struct mbuf *m;
111331d98677SRui Paulo 	int pktlen;
111431d98677SRui Paulo 
111531d98677SRui Paulo 	if (__predict_false(len < sizeof(*bss)))
111631d98677SRui Paulo 		return;
111731d98677SRui Paulo 	bss = (struct ndis_wlan_bssid_ex *)buf;
111831d98677SRui Paulo 	if (__predict_false(len < sizeof(*bss) + le32toh(bss->ieslen)))
111931d98677SRui Paulo 		return;
112031d98677SRui Paulo 
112131d98677SRui Paulo 	DPRINTFN(2, "found BSS %s: len=%d chan=%d inframode=%d "
112231d98677SRui Paulo 	    "networktype=%d privacy=%d\n",
112331d98677SRui Paulo 	    ether_sprintf(bss->macaddr), le32toh(bss->len),
112431d98677SRui Paulo 	    le32toh(bss->config.dsconfig), le32toh(bss->inframode),
112531d98677SRui Paulo 	    le32toh(bss->networktype), le32toh(bss->privacy));
112631d98677SRui Paulo 
112731d98677SRui Paulo 	/* Build a fake beacon frame to let net80211 do all the parsing. */
112831d98677SRui Paulo 	pktlen = sizeof(*wh) + le32toh(bss->ieslen);
112931d98677SRui Paulo 	if (__predict_false(pktlen > MCLBYTES))
113031d98677SRui Paulo 		return;
1131107f00c0SKevin Lo 	m = m_get2(pktlen, M_NOWAIT, MT_DATA, M_PKTHDR);
113231d98677SRui Paulo 	if (__predict_false(m == NULL))
113331d98677SRui Paulo 		return;
113431d98677SRui Paulo 	wh = mtod(m, struct ieee80211_frame *);
113531d98677SRui Paulo 	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
113631d98677SRui Paulo 	    IEEE80211_FC0_SUBTYPE_BEACON;
113731d98677SRui Paulo 	wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
11388cfe5440SHans Petter Selasky 	USETW(wh->i_dur, 0);
1139*7a79cebfSGleb Smirnoff 	IEEE80211_ADDR_COPY(wh->i_addr1, ieee80211broadcastaddr);
114031d98677SRui Paulo 	IEEE80211_ADDR_COPY(wh->i_addr2, bss->macaddr);
114131d98677SRui Paulo 	IEEE80211_ADDR_COPY(wh->i_addr3, bss->macaddr);
114231d98677SRui Paulo 	*(uint16_t *)wh->i_seq = 0;
114331d98677SRui Paulo 	memcpy(&wh[1], (uint8_t *)&bss[1], le32toh(bss->ieslen));
114431d98677SRui Paulo 
114531d98677SRui Paulo 	/* Finalize mbuf. */
114631d98677SRui Paulo 	m->m_pkthdr.len = m->m_len = pktlen;
114731d98677SRui Paulo 	/* Fix the channel. */
114831d98677SRui Paulo 	c = ieee80211_find_channel_byieee(ic,
114931d98677SRui Paulo 	    le32toh(bss->config.dsconfig),
115031d98677SRui Paulo 	    IEEE80211_CHAN_G);
115131d98677SRui Paulo 	if (c) {
115231d98677SRui Paulo 		ic->ic_curchan = c;
115331d98677SRui Paulo 		ieee80211_radiotap_chan_change(ic);
115431d98677SRui Paulo 	}
115531d98677SRui Paulo 	/* XXX avoid a LOR */
115631d98677SRui Paulo 	RSU_UNLOCK(sc);
115731d98677SRui Paulo 	ieee80211_input_all(ic, m, le32toh(bss->rssi), 0);
115831d98677SRui Paulo 	RSU_LOCK(sc);
115931d98677SRui Paulo }
116031d98677SRui Paulo 
116131d98677SRui Paulo static void
116231d98677SRui Paulo rsu_event_join_bss(struct rsu_softc *sc, uint8_t *buf, int len)
116331d98677SRui Paulo {
1164*7a79cebfSGleb Smirnoff 	struct ieee80211com *ic = &sc->sc_ic;
116531d98677SRui Paulo 	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
116631d98677SRui Paulo 	struct ieee80211_node *ni = vap->iv_bss;
116731d98677SRui Paulo 	struct r92s_event_join_bss *rsp;
1168bb03cd6fSHans Petter Selasky 	uint32_t tmp;
116931d98677SRui Paulo 	int res;
117031d98677SRui Paulo 
117131d98677SRui Paulo 	if (__predict_false(len < sizeof(*rsp)))
117231d98677SRui Paulo 		return;
117331d98677SRui Paulo 	rsp = (struct r92s_event_join_bss *)buf;
117431d98677SRui Paulo 	res = (int)le32toh(rsp->join_res);
117531d98677SRui Paulo 
117631d98677SRui Paulo 	DPRINTF("Rx join BSS event len=%d res=%d\n", len, res);
117731d98677SRui Paulo 	if (res <= 0) {
117831d98677SRui Paulo 		RSU_UNLOCK(sc);
117931d98677SRui Paulo 		ieee80211_new_state(vap, IEEE80211_S_SCAN, -1);
118031d98677SRui Paulo 		RSU_LOCK(sc);
118131d98677SRui Paulo 		return;
118231d98677SRui Paulo 	}
1183bb03cd6fSHans Petter Selasky 	tmp = le32toh(rsp->associd);
1184bb03cd6fSHans Petter Selasky 	if (tmp >= vap->iv_max_aid) {
1185bb03cd6fSHans Petter Selasky 		DPRINTF("Assoc ID overflow\n");
1186bb03cd6fSHans Petter Selasky 		tmp = 1;
1187bb03cd6fSHans Petter Selasky 	}
118831d98677SRui Paulo 	DPRINTF("associated with %s associd=%d\n",
1189bb03cd6fSHans Petter Selasky 	    ether_sprintf(rsp->bss.macaddr), tmp);
1190bb03cd6fSHans Petter Selasky 	ni->ni_associd = tmp | 0xc000;
119131d98677SRui Paulo 	RSU_UNLOCK(sc);
119231d98677SRui Paulo 	ieee80211_new_state(vap, IEEE80211_S_RUN,
119331d98677SRui Paulo 	    IEEE80211_FC0_SUBTYPE_ASSOC_RESP);
119431d98677SRui Paulo 	RSU_LOCK(sc);
119531d98677SRui Paulo }
119631d98677SRui Paulo 
119731d98677SRui Paulo static void
119831d98677SRui Paulo rsu_rx_event(struct rsu_softc *sc, uint8_t code, uint8_t *buf, int len)
119931d98677SRui Paulo {
1200*7a79cebfSGleb Smirnoff 	struct ieee80211com *ic = &sc->sc_ic;
120131d98677SRui Paulo 	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
120231d98677SRui Paulo 
120331d98677SRui Paulo 	DPRINTFN(4, "Rx event code=%d len=%d\n", code, len);
120431d98677SRui Paulo 	switch (code) {
120531d98677SRui Paulo 	case R92S_EVT_SURVEY:
120631d98677SRui Paulo 		if (vap->iv_state == IEEE80211_S_SCAN)
120731d98677SRui Paulo 			rsu_event_survey(sc, buf, len);
120831d98677SRui Paulo 		break;
120931d98677SRui Paulo 	case R92S_EVT_SURVEY_DONE:
121031d98677SRui Paulo 		DPRINTF("site survey pass %d done, found %d BSS\n",
1211*7a79cebfSGleb Smirnoff 		    sc->sc_scan_pass, le32toh(*(uint32_t *)buf));
121231d98677SRui Paulo 		if (vap->iv_state != IEEE80211_S_SCAN)
121331d98677SRui Paulo 			break;	/* Ignore if not scanning. */
1214*7a79cebfSGleb Smirnoff 		if (sc->sc_scan_pass == 0 && vap->iv_des_nssid != 0) {
121531d98677SRui Paulo 			/* Schedule a directed scan for hidden APs. */
1216*7a79cebfSGleb Smirnoff 			sc->sc_scan_pass = 1;
121731d98677SRui Paulo 			RSU_UNLOCK(sc);
121831d98677SRui Paulo 			ieee80211_new_state(vap, IEEE80211_S_SCAN, -1);
121931d98677SRui Paulo 			RSU_LOCK(sc);
122031d98677SRui Paulo 			break;
122131d98677SRui Paulo 		}
1222*7a79cebfSGleb Smirnoff 		sc->sc_scan_pass = 0;
122331d98677SRui Paulo 		break;
122431d98677SRui Paulo 	case R92S_EVT_JOIN_BSS:
122531d98677SRui Paulo 		if (vap->iv_state == IEEE80211_S_AUTH)
122631d98677SRui Paulo 			rsu_event_join_bss(sc, buf, len);
122731d98677SRui Paulo 		break;
1228910593b5SHans Petter Selasky #if 0
1229910593b5SHans Petter Selasky XXX This event is occurring regularly, possibly due to some power saving event
1230910593b5SHans Petter Selasky XXX and disrupts the WLAN traffic. Disable for now.
123131d98677SRui Paulo 	case R92S_EVT_DEL_STA:
123231d98677SRui Paulo 		DPRINTF("disassociated from %s\n", ether_sprintf(buf));
123331d98677SRui Paulo 		if (vap->iv_state == IEEE80211_S_RUN &&
123431d98677SRui Paulo 		    IEEE80211_ADDR_EQ(vap->iv_bss->ni_bssid, buf)) {
123531d98677SRui Paulo 			RSU_UNLOCK(sc);
123631d98677SRui Paulo 			ieee80211_new_state(vap, IEEE80211_S_SCAN, -1);
123731d98677SRui Paulo 			RSU_LOCK(sc);
123831d98677SRui Paulo 		}
123931d98677SRui Paulo 		break;
1240910593b5SHans Petter Selasky #endif
124131d98677SRui Paulo 	case R92S_EVT_WPS_PBC:
124231d98677SRui Paulo 		DPRINTF("WPS PBC pushed.\n");
124331d98677SRui Paulo 		break;
124431d98677SRui Paulo 	case R92S_EVT_FWDBG:
1245*7a79cebfSGleb Smirnoff 		if (vap->iv_ifp->if_flags & IFF_DEBUG) {
124631d98677SRui Paulo 			buf[60] = '\0';
124731d98677SRui Paulo 			printf("FWDBG: %s\n", (char *)buf);
124831d98677SRui Paulo 		}
124931d98677SRui Paulo 		break;
1250910593b5SHans Petter Selasky 	default:
1251910593b5SHans Petter Selasky 		break;
125231d98677SRui Paulo 	}
125331d98677SRui Paulo }
125431d98677SRui Paulo 
125531d98677SRui Paulo static void
125631d98677SRui Paulo rsu_rx_multi_event(struct rsu_softc *sc, uint8_t *buf, int len)
125731d98677SRui Paulo {
125831d98677SRui Paulo 	struct r92s_fw_cmd_hdr *cmd;
125931d98677SRui Paulo 	int cmdsz;
126031d98677SRui Paulo 
126131d98677SRui Paulo 	DPRINTFN(6, "Rx events len=%d\n", len);
126231d98677SRui Paulo 
126331d98677SRui Paulo 	/* Skip Rx status. */
126431d98677SRui Paulo 	buf += sizeof(struct r92s_rx_stat);
126531d98677SRui Paulo 	len -= sizeof(struct r92s_rx_stat);
126631d98677SRui Paulo 
126731d98677SRui Paulo 	/* Process all events. */
126831d98677SRui Paulo 	for (;;) {
126931d98677SRui Paulo 		/* Check that command header fits. */
127031d98677SRui Paulo 		if (__predict_false(len < sizeof(*cmd)))
127131d98677SRui Paulo 			break;
127231d98677SRui Paulo 		cmd = (struct r92s_fw_cmd_hdr *)buf;
127331d98677SRui Paulo 		/* Check that command payload fits. */
127431d98677SRui Paulo 		cmdsz = le16toh(cmd->len);
127531d98677SRui Paulo 		if (__predict_false(len < sizeof(*cmd) + cmdsz))
127631d98677SRui Paulo 			break;
127731d98677SRui Paulo 
127831d98677SRui Paulo 		/* Process firmware event. */
127931d98677SRui Paulo 		rsu_rx_event(sc, cmd->code, (uint8_t *)&cmd[1], cmdsz);
128031d98677SRui Paulo 
128131d98677SRui Paulo 		if (!(cmd->seq & R92S_FW_CMD_MORE))
128231d98677SRui Paulo 			break;
128331d98677SRui Paulo 		buf += sizeof(*cmd) + cmdsz;
128431d98677SRui Paulo 		len -= sizeof(*cmd) + cmdsz;
128531d98677SRui Paulo 	}
128631d98677SRui Paulo }
128731d98677SRui Paulo 
128831d98677SRui Paulo static int8_t
128931d98677SRui Paulo rsu_get_rssi(struct rsu_softc *sc, int rate, void *physt)
129031d98677SRui Paulo {
129131d98677SRui Paulo 	static const int8_t cckoff[] = { 14, -2, -20, -40 };
129231d98677SRui Paulo 	struct r92s_rx_phystat *phy;
129331d98677SRui Paulo 	struct r92s_rx_cck *cck;
129431d98677SRui Paulo 	uint8_t rpt;
129531d98677SRui Paulo 	int8_t rssi;
129631d98677SRui Paulo 
129731d98677SRui Paulo 	if (rate <= 3) {
129831d98677SRui Paulo 		cck = (struct r92s_rx_cck *)physt;
129931d98677SRui Paulo 		rpt = (cck->agc_rpt >> 6) & 0x3;
130031d98677SRui Paulo 		rssi = cck->agc_rpt & 0x3e;
130131d98677SRui Paulo 		rssi = cckoff[rpt] - rssi;
130231d98677SRui Paulo 	} else {	/* OFDM/HT. */
130331d98677SRui Paulo 		phy = (struct r92s_rx_phystat *)physt;
130431d98677SRui Paulo 		rssi = ((le32toh(phy->phydw1) >> 1) & 0x7f) - 106;
130531d98677SRui Paulo 	}
130631d98677SRui Paulo 	return (rssi);
130731d98677SRui Paulo }
130831d98677SRui Paulo 
130931d98677SRui Paulo static struct mbuf *
131031d98677SRui Paulo rsu_rx_frame(struct rsu_softc *sc, uint8_t *buf, int pktlen, int *rssi)
131131d98677SRui Paulo {
1312*7a79cebfSGleb Smirnoff 	struct ieee80211com *ic = &sc->sc_ic;
131331d98677SRui Paulo 	struct ieee80211_frame *wh;
131431d98677SRui Paulo 	struct r92s_rx_stat *stat;
131531d98677SRui Paulo 	uint32_t rxdw0, rxdw3;
131631d98677SRui Paulo 	struct mbuf *m;
131731d98677SRui Paulo 	uint8_t rate;
131831d98677SRui Paulo 	int infosz;
131931d98677SRui Paulo 
132031d98677SRui Paulo 	stat = (struct r92s_rx_stat *)buf;
132131d98677SRui Paulo 	rxdw0 = le32toh(stat->rxdw0);
132231d98677SRui Paulo 	rxdw3 = le32toh(stat->rxdw3);
132331d98677SRui Paulo 
132431d98677SRui Paulo 	if (__predict_false(rxdw0 & R92S_RXDW0_CRCERR)) {
1325*7a79cebfSGleb Smirnoff 		counter_u64_add(ic->ic_ierrors, 1);
132631d98677SRui Paulo 		return NULL;
132731d98677SRui Paulo 	}
132831d98677SRui Paulo 	if (__predict_false(pktlen < sizeof(*wh) || pktlen > MCLBYTES)) {
1329*7a79cebfSGleb Smirnoff 		counter_u64_add(ic->ic_ierrors, 1);
133031d98677SRui Paulo 		return NULL;
133131d98677SRui Paulo 	}
133231d98677SRui Paulo 
133331d98677SRui Paulo 	rate = MS(rxdw3, R92S_RXDW3_RATE);
133431d98677SRui Paulo 	infosz = MS(rxdw0, R92S_RXDW0_INFOSZ) * 8;
133531d98677SRui Paulo 
133631d98677SRui Paulo 	/* Get RSSI from PHY status descriptor if present. */
133731d98677SRui Paulo 	if (infosz != 0)
133831d98677SRui Paulo 		*rssi = rsu_get_rssi(sc, rate, &stat[1]);
133931d98677SRui Paulo 	else
134031d98677SRui Paulo 		*rssi = 0;
134131d98677SRui Paulo 
134231d98677SRui Paulo 	DPRINTFN(5, "Rx frame len=%d rate=%d infosz=%d rssi=%d\n",
134331d98677SRui Paulo 	    pktlen, rate, infosz, *rssi);
134431d98677SRui Paulo 
1345107f00c0SKevin Lo 	m = m_get2(pktlen, M_NOWAIT, MT_DATA, M_PKTHDR);
134631d98677SRui Paulo 	if (__predict_false(m == NULL)) {
1347*7a79cebfSGleb Smirnoff 		counter_u64_add(ic->ic_ierrors, 1);
134831d98677SRui Paulo 		return NULL;
134931d98677SRui Paulo 	}
135031d98677SRui Paulo 	/* Hardware does Rx TCP checksum offload. */
135131d98677SRui Paulo 	if (rxdw3 & R92S_RXDW3_TCPCHKVALID) {
135231d98677SRui Paulo 		if (__predict_true(rxdw3 & R92S_RXDW3_TCPCHKRPT))
135331d98677SRui Paulo 			m->m_pkthdr.csum_flags |= CSUM_DATA_VALID;
135431d98677SRui Paulo 	}
135531d98677SRui Paulo 	wh = (struct ieee80211_frame *)((uint8_t *)&stat[1] + infosz);
135631d98677SRui Paulo 	memcpy(mtod(m, uint8_t *), wh, pktlen);
135731d98677SRui Paulo 	m->m_pkthdr.len = m->m_len = pktlen;
135831d98677SRui Paulo 
135931d98677SRui Paulo 	if (ieee80211_radiotap_active(ic)) {
136031d98677SRui Paulo 		struct rsu_rx_radiotap_header *tap = &sc->sc_rxtap;
136131d98677SRui Paulo 
136231d98677SRui Paulo 		/* Map HW rate index to 802.11 rate. */
136331d98677SRui Paulo 		tap->wr_flags = 2;
136431d98677SRui Paulo 		if (!(rxdw3 & R92S_RXDW3_HTC)) {
136531d98677SRui Paulo 			switch (rate) {
136631d98677SRui Paulo 			/* CCK. */
136731d98677SRui Paulo 			case  0: tap->wr_rate =   2; break;
136831d98677SRui Paulo 			case  1: tap->wr_rate =   4; break;
136931d98677SRui Paulo 			case  2: tap->wr_rate =  11; break;
137031d98677SRui Paulo 			case  3: tap->wr_rate =  22; break;
137131d98677SRui Paulo 			/* OFDM. */
137231d98677SRui Paulo 			case  4: tap->wr_rate =  12; break;
137331d98677SRui Paulo 			case  5: tap->wr_rate =  18; break;
137431d98677SRui Paulo 			case  6: tap->wr_rate =  24; break;
137531d98677SRui Paulo 			case  7: tap->wr_rate =  36; break;
137631d98677SRui Paulo 			case  8: tap->wr_rate =  48; break;
137731d98677SRui Paulo 			case  9: tap->wr_rate =  72; break;
137831d98677SRui Paulo 			case 10: tap->wr_rate =  96; break;
137931d98677SRui Paulo 			case 11: tap->wr_rate = 108; break;
138031d98677SRui Paulo 			}
138131d98677SRui Paulo 		} else if (rate >= 12) {	/* MCS0~15. */
138231d98677SRui Paulo 			/* Bit 7 set means HT MCS instead of rate. */
138331d98677SRui Paulo 			tap->wr_rate = 0x80 | (rate - 12);
138431d98677SRui Paulo 		}
138531d98677SRui Paulo 		tap->wr_dbm_antsignal = *rssi;
138631d98677SRui Paulo 		tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq);
138731d98677SRui Paulo 		tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags);
138831d98677SRui Paulo 	}
138931d98677SRui Paulo 
139031d98677SRui Paulo 	return (m);
139131d98677SRui Paulo }
139231d98677SRui Paulo 
139331d98677SRui Paulo static struct mbuf *
139431d98677SRui Paulo rsu_rx_multi_frame(struct rsu_softc *sc, uint8_t *buf, int len, int *rssi)
139531d98677SRui Paulo {
139631d98677SRui Paulo 	struct r92s_rx_stat *stat;
139731d98677SRui Paulo 	uint32_t rxdw0;
139831d98677SRui Paulo 	int totlen, pktlen, infosz, npkts;
139931d98677SRui Paulo 	struct mbuf *m, *m0 = NULL, *prevm = NULL;
140031d98677SRui Paulo 
140131d98677SRui Paulo 	/* Get the number of encapsulated frames. */
140231d98677SRui Paulo 	stat = (struct r92s_rx_stat *)buf;
140331d98677SRui Paulo 	npkts = MS(le32toh(stat->rxdw2), R92S_RXDW2_PKTCNT);
140431d98677SRui Paulo 	DPRINTFN(6, "Rx %d frames in one chunk\n", npkts);
140531d98677SRui Paulo 
140631d98677SRui Paulo 	/* Process all of them. */
140731d98677SRui Paulo 	while (npkts-- > 0) {
140831d98677SRui Paulo 		if (__predict_false(len < sizeof(*stat)))
140931d98677SRui Paulo 			break;
141031d98677SRui Paulo 		stat = (struct r92s_rx_stat *)buf;
141131d98677SRui Paulo 		rxdw0 = le32toh(stat->rxdw0);
141231d98677SRui Paulo 
141331d98677SRui Paulo 		pktlen = MS(rxdw0, R92S_RXDW0_PKTLEN);
141431d98677SRui Paulo 		if (__predict_false(pktlen == 0))
141531d98677SRui Paulo 			break;
141631d98677SRui Paulo 
141731d98677SRui Paulo 		infosz = MS(rxdw0, R92S_RXDW0_INFOSZ) * 8;
141831d98677SRui Paulo 
141931d98677SRui Paulo 		/* Make sure everything fits in xfer. */
142031d98677SRui Paulo 		totlen = sizeof(*stat) + infosz + pktlen;
142131d98677SRui Paulo 		if (__predict_false(totlen > len))
142231d98677SRui Paulo 			break;
142331d98677SRui Paulo 
142431d98677SRui Paulo 		/* Process 802.11 frame. */
142531d98677SRui Paulo 		m = rsu_rx_frame(sc, buf, pktlen, rssi);
142631d98677SRui Paulo 		if (m0 == NULL)
142731d98677SRui Paulo 			m0 = m;
142831d98677SRui Paulo 		if (prevm == NULL)
142931d98677SRui Paulo 			prevm = m;
143031d98677SRui Paulo 		else {
143131d98677SRui Paulo 			prevm->m_next = m;
143231d98677SRui Paulo 			prevm = m;
143331d98677SRui Paulo 		}
143431d98677SRui Paulo 		/* Next chunk is 128-byte aligned. */
143531d98677SRui Paulo 		totlen = (totlen + 127) & ~127;
143631d98677SRui Paulo 		buf += totlen;
143731d98677SRui Paulo 		len -= totlen;
143831d98677SRui Paulo 	}
143931d98677SRui Paulo 
144031d98677SRui Paulo 	return (m0);
144131d98677SRui Paulo }
144231d98677SRui Paulo 
144331d98677SRui Paulo static struct mbuf *
144431d98677SRui Paulo rsu_rxeof(struct usb_xfer *xfer, struct rsu_data *data, int *rssi)
144531d98677SRui Paulo {
144631d98677SRui Paulo 	struct rsu_softc *sc = data->sc;
1447*7a79cebfSGleb Smirnoff 	struct ieee80211com *ic = &sc->sc_ic;
144831d98677SRui Paulo 	struct r92s_rx_stat *stat;
144931d98677SRui Paulo 	int len;
145031d98677SRui Paulo 
145131d98677SRui Paulo 	usbd_xfer_status(xfer, &len, NULL, NULL, NULL);
145231d98677SRui Paulo 
145331d98677SRui Paulo 	if (__predict_false(len < sizeof(*stat))) {
145431d98677SRui Paulo 		DPRINTF("xfer too short %d\n", len);
1455*7a79cebfSGleb Smirnoff 		counter_u64_add(ic->ic_ierrors, 1);
145631d98677SRui Paulo 		return (NULL);
145731d98677SRui Paulo 	}
145831d98677SRui Paulo 	/* Determine if it is a firmware C2H event or an 802.11 frame. */
145931d98677SRui Paulo 	stat = (struct r92s_rx_stat *)data->buf;
146031d98677SRui Paulo 	if ((le32toh(stat->rxdw1) & 0x1ff) == 0x1ff) {
146131d98677SRui Paulo 		rsu_rx_multi_event(sc, data->buf, len);
146231d98677SRui Paulo 		/* No packets to process. */
146331d98677SRui Paulo 		return (NULL);
146431d98677SRui Paulo 	} else
146531d98677SRui Paulo 		return (rsu_rx_multi_frame(sc, data->buf, len, rssi));
146631d98677SRui Paulo }
146731d98677SRui Paulo 
146831d98677SRui Paulo static void
146931d98677SRui Paulo rsu_bulk_rx_callback(struct usb_xfer *xfer, usb_error_t error)
147031d98677SRui Paulo {
147131d98677SRui Paulo 	struct rsu_softc *sc = usbd_xfer_softc(xfer);
1472*7a79cebfSGleb Smirnoff 	struct ieee80211com *ic = &sc->sc_ic;
147331d98677SRui Paulo 	struct ieee80211_frame *wh;
147431d98677SRui Paulo 	struct ieee80211_node *ni;
147531d98677SRui Paulo 	struct mbuf *m = NULL, *next;
147631d98677SRui Paulo 	struct rsu_data *data;
147731d98677SRui Paulo 	int rssi = 1;
147831d98677SRui Paulo 
147931d98677SRui Paulo 	RSU_ASSERT_LOCKED(sc);
148031d98677SRui Paulo 
148131d98677SRui Paulo 	switch (USB_GET_STATE(xfer)) {
148231d98677SRui Paulo 	case USB_ST_TRANSFERRED:
148331d98677SRui Paulo 		data = STAILQ_FIRST(&sc->sc_rx_active);
148431d98677SRui Paulo 		if (data == NULL)
148531d98677SRui Paulo 			goto tr_setup;
148631d98677SRui Paulo 		STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next);
148731d98677SRui Paulo 		m = rsu_rxeof(xfer, data, &rssi);
148831d98677SRui Paulo 		STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next);
148931d98677SRui Paulo 		/* FALLTHROUGH */
149031d98677SRui Paulo 	case USB_ST_SETUP:
149131d98677SRui Paulo tr_setup:
149231d98677SRui Paulo 		data = STAILQ_FIRST(&sc->sc_rx_inactive);
149331d98677SRui Paulo 		if (data == NULL) {
149431d98677SRui Paulo 			KASSERT(m == NULL, ("mbuf isn't NULL"));
149531d98677SRui Paulo 			return;
149631d98677SRui Paulo 		}
149731d98677SRui Paulo 		STAILQ_REMOVE_HEAD(&sc->sc_rx_inactive, next);
149831d98677SRui Paulo 		STAILQ_INSERT_TAIL(&sc->sc_rx_active, data, next);
149931d98677SRui Paulo 		usbd_xfer_set_frame_data(xfer, 0, data->buf,
150031d98677SRui Paulo 		    usbd_xfer_max_len(xfer));
150131d98677SRui Paulo 		usbd_transfer_submit(xfer);
150231d98677SRui Paulo 		/*
150331d98677SRui Paulo 		 * To avoid LOR we should unlock our private mutex here to call
150431d98677SRui Paulo 		 * ieee80211_input() because here is at the end of a USB
150531d98677SRui Paulo 		 * callback and safe to unlock.
150631d98677SRui Paulo 		 */
150731d98677SRui Paulo 		RSU_UNLOCK(sc);
150831d98677SRui Paulo 		while (m != NULL) {
150931d98677SRui Paulo 			next = m->m_next;
151031d98677SRui Paulo 			m->m_next = NULL;
151131d98677SRui Paulo 			wh = mtod(m, struct ieee80211_frame *);
151231d98677SRui Paulo 			ni = ieee80211_find_rxnode(ic,
151331d98677SRui Paulo 			    (struct ieee80211_frame_min *)wh);
151431d98677SRui Paulo 			if (ni != NULL) {
151531d98677SRui Paulo 				(void)ieee80211_input(ni, m, rssi, 0);
151631d98677SRui Paulo 				ieee80211_free_node(ni);
151731d98677SRui Paulo 			} else
151831d98677SRui Paulo 				(void)ieee80211_input_all(ic, m, rssi, 0);
151931d98677SRui Paulo 			m = next;
152031d98677SRui Paulo 		}
152131d98677SRui Paulo 		RSU_LOCK(sc);
152231d98677SRui Paulo 		break;
152331d98677SRui Paulo 	default:
152431d98677SRui Paulo 		/* needs it to the inactive queue due to a error. */
152531d98677SRui Paulo 		data = STAILQ_FIRST(&sc->sc_rx_active);
152631d98677SRui Paulo 		if (data != NULL) {
152731d98677SRui Paulo 			STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next);
152831d98677SRui Paulo 			STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next);
152931d98677SRui Paulo 		}
153031d98677SRui Paulo 		if (error != USB_ERR_CANCELLED) {
153131d98677SRui Paulo 			usbd_xfer_set_stall(xfer);
1532*7a79cebfSGleb Smirnoff 			counter_u64_add(ic->ic_ierrors, 1);
153331d98677SRui Paulo 			goto tr_setup;
153431d98677SRui Paulo 		}
153531d98677SRui Paulo 		break;
153631d98677SRui Paulo 	}
153731d98677SRui Paulo 
153831d98677SRui Paulo }
153931d98677SRui Paulo 
154031d98677SRui Paulo static void
154131d98677SRui Paulo rsu_txeof(struct usb_xfer *xfer, struct rsu_data *data)
154231d98677SRui Paulo {
154331d98677SRui Paulo 
154431d98677SRui Paulo 	if (data->m) {
154531d98677SRui Paulo 		/* XXX status? */
1546*7a79cebfSGleb Smirnoff 		ieee80211_tx_complete(data->ni, data->m, 0);
154731d98677SRui Paulo 		data->m = NULL;
154831d98677SRui Paulo 		data->ni = NULL;
154931d98677SRui Paulo 	}
155031d98677SRui Paulo }
155131d98677SRui Paulo 
155231d98677SRui Paulo static void
1553400b4e53SHans Petter Selasky rsu_bulk_tx_callback_sub(struct usb_xfer *xfer, usb_error_t error,
1554400b4e53SHans Petter Selasky     uint8_t which)
155531d98677SRui Paulo {
155631d98677SRui Paulo 	struct rsu_softc *sc = usbd_xfer_softc(xfer);
1557*7a79cebfSGleb Smirnoff 	struct ieee80211com *ic = &sc->sc_ic;
155831d98677SRui Paulo 	struct rsu_data *data;
155931d98677SRui Paulo 
156031d98677SRui Paulo 	RSU_ASSERT_LOCKED(sc);
156131d98677SRui Paulo 
156231d98677SRui Paulo 	switch (USB_GET_STATE(xfer)) {
156331d98677SRui Paulo 	case USB_ST_TRANSFERRED:
1564400b4e53SHans Petter Selasky 		data = STAILQ_FIRST(&sc->sc_tx_active[which]);
156531d98677SRui Paulo 		if (data == NULL)
156631d98677SRui Paulo 			goto tr_setup;
156731d98677SRui Paulo 		DPRINTF("transfer done %p\n", data);
1568400b4e53SHans Petter Selasky 		STAILQ_REMOVE_HEAD(&sc->sc_tx_active[which], next);
156931d98677SRui Paulo 		rsu_txeof(xfer, data);
157031d98677SRui Paulo 		STAILQ_INSERT_TAIL(&sc->sc_tx_inactive, data, next);
157131d98677SRui Paulo 		/* FALLTHROUGH */
157231d98677SRui Paulo 	case USB_ST_SETUP:
157331d98677SRui Paulo tr_setup:
1574400b4e53SHans Petter Selasky 		data = STAILQ_FIRST(&sc->sc_tx_pending[which]);
157531d98677SRui Paulo 		if (data == NULL) {
157631d98677SRui Paulo 			DPRINTF("empty pending queue sc %p\n", sc);
157731d98677SRui Paulo 			return;
157831d98677SRui Paulo 		}
1579400b4e53SHans Petter Selasky 		STAILQ_REMOVE_HEAD(&sc->sc_tx_pending[which], next);
1580400b4e53SHans Petter Selasky 		STAILQ_INSERT_TAIL(&sc->sc_tx_active[which], data, next);
158131d98677SRui Paulo 		usbd_xfer_set_frame_data(xfer, 0, data->buf, data->buflen);
158231d98677SRui Paulo 		DPRINTF("submitting transfer %p\n", data);
158331d98677SRui Paulo 		usbd_transfer_submit(xfer);
158431d98677SRui Paulo 		break;
158531d98677SRui Paulo 	default:
1586400b4e53SHans Petter Selasky 		data = STAILQ_FIRST(&sc->sc_tx_active[which]);
1587400b4e53SHans Petter Selasky 		if (data != NULL) {
1588400b4e53SHans Petter Selasky 			STAILQ_REMOVE_HEAD(&sc->sc_tx_active[which], next);
1589400b4e53SHans Petter Selasky 			rsu_txeof(xfer, data);
1590400b4e53SHans Petter Selasky 			STAILQ_INSERT_TAIL(&sc->sc_tx_inactive, data, next);
159131d98677SRui Paulo 		}
1592*7a79cebfSGleb Smirnoff 		counter_u64_add(ic->ic_oerrors, 1);
1593400b4e53SHans Petter Selasky 
159431d98677SRui Paulo 		if (error != USB_ERR_CANCELLED) {
159531d98677SRui Paulo 			usbd_xfer_set_stall(xfer);
159631d98677SRui Paulo 			goto tr_setup;
159731d98677SRui Paulo 		}
159831d98677SRui Paulo 		break;
159931d98677SRui Paulo 	}
160031d98677SRui Paulo }
160131d98677SRui Paulo 
1602400b4e53SHans Petter Selasky static void
1603910593b5SHans Petter Selasky rsu_bulk_tx_callback_be_bk(struct usb_xfer *xfer, usb_error_t error)
1604400b4e53SHans Petter Selasky {
1605910593b5SHans Petter Selasky 	rsu_bulk_tx_callback_sub(xfer, error, RSU_BULK_TX_BE_BK);
1606400b4e53SHans Petter Selasky }
1607400b4e53SHans Petter Selasky 
1608400b4e53SHans Petter Selasky static void
1609910593b5SHans Petter Selasky rsu_bulk_tx_callback_vi_vo(struct usb_xfer *xfer, usb_error_t error)
1610400b4e53SHans Petter Selasky {
1611910593b5SHans Petter Selasky 	rsu_bulk_tx_callback_sub(xfer, error, RSU_BULK_TX_VI_VO);
1612400b4e53SHans Petter Selasky }
1613400b4e53SHans Petter Selasky 
161431d98677SRui Paulo static int
161531d98677SRui Paulo rsu_tx_start(struct rsu_softc *sc, struct ieee80211_node *ni,
1616400b4e53SHans Petter Selasky     struct mbuf *m0, struct rsu_data *data)
161731d98677SRui Paulo {
1618*7a79cebfSGleb Smirnoff 	struct ieee80211com *ic = &sc->sc_ic;
161931d98677SRui Paulo         struct ieee80211vap *vap = ni->ni_vap;
162031d98677SRui Paulo 	struct ieee80211_frame *wh;
162131d98677SRui Paulo 	struct ieee80211_key *k = NULL;
162231d98677SRui Paulo 	struct r92s_tx_desc *txd;
1623400b4e53SHans Petter Selasky 	uint8_t type;
1624400b4e53SHans Petter Selasky 	uint8_t tid = 0;
1625400b4e53SHans Petter Selasky 	uint8_t which;
1626400b4e53SHans Petter Selasky 	int hasqos;
1627400b4e53SHans Petter Selasky 	int xferlen;
162831d98677SRui Paulo 
162931d98677SRui Paulo 	RSU_ASSERT_LOCKED(sc);
163031d98677SRui Paulo 
163131d98677SRui Paulo 	wh = mtod(m0, struct ieee80211_frame *);
163231d98677SRui Paulo 	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
163331d98677SRui Paulo 
16345945b5f5SKevin Lo 	if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
163531d98677SRui Paulo 		k = ieee80211_crypto_encap(ni, m0);
163631d98677SRui Paulo 		if (k == NULL) {
163731d98677SRui Paulo 			device_printf(sc->sc_dev,
163831d98677SRui Paulo 			    "ieee80211_crypto_encap returns NULL.\n");
163931d98677SRui Paulo 			/* XXX we don't expect the fragmented frames */
164031d98677SRui Paulo 			m_freem(m0);
164131d98677SRui Paulo 			return (ENOBUFS);
164231d98677SRui Paulo 		}
164331d98677SRui Paulo 		wh = mtod(m0, struct ieee80211_frame *);
164431d98677SRui Paulo 	}
164531d98677SRui Paulo 	switch (type) {
164631d98677SRui Paulo 	case IEEE80211_FC0_TYPE_CTL:
164731d98677SRui Paulo 	case IEEE80211_FC0_TYPE_MGT:
1648910593b5SHans Petter Selasky 		which = rsu_wme_ac_xfer_map[WME_AC_VO];
164931d98677SRui Paulo 		break;
165031d98677SRui Paulo 	default:
1651910593b5SHans Petter Selasky 		which = rsu_wme_ac_xfer_map[M_WME_GETAC(m0)];
165231d98677SRui Paulo 		break;
165331d98677SRui Paulo 	}
165431d98677SRui Paulo 	hasqos = 0;
165531d98677SRui Paulo 
165631d98677SRui Paulo 	/* Fill Tx descriptor. */
165731d98677SRui Paulo 	txd = (struct r92s_tx_desc *)data->buf;
165831d98677SRui Paulo 	memset(txd, 0, sizeof(*txd));
165931d98677SRui Paulo 
166031d98677SRui Paulo 	txd->txdw0 |= htole32(
166131d98677SRui Paulo 	    SM(R92S_TXDW0_PKTLEN, m0->m_pkthdr.len) |
166231d98677SRui Paulo 	    SM(R92S_TXDW0_OFFSET, sizeof(*txd)) |
166331d98677SRui Paulo 	    R92S_TXDW0_OWN | R92S_TXDW0_FSG | R92S_TXDW0_LSG);
166431d98677SRui Paulo 
166531d98677SRui Paulo 	txd->txdw1 |= htole32(
166631d98677SRui Paulo 	    SM(R92S_TXDW1_MACID, R92S_MACID_BSS) |
166731d98677SRui Paulo 	    SM(R92S_TXDW1_QSEL, R92S_TXDW1_QSEL_BE));
166831d98677SRui Paulo 	if (!hasqos)
166931d98677SRui Paulo 		txd->txdw1 |= htole32(R92S_TXDW1_NONQOS);
167031d98677SRui Paulo #ifdef notyet
167131d98677SRui Paulo 	if (k != NULL) {
167231d98677SRui Paulo 		switch (k->wk_cipher->ic_cipher) {
167331d98677SRui Paulo 		case IEEE80211_CIPHER_WEP:
167431d98677SRui Paulo 			cipher = R92S_TXDW1_CIPHER_WEP;
167531d98677SRui Paulo 			break;
167631d98677SRui Paulo 		case IEEE80211_CIPHER_TKIP:
167731d98677SRui Paulo 			cipher = R92S_TXDW1_CIPHER_TKIP;
167831d98677SRui Paulo 			break;
167931d98677SRui Paulo 		case IEEE80211_CIPHER_AES_CCM:
168031d98677SRui Paulo 			cipher = R92S_TXDW1_CIPHER_AES;
168131d98677SRui Paulo 			break;
168231d98677SRui Paulo 		default:
168331d98677SRui Paulo 			cipher = R92S_TXDW1_CIPHER_NONE;
168431d98677SRui Paulo 		}
168531d98677SRui Paulo 		txd->txdw1 |= htole32(
168631d98677SRui Paulo 		    SM(R92S_TXDW1_CIPHER, cipher) |
168731d98677SRui Paulo 		    SM(R92S_TXDW1_KEYIDX, k->k_id));
168831d98677SRui Paulo 	}
168931d98677SRui Paulo #endif
169031d98677SRui Paulo 	txd->txdw2 |= htole32(R92S_TXDW2_BK);
169131d98677SRui Paulo 	if (IEEE80211_IS_MULTICAST(wh->i_addr1))
169231d98677SRui Paulo 		txd->txdw2 |= htole32(R92S_TXDW2_BMCAST);
169331d98677SRui Paulo 	/*
169431d98677SRui Paulo 	 * Firmware will use and increment the sequence number for the
169531d98677SRui Paulo 	 * specified TID.
169631d98677SRui Paulo 	 */
169731d98677SRui Paulo 	txd->txdw3 |= htole32(SM(R92S_TXDW3_SEQ, tid));
169831d98677SRui Paulo 
169931d98677SRui Paulo 	if (ieee80211_radiotap_active_vap(vap)) {
170031d98677SRui Paulo 		struct rsu_tx_radiotap_header *tap = &sc->sc_txtap;
170131d98677SRui Paulo 
170231d98677SRui Paulo 		tap->wt_flags = 0;
170331d98677SRui Paulo 		tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq);
170431d98677SRui Paulo 		tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
170531d98677SRui Paulo 		ieee80211_radiotap_tx(vap, m0);
170631d98677SRui Paulo 	}
170731d98677SRui Paulo 	xferlen = sizeof(*txd) + m0->m_pkthdr.len;
170831d98677SRui Paulo 	m_copydata(m0, 0, m0->m_pkthdr.len, (caddr_t)&txd[1]);
170931d98677SRui Paulo 
171031d98677SRui Paulo 	data->buflen = xferlen;
171131d98677SRui Paulo 	data->ni = ni;
171231d98677SRui Paulo 	data->m = m0;
1713400b4e53SHans Petter Selasky 	STAILQ_INSERT_TAIL(&sc->sc_tx_pending[which], data, next);
171431d98677SRui Paulo 
1715400b4e53SHans Petter Selasky 	/* start transfer, if any */
1716910593b5SHans Petter Selasky 	usbd_transfer_start(sc->sc_xfer[which]);
171731d98677SRui Paulo 	return (0);
171831d98677SRui Paulo }
171931d98677SRui Paulo 
1720*7a79cebfSGleb Smirnoff static int
1721*7a79cebfSGleb Smirnoff rsu_transmit(struct ieee80211com *ic, struct mbuf *m)
172231d98677SRui Paulo {
1723*7a79cebfSGleb Smirnoff 	struct rsu_softc *sc = ic->ic_softc;
1724*7a79cebfSGleb Smirnoff 	int error;
172531d98677SRui Paulo 
172631d98677SRui Paulo 	RSU_LOCK(sc);
1727*7a79cebfSGleb Smirnoff 	if (!sc->sc_running) {
172831d98677SRui Paulo 		RSU_UNLOCK(sc);
1729*7a79cebfSGleb Smirnoff 		return (ENXIO);
1730*7a79cebfSGleb Smirnoff 	}
1731*7a79cebfSGleb Smirnoff 	error = mbufq_enqueue(&sc->sc_snd, m);
1732*7a79cebfSGleb Smirnoff 	if (error) {
1733*7a79cebfSGleb Smirnoff 		RSU_UNLOCK(sc);
1734*7a79cebfSGleb Smirnoff 		return (error);
1735*7a79cebfSGleb Smirnoff 	}
1736*7a79cebfSGleb Smirnoff 	rsu_start(sc);
1737*7a79cebfSGleb Smirnoff 	RSU_UNLOCK(sc);
1738*7a79cebfSGleb Smirnoff 
1739*7a79cebfSGleb Smirnoff 	return (0);
174031d98677SRui Paulo }
174131d98677SRui Paulo 
174231d98677SRui Paulo static void
1743*7a79cebfSGleb Smirnoff rsu_start(struct rsu_softc *sc)
174431d98677SRui Paulo {
174531d98677SRui Paulo 	struct ieee80211_node *ni;
174631d98677SRui Paulo 	struct rsu_data *bf;
1747400b4e53SHans Petter Selasky 	struct mbuf *m;
174831d98677SRui Paulo 
174931d98677SRui Paulo 	RSU_ASSERT_LOCKED(sc);
175031d98677SRui Paulo 
1751*7a79cebfSGleb Smirnoff 	while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) {
1752*7a79cebfSGleb Smirnoff 		bf = rsu_getbuf(sc);
1753*7a79cebfSGleb Smirnoff 		if (bf == NULL) {
1754*7a79cebfSGleb Smirnoff 			mbufq_prepend(&sc->sc_snd, m);
175531d98677SRui Paulo 			break;
1756*7a79cebfSGleb Smirnoff 		}
1757*7a79cebfSGleb Smirnoff 
175831d98677SRui Paulo 		ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
175931d98677SRui Paulo 		m->m_pkthdr.rcvif = NULL;
176031d98677SRui Paulo 
1761*7a79cebfSGleb Smirnoff 		if (rsu_tx_start(sc, ni, m, bf) != 0) {
1762*7a79cebfSGleb Smirnoff 			if_inc_counter(ni->ni_vap->iv_ifp,
1763*7a79cebfSGleb Smirnoff 			    IFCOUNTER_OERRORS, 1);
176431d98677SRui Paulo 			STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, bf, next);
176531d98677SRui Paulo 			ieee80211_free_node(ni);
1766*7a79cebfSGleb Smirnoff 			break;
176731d98677SRui Paulo 		}
176831d98677SRui Paulo 	}
176931d98677SRui Paulo }
177031d98677SRui Paulo 
1771*7a79cebfSGleb Smirnoff static void
1772*7a79cebfSGleb Smirnoff rsu_parent(struct ieee80211com *ic)
177331d98677SRui Paulo {
1774d3fdd08cSAdrian Chadd 	struct rsu_softc *sc = ic->ic_softc;
1775*7a79cebfSGleb Smirnoff 	int startall = 0;
177631d98677SRui Paulo 
1777*7a79cebfSGleb Smirnoff 	RSU_LOCK(sc);
1778*7a79cebfSGleb Smirnoff 	if (ic->ic_nrunning > 0) {
1779*7a79cebfSGleb Smirnoff 		if (!sc->sc_running) {
1780d3fdd08cSAdrian Chadd 			rsu_init(sc);
178131d98677SRui Paulo 			startall = 1;
178231d98677SRui Paulo 		}
1783*7a79cebfSGleb Smirnoff 	} else if (sc->sc_running)
1784*7a79cebfSGleb Smirnoff 		rsu_stop(sc);
1785*7a79cebfSGleb Smirnoff 	RSU_UNLOCK(sc);
1786*7a79cebfSGleb Smirnoff 
178731d98677SRui Paulo 	if (startall)
178831d98677SRui Paulo 		ieee80211_start_all(ic);
178931d98677SRui Paulo }
179031d98677SRui Paulo 
179131d98677SRui Paulo /*
179231d98677SRui Paulo  * Power on sequence for A-cut adapters.
179331d98677SRui Paulo  */
179431d98677SRui Paulo static void
179531d98677SRui Paulo rsu_power_on_acut(struct rsu_softc *sc)
179631d98677SRui Paulo {
179731d98677SRui Paulo 	uint32_t reg;
179831d98677SRui Paulo 
179931d98677SRui Paulo 	rsu_write_1(sc, R92S_SPS0_CTRL + 1, 0x53);
180031d98677SRui Paulo 	rsu_write_1(sc, R92S_SPS0_CTRL + 0, 0x57);
180131d98677SRui Paulo 
180231d98677SRui Paulo 	/* Enable AFE macro block's bandgap and Mbias. */
180331d98677SRui Paulo 	rsu_write_1(sc, R92S_AFE_MISC,
180431d98677SRui Paulo 	    rsu_read_1(sc, R92S_AFE_MISC) |
180531d98677SRui Paulo 	    R92S_AFE_MISC_BGEN | R92S_AFE_MISC_MBEN);
180631d98677SRui Paulo 	/* Enable LDOA15 block. */
180731d98677SRui Paulo 	rsu_write_1(sc, R92S_LDOA15_CTRL,
180831d98677SRui Paulo 	    rsu_read_1(sc, R92S_LDOA15_CTRL) | R92S_LDA15_EN);
180931d98677SRui Paulo 
181031d98677SRui Paulo 	rsu_write_1(sc, R92S_SPS1_CTRL,
181131d98677SRui Paulo 	    rsu_read_1(sc, R92S_SPS1_CTRL) | R92S_SPS1_LDEN);
181231d98677SRui Paulo 	usb_pause_mtx(&sc->sc_mtx, 2 * hz);
181331d98677SRui Paulo 	/* Enable switch regulator block. */
181431d98677SRui Paulo 	rsu_write_1(sc, R92S_SPS1_CTRL,
181531d98677SRui Paulo 	    rsu_read_1(sc, R92S_SPS1_CTRL) | R92S_SPS1_SWEN);
181631d98677SRui Paulo 
181731d98677SRui Paulo 	rsu_write_4(sc, R92S_SPS1_CTRL, 0x00a7b267);
181831d98677SRui Paulo 
181931d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1,
182031d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_ISO_CTRL + 1) | 0x08);
182131d98677SRui Paulo 
182231d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_FUNC_EN + 1,
182331d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x20);
182431d98677SRui Paulo 
182531d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1,
182631d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_ISO_CTRL + 1) & ~0x90);
182731d98677SRui Paulo 
182831d98677SRui Paulo 	/* Enable AFE clock. */
182931d98677SRui Paulo 	rsu_write_1(sc, R92S_AFE_XTAL_CTRL + 1,
183031d98677SRui Paulo 	    rsu_read_1(sc, R92S_AFE_XTAL_CTRL + 1) & ~0x04);
183131d98677SRui Paulo 	/* Enable AFE PLL macro block. */
183231d98677SRui Paulo 	rsu_write_1(sc, R92S_AFE_PLL_CTRL,
183331d98677SRui Paulo 	    rsu_read_1(sc, R92S_AFE_PLL_CTRL) | 0x11);
183431d98677SRui Paulo 	/* Attach AFE PLL to MACTOP/BB. */
183531d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_ISO_CTRL,
183631d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_ISO_CTRL) & ~0x11);
183731d98677SRui Paulo 
183831d98677SRui Paulo 	/* Switch to 40MHz clock instead of 80MHz. */
183931d98677SRui Paulo 	rsu_write_2(sc, R92S_SYS_CLKR,
184031d98677SRui Paulo 	    rsu_read_2(sc, R92S_SYS_CLKR) & ~R92S_SYS_CLKSEL);
184131d98677SRui Paulo 
184231d98677SRui Paulo 	/* Enable MAC clock. */
184331d98677SRui Paulo 	rsu_write_2(sc, R92S_SYS_CLKR,
184431d98677SRui Paulo 	    rsu_read_2(sc, R92S_SYS_CLKR) |
184531d98677SRui Paulo 	    R92S_MAC_CLK_EN | R92S_SYS_CLK_EN);
184631d98677SRui Paulo 
184731d98677SRui Paulo 	rsu_write_1(sc, R92S_PMC_FSM, 0x02);
184831d98677SRui Paulo 
184931d98677SRui Paulo 	/* Enable digital core and IOREG R/W. */
185031d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_FUNC_EN + 1,
185131d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x08);
185231d98677SRui Paulo 
185331d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_FUNC_EN + 1,
185431d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x80);
185531d98677SRui Paulo 
185631d98677SRui Paulo 	/* Switch the control path to firmware. */
185731d98677SRui Paulo 	reg = rsu_read_2(sc, R92S_SYS_CLKR);
185831d98677SRui Paulo 	reg = (reg & ~R92S_SWHW_SEL) | R92S_FWHW_SEL;
185931d98677SRui Paulo 	rsu_write_2(sc, R92S_SYS_CLKR, reg);
186031d98677SRui Paulo 
186131d98677SRui Paulo 	rsu_write_2(sc, R92S_CR, 0x37fc);
186231d98677SRui Paulo 
186331d98677SRui Paulo 	/* Fix USB RX FIFO issue. */
186431d98677SRui Paulo 	rsu_write_1(sc, 0xfe5c,
186531d98677SRui Paulo 	    rsu_read_1(sc, 0xfe5c) | 0x80);
186631d98677SRui Paulo 	rsu_write_1(sc, 0x00ab,
186731d98677SRui Paulo 	    rsu_read_1(sc, 0x00ab) | 0xc0);
186831d98677SRui Paulo 
186931d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_CLKR,
187031d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_CLKR) & ~R92S_SYS_CPU_CLKSEL);
187131d98677SRui Paulo }
187231d98677SRui Paulo 
187331d98677SRui Paulo /*
187431d98677SRui Paulo  * Power on sequence for B-cut and C-cut adapters.
187531d98677SRui Paulo  */
187631d98677SRui Paulo static void
187731d98677SRui Paulo rsu_power_on_bcut(struct rsu_softc *sc)
187831d98677SRui Paulo {
187931d98677SRui Paulo 	uint32_t reg;
188031d98677SRui Paulo 	int ntries;
188131d98677SRui Paulo 
188231d98677SRui Paulo 	/* Prevent eFuse leakage. */
188331d98677SRui Paulo 	rsu_write_1(sc, 0x37, 0xb0);
1884400b4e53SHans Petter Selasky 	usb_pause_mtx(&sc->sc_mtx, hz / 100);
188531d98677SRui Paulo 	rsu_write_1(sc, 0x37, 0x30);
188631d98677SRui Paulo 
188731d98677SRui Paulo 	/* Switch the control path to hardware. */
188831d98677SRui Paulo 	reg = rsu_read_2(sc, R92S_SYS_CLKR);
188931d98677SRui Paulo 	if (reg & R92S_FWHW_SEL) {
189031d98677SRui Paulo 		rsu_write_2(sc, R92S_SYS_CLKR,
189131d98677SRui Paulo 		    reg & ~(R92S_SWHW_SEL | R92S_FWHW_SEL));
189231d98677SRui Paulo 	}
189331d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_FUNC_EN + 1,
189431d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) & ~0x8c);
1895b41381aeSHans Petter Selasky 	rsu_ms_delay(sc);
189631d98677SRui Paulo 
189731d98677SRui Paulo 	rsu_write_1(sc, R92S_SPS0_CTRL + 1, 0x53);
189831d98677SRui Paulo 	rsu_write_1(sc, R92S_SPS0_CTRL + 0, 0x57);
189931d98677SRui Paulo 
190031d98677SRui Paulo 	reg = rsu_read_1(sc, R92S_AFE_MISC);
190131d98677SRui Paulo 	rsu_write_1(sc, R92S_AFE_MISC, reg | R92S_AFE_MISC_BGEN);
190231d98677SRui Paulo 	rsu_write_1(sc, R92S_AFE_MISC, reg | R92S_AFE_MISC_BGEN |
190331d98677SRui Paulo 	    R92S_AFE_MISC_MBEN | R92S_AFE_MISC_I32_EN);
190431d98677SRui Paulo 
190531d98677SRui Paulo 	/* Enable PLL. */
190631d98677SRui Paulo 	rsu_write_1(sc, R92S_LDOA15_CTRL,
190731d98677SRui Paulo 	    rsu_read_1(sc, R92S_LDOA15_CTRL) | R92S_LDA15_EN);
190831d98677SRui Paulo 
190931d98677SRui Paulo 	rsu_write_1(sc, R92S_LDOV12D_CTRL,
191031d98677SRui Paulo 	    rsu_read_1(sc, R92S_LDOV12D_CTRL) | R92S_LDV12_EN);
191131d98677SRui Paulo 
191231d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1,
191331d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_ISO_CTRL + 1) | 0x08);
191431d98677SRui Paulo 
191531d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_FUNC_EN + 1,
191631d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x20);
191731d98677SRui Paulo 
191831d98677SRui Paulo 	/* Support 64KB IMEM. */
191931d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1,
192031d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_ISO_CTRL + 1) & ~0x97);
192131d98677SRui Paulo 
192231d98677SRui Paulo 	/* Enable AFE clock. */
192331d98677SRui Paulo 	rsu_write_1(sc, R92S_AFE_XTAL_CTRL + 1,
192431d98677SRui Paulo 	    rsu_read_1(sc, R92S_AFE_XTAL_CTRL + 1) & ~0x04);
192531d98677SRui Paulo 	/* Enable AFE PLL macro block. */
192631d98677SRui Paulo 	reg = rsu_read_1(sc, R92S_AFE_PLL_CTRL);
192731d98677SRui Paulo 	rsu_write_1(sc, R92S_AFE_PLL_CTRL, reg | 0x11);
1928b41381aeSHans Petter Selasky 	rsu_ms_delay(sc);
192931d98677SRui Paulo 	rsu_write_1(sc, R92S_AFE_PLL_CTRL, reg | 0x51);
1930b41381aeSHans Petter Selasky 	rsu_ms_delay(sc);
193131d98677SRui Paulo 	rsu_write_1(sc, R92S_AFE_PLL_CTRL, reg | 0x11);
1932b41381aeSHans Petter Selasky 	rsu_ms_delay(sc);
193331d98677SRui Paulo 
193431d98677SRui Paulo 	/* Attach AFE PLL to MACTOP/BB. */
193531d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_ISO_CTRL,
193631d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_ISO_CTRL) & ~0x11);
193731d98677SRui Paulo 
193831d98677SRui Paulo 	/* Switch to 40MHz clock. */
193931d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_CLKR, 0x00);
194031d98677SRui Paulo 	/* Disable CPU clock and 80MHz SSC. */
194131d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_CLKR,
194231d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_CLKR) | 0xa0);
194331d98677SRui Paulo 	/* Enable MAC clock. */
194431d98677SRui Paulo 	rsu_write_2(sc, R92S_SYS_CLKR,
194531d98677SRui Paulo 	    rsu_read_2(sc, R92S_SYS_CLKR) |
194631d98677SRui Paulo 	    R92S_MAC_CLK_EN | R92S_SYS_CLK_EN);
194731d98677SRui Paulo 
194831d98677SRui Paulo 	rsu_write_1(sc, R92S_PMC_FSM, 0x02);
194931d98677SRui Paulo 
195031d98677SRui Paulo 	/* Enable digital core and IOREG R/W. */
195131d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_FUNC_EN + 1,
195231d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x08);
195331d98677SRui Paulo 
195431d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_FUNC_EN + 1,
195531d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x80);
195631d98677SRui Paulo 
195731d98677SRui Paulo 	/* Switch the control path to firmware. */
195831d98677SRui Paulo 	reg = rsu_read_2(sc, R92S_SYS_CLKR);
195931d98677SRui Paulo 	reg = (reg & ~R92S_SWHW_SEL) | R92S_FWHW_SEL;
196031d98677SRui Paulo 	rsu_write_2(sc, R92S_SYS_CLKR, reg);
196131d98677SRui Paulo 
196231d98677SRui Paulo 	rsu_write_2(sc, R92S_CR, 0x37fc);
196331d98677SRui Paulo 
196431d98677SRui Paulo 	/* Fix USB RX FIFO issue. */
196531d98677SRui Paulo 	rsu_write_1(sc, 0xfe5c,
196631d98677SRui Paulo 	    rsu_read_1(sc, 0xfe5c) | 0x80);
196731d98677SRui Paulo 
196831d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_CLKR,
196931d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_CLKR) & ~R92S_SYS_CPU_CLKSEL);
197031d98677SRui Paulo 
197131d98677SRui Paulo 	rsu_write_1(sc, 0xfe1c, 0x80);
197231d98677SRui Paulo 
197331d98677SRui Paulo 	/* Make sure TxDMA is ready to download firmware. */
197431d98677SRui Paulo 	for (ntries = 0; ntries < 20; ntries++) {
197531d98677SRui Paulo 		reg = rsu_read_1(sc, R92S_TCR);
197631d98677SRui Paulo 		if ((reg & (R92S_TCR_IMEM_CHK_RPT | R92S_TCR_EMEM_CHK_RPT)) ==
197731d98677SRui Paulo 		    (R92S_TCR_IMEM_CHK_RPT | R92S_TCR_EMEM_CHK_RPT))
197831d98677SRui Paulo 			break;
1979b41381aeSHans Petter Selasky 		rsu_ms_delay(sc);
198031d98677SRui Paulo 	}
198131d98677SRui Paulo 	if (ntries == 20) {
198231d98677SRui Paulo 		DPRINTF("TxDMA is not ready\n");
198331d98677SRui Paulo 		/* Reset TxDMA. */
198431d98677SRui Paulo 		reg = rsu_read_1(sc, R92S_CR);
198531d98677SRui Paulo 		rsu_write_1(sc, R92S_CR, reg & ~R92S_CR_TXDMA_EN);
1986b41381aeSHans Petter Selasky 		rsu_ms_delay(sc);
198731d98677SRui Paulo 		rsu_write_1(sc, R92S_CR, reg | R92S_CR_TXDMA_EN);
198831d98677SRui Paulo 	}
198931d98677SRui Paulo }
199031d98677SRui Paulo 
199131d98677SRui Paulo static void
199231d98677SRui Paulo rsu_power_off(struct rsu_softc *sc)
199331d98677SRui Paulo {
199431d98677SRui Paulo 	/* Turn RF off. */
199531d98677SRui Paulo 	rsu_write_1(sc, R92S_RF_CTRL, 0x00);
1996400b4e53SHans Petter Selasky 	usb_pause_mtx(&sc->sc_mtx, hz / 200);
199731d98677SRui Paulo 
199831d98677SRui Paulo 	/* Turn MAC off. */
199931d98677SRui Paulo 	/* Switch control path. */
200031d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_CLKR + 1, 0x38);
200131d98677SRui Paulo 	/* Reset MACTOP. */
200231d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, 0x70);
200331d98677SRui Paulo 	rsu_write_1(sc, R92S_PMC_FSM, 0x06);
200431d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_ISO_CTRL + 0, 0xf9);
200531d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1, 0xe8);
200631d98677SRui Paulo 
200731d98677SRui Paulo 	/* Disable AFE PLL. */
200831d98677SRui Paulo 	rsu_write_1(sc, R92S_AFE_PLL_CTRL, 0x00);
200931d98677SRui Paulo 	/* Disable A15V. */
201031d98677SRui Paulo 	rsu_write_1(sc, R92S_LDOA15_CTRL, 0x54);
201131d98677SRui Paulo 	/* Disable eFuse 1.2V. */
201231d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, 0x50);
201331d98677SRui Paulo 	rsu_write_1(sc, R92S_LDOV12D_CTRL, 0x24);
201431d98677SRui Paulo 	/* Enable AFE macro block's bandgap and Mbias. */
201531d98677SRui Paulo 	rsu_write_1(sc, R92S_AFE_MISC, 0x30);
201631d98677SRui Paulo 	/* Disable 1.6V LDO. */
201731d98677SRui Paulo 	rsu_write_1(sc, R92S_SPS0_CTRL + 0, 0x56);
201831d98677SRui Paulo 	rsu_write_1(sc, R92S_SPS0_CTRL + 1, 0x43);
201931d98677SRui Paulo }
202031d98677SRui Paulo 
202131d98677SRui Paulo static int
20226d9b2f85SRui Paulo rsu_fw_loadsection(struct rsu_softc *sc, const uint8_t *buf, int len)
202331d98677SRui Paulo {
2024910593b5SHans Petter Selasky 	const uint8_t which = rsu_wme_ac_xfer_map[WME_AC_VO];
202531d98677SRui Paulo 	struct rsu_data *data;
202631d98677SRui Paulo 	struct r92s_tx_desc *txd;
202731d98677SRui Paulo 	int mlen;
202831d98677SRui Paulo 
202931d98677SRui Paulo 	while (len > 0) {
203031d98677SRui Paulo 		data = rsu_getbuf(sc);
203131d98677SRui Paulo 		if (data == NULL)
203231d98677SRui Paulo 			return (ENOMEM);
203331d98677SRui Paulo 		txd = (struct r92s_tx_desc *)data->buf;
203431d98677SRui Paulo 		memset(txd, 0, sizeof(*txd));
203531d98677SRui Paulo 		if (len <= RSU_TXBUFSZ - sizeof(*txd)) {
203631d98677SRui Paulo 			/* Last chunk. */
203731d98677SRui Paulo 			txd->txdw0 |= htole32(R92S_TXDW0_LINIP);
203831d98677SRui Paulo 			mlen = len;
203931d98677SRui Paulo 		} else
204031d98677SRui Paulo 			mlen = RSU_TXBUFSZ - sizeof(*txd);
204131d98677SRui Paulo 		txd->txdw0 |= htole32(SM(R92S_TXDW0_PKTLEN, mlen));
204231d98677SRui Paulo 		memcpy(&txd[1], buf, mlen);
204331d98677SRui Paulo 		data->buflen = sizeof(*txd) + mlen;
204431d98677SRui Paulo 		DPRINTF("starting transfer %p\n", data);
2045400b4e53SHans Petter Selasky 		STAILQ_INSERT_TAIL(&sc->sc_tx_pending[which], data, next);
204631d98677SRui Paulo 		buf += mlen;
204731d98677SRui Paulo 		len -= mlen;
204831d98677SRui Paulo 	}
2049910593b5SHans Petter Selasky 	usbd_transfer_start(sc->sc_xfer[which]);
205031d98677SRui Paulo 	return (0);
205131d98677SRui Paulo }
205231d98677SRui Paulo 
205331d98677SRui Paulo static int
205431d98677SRui Paulo rsu_load_firmware(struct rsu_softc *sc)
205531d98677SRui Paulo {
20566d9b2f85SRui Paulo 	const struct r92s_fw_hdr *hdr;
205731d98677SRui Paulo 	struct r92s_fw_priv *dmem;
20586d9b2f85SRui Paulo 	const uint8_t *imem, *emem;
205931d98677SRui Paulo 	int imemsz, ememsz;
206031d98677SRui Paulo 	const struct firmware *fw;
206131d98677SRui Paulo 	size_t size;
206231d98677SRui Paulo 	uint32_t reg;
206331d98677SRui Paulo 	int ntries, error;
206431d98677SRui Paulo 
2065910593b5SHans Petter Selasky 	if (rsu_read_1(sc, R92S_TCR) & R92S_TCR_FWRDY) {
2066910593b5SHans Petter Selasky 		DPRINTF("Firmware already loaded\n");
2067910593b5SHans Petter Selasky 		return (0);
2068910593b5SHans Petter Selasky 	}
2069910593b5SHans Petter Selasky 
207031d98677SRui Paulo 	RSU_UNLOCK(sc);
207131d98677SRui Paulo 	/* Read firmware image from the filesystem. */
207231d98677SRui Paulo 	if ((fw = firmware_get("rsu-rtl8712fw")) == NULL) {
207331d98677SRui Paulo 		device_printf(sc->sc_dev,
207431d98677SRui Paulo 		    "%s: failed load firmware of file rsu-rtl8712fw\n",
207531d98677SRui Paulo 		    __func__);
207631d98677SRui Paulo 		RSU_LOCK(sc);
207731d98677SRui Paulo 		return (ENXIO);
207831d98677SRui Paulo 	}
207931d98677SRui Paulo 	RSU_LOCK(sc);
208031d98677SRui Paulo 	size = fw->datasize;
208131d98677SRui Paulo 	if (size < sizeof(*hdr)) {
208231d98677SRui Paulo 		device_printf(sc->sc_dev, "firmware too short\n");
208331d98677SRui Paulo 		error = EINVAL;
208431d98677SRui Paulo 		goto fail;
208531d98677SRui Paulo 	}
20866d9b2f85SRui Paulo 	hdr = (const struct r92s_fw_hdr *)fw->data;
208731d98677SRui Paulo 	if (hdr->signature != htole16(0x8712) &&
208831d98677SRui Paulo 	    hdr->signature != htole16(0x8192)) {
208931d98677SRui Paulo 		device_printf(sc->sc_dev,
209031d98677SRui Paulo 		    "invalid firmware signature 0x%x\n",
209131d98677SRui Paulo 		    le16toh(hdr->signature));
209231d98677SRui Paulo 		error = EINVAL;
209331d98677SRui Paulo 		goto fail;
209431d98677SRui Paulo 	}
209531d98677SRui Paulo 	DPRINTF("FW V%d %02x-%02x %02x:%02x\n", le16toh(hdr->version),
209631d98677SRui Paulo 	    hdr->month, hdr->day, hdr->hour, hdr->minute);
209731d98677SRui Paulo 
209831d98677SRui Paulo 	/* Make sure that driver and firmware are in sync. */
209931d98677SRui Paulo 	if (hdr->privsz != htole32(sizeof(*dmem))) {
210031d98677SRui Paulo 		device_printf(sc->sc_dev, "unsupported firmware image\n");
210131d98677SRui Paulo 		error = EINVAL;
210231d98677SRui Paulo 		goto fail;
210331d98677SRui Paulo 	}
210431d98677SRui Paulo 	/* Get FW sections sizes. */
210531d98677SRui Paulo 	imemsz = le32toh(hdr->imemsz);
210631d98677SRui Paulo 	ememsz = le32toh(hdr->sramsz);
210731d98677SRui Paulo 	/* Check that all FW sections fit in image. */
210831d98677SRui Paulo 	if (size < sizeof(*hdr) + imemsz + ememsz) {
210931d98677SRui Paulo 		device_printf(sc->sc_dev, "firmware too short\n");
211031d98677SRui Paulo 		error = EINVAL;
211131d98677SRui Paulo 		goto fail;
211231d98677SRui Paulo 	}
21136d9b2f85SRui Paulo 	imem = (const uint8_t *)&hdr[1];
211431d98677SRui Paulo 	emem = imem + imemsz;
211531d98677SRui Paulo 
211631d98677SRui Paulo 	/* Load IMEM section. */
211731d98677SRui Paulo 	error = rsu_fw_loadsection(sc, imem, imemsz);
211831d98677SRui Paulo 	if (error != 0) {
211931d98677SRui Paulo 		device_printf(sc->sc_dev,
212031d98677SRui Paulo 		    "could not load firmware section %s\n", "IMEM");
212131d98677SRui Paulo 		goto fail;
212231d98677SRui Paulo 	}
212331d98677SRui Paulo 	/* Wait for load to complete. */
2124885476cbSHans Petter Selasky 	for (ntries = 0; ntries != 50; ntries++) {
2125400b4e53SHans Petter Selasky 		usb_pause_mtx(&sc->sc_mtx, hz / 100);
2126910593b5SHans Petter Selasky 		reg = rsu_read_1(sc, R92S_TCR);
212731d98677SRui Paulo 		if (reg & R92S_TCR_IMEM_CODE_DONE)
212831d98677SRui Paulo 			break;
212931d98677SRui Paulo 	}
2130885476cbSHans Petter Selasky 	if (ntries == 50) {
2131885476cbSHans Petter Selasky 		device_printf(sc->sc_dev, "timeout waiting for IMEM transfer\n");
213231d98677SRui Paulo 		error = ETIMEDOUT;
213331d98677SRui Paulo 		goto fail;
213431d98677SRui Paulo 	}
213531d98677SRui Paulo 	/* Load EMEM section. */
213631d98677SRui Paulo 	error = rsu_fw_loadsection(sc, emem, ememsz);
213731d98677SRui Paulo 	if (error != 0) {
213831d98677SRui Paulo 		device_printf(sc->sc_dev,
213931d98677SRui Paulo 		    "could not load firmware section %s\n", "EMEM");
214031d98677SRui Paulo 		goto fail;
214131d98677SRui Paulo 	}
214231d98677SRui Paulo 	/* Wait for load to complete. */
21431593e875SHans Petter Selasky 	for (ntries = 0; ntries != 50; ntries++) {
2144400b4e53SHans Petter Selasky 		usb_pause_mtx(&sc->sc_mtx, hz / 100);
214531d98677SRui Paulo 		reg = rsu_read_2(sc, R92S_TCR);
214631d98677SRui Paulo 		if (reg & R92S_TCR_EMEM_CODE_DONE)
214731d98677SRui Paulo 			break;
214831d98677SRui Paulo 	}
21491593e875SHans Petter Selasky 	if (ntries == 50) {
2150885476cbSHans Petter Selasky 		device_printf(sc->sc_dev, "timeout waiting for EMEM transfer\n");
215131d98677SRui Paulo 		error = ETIMEDOUT;
215231d98677SRui Paulo 		goto fail;
215331d98677SRui Paulo 	}
215431d98677SRui Paulo 	/* Enable CPU. */
215531d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_CLKR,
215631d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_CLKR) | R92S_SYS_CPU_CLKSEL);
215731d98677SRui Paulo 	if (!(rsu_read_1(sc, R92S_SYS_CLKR) & R92S_SYS_CPU_CLKSEL)) {
215831d98677SRui Paulo 		device_printf(sc->sc_dev, "could not enable system clock\n");
215931d98677SRui Paulo 		error = EIO;
216031d98677SRui Paulo 		goto fail;
216131d98677SRui Paulo 	}
216231d98677SRui Paulo 	rsu_write_2(sc, R92S_SYS_FUNC_EN,
216331d98677SRui Paulo 	    rsu_read_2(sc, R92S_SYS_FUNC_EN) | R92S_FEN_CPUEN);
216431d98677SRui Paulo 	if (!(rsu_read_2(sc, R92S_SYS_FUNC_EN) & R92S_FEN_CPUEN)) {
216531d98677SRui Paulo 		device_printf(sc->sc_dev,
216631d98677SRui Paulo 		    "could not enable microcontroller\n");
216731d98677SRui Paulo 		error = EIO;
216831d98677SRui Paulo 		goto fail;
216931d98677SRui Paulo 	}
217031d98677SRui Paulo 	/* Wait for CPU to initialize. */
217131d98677SRui Paulo 	for (ntries = 0; ntries < 100; ntries++) {
2172910593b5SHans Petter Selasky 		if (rsu_read_1(sc, R92S_TCR) & R92S_TCR_IMEM_RDY)
217331d98677SRui Paulo 			break;
2174b41381aeSHans Petter Selasky 		rsu_ms_delay(sc);
217531d98677SRui Paulo 	}
217631d98677SRui Paulo 	if (ntries == 100) {
217731d98677SRui Paulo 		device_printf(sc->sc_dev,
217831d98677SRui Paulo 		    "timeout waiting for microcontroller\n");
217931d98677SRui Paulo 		error = ETIMEDOUT;
218031d98677SRui Paulo 		goto fail;
218131d98677SRui Paulo 	}
218231d98677SRui Paulo 
218331d98677SRui Paulo 	/* Update DMEM section before loading. */
21846d9b2f85SRui Paulo 	dmem = __DECONST(struct r92s_fw_priv *, &hdr->priv);
218531d98677SRui Paulo 	memset(dmem, 0, sizeof(*dmem));
218631d98677SRui Paulo 	dmem->hci_sel = R92S_HCI_SEL_USB | R92S_HCI_SEL_8172;
2187400b4e53SHans Petter Selasky 	dmem->nendpoints = 0;
218831d98677SRui Paulo 	dmem->rf_config = 0x12;	/* 1T2R */
218931d98677SRui Paulo 	dmem->vcs_type = R92S_VCS_TYPE_AUTO;
219031d98677SRui Paulo 	dmem->vcs_mode = R92S_VCS_MODE_RTS_CTS;
219131d98677SRui Paulo #ifdef notyet
219231d98677SRui Paulo 	dmem->bw40_en = (ic->ic_htcaps & IEEE80211_HTCAP_CBW20_40) != 0;
219331d98677SRui Paulo #endif
219431d98677SRui Paulo 	dmem->turbo_mode = 1;
219531d98677SRui Paulo 	/* Load DMEM section. */
219631d98677SRui Paulo 	error = rsu_fw_loadsection(sc, (uint8_t *)dmem, sizeof(*dmem));
219731d98677SRui Paulo 	if (error != 0) {
219831d98677SRui Paulo 		device_printf(sc->sc_dev,
219931d98677SRui Paulo 		    "could not load firmware section %s\n", "DMEM");
220031d98677SRui Paulo 		goto fail;
220131d98677SRui Paulo 	}
220231d98677SRui Paulo 	/* Wait for load to complete. */
220331d98677SRui Paulo 	for (ntries = 0; ntries < 100; ntries++) {
2204910593b5SHans Petter Selasky 		if (rsu_read_1(sc, R92S_TCR) & R92S_TCR_DMEM_CODE_DONE)
220531d98677SRui Paulo 			break;
2206b41381aeSHans Petter Selasky 		rsu_ms_delay(sc);
220731d98677SRui Paulo 	}
220831d98677SRui Paulo 	if (ntries == 100) {
220931d98677SRui Paulo 		device_printf(sc->sc_dev, "timeout waiting for %s transfer\n",
221031d98677SRui Paulo 		    "DMEM");
221131d98677SRui Paulo 		error = ETIMEDOUT;
221231d98677SRui Paulo 		goto fail;
221331d98677SRui Paulo 	}
221431d98677SRui Paulo 	/* Wait for firmware readiness. */
221531d98677SRui Paulo 	for (ntries = 0; ntries < 60; ntries++) {
2216910593b5SHans Petter Selasky 		if (!(rsu_read_1(sc, R92S_TCR) & R92S_TCR_FWRDY))
221731d98677SRui Paulo 			break;
2218b41381aeSHans Petter Selasky 		rsu_ms_delay(sc);
221931d98677SRui Paulo 	}
222031d98677SRui Paulo 	if (ntries == 60) {
222131d98677SRui Paulo 		device_printf(sc->sc_dev,
222231d98677SRui Paulo 		    "timeout waiting for firmware readiness\n");
222331d98677SRui Paulo 		error = ETIMEDOUT;
222431d98677SRui Paulo 		goto fail;
222531d98677SRui Paulo 	}
222631d98677SRui Paulo  fail:
222731d98677SRui Paulo 	firmware_put(fw, FIRMWARE_UNLOAD);
222831d98677SRui Paulo 	return (error);
222931d98677SRui Paulo }
223031d98677SRui Paulo 
223131d98677SRui Paulo 
223231d98677SRui Paulo static int
223331d98677SRui Paulo rsu_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
223431d98677SRui Paulo     const struct ieee80211_bpf_params *params)
223531d98677SRui Paulo {
223631d98677SRui Paulo 	struct ieee80211com *ic = ni->ni_ic;
2237d3fdd08cSAdrian Chadd 	struct rsu_softc *sc = ic->ic_softc;
223831d98677SRui Paulo 	struct rsu_data *bf;
223931d98677SRui Paulo 
224031d98677SRui Paulo 	/* prevent management frames from being sent if we're not ready */
2241*7a79cebfSGleb Smirnoff 	if (!sc->sc_running) {
224231d98677SRui Paulo 		m_freem(m);
224331d98677SRui Paulo 		ieee80211_free_node(ni);
224431d98677SRui Paulo 		return (ENETDOWN);
224531d98677SRui Paulo 	}
224631d98677SRui Paulo 	RSU_LOCK(sc);
224731d98677SRui Paulo 	bf = rsu_getbuf(sc);
224831d98677SRui Paulo 	if (bf == NULL) {
224931d98677SRui Paulo 		ieee80211_free_node(ni);
225031d98677SRui Paulo 		m_freem(m);
225131d98677SRui Paulo 		RSU_UNLOCK(sc);
225231d98677SRui Paulo 		return (ENOBUFS);
225331d98677SRui Paulo 	}
2254400b4e53SHans Petter Selasky 	if (rsu_tx_start(sc, ni, m, bf) != 0) {
225531d98677SRui Paulo 		ieee80211_free_node(ni);
225631d98677SRui Paulo 		STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, bf, next);
225731d98677SRui Paulo 		RSU_UNLOCK(sc);
225831d98677SRui Paulo 		return (EIO);
225931d98677SRui Paulo 	}
226031d98677SRui Paulo 	RSU_UNLOCK(sc);
226131d98677SRui Paulo 
226231d98677SRui Paulo 	return (0);
226331d98677SRui Paulo }
226431d98677SRui Paulo 
226531d98677SRui Paulo static void
2266*7a79cebfSGleb Smirnoff rsu_init(struct rsu_softc *sc)
226731d98677SRui Paulo {
2268*7a79cebfSGleb Smirnoff 	struct ieee80211com *ic = &sc->sc_ic;
2269*7a79cebfSGleb Smirnoff 	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
2270*7a79cebfSGleb Smirnoff 	uint8_t macaddr[IEEE80211_ADDR_LEN];
227131d98677SRui Paulo 	struct r92s_set_pwr_mode cmd;
227231d98677SRui Paulo 	int error;
2273885476cbSHans Petter Selasky 	int i;
227431d98677SRui Paulo 
2275*7a79cebfSGleb Smirnoff 	RSU_ASSERT_LOCKED(sc);
2276*7a79cebfSGleb Smirnoff 
227731d98677SRui Paulo 	/* Init host async commands ring. */
227831d98677SRui Paulo 	sc->cmdq.cur = sc->cmdq.next = sc->cmdq.queued = 0;
227931d98677SRui Paulo 
228031d98677SRui Paulo 	/* Power on adapter. */
228131d98677SRui Paulo 	if (sc->cut == 1)
228231d98677SRui Paulo 		rsu_power_on_acut(sc);
228331d98677SRui Paulo 	else
228431d98677SRui Paulo 		rsu_power_on_bcut(sc);
2285910593b5SHans Petter Selasky 
228631d98677SRui Paulo 	/* Load firmware. */
228731d98677SRui Paulo 	error = rsu_load_firmware(sc);
228831d98677SRui Paulo 	if (error != 0)
228931d98677SRui Paulo 		goto fail;
229031d98677SRui Paulo 
229131d98677SRui Paulo 	/* Enable Rx TCP checksum offload. */
229231d98677SRui Paulo 	rsu_write_4(sc, R92S_RCR,
229331d98677SRui Paulo 	    rsu_read_4(sc, R92S_RCR) | 0x04000000);
229431d98677SRui Paulo 	/* Append PHY status. */
229531d98677SRui Paulo 	rsu_write_4(sc, R92S_RCR,
229631d98677SRui Paulo 	    rsu_read_4(sc, R92S_RCR) | 0x02000000);
229731d98677SRui Paulo 
229831d98677SRui Paulo 	rsu_write_4(sc, R92S_CR,
229931d98677SRui Paulo 	    rsu_read_4(sc, R92S_CR) & ~0xff000000);
230031d98677SRui Paulo 
230131d98677SRui Paulo 	/* Use 128 bytes pages. */
230231d98677SRui Paulo 	rsu_write_1(sc, 0x00b5,
230331d98677SRui Paulo 	    rsu_read_1(sc, 0x00b5) | 0x01);
230431d98677SRui Paulo 	/* Enable USB Rx aggregation. */
230531d98677SRui Paulo 	rsu_write_1(sc, 0x00bd,
230631d98677SRui Paulo 	    rsu_read_1(sc, 0x00bd) | 0x80);
230731d98677SRui Paulo 	/* Set USB Rx aggregation threshold. */
230831d98677SRui Paulo 	rsu_write_1(sc, 0x00d9, 0x01);
230931d98677SRui Paulo 	/* Set USB Rx aggregation timeout (1.7ms/4). */
231031d98677SRui Paulo 	rsu_write_1(sc, 0xfe5b, 0x04);
231131d98677SRui Paulo 	/* Fix USB Rx FIFO issue. */
231231d98677SRui Paulo 	rsu_write_1(sc, 0xfe5c,
231331d98677SRui Paulo 	    rsu_read_1(sc, 0xfe5c) | 0x80);
231431d98677SRui Paulo 
231531d98677SRui Paulo 	/* Set MAC address. */
2316*7a79cebfSGleb Smirnoff 	IEEE80211_ADDR_COPY(macaddr, vap ? vap->iv_myaddr : ic->ic_macaddr);
2317*7a79cebfSGleb Smirnoff 	rsu_write_region_1(sc, R92S_MACID, macaddr, IEEE80211_ADDR_LEN);
231831d98677SRui Paulo 
2319b41381aeSHans Petter Selasky 	/* It really takes 1.5 seconds for the firmware to boot: */
2320b41381aeSHans Petter Selasky 	usb_pause_mtx(&sc->sc_mtx, (3 * hz) / 2);
232131d98677SRui Paulo 
2322*7a79cebfSGleb Smirnoff 	DPRINTF("setting MAC address to %s\n", ether_sprintf(macaddr));
2323*7a79cebfSGleb Smirnoff 	error = rsu_fw_cmd(sc, R92S_CMD_SET_MAC_ADDRESS, macaddr,
232431d98677SRui Paulo 	    IEEE80211_ADDR_LEN);
232531d98677SRui Paulo 	if (error != 0) {
232631d98677SRui Paulo 		device_printf(sc->sc_dev, "could not set MAC address\n");
232731d98677SRui Paulo 		goto fail;
232831d98677SRui Paulo 	}
232931d98677SRui Paulo 
233031d98677SRui Paulo 	rsu_write_1(sc, R92S_USB_HRPWM,
233131d98677SRui Paulo 	    R92S_USB_HRPWM_PS_ST_ACTIVE | R92S_USB_HRPWM_PS_ALL_ON);
233231d98677SRui Paulo 
233331d98677SRui Paulo 	memset(&cmd, 0, sizeof(cmd));
233431d98677SRui Paulo 	cmd.mode = R92S_PS_MODE_ACTIVE;
233531d98677SRui Paulo 	DPRINTF("setting ps mode to %d\n", cmd.mode);
233631d98677SRui Paulo 	error = rsu_fw_cmd(sc, R92S_CMD_SET_PWR_MODE, &cmd, sizeof(cmd));
233731d98677SRui Paulo 	if (error != 0) {
233831d98677SRui Paulo 		device_printf(sc->sc_dev, "could not set PS mode\n");
233931d98677SRui Paulo 		goto fail;
234031d98677SRui Paulo 	}
234131d98677SRui Paulo 
234231d98677SRui Paulo #if 0
234331d98677SRui Paulo 	if (ic->ic_htcaps & IEEE80211_HTCAP_CBW20_40) {
234431d98677SRui Paulo 		/* Enable 40MHz mode. */
234531d98677SRui Paulo 		error = rsu_fw_iocmd(sc,
234631d98677SRui Paulo 		    SM(R92S_IOCMD_CLASS, 0xf4) |
234731d98677SRui Paulo 		    SM(R92S_IOCMD_INDEX, 0x00) |
234831d98677SRui Paulo 		    SM(R92S_IOCMD_VALUE, 0x0007));
234931d98677SRui Paulo 		if (error != 0) {
235031d98677SRui Paulo 			device_printf(sc->sc_dev,
235131d98677SRui Paulo 			    "could not enable 40MHz mode\n");
235231d98677SRui Paulo 			goto fail;
235331d98677SRui Paulo 		}
235431d98677SRui Paulo 	}
235531d98677SRui Paulo 
235631d98677SRui Paulo 	/* Set default channel. */
235731d98677SRui Paulo 	ic->ic_bss->ni_chan = ic->ic_ibss_chan;
235831d98677SRui Paulo #endif
2359*7a79cebfSGleb Smirnoff 	sc->sc_scan_pass = 0;
236031d98677SRui Paulo 	usbd_transfer_start(sc->sc_xfer[RSU_BULK_RX]);
236131d98677SRui Paulo 
236231d98677SRui Paulo 	/* We're ready to go. */
2363*7a79cebfSGleb Smirnoff 	sc->sc_running = 1;
236431d98677SRui Paulo 	return;
236531d98677SRui Paulo fail:
2366885476cbSHans Petter Selasky 	/* Need to stop all failed transfers, if any */
2367885476cbSHans Petter Selasky 	for (i = 0; i != RSU_N_TRANSFER; i++)
2368885476cbSHans Petter Selasky 		usbd_transfer_stop(sc->sc_xfer[i]);
236931d98677SRui Paulo }
237031d98677SRui Paulo 
237131d98677SRui Paulo static void
2372*7a79cebfSGleb Smirnoff rsu_stop(struct rsu_softc *sc)
237331d98677SRui Paulo {
237431d98677SRui Paulo 	int i;
237531d98677SRui Paulo 
2376*7a79cebfSGleb Smirnoff 	sc->sc_running = 0;
237731d98677SRui Paulo 	sc->sc_calibrating = 0;
237831d98677SRui Paulo 	taskqueue_cancel_timeout(taskqueue_thread, &sc->calib_task, NULL);
237931d98677SRui Paulo 
238031d98677SRui Paulo 	/* Power off adapter. */
238131d98677SRui Paulo 	rsu_power_off(sc);
238231d98677SRui Paulo 
238331d98677SRui Paulo 	for (i = 0; i < RSU_N_TRANSFER; i++)
238431d98677SRui Paulo 		usbd_transfer_stop(sc->sc_xfer[i]);
238531d98677SRui Paulo }
238631d98677SRui Paulo 
2387b41381aeSHans Petter Selasky static void
2388b41381aeSHans Petter Selasky rsu_ms_delay(struct rsu_softc *sc)
2389b41381aeSHans Petter Selasky {
2390b41381aeSHans Petter Selasky 	usb_pause_mtx(&sc->sc_mtx, hz / 1000);
2391b41381aeSHans Petter Selasky }
2392