xref: /freebsd/sys/dev/usb/wlan/if_rsu.c (revision d3fdd08cf34a8fa321f048f11f5e4142ed19233b)
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 *);
190ba2c1fbcSAdrian Chadd static void	rsu_init(void *);
191ba2c1fbcSAdrian Chadd static void	rsu_init_locked(struct rsu_softc *);
19231d98677SRui Paulo static int	rsu_tx_start(struct rsu_softc *, struct ieee80211_node *,
193400b4e53SHans Petter Selasky 		    struct mbuf *, struct rsu_data *);
194ba2c1fbcSAdrian Chadd static void	rsu_start(struct ifnet *);
195ba2c1fbcSAdrian Chadd static void	rsu_start_locked(struct ifnet *);
196ba2c1fbcSAdrian Chadd static int	rsu_ioctl(struct ifnet *, u_long, caddr_t);
197ba2c1fbcSAdrian Chadd static void	rsu_stop(struct ifnet *, int);
198ba2c1fbcSAdrian Chadd static void	rsu_stop_locked(struct ifnet *, int);
199b41381aeSHans Petter Selasky static void	rsu_ms_delay(struct rsu_softc *);
20031d98677SRui Paulo 
20131d98677SRui Paulo static device_method_t rsu_methods[] = {
20231d98677SRui Paulo 	DEVMETHOD(device_probe,		rsu_match),
20331d98677SRui Paulo 	DEVMETHOD(device_attach,	rsu_attach),
20431d98677SRui Paulo 	DEVMETHOD(device_detach,	rsu_detach),
20531d98677SRui Paulo 
20631d98677SRui Paulo 	DEVMETHOD_END
20731d98677SRui Paulo };
20831d98677SRui Paulo 
20931d98677SRui Paulo static driver_t rsu_driver = {
21031d98677SRui Paulo 	.name = "rsu",
21131d98677SRui Paulo 	.methods = rsu_methods,
21231d98677SRui Paulo 	.size = sizeof(struct rsu_softc)
21331d98677SRui Paulo };
21431d98677SRui Paulo 
21531d98677SRui Paulo static devclass_t rsu_devclass;
21631d98677SRui Paulo 
21731d98677SRui Paulo DRIVER_MODULE(rsu, uhub, rsu_driver, rsu_devclass, NULL, 0);
21831d98677SRui Paulo MODULE_DEPEND(rsu, wlan, 1, 1, 1);
21931d98677SRui Paulo MODULE_DEPEND(rsu, usb, 1, 1, 1);
22031d98677SRui Paulo MODULE_DEPEND(rsu, firmware, 1, 1, 1);
22131d98677SRui Paulo MODULE_VERSION(rsu, 1);
22231d98677SRui Paulo 
223910593b5SHans Petter Selasky static uint8_t rsu_wme_ac_xfer_map[4] = {
224910593b5SHans Petter Selasky 	[WME_AC_BE] = RSU_BULK_TX_BE_BK,
225910593b5SHans Petter Selasky 	[WME_AC_BK] = RSU_BULK_TX_BE_BK,
226910593b5SHans Petter Selasky 	[WME_AC_VI] = RSU_BULK_TX_VI_VO,
227910593b5SHans Petter Selasky 	[WME_AC_VO] = RSU_BULK_TX_VI_VO,
228910593b5SHans Petter Selasky };
229910593b5SHans Petter Selasky 
23031d98677SRui Paulo static const struct usb_config rsu_config[RSU_N_TRANSFER] = {
23131d98677SRui Paulo 	[RSU_BULK_RX] = {
23231d98677SRui Paulo 		.type = UE_BULK,
23331d98677SRui Paulo 		.endpoint = UE_ADDR_ANY,
23431d98677SRui Paulo 		.direction = UE_DIR_IN,
23531d98677SRui Paulo 		.bufsize = RSU_RXBUFSZ,
23631d98677SRui Paulo 		.flags = {
23731d98677SRui Paulo 			.pipe_bof = 1,
23831d98677SRui Paulo 			.short_xfer_ok = 1
23931d98677SRui Paulo 		},
24031d98677SRui Paulo 		.callback = rsu_bulk_rx_callback
24131d98677SRui Paulo 	},
242910593b5SHans Petter Selasky 	[RSU_BULK_TX_BE_BK] = {
24331d98677SRui Paulo 		.type = UE_BULK,
24431d98677SRui Paulo 		.endpoint = 0x06,
24531d98677SRui Paulo 		.direction = UE_DIR_OUT,
24631d98677SRui Paulo 		.bufsize = RSU_TXBUFSZ,
24731d98677SRui Paulo 		.flags = {
24831d98677SRui Paulo 			.ext_buffer = 1,
24931d98677SRui Paulo 			.pipe_bof = 1,
25031d98677SRui Paulo 			.force_short_xfer = 1
25131d98677SRui Paulo 		},
252910593b5SHans Petter Selasky 		.callback = rsu_bulk_tx_callback_be_bk,
25331d98677SRui Paulo 		.timeout = RSU_TX_TIMEOUT
25431d98677SRui Paulo 	},
255910593b5SHans Petter Selasky 	[RSU_BULK_TX_VI_VO] = {
25631d98677SRui Paulo 		.type = UE_BULK,
25731d98677SRui Paulo 		.endpoint = 0x04,
25831d98677SRui Paulo 		.direction = UE_DIR_OUT,
25931d98677SRui Paulo 		.bufsize = RSU_TXBUFSZ,
26031d98677SRui Paulo 		.flags = {
26131d98677SRui Paulo 			.ext_buffer = 1,
26231d98677SRui Paulo 			.pipe_bof = 1,
26331d98677SRui Paulo 			.force_short_xfer = 1
26431d98677SRui Paulo 		},
265910593b5SHans Petter Selasky 		.callback = rsu_bulk_tx_callback_vi_vo,
26631d98677SRui Paulo 		.timeout = RSU_TX_TIMEOUT
26731d98677SRui Paulo 	},
26831d98677SRui Paulo };
26931d98677SRui Paulo 
27031d98677SRui Paulo static int
27131d98677SRui Paulo rsu_match(device_t self)
27231d98677SRui Paulo {
27331d98677SRui Paulo 	struct usb_attach_arg *uaa = device_get_ivars(self);
27431d98677SRui Paulo 
27531d98677SRui Paulo 	if (uaa->usb_mode != USB_MODE_HOST ||
27631d98677SRui Paulo 	    uaa->info.bIfaceIndex != 0 ||
27731d98677SRui Paulo 	    uaa->info.bConfigIndex != 0)
27831d98677SRui Paulo 		return (ENXIO);
27931d98677SRui Paulo 
28031d98677SRui Paulo 	return (usbd_lookup_id_by_uaa(rsu_devs, sizeof(rsu_devs), uaa));
28131d98677SRui Paulo }
28231d98677SRui Paulo 
28331d98677SRui Paulo static int
28431d98677SRui Paulo rsu_attach(device_t self)
28531d98677SRui Paulo {
28631d98677SRui Paulo 	struct usb_attach_arg *uaa = device_get_ivars(self);
28731d98677SRui Paulo 	struct rsu_softc *sc = device_get_softc(self);
288ba2c1fbcSAdrian Chadd 	struct ifnet *ifp;
289ba2c1fbcSAdrian Chadd 	struct ieee80211com *ic;
29031d98677SRui Paulo 	int error;
29131d98677SRui Paulo 	uint8_t iface_index, bands;
29231d98677SRui Paulo 
29331d98677SRui Paulo 	device_set_usb_desc(self);
29431d98677SRui Paulo 	sc->sc_udev = uaa->device;
29531d98677SRui Paulo 	sc->sc_dev = self;
29631d98677SRui Paulo 
29731d98677SRui Paulo 	mtx_init(&sc->sc_mtx, device_get_nameunit(self), MTX_NETWORK_LOCK,
29831d98677SRui Paulo 	    MTX_DEF);
29931d98677SRui Paulo 	TIMEOUT_TASK_INIT(taskqueue_thread, &sc->calib_task, 0,
30031d98677SRui Paulo 	    rsu_calib_task, sc);
30131d98677SRui Paulo 
302885476cbSHans Petter Selasky 	/* Allocate Tx/Rx buffers. */
303885476cbSHans Petter Selasky 	error = rsu_alloc_rx_list(sc);
304885476cbSHans Petter Selasky 	if (error != 0) {
305885476cbSHans Petter Selasky 		device_printf(sc->sc_dev, "could not allocate Rx buffers\n");
306885476cbSHans Petter Selasky 		goto fail_usb;
307885476cbSHans Petter Selasky 	}
308885476cbSHans Petter Selasky 
309885476cbSHans Petter Selasky 	error = rsu_alloc_tx_list(sc);
310885476cbSHans Petter Selasky 	if (error != 0) {
311885476cbSHans Petter Selasky 		device_printf(sc->sc_dev, "could not allocate Tx buffers\n");
312885476cbSHans Petter Selasky 		rsu_free_rx_list(sc);
313885476cbSHans Petter Selasky 		goto fail_usb;
314885476cbSHans Petter Selasky 	}
315885476cbSHans Petter Selasky 
31631d98677SRui Paulo 	iface_index = 0;
31731d98677SRui Paulo 	error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer,
31831d98677SRui Paulo 	    rsu_config, RSU_N_TRANSFER, sc, &sc->sc_mtx);
31931d98677SRui Paulo 	if (error) {
32031d98677SRui Paulo 		device_printf(sc->sc_dev,
32131d98677SRui Paulo 		    "could not allocate USB transfers, err=%s\n",
32231d98677SRui Paulo 		    usbd_errstr(error));
32353dfd5c1SRui Paulo 		goto fail_usb;
32431d98677SRui Paulo 	}
32531d98677SRui Paulo 	RSU_LOCK(sc);
32631d98677SRui Paulo 	/* Read chip revision. */
32731d98677SRui Paulo 	sc->cut = MS(rsu_read_4(sc, R92S_PMC_FSM), R92S_PMC_FSM_CUT);
32831d98677SRui Paulo 	if (sc->cut != 3)
32931d98677SRui Paulo 		sc->cut = (sc->cut >> 1) + 1;
33031d98677SRui Paulo 	error = rsu_read_rom(sc);
331abfa11d6SHans Petter Selasky 	RSU_UNLOCK(sc);
33231d98677SRui Paulo 	if (error != 0) {
33331d98677SRui Paulo 		device_printf(self, "could not read ROM\n");
33453dfd5c1SRui Paulo 		goto fail_rom;
33531d98677SRui Paulo 	}
336ba2c1fbcSAdrian Chadd 	IEEE80211_ADDR_COPY(sc->sc_bssid, &sc->rom[0x12]);
33731d98677SRui Paulo 	device_printf(self, "MAC/BB RTL8712 cut %d\n", sc->cut);
338ba2c1fbcSAdrian Chadd 	ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211);
339ba2c1fbcSAdrian Chadd 	if (ifp == NULL) {
340ba2c1fbcSAdrian Chadd 		device_printf(self, "cannot allocate interface\n");
341ba2c1fbcSAdrian Chadd 		goto fail_ifalloc;
342ba2c1fbcSAdrian Chadd 	}
343ba2c1fbcSAdrian Chadd 	ic = ifp->if_l2com;
344ba2c1fbcSAdrian Chadd 	ifp->if_softc = sc;
345ba2c1fbcSAdrian Chadd 	if_initname(ifp, "rsu", device_get_unit(self));
346ba2c1fbcSAdrian Chadd 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
347ba2c1fbcSAdrian Chadd 	ifp->if_init = rsu_init;
348ba2c1fbcSAdrian Chadd 	ifp->if_ioctl = rsu_ioctl;
349ba2c1fbcSAdrian Chadd 	ifp->if_start = rsu_start;
350ba2c1fbcSAdrian Chadd 	IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen);
351ba2c1fbcSAdrian Chadd 	ifp->if_snd.ifq_drv_maxlen = ifqmaxlen;
352ba2c1fbcSAdrian Chadd 	IFQ_SET_READY(&ifp->if_snd);
353ba2c1fbcSAdrian Chadd 	ifp->if_capabilities |= IFCAP_RXCSUM;
354ba2c1fbcSAdrian Chadd 	ifp->if_capenable |= IFCAP_RXCSUM;
355ba2c1fbcSAdrian Chadd 	ifp->if_hwassist = CSUM_TCP;
35631d98677SRui Paulo 
357ba2c1fbcSAdrian Chadd 	ic->ic_ifp = ifp;
35859686fe9SGleb Smirnoff 	ic->ic_softc = sc;
359c8550c02SGleb Smirnoff 	ic->ic_name = device_get_nameunit(self);
36031d98677SRui Paulo 	ic->ic_phytype = IEEE80211_T_OFDM;	/* Not only, but not used. */
36131d98677SRui Paulo 	ic->ic_opmode = IEEE80211_M_STA;	/* Default to BSS mode. */
36231d98677SRui Paulo 
36331d98677SRui Paulo 	/* Set device capabilities. */
36431d98677SRui Paulo 	ic->ic_caps =
36531d98677SRui Paulo 	    IEEE80211_C_STA |		/* station mode */
36631d98677SRui Paulo 	    IEEE80211_C_BGSCAN |	/* Background scan. */
36731d98677SRui Paulo 	    IEEE80211_C_SHPREAMBLE |	/* Short preamble supported. */
36831d98677SRui Paulo 	    IEEE80211_C_SHSLOT |	/* Short slot time supported. */
36931d98677SRui Paulo 	    IEEE80211_C_WPA;		/* WPA/RSN. */
37031d98677SRui Paulo 
37131d98677SRui Paulo #if 0
37231d98677SRui Paulo 	/* Check if HT support is present. */
37331d98677SRui Paulo 	if (usb_lookup(rsu_devs_noht, uaa->vendor, uaa->product) == NULL) {
37431d98677SRui Paulo 		/* Set HT capabilities. */
37531d98677SRui Paulo 		ic->ic_htcaps =
37631d98677SRui Paulo 		    IEEE80211_HTCAP_CBW20_40 |
37731d98677SRui Paulo 		    IEEE80211_HTCAP_DSSSCCK40;
37831d98677SRui Paulo 		/* Set supported HT rates. */
37931d98677SRui Paulo 		for (i = 0; i < 2; i++)
38031d98677SRui Paulo 			ic->ic_sup_mcs[i] = 0xff;
38131d98677SRui Paulo 	}
38231d98677SRui Paulo #endif
38331d98677SRui Paulo 
38431d98677SRui Paulo 	/* Set supported .11b and .11g rates. */
38531d98677SRui Paulo 	bands = 0;
38631d98677SRui Paulo 	setbit(&bands, IEEE80211_MODE_11B);
38731d98677SRui Paulo 	setbit(&bands, IEEE80211_MODE_11G);
38831d98677SRui Paulo 	ieee80211_init_channels(ic, NULL, &bands);
38931d98677SRui Paulo 
390ba2c1fbcSAdrian Chadd 	ieee80211_ifattach(ic, sc->sc_bssid);
39131d98677SRui Paulo 	ic->ic_raw_xmit = rsu_raw_xmit;
39231d98677SRui Paulo 	ic->ic_scan_start = rsu_scan_start;
39331d98677SRui Paulo 	ic->ic_scan_end = rsu_scan_end;
39431d98677SRui Paulo 	ic->ic_set_channel = rsu_set_channel;
39531d98677SRui Paulo 	ic->ic_vap_create = rsu_vap_create;
39631d98677SRui Paulo 	ic->ic_vap_delete = rsu_vap_delete;
39731d98677SRui Paulo 	ic->ic_update_mcast = rsu_update_mcast;
39831d98677SRui Paulo 
39931d98677SRui Paulo 	ieee80211_radiotap_attach(ic, &sc->sc_txtap.wt_ihdr,
40031d98677SRui Paulo 	    sizeof(sc->sc_txtap), RSU_TX_RADIOTAP_PRESENT,
40131d98677SRui Paulo 	    &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap),
40231d98677SRui Paulo 	    RSU_RX_RADIOTAP_PRESENT);
40331d98677SRui Paulo 
40431d98677SRui Paulo 	if (bootverbose)
40531d98677SRui Paulo 		ieee80211_announce(ic);
40631d98677SRui Paulo 
40731d98677SRui Paulo 	return (0);
40831d98677SRui Paulo 
409ba2c1fbcSAdrian Chadd fail_ifalloc:
41053dfd5c1SRui Paulo fail_rom:
41153dfd5c1SRui Paulo 	usbd_transfer_unsetup(sc->sc_xfer, RSU_N_TRANSFER);
41253dfd5c1SRui Paulo fail_usb:
41353dfd5c1SRui Paulo 	mtx_destroy(&sc->sc_mtx);
41431d98677SRui Paulo 	return (ENXIO);
41531d98677SRui Paulo }
41631d98677SRui Paulo 
41731d98677SRui Paulo static int
41831d98677SRui Paulo rsu_detach(device_t self)
41931d98677SRui Paulo {
42031d98677SRui Paulo 	struct rsu_softc *sc = device_get_softc(self);
421ba2c1fbcSAdrian Chadd 	struct ifnet *ifp = sc->sc_ifp;
422ba2c1fbcSAdrian Chadd 	struct ieee80211com *ic = ifp->if_l2com;
42331d98677SRui Paulo 
424ba2c1fbcSAdrian Chadd 	rsu_stop(ifp, 1);
42531d98677SRui Paulo 	usbd_transfer_unsetup(sc->sc_xfer, RSU_N_TRANSFER);
42631d98677SRui Paulo 	ieee80211_ifdetach(ic);
42731d98677SRui Paulo 
42831d98677SRui Paulo 	taskqueue_drain_timeout(taskqueue_thread, &sc->calib_task);
42931d98677SRui Paulo 
43031d98677SRui Paulo 	/* Free Tx/Rx buffers. */
43131d98677SRui Paulo 	rsu_free_tx_list(sc);
43231d98677SRui Paulo 	rsu_free_rx_list(sc);
43331d98677SRui Paulo 
434ba2c1fbcSAdrian Chadd 	if_free(ifp);
43531d98677SRui Paulo 	mtx_destroy(&sc->sc_mtx);
43631d98677SRui Paulo 
43731d98677SRui Paulo 	return (0);
43831d98677SRui Paulo }
43931d98677SRui Paulo 
44031d98677SRui Paulo static usb_error_t
44131d98677SRui Paulo rsu_do_request(struct rsu_softc *sc, struct usb_device_request *req,
44231d98677SRui Paulo     void *data)
44331d98677SRui Paulo {
44431d98677SRui Paulo 	usb_error_t err;
44531d98677SRui Paulo 	int ntries = 10;
44631d98677SRui Paulo 
44731d98677SRui Paulo 	RSU_ASSERT_LOCKED(sc);
44831d98677SRui Paulo 
44931d98677SRui Paulo 	while (ntries--) {
45031d98677SRui Paulo 		err = usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx,
45131d98677SRui Paulo 		    req, data, 0, NULL, 250 /* ms */);
4527243077cSHans Petter Selasky 		if (err == 0 || err == USB_ERR_NOT_CONFIGURED)
45331d98677SRui Paulo 			break;
45431d98677SRui Paulo 		DPRINTFN(1, "Control request failed, %s (retrying)\n",
45531d98677SRui Paulo 		    usbd_errstr(err));
45631d98677SRui Paulo 		usb_pause_mtx(&sc->sc_mtx, hz / 100);
45731d98677SRui Paulo         }
45831d98677SRui Paulo 
45931d98677SRui Paulo         return (err);
46031d98677SRui Paulo }
46131d98677SRui Paulo 
46231d98677SRui Paulo static struct ieee80211vap *
46331d98677SRui Paulo rsu_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit,
46431d98677SRui Paulo     enum ieee80211_opmode opmode, int flags,
46531d98677SRui Paulo     const uint8_t bssid[IEEE80211_ADDR_LEN],
46631d98677SRui Paulo     const uint8_t mac[IEEE80211_ADDR_LEN])
46731d98677SRui Paulo {
46831d98677SRui Paulo 	struct rsu_vap *uvp;
46931d98677SRui Paulo 	struct ieee80211vap *vap;
47031d98677SRui Paulo 
47131d98677SRui Paulo 	if (!TAILQ_EMPTY(&ic->ic_vaps))         /* only one at a time */
47231d98677SRui Paulo 		return (NULL);
47331d98677SRui Paulo 
474ba2c1fbcSAdrian Chadd 	uvp = (struct rsu_vap *) malloc(sizeof(struct rsu_vap),
475ba2c1fbcSAdrian Chadd 	    M_80211_VAP, M_NOWAIT | M_ZERO);
476ba2c1fbcSAdrian Chadd 	if (uvp == NULL)
477ba2c1fbcSAdrian Chadd 		return (NULL);
47831d98677SRui Paulo 	vap = &uvp->vap;
479bb2f69e8SHans Petter Selasky 
480bb2f69e8SHans Petter Selasky 	if (ieee80211_vap_setup(ic, vap, name, unit, opmode,
481ba2c1fbcSAdrian Chadd 	    flags, bssid, mac) != 0) {
482bb2f69e8SHans Petter Selasky 		/* out of memory */
483bb2f69e8SHans Petter Selasky 		free(uvp, M_80211_VAP);
484bb2f69e8SHans Petter Selasky 		return (NULL);
485bb2f69e8SHans Petter Selasky 	}
48631d98677SRui Paulo 
48731d98677SRui Paulo 	/* override state transition machine */
48831d98677SRui Paulo 	uvp->newstate = vap->iv_newstate;
48931d98677SRui Paulo 	vap->iv_newstate = rsu_newstate;
49031d98677SRui Paulo 
49131d98677SRui Paulo 	/* complete setup */
49231d98677SRui Paulo 	ieee80211_vap_attach(vap, ieee80211_media_change,
493ba2c1fbcSAdrian Chadd 	    ieee80211_media_status);
49431d98677SRui Paulo 	ic->ic_opmode = opmode;
49531d98677SRui Paulo 
49631d98677SRui Paulo 	return (vap);
49731d98677SRui Paulo }
49831d98677SRui Paulo 
49931d98677SRui Paulo static void
50031d98677SRui Paulo rsu_vap_delete(struct ieee80211vap *vap)
50131d98677SRui Paulo {
50231d98677SRui Paulo 	struct rsu_vap *uvp = RSU_VAP(vap);
50331d98677SRui Paulo 
50431d98677SRui Paulo 	ieee80211_vap_detach(vap);
50531d98677SRui Paulo 	free(uvp, M_80211_VAP);
50631d98677SRui Paulo }
50731d98677SRui Paulo 
50831d98677SRui Paulo static void
50931d98677SRui Paulo rsu_scan_start(struct ieee80211com *ic)
51031d98677SRui Paulo {
51131d98677SRui Paulo 	int error;
512*d3fdd08cSAdrian Chadd 	struct rsu_softc *sc = ic->ic_softc;
51331d98677SRui Paulo 
51431d98677SRui Paulo 	/* Scanning is done by the firmware. */
51531d98677SRui Paulo 	RSU_LOCK(sc);
51631d98677SRui Paulo 	error = rsu_site_survey(sc, TAILQ_FIRST(&ic->ic_vaps));
51731d98677SRui Paulo 	RSU_UNLOCK(sc);
51831d98677SRui Paulo 	if (error != 0)
51931d98677SRui Paulo 		device_printf(sc->sc_dev,
52031d98677SRui Paulo 		    "could not send site survey command\n");
52131d98677SRui Paulo }
52231d98677SRui Paulo 
52331d98677SRui Paulo static void
52431d98677SRui Paulo rsu_scan_end(struct ieee80211com *ic)
52531d98677SRui Paulo {
52631d98677SRui Paulo 	/* Nothing to do here. */
52731d98677SRui Paulo }
52831d98677SRui Paulo 
52931d98677SRui Paulo static void
53031d98677SRui Paulo rsu_set_channel(struct ieee80211com *ic __unused)
53131d98677SRui Paulo {
53231d98677SRui Paulo 	/* We are unable to switch channels, yet. */
53331d98677SRui Paulo }
53431d98677SRui Paulo 
53531d98677SRui Paulo static void
536272f6adeSGleb Smirnoff rsu_update_mcast(struct ieee80211com *ic)
53731d98677SRui Paulo {
53831d98677SRui Paulo         /* XXX do nothing?  */
53931d98677SRui Paulo }
54031d98677SRui Paulo 
54131d98677SRui Paulo static int
54231d98677SRui Paulo rsu_alloc_list(struct rsu_softc *sc, struct rsu_data data[],
54331d98677SRui Paulo     int ndata, int maxsz)
54431d98677SRui Paulo {
54531d98677SRui Paulo 	int i, error;
54631d98677SRui Paulo 
54731d98677SRui Paulo 	for (i = 0; i < ndata; i++) {
54831d98677SRui Paulo 		struct rsu_data *dp = &data[i];
54931d98677SRui Paulo 		dp->sc = sc;
55031d98677SRui Paulo 		dp->m = NULL;
55131d98677SRui Paulo 		dp->buf = malloc(maxsz, M_USBDEV, M_NOWAIT);
55231d98677SRui Paulo 		if (dp->buf == NULL) {
55331d98677SRui Paulo 			device_printf(sc->sc_dev,
55431d98677SRui Paulo 			    "could not allocate buffer\n");
55531d98677SRui Paulo 			error = ENOMEM;
55631d98677SRui Paulo 			goto fail;
55731d98677SRui Paulo 		}
55831d98677SRui Paulo 		dp->ni = NULL;
55931d98677SRui Paulo 	}
56031d98677SRui Paulo 
56131d98677SRui Paulo 	return (0);
56231d98677SRui Paulo fail:
56331d98677SRui Paulo 	rsu_free_list(sc, data, ndata);
56431d98677SRui Paulo 	return (error);
56531d98677SRui Paulo }
56631d98677SRui Paulo 
56731d98677SRui Paulo static int
56831d98677SRui Paulo rsu_alloc_rx_list(struct rsu_softc *sc)
56931d98677SRui Paulo {
57031d98677SRui Paulo         int error, i;
57131d98677SRui Paulo 
57231d98677SRui Paulo 	error = rsu_alloc_list(sc, sc->sc_rx, RSU_RX_LIST_COUNT,
57331d98677SRui Paulo 	    RSU_RXBUFSZ);
57431d98677SRui Paulo 	if (error != 0)
57531d98677SRui Paulo 		return (error);
57631d98677SRui Paulo 
57731d98677SRui Paulo 	STAILQ_INIT(&sc->sc_rx_active);
57831d98677SRui Paulo 	STAILQ_INIT(&sc->sc_rx_inactive);
57931d98677SRui Paulo 
58031d98677SRui Paulo 	for (i = 0; i < RSU_RX_LIST_COUNT; i++)
58131d98677SRui Paulo 		STAILQ_INSERT_HEAD(&sc->sc_rx_inactive, &sc->sc_rx[i], next);
58231d98677SRui Paulo 
58331d98677SRui Paulo 	return (0);
58431d98677SRui Paulo }
58531d98677SRui Paulo 
58631d98677SRui Paulo static int
58731d98677SRui Paulo rsu_alloc_tx_list(struct rsu_softc *sc)
58831d98677SRui Paulo {
58931d98677SRui Paulo 	int error, i;
59031d98677SRui Paulo 
59131d98677SRui Paulo 	error = rsu_alloc_list(sc, sc->sc_tx, RSU_TX_LIST_COUNT,
59231d98677SRui Paulo 	    RSU_TXBUFSZ);
59331d98677SRui Paulo 	if (error != 0)
59431d98677SRui Paulo 		return (error);
59531d98677SRui Paulo 
59631d98677SRui Paulo 	STAILQ_INIT(&sc->sc_tx_inactive);
597400b4e53SHans Petter Selasky 
598910593b5SHans Petter Selasky 	for (i = 0; i != RSU_N_TRANSFER; i++) {
599400b4e53SHans Petter Selasky 		STAILQ_INIT(&sc->sc_tx_active[i]);
600400b4e53SHans Petter Selasky 		STAILQ_INIT(&sc->sc_tx_pending[i]);
601400b4e53SHans Petter Selasky 	}
60231d98677SRui Paulo 
60331d98677SRui Paulo 	for (i = 0; i < RSU_TX_LIST_COUNT; i++) {
60431d98677SRui Paulo 		STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, &sc->sc_tx[i], next);
60531d98677SRui Paulo 	}
60631d98677SRui Paulo 
60731d98677SRui Paulo 	return (0);
60831d98677SRui Paulo }
60931d98677SRui Paulo 
61031d98677SRui Paulo static void
61131d98677SRui Paulo rsu_free_tx_list(struct rsu_softc *sc)
61231d98677SRui Paulo {
613885476cbSHans Petter Selasky 	int i;
614885476cbSHans Petter Selasky 
615885476cbSHans Petter Selasky 	/* prevent further allocations from TX list(s) */
616885476cbSHans Petter Selasky 	STAILQ_INIT(&sc->sc_tx_inactive);
617885476cbSHans Petter Selasky 
618910593b5SHans Petter Selasky 	for (i = 0; i != RSU_N_TRANSFER; i++) {
619885476cbSHans Petter Selasky 		STAILQ_INIT(&sc->sc_tx_active[i]);
620885476cbSHans Petter Selasky 		STAILQ_INIT(&sc->sc_tx_pending[i]);
621885476cbSHans Petter Selasky 	}
622885476cbSHans Petter Selasky 
62331d98677SRui Paulo 	rsu_free_list(sc, sc->sc_tx, RSU_TX_LIST_COUNT);
62431d98677SRui Paulo }
62531d98677SRui Paulo 
62631d98677SRui Paulo static void
62731d98677SRui Paulo rsu_free_rx_list(struct rsu_softc *sc)
62831d98677SRui Paulo {
629885476cbSHans Petter Selasky 	/* prevent further allocations from RX list(s) */
630885476cbSHans Petter Selasky 	STAILQ_INIT(&sc->sc_rx_inactive);
631885476cbSHans Petter Selasky 	STAILQ_INIT(&sc->sc_rx_active);
632885476cbSHans Petter Selasky 
63331d98677SRui Paulo 	rsu_free_list(sc, sc->sc_rx, RSU_RX_LIST_COUNT);
63431d98677SRui Paulo }
63531d98677SRui Paulo 
63631d98677SRui Paulo static void
63731d98677SRui Paulo rsu_free_list(struct rsu_softc *sc, struct rsu_data data[], int ndata)
63831d98677SRui Paulo {
63931d98677SRui Paulo 	int i;
64031d98677SRui Paulo 
64131d98677SRui Paulo 	for (i = 0; i < ndata; i++) {
64231d98677SRui Paulo 		struct rsu_data *dp = &data[i];
64331d98677SRui Paulo 
64431d98677SRui Paulo 		if (dp->buf != NULL) {
64531d98677SRui Paulo 			free(dp->buf, M_USBDEV);
64631d98677SRui Paulo 			dp->buf = NULL;
64731d98677SRui Paulo 		}
64831d98677SRui Paulo 		if (dp->ni != NULL) {
64931d98677SRui Paulo 			ieee80211_free_node(dp->ni);
65031d98677SRui Paulo 			dp->ni = NULL;
65131d98677SRui Paulo 		}
65231d98677SRui Paulo 	}
65331d98677SRui Paulo }
65431d98677SRui Paulo 
65531d98677SRui Paulo static struct rsu_data *
65631d98677SRui Paulo _rsu_getbuf(struct rsu_softc *sc)
65731d98677SRui Paulo {
65831d98677SRui Paulo 	struct rsu_data *bf;
65931d98677SRui Paulo 
66031d98677SRui Paulo 	bf = STAILQ_FIRST(&sc->sc_tx_inactive);
66131d98677SRui Paulo 	if (bf != NULL)
66231d98677SRui Paulo 		STAILQ_REMOVE_HEAD(&sc->sc_tx_inactive, next);
66331d98677SRui Paulo 	else
66431d98677SRui Paulo 		bf = NULL;
66531d98677SRui Paulo 	if (bf == NULL)
66631d98677SRui Paulo 		DPRINTF("out of xmit buffers\n");
66731d98677SRui Paulo         return (bf);
66831d98677SRui Paulo }
66931d98677SRui Paulo 
67031d98677SRui Paulo static struct rsu_data *
67131d98677SRui Paulo rsu_getbuf(struct rsu_softc *sc)
67231d98677SRui Paulo {
67331d98677SRui Paulo 	struct rsu_data *bf;
67431d98677SRui Paulo 
67531d98677SRui Paulo 	RSU_ASSERT_LOCKED(sc);
67631d98677SRui Paulo 
67731d98677SRui Paulo 	bf = _rsu_getbuf(sc);
678ba2c1fbcSAdrian Chadd 	if (bf == NULL) {
679ba2c1fbcSAdrian Chadd 		struct ifnet *ifp = sc->sc_ifp;
68031d98677SRui Paulo 		DPRINTF("stop queue\n");
681ba2c1fbcSAdrian Chadd 		ifp->if_drv_flags |= IFF_DRV_OACTIVE;
682ba2c1fbcSAdrian Chadd 	}
68331d98677SRui Paulo 	return (bf);
68431d98677SRui Paulo }
68531d98677SRui Paulo 
68631d98677SRui Paulo static int
68731d98677SRui Paulo rsu_write_region_1(struct rsu_softc *sc, uint16_t addr, uint8_t *buf,
68831d98677SRui Paulo     int len)
68931d98677SRui Paulo {
69031d98677SRui Paulo 	usb_device_request_t req;
69131d98677SRui Paulo 
69231d98677SRui Paulo 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
69331d98677SRui Paulo 	req.bRequest = R92S_REQ_REGS;
69431d98677SRui Paulo 	USETW(req.wValue, addr);
69531d98677SRui Paulo 	USETW(req.wIndex, 0);
69631d98677SRui Paulo 	USETW(req.wLength, len);
69731d98677SRui Paulo 
69831d98677SRui Paulo 	return (rsu_do_request(sc, &req, buf));
69931d98677SRui Paulo }
70031d98677SRui Paulo 
70131d98677SRui Paulo static void
70231d98677SRui Paulo rsu_write_1(struct rsu_softc *sc, uint16_t addr, uint8_t val)
70331d98677SRui Paulo {
70431d98677SRui Paulo 	rsu_write_region_1(sc, addr, &val, 1);
70531d98677SRui Paulo }
70631d98677SRui Paulo 
70731d98677SRui Paulo static void
70831d98677SRui Paulo rsu_write_2(struct rsu_softc *sc, uint16_t addr, uint16_t val)
70931d98677SRui Paulo {
71031d98677SRui Paulo 	val = htole16(val);
71131d98677SRui Paulo 	rsu_write_region_1(sc, addr, (uint8_t *)&val, 2);
71231d98677SRui Paulo }
71331d98677SRui Paulo 
71431d98677SRui Paulo static void
71531d98677SRui Paulo rsu_write_4(struct rsu_softc *sc, uint16_t addr, uint32_t val)
71631d98677SRui Paulo {
71731d98677SRui Paulo 	val = htole32(val);
71831d98677SRui Paulo 	rsu_write_region_1(sc, addr, (uint8_t *)&val, 4);
71931d98677SRui Paulo }
72031d98677SRui Paulo 
72131d98677SRui Paulo static int
72231d98677SRui Paulo rsu_read_region_1(struct rsu_softc *sc, uint16_t addr, uint8_t *buf,
72331d98677SRui Paulo     int len)
72431d98677SRui Paulo {
72531d98677SRui Paulo 	usb_device_request_t req;
72631d98677SRui Paulo 
72731d98677SRui Paulo 	req.bmRequestType = UT_READ_VENDOR_DEVICE;
72831d98677SRui Paulo 	req.bRequest = R92S_REQ_REGS;
72931d98677SRui Paulo 	USETW(req.wValue, addr);
73031d98677SRui Paulo 	USETW(req.wIndex, 0);
73131d98677SRui Paulo 	USETW(req.wLength, len);
73231d98677SRui Paulo 
73331d98677SRui Paulo 	return (rsu_do_request(sc, &req, buf));
73431d98677SRui Paulo }
73531d98677SRui Paulo 
73631d98677SRui Paulo static uint8_t
73731d98677SRui Paulo rsu_read_1(struct rsu_softc *sc, uint16_t addr)
73831d98677SRui Paulo {
73931d98677SRui Paulo 	uint8_t val;
74031d98677SRui Paulo 
74131d98677SRui Paulo 	if (rsu_read_region_1(sc, addr, &val, 1) != 0)
74231d98677SRui Paulo 		return (0xff);
74331d98677SRui Paulo 	return (val);
74431d98677SRui Paulo }
74531d98677SRui Paulo 
74631d98677SRui Paulo static uint16_t
74731d98677SRui Paulo rsu_read_2(struct rsu_softc *sc, uint16_t addr)
74831d98677SRui Paulo {
74931d98677SRui Paulo 	uint16_t val;
75031d98677SRui Paulo 
75131d98677SRui Paulo 	if (rsu_read_region_1(sc, addr, (uint8_t *)&val, 2) != 0)
75231d98677SRui Paulo 		return (0xffff);
75331d98677SRui Paulo 	return (le16toh(val));
75431d98677SRui Paulo }
75531d98677SRui Paulo 
75631d98677SRui Paulo static uint32_t
75731d98677SRui Paulo rsu_read_4(struct rsu_softc *sc, uint16_t addr)
75831d98677SRui Paulo {
75931d98677SRui Paulo 	uint32_t val;
76031d98677SRui Paulo 
76131d98677SRui Paulo 	if (rsu_read_region_1(sc, addr, (uint8_t *)&val, 4) != 0)
76231d98677SRui Paulo 		return (0xffffffff);
76331d98677SRui Paulo 	return (le32toh(val));
76431d98677SRui Paulo }
76531d98677SRui Paulo 
76631d98677SRui Paulo static int
76731d98677SRui Paulo rsu_fw_iocmd(struct rsu_softc *sc, uint32_t iocmd)
76831d98677SRui Paulo {
76931d98677SRui Paulo 	int ntries;
77031d98677SRui Paulo 
77131d98677SRui Paulo 	rsu_write_4(sc, R92S_IOCMD_CTRL, iocmd);
772b41381aeSHans Petter Selasky 	rsu_ms_delay(sc);
77331d98677SRui Paulo 	for (ntries = 0; ntries < 50; ntries++) {
77431d98677SRui Paulo 		if (rsu_read_4(sc, R92S_IOCMD_CTRL) == 0)
77531d98677SRui Paulo 			return (0);
776b41381aeSHans Petter Selasky 		rsu_ms_delay(sc);
77731d98677SRui Paulo 	}
77831d98677SRui Paulo 	return (ETIMEDOUT);
77931d98677SRui Paulo }
78031d98677SRui Paulo 
78131d98677SRui Paulo static uint8_t
78231d98677SRui Paulo rsu_efuse_read_1(struct rsu_softc *sc, uint16_t addr)
78331d98677SRui Paulo {
78431d98677SRui Paulo 	uint32_t reg;
78531d98677SRui Paulo 	int ntries;
78631d98677SRui Paulo 
78731d98677SRui Paulo 	reg = rsu_read_4(sc, R92S_EFUSE_CTRL);
78831d98677SRui Paulo 	reg = RW(reg, R92S_EFUSE_CTRL_ADDR, addr);
78931d98677SRui Paulo 	reg &= ~R92S_EFUSE_CTRL_VALID;
79031d98677SRui Paulo 	rsu_write_4(sc, R92S_EFUSE_CTRL, reg);
79131d98677SRui Paulo 	/* Wait for read operation to complete. */
79231d98677SRui Paulo 	for (ntries = 0; ntries < 100; ntries++) {
79331d98677SRui Paulo 		reg = rsu_read_4(sc, R92S_EFUSE_CTRL);
79431d98677SRui Paulo 		if (reg & R92S_EFUSE_CTRL_VALID)
79531d98677SRui Paulo 			return (MS(reg, R92S_EFUSE_CTRL_DATA));
796b41381aeSHans Petter Selasky 		rsu_ms_delay(sc);
79731d98677SRui Paulo 	}
79831d98677SRui Paulo 	device_printf(sc->sc_dev,
79931d98677SRui Paulo 	    "could not read efuse byte at address 0x%x\n", addr);
80031d98677SRui Paulo 	return (0xff);
80131d98677SRui Paulo }
80231d98677SRui Paulo 
80331d98677SRui Paulo static int
80431d98677SRui Paulo rsu_read_rom(struct rsu_softc *sc)
80531d98677SRui Paulo {
80631d98677SRui Paulo 	uint8_t *rom = sc->rom;
80731d98677SRui Paulo 	uint16_t addr = 0;
80831d98677SRui Paulo 	uint32_t reg;
80931d98677SRui Paulo 	uint8_t off, msk;
81031d98677SRui Paulo 	int i;
81131d98677SRui Paulo 
81231d98677SRui Paulo 	/* Make sure that ROM type is eFuse and that autoload succeeded. */
81331d98677SRui Paulo 	reg = rsu_read_1(sc, R92S_EE_9346CR);
81431d98677SRui Paulo 	if ((reg & (R92S_9356SEL | R92S_EEPROM_EN)) != R92S_EEPROM_EN)
81531d98677SRui Paulo 		return (EIO);
81631d98677SRui Paulo 
81731d98677SRui Paulo 	/* Turn on 2.5V to prevent eFuse leakage. */
81831d98677SRui Paulo 	reg = rsu_read_1(sc, R92S_EFUSE_TEST + 3);
81931d98677SRui Paulo 	rsu_write_1(sc, R92S_EFUSE_TEST + 3, reg | 0x80);
820b41381aeSHans Petter Selasky 	rsu_ms_delay(sc);
82131d98677SRui Paulo 	rsu_write_1(sc, R92S_EFUSE_TEST + 3, reg & ~0x80);
82231d98677SRui Paulo 
82331d98677SRui Paulo 	/* Read full ROM image. */
82431d98677SRui Paulo 	memset(&sc->rom, 0xff, sizeof(sc->rom));
82531d98677SRui Paulo 	while (addr < 512) {
82631d98677SRui Paulo 		reg = rsu_efuse_read_1(sc, addr);
82731d98677SRui Paulo 		if (reg == 0xff)
82831d98677SRui Paulo 			break;
82931d98677SRui Paulo 		addr++;
83031d98677SRui Paulo 		off = reg >> 4;
83131d98677SRui Paulo 		msk = reg & 0xf;
83231d98677SRui Paulo 		for (i = 0; i < 4; i++) {
83331d98677SRui Paulo 			if (msk & (1 << i))
83431d98677SRui Paulo 				continue;
83531d98677SRui Paulo 			rom[off * 8 + i * 2 + 0] =
83631d98677SRui Paulo 			    rsu_efuse_read_1(sc, addr);
83731d98677SRui Paulo 			addr++;
83831d98677SRui Paulo 			rom[off * 8 + i * 2 + 1] =
83931d98677SRui Paulo 			    rsu_efuse_read_1(sc, addr);
84031d98677SRui Paulo 			addr++;
84131d98677SRui Paulo 		}
84231d98677SRui Paulo 	}
84331d98677SRui Paulo #ifdef USB_DEBUG
84431d98677SRui Paulo 	if (rsu_debug >= 5) {
84531d98677SRui Paulo 		/* Dump ROM content. */
84631d98677SRui Paulo 		printf("\n");
84731d98677SRui Paulo 		for (i = 0; i < sizeof(sc->rom); i++)
84831d98677SRui Paulo 			printf("%02x:", rom[i]);
84931d98677SRui Paulo 		printf("\n");
85031d98677SRui Paulo 	}
85131d98677SRui Paulo #endif
85231d98677SRui Paulo 	return (0);
85331d98677SRui Paulo }
85431d98677SRui Paulo 
85531d98677SRui Paulo static int
85631d98677SRui Paulo rsu_fw_cmd(struct rsu_softc *sc, uint8_t code, void *buf, int len)
85731d98677SRui Paulo {
858910593b5SHans Petter Selasky 	const uint8_t which = rsu_wme_ac_xfer_map[WME_AC_VO];
85931d98677SRui Paulo 	struct rsu_data *data;
86031d98677SRui Paulo 	struct r92s_tx_desc *txd;
86131d98677SRui Paulo 	struct r92s_fw_cmd_hdr *cmd;
862400b4e53SHans Petter Selasky 	int cmdsz;
863400b4e53SHans Petter Selasky 	int xferlen;
86431d98677SRui Paulo 
86531d98677SRui Paulo 	data = rsu_getbuf(sc);
86631d98677SRui Paulo 	if (data == NULL)
86731d98677SRui Paulo 		return (ENOMEM);
86831d98677SRui Paulo 
86931d98677SRui Paulo 	/* Round-up command length to a multiple of 8 bytes. */
87031d98677SRui Paulo 	cmdsz = (len + 7) & ~7;
87131d98677SRui Paulo 
87231d98677SRui Paulo 	xferlen = sizeof(*txd) + sizeof(*cmd) + cmdsz;
87331d98677SRui Paulo 	KASSERT(xferlen <= RSU_TXBUFSZ, ("%s: invalid length", __func__));
87431d98677SRui Paulo 	memset(data->buf, 0, xferlen);
87531d98677SRui Paulo 
87631d98677SRui Paulo 	/* Setup Tx descriptor. */
87731d98677SRui Paulo 	txd = (struct r92s_tx_desc *)data->buf;
87831d98677SRui Paulo 	txd->txdw0 = htole32(
87931d98677SRui Paulo 	    SM(R92S_TXDW0_OFFSET, sizeof(*txd)) |
88031d98677SRui Paulo 	    SM(R92S_TXDW0_PKTLEN, sizeof(*cmd) + cmdsz) |
88131d98677SRui Paulo 	    R92S_TXDW0_OWN | R92S_TXDW0_FSG | R92S_TXDW0_LSG);
88231d98677SRui Paulo 	txd->txdw1 = htole32(SM(R92S_TXDW1_QSEL, R92S_TXDW1_QSEL_H2C));
88331d98677SRui Paulo 
88431d98677SRui Paulo 	/* Setup command header. */
88531d98677SRui Paulo 	cmd = (struct r92s_fw_cmd_hdr *)&txd[1];
88631d98677SRui Paulo 	cmd->len = htole16(cmdsz);
88731d98677SRui Paulo 	cmd->code = code;
88831d98677SRui Paulo 	cmd->seq = sc->cmd_seq;
88931d98677SRui Paulo 	sc->cmd_seq = (sc->cmd_seq + 1) & 0x7f;
89031d98677SRui Paulo 
89131d98677SRui Paulo 	/* Copy command payload. */
89231d98677SRui Paulo 	memcpy(&cmd[1], buf, len);
89331d98677SRui Paulo 
89431d98677SRui Paulo 	DPRINTFN(2, "Tx cmd code=0x%x len=0x%x\n", code, cmdsz);
89531d98677SRui Paulo 	data->buflen = xferlen;
896400b4e53SHans Petter Selasky 	STAILQ_INSERT_TAIL(&sc->sc_tx_pending[which], data, next);
897910593b5SHans Petter Selasky 	usbd_transfer_start(sc->sc_xfer[which]);
89831d98677SRui Paulo 
89931d98677SRui Paulo 	return (0);
90031d98677SRui Paulo }
90131d98677SRui Paulo 
90231d98677SRui Paulo /* ARGSUSED */
90331d98677SRui Paulo static void
90431d98677SRui Paulo rsu_calib_task(void *arg, int pending __unused)
90531d98677SRui Paulo {
90631d98677SRui Paulo 	struct rsu_softc *sc = arg;
90731d98677SRui Paulo 	uint32_t reg;
90831d98677SRui Paulo 
90931d98677SRui Paulo 	DPRINTFN(6, "running calibration task\n");
910910593b5SHans Petter Selasky 
91131d98677SRui Paulo 	RSU_LOCK(sc);
91231d98677SRui Paulo #ifdef notyet
91331d98677SRui Paulo 	/* Read WPS PBC status. */
91431d98677SRui Paulo 	rsu_write_1(sc, R92S_MAC_PINMUX_CTRL,
91531d98677SRui Paulo 	    R92S_GPIOMUX_EN | SM(R92S_GPIOSEL_GPIO, R92S_GPIOSEL_GPIO_JTAG));
91631d98677SRui Paulo 	rsu_write_1(sc, R92S_GPIO_IO_SEL,
91731d98677SRui Paulo 	    rsu_read_1(sc, R92S_GPIO_IO_SEL) & ~R92S_GPIO_WPS);
91831d98677SRui Paulo 	reg = rsu_read_1(sc, R92S_GPIO_CTRL);
91931d98677SRui Paulo 	if (reg != 0xff && (reg & R92S_GPIO_WPS))
92031d98677SRui Paulo 		DPRINTF(("WPS PBC is pushed\n"));
92131d98677SRui Paulo #endif
92231d98677SRui Paulo 	/* Read current signal level. */
92331d98677SRui Paulo 	if (rsu_fw_iocmd(sc, 0xf4000001) == 0) {
92431d98677SRui Paulo 		reg = rsu_read_4(sc, R92S_IOCMD_DATA);
92531d98677SRui Paulo 		DPRINTFN(8, "RSSI=%d%%\n", reg >> 4);
92631d98677SRui Paulo 	}
927910593b5SHans Petter Selasky 	if (sc->sc_calibrating)
928910593b5SHans Petter Selasky 		taskqueue_enqueue_timeout(taskqueue_thread, &sc->calib_task, hz);
92931d98677SRui Paulo 	RSU_UNLOCK(sc);
93031d98677SRui Paulo }
93131d98677SRui Paulo 
93231d98677SRui Paulo static int
93331d98677SRui Paulo rsu_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
93431d98677SRui Paulo {
93531d98677SRui Paulo 	struct rsu_vap *uvp = RSU_VAP(vap);
93631d98677SRui Paulo 	struct ieee80211com *ic = vap->iv_ic;
937*d3fdd08cSAdrian Chadd 	struct rsu_softc *sc = ic->ic_softc;
93831d98677SRui Paulo 	struct ieee80211_node *ni;
93931d98677SRui Paulo 	struct ieee80211_rateset *rs;
94031d98677SRui Paulo 	enum ieee80211_state ostate;
94131d98677SRui Paulo 	int error, startcal = 0;
94231d98677SRui Paulo 
94331d98677SRui Paulo 	ostate = vap->iv_state;
94431d98677SRui Paulo 	DPRINTF("%s -> %s\n", ieee80211_state_name[ostate],
94531d98677SRui Paulo 	    ieee80211_state_name[nstate]);
94631d98677SRui Paulo 
94731d98677SRui Paulo 	IEEE80211_UNLOCK(ic);
94831d98677SRui Paulo 	if (ostate == IEEE80211_S_RUN) {
94931d98677SRui Paulo 		RSU_LOCK(sc);
95031d98677SRui Paulo 		/* Stop calibration. */
95131d98677SRui Paulo 		sc->sc_calibrating = 0;
95231d98677SRui Paulo 		RSU_UNLOCK(sc);
95331d98677SRui Paulo 		taskqueue_drain_timeout(taskqueue_thread, &sc->calib_task);
95431d98677SRui Paulo 		/* Disassociate from our current BSS. */
95531d98677SRui Paulo 		RSU_LOCK(sc);
95631d98677SRui Paulo 		rsu_disconnect(sc);
95731d98677SRui Paulo 	} else
95831d98677SRui Paulo 		RSU_LOCK(sc);
95931d98677SRui Paulo 	switch (nstate) {
96031d98677SRui Paulo 	case IEEE80211_S_INIT:
96131d98677SRui Paulo 		break;
96231d98677SRui Paulo 	case IEEE80211_S_AUTH:
96331d98677SRui Paulo 		ni = ieee80211_ref_node(vap->iv_bss);
96431d98677SRui Paulo 		error = rsu_join_bss(sc, ni);
96531d98677SRui Paulo 		ieee80211_free_node(ni);
96631d98677SRui Paulo 		if (error != 0) {
96731d98677SRui Paulo 			device_printf(sc->sc_dev,
96831d98677SRui Paulo 			    "could not send join command\n");
96931d98677SRui Paulo 		}
97031d98677SRui Paulo 		break;
97131d98677SRui Paulo 	case IEEE80211_S_RUN:
97231d98677SRui Paulo 		ni = ieee80211_ref_node(vap->iv_bss);
97331d98677SRui Paulo 		rs = &ni->ni_rates;
97431d98677SRui Paulo 		/* Indicate highest supported rate. */
97531d98677SRui Paulo 		ni->ni_txrate = rs->rs_rates[rs->rs_nrates - 1];
97631d98677SRui Paulo 		ieee80211_free_node(ni);
97731d98677SRui Paulo 		startcal = 1;
97831d98677SRui Paulo 		break;
97931d98677SRui Paulo 	default:
98031d98677SRui Paulo 		break;
98131d98677SRui Paulo 	}
98231d98677SRui Paulo 	sc->sc_calibrating = 1;
983910593b5SHans Petter Selasky 	/* Start periodic calibration. */
984910593b5SHans Petter Selasky 	taskqueue_enqueue_timeout(taskqueue_thread, &sc->calib_task, hz);
98531d98677SRui Paulo 	RSU_UNLOCK(sc);
98631d98677SRui Paulo 	IEEE80211_LOCK(ic);
98731d98677SRui Paulo 	return (uvp->newstate(vap, nstate, arg));
98831d98677SRui Paulo }
98931d98677SRui Paulo 
99031d98677SRui Paulo #ifdef notyet
99131d98677SRui Paulo static void
99231d98677SRui Paulo rsu_set_key(struct rsu_softc *sc, const struct ieee80211_key *k)
99331d98677SRui Paulo {
99431d98677SRui Paulo 	struct r92s_fw_cmd_set_key key;
99531d98677SRui Paulo 
99631d98677SRui Paulo 	memset(&key, 0, sizeof(key));
99731d98677SRui Paulo 	/* Map net80211 cipher to HW crypto algorithm. */
99831d98677SRui Paulo 	switch (k->wk_cipher->ic_cipher) {
99931d98677SRui Paulo 	case IEEE80211_CIPHER_WEP:
100031d98677SRui Paulo 		if (k->wk_keylen < 8)
100131d98677SRui Paulo 			key.algo = R92S_KEY_ALGO_WEP40;
100231d98677SRui Paulo 		else
100331d98677SRui Paulo 			key.algo = R92S_KEY_ALGO_WEP104;
100431d98677SRui Paulo 		break;
100531d98677SRui Paulo 	case IEEE80211_CIPHER_TKIP:
100631d98677SRui Paulo 		key.algo = R92S_KEY_ALGO_TKIP;
100731d98677SRui Paulo 		break;
100831d98677SRui Paulo 	case IEEE80211_CIPHER_AES_CCM:
100931d98677SRui Paulo 		key.algo = R92S_KEY_ALGO_AES;
101031d98677SRui Paulo 		break;
101131d98677SRui Paulo 	default:
101231d98677SRui Paulo 		return;
101331d98677SRui Paulo 	}
101431d98677SRui Paulo 	key.id = k->wk_keyix;
101531d98677SRui Paulo 	key.grpkey = (k->wk_flags & IEEE80211_KEY_GROUP) != 0;
101631d98677SRui Paulo 	memcpy(key.key, k->wk_key, MIN(k->wk_keylen, sizeof(key.key)));
101731d98677SRui Paulo 	(void)rsu_fw_cmd(sc, R92S_CMD_SET_KEY, &key, sizeof(key));
101831d98677SRui Paulo }
101931d98677SRui Paulo 
102031d98677SRui Paulo static void
102131d98677SRui Paulo rsu_delete_key(struct rsu_softc *sc, const struct ieee80211_key *k)
102231d98677SRui Paulo {
102331d98677SRui Paulo 	struct r92s_fw_cmd_set_key key;
102431d98677SRui Paulo 
102531d98677SRui Paulo 	memset(&key, 0, sizeof(key));
102631d98677SRui Paulo 	key.id = k->wk_keyix;
102731d98677SRui Paulo 	(void)rsu_fw_cmd(sc, R92S_CMD_SET_KEY, &key, sizeof(key));
102831d98677SRui Paulo }
102931d98677SRui Paulo #endif
103031d98677SRui Paulo 
103131d98677SRui Paulo static int
103231d98677SRui Paulo rsu_site_survey(struct rsu_softc *sc, struct ieee80211vap *vap)
103331d98677SRui Paulo {
103431d98677SRui Paulo 	struct r92s_fw_cmd_sitesurvey cmd;
1035ba2c1fbcSAdrian Chadd 	struct ifnet *ifp = sc->sc_ifp;
1036ba2c1fbcSAdrian Chadd 	struct ieee80211com *ic = ifp->if_l2com;
103731d98677SRui Paulo 
103831d98677SRui Paulo 	memset(&cmd, 0, sizeof(cmd));
1039ba2c1fbcSAdrian Chadd 	if ((ic->ic_flags & IEEE80211_F_ASCAN) || sc->scan_pass == 1)
104031d98677SRui Paulo 		cmd.active = htole32(1);
104131d98677SRui Paulo 	cmd.limit = htole32(48);
1042ba2c1fbcSAdrian Chadd 	if (sc->scan_pass == 1 && vap->iv_des_nssid > 0) {
104331d98677SRui Paulo 		/* Do a directed scan for second pass. */
104431d98677SRui Paulo 		cmd.ssidlen = htole32(vap->iv_des_ssid[0].len);
104531d98677SRui Paulo 		memcpy(cmd.ssid, vap->iv_des_ssid[0].ssid,
104631d98677SRui Paulo 		    vap->iv_des_ssid[0].len);
104731d98677SRui Paulo 
104831d98677SRui Paulo 	}
1049ba2c1fbcSAdrian Chadd 	DPRINTF("sending site survey command, pass=%d\n", sc->scan_pass);
105031d98677SRui Paulo 	return (rsu_fw_cmd(sc, R92S_CMD_SITE_SURVEY, &cmd, sizeof(cmd)));
105131d98677SRui Paulo }
105231d98677SRui Paulo 
105331d98677SRui Paulo static int
105431d98677SRui Paulo rsu_join_bss(struct rsu_softc *sc, struct ieee80211_node *ni)
105531d98677SRui Paulo {
1056ba2c1fbcSAdrian Chadd 	struct ifnet *ifp = sc->sc_ifp;
1057ba2c1fbcSAdrian Chadd 	struct ieee80211com *ic = ifp->if_l2com;
105831d98677SRui Paulo 	struct ieee80211vap *vap = ni->ni_vap;
105931d98677SRui Paulo 	struct ndis_wlan_bssid_ex *bss;
106031d98677SRui Paulo 	struct ndis_802_11_fixed_ies *fixed;
106131d98677SRui Paulo 	struct r92s_fw_cmd_auth auth;
1062bb03cd6fSHans Petter Selasky 	uint8_t buf[sizeof(*bss) + 128] __aligned(4);
1063bb03cd6fSHans Petter Selasky 	uint8_t *frm;
106431d98677SRui Paulo 	uint8_t opmode;
106531d98677SRui Paulo 	int error;
106631d98677SRui Paulo 
106731d98677SRui Paulo 	/* Let the FW decide the opmode based on the capinfo field. */
106831d98677SRui Paulo 	opmode = NDIS802_11AUTOUNKNOWN;
106931d98677SRui Paulo 	DPRINTF("setting operating mode to %d\n", opmode);
107031d98677SRui Paulo 	error = rsu_fw_cmd(sc, R92S_CMD_SET_OPMODE, &opmode, sizeof(opmode));
107131d98677SRui Paulo 	if (error != 0)
107231d98677SRui Paulo 		return (error);
107331d98677SRui Paulo 
107431d98677SRui Paulo 	memset(&auth, 0, sizeof(auth));
107531d98677SRui Paulo 	if (vap->iv_flags & IEEE80211_F_WPA) {
107631d98677SRui Paulo 		auth.mode = R92S_AUTHMODE_WPA;
1077bb03cd6fSHans Petter Selasky 		auth.dot1x = (ni->ni_authmode == IEEE80211_AUTH_8021X);
107831d98677SRui Paulo 	} else
107931d98677SRui Paulo 		auth.mode = R92S_AUTHMODE_OPEN;
108031d98677SRui Paulo 	DPRINTF("setting auth mode to %d\n", auth.mode);
108131d98677SRui Paulo 	error = rsu_fw_cmd(sc, R92S_CMD_SET_AUTH, &auth, sizeof(auth));
108231d98677SRui Paulo 	if (error != 0)
108331d98677SRui Paulo 		return (error);
108431d98677SRui Paulo 
108531d98677SRui Paulo 	memset(buf, 0, sizeof(buf));
108631d98677SRui Paulo 	bss = (struct ndis_wlan_bssid_ex *)buf;
108731d98677SRui Paulo 	IEEE80211_ADDR_COPY(bss->macaddr, ni->ni_bssid);
108831d98677SRui Paulo 	bss->ssid.ssidlen = htole32(ni->ni_esslen);
108931d98677SRui Paulo 	memcpy(bss->ssid.ssid, ni->ni_essid, ni->ni_esslen);
109031d98677SRui Paulo 	if (vap->iv_flags & (IEEE80211_F_PRIVACY | IEEE80211_F_WPA))
109131d98677SRui Paulo 		bss->privacy = htole32(1);
109231d98677SRui Paulo 	bss->rssi = htole32(ni->ni_avgrssi);
109331d98677SRui Paulo 	if (ic->ic_curmode == IEEE80211_MODE_11B)
109431d98677SRui Paulo 		bss->networktype = htole32(NDIS802_11DS);
109531d98677SRui Paulo 	else
109631d98677SRui Paulo 		bss->networktype = htole32(NDIS802_11OFDM24);
109731d98677SRui Paulo 	bss->config.len = htole32(sizeof(bss->config));
109831d98677SRui Paulo 	bss->config.bintval = htole32(ni->ni_intval);
109931d98677SRui Paulo 	bss->config.dsconfig = htole32(ieee80211_chan2ieee(ic, ni->ni_chan));
110031d98677SRui Paulo 	bss->inframode = htole32(NDIS802_11INFRASTRUCTURE);
110131d98677SRui Paulo 	memcpy(bss->supprates, ni->ni_rates.rs_rates,
110231d98677SRui Paulo 	    ni->ni_rates.rs_nrates);
110331d98677SRui Paulo 	/* Write the fixed fields of the beacon frame. */
110431d98677SRui Paulo 	fixed = (struct ndis_802_11_fixed_ies *)&bss[1];
110531d98677SRui Paulo 	memcpy(&fixed->tstamp, ni->ni_tstamp.data, 8);
110631d98677SRui Paulo 	fixed->bintval = htole16(ni->ni_intval);
110731d98677SRui Paulo 	fixed->capabilities = htole16(ni->ni_capinfo);
110831d98677SRui Paulo 	/* Write IEs to be included in the association request. */
110931d98677SRui Paulo 	frm = (uint8_t *)&fixed[1];
111031d98677SRui Paulo 	frm = ieee80211_add_rsn(frm, vap);
111131d98677SRui Paulo 	frm = ieee80211_add_wpa(frm, vap);
111231d98677SRui Paulo 	frm = ieee80211_add_qos(frm, ni);
111331d98677SRui Paulo 	if (ni->ni_flags & IEEE80211_NODE_HT)
111431d98677SRui Paulo 		frm = ieee80211_add_htcap(frm, ni);
111531d98677SRui Paulo 	bss->ieslen = htole32(frm - (uint8_t *)fixed);
111631d98677SRui Paulo 	bss->len = htole32(((frm - buf) + 3) & ~3);
111731d98677SRui Paulo 	DPRINTF("sending join bss command to %s chan %d\n",
111831d98677SRui Paulo 	    ether_sprintf(bss->macaddr), le32toh(bss->config.dsconfig));
111931d98677SRui Paulo 	return (rsu_fw_cmd(sc, R92S_CMD_JOIN_BSS, buf, sizeof(buf)));
112031d98677SRui Paulo }
112131d98677SRui Paulo 
112231d98677SRui Paulo static int
112331d98677SRui Paulo rsu_disconnect(struct rsu_softc *sc)
112431d98677SRui Paulo {
112531d98677SRui Paulo 	uint32_t zero = 0;	/* :-) */
112631d98677SRui Paulo 
112731d98677SRui Paulo 	/* Disassociate from our current BSS. */
112831d98677SRui Paulo 	DPRINTF("sending disconnect command\n");
112931d98677SRui Paulo 	return (rsu_fw_cmd(sc, R92S_CMD_DISCONNECT, &zero, sizeof(zero)));
113031d98677SRui Paulo }
113131d98677SRui Paulo 
113231d98677SRui Paulo static void
113331d98677SRui Paulo rsu_event_survey(struct rsu_softc *sc, uint8_t *buf, int len)
113431d98677SRui Paulo {
1135ba2c1fbcSAdrian Chadd 	struct ifnet *ifp = sc->sc_ifp;
1136ba2c1fbcSAdrian Chadd 	struct ieee80211com *ic = ifp->if_l2com;
113731d98677SRui Paulo 	struct ieee80211_frame *wh;
113831d98677SRui Paulo 	struct ieee80211_channel *c;
113931d98677SRui Paulo 	struct ndis_wlan_bssid_ex *bss;
114031d98677SRui Paulo 	struct mbuf *m;
114131d98677SRui Paulo 	int pktlen;
114231d98677SRui Paulo 
114331d98677SRui Paulo 	if (__predict_false(len < sizeof(*bss)))
114431d98677SRui Paulo 		return;
114531d98677SRui Paulo 	bss = (struct ndis_wlan_bssid_ex *)buf;
114631d98677SRui Paulo 	if (__predict_false(len < sizeof(*bss) + le32toh(bss->ieslen)))
114731d98677SRui Paulo 		return;
114831d98677SRui Paulo 
114931d98677SRui Paulo 	DPRINTFN(2, "found BSS %s: len=%d chan=%d inframode=%d "
115031d98677SRui Paulo 	    "networktype=%d privacy=%d\n",
115131d98677SRui Paulo 	    ether_sprintf(bss->macaddr), le32toh(bss->len),
115231d98677SRui Paulo 	    le32toh(bss->config.dsconfig), le32toh(bss->inframode),
115331d98677SRui Paulo 	    le32toh(bss->networktype), le32toh(bss->privacy));
115431d98677SRui Paulo 
115531d98677SRui Paulo 	/* Build a fake beacon frame to let net80211 do all the parsing. */
115631d98677SRui Paulo 	pktlen = sizeof(*wh) + le32toh(bss->ieslen);
115731d98677SRui Paulo 	if (__predict_false(pktlen > MCLBYTES))
115831d98677SRui Paulo 		return;
1159107f00c0SKevin Lo 	m = m_get2(pktlen, M_NOWAIT, MT_DATA, M_PKTHDR);
116031d98677SRui Paulo 	if (__predict_false(m == NULL))
116131d98677SRui Paulo 		return;
116231d98677SRui Paulo 	wh = mtod(m, struct ieee80211_frame *);
116331d98677SRui Paulo 	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
116431d98677SRui Paulo 	    IEEE80211_FC0_SUBTYPE_BEACON;
116531d98677SRui Paulo 	wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
11668cfe5440SHans Petter Selasky 	USETW(wh->i_dur, 0);
1167ba2c1fbcSAdrian Chadd 	IEEE80211_ADDR_COPY(wh->i_addr1, ifp->if_broadcastaddr);
116831d98677SRui Paulo 	IEEE80211_ADDR_COPY(wh->i_addr2, bss->macaddr);
116931d98677SRui Paulo 	IEEE80211_ADDR_COPY(wh->i_addr3, bss->macaddr);
117031d98677SRui Paulo 	*(uint16_t *)wh->i_seq = 0;
117131d98677SRui Paulo 	memcpy(&wh[1], (uint8_t *)&bss[1], le32toh(bss->ieslen));
117231d98677SRui Paulo 
117331d98677SRui Paulo 	/* Finalize mbuf. */
117431d98677SRui Paulo 	m->m_pkthdr.len = m->m_len = pktlen;
1175ba2c1fbcSAdrian Chadd 	m->m_pkthdr.rcvif = ifp;
117631d98677SRui Paulo 	/* Fix the channel. */
117731d98677SRui Paulo 	c = ieee80211_find_channel_byieee(ic,
117831d98677SRui Paulo 	    le32toh(bss->config.dsconfig),
117931d98677SRui Paulo 	    IEEE80211_CHAN_G);
118031d98677SRui Paulo 	if (c) {
118131d98677SRui Paulo 		ic->ic_curchan = c;
118231d98677SRui Paulo 		ieee80211_radiotap_chan_change(ic);
118331d98677SRui Paulo 	}
118431d98677SRui Paulo 	/* XXX avoid a LOR */
118531d98677SRui Paulo 	RSU_UNLOCK(sc);
118631d98677SRui Paulo 	ieee80211_input_all(ic, m, le32toh(bss->rssi), 0);
118731d98677SRui Paulo 	RSU_LOCK(sc);
118831d98677SRui Paulo }
118931d98677SRui Paulo 
119031d98677SRui Paulo static void
119131d98677SRui Paulo rsu_event_join_bss(struct rsu_softc *sc, uint8_t *buf, int len)
119231d98677SRui Paulo {
1193ba2c1fbcSAdrian Chadd 	struct ifnet *ifp = sc->sc_ifp;
1194ba2c1fbcSAdrian Chadd 	struct ieee80211com *ic = ifp->if_l2com;
119531d98677SRui Paulo 	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
119631d98677SRui Paulo 	struct ieee80211_node *ni = vap->iv_bss;
119731d98677SRui Paulo 	struct r92s_event_join_bss *rsp;
1198bb03cd6fSHans Petter Selasky 	uint32_t tmp;
119931d98677SRui Paulo 	int res;
120031d98677SRui Paulo 
120131d98677SRui Paulo 	if (__predict_false(len < sizeof(*rsp)))
120231d98677SRui Paulo 		return;
120331d98677SRui Paulo 	rsp = (struct r92s_event_join_bss *)buf;
120431d98677SRui Paulo 	res = (int)le32toh(rsp->join_res);
120531d98677SRui Paulo 
120631d98677SRui Paulo 	DPRINTF("Rx join BSS event len=%d res=%d\n", len, res);
120731d98677SRui Paulo 	if (res <= 0) {
120831d98677SRui Paulo 		RSU_UNLOCK(sc);
120931d98677SRui Paulo 		ieee80211_new_state(vap, IEEE80211_S_SCAN, -1);
121031d98677SRui Paulo 		RSU_LOCK(sc);
121131d98677SRui Paulo 		return;
121231d98677SRui Paulo 	}
1213bb03cd6fSHans Petter Selasky 	tmp = le32toh(rsp->associd);
1214bb03cd6fSHans Petter Selasky 	if (tmp >= vap->iv_max_aid) {
1215bb03cd6fSHans Petter Selasky 		DPRINTF("Assoc ID overflow\n");
1216bb03cd6fSHans Petter Selasky 		tmp = 1;
1217bb03cd6fSHans Petter Selasky 	}
121831d98677SRui Paulo 	DPRINTF("associated with %s associd=%d\n",
1219bb03cd6fSHans Petter Selasky 	    ether_sprintf(rsp->bss.macaddr), tmp);
1220bb03cd6fSHans Petter Selasky 	ni->ni_associd = tmp | 0xc000;
122131d98677SRui Paulo 	RSU_UNLOCK(sc);
122231d98677SRui Paulo 	ieee80211_new_state(vap, IEEE80211_S_RUN,
122331d98677SRui Paulo 	    IEEE80211_FC0_SUBTYPE_ASSOC_RESP);
122431d98677SRui Paulo 	RSU_LOCK(sc);
122531d98677SRui Paulo }
122631d98677SRui Paulo 
122731d98677SRui Paulo static void
122831d98677SRui Paulo rsu_rx_event(struct rsu_softc *sc, uint8_t code, uint8_t *buf, int len)
122931d98677SRui Paulo {
1230ba2c1fbcSAdrian Chadd 	struct ifnet *ifp = sc->sc_ifp;
1231ba2c1fbcSAdrian Chadd 	struct ieee80211com *ic = ifp->if_l2com;
123231d98677SRui Paulo 	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
123331d98677SRui Paulo 
123431d98677SRui Paulo 	DPRINTFN(4, "Rx event code=%d len=%d\n", code, len);
123531d98677SRui Paulo 	switch (code) {
123631d98677SRui Paulo 	case R92S_EVT_SURVEY:
123731d98677SRui Paulo 		if (vap->iv_state == IEEE80211_S_SCAN)
123831d98677SRui Paulo 			rsu_event_survey(sc, buf, len);
123931d98677SRui Paulo 		break;
124031d98677SRui Paulo 	case R92S_EVT_SURVEY_DONE:
124131d98677SRui Paulo 		DPRINTF("site survey pass %d done, found %d BSS\n",
1242ba2c1fbcSAdrian Chadd 		    sc->scan_pass, le32toh(*(uint32_t *)buf));
124331d98677SRui Paulo 		if (vap->iv_state != IEEE80211_S_SCAN)
124431d98677SRui Paulo 			break;	/* Ignore if not scanning. */
1245ba2c1fbcSAdrian Chadd 		if (sc->scan_pass == 0 && vap->iv_des_nssid != 0) {
124631d98677SRui Paulo 			/* Schedule a directed scan for hidden APs. */
1247ba2c1fbcSAdrian Chadd 			sc->scan_pass = 1;
124831d98677SRui Paulo 			RSU_UNLOCK(sc);
124931d98677SRui Paulo 			ieee80211_new_state(vap, IEEE80211_S_SCAN, -1);
125031d98677SRui Paulo 			RSU_LOCK(sc);
125131d98677SRui Paulo 			break;
125231d98677SRui Paulo 		}
1253ba2c1fbcSAdrian Chadd 		sc->scan_pass = 0;
125431d98677SRui Paulo 		break;
125531d98677SRui Paulo 	case R92S_EVT_JOIN_BSS:
125631d98677SRui Paulo 		if (vap->iv_state == IEEE80211_S_AUTH)
125731d98677SRui Paulo 			rsu_event_join_bss(sc, buf, len);
125831d98677SRui Paulo 		break;
1259910593b5SHans Petter Selasky #if 0
1260910593b5SHans Petter Selasky XXX This event is occurring regularly, possibly due to some power saving event
1261910593b5SHans Petter Selasky XXX and disrupts the WLAN traffic. Disable for now.
126231d98677SRui Paulo 	case R92S_EVT_DEL_STA:
126331d98677SRui Paulo 		DPRINTF("disassociated from %s\n", ether_sprintf(buf));
126431d98677SRui Paulo 		if (vap->iv_state == IEEE80211_S_RUN &&
126531d98677SRui Paulo 		    IEEE80211_ADDR_EQ(vap->iv_bss->ni_bssid, buf)) {
126631d98677SRui Paulo 			RSU_UNLOCK(sc);
126731d98677SRui Paulo 			ieee80211_new_state(vap, IEEE80211_S_SCAN, -1);
126831d98677SRui Paulo 			RSU_LOCK(sc);
126931d98677SRui Paulo 		}
127031d98677SRui Paulo 		break;
1271910593b5SHans Petter Selasky #endif
127231d98677SRui Paulo 	case R92S_EVT_WPS_PBC:
127331d98677SRui Paulo 		DPRINTF("WPS PBC pushed.\n");
127431d98677SRui Paulo 		break;
127531d98677SRui Paulo 	case R92S_EVT_FWDBG:
1276ba2c1fbcSAdrian Chadd 		if (ifp->if_flags & IFF_DEBUG) {
127731d98677SRui Paulo 			buf[60] = '\0';
127831d98677SRui Paulo 			printf("FWDBG: %s\n", (char *)buf);
127931d98677SRui Paulo 		}
128031d98677SRui Paulo 		break;
1281910593b5SHans Petter Selasky 	default:
1282910593b5SHans Petter Selasky 		break;
128331d98677SRui Paulo 	}
128431d98677SRui Paulo }
128531d98677SRui Paulo 
128631d98677SRui Paulo static void
128731d98677SRui Paulo rsu_rx_multi_event(struct rsu_softc *sc, uint8_t *buf, int len)
128831d98677SRui Paulo {
128931d98677SRui Paulo 	struct r92s_fw_cmd_hdr *cmd;
129031d98677SRui Paulo 	int cmdsz;
129131d98677SRui Paulo 
129231d98677SRui Paulo 	DPRINTFN(6, "Rx events len=%d\n", len);
129331d98677SRui Paulo 
129431d98677SRui Paulo 	/* Skip Rx status. */
129531d98677SRui Paulo 	buf += sizeof(struct r92s_rx_stat);
129631d98677SRui Paulo 	len -= sizeof(struct r92s_rx_stat);
129731d98677SRui Paulo 
129831d98677SRui Paulo 	/* Process all events. */
129931d98677SRui Paulo 	for (;;) {
130031d98677SRui Paulo 		/* Check that command header fits. */
130131d98677SRui Paulo 		if (__predict_false(len < sizeof(*cmd)))
130231d98677SRui Paulo 			break;
130331d98677SRui Paulo 		cmd = (struct r92s_fw_cmd_hdr *)buf;
130431d98677SRui Paulo 		/* Check that command payload fits. */
130531d98677SRui Paulo 		cmdsz = le16toh(cmd->len);
130631d98677SRui Paulo 		if (__predict_false(len < sizeof(*cmd) + cmdsz))
130731d98677SRui Paulo 			break;
130831d98677SRui Paulo 
130931d98677SRui Paulo 		/* Process firmware event. */
131031d98677SRui Paulo 		rsu_rx_event(sc, cmd->code, (uint8_t *)&cmd[1], cmdsz);
131131d98677SRui Paulo 
131231d98677SRui Paulo 		if (!(cmd->seq & R92S_FW_CMD_MORE))
131331d98677SRui Paulo 			break;
131431d98677SRui Paulo 		buf += sizeof(*cmd) + cmdsz;
131531d98677SRui Paulo 		len -= sizeof(*cmd) + cmdsz;
131631d98677SRui Paulo 	}
131731d98677SRui Paulo }
131831d98677SRui Paulo 
131931d98677SRui Paulo static int8_t
132031d98677SRui Paulo rsu_get_rssi(struct rsu_softc *sc, int rate, void *physt)
132131d98677SRui Paulo {
132231d98677SRui Paulo 	static const int8_t cckoff[] = { 14, -2, -20, -40 };
132331d98677SRui Paulo 	struct r92s_rx_phystat *phy;
132431d98677SRui Paulo 	struct r92s_rx_cck *cck;
132531d98677SRui Paulo 	uint8_t rpt;
132631d98677SRui Paulo 	int8_t rssi;
132731d98677SRui Paulo 
132831d98677SRui Paulo 	if (rate <= 3) {
132931d98677SRui Paulo 		cck = (struct r92s_rx_cck *)physt;
133031d98677SRui Paulo 		rpt = (cck->agc_rpt >> 6) & 0x3;
133131d98677SRui Paulo 		rssi = cck->agc_rpt & 0x3e;
133231d98677SRui Paulo 		rssi = cckoff[rpt] - rssi;
133331d98677SRui Paulo 	} else {	/* OFDM/HT. */
133431d98677SRui Paulo 		phy = (struct r92s_rx_phystat *)physt;
133531d98677SRui Paulo 		rssi = ((le32toh(phy->phydw1) >> 1) & 0x7f) - 106;
133631d98677SRui Paulo 	}
133731d98677SRui Paulo 	return (rssi);
133831d98677SRui Paulo }
133931d98677SRui Paulo 
134031d98677SRui Paulo static struct mbuf *
134131d98677SRui Paulo rsu_rx_frame(struct rsu_softc *sc, uint8_t *buf, int pktlen, int *rssi)
134231d98677SRui Paulo {
1343ba2c1fbcSAdrian Chadd 	struct ifnet *ifp = sc->sc_ifp;
1344ba2c1fbcSAdrian Chadd 	struct ieee80211com *ic = ifp->if_l2com;
134531d98677SRui Paulo 	struct ieee80211_frame *wh;
134631d98677SRui Paulo 	struct r92s_rx_stat *stat;
134731d98677SRui Paulo 	uint32_t rxdw0, rxdw3;
134831d98677SRui Paulo 	struct mbuf *m;
134931d98677SRui Paulo 	uint8_t rate;
135031d98677SRui Paulo 	int infosz;
135131d98677SRui Paulo 
135231d98677SRui Paulo 	stat = (struct r92s_rx_stat *)buf;
135331d98677SRui Paulo 	rxdw0 = le32toh(stat->rxdw0);
135431d98677SRui Paulo 	rxdw3 = le32toh(stat->rxdw3);
135531d98677SRui Paulo 
135631d98677SRui Paulo 	if (__predict_false(rxdw0 & R92S_RXDW0_CRCERR)) {
1357ba2c1fbcSAdrian Chadd 		if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
135831d98677SRui Paulo 		return NULL;
135931d98677SRui Paulo 	}
136031d98677SRui Paulo 	if (__predict_false(pktlen < sizeof(*wh) || pktlen > MCLBYTES)) {
1361ba2c1fbcSAdrian Chadd 		if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
136231d98677SRui Paulo 		return NULL;
136331d98677SRui Paulo 	}
136431d98677SRui Paulo 
136531d98677SRui Paulo 	rate = MS(rxdw3, R92S_RXDW3_RATE);
136631d98677SRui Paulo 	infosz = MS(rxdw0, R92S_RXDW0_INFOSZ) * 8;
136731d98677SRui Paulo 
136831d98677SRui Paulo 	/* Get RSSI from PHY status descriptor if present. */
136931d98677SRui Paulo 	if (infosz != 0)
137031d98677SRui Paulo 		*rssi = rsu_get_rssi(sc, rate, &stat[1]);
137131d98677SRui Paulo 	else
137231d98677SRui Paulo 		*rssi = 0;
137331d98677SRui Paulo 
137431d98677SRui Paulo 	DPRINTFN(5, "Rx frame len=%d rate=%d infosz=%d rssi=%d\n",
137531d98677SRui Paulo 	    pktlen, rate, infosz, *rssi);
137631d98677SRui Paulo 
1377107f00c0SKevin Lo 	m = m_get2(pktlen, M_NOWAIT, MT_DATA, M_PKTHDR);
137831d98677SRui Paulo 	if (__predict_false(m == NULL)) {
1379ba2c1fbcSAdrian Chadd 		if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
138031d98677SRui Paulo 		return NULL;
138131d98677SRui Paulo 	}
1382ba2c1fbcSAdrian Chadd 	/* Finalize mbuf. */
1383ba2c1fbcSAdrian Chadd 	m->m_pkthdr.rcvif = ifp;
138431d98677SRui Paulo 	/* Hardware does Rx TCP checksum offload. */
138531d98677SRui Paulo 	if (rxdw3 & R92S_RXDW3_TCPCHKVALID) {
138631d98677SRui Paulo 		if (__predict_true(rxdw3 & R92S_RXDW3_TCPCHKRPT))
138731d98677SRui Paulo 			m->m_pkthdr.csum_flags |= CSUM_DATA_VALID;
138831d98677SRui Paulo 	}
138931d98677SRui Paulo 	wh = (struct ieee80211_frame *)((uint8_t *)&stat[1] + infosz);
139031d98677SRui Paulo 	memcpy(mtod(m, uint8_t *), wh, pktlen);
139131d98677SRui Paulo 	m->m_pkthdr.len = m->m_len = pktlen;
139231d98677SRui Paulo 
139331d98677SRui Paulo 	if (ieee80211_radiotap_active(ic)) {
139431d98677SRui Paulo 		struct rsu_rx_radiotap_header *tap = &sc->sc_rxtap;
139531d98677SRui Paulo 
139631d98677SRui Paulo 		/* Map HW rate index to 802.11 rate. */
139731d98677SRui Paulo 		tap->wr_flags = 2;
139831d98677SRui Paulo 		if (!(rxdw3 & R92S_RXDW3_HTC)) {
139931d98677SRui Paulo 			switch (rate) {
140031d98677SRui Paulo 			/* CCK. */
140131d98677SRui Paulo 			case  0: tap->wr_rate =   2; break;
140231d98677SRui Paulo 			case  1: tap->wr_rate =   4; break;
140331d98677SRui Paulo 			case  2: tap->wr_rate =  11; break;
140431d98677SRui Paulo 			case  3: tap->wr_rate =  22; break;
140531d98677SRui Paulo 			/* OFDM. */
140631d98677SRui Paulo 			case  4: tap->wr_rate =  12; break;
140731d98677SRui Paulo 			case  5: tap->wr_rate =  18; break;
140831d98677SRui Paulo 			case  6: tap->wr_rate =  24; break;
140931d98677SRui Paulo 			case  7: tap->wr_rate =  36; break;
141031d98677SRui Paulo 			case  8: tap->wr_rate =  48; break;
141131d98677SRui Paulo 			case  9: tap->wr_rate =  72; break;
141231d98677SRui Paulo 			case 10: tap->wr_rate =  96; break;
141331d98677SRui Paulo 			case 11: tap->wr_rate = 108; break;
141431d98677SRui Paulo 			}
141531d98677SRui Paulo 		} else if (rate >= 12) {	/* MCS0~15. */
141631d98677SRui Paulo 			/* Bit 7 set means HT MCS instead of rate. */
141731d98677SRui Paulo 			tap->wr_rate = 0x80 | (rate - 12);
141831d98677SRui Paulo 		}
141931d98677SRui Paulo 		tap->wr_dbm_antsignal = *rssi;
142031d98677SRui Paulo 		tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq);
142131d98677SRui Paulo 		tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags);
142231d98677SRui Paulo 	}
142331d98677SRui Paulo 
142431d98677SRui Paulo 	return (m);
142531d98677SRui Paulo }
142631d98677SRui Paulo 
142731d98677SRui Paulo static struct mbuf *
142831d98677SRui Paulo rsu_rx_multi_frame(struct rsu_softc *sc, uint8_t *buf, int len, int *rssi)
142931d98677SRui Paulo {
143031d98677SRui Paulo 	struct r92s_rx_stat *stat;
143131d98677SRui Paulo 	uint32_t rxdw0;
143231d98677SRui Paulo 	int totlen, pktlen, infosz, npkts;
143331d98677SRui Paulo 	struct mbuf *m, *m0 = NULL, *prevm = NULL;
143431d98677SRui Paulo 
143531d98677SRui Paulo 	/* Get the number of encapsulated frames. */
143631d98677SRui Paulo 	stat = (struct r92s_rx_stat *)buf;
143731d98677SRui Paulo 	npkts = MS(le32toh(stat->rxdw2), R92S_RXDW2_PKTCNT);
143831d98677SRui Paulo 	DPRINTFN(6, "Rx %d frames in one chunk\n", npkts);
143931d98677SRui Paulo 
144031d98677SRui Paulo 	/* Process all of them. */
144131d98677SRui Paulo 	while (npkts-- > 0) {
144231d98677SRui Paulo 		if (__predict_false(len < sizeof(*stat)))
144331d98677SRui Paulo 			break;
144431d98677SRui Paulo 		stat = (struct r92s_rx_stat *)buf;
144531d98677SRui Paulo 		rxdw0 = le32toh(stat->rxdw0);
144631d98677SRui Paulo 
144731d98677SRui Paulo 		pktlen = MS(rxdw0, R92S_RXDW0_PKTLEN);
144831d98677SRui Paulo 		if (__predict_false(pktlen == 0))
144931d98677SRui Paulo 			break;
145031d98677SRui Paulo 
145131d98677SRui Paulo 		infosz = MS(rxdw0, R92S_RXDW0_INFOSZ) * 8;
145231d98677SRui Paulo 
145331d98677SRui Paulo 		/* Make sure everything fits in xfer. */
145431d98677SRui Paulo 		totlen = sizeof(*stat) + infosz + pktlen;
145531d98677SRui Paulo 		if (__predict_false(totlen > len))
145631d98677SRui Paulo 			break;
145731d98677SRui Paulo 
145831d98677SRui Paulo 		/* Process 802.11 frame. */
145931d98677SRui Paulo 		m = rsu_rx_frame(sc, buf, pktlen, rssi);
146031d98677SRui Paulo 		if (m0 == NULL)
146131d98677SRui Paulo 			m0 = m;
146231d98677SRui Paulo 		if (prevm == NULL)
146331d98677SRui Paulo 			prevm = m;
146431d98677SRui Paulo 		else {
146531d98677SRui Paulo 			prevm->m_next = m;
146631d98677SRui Paulo 			prevm = m;
146731d98677SRui Paulo 		}
146831d98677SRui Paulo 		/* Next chunk is 128-byte aligned. */
146931d98677SRui Paulo 		totlen = (totlen + 127) & ~127;
147031d98677SRui Paulo 		buf += totlen;
147131d98677SRui Paulo 		len -= totlen;
147231d98677SRui Paulo 	}
147331d98677SRui Paulo 
147431d98677SRui Paulo 	return (m0);
147531d98677SRui Paulo }
147631d98677SRui Paulo 
147731d98677SRui Paulo static struct mbuf *
147831d98677SRui Paulo rsu_rxeof(struct usb_xfer *xfer, struct rsu_data *data, int *rssi)
147931d98677SRui Paulo {
148031d98677SRui Paulo 	struct rsu_softc *sc = data->sc;
148131d98677SRui Paulo 	struct r92s_rx_stat *stat;
148231d98677SRui Paulo 	int len;
148331d98677SRui Paulo 
148431d98677SRui Paulo 	usbd_xfer_status(xfer, &len, NULL, NULL, NULL);
148531d98677SRui Paulo 
148631d98677SRui Paulo 	if (__predict_false(len < sizeof(*stat))) {
148731d98677SRui Paulo 		DPRINTF("xfer too short %d\n", len);
1488ba2c1fbcSAdrian Chadd 		if_inc_counter(sc->sc_ifp, IFCOUNTER_IERRORS, 1);
148931d98677SRui Paulo 		return (NULL);
149031d98677SRui Paulo 	}
149131d98677SRui Paulo 	/* Determine if it is a firmware C2H event or an 802.11 frame. */
149231d98677SRui Paulo 	stat = (struct r92s_rx_stat *)data->buf;
149331d98677SRui Paulo 	if ((le32toh(stat->rxdw1) & 0x1ff) == 0x1ff) {
149431d98677SRui Paulo 		rsu_rx_multi_event(sc, data->buf, len);
149531d98677SRui Paulo 		/* No packets to process. */
149631d98677SRui Paulo 		return (NULL);
149731d98677SRui Paulo 	} else
149831d98677SRui Paulo 		return (rsu_rx_multi_frame(sc, data->buf, len, rssi));
149931d98677SRui Paulo }
150031d98677SRui Paulo 
150131d98677SRui Paulo static void
150231d98677SRui Paulo rsu_bulk_rx_callback(struct usb_xfer *xfer, usb_error_t error)
150331d98677SRui Paulo {
150431d98677SRui Paulo 	struct rsu_softc *sc = usbd_xfer_softc(xfer);
1505ba2c1fbcSAdrian Chadd 	struct ifnet *ifp = sc->sc_ifp;
1506ba2c1fbcSAdrian Chadd 	struct ieee80211com *ic = ifp->if_l2com;
150731d98677SRui Paulo 	struct ieee80211_frame *wh;
150831d98677SRui Paulo 	struct ieee80211_node *ni;
150931d98677SRui Paulo 	struct mbuf *m = NULL, *next;
151031d98677SRui Paulo 	struct rsu_data *data;
151131d98677SRui Paulo 	int rssi = 1;
151231d98677SRui Paulo 
151331d98677SRui Paulo 	RSU_ASSERT_LOCKED(sc);
151431d98677SRui Paulo 
151531d98677SRui Paulo 	switch (USB_GET_STATE(xfer)) {
151631d98677SRui Paulo 	case USB_ST_TRANSFERRED:
151731d98677SRui Paulo 		data = STAILQ_FIRST(&sc->sc_rx_active);
151831d98677SRui Paulo 		if (data == NULL)
151931d98677SRui Paulo 			goto tr_setup;
152031d98677SRui Paulo 		STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next);
152131d98677SRui Paulo 		m = rsu_rxeof(xfer, data, &rssi);
152231d98677SRui Paulo 		STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next);
152331d98677SRui Paulo 		/* FALLTHROUGH */
152431d98677SRui Paulo 	case USB_ST_SETUP:
152531d98677SRui Paulo tr_setup:
152631d98677SRui Paulo 		data = STAILQ_FIRST(&sc->sc_rx_inactive);
152731d98677SRui Paulo 		if (data == NULL) {
152831d98677SRui Paulo 			KASSERT(m == NULL, ("mbuf isn't NULL"));
152931d98677SRui Paulo 			return;
153031d98677SRui Paulo 		}
153131d98677SRui Paulo 		STAILQ_REMOVE_HEAD(&sc->sc_rx_inactive, next);
153231d98677SRui Paulo 		STAILQ_INSERT_TAIL(&sc->sc_rx_active, data, next);
153331d98677SRui Paulo 		usbd_xfer_set_frame_data(xfer, 0, data->buf,
153431d98677SRui Paulo 		    usbd_xfer_max_len(xfer));
153531d98677SRui Paulo 		usbd_transfer_submit(xfer);
153631d98677SRui Paulo 		/*
153731d98677SRui Paulo 		 * To avoid LOR we should unlock our private mutex here to call
153831d98677SRui Paulo 		 * ieee80211_input() because here is at the end of a USB
153931d98677SRui Paulo 		 * callback and safe to unlock.
154031d98677SRui Paulo 		 */
154131d98677SRui Paulo 		RSU_UNLOCK(sc);
154231d98677SRui Paulo 		while (m != NULL) {
154331d98677SRui Paulo 			next = m->m_next;
154431d98677SRui Paulo 			m->m_next = NULL;
154531d98677SRui Paulo 			wh = mtod(m, struct ieee80211_frame *);
154631d98677SRui Paulo 			ni = ieee80211_find_rxnode(ic,
154731d98677SRui Paulo 			    (struct ieee80211_frame_min *)wh);
154831d98677SRui Paulo 			if (ni != NULL) {
154931d98677SRui Paulo 				(void)ieee80211_input(ni, m, rssi, 0);
155031d98677SRui Paulo 				ieee80211_free_node(ni);
155131d98677SRui Paulo 			} else
155231d98677SRui Paulo 				(void)ieee80211_input_all(ic, m, rssi, 0);
155331d98677SRui Paulo 			m = next;
155431d98677SRui Paulo 		}
155531d98677SRui Paulo 		RSU_LOCK(sc);
155631d98677SRui Paulo 		break;
155731d98677SRui Paulo 	default:
155831d98677SRui Paulo 		/* needs it to the inactive queue due to a error. */
155931d98677SRui Paulo 		data = STAILQ_FIRST(&sc->sc_rx_active);
156031d98677SRui Paulo 		if (data != NULL) {
156131d98677SRui Paulo 			STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next);
156231d98677SRui Paulo 			STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next);
156331d98677SRui Paulo 		}
156431d98677SRui Paulo 		if (error != USB_ERR_CANCELLED) {
156531d98677SRui Paulo 			usbd_xfer_set_stall(xfer);
1566ba2c1fbcSAdrian Chadd 			if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
156731d98677SRui Paulo 			goto tr_setup;
156831d98677SRui Paulo 		}
156931d98677SRui Paulo 		break;
157031d98677SRui Paulo 	}
157131d98677SRui Paulo 
157231d98677SRui Paulo }
157331d98677SRui Paulo 
1574ba2c1fbcSAdrian Chadd 
157531d98677SRui Paulo static void
157631d98677SRui Paulo rsu_txeof(struct usb_xfer *xfer, struct rsu_data *data)
157731d98677SRui Paulo {
1578ba2c1fbcSAdrian Chadd 	struct rsu_softc *sc = usbd_xfer_softc(xfer);
1579ba2c1fbcSAdrian Chadd 	struct ifnet *ifp = sc->sc_ifp;
1580ba2c1fbcSAdrian Chadd 	struct mbuf *m;
158131d98677SRui Paulo 
1582ba2c1fbcSAdrian Chadd 	RSU_ASSERT_LOCKED(sc);
1583ba2c1fbcSAdrian Chadd 
1584ba2c1fbcSAdrian Chadd 	/*
1585ba2c1fbcSAdrian Chadd 	 * Do any tx complete callback.  Note this must be done before releasing
1586ba2c1fbcSAdrian Chadd 	 * the node reference.
1587ba2c1fbcSAdrian Chadd 	 */
158831d98677SRui Paulo 	if (data->m) {
1589ba2c1fbcSAdrian Chadd 		m = data->m;
1590ba2c1fbcSAdrian Chadd 		if (m->m_flags & M_TXCB) {
159131d98677SRui Paulo 			/* XXX status? */
1592ba2c1fbcSAdrian Chadd 			ieee80211_process_callback(data->ni, m, 0);
1593ba2c1fbcSAdrian Chadd 		}
1594ba2c1fbcSAdrian Chadd 		m_freem(m);
159531d98677SRui Paulo 		data->m = NULL;
1596ba2c1fbcSAdrian Chadd 	}
1597ba2c1fbcSAdrian Chadd 	if (data->ni) {
1598ba2c1fbcSAdrian Chadd 		ieee80211_free_node(data->ni);
159931d98677SRui Paulo 		data->ni = NULL;
160031d98677SRui Paulo 	}
1601ba2c1fbcSAdrian Chadd 	if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
1602ba2c1fbcSAdrian Chadd 	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
160331d98677SRui Paulo }
160431d98677SRui Paulo 
160531d98677SRui Paulo static void
1606400b4e53SHans Petter Selasky rsu_bulk_tx_callback_sub(struct usb_xfer *xfer, usb_error_t error,
1607400b4e53SHans Petter Selasky     uint8_t which)
160831d98677SRui Paulo {
160931d98677SRui Paulo 	struct rsu_softc *sc = usbd_xfer_softc(xfer);
1610ba2c1fbcSAdrian Chadd 	struct ifnet *ifp = sc->sc_ifp;
161131d98677SRui Paulo 	struct rsu_data *data;
161231d98677SRui Paulo 
161331d98677SRui Paulo 	RSU_ASSERT_LOCKED(sc);
161431d98677SRui Paulo 
161531d98677SRui Paulo 	switch (USB_GET_STATE(xfer)) {
161631d98677SRui Paulo 	case USB_ST_TRANSFERRED:
1617400b4e53SHans Petter Selasky 		data = STAILQ_FIRST(&sc->sc_tx_active[which]);
161831d98677SRui Paulo 		if (data == NULL)
161931d98677SRui Paulo 			goto tr_setup;
162031d98677SRui Paulo 		DPRINTF("transfer done %p\n", data);
1621400b4e53SHans Petter Selasky 		STAILQ_REMOVE_HEAD(&sc->sc_tx_active[which], next);
162231d98677SRui Paulo 		rsu_txeof(xfer, data);
162331d98677SRui Paulo 		STAILQ_INSERT_TAIL(&sc->sc_tx_inactive, data, next);
162431d98677SRui Paulo 		/* FALLTHROUGH */
162531d98677SRui Paulo 	case USB_ST_SETUP:
162631d98677SRui Paulo tr_setup:
1627400b4e53SHans Petter Selasky 		data = STAILQ_FIRST(&sc->sc_tx_pending[which]);
162831d98677SRui Paulo 		if (data == NULL) {
162931d98677SRui Paulo 			DPRINTF("empty pending queue sc %p\n", sc);
163031d98677SRui Paulo 			return;
163131d98677SRui Paulo 		}
1632400b4e53SHans Petter Selasky 		STAILQ_REMOVE_HEAD(&sc->sc_tx_pending[which], next);
1633400b4e53SHans Petter Selasky 		STAILQ_INSERT_TAIL(&sc->sc_tx_active[which], data, next);
163431d98677SRui Paulo 		usbd_xfer_set_frame_data(xfer, 0, data->buf, data->buflen);
163531d98677SRui Paulo 		DPRINTF("submitting transfer %p\n", data);
163631d98677SRui Paulo 		usbd_transfer_submit(xfer);
163731d98677SRui Paulo 		break;
163831d98677SRui Paulo 	default:
1639400b4e53SHans Petter Selasky 		data = STAILQ_FIRST(&sc->sc_tx_active[which]);
1640400b4e53SHans Petter Selasky 		if (data != NULL) {
1641400b4e53SHans Petter Selasky 			STAILQ_REMOVE_HEAD(&sc->sc_tx_active[which], next);
1642400b4e53SHans Petter Selasky 			rsu_txeof(xfer, data);
1643400b4e53SHans Petter Selasky 			STAILQ_INSERT_TAIL(&sc->sc_tx_inactive, data, next);
164431d98677SRui Paulo 		}
1645ba2c1fbcSAdrian Chadd 		if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
1646400b4e53SHans Petter Selasky 
164731d98677SRui Paulo 		if (error != USB_ERR_CANCELLED) {
164831d98677SRui Paulo 			usbd_xfer_set_stall(xfer);
164931d98677SRui Paulo 			goto tr_setup;
165031d98677SRui Paulo 		}
165131d98677SRui Paulo 		break;
165231d98677SRui Paulo 	}
165331d98677SRui Paulo }
165431d98677SRui Paulo 
1655400b4e53SHans Petter Selasky static void
1656910593b5SHans Petter Selasky rsu_bulk_tx_callback_be_bk(struct usb_xfer *xfer, usb_error_t error)
1657400b4e53SHans Petter Selasky {
1658910593b5SHans Petter Selasky 	rsu_bulk_tx_callback_sub(xfer, error, RSU_BULK_TX_BE_BK);
1659400b4e53SHans Petter Selasky }
1660400b4e53SHans Petter Selasky 
1661400b4e53SHans Petter Selasky static void
1662910593b5SHans Petter Selasky rsu_bulk_tx_callback_vi_vo(struct usb_xfer *xfer, usb_error_t error)
1663400b4e53SHans Petter Selasky {
1664910593b5SHans Petter Selasky 	rsu_bulk_tx_callback_sub(xfer, error, RSU_BULK_TX_VI_VO);
1665400b4e53SHans Petter Selasky }
1666400b4e53SHans Petter Selasky 
166731d98677SRui Paulo static int
166831d98677SRui Paulo rsu_tx_start(struct rsu_softc *sc, struct ieee80211_node *ni,
1669400b4e53SHans Petter Selasky     struct mbuf *m0, struct rsu_data *data)
167031d98677SRui Paulo {
1671ba2c1fbcSAdrian Chadd 	struct ifnet *ifp = sc->sc_ifp;
1672ba2c1fbcSAdrian Chadd 	struct ieee80211com *ic = ifp->if_l2com;
167331d98677SRui Paulo         struct ieee80211vap *vap = ni->ni_vap;
167431d98677SRui Paulo 	struct ieee80211_frame *wh;
167531d98677SRui Paulo 	struct ieee80211_key *k = NULL;
167631d98677SRui Paulo 	struct r92s_tx_desc *txd;
1677400b4e53SHans Petter Selasky 	uint8_t type;
1678400b4e53SHans Petter Selasky 	uint8_t tid = 0;
1679400b4e53SHans Petter Selasky 	uint8_t which;
1680400b4e53SHans Petter Selasky 	int hasqos;
1681400b4e53SHans Petter Selasky 	int xferlen;
168231d98677SRui Paulo 
168331d98677SRui Paulo 	RSU_ASSERT_LOCKED(sc);
168431d98677SRui Paulo 
168531d98677SRui Paulo 	wh = mtod(m0, struct ieee80211_frame *);
168631d98677SRui Paulo 	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
168731d98677SRui Paulo 
16885945b5f5SKevin Lo 	if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
168931d98677SRui Paulo 		k = ieee80211_crypto_encap(ni, m0);
169031d98677SRui Paulo 		if (k == NULL) {
169131d98677SRui Paulo 			device_printf(sc->sc_dev,
169231d98677SRui Paulo 			    "ieee80211_crypto_encap returns NULL.\n");
169331d98677SRui Paulo 			/* XXX we don't expect the fragmented frames */
169431d98677SRui Paulo 			m_freem(m0);
169531d98677SRui Paulo 			return (ENOBUFS);
169631d98677SRui Paulo 		}
169731d98677SRui Paulo 		wh = mtod(m0, struct ieee80211_frame *);
169831d98677SRui Paulo 	}
169931d98677SRui Paulo 	switch (type) {
170031d98677SRui Paulo 	case IEEE80211_FC0_TYPE_CTL:
170131d98677SRui Paulo 	case IEEE80211_FC0_TYPE_MGT:
1702910593b5SHans Petter Selasky 		which = rsu_wme_ac_xfer_map[WME_AC_VO];
170331d98677SRui Paulo 		break;
170431d98677SRui Paulo 	default:
1705910593b5SHans Petter Selasky 		which = rsu_wme_ac_xfer_map[M_WME_GETAC(m0)];
170631d98677SRui Paulo 		break;
170731d98677SRui Paulo 	}
170831d98677SRui Paulo 	hasqos = 0;
170931d98677SRui Paulo 
171031d98677SRui Paulo 	/* Fill Tx descriptor. */
171131d98677SRui Paulo 	txd = (struct r92s_tx_desc *)data->buf;
171231d98677SRui Paulo 	memset(txd, 0, sizeof(*txd));
171331d98677SRui Paulo 
171431d98677SRui Paulo 	txd->txdw0 |= htole32(
171531d98677SRui Paulo 	    SM(R92S_TXDW0_PKTLEN, m0->m_pkthdr.len) |
171631d98677SRui Paulo 	    SM(R92S_TXDW0_OFFSET, sizeof(*txd)) |
171731d98677SRui Paulo 	    R92S_TXDW0_OWN | R92S_TXDW0_FSG | R92S_TXDW0_LSG);
171831d98677SRui Paulo 
171931d98677SRui Paulo 	txd->txdw1 |= htole32(
172031d98677SRui Paulo 	    SM(R92S_TXDW1_MACID, R92S_MACID_BSS) |
172131d98677SRui Paulo 	    SM(R92S_TXDW1_QSEL, R92S_TXDW1_QSEL_BE));
172231d98677SRui Paulo 	if (!hasqos)
172331d98677SRui Paulo 		txd->txdw1 |= htole32(R92S_TXDW1_NONQOS);
172431d98677SRui Paulo #ifdef notyet
172531d98677SRui Paulo 	if (k != NULL) {
172631d98677SRui Paulo 		switch (k->wk_cipher->ic_cipher) {
172731d98677SRui Paulo 		case IEEE80211_CIPHER_WEP:
172831d98677SRui Paulo 			cipher = R92S_TXDW1_CIPHER_WEP;
172931d98677SRui Paulo 			break;
173031d98677SRui Paulo 		case IEEE80211_CIPHER_TKIP:
173131d98677SRui Paulo 			cipher = R92S_TXDW1_CIPHER_TKIP;
173231d98677SRui Paulo 			break;
173331d98677SRui Paulo 		case IEEE80211_CIPHER_AES_CCM:
173431d98677SRui Paulo 			cipher = R92S_TXDW1_CIPHER_AES;
173531d98677SRui Paulo 			break;
173631d98677SRui Paulo 		default:
173731d98677SRui Paulo 			cipher = R92S_TXDW1_CIPHER_NONE;
173831d98677SRui Paulo 		}
173931d98677SRui Paulo 		txd->txdw1 |= htole32(
174031d98677SRui Paulo 		    SM(R92S_TXDW1_CIPHER, cipher) |
174131d98677SRui Paulo 		    SM(R92S_TXDW1_KEYIDX, k->k_id));
174231d98677SRui Paulo 	}
174331d98677SRui Paulo #endif
174431d98677SRui Paulo 	txd->txdw2 |= htole32(R92S_TXDW2_BK);
174531d98677SRui Paulo 	if (IEEE80211_IS_MULTICAST(wh->i_addr1))
174631d98677SRui Paulo 		txd->txdw2 |= htole32(R92S_TXDW2_BMCAST);
174731d98677SRui Paulo 	/*
174831d98677SRui Paulo 	 * Firmware will use and increment the sequence number for the
174931d98677SRui Paulo 	 * specified TID.
175031d98677SRui Paulo 	 */
175131d98677SRui Paulo 	txd->txdw3 |= htole32(SM(R92S_TXDW3_SEQ, tid));
175231d98677SRui Paulo 
175331d98677SRui Paulo 	if (ieee80211_radiotap_active_vap(vap)) {
175431d98677SRui Paulo 		struct rsu_tx_radiotap_header *tap = &sc->sc_txtap;
175531d98677SRui Paulo 
175631d98677SRui Paulo 		tap->wt_flags = 0;
175731d98677SRui Paulo 		tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq);
175831d98677SRui Paulo 		tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
175931d98677SRui Paulo 		ieee80211_radiotap_tx(vap, m0);
176031d98677SRui Paulo 	}
176131d98677SRui Paulo 	xferlen = sizeof(*txd) + m0->m_pkthdr.len;
176231d98677SRui Paulo 	m_copydata(m0, 0, m0->m_pkthdr.len, (caddr_t)&txd[1]);
176331d98677SRui Paulo 
176431d98677SRui Paulo 	data->buflen = xferlen;
176531d98677SRui Paulo 	data->ni = ni;
176631d98677SRui Paulo 	data->m = m0;
1767400b4e53SHans Petter Selasky 	STAILQ_INSERT_TAIL(&sc->sc_tx_pending[which], data, next);
176831d98677SRui Paulo 
1769400b4e53SHans Petter Selasky 	/* start transfer, if any */
1770910593b5SHans Petter Selasky 	usbd_transfer_start(sc->sc_xfer[which]);
177131d98677SRui Paulo 	return (0);
177231d98677SRui Paulo }
177331d98677SRui Paulo 
1774ba2c1fbcSAdrian Chadd static void
1775ba2c1fbcSAdrian Chadd rsu_start(struct ifnet *ifp)
177631d98677SRui Paulo {
1777ba2c1fbcSAdrian Chadd 	struct rsu_softc *sc = ifp->if_softc;
1778ba2c1fbcSAdrian Chadd 
1779ba2c1fbcSAdrian Chadd 	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
1780ba2c1fbcSAdrian Chadd 		return;
178131d98677SRui Paulo 
178231d98677SRui Paulo 	RSU_LOCK(sc);
1783ba2c1fbcSAdrian Chadd 	rsu_start_locked(ifp);
178431d98677SRui Paulo 	RSU_UNLOCK(sc);
178531d98677SRui Paulo }
178631d98677SRui Paulo 
178731d98677SRui Paulo static void
1788ba2c1fbcSAdrian Chadd rsu_start_locked(struct ifnet *ifp)
178931d98677SRui Paulo {
1790ba2c1fbcSAdrian Chadd 	struct rsu_softc *sc = ifp->if_softc;
179131d98677SRui Paulo 	struct ieee80211_node *ni;
179231d98677SRui Paulo 	struct rsu_data *bf;
1793400b4e53SHans Petter Selasky 	struct mbuf *m;
179431d98677SRui Paulo 
179531d98677SRui Paulo 	RSU_ASSERT_LOCKED(sc);
179631d98677SRui Paulo 
1797ba2c1fbcSAdrian Chadd 	for (;;) {
1798ba2c1fbcSAdrian Chadd 		IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
1799ba2c1fbcSAdrian Chadd 		if (m == NULL)
180031d98677SRui Paulo 			break;
180131d98677SRui Paulo 		ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
180231d98677SRui Paulo 		m->m_pkthdr.rcvif = NULL;
180331d98677SRui Paulo 
1804ba2c1fbcSAdrian Chadd 		bf = rsu_getbuf(sc);
1805ba2c1fbcSAdrian Chadd 		if (bf == NULL) {
1806ba2c1fbcSAdrian Chadd 			if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1);
1807ba2c1fbcSAdrian Chadd 			m_freem(m);
1808ba2c1fbcSAdrian Chadd 			ieee80211_free_node(ni);
1809ba2c1fbcSAdrian Chadd 		} else if (rsu_tx_start(sc, ni, m, bf) != 0) {
1810ba2c1fbcSAdrian Chadd 			if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
181131d98677SRui Paulo 			STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, bf, next);
181231d98677SRui Paulo 			ieee80211_free_node(ni);
181331d98677SRui Paulo 		}
181431d98677SRui Paulo 	}
181531d98677SRui Paulo }
181631d98677SRui Paulo 
1817ba2c1fbcSAdrian Chadd static int
1818ba2c1fbcSAdrian Chadd rsu_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
181931d98677SRui Paulo {
1820ba2c1fbcSAdrian Chadd 	struct ieee80211com *ic = ifp->if_l2com;
1821*d3fdd08cSAdrian Chadd 	struct rsu_softc *sc = ic->ic_softc;
1822ba2c1fbcSAdrian Chadd 	struct ifreq *ifr = (struct ifreq *) data;
1823ba2c1fbcSAdrian Chadd 	int error = 0, startall = 0;
182431d98677SRui Paulo 
1825ba2c1fbcSAdrian Chadd 	switch (cmd) {
1826ba2c1fbcSAdrian Chadd 	case SIOCSIFFLAGS:
1827ba2c1fbcSAdrian Chadd 		if (ifp->if_flags & IFF_UP) {
1828ba2c1fbcSAdrian Chadd 			if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
1829*d3fdd08cSAdrian Chadd 				rsu_init(sc);
183031d98677SRui Paulo 				startall = 1;
183131d98677SRui Paulo 			}
1832ba2c1fbcSAdrian Chadd 		} else {
1833ba2c1fbcSAdrian Chadd 			if (ifp->if_drv_flags & IFF_DRV_RUNNING)
1834ba2c1fbcSAdrian Chadd 				rsu_stop(ifp, 1);
1835ba2c1fbcSAdrian Chadd 		}
183631d98677SRui Paulo 		if (startall)
183731d98677SRui Paulo 			ieee80211_start_all(ic);
1838ba2c1fbcSAdrian Chadd 		break;
1839ba2c1fbcSAdrian Chadd 	case SIOCGIFMEDIA:
1840ba2c1fbcSAdrian Chadd 		error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd);
1841ba2c1fbcSAdrian Chadd 		break;
1842ba2c1fbcSAdrian Chadd 	case SIOCGIFADDR:
1843ba2c1fbcSAdrian Chadd 		error = ether_ioctl(ifp, cmd, data);
1844ba2c1fbcSAdrian Chadd 		break;
1845ba2c1fbcSAdrian Chadd 	default:
1846ba2c1fbcSAdrian Chadd 		error = EINVAL;
1847ba2c1fbcSAdrian Chadd 		break;
1848ba2c1fbcSAdrian Chadd 	}
1849ba2c1fbcSAdrian Chadd 
1850ba2c1fbcSAdrian Chadd 	return (error);
185131d98677SRui Paulo }
185231d98677SRui Paulo 
185331d98677SRui Paulo /*
185431d98677SRui Paulo  * Power on sequence for A-cut adapters.
185531d98677SRui Paulo  */
185631d98677SRui Paulo static void
185731d98677SRui Paulo rsu_power_on_acut(struct rsu_softc *sc)
185831d98677SRui Paulo {
185931d98677SRui Paulo 	uint32_t reg;
186031d98677SRui Paulo 
186131d98677SRui Paulo 	rsu_write_1(sc, R92S_SPS0_CTRL + 1, 0x53);
186231d98677SRui Paulo 	rsu_write_1(sc, R92S_SPS0_CTRL + 0, 0x57);
186331d98677SRui Paulo 
186431d98677SRui Paulo 	/* Enable AFE macro block's bandgap and Mbias. */
186531d98677SRui Paulo 	rsu_write_1(sc, R92S_AFE_MISC,
186631d98677SRui Paulo 	    rsu_read_1(sc, R92S_AFE_MISC) |
186731d98677SRui Paulo 	    R92S_AFE_MISC_BGEN | R92S_AFE_MISC_MBEN);
186831d98677SRui Paulo 	/* Enable LDOA15 block. */
186931d98677SRui Paulo 	rsu_write_1(sc, R92S_LDOA15_CTRL,
187031d98677SRui Paulo 	    rsu_read_1(sc, R92S_LDOA15_CTRL) | R92S_LDA15_EN);
187131d98677SRui Paulo 
187231d98677SRui Paulo 	rsu_write_1(sc, R92S_SPS1_CTRL,
187331d98677SRui Paulo 	    rsu_read_1(sc, R92S_SPS1_CTRL) | R92S_SPS1_LDEN);
187431d98677SRui Paulo 	usb_pause_mtx(&sc->sc_mtx, 2 * hz);
187531d98677SRui Paulo 	/* Enable switch regulator block. */
187631d98677SRui Paulo 	rsu_write_1(sc, R92S_SPS1_CTRL,
187731d98677SRui Paulo 	    rsu_read_1(sc, R92S_SPS1_CTRL) | R92S_SPS1_SWEN);
187831d98677SRui Paulo 
187931d98677SRui Paulo 	rsu_write_4(sc, R92S_SPS1_CTRL, 0x00a7b267);
188031d98677SRui Paulo 
188131d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1,
188231d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_ISO_CTRL + 1) | 0x08);
188331d98677SRui Paulo 
188431d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_FUNC_EN + 1,
188531d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x20);
188631d98677SRui Paulo 
188731d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1,
188831d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_ISO_CTRL + 1) & ~0x90);
188931d98677SRui Paulo 
189031d98677SRui Paulo 	/* Enable AFE clock. */
189131d98677SRui Paulo 	rsu_write_1(sc, R92S_AFE_XTAL_CTRL + 1,
189231d98677SRui Paulo 	    rsu_read_1(sc, R92S_AFE_XTAL_CTRL + 1) & ~0x04);
189331d98677SRui Paulo 	/* Enable AFE PLL macro block. */
189431d98677SRui Paulo 	rsu_write_1(sc, R92S_AFE_PLL_CTRL,
189531d98677SRui Paulo 	    rsu_read_1(sc, R92S_AFE_PLL_CTRL) | 0x11);
189631d98677SRui Paulo 	/* Attach AFE PLL to MACTOP/BB. */
189731d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_ISO_CTRL,
189831d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_ISO_CTRL) & ~0x11);
189931d98677SRui Paulo 
190031d98677SRui Paulo 	/* Switch to 40MHz clock instead of 80MHz. */
190131d98677SRui Paulo 	rsu_write_2(sc, R92S_SYS_CLKR,
190231d98677SRui Paulo 	    rsu_read_2(sc, R92S_SYS_CLKR) & ~R92S_SYS_CLKSEL);
190331d98677SRui Paulo 
190431d98677SRui Paulo 	/* Enable MAC clock. */
190531d98677SRui Paulo 	rsu_write_2(sc, R92S_SYS_CLKR,
190631d98677SRui Paulo 	    rsu_read_2(sc, R92S_SYS_CLKR) |
190731d98677SRui Paulo 	    R92S_MAC_CLK_EN | R92S_SYS_CLK_EN);
190831d98677SRui Paulo 
190931d98677SRui Paulo 	rsu_write_1(sc, R92S_PMC_FSM, 0x02);
191031d98677SRui Paulo 
191131d98677SRui Paulo 	/* Enable digital core and IOREG R/W. */
191231d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_FUNC_EN + 1,
191331d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_FUNC_EN + 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) | 0x80);
191731d98677SRui Paulo 
191831d98677SRui Paulo 	/* Switch the control path to firmware. */
191931d98677SRui Paulo 	reg = rsu_read_2(sc, R92S_SYS_CLKR);
192031d98677SRui Paulo 	reg = (reg & ~R92S_SWHW_SEL) | R92S_FWHW_SEL;
192131d98677SRui Paulo 	rsu_write_2(sc, R92S_SYS_CLKR, reg);
192231d98677SRui Paulo 
192331d98677SRui Paulo 	rsu_write_2(sc, R92S_CR, 0x37fc);
192431d98677SRui Paulo 
192531d98677SRui Paulo 	/* Fix USB RX FIFO issue. */
192631d98677SRui Paulo 	rsu_write_1(sc, 0xfe5c,
192731d98677SRui Paulo 	    rsu_read_1(sc, 0xfe5c) | 0x80);
192831d98677SRui Paulo 	rsu_write_1(sc, 0x00ab,
192931d98677SRui Paulo 	    rsu_read_1(sc, 0x00ab) | 0xc0);
193031d98677SRui Paulo 
193131d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_CLKR,
193231d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_CLKR) & ~R92S_SYS_CPU_CLKSEL);
193331d98677SRui Paulo }
193431d98677SRui Paulo 
193531d98677SRui Paulo /*
193631d98677SRui Paulo  * Power on sequence for B-cut and C-cut adapters.
193731d98677SRui Paulo  */
193831d98677SRui Paulo static void
193931d98677SRui Paulo rsu_power_on_bcut(struct rsu_softc *sc)
194031d98677SRui Paulo {
194131d98677SRui Paulo 	uint32_t reg;
194231d98677SRui Paulo 	int ntries;
194331d98677SRui Paulo 
194431d98677SRui Paulo 	/* Prevent eFuse leakage. */
194531d98677SRui Paulo 	rsu_write_1(sc, 0x37, 0xb0);
1946400b4e53SHans Petter Selasky 	usb_pause_mtx(&sc->sc_mtx, hz / 100);
194731d98677SRui Paulo 	rsu_write_1(sc, 0x37, 0x30);
194831d98677SRui Paulo 
194931d98677SRui Paulo 	/* Switch the control path to hardware. */
195031d98677SRui Paulo 	reg = rsu_read_2(sc, R92S_SYS_CLKR);
195131d98677SRui Paulo 	if (reg & R92S_FWHW_SEL) {
195231d98677SRui Paulo 		rsu_write_2(sc, R92S_SYS_CLKR,
195331d98677SRui Paulo 		    reg & ~(R92S_SWHW_SEL | R92S_FWHW_SEL));
195431d98677SRui Paulo 	}
195531d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_FUNC_EN + 1,
195631d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) & ~0x8c);
1957b41381aeSHans Petter Selasky 	rsu_ms_delay(sc);
195831d98677SRui Paulo 
195931d98677SRui Paulo 	rsu_write_1(sc, R92S_SPS0_CTRL + 1, 0x53);
196031d98677SRui Paulo 	rsu_write_1(sc, R92S_SPS0_CTRL + 0, 0x57);
196131d98677SRui Paulo 
196231d98677SRui Paulo 	reg = rsu_read_1(sc, R92S_AFE_MISC);
196331d98677SRui Paulo 	rsu_write_1(sc, R92S_AFE_MISC, reg | R92S_AFE_MISC_BGEN);
196431d98677SRui Paulo 	rsu_write_1(sc, R92S_AFE_MISC, reg | R92S_AFE_MISC_BGEN |
196531d98677SRui Paulo 	    R92S_AFE_MISC_MBEN | R92S_AFE_MISC_I32_EN);
196631d98677SRui Paulo 
196731d98677SRui Paulo 	/* Enable PLL. */
196831d98677SRui Paulo 	rsu_write_1(sc, R92S_LDOA15_CTRL,
196931d98677SRui Paulo 	    rsu_read_1(sc, R92S_LDOA15_CTRL) | R92S_LDA15_EN);
197031d98677SRui Paulo 
197131d98677SRui Paulo 	rsu_write_1(sc, R92S_LDOV12D_CTRL,
197231d98677SRui Paulo 	    rsu_read_1(sc, R92S_LDOV12D_CTRL) | R92S_LDV12_EN);
197331d98677SRui Paulo 
197431d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1,
197531d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_ISO_CTRL + 1) | 0x08);
197631d98677SRui Paulo 
197731d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_FUNC_EN + 1,
197831d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x20);
197931d98677SRui Paulo 
198031d98677SRui Paulo 	/* Support 64KB IMEM. */
198131d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1,
198231d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_ISO_CTRL + 1) & ~0x97);
198331d98677SRui Paulo 
198431d98677SRui Paulo 	/* Enable AFE clock. */
198531d98677SRui Paulo 	rsu_write_1(sc, R92S_AFE_XTAL_CTRL + 1,
198631d98677SRui Paulo 	    rsu_read_1(sc, R92S_AFE_XTAL_CTRL + 1) & ~0x04);
198731d98677SRui Paulo 	/* Enable AFE PLL macro block. */
198831d98677SRui Paulo 	reg = rsu_read_1(sc, R92S_AFE_PLL_CTRL);
198931d98677SRui Paulo 	rsu_write_1(sc, R92S_AFE_PLL_CTRL, reg | 0x11);
1990b41381aeSHans Petter Selasky 	rsu_ms_delay(sc);
199131d98677SRui Paulo 	rsu_write_1(sc, R92S_AFE_PLL_CTRL, reg | 0x51);
1992b41381aeSHans Petter Selasky 	rsu_ms_delay(sc);
199331d98677SRui Paulo 	rsu_write_1(sc, R92S_AFE_PLL_CTRL, reg | 0x11);
1994b41381aeSHans Petter Selasky 	rsu_ms_delay(sc);
199531d98677SRui Paulo 
199631d98677SRui Paulo 	/* Attach AFE PLL to MACTOP/BB. */
199731d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_ISO_CTRL,
199831d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_ISO_CTRL) & ~0x11);
199931d98677SRui Paulo 
200031d98677SRui Paulo 	/* Switch to 40MHz clock. */
200131d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_CLKR, 0x00);
200231d98677SRui Paulo 	/* Disable CPU clock and 80MHz SSC. */
200331d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_CLKR,
200431d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_CLKR) | 0xa0);
200531d98677SRui Paulo 	/* Enable MAC clock. */
200631d98677SRui Paulo 	rsu_write_2(sc, R92S_SYS_CLKR,
200731d98677SRui Paulo 	    rsu_read_2(sc, R92S_SYS_CLKR) |
200831d98677SRui Paulo 	    R92S_MAC_CLK_EN | R92S_SYS_CLK_EN);
200931d98677SRui Paulo 
201031d98677SRui Paulo 	rsu_write_1(sc, R92S_PMC_FSM, 0x02);
201131d98677SRui Paulo 
201231d98677SRui Paulo 	/* Enable digital core and IOREG R/W. */
201331d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_FUNC_EN + 1,
201431d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x08);
201531d98677SRui Paulo 
201631d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_FUNC_EN + 1,
201731d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x80);
201831d98677SRui Paulo 
201931d98677SRui Paulo 	/* Switch the control path to firmware. */
202031d98677SRui Paulo 	reg = rsu_read_2(sc, R92S_SYS_CLKR);
202131d98677SRui Paulo 	reg = (reg & ~R92S_SWHW_SEL) | R92S_FWHW_SEL;
202231d98677SRui Paulo 	rsu_write_2(sc, R92S_SYS_CLKR, reg);
202331d98677SRui Paulo 
202431d98677SRui Paulo 	rsu_write_2(sc, R92S_CR, 0x37fc);
202531d98677SRui Paulo 
202631d98677SRui Paulo 	/* Fix USB RX FIFO issue. */
202731d98677SRui Paulo 	rsu_write_1(sc, 0xfe5c,
202831d98677SRui Paulo 	    rsu_read_1(sc, 0xfe5c) | 0x80);
202931d98677SRui Paulo 
203031d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_CLKR,
203131d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_CLKR) & ~R92S_SYS_CPU_CLKSEL);
203231d98677SRui Paulo 
203331d98677SRui Paulo 	rsu_write_1(sc, 0xfe1c, 0x80);
203431d98677SRui Paulo 
203531d98677SRui Paulo 	/* Make sure TxDMA is ready to download firmware. */
203631d98677SRui Paulo 	for (ntries = 0; ntries < 20; ntries++) {
203731d98677SRui Paulo 		reg = rsu_read_1(sc, R92S_TCR);
203831d98677SRui Paulo 		if ((reg & (R92S_TCR_IMEM_CHK_RPT | R92S_TCR_EMEM_CHK_RPT)) ==
203931d98677SRui Paulo 		    (R92S_TCR_IMEM_CHK_RPT | R92S_TCR_EMEM_CHK_RPT))
204031d98677SRui Paulo 			break;
2041b41381aeSHans Petter Selasky 		rsu_ms_delay(sc);
204231d98677SRui Paulo 	}
204331d98677SRui Paulo 	if (ntries == 20) {
204431d98677SRui Paulo 		DPRINTF("TxDMA is not ready\n");
204531d98677SRui Paulo 		/* Reset TxDMA. */
204631d98677SRui Paulo 		reg = rsu_read_1(sc, R92S_CR);
204731d98677SRui Paulo 		rsu_write_1(sc, R92S_CR, reg & ~R92S_CR_TXDMA_EN);
2048b41381aeSHans Petter Selasky 		rsu_ms_delay(sc);
204931d98677SRui Paulo 		rsu_write_1(sc, R92S_CR, reg | R92S_CR_TXDMA_EN);
205031d98677SRui Paulo 	}
205131d98677SRui Paulo }
205231d98677SRui Paulo 
205331d98677SRui Paulo static void
205431d98677SRui Paulo rsu_power_off(struct rsu_softc *sc)
205531d98677SRui Paulo {
205631d98677SRui Paulo 	/* Turn RF off. */
205731d98677SRui Paulo 	rsu_write_1(sc, R92S_RF_CTRL, 0x00);
2058400b4e53SHans Petter Selasky 	usb_pause_mtx(&sc->sc_mtx, hz / 200);
205931d98677SRui Paulo 
206031d98677SRui Paulo 	/* Turn MAC off. */
206131d98677SRui Paulo 	/* Switch control path. */
206231d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_CLKR + 1, 0x38);
206331d98677SRui Paulo 	/* Reset MACTOP. */
206431d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, 0x70);
206531d98677SRui Paulo 	rsu_write_1(sc, R92S_PMC_FSM, 0x06);
206631d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_ISO_CTRL + 0, 0xf9);
206731d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1, 0xe8);
206831d98677SRui Paulo 
206931d98677SRui Paulo 	/* Disable AFE PLL. */
207031d98677SRui Paulo 	rsu_write_1(sc, R92S_AFE_PLL_CTRL, 0x00);
207131d98677SRui Paulo 	/* Disable A15V. */
207231d98677SRui Paulo 	rsu_write_1(sc, R92S_LDOA15_CTRL, 0x54);
207331d98677SRui Paulo 	/* Disable eFuse 1.2V. */
207431d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, 0x50);
207531d98677SRui Paulo 	rsu_write_1(sc, R92S_LDOV12D_CTRL, 0x24);
207631d98677SRui Paulo 	/* Enable AFE macro block's bandgap and Mbias. */
207731d98677SRui Paulo 	rsu_write_1(sc, R92S_AFE_MISC, 0x30);
207831d98677SRui Paulo 	/* Disable 1.6V LDO. */
207931d98677SRui Paulo 	rsu_write_1(sc, R92S_SPS0_CTRL + 0, 0x56);
208031d98677SRui Paulo 	rsu_write_1(sc, R92S_SPS0_CTRL + 1, 0x43);
208131d98677SRui Paulo }
208231d98677SRui Paulo 
208331d98677SRui Paulo static int
20846d9b2f85SRui Paulo rsu_fw_loadsection(struct rsu_softc *sc, const uint8_t *buf, int len)
208531d98677SRui Paulo {
2086910593b5SHans Petter Selasky 	const uint8_t which = rsu_wme_ac_xfer_map[WME_AC_VO];
208731d98677SRui Paulo 	struct rsu_data *data;
208831d98677SRui Paulo 	struct r92s_tx_desc *txd;
208931d98677SRui Paulo 	int mlen;
209031d98677SRui Paulo 
209131d98677SRui Paulo 	while (len > 0) {
209231d98677SRui Paulo 		data = rsu_getbuf(sc);
209331d98677SRui Paulo 		if (data == NULL)
209431d98677SRui Paulo 			return (ENOMEM);
209531d98677SRui Paulo 		txd = (struct r92s_tx_desc *)data->buf;
209631d98677SRui Paulo 		memset(txd, 0, sizeof(*txd));
209731d98677SRui Paulo 		if (len <= RSU_TXBUFSZ - sizeof(*txd)) {
209831d98677SRui Paulo 			/* Last chunk. */
209931d98677SRui Paulo 			txd->txdw0 |= htole32(R92S_TXDW0_LINIP);
210031d98677SRui Paulo 			mlen = len;
210131d98677SRui Paulo 		} else
210231d98677SRui Paulo 			mlen = RSU_TXBUFSZ - sizeof(*txd);
210331d98677SRui Paulo 		txd->txdw0 |= htole32(SM(R92S_TXDW0_PKTLEN, mlen));
210431d98677SRui Paulo 		memcpy(&txd[1], buf, mlen);
210531d98677SRui Paulo 		data->buflen = sizeof(*txd) + mlen;
210631d98677SRui Paulo 		DPRINTF("starting transfer %p\n", data);
2107400b4e53SHans Petter Selasky 		STAILQ_INSERT_TAIL(&sc->sc_tx_pending[which], data, next);
210831d98677SRui Paulo 		buf += mlen;
210931d98677SRui Paulo 		len -= mlen;
211031d98677SRui Paulo 	}
2111910593b5SHans Petter Selasky 	usbd_transfer_start(sc->sc_xfer[which]);
211231d98677SRui Paulo 	return (0);
211331d98677SRui Paulo }
211431d98677SRui Paulo 
211531d98677SRui Paulo static int
211631d98677SRui Paulo rsu_load_firmware(struct rsu_softc *sc)
211731d98677SRui Paulo {
21186d9b2f85SRui Paulo 	const struct r92s_fw_hdr *hdr;
211931d98677SRui Paulo 	struct r92s_fw_priv *dmem;
21206d9b2f85SRui Paulo 	const uint8_t *imem, *emem;
212131d98677SRui Paulo 	int imemsz, ememsz;
212231d98677SRui Paulo 	const struct firmware *fw;
212331d98677SRui Paulo 	size_t size;
212431d98677SRui Paulo 	uint32_t reg;
212531d98677SRui Paulo 	int ntries, error;
212631d98677SRui Paulo 
2127910593b5SHans Petter Selasky 	if (rsu_read_1(sc, R92S_TCR) & R92S_TCR_FWRDY) {
2128910593b5SHans Petter Selasky 		DPRINTF("Firmware already loaded\n");
2129910593b5SHans Petter Selasky 		return (0);
2130910593b5SHans Petter Selasky 	}
2131910593b5SHans Petter Selasky 
213231d98677SRui Paulo 	RSU_UNLOCK(sc);
213331d98677SRui Paulo 	/* Read firmware image from the filesystem. */
213431d98677SRui Paulo 	if ((fw = firmware_get("rsu-rtl8712fw")) == NULL) {
213531d98677SRui Paulo 		device_printf(sc->sc_dev,
213631d98677SRui Paulo 		    "%s: failed load firmware of file rsu-rtl8712fw\n",
213731d98677SRui Paulo 		    __func__);
213831d98677SRui Paulo 		RSU_LOCK(sc);
213931d98677SRui Paulo 		return (ENXIO);
214031d98677SRui Paulo 	}
214131d98677SRui Paulo 	RSU_LOCK(sc);
214231d98677SRui Paulo 	size = fw->datasize;
214331d98677SRui Paulo 	if (size < sizeof(*hdr)) {
214431d98677SRui Paulo 		device_printf(sc->sc_dev, "firmware too short\n");
214531d98677SRui Paulo 		error = EINVAL;
214631d98677SRui Paulo 		goto fail;
214731d98677SRui Paulo 	}
21486d9b2f85SRui Paulo 	hdr = (const struct r92s_fw_hdr *)fw->data;
214931d98677SRui Paulo 	if (hdr->signature != htole16(0x8712) &&
215031d98677SRui Paulo 	    hdr->signature != htole16(0x8192)) {
215131d98677SRui Paulo 		device_printf(sc->sc_dev,
215231d98677SRui Paulo 		    "invalid firmware signature 0x%x\n",
215331d98677SRui Paulo 		    le16toh(hdr->signature));
215431d98677SRui Paulo 		error = EINVAL;
215531d98677SRui Paulo 		goto fail;
215631d98677SRui Paulo 	}
215731d98677SRui Paulo 	DPRINTF("FW V%d %02x-%02x %02x:%02x\n", le16toh(hdr->version),
215831d98677SRui Paulo 	    hdr->month, hdr->day, hdr->hour, hdr->minute);
215931d98677SRui Paulo 
216031d98677SRui Paulo 	/* Make sure that driver and firmware are in sync. */
216131d98677SRui Paulo 	if (hdr->privsz != htole32(sizeof(*dmem))) {
216231d98677SRui Paulo 		device_printf(sc->sc_dev, "unsupported firmware image\n");
216331d98677SRui Paulo 		error = EINVAL;
216431d98677SRui Paulo 		goto fail;
216531d98677SRui Paulo 	}
216631d98677SRui Paulo 	/* Get FW sections sizes. */
216731d98677SRui Paulo 	imemsz = le32toh(hdr->imemsz);
216831d98677SRui Paulo 	ememsz = le32toh(hdr->sramsz);
216931d98677SRui Paulo 	/* Check that all FW sections fit in image. */
217031d98677SRui Paulo 	if (size < sizeof(*hdr) + imemsz + ememsz) {
217131d98677SRui Paulo 		device_printf(sc->sc_dev, "firmware too short\n");
217231d98677SRui Paulo 		error = EINVAL;
217331d98677SRui Paulo 		goto fail;
217431d98677SRui Paulo 	}
21756d9b2f85SRui Paulo 	imem = (const uint8_t *)&hdr[1];
217631d98677SRui Paulo 	emem = imem + imemsz;
217731d98677SRui Paulo 
217831d98677SRui Paulo 	/* Load IMEM section. */
217931d98677SRui Paulo 	error = rsu_fw_loadsection(sc, imem, imemsz);
218031d98677SRui Paulo 	if (error != 0) {
218131d98677SRui Paulo 		device_printf(sc->sc_dev,
218231d98677SRui Paulo 		    "could not load firmware section %s\n", "IMEM");
218331d98677SRui Paulo 		goto fail;
218431d98677SRui Paulo 	}
218531d98677SRui Paulo 	/* Wait for load to complete. */
2186885476cbSHans Petter Selasky 	for (ntries = 0; ntries != 50; ntries++) {
2187400b4e53SHans Petter Selasky 		usb_pause_mtx(&sc->sc_mtx, hz / 100);
2188910593b5SHans Petter Selasky 		reg = rsu_read_1(sc, R92S_TCR);
218931d98677SRui Paulo 		if (reg & R92S_TCR_IMEM_CODE_DONE)
219031d98677SRui Paulo 			break;
219131d98677SRui Paulo 	}
2192885476cbSHans Petter Selasky 	if (ntries == 50) {
2193885476cbSHans Petter Selasky 		device_printf(sc->sc_dev, "timeout waiting for IMEM transfer\n");
219431d98677SRui Paulo 		error = ETIMEDOUT;
219531d98677SRui Paulo 		goto fail;
219631d98677SRui Paulo 	}
219731d98677SRui Paulo 	/* Load EMEM section. */
219831d98677SRui Paulo 	error = rsu_fw_loadsection(sc, emem, ememsz);
219931d98677SRui Paulo 	if (error != 0) {
220031d98677SRui Paulo 		device_printf(sc->sc_dev,
220131d98677SRui Paulo 		    "could not load firmware section %s\n", "EMEM");
220231d98677SRui Paulo 		goto fail;
220331d98677SRui Paulo 	}
220431d98677SRui Paulo 	/* Wait for load to complete. */
22051593e875SHans Petter Selasky 	for (ntries = 0; ntries != 50; ntries++) {
2206400b4e53SHans Petter Selasky 		usb_pause_mtx(&sc->sc_mtx, hz / 100);
220731d98677SRui Paulo 		reg = rsu_read_2(sc, R92S_TCR);
220831d98677SRui Paulo 		if (reg & R92S_TCR_EMEM_CODE_DONE)
220931d98677SRui Paulo 			break;
221031d98677SRui Paulo 	}
22111593e875SHans Petter Selasky 	if (ntries == 50) {
2212885476cbSHans Petter Selasky 		device_printf(sc->sc_dev, "timeout waiting for EMEM transfer\n");
221331d98677SRui Paulo 		error = ETIMEDOUT;
221431d98677SRui Paulo 		goto fail;
221531d98677SRui Paulo 	}
221631d98677SRui Paulo 	/* Enable CPU. */
221731d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_CLKR,
221831d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_CLKR) | R92S_SYS_CPU_CLKSEL);
221931d98677SRui Paulo 	if (!(rsu_read_1(sc, R92S_SYS_CLKR) & R92S_SYS_CPU_CLKSEL)) {
222031d98677SRui Paulo 		device_printf(sc->sc_dev, "could not enable system clock\n");
222131d98677SRui Paulo 		error = EIO;
222231d98677SRui Paulo 		goto fail;
222331d98677SRui Paulo 	}
222431d98677SRui Paulo 	rsu_write_2(sc, R92S_SYS_FUNC_EN,
222531d98677SRui Paulo 	    rsu_read_2(sc, R92S_SYS_FUNC_EN) | R92S_FEN_CPUEN);
222631d98677SRui Paulo 	if (!(rsu_read_2(sc, R92S_SYS_FUNC_EN) & R92S_FEN_CPUEN)) {
222731d98677SRui Paulo 		device_printf(sc->sc_dev,
222831d98677SRui Paulo 		    "could not enable microcontroller\n");
222931d98677SRui Paulo 		error = EIO;
223031d98677SRui Paulo 		goto fail;
223131d98677SRui Paulo 	}
223231d98677SRui Paulo 	/* Wait for CPU to initialize. */
223331d98677SRui Paulo 	for (ntries = 0; ntries < 100; ntries++) {
2234910593b5SHans Petter Selasky 		if (rsu_read_1(sc, R92S_TCR) & R92S_TCR_IMEM_RDY)
223531d98677SRui Paulo 			break;
2236b41381aeSHans Petter Selasky 		rsu_ms_delay(sc);
223731d98677SRui Paulo 	}
223831d98677SRui Paulo 	if (ntries == 100) {
223931d98677SRui Paulo 		device_printf(sc->sc_dev,
224031d98677SRui Paulo 		    "timeout waiting for microcontroller\n");
224131d98677SRui Paulo 		error = ETIMEDOUT;
224231d98677SRui Paulo 		goto fail;
224331d98677SRui Paulo 	}
224431d98677SRui Paulo 
224531d98677SRui Paulo 	/* Update DMEM section before loading. */
22466d9b2f85SRui Paulo 	dmem = __DECONST(struct r92s_fw_priv *, &hdr->priv);
224731d98677SRui Paulo 	memset(dmem, 0, sizeof(*dmem));
224831d98677SRui Paulo 	dmem->hci_sel = R92S_HCI_SEL_USB | R92S_HCI_SEL_8172;
2249400b4e53SHans Petter Selasky 	dmem->nendpoints = 0;
225031d98677SRui Paulo 	dmem->rf_config = 0x12;	/* 1T2R */
225131d98677SRui Paulo 	dmem->vcs_type = R92S_VCS_TYPE_AUTO;
225231d98677SRui Paulo 	dmem->vcs_mode = R92S_VCS_MODE_RTS_CTS;
225331d98677SRui Paulo #ifdef notyet
225431d98677SRui Paulo 	dmem->bw40_en = (ic->ic_htcaps & IEEE80211_HTCAP_CBW20_40) != 0;
225531d98677SRui Paulo #endif
225631d98677SRui Paulo 	dmem->turbo_mode = 1;
225731d98677SRui Paulo 	/* Load DMEM section. */
225831d98677SRui Paulo 	error = rsu_fw_loadsection(sc, (uint8_t *)dmem, sizeof(*dmem));
225931d98677SRui Paulo 	if (error != 0) {
226031d98677SRui Paulo 		device_printf(sc->sc_dev,
226131d98677SRui Paulo 		    "could not load firmware section %s\n", "DMEM");
226231d98677SRui Paulo 		goto fail;
226331d98677SRui Paulo 	}
226431d98677SRui Paulo 	/* Wait for load to complete. */
226531d98677SRui Paulo 	for (ntries = 0; ntries < 100; ntries++) {
2266910593b5SHans Petter Selasky 		if (rsu_read_1(sc, R92S_TCR) & R92S_TCR_DMEM_CODE_DONE)
226731d98677SRui Paulo 			break;
2268b41381aeSHans Petter Selasky 		rsu_ms_delay(sc);
226931d98677SRui Paulo 	}
227031d98677SRui Paulo 	if (ntries == 100) {
227131d98677SRui Paulo 		device_printf(sc->sc_dev, "timeout waiting for %s transfer\n",
227231d98677SRui Paulo 		    "DMEM");
227331d98677SRui Paulo 		error = ETIMEDOUT;
227431d98677SRui Paulo 		goto fail;
227531d98677SRui Paulo 	}
227631d98677SRui Paulo 	/* Wait for firmware readiness. */
227731d98677SRui Paulo 	for (ntries = 0; ntries < 60; ntries++) {
2278910593b5SHans Petter Selasky 		if (!(rsu_read_1(sc, R92S_TCR) & R92S_TCR_FWRDY))
227931d98677SRui Paulo 			break;
2280b41381aeSHans Petter Selasky 		rsu_ms_delay(sc);
228131d98677SRui Paulo 	}
228231d98677SRui Paulo 	if (ntries == 60) {
228331d98677SRui Paulo 		device_printf(sc->sc_dev,
228431d98677SRui Paulo 		    "timeout waiting for firmware readiness\n");
228531d98677SRui Paulo 		error = ETIMEDOUT;
228631d98677SRui Paulo 		goto fail;
228731d98677SRui Paulo 	}
228831d98677SRui Paulo  fail:
228931d98677SRui Paulo 	firmware_put(fw, FIRMWARE_UNLOAD);
229031d98677SRui Paulo 	return (error);
229131d98677SRui Paulo }
229231d98677SRui Paulo 
229331d98677SRui Paulo 
229431d98677SRui Paulo static int
229531d98677SRui Paulo rsu_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
229631d98677SRui Paulo     const struct ieee80211_bpf_params *params)
229731d98677SRui Paulo {
229831d98677SRui Paulo 	struct ieee80211com *ic = ni->ni_ic;
2299ba2c1fbcSAdrian Chadd 	struct ifnet *ifp = ic->ic_ifp;
2300*d3fdd08cSAdrian Chadd 	struct rsu_softc *sc = ic->ic_softc;
230131d98677SRui Paulo 	struct rsu_data *bf;
230231d98677SRui Paulo 
230331d98677SRui Paulo 	/* prevent management frames from being sent if we're not ready */
2304ba2c1fbcSAdrian Chadd 	if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
230531d98677SRui Paulo 		m_freem(m);
230631d98677SRui Paulo 		ieee80211_free_node(ni);
230731d98677SRui Paulo 		return (ENETDOWN);
230831d98677SRui Paulo 	}
230931d98677SRui Paulo 	RSU_LOCK(sc);
231031d98677SRui Paulo 	bf = rsu_getbuf(sc);
231131d98677SRui Paulo 	if (bf == NULL) {
231231d98677SRui Paulo 		ieee80211_free_node(ni);
231331d98677SRui Paulo 		m_freem(m);
231431d98677SRui Paulo 		RSU_UNLOCK(sc);
231531d98677SRui Paulo 		return (ENOBUFS);
231631d98677SRui Paulo 	}
2317ba2c1fbcSAdrian Chadd 	if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
2318400b4e53SHans Petter Selasky 	if (rsu_tx_start(sc, ni, m, bf) != 0) {
231931d98677SRui Paulo 		ieee80211_free_node(ni);
2320ba2c1fbcSAdrian Chadd 		if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
232131d98677SRui Paulo 		STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, bf, next);
232231d98677SRui Paulo 		RSU_UNLOCK(sc);
232331d98677SRui Paulo 		return (EIO);
232431d98677SRui Paulo 	}
232531d98677SRui Paulo 	RSU_UNLOCK(sc);
232631d98677SRui Paulo 
232731d98677SRui Paulo 	return (0);
232831d98677SRui Paulo }
232931d98677SRui Paulo 
233031d98677SRui Paulo static void
2331ba2c1fbcSAdrian Chadd rsu_init(void *arg)
233231d98677SRui Paulo {
2333ba2c1fbcSAdrian Chadd 	struct rsu_softc *sc = arg;
2334ba2c1fbcSAdrian Chadd 
2335ba2c1fbcSAdrian Chadd 	RSU_LOCK(sc);
2336ba2c1fbcSAdrian Chadd 	rsu_init_locked(arg);
2337ba2c1fbcSAdrian Chadd 	RSU_UNLOCK(sc);
2338ba2c1fbcSAdrian Chadd }
2339ba2c1fbcSAdrian Chadd 
2340ba2c1fbcSAdrian Chadd static void
2341ba2c1fbcSAdrian Chadd rsu_init_locked(struct rsu_softc *sc)
2342ba2c1fbcSAdrian Chadd {
2343ba2c1fbcSAdrian Chadd 	struct ifnet *ifp = sc->sc_ifp;
234431d98677SRui Paulo 	struct r92s_set_pwr_mode cmd;
234531d98677SRui Paulo 	int error;
2346885476cbSHans Petter Selasky 	int i;
234731d98677SRui Paulo 
234831d98677SRui Paulo 	/* Init host async commands ring. */
234931d98677SRui Paulo 	sc->cmdq.cur = sc->cmdq.next = sc->cmdq.queued = 0;
235031d98677SRui Paulo 
235131d98677SRui Paulo 	/* Power on adapter. */
235231d98677SRui Paulo 	if (sc->cut == 1)
235331d98677SRui Paulo 		rsu_power_on_acut(sc);
235431d98677SRui Paulo 	else
235531d98677SRui Paulo 		rsu_power_on_bcut(sc);
2356910593b5SHans Petter Selasky 
235731d98677SRui Paulo 	/* Load firmware. */
235831d98677SRui Paulo 	error = rsu_load_firmware(sc);
235931d98677SRui Paulo 	if (error != 0)
236031d98677SRui Paulo 		goto fail;
236131d98677SRui Paulo 
236231d98677SRui Paulo 	/* Enable Rx TCP checksum offload. */
236331d98677SRui Paulo 	rsu_write_4(sc, R92S_RCR,
236431d98677SRui Paulo 	    rsu_read_4(sc, R92S_RCR) | 0x04000000);
236531d98677SRui Paulo 	/* Append PHY status. */
236631d98677SRui Paulo 	rsu_write_4(sc, R92S_RCR,
236731d98677SRui Paulo 	    rsu_read_4(sc, R92S_RCR) | 0x02000000);
236831d98677SRui Paulo 
236931d98677SRui Paulo 	rsu_write_4(sc, R92S_CR,
237031d98677SRui Paulo 	    rsu_read_4(sc, R92S_CR) & ~0xff000000);
237131d98677SRui Paulo 
237231d98677SRui Paulo 	/* Use 128 bytes pages. */
237331d98677SRui Paulo 	rsu_write_1(sc, 0x00b5,
237431d98677SRui Paulo 	    rsu_read_1(sc, 0x00b5) | 0x01);
237531d98677SRui Paulo 	/* Enable USB Rx aggregation. */
237631d98677SRui Paulo 	rsu_write_1(sc, 0x00bd,
237731d98677SRui Paulo 	    rsu_read_1(sc, 0x00bd) | 0x80);
237831d98677SRui Paulo 	/* Set USB Rx aggregation threshold. */
237931d98677SRui Paulo 	rsu_write_1(sc, 0x00d9, 0x01);
238031d98677SRui Paulo 	/* Set USB Rx aggregation timeout (1.7ms/4). */
238131d98677SRui Paulo 	rsu_write_1(sc, 0xfe5b, 0x04);
238231d98677SRui Paulo 	/* Fix USB Rx FIFO issue. */
238331d98677SRui Paulo 	rsu_write_1(sc, 0xfe5c,
238431d98677SRui Paulo 	    rsu_read_1(sc, 0xfe5c) | 0x80);
238531d98677SRui Paulo 
238631d98677SRui Paulo 	/* Set MAC address. */
2387ba2c1fbcSAdrian Chadd 	rsu_write_region_1(sc, R92S_MACID, IF_LLADDR(ifp),
2388ba2c1fbcSAdrian Chadd 	    IEEE80211_ADDR_LEN);
238931d98677SRui Paulo 
2390b41381aeSHans Petter Selasky 	/* It really takes 1.5 seconds for the firmware to boot: */
2391b41381aeSHans Petter Selasky 	usb_pause_mtx(&sc->sc_mtx, (3 * hz) / 2);
239231d98677SRui Paulo 
2393ba2c1fbcSAdrian Chadd 	DPRINTF("setting MAC address to %s\n", ether_sprintf(IF_LLADDR(ifp)));
2394ba2c1fbcSAdrian Chadd 	error = rsu_fw_cmd(sc, R92S_CMD_SET_MAC_ADDRESS, IF_LLADDR(ifp),
239531d98677SRui Paulo 	    IEEE80211_ADDR_LEN);
239631d98677SRui Paulo 	if (error != 0) {
239731d98677SRui Paulo 		device_printf(sc->sc_dev, "could not set MAC address\n");
239831d98677SRui Paulo 		goto fail;
239931d98677SRui Paulo 	}
240031d98677SRui Paulo 
240131d98677SRui Paulo 	rsu_write_1(sc, R92S_USB_HRPWM,
240231d98677SRui Paulo 	    R92S_USB_HRPWM_PS_ST_ACTIVE | R92S_USB_HRPWM_PS_ALL_ON);
240331d98677SRui Paulo 
240431d98677SRui Paulo 	memset(&cmd, 0, sizeof(cmd));
240531d98677SRui Paulo 	cmd.mode = R92S_PS_MODE_ACTIVE;
240631d98677SRui Paulo 	DPRINTF("setting ps mode to %d\n", cmd.mode);
240731d98677SRui Paulo 	error = rsu_fw_cmd(sc, R92S_CMD_SET_PWR_MODE, &cmd, sizeof(cmd));
240831d98677SRui Paulo 	if (error != 0) {
240931d98677SRui Paulo 		device_printf(sc->sc_dev, "could not set PS mode\n");
241031d98677SRui Paulo 		goto fail;
241131d98677SRui Paulo 	}
241231d98677SRui Paulo 
241331d98677SRui Paulo #if 0
241431d98677SRui Paulo 	if (ic->ic_htcaps & IEEE80211_HTCAP_CBW20_40) {
241531d98677SRui Paulo 		/* Enable 40MHz mode. */
241631d98677SRui Paulo 		error = rsu_fw_iocmd(sc,
241731d98677SRui Paulo 		    SM(R92S_IOCMD_CLASS, 0xf4) |
241831d98677SRui Paulo 		    SM(R92S_IOCMD_INDEX, 0x00) |
241931d98677SRui Paulo 		    SM(R92S_IOCMD_VALUE, 0x0007));
242031d98677SRui Paulo 		if (error != 0) {
242131d98677SRui Paulo 			device_printf(sc->sc_dev,
242231d98677SRui Paulo 			    "could not enable 40MHz mode\n");
242331d98677SRui Paulo 			goto fail;
242431d98677SRui Paulo 		}
242531d98677SRui Paulo 	}
242631d98677SRui Paulo 
242731d98677SRui Paulo 	/* Set default channel. */
242831d98677SRui Paulo 	ic->ic_bss->ni_chan = ic->ic_ibss_chan;
242931d98677SRui Paulo #endif
2430ba2c1fbcSAdrian Chadd 	sc->scan_pass = 0;
243131d98677SRui Paulo 	usbd_transfer_start(sc->sc_xfer[RSU_BULK_RX]);
243231d98677SRui Paulo 
243331d98677SRui Paulo 	/* We're ready to go. */
2434ba2c1fbcSAdrian Chadd 	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
2435ba2c1fbcSAdrian Chadd 	ifp->if_drv_flags |= IFF_DRV_RUNNING;
243631d98677SRui Paulo 	return;
243731d98677SRui Paulo fail:
2438885476cbSHans Petter Selasky 	/* Need to stop all failed transfers, if any */
2439885476cbSHans Petter Selasky 	for (i = 0; i != RSU_N_TRANSFER; i++)
2440885476cbSHans Petter Selasky 		usbd_transfer_stop(sc->sc_xfer[i]);
244131d98677SRui Paulo }
244231d98677SRui Paulo 
244331d98677SRui Paulo static void
2444ba2c1fbcSAdrian Chadd rsu_stop(struct ifnet *ifp, int disable)
244531d98677SRui Paulo {
2446ba2c1fbcSAdrian Chadd 	struct rsu_softc *sc = ifp->if_softc;
2447ba2c1fbcSAdrian Chadd 
2448ba2c1fbcSAdrian Chadd 	RSU_LOCK(sc);
2449ba2c1fbcSAdrian Chadd 	rsu_stop_locked(ifp, disable);
2450ba2c1fbcSAdrian Chadd 	RSU_UNLOCK(sc);
2451ba2c1fbcSAdrian Chadd }
2452ba2c1fbcSAdrian Chadd 
2453ba2c1fbcSAdrian Chadd static void
2454ba2c1fbcSAdrian Chadd rsu_stop_locked(struct ifnet *ifp, int disable __unused)
2455ba2c1fbcSAdrian Chadd {
2456ba2c1fbcSAdrian Chadd 	struct rsu_softc *sc = ifp->if_softc;
245731d98677SRui Paulo 	int i;
245831d98677SRui Paulo 
2459ba2c1fbcSAdrian Chadd 	ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
246031d98677SRui Paulo 	sc->sc_calibrating = 0;
246131d98677SRui Paulo 	taskqueue_cancel_timeout(taskqueue_thread, &sc->calib_task, NULL);
246231d98677SRui Paulo 
246331d98677SRui Paulo 	/* Power off adapter. */
246431d98677SRui Paulo 	rsu_power_off(sc);
246531d98677SRui Paulo 
246631d98677SRui Paulo 	for (i = 0; i < RSU_N_TRANSFER; i++)
246731d98677SRui Paulo 		usbd_transfer_stop(sc->sc_xfer[i]);
246831d98677SRui Paulo }
246931d98677SRui Paulo 
2470b41381aeSHans Petter Selasky static void
2471b41381aeSHans Petter Selasky rsu_ms_delay(struct rsu_softc *sc)
2472b41381aeSHans Petter Selasky {
2473b41381aeSHans Petter Selasky 	usb_pause_mtx(&sc->sc_mtx, hz / 1000);
2474b41381aeSHans Petter Selasky }
2475