xref: /openbsd-src/sys/dev/usb/if_urtwn.c (revision 05018d8c87940b86c97370e3f684df3b4d705c9f)
1*05018d8cSjsg /*	$OpenBSD: if_urtwn.c,v 1.112 2024/10/22 22:21:25 jsg Exp $	*/
29ec5c125Sdamien 
39ec5c125Sdamien /*-
49ec5c125Sdamien  * Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr>
5c349b8bfSstsp  * Copyright (c) 2014 Kevin Lo <kevlo@FreeBSD.org>
690540544Sjmatthew  * Copyright (c) 2016 Nathanial Sloss <nathanialsloss@yahoo.com.au>
79ec5c125Sdamien  *
89ec5c125Sdamien  * Permission to use, copy, modify, and distribute this software for any
99ec5c125Sdamien  * purpose with or without fee is hereby granted, provided that the above
109ec5c125Sdamien  * copyright notice and this permission notice appear in all copies.
119ec5c125Sdamien  *
129ec5c125Sdamien  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
139ec5c125Sdamien  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
149ec5c125Sdamien  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
159ec5c125Sdamien  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
169ec5c125Sdamien  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
179ec5c125Sdamien  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
189ec5c125Sdamien  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
199ec5c125Sdamien  */
209ec5c125Sdamien 
219ec5c125Sdamien /*
22067851b1Skevlo  * Driver for Realtek RTL8188CE-VAU/RTL8188CUS/RTL8188EU/RTL8188FTV/RTL8188RU/
23067851b1Skevlo  * RTL8192CU/RTL8192EU.
249ec5c125Sdamien  */
259ec5c125Sdamien 
269ec5c125Sdamien #include "bpfilter.h"
279ec5c125Sdamien 
289ec5c125Sdamien #include <sys/param.h>
299ec5c125Sdamien #include <sys/mbuf.h>
309ec5c125Sdamien #include <sys/systm.h>
319ec5c125Sdamien #include <sys/timeout.h>
329ec5c125Sdamien #include <sys/device.h>
339b18ffb8Sguenther #include <sys/endian.h>
349ec5c125Sdamien 
3590540544Sjmatthew #include <machine/bus.h>
369ec5c125Sdamien #include <machine/intr.h>
379ec5c125Sdamien 
389ec5c125Sdamien #if NBPFILTER > 0
399ec5c125Sdamien #include <net/bpf.h>
409ec5c125Sdamien #endif
419ec5c125Sdamien #include <net/if.h>
429ec5c125Sdamien #include <net/if_media.h>
439ec5c125Sdamien 
449ec5c125Sdamien #include <netinet/in.h>
459ec5c125Sdamien #include <netinet/if_ether.h>
469ec5c125Sdamien 
479ec5c125Sdamien #include <net80211/ieee80211_var.h>
48a087613eSstsp #include <net80211/ieee80211_amrr.h>
499ec5c125Sdamien #include <net80211/ieee80211_radiotap.h>
509ec5c125Sdamien 
519ec5c125Sdamien #include <dev/usb/usb.h>
529ec5c125Sdamien #include <dev/usb/usbdi.h>
5390540544Sjmatthew #include <dev/usb/usbdivar.h>
549ec5c125Sdamien #include <dev/usb/usbdevs.h>
559ec5c125Sdamien 
5653c3c4d0Sstsp #include <dev/ic/r92creg.h>
5723340becSstsp #include <dev/ic/rtwnvar.h>
582a915f22Sstsp 
592a915f22Sstsp /* Maximum number of output pipes is 3. */
602a915f22Sstsp #define R92C_MAX_EPOUT	3
612a915f22Sstsp 
6208300c66Skevlo #define R92C_HQ_NPAGES		12
6308300c66Skevlo #define R92C_LQ_NPAGES		2
6408300c66Skevlo #define R92C_NQ_NPAGES		2
652a915f22Sstsp #define R92C_TXPKTBUF_COUNT	256
662a915f22Sstsp #define R92C_TX_PAGE_COUNT	248
6708300c66Skevlo #define R92C_MAX_RX_DMA_SIZE	0x2800
6890540544Sjmatthew 
6908300c66Skevlo #define R88E_HQ_NPAGES		0
7008300c66Skevlo #define R88E_LQ_NPAGES		9
7108300c66Skevlo #define R88E_NQ_NPAGES		0
722a915f22Sstsp #define R88E_TXPKTBUF_COUNT	177
7308300c66Skevlo #define R88E_TX_PAGE_COUNT	168
7408300c66Skevlo #define R88E_MAX_RX_DMA_SIZE	0x2400
752a915f22Sstsp 
76067851b1Skevlo #define R88F_HQ_NPAGES		12
77067851b1Skevlo #define R88F_LQ_NPAGES		2
78067851b1Skevlo #define R88F_NQ_NPAGES		2
79067851b1Skevlo #define R88F_TXPKTBUF_COUNT	177
80067851b1Skevlo #define R88F_TX_PAGE_COUNT	247
81067851b1Skevlo #define R88F_MAX_RX_DMA_SIZE	0x3f80
82067851b1Skevlo 
8390540544Sjmatthew #define R92E_HQ_NPAGES		16
8490540544Sjmatthew #define R92E_LQ_NPAGES		16
8590540544Sjmatthew #define R92E_NQ_NPAGES		16
8690540544Sjmatthew #define R92E_TX_PAGE_COUNT	248
8790540544Sjmatthew #define R92E_MAX_RX_DMA_SIZE	0x3fc0
8890540544Sjmatthew 
8990540544Sjmatthew #define R92C_TXDESC_SUMSIZE	32
9090540544Sjmatthew #define R92C_TXDESC_SUMOFFSET	14
9190540544Sjmatthew 
922a915f22Sstsp /* USB Requests. */
932a915f22Sstsp #define R92C_REQ_REGS	0x05
942a915f22Sstsp 
952a915f22Sstsp /*
962a915f22Sstsp  * Driver definitions.
972a915f22Sstsp  */
982a915f22Sstsp #define URTWN_RX_LIST_COUNT		1
992a915f22Sstsp #define URTWN_TX_LIST_COUNT		8
1002a915f22Sstsp #define URTWN_HOST_CMD_RING_COUNT	32
1012a915f22Sstsp 
1022a915f22Sstsp #define URTWN_RXBUFSZ	(16 * 1024)
10390540544Sjmatthew #define URTWN_TXBUFSZ	(sizeof(struct r92e_tx_desc_usb) + IEEE80211_MAX_LEN)
1042a915f22Sstsp 
1052a915f22Sstsp #define URTWN_RIDX_COUNT	28
1062a915f22Sstsp 
1072a915f22Sstsp #define URTWN_TX_TIMEOUT	5000	/* ms */
1082a915f22Sstsp 
1092a915f22Sstsp #define URTWN_LED_LINK	0
1102a915f22Sstsp #define URTWN_LED_DATA	1
1112a915f22Sstsp 
1122a915f22Sstsp struct urtwn_rx_radiotap_header {
1132a915f22Sstsp 	struct ieee80211_radiotap_header wr_ihdr;
1142a915f22Sstsp 	uint8_t		wr_flags;
1152a915f22Sstsp 	uint8_t		wr_rate;
1162a915f22Sstsp 	uint16_t	wr_chan_freq;
1172a915f22Sstsp 	uint16_t	wr_chan_flags;
1182a915f22Sstsp 	uint8_t		wr_dbm_antsignal;
1192a915f22Sstsp } __packed;
1202a915f22Sstsp 
1212a915f22Sstsp #define URTWN_RX_RADIOTAP_PRESENT			\
1222a915f22Sstsp 	(1 << IEEE80211_RADIOTAP_FLAGS |		\
1232a915f22Sstsp 	 1 << IEEE80211_RADIOTAP_RATE |			\
1242a915f22Sstsp 	 1 << IEEE80211_RADIOTAP_CHANNEL |		\
1252a915f22Sstsp 	 1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL)
1262a915f22Sstsp 
1272a915f22Sstsp struct urtwn_tx_radiotap_header {
1282a915f22Sstsp 	struct ieee80211_radiotap_header wt_ihdr;
1292a915f22Sstsp 	uint8_t		wt_flags;
1302a915f22Sstsp 	uint16_t	wt_chan_freq;
1312a915f22Sstsp 	uint16_t	wt_chan_flags;
1322a915f22Sstsp } __packed;
1332a915f22Sstsp 
1342a915f22Sstsp #define URTWN_TX_RADIOTAP_PRESENT			\
1352a915f22Sstsp 	(1 << IEEE80211_RADIOTAP_FLAGS |		\
1362a915f22Sstsp 	 1 << IEEE80211_RADIOTAP_CHANNEL)
1372a915f22Sstsp 
1382a915f22Sstsp struct urtwn_softc;
1392a915f22Sstsp 
1402a915f22Sstsp struct urtwn_rx_data {
1412a915f22Sstsp 	struct urtwn_softc	*sc;
1422a915f22Sstsp 	struct usbd_xfer	*xfer;
1432a915f22Sstsp 	uint8_t			*buf;
1442a915f22Sstsp };
1452a915f22Sstsp 
1462a915f22Sstsp struct urtwn_tx_data {
1472a915f22Sstsp 	struct urtwn_softc		*sc;
1482a915f22Sstsp 	struct usbd_pipe		*pipe;
1492a915f22Sstsp 	struct usbd_xfer		*xfer;
1502a915f22Sstsp 	uint8_t				*buf;
1512a915f22Sstsp 	TAILQ_ENTRY(urtwn_tx_data)	next;
1522a915f22Sstsp };
1532a915f22Sstsp 
1542a915f22Sstsp struct urtwn_host_cmd {
1552a915f22Sstsp 	void	(*cb)(struct urtwn_softc *, void *);
1562a915f22Sstsp 	uint8_t	data[256];
1572a915f22Sstsp };
1582a915f22Sstsp 
1592a915f22Sstsp struct urtwn_cmd_newstate {
1602a915f22Sstsp 	enum ieee80211_state	state;
1612a915f22Sstsp 	int			arg;
1622a915f22Sstsp };
1632a915f22Sstsp 
1642a915f22Sstsp struct urtwn_cmd_key {
1652a915f22Sstsp 	struct ieee80211_key	key;
16623340becSstsp 	struct ieee80211_node	*ni;
1672a915f22Sstsp };
1682a915f22Sstsp 
1692a915f22Sstsp struct urtwn_host_cmd_ring {
1702a915f22Sstsp 	struct urtwn_host_cmd	cmd[URTWN_HOST_CMD_RING_COUNT];
1712a915f22Sstsp 	int			cur;
1722a915f22Sstsp 	int			next;
1732a915f22Sstsp 	int			queued;
1742a915f22Sstsp };
1752a915f22Sstsp 
1762a915f22Sstsp struct urtwn_softc {
1772a915f22Sstsp 	struct device			sc_dev;
17823340becSstsp 	struct rtwn_softc		sc_sc;
17923340becSstsp 
1802a915f22Sstsp 	struct usbd_device		*sc_udev;
1812a915f22Sstsp 	struct usbd_interface		*sc_iface;
1822a915f22Sstsp 	struct usb_task			sc_task;
18323340becSstsp 
1842a915f22Sstsp 	struct timeout			scan_to;
1852a915f22Sstsp 	struct timeout			calib_to;
18623340becSstsp 
18708300c66Skevlo 	int				ntx;
1882a915f22Sstsp 	struct usbd_pipe		*rx_pipe;
1892a915f22Sstsp 	struct usbd_pipe		*tx_pipe[R92C_MAX_EPOUT];
1902a915f22Sstsp 	int				ac2idx[EDCA_NUM_AC];
1912a915f22Sstsp 
1922a915f22Sstsp 	struct urtwn_host_cmd_ring	cmdq;
1932a915f22Sstsp 	struct urtwn_rx_data		rx_data[URTWN_RX_LIST_COUNT];
1942a915f22Sstsp 	struct urtwn_tx_data		tx_data[URTWN_TX_LIST_COUNT];
1952a915f22Sstsp 	TAILQ_HEAD(, urtwn_tx_data)	tx_free_list;
1962a915f22Sstsp 
197a087613eSstsp 	struct ieee80211_amrr		amrr;
198a087613eSstsp 	struct ieee80211_amrr_node	amn;
199a087613eSstsp 
2002a915f22Sstsp #if NBPFILTER > 0
2012a915f22Sstsp 	caddr_t				sc_drvbpf;
2022a915f22Sstsp 
2032a915f22Sstsp 	union {
2042a915f22Sstsp 		struct urtwn_rx_radiotap_header th;
2052a915f22Sstsp 		uint8_t	pad[64];
2062a915f22Sstsp 	}				sc_rxtapu;
2072a915f22Sstsp #define sc_rxtap	sc_rxtapu.th
2082a915f22Sstsp 	int				sc_rxtap_len;
2092a915f22Sstsp 
2102a915f22Sstsp 	union {
2112a915f22Sstsp 		struct urtwn_tx_radiotap_header th;
2122a915f22Sstsp 		uint8_t	pad[64];
2132a915f22Sstsp 	}				sc_txtapu;
2142a915f22Sstsp #define sc_txtap	sc_txtapu.th
2152a915f22Sstsp 	int				sc_txtap_len;
2162a915f22Sstsp #endif
2172362e6a4Skrw 	int				sc_key_tasks;
2182a915f22Sstsp };
2199ec5c125Sdamien 
2209ec5c125Sdamien #ifdef URTWN_DEBUG
2219ec5c125Sdamien #define DPRINTF(x)	do { if (urtwn_debug) printf x; } while (0)
2229ec5c125Sdamien #define DPRINTFN(n, x)	do { if (urtwn_debug >= (n)) printf x; } while (0)
2239ec5c125Sdamien int urtwn_debug = 4;
2249ec5c125Sdamien #else
2259ec5c125Sdamien #define DPRINTF(x)
2269ec5c125Sdamien #define DPRINTFN(n, x)
2279ec5c125Sdamien #endif
2289ec5c125Sdamien 
22934a2cadeSkevlo /*
23034a2cadeSkevlo  * Various supported device vendors/products.
23134a2cadeSkevlo  */
23234a2cadeSkevlo #define URTWN_DEV(v, p, f)					\
23334a2cadeSkevlo         { { USB_VENDOR_##v, USB_PRODUCT_##v##_##p }, (f) | RTWN_CHIP_USB }
23434a2cadeSkevlo #define URTWN_DEV_8192CU(v, p)	URTWN_DEV(v, p, RTWN_CHIP_92C | RTWN_CHIP_88C)
23590540544Sjmatthew #define URTWN_DEV_8192EU(v, p)	URTWN_DEV(v, p, RTWN_CHIP_92E)
236067851b1Skevlo #define URTWN_DEV_8188EU(v, p)	URTWN_DEV(v, p, RTWN_CHIP_88E)
237067851b1Skevlo #define URTWN_DEV_8188F(v, p)	URTWN_DEV(v, p, RTWN_CHIP_88F)
23834a2cadeSkevlo static const struct urtwn_type {
23934a2cadeSkevlo 	struct usb_devno        dev;
24034a2cadeSkevlo 	uint32_t		chip;
24134a2cadeSkevlo } urtwn_devs[] = {
24234a2cadeSkevlo 	URTWN_DEV_8192CU(ABOCOM,	RTL8188CU_1),
24334a2cadeSkevlo 	URTWN_DEV_8192CU(ABOCOM,	RTL8188CU_2),
24434a2cadeSkevlo 	URTWN_DEV_8192CU(ABOCOM,	RTL8192CU),
24534a2cadeSkevlo 	URTWN_DEV_8192CU(ASUS,		RTL8192CU),
24634a2cadeSkevlo 	URTWN_DEV_8192CU(ASUS,		RTL8192CU_2),
24734a2cadeSkevlo 	URTWN_DEV_8192CU(ASUS,		RTL8192CU_3),
24834a2cadeSkevlo 	URTWN_DEV_8192CU(AZUREWAVE,	RTL8188CE_1),
24934a2cadeSkevlo 	URTWN_DEV_8192CU(AZUREWAVE,	RTL8188CE_2),
25034a2cadeSkevlo 	URTWN_DEV_8192CU(AZUREWAVE,	RTL8188CU),
25134a2cadeSkevlo 	URTWN_DEV_8192CU(BELKIN,	F7D2102),
25234a2cadeSkevlo 	URTWN_DEV_8192CU(BELKIN,	F9L1004V1),
25334a2cadeSkevlo 	URTWN_DEV_8192CU(BELKIN,	RTL8188CU),
25434a2cadeSkevlo 	URTWN_DEV_8192CU(BELKIN,	RTL8188CUS),
25534a2cadeSkevlo 	URTWN_DEV_8192CU(BELKIN,	RTL8192CU),
25634a2cadeSkevlo 	URTWN_DEV_8192CU(BELKIN,	RTL8192CU_1),
25734a2cadeSkevlo 	URTWN_DEV_8192CU(CHICONY,	RTL8188CUS_1),
25834a2cadeSkevlo 	URTWN_DEV_8192CU(CHICONY,	RTL8188CUS_2),
25934a2cadeSkevlo 	URTWN_DEV_8192CU(CHICONY,	RTL8188CUS_3),
26034a2cadeSkevlo 	URTWN_DEV_8192CU(CHICONY,	RTL8188CUS_4),
26134a2cadeSkevlo 	URTWN_DEV_8192CU(CHICONY,	RTL8188CUS_5),
26234a2cadeSkevlo 	URTWN_DEV_8192CU(CHICONY,	RTL8188CUS_6),
26334a2cadeSkevlo 	URTWN_DEV_8192CU(COMPARE,	RTL8192CU),
26434a2cadeSkevlo 	URTWN_DEV_8192CU(COREGA,	RTL8192CU),
26534a2cadeSkevlo 	URTWN_DEV_8192CU(DLINK,		DWA131B),
26634a2cadeSkevlo 	URTWN_DEV_8192CU(DLINK,		RTL8188CU),
26734a2cadeSkevlo 	URTWN_DEV_8192CU(DLINK,		RTL8192CU_1),
26834a2cadeSkevlo 	URTWN_DEV_8192CU(DLINK,		RTL8192CU_2),
26934a2cadeSkevlo 	URTWN_DEV_8192CU(DLINK,		RTL8192CU_3),
27034a2cadeSkevlo 	URTWN_DEV_8192CU(DLINK,		RTL8192CU_4),
27134a2cadeSkevlo 	URTWN_DEV_8192CU(EDIMAX,	EW7811UN),
27234a2cadeSkevlo 	URTWN_DEV_8192CU(EDIMAX,	RTL8192CU),
27334a2cadeSkevlo 	URTWN_DEV_8192CU(FEIXUN,	RTL8188CU),
27434a2cadeSkevlo 	URTWN_DEV_8192CU(FEIXUN,	RTL8192CU),
27534a2cadeSkevlo 	URTWN_DEV_8192CU(GUILLEMOT,	HWNUP150),
27634a2cadeSkevlo 	URTWN_DEV_8192CU(GUILLEMOT,	RTL8192CU),
27734a2cadeSkevlo 	URTWN_DEV_8192CU(HAWKING,	RTL8192CU),
27834a2cadeSkevlo 	URTWN_DEV_8192CU(HAWKING,	RTL8192CU_2),
27934a2cadeSkevlo 	URTWN_DEV_8192CU(HP3,		RTL8188CU),
28034a2cadeSkevlo 	URTWN_DEV_8192CU(IODATA,	WNG150UM),
28134a2cadeSkevlo 	URTWN_DEV_8192CU(IODATA,	RTL8192CU),
28234a2cadeSkevlo 	URTWN_DEV_8192CU(NETGEAR,	N300MA),
28334a2cadeSkevlo 	URTWN_DEV_8192CU(NETGEAR,	WNA1000M),
284afb55d64Sjsg 	URTWN_DEV_8192CU(NETGEAR,	WNA1000MV2),
28534a2cadeSkevlo 	URTWN_DEV_8192CU(NETGEAR,	RTL8192CU),
28634a2cadeSkevlo 	URTWN_DEV_8192CU(NETGEAR4,	RTL8188CU),
28734a2cadeSkevlo 	URTWN_DEV_8192CU(NETWEEN,	RTL8192CU),
28834a2cadeSkevlo 	URTWN_DEV_8192CU(NOVATECH,	RTL8188CU),
28934a2cadeSkevlo 	URTWN_DEV_8192CU(PLANEX2,	RTL8188CU_1),
29034a2cadeSkevlo 	URTWN_DEV_8192CU(PLANEX2,	RTL8188CU_2),
29134a2cadeSkevlo 	URTWN_DEV_8192CU(PLANEX2,	RTL8188CU_3),
29234a2cadeSkevlo 	URTWN_DEV_8192CU(PLANEX2,	RTL8188CU_4),
29334a2cadeSkevlo 	URTWN_DEV_8192CU(PLANEX2,	RTL8188CUS),
29434a2cadeSkevlo 	URTWN_DEV_8192CU(PLANEX2,	RTL8192CU),
29534a2cadeSkevlo 	URTWN_DEV_8192CU(REALTEK,	RTL8188CE_0),
29634a2cadeSkevlo 	URTWN_DEV_8192CU(REALTEK,	RTL8188CE_1),
29734a2cadeSkevlo 	URTWN_DEV_8192CU(REALTEK,	RTL8188CTV),
29834a2cadeSkevlo 	URTWN_DEV_8192CU(REALTEK,	RTL8188CU_0),
29934a2cadeSkevlo 	URTWN_DEV_8192CU(REALTEK,	RTL8188CU_1),
30034a2cadeSkevlo 	URTWN_DEV_8192CU(REALTEK,	RTL8188CU_2),
30134a2cadeSkevlo 	URTWN_DEV_8192CU(REALTEK,	RTL8188CU_3),
30234a2cadeSkevlo 	URTWN_DEV_8192CU(REALTEK,	RTL8188CU_4),
30334a2cadeSkevlo 	URTWN_DEV_8192CU(REALTEK,	RTL8188CU_5),
30434a2cadeSkevlo 	URTWN_DEV_8192CU(REALTEK,	RTL8188CU_COMBO),
30534a2cadeSkevlo 	URTWN_DEV_8192CU(REALTEK,	RTL8188CUS),
30634a2cadeSkevlo 	URTWN_DEV_8192CU(REALTEK,	RTL8188RU),
30734a2cadeSkevlo 	URTWN_DEV_8192CU(REALTEK,	RTL8188RU_2),
30834a2cadeSkevlo 	URTWN_DEV_8192CU(REALTEK,	RTL8188RU_3),
30934a2cadeSkevlo 	URTWN_DEV_8192CU(REALTEK,	RTL8191CU),
31034a2cadeSkevlo 	URTWN_DEV_8192CU(REALTEK,	RTL8192CE),
31134a2cadeSkevlo 	URTWN_DEV_8192CU(REALTEK,	RTL8192CE_VAU),
31234a2cadeSkevlo 	URTWN_DEV_8192CU(REALTEK,	RTL8192CU),
31334a2cadeSkevlo 	URTWN_DEV_8192CU(SITECOMEU,	RTL8188CU),
31434a2cadeSkevlo 	URTWN_DEV_8192CU(SITECOMEU,	RTL8188CU_2),
31534a2cadeSkevlo 	URTWN_DEV_8192CU(SITECOMEU,	RTL8192CU),
31634a2cadeSkevlo 	URTWN_DEV_8192CU(SITECOMEU,	RTL8192CU_2),
31734a2cadeSkevlo 	URTWN_DEV_8192CU(SITECOMEU,	WLA2100V2),
31834a2cadeSkevlo 	URTWN_DEV_8192CU(TPLINK,	RTL8192CU),
31934a2cadeSkevlo 	URTWN_DEV_8192CU(TRENDNET,	RTL8188CU),
32034a2cadeSkevlo 	URTWN_DEV_8192CU(TRENDNET,	RTL8192CU),
32134a2cadeSkevlo 	URTWN_DEV_8192CU(ZYXEL,		RTL8192CU),
322c349b8bfSstsp 	/* URTWN_RTL8188E */
323ccaa33a7Ssthen 	URTWN_DEV_8188EU(ABOCOM,	RTL8188EU),
324dd69d388Sjsg 	URTWN_DEV_8188EU(DLINK,		DWA121B1),
32534a2cadeSkevlo 	URTWN_DEV_8188EU(DLINK,		DWA123D1),
32634a2cadeSkevlo 	URTWN_DEV_8188EU(DLINK,		DWA125D1),
327c63cca64Sjmatthew 	URTWN_DEV_8188EU(EDIMAX,	EW7811UNV2),
32834a2cadeSkevlo 	URTWN_DEV_8188EU(ELECOM,	WDC150SU2M),
329a1d28b40Sjsg 	URTWN_DEV_8188EU(MERCUSYS,	MW150USV2),
33034a2cadeSkevlo 	URTWN_DEV_8188EU(REALTEK,	RTL8188ETV),
3318d713102Sbenno 	URTWN_DEV_8188EU(REALTEK,	RTL8188EU),
33290540544Sjmatthew 	URTWN_DEV_8188EU(TPLINK,	RTL8188EUS),
333bf5e189fSstsp 	URTWN_DEV_8188EU(ASUS,  	RTL8188EUS),
334067851b1Skevlo 	/* URTWN_RTL8188FTV */
335067851b1Skevlo 	URTWN_DEV_8188F(REALTEK,	RTL8188FTV),
336bf5e189fSstsp 
33790540544Sjmatthew 	/* URTWN_RTL8192EU */
33833410555Skevlo 	URTWN_DEV_8192EU(DLINK,		DWA131E1),
3391d8ba341Sjmatthew 	URTWN_DEV_8192EU(REALTEK,	RTL8192EU),
34027da0d9aSjsg 	URTWN_DEV_8192EU(REALTEK,	RTL8192EU_2),
3415fbe5b8cSstsp 	URTWN_DEV_8192EU(TPLINK,	RTL8192EU),
342018f6d9cSjsg 	URTWN_DEV_8192EU(TPLINK,	RTL8192EU_2),
343018f6d9cSjsg 	URTWN_DEV_8192EU(TPLINK,	RTL8192EU_3)
3449ec5c125Sdamien };
3459ec5c125Sdamien 
34634a2cadeSkevlo #define urtwn_lookup(v, p)	\
34734a2cadeSkevlo 	((const struct urtwn_type *)usb_lookup(urtwn_devs, v, p))
34834a2cadeSkevlo 
3499ec5c125Sdamien int		urtwn_match(struct device *, void *, void *);
3509ec5c125Sdamien void		urtwn_attach(struct device *, struct device *, void *);
3519ec5c125Sdamien int		urtwn_detach(struct device *, int);
3529ec5c125Sdamien int		urtwn_open_pipes(struct urtwn_softc *);
3539ec5c125Sdamien void		urtwn_close_pipes(struct urtwn_softc *);
3549ec5c125Sdamien int		urtwn_alloc_rx_list(struct urtwn_softc *);
3559ec5c125Sdamien void		urtwn_free_rx_list(struct urtwn_softc *);
3569ec5c125Sdamien int		urtwn_alloc_tx_list(struct urtwn_softc *);
3579ec5c125Sdamien void		urtwn_free_tx_list(struct urtwn_softc *);
3589ec5c125Sdamien void		urtwn_task(void *);
3599ec5c125Sdamien void		urtwn_do_async(struct urtwn_softc *,
3609ec5c125Sdamien 		    void (*)(struct urtwn_softc *, void *), void *, int);
36123340becSstsp void		urtwn_wait_async(void *);
3629ec5c125Sdamien int		urtwn_write_region_1(struct urtwn_softc *, uint16_t, uint8_t *,
3639ec5c125Sdamien 		    int);
36423340becSstsp void		urtwn_write_1(void *, uint16_t, uint8_t);
36523340becSstsp void		urtwn_write_2(void *, uint16_t, uint16_t);
36623340becSstsp void		urtwn_write_4(void *, uint16_t, uint32_t);
3679ec5c125Sdamien int		urtwn_read_region_1(struct urtwn_softc *, uint16_t, uint8_t *,
3689ec5c125Sdamien 		    int);
36923340becSstsp uint8_t		urtwn_read_1(void *, uint16_t);
37023340becSstsp uint16_t	urtwn_read_2(void *, uint16_t);
37123340becSstsp uint32_t	urtwn_read_4(void *, uint16_t);
3729ec5c125Sdamien int		urtwn_llt_write(struct urtwn_softc *, uint32_t, uint32_t);
3739ec5c125Sdamien void		urtwn_calib_to(void *);
3749ec5c125Sdamien void		urtwn_calib_cb(struct urtwn_softc *, void *);
37523340becSstsp void		urtwn_scan_to(void *);
3769ec5c125Sdamien void		urtwn_next_scan(void *);
37723340becSstsp void		urtwn_cancel_scan(void *);
3789ec5c125Sdamien int		urtwn_newstate(struct ieee80211com *, enum ieee80211_state,
3799ec5c125Sdamien 		    int);
3809ec5c125Sdamien void		urtwn_newstate_cb(struct urtwn_softc *, void *);
381fb1f57eeSstsp void		urtwn_updateslot(struct ieee80211com *);
382fb1f57eeSstsp void		urtwn_updateslot_cb(struct urtwn_softc *, void *);
3839ec5c125Sdamien void		urtwn_updateedca(struct ieee80211com *);
3849ec5c125Sdamien void		urtwn_updateedca_cb(struct urtwn_softc *, void *);
3859ec5c125Sdamien int		urtwn_set_key(struct ieee80211com *, struct ieee80211_node *,
3869ec5c125Sdamien 		    struct ieee80211_key *);
3879ec5c125Sdamien void		urtwn_set_key_cb(struct urtwn_softc *, void *);
3889ec5c125Sdamien void		urtwn_delete_key(struct ieee80211com *,
3899ec5c125Sdamien 		    struct ieee80211_node *, struct ieee80211_key *);
3909ec5c125Sdamien void		urtwn_delete_key_cb(struct urtwn_softc *, void *);
3918fbaf8a2Sstsp void		urtwn_rx_frame(struct urtwn_softc *, uint8_t *, int,
3928fbaf8a2Sstsp 		    struct mbuf_list *);
393ab0b1be7Smglocker void		urtwn_rxeof(struct usbd_xfer *, void *,
3949ec5c125Sdamien 		    usbd_status);
395ab0b1be7Smglocker void		urtwn_txeof(struct usbd_xfer *, void *,
3969ec5c125Sdamien 		    usbd_status);
39723340becSstsp int		urtwn_tx(void *, struct mbuf *, struct ieee80211_node *);
3989ec5c125Sdamien int		urtwn_ioctl(struct ifnet *, u_long, caddr_t);
39923340becSstsp int		urtwn_power_on(void *);
40023340becSstsp int		urtwn_alloc_buffers(void *);
401c349b8bfSstsp int		urtwn_r92c_power_on(struct urtwn_softc *);
40290540544Sjmatthew int		urtwn_r92e_power_on(struct urtwn_softc *);
403c349b8bfSstsp int		urtwn_r88e_power_on(struct urtwn_softc *);
404067851b1Skevlo int		urtwn_r88f_power_on(struct urtwn_softc *);
40508300c66Skevlo int		urtwn_llt_init(struct urtwn_softc *, int);
40623340becSstsp int		urtwn_fw_loadpage(void *, int, uint8_t *, int);
40723340becSstsp int		urtwn_load_firmware(void *, u_char **, size_t *);
40823340becSstsp int		urtwn_dma_init(void *);
4092d2a7e26Skevlo void		urtwn_aggr_init(void *);
41023340becSstsp void		urtwn_mac_init(void *);
41123340becSstsp void		urtwn_bb_init(void *);
4122d2a7e26Skevlo void		urtwn_burstlen_init(struct urtwn_softc *);
41323340becSstsp int		urtwn_init(void *);
41423340becSstsp void		urtwn_stop(void *);
41523340becSstsp int		urtwn_is_oactive(void *);
41623340becSstsp void		urtwn_next_calib(void *);
41723340becSstsp void		urtwn_cancel_calib(void *);
4189ec5c125Sdamien 
4199ec5c125Sdamien /* Aliases. */
4209ec5c125Sdamien #define	urtwn_bb_write	urtwn_write_4
4219ec5c125Sdamien #define urtwn_bb_read	urtwn_read_4
4229ec5c125Sdamien 
4239ec5c125Sdamien struct cfdriver urtwn_cd = {
4249ec5c125Sdamien 	NULL, "urtwn", DV_IFNET
4259ec5c125Sdamien };
4269ec5c125Sdamien 
4279ec5c125Sdamien const struct cfattach urtwn_ca = {
42853c6612dSmpi 	sizeof(struct urtwn_softc), urtwn_match, urtwn_attach, urtwn_detach
4299ec5c125Sdamien };
4309ec5c125Sdamien 
4319ec5c125Sdamien int
4329ec5c125Sdamien urtwn_match(struct device *parent, void *match, void *aux)
4339ec5c125Sdamien {
4349ec5c125Sdamien 	struct usb_attach_arg *uaa = aux;
4359ec5c125Sdamien 
4367ebc5b51Smpi 	if (uaa->iface == NULL || uaa->configno != 1)
4379ec5c125Sdamien 		return (UMATCH_NONE);
4389ec5c125Sdamien 
43934a2cadeSkevlo 	return ((urtwn_lookup(uaa->vendor, uaa->product) != NULL) ?
4407ebc5b51Smpi 	    UMATCH_VENDOR_PRODUCT_CONF_IFACE : UMATCH_NONE);
4419ec5c125Sdamien }
4429ec5c125Sdamien 
4439ec5c125Sdamien void
4449ec5c125Sdamien urtwn_attach(struct device *parent, struct device *self, void *aux)
4459ec5c125Sdamien {
4469ec5c125Sdamien 	struct urtwn_softc *sc = (struct urtwn_softc *)self;
4479ec5c125Sdamien 	struct usb_attach_arg *uaa = aux;
44823340becSstsp 	struct ifnet *ifp;
44923340becSstsp 	struct ieee80211com *ic = &sc->sc_sc.sc_ic;
4509ec5c125Sdamien 
4519ec5c125Sdamien 	sc->sc_udev = uaa->device;
4527ebc5b51Smpi 	sc->sc_iface = uaa->iface;
4539ec5c125Sdamien 
45434a2cadeSkevlo 	sc->sc_sc.chip = urtwn_lookup(uaa->vendor, uaa->product)->chip;
45534a2cadeSkevlo 
4569ec5c125Sdamien 	usb_init_task(&sc->sc_task, urtwn_task, sc, USB_TASK_TYPE_GENERIC);
45723340becSstsp 	timeout_set(&sc->scan_to, urtwn_scan_to, sc);
4589ec5c125Sdamien 	timeout_set(&sc->calib_to, urtwn_calib_to, sc);
45923340becSstsp 	if (urtwn_open_pipes(sc) != 0)
46023340becSstsp 		return;
4619ec5c125Sdamien 
462a087613eSstsp 	sc->amrr.amrr_min_success_threshold =  1;
463a087613eSstsp 	sc->amrr.amrr_max_success_threshold = 10;
464a087613eSstsp 
46523340becSstsp 	/* Attach the bus-agnostic driver. */
46623340becSstsp 	sc->sc_sc.sc_ops.cookie = sc;
46723340becSstsp 	sc->sc_sc.sc_ops.write_1 = urtwn_write_1;
46823340becSstsp 	sc->sc_sc.sc_ops.write_2 = urtwn_write_2;
46923340becSstsp 	sc->sc_sc.sc_ops.write_4 = urtwn_write_4;
47023340becSstsp 	sc->sc_sc.sc_ops.read_1 = urtwn_read_1;
47123340becSstsp 	sc->sc_sc.sc_ops.read_2 = urtwn_read_2;
47223340becSstsp 	sc->sc_sc.sc_ops.read_4 = urtwn_read_4;
47323340becSstsp 	sc->sc_sc.sc_ops.tx = urtwn_tx;
47423340becSstsp 	sc->sc_sc.sc_ops.power_on = urtwn_power_on;
47523340becSstsp 	sc->sc_sc.sc_ops.dma_init = urtwn_dma_init;
47623340becSstsp 	sc->sc_sc.sc_ops.fw_loadpage = urtwn_fw_loadpage;
47723340becSstsp 	sc->sc_sc.sc_ops.load_firmware = urtwn_load_firmware;
4782d2a7e26Skevlo 	sc->sc_sc.sc_ops.aggr_init = urtwn_aggr_init;
47923340becSstsp 	sc->sc_sc.sc_ops.mac_init = urtwn_mac_init;
48023340becSstsp 	sc->sc_sc.sc_ops.bb_init = urtwn_bb_init;
48123340becSstsp 	sc->sc_sc.sc_ops.alloc_buffers = urtwn_alloc_buffers;
48223340becSstsp 	sc->sc_sc.sc_ops.init = urtwn_init;
48323340becSstsp 	sc->sc_sc.sc_ops.stop = urtwn_stop;
48423340becSstsp 	sc->sc_sc.sc_ops.is_oactive = urtwn_is_oactive;
48523340becSstsp 	sc->sc_sc.sc_ops.next_calib = urtwn_next_calib;
48623340becSstsp 	sc->sc_sc.sc_ops.cancel_calib = urtwn_cancel_calib;
48723340becSstsp 	sc->sc_sc.sc_ops.next_scan = urtwn_next_scan;
48823340becSstsp 	sc->sc_sc.sc_ops.cancel_scan = urtwn_cancel_scan;
48923340becSstsp 	sc->sc_sc.sc_ops.wait_async = urtwn_wait_async;
49034a2cadeSkevlo 	if (rtwn_attach(&sc->sc_dev, &sc->sc_sc) != 0) {
49123340becSstsp 		urtwn_close_pipes(sc);
4929ec5c125Sdamien 		return;
4939ec5c125Sdamien 	}
4949ec5c125Sdamien 
49523340becSstsp 	/* ifp is now valid */
49623340becSstsp 	ifp = &sc->sc_sc.sc_ic.ic_if;
4979ec5c125Sdamien 	ifp->if_ioctl = urtwn_ioctl;
4989ec5c125Sdamien 
499fb1f57eeSstsp 	ic->ic_updateslot = urtwn_updateslot;
5009ec5c125Sdamien 	ic->ic_updateedca = urtwn_updateedca;
5019ec5c125Sdamien 	ic->ic_set_key = urtwn_set_key;
5029ec5c125Sdamien 	ic->ic_delete_key = urtwn_delete_key;
5039ec5c125Sdamien 	/* Override state transition machine. */
5049ec5c125Sdamien 	ic->ic_newstate = urtwn_newstate;
5059ec5c125Sdamien 
5069ec5c125Sdamien #if NBPFILTER > 0
5079ec5c125Sdamien 	bpfattach(&sc->sc_drvbpf, ifp, DLT_IEEE802_11_RADIO,
5089ec5c125Sdamien 	    sizeof(struct ieee80211_frame) + IEEE80211_RADIOTAP_HDRLEN);
5099ec5c125Sdamien 
5109ec5c125Sdamien 	sc->sc_rxtap_len = sizeof(sc->sc_rxtapu);
5119ec5c125Sdamien 	sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len);
5129ec5c125Sdamien 	sc->sc_rxtap.wr_ihdr.it_present = htole32(URTWN_RX_RADIOTAP_PRESENT);
5139ec5c125Sdamien 
5149ec5c125Sdamien 	sc->sc_txtap_len = sizeof(sc->sc_txtapu);
5159ec5c125Sdamien 	sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len);
5169ec5c125Sdamien 	sc->sc_txtap.wt_ihdr.it_present = htole32(URTWN_TX_RADIOTAP_PRESENT);
5179ec5c125Sdamien #endif
5189ec5c125Sdamien }
5199ec5c125Sdamien 
5209ec5c125Sdamien int
5219ec5c125Sdamien urtwn_detach(struct device *self, int flags)
5229ec5c125Sdamien {
5239ec5c125Sdamien 	struct urtwn_softc *sc = (struct urtwn_softc *)self;
5249ec5c125Sdamien 	int s;
5259ec5c125Sdamien 
5265ad57547Sjakemsr 	s = splusb();
5279ec5c125Sdamien 
5289ec5c125Sdamien 	if (timeout_initialized(&sc->scan_to))
5299ec5c125Sdamien 		timeout_del(&sc->scan_to);
5309ec5c125Sdamien 	if (timeout_initialized(&sc->calib_to))
5319ec5c125Sdamien 		timeout_del(&sc->calib_to);
5325ad57547Sjakemsr 
5335ad57547Sjakemsr 	/* Wait for all async commands to complete. */
5345ad57547Sjakemsr 	usb_rem_wait_task(sc->sc_udev, &sc->sc_task);
5355ad57547Sjakemsr 
5365ad57547Sjakemsr 	usbd_ref_wait(sc->sc_udev);
5375ad57547Sjakemsr 
53823340becSstsp 	rtwn_detach(&sc->sc_sc, flags);
5399ec5c125Sdamien 
5409ec5c125Sdamien 	/* Abort and close Tx/Rx pipes. */
5419ec5c125Sdamien 	urtwn_close_pipes(sc);
5429ec5c125Sdamien 
5439ec5c125Sdamien 	/* Free Tx/Rx buffers. */
5449ec5c125Sdamien 	urtwn_free_tx_list(sc);
5459ec5c125Sdamien 	urtwn_free_rx_list(sc);
5469ec5c125Sdamien 	splx(s);
5479ec5c125Sdamien 
5489ec5c125Sdamien 	return (0);
5499ec5c125Sdamien }
5509ec5c125Sdamien 
5519ec5c125Sdamien int
5529ec5c125Sdamien urtwn_open_pipes(struct urtwn_softc *sc)
5539ec5c125Sdamien {
5549ec5c125Sdamien 	/* Bulk-out endpoints addresses (from highest to lowest prio). */
55508300c66Skevlo 	uint8_t epaddr[R92C_MAX_EPOUT] = { 0, 0, 0 };
55608300c66Skevlo 	uint8_t rx_no;
5579ec5c125Sdamien 	usb_interface_descriptor_t *id;
5589ec5c125Sdamien 	usb_endpoint_descriptor_t *ed;
5590b85f768Sstsp 	int i, error, nrx = 0;
5609ec5c125Sdamien 
56108300c66Skevlo 	/* Find all bulk endpoints. */
5629ec5c125Sdamien 	id = usbd_get_interface_descriptor(sc->sc_iface);
5639ec5c125Sdamien 	for (i = 0; i < id->bNumEndpoints; i++) {
5649ec5c125Sdamien 		ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
56508300c66Skevlo 		if (ed == NULL || UE_GET_XFERTYPE(ed->bmAttributes) != UE_BULK)
56608300c66Skevlo 			continue;
56708300c66Skevlo 
5680b85f768Sstsp 		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN) {
56908300c66Skevlo 			rx_no = ed->bEndpointAddress;
5700b85f768Sstsp 			nrx++;
5710b85f768Sstsp 		} else {
57239ab79cfSstsp 			if (sc->ntx < R92C_MAX_EPOUT)
57308300c66Skevlo 				epaddr[sc->ntx] = ed->bEndpointAddress;
57408300c66Skevlo 			sc->ntx++;
5759ec5c125Sdamien 		}
57608300c66Skevlo 	}
5770b85f768Sstsp 	if (nrx == 0) {
5780b85f768Sstsp 		printf("%s: %d: invalid number of Rx bulk pipes\n",
5790b85f768Sstsp 		    sc->sc_dev.dv_xname, nrx);
5800b85f768Sstsp 		return (EIO);
5810b85f768Sstsp 	}
58208300c66Skevlo 	DPRINTF(("found %d bulk-out pipes\n", sc->ntx));
58308300c66Skevlo 	if (sc->ntx == 0 || sc->ntx > R92C_MAX_EPOUT) {
5849ec5c125Sdamien 		printf("%s: %d: invalid number of Tx bulk pipes\n",
58508300c66Skevlo 		    sc->sc_dev.dv_xname, sc->ntx);
5869ec5c125Sdamien 		return (EIO);
5879ec5c125Sdamien 	}
5889ec5c125Sdamien 
58908300c66Skevlo 	/* Open bulk-in pipe. */
59008300c66Skevlo 	error = usbd_open_pipe(sc->sc_iface, rx_no, 0, &sc->rx_pipe);
5919ec5c125Sdamien 	if (error != 0) {
5929ec5c125Sdamien 		printf("%s: could not open Rx bulk pipe\n",
5939ec5c125Sdamien 		    sc->sc_dev.dv_xname);
5949ec5c125Sdamien 		goto fail;
5959ec5c125Sdamien 	}
5969ec5c125Sdamien 
5979ec5c125Sdamien 	/* Open bulk-out pipes (up to 3). */
59808300c66Skevlo 	for (i = 0; i < sc->ntx; i++) {
5999ec5c125Sdamien 		error = usbd_open_pipe(sc->sc_iface, epaddr[i], 0,
6009ec5c125Sdamien 		    &sc->tx_pipe[i]);
6019ec5c125Sdamien 		if (error != 0) {
6029ec5c125Sdamien 			printf("%s: could not open Tx bulk pipe 0x%02x\n",
6039ec5c125Sdamien 			    sc->sc_dev.dv_xname, epaddr[i]);
6049ec5c125Sdamien 			goto fail;
6059ec5c125Sdamien 		}
6069ec5c125Sdamien 	}
6079ec5c125Sdamien 
6089ec5c125Sdamien 	/* Map 802.11 access categories to USB pipes. */
6099ec5c125Sdamien 	sc->ac2idx[EDCA_AC_BK] =
61008300c66Skevlo 	sc->ac2idx[EDCA_AC_BE] = (sc->ntx == 3) ? 2 : ((sc->ntx == 2) ? 1 : 0);
61108300c66Skevlo 	sc->ac2idx[EDCA_AC_VI] = (sc->ntx == 3) ? 1 : 0;
6129ec5c125Sdamien 	sc->ac2idx[EDCA_AC_VO] = 0;	/* Always use highest prio. */
6139ec5c125Sdamien 
6149ec5c125Sdamien 	if (error != 0)
6159ec5c125Sdamien  fail:		urtwn_close_pipes(sc);
6169ec5c125Sdamien 	return (error);
6179ec5c125Sdamien }
6189ec5c125Sdamien 
6199ec5c125Sdamien void
6209ec5c125Sdamien urtwn_close_pipes(struct urtwn_softc *sc)
6219ec5c125Sdamien {
6229ec5c125Sdamien 	int i;
6239ec5c125Sdamien 
6249ec5c125Sdamien 	/* Close Rx pipe. */
6258bca19d0Skevlo 	if (sc->rx_pipe != NULL) {
6269ec5c125Sdamien 		usbd_close_pipe(sc->rx_pipe);
6278bca19d0Skevlo 		sc->rx_pipe = NULL;
6288bca19d0Skevlo 	}
6299ec5c125Sdamien 	/* Close Tx pipes. */
6309ec5c125Sdamien 	for (i = 0; i < R92C_MAX_EPOUT; i++) {
6319ec5c125Sdamien 		if (sc->tx_pipe[i] == NULL)
6329ec5c125Sdamien 			continue;
6339ec5c125Sdamien 		usbd_close_pipe(sc->tx_pipe[i]);
6348bca19d0Skevlo 		sc->tx_pipe[i] = NULL;
6359ec5c125Sdamien 	}
6369ec5c125Sdamien }
6379ec5c125Sdamien 
6389ec5c125Sdamien int
6399ec5c125Sdamien urtwn_alloc_rx_list(struct urtwn_softc *sc)
6409ec5c125Sdamien {
6419ec5c125Sdamien 	struct urtwn_rx_data *data;
6429ec5c125Sdamien 	int i, error = 0;
6439ec5c125Sdamien 
6449ec5c125Sdamien 	for (i = 0; i < URTWN_RX_LIST_COUNT; i++) {
6459ec5c125Sdamien 		data = &sc->rx_data[i];
6469ec5c125Sdamien 
6479ec5c125Sdamien 		data->sc = sc;	/* Backpointer for callbacks. */
6489ec5c125Sdamien 
6499ec5c125Sdamien 		data->xfer = usbd_alloc_xfer(sc->sc_udev);
6509ec5c125Sdamien 		if (data->xfer == NULL) {
6519ec5c125Sdamien 			printf("%s: could not allocate xfer\n",
6529ec5c125Sdamien 			    sc->sc_dev.dv_xname);
6539ec5c125Sdamien 			error = ENOMEM;
6549ec5c125Sdamien 			break;
6559ec5c125Sdamien 		}
6569ec5c125Sdamien 		data->buf = usbd_alloc_buffer(data->xfer, URTWN_RXBUFSZ);
6579ec5c125Sdamien 		if (data->buf == NULL) {
6589ec5c125Sdamien 			printf("%s: could not allocate xfer buffer\n",
6599ec5c125Sdamien 			    sc->sc_dev.dv_xname);
6609ec5c125Sdamien 			error = ENOMEM;
6619ec5c125Sdamien 			break;
6629ec5c125Sdamien 		}
6639ec5c125Sdamien 	}
6649ec5c125Sdamien 	if (error != 0)
6659ec5c125Sdamien 		urtwn_free_rx_list(sc);
6669ec5c125Sdamien 	return (error);
6679ec5c125Sdamien }
6689ec5c125Sdamien 
6699ec5c125Sdamien void
6709ec5c125Sdamien urtwn_free_rx_list(struct urtwn_softc *sc)
6719ec5c125Sdamien {
6729ec5c125Sdamien 	int i;
6739ec5c125Sdamien 
6749ec5c125Sdamien 	/* NB: Caller must abort pipe first. */
6759ec5c125Sdamien 	for (i = 0; i < URTWN_RX_LIST_COUNT; i++) {
6769ec5c125Sdamien 		if (sc->rx_data[i].xfer != NULL)
6779ec5c125Sdamien 			usbd_free_xfer(sc->rx_data[i].xfer);
6789ec5c125Sdamien 		sc->rx_data[i].xfer = NULL;
6799ec5c125Sdamien 	}
6809ec5c125Sdamien }
6819ec5c125Sdamien 
6829ec5c125Sdamien int
6839ec5c125Sdamien urtwn_alloc_tx_list(struct urtwn_softc *sc)
6849ec5c125Sdamien {
6859ec5c125Sdamien 	struct urtwn_tx_data *data;
6869ec5c125Sdamien 	int i, error = 0;
6879ec5c125Sdamien 
6889ec5c125Sdamien 	TAILQ_INIT(&sc->tx_free_list);
6899ec5c125Sdamien 	for (i = 0; i < URTWN_TX_LIST_COUNT; i++) {
6909ec5c125Sdamien 		data = &sc->tx_data[i];
6919ec5c125Sdamien 
6929ec5c125Sdamien 		data->sc = sc;	/* Backpointer for callbacks. */
6939ec5c125Sdamien 
6949ec5c125Sdamien 		data->xfer = usbd_alloc_xfer(sc->sc_udev);
6959ec5c125Sdamien 		if (data->xfer == NULL) {
6969ec5c125Sdamien 			printf("%s: could not allocate xfer\n",
6979ec5c125Sdamien 			    sc->sc_dev.dv_xname);
6989ec5c125Sdamien 			error = ENOMEM;
6999ec5c125Sdamien 			break;
7009ec5c125Sdamien 		}
7019ec5c125Sdamien 		data->buf = usbd_alloc_buffer(data->xfer, URTWN_TXBUFSZ);
7029ec5c125Sdamien 		if (data->buf == NULL) {
7039ec5c125Sdamien 			printf("%s: could not allocate xfer buffer\n",
7049ec5c125Sdamien 			    sc->sc_dev.dv_xname);
7059ec5c125Sdamien 			error = ENOMEM;
7069ec5c125Sdamien 			break;
7079ec5c125Sdamien 		}
7089ec5c125Sdamien 		/* Append this Tx buffer to our free list. */
7099ec5c125Sdamien 		TAILQ_INSERT_TAIL(&sc->tx_free_list, data, next);
7109ec5c125Sdamien 	}
7119ec5c125Sdamien 	if (error != 0)
7129ec5c125Sdamien 		urtwn_free_tx_list(sc);
7139ec5c125Sdamien 	return (error);
7149ec5c125Sdamien }
7159ec5c125Sdamien 
7169ec5c125Sdamien void
7179ec5c125Sdamien urtwn_free_tx_list(struct urtwn_softc *sc)
7189ec5c125Sdamien {
7199ec5c125Sdamien 	int i;
7209ec5c125Sdamien 
7219ec5c125Sdamien 	/* NB: Caller must abort pipe first. */
7229ec5c125Sdamien 	for (i = 0; i < URTWN_TX_LIST_COUNT; i++) {
7239ec5c125Sdamien 		if (sc->tx_data[i].xfer != NULL)
7249ec5c125Sdamien 			usbd_free_xfer(sc->tx_data[i].xfer);
7259ec5c125Sdamien 		sc->tx_data[i].xfer = NULL;
7269ec5c125Sdamien 	}
7279ec5c125Sdamien }
7289ec5c125Sdamien 
7299ec5c125Sdamien void
7309ec5c125Sdamien urtwn_task(void *arg)
7319ec5c125Sdamien {
7329ec5c125Sdamien 	struct urtwn_softc *sc = arg;
7339ec5c125Sdamien 	struct urtwn_host_cmd_ring *ring = &sc->cmdq;
7349ec5c125Sdamien 	struct urtwn_host_cmd *cmd;
7359ec5c125Sdamien 	int s;
7369ec5c125Sdamien 
7379ec5c125Sdamien 	/* Process host commands. */
7389ec5c125Sdamien 	s = splusb();
7399ec5c125Sdamien 	while (ring->next != ring->cur) {
7409ec5c125Sdamien 		cmd = &ring->cmd[ring->next];
7419ec5c125Sdamien 		splx(s);
7429ec5c125Sdamien 		/* Invoke callback. */
7439ec5c125Sdamien 		cmd->cb(sc, cmd->data);
7449ec5c125Sdamien 		s = splusb();
7459ec5c125Sdamien 		ring->queued--;
7469ec5c125Sdamien 		ring->next = (ring->next + 1) % URTWN_HOST_CMD_RING_COUNT;
7479ec5c125Sdamien 	}
7489ec5c125Sdamien 	splx(s);
7499ec5c125Sdamien }
7509ec5c125Sdamien 
7519ec5c125Sdamien void
7529ec5c125Sdamien urtwn_do_async(struct urtwn_softc *sc,
7539ec5c125Sdamien     void (*cb)(struct urtwn_softc *, void *), void *arg, int len)
7549ec5c125Sdamien {
7559ec5c125Sdamien 	struct urtwn_host_cmd_ring *ring = &sc->cmdq;
7569ec5c125Sdamien 	struct urtwn_host_cmd *cmd;
7579ec5c125Sdamien 	int s;
7589ec5c125Sdamien 
7599ec5c125Sdamien 	s = splusb();
7609ec5c125Sdamien 	cmd = &ring->cmd[ring->cur];
7619ec5c125Sdamien 	cmd->cb = cb;
7629ec5c125Sdamien 	KASSERT(len <= sizeof(cmd->data));
7639ec5c125Sdamien 	memcpy(cmd->data, arg, len);
7649ec5c125Sdamien 	ring->cur = (ring->cur + 1) % URTWN_HOST_CMD_RING_COUNT;
7659ec5c125Sdamien 
7669ec5c125Sdamien 	/* If there is no pending command already, schedule a task. */
7679ec5c125Sdamien 	if (++ring->queued == 1)
7689ec5c125Sdamien 		usb_add_task(sc->sc_udev, &sc->sc_task);
7699ec5c125Sdamien 	splx(s);
7709ec5c125Sdamien }
7719ec5c125Sdamien 
7729ec5c125Sdamien void
77323340becSstsp urtwn_wait_async(void *cookie)
7749ec5c125Sdamien {
77523340becSstsp 	struct urtwn_softc *sc = cookie;
77623340becSstsp 	int s;
77723340becSstsp 
77823340becSstsp 	s = splusb();
7799ec5c125Sdamien 	/* Wait for all queued asynchronous commands to complete. */
7805ad57547Sjakemsr 	usb_wait_task(sc->sc_udev, &sc->sc_task);
78123340becSstsp 	splx(s);
7829ec5c125Sdamien }
7839ec5c125Sdamien 
7849ec5c125Sdamien int
7859ec5c125Sdamien urtwn_write_region_1(struct urtwn_softc *sc, uint16_t addr, uint8_t *buf,
7869ec5c125Sdamien     int len)
7879ec5c125Sdamien {
7889ec5c125Sdamien 	usb_device_request_t req;
7899ec5c125Sdamien 
7909ec5c125Sdamien 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
7919ec5c125Sdamien 	req.bRequest = R92C_REQ_REGS;
7929ec5c125Sdamien 	USETW(req.wValue, addr);
7939ec5c125Sdamien 	USETW(req.wIndex, 0);
7949ec5c125Sdamien 	USETW(req.wLength, len);
7959ec5c125Sdamien 	return (usbd_do_request(sc->sc_udev, &req, buf));
7969ec5c125Sdamien }
7979ec5c125Sdamien 
7989ec5c125Sdamien void
79923340becSstsp urtwn_write_1(void *cookie, uint16_t addr, uint8_t val)
8009ec5c125Sdamien {
80123340becSstsp 	struct urtwn_softc *sc = cookie;
80223340becSstsp 
8039ec5c125Sdamien 	urtwn_write_region_1(sc, addr, &val, 1);
8049ec5c125Sdamien }
8059ec5c125Sdamien 
8069ec5c125Sdamien void
80723340becSstsp urtwn_write_2(void *cookie, uint16_t addr, uint16_t val)
8089ec5c125Sdamien {
80923340becSstsp 	struct urtwn_softc *sc = cookie;
81023340becSstsp 
8119ec5c125Sdamien 	val = htole16(val);
8129ec5c125Sdamien 	urtwn_write_region_1(sc, addr, (uint8_t *)&val, 2);
8139ec5c125Sdamien }
8149ec5c125Sdamien 
8159ec5c125Sdamien void
81623340becSstsp urtwn_write_4(void *cookie, uint16_t addr, uint32_t val)
8179ec5c125Sdamien {
81823340becSstsp 	struct urtwn_softc *sc = cookie;
81923340becSstsp 
8209ec5c125Sdamien 	val = htole32(val);
8219ec5c125Sdamien 	urtwn_write_region_1(sc, addr, (uint8_t *)&val, 4);
8229ec5c125Sdamien }
8239ec5c125Sdamien 
8249ec5c125Sdamien int
8259ec5c125Sdamien urtwn_read_region_1(struct urtwn_softc *sc, uint16_t addr, uint8_t *buf,
8269ec5c125Sdamien     int len)
8279ec5c125Sdamien {
8289ec5c125Sdamien 	usb_device_request_t req;
8299ec5c125Sdamien 
8309ec5c125Sdamien 	req.bmRequestType = UT_READ_VENDOR_DEVICE;
8319ec5c125Sdamien 	req.bRequest = R92C_REQ_REGS;
8329ec5c125Sdamien 	USETW(req.wValue, addr);
8339ec5c125Sdamien 	USETW(req.wIndex, 0);
8349ec5c125Sdamien 	USETW(req.wLength, len);
8359ec5c125Sdamien 	return (usbd_do_request(sc->sc_udev, &req, buf));
8369ec5c125Sdamien }
8379ec5c125Sdamien 
8389ec5c125Sdamien uint8_t
83923340becSstsp urtwn_read_1(void *cookie, uint16_t addr)
8409ec5c125Sdamien {
84123340becSstsp 	struct urtwn_softc *sc = cookie;
8429ec5c125Sdamien 	uint8_t val;
8439ec5c125Sdamien 
8449ec5c125Sdamien 	if (urtwn_read_region_1(sc, addr, &val, 1) != 0)
8459ec5c125Sdamien 		return (0xff);
8469ec5c125Sdamien 	return (val);
8479ec5c125Sdamien }
8489ec5c125Sdamien 
8499ec5c125Sdamien uint16_t
85023340becSstsp urtwn_read_2(void *cookie, uint16_t addr)
8519ec5c125Sdamien {
85223340becSstsp 	struct urtwn_softc *sc = cookie;
8539ec5c125Sdamien 	uint16_t val;
8549ec5c125Sdamien 
8559ec5c125Sdamien 	if (urtwn_read_region_1(sc, addr, (uint8_t *)&val, 2) != 0)
8569ec5c125Sdamien 		return (0xffff);
8579ec5c125Sdamien 	return (letoh16(val));
8589ec5c125Sdamien }
8599ec5c125Sdamien 
8609ec5c125Sdamien uint32_t
86123340becSstsp urtwn_read_4(void *cookie, uint16_t addr)
8629ec5c125Sdamien {
86323340becSstsp 	struct urtwn_softc *sc = cookie;
8649ec5c125Sdamien 	uint32_t val;
8659ec5c125Sdamien 
8669ec5c125Sdamien 	if (urtwn_read_region_1(sc, addr, (uint8_t *)&val, 4) != 0)
8679ec5c125Sdamien 		return (0xffffffff);
8689ec5c125Sdamien 	return (letoh32(val));
8699ec5c125Sdamien }
8709ec5c125Sdamien 
8719ec5c125Sdamien int
8729ec5c125Sdamien urtwn_llt_write(struct urtwn_softc *sc, uint32_t addr, uint32_t data)
8739ec5c125Sdamien {
8749ec5c125Sdamien 	int ntries;
8759ec5c125Sdamien 
8769ec5c125Sdamien 	urtwn_write_4(sc, R92C_LLT_INIT,
8779ec5c125Sdamien 	    SM(R92C_LLT_INIT_OP, R92C_LLT_INIT_OP_WRITE) |
8789ec5c125Sdamien 	    SM(R92C_LLT_INIT_ADDR, addr) |
8799ec5c125Sdamien 	    SM(R92C_LLT_INIT_DATA, data));
8809ec5c125Sdamien 	/* Wait for write operation to complete. */
8819ec5c125Sdamien 	for (ntries = 0; ntries < 20; ntries++) {
8829ec5c125Sdamien 		if (MS(urtwn_read_4(sc, R92C_LLT_INIT), R92C_LLT_INIT_OP) ==
8839ec5c125Sdamien 		    R92C_LLT_INIT_OP_NO_ACTIVE)
8849ec5c125Sdamien 			return (0);
8859ec5c125Sdamien 		DELAY(5);
8869ec5c125Sdamien 	}
8879ec5c125Sdamien 	return (ETIMEDOUT);
8889ec5c125Sdamien }
8899ec5c125Sdamien 
8909ec5c125Sdamien void
8919ec5c125Sdamien urtwn_calib_to(void *arg)
8929ec5c125Sdamien {
8939ec5c125Sdamien 	struct urtwn_softc *sc = arg;
8949ec5c125Sdamien 
8955ad57547Sjakemsr 	if (usbd_is_dying(sc->sc_udev))
8965ad57547Sjakemsr 		return;
8975ad57547Sjakemsr 
8985ad57547Sjakemsr 	usbd_ref_incr(sc->sc_udev);
8995ad57547Sjakemsr 
9009ec5c125Sdamien 	/* Do it in a process context. */
9019ec5c125Sdamien 	urtwn_do_async(sc, urtwn_calib_cb, NULL, 0);
9025ad57547Sjakemsr 
9035ad57547Sjakemsr 	usbd_ref_decr(sc->sc_udev);
9049ec5c125Sdamien }
9059ec5c125Sdamien 
9069ec5c125Sdamien void
9079ec5c125Sdamien urtwn_calib_cb(struct urtwn_softc *sc, void *arg)
9089ec5c125Sdamien {
909a087613eSstsp 	struct ieee80211com *ic = &sc->sc_sc.sc_ic;
910a087613eSstsp 	int s;
911a087613eSstsp 
912a087613eSstsp 	s = splnet();
913a087613eSstsp 	if (ic->ic_opmode == IEEE80211_M_STA) {
914a087613eSstsp 		ieee80211_amrr_choose(&sc->amrr, ic->ic_bss, &sc->amn);
915a087613eSstsp 	}
916a087613eSstsp 	splx(s);
917a087613eSstsp 
91823340becSstsp 	rtwn_calib(&sc->sc_sc);
9199ec5c125Sdamien }
9209ec5c125Sdamien 
92123340becSstsp void
92223340becSstsp urtwn_next_calib(void *cookie)
92323340becSstsp {
92423340becSstsp 	struct urtwn_softc *sc = cookie;
9255e709a81Sdamien 
9265ad57547Sjakemsr 	if (!usbd_is_dying(sc->sc_udev))
9279ec5c125Sdamien 		timeout_add_sec(&sc->calib_to, 2);
9289ec5c125Sdamien }
9299ec5c125Sdamien 
9309ec5c125Sdamien void
93123340becSstsp urtwn_cancel_calib(void *cookie)
93223340becSstsp {
93323340becSstsp 	struct urtwn_softc *sc = cookie;
93423340becSstsp 
93523340becSstsp 	if (timeout_initialized(&sc->calib_to))
93623340becSstsp 		timeout_del(&sc->calib_to);
93723340becSstsp }
93823340becSstsp 
93923340becSstsp void
94023340becSstsp urtwn_scan_to(void *arg)
9419ec5c125Sdamien {
9429ec5c125Sdamien 	struct urtwn_softc *sc = arg;
9439ec5c125Sdamien 
9445ad57547Sjakemsr 	if (usbd_is_dying(sc->sc_udev))
9455ad57547Sjakemsr 		return;
9465ad57547Sjakemsr 
9475ad57547Sjakemsr 	usbd_ref_incr(sc->sc_udev);
94823340becSstsp 	rtwn_next_scan(&sc->sc_sc);
9495ad57547Sjakemsr 	usbd_ref_decr(sc->sc_udev);
9509ec5c125Sdamien }
9519ec5c125Sdamien 
95223340becSstsp void
95323340becSstsp urtwn_next_scan(void *arg)
95423340becSstsp {
95523340becSstsp 	struct urtwn_softc *sc = arg;
95623340becSstsp 
95723340becSstsp 	if (!usbd_is_dying(sc->sc_udev))
95823340becSstsp 		timeout_add_msec(&sc->scan_to, 200);
95923340becSstsp }
96023340becSstsp 
96123340becSstsp void
96223340becSstsp urtwn_cancel_scan(void *cookie)
96323340becSstsp {
96423340becSstsp 	struct urtwn_softc *sc = cookie;
96523340becSstsp 
96623340becSstsp 	if (timeout_initialized(&sc->scan_to))
96723340becSstsp 		timeout_del(&sc->scan_to);
96823340becSstsp }
96923340becSstsp 
9709ec5c125Sdamien int
9719ec5c125Sdamien urtwn_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
9729ec5c125Sdamien {
97323340becSstsp 	struct rtwn_softc *sc_sc = ic->ic_softc;
97423340becSstsp 	struct device *self = sc_sc->sc_pdev;
97523340becSstsp 	struct urtwn_softc *sc = (struct urtwn_softc *)self;
9769ec5c125Sdamien 	struct urtwn_cmd_newstate cmd;
9779ec5c125Sdamien 
9789ec5c125Sdamien 	/* Do it in a process context. */
9799ec5c125Sdamien 	cmd.state = nstate;
9809ec5c125Sdamien 	cmd.arg = arg;
9819ec5c125Sdamien 	urtwn_do_async(sc, urtwn_newstate_cb, &cmd, sizeof(cmd));
9829ec5c125Sdamien 	return (0);
9839ec5c125Sdamien }
9849ec5c125Sdamien 
9859ec5c125Sdamien void
9869ec5c125Sdamien urtwn_newstate_cb(struct urtwn_softc *sc, void *arg)
9879ec5c125Sdamien {
9889ec5c125Sdamien 	struct urtwn_cmd_newstate *cmd = arg;
98923340becSstsp 	struct ieee80211com *ic = &sc->sc_sc.sc_ic;
9909ec5c125Sdamien 
99123340becSstsp 	rtwn_newstate(ic, cmd->state, cmd->arg);
9929ec5c125Sdamien }
9939ec5c125Sdamien 
9949ec5c125Sdamien void
995fb1f57eeSstsp urtwn_updateslot(struct ieee80211com *ic)
996fb1f57eeSstsp {
997fb1f57eeSstsp 	struct rtwn_softc *sc_sc = ic->ic_softc;
998fb1f57eeSstsp 	struct device *self = sc_sc->sc_pdev;
999fb1f57eeSstsp 	struct urtwn_softc *sc = (struct urtwn_softc *)self;
1000fb1f57eeSstsp 
1001fb1f57eeSstsp 	/* Do it in a process context. */
1002fb1f57eeSstsp 	urtwn_do_async(sc, urtwn_updateslot_cb, NULL, 0);
1003fb1f57eeSstsp }
1004fb1f57eeSstsp 
1005fb1f57eeSstsp void
1006fb1f57eeSstsp urtwn_updateslot_cb(struct urtwn_softc *sc, void *arg)
1007fb1f57eeSstsp {
1008fb1f57eeSstsp 	struct ieee80211com *ic = &sc->sc_sc.sc_ic;
1009fb1f57eeSstsp 
1010fb1f57eeSstsp 	rtwn_updateslot(ic);
1011fb1f57eeSstsp }
1012fb1f57eeSstsp 
1013fb1f57eeSstsp void
10149ec5c125Sdamien urtwn_updateedca(struct ieee80211com *ic)
10159ec5c125Sdamien {
101623340becSstsp 	struct rtwn_softc *sc_sc = ic->ic_softc;
101723340becSstsp 	struct device *self = sc_sc->sc_pdev;
101823340becSstsp 	struct urtwn_softc *sc = (struct urtwn_softc *)self;
101923340becSstsp 
10209ec5c125Sdamien 	/* Do it in a process context. */
102123340becSstsp 	urtwn_do_async(sc, urtwn_updateedca_cb, NULL, 0);
10229ec5c125Sdamien }
10239ec5c125Sdamien 
10249ec5c125Sdamien void
10259ec5c125Sdamien urtwn_updateedca_cb(struct urtwn_softc *sc, void *arg)
10269ec5c125Sdamien {
102723340becSstsp 	struct ieee80211com *ic = &sc->sc_sc.sc_ic;
10289ec5c125Sdamien 
102923340becSstsp 	rtwn_updateedca(ic);
10309ec5c125Sdamien }
10319ec5c125Sdamien 
10329ec5c125Sdamien int
10339ec5c125Sdamien urtwn_set_key(struct ieee80211com *ic, struct ieee80211_node *ni,
10349ec5c125Sdamien     struct ieee80211_key *k)
10359ec5c125Sdamien {
103623340becSstsp 	struct rtwn_softc *sc_sc = ic->ic_softc;
103723340becSstsp 	struct device *self = sc_sc->sc_pdev;
103823340becSstsp 	struct urtwn_softc *sc = (struct urtwn_softc *)self;
10399ec5c125Sdamien 	struct urtwn_cmd_key cmd;
10409ec5c125Sdamien 
1041a299d652Sjmatthew 	/* Only handle keys for CCMP */
1042a299d652Sjmatthew 	if (k->k_cipher != IEEE80211_CIPHER_CCMP)
1043a299d652Sjmatthew 		return ieee80211_set_key(ic, ni, k);
1044a299d652Sjmatthew 
10459ec5c125Sdamien 	/* Defer setting of WEP keys until interface is brought up. */
10469ec5c125Sdamien 	if ((ic->ic_if.if_flags & (IFF_UP | IFF_RUNNING)) !=
10479ec5c125Sdamien 	    (IFF_UP | IFF_RUNNING))
10489ec5c125Sdamien 		return (0);
10499ec5c125Sdamien 
10509ec5c125Sdamien 	/* Do it in a process context. */
10519ec5c125Sdamien 	cmd.key = *k;
105223340becSstsp 	cmd.ni = ni;
10539ec5c125Sdamien 	urtwn_do_async(sc, urtwn_set_key_cb, &cmd, sizeof(cmd));
10542362e6a4Skrw 	sc->sc_key_tasks++;
10552362e6a4Skrw 
10562362e6a4Skrw 	return (EBUSY);
10579ec5c125Sdamien }
10589ec5c125Sdamien 
10599ec5c125Sdamien void
10609ec5c125Sdamien urtwn_set_key_cb(struct urtwn_softc *sc, void *arg)
10619ec5c125Sdamien {
106223340becSstsp 	struct ieee80211com *ic = &sc->sc_sc.sc_ic;
10639ec5c125Sdamien 	struct urtwn_cmd_key *cmd = arg;
10649ec5c125Sdamien 
10652362e6a4Skrw 	sc->sc_key_tasks--;
10662362e6a4Skrw 
10672362e6a4Skrw 	if (rtwn_set_key(ic, cmd->ni, &cmd->key) == 0) {
10682362e6a4Skrw 		if (sc->sc_key_tasks == 0) {
10692362e6a4Skrw 			DPRINTF(("marking port %s valid\n",
10702362e6a4Skrw 			    ether_sprintf(cmd->ni->ni_macaddr)));
10712362e6a4Skrw 			cmd->ni->ni_port_valid = 1;
10722362e6a4Skrw 			ieee80211_set_link_state(ic, LINK_STATE_UP);
10732362e6a4Skrw 		}
10742362e6a4Skrw 	} else {
10752362e6a4Skrw 		IEEE80211_SEND_MGMT(ic, cmd->ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
10762362e6a4Skrw 		    IEEE80211_REASON_AUTH_LEAVE);
10772362e6a4Skrw 		ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
10782362e6a4Skrw 	}
10799ec5c125Sdamien }
10809ec5c125Sdamien 
10819ec5c125Sdamien void
10829ec5c125Sdamien urtwn_delete_key(struct ieee80211com *ic, struct ieee80211_node *ni,
10839ec5c125Sdamien     struct ieee80211_key *k)
10849ec5c125Sdamien {
108523340becSstsp 	struct rtwn_softc *sc_sc = ic->ic_softc;
108623340becSstsp 	struct device *self = sc_sc->sc_pdev;
108723340becSstsp 	struct urtwn_softc *sc = (struct urtwn_softc *)self;
10889ec5c125Sdamien 	struct urtwn_cmd_key cmd;
10899ec5c125Sdamien 
1090a299d652Sjmatthew 	/* Only handle keys for CCMP */
1091a299d652Sjmatthew 	if (k->k_cipher != IEEE80211_CIPHER_CCMP) {
1092a299d652Sjmatthew 		ieee80211_delete_key(ic, ni, k);
1093a299d652Sjmatthew 		return;
1094a299d652Sjmatthew 	}
1095a299d652Sjmatthew 
10969ec5c125Sdamien 	if (!(ic->ic_if.if_flags & IFF_RUNNING) ||
10979ec5c125Sdamien 	    ic->ic_state != IEEE80211_S_RUN)
10989ec5c125Sdamien 		return;	/* Nothing to do. */
10999ec5c125Sdamien 
11009ec5c125Sdamien 	/* Do it in a process context. */
11019ec5c125Sdamien 	cmd.key = *k;
110223340becSstsp 	cmd.ni = ni;
11039ec5c125Sdamien 	urtwn_do_async(sc, urtwn_delete_key_cb, &cmd, sizeof(cmd));
11049ec5c125Sdamien }
11059ec5c125Sdamien 
11069ec5c125Sdamien void
11079ec5c125Sdamien urtwn_delete_key_cb(struct urtwn_softc *sc, void *arg)
11089ec5c125Sdamien {
110923340becSstsp 	struct ieee80211com *ic = &sc->sc_sc.sc_ic;
11109ec5c125Sdamien 	struct urtwn_cmd_key *cmd = arg;
11119ec5c125Sdamien 
111223340becSstsp 	rtwn_delete_key(ic, cmd->ni, &cmd->key);
1113c349b8bfSstsp }
1114c349b8bfSstsp 
1115a299d652Sjmatthew int
1116a299d652Sjmatthew urtwn_ccmp_decap(struct urtwn_softc *sc, struct mbuf *m,
1117a299d652Sjmatthew     struct ieee80211_node *ni)
1118a299d652Sjmatthew {
1119a299d652Sjmatthew 	struct ieee80211com *ic = &sc->sc_sc.sc_ic;
1120a299d652Sjmatthew 	struct ieee80211_key *k;
1121a299d652Sjmatthew 	struct ieee80211_frame *wh;
1122a299d652Sjmatthew 	uint64_t pn, *prsc;
1123a299d652Sjmatthew 	uint8_t *ivp;
1124a299d652Sjmatthew 	uint8_t tid;
1125a299d652Sjmatthew 	int hdrlen, hasqos;
1126a299d652Sjmatthew 
1127a299d652Sjmatthew 	k = ieee80211_get_rxkey(ic, m, ni);
1128a299d652Sjmatthew 	if (k == NULL)
1129a299d652Sjmatthew 		return 1;
1130a299d652Sjmatthew 
1131a299d652Sjmatthew 	wh = mtod(m, struct ieee80211_frame *);
1132a299d652Sjmatthew 	hdrlen = ieee80211_get_hdrlen(wh);
1133a299d652Sjmatthew 	ivp = (uint8_t *)wh + hdrlen;
1134a299d652Sjmatthew 
1135a299d652Sjmatthew 	/* Check that ExtIV bit is set. */
1136a299d652Sjmatthew 	if (!(ivp[3] & IEEE80211_WEP_EXTIV))
1137a299d652Sjmatthew 		return 1;
1138a299d652Sjmatthew 
1139a299d652Sjmatthew 	hasqos = ieee80211_has_qos(wh);
1140a299d652Sjmatthew 	tid = hasqos ? ieee80211_get_qos(wh) & IEEE80211_QOS_TID : 0;
1141a299d652Sjmatthew 	prsc = &k->k_rsc[tid];
1142a299d652Sjmatthew 
1143a299d652Sjmatthew 	/* Extract the 48-bit PN from the CCMP header. */
1144a299d652Sjmatthew 	pn = (uint64_t)ivp[0]       |
1145a299d652Sjmatthew 	     (uint64_t)ivp[1] <<  8 |
1146a299d652Sjmatthew 	     (uint64_t)ivp[4] << 16 |
1147a299d652Sjmatthew 	     (uint64_t)ivp[5] << 24 |
1148a299d652Sjmatthew 	     (uint64_t)ivp[6] << 32 |
1149a299d652Sjmatthew 	     (uint64_t)ivp[7] << 40;
1150a299d652Sjmatthew 	if (pn <= *prsc) {
1151a299d652Sjmatthew 		ic->ic_stats.is_ccmp_replays++;
1152a299d652Sjmatthew 		return 1;
1153a299d652Sjmatthew 	}
1154a299d652Sjmatthew 	/* Last seen packet number is updated in ieee80211_inputm(). */
1155a299d652Sjmatthew 
1156a299d652Sjmatthew 	/* Strip MIC. IV will be stripped by ieee80211_inputm(). */
1157a299d652Sjmatthew 	m_adj(m, -IEEE80211_CCMP_MICLEN);
1158a299d652Sjmatthew 	return 0;
1159a299d652Sjmatthew }
1160a299d652Sjmatthew 
11619ec5c125Sdamien void
11628fbaf8a2Sstsp urtwn_rx_frame(struct urtwn_softc *sc, uint8_t *buf, int pktlen,
11638fbaf8a2Sstsp     struct mbuf_list *ml)
11649ec5c125Sdamien {
116523340becSstsp 	struct ieee80211com *ic = &sc->sc_sc.sc_ic;
11669ec5c125Sdamien 	struct ifnet *ifp = &ic->ic_if;
11679ec5c125Sdamien 	struct ieee80211_rxinfo rxi;
11689ec5c125Sdamien 	struct ieee80211_frame *wh;
11699ec5c125Sdamien 	struct ieee80211_node *ni;
11708caabc14Sstsp 	struct r92c_rx_desc_usb *rxd;
11719ec5c125Sdamien 	uint32_t rxdw0, rxdw3;
11729ec5c125Sdamien 	struct mbuf *m;
11739ec5c125Sdamien 	uint8_t rate;
11749ec5c125Sdamien 	int8_t rssi = 0;
11759ec5c125Sdamien 	int s, infosz;
11769ec5c125Sdamien 
11778caabc14Sstsp 	rxd = (struct r92c_rx_desc_usb *)buf;
11788caabc14Sstsp 	rxdw0 = letoh32(rxd->rxdw0);
11798caabc14Sstsp 	rxdw3 = letoh32(rxd->rxdw3);
11809ec5c125Sdamien 
11819ec5c125Sdamien 	if (__predict_false(rxdw0 & (R92C_RXDW0_CRCERR | R92C_RXDW0_ICVERR))) {
11829ec5c125Sdamien 		/*
11839ec5c125Sdamien 		 * This should not happen since we setup our Rx filter
11849ec5c125Sdamien 		 * to not receive these frames.
11859ec5c125Sdamien 		 */
11869ec5c125Sdamien 		ifp->if_ierrors++;
11879ec5c125Sdamien 		return;
11889ec5c125Sdamien 	}
1189882a6c16Sdamien 	if (__predict_false(pktlen < sizeof(*wh) || pktlen > MCLBYTES)) {
11909ec5c125Sdamien 		ifp->if_ierrors++;
11919ec5c125Sdamien 		return;
11929ec5c125Sdamien 	}
11939ec5c125Sdamien 
1194067851b1Skevlo 	rate = (sc->sc_sc.chip & (RTWN_CHIP_88F | RTWN_CHIP_92E)) ?
1195969fab7dSkevlo 	    MS(rxdw3, R92E_RXDW3_RATE) : MS(rxdw3, R92C_RXDW3_RATE);
11969ec5c125Sdamien 	infosz = MS(rxdw0, R92C_RXDW0_INFOSZ) * 8;
11979ec5c125Sdamien 
11989ec5c125Sdamien 	/* Get RSSI from PHY status descriptor if present. */
11999ec5c125Sdamien 	if (infosz != 0 && (rxdw0 & R92C_RXDW0_PHYST)) {
120023340becSstsp 		rssi = rtwn_get_rssi(&sc->sc_sc, rate, &rxd[1]);
12019ec5c125Sdamien 		/* Update our average RSSI. */
120223340becSstsp 		rtwn_update_avgrssi(&sc->sc_sc, rate, rssi);
12039ec5c125Sdamien 	}
12049ec5c125Sdamien 
12059ec5c125Sdamien 	DPRINTFN(5, ("Rx frame len=%d rate=%d infosz=%d rssi=%d\n",
12069ec5c125Sdamien 	    pktlen, rate, infosz, rssi));
12079ec5c125Sdamien 
12089ec5c125Sdamien 	MGETHDR(m, M_DONTWAIT, MT_DATA);
12099ec5c125Sdamien 	if (__predict_false(m == NULL)) {
12109ec5c125Sdamien 		ifp->if_ierrors++;
12119ec5c125Sdamien 		return;
12129ec5c125Sdamien 	}
12139ec5c125Sdamien 	if (pktlen > MHLEN) {
12149ec5c125Sdamien 		MCLGET(m, M_DONTWAIT);
12159ec5c125Sdamien 		if (__predict_false(!(m->m_flags & M_EXT))) {
12169ec5c125Sdamien 			ifp->if_ierrors++;
12179ec5c125Sdamien 			m_freem(m);
12189ec5c125Sdamien 			return;
12199ec5c125Sdamien 		}
12209ec5c125Sdamien 	}
12219ec5c125Sdamien 	/* Finalize mbuf. */
12228caabc14Sstsp 	wh = (struct ieee80211_frame *)((uint8_t *)&rxd[1] + infosz);
12239ec5c125Sdamien 	memcpy(mtod(m, uint8_t *), wh, pktlen);
12249ec5c125Sdamien 	m->m_pkthdr.len = m->m_len = pktlen;
12259ec5c125Sdamien 
12269ec5c125Sdamien 	s = splnet();
12279ec5c125Sdamien #if NBPFILTER > 0
12289ec5c125Sdamien 	if (__predict_false(sc->sc_drvbpf != NULL)) {
12299ec5c125Sdamien 		struct urtwn_rx_radiotap_header *tap = &sc->sc_rxtap;
12309ec5c125Sdamien 		struct mbuf mb;
12319ec5c125Sdamien 
12329ec5c125Sdamien 		tap->wr_flags = 0;
12339ec5c125Sdamien 		/* Map HW rate index to 802.11 rate. */
12349ec5c125Sdamien 		if (!(rxdw3 & R92C_RXDW3_HT)) {
12359ec5c125Sdamien 			switch (rate) {
12369ec5c125Sdamien 			/* CCK. */
12379ec5c125Sdamien 			case  0: tap->wr_rate =   2; break;
12389ec5c125Sdamien 			case  1: tap->wr_rate =   4; break;
12399ec5c125Sdamien 			case  2: tap->wr_rate =  11; break;
12409ec5c125Sdamien 			case  3: tap->wr_rate =  22; break;
12419ec5c125Sdamien 			/* OFDM. */
12429ec5c125Sdamien 			case  4: tap->wr_rate =  12; break;
12439ec5c125Sdamien 			case  5: tap->wr_rate =  18; break;
12449ec5c125Sdamien 			case  6: tap->wr_rate =  24; break;
12459ec5c125Sdamien 			case  7: tap->wr_rate =  36; break;
12469ec5c125Sdamien 			case  8: tap->wr_rate =  48; break;
12479ec5c125Sdamien 			case  9: tap->wr_rate =  72; break;
12489ec5c125Sdamien 			case 10: tap->wr_rate =  96; break;
12499ec5c125Sdamien 			case 11: tap->wr_rate = 108; break;
12509ec5c125Sdamien 			}
1251fdfa064bSkevlo 			if (rate <= 3)
1252fdfa064bSkevlo 				tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
12539ec5c125Sdamien 		} else if (rate >= 12) {	/* MCS0~15. */
12549ec5c125Sdamien 			/* Bit 7 set means HT MCS instead of rate. */
12559ec5c125Sdamien 			tap->wr_rate = 0x80 | (rate - 12);
12569ec5c125Sdamien 		}
12579ec5c125Sdamien 		tap->wr_dbm_antsignal = rssi;
12589ec5c125Sdamien 		tap->wr_chan_freq = htole16(ic->ic_ibss_chan->ic_freq);
12599ec5c125Sdamien 		tap->wr_chan_flags = htole16(ic->ic_ibss_chan->ic_flags);
12609ec5c125Sdamien 
12619ec5c125Sdamien 		mb.m_data = (caddr_t)tap;
12629ec5c125Sdamien 		mb.m_len = sc->sc_rxtap_len;
12639ec5c125Sdamien 		mb.m_next = m;
12649ec5c125Sdamien 		mb.m_nextpkt = NULL;
12659ec5c125Sdamien 		mb.m_type = 0;
12669ec5c125Sdamien 		mb.m_flags = 0;
12679ec5c125Sdamien 		bpf_mtap(sc->sc_drvbpf, &mb, BPF_DIRECTION_IN);
12689ec5c125Sdamien 	}
12699ec5c125Sdamien #endif
12709ec5c125Sdamien 
12719ec5c125Sdamien 	ni = ieee80211_find_rxnode(ic, wh);
127252a13037Sstsp 	memset(&rxi, 0, sizeof(rxi));
12739ec5c125Sdamien 	rxi.rxi_rssi = rssi;
1274a299d652Sjmatthew 
1275a299d652Sjmatthew 	/* Handle hardware decryption. */
1276a299d652Sjmatthew 	if (((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_CTL)
1277a299d652Sjmatthew 	    && (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) &&
1278a299d652Sjmatthew 	    (ni->ni_flags & IEEE80211_NODE_RXPROT) &&
12797104388aSstsp 	    ((!IEEE80211_IS_MULTICAST(wh->i_addr1) &&
12807104388aSstsp 	    ni->ni_pairwise_key.k_cipher == IEEE80211_CIPHER_CCMP) ||
12817104388aSstsp 	    (IEEE80211_IS_MULTICAST(wh->i_addr1) &&
12827104388aSstsp 	    ni->ni_rsngroupcipher == IEEE80211_CIPHER_CCMP))) {
1283a299d652Sjmatthew 		if (urtwn_ccmp_decap(sc, m, ni) != 0) {
1284a299d652Sjmatthew 			ifp->if_ierrors++;
1285a299d652Sjmatthew 			m_freem(m);
1286a299d652Sjmatthew 			ieee80211_release_node(ic, ni);
128706a5d685Sjmatthew 			splx(s);
1288a299d652Sjmatthew 			return;
1289a299d652Sjmatthew 		}
1290a299d652Sjmatthew 		rxi.rxi_flags |= IEEE80211_RXI_HWDEC;
1291a299d652Sjmatthew 	}
1292a299d652Sjmatthew 
12938fbaf8a2Sstsp 	ieee80211_inputm(ifp, m, ni, &rxi, ml);
12949ec5c125Sdamien 	/* Node is no longer needed. */
12959ec5c125Sdamien 	ieee80211_release_node(ic, ni);
12969ec5c125Sdamien 	splx(s);
12979ec5c125Sdamien }
12989ec5c125Sdamien 
12999ec5c125Sdamien void
1300ab0b1be7Smglocker urtwn_rxeof(struct usbd_xfer *xfer, void *priv,
13019ec5c125Sdamien     usbd_status status)
13029ec5c125Sdamien {
13038fbaf8a2Sstsp 	struct mbuf_list ml = MBUF_LIST_INITIALIZER();
13049ec5c125Sdamien 	struct urtwn_rx_data *data = priv;
13059ec5c125Sdamien 	struct urtwn_softc *sc = data->sc;
13068fbaf8a2Sstsp 	struct ieee80211com *ic = &sc->sc_sc.sc_ic;
13078caabc14Sstsp 	struct r92c_rx_desc_usb *rxd;
13089ec5c125Sdamien 	uint32_t rxdw0;
13099ec5c125Sdamien 	uint8_t *buf;
131090540544Sjmatthew 	int len, totlen, pktlen, infosz, npkts, error, align;
13119ec5c125Sdamien 
13129ec5c125Sdamien 	if (__predict_false(status != USBD_NORMAL_COMPLETION)) {
13139ec5c125Sdamien 		DPRINTF(("RX status=%d\n", status));
13149ec5c125Sdamien 		if (status == USBD_STALLED)
13159ec5c125Sdamien 			usbd_clear_endpoint_stall_async(sc->rx_pipe);
13169ec5c125Sdamien 		if (status != USBD_CANCELLED)
13179ec5c125Sdamien 			goto resubmit;
13189ec5c125Sdamien 		return;
13199ec5c125Sdamien 	}
13209ec5c125Sdamien 	usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL);
13219ec5c125Sdamien 
13228caabc14Sstsp 	if (__predict_false(len < sizeof(*rxd))) {
13239ec5c125Sdamien 		DPRINTF(("xfer too short %d\n", len));
13249ec5c125Sdamien 		goto resubmit;
13259ec5c125Sdamien 	}
13269ec5c125Sdamien 	buf = data->buf;
13279ec5c125Sdamien 
13289ec5c125Sdamien 	/* Get the number of encapsulated frames. */
13298caabc14Sstsp 	rxd = (struct r92c_rx_desc_usb *)buf;
13308caabc14Sstsp 	npkts = MS(letoh32(rxd->rxdw2), R92C_RXDW2_PKTCNT);
133123340becSstsp 	DPRINTFN(4, ("Rx %d frames in one chunk\n", npkts));
13329ec5c125Sdamien 
1333a087613eSstsp 	if (sc->sc_sc.chip & RTWN_CHIP_88E) {
1334a087613eSstsp 		int ntries, type;
1335a087613eSstsp 		struct r88e_tx_rpt_ccx *rxstat;
1336a087613eSstsp 
1337a087613eSstsp 		type = MS(letoh32(rxd->rxdw3), R88E_RXDW3_RPT);
1338a087613eSstsp 
1339a087613eSstsp 		if (type == R88E_RXDW3_RPT_TX1) {
1340a087613eSstsp 			buf += sizeof(struct r92c_rx_desc_usb);
1341a087613eSstsp 			rxstat = (struct r88e_tx_rpt_ccx *)buf;
1342a087613eSstsp 			ntries = MS(letoh32(rxstat->rptb2),
1343a087613eSstsp 			    R88E_RPTB2_RETRY_CNT);
1344a087613eSstsp 
1345a087613eSstsp 			if (rxstat->rptb1 & R88E_RPTB1_PKT_OK)
1346a087613eSstsp 				sc->amn.amn_txcnt++;
1347a087613eSstsp 			if (ntries > 0)
1348a087613eSstsp 				sc->amn.amn_retrycnt++;
1349a087613eSstsp 
1350a087613eSstsp 			goto resubmit;
1351a087613eSstsp 		}
1352067851b1Skevlo 	} else if (sc->sc_sc.chip & (RTWN_CHIP_88F | RTWN_CHIP_92E)) {
135390540544Sjmatthew 		int type;
135490540544Sjmatthew 		struct r92e_c2h_tx_rpt *txrpt;
135590540544Sjmatthew 
135690540544Sjmatthew 		if (letoh32(rxd->rxdw2) & R92E_RXDW2_RPT_C2H) {
135790540544Sjmatthew 			if (len < sizeof(struct r92c_rx_desc_usb) + 2)
135890540544Sjmatthew 				goto resubmit;
135990540544Sjmatthew 
136090540544Sjmatthew 			type = buf[sizeof(struct r92c_rx_desc_usb)];
136190540544Sjmatthew 			switch (type) {
136290540544Sjmatthew 			case R92C_C2HEVT_TX_REPORT:
136390540544Sjmatthew 				buf += sizeof(struct r92c_rx_desc_usb) + 2;
136490540544Sjmatthew 				txrpt = (struct r92e_c2h_tx_rpt *)buf;
136590540544Sjmatthew 				if (MS(txrpt->rptb2, R92E_RPTB2_RETRY_CNT) > 0)
136690540544Sjmatthew 					sc->amn.amn_retrycnt++;
136790540544Sjmatthew 				if ((txrpt->rptb0 & (R92E_RPTB0_RETRY_OVER |
136890540544Sjmatthew 				    R92E_RPTB0_LIFE_EXPIRE)) == 0)
136990540544Sjmatthew 					sc->amn.amn_txcnt++;
137090540544Sjmatthew 				break;
137190540544Sjmatthew 			default:
137290540544Sjmatthew 				break;
1373a087613eSstsp 			}
137490540544Sjmatthew 			goto resubmit;
137590540544Sjmatthew 		}
137690540544Sjmatthew 	}
137790540544Sjmatthew 
1378067851b1Skevlo 	align = ((sc->sc_sc.chip & (RTWN_CHIP_88F | RTWN_CHIP_92E)) ? 7 : 127);
1379a087613eSstsp 
13809ec5c125Sdamien 	/* Process all of them. */
13819ec5c125Sdamien 	while (npkts-- > 0) {
13828caabc14Sstsp 		if (__predict_false(len < sizeof(*rxd)))
13839ec5c125Sdamien 			break;
13848caabc14Sstsp 		rxd = (struct r92c_rx_desc_usb *)buf;
13858caabc14Sstsp 		rxdw0 = letoh32(rxd->rxdw0);
13869ec5c125Sdamien 
13879ec5c125Sdamien 		pktlen = MS(rxdw0, R92C_RXDW0_PKTLEN);
13889ec5c125Sdamien 		if (__predict_false(pktlen == 0))
13899ec5c125Sdamien 			break;
13909ec5c125Sdamien 
13919ec5c125Sdamien 		infosz = MS(rxdw0, R92C_RXDW0_INFOSZ) * 8;
13929ec5c125Sdamien 
13939ec5c125Sdamien 		/* Make sure everything fits in xfer. */
13948caabc14Sstsp 		totlen = sizeof(*rxd) + infosz + pktlen;
13959ec5c125Sdamien 		if (__predict_false(totlen > len))
13969ec5c125Sdamien 			break;
13979ec5c125Sdamien 
13989ec5c125Sdamien 		/* Process 802.11 frame. */
13998fbaf8a2Sstsp 		urtwn_rx_frame(sc, buf, pktlen, &ml);
14009ec5c125Sdamien 
140190540544Sjmatthew 		/* Handle chunk alignment. */
140290540544Sjmatthew 		totlen = (totlen + align) & ~align;
14039ec5c125Sdamien 		buf += totlen;
14049ec5c125Sdamien 		len -= totlen;
14059ec5c125Sdamien 	}
14068fbaf8a2Sstsp 	if_input(&ic->ic_if, &ml);
14079ec5c125Sdamien 
14089ec5c125Sdamien  resubmit:
14099ec5c125Sdamien 	/* Setup a new transfer. */
14109ec5c125Sdamien 	usbd_setup_xfer(xfer, sc->rx_pipe, data, data->buf, URTWN_RXBUFSZ,
14119ec5c125Sdamien 	    USBD_SHORT_XFER_OK | USBD_NO_COPY, USBD_NO_TIMEOUT, urtwn_rxeof);
141223340becSstsp 	error = usbd_transfer(data->xfer);
141323340becSstsp 	if (error != 0 && error != USBD_IN_PROGRESS)
141423340becSstsp 		DPRINTF(("could not set up new transfer: %d\n", error));
14159ec5c125Sdamien }
14169ec5c125Sdamien 
14179ec5c125Sdamien void
1418ab0b1be7Smglocker urtwn_txeof(struct usbd_xfer *xfer, void *priv,
14199ec5c125Sdamien     usbd_status status)
14209ec5c125Sdamien {
14219ec5c125Sdamien 	struct urtwn_tx_data *data = priv;
14229ec5c125Sdamien 	struct urtwn_softc *sc = data->sc;
142323340becSstsp 	struct ifnet *ifp = &sc->sc_sc.sc_ic.ic_if;
14249ec5c125Sdamien 	int s;
14259ec5c125Sdamien 
14269ec5c125Sdamien 	s = splnet();
14279ec5c125Sdamien 	/* Put this Tx buffer back to our free list. */
14289ec5c125Sdamien 	TAILQ_INSERT_TAIL(&sc->tx_free_list, data, next);
14299ec5c125Sdamien 
14309ec5c125Sdamien 	if (__predict_false(status != USBD_NORMAL_COMPLETION)) {
14319ec5c125Sdamien 		DPRINTF(("TX status=%d\n", status));
14329ec5c125Sdamien 		if (status == USBD_STALLED)
14339ec5c125Sdamien 			usbd_clear_endpoint_stall_async(data->pipe);
14349ec5c125Sdamien 		ifp->if_oerrors++;
14359ec5c125Sdamien 		splx(s);
14369ec5c125Sdamien 		return;
14379ec5c125Sdamien 	}
143823340becSstsp 	sc->sc_sc.sc_tx_timer = 0;
14399ec5c125Sdamien 
14409ec5c125Sdamien 	/* We just released a Tx buffer, notify Tx. */
1441de6cd8fbSdlg 	if (ifq_is_oactive(&ifp->if_snd)) {
1442de6cd8fbSdlg 		ifq_clr_oactive(&ifp->if_snd);
144323340becSstsp 		rtwn_start(ifp);
14449ec5c125Sdamien 	}
14459ec5c125Sdamien 	splx(s);
14469ec5c125Sdamien }
14479ec5c125Sdamien 
144890540544Sjmatthew void
144990540544Sjmatthew urtwn_tx_fill_desc(struct urtwn_softc *sc, uint8_t **txdp, struct mbuf *m,
145090540544Sjmatthew     struct ieee80211_frame *wh, struct ieee80211_key *k,
145190540544Sjmatthew     struct ieee80211_node *ni)
14529ec5c125Sdamien {
14538caabc14Sstsp 	struct r92c_tx_desc_usb *txd;
145490540544Sjmatthew 	struct ieee80211com *ic = &sc->sc_sc.sc_ic;
1455efc8f9f7Sstsp 	uint8_t raid, type, rtsrate;
1456a299d652Sjmatthew 	uint32_t pktlen;
14579ec5c125Sdamien 
145890540544Sjmatthew 	txd = (struct r92c_tx_desc_usb *)*txdp;
145990540544Sjmatthew 	(*txdp) += sizeof(*txd);
14609ec5c125Sdamien 	memset(txd, 0, sizeof(*txd));
14619ec5c125Sdamien 
1462a299d652Sjmatthew 	pktlen = m->m_pkthdr.len;
1463a299d652Sjmatthew 	if (k != NULL && k->k_cipher == IEEE80211_CIPHER_CCMP) {
1464a299d652Sjmatthew 		txd->txdw1 |= htole32(SM(R92C_TXDW1_CIPHER,
1465a299d652Sjmatthew 		    R92C_TXDW1_CIPHER_AES));
1466a299d652Sjmatthew 		pktlen += IEEE80211_CCMP_HDRLEN;
1467a299d652Sjmatthew 	}
1468a299d652Sjmatthew 
146990540544Sjmatthew 	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
147090540544Sjmatthew 
14719ec5c125Sdamien 	txd->txdw0 |= htole32(
1472a299d652Sjmatthew 	    SM(R92C_TXDW0_PKTLEN, pktlen) |
14739ec5c125Sdamien 	    SM(R92C_TXDW0_OFFSET, sizeof(*txd)) |
14749ec5c125Sdamien 	    R92C_TXDW0_OWN | R92C_TXDW0_FSG | R92C_TXDW0_LSG);
14759ec5c125Sdamien 	if (IEEE80211_IS_MULTICAST(wh->i_addr1))
14769ec5c125Sdamien 		txd->txdw0 |= htole32(R92C_TXDW0_BMCAST);
14779ec5c125Sdamien 
14789ec5c125Sdamien 	if (!IEEE80211_IS_MULTICAST(wh->i_addr1) &&
14799ec5c125Sdamien 	    type == IEEE80211_FC0_TYPE_DATA) {
148056d11693Sstsp 		if (ic->ic_curmode == IEEE80211_MODE_11B ||
148123340becSstsp 		    (sc->sc_sc.sc_flags & RTWN_FLAG_FORCE_RAID_11B))
14829ec5c125Sdamien 			raid = R92C_RAID_11B;
14839ec5c125Sdamien 		else
14849ec5c125Sdamien 			raid = R92C_RAID_11BG;
148523340becSstsp 		if (sc->sc_sc.chip & RTWN_CHIP_88E) {
1486c349b8bfSstsp 			txd->txdw1 |= htole32(
148753c3c4d0Sstsp 			    SM(R88E_TXDW1_MACID, R92C_MACID_BSS) |
1488c349b8bfSstsp 			    SM(R92C_TXDW1_QSEL, R92C_TXDW1_QSEL_BE) |
1489c349b8bfSstsp 			    SM(R92C_TXDW1_RAID, raid));
1490c349b8bfSstsp 			txd->txdw2 |= htole32(R88E_TXDW2_AGGBK);
1491a087613eSstsp 			/* Request TX status report for AMRR */
1492a087613eSstsp 			txd->txdw2 |= htole32(R92C_TXDW2_CCX_RPT);
1493c349b8bfSstsp 		} else {
14949ec5c125Sdamien 			txd->txdw1 |= htole32(
149553c3c4d0Sstsp 			    SM(R92C_TXDW1_MACID, R92C_MACID_BSS) |
14969ec5c125Sdamien 			    SM(R92C_TXDW1_QSEL, R92C_TXDW1_QSEL_BE) |
1497c349b8bfSstsp 			    SM(R92C_TXDW1_RAID, raid) | R92C_TXDW1_AGGBK);
1498c349b8bfSstsp 		}
14999ec5c125Sdamien 
1500a299d652Sjmatthew 		if (pktlen + IEEE80211_CRC_LEN > ic->ic_rtsthreshold) {
15015098e9a8Sstsp 			txd->txdw4 |= htole32(R92C_TXDW4_RTSEN |
15025098e9a8Sstsp 			    R92C_TXDW4_HWRTSEN);
15035098e9a8Sstsp 		} else if (ic->ic_flags & IEEE80211_F_USEPROT) {
15049ec5c125Sdamien 			if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) {
15059ec5c125Sdamien 				txd->txdw4 |= htole32(R92C_TXDW4_CTS2SELF |
15069ec5c125Sdamien 				    R92C_TXDW4_HWRTSEN);
15079ec5c125Sdamien 			} else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) {
15089ec5c125Sdamien 				txd->txdw4 |= htole32(R92C_TXDW4_RTSEN |
15099ec5c125Sdamien 				    R92C_TXDW4_HWRTSEN);
15109ec5c125Sdamien 			}
15119ec5c125Sdamien 		}
15129ec5c125Sdamien 		txd->txdw5 |= htole32(0x0001ff00);
15139ec5c125Sdamien 
1514efc8f9f7Sstsp 		if (ic->ic_curmode == IEEE80211_MODE_11B)
1515efc8f9f7Sstsp 			rtsrate = 0; /* CCK1 */
1516efc8f9f7Sstsp 		else
1517efc8f9f7Sstsp 			rtsrate = 8; /* OFDM24 */
1518efc8f9f7Sstsp 
1519a087613eSstsp 		if (sc->sc_sc.chip & RTWN_CHIP_88E) {
1520a087613eSstsp 			/* Use AMRR */
1521a087613eSstsp 			txd->txdw4 |= htole32(R92C_TXDW4_DRVRATE);
1522efc8f9f7Sstsp 			txd->txdw4 |= htole32(SM(R92C_TXDW4_RTSRATE, rtsrate));
1523a087613eSstsp 			txd->txdw5 |= htole32(SM(R92C_TXDW5_DATARATE,
1524a087613eSstsp 			    ni->ni_txrate));
1525a087613eSstsp 		} else {
1526efc8f9f7Sstsp 			/* Send data at OFDM54. */
1527efc8f9f7Sstsp 			txd->txdw4 |= htole32(SM(R92C_TXDW4_RTSRATE, rtsrate));
1528a087613eSstsp 			txd->txdw5 |= htole32(SM(R92C_TXDW5_DATARATE, 11));
1529a087613eSstsp 		}
15309ec5c125Sdamien 	} else {
15319ec5c125Sdamien 		txd->txdw1 |= htole32(
15329ec5c125Sdamien 		    SM(R92C_TXDW1_MACID, 0) |
15339ec5c125Sdamien 		    SM(R92C_TXDW1_QSEL, R92C_TXDW1_QSEL_MGNT) |
15349ec5c125Sdamien 		    SM(R92C_TXDW1_RAID, R92C_RAID_11B));
15359ec5c125Sdamien 
15369ec5c125Sdamien 		/* Force CCK1. */
15379ec5c125Sdamien 		txd->txdw4 |= htole32(R92C_TXDW4_DRVRATE);
15389ec5c125Sdamien 		txd->txdw5 |= htole32(SM(R92C_TXDW5_DATARATE, 0));
15399ec5c125Sdamien 	}
15409ec5c125Sdamien 	/* Set sequence number (already little endian). */
15419befa0edSjmatthew 	txd->txdseq |= (*(uint16_t *)wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT;
15429ec5c125Sdamien 
154390540544Sjmatthew 	if (!ieee80211_has_qos(wh)) {
15449ec5c125Sdamien 		/* Use HW sequence numbering for non-QoS frames. */
15459ec5c125Sdamien 		txd->txdw4  |= htole32(R92C_TXDW4_HWSEQ);
15469befa0edSjmatthew 		txd->txdseq |= htole16(R92C_TXDW3_HWSEQEN);
15479ec5c125Sdamien 	} else
15489ec5c125Sdamien 		txd->txdw4 |= htole32(R92C_TXDW4_QOS);
154990540544Sjmatthew }
155090540544Sjmatthew 
155190540544Sjmatthew void
155290540544Sjmatthew urtwn_tx_fill_desc_gen2(struct urtwn_softc *sc, uint8_t **txdp, struct mbuf *m,
155390540544Sjmatthew     struct ieee80211_frame *wh, struct ieee80211_key *k,
155490540544Sjmatthew     struct ieee80211_node *ni)
155590540544Sjmatthew {
155690540544Sjmatthew 	struct r92e_tx_desc_usb *txd;
155790540544Sjmatthew 	struct ieee80211com *ic = &sc->sc_sc.sc_ic;
155890540544Sjmatthew 	uint8_t raid, type;
1559a299d652Sjmatthew 	uint32_t pktlen;
156090540544Sjmatthew 
156190540544Sjmatthew 	txd = (struct r92e_tx_desc_usb *)*txdp;
156290540544Sjmatthew 	(*txdp) += sizeof(*txd);
156390540544Sjmatthew 	memset(txd, 0, sizeof(*txd));
156490540544Sjmatthew 
156590540544Sjmatthew 	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
156690540544Sjmatthew 
1567a299d652Sjmatthew 	pktlen = m->m_pkthdr.len;
1568a299d652Sjmatthew 	if (k != NULL && k->k_cipher == IEEE80211_CIPHER_CCMP) {
1569a299d652Sjmatthew 		txd->txdw1 |= htole32(SM(R92C_TXDW1_CIPHER,
1570a299d652Sjmatthew 		    R92C_TXDW1_CIPHER_AES));
1571a299d652Sjmatthew 		pktlen += IEEE80211_CCMP_HDRLEN;
1572a299d652Sjmatthew 	}
1573a299d652Sjmatthew 
157490540544Sjmatthew 	txd->txdw0 |= htole32(
1575a299d652Sjmatthew 	    SM(R92C_TXDW0_PKTLEN, pktlen) |
157690540544Sjmatthew 	    SM(R92C_TXDW0_OFFSET, sizeof(*txd)) |
157790540544Sjmatthew 	    R92C_TXDW0_OWN | R92C_TXDW0_FSG | R92C_TXDW0_LSG);
157890540544Sjmatthew 	if (IEEE80211_IS_MULTICAST(wh->i_addr1))
157990540544Sjmatthew 		txd->txdw0 |= htole32(R92C_TXDW0_BMCAST);
158090540544Sjmatthew 
158190540544Sjmatthew 	if (!IEEE80211_IS_MULTICAST(wh->i_addr1) &&
158290540544Sjmatthew 	    type == IEEE80211_FC0_TYPE_DATA) {
158390540544Sjmatthew 		if (ic->ic_curmode == IEEE80211_MODE_11B ||
158490540544Sjmatthew 		    (sc->sc_sc.sc_flags & RTWN_FLAG_FORCE_RAID_11B))
158590540544Sjmatthew 			raid = R92E_RAID_11B;
158690540544Sjmatthew 		else
158790540544Sjmatthew 			raid = R92E_RAID_11BG;
158890540544Sjmatthew 		txd->txdw1 |= htole32(
158990540544Sjmatthew 		    SM(R92E_TXDW1_MACID, R92C_MACID_BSS) |
159090540544Sjmatthew 		    SM(R92C_TXDW1_QSEL, R92C_TXDW1_QSEL_BE) |
159190540544Sjmatthew 		    SM(R92C_TXDW1_RAID, raid));
159290540544Sjmatthew 		/* Request TX status report for AMRR */
159390540544Sjmatthew 		txd->txdw2 |= htole32(R92C_TXDW2_CCX_RPT | R88E_TXDW2_AGGBK);
159490540544Sjmatthew 
1595a299d652Sjmatthew 		if (pktlen + IEEE80211_CRC_LEN > ic->ic_rtsthreshold) {
159690540544Sjmatthew 			txd->txdw4 |= htole32(R92C_TXDW4_RTSEN |
159790540544Sjmatthew 			    R92C_TXDW4_HWRTSEN);
159890540544Sjmatthew 		} else if (ic->ic_flags & IEEE80211_F_USEPROT) {
159990540544Sjmatthew 			if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) {
160090540544Sjmatthew 				txd->txdw4 |= htole32(R92C_TXDW4_CTS2SELF |
160190540544Sjmatthew 				    R92C_TXDW4_HWRTSEN);
160290540544Sjmatthew 			} else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) {
160390540544Sjmatthew 				txd->txdw4 |= htole32(R92C_TXDW4_RTSEN |
160490540544Sjmatthew 				    R92C_TXDW4_HWRTSEN);
160590540544Sjmatthew 			}
160690540544Sjmatthew 		}
160790540544Sjmatthew 		txd->txdw5 |= htole32(0x0001ff00);
160890540544Sjmatthew 
160990540544Sjmatthew 		/* Use AMRR */
161090540544Sjmatthew 		txd->txdw3 |= htole32(R92E_TXDW3_DRVRATE);
1611efc8f9f7Sstsp 		txd->txdw4 |= htole32(SM(R92E_TXDW4_RTSRATE, 8));
161290540544Sjmatthew 		txd->txdw4 |= htole32(SM(R92E_TXDW4_DATARATE, ni->ni_txrate));
161390540544Sjmatthew 	} else {
161490540544Sjmatthew 		txd->txdw1 |= htole32(
161590540544Sjmatthew 		    SM(R92E_TXDW1_MACID, 0) |
161690540544Sjmatthew 		    SM(R92C_TXDW1_QSEL, R92C_TXDW1_QSEL_MGNT) |
161790540544Sjmatthew 		    SM(R92C_TXDW1_RAID, R92E_RAID_11B));
161890540544Sjmatthew 
161990540544Sjmatthew 		/* Force CCK1. */
162090540544Sjmatthew 		txd->txdw3 |= htole32(R92E_TXDW3_DRVRATE);
162190540544Sjmatthew 		txd->txdw4 |= htole32(SM(R92E_TXDW4_DATARATE, 0));
162290540544Sjmatthew 	}
162390540544Sjmatthew 	txd->txdw4 |= htole32(SM(R92E_TXDW4_DATARATEFB, 0x1f));
162490540544Sjmatthew 
162590540544Sjmatthew 	txd->txdseq2 |= htole16(SM(R92E_TXDSEQ2_HWSEQ, *(uint16_t *)wh->i_seq));
162690540544Sjmatthew 
162790540544Sjmatthew 	if (!ieee80211_has_qos(wh)) {
162890540544Sjmatthew 		/* Use HW sequence numbering for non-QoS frames. */
162990540544Sjmatthew 		txd->txdw7 |= htole16(R92C_TXDW3_HWSEQEN);
163090540544Sjmatthew 	}
163190540544Sjmatthew }
163290540544Sjmatthew 
163390540544Sjmatthew int
163490540544Sjmatthew urtwn_tx(void *cookie, struct mbuf *m, struct ieee80211_node *ni)
163590540544Sjmatthew {
163690540544Sjmatthew 	struct urtwn_softc *sc = cookie;
163790540544Sjmatthew 	struct ieee80211com *ic = &sc->sc_sc.sc_ic;
163890540544Sjmatthew 	struct ieee80211_frame *wh;
163990540544Sjmatthew 	struct ieee80211_key *k = NULL;
164090540544Sjmatthew 	struct urtwn_tx_data *data;
164190540544Sjmatthew 	struct usbd_pipe *pipe;
164290540544Sjmatthew 	uint16_t qos, sum;
164390540544Sjmatthew 	uint8_t tid, qid;
1644a299d652Sjmatthew 	int i, xferlen, error, headerlen;
164590540544Sjmatthew 	uint8_t *txdp;
164690540544Sjmatthew 
164790540544Sjmatthew 	wh = mtod(m, struct ieee80211_frame *);
164890540544Sjmatthew 
164990540544Sjmatthew 	if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
165090540544Sjmatthew 		k = ieee80211_get_txkey(ic, wh, ni);
1651a299d652Sjmatthew 		if (k->k_cipher != IEEE80211_CIPHER_CCMP) {
165290540544Sjmatthew 			if ((m = ieee80211_encrypt(ic, m, k)) == NULL)
165390540544Sjmatthew 				return (ENOBUFS);
165490540544Sjmatthew 			wh = mtod(m, struct ieee80211_frame *);
165590540544Sjmatthew 		}
1656a299d652Sjmatthew 	}
165790540544Sjmatthew 
165890540544Sjmatthew 	if (ieee80211_has_qos(wh)) {
165990540544Sjmatthew 		qos = ieee80211_get_qos(wh);
166090540544Sjmatthew 		tid = qos & IEEE80211_QOS_TID;
166190540544Sjmatthew 		qid = ieee80211_up_to_ac(ic, tid);
1662c6918e3cSjmatthew 	} else if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK)
166390540544Sjmatthew 	    != IEEE80211_FC0_TYPE_DATA) {
166490540544Sjmatthew 		/* Use AC VO for management frames. */
166590540544Sjmatthew 		qid = EDCA_AC_VO;
166690540544Sjmatthew 	} else
166790540544Sjmatthew 		qid = EDCA_AC_BE;
166890540544Sjmatthew 
166990540544Sjmatthew 	/* Get the USB pipe to use for this AC. */
167090540544Sjmatthew 	pipe = sc->tx_pipe[sc->ac2idx[qid]];
167190540544Sjmatthew 
167290540544Sjmatthew 	/* Grab a Tx buffer from our free list. */
167390540544Sjmatthew 	data = TAILQ_FIRST(&sc->tx_free_list);
167490540544Sjmatthew 	TAILQ_REMOVE(&sc->tx_free_list, data, next);
167590540544Sjmatthew 
167690540544Sjmatthew 	/* Fill Tx descriptor. */
167790540544Sjmatthew 	txdp = data->buf;
1678067851b1Skevlo 	if (sc->sc_sc.chip & (RTWN_CHIP_88F | RTWN_CHIP_92E))
167990540544Sjmatthew 		urtwn_tx_fill_desc_gen2(sc, &txdp, m, wh, k, ni);
168090540544Sjmatthew 	else
168190540544Sjmatthew 		urtwn_tx_fill_desc(sc, &txdp, m, wh, k, ni);
16829ec5c125Sdamien 
16839ec5c125Sdamien 	/* Compute Tx descriptor checksum. */
16849ec5c125Sdamien 	sum = 0;
168590540544Sjmatthew 	for (i = 0; i < R92C_TXDESC_SUMSIZE / 2; i++)
168690540544Sjmatthew 		sum ^= ((uint16_t *)data->buf)[i];
168790540544Sjmatthew 	((uint16_t *)data->buf)[R92C_TXDESC_SUMOFFSET] = sum;
16889ec5c125Sdamien 
16899ec5c125Sdamien #if NBPFILTER > 0
16909ec5c125Sdamien 	if (__predict_false(sc->sc_drvbpf != NULL)) {
16919ec5c125Sdamien 		struct urtwn_tx_radiotap_header *tap = &sc->sc_txtap;
16929ec5c125Sdamien 		struct mbuf mb;
16939ec5c125Sdamien 
16949ec5c125Sdamien 		tap->wt_flags = 0;
16959ec5c125Sdamien 		tap->wt_chan_freq = htole16(ic->ic_bss->ni_chan->ic_freq);
16969ec5c125Sdamien 		tap->wt_chan_flags = htole16(ic->ic_bss->ni_chan->ic_flags);
16979ec5c125Sdamien 
16989ec5c125Sdamien 		mb.m_data = (caddr_t)tap;
16999ec5c125Sdamien 		mb.m_len = sc->sc_txtap_len;
17009ec5c125Sdamien 		mb.m_next = m;
17019ec5c125Sdamien 		mb.m_nextpkt = NULL;
17029ec5c125Sdamien 		mb.m_type = 0;
17039ec5c125Sdamien 		mb.m_flags = 0;
17049ec5c125Sdamien 		bpf_mtap(sc->sc_drvbpf, &mb, BPF_DIRECTION_OUT);
17059ec5c125Sdamien 	}
17069ec5c125Sdamien #endif
17079ec5c125Sdamien 
1708a299d652Sjmatthew 	if (k != NULL && k->k_cipher == IEEE80211_CIPHER_CCMP) {
1709a299d652Sjmatthew 		xferlen = (txdp - data->buf) + m->m_pkthdr.len +
1710a299d652Sjmatthew 		    IEEE80211_CCMP_HDRLEN;
1711a299d652Sjmatthew 		headerlen = ieee80211_get_hdrlen(wh);
1712a299d652Sjmatthew 
1713a299d652Sjmatthew 		m_copydata(m, 0, headerlen, txdp);
1714a299d652Sjmatthew 		txdp += headerlen;
1715a299d652Sjmatthew 
1716a299d652Sjmatthew 		k->k_tsc++;
1717a299d652Sjmatthew 		txdp[0] = k->k_tsc;
1718a299d652Sjmatthew 		txdp[1] = k->k_tsc >> 8;
1719a299d652Sjmatthew 		txdp[2] = 0;
1720a299d652Sjmatthew 		txdp[3] = k->k_id | IEEE80211_WEP_EXTIV;
1721a299d652Sjmatthew 		txdp[4] = k->k_tsc >> 16;
1722a299d652Sjmatthew 		txdp[5] = k->k_tsc >> 24;
1723a299d652Sjmatthew 		txdp[6] = k->k_tsc >> 32;
1724a299d652Sjmatthew 		txdp[7] = k->k_tsc >> 40;
1725a299d652Sjmatthew 		txdp += IEEE80211_CCMP_HDRLEN;
1726a299d652Sjmatthew 
1727a299d652Sjmatthew 		m_copydata(m, headerlen, m->m_pkthdr.len - headerlen, txdp);
1728df3da441Sstsp 		m_freem(m);
1729a299d652Sjmatthew 	} else {
173090540544Sjmatthew 		xferlen = (txdp - data->buf) + m->m_pkthdr.len;
173190540544Sjmatthew 		m_copydata(m, 0, m->m_pkthdr.len, txdp);
17329ec5c125Sdamien 		m_freem(m);
1733a299d652Sjmatthew 	}
17349ec5c125Sdamien 
17359ec5c125Sdamien 	data->pipe = pipe;
17369ec5c125Sdamien 	usbd_setup_xfer(data->xfer, pipe, data, data->buf, xferlen,
17379ec5c125Sdamien 	    USBD_FORCE_SHORT_XFER | USBD_NO_COPY, URTWN_TX_TIMEOUT,
17389ec5c125Sdamien 	    urtwn_txeof);
17399ec5c125Sdamien 	error = usbd_transfer(data->xfer);
17409ec5c125Sdamien 	if (__predict_false(error != USBD_IN_PROGRESS && error != 0)) {
17419ec5c125Sdamien 		/* Put this Tx buffer back to our free list. */
17429ec5c125Sdamien 		TAILQ_INSERT_TAIL(&sc->tx_free_list, data, next);
17439ec5c125Sdamien 		return (error);
17449ec5c125Sdamien 	}
17459ec5c125Sdamien 	ieee80211_release_node(ic, ni);
17469ec5c125Sdamien 	return (0);
17479ec5c125Sdamien }
17489ec5c125Sdamien 
17499ec5c125Sdamien int
17509ec5c125Sdamien urtwn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
17519ec5c125Sdamien {
175223340becSstsp 	struct rtwn_softc *sc_sc = ifp->if_softc;
175323340becSstsp 	struct device *self = sc_sc->sc_pdev;
175423340becSstsp 	struct urtwn_softc *sc = (struct urtwn_softc *)self;
175523340becSstsp 	int error;
17569ec5c125Sdamien 
17575ad57547Sjakemsr 	if (usbd_is_dying(sc->sc_udev))
17585ad57547Sjakemsr 		return ENXIO;
17595ad57547Sjakemsr 
17605ad57547Sjakemsr 	usbd_ref_incr(sc->sc_udev);
176123340becSstsp 	error = rtwn_ioctl(ifp, cmd, data);
17625ad57547Sjakemsr 	usbd_ref_decr(sc->sc_udev);
17635ad57547Sjakemsr 
17649ec5c125Sdamien 	return (error);
17659ec5c125Sdamien }
17669ec5c125Sdamien 
1767c349b8bfSstsp int
1768c349b8bfSstsp urtwn_r92c_power_on(struct urtwn_softc *sc)
1769c349b8bfSstsp {
17709ec5c125Sdamien 	uint32_t reg;
17719ec5c125Sdamien 	int ntries;
17729ec5c125Sdamien 
17739ec5c125Sdamien 	/* Wait for autoload done bit. */
17749ec5c125Sdamien 	for (ntries = 0; ntries < 1000; ntries++) {
17759ec5c125Sdamien 		if (urtwn_read_1(sc, R92C_APS_FSMCO) & R92C_APS_FSMCO_PFM_ALDN)
17769ec5c125Sdamien 			break;
17779ec5c125Sdamien 		DELAY(5);
17789ec5c125Sdamien 	}
17799ec5c125Sdamien 	if (ntries == 1000) {
17809ec5c125Sdamien 		printf("%s: timeout waiting for chip autoload\n",
17819ec5c125Sdamien 		    sc->sc_dev.dv_xname);
17829ec5c125Sdamien 		return (ETIMEDOUT);
17839ec5c125Sdamien 	}
17849ec5c125Sdamien 
17859ec5c125Sdamien 	/* Unlock ISO/CLK/Power control register. */
17869ec5c125Sdamien 	urtwn_write_1(sc, R92C_RSV_CTRL, 0);
17879ec5c125Sdamien 	/* Move SPS into PWM mode. */
17889ec5c125Sdamien 	urtwn_write_1(sc, R92C_SPS0_CTRL, 0x2b);
17899ec5c125Sdamien 	DELAY(100);
17909ec5c125Sdamien 
17919ec5c125Sdamien 	reg = urtwn_read_1(sc, R92C_LDOV12D_CTRL);
17929ec5c125Sdamien 	if (!(reg & R92C_LDOV12D_CTRL_LDV12_EN)) {
17939ec5c125Sdamien 		urtwn_write_1(sc, R92C_LDOV12D_CTRL,
17949ec5c125Sdamien 		    reg | R92C_LDOV12D_CTRL_LDV12_EN);
17959ec5c125Sdamien 		DELAY(100);
17969ec5c125Sdamien 		urtwn_write_1(sc, R92C_SYS_ISO_CTRL,
17979ec5c125Sdamien 		    urtwn_read_1(sc, R92C_SYS_ISO_CTRL) &
17989ec5c125Sdamien 		    ~R92C_SYS_ISO_CTRL_MD2PP);
17999ec5c125Sdamien 	}
18009ec5c125Sdamien 
18019ec5c125Sdamien 	/* Auto enable WLAN. */
18029ec5c125Sdamien 	urtwn_write_2(sc, R92C_APS_FSMCO,
18039ec5c125Sdamien 	    urtwn_read_2(sc, R92C_APS_FSMCO) | R92C_APS_FSMCO_APFM_ONMAC);
18049ec5c125Sdamien 	for (ntries = 0; ntries < 1000; ntries++) {
1805ae90c074Smpi 		if (!(urtwn_read_2(sc, R92C_APS_FSMCO) &
1806ae90c074Smpi 		    R92C_APS_FSMCO_APFM_ONMAC))
18079ec5c125Sdamien 			break;
18089ec5c125Sdamien 		DELAY(5);
18099ec5c125Sdamien 	}
18109ec5c125Sdamien 	if (ntries == 1000) {
18119ec5c125Sdamien 		printf("%s: timeout waiting for MAC auto ON\n",
18129ec5c125Sdamien 		    sc->sc_dev.dv_xname);
18139ec5c125Sdamien 		return (ETIMEDOUT);
18149ec5c125Sdamien 	}
18159ec5c125Sdamien 
18169ec5c125Sdamien 	/* Enable radio, GPIO and LED functions. */
18179ec5c125Sdamien 	urtwn_write_2(sc, R92C_APS_FSMCO,
18189ec5c125Sdamien 	    R92C_APS_FSMCO_AFSM_HSUS |
18199ec5c125Sdamien 	    R92C_APS_FSMCO_PDN_EN |
18209ec5c125Sdamien 	    R92C_APS_FSMCO_PFM_ALDN);
18219ec5c125Sdamien 	/* Release RF digital isolation. */
18229ec5c125Sdamien 	urtwn_write_2(sc, R92C_SYS_ISO_CTRL,
18239ec5c125Sdamien 	    urtwn_read_2(sc, R92C_SYS_ISO_CTRL) & ~R92C_SYS_ISO_CTRL_DIOR);
18249ec5c125Sdamien 
18259ec5c125Sdamien 	/* Enable MAC DMA/WMAC/SCHEDULE/SEC blocks. */
18269ec5c125Sdamien 	reg = urtwn_read_2(sc, R92C_CR);
18279ec5c125Sdamien 	reg |= R92C_CR_HCI_TXDMA_EN | R92C_CR_HCI_RXDMA_EN |
18289ec5c125Sdamien 	    R92C_CR_TXDMA_EN | R92C_CR_RXDMA_EN | R92C_CR_PROTOCOL_EN |
18299ec5c125Sdamien 	    R92C_CR_SCHEDULE_EN | R92C_CR_MACTXEN | R92C_CR_MACRXEN |
18309ec5c125Sdamien 	    R92C_CR_ENSEC;
18319ec5c125Sdamien 	urtwn_write_2(sc, R92C_CR, reg);
18329ec5c125Sdamien 
18339ec5c125Sdamien 	urtwn_write_1(sc, 0xfe10, 0x19);
18349ec5c125Sdamien 	return (0);
18359ec5c125Sdamien }
18369ec5c125Sdamien 
18379ec5c125Sdamien int
183890540544Sjmatthew urtwn_r92e_power_on(struct urtwn_softc *sc)
183990540544Sjmatthew {
184090540544Sjmatthew 	uint32_t reg;
184190540544Sjmatthew 	int ntries;
184290540544Sjmatthew 
184390540544Sjmatthew 	if (urtwn_read_4(sc, R92C_SYS_CFG) & R92E_SYS_CFG_SPSLDO_SEL) {
184490540544Sjmatthew 		/* LDO. */
184590540544Sjmatthew 		urtwn_write_1(sc, R92E_LDO_SWR_CTRL, 0xc3);
184690540544Sjmatthew 	} else {
184790540544Sjmatthew 		reg = urtwn_read_4(sc, R92C_SYS_SWR_CTRL2);
184890540544Sjmatthew 		reg &= 0xff0fffff;
184990540544Sjmatthew 		reg |= 0x00500000;
185090540544Sjmatthew 		urtwn_write_4(sc, R92C_SYS_SWR_CTRL2, reg);
185190540544Sjmatthew 		urtwn_write_1(sc, R92E_LDO_SWR_CTRL, 0x83);
185290540544Sjmatthew 	}
185390540544Sjmatthew 
185490540544Sjmatthew 	/* 40MHz crystal source */
185590540544Sjmatthew 	urtwn_write_1(sc, R92C_AFE_PLL_CTRL,
185690540544Sjmatthew 	    urtwn_read_1(sc, R92C_AFE_PLL_CTRL) & 0xfb);
185790540544Sjmatthew 	urtwn_write_4(sc, R92C_AFE_XTAL_CTRL_EXT,
185890540544Sjmatthew 	    urtwn_read_4(sc, R92C_AFE_XTAL_CTRL_EXT) & 0xfffffc7f);
185990540544Sjmatthew 
186090540544Sjmatthew 	urtwn_write_1(sc, R92C_AFE_PLL_CTRL,
186190540544Sjmatthew 	    urtwn_read_1(sc, R92C_AFE_PLL_CTRL) & 0xbf);
186290540544Sjmatthew 	urtwn_write_4(sc, R92C_AFE_XTAL_CTRL_EXT,
186390540544Sjmatthew 	    urtwn_read_4(sc, R92C_AFE_XTAL_CTRL_EXT) & 0xffdfffff);
186490540544Sjmatthew 
186590540544Sjmatthew 	/* Disable HWPDN. */
186690540544Sjmatthew 	urtwn_write_2(sc, R92C_APS_FSMCO,
186790540544Sjmatthew 	    urtwn_read_2(sc, R92C_APS_FSMCO) & ~R92C_APS_FSMCO_APDM_HPDN);
186890540544Sjmatthew 	for (ntries = 0; ntries < 5000; ntries++) {
186990540544Sjmatthew 		if (urtwn_read_4(sc, R92C_APS_FSMCO) & R92C_APS_FSMCO_SUS_HOST)
187090540544Sjmatthew 			break;
187190540544Sjmatthew 		DELAY(10);
187290540544Sjmatthew 	}
187390540544Sjmatthew 	if (ntries == 5000) {
187490540544Sjmatthew 		printf("%s: timeout waiting for chip power up\n",
187590540544Sjmatthew 		    sc->sc_dev.dv_xname);
187690540544Sjmatthew 		return (ETIMEDOUT);
187790540544Sjmatthew 	}
187890540544Sjmatthew 
187990540544Sjmatthew 	/* Disable WL suspend. */
188090540544Sjmatthew 	urtwn_write_2(sc, R92C_APS_FSMCO,
188190540544Sjmatthew 	    urtwn_read_2(sc, R92C_APS_FSMCO) &
188290540544Sjmatthew 	    ~(R92C_APS_FSMCO_AFSM_HSUS | R92C_APS_FSMCO_AFSM_PCIE));
188390540544Sjmatthew 
188490540544Sjmatthew 	/* Auto enable WLAN. */
188590540544Sjmatthew 	urtwn_write_4(sc, R92C_APS_FSMCO,
188690540544Sjmatthew 	    urtwn_read_4(sc, R92C_APS_FSMCO) | R92C_APS_FSMCO_RDY_MACON);
188790540544Sjmatthew 	urtwn_write_2(sc, R92C_APS_FSMCO,
188890540544Sjmatthew 	    urtwn_read_2(sc, R92C_APS_FSMCO) | R92C_APS_FSMCO_APFM_ONMAC);
188990540544Sjmatthew 	for (ntries = 0; ntries < 5000; ntries++) {
189090540544Sjmatthew 		if (!(urtwn_read_2(sc, R92C_APS_FSMCO) &
189190540544Sjmatthew 		    R92C_APS_FSMCO_APFM_ONMAC))
189290540544Sjmatthew 			break;
189390540544Sjmatthew 		DELAY(10);
189490540544Sjmatthew 	}
189590540544Sjmatthew 	if (ntries == 5000) {
189690540544Sjmatthew 		printf("%s: timeout waiting for MAC auto ON\n",
189790540544Sjmatthew 		    sc->sc_dev.dv_xname);
189890540544Sjmatthew 		return (ETIMEDOUT);
189990540544Sjmatthew 	}
190090540544Sjmatthew 
190190540544Sjmatthew 	/* Enable MAC DMA/WMAC/SCHEDULE/SEC blocks. */
190290540544Sjmatthew 	urtwn_write_2(sc, R92C_CR, 0);
190390540544Sjmatthew 	reg = urtwn_read_2(sc, R92C_CR);
190490540544Sjmatthew 	reg |= R92C_CR_HCI_TXDMA_EN | R92C_CR_HCI_RXDMA_EN |
190590540544Sjmatthew 	    R92C_CR_TXDMA_EN | R92C_CR_RXDMA_EN | R92C_CR_PROTOCOL_EN |
190690540544Sjmatthew 	    R92C_CR_SCHEDULE_EN | R92C_CR_ENSEC | R92C_CR_CALTMR_EN;
190790540544Sjmatthew 	urtwn_write_2(sc, R92C_CR, reg);
190890540544Sjmatthew 	return (0);
190990540544Sjmatthew }
191090540544Sjmatthew 
191190540544Sjmatthew int
1912c349b8bfSstsp urtwn_r88e_power_on(struct urtwn_softc *sc)
1913c349b8bfSstsp {
1914c349b8bfSstsp 	uint32_t reg;
1915c349b8bfSstsp 	int ntries;
1916c349b8bfSstsp 
1917c349b8bfSstsp 	/* Wait for power ready bit. */
1918c349b8bfSstsp 	for (ntries = 0; ntries < 5000; ntries++) {
1919a55d0e84Sstsp 		if (urtwn_read_4(sc, R92C_APS_FSMCO) & R92C_APS_FSMCO_SUS_HOST)
1920c349b8bfSstsp 			break;
1921c349b8bfSstsp 		DELAY(10);
1922c349b8bfSstsp 	}
1923c349b8bfSstsp 	if (ntries == 5000) {
1924c349b8bfSstsp 		printf("%s: timeout waiting for chip power up\n",
1925c349b8bfSstsp 		    sc->sc_dev.dv_xname);
1926c349b8bfSstsp 		return (ETIMEDOUT);
1927c349b8bfSstsp 	}
1928c349b8bfSstsp 
1929c349b8bfSstsp 	/* Reset BB. */
1930c349b8bfSstsp 	urtwn_write_1(sc, R92C_SYS_FUNC_EN,
1931c349b8bfSstsp 	    urtwn_read_1(sc, R92C_SYS_FUNC_EN) & ~(R92C_SYS_FUNC_EN_BBRSTB |
1932c349b8bfSstsp 	    R92C_SYS_FUNC_EN_BB_GLB_RST));
1933c349b8bfSstsp 
1934a55d0e84Sstsp 	urtwn_write_1(sc, R92C_AFE_XTAL_CTRL + 2,
1935a55d0e84Sstsp 	    urtwn_read_1(sc, R92C_AFE_XTAL_CTRL + 2) | 0x80);
1936c349b8bfSstsp 
1937c349b8bfSstsp 	/* Disable HWPDN. */
1938a55d0e84Sstsp 	urtwn_write_2(sc, R92C_APS_FSMCO,
1939a55d0e84Sstsp 	    urtwn_read_2(sc, R92C_APS_FSMCO) & ~R92C_APS_FSMCO_APDM_HPDN);
1940c349b8bfSstsp 	/* Disable WL suspend. */
1941a55d0e84Sstsp 	urtwn_write_2(sc, R92C_APS_FSMCO,
1942a55d0e84Sstsp 	    urtwn_read_2(sc, R92C_APS_FSMCO) &
1943a55d0e84Sstsp 	    ~(R92C_APS_FSMCO_AFSM_HSUS | R92C_APS_FSMCO_AFSM_PCIE));
1944c349b8bfSstsp 
194508300c66Skevlo 	/* Auto enable WLAN. */
1946a55d0e84Sstsp 	urtwn_write_2(sc, R92C_APS_FSMCO,
1947a55d0e84Sstsp 	    urtwn_read_2(sc, R92C_APS_FSMCO) | R92C_APS_FSMCO_APFM_ONMAC);
1948c349b8bfSstsp 	for (ntries = 0; ntries < 5000; ntries++) {
1949a55d0e84Sstsp 		if (!(urtwn_read_2(sc, R92C_APS_FSMCO) &
1950a55d0e84Sstsp 		    R92C_APS_FSMCO_APFM_ONMAC))
1951c349b8bfSstsp 			break;
1952c349b8bfSstsp 		DELAY(10);
1953c349b8bfSstsp 	}
195408300c66Skevlo 	if (ntries == 5000) {
195508300c66Skevlo 		printf("%s: timeout waiting for MAC auto ON\n",
195608300c66Skevlo 		    sc->sc_dev.dv_xname);
1957c349b8bfSstsp 		return (ETIMEDOUT);
195808300c66Skevlo 	}
1959c349b8bfSstsp 
1960c349b8bfSstsp 	/* Enable LDO normal mode. */
1961a55d0e84Sstsp 	urtwn_write_1(sc, R92C_LPLDO_CTRL,
1962a55d0e84Sstsp 	    urtwn_read_1(sc, R92C_LPLDO_CTRL) & ~0x10);
1963c349b8bfSstsp 
1964c349b8bfSstsp 	/* Enable MAC DMA/WMAC/SCHEDULE/SEC blocks. */
1965c349b8bfSstsp 	urtwn_write_2(sc, R92C_CR, 0);
1966c349b8bfSstsp 	reg = urtwn_read_2(sc, R92C_CR);
1967c349b8bfSstsp 	reg |= R92C_CR_HCI_TXDMA_EN | R92C_CR_HCI_RXDMA_EN |
1968c349b8bfSstsp 	    R92C_CR_TXDMA_EN | R92C_CR_RXDMA_EN | R92C_CR_PROTOCOL_EN |
1969c349b8bfSstsp 	    R92C_CR_SCHEDULE_EN | R92C_CR_ENSEC | R92C_CR_CALTMR_EN;
1970c349b8bfSstsp 	urtwn_write_2(sc, R92C_CR, reg);
1971c349b8bfSstsp 	return (0);
1972c349b8bfSstsp }
1973c349b8bfSstsp 
1974c349b8bfSstsp int
1975067851b1Skevlo urtwn_r88f_power_on(struct urtwn_softc *sc)
1976067851b1Skevlo {
1977067851b1Skevlo 	uint32_t reg;
1978067851b1Skevlo 	int ntries;
1979067851b1Skevlo 
1980067851b1Skevlo 	/* Enable WL suspend. */
1981067851b1Skevlo 	urtwn_write_2(sc, R92C_APS_FSMCO,
1982067851b1Skevlo 	    urtwn_read_2(sc, R92C_APS_FSMCO) &
1983067851b1Skevlo 	    ~(R92C_APS_FSMCO_AFSM_HSUS | R92C_APS_FSMCO_AFSM_PCIE));
1984067851b1Skevlo 	/* Turn off USB APHY LDO under suspend mode. */
1985067851b1Skevlo 	urtwn_write_1(sc, 0xc4, urtwn_read_1(sc, 0xc4) & ~0x10);
1986067851b1Skevlo 
1987067851b1Skevlo 	/* Disable SW LPS. */
1988067851b1Skevlo 	urtwn_write_2(sc, R92C_APS_FSMCO,
1989067851b1Skevlo 	    urtwn_read_2(sc, R92C_APS_FSMCO) & ~R92C_APS_FSMCO_APFM_RSM);
1990067851b1Skevlo 	/* Wait for power ready bit. */
1991067851b1Skevlo 	for (ntries = 0; ntries < 5000; ntries++) {
1992067851b1Skevlo 		if (urtwn_read_4(sc, R92C_APS_FSMCO) & R92C_APS_FSMCO_SUS_HOST)
1993067851b1Skevlo 			break;
1994067851b1Skevlo 		DELAY(10);
1995067851b1Skevlo 	}
1996067851b1Skevlo 	if (ntries == 5000) {
1997067851b1Skevlo 		printf("%s: timeout waiting for chip power up\n",
1998067851b1Skevlo 		    sc->sc_dev.dv_xname);
1999067851b1Skevlo 		return (ETIMEDOUT);
2000067851b1Skevlo 	}
2001067851b1Skevlo 	/* Disable HWPDN. */
2002067851b1Skevlo 	urtwn_write_2(sc, R92C_APS_FSMCO,
2003067851b1Skevlo 	    urtwn_read_2(sc, R92C_APS_FSMCO) & ~R92C_APS_FSMCO_APDM_HPDN);
2004067851b1Skevlo 	/* Disable WL suspend. */
2005067851b1Skevlo 	urtwn_write_2(sc, R92C_APS_FSMCO,
2006067851b1Skevlo 	    urtwn_read_2(sc, R92C_APS_FSMCO) & ~R92C_APS_FSMCO_AFSM_HSUS);
2007067851b1Skevlo 	/* Auto enable WLAN. */
2008067851b1Skevlo 	urtwn_write_2(sc, R92C_APS_FSMCO,
2009067851b1Skevlo 	    urtwn_read_2(sc, R92C_APS_FSMCO) | R92C_APS_FSMCO_APFM_ONMAC);
2010067851b1Skevlo 	for (ntries = 0; ntries < 5000; ntries++) {
2011067851b1Skevlo 		if (!(urtwn_read_2(sc, R92C_APS_FSMCO) &
2012067851b1Skevlo 		    R92C_APS_FSMCO_APFM_ONMAC))
2013067851b1Skevlo 			break;
2014067851b1Skevlo 		DELAY(10);
2015067851b1Skevlo 	}
2016067851b1Skevlo 	if (ntries == 5000) {
2017067851b1Skevlo 		printf("%s: timeout waiting for MAC auto ON\n",
2018067851b1Skevlo 		    sc->sc_dev.dv_xname);
2019067851b1Skevlo 		return (ETIMEDOUT);
2020067851b1Skevlo 	}
2021067851b1Skevlo 	/* Reduce RF noise. */
2022067851b1Skevlo 	urtwn_write_1(sc, R92C_AFE_LDO_CTRL, 0x35);
2023067851b1Skevlo 
2024067851b1Skevlo 	/* Enable MAC DMA/WMAC/SCHEDULE/SEC blocks. */
2025067851b1Skevlo 	urtwn_write_2(sc, R92C_CR, 0);
2026067851b1Skevlo 	reg = urtwn_read_2(sc, R92C_CR);
2027067851b1Skevlo 	reg |= R92C_CR_HCI_TXDMA_EN | R92C_CR_HCI_RXDMA_EN |
2028067851b1Skevlo 	    R92C_CR_TXDMA_EN | R92C_CR_RXDMA_EN | R92C_CR_PROTOCOL_EN |
2029067851b1Skevlo 	    R92C_CR_SCHEDULE_EN | R92C_CR_ENSEC | R92C_CR_CALTMR_EN;
2030067851b1Skevlo 	urtwn_write_2(sc, R92C_CR, reg);
2031067851b1Skevlo 	return (0);
2032067851b1Skevlo }
2033067851b1Skevlo 
2034067851b1Skevlo int
203508300c66Skevlo urtwn_llt_init(struct urtwn_softc *sc, int page_count)
20369ec5c125Sdamien {
203708300c66Skevlo 	int i, error, pktbuf_count;
20389ec5c125Sdamien 
203923340becSstsp 	pktbuf_count = (sc->sc_sc.chip & RTWN_CHIP_88E) ?
2040c349b8bfSstsp 	    R88E_TXPKTBUF_COUNT : R92C_TXPKTBUF_COUNT;
2041c349b8bfSstsp 
2042c349b8bfSstsp 	/* Reserve pages [0; page_count]. */
2043c349b8bfSstsp 	for (i = 0; i < page_count; i++) {
20449ec5c125Sdamien 		if ((error = urtwn_llt_write(sc, i, i + 1)) != 0)
20459ec5c125Sdamien 			return (error);
20469ec5c125Sdamien 	}
20479ec5c125Sdamien 	/* NB: 0xff indicates end-of-list. */
20489ec5c125Sdamien 	if ((error = urtwn_llt_write(sc, i, 0xff)) != 0)
20499ec5c125Sdamien 		return (error);
20509ec5c125Sdamien 	/*
2051c349b8bfSstsp 	 * Use pages [page_count + 1; pktbuf_count - 1]
20529ec5c125Sdamien 	 * as ring buffer.
20539ec5c125Sdamien 	 */
2054c349b8bfSstsp 	for (++i; i < pktbuf_count - 1; i++) {
20559ec5c125Sdamien 		if ((error = urtwn_llt_write(sc, i, i + 1)) != 0)
20569ec5c125Sdamien 			return (error);
20579ec5c125Sdamien 	}
20589ec5c125Sdamien 	/* Make the last page point to the beginning of the ring buffer. */
2059c349b8bfSstsp 	error = urtwn_llt_write(sc, i, page_count + 1);
20609ec5c125Sdamien 	return (error);
20619ec5c125Sdamien }
20629ec5c125Sdamien 
20639ec5c125Sdamien int
206490540544Sjmatthew urtwn_auto_llt_init(struct urtwn_softc *sc)
206590540544Sjmatthew {
206690540544Sjmatthew 	int ntries;
206790540544Sjmatthew 
2068067851b1Skevlo 	urtwn_write_4(sc, R92E_AUTO_LLT,
2069067851b1Skevlo 	    urtwn_read_4(sc, R92E_AUTO_LLT) | R92E_AUTO_LLT_EN);
207090540544Sjmatthew 	for (ntries = 0; ntries < 1000; ntries++) {
207190540544Sjmatthew 		if (!(urtwn_read_4(sc, R92E_AUTO_LLT) & R92E_AUTO_LLT_EN))
207290540544Sjmatthew 			return (0);
207390540544Sjmatthew 		DELAY(2);
207490540544Sjmatthew 	}
207590540544Sjmatthew 
207690540544Sjmatthew 	return (ETIMEDOUT);
207790540544Sjmatthew }
207890540544Sjmatthew 
207990540544Sjmatthew int
208023340becSstsp urtwn_fw_loadpage(void *cookie, int page, uint8_t *buf, int len)
20819ec5c125Sdamien {
208223340becSstsp 	struct urtwn_softc *sc = cookie;
20839ec5c125Sdamien 	uint32_t reg;
20848bca19d0Skevlo 	int maxblksz, off, mlen, error = 0;
20859ec5c125Sdamien 
20869ec5c125Sdamien 	reg = urtwn_read_4(sc, R92C_MCUFWDL);
20879ec5c125Sdamien 	reg = RW(reg, R92C_MCUFWDL_PAGE, page);
20889ec5c125Sdamien 	urtwn_write_4(sc, R92C_MCUFWDL, reg);
20899ec5c125Sdamien 
20908bca19d0Skevlo 	maxblksz = (sc->sc_sc.chip & RTWN_CHIP_92E) ? 254 : 196;
20918bca19d0Skevlo 
20929ec5c125Sdamien 	off = R92C_FW_START_ADDR;
20939ec5c125Sdamien 	while (len > 0) {
20948bca19d0Skevlo 		if (len > maxblksz)
20958bca19d0Skevlo 			mlen = maxblksz;
20969ec5c125Sdamien 		else if (len > 4)
20979ec5c125Sdamien 			mlen = 4;
20989ec5c125Sdamien 		else
20999ec5c125Sdamien 			mlen = 1;
21009ec5c125Sdamien 		error = urtwn_write_region_1(sc, off, buf, mlen);
21019ec5c125Sdamien 		if (error != 0)
21029ec5c125Sdamien 			break;
21039ec5c125Sdamien 		off += mlen;
21049ec5c125Sdamien 		buf += mlen;
21059ec5c125Sdamien 		len -= mlen;
21069ec5c125Sdamien 	}
21079ec5c125Sdamien 	return (error);
21089ec5c125Sdamien }
21099ec5c125Sdamien 
21109ec5c125Sdamien int
211123340becSstsp urtwn_load_firmware(void *cookie, u_char **fw, size_t *len)
21129ec5c125Sdamien {
211323340becSstsp 	struct urtwn_softc *sc = cookie;
21149ec5c125Sdamien 	const char *name;
211523340becSstsp 	int error;
21169ec5c125Sdamien 
211790540544Sjmatthew 	if (sc->sc_sc.chip & RTWN_CHIP_92E)
21181e1b982aSkevlo 		name = "urtwn-rtl8192eu";
211990540544Sjmatthew 	else if (sc->sc_sc.chip & RTWN_CHIP_88E)
21201e1b982aSkevlo 		name = "urtwn-rtl8188eu";
2121067851b1Skevlo 	else if (sc->sc_sc.chip & RTWN_CHIP_88F)
2122067851b1Skevlo 		name = "urtwn-rtl8188ftv";
212323340becSstsp 	else if ((sc->sc_sc.chip & (RTWN_CHIP_UMC_A_CUT | RTWN_CHIP_92C)) ==
212423340becSstsp 		    RTWN_CHIP_UMC_A_CUT)
21251e1b982aSkevlo 		name = "urtwn-rtl8192cU";
21269ec5c125Sdamien 	else
21271e1b982aSkevlo 		name = "urtwn-rtl8192cT";
212823340becSstsp 
212923340becSstsp 	error = loadfirmware(name, fw, len);
213023340becSstsp 	if (error)
213123340becSstsp 		printf("%s: could not read firmware %s (error %d)\n",
21329ec5c125Sdamien 		    sc->sc_dev.dv_xname, name, error);
21339ec5c125Sdamien 	return (error);
21349ec5c125Sdamien }
21359ec5c125Sdamien 
213623340becSstsp int
213723340becSstsp urtwn_dma_init(void *cookie)
213823340becSstsp {
213923340becSstsp 	struct urtwn_softc *sc = cookie;
214008300c66Skevlo 	uint32_t reg;
214108300c66Skevlo 	uint16_t dmasize;
214208300c66Skevlo 	int hqpages, lqpages, nqpages, pagecnt, boundary;
214308300c66Skevlo 	int error, hashq, haslq, hasnq;
2144c349b8bfSstsp 
214508300c66Skevlo 	/* Default initialization of chipset values. */
214608300c66Skevlo 	if (sc->sc_sc.chip & RTWN_CHIP_88E) {
214708300c66Skevlo 		hqpages = R88E_HQ_NPAGES;
214808300c66Skevlo 		lqpages = R88E_LQ_NPAGES;
214908300c66Skevlo 		nqpages = R88E_NQ_NPAGES;
215008300c66Skevlo 		pagecnt = R88E_TX_PAGE_COUNT;
215108300c66Skevlo 		dmasize = R88E_MAX_RX_DMA_SIZE;
2152067851b1Skevlo 	} else if (sc->sc_sc.chip & RTWN_CHIP_88F) {
2153067851b1Skevlo 		hqpages = R88F_HQ_NPAGES;
2154067851b1Skevlo 		lqpages = R88F_LQ_NPAGES;
2155067851b1Skevlo 		nqpages = R88F_NQ_NPAGES;
2156067851b1Skevlo 		pagecnt = R88F_TX_PAGE_COUNT;
2157067851b1Skevlo 		dmasize = R88F_MAX_RX_DMA_SIZE;
215890540544Sjmatthew 	} else if (sc->sc_sc.chip & RTWN_CHIP_92E) {
215990540544Sjmatthew 		hqpages = R92E_HQ_NPAGES;
216090540544Sjmatthew 		lqpages = R92E_LQ_NPAGES;
216190540544Sjmatthew 		nqpages = R92E_NQ_NPAGES;
216290540544Sjmatthew 		pagecnt = R92E_TX_PAGE_COUNT;
216390540544Sjmatthew 		dmasize = R92E_MAX_RX_DMA_SIZE;
216408300c66Skevlo 	} else {
216508300c66Skevlo 		hqpages = R92C_HQ_NPAGES;
216608300c66Skevlo 		lqpages = R92C_LQ_NPAGES;
216708300c66Skevlo 		nqpages = R92C_NQ_NPAGES;
216808300c66Skevlo 		pagecnt = R92C_TX_PAGE_COUNT;
216908300c66Skevlo 		dmasize = R92C_MAX_RX_DMA_SIZE;
21709ec5c125Sdamien 	}
2171067851b1Skevlo 	boundary = pagecnt + 1;
21729ec5c125Sdamien 
21739ec5c125Sdamien 	/* Initialize LLT table. */
2174067851b1Skevlo 	if (sc->sc_sc.chip & (RTWN_CHIP_88F | RTWN_CHIP_92E))
217590540544Sjmatthew 		error = urtwn_auto_llt_init(sc);
2176067851b1Skevlo 	else
217708300c66Skevlo 		error = urtwn_llt_init(sc, pagecnt);
21789ec5c125Sdamien 	if (error != 0)
21799ec5c125Sdamien 		return (error);
21809ec5c125Sdamien 
21819ec5c125Sdamien 	/* Get Tx queues to USB endpoints mapping. */
21829ec5c125Sdamien 	hashq = hasnq = haslq = 0;
218308300c66Skevlo 	switch (sc->ntx) {
218408300c66Skevlo 	case 3:
21859ec5c125Sdamien 		haslq = 1;
218608300c66Skevlo 		pagecnt -= lqpages;
218708300c66Skevlo 		/* FALLTHROUGH */
218808300c66Skevlo 	case 2:
218908300c66Skevlo 		hasnq = 1;
219008300c66Skevlo 		pagecnt -= nqpages;
219108300c66Skevlo 		/* FALLTHROUGH */
219208300c66Skevlo 	case 1:
219308300c66Skevlo 		hashq = 1;
219408300c66Skevlo 		pagecnt -= hqpages;
219508300c66Skevlo 		break;
219608300c66Skevlo 	}
21979ec5c125Sdamien 
21989ec5c125Sdamien 	/* Set number of pages for normal priority queue. */
21999ec5c125Sdamien 	urtwn_write_1(sc, R92C_RQPN_NPQ, hasnq ? nqpages : 0);
22009ec5c125Sdamien 	urtwn_write_4(sc, R92C_RQPN,
22019ec5c125Sdamien 	    /* Set number of pages for public queue. */
220208300c66Skevlo 	    SM(R92C_RQPN_PUBQ, pagecnt) |
22039ec5c125Sdamien 	    /* Set number of pages for high priority queue. */
220408300c66Skevlo 	    SM(R92C_RQPN_HPQ, hashq ? hqpages : 0) |
22059ec5c125Sdamien 	    /* Set number of pages for low priority queue. */
220608300c66Skevlo 	    SM(R92C_RQPN_LPQ, haslq ? lqpages : 0) |
22079ec5c125Sdamien 	    /* Load values. */
22089ec5c125Sdamien 	    R92C_RQPN_LD);
22099ec5c125Sdamien 
221008300c66Skevlo 	urtwn_write_1(sc, R92C_TXPKTBUF_BCNQ_BDNY, boundary);
221108300c66Skevlo 	urtwn_write_1(sc, R92C_TXPKTBUF_MGQ_BDNY, boundary);
221208300c66Skevlo 	urtwn_write_1(sc, R92C_TXPKTBUF_WMAC_LBK_BF_HD, boundary);
221308300c66Skevlo 	urtwn_write_1(sc, R92C_TRXFF_BNDY, boundary);
221408300c66Skevlo 	urtwn_write_1(sc, R92C_TDECTRL + 1, boundary);
22159ec5c125Sdamien 
22169ec5c125Sdamien 	/* Set queue to USB pipe mapping. */
22179ec5c125Sdamien 	reg = urtwn_read_2(sc, R92C_TRXDMA_CTRL);
22189ec5c125Sdamien 	reg &= ~R92C_TRXDMA_CTRL_QMAP_M;
221908300c66Skevlo 	if (haslq)
222008300c66Skevlo 		reg |= R92C_TRXDMA_CTRL_QMAP_3EP;
222108300c66Skevlo 	else if (hashq) {
222208300c66Skevlo 		if (!hasnq)
22239ec5c125Sdamien 			reg |= R92C_TRXDMA_CTRL_QMAP_HQ;
22249ec5c125Sdamien 		else
22259ec5c125Sdamien 			reg |= R92C_TRXDMA_CTRL_QMAP_HQ_NQ;
22269ec5c125Sdamien 	}
2227c349b8bfSstsp 	urtwn_write_2(sc, R92C_TRXDMA_CTRL, reg);
2228c349b8bfSstsp 
2229c349b8bfSstsp 	/* Set Tx/Rx transfer page boundary. */
223008300c66Skevlo 	urtwn_write_2(sc, R92C_TRXFF_BNDY + 2, dmasize - 1);
2231c349b8bfSstsp 
223290540544Sjmatthew 	if (!(sc->sc_sc.chip & RTWN_CHIP_92E)) {
2233c349b8bfSstsp 		/* Set Tx/Rx transfer page size. */
2234067851b1Skevlo 		if (sc->sc_sc.chip & RTWN_CHIP_88F) {
2235067851b1Skevlo 			urtwn_write_1(sc, R92C_PBP,
2236067851b1Skevlo 			    SM(R92C_PBP_PSRX, R92C_PBP_256) |
2237067851b1Skevlo 			    SM(R92C_PBP_PSTX, R92C_PBP_256));
2238067851b1Skevlo 		} else {
2239c349b8bfSstsp 			urtwn_write_1(sc, R92C_PBP,
2240c349b8bfSstsp 			    SM(R92C_PBP_PSRX, R92C_PBP_128) |
2241c349b8bfSstsp 			    SM(R92C_PBP_PSTX, R92C_PBP_128));
224290540544Sjmatthew 		}
2243067851b1Skevlo 	}
224408300c66Skevlo 	return (error);
2245c349b8bfSstsp }
2246c349b8bfSstsp 
22479ec5c125Sdamien void
22482d2a7e26Skevlo urtwn_aggr_init(void *cookie)
22492d2a7e26Skevlo {
22502d2a7e26Skevlo 	struct urtwn_softc *sc = cookie;
22512d2a7e26Skevlo 	uint32_t reg = 0;
22522d2a7e26Skevlo 	int dmasize, dmatiming, ndesc;
22532d2a7e26Skevlo 
22542d2a7e26Skevlo 	/* Set burst packet length. */
2255067851b1Skevlo 	if (sc->sc_sc.chip & (RTWN_CHIP_88F | RTWN_CHIP_92E))
22562d2a7e26Skevlo 		urtwn_burstlen_init(sc);
22572d2a7e26Skevlo 
2258067851b1Skevlo 	if (sc->sc_sc.chip & RTWN_CHIP_88F) {
2259067851b1Skevlo 		dmasize = 5;
2260067851b1Skevlo 		dmatiming = 32;
2261067851b1Skevlo 		ndesc = 6;
2262067851b1Skevlo 	} else if (sc->sc_sc.chip & RTWN_CHIP_92E) {
22632d2a7e26Skevlo 		dmasize = 6;
22642d2a7e26Skevlo 		dmatiming = 32;
22652d2a7e26Skevlo 		ndesc = 3;
22662d2a7e26Skevlo 	} else {
22672d2a7e26Skevlo 		dmasize = 48;
22682d2a7e26Skevlo 		dmatiming = 4;
22692d2a7e26Skevlo 		ndesc = (sc->sc_sc.chip & RTWN_CHIP_88E) ? 1 : 6;
22702d2a7e26Skevlo 	}
22712d2a7e26Skevlo 
22722d2a7e26Skevlo 	/* Tx aggregation setting. */
22732d2a7e26Skevlo 	reg = urtwn_read_4(sc, R92C_TDECTRL);
22742d2a7e26Skevlo 	reg = RW(reg, R92C_TDECTRL_BLK_DESC_NUM, ndesc);
22752d2a7e26Skevlo 	urtwn_write_4(sc, R92C_TDECTRL, reg);
2276067851b1Skevlo 	if (sc->sc_sc.chip & (RTWN_CHIP_88F | RTWN_CHIP_92E))
22778bca19d0Skevlo 		urtwn_write_1(sc, R92E_DWBCN1_CTRL, ndesc << 1);
22782d2a7e26Skevlo 
22792d2a7e26Skevlo 	/* Rx aggregation setting. */
22802d2a7e26Skevlo 	urtwn_write_1(sc, R92C_TRXDMA_CTRL,
22818bca19d0Skevlo 	    urtwn_read_1(sc, R92C_TRXDMA_CTRL) | R92C_TRXDMA_CTRL_RXDMA_AGG_EN);
22822d2a7e26Skevlo 
22832d2a7e26Skevlo 	urtwn_write_1(sc, R92C_RXDMA_AGG_PG_TH, dmasize);
22842d2a7e26Skevlo 	if (sc->sc_sc.chip & (RTWN_CHIP_92C | RTWN_CHIP_88C))
22852d2a7e26Skevlo 		urtwn_write_1(sc, R92C_USB_DMA_AGG_TO, dmatiming);
22862d2a7e26Skevlo 	else
22872d2a7e26Skevlo 		urtwn_write_1(sc, R92C_RXDMA_AGG_PG_TH + 1, dmatiming);
22882d2a7e26Skevlo 
2289067851b1Skevlo 	if (sc->sc_sc.chip & RTWN_CHIP_88F) {
2290067851b1Skevlo 		urtwn_write_1(sc, R92E_RXDMA_PRO,
2291067851b1Skevlo 		    urtwn_read_1(sc, R92E_RXDMA_PRO) | R92E_RXDMA_PRO_DMA_MODE);
2292067851b1Skevlo 	}
2293067851b1Skevlo 
22942d2a7e26Skevlo 	/* Drop incorrect bulk out. */
22952d2a7e26Skevlo 	urtwn_write_4(sc, R92C_TXDMA_OFFSET_CHK,
22962d2a7e26Skevlo 	    urtwn_read_4(sc, R92C_TXDMA_OFFSET_CHK) |
22972d2a7e26Skevlo 	    R92C_TXDMA_OFFSET_CHK_DROP_DATA_EN);
22982d2a7e26Skevlo }
22992d2a7e26Skevlo 
23002d2a7e26Skevlo void
230123340becSstsp urtwn_mac_init(void *cookie)
23029ec5c125Sdamien {
230323340becSstsp 	struct urtwn_softc *sc = cookie;
23049ec5c125Sdamien 	int i;
23059ec5c125Sdamien 
23069ec5c125Sdamien 	/* Write MAC initialization values. */
230723340becSstsp 	if (sc->sc_sc.chip & RTWN_CHIP_88E) {
2308c349b8bfSstsp 		for (i = 0; i < nitems(rtl8188eu_mac); i++) {
2309c349b8bfSstsp 			urtwn_write_1(sc, rtl8188eu_mac[i].reg,
2310c349b8bfSstsp 			    rtl8188eu_mac[i].val);
2311c349b8bfSstsp 		}
2312c349b8bfSstsp 		urtwn_write_1(sc, R92C_MAX_AGGR_NUM, 0x07);
2313067851b1Skevlo 	} else if (sc->sc_sc.chip & RTWN_CHIP_88F) {
2314067851b1Skevlo 		for (i = 0; i < nitems(rtl8188ftv_mac); i++) {
2315067851b1Skevlo 			urtwn_write_1(sc, rtl8188ftv_mac[i].reg,
2316067851b1Skevlo 			    rtl8188ftv_mac[i].val);
2317067851b1Skevlo 		}
231890540544Sjmatthew 	} else if (sc->sc_sc.chip & RTWN_CHIP_92E) {
231990540544Sjmatthew 		for (i = 0; i < nitems(rtl8192eu_mac); i++) {
232090540544Sjmatthew 			urtwn_write_1(sc, rtl8192eu_mac[i].reg,
232190540544Sjmatthew 			    rtl8192eu_mac[i].val);
232290540544Sjmatthew 		}
2323c349b8bfSstsp 	} else {
23249ec5c125Sdamien 		for (i = 0; i < nitems(rtl8192cu_mac); i++)
2325c349b8bfSstsp 			urtwn_write_1(sc, rtl8192cu_mac[i].reg,
2326c349b8bfSstsp 			    rtl8192cu_mac[i].val);
2327c349b8bfSstsp 	}
23289ec5c125Sdamien }
23299ec5c125Sdamien 
23309ec5c125Sdamien void
233123340becSstsp urtwn_bb_init(void *cookie)
23329ec5c125Sdamien {
233323340becSstsp 	struct urtwn_softc *sc = cookie;
233453c3c4d0Sstsp 	const struct r92c_bb_prog *prog;
23359ec5c125Sdamien 	uint32_t reg;
233634a2cadeSkevlo 	uint8_t xtal;
23379ec5c125Sdamien 	int i;
23389ec5c125Sdamien 
23399ec5c125Sdamien 	/* Enable BB and RF. */
23409ec5c125Sdamien 	urtwn_write_2(sc, R92C_SYS_FUNC_EN,
23419ec5c125Sdamien 	    urtwn_read_2(sc, R92C_SYS_FUNC_EN) |
23429ec5c125Sdamien 	    R92C_SYS_FUNC_EN_BBRSTB | R92C_SYS_FUNC_EN_BB_GLB_RST |
23439ec5c125Sdamien 	    R92C_SYS_FUNC_EN_DIO_RF);
23449ec5c125Sdamien 
2345067851b1Skevlo 	if (!(sc->sc_sc.chip & (RTWN_CHIP_88E | RTWN_CHIP_88F | RTWN_CHIP_92E)))
23469ec5c125Sdamien 		urtwn_write_2(sc, R92C_AFE_PLL_CTRL, 0xdb83);
23479ec5c125Sdamien 
23489ec5c125Sdamien 	urtwn_write_1(sc, R92C_RF_CTRL,
23499ec5c125Sdamien 	    R92C_RF_CTRL_EN | R92C_RF_CTRL_RSTB | R92C_RF_CTRL_SDMRSTB);
23509ec5c125Sdamien 	urtwn_write_1(sc, R92C_SYS_FUNC_EN,
23519ec5c125Sdamien 	    R92C_SYS_FUNC_EN_USBA | R92C_SYS_FUNC_EN_USBD |
23529ec5c125Sdamien 	    R92C_SYS_FUNC_EN_BB_GLB_RST | R92C_SYS_FUNC_EN_BBRSTB);
23539ec5c125Sdamien 
2354067851b1Skevlo 	if (!(sc->sc_sc.chip &
2355067851b1Skevlo 	    (RTWN_CHIP_88E | RTWN_CHIP_88F | RTWN_CHIP_92E))) {
23569ec5c125Sdamien 		urtwn_write_1(sc, R92C_LDOHCI12_CTRL, 0x0f);
23579ec5c125Sdamien 		urtwn_write_1(sc, 0x15, 0xe9);
23589ec5c125Sdamien 		urtwn_write_1(sc, R92C_AFE_XTAL_CTRL + 1, 0x80);
2359c349b8bfSstsp 	}
23609ec5c125Sdamien 
23619ec5c125Sdamien 	/* Select BB programming based on board type. */
236223340becSstsp 	if (sc->sc_sc.chip & RTWN_CHIP_88E)
2363c349b8bfSstsp 		prog = &rtl8188eu_bb_prog;
2364067851b1Skevlo 	else if (sc->sc_sc.chip & RTWN_CHIP_88F)
2365067851b1Skevlo 		prog = &rtl8188ftv_bb_prog;
236690540544Sjmatthew 	else if (sc->sc_sc.chip & RTWN_CHIP_92E)
236790540544Sjmatthew 		prog = &rtl8192eu_bb_prog;
236823340becSstsp 	else if (!(sc->sc_sc.chip & RTWN_CHIP_92C)) {
236923340becSstsp 		if (sc->sc_sc.board_type == R92C_BOARD_TYPE_MINICARD)
23709ec5c125Sdamien 			prog = &rtl8188ce_bb_prog;
237123340becSstsp 		else if (sc->sc_sc.board_type == R92C_BOARD_TYPE_HIGHPA)
23729ec5c125Sdamien 			prog = &rtl8188ru_bb_prog;
23739ec5c125Sdamien 		else
23749ec5c125Sdamien 			prog = &rtl8188cu_bb_prog;
23759ec5c125Sdamien 	} else {
237623340becSstsp 		if (sc->sc_sc.board_type == R92C_BOARD_TYPE_MINICARD)
23779ec5c125Sdamien 			prog = &rtl8192ce_bb_prog;
23789ec5c125Sdamien 		else
23799ec5c125Sdamien 			prog = &rtl8192cu_bb_prog;
23809ec5c125Sdamien 	}
23819ec5c125Sdamien 	/* Write BB initialization values. */
23829ec5c125Sdamien 	for (i = 0; i < prog->count; i++) {
23839ec5c125Sdamien 		urtwn_bb_write(sc, prog->regs[i], prog->vals[i]);
23849ec5c125Sdamien 		DELAY(1);
23859ec5c125Sdamien 	}
23869ec5c125Sdamien 
238723340becSstsp 	if (sc->sc_sc.chip & RTWN_CHIP_92C_1T2R) {
23889ec5c125Sdamien 		/* 8192C 1T only configuration. */
23899ec5c125Sdamien 		reg = urtwn_bb_read(sc, R92C_FPGA0_TXINFO);
23909ec5c125Sdamien 		reg = (reg & ~0x00000003) | 0x2;
23919ec5c125Sdamien 		urtwn_bb_write(sc, R92C_FPGA0_TXINFO, reg);
23929ec5c125Sdamien 
23939ec5c125Sdamien 		reg = urtwn_bb_read(sc, R92C_FPGA1_TXINFO);
23949ec5c125Sdamien 		reg = (reg & ~0x00300033) | 0x00200022;
23959ec5c125Sdamien 		urtwn_bb_write(sc, R92C_FPGA1_TXINFO, reg);
23969ec5c125Sdamien 
23979ec5c125Sdamien 		reg = urtwn_bb_read(sc, R92C_CCK0_AFESETTING);
23989ec5c125Sdamien 		reg = (reg & ~0xff000000) | 0x45 << 24;
23999ec5c125Sdamien 		urtwn_bb_write(sc, R92C_CCK0_AFESETTING, reg);
24009ec5c125Sdamien 
24019ec5c125Sdamien 		reg = urtwn_bb_read(sc, R92C_OFDM0_TRXPATHENA);
24029ec5c125Sdamien 		reg = (reg & ~0x000000ff) | 0x23;
24039ec5c125Sdamien 		urtwn_bb_write(sc, R92C_OFDM0_TRXPATHENA, reg);
24049ec5c125Sdamien 
24059ec5c125Sdamien 		reg = urtwn_bb_read(sc, R92C_OFDM0_AGCPARAM1);
24069ec5c125Sdamien 		reg = (reg & ~0x00000030) | 1 << 4;
24079ec5c125Sdamien 		urtwn_bb_write(sc, R92C_OFDM0_AGCPARAM1, reg);
24089ec5c125Sdamien 
24099ec5c125Sdamien 		reg = urtwn_bb_read(sc, 0xe74);
24109ec5c125Sdamien 		reg = (reg & ~0x0c000000) | 2 << 26;
24119ec5c125Sdamien 		urtwn_bb_write(sc, 0xe74, reg);
24129ec5c125Sdamien 		reg = urtwn_bb_read(sc, 0xe78);
24139ec5c125Sdamien 		reg = (reg & ~0x0c000000) | 2 << 26;
24149ec5c125Sdamien 		urtwn_bb_write(sc, 0xe78, reg);
24159ec5c125Sdamien 		reg = urtwn_bb_read(sc, 0xe7c);
24169ec5c125Sdamien 		reg = (reg & ~0x0c000000) | 2 << 26;
24179ec5c125Sdamien 		urtwn_bb_write(sc, 0xe7c, reg);
24189ec5c125Sdamien 		reg = urtwn_bb_read(sc, 0xe80);
24199ec5c125Sdamien 		reg = (reg & ~0x0c000000) | 2 << 26;
24209ec5c125Sdamien 		urtwn_bb_write(sc, 0xe80, reg);
24219ec5c125Sdamien 		reg = urtwn_bb_read(sc, 0xe88);
24229ec5c125Sdamien 		reg = (reg & ~0x0c000000) | 2 << 26;
24239ec5c125Sdamien 		urtwn_bb_write(sc, 0xe88, reg);
24249ec5c125Sdamien 	}
24259ec5c125Sdamien 
24269ec5c125Sdamien 	/* Write AGC values. */
24279ec5c125Sdamien 	for (i = 0; i < prog->agccount; i++) {
24289ec5c125Sdamien 		urtwn_bb_write(sc, R92C_OFDM0_AGCRSSITABLE,
24299ec5c125Sdamien 		    prog->agcvals[i]);
24309ec5c125Sdamien 		DELAY(1);
24319ec5c125Sdamien 	}
24329ec5c125Sdamien 
2433067851b1Skevlo 	if (sc->sc_sc.chip & (RTWN_CHIP_88E | RTWN_CHIP_88F)) {
2434c349b8bfSstsp 		urtwn_bb_write(sc, R92C_OFDM0_AGCCORE1(0), 0x69553422);
2435c349b8bfSstsp 		DELAY(1);
2436c349b8bfSstsp 		urtwn_bb_write(sc, R92C_OFDM0_AGCCORE1(0), 0x69553420);
2437c349b8bfSstsp 		DELAY(1);
243890540544Sjmatthew 	} else if (sc->sc_sc.chip & RTWN_CHIP_92E) {
243990540544Sjmatthew 		urtwn_bb_write(sc, R92C_OFDM0_AGCCORE1(0), 0x00040022);
244090540544Sjmatthew 		DELAY(1);
244190540544Sjmatthew 		urtwn_bb_write(sc, R92C_OFDM0_AGCCORE1(0), 0x00040020);
244290540544Sjmatthew 		DELAY(1);
244390540544Sjmatthew 	}
2444c349b8bfSstsp 
2445067851b1Skevlo 	if (sc->sc_sc.chip & (RTWN_CHIP_88E | RTWN_CHIP_88F)) {
244634a2cadeSkevlo 		xtal = sc->sc_sc.crystal_cap & 0x3f;
2447c349b8bfSstsp 		reg = urtwn_bb_read(sc, R92C_AFE_XTAL_CTRL);
2448c349b8bfSstsp 		urtwn_bb_write(sc, R92C_AFE_XTAL_CTRL,
244934a2cadeSkevlo 		    RW(reg, R92C_AFE_XTAL_CTRL_ADDR, xtal | xtal << 6));
245090540544Sjmatthew 	} else if (sc->sc_sc.chip & RTWN_CHIP_92E) {
245190540544Sjmatthew 		xtal = sc->sc_sc.crystal_cap & 0x3f;
24528bca19d0Skevlo 		reg = urtwn_bb_read(sc, R92C_AFE_CTRL3);
24538bca19d0Skevlo 		urtwn_bb_write(sc, R92C_AFE_CTRL3,
24548bca19d0Skevlo 		    RW(reg, R92C_AFE_CTRL3_ADDR, xtal | xtal << 6));
245590540544Sjmatthew 		urtwn_write_4(sc, R92C_AFE_XTAL_CTRL, 0x000f81fb);
24569ec5c125Sdamien 	}
245723f3755dSstsp 
245823f3755dSstsp 	if (urtwn_bb_read(sc, R92C_HSSI_PARAM2(0)) & R92C_HSSI_PARAM2_CCK_HIPWR)
245923f3755dSstsp 		sc->sc_sc.sc_flags |= RTWN_FLAG_CCK_HIPWR;
24609ec5c125Sdamien }
24619ec5c125Sdamien 
24622d2a7e26Skevlo void
24632d2a7e26Skevlo urtwn_burstlen_init(struct urtwn_softc *sc)
24642d2a7e26Skevlo {
24652d2a7e26Skevlo 	uint8_t reg;
24662d2a7e26Skevlo 
24672d2a7e26Skevlo 	reg = urtwn_read_1(sc, R92E_RXDMA_PRO);
24682d2a7e26Skevlo 	reg &= ~0x30;
24692d2a7e26Skevlo 	switch (sc->sc_udev->speed) {
24702d2a7e26Skevlo 	case USB_SPEED_HIGH:
24712d2a7e26Skevlo 		urtwn_write_1(sc, R92E_RXDMA_PRO, reg | 0x1e);
24722d2a7e26Skevlo 		break;
24732d2a7e26Skevlo 	default:
24742d2a7e26Skevlo 		urtwn_write_1(sc, R92E_RXDMA_PRO, reg | 0x2e);
24752d2a7e26Skevlo 		break;
24762d2a7e26Skevlo 	}
2477067851b1Skevlo 
2478067851b1Skevlo 	if (sc->sc_sc.chip & RTWN_CHIP_88F) {
2479067851b1Skevlo 		/* Setup AMPDU aggregation. */
2480067851b1Skevlo 		urtwn_write_1(sc, R88F_HT_SINGLE_AMPDU,
2481067851b1Skevlo 		    urtwn_read_1(sc, R88F_HT_SINGLE_AMPDU) |
2482067851b1Skevlo 		    R88F_HT_SINGLE_AMPDU_EN);
2483067851b1Skevlo 		urtwn_write_2(sc, R92C_MAX_AGGR_NUM, 0x0c14);
2484067851b1Skevlo 		urtwn_write_1(sc, R88F_AMPDU_MAX_TIME, 0x70);
2485067851b1Skevlo 		urtwn_write_4(sc, R92C_AGGLEN_LMT, 0xffffffff);
2486067851b1Skevlo 
2487067851b1Skevlo 		/* For VHT packet length 11K */
2488067851b1Skevlo 		urtwn_write_1(sc, R88F_RX_PKT_LIMIT, 0x18);
2489067851b1Skevlo 
2490067851b1Skevlo 		urtwn_write_1(sc, R92C_PIFS, 0);
2491067851b1Skevlo 		urtwn_write_1(sc, R92C_FWHW_TXQ_CTRL, 0x80);
2492067851b1Skevlo 		urtwn_write_4(sc, R92C_FAST_EDCA_CTRL, 0x03086666);
2493067851b1Skevlo 		urtwn_write_1(sc, R92C_USTIME_TSF, 0x28);
2494067851b1Skevlo 		urtwn_write_1(sc, R88F_USTIME_EDCA, 0x28);
2495067851b1Skevlo 
249654fbbda3Sjsg 		/* To prevent bus resetting the mac. */
2497067851b1Skevlo 		urtwn_write_1(sc, R92C_RSV_CTRL,
2498067851b1Skevlo 		    urtwn_read_1(sc, R92C_RSV_CTRL) |
2499067851b1Skevlo 		    R92C_RSV_CTRL_R_DIS_PRST_0 | R92C_RSV_CTRL_R_DIS_PRST_1);
2500067851b1Skevlo 	}
25012d2a7e26Skevlo }
25022d2a7e26Skevlo 
25039ec5c125Sdamien int
250423340becSstsp urtwn_power_on(void *cookie)
25059ec5c125Sdamien {
250623340becSstsp 	struct urtwn_softc *sc = cookie;
25079ec5c125Sdamien 
250823340becSstsp 	if (sc->sc_sc.chip & RTWN_CHIP_88E)
250934a2cadeSkevlo 		return (urtwn_r88e_power_on(sc));
2510067851b1Skevlo 	else if (sc->sc_sc.chip & RTWN_CHIP_88F)
2511067851b1Skevlo 		return (urtwn_r88f_power_on(sc));
251290540544Sjmatthew 	else if (sc->sc_sc.chip & RTWN_CHIP_92E)
251390540544Sjmatthew 		return (urtwn_r92e_power_on(sc));
25149ec5c125Sdamien 
251534a2cadeSkevlo 	return (urtwn_r92c_power_on(sc));
25165e709a81Sdamien }
25175e709a81Sdamien 
25189ec5c125Sdamien int
251923340becSstsp urtwn_alloc_buffers(void *cookie)
25209ec5c125Sdamien {
252123340becSstsp 	struct urtwn_softc *sc = cookie;
252223340becSstsp 	int error;
25239ec5c125Sdamien 
25249ec5c125Sdamien 	/* Init host async commands ring. */
25259ec5c125Sdamien 	sc->cmdq.cur = sc->cmdq.next = sc->cmdq.queued = 0;
25269ec5c125Sdamien 
25279ec5c125Sdamien 	/* Allocate Tx/Rx buffers. */
25289ec5c125Sdamien 	error = urtwn_alloc_rx_list(sc);
25299ec5c125Sdamien 	if (error != 0) {
25309ec5c125Sdamien 		printf("%s: could not allocate Rx buffers\n",
25319ec5c125Sdamien 		    sc->sc_dev.dv_xname);
253223340becSstsp 		return (error);
25339ec5c125Sdamien 	}
25349ec5c125Sdamien 	error = urtwn_alloc_tx_list(sc);
25359ec5c125Sdamien 	if (error != 0) {
25369ec5c125Sdamien 		printf("%s: could not allocate Tx buffers\n",
25379ec5c125Sdamien 		    sc->sc_dev.dv_xname);
253823340becSstsp 		return (error);
25399ec5c125Sdamien 	}
2540c349b8bfSstsp 
254123340becSstsp 	return (0);
2542c349b8bfSstsp }
25439ec5c125Sdamien 
254423340becSstsp int
254523340becSstsp urtwn_init(void *cookie)
254623340becSstsp {
254723340becSstsp 	struct urtwn_softc *sc = cookie;
254823340becSstsp 	int i, error;
25499ec5c125Sdamien 
25508bca19d0Skevlo 	/* Reset USB mode switch setting. */
255190540544Sjmatthew 	if (sc->sc_sc.chip & RTWN_CHIP_92E)
255290540544Sjmatthew 		urtwn_write_1(sc, R92C_ACLK_MON, 0);
255390540544Sjmatthew 
25549ec5c125Sdamien 	/* Queue Rx xfers. */
25559ec5c125Sdamien 	for (i = 0; i < URTWN_RX_LIST_COUNT; i++) {
255623340becSstsp 		struct urtwn_rx_data *data = &sc->rx_data[i];
25579ec5c125Sdamien 
25589ec5c125Sdamien 		usbd_setup_xfer(data->xfer, sc->rx_pipe, data, data->buf,
25599ec5c125Sdamien 		    URTWN_RXBUFSZ, USBD_SHORT_XFER_OK | USBD_NO_COPY,
25609ec5c125Sdamien 		    USBD_NO_TIMEOUT, urtwn_rxeof);
25619ec5c125Sdamien 		error = usbd_transfer(data->xfer);
25629ec5c125Sdamien 		if (error != 0 && error != USBD_IN_PROGRESS)
25639ec5c125Sdamien 			return (error);
25649ec5c125Sdamien 	}
25659ec5c125Sdamien 
2566a087613eSstsp 	ieee80211_amrr_node_init(&sc->amrr, &sc->amn);
2567a087613eSstsp 
2568a087613eSstsp 	/*
2569a087613eSstsp 	 * Enable TX reports for AMRR.
2570a087613eSstsp 	 * In order to get reports we need to explicitly reset the register.
2571a087613eSstsp 	 */
2572a087613eSstsp 	if (sc->sc_sc.chip & RTWN_CHIP_88E)
2573a087613eSstsp 		urtwn_write_1(sc, R88E_TX_RPT_CTRL, (urtwn_read_1(sc,
25742f5d27e0Skevlo 		    R88E_TX_RPT_CTRL) & ~0) | R88E_TX_RPT_CTRL_EN);
2575a087613eSstsp 
257623340becSstsp 	return (0);
257723340becSstsp }
257823340becSstsp 
25799ec5c125Sdamien void
258023340becSstsp urtwn_stop(void *cookie)
25819ec5c125Sdamien {
258223340becSstsp 	struct urtwn_softc *sc = cookie;
258323340becSstsp 	int i;
25849ec5c125Sdamien 
25859ec5c125Sdamien 	/* Abort Tx. */
25869ec5c125Sdamien 	for (i = 0; i < R92C_MAX_EPOUT; i++) {
25879ec5c125Sdamien 		if (sc->tx_pipe[i] != NULL)
25889ec5c125Sdamien 			usbd_abort_pipe(sc->tx_pipe[i]);
25899ec5c125Sdamien 	}
25909ec5c125Sdamien 	/* Stop Rx pipe. */
25919ec5c125Sdamien 	usbd_abort_pipe(sc->rx_pipe);
25929ec5c125Sdamien 	/* Free Tx/Rx buffers. */
25939ec5c125Sdamien 	urtwn_free_tx_list(sc);
25949ec5c125Sdamien 	urtwn_free_rx_list(sc);
25959ec5c125Sdamien }
259623340becSstsp 
259723340becSstsp int
259823340becSstsp urtwn_is_oactive(void *cookie)
259923340becSstsp {
260023340becSstsp 	struct urtwn_softc *sc = cookie;
260123340becSstsp 
260223340becSstsp 	return (TAILQ_EMPTY(&sc->tx_free_list));
260323340becSstsp }
2604