xref: /freebsd/sys/dev/usb/wlan/if_rsu.c (revision 31d9867769df3c14e87a6de68a20ba516ecba9f6)
1*31d98677SRui Paulo /*	$OpenBSD: if_rsu.c,v 1.17 2013/04/15 09:23:01 mglocker Exp $	*/
2*31d98677SRui Paulo 
3*31d98677SRui Paulo /*-
4*31d98677SRui Paulo  * Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr>
5*31d98677SRui Paulo  *
6*31d98677SRui Paulo  * Permission to use, copy, modify, and distribute this software for any
7*31d98677SRui Paulo  * purpose with or without fee is hereby granted, provided that the above
8*31d98677SRui Paulo  * copyright notice and this permission notice appear in all copies.
9*31d98677SRui Paulo  *
10*31d98677SRui Paulo  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11*31d98677SRui Paulo  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12*31d98677SRui Paulo  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13*31d98677SRui Paulo  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14*31d98677SRui Paulo  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15*31d98677SRui Paulo  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16*31d98677SRui Paulo  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*31d98677SRui Paulo  */
18*31d98677SRui Paulo #include <sys/cdefs.h>
19*31d98677SRui Paulo __FBSDID("$FreeBSD$");
20*31d98677SRui Paulo 
21*31d98677SRui Paulo /*
22*31d98677SRui Paulo  * Driver for Realtek RTL8188SU/RTL8191SU/RTL8192SU.
23*31d98677SRui Paulo  *
24*31d98677SRui Paulo  * TODO:
25*31d98677SRui Paulo  *   o 11n support
26*31d98677SRui Paulo  *   o h/w crypto
27*31d98677SRui Paulo  *   o hostap / ibss / mesh
28*31d98677SRui Paulo  */
29*31d98677SRui Paulo #include <sys/param.h>
30*31d98677SRui Paulo #include <sys/endian.h>
31*31d98677SRui Paulo #include <sys/sockio.h>
32*31d98677SRui Paulo #include <sys/mbuf.h>
33*31d98677SRui Paulo #include <sys/kernel.h>
34*31d98677SRui Paulo #include <sys/socket.h>
35*31d98677SRui Paulo #include <sys/systm.h>
36*31d98677SRui Paulo #include <sys/conf.h>
37*31d98677SRui Paulo #include <sys/bus.h>
38*31d98677SRui Paulo #include <sys/rman.h>
39*31d98677SRui Paulo #include <sys/firmware.h>
40*31d98677SRui Paulo #include <sys/module.h>
41*31d98677SRui Paulo 
42*31d98677SRui Paulo #include <machine/bus.h>
43*31d98677SRui Paulo #include <machine/resource.h>
44*31d98677SRui Paulo 
45*31d98677SRui Paulo #include <net/bpf.h>
46*31d98677SRui Paulo #include <net/if.h>
47*31d98677SRui Paulo #include <net/if_arp.h>
48*31d98677SRui Paulo #include <net/if_dl.h>
49*31d98677SRui Paulo #include <net/if_media.h>
50*31d98677SRui Paulo #include <net/if_types.h>
51*31d98677SRui Paulo 
52*31d98677SRui Paulo #include <netinet/in.h>
53*31d98677SRui Paulo #include <netinet/in_systm.h>
54*31d98677SRui Paulo #include <netinet/in_var.h>
55*31d98677SRui Paulo #include <netinet/if_ether.h>
56*31d98677SRui Paulo #include <netinet/ip.h>
57*31d98677SRui Paulo 
58*31d98677SRui Paulo #include <net80211/ieee80211_var.h>
59*31d98677SRui Paulo #include <net80211/ieee80211_regdomain.h>
60*31d98677SRui Paulo #include <net80211/ieee80211_radiotap.h>
61*31d98677SRui Paulo 
62*31d98677SRui Paulo #include <dev/usb/usb.h>
63*31d98677SRui Paulo #include <dev/usb/usbdi.h>
64*31d98677SRui Paulo #include "usbdevs.h"
65*31d98677SRui Paulo 
66*31d98677SRui Paulo #define USB_DEBUG_VAR rsu_debug
67*31d98677SRui Paulo #include <dev/usb/usb_debug.h>
68*31d98677SRui Paulo 
69*31d98677SRui Paulo #include <dev/usb/wlan/if_rsureg.h>
70*31d98677SRui Paulo 
71*31d98677SRui Paulo #ifdef USB_DEBUG
72*31d98677SRui Paulo static int rsu_debug = 0;
73*31d98677SRui Paulo SYSCTL_NODE(_hw_usb, OID_AUTO, rsu, CTLFLAG_RW, 0, "USB rsu");
74*31d98677SRui Paulo SYSCTL_INT(_hw_usb_rsu, OID_AUTO, debug, CTLFLAG_RW, &rsu_debug, 0,
75*31d98677SRui Paulo     "Debug level");
76*31d98677SRui Paulo #endif
77*31d98677SRui Paulo 
78*31d98677SRui Paulo static const STRUCT_USB_HOST_ID rsu_devs[] = {
79*31d98677SRui Paulo #define	RSU_HT_NOT_SUPPORTED 0
80*31d98677SRui Paulo #define	RSU_HT_SUPPORTED 1
81*31d98677SRui Paulo #define RSU_DEV_HT(v,p)  { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, \
82*31d98677SRui Paulo 				   RSU_HT_SUPPORTED) }
83*31d98677SRui Paulo #define RSU_DEV(v,p)     { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, \
84*31d98677SRui Paulo 				   RSU_HT_NOT_SUPPORTED) }
85*31d98677SRui Paulo 	RSU_DEV(ASUS,			RTL8192SU),
86*31d98677SRui Paulo 	RSU_DEV(AZUREWAVE,		RTL8192SU_4),
87*31d98677SRui Paulo 	RSU_DEV_HT(ACCTON,		RTL8192SU),
88*31d98677SRui Paulo 	RSU_DEV_HT(ASUS,		USBN10),
89*31d98677SRui Paulo 	RSU_DEV_HT(AZUREWAVE,		RTL8192SU_1),
90*31d98677SRui Paulo 	RSU_DEV_HT(AZUREWAVE,		RTL8192SU_2),
91*31d98677SRui Paulo 	RSU_DEV_HT(AZUREWAVE,		RTL8192SU_3),
92*31d98677SRui Paulo 	RSU_DEV_HT(AZUREWAVE,		RTL8192SU_5),
93*31d98677SRui Paulo 	RSU_DEV_HT(BELKIN,		RTL8192SU_1),
94*31d98677SRui Paulo 	RSU_DEV_HT(BELKIN,		RTL8192SU_2),
95*31d98677SRui Paulo 	RSU_DEV_HT(BELKIN,		RTL8192SU_3),
96*31d98677SRui Paulo 	RSU_DEV_HT(CONCEPTRONIC2,	RTL8192SU_1),
97*31d98677SRui Paulo 	RSU_DEV_HT(CONCEPTRONIC2,	RTL8192SU_2),
98*31d98677SRui Paulo 	RSU_DEV_HT(CONCEPTRONIC2,	RTL8192SU_3),
99*31d98677SRui Paulo 	RSU_DEV_HT(COREGA,		RTL8192SU),
100*31d98677SRui Paulo 	RSU_DEV_HT(DLINK2,		DWA131A1),
101*31d98677SRui Paulo 	RSU_DEV_HT(DLINK2,		RTL8192SU_1),
102*31d98677SRui Paulo 	RSU_DEV_HT(DLINK2,		RTL8192SU_2),
103*31d98677SRui Paulo 	RSU_DEV_HT(EDIMAX,		RTL8192SU_1),
104*31d98677SRui Paulo 	RSU_DEV_HT(EDIMAX,		RTL8192SU_2),
105*31d98677SRui Paulo 	RSU_DEV_HT(EDIMAX,		RTL8192SU_3),
106*31d98677SRui Paulo 	RSU_DEV_HT(GUILLEMOT,		HWGUN54),
107*31d98677SRui Paulo 	RSU_DEV_HT(GUILLEMOT,		HWNUM300),
108*31d98677SRui Paulo 	RSU_DEV_HT(HAWKING,		RTL8192SU_1),
109*31d98677SRui Paulo 	RSU_DEV_HT(HAWKING,		RTL8192SU_2),
110*31d98677SRui Paulo 	RSU_DEV_HT(PLANEX2,		GWUSNANO),
111*31d98677SRui Paulo 	RSU_DEV_HT(REALTEK,		RTL8171),
112*31d98677SRui Paulo 	RSU_DEV_HT(REALTEK,		RTL8172),
113*31d98677SRui Paulo 	RSU_DEV_HT(REALTEK,		RTL8173),
114*31d98677SRui Paulo 	RSU_DEV_HT(REALTEK,		RTL8174),
115*31d98677SRui Paulo 	RSU_DEV_HT(REALTEK,		RTL8192SU),
116*31d98677SRui Paulo 	RSU_DEV_HT(REALTEK,		RTL8712),
117*31d98677SRui Paulo 	RSU_DEV_HT(REALTEK,		RTL8713),
118*31d98677SRui Paulo 	RSU_DEV_HT(SENAO,		RTL8192SU_1),
119*31d98677SRui Paulo 	RSU_DEV_HT(SENAO,		RTL8192SU_2),
120*31d98677SRui Paulo 	RSU_DEV_HT(SITECOMEU,		WL349V1),
121*31d98677SRui Paulo 	RSU_DEV_HT(SITECOMEU,		WL353),
122*31d98677SRui Paulo 	RSU_DEV_HT(SWEEX2,		LW154),
123*31d98677SRui Paulo #undef RSU_DEV_HT
124*31d98677SRui Paulo #undef RSU_DEV
125*31d98677SRui Paulo };
126*31d98677SRui Paulo 
127*31d98677SRui Paulo static device_probe_t   rsu_match;
128*31d98677SRui Paulo static device_attach_t  rsu_attach;
129*31d98677SRui Paulo static device_detach_t  rsu_detach;
130*31d98677SRui Paulo static usb_callback_t   rsu_bulk_tx_callback;
131*31d98677SRui Paulo static usb_callback_t   rsu_bulk_rx_callback;
132*31d98677SRui Paulo static usb_error_t	rsu_do_request(struct rsu_softc *,
133*31d98677SRui Paulo 			    struct usb_device_request *, void *);
134*31d98677SRui Paulo static struct ieee80211vap *
135*31d98677SRui Paulo 		rsu_vap_create(struct ieee80211com *, const char name[],
136*31d98677SRui Paulo 		    int, enum ieee80211_opmode, int, const uint8_t bssid[],
137*31d98677SRui Paulo 		    const uint8_t mac[]);
138*31d98677SRui Paulo static void	rsu_vap_delete(struct ieee80211vap *);
139*31d98677SRui Paulo static void	rsu_scan_start(struct ieee80211com *);
140*31d98677SRui Paulo static void	rsu_scan_end(struct ieee80211com *);
141*31d98677SRui Paulo static void	rsu_set_channel(struct ieee80211com *);
142*31d98677SRui Paulo static void	rsu_update_mcast(struct ifnet *);
143*31d98677SRui Paulo static int	rsu_alloc_rx_list(struct rsu_softc *);
144*31d98677SRui Paulo static void	rsu_free_rx_list(struct rsu_softc *);
145*31d98677SRui Paulo static int	rsu_alloc_tx_list(struct rsu_softc *);
146*31d98677SRui Paulo static void	rsu_free_tx_list(struct rsu_softc *);
147*31d98677SRui Paulo static void	rsu_free_list(struct rsu_softc *, struct rsu_data [], int);
148*31d98677SRui Paulo static struct rsu_data *_rsu_getbuf(struct rsu_softc *);
149*31d98677SRui Paulo static struct rsu_data *rsu_getbuf(struct rsu_softc *);
150*31d98677SRui Paulo static int	rsu_write_region_1(struct rsu_softc *, uint16_t, uint8_t *,
151*31d98677SRui Paulo 		    int);
152*31d98677SRui Paulo static void	rsu_write_1(struct rsu_softc *, uint16_t, uint8_t);
153*31d98677SRui Paulo static void	rsu_write_2(struct rsu_softc *, uint16_t, uint16_t);
154*31d98677SRui Paulo static void	rsu_write_4(struct rsu_softc *, uint16_t, uint32_t);
155*31d98677SRui Paulo static int	rsu_read_region_1(struct rsu_softc *, uint16_t, uint8_t *,
156*31d98677SRui Paulo 		    int);
157*31d98677SRui Paulo static uint8_t	rsu_read_1(struct rsu_softc *, uint16_t);
158*31d98677SRui Paulo static uint16_t	rsu_read_2(struct rsu_softc *, uint16_t);
159*31d98677SRui Paulo static uint32_t	rsu_read_4(struct rsu_softc *, uint16_t);
160*31d98677SRui Paulo static int	rsu_fw_iocmd(struct rsu_softc *, uint32_t);
161*31d98677SRui Paulo static uint8_t	rsu_efuse_read_1(struct rsu_softc *, uint16_t);
162*31d98677SRui Paulo static int	rsu_read_rom(struct rsu_softc *);
163*31d98677SRui Paulo static int	rsu_fw_cmd(struct rsu_softc *, uint8_t, void *, int);
164*31d98677SRui Paulo static void	rsu_calib_task(void *, int);
165*31d98677SRui Paulo static int	rsu_newstate(struct ieee80211vap *, enum ieee80211_state, int);
166*31d98677SRui Paulo #ifdef notyet
167*31d98677SRui Paulo static void	rsu_set_key(struct rsu_softc *, const struct ieee80211_key *);
168*31d98677SRui Paulo static void	rsu_delete_key(struct rsu_softc *, const struct ieee80211_key *);
169*31d98677SRui Paulo #endif
170*31d98677SRui Paulo static int	rsu_site_survey(struct rsu_softc *, struct ieee80211vap *);
171*31d98677SRui Paulo static int	rsu_join_bss(struct rsu_softc *, struct ieee80211_node *);
172*31d98677SRui Paulo static int	rsu_disconnect(struct rsu_softc *);
173*31d98677SRui Paulo static void	rsu_event_survey(struct rsu_softc *, uint8_t *, int);
174*31d98677SRui Paulo static void	rsu_event_join_bss(struct rsu_softc *, uint8_t *, int);
175*31d98677SRui Paulo static void	rsu_rx_event(struct rsu_softc *, uint8_t, uint8_t *, int);
176*31d98677SRui Paulo static void	rsu_rx_multi_event(struct rsu_softc *, uint8_t *, int);
177*31d98677SRui Paulo static int8_t	rsu_get_rssi(struct rsu_softc *, int, void *);
178*31d98677SRui Paulo static struct mbuf *
179*31d98677SRui Paulo 		rsu_rx_frame(struct rsu_softc *, uint8_t *, int, int *);
180*31d98677SRui Paulo static struct mbuf *
181*31d98677SRui Paulo 		rsu_rx_multi_frame(struct rsu_softc *, uint8_t *, int, int *);
182*31d98677SRui Paulo static struct mbuf *
183*31d98677SRui Paulo 		rsu_rxeof(struct usb_xfer *, struct rsu_data *, int *);
184*31d98677SRui Paulo static void	rsu_txeof(struct usb_xfer *, struct rsu_data *);
185*31d98677SRui Paulo static int	rsu_raw_xmit(struct ieee80211_node *, struct mbuf *,
186*31d98677SRui Paulo 		    const struct ieee80211_bpf_params *);
187*31d98677SRui Paulo static void	rsu_init(void *);
188*31d98677SRui Paulo static void	rsu_init_locked(struct rsu_softc *);
189*31d98677SRui Paulo static void	rsu_watchdog(void *);
190*31d98677SRui Paulo static int	rsu_tx_start(struct rsu_softc *, struct ieee80211_node *,
191*31d98677SRui Paulo 		    struct mbuf *, struct rsu_data *);
192*31d98677SRui Paulo static void	rsu_start(struct ifnet *);
193*31d98677SRui Paulo static void	rsu_start_locked(struct ifnet *);
194*31d98677SRui Paulo static int	rsu_ioctl(struct ifnet *, u_long, caddr_t);
195*31d98677SRui Paulo static void	rsu_stop(struct ifnet *, int);
196*31d98677SRui Paulo static void	rsu_stop_locked(struct ifnet *, int);
197*31d98677SRui Paulo 
198*31d98677SRui Paulo static device_method_t rsu_methods[] = {
199*31d98677SRui Paulo 	DEVMETHOD(device_probe,		rsu_match),
200*31d98677SRui Paulo 	DEVMETHOD(device_attach,	rsu_attach),
201*31d98677SRui Paulo 	DEVMETHOD(device_detach,	rsu_detach),
202*31d98677SRui Paulo 
203*31d98677SRui Paulo 	DEVMETHOD_END
204*31d98677SRui Paulo };
205*31d98677SRui Paulo 
206*31d98677SRui Paulo static driver_t rsu_driver = {
207*31d98677SRui Paulo 	.name = "rsu",
208*31d98677SRui Paulo 	.methods = rsu_methods,
209*31d98677SRui Paulo 	.size = sizeof(struct rsu_softc)
210*31d98677SRui Paulo };
211*31d98677SRui Paulo 
212*31d98677SRui Paulo static devclass_t rsu_devclass;
213*31d98677SRui Paulo 
214*31d98677SRui Paulo DRIVER_MODULE(rsu, uhub, rsu_driver, rsu_devclass, NULL, 0);
215*31d98677SRui Paulo MODULE_DEPEND(rsu, wlan, 1, 1, 1);
216*31d98677SRui Paulo MODULE_DEPEND(rsu, usb, 1, 1, 1);
217*31d98677SRui Paulo MODULE_DEPEND(rsu, firmware, 1, 1, 1);
218*31d98677SRui Paulo MODULE_VERSION(rsu, 1);
219*31d98677SRui Paulo 
220*31d98677SRui Paulo static const struct usb_config rsu_config[RSU_N_TRANSFER] = {
221*31d98677SRui Paulo 	[RSU_BULK_RX] = {
222*31d98677SRui Paulo 		.type = UE_BULK,
223*31d98677SRui Paulo 		.endpoint = UE_ADDR_ANY,
224*31d98677SRui Paulo 		.direction = UE_DIR_IN,
225*31d98677SRui Paulo 		.bufsize = RSU_RXBUFSZ,
226*31d98677SRui Paulo 		.flags = {
227*31d98677SRui Paulo 			.pipe_bof = 1,
228*31d98677SRui Paulo 			.short_xfer_ok = 1
229*31d98677SRui Paulo 		},
230*31d98677SRui Paulo 		.callback = rsu_bulk_rx_callback
231*31d98677SRui Paulo 	},
232*31d98677SRui Paulo 	[RSU_BULK_TX_BE] = {
233*31d98677SRui Paulo 		.type = UE_BULK,
234*31d98677SRui Paulo 		.endpoint = 0x06,
235*31d98677SRui Paulo 		.direction = UE_DIR_OUT,
236*31d98677SRui Paulo 		.bufsize = RSU_TXBUFSZ,
237*31d98677SRui Paulo 		.flags = {
238*31d98677SRui Paulo 			.ext_buffer = 1,
239*31d98677SRui Paulo 			.pipe_bof = 1,
240*31d98677SRui Paulo 			.force_short_xfer = 1
241*31d98677SRui Paulo 		},
242*31d98677SRui Paulo 		.callback = rsu_bulk_tx_callback,
243*31d98677SRui Paulo 		.timeout = RSU_TX_TIMEOUT
244*31d98677SRui Paulo 	},
245*31d98677SRui Paulo 	[RSU_BULK_TX_BK] = {
246*31d98677SRui Paulo 		.type = UE_BULK,
247*31d98677SRui Paulo 		.endpoint = 0x06,
248*31d98677SRui Paulo 		.direction = UE_DIR_OUT,
249*31d98677SRui Paulo 		.bufsize = RSU_TXBUFSZ,
250*31d98677SRui Paulo 		.flags = {
251*31d98677SRui Paulo 			.ext_buffer = 1,
252*31d98677SRui Paulo 			.pipe_bof = 1,
253*31d98677SRui Paulo 			.force_short_xfer = 1
254*31d98677SRui Paulo 		},
255*31d98677SRui Paulo 		.callback = rsu_bulk_tx_callback,
256*31d98677SRui Paulo 		.timeout = RSU_TX_TIMEOUT
257*31d98677SRui Paulo 	},
258*31d98677SRui Paulo 	[RSU_BULK_TX_VI] = {
259*31d98677SRui Paulo 		.type = UE_BULK,
260*31d98677SRui Paulo 		.endpoint = 0x04,
261*31d98677SRui Paulo 		.direction = UE_DIR_OUT,
262*31d98677SRui Paulo 		.bufsize = RSU_TXBUFSZ,
263*31d98677SRui Paulo 		.flags = {
264*31d98677SRui Paulo 			.ext_buffer = 1,
265*31d98677SRui Paulo 			.pipe_bof = 1,
266*31d98677SRui Paulo 			.force_short_xfer = 1
267*31d98677SRui Paulo 		},
268*31d98677SRui Paulo 		.callback = rsu_bulk_tx_callback,
269*31d98677SRui Paulo 		.timeout = RSU_TX_TIMEOUT
270*31d98677SRui Paulo 	},
271*31d98677SRui Paulo 	[RSU_BULK_TX_VO] = {
272*31d98677SRui Paulo 		.type = UE_BULK,
273*31d98677SRui Paulo 		.endpoint = 0x04,
274*31d98677SRui Paulo 		.direction = UE_DIR_OUT,
275*31d98677SRui Paulo 		.bufsize = RSU_TXBUFSZ,
276*31d98677SRui Paulo 		.flags = {
277*31d98677SRui Paulo 			.ext_buffer = 1,
278*31d98677SRui Paulo 			.pipe_bof = 1,
279*31d98677SRui Paulo 			.force_short_xfer = 1
280*31d98677SRui Paulo 		},
281*31d98677SRui Paulo 		.callback = rsu_bulk_tx_callback,
282*31d98677SRui Paulo 		.timeout = RSU_TX_TIMEOUT
283*31d98677SRui Paulo 	},
284*31d98677SRui Paulo };
285*31d98677SRui Paulo 
286*31d98677SRui Paulo static int
287*31d98677SRui Paulo rsu_match(device_t self)
288*31d98677SRui Paulo {
289*31d98677SRui Paulo 	struct usb_attach_arg *uaa = device_get_ivars(self);
290*31d98677SRui Paulo 
291*31d98677SRui Paulo 	if (uaa->usb_mode != USB_MODE_HOST ||
292*31d98677SRui Paulo 	    uaa->info.bIfaceIndex != 0 ||
293*31d98677SRui Paulo 	    uaa->info.bConfigIndex != 0)
294*31d98677SRui Paulo 		return (ENXIO);
295*31d98677SRui Paulo 
296*31d98677SRui Paulo 	return (usbd_lookup_id_by_uaa(rsu_devs, sizeof(rsu_devs), uaa));
297*31d98677SRui Paulo }
298*31d98677SRui Paulo 
299*31d98677SRui Paulo static int
300*31d98677SRui Paulo rsu_attach(device_t self)
301*31d98677SRui Paulo {
302*31d98677SRui Paulo 	struct usb_attach_arg *uaa = device_get_ivars(self);
303*31d98677SRui Paulo 	struct rsu_softc *sc = device_get_softc(self);
304*31d98677SRui Paulo 	struct ifnet *ifp;
305*31d98677SRui Paulo 	struct ieee80211com *ic;
306*31d98677SRui Paulo 	int error;
307*31d98677SRui Paulo 	uint8_t iface_index, bands;
308*31d98677SRui Paulo 
309*31d98677SRui Paulo 	device_set_usb_desc(self);
310*31d98677SRui Paulo 	sc->sc_udev = uaa->device;
311*31d98677SRui Paulo 	sc->sc_dev = self;
312*31d98677SRui Paulo 
313*31d98677SRui Paulo 	mtx_init(&sc->sc_mtx, device_get_nameunit(self), MTX_NETWORK_LOCK,
314*31d98677SRui Paulo 	    MTX_DEF);
315*31d98677SRui Paulo 	TIMEOUT_TASK_INIT(taskqueue_thread, &sc->calib_task, 0,
316*31d98677SRui Paulo 	    rsu_calib_task, sc);
317*31d98677SRui Paulo 	callout_init(&sc->sc_watchdog_ch, 0);
318*31d98677SRui Paulo 
319*31d98677SRui Paulo 	iface_index = 0;
320*31d98677SRui Paulo 	error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer,
321*31d98677SRui Paulo 	    rsu_config, RSU_N_TRANSFER, sc, &sc->sc_mtx);
322*31d98677SRui Paulo 	if (error) {
323*31d98677SRui Paulo 		device_printf(sc->sc_dev,
324*31d98677SRui Paulo 		    "could not allocate USB transfers, err=%s\n",
325*31d98677SRui Paulo 		    usbd_errstr(error));
326*31d98677SRui Paulo 		goto detach;
327*31d98677SRui Paulo 	}
328*31d98677SRui Paulo 	RSU_LOCK(sc);
329*31d98677SRui Paulo 	/* Read chip revision. */
330*31d98677SRui Paulo 	sc->cut = MS(rsu_read_4(sc, R92S_PMC_FSM), R92S_PMC_FSM_CUT);
331*31d98677SRui Paulo 	if (sc->cut != 3)
332*31d98677SRui Paulo 		sc->cut = (sc->cut >> 1) + 1;
333*31d98677SRui Paulo 	error = rsu_read_rom(sc);
334*31d98677SRui Paulo 	if (error != 0) {
335*31d98677SRui Paulo 		device_printf(self, "could not read ROM\n");
336*31d98677SRui Paulo 		goto detach;
337*31d98677SRui Paulo 	}
338*31d98677SRui Paulo 	RSU_UNLOCK(sc);
339*31d98677SRui Paulo 	IEEE80211_ADDR_COPY(sc->sc_bssid, &sc->rom[0x12]);
340*31d98677SRui Paulo 	device_printf(self, "MAC/BB RTL8712 cut %d\n", sc->cut);
341*31d98677SRui Paulo 	ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211);
342*31d98677SRui Paulo 	if (ifp == NULL) {
343*31d98677SRui Paulo 		device_printf(self, "cannot allocate interface\n");
344*31d98677SRui Paulo 		goto detach;
345*31d98677SRui Paulo 	}
346*31d98677SRui Paulo 	ic = ifp->if_l2com;
347*31d98677SRui Paulo 	ifp->if_softc = sc;
348*31d98677SRui Paulo 	if_initname(ifp, "rsu", device_get_unit(self));
349*31d98677SRui Paulo 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
350*31d98677SRui Paulo 	ifp->if_init = rsu_init;
351*31d98677SRui Paulo 	ifp->if_ioctl = rsu_ioctl;
352*31d98677SRui Paulo 	ifp->if_start = rsu_start;
353*31d98677SRui Paulo 	IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen);
354*31d98677SRui Paulo 	ifp->if_snd.ifq_drv_maxlen = ifqmaxlen;
355*31d98677SRui Paulo 	IFQ_SET_READY(&ifp->if_snd);
356*31d98677SRui Paulo 	ifp->if_capabilities |= IFCAP_RXCSUM;
357*31d98677SRui Paulo 	ifp->if_capenable |= IFCAP_RXCSUM;
358*31d98677SRui Paulo 	ifp->if_hwassist = CSUM_TCP;
359*31d98677SRui Paulo 
360*31d98677SRui Paulo 	ic->ic_ifp = ifp;
361*31d98677SRui Paulo 	ic->ic_phytype = IEEE80211_T_OFDM;	/* Not only, but not used. */
362*31d98677SRui Paulo 	ic->ic_opmode = IEEE80211_M_STA;	/* Default to BSS mode. */
363*31d98677SRui Paulo 
364*31d98677SRui Paulo 	/* Set device capabilities. */
365*31d98677SRui Paulo 	ic->ic_caps =
366*31d98677SRui Paulo 	    IEEE80211_C_STA |		/* station mode */
367*31d98677SRui Paulo 	    IEEE80211_C_BGSCAN |	/* Background scan. */
368*31d98677SRui Paulo 	    IEEE80211_C_SHPREAMBLE |	/* Short preamble supported. */
369*31d98677SRui Paulo 	    IEEE80211_C_SHSLOT |	/* Short slot time supported. */
370*31d98677SRui Paulo 	    IEEE80211_C_WPA;		/* WPA/RSN. */
371*31d98677SRui Paulo 
372*31d98677SRui Paulo #if 0
373*31d98677SRui Paulo 	/* Check if HT support is present. */
374*31d98677SRui Paulo 	if (usb_lookup(rsu_devs_noht, uaa->vendor, uaa->product) == NULL) {
375*31d98677SRui Paulo 		/* Set HT capabilities. */
376*31d98677SRui Paulo 		ic->ic_htcaps =
377*31d98677SRui Paulo 		    IEEE80211_HTCAP_CBW20_40 |
378*31d98677SRui Paulo 		    IEEE80211_HTCAP_DSSSCCK40;
379*31d98677SRui Paulo 		/* Set supported HT rates. */
380*31d98677SRui Paulo 		for (i = 0; i < 2; i++)
381*31d98677SRui Paulo 			ic->ic_sup_mcs[i] = 0xff;
382*31d98677SRui Paulo 	}
383*31d98677SRui Paulo #endif
384*31d98677SRui Paulo 
385*31d98677SRui Paulo 	/* Set supported .11b and .11g rates. */
386*31d98677SRui Paulo 	bands = 0;
387*31d98677SRui Paulo 	setbit(&bands, IEEE80211_MODE_11B);
388*31d98677SRui Paulo 	setbit(&bands, IEEE80211_MODE_11G);
389*31d98677SRui Paulo 	ieee80211_init_channels(ic, NULL, &bands);
390*31d98677SRui Paulo 
391*31d98677SRui Paulo 	ieee80211_ifattach(ic, sc->sc_bssid);
392*31d98677SRui Paulo 	ic->ic_raw_xmit = rsu_raw_xmit;
393*31d98677SRui Paulo 	ic->ic_scan_start = rsu_scan_start;
394*31d98677SRui Paulo 	ic->ic_scan_end = rsu_scan_end;
395*31d98677SRui Paulo 	ic->ic_set_channel = rsu_set_channel;
396*31d98677SRui Paulo 	ic->ic_vap_create = rsu_vap_create;
397*31d98677SRui Paulo 	ic->ic_vap_delete = rsu_vap_delete;
398*31d98677SRui Paulo 	ic->ic_update_mcast = rsu_update_mcast;
399*31d98677SRui Paulo 
400*31d98677SRui Paulo 	ieee80211_radiotap_attach(ic, &sc->sc_txtap.wt_ihdr,
401*31d98677SRui Paulo 	    sizeof(sc->sc_txtap), RSU_TX_RADIOTAP_PRESENT,
402*31d98677SRui Paulo 	    &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap),
403*31d98677SRui Paulo 	    RSU_RX_RADIOTAP_PRESENT);
404*31d98677SRui Paulo 
405*31d98677SRui Paulo 	if (bootverbose)
406*31d98677SRui Paulo 		ieee80211_announce(ic);
407*31d98677SRui Paulo 
408*31d98677SRui Paulo 	return (0);
409*31d98677SRui Paulo 
410*31d98677SRui Paulo detach:
411*31d98677SRui Paulo 	rsu_detach(self);
412*31d98677SRui Paulo 	return (ENXIO);
413*31d98677SRui Paulo }
414*31d98677SRui Paulo 
415*31d98677SRui Paulo static int
416*31d98677SRui Paulo rsu_detach(device_t self)
417*31d98677SRui Paulo {
418*31d98677SRui Paulo 	struct rsu_softc *sc = device_get_softc(self);
419*31d98677SRui Paulo 	struct ifnet *ifp = sc->sc_ifp;
420*31d98677SRui Paulo 	struct ieee80211com *ic = ifp->if_l2com;
421*31d98677SRui Paulo 
422*31d98677SRui Paulo 	if (!device_is_attached(self))
423*31d98677SRui Paulo 		return (0);
424*31d98677SRui Paulo 	rsu_stop(ifp, 1);
425*31d98677SRui Paulo 	usbd_transfer_unsetup(sc->sc_xfer, RSU_N_TRANSFER);
426*31d98677SRui Paulo 	ieee80211_ifdetach(ic);
427*31d98677SRui Paulo 
428*31d98677SRui Paulo 	callout_drain(&sc->sc_watchdog_ch);
429*31d98677SRui Paulo 	taskqueue_drain_timeout(taskqueue_thread, &sc->calib_task);
430*31d98677SRui Paulo 
431*31d98677SRui Paulo 	/* Free Tx/Rx buffers. */
432*31d98677SRui Paulo 	rsu_free_tx_list(sc);
433*31d98677SRui Paulo 	rsu_free_rx_list(sc);
434*31d98677SRui Paulo 
435*31d98677SRui Paulo 	if_free(ifp);
436*31d98677SRui Paulo 	mtx_destroy(&sc->sc_mtx);
437*31d98677SRui Paulo 
438*31d98677SRui Paulo 	return (0);
439*31d98677SRui Paulo }
440*31d98677SRui Paulo 
441*31d98677SRui Paulo static usb_error_t
442*31d98677SRui Paulo rsu_do_request(struct rsu_softc *sc, struct usb_device_request *req,
443*31d98677SRui Paulo     void *data)
444*31d98677SRui Paulo {
445*31d98677SRui Paulo 	usb_error_t err;
446*31d98677SRui Paulo 	int ntries = 10;
447*31d98677SRui Paulo 
448*31d98677SRui Paulo 	RSU_ASSERT_LOCKED(sc);
449*31d98677SRui Paulo 
450*31d98677SRui Paulo 	while (ntries--) {
451*31d98677SRui Paulo 		err = usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx,
452*31d98677SRui Paulo 		    req, data, 0, NULL, 250 /* ms */);
453*31d98677SRui Paulo 		if (err == 0 || !device_is_attached(sc->sc_dev))
454*31d98677SRui Paulo 			break;
455*31d98677SRui Paulo 		DPRINTFN(1, "Control request failed, %s (retrying)\n",
456*31d98677SRui Paulo 		    usbd_errstr(err));
457*31d98677SRui Paulo 		usb_pause_mtx(&sc->sc_mtx, hz / 100);
458*31d98677SRui Paulo         }
459*31d98677SRui Paulo 
460*31d98677SRui Paulo         return (err);
461*31d98677SRui Paulo }
462*31d98677SRui Paulo 
463*31d98677SRui Paulo static struct ieee80211vap *
464*31d98677SRui Paulo rsu_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit,
465*31d98677SRui Paulo     enum ieee80211_opmode opmode, int flags,
466*31d98677SRui Paulo     const uint8_t bssid[IEEE80211_ADDR_LEN],
467*31d98677SRui Paulo     const uint8_t mac[IEEE80211_ADDR_LEN])
468*31d98677SRui Paulo {
469*31d98677SRui Paulo 	struct rsu_vap *uvp;
470*31d98677SRui Paulo 	struct ieee80211vap *vap;
471*31d98677SRui Paulo 
472*31d98677SRui Paulo 	if (!TAILQ_EMPTY(&ic->ic_vaps))         /* only one at a time */
473*31d98677SRui Paulo 		return (NULL);
474*31d98677SRui Paulo 
475*31d98677SRui Paulo 	uvp = (struct rsu_vap *) malloc(sizeof(struct rsu_vap),
476*31d98677SRui Paulo 	    M_80211_VAP, M_NOWAIT | M_ZERO);
477*31d98677SRui Paulo 	if (uvp == NULL)
478*31d98677SRui Paulo 		return (NULL);
479*31d98677SRui Paulo 	vap = &uvp->vap;
480*31d98677SRui Paulo 	ieee80211_vap_setup(ic, vap, name, unit, opmode,
481*31d98677SRui Paulo 	    flags, bssid, mac);
482*31d98677SRui Paulo 
483*31d98677SRui Paulo 	/* override state transition machine */
484*31d98677SRui Paulo 	uvp->newstate = vap->iv_newstate;
485*31d98677SRui Paulo 	vap->iv_newstate = rsu_newstate;
486*31d98677SRui Paulo 
487*31d98677SRui Paulo 	/* complete setup */
488*31d98677SRui Paulo 	ieee80211_vap_attach(vap, ieee80211_media_change,
489*31d98677SRui Paulo 	    ieee80211_media_status);
490*31d98677SRui Paulo 	ic->ic_opmode = opmode;
491*31d98677SRui Paulo 
492*31d98677SRui Paulo 	return (vap);
493*31d98677SRui Paulo }
494*31d98677SRui Paulo 
495*31d98677SRui Paulo static void
496*31d98677SRui Paulo rsu_vap_delete(struct ieee80211vap *vap)
497*31d98677SRui Paulo {
498*31d98677SRui Paulo 	struct rsu_vap *uvp = RSU_VAP(vap);
499*31d98677SRui Paulo 
500*31d98677SRui Paulo 	ieee80211_vap_detach(vap);
501*31d98677SRui Paulo 	free(uvp, M_80211_VAP);
502*31d98677SRui Paulo }
503*31d98677SRui Paulo 
504*31d98677SRui Paulo static void
505*31d98677SRui Paulo rsu_scan_start(struct ieee80211com *ic)
506*31d98677SRui Paulo {
507*31d98677SRui Paulo 	int error;
508*31d98677SRui Paulo 	struct ifnet *ifp = ic->ic_ifp;
509*31d98677SRui Paulo 	struct rsu_softc *sc = ifp->if_softc;
510*31d98677SRui Paulo 
511*31d98677SRui Paulo 	/* Scanning is done by the firmware. */
512*31d98677SRui Paulo 	RSU_LOCK(sc);
513*31d98677SRui Paulo 	error = rsu_site_survey(sc, TAILQ_FIRST(&ic->ic_vaps));
514*31d98677SRui Paulo 	RSU_UNLOCK(sc);
515*31d98677SRui Paulo 	if (error != 0)
516*31d98677SRui Paulo 		device_printf(sc->sc_dev,
517*31d98677SRui Paulo 		    "could not send site survey command\n");
518*31d98677SRui Paulo }
519*31d98677SRui Paulo 
520*31d98677SRui Paulo static void
521*31d98677SRui Paulo rsu_scan_end(struct ieee80211com *ic)
522*31d98677SRui Paulo {
523*31d98677SRui Paulo 	/* Nothing to do here. */
524*31d98677SRui Paulo }
525*31d98677SRui Paulo 
526*31d98677SRui Paulo static void
527*31d98677SRui Paulo rsu_set_channel(struct ieee80211com *ic __unused)
528*31d98677SRui Paulo {
529*31d98677SRui Paulo 	/* We are unable to switch channels, yet. */
530*31d98677SRui Paulo }
531*31d98677SRui Paulo 
532*31d98677SRui Paulo static void
533*31d98677SRui Paulo rsu_update_mcast(struct ifnet *ifp)
534*31d98677SRui Paulo {
535*31d98677SRui Paulo         /* XXX do nothing?  */
536*31d98677SRui Paulo }
537*31d98677SRui Paulo 
538*31d98677SRui Paulo static int
539*31d98677SRui Paulo rsu_alloc_list(struct rsu_softc *sc, struct rsu_data data[],
540*31d98677SRui Paulo     int ndata, int maxsz)
541*31d98677SRui Paulo {
542*31d98677SRui Paulo 	int i, error;
543*31d98677SRui Paulo 
544*31d98677SRui Paulo 	for (i = 0; i < ndata; i++) {
545*31d98677SRui Paulo 		struct rsu_data *dp = &data[i];
546*31d98677SRui Paulo 		dp->sc = sc;
547*31d98677SRui Paulo 		dp->m = NULL;
548*31d98677SRui Paulo 		dp->buf = malloc(maxsz, M_USBDEV, M_NOWAIT);
549*31d98677SRui Paulo 		if (dp->buf == NULL) {
550*31d98677SRui Paulo 			device_printf(sc->sc_dev,
551*31d98677SRui Paulo 			    "could not allocate buffer\n");
552*31d98677SRui Paulo 			error = ENOMEM;
553*31d98677SRui Paulo 			goto fail;
554*31d98677SRui Paulo 		}
555*31d98677SRui Paulo 		dp->ni = NULL;
556*31d98677SRui Paulo 	}
557*31d98677SRui Paulo 
558*31d98677SRui Paulo 	return (0);
559*31d98677SRui Paulo fail:
560*31d98677SRui Paulo 	rsu_free_list(sc, data, ndata);
561*31d98677SRui Paulo 	return (error);
562*31d98677SRui Paulo }
563*31d98677SRui Paulo 
564*31d98677SRui Paulo static int
565*31d98677SRui Paulo rsu_alloc_rx_list(struct rsu_softc *sc)
566*31d98677SRui Paulo {
567*31d98677SRui Paulo         int error, i;
568*31d98677SRui Paulo 
569*31d98677SRui Paulo 	error = rsu_alloc_list(sc, sc->sc_rx, RSU_RX_LIST_COUNT,
570*31d98677SRui Paulo 	    RSU_RXBUFSZ);
571*31d98677SRui Paulo 	if (error != 0)
572*31d98677SRui Paulo 		return (error);
573*31d98677SRui Paulo 
574*31d98677SRui Paulo 	STAILQ_INIT(&sc->sc_rx_active);
575*31d98677SRui Paulo 	STAILQ_INIT(&sc->sc_rx_inactive);
576*31d98677SRui Paulo 
577*31d98677SRui Paulo 	for (i = 0; i < RSU_RX_LIST_COUNT; i++)
578*31d98677SRui Paulo 		STAILQ_INSERT_HEAD(&sc->sc_rx_inactive, &sc->sc_rx[i], next);
579*31d98677SRui Paulo 
580*31d98677SRui Paulo 	return (0);
581*31d98677SRui Paulo }
582*31d98677SRui Paulo 
583*31d98677SRui Paulo static int
584*31d98677SRui Paulo rsu_alloc_tx_list(struct rsu_softc *sc)
585*31d98677SRui Paulo {
586*31d98677SRui Paulo 	int error, i;
587*31d98677SRui Paulo 
588*31d98677SRui Paulo 	error = rsu_alloc_list(sc, sc->sc_tx, RSU_TX_LIST_COUNT,
589*31d98677SRui Paulo 	    RSU_TXBUFSZ);
590*31d98677SRui Paulo 	if (error != 0)
591*31d98677SRui Paulo 		return (error);
592*31d98677SRui Paulo 
593*31d98677SRui Paulo 	STAILQ_INIT(&sc->sc_tx_active);
594*31d98677SRui Paulo 	STAILQ_INIT(&sc->sc_tx_inactive);
595*31d98677SRui Paulo 	STAILQ_INIT(&sc->sc_tx_pending);
596*31d98677SRui Paulo 
597*31d98677SRui Paulo 	for (i = 0; i < RSU_TX_LIST_COUNT; i++) {
598*31d98677SRui Paulo 		STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, &sc->sc_tx[i], next);
599*31d98677SRui Paulo 	}
600*31d98677SRui Paulo 
601*31d98677SRui Paulo 	return (0);
602*31d98677SRui Paulo }
603*31d98677SRui Paulo 
604*31d98677SRui Paulo static void
605*31d98677SRui Paulo rsu_free_tx_list(struct rsu_softc *sc)
606*31d98677SRui Paulo {
607*31d98677SRui Paulo 	rsu_free_list(sc, sc->sc_tx, RSU_TX_LIST_COUNT);
608*31d98677SRui Paulo }
609*31d98677SRui Paulo 
610*31d98677SRui Paulo static void
611*31d98677SRui Paulo rsu_free_rx_list(struct rsu_softc *sc)
612*31d98677SRui Paulo {
613*31d98677SRui Paulo 	rsu_free_list(sc, sc->sc_rx, RSU_RX_LIST_COUNT);
614*31d98677SRui Paulo }
615*31d98677SRui Paulo 
616*31d98677SRui Paulo static void
617*31d98677SRui Paulo rsu_free_list(struct rsu_softc *sc, struct rsu_data data[], int ndata)
618*31d98677SRui Paulo {
619*31d98677SRui Paulo 	int i;
620*31d98677SRui Paulo 
621*31d98677SRui Paulo 	for (i = 0; i < ndata; i++) {
622*31d98677SRui Paulo 		struct rsu_data *dp = &data[i];
623*31d98677SRui Paulo 
624*31d98677SRui Paulo 		if (dp->buf != NULL) {
625*31d98677SRui Paulo 			free(dp->buf, M_USBDEV);
626*31d98677SRui Paulo 			dp->buf = NULL;
627*31d98677SRui Paulo 		}
628*31d98677SRui Paulo 		if (dp->ni != NULL) {
629*31d98677SRui Paulo 			ieee80211_free_node(dp->ni);
630*31d98677SRui Paulo 			dp->ni = NULL;
631*31d98677SRui Paulo 		}
632*31d98677SRui Paulo 	}
633*31d98677SRui Paulo }
634*31d98677SRui Paulo 
635*31d98677SRui Paulo static struct rsu_data *
636*31d98677SRui Paulo _rsu_getbuf(struct rsu_softc *sc)
637*31d98677SRui Paulo {
638*31d98677SRui Paulo 	struct rsu_data *bf;
639*31d98677SRui Paulo 
640*31d98677SRui Paulo 	bf = STAILQ_FIRST(&sc->sc_tx_inactive);
641*31d98677SRui Paulo 	if (bf != NULL)
642*31d98677SRui Paulo 		STAILQ_REMOVE_HEAD(&sc->sc_tx_inactive, next);
643*31d98677SRui Paulo 	else
644*31d98677SRui Paulo 		bf = NULL;
645*31d98677SRui Paulo 	if (bf == NULL)
646*31d98677SRui Paulo 		DPRINTF("out of xmit buffers\n");
647*31d98677SRui Paulo         return (bf);
648*31d98677SRui Paulo }
649*31d98677SRui Paulo 
650*31d98677SRui Paulo static struct rsu_data *
651*31d98677SRui Paulo rsu_getbuf(struct rsu_softc *sc)
652*31d98677SRui Paulo {
653*31d98677SRui Paulo 	struct rsu_data *bf;
654*31d98677SRui Paulo 
655*31d98677SRui Paulo 	RSU_ASSERT_LOCKED(sc);
656*31d98677SRui Paulo 
657*31d98677SRui Paulo 	bf = _rsu_getbuf(sc);
658*31d98677SRui Paulo 	if (bf == NULL) {
659*31d98677SRui Paulo 		struct ifnet *ifp = sc->sc_ifp;
660*31d98677SRui Paulo 		DPRINTF("stop queue\n");
661*31d98677SRui Paulo 		ifp->if_drv_flags |= IFF_DRV_OACTIVE;
662*31d98677SRui Paulo 	}
663*31d98677SRui Paulo 	return (bf);
664*31d98677SRui Paulo }
665*31d98677SRui Paulo 
666*31d98677SRui Paulo static int
667*31d98677SRui Paulo rsu_write_region_1(struct rsu_softc *sc, uint16_t addr, uint8_t *buf,
668*31d98677SRui Paulo     int len)
669*31d98677SRui Paulo {
670*31d98677SRui Paulo 	usb_device_request_t req;
671*31d98677SRui Paulo 
672*31d98677SRui Paulo 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
673*31d98677SRui Paulo 	req.bRequest = R92S_REQ_REGS;
674*31d98677SRui Paulo 	USETW(req.wValue, addr);
675*31d98677SRui Paulo 	USETW(req.wIndex, 0);
676*31d98677SRui Paulo 	USETW(req.wLength, len);
677*31d98677SRui Paulo 
678*31d98677SRui Paulo 	return (rsu_do_request(sc, &req, buf));
679*31d98677SRui Paulo }
680*31d98677SRui Paulo 
681*31d98677SRui Paulo static void
682*31d98677SRui Paulo rsu_write_1(struct rsu_softc *sc, uint16_t addr, uint8_t val)
683*31d98677SRui Paulo {
684*31d98677SRui Paulo 	rsu_write_region_1(sc, addr, &val, 1);
685*31d98677SRui Paulo }
686*31d98677SRui Paulo 
687*31d98677SRui Paulo static void
688*31d98677SRui Paulo rsu_write_2(struct rsu_softc *sc, uint16_t addr, uint16_t val)
689*31d98677SRui Paulo {
690*31d98677SRui Paulo 	val = htole16(val);
691*31d98677SRui Paulo 	rsu_write_region_1(sc, addr, (uint8_t *)&val, 2);
692*31d98677SRui Paulo }
693*31d98677SRui Paulo 
694*31d98677SRui Paulo static void
695*31d98677SRui Paulo rsu_write_4(struct rsu_softc *sc, uint16_t addr, uint32_t val)
696*31d98677SRui Paulo {
697*31d98677SRui Paulo 	val = htole32(val);
698*31d98677SRui Paulo 	rsu_write_region_1(sc, addr, (uint8_t *)&val, 4);
699*31d98677SRui Paulo }
700*31d98677SRui Paulo 
701*31d98677SRui Paulo static int
702*31d98677SRui Paulo rsu_read_region_1(struct rsu_softc *sc, uint16_t addr, uint8_t *buf,
703*31d98677SRui Paulo     int len)
704*31d98677SRui Paulo {
705*31d98677SRui Paulo 	usb_device_request_t req;
706*31d98677SRui Paulo 
707*31d98677SRui Paulo 	req.bmRequestType = UT_READ_VENDOR_DEVICE;
708*31d98677SRui Paulo 	req.bRequest = R92S_REQ_REGS;
709*31d98677SRui Paulo 	USETW(req.wValue, addr);
710*31d98677SRui Paulo 	USETW(req.wIndex, 0);
711*31d98677SRui Paulo 	USETW(req.wLength, len);
712*31d98677SRui Paulo 
713*31d98677SRui Paulo 	return (rsu_do_request(sc, &req, buf));
714*31d98677SRui Paulo }
715*31d98677SRui Paulo 
716*31d98677SRui Paulo static uint8_t
717*31d98677SRui Paulo rsu_read_1(struct rsu_softc *sc, uint16_t addr)
718*31d98677SRui Paulo {
719*31d98677SRui Paulo 	uint8_t val;
720*31d98677SRui Paulo 
721*31d98677SRui Paulo 	if (rsu_read_region_1(sc, addr, &val, 1) != 0)
722*31d98677SRui Paulo 		return (0xff);
723*31d98677SRui Paulo 	return (val);
724*31d98677SRui Paulo }
725*31d98677SRui Paulo 
726*31d98677SRui Paulo static uint16_t
727*31d98677SRui Paulo rsu_read_2(struct rsu_softc *sc, uint16_t addr)
728*31d98677SRui Paulo {
729*31d98677SRui Paulo 	uint16_t val;
730*31d98677SRui Paulo 
731*31d98677SRui Paulo 	if (rsu_read_region_1(sc, addr, (uint8_t *)&val, 2) != 0)
732*31d98677SRui Paulo 		return (0xffff);
733*31d98677SRui Paulo 	return (le16toh(val));
734*31d98677SRui Paulo }
735*31d98677SRui Paulo 
736*31d98677SRui Paulo static uint32_t
737*31d98677SRui Paulo rsu_read_4(struct rsu_softc *sc, uint16_t addr)
738*31d98677SRui Paulo {
739*31d98677SRui Paulo 	uint32_t val;
740*31d98677SRui Paulo 
741*31d98677SRui Paulo 	if (rsu_read_region_1(sc, addr, (uint8_t *)&val, 4) != 0)
742*31d98677SRui Paulo 		return (0xffffffff);
743*31d98677SRui Paulo 	return (le32toh(val));
744*31d98677SRui Paulo }
745*31d98677SRui Paulo 
746*31d98677SRui Paulo static int
747*31d98677SRui Paulo rsu_fw_iocmd(struct rsu_softc *sc, uint32_t iocmd)
748*31d98677SRui Paulo {
749*31d98677SRui Paulo 	int ntries;
750*31d98677SRui Paulo 
751*31d98677SRui Paulo 	rsu_write_4(sc, R92S_IOCMD_CTRL, iocmd);
752*31d98677SRui Paulo 	DELAY(100);
753*31d98677SRui Paulo 	for (ntries = 0; ntries < 50; ntries++) {
754*31d98677SRui Paulo 		if (rsu_read_4(sc, R92S_IOCMD_CTRL) == 0)
755*31d98677SRui Paulo 			return (0);
756*31d98677SRui Paulo 		DELAY(10);
757*31d98677SRui Paulo 	}
758*31d98677SRui Paulo 	return (ETIMEDOUT);
759*31d98677SRui Paulo }
760*31d98677SRui Paulo 
761*31d98677SRui Paulo static uint8_t
762*31d98677SRui Paulo rsu_efuse_read_1(struct rsu_softc *sc, uint16_t addr)
763*31d98677SRui Paulo {
764*31d98677SRui Paulo 	uint32_t reg;
765*31d98677SRui Paulo 	int ntries;
766*31d98677SRui Paulo 
767*31d98677SRui Paulo 	reg = rsu_read_4(sc, R92S_EFUSE_CTRL);
768*31d98677SRui Paulo 	reg = RW(reg, R92S_EFUSE_CTRL_ADDR, addr);
769*31d98677SRui Paulo 	reg &= ~R92S_EFUSE_CTRL_VALID;
770*31d98677SRui Paulo 	rsu_write_4(sc, R92S_EFUSE_CTRL, reg);
771*31d98677SRui Paulo 	/* Wait for read operation to complete. */
772*31d98677SRui Paulo 	for (ntries = 0; ntries < 100; ntries++) {
773*31d98677SRui Paulo 		reg = rsu_read_4(sc, R92S_EFUSE_CTRL);
774*31d98677SRui Paulo 		if (reg & R92S_EFUSE_CTRL_VALID)
775*31d98677SRui Paulo 			return (MS(reg, R92S_EFUSE_CTRL_DATA));
776*31d98677SRui Paulo 		DELAY(5);
777*31d98677SRui Paulo 	}
778*31d98677SRui Paulo 	device_printf(sc->sc_dev,
779*31d98677SRui Paulo 	    "could not read efuse byte at address 0x%x\n", addr);
780*31d98677SRui Paulo 	return (0xff);
781*31d98677SRui Paulo }
782*31d98677SRui Paulo 
783*31d98677SRui Paulo static int
784*31d98677SRui Paulo rsu_read_rom(struct rsu_softc *sc)
785*31d98677SRui Paulo {
786*31d98677SRui Paulo 	uint8_t *rom = sc->rom;
787*31d98677SRui Paulo 	uint16_t addr = 0;
788*31d98677SRui Paulo 	uint32_t reg;
789*31d98677SRui Paulo 	uint8_t off, msk;
790*31d98677SRui Paulo 	int i;
791*31d98677SRui Paulo 
792*31d98677SRui Paulo 	/* Make sure that ROM type is eFuse and that autoload succeeded. */
793*31d98677SRui Paulo 	reg = rsu_read_1(sc, R92S_EE_9346CR);
794*31d98677SRui Paulo 	if ((reg & (R92S_9356SEL | R92S_EEPROM_EN)) != R92S_EEPROM_EN)
795*31d98677SRui Paulo 		return (EIO);
796*31d98677SRui Paulo 
797*31d98677SRui Paulo 	/* Turn on 2.5V to prevent eFuse leakage. */
798*31d98677SRui Paulo 	reg = rsu_read_1(sc, R92S_EFUSE_TEST + 3);
799*31d98677SRui Paulo 	rsu_write_1(sc, R92S_EFUSE_TEST + 3, reg | 0x80);
800*31d98677SRui Paulo 	DELAY(1000);
801*31d98677SRui Paulo 	rsu_write_1(sc, R92S_EFUSE_TEST + 3, reg & ~0x80);
802*31d98677SRui Paulo 
803*31d98677SRui Paulo 	/* Read full ROM image. */
804*31d98677SRui Paulo 	memset(&sc->rom, 0xff, sizeof(sc->rom));
805*31d98677SRui Paulo 	while (addr < 512) {
806*31d98677SRui Paulo 		reg = rsu_efuse_read_1(sc, addr);
807*31d98677SRui Paulo 		if (reg == 0xff)
808*31d98677SRui Paulo 			break;
809*31d98677SRui Paulo 		addr++;
810*31d98677SRui Paulo 		off = reg >> 4;
811*31d98677SRui Paulo 		msk = reg & 0xf;
812*31d98677SRui Paulo 		for (i = 0; i < 4; i++) {
813*31d98677SRui Paulo 			if (msk & (1 << i))
814*31d98677SRui Paulo 				continue;
815*31d98677SRui Paulo 			rom[off * 8 + i * 2 + 0] =
816*31d98677SRui Paulo 			    rsu_efuse_read_1(sc, addr);
817*31d98677SRui Paulo 			addr++;
818*31d98677SRui Paulo 			rom[off * 8 + i * 2 + 1] =
819*31d98677SRui Paulo 			    rsu_efuse_read_1(sc, addr);
820*31d98677SRui Paulo 			addr++;
821*31d98677SRui Paulo 		}
822*31d98677SRui Paulo 	}
823*31d98677SRui Paulo #ifdef USB_DEBUG
824*31d98677SRui Paulo 	if (rsu_debug >= 5) {
825*31d98677SRui Paulo 		/* Dump ROM content. */
826*31d98677SRui Paulo 		printf("\n");
827*31d98677SRui Paulo 		for (i = 0; i < sizeof(sc->rom); i++)
828*31d98677SRui Paulo 			printf("%02x:", rom[i]);
829*31d98677SRui Paulo 		printf("\n");
830*31d98677SRui Paulo 	}
831*31d98677SRui Paulo #endif
832*31d98677SRui Paulo 	return (0);
833*31d98677SRui Paulo }
834*31d98677SRui Paulo 
835*31d98677SRui Paulo static int
836*31d98677SRui Paulo rsu_fw_cmd(struct rsu_softc *sc, uint8_t code, void *buf, int len)
837*31d98677SRui Paulo {
838*31d98677SRui Paulo 	struct rsu_data *data;
839*31d98677SRui Paulo 	struct r92s_tx_desc *txd;
840*31d98677SRui Paulo 	struct r92s_fw_cmd_hdr *cmd;
841*31d98677SRui Paulo 	int cmdsz, xferlen;
842*31d98677SRui Paulo 
843*31d98677SRui Paulo 	data = rsu_getbuf(sc);
844*31d98677SRui Paulo 	if (data == NULL)
845*31d98677SRui Paulo 		return (ENOMEM);
846*31d98677SRui Paulo 
847*31d98677SRui Paulo 	/* Round-up command length to a multiple of 8 bytes. */
848*31d98677SRui Paulo 	cmdsz = (len + 7) & ~7;
849*31d98677SRui Paulo 
850*31d98677SRui Paulo 	xferlen = sizeof(*txd) + sizeof(*cmd) + cmdsz;
851*31d98677SRui Paulo 	KASSERT(xferlen <= RSU_TXBUFSZ, ("%s: invalid length", __func__));
852*31d98677SRui Paulo 	memset(data->buf, 0, xferlen);
853*31d98677SRui Paulo 
854*31d98677SRui Paulo 	/* Setup Tx descriptor. */
855*31d98677SRui Paulo 	txd = (struct r92s_tx_desc *)data->buf;
856*31d98677SRui Paulo 	txd->txdw0 = htole32(
857*31d98677SRui Paulo 	    SM(R92S_TXDW0_OFFSET, sizeof(*txd)) |
858*31d98677SRui Paulo 	    SM(R92S_TXDW0_PKTLEN, sizeof(*cmd) + cmdsz) |
859*31d98677SRui Paulo 	    R92S_TXDW0_OWN | R92S_TXDW0_FSG | R92S_TXDW0_LSG);
860*31d98677SRui Paulo 	txd->txdw1 = htole32(SM(R92S_TXDW1_QSEL, R92S_TXDW1_QSEL_H2C));
861*31d98677SRui Paulo 
862*31d98677SRui Paulo 	/* Setup command header. */
863*31d98677SRui Paulo 	cmd = (struct r92s_fw_cmd_hdr *)&txd[1];
864*31d98677SRui Paulo 	cmd->len = htole16(cmdsz);
865*31d98677SRui Paulo 	cmd->code = code;
866*31d98677SRui Paulo 	cmd->seq = sc->cmd_seq;
867*31d98677SRui Paulo 	sc->cmd_seq = (sc->cmd_seq + 1) & 0x7f;
868*31d98677SRui Paulo 
869*31d98677SRui Paulo 	/* Copy command payload. */
870*31d98677SRui Paulo 	memcpy(&cmd[1], buf, len);
871*31d98677SRui Paulo 
872*31d98677SRui Paulo 	DPRINTFN(2, "Tx cmd code=0x%x len=0x%x\n", code, cmdsz);
873*31d98677SRui Paulo 	data->buflen = xferlen;
874*31d98677SRui Paulo 	STAILQ_INSERT_TAIL(&sc->sc_tx_pending, data, next);
875*31d98677SRui Paulo 	usbd_transfer_start(sc->sc_xfer[RSU_BULK_TX_VO]);
876*31d98677SRui Paulo 
877*31d98677SRui Paulo 	return (0);
878*31d98677SRui Paulo }
879*31d98677SRui Paulo 
880*31d98677SRui Paulo /* ARGSUSED */
881*31d98677SRui Paulo static void
882*31d98677SRui Paulo rsu_calib_task(void *arg, int pending __unused)
883*31d98677SRui Paulo {
884*31d98677SRui Paulo 	struct rsu_softc *sc = arg;
885*31d98677SRui Paulo 	uint32_t reg;
886*31d98677SRui Paulo 
887*31d98677SRui Paulo 	DPRINTFN(6, "running calibration task\n");
888*31d98677SRui Paulo 	RSU_LOCK(sc);
889*31d98677SRui Paulo #ifdef notyet
890*31d98677SRui Paulo 	/* Read WPS PBC status. */
891*31d98677SRui Paulo 	rsu_write_1(sc, R92S_MAC_PINMUX_CTRL,
892*31d98677SRui Paulo 	    R92S_GPIOMUX_EN | SM(R92S_GPIOSEL_GPIO, R92S_GPIOSEL_GPIO_JTAG));
893*31d98677SRui Paulo 	rsu_write_1(sc, R92S_GPIO_IO_SEL,
894*31d98677SRui Paulo 	    rsu_read_1(sc, R92S_GPIO_IO_SEL) & ~R92S_GPIO_WPS);
895*31d98677SRui Paulo 	reg = rsu_read_1(sc, R92S_GPIO_CTRL);
896*31d98677SRui Paulo 	if (reg != 0xff && (reg & R92S_GPIO_WPS))
897*31d98677SRui Paulo 		DPRINTF(("WPS PBC is pushed\n"));
898*31d98677SRui Paulo #endif
899*31d98677SRui Paulo 	/* Read current signal level. */
900*31d98677SRui Paulo 	if (rsu_fw_iocmd(sc, 0xf4000001) == 0) {
901*31d98677SRui Paulo 		reg = rsu_read_4(sc, R92S_IOCMD_DATA);
902*31d98677SRui Paulo 		DPRINTFN(8, "RSSI=%d%%\n", reg >> 4);
903*31d98677SRui Paulo 	}
904*31d98677SRui Paulo 	if (sc->sc_calibrating) {
905*31d98677SRui Paulo 		RSU_UNLOCK(sc);
906*31d98677SRui Paulo 		taskqueue_enqueue_timeout(taskqueue_thread, &sc->calib_task,
907*31d98677SRui Paulo 		    hz * 2);
908*31d98677SRui Paulo 	} else
909*31d98677SRui Paulo 		RSU_UNLOCK(sc);
910*31d98677SRui Paulo }
911*31d98677SRui Paulo 
912*31d98677SRui Paulo static int
913*31d98677SRui Paulo rsu_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
914*31d98677SRui Paulo {
915*31d98677SRui Paulo 	struct rsu_vap *uvp = RSU_VAP(vap);
916*31d98677SRui Paulo 	struct ieee80211com *ic = vap->iv_ic;
917*31d98677SRui Paulo 	struct rsu_softc *sc = ic->ic_ifp->if_softc;
918*31d98677SRui Paulo 	struct ieee80211_node *ni;
919*31d98677SRui Paulo 	struct ieee80211_rateset *rs;
920*31d98677SRui Paulo 	enum ieee80211_state ostate;
921*31d98677SRui Paulo 	int error, startcal = 0;
922*31d98677SRui Paulo 
923*31d98677SRui Paulo 	ostate = vap->iv_state;
924*31d98677SRui Paulo 	DPRINTF("%s -> %s\n", ieee80211_state_name[ostate],
925*31d98677SRui Paulo 	    ieee80211_state_name[nstate]);
926*31d98677SRui Paulo 
927*31d98677SRui Paulo 	IEEE80211_UNLOCK(ic);
928*31d98677SRui Paulo 	if (ostate == IEEE80211_S_RUN) {
929*31d98677SRui Paulo 		RSU_LOCK(sc);
930*31d98677SRui Paulo 		/* Stop calibration. */
931*31d98677SRui Paulo 		sc->sc_calibrating = 0;
932*31d98677SRui Paulo 		RSU_UNLOCK(sc);
933*31d98677SRui Paulo 		taskqueue_drain_timeout(taskqueue_thread, &sc->calib_task);
934*31d98677SRui Paulo 		/* Disassociate from our current BSS. */
935*31d98677SRui Paulo 		RSU_LOCK(sc);
936*31d98677SRui Paulo 		rsu_disconnect(sc);
937*31d98677SRui Paulo 	} else
938*31d98677SRui Paulo 		RSU_LOCK(sc);
939*31d98677SRui Paulo 	switch (nstate) {
940*31d98677SRui Paulo 	case IEEE80211_S_INIT:
941*31d98677SRui Paulo 		break;
942*31d98677SRui Paulo 	case IEEE80211_S_AUTH:
943*31d98677SRui Paulo 		ni = ieee80211_ref_node(vap->iv_bss);
944*31d98677SRui Paulo 		error = rsu_join_bss(sc, ni);
945*31d98677SRui Paulo 		ieee80211_free_node(ni);
946*31d98677SRui Paulo 		if (error != 0) {
947*31d98677SRui Paulo 			device_printf(sc->sc_dev,
948*31d98677SRui Paulo 			    "could not send join command\n");
949*31d98677SRui Paulo 		}
950*31d98677SRui Paulo 		break;
951*31d98677SRui Paulo 	case IEEE80211_S_RUN:
952*31d98677SRui Paulo 		ni = ieee80211_ref_node(vap->iv_bss);
953*31d98677SRui Paulo 		rs = &ni->ni_rates;
954*31d98677SRui Paulo 		/* Indicate highest supported rate. */
955*31d98677SRui Paulo 		ni->ni_txrate = rs->rs_rates[rs->rs_nrates - 1];
956*31d98677SRui Paulo 		ieee80211_free_node(ni);
957*31d98677SRui Paulo 		startcal = 1;
958*31d98677SRui Paulo 		break;
959*31d98677SRui Paulo 	default:
960*31d98677SRui Paulo 		break;
961*31d98677SRui Paulo 	}
962*31d98677SRui Paulo 	sc->sc_calibrating = 1;
963*31d98677SRui Paulo 	RSU_UNLOCK(sc);
964*31d98677SRui Paulo 	IEEE80211_LOCK(ic);
965*31d98677SRui Paulo 	/* Start periodic calibration. */
966*31d98677SRui Paulo 	taskqueue_enqueue_timeout(taskqueue_thread, &sc->calib_task, hz * 2);
967*31d98677SRui Paulo 
968*31d98677SRui Paulo 	return (uvp->newstate(vap, nstate, arg));
969*31d98677SRui Paulo }
970*31d98677SRui Paulo 
971*31d98677SRui Paulo #ifdef notyet
972*31d98677SRui Paulo static void
973*31d98677SRui Paulo rsu_set_key(struct rsu_softc *sc, const struct ieee80211_key *k)
974*31d98677SRui Paulo {
975*31d98677SRui Paulo 	struct r92s_fw_cmd_set_key key;
976*31d98677SRui Paulo 
977*31d98677SRui Paulo 	memset(&key, 0, sizeof(key));
978*31d98677SRui Paulo 	/* Map net80211 cipher to HW crypto algorithm. */
979*31d98677SRui Paulo 	switch (k->wk_cipher->ic_cipher) {
980*31d98677SRui Paulo 	case IEEE80211_CIPHER_WEP:
981*31d98677SRui Paulo 		if (k->wk_keylen < 8)
982*31d98677SRui Paulo 			key.algo = R92S_KEY_ALGO_WEP40;
983*31d98677SRui Paulo 		else
984*31d98677SRui Paulo 			key.algo = R92S_KEY_ALGO_WEP104;
985*31d98677SRui Paulo 		break;
986*31d98677SRui Paulo 	case IEEE80211_CIPHER_TKIP:
987*31d98677SRui Paulo 		key.algo = R92S_KEY_ALGO_TKIP;
988*31d98677SRui Paulo 		break;
989*31d98677SRui Paulo 	case IEEE80211_CIPHER_AES_CCM:
990*31d98677SRui Paulo 		key.algo = R92S_KEY_ALGO_AES;
991*31d98677SRui Paulo 		break;
992*31d98677SRui Paulo 	default:
993*31d98677SRui Paulo 		return;
994*31d98677SRui Paulo 	}
995*31d98677SRui Paulo 	key.id = k->wk_keyix;
996*31d98677SRui Paulo 	key.grpkey = (k->wk_flags & IEEE80211_KEY_GROUP) != 0;
997*31d98677SRui Paulo 	memcpy(key.key, k->wk_key, MIN(k->wk_keylen, sizeof(key.key)));
998*31d98677SRui Paulo 	(void)rsu_fw_cmd(sc, R92S_CMD_SET_KEY, &key, sizeof(key));
999*31d98677SRui Paulo }
1000*31d98677SRui Paulo 
1001*31d98677SRui Paulo static void
1002*31d98677SRui Paulo rsu_delete_key(struct rsu_softc *sc, const struct ieee80211_key *k)
1003*31d98677SRui Paulo {
1004*31d98677SRui Paulo 	struct r92s_fw_cmd_set_key key;
1005*31d98677SRui Paulo 
1006*31d98677SRui Paulo 	memset(&key, 0, sizeof(key));
1007*31d98677SRui Paulo 	key.id = k->wk_keyix;
1008*31d98677SRui Paulo 	(void)rsu_fw_cmd(sc, R92S_CMD_SET_KEY, &key, sizeof(key));
1009*31d98677SRui Paulo }
1010*31d98677SRui Paulo #endif
1011*31d98677SRui Paulo 
1012*31d98677SRui Paulo static int
1013*31d98677SRui Paulo rsu_site_survey(struct rsu_softc *sc, struct ieee80211vap *vap)
1014*31d98677SRui Paulo {
1015*31d98677SRui Paulo 	struct r92s_fw_cmd_sitesurvey cmd;
1016*31d98677SRui Paulo 	struct ifnet *ifp = sc->sc_ifp;
1017*31d98677SRui Paulo 	struct ieee80211com *ic = ifp->if_l2com;
1018*31d98677SRui Paulo 
1019*31d98677SRui Paulo 	memset(&cmd, 0, sizeof(cmd));
1020*31d98677SRui Paulo 	if ((ic->ic_flags & IEEE80211_F_ASCAN) || sc->scan_pass == 1)
1021*31d98677SRui Paulo 		cmd.active = htole32(1);
1022*31d98677SRui Paulo 	cmd.limit = htole32(48);
1023*31d98677SRui Paulo 	if (sc->scan_pass == 1 && vap->iv_des_nssid > 0) {
1024*31d98677SRui Paulo 		/* Do a directed scan for second pass. */
1025*31d98677SRui Paulo 		cmd.ssidlen = htole32(vap->iv_des_ssid[0].len);
1026*31d98677SRui Paulo 		memcpy(cmd.ssid, vap->iv_des_ssid[0].ssid,
1027*31d98677SRui Paulo 		    vap->iv_des_ssid[0].len);
1028*31d98677SRui Paulo 
1029*31d98677SRui Paulo 	}
1030*31d98677SRui Paulo 	DPRINTF("sending site survey command, pass=%d\n", sc->scan_pass);
1031*31d98677SRui Paulo 	return (rsu_fw_cmd(sc, R92S_CMD_SITE_SURVEY, &cmd, sizeof(cmd)));
1032*31d98677SRui Paulo }
1033*31d98677SRui Paulo 
1034*31d98677SRui Paulo static int
1035*31d98677SRui Paulo rsu_join_bss(struct rsu_softc *sc, struct ieee80211_node *ni)
1036*31d98677SRui Paulo {
1037*31d98677SRui Paulo 	struct ifnet *ifp = sc->sc_ifp;
1038*31d98677SRui Paulo 	struct ieee80211com *ic = ifp->if_l2com;
1039*31d98677SRui Paulo 	struct ieee80211vap *vap = ni->ni_vap;
1040*31d98677SRui Paulo 	struct ndis_wlan_bssid_ex *bss;
1041*31d98677SRui Paulo 	struct ndis_802_11_fixed_ies *fixed;
1042*31d98677SRui Paulo 	struct r92s_fw_cmd_auth auth;
1043*31d98677SRui Paulo 	uint8_t buf[sizeof(*bss) + 128], *frm;
1044*31d98677SRui Paulo 	uint8_t opmode;
1045*31d98677SRui Paulo 	int error;
1046*31d98677SRui Paulo 
1047*31d98677SRui Paulo 	/* Let the FW decide the opmode based on the capinfo field. */
1048*31d98677SRui Paulo 	opmode = NDIS802_11AUTOUNKNOWN;
1049*31d98677SRui Paulo 	DPRINTF("setting operating mode to %d\n", opmode);
1050*31d98677SRui Paulo 	error = rsu_fw_cmd(sc, R92S_CMD_SET_OPMODE, &opmode, sizeof(opmode));
1051*31d98677SRui Paulo 	if (error != 0)
1052*31d98677SRui Paulo 		return (error);
1053*31d98677SRui Paulo 
1054*31d98677SRui Paulo 	memset(&auth, 0, sizeof(auth));
1055*31d98677SRui Paulo 	if (vap->iv_flags & IEEE80211_F_WPA) {
1056*31d98677SRui Paulo 		auth.mode = R92S_AUTHMODE_WPA;
1057*31d98677SRui Paulo 		auth.dot1x = ni->ni_authmode == IEEE80211_AUTH_8021X;
1058*31d98677SRui Paulo 	} else
1059*31d98677SRui Paulo 		auth.mode = R92S_AUTHMODE_OPEN;
1060*31d98677SRui Paulo 	DPRINTF("setting auth mode to %d\n", auth.mode);
1061*31d98677SRui Paulo 	error = rsu_fw_cmd(sc, R92S_CMD_SET_AUTH, &auth, sizeof(auth));
1062*31d98677SRui Paulo 	if (error != 0)
1063*31d98677SRui Paulo 		return (error);
1064*31d98677SRui Paulo 
1065*31d98677SRui Paulo 	memset(buf, 0, sizeof(buf));
1066*31d98677SRui Paulo 	bss = (struct ndis_wlan_bssid_ex *)buf;
1067*31d98677SRui Paulo 	IEEE80211_ADDR_COPY(bss->macaddr, ni->ni_bssid);
1068*31d98677SRui Paulo 	bss->ssid.ssidlen = htole32(ni->ni_esslen);
1069*31d98677SRui Paulo 	memcpy(bss->ssid.ssid, ni->ni_essid, ni->ni_esslen);
1070*31d98677SRui Paulo 	if (vap->iv_flags & (IEEE80211_F_PRIVACY | IEEE80211_F_WPA))
1071*31d98677SRui Paulo 		bss->privacy = htole32(1);
1072*31d98677SRui Paulo 	bss->rssi = htole32(ni->ni_avgrssi);
1073*31d98677SRui Paulo 	if (ic->ic_curmode == IEEE80211_MODE_11B)
1074*31d98677SRui Paulo 		bss->networktype = htole32(NDIS802_11DS);
1075*31d98677SRui Paulo 	else
1076*31d98677SRui Paulo 		bss->networktype = htole32(NDIS802_11OFDM24);
1077*31d98677SRui Paulo 	bss->config.len = htole32(sizeof(bss->config));
1078*31d98677SRui Paulo 	bss->config.bintval = htole32(ni->ni_intval);
1079*31d98677SRui Paulo 	bss->config.dsconfig = htole32(ieee80211_chan2ieee(ic, ni->ni_chan));
1080*31d98677SRui Paulo 	bss->inframode = htole32(NDIS802_11INFRASTRUCTURE);
1081*31d98677SRui Paulo 	memcpy(bss->supprates, ni->ni_rates.rs_rates,
1082*31d98677SRui Paulo 	    ni->ni_rates.rs_nrates);
1083*31d98677SRui Paulo 	/* Write the fixed fields of the beacon frame. */
1084*31d98677SRui Paulo 	fixed = (struct ndis_802_11_fixed_ies *)&bss[1];
1085*31d98677SRui Paulo 	memcpy(&fixed->tstamp, ni->ni_tstamp.data, 8);
1086*31d98677SRui Paulo 	fixed->bintval = htole16(ni->ni_intval);
1087*31d98677SRui Paulo 	fixed->capabilities = htole16(ni->ni_capinfo);
1088*31d98677SRui Paulo 	/* Write IEs to be included in the association request. */
1089*31d98677SRui Paulo 	frm = (uint8_t *)&fixed[1];
1090*31d98677SRui Paulo 	frm = ieee80211_add_rsn(frm, vap);
1091*31d98677SRui Paulo 	frm = ieee80211_add_wpa(frm, vap);
1092*31d98677SRui Paulo 	frm = ieee80211_add_qos(frm, ni);
1093*31d98677SRui Paulo 	if (ni->ni_flags & IEEE80211_NODE_HT)
1094*31d98677SRui Paulo 		frm = ieee80211_add_htcap(frm, ni);
1095*31d98677SRui Paulo 	bss->ieslen = htole32(frm - (uint8_t *)fixed);
1096*31d98677SRui Paulo 	bss->len = htole32(((frm - buf) + 3) & ~3);
1097*31d98677SRui Paulo 	DPRINTF("sending join bss command to %s chan %d\n",
1098*31d98677SRui Paulo 	    ether_sprintf(bss->macaddr), le32toh(bss->config.dsconfig));
1099*31d98677SRui Paulo 	return (rsu_fw_cmd(sc, R92S_CMD_JOIN_BSS, buf, sizeof(buf)));
1100*31d98677SRui Paulo }
1101*31d98677SRui Paulo 
1102*31d98677SRui Paulo static int
1103*31d98677SRui Paulo rsu_disconnect(struct rsu_softc *sc)
1104*31d98677SRui Paulo {
1105*31d98677SRui Paulo 	uint32_t zero = 0;	/* :-) */
1106*31d98677SRui Paulo 
1107*31d98677SRui Paulo 	/* Disassociate from our current BSS. */
1108*31d98677SRui Paulo 	DPRINTF("sending disconnect command\n");
1109*31d98677SRui Paulo 	return (rsu_fw_cmd(sc, R92S_CMD_DISCONNECT, &zero, sizeof(zero)));
1110*31d98677SRui Paulo }
1111*31d98677SRui Paulo 
1112*31d98677SRui Paulo static void
1113*31d98677SRui Paulo rsu_event_survey(struct rsu_softc *sc, uint8_t *buf, int len)
1114*31d98677SRui Paulo {
1115*31d98677SRui Paulo 	struct ifnet *ifp = sc->sc_ifp;
1116*31d98677SRui Paulo 	struct ieee80211com *ic = ifp->if_l2com;
1117*31d98677SRui Paulo 	struct ieee80211_frame *wh;
1118*31d98677SRui Paulo 	struct ieee80211_channel *c;
1119*31d98677SRui Paulo 	struct ndis_wlan_bssid_ex *bss;
1120*31d98677SRui Paulo 	struct mbuf *m;
1121*31d98677SRui Paulo 	int pktlen;
1122*31d98677SRui Paulo 
1123*31d98677SRui Paulo 	if (__predict_false(len < sizeof(*bss)))
1124*31d98677SRui Paulo 		return;
1125*31d98677SRui Paulo 	bss = (struct ndis_wlan_bssid_ex *)buf;
1126*31d98677SRui Paulo 	if (__predict_false(len < sizeof(*bss) + le32toh(bss->ieslen)))
1127*31d98677SRui Paulo 		return;
1128*31d98677SRui Paulo 
1129*31d98677SRui Paulo 	DPRINTFN(2, "found BSS %s: len=%d chan=%d inframode=%d "
1130*31d98677SRui Paulo 	    "networktype=%d privacy=%d\n",
1131*31d98677SRui Paulo 	    ether_sprintf(bss->macaddr), le32toh(bss->len),
1132*31d98677SRui Paulo 	    le32toh(bss->config.dsconfig), le32toh(bss->inframode),
1133*31d98677SRui Paulo 	    le32toh(bss->networktype), le32toh(bss->privacy));
1134*31d98677SRui Paulo 
1135*31d98677SRui Paulo 	/* Build a fake beacon frame to let net80211 do all the parsing. */
1136*31d98677SRui Paulo 	pktlen = sizeof(*wh) + le32toh(bss->ieslen);
1137*31d98677SRui Paulo 	if (__predict_false(pktlen > MCLBYTES))
1138*31d98677SRui Paulo 		return;
1139*31d98677SRui Paulo 	MGETHDR(m, M_DONTWAIT, MT_DATA);
1140*31d98677SRui Paulo 	if (__predict_false(m == NULL))
1141*31d98677SRui Paulo 		return;
1142*31d98677SRui Paulo 	if (pktlen > MHLEN) {
1143*31d98677SRui Paulo 		MCLGET(m, M_DONTWAIT);
1144*31d98677SRui Paulo 		if (!(m->m_flags & M_EXT)) {
1145*31d98677SRui Paulo 			m_free(m);
1146*31d98677SRui Paulo 			return;
1147*31d98677SRui Paulo 		}
1148*31d98677SRui Paulo 	}
1149*31d98677SRui Paulo 	wh = mtod(m, struct ieee80211_frame *);
1150*31d98677SRui Paulo 	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
1151*31d98677SRui Paulo 	    IEEE80211_FC0_SUBTYPE_BEACON;
1152*31d98677SRui Paulo 	wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
1153*31d98677SRui Paulo 	*(uint16_t *)wh->i_dur = 0;
1154*31d98677SRui Paulo 	IEEE80211_ADDR_COPY(wh->i_addr1, ifp->if_broadcastaddr);
1155*31d98677SRui Paulo 	IEEE80211_ADDR_COPY(wh->i_addr2, bss->macaddr);
1156*31d98677SRui Paulo 	IEEE80211_ADDR_COPY(wh->i_addr3, bss->macaddr);
1157*31d98677SRui Paulo 	*(uint16_t *)wh->i_seq = 0;
1158*31d98677SRui Paulo 	memcpy(&wh[1], (uint8_t *)&bss[1], le32toh(bss->ieslen));
1159*31d98677SRui Paulo 
1160*31d98677SRui Paulo 	/* Finalize mbuf. */
1161*31d98677SRui Paulo 	m->m_pkthdr.len = m->m_len = pktlen;
1162*31d98677SRui Paulo 	m->m_pkthdr.rcvif = ifp;
1163*31d98677SRui Paulo 	/* Fix the channel. */
1164*31d98677SRui Paulo 	c = ieee80211_find_channel_byieee(ic,
1165*31d98677SRui Paulo 	    le32toh(bss->config.dsconfig),
1166*31d98677SRui Paulo 	    IEEE80211_CHAN_G);
1167*31d98677SRui Paulo 	if (c) {
1168*31d98677SRui Paulo 		ic->ic_curchan = c;
1169*31d98677SRui Paulo 		ieee80211_radiotap_chan_change(ic);
1170*31d98677SRui Paulo 	}
1171*31d98677SRui Paulo 	/* XXX avoid a LOR */
1172*31d98677SRui Paulo 	RSU_UNLOCK(sc);
1173*31d98677SRui Paulo 	ieee80211_input_all(ic, m, le32toh(bss->rssi), 0);
1174*31d98677SRui Paulo 	RSU_LOCK(sc);
1175*31d98677SRui Paulo }
1176*31d98677SRui Paulo 
1177*31d98677SRui Paulo static void
1178*31d98677SRui Paulo rsu_event_join_bss(struct rsu_softc *sc, uint8_t *buf, int len)
1179*31d98677SRui Paulo {
1180*31d98677SRui Paulo 	struct ifnet *ifp = sc->sc_ifp;
1181*31d98677SRui Paulo 	struct ieee80211com *ic = ifp->if_l2com;
1182*31d98677SRui Paulo 	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
1183*31d98677SRui Paulo 	struct ieee80211_node *ni = vap->iv_bss;
1184*31d98677SRui Paulo 	struct r92s_event_join_bss *rsp;
1185*31d98677SRui Paulo 	int res;
1186*31d98677SRui Paulo 
1187*31d98677SRui Paulo 	if (__predict_false(len < sizeof(*rsp)))
1188*31d98677SRui Paulo 		return;
1189*31d98677SRui Paulo 	rsp = (struct r92s_event_join_bss *)buf;
1190*31d98677SRui Paulo 	res = (int)le32toh(rsp->join_res);
1191*31d98677SRui Paulo 
1192*31d98677SRui Paulo 	DPRINTF("Rx join BSS event len=%d res=%d\n", len, res);
1193*31d98677SRui Paulo 	if (res <= 0) {
1194*31d98677SRui Paulo 		RSU_UNLOCK(sc);
1195*31d98677SRui Paulo 		ieee80211_new_state(vap, IEEE80211_S_SCAN, -1);
1196*31d98677SRui Paulo 		RSU_LOCK(sc);
1197*31d98677SRui Paulo 		return;
1198*31d98677SRui Paulo 	}
1199*31d98677SRui Paulo 	DPRINTF("associated with %s associd=%d\n",
1200*31d98677SRui Paulo 	    ether_sprintf(rsp->bss.macaddr), le32toh(rsp->associd));
1201*31d98677SRui Paulo 	ni->ni_associd = le32toh(rsp->associd) | 0xc000;
1202*31d98677SRui Paulo 	RSU_UNLOCK(sc);
1203*31d98677SRui Paulo 	ieee80211_new_state(vap, IEEE80211_S_RUN,
1204*31d98677SRui Paulo 	    IEEE80211_FC0_SUBTYPE_ASSOC_RESP);
1205*31d98677SRui Paulo 	RSU_LOCK(sc);
1206*31d98677SRui Paulo }
1207*31d98677SRui Paulo 
1208*31d98677SRui Paulo static void
1209*31d98677SRui Paulo rsu_rx_event(struct rsu_softc *sc, uint8_t code, uint8_t *buf, int len)
1210*31d98677SRui Paulo {
1211*31d98677SRui Paulo 	struct ifnet *ifp = sc->sc_ifp;
1212*31d98677SRui Paulo 	struct ieee80211com *ic = ifp->if_l2com;
1213*31d98677SRui Paulo 	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
1214*31d98677SRui Paulo 
1215*31d98677SRui Paulo 	DPRINTFN(4, "Rx event code=%d len=%d\n", code, len);
1216*31d98677SRui Paulo 	switch (code) {
1217*31d98677SRui Paulo 	case R92S_EVT_SURVEY:
1218*31d98677SRui Paulo 		if (vap->iv_state == IEEE80211_S_SCAN)
1219*31d98677SRui Paulo 			rsu_event_survey(sc, buf, len);
1220*31d98677SRui Paulo 		break;
1221*31d98677SRui Paulo 	case R92S_EVT_SURVEY_DONE:
1222*31d98677SRui Paulo 		DPRINTF("site survey pass %d done, found %d BSS\n",
1223*31d98677SRui Paulo 		    sc->scan_pass, le32toh(*(uint32_t *)buf));
1224*31d98677SRui Paulo 		if (vap->iv_state != IEEE80211_S_SCAN)
1225*31d98677SRui Paulo 			break;	/* Ignore if not scanning. */
1226*31d98677SRui Paulo 		if (sc->scan_pass == 0 && vap->iv_des_nssid != 0) {
1227*31d98677SRui Paulo 			/* Schedule a directed scan for hidden APs. */
1228*31d98677SRui Paulo 			sc->scan_pass = 1;
1229*31d98677SRui Paulo 			RSU_UNLOCK(sc);
1230*31d98677SRui Paulo 			ieee80211_new_state(vap, IEEE80211_S_SCAN, -1);
1231*31d98677SRui Paulo 			RSU_LOCK(sc);
1232*31d98677SRui Paulo 			break;
1233*31d98677SRui Paulo 		}
1234*31d98677SRui Paulo 		sc->scan_pass = 0;
1235*31d98677SRui Paulo 		break;
1236*31d98677SRui Paulo 	case R92S_EVT_JOIN_BSS:
1237*31d98677SRui Paulo 		if (vap->iv_state == IEEE80211_S_AUTH)
1238*31d98677SRui Paulo 			rsu_event_join_bss(sc, buf, len);
1239*31d98677SRui Paulo 		break;
1240*31d98677SRui Paulo 	case R92S_EVT_DEL_STA:
1241*31d98677SRui Paulo 		DPRINTF("disassociated from %s\n", ether_sprintf(buf));
1242*31d98677SRui Paulo 		if (vap->iv_state == IEEE80211_S_RUN &&
1243*31d98677SRui Paulo 		    IEEE80211_ADDR_EQ(vap->iv_bss->ni_bssid, buf)) {
1244*31d98677SRui Paulo 			RSU_UNLOCK(sc);
1245*31d98677SRui Paulo 			ieee80211_new_state(vap, IEEE80211_S_SCAN, -1);
1246*31d98677SRui Paulo 			RSU_LOCK(sc);
1247*31d98677SRui Paulo 		}
1248*31d98677SRui Paulo 		break;
1249*31d98677SRui Paulo 	case R92S_EVT_WPS_PBC:
1250*31d98677SRui Paulo 		DPRINTF("WPS PBC pushed.\n");
1251*31d98677SRui Paulo 		break;
1252*31d98677SRui Paulo 	case R92S_EVT_FWDBG:
1253*31d98677SRui Paulo 		if (ifp->if_flags & IFF_DEBUG) {
1254*31d98677SRui Paulo 			buf[60] = '\0';
1255*31d98677SRui Paulo 			printf("FWDBG: %s\n", (char *)buf);
1256*31d98677SRui Paulo 		}
1257*31d98677SRui Paulo 		break;
1258*31d98677SRui Paulo 	}
1259*31d98677SRui Paulo }
1260*31d98677SRui Paulo 
1261*31d98677SRui Paulo static void
1262*31d98677SRui Paulo rsu_rx_multi_event(struct rsu_softc *sc, uint8_t *buf, int len)
1263*31d98677SRui Paulo {
1264*31d98677SRui Paulo 	struct r92s_fw_cmd_hdr *cmd;
1265*31d98677SRui Paulo 	int cmdsz;
1266*31d98677SRui Paulo 
1267*31d98677SRui Paulo 	DPRINTFN(6, "Rx events len=%d\n", len);
1268*31d98677SRui Paulo 
1269*31d98677SRui Paulo 	/* Skip Rx status. */
1270*31d98677SRui Paulo 	buf += sizeof(struct r92s_rx_stat);
1271*31d98677SRui Paulo 	len -= sizeof(struct r92s_rx_stat);
1272*31d98677SRui Paulo 
1273*31d98677SRui Paulo 	/* Process all events. */
1274*31d98677SRui Paulo 	for (;;) {
1275*31d98677SRui Paulo 		/* Check that command header fits. */
1276*31d98677SRui Paulo 		if (__predict_false(len < sizeof(*cmd)))
1277*31d98677SRui Paulo 			break;
1278*31d98677SRui Paulo 		cmd = (struct r92s_fw_cmd_hdr *)buf;
1279*31d98677SRui Paulo 		/* Check that command payload fits. */
1280*31d98677SRui Paulo 		cmdsz = le16toh(cmd->len);
1281*31d98677SRui Paulo 		if (__predict_false(len < sizeof(*cmd) + cmdsz))
1282*31d98677SRui Paulo 			break;
1283*31d98677SRui Paulo 
1284*31d98677SRui Paulo 		/* Process firmware event. */
1285*31d98677SRui Paulo 		rsu_rx_event(sc, cmd->code, (uint8_t *)&cmd[1], cmdsz);
1286*31d98677SRui Paulo 
1287*31d98677SRui Paulo 		if (!(cmd->seq & R92S_FW_CMD_MORE))
1288*31d98677SRui Paulo 			break;
1289*31d98677SRui Paulo 		buf += sizeof(*cmd) + cmdsz;
1290*31d98677SRui Paulo 		len -= sizeof(*cmd) + cmdsz;
1291*31d98677SRui Paulo 	}
1292*31d98677SRui Paulo }
1293*31d98677SRui Paulo 
1294*31d98677SRui Paulo static int8_t
1295*31d98677SRui Paulo rsu_get_rssi(struct rsu_softc *sc, int rate, void *physt)
1296*31d98677SRui Paulo {
1297*31d98677SRui Paulo 	static const int8_t cckoff[] = { 14, -2, -20, -40 };
1298*31d98677SRui Paulo 	struct r92s_rx_phystat *phy;
1299*31d98677SRui Paulo 	struct r92s_rx_cck *cck;
1300*31d98677SRui Paulo 	uint8_t rpt;
1301*31d98677SRui Paulo 	int8_t rssi;
1302*31d98677SRui Paulo 
1303*31d98677SRui Paulo 	if (rate <= 3) {
1304*31d98677SRui Paulo 		cck = (struct r92s_rx_cck *)physt;
1305*31d98677SRui Paulo 		rpt = (cck->agc_rpt >> 6) & 0x3;
1306*31d98677SRui Paulo 		rssi = cck->agc_rpt & 0x3e;
1307*31d98677SRui Paulo 		rssi = cckoff[rpt] - rssi;
1308*31d98677SRui Paulo 	} else {	/* OFDM/HT. */
1309*31d98677SRui Paulo 		phy = (struct r92s_rx_phystat *)physt;
1310*31d98677SRui Paulo 		rssi = ((le32toh(phy->phydw1) >> 1) & 0x7f) - 106;
1311*31d98677SRui Paulo 	}
1312*31d98677SRui Paulo 	return (rssi);
1313*31d98677SRui Paulo }
1314*31d98677SRui Paulo 
1315*31d98677SRui Paulo static struct mbuf *
1316*31d98677SRui Paulo rsu_rx_frame(struct rsu_softc *sc, uint8_t *buf, int pktlen, int *rssi)
1317*31d98677SRui Paulo {
1318*31d98677SRui Paulo 	struct ifnet *ifp = sc->sc_ifp;
1319*31d98677SRui Paulo 	struct ieee80211com *ic = ifp->if_l2com;
1320*31d98677SRui Paulo 	struct ieee80211_frame *wh;
1321*31d98677SRui Paulo 	struct r92s_rx_stat *stat;
1322*31d98677SRui Paulo 	uint32_t rxdw0, rxdw3;
1323*31d98677SRui Paulo 	struct mbuf *m;
1324*31d98677SRui Paulo 	uint8_t rate;
1325*31d98677SRui Paulo 	int infosz;
1326*31d98677SRui Paulo 
1327*31d98677SRui Paulo 	stat = (struct r92s_rx_stat *)buf;
1328*31d98677SRui Paulo 	rxdw0 = le32toh(stat->rxdw0);
1329*31d98677SRui Paulo 	rxdw3 = le32toh(stat->rxdw3);
1330*31d98677SRui Paulo 
1331*31d98677SRui Paulo 	if (__predict_false(rxdw0 & R92S_RXDW0_CRCERR)) {
1332*31d98677SRui Paulo 		ifp->if_ierrors++;
1333*31d98677SRui Paulo 		return NULL;
1334*31d98677SRui Paulo 	}
1335*31d98677SRui Paulo 	if (__predict_false(pktlen < sizeof(*wh) || pktlen > MCLBYTES)) {
1336*31d98677SRui Paulo 		ifp->if_ierrors++;
1337*31d98677SRui Paulo 		return NULL;
1338*31d98677SRui Paulo 	}
1339*31d98677SRui Paulo 
1340*31d98677SRui Paulo 	rate = MS(rxdw3, R92S_RXDW3_RATE);
1341*31d98677SRui Paulo 	infosz = MS(rxdw0, R92S_RXDW0_INFOSZ) * 8;
1342*31d98677SRui Paulo 
1343*31d98677SRui Paulo 	/* Get RSSI from PHY status descriptor if present. */
1344*31d98677SRui Paulo 	if (infosz != 0)
1345*31d98677SRui Paulo 		*rssi = rsu_get_rssi(sc, rate, &stat[1]);
1346*31d98677SRui Paulo 	else
1347*31d98677SRui Paulo 		*rssi = 0;
1348*31d98677SRui Paulo 
1349*31d98677SRui Paulo 	DPRINTFN(5, "Rx frame len=%d rate=%d infosz=%d rssi=%d\n",
1350*31d98677SRui Paulo 	    pktlen, rate, infosz, *rssi);
1351*31d98677SRui Paulo 
1352*31d98677SRui Paulo 	MGETHDR(m, M_DONTWAIT, MT_DATA);
1353*31d98677SRui Paulo 	if (__predict_false(m == NULL)) {
1354*31d98677SRui Paulo 		ifp->if_ierrors++;
1355*31d98677SRui Paulo 		return NULL;
1356*31d98677SRui Paulo 	}
1357*31d98677SRui Paulo 	if (pktlen > MHLEN) {
1358*31d98677SRui Paulo 		MCLGET(m, M_DONTWAIT);
1359*31d98677SRui Paulo 		if (__predict_false(!(m->m_flags & M_EXT))) {
1360*31d98677SRui Paulo 			ifp->if_ierrors++;
1361*31d98677SRui Paulo 			m_freem(m);
1362*31d98677SRui Paulo 			return NULL;
1363*31d98677SRui Paulo 		}
1364*31d98677SRui Paulo 	}
1365*31d98677SRui Paulo 	/* Finalize mbuf. */
1366*31d98677SRui Paulo 	m->m_pkthdr.rcvif = ifp;
1367*31d98677SRui Paulo 	/* Hardware does Rx TCP checksum offload. */
1368*31d98677SRui Paulo 	if (rxdw3 & R92S_RXDW3_TCPCHKVALID) {
1369*31d98677SRui Paulo 		if (__predict_true(rxdw3 & R92S_RXDW3_TCPCHKRPT))
1370*31d98677SRui Paulo 			m->m_pkthdr.csum_flags |= CSUM_DATA_VALID;
1371*31d98677SRui Paulo 	}
1372*31d98677SRui Paulo 	wh = (struct ieee80211_frame *)((uint8_t *)&stat[1] + infosz);
1373*31d98677SRui Paulo 	memcpy(mtod(m, uint8_t *), wh, pktlen);
1374*31d98677SRui Paulo 	m->m_pkthdr.len = m->m_len = pktlen;
1375*31d98677SRui Paulo 
1376*31d98677SRui Paulo 	if (ieee80211_radiotap_active(ic)) {
1377*31d98677SRui Paulo 		struct rsu_rx_radiotap_header *tap = &sc->sc_rxtap;
1378*31d98677SRui Paulo 
1379*31d98677SRui Paulo 		/* Map HW rate index to 802.11 rate. */
1380*31d98677SRui Paulo 		tap->wr_flags = 2;
1381*31d98677SRui Paulo 		if (!(rxdw3 & R92S_RXDW3_HTC)) {
1382*31d98677SRui Paulo 			switch (rate) {
1383*31d98677SRui Paulo 			/* CCK. */
1384*31d98677SRui Paulo 			case  0: tap->wr_rate =   2; break;
1385*31d98677SRui Paulo 			case  1: tap->wr_rate =   4; break;
1386*31d98677SRui Paulo 			case  2: tap->wr_rate =  11; break;
1387*31d98677SRui Paulo 			case  3: tap->wr_rate =  22; break;
1388*31d98677SRui Paulo 			/* OFDM. */
1389*31d98677SRui Paulo 			case  4: tap->wr_rate =  12; break;
1390*31d98677SRui Paulo 			case  5: tap->wr_rate =  18; break;
1391*31d98677SRui Paulo 			case  6: tap->wr_rate =  24; break;
1392*31d98677SRui Paulo 			case  7: tap->wr_rate =  36; break;
1393*31d98677SRui Paulo 			case  8: tap->wr_rate =  48; break;
1394*31d98677SRui Paulo 			case  9: tap->wr_rate =  72; break;
1395*31d98677SRui Paulo 			case 10: tap->wr_rate =  96; break;
1396*31d98677SRui Paulo 			case 11: tap->wr_rate = 108; break;
1397*31d98677SRui Paulo 			}
1398*31d98677SRui Paulo 		} else if (rate >= 12) {	/* MCS0~15. */
1399*31d98677SRui Paulo 			/* Bit 7 set means HT MCS instead of rate. */
1400*31d98677SRui Paulo 			tap->wr_rate = 0x80 | (rate - 12);
1401*31d98677SRui Paulo 		}
1402*31d98677SRui Paulo 		tap->wr_dbm_antsignal = *rssi;
1403*31d98677SRui Paulo 		tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq);
1404*31d98677SRui Paulo 		tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags);
1405*31d98677SRui Paulo 	}
1406*31d98677SRui Paulo 
1407*31d98677SRui Paulo 	return (m);
1408*31d98677SRui Paulo }
1409*31d98677SRui Paulo 
1410*31d98677SRui Paulo static struct mbuf *
1411*31d98677SRui Paulo rsu_rx_multi_frame(struct rsu_softc *sc, uint8_t *buf, int len, int *rssi)
1412*31d98677SRui Paulo {
1413*31d98677SRui Paulo 	struct r92s_rx_stat *stat;
1414*31d98677SRui Paulo 	uint32_t rxdw0;
1415*31d98677SRui Paulo 	int totlen, pktlen, infosz, npkts;
1416*31d98677SRui Paulo 	struct mbuf *m, *m0 = NULL, *prevm = NULL;
1417*31d98677SRui Paulo 
1418*31d98677SRui Paulo 	/* Get the number of encapsulated frames. */
1419*31d98677SRui Paulo 	stat = (struct r92s_rx_stat *)buf;
1420*31d98677SRui Paulo 	npkts = MS(le32toh(stat->rxdw2), R92S_RXDW2_PKTCNT);
1421*31d98677SRui Paulo 	DPRINTFN(6, "Rx %d frames in one chunk\n", npkts);
1422*31d98677SRui Paulo 
1423*31d98677SRui Paulo 	/* Process all of them. */
1424*31d98677SRui Paulo 	while (npkts-- > 0) {
1425*31d98677SRui Paulo 		if (__predict_false(len < sizeof(*stat)))
1426*31d98677SRui Paulo 			break;
1427*31d98677SRui Paulo 		stat = (struct r92s_rx_stat *)buf;
1428*31d98677SRui Paulo 		rxdw0 = le32toh(stat->rxdw0);
1429*31d98677SRui Paulo 
1430*31d98677SRui Paulo 		pktlen = MS(rxdw0, R92S_RXDW0_PKTLEN);
1431*31d98677SRui Paulo 		if (__predict_false(pktlen == 0))
1432*31d98677SRui Paulo 			break;
1433*31d98677SRui Paulo 
1434*31d98677SRui Paulo 		infosz = MS(rxdw0, R92S_RXDW0_INFOSZ) * 8;
1435*31d98677SRui Paulo 
1436*31d98677SRui Paulo 		/* Make sure everything fits in xfer. */
1437*31d98677SRui Paulo 		totlen = sizeof(*stat) + infosz + pktlen;
1438*31d98677SRui Paulo 		if (__predict_false(totlen > len))
1439*31d98677SRui Paulo 			break;
1440*31d98677SRui Paulo 
1441*31d98677SRui Paulo 		/* Process 802.11 frame. */
1442*31d98677SRui Paulo 		m = rsu_rx_frame(sc, buf, pktlen, rssi);
1443*31d98677SRui Paulo 		if (m0 == NULL)
1444*31d98677SRui Paulo 			m0 = m;
1445*31d98677SRui Paulo 		if (prevm == NULL)
1446*31d98677SRui Paulo 			prevm = m;
1447*31d98677SRui Paulo 		else {
1448*31d98677SRui Paulo 			prevm->m_next = m;
1449*31d98677SRui Paulo 			prevm = m;
1450*31d98677SRui Paulo 		}
1451*31d98677SRui Paulo 		/* Next chunk is 128-byte aligned. */
1452*31d98677SRui Paulo 		totlen = (totlen + 127) & ~127;
1453*31d98677SRui Paulo 		buf += totlen;
1454*31d98677SRui Paulo 		len -= totlen;
1455*31d98677SRui Paulo 	}
1456*31d98677SRui Paulo 
1457*31d98677SRui Paulo 	return (m0);
1458*31d98677SRui Paulo }
1459*31d98677SRui Paulo 
1460*31d98677SRui Paulo static struct mbuf *
1461*31d98677SRui Paulo rsu_rxeof(struct usb_xfer *xfer, struct rsu_data *data, int *rssi)
1462*31d98677SRui Paulo {
1463*31d98677SRui Paulo 	struct rsu_softc *sc = data->sc;
1464*31d98677SRui Paulo 	struct r92s_rx_stat *stat;
1465*31d98677SRui Paulo 	int len;
1466*31d98677SRui Paulo 
1467*31d98677SRui Paulo 	usbd_xfer_status(xfer, &len, NULL, NULL, NULL);
1468*31d98677SRui Paulo 
1469*31d98677SRui Paulo 	if (__predict_false(len < sizeof(*stat))) {
1470*31d98677SRui Paulo 		DPRINTF("xfer too short %d\n", len);
1471*31d98677SRui Paulo 		sc->sc_ifp->if_ierrors++;
1472*31d98677SRui Paulo 		return (NULL);
1473*31d98677SRui Paulo 	}
1474*31d98677SRui Paulo 	/* Determine if it is a firmware C2H event or an 802.11 frame. */
1475*31d98677SRui Paulo 	stat = (struct r92s_rx_stat *)data->buf;
1476*31d98677SRui Paulo 	if ((le32toh(stat->rxdw1) & 0x1ff) == 0x1ff) {
1477*31d98677SRui Paulo 		rsu_rx_multi_event(sc, data->buf, len);
1478*31d98677SRui Paulo 		/* No packets to process. */
1479*31d98677SRui Paulo 		return (NULL);
1480*31d98677SRui Paulo 	} else
1481*31d98677SRui Paulo 		return (rsu_rx_multi_frame(sc, data->buf, len, rssi));
1482*31d98677SRui Paulo }
1483*31d98677SRui Paulo 
1484*31d98677SRui Paulo static void
1485*31d98677SRui Paulo rsu_bulk_rx_callback(struct usb_xfer *xfer, usb_error_t error)
1486*31d98677SRui Paulo {
1487*31d98677SRui Paulo 	struct rsu_softc *sc = usbd_xfer_softc(xfer);
1488*31d98677SRui Paulo 	struct ifnet *ifp = sc->sc_ifp;
1489*31d98677SRui Paulo 	struct ieee80211com *ic = ifp->if_l2com;
1490*31d98677SRui Paulo 	struct ieee80211_frame *wh;
1491*31d98677SRui Paulo 	struct ieee80211_node *ni;
1492*31d98677SRui Paulo 	struct mbuf *m = NULL, *next;
1493*31d98677SRui Paulo 	struct rsu_data *data;
1494*31d98677SRui Paulo 	int rssi = 1;
1495*31d98677SRui Paulo 
1496*31d98677SRui Paulo 	RSU_ASSERT_LOCKED(sc);
1497*31d98677SRui Paulo 
1498*31d98677SRui Paulo 	switch (USB_GET_STATE(xfer)) {
1499*31d98677SRui Paulo 	case USB_ST_TRANSFERRED:
1500*31d98677SRui Paulo 		data = STAILQ_FIRST(&sc->sc_rx_active);
1501*31d98677SRui Paulo 		if (data == NULL)
1502*31d98677SRui Paulo 			goto tr_setup;
1503*31d98677SRui Paulo 		STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next);
1504*31d98677SRui Paulo 		m = rsu_rxeof(xfer, data, &rssi);
1505*31d98677SRui Paulo 		STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next);
1506*31d98677SRui Paulo 		/* FALLTHROUGH */
1507*31d98677SRui Paulo 	case USB_ST_SETUP:
1508*31d98677SRui Paulo tr_setup:
1509*31d98677SRui Paulo 		data = STAILQ_FIRST(&sc->sc_rx_inactive);
1510*31d98677SRui Paulo 		if (data == NULL) {
1511*31d98677SRui Paulo 			KASSERT(m == NULL, ("mbuf isn't NULL"));
1512*31d98677SRui Paulo 			return;
1513*31d98677SRui Paulo 		}
1514*31d98677SRui Paulo 		STAILQ_REMOVE_HEAD(&sc->sc_rx_inactive, next);
1515*31d98677SRui Paulo 		STAILQ_INSERT_TAIL(&sc->sc_rx_active, data, next);
1516*31d98677SRui Paulo 		usbd_xfer_set_frame_data(xfer, 0, data->buf,
1517*31d98677SRui Paulo 		    usbd_xfer_max_len(xfer));
1518*31d98677SRui Paulo 		usbd_transfer_submit(xfer);
1519*31d98677SRui Paulo 		/*
1520*31d98677SRui Paulo 		 * To avoid LOR we should unlock our private mutex here to call
1521*31d98677SRui Paulo 		 * ieee80211_input() because here is at the end of a USB
1522*31d98677SRui Paulo 		 * callback and safe to unlock.
1523*31d98677SRui Paulo 		 */
1524*31d98677SRui Paulo 		RSU_UNLOCK(sc);
1525*31d98677SRui Paulo 		while (m != NULL) {
1526*31d98677SRui Paulo 			next = m->m_next;
1527*31d98677SRui Paulo 			m->m_next = NULL;
1528*31d98677SRui Paulo 			wh = mtod(m, struct ieee80211_frame *);
1529*31d98677SRui Paulo 			ni = ieee80211_find_rxnode(ic,
1530*31d98677SRui Paulo 			    (struct ieee80211_frame_min *)wh);
1531*31d98677SRui Paulo 			if (ni != NULL) {
1532*31d98677SRui Paulo 				(void)ieee80211_input(ni, m, rssi, 0);
1533*31d98677SRui Paulo 				ieee80211_free_node(ni);
1534*31d98677SRui Paulo 			} else
1535*31d98677SRui Paulo 				(void)ieee80211_input_all(ic, m, rssi, 0);
1536*31d98677SRui Paulo 			m = next;
1537*31d98677SRui Paulo 		}
1538*31d98677SRui Paulo 		RSU_LOCK(sc);
1539*31d98677SRui Paulo 		break;
1540*31d98677SRui Paulo 	default:
1541*31d98677SRui Paulo 		/* needs it to the inactive queue due to a error. */
1542*31d98677SRui Paulo 		data = STAILQ_FIRST(&sc->sc_rx_active);
1543*31d98677SRui Paulo 		if (data != NULL) {
1544*31d98677SRui Paulo 			STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next);
1545*31d98677SRui Paulo 			STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next);
1546*31d98677SRui Paulo 		}
1547*31d98677SRui Paulo 		if (error != USB_ERR_CANCELLED) {
1548*31d98677SRui Paulo 			usbd_xfer_set_stall(xfer);
1549*31d98677SRui Paulo 			ifp->if_ierrors++;
1550*31d98677SRui Paulo 			goto tr_setup;
1551*31d98677SRui Paulo 		}
1552*31d98677SRui Paulo 		break;
1553*31d98677SRui Paulo 	}
1554*31d98677SRui Paulo 
1555*31d98677SRui Paulo }
1556*31d98677SRui Paulo 
1557*31d98677SRui Paulo 
1558*31d98677SRui Paulo static void
1559*31d98677SRui Paulo rsu_txeof(struct usb_xfer *xfer, struct rsu_data *data)
1560*31d98677SRui Paulo {
1561*31d98677SRui Paulo 	struct rsu_softc *sc = usbd_xfer_softc(xfer);
1562*31d98677SRui Paulo 	struct ifnet *ifp = sc->sc_ifp;
1563*31d98677SRui Paulo 	struct mbuf *m;
1564*31d98677SRui Paulo 
1565*31d98677SRui Paulo 	RSU_ASSERT_LOCKED(sc);
1566*31d98677SRui Paulo 
1567*31d98677SRui Paulo 	/*
1568*31d98677SRui Paulo 	 * Do any tx complete callback.  Note this must be done before releasing
1569*31d98677SRui Paulo 	 * the node reference.
1570*31d98677SRui Paulo 	 */
1571*31d98677SRui Paulo 	if (data->m) {
1572*31d98677SRui Paulo 		m = data->m;
1573*31d98677SRui Paulo 		if (m->m_flags & M_TXCB) {
1574*31d98677SRui Paulo 			/* XXX status? */
1575*31d98677SRui Paulo 			ieee80211_process_callback(data->ni, m, 0);
1576*31d98677SRui Paulo 		}
1577*31d98677SRui Paulo 		m_freem(m);
1578*31d98677SRui Paulo 		data->m = NULL;
1579*31d98677SRui Paulo 	}
1580*31d98677SRui Paulo 	if (data->ni) {
1581*31d98677SRui Paulo 		ieee80211_free_node(data->ni);
1582*31d98677SRui Paulo 		data->ni = NULL;
1583*31d98677SRui Paulo 	}
1584*31d98677SRui Paulo 	sc->sc_tx_timer = 0;
1585*31d98677SRui Paulo 	ifp->if_opackets++;
1586*31d98677SRui Paulo 	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
1587*31d98677SRui Paulo }
1588*31d98677SRui Paulo 
1589*31d98677SRui Paulo static void
1590*31d98677SRui Paulo rsu_bulk_tx_callback(struct usb_xfer *xfer, usb_error_t error)
1591*31d98677SRui Paulo {
1592*31d98677SRui Paulo 	struct rsu_softc *sc = usbd_xfer_softc(xfer);
1593*31d98677SRui Paulo 	struct ifnet *ifp = sc->sc_ifp;
1594*31d98677SRui Paulo 	struct rsu_data *data;
1595*31d98677SRui Paulo 
1596*31d98677SRui Paulo 	RSU_ASSERT_LOCKED(sc);
1597*31d98677SRui Paulo 
1598*31d98677SRui Paulo 	switch (USB_GET_STATE(xfer)) {
1599*31d98677SRui Paulo 	case USB_ST_TRANSFERRED:
1600*31d98677SRui Paulo 		data = STAILQ_FIRST(&sc->sc_tx_active);
1601*31d98677SRui Paulo 		if (data == NULL)
1602*31d98677SRui Paulo 			goto tr_setup;
1603*31d98677SRui Paulo 		DPRINTF("transfer done %p\n", data);
1604*31d98677SRui Paulo 		STAILQ_REMOVE_HEAD(&sc->sc_tx_active, next);
1605*31d98677SRui Paulo 		rsu_txeof(xfer, data);
1606*31d98677SRui Paulo 		STAILQ_INSERT_TAIL(&sc->sc_tx_inactive, data, next);
1607*31d98677SRui Paulo 		/* FALLTHROUGH */
1608*31d98677SRui Paulo 	case USB_ST_SETUP:
1609*31d98677SRui Paulo tr_setup:
1610*31d98677SRui Paulo 		data = STAILQ_FIRST(&sc->sc_tx_pending);
1611*31d98677SRui Paulo 		if (data == NULL) {
1612*31d98677SRui Paulo 			DPRINTF("empty pending queue sc %p\n", sc);
1613*31d98677SRui Paulo 			return;
1614*31d98677SRui Paulo 		}
1615*31d98677SRui Paulo 		STAILQ_REMOVE_HEAD(&sc->sc_tx_pending, next);
1616*31d98677SRui Paulo 		STAILQ_INSERT_TAIL(&sc->sc_tx_active, data, next);
1617*31d98677SRui Paulo 		usbd_xfer_set_frame_data(xfer, 0, data->buf, data->buflen);
1618*31d98677SRui Paulo 		DPRINTF("submitting transfer %p\n", data);
1619*31d98677SRui Paulo 		usbd_transfer_submit(xfer);
1620*31d98677SRui Paulo 		rsu_start_locked(ifp);
1621*31d98677SRui Paulo 		break;
1622*31d98677SRui Paulo 	default:
1623*31d98677SRui Paulo 		data = STAILQ_FIRST(&sc->sc_tx_active);
1624*31d98677SRui Paulo 		if (data == NULL)
1625*31d98677SRui Paulo 			goto tr_setup;
1626*31d98677SRui Paulo 		if (data->ni != NULL) {
1627*31d98677SRui Paulo 			ieee80211_free_node(data->ni);
1628*31d98677SRui Paulo 			data->ni = NULL;
1629*31d98677SRui Paulo 			ifp->if_oerrors++;
1630*31d98677SRui Paulo 		}
1631*31d98677SRui Paulo 		if (error != USB_ERR_CANCELLED) {
1632*31d98677SRui Paulo 			usbd_xfer_set_stall(xfer);
1633*31d98677SRui Paulo 			goto tr_setup;
1634*31d98677SRui Paulo 		}
1635*31d98677SRui Paulo 		break;
1636*31d98677SRui Paulo 	}
1637*31d98677SRui Paulo }
1638*31d98677SRui Paulo 
1639*31d98677SRui Paulo static int
1640*31d98677SRui Paulo rsu_tx_start(struct rsu_softc *sc, struct ieee80211_node *ni,
1641*31d98677SRui Paulo     struct mbuf *m0, struct rsu_data *data)
1642*31d98677SRui Paulo {
1643*31d98677SRui Paulo 	struct ifnet *ifp = sc->sc_ifp;
1644*31d98677SRui Paulo 	struct ieee80211com *ic = ifp->if_l2com;
1645*31d98677SRui Paulo         struct ieee80211vap *vap = ni->ni_vap;
1646*31d98677SRui Paulo 	struct ieee80211_frame *wh;
1647*31d98677SRui Paulo 	struct ieee80211_key *k = NULL;
1648*31d98677SRui Paulo 	struct r92s_tx_desc *txd;
1649*31d98677SRui Paulo 	struct usb_xfer *xfer;
1650*31d98677SRui Paulo 	uint8_t type, tid = 0;
1651*31d98677SRui Paulo 	int hasqos, xferlen;
1652*31d98677SRui Paulo 	struct usb_xfer *rsu_pipes[4] = {
1653*31d98677SRui Paulo 		sc->sc_xfer[RSU_BULK_TX_BE],
1654*31d98677SRui Paulo 		sc->sc_xfer[RSU_BULK_TX_BK],
1655*31d98677SRui Paulo 		sc->sc_xfer[RSU_BULK_TX_VI],
1656*31d98677SRui Paulo 		sc->sc_xfer[RSU_BULK_TX_VO]
1657*31d98677SRui Paulo 	};
1658*31d98677SRui Paulo 
1659*31d98677SRui Paulo 	RSU_ASSERT_LOCKED(sc);
1660*31d98677SRui Paulo 
1661*31d98677SRui Paulo 	wh = mtod(m0, struct ieee80211_frame *);
1662*31d98677SRui Paulo 	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
1663*31d98677SRui Paulo 
1664*31d98677SRui Paulo 	if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
1665*31d98677SRui Paulo 		k = ieee80211_crypto_encap(ni, m0);
1666*31d98677SRui Paulo 		if (k == NULL) {
1667*31d98677SRui Paulo 			device_printf(sc->sc_dev,
1668*31d98677SRui Paulo 			    "ieee80211_crypto_encap returns NULL.\n");
1669*31d98677SRui Paulo 			/* XXX we don't expect the fragmented frames */
1670*31d98677SRui Paulo 			m_freem(m0);
1671*31d98677SRui Paulo 			return (ENOBUFS);
1672*31d98677SRui Paulo 		}
1673*31d98677SRui Paulo 		wh = mtod(m0, struct ieee80211_frame *);
1674*31d98677SRui Paulo 	}
1675*31d98677SRui Paulo 	switch (type) {
1676*31d98677SRui Paulo 	case IEEE80211_FC0_TYPE_CTL:
1677*31d98677SRui Paulo 	case IEEE80211_FC0_TYPE_MGT:
1678*31d98677SRui Paulo 		xfer = sc->sc_xfer[RSU_BULK_TX_VO];
1679*31d98677SRui Paulo 		break;
1680*31d98677SRui Paulo 	default:
1681*31d98677SRui Paulo 		KASSERT(M_WME_GETAC(m0) < 4,
1682*31d98677SRui Paulo 		    ("unsupported WME pipe %d", M_WME_GETAC(m0)));
1683*31d98677SRui Paulo 		xfer = rsu_pipes[M_WME_GETAC(m0)];
1684*31d98677SRui Paulo 		break;
1685*31d98677SRui Paulo 	}
1686*31d98677SRui Paulo 	hasqos = 0;
1687*31d98677SRui Paulo 
1688*31d98677SRui Paulo 	/* Fill Tx descriptor. */
1689*31d98677SRui Paulo 	txd = (struct r92s_tx_desc *)data->buf;
1690*31d98677SRui Paulo 	memset(txd, 0, sizeof(*txd));
1691*31d98677SRui Paulo 
1692*31d98677SRui Paulo 	txd->txdw0 |= htole32(
1693*31d98677SRui Paulo 	    SM(R92S_TXDW0_PKTLEN, m0->m_pkthdr.len) |
1694*31d98677SRui Paulo 	    SM(R92S_TXDW0_OFFSET, sizeof(*txd)) |
1695*31d98677SRui Paulo 	    R92S_TXDW0_OWN | R92S_TXDW0_FSG | R92S_TXDW0_LSG);
1696*31d98677SRui Paulo 
1697*31d98677SRui Paulo 	txd->txdw1 |= htole32(
1698*31d98677SRui Paulo 	    SM(R92S_TXDW1_MACID, R92S_MACID_BSS) |
1699*31d98677SRui Paulo 	    SM(R92S_TXDW1_QSEL, R92S_TXDW1_QSEL_BE));
1700*31d98677SRui Paulo 	if (!hasqos)
1701*31d98677SRui Paulo 		txd->txdw1 |= htole32(R92S_TXDW1_NONQOS);
1702*31d98677SRui Paulo #ifdef notyet
1703*31d98677SRui Paulo 	if (k != NULL) {
1704*31d98677SRui Paulo 		switch (k->wk_cipher->ic_cipher) {
1705*31d98677SRui Paulo 		case IEEE80211_CIPHER_WEP:
1706*31d98677SRui Paulo 			cipher = R92S_TXDW1_CIPHER_WEP;
1707*31d98677SRui Paulo 			break;
1708*31d98677SRui Paulo 		case IEEE80211_CIPHER_TKIP:
1709*31d98677SRui Paulo 			cipher = R92S_TXDW1_CIPHER_TKIP;
1710*31d98677SRui Paulo 			break;
1711*31d98677SRui Paulo 		case IEEE80211_CIPHER_AES_CCM:
1712*31d98677SRui Paulo 			cipher = R92S_TXDW1_CIPHER_AES;
1713*31d98677SRui Paulo 			break;
1714*31d98677SRui Paulo 		default:
1715*31d98677SRui Paulo 			cipher = R92S_TXDW1_CIPHER_NONE;
1716*31d98677SRui Paulo 		}
1717*31d98677SRui Paulo 		txd->txdw1 |= htole32(
1718*31d98677SRui Paulo 		    SM(R92S_TXDW1_CIPHER, cipher) |
1719*31d98677SRui Paulo 		    SM(R92S_TXDW1_KEYIDX, k->k_id));
1720*31d98677SRui Paulo 	}
1721*31d98677SRui Paulo #endif
1722*31d98677SRui Paulo 	txd->txdw2 |= htole32(R92S_TXDW2_BK);
1723*31d98677SRui Paulo 	if (IEEE80211_IS_MULTICAST(wh->i_addr1))
1724*31d98677SRui Paulo 		txd->txdw2 |= htole32(R92S_TXDW2_BMCAST);
1725*31d98677SRui Paulo 	/*
1726*31d98677SRui Paulo 	 * Firmware will use and increment the sequence number for the
1727*31d98677SRui Paulo 	 * specified TID.
1728*31d98677SRui Paulo 	 */
1729*31d98677SRui Paulo 	txd->txdw3 |= htole32(SM(R92S_TXDW3_SEQ, tid));
1730*31d98677SRui Paulo 
1731*31d98677SRui Paulo 	if (ieee80211_radiotap_active_vap(vap)) {
1732*31d98677SRui Paulo 		struct rsu_tx_radiotap_header *tap = &sc->sc_txtap;
1733*31d98677SRui Paulo 
1734*31d98677SRui Paulo 		tap->wt_flags = 0;
1735*31d98677SRui Paulo 		tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq);
1736*31d98677SRui Paulo 		tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
1737*31d98677SRui Paulo 		ieee80211_radiotap_tx(vap, m0);
1738*31d98677SRui Paulo 	}
1739*31d98677SRui Paulo 	xferlen = sizeof(*txd) + m0->m_pkthdr.len;
1740*31d98677SRui Paulo 	m_copydata(m0, 0, m0->m_pkthdr.len, (caddr_t)&txd[1]);
1741*31d98677SRui Paulo 
1742*31d98677SRui Paulo 	data->buflen = xferlen;
1743*31d98677SRui Paulo 	data->ni = ni;
1744*31d98677SRui Paulo 	data->m = m0;
1745*31d98677SRui Paulo 	STAILQ_INSERT_TAIL(&sc->sc_tx_pending, data, next);
1746*31d98677SRui Paulo 	usbd_transfer_start(xfer);
1747*31d98677SRui Paulo 
1748*31d98677SRui Paulo 	return (0);
1749*31d98677SRui Paulo }
1750*31d98677SRui Paulo 
1751*31d98677SRui Paulo static void
1752*31d98677SRui Paulo rsu_start(struct ifnet *ifp)
1753*31d98677SRui Paulo {
1754*31d98677SRui Paulo 	struct rsu_softc *sc = ifp->if_softc;
1755*31d98677SRui Paulo 
1756*31d98677SRui Paulo 	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
1757*31d98677SRui Paulo 		return;
1758*31d98677SRui Paulo 
1759*31d98677SRui Paulo 	RSU_LOCK(sc);
1760*31d98677SRui Paulo 	rsu_start_locked(ifp);
1761*31d98677SRui Paulo 	RSU_UNLOCK(sc);
1762*31d98677SRui Paulo }
1763*31d98677SRui Paulo 
1764*31d98677SRui Paulo static void
1765*31d98677SRui Paulo rsu_start_locked(struct ifnet *ifp)
1766*31d98677SRui Paulo {
1767*31d98677SRui Paulo 	struct rsu_softc *sc = ifp->if_softc;
1768*31d98677SRui Paulo 	struct ieee80211_node *ni;
1769*31d98677SRui Paulo 	struct mbuf *m;
1770*31d98677SRui Paulo 	struct rsu_data *bf;
1771*31d98677SRui Paulo 
1772*31d98677SRui Paulo 	RSU_ASSERT_LOCKED(sc);
1773*31d98677SRui Paulo 
1774*31d98677SRui Paulo 	for (;;) {
1775*31d98677SRui Paulo 		IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
1776*31d98677SRui Paulo 		if (m == NULL)
1777*31d98677SRui Paulo 			break;
1778*31d98677SRui Paulo 		bf = rsu_getbuf(sc);
1779*31d98677SRui Paulo 		if (bf == NULL) {
1780*31d98677SRui Paulo 			IFQ_DRV_PREPEND(&ifp->if_snd, m);
1781*31d98677SRui Paulo 			break;
1782*31d98677SRui Paulo 		}
1783*31d98677SRui Paulo 		ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
1784*31d98677SRui Paulo 		m->m_pkthdr.rcvif = NULL;
1785*31d98677SRui Paulo 
1786*31d98677SRui Paulo 		if (rsu_tx_start(sc, ni, m, bf) != 0) {
1787*31d98677SRui Paulo 			ifp->if_oerrors++;
1788*31d98677SRui Paulo 			STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, bf, next);
1789*31d98677SRui Paulo 			ieee80211_free_node(ni);
1790*31d98677SRui Paulo 			break;
1791*31d98677SRui Paulo 		}
1792*31d98677SRui Paulo 		sc->sc_tx_timer = 5;
1793*31d98677SRui Paulo 		callout_reset(&sc->sc_watchdog_ch, hz, rsu_watchdog, sc);
1794*31d98677SRui Paulo 	}
1795*31d98677SRui Paulo }
1796*31d98677SRui Paulo 
1797*31d98677SRui Paulo static void
1798*31d98677SRui Paulo rsu_watchdog(void *arg)
1799*31d98677SRui Paulo {
1800*31d98677SRui Paulo 	struct rsu_softc *sc = arg;
1801*31d98677SRui Paulo 	struct ifnet *ifp = sc->sc_ifp;
1802*31d98677SRui Paulo 
1803*31d98677SRui Paulo 	if (sc->sc_tx_timer > 0) {
1804*31d98677SRui Paulo 		if (--sc->sc_tx_timer == 0) {
1805*31d98677SRui Paulo 			device_printf(sc->sc_dev, "device timeout\n");
1806*31d98677SRui Paulo 			/* rsu_init(ifp); XXX needs a process context! */
1807*31d98677SRui Paulo 			ifp->if_oerrors++;
1808*31d98677SRui Paulo 			return;
1809*31d98677SRui Paulo 		}
1810*31d98677SRui Paulo 		callout_reset(&sc->sc_watchdog_ch, hz, rsu_watchdog, sc);
1811*31d98677SRui Paulo 	}
1812*31d98677SRui Paulo }
1813*31d98677SRui Paulo 
1814*31d98677SRui Paulo static int
1815*31d98677SRui Paulo rsu_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
1816*31d98677SRui Paulo {
1817*31d98677SRui Paulo 	struct ieee80211com *ic = ifp->if_l2com;
1818*31d98677SRui Paulo 	struct ifreq *ifr = (struct ifreq *) data;
1819*31d98677SRui Paulo 	int error = 0, startall = 0;
1820*31d98677SRui Paulo 
1821*31d98677SRui Paulo 	switch (cmd) {
1822*31d98677SRui Paulo 	case SIOCSIFFLAGS:
1823*31d98677SRui Paulo 		if (ifp->if_flags & IFF_UP) {
1824*31d98677SRui Paulo 			if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
1825*31d98677SRui Paulo 				rsu_init(ifp->if_softc);
1826*31d98677SRui Paulo 				startall = 1;
1827*31d98677SRui Paulo 			}
1828*31d98677SRui Paulo 		} else {
1829*31d98677SRui Paulo 			if (ifp->if_drv_flags & IFF_DRV_RUNNING)
1830*31d98677SRui Paulo 				rsu_stop(ifp, 1);
1831*31d98677SRui Paulo 		}
1832*31d98677SRui Paulo 		if (startall)
1833*31d98677SRui Paulo 			ieee80211_start_all(ic);
1834*31d98677SRui Paulo 		break;
1835*31d98677SRui Paulo 	case SIOCGIFMEDIA:
1836*31d98677SRui Paulo 		error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd);
1837*31d98677SRui Paulo 		break;
1838*31d98677SRui Paulo 	case SIOCGIFADDR:
1839*31d98677SRui Paulo 		error = ether_ioctl(ifp, cmd, data);
1840*31d98677SRui Paulo 		break;
1841*31d98677SRui Paulo 	default:
1842*31d98677SRui Paulo 		error = EINVAL;
1843*31d98677SRui Paulo 		break;
1844*31d98677SRui Paulo 	}
1845*31d98677SRui Paulo 
1846*31d98677SRui Paulo 	return (error);
1847*31d98677SRui Paulo }
1848*31d98677SRui Paulo 
1849*31d98677SRui Paulo /*
1850*31d98677SRui Paulo  * Power on sequence for A-cut adapters.
1851*31d98677SRui Paulo  */
1852*31d98677SRui Paulo static void
1853*31d98677SRui Paulo rsu_power_on_acut(struct rsu_softc *sc)
1854*31d98677SRui Paulo {
1855*31d98677SRui Paulo 	uint32_t reg;
1856*31d98677SRui Paulo 
1857*31d98677SRui Paulo 	rsu_write_1(sc, R92S_SPS0_CTRL + 1, 0x53);
1858*31d98677SRui Paulo 	rsu_write_1(sc, R92S_SPS0_CTRL + 0, 0x57);
1859*31d98677SRui Paulo 
1860*31d98677SRui Paulo 	/* Enable AFE macro block's bandgap and Mbias. */
1861*31d98677SRui Paulo 	rsu_write_1(sc, R92S_AFE_MISC,
1862*31d98677SRui Paulo 	    rsu_read_1(sc, R92S_AFE_MISC) |
1863*31d98677SRui Paulo 	    R92S_AFE_MISC_BGEN | R92S_AFE_MISC_MBEN);
1864*31d98677SRui Paulo 	/* Enable LDOA15 block. */
1865*31d98677SRui Paulo 	rsu_write_1(sc, R92S_LDOA15_CTRL,
1866*31d98677SRui Paulo 	    rsu_read_1(sc, R92S_LDOA15_CTRL) | R92S_LDA15_EN);
1867*31d98677SRui Paulo 
1868*31d98677SRui Paulo 	rsu_write_1(sc, R92S_SPS1_CTRL,
1869*31d98677SRui Paulo 	    rsu_read_1(sc, R92S_SPS1_CTRL) | R92S_SPS1_LDEN);
1870*31d98677SRui Paulo 	usb_pause_mtx(&sc->sc_mtx, 2 * hz);
1871*31d98677SRui Paulo 	/* Enable switch regulator block. */
1872*31d98677SRui Paulo 	rsu_write_1(sc, R92S_SPS1_CTRL,
1873*31d98677SRui Paulo 	    rsu_read_1(sc, R92S_SPS1_CTRL) | R92S_SPS1_SWEN);
1874*31d98677SRui Paulo 
1875*31d98677SRui Paulo 	rsu_write_4(sc, R92S_SPS1_CTRL, 0x00a7b267);
1876*31d98677SRui Paulo 
1877*31d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1,
1878*31d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_ISO_CTRL + 1) | 0x08);
1879*31d98677SRui Paulo 
1880*31d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_FUNC_EN + 1,
1881*31d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x20);
1882*31d98677SRui Paulo 
1883*31d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1,
1884*31d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_ISO_CTRL + 1) & ~0x90);
1885*31d98677SRui Paulo 
1886*31d98677SRui Paulo 	/* Enable AFE clock. */
1887*31d98677SRui Paulo 	rsu_write_1(sc, R92S_AFE_XTAL_CTRL + 1,
1888*31d98677SRui Paulo 	    rsu_read_1(sc, R92S_AFE_XTAL_CTRL + 1) & ~0x04);
1889*31d98677SRui Paulo 	/* Enable AFE PLL macro block. */
1890*31d98677SRui Paulo 	rsu_write_1(sc, R92S_AFE_PLL_CTRL,
1891*31d98677SRui Paulo 	    rsu_read_1(sc, R92S_AFE_PLL_CTRL) | 0x11);
1892*31d98677SRui Paulo 	/* Attach AFE PLL to MACTOP/BB. */
1893*31d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_ISO_CTRL,
1894*31d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_ISO_CTRL) & ~0x11);
1895*31d98677SRui Paulo 
1896*31d98677SRui Paulo 	/* Switch to 40MHz clock instead of 80MHz. */
1897*31d98677SRui Paulo 	rsu_write_2(sc, R92S_SYS_CLKR,
1898*31d98677SRui Paulo 	    rsu_read_2(sc, R92S_SYS_CLKR) & ~R92S_SYS_CLKSEL);
1899*31d98677SRui Paulo 
1900*31d98677SRui Paulo 	/* Enable MAC clock. */
1901*31d98677SRui Paulo 	rsu_write_2(sc, R92S_SYS_CLKR,
1902*31d98677SRui Paulo 	    rsu_read_2(sc, R92S_SYS_CLKR) |
1903*31d98677SRui Paulo 	    R92S_MAC_CLK_EN | R92S_SYS_CLK_EN);
1904*31d98677SRui Paulo 
1905*31d98677SRui Paulo 	rsu_write_1(sc, R92S_PMC_FSM, 0x02);
1906*31d98677SRui Paulo 
1907*31d98677SRui Paulo 	/* Enable digital core and IOREG R/W. */
1908*31d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_FUNC_EN + 1,
1909*31d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x08);
1910*31d98677SRui Paulo 
1911*31d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_FUNC_EN + 1,
1912*31d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x80);
1913*31d98677SRui Paulo 
1914*31d98677SRui Paulo 	/* Switch the control path to firmware. */
1915*31d98677SRui Paulo 	reg = rsu_read_2(sc, R92S_SYS_CLKR);
1916*31d98677SRui Paulo 	reg = (reg & ~R92S_SWHW_SEL) | R92S_FWHW_SEL;
1917*31d98677SRui Paulo 	rsu_write_2(sc, R92S_SYS_CLKR, reg);
1918*31d98677SRui Paulo 
1919*31d98677SRui Paulo 	rsu_write_2(sc, R92S_CR, 0x37fc);
1920*31d98677SRui Paulo 
1921*31d98677SRui Paulo 	/* Fix USB RX FIFO issue. */
1922*31d98677SRui Paulo 	rsu_write_1(sc, 0xfe5c,
1923*31d98677SRui Paulo 	    rsu_read_1(sc, 0xfe5c) | 0x80);
1924*31d98677SRui Paulo 	rsu_write_1(sc, 0x00ab,
1925*31d98677SRui Paulo 	    rsu_read_1(sc, 0x00ab) | 0xc0);
1926*31d98677SRui Paulo 
1927*31d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_CLKR,
1928*31d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_CLKR) & ~R92S_SYS_CPU_CLKSEL);
1929*31d98677SRui Paulo }
1930*31d98677SRui Paulo 
1931*31d98677SRui Paulo /*
1932*31d98677SRui Paulo  * Power on sequence for B-cut and C-cut adapters.
1933*31d98677SRui Paulo  */
1934*31d98677SRui Paulo static void
1935*31d98677SRui Paulo rsu_power_on_bcut(struct rsu_softc *sc)
1936*31d98677SRui Paulo {
1937*31d98677SRui Paulo 	uint32_t reg;
1938*31d98677SRui Paulo 	int ntries;
1939*31d98677SRui Paulo 
1940*31d98677SRui Paulo 	/* Prevent eFuse leakage. */
1941*31d98677SRui Paulo 	rsu_write_1(sc, 0x37, 0xb0);
1942*31d98677SRui Paulo 	usb_pause_mtx(&sc->sc_mtx, 10);
1943*31d98677SRui Paulo 	rsu_write_1(sc, 0x37, 0x30);
1944*31d98677SRui Paulo 
1945*31d98677SRui Paulo 	/* Switch the control path to hardware. */
1946*31d98677SRui Paulo 	reg = rsu_read_2(sc, R92S_SYS_CLKR);
1947*31d98677SRui Paulo 	if (reg & R92S_FWHW_SEL) {
1948*31d98677SRui Paulo 		rsu_write_2(sc, R92S_SYS_CLKR,
1949*31d98677SRui Paulo 		    reg & ~(R92S_SWHW_SEL | R92S_FWHW_SEL));
1950*31d98677SRui Paulo 	}
1951*31d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_FUNC_EN + 1,
1952*31d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) & ~0x8c);
1953*31d98677SRui Paulo 	DELAY(1000);
1954*31d98677SRui Paulo 
1955*31d98677SRui Paulo 	rsu_write_1(sc, R92S_SPS0_CTRL + 1, 0x53);
1956*31d98677SRui Paulo 	rsu_write_1(sc, R92S_SPS0_CTRL + 0, 0x57);
1957*31d98677SRui Paulo 
1958*31d98677SRui Paulo 	reg = rsu_read_1(sc, R92S_AFE_MISC);
1959*31d98677SRui Paulo 	rsu_write_1(sc, R92S_AFE_MISC, reg | R92S_AFE_MISC_BGEN);
1960*31d98677SRui Paulo 	rsu_write_1(sc, R92S_AFE_MISC, reg | R92S_AFE_MISC_BGEN |
1961*31d98677SRui Paulo 	    R92S_AFE_MISC_MBEN | R92S_AFE_MISC_I32_EN);
1962*31d98677SRui Paulo 
1963*31d98677SRui Paulo 	/* Enable PLL. */
1964*31d98677SRui Paulo 	rsu_write_1(sc, R92S_LDOA15_CTRL,
1965*31d98677SRui Paulo 	    rsu_read_1(sc, R92S_LDOA15_CTRL) | R92S_LDA15_EN);
1966*31d98677SRui Paulo 
1967*31d98677SRui Paulo 	rsu_write_1(sc, R92S_LDOV12D_CTRL,
1968*31d98677SRui Paulo 	    rsu_read_1(sc, R92S_LDOV12D_CTRL) | R92S_LDV12_EN);
1969*31d98677SRui Paulo 
1970*31d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1,
1971*31d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_ISO_CTRL + 1) | 0x08);
1972*31d98677SRui Paulo 
1973*31d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_FUNC_EN + 1,
1974*31d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x20);
1975*31d98677SRui Paulo 
1976*31d98677SRui Paulo 	/* Support 64KB IMEM. */
1977*31d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1,
1978*31d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_ISO_CTRL + 1) & ~0x97);
1979*31d98677SRui Paulo 
1980*31d98677SRui Paulo 	/* Enable AFE clock. */
1981*31d98677SRui Paulo 	rsu_write_1(sc, R92S_AFE_XTAL_CTRL + 1,
1982*31d98677SRui Paulo 	    rsu_read_1(sc, R92S_AFE_XTAL_CTRL + 1) & ~0x04);
1983*31d98677SRui Paulo 	/* Enable AFE PLL macro block. */
1984*31d98677SRui Paulo 	reg = rsu_read_1(sc, R92S_AFE_PLL_CTRL);
1985*31d98677SRui Paulo 	rsu_write_1(sc, R92S_AFE_PLL_CTRL, reg | 0x11);
1986*31d98677SRui Paulo 	DELAY(500);
1987*31d98677SRui Paulo 	rsu_write_1(sc, R92S_AFE_PLL_CTRL, reg | 0x51);
1988*31d98677SRui Paulo 	DELAY(500);
1989*31d98677SRui Paulo 	rsu_write_1(sc, R92S_AFE_PLL_CTRL, reg | 0x11);
1990*31d98677SRui Paulo 	DELAY(500);
1991*31d98677SRui Paulo 
1992*31d98677SRui Paulo 	/* Attach AFE PLL to MACTOP/BB. */
1993*31d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_ISO_CTRL,
1994*31d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_ISO_CTRL) & ~0x11);
1995*31d98677SRui Paulo 
1996*31d98677SRui Paulo 	/* Switch to 40MHz clock. */
1997*31d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_CLKR, 0x00);
1998*31d98677SRui Paulo 	/* Disable CPU clock and 80MHz SSC. */
1999*31d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_CLKR,
2000*31d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_CLKR) | 0xa0);
2001*31d98677SRui Paulo 	/* Enable MAC clock. */
2002*31d98677SRui Paulo 	rsu_write_2(sc, R92S_SYS_CLKR,
2003*31d98677SRui Paulo 	    rsu_read_2(sc, R92S_SYS_CLKR) |
2004*31d98677SRui Paulo 	    R92S_MAC_CLK_EN | R92S_SYS_CLK_EN);
2005*31d98677SRui Paulo 
2006*31d98677SRui Paulo 	rsu_write_1(sc, R92S_PMC_FSM, 0x02);
2007*31d98677SRui Paulo 
2008*31d98677SRui Paulo 	/* Enable digital core and IOREG R/W. */
2009*31d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_FUNC_EN + 1,
2010*31d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x08);
2011*31d98677SRui Paulo 
2012*31d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_FUNC_EN + 1,
2013*31d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x80);
2014*31d98677SRui Paulo 
2015*31d98677SRui Paulo 	/* Switch the control path to firmware. */
2016*31d98677SRui Paulo 	reg = rsu_read_2(sc, R92S_SYS_CLKR);
2017*31d98677SRui Paulo 	reg = (reg & ~R92S_SWHW_SEL) | R92S_FWHW_SEL;
2018*31d98677SRui Paulo 	rsu_write_2(sc, R92S_SYS_CLKR, reg);
2019*31d98677SRui Paulo 
2020*31d98677SRui Paulo 	rsu_write_2(sc, R92S_CR, 0x37fc);
2021*31d98677SRui Paulo 
2022*31d98677SRui Paulo 	/* Fix USB RX FIFO issue. */
2023*31d98677SRui Paulo 	rsu_write_1(sc, 0xfe5c,
2024*31d98677SRui Paulo 	    rsu_read_1(sc, 0xfe5c) | 0x80);
2025*31d98677SRui Paulo 
2026*31d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_CLKR,
2027*31d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_CLKR) & ~R92S_SYS_CPU_CLKSEL);
2028*31d98677SRui Paulo 
2029*31d98677SRui Paulo 	rsu_write_1(sc, 0xfe1c, 0x80);
2030*31d98677SRui Paulo 
2031*31d98677SRui Paulo 	/* Make sure TxDMA is ready to download firmware. */
2032*31d98677SRui Paulo 	for (ntries = 0; ntries < 20; ntries++) {
2033*31d98677SRui Paulo 		reg = rsu_read_1(sc, R92S_TCR);
2034*31d98677SRui Paulo 		if ((reg & (R92S_TCR_IMEM_CHK_RPT | R92S_TCR_EMEM_CHK_RPT)) ==
2035*31d98677SRui Paulo 		    (R92S_TCR_IMEM_CHK_RPT | R92S_TCR_EMEM_CHK_RPT))
2036*31d98677SRui Paulo 			break;
2037*31d98677SRui Paulo 		DELAY(5);
2038*31d98677SRui Paulo 	}
2039*31d98677SRui Paulo 	if (ntries == 20) {
2040*31d98677SRui Paulo 		DPRINTF("TxDMA is not ready\n");
2041*31d98677SRui Paulo 		/* Reset TxDMA. */
2042*31d98677SRui Paulo 		reg = rsu_read_1(sc, R92S_CR);
2043*31d98677SRui Paulo 		rsu_write_1(sc, R92S_CR, reg & ~R92S_CR_TXDMA_EN);
2044*31d98677SRui Paulo 		DELAY(2);
2045*31d98677SRui Paulo 		rsu_write_1(sc, R92S_CR, reg | R92S_CR_TXDMA_EN);
2046*31d98677SRui Paulo 	}
2047*31d98677SRui Paulo }
2048*31d98677SRui Paulo 
2049*31d98677SRui Paulo static void
2050*31d98677SRui Paulo rsu_power_off(struct rsu_softc *sc)
2051*31d98677SRui Paulo {
2052*31d98677SRui Paulo 	/* Turn RF off. */
2053*31d98677SRui Paulo 	rsu_write_1(sc, R92S_RF_CTRL, 0x00);
2054*31d98677SRui Paulo 	usb_pause_mtx(&sc->sc_mtx, 5);
2055*31d98677SRui Paulo 
2056*31d98677SRui Paulo 	/* Turn MAC off. */
2057*31d98677SRui Paulo 	/* Switch control path. */
2058*31d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_CLKR + 1, 0x38);
2059*31d98677SRui Paulo 	/* Reset MACTOP. */
2060*31d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, 0x70);
2061*31d98677SRui Paulo 	rsu_write_1(sc, R92S_PMC_FSM, 0x06);
2062*31d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_ISO_CTRL + 0, 0xf9);
2063*31d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1, 0xe8);
2064*31d98677SRui Paulo 
2065*31d98677SRui Paulo 	/* Disable AFE PLL. */
2066*31d98677SRui Paulo 	rsu_write_1(sc, R92S_AFE_PLL_CTRL, 0x00);
2067*31d98677SRui Paulo 	/* Disable A15V. */
2068*31d98677SRui Paulo 	rsu_write_1(sc, R92S_LDOA15_CTRL, 0x54);
2069*31d98677SRui Paulo 	/* Disable eFuse 1.2V. */
2070*31d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, 0x50);
2071*31d98677SRui Paulo 	rsu_write_1(sc, R92S_LDOV12D_CTRL, 0x24);
2072*31d98677SRui Paulo 	/* Enable AFE macro block's bandgap and Mbias. */
2073*31d98677SRui Paulo 	rsu_write_1(sc, R92S_AFE_MISC, 0x30);
2074*31d98677SRui Paulo 	/* Disable 1.6V LDO. */
2075*31d98677SRui Paulo 	rsu_write_1(sc, R92S_SPS0_CTRL + 0, 0x56);
2076*31d98677SRui Paulo 	rsu_write_1(sc, R92S_SPS0_CTRL + 1, 0x43);
2077*31d98677SRui Paulo }
2078*31d98677SRui Paulo 
2079*31d98677SRui Paulo static int
2080*31d98677SRui Paulo rsu_fw_loadsection(struct rsu_softc *sc, uint8_t *buf, int len)
2081*31d98677SRui Paulo {
2082*31d98677SRui Paulo 	struct rsu_data *data;
2083*31d98677SRui Paulo 	struct r92s_tx_desc *txd;
2084*31d98677SRui Paulo 	int mlen;
2085*31d98677SRui Paulo 
2086*31d98677SRui Paulo 	while (len > 0) {
2087*31d98677SRui Paulo 		data = rsu_getbuf(sc);
2088*31d98677SRui Paulo 		if (data == NULL)
2089*31d98677SRui Paulo 			return (ENOMEM);
2090*31d98677SRui Paulo 		txd = (struct r92s_tx_desc *)data->buf;
2091*31d98677SRui Paulo 		memset(txd, 0, sizeof(*txd));
2092*31d98677SRui Paulo 		if (len <= RSU_TXBUFSZ - sizeof(*txd)) {
2093*31d98677SRui Paulo 			/* Last chunk. */
2094*31d98677SRui Paulo 			txd->txdw0 |= htole32(R92S_TXDW0_LINIP);
2095*31d98677SRui Paulo 			mlen = len;
2096*31d98677SRui Paulo 		} else
2097*31d98677SRui Paulo 			mlen = RSU_TXBUFSZ - sizeof(*txd);
2098*31d98677SRui Paulo 		txd->txdw0 |= htole32(SM(R92S_TXDW0_PKTLEN, mlen));
2099*31d98677SRui Paulo 		memcpy(&txd[1], buf, mlen);
2100*31d98677SRui Paulo 		data->buflen = sizeof(*txd) + mlen;
2101*31d98677SRui Paulo 		DPRINTF("starting transfer %p\n", data);
2102*31d98677SRui Paulo 		STAILQ_INSERT_TAIL(&sc->sc_tx_pending, data, next);
2103*31d98677SRui Paulo 		buf += mlen;
2104*31d98677SRui Paulo 		len -= mlen;
2105*31d98677SRui Paulo 	}
2106*31d98677SRui Paulo 	usbd_transfer_start(sc->sc_xfer[RSU_BULK_TX_VO]);
2107*31d98677SRui Paulo 
2108*31d98677SRui Paulo 	return (0);
2109*31d98677SRui Paulo }
2110*31d98677SRui Paulo 
2111*31d98677SRui Paulo static int
2112*31d98677SRui Paulo rsu_load_firmware(struct rsu_softc *sc)
2113*31d98677SRui Paulo {
2114*31d98677SRui Paulo 	struct r92s_fw_hdr *hdr;
2115*31d98677SRui Paulo 	struct r92s_fw_priv *dmem;
2116*31d98677SRui Paulo 	uint8_t *imem, *emem;
2117*31d98677SRui Paulo 	int imemsz, ememsz;
2118*31d98677SRui Paulo 	const struct firmware *fw;
2119*31d98677SRui Paulo 	size_t size;
2120*31d98677SRui Paulo 	uint32_t reg;
2121*31d98677SRui Paulo 	int ntries, error;
2122*31d98677SRui Paulo 
2123*31d98677SRui Paulo 	RSU_UNLOCK(sc);
2124*31d98677SRui Paulo 	/* Read firmware image from the filesystem. */
2125*31d98677SRui Paulo 	if ((fw = firmware_get("rsu-rtl8712fw")) == NULL) {
2126*31d98677SRui Paulo 		device_printf(sc->sc_dev,
2127*31d98677SRui Paulo 		    "%s: failed load firmware of file rsu-rtl8712fw\n",
2128*31d98677SRui Paulo 		    __func__);
2129*31d98677SRui Paulo 		RSU_LOCK(sc);
2130*31d98677SRui Paulo 		return (ENXIO);
2131*31d98677SRui Paulo 	}
2132*31d98677SRui Paulo 	RSU_LOCK(sc);
2133*31d98677SRui Paulo 	size = fw->datasize;
2134*31d98677SRui Paulo 	if (size < sizeof(*hdr)) {
2135*31d98677SRui Paulo 		device_printf(sc->sc_dev, "firmware too short\n");
2136*31d98677SRui Paulo 		error = EINVAL;
2137*31d98677SRui Paulo 		goto fail;
2138*31d98677SRui Paulo 	}
2139*31d98677SRui Paulo 	hdr = (struct r92s_fw_hdr *)fw->data;
2140*31d98677SRui Paulo 	if (hdr->signature != htole16(0x8712) &&
2141*31d98677SRui Paulo 	    hdr->signature != htole16(0x8192)) {
2142*31d98677SRui Paulo 		device_printf(sc->sc_dev,
2143*31d98677SRui Paulo 		    "invalid firmware signature 0x%x\n",
2144*31d98677SRui Paulo 		    le16toh(hdr->signature));
2145*31d98677SRui Paulo 		error = EINVAL;
2146*31d98677SRui Paulo 		goto fail;
2147*31d98677SRui Paulo 	}
2148*31d98677SRui Paulo 	DPRINTF("FW V%d %02x-%02x %02x:%02x\n", le16toh(hdr->version),
2149*31d98677SRui Paulo 	    hdr->month, hdr->day, hdr->hour, hdr->minute);
2150*31d98677SRui Paulo 
2151*31d98677SRui Paulo 	/* Make sure that driver and firmware are in sync. */
2152*31d98677SRui Paulo 	if (hdr->privsz != htole32(sizeof(*dmem))) {
2153*31d98677SRui Paulo 		device_printf(sc->sc_dev, "unsupported firmware image\n");
2154*31d98677SRui Paulo 		error = EINVAL;
2155*31d98677SRui Paulo 		goto fail;
2156*31d98677SRui Paulo 	}
2157*31d98677SRui Paulo 	/* Get FW sections sizes. */
2158*31d98677SRui Paulo 	imemsz = le32toh(hdr->imemsz);
2159*31d98677SRui Paulo 	ememsz = le32toh(hdr->sramsz);
2160*31d98677SRui Paulo 	/* Check that all FW sections fit in image. */
2161*31d98677SRui Paulo 	if (size < sizeof(*hdr) + imemsz + ememsz) {
2162*31d98677SRui Paulo 		device_printf(sc->sc_dev, "firmware too short\n");
2163*31d98677SRui Paulo 		error = EINVAL;
2164*31d98677SRui Paulo 		goto fail;
2165*31d98677SRui Paulo 	}
2166*31d98677SRui Paulo 	imem = (uint8_t *)&hdr[1];
2167*31d98677SRui Paulo 	emem = imem + imemsz;
2168*31d98677SRui Paulo 
2169*31d98677SRui Paulo 	/* Load IMEM section. */
2170*31d98677SRui Paulo 	error = rsu_fw_loadsection(sc, imem, imemsz);
2171*31d98677SRui Paulo 	if (error != 0) {
2172*31d98677SRui Paulo 		device_printf(sc->sc_dev,
2173*31d98677SRui Paulo 		    "could not load firmware section %s\n", "IMEM");
2174*31d98677SRui Paulo 		goto fail;
2175*31d98677SRui Paulo 	}
2176*31d98677SRui Paulo 	/* Wait for load to complete. */
2177*31d98677SRui Paulo 	for (ntries = 0; ntries < 10; ntries++) {
2178*31d98677SRui Paulo 		usb_pause_mtx(&sc->sc_mtx, 10);
2179*31d98677SRui Paulo 		reg = rsu_read_2(sc, R92S_TCR);
2180*31d98677SRui Paulo 		if (reg & R92S_TCR_IMEM_CODE_DONE)
2181*31d98677SRui Paulo 			break;
2182*31d98677SRui Paulo 	}
2183*31d98677SRui Paulo 	if (ntries == 10 || !(reg & R92S_TCR_IMEM_CHK_RPT)) {
2184*31d98677SRui Paulo 		device_printf(sc->sc_dev, "timeout waiting for %s transfer\n",
2185*31d98677SRui Paulo 		    "IMEM");
2186*31d98677SRui Paulo 		error = ETIMEDOUT;
2187*31d98677SRui Paulo 		goto fail;
2188*31d98677SRui Paulo 	}
2189*31d98677SRui Paulo 
2190*31d98677SRui Paulo 	/* Load EMEM section. */
2191*31d98677SRui Paulo 	error = rsu_fw_loadsection(sc, emem, ememsz);
2192*31d98677SRui Paulo 	if (error != 0) {
2193*31d98677SRui Paulo 		device_printf(sc->sc_dev,
2194*31d98677SRui Paulo 		    "could not load firmware section %s\n", "EMEM");
2195*31d98677SRui Paulo 		goto fail;
2196*31d98677SRui Paulo 	}
2197*31d98677SRui Paulo 	/* Wait for load to complete. */
2198*31d98677SRui Paulo 	for (ntries = 0; ntries < 10; ntries++) {
2199*31d98677SRui Paulo 		usb_pause_mtx(&sc->sc_mtx, 10);
2200*31d98677SRui Paulo 		reg = rsu_read_2(sc, R92S_TCR);
2201*31d98677SRui Paulo 		if (reg & R92S_TCR_EMEM_CODE_DONE)
2202*31d98677SRui Paulo 			break;
2203*31d98677SRui Paulo 	}
2204*31d98677SRui Paulo 	if (ntries == 10 || !(reg & R92S_TCR_EMEM_CHK_RPT)) {
2205*31d98677SRui Paulo 		device_printf(sc->sc_dev, "timeout waiting for %s transfer\n",
2206*31d98677SRui Paulo 		    "EMEM");
2207*31d98677SRui Paulo 		error = ETIMEDOUT;
2208*31d98677SRui Paulo 		goto fail;
2209*31d98677SRui Paulo 	}
2210*31d98677SRui Paulo 
2211*31d98677SRui Paulo 	/* Enable CPU. */
2212*31d98677SRui Paulo 	rsu_write_1(sc, R92S_SYS_CLKR,
2213*31d98677SRui Paulo 	    rsu_read_1(sc, R92S_SYS_CLKR) | R92S_SYS_CPU_CLKSEL);
2214*31d98677SRui Paulo 	if (!(rsu_read_1(sc, R92S_SYS_CLKR) & R92S_SYS_CPU_CLKSEL)) {
2215*31d98677SRui Paulo 		device_printf(sc->sc_dev, "could not enable system clock\n");
2216*31d98677SRui Paulo 		error = EIO;
2217*31d98677SRui Paulo 		goto fail;
2218*31d98677SRui Paulo 	}
2219*31d98677SRui Paulo 	rsu_write_2(sc, R92S_SYS_FUNC_EN,
2220*31d98677SRui Paulo 	    rsu_read_2(sc, R92S_SYS_FUNC_EN) | R92S_FEN_CPUEN);
2221*31d98677SRui Paulo 	if (!(rsu_read_2(sc, R92S_SYS_FUNC_EN) & R92S_FEN_CPUEN)) {
2222*31d98677SRui Paulo 		device_printf(sc->sc_dev,
2223*31d98677SRui Paulo 		    "could not enable microcontroller\n");
2224*31d98677SRui Paulo 		error = EIO;
2225*31d98677SRui Paulo 		goto fail;
2226*31d98677SRui Paulo 	}
2227*31d98677SRui Paulo 	/* Wait for CPU to initialize. */
2228*31d98677SRui Paulo 	for (ntries = 0; ntries < 100; ntries++) {
2229*31d98677SRui Paulo 		if (rsu_read_2(sc, R92S_TCR) & R92S_TCR_IMEM_RDY)
2230*31d98677SRui Paulo 			break;
2231*31d98677SRui Paulo 		DELAY(1000);
2232*31d98677SRui Paulo 	}
2233*31d98677SRui Paulo 	if (ntries == 100) {
2234*31d98677SRui Paulo 		device_printf(sc->sc_dev,
2235*31d98677SRui Paulo 		    "timeout waiting for microcontroller\n");
2236*31d98677SRui Paulo 		error = ETIMEDOUT;
2237*31d98677SRui Paulo 		goto fail;
2238*31d98677SRui Paulo 	}
2239*31d98677SRui Paulo 
2240*31d98677SRui Paulo 	/* Update DMEM section before loading. */
2241*31d98677SRui Paulo 	dmem = &hdr->priv;
2242*31d98677SRui Paulo 	memset(dmem, 0, sizeof(*dmem));
2243*31d98677SRui Paulo 	dmem->hci_sel = R92S_HCI_SEL_USB | R92S_HCI_SEL_8172;
2244*31d98677SRui Paulo 	dmem->nendpoints = sc->npipes;
2245*31d98677SRui Paulo 	dmem->rf_config = 0x12;	/* 1T2R */
2246*31d98677SRui Paulo 	dmem->vcs_type = R92S_VCS_TYPE_AUTO;
2247*31d98677SRui Paulo 	dmem->vcs_mode = R92S_VCS_MODE_RTS_CTS;
2248*31d98677SRui Paulo #ifdef notyet
2249*31d98677SRui Paulo 	dmem->bw40_en = (ic->ic_htcaps & IEEE80211_HTCAP_CBW20_40) != 0;
2250*31d98677SRui Paulo #endif
2251*31d98677SRui Paulo 	dmem->turbo_mode = 1;
2252*31d98677SRui Paulo 	/* Load DMEM section. */
2253*31d98677SRui Paulo 	error = rsu_fw_loadsection(sc, (uint8_t *)dmem, sizeof(*dmem));
2254*31d98677SRui Paulo 	if (error != 0) {
2255*31d98677SRui Paulo 		device_printf(sc->sc_dev,
2256*31d98677SRui Paulo 		    "could not load firmware section %s\n", "DMEM");
2257*31d98677SRui Paulo 		goto fail;
2258*31d98677SRui Paulo 	}
2259*31d98677SRui Paulo 	/* Wait for load to complete. */
2260*31d98677SRui Paulo 	for (ntries = 0; ntries < 100; ntries++) {
2261*31d98677SRui Paulo 		if (rsu_read_2(sc, R92S_TCR) & R92S_TCR_DMEM_CODE_DONE)
2262*31d98677SRui Paulo 			break;
2263*31d98677SRui Paulo 		DELAY(1000);
2264*31d98677SRui Paulo 	}
2265*31d98677SRui Paulo 	if (ntries == 100) {
2266*31d98677SRui Paulo 		device_printf(sc->sc_dev, "timeout waiting for %s transfer\n",
2267*31d98677SRui Paulo 		    "DMEM");
2268*31d98677SRui Paulo 		error = ETIMEDOUT;
2269*31d98677SRui Paulo 		goto fail;
2270*31d98677SRui Paulo 	}
2271*31d98677SRui Paulo 	/* Wait for firmware readiness. */
2272*31d98677SRui Paulo 	for (ntries = 0; ntries < 60; ntries++) {
2273*31d98677SRui Paulo 		if (!(rsu_read_2(sc, R92S_TCR) & R92S_TCR_FWRDY))
2274*31d98677SRui Paulo 			break;
2275*31d98677SRui Paulo 		DELAY(1000);
2276*31d98677SRui Paulo 	}
2277*31d98677SRui Paulo 	if (ntries == 60) {
2278*31d98677SRui Paulo 		device_printf(sc->sc_dev,
2279*31d98677SRui Paulo 		    "timeout waiting for firmware readiness\n");
2280*31d98677SRui Paulo 		error = ETIMEDOUT;
2281*31d98677SRui Paulo 		goto fail;
2282*31d98677SRui Paulo 	}
2283*31d98677SRui Paulo  fail:
2284*31d98677SRui Paulo 	firmware_put(fw, FIRMWARE_UNLOAD);
2285*31d98677SRui Paulo 	return (error);
2286*31d98677SRui Paulo }
2287*31d98677SRui Paulo 
2288*31d98677SRui Paulo 
2289*31d98677SRui Paulo static int
2290*31d98677SRui Paulo rsu_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
2291*31d98677SRui Paulo     const struct ieee80211_bpf_params *params)
2292*31d98677SRui Paulo {
2293*31d98677SRui Paulo 	struct ieee80211com *ic = ni->ni_ic;
2294*31d98677SRui Paulo 	struct ifnet *ifp = ic->ic_ifp;
2295*31d98677SRui Paulo 	struct rsu_softc *sc = ifp->if_softc;
2296*31d98677SRui Paulo 	struct rsu_data *bf;
2297*31d98677SRui Paulo 
2298*31d98677SRui Paulo 	/* prevent management frames from being sent if we're not ready */
2299*31d98677SRui Paulo 	if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
2300*31d98677SRui Paulo 		m_freem(m);
2301*31d98677SRui Paulo 		ieee80211_free_node(ni);
2302*31d98677SRui Paulo 		return (ENETDOWN);
2303*31d98677SRui Paulo 	}
2304*31d98677SRui Paulo 	RSU_LOCK(sc);
2305*31d98677SRui Paulo 	bf = rsu_getbuf(sc);
2306*31d98677SRui Paulo 	if (bf == NULL) {
2307*31d98677SRui Paulo 		ieee80211_free_node(ni);
2308*31d98677SRui Paulo 		m_freem(m);
2309*31d98677SRui Paulo 		RSU_UNLOCK(sc);
2310*31d98677SRui Paulo 		return (ENOBUFS);
2311*31d98677SRui Paulo 	}
2312*31d98677SRui Paulo 	ifp->if_opackets++;
2313*31d98677SRui Paulo 	if (rsu_tx_start(sc, ni, m, bf) != 0) {
2314*31d98677SRui Paulo 		ieee80211_free_node(ni);
2315*31d98677SRui Paulo 		ifp->if_oerrors++;
2316*31d98677SRui Paulo 		STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, bf, next);
2317*31d98677SRui Paulo 		RSU_UNLOCK(sc);
2318*31d98677SRui Paulo 		return (EIO);
2319*31d98677SRui Paulo 	}
2320*31d98677SRui Paulo 	RSU_UNLOCK(sc);
2321*31d98677SRui Paulo 	sc->sc_tx_timer = 5;
2322*31d98677SRui Paulo 
2323*31d98677SRui Paulo 	return (0);
2324*31d98677SRui Paulo }
2325*31d98677SRui Paulo 
2326*31d98677SRui Paulo static void
2327*31d98677SRui Paulo rsu_init(void *arg)
2328*31d98677SRui Paulo {
2329*31d98677SRui Paulo 	struct rsu_softc *sc = arg;
2330*31d98677SRui Paulo 
2331*31d98677SRui Paulo 	RSU_LOCK(sc);
2332*31d98677SRui Paulo 	rsu_init_locked(arg);
2333*31d98677SRui Paulo 	RSU_UNLOCK(sc);
2334*31d98677SRui Paulo }
2335*31d98677SRui Paulo 
2336*31d98677SRui Paulo static void
2337*31d98677SRui Paulo rsu_init_locked(struct rsu_softc *sc)
2338*31d98677SRui Paulo {
2339*31d98677SRui Paulo 	struct ifnet *ifp = sc->sc_ifp;
2340*31d98677SRui Paulo 	struct r92s_set_pwr_mode cmd;
2341*31d98677SRui Paulo 	int error;
2342*31d98677SRui Paulo 
2343*31d98677SRui Paulo 	/* Init host async commands ring. */
2344*31d98677SRui Paulo 	sc->cmdq.cur = sc->cmdq.next = sc->cmdq.queued = 0;
2345*31d98677SRui Paulo 
2346*31d98677SRui Paulo 	/* Allocate Tx/Rx buffers. */
2347*31d98677SRui Paulo 	error = rsu_alloc_rx_list(sc);
2348*31d98677SRui Paulo 	if (error != 0) {
2349*31d98677SRui Paulo 		device_printf(sc->sc_dev, "could not allocate Rx buffers\n");
2350*31d98677SRui Paulo 		return;
2351*31d98677SRui Paulo 	}
2352*31d98677SRui Paulo 	error = rsu_alloc_tx_list(sc);
2353*31d98677SRui Paulo 	if (error != 0) {
2354*31d98677SRui Paulo 		device_printf(sc->sc_dev, "could not allocate Tx buffers\n");
2355*31d98677SRui Paulo 		rsu_free_rx_list(sc);
2356*31d98677SRui Paulo 		return;
2357*31d98677SRui Paulo 	}
2358*31d98677SRui Paulo 	/* Power on adapter. */
2359*31d98677SRui Paulo 	if (sc->cut == 1)
2360*31d98677SRui Paulo 		rsu_power_on_acut(sc);
2361*31d98677SRui Paulo 	else
2362*31d98677SRui Paulo 		rsu_power_on_bcut(sc);
2363*31d98677SRui Paulo 	/* Load firmware. */
2364*31d98677SRui Paulo 	error = rsu_load_firmware(sc);
2365*31d98677SRui Paulo 	if (error != 0)
2366*31d98677SRui Paulo 		goto fail;
2367*31d98677SRui Paulo 
2368*31d98677SRui Paulo 	/* Enable Rx TCP checksum offload. */
2369*31d98677SRui Paulo 	rsu_write_4(sc, R92S_RCR,
2370*31d98677SRui Paulo 	    rsu_read_4(sc, R92S_RCR) | 0x04000000);
2371*31d98677SRui Paulo 	/* Append PHY status. */
2372*31d98677SRui Paulo 	rsu_write_4(sc, R92S_RCR,
2373*31d98677SRui Paulo 	    rsu_read_4(sc, R92S_RCR) | 0x02000000);
2374*31d98677SRui Paulo 
2375*31d98677SRui Paulo 	rsu_write_4(sc, R92S_CR,
2376*31d98677SRui Paulo 	    rsu_read_4(sc, R92S_CR) & ~0xff000000);
2377*31d98677SRui Paulo 
2378*31d98677SRui Paulo 	/* Use 128 bytes pages. */
2379*31d98677SRui Paulo 	rsu_write_1(sc, 0x00b5,
2380*31d98677SRui Paulo 	    rsu_read_1(sc, 0x00b5) | 0x01);
2381*31d98677SRui Paulo 	/* Enable USB Rx aggregation. */
2382*31d98677SRui Paulo 	rsu_write_1(sc, 0x00bd,
2383*31d98677SRui Paulo 	    rsu_read_1(sc, 0x00bd) | 0x80);
2384*31d98677SRui Paulo 	/* Set USB Rx aggregation threshold. */
2385*31d98677SRui Paulo 	rsu_write_1(sc, 0x00d9, 0x01);
2386*31d98677SRui Paulo 	/* Set USB Rx aggregation timeout (1.7ms/4). */
2387*31d98677SRui Paulo 	rsu_write_1(sc, 0xfe5b, 0x04);
2388*31d98677SRui Paulo 	/* Fix USB Rx FIFO issue. */
2389*31d98677SRui Paulo 	rsu_write_1(sc, 0xfe5c,
2390*31d98677SRui Paulo 	    rsu_read_1(sc, 0xfe5c) | 0x80);
2391*31d98677SRui Paulo 
2392*31d98677SRui Paulo 	/* Set MAC address. */
2393*31d98677SRui Paulo 	rsu_write_region_1(sc, R92S_MACID, IF_LLADDR(ifp),
2394*31d98677SRui Paulo 	    IEEE80211_ADDR_LEN);
2395*31d98677SRui Paulo 
2396*31d98677SRui Paulo 	/* NB: it really takes that long for firmware to boot. */
2397*31d98677SRui Paulo 	usb_pause_mtx(&sc->sc_mtx, 1500);
2398*31d98677SRui Paulo 
2399*31d98677SRui Paulo 	DPRINTF("setting MAC address to %s\n", ether_sprintf(IF_LLADDR(ifp)));
2400*31d98677SRui Paulo 	error = rsu_fw_cmd(sc, R92S_CMD_SET_MAC_ADDRESS, IF_LLADDR(ifp),
2401*31d98677SRui Paulo 	    IEEE80211_ADDR_LEN);
2402*31d98677SRui Paulo 	if (error != 0) {
2403*31d98677SRui Paulo 		device_printf(sc->sc_dev, "could not set MAC address\n");
2404*31d98677SRui Paulo 		goto fail;
2405*31d98677SRui Paulo 	}
2406*31d98677SRui Paulo 
2407*31d98677SRui Paulo 	rsu_write_1(sc, R92S_USB_HRPWM,
2408*31d98677SRui Paulo 	    R92S_USB_HRPWM_PS_ST_ACTIVE | R92S_USB_HRPWM_PS_ALL_ON);
2409*31d98677SRui Paulo 
2410*31d98677SRui Paulo 	memset(&cmd, 0, sizeof(cmd));
2411*31d98677SRui Paulo 	cmd.mode = R92S_PS_MODE_ACTIVE;
2412*31d98677SRui Paulo 	DPRINTF("setting ps mode to %d\n", cmd.mode);
2413*31d98677SRui Paulo 	error = rsu_fw_cmd(sc, R92S_CMD_SET_PWR_MODE, &cmd, sizeof(cmd));
2414*31d98677SRui Paulo 	if (error != 0) {
2415*31d98677SRui Paulo 		device_printf(sc->sc_dev, "could not set PS mode\n");
2416*31d98677SRui Paulo 		goto fail;
2417*31d98677SRui Paulo 	}
2418*31d98677SRui Paulo 
2419*31d98677SRui Paulo #if 0
2420*31d98677SRui Paulo 	if (ic->ic_htcaps & IEEE80211_HTCAP_CBW20_40) {
2421*31d98677SRui Paulo 		/* Enable 40MHz mode. */
2422*31d98677SRui Paulo 		error = rsu_fw_iocmd(sc,
2423*31d98677SRui Paulo 		    SM(R92S_IOCMD_CLASS, 0xf4) |
2424*31d98677SRui Paulo 		    SM(R92S_IOCMD_INDEX, 0x00) |
2425*31d98677SRui Paulo 		    SM(R92S_IOCMD_VALUE, 0x0007));
2426*31d98677SRui Paulo 		if (error != 0) {
2427*31d98677SRui Paulo 			device_printf(sc->sc_dev,
2428*31d98677SRui Paulo 			    "could not enable 40MHz mode\n");
2429*31d98677SRui Paulo 			goto fail;
2430*31d98677SRui Paulo 		}
2431*31d98677SRui Paulo 	}
2432*31d98677SRui Paulo 
2433*31d98677SRui Paulo 	/* Set default channel. */
2434*31d98677SRui Paulo 	ic->ic_bss->ni_chan = ic->ic_ibss_chan;
2435*31d98677SRui Paulo #endif
2436*31d98677SRui Paulo 	sc->scan_pass = 0;
2437*31d98677SRui Paulo 	usbd_transfer_start(sc->sc_xfer[RSU_BULK_RX]);
2438*31d98677SRui Paulo 
2439*31d98677SRui Paulo 	/* We're ready to go. */
2440*31d98677SRui Paulo 	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
2441*31d98677SRui Paulo 	ifp->if_drv_flags |= IFF_DRV_RUNNING;
2442*31d98677SRui Paulo 
2443*31d98677SRui Paulo 	callout_reset(&sc->sc_watchdog_ch, hz, rsu_watchdog, sc);
2444*31d98677SRui Paulo 
2445*31d98677SRui Paulo 	return;
2446*31d98677SRui Paulo fail:
2447*31d98677SRui Paulo 	rsu_free_rx_list(sc);
2448*31d98677SRui Paulo 	rsu_free_tx_list(sc);
2449*31d98677SRui Paulo 	return;
2450*31d98677SRui Paulo }
2451*31d98677SRui Paulo 
2452*31d98677SRui Paulo static void
2453*31d98677SRui Paulo rsu_stop(struct ifnet *ifp, int disable)
2454*31d98677SRui Paulo {
2455*31d98677SRui Paulo 	struct rsu_softc *sc = ifp->if_softc;
2456*31d98677SRui Paulo 
2457*31d98677SRui Paulo 	RSU_LOCK(sc);
2458*31d98677SRui Paulo 	rsu_stop_locked(ifp, disable);
2459*31d98677SRui Paulo 	RSU_UNLOCK(sc);
2460*31d98677SRui Paulo }
2461*31d98677SRui Paulo 
2462*31d98677SRui Paulo static void
2463*31d98677SRui Paulo rsu_stop_locked(struct ifnet *ifp, int disable __unused)
2464*31d98677SRui Paulo {
2465*31d98677SRui Paulo 	struct rsu_softc *sc = ifp->if_softc;
2466*31d98677SRui Paulo 	int i;
2467*31d98677SRui Paulo 
2468*31d98677SRui Paulo 	ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
2469*31d98677SRui Paulo 	callout_stop(&sc->sc_watchdog_ch);
2470*31d98677SRui Paulo 	sc->sc_calibrating = 0;
2471*31d98677SRui Paulo 	taskqueue_cancel_timeout(taskqueue_thread, &sc->calib_task, NULL);
2472*31d98677SRui Paulo 
2473*31d98677SRui Paulo 	/* Power off adapter. */
2474*31d98677SRui Paulo 	rsu_power_off(sc);
2475*31d98677SRui Paulo 
2476*31d98677SRui Paulo 	for (i = 0; i < RSU_N_TRANSFER; i++)
2477*31d98677SRui Paulo 		usbd_transfer_stop(sc->sc_xfer[i]);
2478*31d98677SRui Paulo }
2479*31d98677SRui Paulo 
2480