xref: /dflybsd-src/sys/dev/netif/iwi/if_iwi.c (revision 05d02a3813e2bef176c69d68035311fd2efbd031)
10e474d75SJohannes Hofmann /*-
2b50e4759SMatthew Dillon  * Copyright (c) 2004, 2005
30e474d75SJohannes Hofmann  *      Damien Bergamini <damien.bergamini@free.fr>. All rights reserved.
40e474d75SJohannes Hofmann  * Copyright (c) 2005-2006 Sam Leffler, Errno Consulting
50e474d75SJohannes Hofmann  * Copyright (c) 2007 Andrew Thompson <thompsa@FreeBSD.org>
6b50e4759SMatthew Dillon  *
7b50e4759SMatthew Dillon  * Redistribution and use in source and binary forms, with or without
8b50e4759SMatthew Dillon  * modification, are permitted provided that the following conditions
9b50e4759SMatthew Dillon  * are met:
10b50e4759SMatthew Dillon  * 1. Redistributions of source code must retain the above copyright
11b50e4759SMatthew Dillon  *    notice unmodified, this list of conditions, and the following
12b50e4759SMatthew Dillon  *    disclaimer.
13b50e4759SMatthew Dillon  * 2. Redistributions in binary form must reproduce the above copyright
14b50e4759SMatthew Dillon  *    notice, this list of conditions and the following disclaimer in the
15b50e4759SMatthew Dillon  *    documentation and/or other materials provided with the distribution.
16b50e4759SMatthew Dillon  *
17b50e4759SMatthew Dillon  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18b50e4759SMatthew Dillon  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19b50e4759SMatthew Dillon  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20b50e4759SMatthew Dillon  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21b50e4759SMatthew Dillon  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22b50e4759SMatthew Dillon  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23b50e4759SMatthew Dillon  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24b50e4759SMatthew Dillon  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25b50e4759SMatthew Dillon  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26b50e4759SMatthew Dillon  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27b50e4759SMatthew Dillon  * SUCH DAMAGE.
28b50e4759SMatthew Dillon  *
29c4fe7bb1SImre Vadász  * $FreeBSD: head/sys/dev/iwi/if_iwi.c 298818 2016-04-29 22:14:11Z avos $
30b50e4759SMatthew Dillon  */
31b50e4759SMatthew Dillon 
32b50e4759SMatthew Dillon /*-
33841ab66cSSepherosa Ziehau  * Intel(R) PRO/Wireless 2200BG/2225BG/2915ABG driver
34b50e4759SMatthew Dillon  * http://www.intel.com/network/connectivity/products/wireless/prowireless_mobile.htm
35b50e4759SMatthew Dillon  */
36b50e4759SMatthew Dillon 
37b50e4759SMatthew Dillon #include <sys/param.h>
38b50e4759SMatthew Dillon #include <sys/sysctl.h>
39b50e4759SMatthew Dillon #include <sys/sockio.h>
40b50e4759SMatthew Dillon #include <sys/mbuf.h>
41b50e4759SMatthew Dillon #include <sys/kernel.h>
42b50e4759SMatthew Dillon #include <sys/socket.h>
43b50e4759SMatthew Dillon #include <sys/systm.h>
44b50e4759SMatthew Dillon #include <sys/malloc.h>
450e474d75SJohannes Hofmann #include <sys/lock.h>
46b50e4759SMatthew Dillon #include <sys/module.h>
470e474d75SJohannes Hofmann #include <sys/bus.h>
48b50e4759SMatthew Dillon #include <sys/endian.h>
49b50e4759SMatthew Dillon #include <sys/proc.h>
500e474d75SJohannes Hofmann #include <sys/mount.h>
510e474d75SJohannes Hofmann #include <sys/linker.h>
520e474d75SJohannes Hofmann #include <sys/firmware.h>
530e474d75SJohannes Hofmann #include <sys/taskqueue.h>
54c4fe7bb1SImre Vadász #if defined(__DragonFly__)
550e474d75SJohannes Hofmann #include <sys/devfs.h>
56c4fe7bb1SImre Vadász #endif
57b50e4759SMatthew Dillon 
58c4fe7bb1SImre Vadász #if !defined(__DragonFly__)
59c4fe7bb1SImre Vadász #include <machine/bus.h>
60c4fe7bb1SImre Vadász #include <machine/resource.h>
61c4fe7bb1SImre Vadász #endif
620e474d75SJohannes Hofmann #include <sys/rman.h>
630e474d75SJohannes Hofmann 
64c4fe7bb1SImre Vadász #if defined(__DragonFly__)
65b50e4759SMatthew Dillon #include <bus/pci/pcivar.h>
66c4fe7bb1SImre Vadász #include <bus/pci/pcireg.h>
67c4fe7bb1SImre Vadász #else
68c4fe7bb1SImre Vadász #include <dev/pci/pcireg.h>
69c4fe7bb1SImre Vadász #include <dev/pci/pcivar.h>
70c4fe7bb1SImre Vadász #endif
71b50e4759SMatthew Dillon 
72b50e4759SMatthew Dillon #include <net/bpf.h>
73b50e4759SMatthew Dillon #include <net/if.h>
74c4fe7bb1SImre Vadász #include <net/if_var.h>
75b50e4759SMatthew Dillon #include <net/if_arp.h>
76b50e4759SMatthew Dillon #include <net/ethernet.h>
77b50e4759SMatthew Dillon #include <net/if_dl.h>
78b50e4759SMatthew Dillon #include <net/if_media.h>
79b50e4759SMatthew Dillon #include <net/if_types.h>
80b50e4759SMatthew Dillon 
81c4fe7bb1SImre Vadász #if defined(__DragonFly__)
82841ab66cSSepherosa Ziehau #include <netproto/802_11/ieee80211_var.h>
83841ab66cSSepherosa Ziehau #include <netproto/802_11/ieee80211_radiotap.h>
840e474d75SJohannes Hofmann #include <netproto/802_11/ieee80211_input.h>
850e474d75SJohannes Hofmann #include <netproto/802_11/ieee80211_regdomain.h>
86c4fe7bb1SImre Vadász #else
87c4fe7bb1SImre Vadász #include <net80211/ieee80211_var.h>
88c4fe7bb1SImre Vadász #include <net80211/ieee80211_radiotap.h>
89c4fe7bb1SImre Vadász #include <net80211/ieee80211_input.h>
90c4fe7bb1SImre Vadász #include <net80211/ieee80211_regdomain.h>
91c4fe7bb1SImre Vadász #endif
92841ab66cSSepherosa Ziehau 
93b50e4759SMatthew Dillon #include <netinet/in.h>
94b50e4759SMatthew Dillon #include <netinet/in_systm.h>
95b50e4759SMatthew Dillon #include <netinet/in_var.h>
96b50e4759SMatthew Dillon #include <netinet/ip.h>
97b50e4759SMatthew Dillon #include <netinet/if_ether.h>
98b50e4759SMatthew Dillon 
99c4fe7bb1SImre Vadász #if defined(__DragonFly__)
100c4fe7bb1SImre Vadász #include "if_iwireg.h"
101c4fe7bb1SImre Vadász #include "if_iwivar.h"
102c4fe7bb1SImre Vadász #else
103c4fe7bb1SImre Vadász #include <dev/iwi/if_iwireg.h>
104c4fe7bb1SImre Vadász #include <dev/iwi/if_iwivar.h>
105c4fe7bb1SImre Vadász #endif
106b50e4759SMatthew Dillon 
1070e474d75SJohannes Hofmann #define IWI_DEBUG
108b50e4759SMatthew Dillon #ifdef IWI_DEBUG
109e3869ec7SSascha Wildner #define DPRINTF(x)	do { if (iwi_debug > 0) kprintf x; } while (0)
110e3869ec7SSascha Wildner #define DPRINTFN(n, x)	do { if (iwi_debug >= (n)) kprintf x; } while (0)
111841ab66cSSepherosa Ziehau int iwi_debug = 0;
112841ab66cSSepherosa Ziehau SYSCTL_INT(_debug, OID_AUTO, iwi, CTLFLAG_RW, &iwi_debug, 0, "iwi debug level");
1130e474d75SJohannes Hofmann 
1140e474d75SJohannes Hofmann static const char *iwi_fw_states[] = {
1150e474d75SJohannes Hofmann 	"IDLE", 		/* IWI_FW_IDLE */
1160e474d75SJohannes Hofmann 	"LOADING",		/* IWI_FW_LOADING */
1170e474d75SJohannes Hofmann 	"ASSOCIATING",		/* IWI_FW_ASSOCIATING */
1180e474d75SJohannes Hofmann 	"DISASSOCIATING",	/* IWI_FW_DISASSOCIATING */
1190e474d75SJohannes Hofmann 	"SCANNING",		/* IWI_FW_SCANNING */
1200e474d75SJohannes Hofmann };
121b50e4759SMatthew Dillon #else
122b50e4759SMatthew Dillon #define DPRINTF(x)
123b50e4759SMatthew Dillon #define DPRINTFN(n, x)
124b50e4759SMatthew Dillon #endif
125b50e4759SMatthew Dillon 
1260e474d75SJohannes Hofmann MODULE_DEPEND(iwi, pci,  1, 1, 1);
1270e474d75SJohannes Hofmann MODULE_DEPEND(iwi, wlan, 1, 1, 1);
1280e474d75SJohannes Hofmann MODULE_DEPEND(iwi, firmware, 1, 1, 1);
1290e474d75SJohannes Hofmann 
1300e474d75SJohannes Hofmann enum {
1310e474d75SJohannes Hofmann 	IWI_LED_TX,
1320e474d75SJohannes Hofmann 	IWI_LED_RX,
1330e474d75SJohannes Hofmann 	IWI_LED_POLL,
1340e474d75SJohannes Hofmann };
1350e474d75SJohannes Hofmann 
1360e474d75SJohannes Hofmann struct iwi_ident {
137841ab66cSSepherosa Ziehau 	uint16_t	vendor;
138841ab66cSSepherosa Ziehau 	uint16_t	device;
139b50e4759SMatthew Dillon 	const char	*name;
1400e474d75SJohannes Hofmann };
1410e474d75SJohannes Hofmann 
1420e474d75SJohannes Hofmann static const struct iwi_ident iwi_ident_table[] = {
1430e474d75SJohannes Hofmann 	{ 0x8086, 0x4220, "Intel(R) PRO/Wireless 2200BG" },
1440e474d75SJohannes Hofmann 	{ 0x8086, 0x4221, "Intel(R) PRO/Wireless 2225BG" },
1450e474d75SJohannes Hofmann 	{ 0x8086, 0x4223, "Intel(R) PRO/Wireless 2915ABG" },
1460e474d75SJohannes Hofmann 	{ 0x8086, 0x4224, "Intel(R) PRO/Wireless 2915ABG" },
1470e474d75SJohannes Hofmann 
148b50e4759SMatthew Dillon 	{ 0, 0, NULL }
149b50e4759SMatthew Dillon };
150b50e4759SMatthew Dillon 
1510e474d75SJohannes Hofmann static struct ieee80211vap *iwi_vap_create(struct ieee80211com *,
152c4fe7bb1SImre Vadász 		    const char [IFNAMSIZ], int, enum ieee80211_opmode, int,
153c4fe7bb1SImre Vadász 		    const uint8_t [IEEE80211_ADDR_LEN],
154c4fe7bb1SImre Vadász 		    const uint8_t [IEEE80211_ADDR_LEN]);
1550e474d75SJohannes Hofmann static void	iwi_vap_delete(struct ieee80211vap *);
156841ab66cSSepherosa Ziehau static void	iwi_dma_map_addr(void *, bus_dma_segment_t *, int, int);
157841ab66cSSepherosa Ziehau static int	iwi_alloc_cmd_ring(struct iwi_softc *, struct iwi_cmd_ring *,
158841ab66cSSepherosa Ziehau 		    int);
159841ab66cSSepherosa Ziehau static void	iwi_reset_cmd_ring(struct iwi_softc *, struct iwi_cmd_ring *);
160841ab66cSSepherosa Ziehau static void	iwi_free_cmd_ring(struct iwi_softc *, struct iwi_cmd_ring *);
161841ab66cSSepherosa Ziehau static int	iwi_alloc_tx_ring(struct iwi_softc *, struct iwi_tx_ring *,
162841ab66cSSepherosa Ziehau 		    int, bus_addr_t, bus_addr_t);
163841ab66cSSepherosa Ziehau static void	iwi_reset_tx_ring(struct iwi_softc *, struct iwi_tx_ring *);
164841ab66cSSepherosa Ziehau static void	iwi_free_tx_ring(struct iwi_softc *, struct iwi_tx_ring *);
165841ab66cSSepherosa Ziehau static int	iwi_alloc_rx_ring(struct iwi_softc *, struct iwi_rx_ring *,
166841ab66cSSepherosa Ziehau 		    int);
167841ab66cSSepherosa Ziehau static void	iwi_reset_rx_ring(struct iwi_softc *, struct iwi_rx_ring *);
168841ab66cSSepherosa Ziehau static void	iwi_free_rx_ring(struct iwi_softc *, struct iwi_rx_ring *);
1690e474d75SJohannes Hofmann static struct ieee80211_node *iwi_node_alloc(struct ieee80211vap *,
1700e474d75SJohannes Hofmann 		    const uint8_t [IEEE80211_ADDR_LEN]);
171841ab66cSSepherosa Ziehau static void	iwi_node_free(struct ieee80211_node *);
172b50e4759SMatthew Dillon static void	iwi_media_status(struct ifnet *, struct ifmediareq *);
1730e474d75SJohannes Hofmann static int	iwi_newstate(struct ieee80211vap *, enum ieee80211_state, int);
1740e474d75SJohannes Hofmann static void	iwi_wme_init(struct iwi_softc *);
175c4fe7bb1SImre Vadász static int	iwi_wme_setparams(struct iwi_softc *);
176841ab66cSSepherosa Ziehau static int	iwi_wme_update(struct ieee80211com *);
177841ab66cSSepherosa Ziehau static uint16_t	iwi_read_prom_word(struct iwi_softc *, uint8_t);
178841ab66cSSepherosa Ziehau static void	iwi_frame_intr(struct iwi_softc *, struct iwi_rx_data *, int,
179841ab66cSSepherosa Ziehau 		    struct iwi_frame *);
180841ab66cSSepherosa Ziehau static void	iwi_notification_intr(struct iwi_softc *, struct iwi_notif *);
181b50e4759SMatthew Dillon static void	iwi_rx_intr(struct iwi_softc *);
182841ab66cSSepherosa Ziehau static void	iwi_tx_intr(struct iwi_softc *, struct iwi_tx_ring *);
183b50e4759SMatthew Dillon static void	iwi_intr(void *);
1840e474d75SJohannes Hofmann static int	iwi_cmd(struct iwi_softc *, uint8_t, void *, uint8_t);
1850e474d75SJohannes Hofmann static void	iwi_write_ibssnode(struct iwi_softc *, const u_int8_t [], int);
186c4fe7bb1SImre Vadász static int	iwi_tx_start(struct iwi_softc *, struct mbuf *,
187841ab66cSSepherosa Ziehau 		    struct ieee80211_node *, int);
1880e474d75SJohannes Hofmann static int	iwi_raw_xmit(struct ieee80211_node *, struct mbuf *,
1890e474d75SJohannes Hofmann 		    const struct ieee80211_bpf_params *);
190c4fe7bb1SImre Vadász static void	iwi_start(struct iwi_softc *);
191c4fe7bb1SImre Vadász static int	iwi_transmit(struct ieee80211com *, struct mbuf *);
1920e474d75SJohannes Hofmann static void	iwi_watchdog(void *);
193c4fe7bb1SImre Vadász static void	iwi_parent(struct ieee80211com *);
194b50e4759SMatthew Dillon static void	iwi_stop_master(struct iwi_softc *);
195b50e4759SMatthew Dillon static int	iwi_reset(struct iwi_softc *);
1960e474d75SJohannes Hofmann static int	iwi_load_ucode(struct iwi_softc *, const struct iwi_fw *);
1970e474d75SJohannes Hofmann static int	iwi_load_firmware(struct iwi_softc *, const struct iwi_fw *);
1980e474d75SJohannes Hofmann static void	iwi_release_fw_dma(struct iwi_softc *sc);
199b50e4759SMatthew Dillon static int	iwi_config(struct iwi_softc *);
2000e474d75SJohannes Hofmann static int	iwi_get_firmware(struct iwi_softc *, enum ieee80211_opmode);
2010e474d75SJohannes Hofmann static void	iwi_put_firmware(struct iwi_softc *);
202c4fe7bb1SImre Vadász static void	iwi_monitor_scan(void *, int);
2030e474d75SJohannes Hofmann static int	iwi_scanchan(struct iwi_softc *, unsigned long, int);
2040e474d75SJohannes Hofmann static void	iwi_scan_start(struct ieee80211com *);
2050e474d75SJohannes Hofmann static void	iwi_scan_end(struct ieee80211com *);
2060e474d75SJohannes Hofmann static void	iwi_set_channel(struct ieee80211com *);
2070e474d75SJohannes Hofmann static void	iwi_scan_curchan(struct ieee80211_scan_state *, unsigned long maxdwell);
2080e474d75SJohannes Hofmann static void	iwi_scan_mindwell(struct ieee80211_scan_state *);
2090e474d75SJohannes Hofmann static int	iwi_auth_and_assoc(struct iwi_softc *, struct ieee80211vap *);
210c4fe7bb1SImre Vadász static void	iwi_disassoc(void *, int);
2110e474d75SJohannes Hofmann static int	iwi_disassociate(struct iwi_softc *, int quiet);
2120e474d75SJohannes Hofmann static void	iwi_init_locked(struct iwi_softc *);
213b50e4759SMatthew Dillon static void	iwi_init(void *);
2140e474d75SJohannes Hofmann static int	iwi_init_fw_dma(struct iwi_softc *, int);
2150e474d75SJohannes Hofmann static void	iwi_stop_locked(void *);
2160e474d75SJohannes Hofmann static void	iwi_stop(struct iwi_softc *);
217c4fe7bb1SImre Vadász static void	iwi_restart(void *, int);
2180e474d75SJohannes Hofmann static int	iwi_getrfkill(struct iwi_softc *);
219c4fe7bb1SImre Vadász static void	iwi_radio_on(void *, int);
220c4fe7bb1SImre Vadász static void	iwi_radio_off(void *, int);
2210e474d75SJohannes Hofmann static void	iwi_sysctlattach(struct iwi_softc *);
2220e474d75SJohannes Hofmann static void	iwi_led_event(struct iwi_softc *, int);
2230e474d75SJohannes Hofmann static void	iwi_ledattach(struct iwi_softc *);
224b50e4759SMatthew Dillon 
225b50e4759SMatthew Dillon static int iwi_probe(device_t);
226b50e4759SMatthew Dillon static int iwi_attach(device_t);
227b50e4759SMatthew Dillon static int iwi_detach(device_t);
228b50e4759SMatthew Dillon static int iwi_shutdown(device_t);
229b50e4759SMatthew Dillon static int iwi_suspend(device_t);
230b50e4759SMatthew Dillon static int iwi_resume(device_t);
231b50e4759SMatthew Dillon 
232b50e4759SMatthew Dillon static device_method_t iwi_methods[] = {
233b50e4759SMatthew Dillon 	/* Device interface */
234b50e4759SMatthew Dillon 	DEVMETHOD(device_probe,		iwi_probe),
235b50e4759SMatthew Dillon 	DEVMETHOD(device_attach,	iwi_attach),
236b50e4759SMatthew Dillon 	DEVMETHOD(device_detach,	iwi_detach),
237b50e4759SMatthew Dillon 	DEVMETHOD(device_shutdown,	iwi_shutdown),
238b50e4759SMatthew Dillon 	DEVMETHOD(device_suspend,	iwi_suspend),
239b50e4759SMatthew Dillon 	DEVMETHOD(device_resume,	iwi_resume),
240b50e4759SMatthew Dillon 
241d3c9c58eSSascha Wildner 	DEVMETHOD_END
242b50e4759SMatthew Dillon };
243b50e4759SMatthew Dillon 
244b50e4759SMatthew Dillon static driver_t iwi_driver = {
245b50e4759SMatthew Dillon 	"iwi",
246b50e4759SMatthew Dillon 	iwi_methods,
247841ab66cSSepherosa Ziehau 	sizeof (struct iwi_softc)
248b50e4759SMatthew Dillon };
249b50e4759SMatthew Dillon 
250b50e4759SMatthew Dillon static devclass_t iwi_devclass;
251b50e4759SMatthew Dillon 
252aa2b9d05SSascha Wildner DRIVER_MODULE(iwi, pci, iwi_driver, iwi_devclass, NULL, NULL);
253b50e4759SMatthew Dillon 
254c4fe7bb1SImre Vadász MODULE_VERSION(iwi, 1);
255c4fe7bb1SImre Vadász 
2560e474d75SJohannes Hofmann static __inline uint8_t
MEM_READ_1(struct iwi_softc * sc,uint32_t addr)2570e474d75SJohannes Hofmann MEM_READ_1(struct iwi_softc *sc, uint32_t addr)
2583660cb22SSepherosa Ziehau {
2590e474d75SJohannes Hofmann 	CSR_WRITE_4(sc, IWI_CSR_INDIRECT_ADDR, addr);
2600e474d75SJohannes Hofmann 	return CSR_READ_1(sc, IWI_CSR_INDIRECT_DATA);
2613660cb22SSepherosa Ziehau }
2623660cb22SSepherosa Ziehau 
2630e474d75SJohannes Hofmann static __inline uint32_t
MEM_READ_4(struct iwi_softc * sc,uint32_t addr)2640e474d75SJohannes Hofmann MEM_READ_4(struct iwi_softc *sc, uint32_t addr)
2650e474d75SJohannes Hofmann {
2660e474d75SJohannes Hofmann 	CSR_WRITE_4(sc, IWI_CSR_INDIRECT_ADDR, addr);
2670e474d75SJohannes Hofmann 	return CSR_READ_4(sc, IWI_CSR_INDIRECT_DATA);
2683660cb22SSepherosa Ziehau }
2693660cb22SSepherosa Ziehau 
270b50e4759SMatthew Dillon static int
iwi_probe(device_t dev)271b50e4759SMatthew Dillon iwi_probe(device_t dev)
272b50e4759SMatthew Dillon {
273b50e4759SMatthew Dillon 	const struct iwi_ident *ident;
274b50e4759SMatthew Dillon 
275b50e4759SMatthew Dillon 	for (ident = iwi_ident_table; ident->name != NULL; ident++) {
2760e474d75SJohannes Hofmann 		if (pci_get_vendor(dev) == ident->vendor &&
2770e474d75SJohannes Hofmann 		    pci_get_device(dev) == ident->device) {
278b50e4759SMatthew Dillon 			device_set_desc(dev, ident->name);
279c4fe7bb1SImre Vadász 			return (BUS_PROBE_DEFAULT);
280b50e4759SMatthew Dillon 		}
281b50e4759SMatthew Dillon 	}
282b50e4759SMatthew Dillon 	return ENXIO;
283b50e4759SMatthew Dillon }
284b50e4759SMatthew Dillon 
285b50e4759SMatthew Dillon static int
iwi_attach(device_t dev)286b50e4759SMatthew Dillon iwi_attach(device_t dev)
287b50e4759SMatthew Dillon {
288b50e4759SMatthew Dillon 	struct iwi_softc *sc = device_get_softc(dev);
289c4fe7bb1SImre Vadász 	struct ieee80211com *ic = &sc->sc_ic;
290841ab66cSSepherosa Ziehau 	uint16_t val;
291c4fe7bb1SImre Vadász 	uint8_t bands[IEEE80211_MODE_BYTES];
2920e474d75SJohannes Hofmann 	int i, error;
293ef634573SJoe Talbott 
294b50e4759SMatthew Dillon 	sc->sc_dev = dev;
2950e474d75SJohannes Hofmann 
296c4fe7bb1SImre Vadász 	IWI_LOCK_INIT(sc);
297c4fe7bb1SImre Vadász 	mbufq_init(&sc->sc_snd, ifqmaxlen);
2980e474d75SJohannes Hofmann 
299c4fe7bb1SImre Vadász #if defined(__DragonFly__)
3000e474d75SJohannes Hofmann 	devfs_clone_bitmap_init(&sc->sc_unr);
301c4fe7bb1SImre Vadász #else
302c4fe7bb1SImre Vadász 	sc->sc_unr = new_unrhdr(1, IWI_MAX_IBSSNODE-1, &sc->sc_mtx);
303c4fe7bb1SImre Vadász #endif
3040e474d75SJohannes Hofmann 
305c4fe7bb1SImre Vadász 	TASK_INIT(&sc->sc_radiontask, 0, iwi_radio_on, sc);
306c4fe7bb1SImre Vadász 	TASK_INIT(&sc->sc_radiofftask, 0, iwi_radio_off, sc);
307c4fe7bb1SImre Vadász 	TASK_INIT(&sc->sc_restarttask, 0, iwi_restart, sc);
308c4fe7bb1SImre Vadász 	TASK_INIT(&sc->sc_disassoctask, 0, iwi_disassoc, sc);
309c4fe7bb1SImre Vadász 	TASK_INIT(&sc->sc_monitortask, 0, iwi_monitor_scan, sc);
3100e474d75SJohannes Hofmann 
311c4fe7bb1SImre Vadász #if defined(__DragonFly__)
312c4fe7bb1SImre Vadász 	callout_init_lk(&sc->sc_wdtimer, &sc->sc_lock);
313c4fe7bb1SImre Vadász 	callout_init_lk(&sc->sc_rftimer, &sc->sc_lock);
314c4fe7bb1SImre Vadász #else
315c4fe7bb1SImre Vadász 	callout_init_mtx(&sc->sc_wdtimer, &sc->sc_mtx, 0);
316c4fe7bb1SImre Vadász 	callout_init_mtx(&sc->sc_rftimer, &sc->sc_mtx, 0);
317c4fe7bb1SImre Vadász #endif
318b50e4759SMatthew Dillon 
319b50e4759SMatthew Dillon 	pci_write_config(dev, 0x41, 0, 1);
320b50e4759SMatthew Dillon 
321b50e4759SMatthew Dillon 	/* enable bus-mastering */
322b50e4759SMatthew Dillon 	pci_enable_busmaster(dev);
323b50e4759SMatthew Dillon 
324c4fe7bb1SImre Vadász 	i = PCIR_BAR(0);
325c4fe7bb1SImre Vadász 	sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &i, RF_ACTIVE);
326b50e4759SMatthew Dillon 	if (sc->mem == NULL) {
327b50e4759SMatthew Dillon 		device_printf(dev, "could not allocate memory resource\n");
3280e474d75SJohannes Hofmann 		goto fail;
329b50e4759SMatthew Dillon 	}
3300e474d75SJohannes Hofmann 
331b50e4759SMatthew Dillon 	sc->sc_st = rman_get_bustag(sc->mem);
332b50e4759SMatthew Dillon 	sc->sc_sh = rman_get_bushandle(sc->mem);
333b50e4759SMatthew Dillon 
334c4fe7bb1SImre Vadász 	i = 0;
335c4fe7bb1SImre Vadász 	sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &i,
336841ab66cSSepherosa Ziehau 	    RF_ACTIVE | RF_SHAREABLE);
337b50e4759SMatthew Dillon 	if (sc->irq == NULL) {
338b50e4759SMatthew Dillon 		device_printf(dev, "could not allocate interrupt resource\n");
339b50e4759SMatthew Dillon 		goto fail;
340b50e4759SMatthew Dillon 	}
341b50e4759SMatthew Dillon 
342841ab66cSSepherosa Ziehau 	if (iwi_reset(sc) != 0) {
343b50e4759SMatthew Dillon 		device_printf(dev, "could not reset adapter\n");
344b50e4759SMatthew Dillon 		goto fail;
345b50e4759SMatthew Dillon 	}
346b50e4759SMatthew Dillon 
347841ab66cSSepherosa Ziehau 	/*
348841ab66cSSepherosa Ziehau 	 * Allocate rings.
349841ab66cSSepherosa Ziehau 	 */
3500e474d75SJohannes Hofmann 	if (iwi_alloc_cmd_ring(sc, &sc->cmdq, IWI_CMD_RING_COUNT) != 0) {
351841ab66cSSepherosa Ziehau 		device_printf(dev, "could not allocate Cmd ring\n");
352b50e4759SMatthew Dillon 		goto fail;
353b50e4759SMatthew Dillon 	}
354b50e4759SMatthew Dillon 
3550e474d75SJohannes Hofmann 	for (i = 0; i < 4; i++) {
3560e474d75SJohannes Hofmann 		error = iwi_alloc_tx_ring(sc, &sc->txq[i], IWI_TX_RING_COUNT,
3570e474d75SJohannes Hofmann 		    IWI_CSR_TX1_RIDX + i * 4,
3580e474d75SJohannes Hofmann 		    IWI_CSR_TX1_WIDX + i * 4);
359841ab66cSSepherosa Ziehau 		if (error != 0) {
3600e474d75SJohannes Hofmann 			device_printf(dev, "could not allocate Tx ring %d\n",
3610e474d75SJohannes Hofmann 				i+i);
362841ab66cSSepherosa Ziehau 			goto fail;
363841ab66cSSepherosa Ziehau 		}
364841ab66cSSepherosa Ziehau 	}
365841ab66cSSepherosa Ziehau 
3660e474d75SJohannes Hofmann 	if (iwi_alloc_rx_ring(sc, &sc->rxq, IWI_RX_RING_COUNT) != 0) {
367841ab66cSSepherosa Ziehau 		device_printf(dev, "could not allocate Rx ring\n");
368841ab66cSSepherosa Ziehau 		goto fail;
369841ab66cSSepherosa Ziehau 	}
370841ab66cSSepherosa Ziehau 
3710e474d75SJohannes Hofmann 	iwi_wme_init(sc);
372841ab66cSSepherosa Ziehau 
37313f98998SMatthew Dillon 	ic->ic_softc = sc;
3744f898719SImre Vadász 	ic->ic_name = device_get_nameunit(dev);
3750e474d75SJohannes Hofmann 	ic->ic_opmode = IEEE80211_M_STA;
376841ab66cSSepherosa Ziehau 	ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
377b50e4759SMatthew Dillon 
378b50e4759SMatthew Dillon 	/* set device capabilities */
379841ab66cSSepherosa Ziehau 	ic->ic_caps =
3800e474d75SJohannes Hofmann 	      IEEE80211_C_STA		/* station mode supported */
3810e474d75SJohannes Hofmann 	    | IEEE80211_C_IBSS		/* IBSS mode supported */
3820e474d75SJohannes Hofmann 	    | IEEE80211_C_MONITOR	/* monitor mode supported */
3830e474d75SJohannes Hofmann 	    | IEEE80211_C_PMGT		/* power save supported */
3840e474d75SJohannes Hofmann 	    | IEEE80211_C_SHPREAMBLE	/* short preamble supported */
3850e474d75SJohannes Hofmann 	    | IEEE80211_C_WPA		/* 802.11i */
3860e474d75SJohannes Hofmann 	    | IEEE80211_C_WME		/* 802.11e */
3870e474d75SJohannes Hofmann #if 0
3880e474d75SJohannes Hofmann 	    | IEEE80211_C_BGSCAN	/* capable of bg scanning */
3890e474d75SJohannes Hofmann #endif
3900e474d75SJohannes Hofmann 	    ;
391b50e4759SMatthew Dillon 
392b50e4759SMatthew Dillon 	/* read MAC address from EEPROM */
393b50e4759SMatthew Dillon 	val = iwi_read_prom_word(sc, IWI_EEPROM_MAC + 0);
394c4fe7bb1SImre Vadász 	ic->ic_macaddr[0] = val & 0xff;
395c4fe7bb1SImre Vadász 	ic->ic_macaddr[1] = val >> 8;
396b50e4759SMatthew Dillon 	val = iwi_read_prom_word(sc, IWI_EEPROM_MAC + 1);
397c4fe7bb1SImre Vadász 	ic->ic_macaddr[2] = val & 0xff;
398c4fe7bb1SImre Vadász 	ic->ic_macaddr[3] = val >> 8;
399b50e4759SMatthew Dillon 	val = iwi_read_prom_word(sc, IWI_EEPROM_MAC + 2);
400c4fe7bb1SImre Vadász 	ic->ic_macaddr[4] = val & 0xff;
401c4fe7bb1SImre Vadász 	ic->ic_macaddr[5] = val >> 8;
402b50e4759SMatthew Dillon 
403c4fe7bb1SImre Vadász 	memset(bands, 0, sizeof(bands));
404c4fe7bb1SImre Vadász 	setbit(bands, IEEE80211_MODE_11B);
405c4fe7bb1SImre Vadász 	setbit(bands, IEEE80211_MODE_11G);
4060e474d75SJohannes Hofmann 	if (pci_get_device(dev) >= 0x4223)
407c4fe7bb1SImre Vadász 		setbit(bands, IEEE80211_MODE_11A);
408c4fe7bb1SImre Vadász 	ieee80211_init_channels(ic, NULL, bands);
409b50e4759SMatthew Dillon 
410c4fe7bb1SImre Vadász 	ieee80211_ifattach(ic);
411841ab66cSSepherosa Ziehau 	/* override default methods */
412841ab66cSSepherosa Ziehau 	ic->ic_node_alloc = iwi_node_alloc;
413841ab66cSSepherosa Ziehau 	sc->sc_node_free = ic->ic_node_free;
414841ab66cSSepherosa Ziehau 	ic->ic_node_free = iwi_node_free;
4150e474d75SJohannes Hofmann 	ic->ic_raw_xmit = iwi_raw_xmit;
4160e474d75SJohannes Hofmann 	ic->ic_scan_start = iwi_scan_start;
4170e474d75SJohannes Hofmann 	ic->ic_scan_end = iwi_scan_end;
4180e474d75SJohannes Hofmann 	ic->ic_set_channel = iwi_set_channel;
4190e474d75SJohannes Hofmann 	ic->ic_scan_curchan = iwi_scan_curchan;
4200e474d75SJohannes Hofmann 	ic->ic_scan_mindwell = iwi_scan_mindwell;
4210e474d75SJohannes Hofmann 	ic->ic_wme.wme_update = iwi_wme_update;
422b50e4759SMatthew Dillon 
4230e474d75SJohannes Hofmann 	ic->ic_vap_create = iwi_vap_create;
4240e474d75SJohannes Hofmann 	ic->ic_vap_delete = iwi_vap_delete;
425c4fe7bb1SImre Vadász 	ic->ic_transmit = iwi_transmit;
426c4fe7bb1SImre Vadász 	ic->ic_parent = iwi_parent;
427b50e4759SMatthew Dillon 
4280e474d75SJohannes Hofmann 	ieee80211_radiotap_attach(ic,
4290e474d75SJohannes Hofmann 	    &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap),
4300e474d75SJohannes Hofmann 		IWI_TX_RADIOTAP_PRESENT,
4310e474d75SJohannes Hofmann 	    &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap),
4320e474d75SJohannes Hofmann 		IWI_RX_RADIOTAP_PRESENT);
433b50e4759SMatthew Dillon 
4340e474d75SJohannes Hofmann 	iwi_sysctlattach(sc);
4350e474d75SJohannes Hofmann 	iwi_ledattach(sc);
4363660cb22SSepherosa Ziehau 
4373660cb22SSepherosa Ziehau 	/*
438841ab66cSSepherosa Ziehau 	 * Hook our interrupt after all initialization is complete.
4394ff3e076SJoerg Sonnenberger 	 */
440c4fe7bb1SImre Vadász #if defined(__DragonFly__)
4410e474d75SJohannes Hofmann 	error = bus_setup_intr(dev, sc->irq, INTR_MPSAFE,
442ef634573SJoe Talbott 	    iwi_intr, sc, &sc->sc_ih, &wlan_global_serializer);
443c4fe7bb1SImre Vadász #else
444c4fe7bb1SImre Vadász 	error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET | INTR_MPSAFE,
445c4fe7bb1SImre Vadász 	    NULL, iwi_intr, sc, &sc->sc_ih);
446c4fe7bb1SImre Vadász #endif
4474ff3e076SJoerg Sonnenberger 	if (error != 0) {
4484ff3e076SJoerg Sonnenberger 		device_printf(dev, "could not set up interrupt\n");
4490e474d75SJohannes Hofmann 		goto fail;
4504ff3e076SJoerg Sonnenberger 	}
4514ff3e076SJoerg Sonnenberger 
452841ab66cSSepherosa Ziehau 	if (bootverbose)
453841ab66cSSepherosa Ziehau 		ieee80211_announce(ic);
454b50e4759SMatthew Dillon 
455841ab66cSSepherosa Ziehau 	return 0;
4564ff3e076SJoerg Sonnenberger fail:
4570e474d75SJohannes Hofmann 	/* XXX fix */
4584ff3e076SJoerg Sonnenberger 	iwi_detach(dev);
459841ab66cSSepherosa Ziehau 	return ENXIO;
460b50e4759SMatthew Dillon }
461b50e4759SMatthew Dillon 
462b50e4759SMatthew Dillon static int
iwi_detach(device_t dev)463b50e4759SMatthew Dillon iwi_detach(device_t dev)
464b50e4759SMatthew Dillon {
465b50e4759SMatthew Dillon 	struct iwi_softc *sc = device_get_softc(dev);
466c4fe7bb1SImre Vadász 	struct ieee80211com *ic = &sc->sc_ic;
467b50e4759SMatthew Dillon 
468c4fe7bb1SImre Vadász 	bus_teardown_intr(dev, sc->irq, sc->sc_ih);
469ef634573SJoe Talbott 
4700e474d75SJohannes Hofmann 	/* NB: do early to drain any pending tasks */
4710e474d75SJohannes Hofmann 	ieee80211_draintask(ic, &sc->sc_radiontask);
4720e474d75SJohannes Hofmann 	ieee80211_draintask(ic, &sc->sc_radiofftask);
4730e474d75SJohannes Hofmann 	ieee80211_draintask(ic, &sc->sc_restarttask);
4740e474d75SJohannes Hofmann 	ieee80211_draintask(ic, &sc->sc_disassoctask);
475c4fe7bb1SImre Vadász 	ieee80211_draintask(ic, &sc->sc_monitortask);
476841ab66cSSepherosa Ziehau 
477b50e4759SMatthew Dillon 	iwi_stop(sc);
478841ab66cSSepherosa Ziehau 
479841ab66cSSepherosa Ziehau 	ieee80211_ifdetach(ic);
4800e474d75SJohannes Hofmann 
4810e474d75SJohannes Hofmann 	iwi_put_firmware(sc);
4820e474d75SJohannes Hofmann 	iwi_release_fw_dma(sc);
483b50e4759SMatthew Dillon 
484841ab66cSSepherosa Ziehau 	iwi_free_cmd_ring(sc, &sc->cmdq);
485841ab66cSSepherosa Ziehau 	iwi_free_tx_ring(sc, &sc->txq[0]);
486841ab66cSSepherosa Ziehau 	iwi_free_tx_ring(sc, &sc->txq[1]);
487841ab66cSSepherosa Ziehau 	iwi_free_tx_ring(sc, &sc->txq[2]);
488841ab66cSSepherosa Ziehau 	iwi_free_tx_ring(sc, &sc->txq[3]);
489841ab66cSSepherosa Ziehau 	iwi_free_rx_ring(sc, &sc->rxq);
490b50e4759SMatthew Dillon 
491c4fe7bb1SImre Vadász 	bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(sc->irq), sc->irq);
492b50e4759SMatthew Dillon 
493c4fe7bb1SImre Vadász 	bus_release_resource(dev, SYS_RES_MEMORY, rman_get_rid(sc->mem),
494c4fe7bb1SImre Vadász 	    sc->mem);
4954ff3e076SJoerg Sonnenberger 
496c4fe7bb1SImre Vadász #if defined(__DragonFly__)
4970e474d75SJohannes Hofmann 	devfs_clone_bitmap_uninit(&sc->sc_unr);
498c4fe7bb1SImre Vadász #else
499c4fe7bb1SImre Vadász 	delete_unrhdr(sc->sc_unr);
500c4fe7bb1SImre Vadász #endif
501c4fe7bb1SImre Vadász 	mbufq_drain(&sc->sc_snd);
5020e474d75SJohannes Hofmann 
503c4fe7bb1SImre Vadász 	IWI_LOCK_DESTROY(sc);
504b50e4759SMatthew Dillon 
505b50e4759SMatthew Dillon 	return 0;
506b50e4759SMatthew Dillon }
507b50e4759SMatthew Dillon 
5080e474d75SJohannes Hofmann static struct ieee80211vap *
iwi_vap_create(struct ieee80211com * ic,const char name[IFNAMSIZ],int unit,enum ieee80211_opmode opmode,int flags,const uint8_t bssid[IEEE80211_ADDR_LEN],const uint8_t mac[IEEE80211_ADDR_LEN])509c4fe7bb1SImre Vadász iwi_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit,
51018ef6e46SSascha Wildner     enum ieee80211_opmode opmode, int flags,
5110e474d75SJohannes Hofmann     const uint8_t bssid[IEEE80211_ADDR_LEN],
5120e474d75SJohannes Hofmann     const uint8_t mac[IEEE80211_ADDR_LEN])
5130e474d75SJohannes Hofmann {
514c4fe7bb1SImre Vadász 	struct iwi_softc *sc = ic->ic_softc;
5150e474d75SJohannes Hofmann 	struct iwi_vap *ivp;
5160e474d75SJohannes Hofmann 	struct ieee80211vap *vap;
5170e474d75SJohannes Hofmann 	int i;
5180e474d75SJohannes Hofmann 
5190e474d75SJohannes Hofmann 	if (!TAILQ_EMPTY(&ic->ic_vaps))		/* only one at a time */
5200e474d75SJohannes Hofmann 		return NULL;
5210e474d75SJohannes Hofmann 	/*
5220e474d75SJohannes Hofmann 	 * Get firmware image (and possibly dma memory) on mode change.
5230e474d75SJohannes Hofmann 	 */
5240e474d75SJohannes Hofmann 	if (iwi_get_firmware(sc, opmode))
5250e474d75SJohannes Hofmann 		return NULL;
5260e474d75SJohannes Hofmann 	/* allocate DMA memory for mapping firmware image */
5270e474d75SJohannes Hofmann 	i = sc->fw_fw.size;
5280e474d75SJohannes Hofmann 	if (sc->fw_boot.size > i)
5290e474d75SJohannes Hofmann 		i = sc->fw_boot.size;
5300e474d75SJohannes Hofmann 	/* XXX do we dma the ucode as well ? */
5310e474d75SJohannes Hofmann 	if (sc->fw_uc.size > i)
5320e474d75SJohannes Hofmann 		i = sc->fw_uc.size;
5330e474d75SJohannes Hofmann 	if (iwi_init_fw_dma(sc, i))
5340e474d75SJohannes Hofmann 		return NULL;
5350e474d75SJohannes Hofmann 
536c4fe7bb1SImre Vadász 	ivp = kmalloc(sizeof(struct iwi_vap), M_80211_VAP, M_WAITOK | M_ZERO);
5370e474d75SJohannes Hofmann 	vap = &ivp->iwi_vap;
538c4fe7bb1SImre Vadász 	ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid);
5390e474d75SJohannes Hofmann 	/* override the default, the setting comes from the linux driver */
5400e474d75SJohannes Hofmann 	vap->iv_bmissthreshold = 24;
5410e474d75SJohannes Hofmann 	/* override with driver methods */
5420e474d75SJohannes Hofmann 	ivp->iwi_newstate = vap->iv_newstate;
5430e474d75SJohannes Hofmann 	vap->iv_newstate = iwi_newstate;
5440e474d75SJohannes Hofmann 
5450e474d75SJohannes Hofmann 	/* complete setup */
546c4fe7bb1SImre Vadász 	ieee80211_vap_attach(vap, ieee80211_media_change, iwi_media_status,
547c4fe7bb1SImre Vadász 	    mac);
5480e474d75SJohannes Hofmann 	ic->ic_opmode = opmode;
5490e474d75SJohannes Hofmann 	return vap;
5500e474d75SJohannes Hofmann }
5510e474d75SJohannes Hofmann 
5520e474d75SJohannes Hofmann static void
iwi_vap_delete(struct ieee80211vap * vap)5530e474d75SJohannes Hofmann iwi_vap_delete(struct ieee80211vap *vap)
5540e474d75SJohannes Hofmann {
5550e474d75SJohannes Hofmann 	struct iwi_vap *ivp = IWI_VAP(vap);
5560e474d75SJohannes Hofmann 
5570e474d75SJohannes Hofmann 	ieee80211_vap_detach(vap);
5580e474d75SJohannes Hofmann 	kfree(ivp, M_80211_VAP);
5590e474d75SJohannes Hofmann }
5600e474d75SJohannes Hofmann 
561841ab66cSSepherosa Ziehau static void
iwi_dma_map_addr(void * arg,bus_dma_segment_t * segs,int nseg,int error)562841ab66cSSepherosa Ziehau iwi_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nseg, int error)
563841ab66cSSepherosa Ziehau {
564841ab66cSSepherosa Ziehau 	if (error != 0)
565841ab66cSSepherosa Ziehau 		return;
566841ab66cSSepherosa Ziehau 
567841ab66cSSepherosa Ziehau 	KASSERT(nseg == 1, ("too many DMA segments, %d should be 1", nseg));
568841ab66cSSepherosa Ziehau 
569841ab66cSSepherosa Ziehau 	*(bus_addr_t *)arg = segs[0].ds_addr;
570841ab66cSSepherosa Ziehau }
571841ab66cSSepherosa Ziehau 
572b50e4759SMatthew Dillon static int
iwi_alloc_cmd_ring(struct iwi_softc * sc,struct iwi_cmd_ring * ring,int count)573841ab66cSSepherosa Ziehau iwi_alloc_cmd_ring(struct iwi_softc *sc, struct iwi_cmd_ring *ring, int count)
574841ab66cSSepherosa Ziehau {
575841ab66cSSepherosa Ziehau 	int error;
576841ab66cSSepherosa Ziehau 
577841ab66cSSepherosa Ziehau 	ring->count = count;
578841ab66cSSepherosa Ziehau 	ring->queued = 0;
579841ab66cSSepherosa Ziehau 	ring->cur = ring->next = 0;
580841ab66cSSepherosa Ziehau 
581c4fe7bb1SImre Vadász #if defined(__DragonFly__)
5820e474d75SJohannes Hofmann 	error = bus_dma_tag_create(NULL, 4, 0,
583030b0c8cSMichael Neumann 	    BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR,
5840e474d75SJohannes Hofmann 	    count * IWI_CMD_DESC_SIZE, 1, count * IWI_CMD_DESC_SIZE,
5850e474d75SJohannes Hofmann 	    0 , &ring->desc_dmat);
586c4fe7bb1SImre Vadász #else
587c4fe7bb1SImre Vadász 	error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 4, 0,
588c4fe7bb1SImre Vadász 	    BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
589c4fe7bb1SImre Vadász 	    count * IWI_CMD_DESC_SIZE, 1, count * IWI_CMD_DESC_SIZE, 0,
590c4fe7bb1SImre Vadász 	    NULL, NULL, &ring->desc_dmat);
591c4fe7bb1SImre Vadász #endif
592841ab66cSSepherosa Ziehau 	if (error != 0) {
593841ab66cSSepherosa Ziehau 		device_printf(sc->sc_dev, "could not create desc DMA tag\n");
594841ab66cSSepherosa Ziehau 		goto fail;
595841ab66cSSepherosa Ziehau 	}
596841ab66cSSepherosa Ziehau 
597841ab66cSSepherosa Ziehau 	error = bus_dmamem_alloc(ring->desc_dmat, (void **)&ring->desc,
5980e474d75SJohannes Hofmann 	    BUS_DMA_NOWAIT | BUS_DMA_ZERO, &ring->desc_map);
599841ab66cSSepherosa Ziehau 	if (error != 0) {
600841ab66cSSepherosa Ziehau 		device_printf(sc->sc_dev, "could not allocate DMA memory\n");
601841ab66cSSepherosa Ziehau 		goto fail;
602841ab66cSSepherosa Ziehau 	}
603841ab66cSSepherosa Ziehau 
604841ab66cSSepherosa Ziehau 	error = bus_dmamap_load(ring->desc_dmat, ring->desc_map, ring->desc,
605841ab66cSSepherosa Ziehau 	    count * IWI_CMD_DESC_SIZE, iwi_dma_map_addr, &ring->physaddr, 0);
606841ab66cSSepherosa Ziehau 	if (error != 0) {
607841ab66cSSepherosa Ziehau 		device_printf(sc->sc_dev, "could not load desc DMA map\n");
6080e474d75SJohannes Hofmann 		goto fail;
609841ab66cSSepherosa Ziehau 	}
610841ab66cSSepherosa Ziehau 
611841ab66cSSepherosa Ziehau 	return 0;
612841ab66cSSepherosa Ziehau 
613841ab66cSSepherosa Ziehau fail:	iwi_free_cmd_ring(sc, ring);
614841ab66cSSepherosa Ziehau 	return error;
615841ab66cSSepherosa Ziehau }
616841ab66cSSepherosa Ziehau 
617841ab66cSSepherosa Ziehau static void
iwi_reset_cmd_ring(struct iwi_softc * sc,struct iwi_cmd_ring * ring)618841ab66cSSepherosa Ziehau iwi_reset_cmd_ring(struct iwi_softc *sc, struct iwi_cmd_ring *ring)
619841ab66cSSepherosa Ziehau {
620841ab66cSSepherosa Ziehau 	ring->queued = 0;
621841ab66cSSepherosa Ziehau 	ring->cur = ring->next = 0;
622841ab66cSSepherosa Ziehau }
623841ab66cSSepherosa Ziehau 
624841ab66cSSepherosa Ziehau static void
iwi_free_cmd_ring(struct iwi_softc * sc,struct iwi_cmd_ring * ring)625841ab66cSSepherosa Ziehau iwi_free_cmd_ring(struct iwi_softc *sc, struct iwi_cmd_ring *ring)
626841ab66cSSepherosa Ziehau {
627841ab66cSSepherosa Ziehau 	if (ring->desc != NULL) {
628841ab66cSSepherosa Ziehau 		bus_dmamap_sync(ring->desc_dmat, ring->desc_map,
629841ab66cSSepherosa Ziehau 		    BUS_DMASYNC_POSTWRITE);
630841ab66cSSepherosa Ziehau 		bus_dmamap_unload(ring->desc_dmat, ring->desc_map);
631841ab66cSSepherosa Ziehau 		bus_dmamem_free(ring->desc_dmat, ring->desc, ring->desc_map);
632841ab66cSSepherosa Ziehau 	}
633841ab66cSSepherosa Ziehau 
6340e474d75SJohannes Hofmann 	if (ring->desc_dmat != NULL)
635841ab66cSSepherosa Ziehau 		bus_dma_tag_destroy(ring->desc_dmat);
636841ab66cSSepherosa Ziehau }
637841ab66cSSepherosa Ziehau 
638841ab66cSSepherosa Ziehau static int
iwi_alloc_tx_ring(struct iwi_softc * sc,struct iwi_tx_ring * ring,int count,bus_addr_t csr_ridx,bus_addr_t csr_widx)639841ab66cSSepherosa Ziehau iwi_alloc_tx_ring(struct iwi_softc *sc, struct iwi_tx_ring *ring, int count,
640841ab66cSSepherosa Ziehau     bus_addr_t csr_ridx, bus_addr_t csr_widx)
641b50e4759SMatthew Dillon {
642b50e4759SMatthew Dillon 	int i, error;
643b50e4759SMatthew Dillon 
644841ab66cSSepherosa Ziehau 	ring->count = count;
645841ab66cSSepherosa Ziehau 	ring->queued = 0;
646841ab66cSSepherosa Ziehau 	ring->cur = ring->next = 0;
647841ab66cSSepherosa Ziehau 	ring->csr_ridx = csr_ridx;
648841ab66cSSepherosa Ziehau 	ring->csr_widx = csr_widx;
649841ab66cSSepherosa Ziehau 
650c4fe7bb1SImre Vadász #if defined(__DragonFly__)
651841ab66cSSepherosa Ziehau 	error = bus_dma_tag_create(NULL, 4, 0, BUS_SPACE_MAXADDR_32BIT,
652030b0c8cSMichael Neumann 	    BUS_SPACE_MAXADDR, count * IWI_TX_DESC_SIZE, 1,
653841ab66cSSepherosa Ziehau 	    count * IWI_TX_DESC_SIZE, 0, &ring->desc_dmat);
654c4fe7bb1SImre Vadász #else
655c4fe7bb1SImre Vadász 	error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 4, 0,
656c4fe7bb1SImre Vadász 	    BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
657c4fe7bb1SImre Vadász 	    count * IWI_TX_DESC_SIZE, 1, count * IWI_TX_DESC_SIZE, 0, NULL,
658c4fe7bb1SImre Vadász 	    NULL, &ring->desc_dmat);
659c4fe7bb1SImre Vadász #endif
660b50e4759SMatthew Dillon 	if (error != 0) {
661841ab66cSSepherosa Ziehau 		device_printf(sc->sc_dev, "could not create desc DMA tag\n");
662b50e4759SMatthew Dillon 		goto fail;
663b50e4759SMatthew Dillon 	}
664b50e4759SMatthew Dillon 
665841ab66cSSepherosa Ziehau 	error = bus_dmamem_alloc(ring->desc_dmat, (void **)&ring->desc,
6660e474d75SJohannes Hofmann 	    BUS_DMA_NOWAIT | BUS_DMA_ZERO, &ring->desc_map);
667b50e4759SMatthew Dillon 	if (error != 0) {
668841ab66cSSepherosa Ziehau 		device_printf(sc->sc_dev, "could not allocate DMA memory\n");
669b50e4759SMatthew Dillon 		goto fail;
670b50e4759SMatthew Dillon 	}
671b50e4759SMatthew Dillon 
672841ab66cSSepherosa Ziehau 	error = bus_dmamap_load(ring->desc_dmat, ring->desc_map, ring->desc,
673841ab66cSSepherosa Ziehau 	    count * IWI_TX_DESC_SIZE, iwi_dma_map_addr, &ring->physaddr, 0);
674b50e4759SMatthew Dillon 	if (error != 0) {
675841ab66cSSepherosa Ziehau 		device_printf(sc->sc_dev, "could not load desc DMA map\n");
676b50e4759SMatthew Dillon 		goto fail;
677b50e4759SMatthew Dillon 	}
678b50e4759SMatthew Dillon 
679efda3bd0SMatthew Dillon 	ring->data = kmalloc(count * sizeof (struct iwi_tx_data), M_DEVBUF,
680841ab66cSSepherosa Ziehau 	    M_WAITOK | M_ZERO);
681c4fe7bb1SImre Vadász 	if (ring->data == NULL) {
682c4fe7bb1SImre Vadász 		device_printf(sc->sc_dev, "could not allocate soft data\n");
683c4fe7bb1SImre Vadász 		error = ENOMEM;
684c4fe7bb1SImre Vadász 		goto fail;
685c4fe7bb1SImre Vadász 	}
686841ab66cSSepherosa Ziehau 
687c4fe7bb1SImre Vadász #if defined(__DragonFly__)
688841ab66cSSepherosa Ziehau 	error = bus_dma_tag_create(NULL, 1, 0, BUS_SPACE_MAXADDR_32BIT,
689030b0c8cSMichael Neumann 	    BUS_SPACE_MAXADDR, MCLBYTES, IWI_MAX_NSEG,
690841ab66cSSepherosa Ziehau 	    MCLBYTES, 0, &ring->data_dmat);
691c4fe7bb1SImre Vadász #else
692c4fe7bb1SImre Vadász 	error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0,
693c4fe7bb1SImre Vadász 	BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES,
694c4fe7bb1SImre Vadász 	IWI_MAX_NSEG, MCLBYTES, 0, NULL, NULL, &ring->data_dmat);
695c4fe7bb1SImre Vadász #endif
696b50e4759SMatthew Dillon 	if (error != 0) {
697841ab66cSSepherosa Ziehau 		device_printf(sc->sc_dev, "could not create data DMA tag\n");
698b50e4759SMatthew Dillon 		goto fail;
699b50e4759SMatthew Dillon 	}
700b50e4759SMatthew Dillon 
701841ab66cSSepherosa Ziehau 	for (i = 0; i < count; i++) {
702841ab66cSSepherosa Ziehau 		error = bus_dmamap_create(ring->data_dmat, 0,
703841ab66cSSepherosa Ziehau 		    &ring->data[i].map);
704b50e4759SMatthew Dillon 		if (error != 0) {
705841ab66cSSepherosa Ziehau 			device_printf(sc->sc_dev, "could not create DMA map\n");
706b50e4759SMatthew Dillon 			goto fail;
707b50e4759SMatthew Dillon 		}
708b50e4759SMatthew Dillon 	}
709b50e4759SMatthew Dillon 
710841ab66cSSepherosa Ziehau 	return 0;
711841ab66cSSepherosa Ziehau 
712841ab66cSSepherosa Ziehau fail:	iwi_free_tx_ring(sc, ring);
713841ab66cSSepherosa Ziehau 	return error;
714841ab66cSSepherosa Ziehau }
715841ab66cSSepherosa Ziehau 
716841ab66cSSepherosa Ziehau static void
iwi_reset_tx_ring(struct iwi_softc * sc,struct iwi_tx_ring * ring)717841ab66cSSepherosa Ziehau iwi_reset_tx_ring(struct iwi_softc *sc, struct iwi_tx_ring *ring)
718841ab66cSSepherosa Ziehau {
719841ab66cSSepherosa Ziehau 	struct iwi_tx_data *data;
720841ab66cSSepherosa Ziehau 	int i;
721841ab66cSSepherosa Ziehau 
722841ab66cSSepherosa Ziehau 	for (i = 0; i < ring->count; i++) {
723841ab66cSSepherosa Ziehau 		data = &ring->data[i];
724841ab66cSSepherosa Ziehau 
725841ab66cSSepherosa Ziehau 		if (data->m != NULL) {
726841ab66cSSepherosa Ziehau 			bus_dmamap_sync(ring->data_dmat, data->map,
727841ab66cSSepherosa Ziehau 			    BUS_DMASYNC_POSTWRITE);
728841ab66cSSepherosa Ziehau 			bus_dmamap_unload(ring->data_dmat, data->map);
729841ab66cSSepherosa Ziehau 			m_freem(data->m);
730841ab66cSSepherosa Ziehau 			data->m = NULL;
731841ab66cSSepherosa Ziehau 		}
732841ab66cSSepherosa Ziehau 
733841ab66cSSepherosa Ziehau 		if (data->ni != NULL) {
734841ab66cSSepherosa Ziehau 			ieee80211_free_node(data->ni);
735841ab66cSSepherosa Ziehau 			data->ni = NULL;
736841ab66cSSepherosa Ziehau 		}
737841ab66cSSepherosa Ziehau 	}
738841ab66cSSepherosa Ziehau 
739841ab66cSSepherosa Ziehau 	ring->queued = 0;
740841ab66cSSepherosa Ziehau 	ring->cur = ring->next = 0;
741841ab66cSSepherosa Ziehau }
742841ab66cSSepherosa Ziehau 
743841ab66cSSepherosa Ziehau static void
iwi_free_tx_ring(struct iwi_softc * sc,struct iwi_tx_ring * ring)744841ab66cSSepherosa Ziehau iwi_free_tx_ring(struct iwi_softc *sc, struct iwi_tx_ring *ring)
745841ab66cSSepherosa Ziehau {
746841ab66cSSepherosa Ziehau 	struct iwi_tx_data *data;
747841ab66cSSepherosa Ziehau 	int i;
748841ab66cSSepherosa Ziehau 
749841ab66cSSepherosa Ziehau 	if (ring->desc != NULL) {
750841ab66cSSepherosa Ziehau 		bus_dmamap_sync(ring->desc_dmat, ring->desc_map,
751841ab66cSSepherosa Ziehau 		    BUS_DMASYNC_POSTWRITE);
752841ab66cSSepherosa Ziehau 		bus_dmamap_unload(ring->desc_dmat, ring->desc_map);
753841ab66cSSepherosa Ziehau 		bus_dmamem_free(ring->desc_dmat, ring->desc, ring->desc_map);
754841ab66cSSepherosa Ziehau 	}
755841ab66cSSepherosa Ziehau 
7560e474d75SJohannes Hofmann 	if (ring->desc_dmat != NULL)
757841ab66cSSepherosa Ziehau 		bus_dma_tag_destroy(ring->desc_dmat);
758841ab66cSSepherosa Ziehau 
759841ab66cSSepherosa Ziehau 	if (ring->data != NULL) {
760841ab66cSSepherosa Ziehau 		for (i = 0; i < ring->count; i++) {
761841ab66cSSepherosa Ziehau 			data = &ring->data[i];
762841ab66cSSepherosa Ziehau 
763841ab66cSSepherosa Ziehau 			if (data->m != NULL) {
764841ab66cSSepherosa Ziehau 				bus_dmamap_sync(ring->data_dmat, data->map,
765841ab66cSSepherosa Ziehau 				    BUS_DMASYNC_POSTWRITE);
766841ab66cSSepherosa Ziehau 				bus_dmamap_unload(ring->data_dmat, data->map);
767841ab66cSSepherosa Ziehau 				m_freem(data->m);
768841ab66cSSepherosa Ziehau 			}
769841ab66cSSepherosa Ziehau 
7700e474d75SJohannes Hofmann 			if (data->ni != NULL)
771841ab66cSSepherosa Ziehau 				ieee80211_free_node(data->ni);
772841ab66cSSepherosa Ziehau 
7730e474d75SJohannes Hofmann 			if (data->map != NULL)
774841ab66cSSepherosa Ziehau 				bus_dmamap_destroy(ring->data_dmat, data->map);
775841ab66cSSepherosa Ziehau 		}
776841ab66cSSepherosa Ziehau 
777efda3bd0SMatthew Dillon 		kfree(ring->data, M_DEVBUF);
778841ab66cSSepherosa Ziehau 	}
779841ab66cSSepherosa Ziehau 
7800e474d75SJohannes Hofmann 	if (ring->data_dmat != NULL)
781841ab66cSSepherosa Ziehau 		bus_dma_tag_destroy(ring->data_dmat);
782841ab66cSSepherosa Ziehau }
783841ab66cSSepherosa Ziehau 
784841ab66cSSepherosa Ziehau static int
iwi_alloc_rx_ring(struct iwi_softc * sc,struct iwi_rx_ring * ring,int count)785841ab66cSSepherosa Ziehau iwi_alloc_rx_ring(struct iwi_softc *sc, struct iwi_rx_ring *ring, int count)
786841ab66cSSepherosa Ziehau {
787841ab66cSSepherosa Ziehau 	struct iwi_rx_data *data;
788841ab66cSSepherosa Ziehau 	int i, error;
789841ab66cSSepherosa Ziehau 
790841ab66cSSepherosa Ziehau 	ring->count = count;
791841ab66cSSepherosa Ziehau 	ring->cur = 0;
792841ab66cSSepherosa Ziehau 
793efda3bd0SMatthew Dillon 	ring->data = kmalloc(count * sizeof (struct iwi_rx_data), M_DEVBUF,
794841ab66cSSepherosa Ziehau 	    M_WAITOK | M_ZERO);
795c4fe7bb1SImre Vadász 	if (ring->data == NULL) {
796c4fe7bb1SImre Vadász 		device_printf(sc->sc_dev, "could not allocate soft data\n");
797c4fe7bb1SImre Vadász 		error = ENOMEM;
798c4fe7bb1SImre Vadász 		goto fail;
799c4fe7bb1SImre Vadász 	}
800841ab66cSSepherosa Ziehau 
801c4fe7bb1SImre Vadász #if defined(__DragonFly__)
802841ab66cSSepherosa Ziehau 	error = bus_dma_tag_create(NULL, 1, 0, BUS_SPACE_MAXADDR_32BIT,
803030b0c8cSMichael Neumann 	    BUS_SPACE_MAXADDR, MCLBYTES, 1, MCLBYTES,
8040e474d75SJohannes Hofmann 	    0, &ring->data_dmat);
805c4fe7bb1SImre Vadász #else
806c4fe7bb1SImre Vadász 	error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0,
807c4fe7bb1SImre Vadász 	    BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES,
808c4fe7bb1SImre Vadász 	    1, MCLBYTES, 0, NULL, NULL, &ring->data_dmat);
809c4fe7bb1SImre Vadász #endif
810b50e4759SMatthew Dillon 	if (error != 0) {
811841ab66cSSepherosa Ziehau 		device_printf(sc->sc_dev, "could not create data DMA tag\n");
812b50e4759SMatthew Dillon 		goto fail;
813b50e4759SMatthew Dillon 	}
814b50e4759SMatthew Dillon 
815841ab66cSSepherosa Ziehau 	for (i = 0; i < count; i++) {
816841ab66cSSepherosa Ziehau 		data = &ring->data[i];
817b50e4759SMatthew Dillon 
818841ab66cSSepherosa Ziehau 		error = bus_dmamap_create(ring->data_dmat, 0, &data->map);
819b50e4759SMatthew Dillon 		if (error != 0) {
820841ab66cSSepherosa Ziehau 			device_printf(sc->sc_dev, "could not create DMA map\n");
821b50e4759SMatthew Dillon 			goto fail;
822b50e4759SMatthew Dillon 		}
823b50e4759SMatthew Dillon 
824b5523eacSSascha Wildner 		data->m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
825841ab66cSSepherosa Ziehau 		if (data->m == NULL) {
826b50e4759SMatthew Dillon 			device_printf(sc->sc_dev,
827b50e4759SMatthew Dillon 			    "could not allocate rx mbuf\n");
828b50e4759SMatthew Dillon 			error = ENOMEM;
829b50e4759SMatthew Dillon 			goto fail;
830b50e4759SMatthew Dillon 		}
831b50e4759SMatthew Dillon 
832841ab66cSSepherosa Ziehau 		error = bus_dmamap_load(ring->data_dmat, data->map,
833841ab66cSSepherosa Ziehau 		    mtod(data->m, void *), MCLBYTES, iwi_dma_map_addr,
834841ab66cSSepherosa Ziehau 		    &data->physaddr, 0);
835b50e4759SMatthew Dillon 		if (error != 0) {
836b50e4759SMatthew Dillon 			device_printf(sc->sc_dev,
837b50e4759SMatthew Dillon 			    "could not load rx buf DMA map");
838b50e4759SMatthew Dillon 			goto fail;
839b50e4759SMatthew Dillon 		}
840841ab66cSSepherosa Ziehau 
841841ab66cSSepherosa Ziehau 		data->reg = IWI_CSR_RX_BASE + i * 4;
842b50e4759SMatthew Dillon 	}
843b50e4759SMatthew Dillon 
844b50e4759SMatthew Dillon 	return 0;
845b50e4759SMatthew Dillon 
846841ab66cSSepherosa Ziehau fail:	iwi_free_rx_ring(sc, ring);
847b50e4759SMatthew Dillon 	return error;
848b50e4759SMatthew Dillon }
849b50e4759SMatthew Dillon 
850b50e4759SMatthew Dillon static void
iwi_reset_rx_ring(struct iwi_softc * sc,struct iwi_rx_ring * ring)851841ab66cSSepherosa Ziehau iwi_reset_rx_ring(struct iwi_softc *sc, struct iwi_rx_ring *ring)
852b50e4759SMatthew Dillon {
853841ab66cSSepherosa Ziehau 	ring->cur = 0;
854841ab66cSSepherosa Ziehau }
855841ab66cSSepherosa Ziehau 
856841ab66cSSepherosa Ziehau static void
iwi_free_rx_ring(struct iwi_softc * sc,struct iwi_rx_ring * ring)857841ab66cSSepherosa Ziehau iwi_free_rx_ring(struct iwi_softc *sc, struct iwi_rx_ring *ring)
858841ab66cSSepherosa Ziehau {
859841ab66cSSepherosa Ziehau 	struct iwi_rx_data *data;
860b50e4759SMatthew Dillon 	int i;
861b50e4759SMatthew Dillon 
862841ab66cSSepherosa Ziehau 	if (ring->data != NULL) {
863841ab66cSSepherosa Ziehau 		for (i = 0; i < ring->count; i++) {
864841ab66cSSepherosa Ziehau 			data = &ring->data[i];
865841ab66cSSepherosa Ziehau 
866841ab66cSSepherosa Ziehau 			if (data->m != NULL) {
867841ab66cSSepherosa Ziehau 				bus_dmamap_sync(ring->data_dmat, data->map,
868841ab66cSSepherosa Ziehau 				    BUS_DMASYNC_POSTREAD);
869841ab66cSSepherosa Ziehau 				bus_dmamap_unload(ring->data_dmat, data->map);
870841ab66cSSepherosa Ziehau 				m_freem(data->m);
871b50e4759SMatthew Dillon 			}
872b50e4759SMatthew Dillon 
8730e474d75SJohannes Hofmann 			if (data->map != NULL)
874841ab66cSSepherosa Ziehau 				bus_dmamap_destroy(ring->data_dmat, data->map);
875b50e4759SMatthew Dillon 		}
876b50e4759SMatthew Dillon 
877efda3bd0SMatthew Dillon 		kfree(ring->data, M_DEVBUF);
878b50e4759SMatthew Dillon 	}
879b50e4759SMatthew Dillon 
8800e474d75SJohannes Hofmann 	if (ring->data_dmat != NULL)
881841ab66cSSepherosa Ziehau 		bus_dma_tag_destroy(ring->data_dmat);
882b50e4759SMatthew Dillon }
883b50e4759SMatthew Dillon 
884b50e4759SMatthew Dillon static int
iwi_shutdown(device_t dev)885b50e4759SMatthew Dillon iwi_shutdown(device_t dev)
886b50e4759SMatthew Dillon {
887b50e4759SMatthew Dillon 	struct iwi_softc *sc = device_get_softc(dev);
888b50e4759SMatthew Dillon 
889b50e4759SMatthew Dillon 	iwi_stop(sc);
8900e474d75SJohannes Hofmann 	iwi_put_firmware(sc);		/* ??? XXX */
891b50e4759SMatthew Dillon 
892b50e4759SMatthew Dillon 	return 0;
893b50e4759SMatthew Dillon }
894b50e4759SMatthew Dillon 
895b50e4759SMatthew Dillon static int
iwi_suspend(device_t dev)896b50e4759SMatthew Dillon iwi_suspend(device_t dev)
897b50e4759SMatthew Dillon {
898b50e4759SMatthew Dillon 	struct iwi_softc *sc = device_get_softc(dev);
899c4fe7bb1SImre Vadász 	struct ieee80211com *ic = &sc->sc_ic;
900b50e4759SMatthew Dillon 
901c4fe7bb1SImre Vadász 	ieee80211_suspend_all(ic);
902b50e4759SMatthew Dillon 	return 0;
903b50e4759SMatthew Dillon }
904b50e4759SMatthew Dillon 
905b50e4759SMatthew Dillon static int
iwi_resume(device_t dev)906b50e4759SMatthew Dillon iwi_resume(device_t dev)
907b50e4759SMatthew Dillon {
908b50e4759SMatthew Dillon 	struct iwi_softc *sc = device_get_softc(dev);
909c4fe7bb1SImre Vadász 	struct ieee80211com *ic = &sc->sc_ic;
910841ab66cSSepherosa Ziehau 
911b50e4759SMatthew Dillon 	pci_write_config(dev, 0x41, 0, 1);
912b50e4759SMatthew Dillon 
913c4fe7bb1SImre Vadász 	ieee80211_resume_all(ic);
914b50e4759SMatthew Dillon 	return 0;
915b50e4759SMatthew Dillon }
916b50e4759SMatthew Dillon 
917841ab66cSSepherosa Ziehau static struct ieee80211_node *
iwi_node_alloc(struct ieee80211vap * vap,const uint8_t mac[IEEE80211_ADDR_LEN])9180e474d75SJohannes Hofmann iwi_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN])
919841ab66cSSepherosa Ziehau {
920841ab66cSSepherosa Ziehau 	struct iwi_node *in;
921841ab66cSSepherosa Ziehau 
922c4fe7bb1SImre Vadász 	in = kmalloc(sizeof (struct iwi_node), M_80211_NODE, M_INTWAIT | M_ZERO);
923841ab66cSSepherosa Ziehau 	if (in == NULL)
924841ab66cSSepherosa Ziehau 		return NULL;
9250e474d75SJohannes Hofmann 	/* XXX assign sta table entry for adhoc */
926841ab66cSSepherosa Ziehau 	in->in_station = -1;
927841ab66cSSepherosa Ziehau 
928841ab66cSSepherosa Ziehau 	return &in->in_node;
929841ab66cSSepherosa Ziehau }
930841ab66cSSepherosa Ziehau 
931841ab66cSSepherosa Ziehau static void
iwi_node_free(struct ieee80211_node * ni)932841ab66cSSepherosa Ziehau iwi_node_free(struct ieee80211_node *ni)
933841ab66cSSepherosa Ziehau {
934841ab66cSSepherosa Ziehau 	struct ieee80211com *ic = ni->ni_ic;
935c4fe7bb1SImre Vadász 	struct iwi_softc *sc = ic->ic_softc;
936841ab66cSSepherosa Ziehau 	struct iwi_node *in = (struct iwi_node *)ni;
937841ab66cSSepherosa Ziehau 
9380e474d75SJohannes Hofmann 	if (in->in_station != -1) {
939c4fe7bb1SImre Vadász #if defined(__DragonFly__)
9401e290df3SAntonio Huete Jimenez 		DPRINTF(("%s mac %s station %u\n", __func__,
941c4fe7bb1SImre Vadász 		    ether_sprintf(ni->ni_macaddr), in->in_station));
942c4fe7bb1SImre Vadász #else
943c4fe7bb1SImre Vadász 		DPRINTF(("%s mac %6D station %u\n", __func__,
944c4fe7bb1SImre Vadász 		    ni->ni_macaddr, ":", in->in_station));
945c4fe7bb1SImre Vadász #endif
946c4fe7bb1SImre Vadász #if defined(__DragonFly__)
9472281ff2eSMatthew Dillon 		devfs_clone_bitmap_put(&sc->sc_unr, in->in_station);
948c4fe7bb1SImre Vadász #else
949c4fe7bb1SImre Vadász 		free_unr(sc->sc_unr, in->in_station);
950c4fe7bb1SImre Vadász #endif
951841ab66cSSepherosa Ziehau 	}
952841ab66cSSepherosa Ziehau 
9530e474d75SJohannes Hofmann 	sc->sc_node_free(ni);
954b50e4759SMatthew Dillon }
955b50e4759SMatthew Dillon 
956841ab66cSSepherosa Ziehau /*
957114b4cbcSSepherosa Ziehau  * Convert h/w rate code to IEEE rate code.
958114b4cbcSSepherosa Ziehau  */
959114b4cbcSSepherosa Ziehau static int
iwi_cvtrate(int iwirate)960114b4cbcSSepherosa Ziehau iwi_cvtrate(int iwirate)
961114b4cbcSSepherosa Ziehau {
962114b4cbcSSepherosa Ziehau 	switch (iwirate) {
963114b4cbcSSepherosa Ziehau 	case IWI_RATE_DS1:	return 2;
964114b4cbcSSepherosa Ziehau 	case IWI_RATE_DS2:	return 4;
965114b4cbcSSepherosa Ziehau 	case IWI_RATE_DS5:	return 11;
966114b4cbcSSepherosa Ziehau 	case IWI_RATE_DS11:	return 22;
967114b4cbcSSepherosa Ziehau 	case IWI_RATE_OFDM6:	return 12;
968114b4cbcSSepherosa Ziehau 	case IWI_RATE_OFDM9:	return 18;
969114b4cbcSSepherosa Ziehau 	case IWI_RATE_OFDM12:	return 24;
970114b4cbcSSepherosa Ziehau 	case IWI_RATE_OFDM18:	return 36;
971114b4cbcSSepherosa Ziehau 	case IWI_RATE_OFDM24:	return 48;
972114b4cbcSSepherosa Ziehau 	case IWI_RATE_OFDM36:	return 72;
973114b4cbcSSepherosa Ziehau 	case IWI_RATE_OFDM48:	return 96;
974114b4cbcSSepherosa Ziehau 	case IWI_RATE_OFDM54:	return 108;
975114b4cbcSSepherosa Ziehau 	}
976114b4cbcSSepherosa Ziehau 	return 0;
977114b4cbcSSepherosa Ziehau }
978114b4cbcSSepherosa Ziehau 
979114b4cbcSSepherosa Ziehau /*
980841ab66cSSepherosa Ziehau  * The firmware automatically adapts the transmit speed.  We report its current
981841ab66cSSepherosa Ziehau  * value here.
982841ab66cSSepherosa Ziehau  */
983b50e4759SMatthew Dillon static void
iwi_media_status(struct ifnet * ifp,struct ifmediareq * imr)984b50e4759SMatthew Dillon iwi_media_status(struct ifnet *ifp, struct ifmediareq *imr)
985b50e4759SMatthew Dillon {
9860e474d75SJohannes Hofmann 	struct ieee80211vap *vap = ifp->if_softc;
9870e474d75SJohannes Hofmann 	struct ieee80211com *ic = vap->iv_ic;
988c4fe7bb1SImre Vadász 	struct iwi_softc *sc = ic->ic_softc;
989c4fe7bb1SImre Vadász 	struct ieee80211_node *ni;
990b50e4759SMatthew Dillon 
991b50e4759SMatthew Dillon 	/* read current transmission rate from adapter */
992c4fe7bb1SImre Vadász 	ni = ieee80211_ref_node(vap->iv_bss);
993c4fe7bb1SImre Vadász 	ni->ni_txrate =
9940e474d75SJohannes Hofmann 	    iwi_cvtrate(CSR_READ_4(sc, IWI_CSR_CURRENT_TX_RATE));
995c4fe7bb1SImre Vadász 	ieee80211_free_node(ni);
9960e474d75SJohannes Hofmann 	ieee80211_media_status(ifp, imr);
997b50e4759SMatthew Dillon }
998b50e4759SMatthew Dillon 
999b50e4759SMatthew Dillon static int
iwi_newstate(struct ieee80211vap * vap,enum ieee80211_state nstate,int arg)10000e474d75SJohannes Hofmann iwi_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
1001b50e4759SMatthew Dillon {
10020e474d75SJohannes Hofmann 	struct iwi_vap *ivp = IWI_VAP(vap);
10030e474d75SJohannes Hofmann 	struct ieee80211com *ic = vap->iv_ic;
1004c4fe7bb1SImre Vadász 	struct iwi_softc *sc = ic->ic_softc;
1005c4fe7bb1SImre Vadász 	IWI_LOCK_DECL;
1006b50e4759SMatthew Dillon 
10070e474d75SJohannes Hofmann 	DPRINTF(("%s: %s -> %s flags 0x%x\n", __func__,
10080e474d75SJohannes Hofmann 		ieee80211_state_name[vap->iv_state],
10090e474d75SJohannes Hofmann 		ieee80211_state_name[nstate], sc->flags));
1010b50e4759SMatthew Dillon 
1011c4fe7bb1SImre Vadász 	IEEE80211_UNLOCK(ic);
1012c4fe7bb1SImre Vadász 	IWI_LOCK(sc);
1013b50e4759SMatthew Dillon 	switch (nstate) {
1014b50e4759SMatthew Dillon 	case IEEE80211_S_INIT:
10150e474d75SJohannes Hofmann 		/*
10160e474d75SJohannes Hofmann 		 * NB: don't try to do this if iwi_stop_master has
10170e474d75SJohannes Hofmann 		 *     shutdown the firmware and disabled interrupts.
10180e474d75SJohannes Hofmann 		 */
10190e474d75SJohannes Hofmann 		if (vap->iv_state == IEEE80211_S_RUN &&
10200e474d75SJohannes Hofmann 		    (sc->flags & IWI_FLAG_FW_INITED))
10210e474d75SJohannes Hofmann 			iwi_disassociate(sc, 0);
1022841ab66cSSepherosa Ziehau 		break;
10230e474d75SJohannes Hofmann 	case IEEE80211_S_AUTH:
10240e474d75SJohannes Hofmann 		iwi_auth_and_assoc(sc, vap);
10250e474d75SJohannes Hofmann 		break;
10260e474d75SJohannes Hofmann 	case IEEE80211_S_RUN:
10270e474d75SJohannes Hofmann 		if (vap->iv_opmode == IEEE80211_M_IBSS &&
10280e474d75SJohannes Hofmann 		    vap->iv_state == IEEE80211_S_SCAN) {
10290e474d75SJohannes Hofmann 			/*
10300e474d75SJohannes Hofmann 			 * XXX when joining an ibss network we are called
10310e474d75SJohannes Hofmann 			 * with a SCAN -> RUN transition on scan complete.
10320e474d75SJohannes Hofmann 			 * Use that to call iwi_auth_and_assoc.  On completing
10330e474d75SJohannes Hofmann 			 * the join we are then called again with an
10340e474d75SJohannes Hofmann 			 * AUTH -> RUN transition and we want to do nothing.
10350e474d75SJohannes Hofmann 			 * This is all totally bogus and needs to be redone.
10360e474d75SJohannes Hofmann 			 */
10370e474d75SJohannes Hofmann 			iwi_auth_and_assoc(sc, vap);
1038c4fe7bb1SImre Vadász 		} else if (vap->iv_opmode == IEEE80211_M_MONITOR)
1039c4fe7bb1SImre Vadász 			ieee80211_runtask(ic, &sc->sc_monitortask);
10400e474d75SJohannes Hofmann 		break;
10410e474d75SJohannes Hofmann 	case IEEE80211_S_ASSOC:
10420e474d75SJohannes Hofmann 		/*
10430e474d75SJohannes Hofmann 		 * If we are transitioning from AUTH then just wait
10440e474d75SJohannes Hofmann 		 * for the ASSOC status to come back from the firmware.
10450e474d75SJohannes Hofmann 		 * Otherwise we need to issue the association request.
10460e474d75SJohannes Hofmann 		 */
10470e474d75SJohannes Hofmann 		if (vap->iv_state == IEEE80211_S_AUTH)
10480e474d75SJohannes Hofmann 			break;
10490e474d75SJohannes Hofmann 		iwi_auth_and_assoc(sc, vap);
10500e474d75SJohannes Hofmann 		break;
10510e474d75SJohannes Hofmann 	default:
1052b50e4759SMatthew Dillon 		break;
1053b50e4759SMatthew Dillon 	}
1054c4fe7bb1SImre Vadász 	IWI_UNLOCK(sc);
1055c4fe7bb1SImre Vadász 	IEEE80211_LOCK(ic);
10560e474d75SJohannes Hofmann 	return ivp->iwi_newstate(vap, nstate, arg);
1057b50e4759SMatthew Dillon }
1058b50e4759SMatthew Dillon 
1059b50e4759SMatthew Dillon /*
1060841ab66cSSepherosa Ziehau  * WME parameters coming from IEEE 802.11e specification.  These values are
1061841ab66cSSepherosa Ziehau  * already declared in ieee80211_proto.c, but they are static so they can't
1062841ab66cSSepherosa Ziehau  * be reused here.
1063b50e4759SMatthew Dillon  */
1064841ab66cSSepherosa Ziehau static const struct wmeParams iwi_wme_cck_params[WME_NUM_AC] = {
1065841ab66cSSepherosa Ziehau 	{ 0, 3, 5,  7,   0 },	/* WME_AC_BE */
1066841ab66cSSepherosa Ziehau 	{ 0, 3, 5, 10,   0 },	/* WME_AC_BK */
1067841ab66cSSepherosa Ziehau 	{ 0, 2, 4,  5, 188 },	/* WME_AC_VI */
1068841ab66cSSepherosa Ziehau 	{ 0, 2, 3,  4, 102 }	/* WME_AC_VO */
1069841ab66cSSepherosa Ziehau };
1070841ab66cSSepherosa Ziehau 
1071841ab66cSSepherosa Ziehau static const struct wmeParams iwi_wme_ofdm_params[WME_NUM_AC] = {
1072841ab66cSSepherosa Ziehau 	{ 0, 3, 4,  6,   0 },	/* WME_AC_BE */
1073841ab66cSSepherosa Ziehau 	{ 0, 3, 4, 10,   0 },	/* WME_AC_BK */
1074841ab66cSSepherosa Ziehau 	{ 0, 2, 3,  4,  94 },	/* WME_AC_VI */
1075841ab66cSSepherosa Ziehau 	{ 0, 2, 2,  3,  47 }	/* WME_AC_VO */
1076841ab66cSSepherosa Ziehau };
1077841ab66cSSepherosa Ziehau #define IWI_EXP2(v)	htole16((1 << (v)) - 1)
1078841ab66cSSepherosa Ziehau #define IWI_USEC(v)	htole16(IEEE80211_TXOP_TO_US(v))
10790e474d75SJohannes Hofmann 
10800e474d75SJohannes Hofmann static void
iwi_wme_init(struct iwi_softc * sc)10810e474d75SJohannes Hofmann iwi_wme_init(struct iwi_softc *sc)
10820e474d75SJohannes Hofmann {
1083841ab66cSSepherosa Ziehau 	const struct wmeParams *wmep;
1084841ab66cSSepherosa Ziehau 	int ac;
1085841ab66cSSepherosa Ziehau 
10860e474d75SJohannes Hofmann 	memset(sc->wme, 0, sizeof sc->wme);
10870e474d75SJohannes Hofmann 	for (ac = 0; ac < WME_NUM_AC; ac++) {
10880e474d75SJohannes Hofmann 		/* set WME values for CCK modulation */
10890e474d75SJohannes Hofmann 		wmep = &iwi_wme_cck_params[ac];
10900e474d75SJohannes Hofmann 		sc->wme[1].aifsn[ac] = wmep->wmep_aifsn;
10910e474d75SJohannes Hofmann 		sc->wme[1].cwmin[ac] = IWI_EXP2(wmep->wmep_logcwmin);
10920e474d75SJohannes Hofmann 		sc->wme[1].cwmax[ac] = IWI_EXP2(wmep->wmep_logcwmax);
10930e474d75SJohannes Hofmann 		sc->wme[1].burst[ac] = IWI_USEC(wmep->wmep_txopLimit);
10940e474d75SJohannes Hofmann 		sc->wme[1].acm[ac]   = wmep->wmep_acm;
10950e474d75SJohannes Hofmann 
10960e474d75SJohannes Hofmann 		/* set WME values for OFDM modulation */
10970e474d75SJohannes Hofmann 		wmep = &iwi_wme_ofdm_params[ac];
10980e474d75SJohannes Hofmann 		sc->wme[2].aifsn[ac] = wmep->wmep_aifsn;
10990e474d75SJohannes Hofmann 		sc->wme[2].cwmin[ac] = IWI_EXP2(wmep->wmep_logcwmin);
11000e474d75SJohannes Hofmann 		sc->wme[2].cwmax[ac] = IWI_EXP2(wmep->wmep_logcwmax);
11010e474d75SJohannes Hofmann 		sc->wme[2].burst[ac] = IWI_USEC(wmep->wmep_txopLimit);
11020e474d75SJohannes Hofmann 		sc->wme[2].acm[ac]   = wmep->wmep_acm;
11030e474d75SJohannes Hofmann 	}
11040e474d75SJohannes Hofmann }
11050e474d75SJohannes Hofmann 
11060e474d75SJohannes Hofmann static int
iwi_wme_setparams(struct iwi_softc * sc)1107c4fe7bb1SImre Vadász iwi_wme_setparams(struct iwi_softc *sc)
11080e474d75SJohannes Hofmann {
1109c4fe7bb1SImre Vadász 	struct ieee80211com *ic = &sc->sc_ic;
11100e474d75SJohannes Hofmann 	const struct wmeParams *wmep;
11110e474d75SJohannes Hofmann 	int ac;
1112841ab66cSSepherosa Ziehau 
1113841ab66cSSepherosa Ziehau 	for (ac = 0; ac < WME_NUM_AC; ac++) {
1114841ab66cSSepherosa Ziehau 		/* set WME values for current operating mode */
1115841ab66cSSepherosa Ziehau 		wmep = &ic->ic_wme.wme_chanParams.cap_wmeParams[ac];
11160e474d75SJohannes Hofmann 		sc->wme[0].aifsn[ac] = wmep->wmep_aifsn;
11170e474d75SJohannes Hofmann 		sc->wme[0].cwmin[ac] = IWI_EXP2(wmep->wmep_logcwmin);
11180e474d75SJohannes Hofmann 		sc->wme[0].cwmax[ac] = IWI_EXP2(wmep->wmep_logcwmax);
11190e474d75SJohannes Hofmann 		sc->wme[0].burst[ac] = IWI_USEC(wmep->wmep_txopLimit);
11200e474d75SJohannes Hofmann 		sc->wme[0].acm[ac]   = wmep->wmep_acm;
1121841ab66cSSepherosa Ziehau 	}
1122841ab66cSSepherosa Ziehau 
1123841ab66cSSepherosa Ziehau 	DPRINTF(("Setting WME parameters\n"));
11240e474d75SJohannes Hofmann 	return iwi_cmd(sc, IWI_CMD_SET_WME_PARAMS, sc->wme, sizeof sc->wme);
11250e474d75SJohannes Hofmann }
1126841ab66cSSepherosa Ziehau #undef IWI_USEC
1127841ab66cSSepherosa Ziehau #undef IWI_EXP2
11280e474d75SJohannes Hofmann 
11290e474d75SJohannes Hofmann static int
iwi_wme_update(struct ieee80211com * ic)11300e474d75SJohannes Hofmann iwi_wme_update(struct ieee80211com *ic)
11310e474d75SJohannes Hofmann {
1132c4fe7bb1SImre Vadász 	struct iwi_softc *sc = ic->ic_softc;
11330e474d75SJohannes Hofmann 	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
1134c4fe7bb1SImre Vadász 	IWI_LOCK_DECL;
11350e474d75SJohannes Hofmann 
11360e474d75SJohannes Hofmann 	/*
11370e474d75SJohannes Hofmann 	 * We may be called to update the WME parameters in
11380e474d75SJohannes Hofmann 	 * the adapter at various places.  If we're already
11390e474d75SJohannes Hofmann 	 * associated then initiate the request immediately;
11400e474d75SJohannes Hofmann 	 * otherwise we assume the params will get sent down
11410e474d75SJohannes Hofmann 	 * to the adapter as part of the work iwi_auth_and_assoc
11420e474d75SJohannes Hofmann 	 * does.
11430e474d75SJohannes Hofmann 	 */
1144c4fe7bb1SImre Vadász 	if (vap->iv_state == IEEE80211_S_RUN) {
1145c4fe7bb1SImre Vadász 		IWI_LOCK(sc);
1146c4fe7bb1SImre Vadász 		iwi_wme_setparams(sc);
1147c4fe7bb1SImre Vadász 		IWI_UNLOCK(sc);
1148c4fe7bb1SImre Vadász 	}
11490e474d75SJohannes Hofmann 	return (0);
11500e474d75SJohannes Hofmann }
11510e474d75SJohannes Hofmann 
11520e474d75SJohannes Hofmann static int
iwi_wme_setie(struct iwi_softc * sc)11530e474d75SJohannes Hofmann iwi_wme_setie(struct iwi_softc *sc)
11540e474d75SJohannes Hofmann {
11550e474d75SJohannes Hofmann 	struct ieee80211_wme_info wme;
11560e474d75SJohannes Hofmann 
11570e474d75SJohannes Hofmann 	memset(&wme, 0, sizeof wme);
11580e474d75SJohannes Hofmann 	wme.wme_id = IEEE80211_ELEMID_VENDOR;
11590e474d75SJohannes Hofmann 	wme.wme_len = sizeof (struct ieee80211_wme_info) - 2;
11600e474d75SJohannes Hofmann 	wme.wme_oui[0] = 0x00;
11610e474d75SJohannes Hofmann 	wme.wme_oui[1] = 0x50;
11620e474d75SJohannes Hofmann 	wme.wme_oui[2] = 0xf2;
11630e474d75SJohannes Hofmann 	wme.wme_type = WME_OUI_TYPE;
11640e474d75SJohannes Hofmann 	wme.wme_subtype = WME_INFO_OUI_SUBTYPE;
11650e474d75SJohannes Hofmann 	wme.wme_version = WME_VERSION;
11660e474d75SJohannes Hofmann 	wme.wme_info = 0;
11670e474d75SJohannes Hofmann 
11680e474d75SJohannes Hofmann 	DPRINTF(("Setting WME IE (len=%u)\n", wme.wme_len));
11690e474d75SJohannes Hofmann 	return iwi_cmd(sc, IWI_CMD_SET_WMEIE, &wme, sizeof wme);
1170841ab66cSSepherosa Ziehau }
1171841ab66cSSepherosa Ziehau 
1172841ab66cSSepherosa Ziehau /*
1173841ab66cSSepherosa Ziehau  * Read 16 bits at address 'addr' from the serial EEPROM.
1174841ab66cSSepherosa Ziehau  */
1175841ab66cSSepherosa Ziehau static uint16_t
iwi_read_prom_word(struct iwi_softc * sc,uint8_t addr)1176841ab66cSSepherosa Ziehau iwi_read_prom_word(struct iwi_softc *sc, uint8_t addr)
1177841ab66cSSepherosa Ziehau {
1178841ab66cSSepherosa Ziehau 	uint32_t tmp;
1179841ab66cSSepherosa Ziehau 	uint16_t val;
1180b50e4759SMatthew Dillon 	int n;
1181b50e4759SMatthew Dillon 
11820e474d75SJohannes Hofmann 	/* clock C once before the first command */
1183b50e4759SMatthew Dillon 	IWI_EEPROM_CTL(sc, 0);
1184b50e4759SMatthew Dillon 	IWI_EEPROM_CTL(sc, IWI_EEPROM_S);
1185b50e4759SMatthew Dillon 	IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_C);
1186b50e4759SMatthew Dillon 	IWI_EEPROM_CTL(sc, IWI_EEPROM_S);
1187b50e4759SMatthew Dillon 
11880e474d75SJohannes Hofmann 	/* write start bit (1) */
1189b50e4759SMatthew Dillon 	IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_D);
1190b50e4759SMatthew Dillon 	IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_D | IWI_EEPROM_C);
1191b50e4759SMatthew Dillon 
11920e474d75SJohannes Hofmann 	/* write READ opcode (10) */
1193b50e4759SMatthew Dillon 	IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_D);
1194b50e4759SMatthew Dillon 	IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_D | IWI_EEPROM_C);
1195b50e4759SMatthew Dillon 	IWI_EEPROM_CTL(sc, IWI_EEPROM_S);
1196b50e4759SMatthew Dillon 	IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_C);
1197b50e4759SMatthew Dillon 
11980e474d75SJohannes Hofmann 	/* write address A7-A0 */
1199b50e4759SMatthew Dillon 	for (n = 7; n >= 0; n--) {
1200b50e4759SMatthew Dillon 		IWI_EEPROM_CTL(sc, IWI_EEPROM_S |
1201b50e4759SMatthew Dillon 		    (((addr >> n) & 1) << IWI_EEPROM_SHIFT_D));
1202b50e4759SMatthew Dillon 		IWI_EEPROM_CTL(sc, IWI_EEPROM_S |
1203b50e4759SMatthew Dillon 		    (((addr >> n) & 1) << IWI_EEPROM_SHIFT_D) | IWI_EEPROM_C);
1204b50e4759SMatthew Dillon 	}
1205b50e4759SMatthew Dillon 
1206b50e4759SMatthew Dillon 	IWI_EEPROM_CTL(sc, IWI_EEPROM_S);
1207b50e4759SMatthew Dillon 
12080e474d75SJohannes Hofmann 	/* read data Q15-Q0 */
1209b50e4759SMatthew Dillon 	val = 0;
1210b50e4759SMatthew Dillon 	for (n = 15; n >= 0; n--) {
1211b50e4759SMatthew Dillon 		IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_C);
1212b50e4759SMatthew Dillon 		IWI_EEPROM_CTL(sc, IWI_EEPROM_S);
1213b50e4759SMatthew Dillon 		tmp = MEM_READ_4(sc, IWI_MEM_EEPROM_CTL);
1214b50e4759SMatthew Dillon 		val |= ((tmp & IWI_EEPROM_Q) >> IWI_EEPROM_SHIFT_Q) << n;
1215b50e4759SMatthew Dillon 	}
1216b50e4759SMatthew Dillon 
1217b50e4759SMatthew Dillon 	IWI_EEPROM_CTL(sc, 0);
1218b50e4759SMatthew Dillon 
12190e474d75SJohannes Hofmann 	/* clear Chip Select and clock C */
1220b50e4759SMatthew Dillon 	IWI_EEPROM_CTL(sc, IWI_EEPROM_S);
1221b50e4759SMatthew Dillon 	IWI_EEPROM_CTL(sc, 0);
1222b50e4759SMatthew Dillon 	IWI_EEPROM_CTL(sc, IWI_EEPROM_C);
1223b50e4759SMatthew Dillon 
1224841ab66cSSepherosa Ziehau 	return val;
1225b50e4759SMatthew Dillon }
1226b50e4759SMatthew Dillon 
1227b50e4759SMatthew Dillon static void
iwi_setcurchan(struct iwi_softc * sc,int chan)12280e474d75SJohannes Hofmann iwi_setcurchan(struct iwi_softc *sc, int chan)
1229b50e4759SMatthew Dillon {
1230c4fe7bb1SImre Vadász 	struct ieee80211com *ic = &sc->sc_ic;
1231b50e4759SMatthew Dillon 
12320e474d75SJohannes Hofmann 	sc->curchan = chan;
12330e474d75SJohannes Hofmann 	ieee80211_radiotap_chan_change(ic);
1234b50e4759SMatthew Dillon }
1235b50e4759SMatthew Dillon 
1236b50e4759SMatthew Dillon static void
iwi_frame_intr(struct iwi_softc * sc,struct iwi_rx_data * data,int i,struct iwi_frame * frame)1237841ab66cSSepherosa Ziehau iwi_frame_intr(struct iwi_softc *sc, struct iwi_rx_data *data, int i,
1238b50e4759SMatthew Dillon     struct iwi_frame *frame)
1239b50e4759SMatthew Dillon {
1240c4fe7bb1SImre Vadász 	struct ieee80211com *ic = &sc->sc_ic;
1241841ab66cSSepherosa Ziehau 	struct mbuf *mnew, *m;
1242b50e4759SMatthew Dillon 	struct ieee80211_node *ni;
12430e474d75SJohannes Hofmann 	int type, error, framelen;
12440e474d75SJohannes Hofmann 	int8_t rssi, nf;
1245c4fe7bb1SImre Vadász 	IWI_LOCK_DECL;
1246b50e4759SMatthew Dillon 
12470e474d75SJohannes Hofmann 	framelen = le16toh(frame->len);
12480e474d75SJohannes Hofmann 	if (framelen < IEEE80211_MIN_LEN || framelen > MCLBYTES) {
12490e474d75SJohannes Hofmann 		/*
12500e474d75SJohannes Hofmann 		 * XXX >MCLBYTES is bogus as it means the h/w dma'd
12510e474d75SJohannes Hofmann 		 *     out of bounds; need to figure out how to limit
12520e474d75SJohannes Hofmann 		 *     frame size in the firmware
12530e474d75SJohannes Hofmann 		 */
12540e474d75SJohannes Hofmann 		/* XXX stat */
12550e474d75SJohannes Hofmann 		DPRINTFN(1,
12560e474d75SJohannes Hofmann 		    ("drop rx frame len=%u chan=%u rssi=%u rssi_dbm=%u\n",
12570e474d75SJohannes Hofmann 		    le16toh(frame->len), frame->chan, frame->rssi,
12580e474d75SJohannes Hofmann 		    frame->rssi_dbm));
1259841ab66cSSepherosa Ziehau 		return;
12600e474d75SJohannes Hofmann 	}
12610e474d75SJohannes Hofmann 
12620e474d75SJohannes Hofmann 	DPRINTFN(5, ("received frame len=%u chan=%u rssi=%u rssi_dbm=%u\n",
12630e474d75SJohannes Hofmann 	    le16toh(frame->len), frame->chan, frame->rssi, frame->rssi_dbm));
12640e474d75SJohannes Hofmann 
12650e474d75SJohannes Hofmann 	if (frame->chan != sc->curchan)
12660e474d75SJohannes Hofmann 		iwi_setcurchan(sc, frame->chan);
1267841ab66cSSepherosa Ziehau 
1268841ab66cSSepherosa Ziehau 	/*
1269841ab66cSSepherosa Ziehau 	 * Try to allocate a new mbuf for this ring element and load it before
1270841ab66cSSepherosa Ziehau 	 * processing the current mbuf. If the ring element cannot be loaded,
1271841ab66cSSepherosa Ziehau 	 * drop the received packet and reuse the old mbuf. In the unlikely
1272841ab66cSSepherosa Ziehau 	 * case that the old mbuf can't be reloaded either, explicitly panic.
1273841ab66cSSepherosa Ziehau 	 */
1274b5523eacSSascha Wildner 	mnew = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
1275841ab66cSSepherosa Ziehau 	if (mnew == NULL) {
1276c4fe7bb1SImre Vadász #if defined(__DragonFly__)
1277c4fe7bb1SImre Vadász 		++ic->ic_ierrors;
1278c4fe7bb1SImre Vadász #else
1279c4fe7bb1SImre Vadász 		counter_u64_add(ic->ic_ierrors, 1);
1280c4fe7bb1SImre Vadász #endif
1281b50e4759SMatthew Dillon 		return;
1282b50e4759SMatthew Dillon 	}
1283b50e4759SMatthew Dillon 
1284841ab66cSSepherosa Ziehau 	bus_dmamap_unload(sc->rxq.data_dmat, data->map);
1285841ab66cSSepherosa Ziehau 
1286841ab66cSSepherosa Ziehau 	error = bus_dmamap_load(sc->rxq.data_dmat, data->map,
1287841ab66cSSepherosa Ziehau 	    mtod(mnew, void *), MCLBYTES, iwi_dma_map_addr, &data->physaddr,
1288841ab66cSSepherosa Ziehau 	    0);
1289841ab66cSSepherosa Ziehau 	if (error != 0) {
1290841ab66cSSepherosa Ziehau 		m_freem(mnew);
1291841ab66cSSepherosa Ziehau 
1292841ab66cSSepherosa Ziehau 		/* try to reload the old mbuf */
1293841ab66cSSepherosa Ziehau 		error = bus_dmamap_load(sc->rxq.data_dmat, data->map,
1294841ab66cSSepherosa Ziehau 		    mtod(data->m, void *), MCLBYTES, iwi_dma_map_addr,
1295841ab66cSSepherosa Ziehau 		    &data->physaddr, 0);
1296841ab66cSSepherosa Ziehau 		if (error != 0) {
1297841ab66cSSepherosa Ziehau 			/* very unlikely that it will fail... */
1298841ab66cSSepherosa Ziehau 			panic("%s: could not load old rx mbuf",
1299841ab66cSSepherosa Ziehau 			    device_get_name(sc->sc_dev));
1300841ab66cSSepherosa Ziehau 		}
1301c4fe7bb1SImre Vadász #if defined(__DragonFly__)
1302c4fe7bb1SImre Vadász 		++ic->ic_ierrors;
1303c4fe7bb1SImre Vadász #else
1304c4fe7bb1SImre Vadász 		counter_u64_add(ic->ic_ierrors, 1);
1305c4fe7bb1SImre Vadász #endif
1306841ab66cSSepherosa Ziehau 		return;
1307841ab66cSSepherosa Ziehau 	}
1308841ab66cSSepherosa Ziehau 
1309841ab66cSSepherosa Ziehau 	/*
1310841ab66cSSepherosa Ziehau 	 * New mbuf successfully loaded, update Rx ring and continue
1311841ab66cSSepherosa Ziehau 	 * processing.
1312841ab66cSSepherosa Ziehau 	 */
1313841ab66cSSepherosa Ziehau 	m = data->m;
1314841ab66cSSepherosa Ziehau 	data->m = mnew;
1315841ab66cSSepherosa Ziehau 	CSR_WRITE_4(sc, data->reg, data->physaddr);
1316b50e4759SMatthew Dillon 
13170e474d75SJohannes Hofmann 	/* finalize mbuf */
1318b50e4759SMatthew Dillon 	m->m_pkthdr.len = m->m_len = sizeof (struct iwi_hdr) +
13190e474d75SJohannes Hofmann 	    sizeof (struct iwi_frame) + framelen;
1320b50e4759SMatthew Dillon 
1321b50e4759SMatthew Dillon 	m_adj(m, sizeof (struct iwi_hdr) + sizeof (struct iwi_frame));
1322b50e4759SMatthew Dillon 
13230e474d75SJohannes Hofmann 	rssi = frame->rssi_dbm;
13240e474d75SJohannes Hofmann 	nf = -95;
13250e474d75SJohannes Hofmann 	if (ieee80211_radiotap_active(ic)) {
1326b50e4759SMatthew Dillon 		struct iwi_rx_radiotap_header *tap = &sc->sc_rxtap;
1327b50e4759SMatthew Dillon 
1328b50e4759SMatthew Dillon 		tap->wr_flags = 0;
13290e474d75SJohannes Hofmann 		tap->wr_antsignal = rssi;
13300e474d75SJohannes Hofmann 		tap->wr_antnoise = nf;
13310e474d75SJohannes Hofmann 		tap->wr_rate = iwi_cvtrate(frame->rate);
1332b50e4759SMatthew Dillon 		tap->wr_antenna = frame->antenna;
1333b50e4759SMatthew Dillon 	}
1334c4fe7bb1SImre Vadász 	IWI_UNLOCK(sc);
1335b50e4759SMatthew Dillon 
13360e474d75SJohannes Hofmann 	ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *));
13370e474d75SJohannes Hofmann 	if (ni != NULL) {
13380e474d75SJohannes Hofmann 		type = ieee80211_input(ni, m, rssi, nf);
1339841ab66cSSepherosa Ziehau 		ieee80211_free_node(ni);
13400e474d75SJohannes Hofmann 	} else
13410e474d75SJohannes Hofmann 		type = ieee80211_input_all(ic, m, rssi, nf);
13420e474d75SJohannes Hofmann 
1343c4fe7bb1SImre Vadász 	IWI_LOCK(sc);
13440e474d75SJohannes Hofmann 	if (sc->sc_softled) {
13450e474d75SJohannes Hofmann 		/*
13460e474d75SJohannes Hofmann 		 * Blink for any data frame.  Otherwise do a
13470e474d75SJohannes Hofmann 		 * heartbeat-style blink when idle.  The latter
13480e474d75SJohannes Hofmann 		 * is mainly for station mode where we depend on
13490e474d75SJohannes Hofmann 		 * periodic beacon frames to trigger the poll event.
13500e474d75SJohannes Hofmann 		 */
13510e474d75SJohannes Hofmann 		if (type == IEEE80211_FC0_TYPE_DATA) {
13520e474d75SJohannes Hofmann 			sc->sc_rxrate = frame->rate;
13530e474d75SJohannes Hofmann 			iwi_led_event(sc, IWI_LED_RX);
13540e474d75SJohannes Hofmann 		} else if (ticks - sc->sc_ledevent >= sc->sc_ledidle)
13550e474d75SJohannes Hofmann 			iwi_led_event(sc, IWI_LED_POLL);
1356b50e4759SMatthew Dillon 	}
13570e474d75SJohannes Hofmann }
13580e474d75SJohannes Hofmann 
13590e474d75SJohannes Hofmann /*
13600e474d75SJohannes Hofmann  * Check for an association response frame to see if QoS
13610e474d75SJohannes Hofmann  * has been negotiated.  We parse just enough to figure
13620e474d75SJohannes Hofmann  * out if we're supposed to use QoS.  The proper solution
13630e474d75SJohannes Hofmann  * is to pass the frame up so ieee80211_input can do the
13640e474d75SJohannes Hofmann  * work but that's made hard by how things currently are
13650e474d75SJohannes Hofmann  * done in the driver.
13660e474d75SJohannes Hofmann  */
13670e474d75SJohannes Hofmann static void
iwi_checkforqos(struct ieee80211vap * vap,const struct ieee80211_frame * wh,int len)13680e474d75SJohannes Hofmann iwi_checkforqos(struct ieee80211vap *vap,
13690e474d75SJohannes Hofmann 	const struct ieee80211_frame *wh, int len)
13700e474d75SJohannes Hofmann {
13710e474d75SJohannes Hofmann #define	SUBTYPE(wh)	((wh)->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK)
13720e474d75SJohannes Hofmann 	const uint8_t *frm, *efrm, *wme;
13730e474d75SJohannes Hofmann 	struct ieee80211_node *ni;
1374c4fe7bb1SImre Vadász 	uint16_t capinfo, status, associd;
13750e474d75SJohannes Hofmann 
13760e474d75SJohannes Hofmann 	/* NB: +8 for capinfo, status, associd, and first ie */
13770e474d75SJohannes Hofmann 	if (!(sizeof(*wh)+8 < len && len < IEEE80211_MAX_LEN) ||
13780e474d75SJohannes Hofmann 	    SUBTYPE(wh) != IEEE80211_FC0_SUBTYPE_ASSOC_RESP)
13790e474d75SJohannes Hofmann 		return;
13800e474d75SJohannes Hofmann 	/*
13810e474d75SJohannes Hofmann 	 * asresp frame format
13820e474d75SJohannes Hofmann 	 *	[2] capability information
13830e474d75SJohannes Hofmann 	 *	[2] status
13840e474d75SJohannes Hofmann 	 *	[2] association ID
13850e474d75SJohannes Hofmann 	 *	[tlv] supported rates
13860e474d75SJohannes Hofmann 	 *	[tlv] extended supported rates
13870e474d75SJohannes Hofmann 	 *	[tlv] WME
13880e474d75SJohannes Hofmann 	 */
13890e474d75SJohannes Hofmann 	frm = (const uint8_t *)&wh[1];
13900e474d75SJohannes Hofmann 	efrm = ((const uint8_t *) wh) + len;
13910e474d75SJohannes Hofmann 
13920e474d75SJohannes Hofmann 	capinfo = le16toh(*(const uint16_t *)frm);
13932a0b1ca7SSascha Wildner 	frm += 2;
1394c4fe7bb1SImre Vadász 	status = le16toh(*(const uint16_t *)frm);
13952a0b1ca7SSascha Wildner 	frm += 2;
13960e474d75SJohannes Hofmann 	associd = le16toh(*(const uint16_t *)frm);
13970e474d75SJohannes Hofmann 	frm += 2;
13980e474d75SJohannes Hofmann 
13990e474d75SJohannes Hofmann 	wme = NULL;
1400c4fe7bb1SImre Vadász 	while (efrm - frm > 1) {
1401c4fe7bb1SImre Vadász 		IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
14020e474d75SJohannes Hofmann 		switch (*frm) {
14030e474d75SJohannes Hofmann 		case IEEE80211_ELEMID_VENDOR:
14040e474d75SJohannes Hofmann 			if (iswmeoui(frm))
14050e474d75SJohannes Hofmann 				wme = frm;
14060e474d75SJohannes Hofmann 			break;
14070e474d75SJohannes Hofmann 		}
14080e474d75SJohannes Hofmann 		frm += frm[1] + 2;
14090e474d75SJohannes Hofmann 	}
14100e474d75SJohannes Hofmann 
1411c4fe7bb1SImre Vadász 	ni = ieee80211_ref_node(vap->iv_bss);
14120e474d75SJohannes Hofmann 	ni->ni_capinfo = capinfo;
1413c4fe7bb1SImre Vadász 	ni->ni_associd = associd & 0x3fff;
14140e474d75SJohannes Hofmann 	if (wme != NULL)
14150e474d75SJohannes Hofmann 		ni->ni_flags |= IEEE80211_NODE_QOS;
14160e474d75SJohannes Hofmann 	else
14170e474d75SJohannes Hofmann 		ni->ni_flags &= ~IEEE80211_NODE_QOS;
1418c4fe7bb1SImre Vadász 	ieee80211_free_node(ni);
14190e474d75SJohannes Hofmann #undef SUBTYPE
14200e474d75SJohannes Hofmann }
14210e474d75SJohannes Hofmann 
1422c4fe7bb1SImre Vadász static void
iwi_notif_link_quality(struct iwi_softc * sc,struct iwi_notif * notif)1423c4fe7bb1SImre Vadász iwi_notif_link_quality(struct iwi_softc *sc, struct iwi_notif *notif)
1424c4fe7bb1SImre Vadász {
1425c4fe7bb1SImre Vadász 	struct iwi_notif_link_quality *lq;
1426c4fe7bb1SImre Vadász 	int len;
1427c4fe7bb1SImre Vadász 
1428c4fe7bb1SImre Vadász 	len = le16toh(notif->len);
1429c4fe7bb1SImre Vadász 
1430c4fe7bb1SImre Vadász 	DPRINTFN(5, ("Notification (%u) - len=%d, sizeof=%zu\n",
1431c4fe7bb1SImre Vadász 	    notif->type,
1432c4fe7bb1SImre Vadász 	    len,
1433c4fe7bb1SImre Vadász 	    sizeof(struct iwi_notif_link_quality)
1434c4fe7bb1SImre Vadász 	    ));
1435c4fe7bb1SImre Vadász 
1436c4fe7bb1SImre Vadász 	/* enforce length */
1437c4fe7bb1SImre Vadász 	if (len != sizeof(struct iwi_notif_link_quality)) {
1438c4fe7bb1SImre Vadász 		DPRINTFN(5, ("Notification: (%u) too short (%d)\n",
1439c4fe7bb1SImre Vadász 		    notif->type,
1440c4fe7bb1SImre Vadász 		    len));
1441c4fe7bb1SImre Vadász 		return;
1442c4fe7bb1SImre Vadász 	}
1443c4fe7bb1SImre Vadász 
1444c4fe7bb1SImre Vadász 	lq = (struct iwi_notif_link_quality *)(notif + 1);
1445c4fe7bb1SImre Vadász 	memcpy(&sc->sc_linkqual, lq, sizeof(sc->sc_linkqual));
1446c4fe7bb1SImre Vadász 	sc->sc_linkqual_valid = 1;
1447c4fe7bb1SImre Vadász }
1448c4fe7bb1SImre Vadász 
14490e474d75SJohannes Hofmann /*
14500e474d75SJohannes Hofmann  * Task queue callbacks for iwi_notification_intr used to avoid LOR's.
14510e474d75SJohannes Hofmann  */
1452b50e4759SMatthew Dillon 
1453b50e4759SMatthew Dillon static void
iwi_notification_intr(struct iwi_softc * sc,struct iwi_notif * notif)1454b50e4759SMatthew Dillon iwi_notification_intr(struct iwi_softc *sc, struct iwi_notif *notif)
1455b50e4759SMatthew Dillon {
1456c4fe7bb1SImre Vadász 	struct ieee80211com *ic = &sc->sc_ic;
14570e474d75SJohannes Hofmann 	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
1458b50e4759SMatthew Dillon 	struct iwi_notif_scan_channel *chan;
1459b50e4759SMatthew Dillon 	struct iwi_notif_scan_complete *scan;
1460b50e4759SMatthew Dillon 	struct iwi_notif_authentication *auth;
1461b50e4759SMatthew Dillon 	struct iwi_notif_association *assoc;
14620e474d75SJohannes Hofmann 	struct iwi_notif_beacon_state *beacon;
1463b50e4759SMatthew Dillon 
1464b50e4759SMatthew Dillon 	switch (notif->type) {
1465b50e4759SMatthew Dillon 	case IWI_NOTIF_TYPE_SCAN_CHANNEL:
1466b50e4759SMatthew Dillon 		chan = (struct iwi_notif_scan_channel *)(notif + 1);
1467b50e4759SMatthew Dillon 
14680e474d75SJohannes Hofmann 		DPRINTFN(3, ("Scan of channel %u complete (%u)\n",
14690e474d75SJohannes Hofmann 		    ieee80211_ieee2mhz(chan->nchan, 0), chan->nchan));
14700e474d75SJohannes Hofmann 
14710e474d75SJohannes Hofmann 		/* Reset the timer, the scan is still going */
14720e474d75SJohannes Hofmann 		sc->sc_state_timer = 3;
1473b50e4759SMatthew Dillon 		break;
1474b50e4759SMatthew Dillon 
1475b50e4759SMatthew Dillon 	case IWI_NOTIF_TYPE_SCAN_COMPLETE:
1476b50e4759SMatthew Dillon 		scan = (struct iwi_notif_scan_complete *)(notif + 1);
1477b50e4759SMatthew Dillon 
1478b50e4759SMatthew Dillon 		DPRINTFN(2, ("Scan completed (%u, %u)\n", scan->nchan,
1479b50e4759SMatthew Dillon 		    scan->status));
1480b50e4759SMatthew Dillon 
14810e474d75SJohannes Hofmann 		IWI_STATE_END(sc, IWI_FW_SCANNING);
14820e474d75SJohannes Hofmann 
1483c4fe7bb1SImre Vadász 		/*
1484c4fe7bb1SImre Vadász 		 * Monitor mode works by doing a passive scan to set
1485c4fe7bb1SImre Vadász 		 * the channel and enable rx.  Because we don't want
1486c4fe7bb1SImre Vadász 		 * to abort a scan lest the firmware crash we scan
1487c4fe7bb1SImre Vadász 		 * for a short period of time and automatically restart
1488c4fe7bb1SImre Vadász 		 * the scan when notified the sweep has completed.
1489c4fe7bb1SImre Vadász 		 */
1490c4fe7bb1SImre Vadász 		if (vap->iv_opmode == IEEE80211_M_MONITOR) {
1491c4fe7bb1SImre Vadász 			ieee80211_runtask(ic, &sc->sc_monitortask);
1492c4fe7bb1SImre Vadász 			break;
1493c4fe7bb1SImre Vadász 		}
1494c4fe7bb1SImre Vadász 
14950e474d75SJohannes Hofmann 		if (scan->status == IWI_SCAN_COMPLETED) {
14960e474d75SJohannes Hofmann 			/* NB: don't need to defer, net80211 does it for us */
14970e474d75SJohannes Hofmann 			ieee80211_scan_next(vap);
14980e474d75SJohannes Hofmann 		}
1499b50e4759SMatthew Dillon 		break;
1500b50e4759SMatthew Dillon 
1501b50e4759SMatthew Dillon 	case IWI_NOTIF_TYPE_AUTHENTICATION:
1502b50e4759SMatthew Dillon 		auth = (struct iwi_notif_authentication *)(notif + 1);
1503b50e4759SMatthew Dillon 		switch (auth->state) {
15040e474d75SJohannes Hofmann 		case IWI_AUTH_SUCCESS:
15050e474d75SJohannes Hofmann 			DPRINTFN(2, ("Authentication succeeeded\n"));
15060e474d75SJohannes Hofmann 			ieee80211_new_state(vap, IEEE80211_S_ASSOC, -1);
1507b50e4759SMatthew Dillon 			break;
15080e474d75SJohannes Hofmann 		case IWI_AUTH_FAIL:
15090e474d75SJohannes Hofmann 			/*
15100e474d75SJohannes Hofmann 			 * These are delivered as an unsolicited deauth
15110e474d75SJohannes Hofmann 			 * (e.g. due to inactivity) or in response to an
15120e474d75SJohannes Hofmann 			 * associate request.
15130e474d75SJohannes Hofmann 			 */
15140e474d75SJohannes Hofmann 			sc->flags &= ~IWI_FLAG_ASSOCIATED;
15150e474d75SJohannes Hofmann 			if (vap->iv_state != IEEE80211_S_RUN) {
15160e474d75SJohannes Hofmann 				DPRINTFN(2, ("Authentication failed\n"));
15170e474d75SJohannes Hofmann 				vap->iv_stats.is_rx_auth_fail++;
15180e474d75SJohannes Hofmann 				IWI_STATE_END(sc, IWI_FW_ASSOCIATING);
15190e474d75SJohannes Hofmann 			} else {
15200e474d75SJohannes Hofmann 				DPRINTFN(2, ("Deauthenticated\n"));
15210e474d75SJohannes Hofmann 				vap->iv_stats.is_rx_deauth++;
15220e474d75SJohannes Hofmann 			}
15230e474d75SJohannes Hofmann 			ieee80211_new_state(vap, IEEE80211_S_SCAN, -1);
1524b50e4759SMatthew Dillon 			break;
15250e474d75SJohannes Hofmann 		case IWI_AUTH_SENT_1:
15260e474d75SJohannes Hofmann 		case IWI_AUTH_RECV_2:
15270e474d75SJohannes Hofmann 		case IWI_AUTH_SEQ1_PASS:
15280e474d75SJohannes Hofmann 			break;
15290e474d75SJohannes Hofmann 		case IWI_AUTH_SEQ1_FAIL:
15300e474d75SJohannes Hofmann 			DPRINTFN(2, ("Initial authentication handshake failed; "
15310e474d75SJohannes Hofmann 				"you probably need shared key\n"));
15320e474d75SJohannes Hofmann 			vap->iv_stats.is_rx_auth_fail++;
15330e474d75SJohannes Hofmann 			IWI_STATE_END(sc, IWI_FW_ASSOCIATING);
15340e474d75SJohannes Hofmann 			/* XXX retry shared key when in auto */
15350e474d75SJohannes Hofmann 			break;
1536b50e4759SMatthew Dillon 		default:
1537b50e4759SMatthew Dillon 			device_printf(sc->sc_dev,
1538b50e4759SMatthew Dillon 			    "unknown authentication state %u\n", auth->state);
15390e474d75SJohannes Hofmann 			break;
1540b50e4759SMatthew Dillon 		}
1541b50e4759SMatthew Dillon 		break;
1542b50e4759SMatthew Dillon 
1543b50e4759SMatthew Dillon 	case IWI_NOTIF_TYPE_ASSOCIATION:
1544b50e4759SMatthew Dillon 		assoc = (struct iwi_notif_association *)(notif + 1);
1545b50e4759SMatthew Dillon 		switch (assoc->state) {
15460e474d75SJohannes Hofmann 		case IWI_AUTH_SUCCESS:
1547841ab66cSSepherosa Ziehau 			/* re-association, do nothing */
1548841ab66cSSepherosa Ziehau 			break;
15490e474d75SJohannes Hofmann 		case IWI_ASSOC_SUCCESS:
15500e474d75SJohannes Hofmann 			DPRINTFN(2, ("Association succeeded\n"));
15510e474d75SJohannes Hofmann 			sc->flags |= IWI_FLAG_ASSOCIATED;
15520e474d75SJohannes Hofmann 			IWI_STATE_END(sc, IWI_FW_ASSOCIATING);
15530e474d75SJohannes Hofmann 			iwi_checkforqos(vap,
15540e474d75SJohannes Hofmann 			    (const struct ieee80211_frame *)(assoc+1),
1555c4fe7bb1SImre Vadász 			    le16toh(notif->len) - sizeof(*assoc) - 1);
15560e474d75SJohannes Hofmann 			ieee80211_new_state(vap, IEEE80211_S_RUN, -1);
15570e474d75SJohannes Hofmann 			break;
15580e474d75SJohannes Hofmann 		case IWI_ASSOC_INIT:
15590e474d75SJohannes Hofmann 			sc->flags &= ~IWI_FLAG_ASSOCIATED;
15600e474d75SJohannes Hofmann 			switch (sc->fw_state) {
15610e474d75SJohannes Hofmann 			case IWI_FW_ASSOCIATING:
15620e474d75SJohannes Hofmann 				DPRINTFN(2, ("Association failed\n"));
15630e474d75SJohannes Hofmann 				IWI_STATE_END(sc, IWI_FW_ASSOCIATING);
15640e474d75SJohannes Hofmann 				ieee80211_new_state(vap, IEEE80211_S_SCAN, -1);
1565b50e4759SMatthew Dillon 				break;
1566b50e4759SMatthew Dillon 
15670e474d75SJohannes Hofmann 			case IWI_FW_DISASSOCIATING:
15680e474d75SJohannes Hofmann 				DPRINTFN(2, ("Dissassociated\n"));
15690e474d75SJohannes Hofmann 				IWI_STATE_END(sc, IWI_FW_DISASSOCIATING);
15700e474d75SJohannes Hofmann 				vap->iv_stats.is_rx_disassoc++;
15710e474d75SJohannes Hofmann 				ieee80211_new_state(vap, IEEE80211_S_SCAN, -1);
1572b50e4759SMatthew Dillon 				break;
15730e474d75SJohannes Hofmann 			}
15740e474d75SJohannes Hofmann 			break;
1575b50e4759SMatthew Dillon 		default:
1576b50e4759SMatthew Dillon 			device_printf(sc->sc_dev,
1577b50e4759SMatthew Dillon 			    "unknown association state %u\n", assoc->state);
15780e474d75SJohannes Hofmann 			break;
1579b50e4759SMatthew Dillon 		}
1580b50e4759SMatthew Dillon 		break;
1581b50e4759SMatthew Dillon 
15820e474d75SJohannes Hofmann 	case IWI_NOTIF_TYPE_BEACON:
15830e474d75SJohannes Hofmann 		/* XXX check struct length */
15840e474d75SJohannes Hofmann 		beacon = (struct iwi_notif_beacon_state *)(notif + 1);
15850e474d75SJohannes Hofmann 
15860e474d75SJohannes Hofmann 		DPRINTFN(5, ("Beacon state (%u, %u)\n",
15870e474d75SJohannes Hofmann 		    beacon->state, le32toh(beacon->number)));
15880e474d75SJohannes Hofmann 
15890e474d75SJohannes Hofmann 		if (beacon->state == IWI_BEACON_MISS) {
15900e474d75SJohannes Hofmann 			/*
15910e474d75SJohannes Hofmann 			 * The firmware notifies us of every beacon miss
15920e474d75SJohannes Hofmann 			 * so we need to track the count against the
15930e474d75SJohannes Hofmann 			 * configured threshold before notifying the
15940e474d75SJohannes Hofmann 			 * 802.11 layer.
15950e474d75SJohannes Hofmann 			 * XXX try to roam, drop assoc only on much higher count
15960e474d75SJohannes Hofmann 			 */
15970e474d75SJohannes Hofmann 			if (le32toh(beacon->number) >= vap->iv_bmissthreshold) {
15980e474d75SJohannes Hofmann 				DPRINTF(("Beacon miss: %u >= %u\n",
15990e474d75SJohannes Hofmann 				    le32toh(beacon->number),
16000e474d75SJohannes Hofmann 				    vap->iv_bmissthreshold));
16010e474d75SJohannes Hofmann 				vap->iv_stats.is_beacon_miss++;
16020e474d75SJohannes Hofmann 				/*
16030e474d75SJohannes Hofmann 				 * It's pointless to notify the 802.11 layer
16040e474d75SJohannes Hofmann 				 * as it'll try to send a probe request (which
16050e474d75SJohannes Hofmann 				 * we'll discard) and then timeout and drop us
16060e474d75SJohannes Hofmann 				 * into scan state.  Instead tell the firmware
16070e474d75SJohannes Hofmann 				 * to disassociate and then on completion we'll
16080e474d75SJohannes Hofmann 				 * kick the state machine to scan.
16090e474d75SJohannes Hofmann 				 */
16100e474d75SJohannes Hofmann 				ieee80211_runtask(ic, &sc->sc_disassoctask);
16110e474d75SJohannes Hofmann 			}
16120e474d75SJohannes Hofmann 		}
16130e474d75SJohannes Hofmann 		break;
16140e474d75SJohannes Hofmann 
16150e474d75SJohannes Hofmann 	case IWI_NOTIF_TYPE_CALIBRATION:
16160e474d75SJohannes Hofmann 	case IWI_NOTIF_TYPE_NOISE:
1617c4fe7bb1SImre Vadász 		/* XXX handle? */
1618841ab66cSSepherosa Ziehau 		DPRINTFN(5, ("Notification (%u)\n", notif->type));
16190e474d75SJohannes Hofmann 		break;
1620c4fe7bb1SImre Vadász 	case IWI_NOTIF_TYPE_LINK_QUALITY:
1621c4fe7bb1SImre Vadász 		iwi_notif_link_quality(sc, notif);
1622c4fe7bb1SImre Vadász 		break;
16230e474d75SJohannes Hofmann 
16240e474d75SJohannes Hofmann 	default:
16250e474d75SJohannes Hofmann 		DPRINTF(("unknown notification type %u flags 0x%x len %u\n",
16260e474d75SJohannes Hofmann 		    notif->type, notif->flags, le16toh(notif->len)));
16270e474d75SJohannes Hofmann 		break;
1628b50e4759SMatthew Dillon 	}
1629b50e4759SMatthew Dillon }
1630b50e4759SMatthew Dillon 
1631b50e4759SMatthew Dillon static void
iwi_rx_intr(struct iwi_softc * sc)1632b50e4759SMatthew Dillon iwi_rx_intr(struct iwi_softc *sc)
1633b50e4759SMatthew Dillon {
1634841ab66cSSepherosa Ziehau 	struct iwi_rx_data *data;
1635b50e4759SMatthew Dillon 	struct iwi_hdr *hdr;
1636841ab66cSSepherosa Ziehau 	uint32_t hw;
1637b50e4759SMatthew Dillon 
1638841ab66cSSepherosa Ziehau 	hw = CSR_READ_4(sc, IWI_CSR_RX_RIDX);
1639b50e4759SMatthew Dillon 
1640841ab66cSSepherosa Ziehau 	for (; sc->rxq.cur != hw;) {
1641841ab66cSSepherosa Ziehau 		data = &sc->rxq.data[sc->rxq.cur];
1642b50e4759SMatthew Dillon 
1643841ab66cSSepherosa Ziehau 		bus_dmamap_sync(sc->rxq.data_dmat, data->map,
1644b50e4759SMatthew Dillon 		    BUS_DMASYNC_POSTREAD);
1645b50e4759SMatthew Dillon 
1646841ab66cSSepherosa Ziehau 		hdr = mtod(data->m, struct iwi_hdr *);
1647b50e4759SMatthew Dillon 
1648b50e4759SMatthew Dillon 		switch (hdr->type) {
1649b50e4759SMatthew Dillon 		case IWI_HDR_TYPE_FRAME:
1650841ab66cSSepherosa Ziehau 			iwi_frame_intr(sc, data, sc->rxq.cur,
1651b50e4759SMatthew Dillon 			    (struct iwi_frame *)(hdr + 1));
1652b50e4759SMatthew Dillon 			break;
1653b50e4759SMatthew Dillon 
1654b50e4759SMatthew Dillon 		case IWI_HDR_TYPE_NOTIF:
1655b50e4759SMatthew Dillon 			iwi_notification_intr(sc,
1656b50e4759SMatthew Dillon 			    (struct iwi_notif *)(hdr + 1));
1657b50e4759SMatthew Dillon 			break;
1658b50e4759SMatthew Dillon 
1659b50e4759SMatthew Dillon 		default:
1660b50e4759SMatthew Dillon 			device_printf(sc->sc_dev, "unknown hdr type %u\n",
1661b50e4759SMatthew Dillon 			    hdr->type);
1662b50e4759SMatthew Dillon 		}
1663841ab66cSSepherosa Ziehau 
1664841ab66cSSepherosa Ziehau 		DPRINTFN(15, ("rx done idx=%u\n", sc->rxq.cur));
1665841ab66cSSepherosa Ziehau 
1666841ab66cSSepherosa Ziehau 		sc->rxq.cur = (sc->rxq.cur + 1) % IWI_RX_RING_COUNT;
1667b50e4759SMatthew Dillon 	}
1668b50e4759SMatthew Dillon 
16690e474d75SJohannes Hofmann 	/* tell the firmware what we have processed */
1670841ab66cSSepherosa Ziehau 	hw = (hw == 0) ? IWI_RX_RING_COUNT - 1 : hw - 1;
1671841ab66cSSepherosa Ziehau 	CSR_WRITE_4(sc, IWI_CSR_RX_WIDX, hw);
1672b50e4759SMatthew Dillon }
1673b50e4759SMatthew Dillon 
1674b50e4759SMatthew Dillon static void
iwi_tx_intr(struct iwi_softc * sc,struct iwi_tx_ring * txq)1675841ab66cSSepherosa Ziehau iwi_tx_intr(struct iwi_softc *sc, struct iwi_tx_ring *txq)
1676b50e4759SMatthew Dillon {
1677841ab66cSSepherosa Ziehau 	struct iwi_tx_data *data;
1678841ab66cSSepherosa Ziehau 	uint32_t hw;
1679b50e4759SMatthew Dillon 
1680841ab66cSSepherosa Ziehau 	hw = CSR_READ_4(sc, txq->csr_ridx);
1681b50e4759SMatthew Dillon 
1682c4fe7bb1SImre Vadász 	while (txq->next != hw) {
1683841ab66cSSepherosa Ziehau 		data = &txq->data[txq->next];
1684c4fe7bb1SImre Vadász 		DPRINTFN(15, ("tx done idx=%u\n", txq->next));
1685841ab66cSSepherosa Ziehau 		bus_dmamap_sync(txq->data_dmat, data->map,
1686b50e4759SMatthew Dillon 		    BUS_DMASYNC_POSTWRITE);
1687841ab66cSSepherosa Ziehau 		bus_dmamap_unload(txq->data_dmat, data->map);
1688c4fe7bb1SImre Vadász 		ieee80211_tx_complete(data->ni, data->m, 0);
1689841ab66cSSepherosa Ziehau 		data->ni = NULL;
1690c4fe7bb1SImre Vadász 		data->m = NULL;
1691841ab66cSSepherosa Ziehau 		txq->queued--;
1692841ab66cSSepherosa Ziehau 		txq->next = (txq->next + 1) % IWI_TX_RING_COUNT;
1693b50e4759SMatthew Dillon 	}
1694841ab66cSSepherosa Ziehau 	sc->sc_tx_timer = 0;
16950e474d75SJohannes Hofmann 	if (sc->sc_softled)
16960e474d75SJohannes Hofmann 		iwi_led_event(sc, IWI_LED_TX);
1697c4fe7bb1SImre Vadász 	iwi_start(sc);
16980e474d75SJohannes Hofmann }
16990e474d75SJohannes Hofmann 
17000e474d75SJohannes Hofmann static void
iwi_fatal_error_intr(struct iwi_softc * sc)17010e474d75SJohannes Hofmann iwi_fatal_error_intr(struct iwi_softc *sc)
17020e474d75SJohannes Hofmann {
1703c4fe7bb1SImre Vadász 	struct ieee80211com *ic = &sc->sc_ic;
17040e474d75SJohannes Hofmann 	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
17050e474d75SJohannes Hofmann 
17060e474d75SJohannes Hofmann 	device_printf(sc->sc_dev, "firmware error\n");
17070e474d75SJohannes Hofmann 	if (vap != NULL)
17080e474d75SJohannes Hofmann 		ieee80211_cancel_scan(vap);
17090e474d75SJohannes Hofmann 	ieee80211_runtask(ic, &sc->sc_restarttask);
17100e474d75SJohannes Hofmann 
17110e474d75SJohannes Hofmann 	sc->flags &= ~IWI_FLAG_BUSY;
17120e474d75SJohannes Hofmann 	sc->sc_busy_timer = 0;
17130e474d75SJohannes Hofmann 	wakeup(sc);
17140e474d75SJohannes Hofmann }
17150e474d75SJohannes Hofmann 
17160e474d75SJohannes Hofmann static void
iwi_radio_off_intr(struct iwi_softc * sc)17170e474d75SJohannes Hofmann iwi_radio_off_intr(struct iwi_softc *sc)
17180e474d75SJohannes Hofmann {
17190e474d75SJohannes Hofmann 
1720c4fe7bb1SImre Vadász 	ieee80211_runtask(&sc->sc_ic, &sc->sc_radiofftask);
1721b50e4759SMatthew Dillon }
1722b50e4759SMatthew Dillon 
1723b50e4759SMatthew Dillon static void
iwi_intr(void * arg)1724b50e4759SMatthew Dillon iwi_intr(void *arg)
1725b50e4759SMatthew Dillon {
1726b50e4759SMatthew Dillon 	struct iwi_softc *sc = arg;
1727841ab66cSSepherosa Ziehau 	uint32_t r;
1728c4fe7bb1SImre Vadász 	IWI_LOCK_DECL;
1729c4fe7bb1SImre Vadász 
1730c4fe7bb1SImre Vadász 	IWI_LOCK(sc);
17310e474d75SJohannes Hofmann 
17320e474d75SJohannes Hofmann 	if ((r = CSR_READ_4(sc, IWI_CSR_INTR)) == 0 || r == 0xffffffff) {
1733c4fe7bb1SImre Vadász 		IWI_UNLOCK(sc);
1734b50e4759SMatthew Dillon 		return;
17350e474d75SJohannes Hofmann 	}
1736b50e4759SMatthew Dillon 
17370e474d75SJohannes Hofmann 	/* acknowledge interrupts */
17380e474d75SJohannes Hofmann 	CSR_WRITE_4(sc, IWI_CSR_INTR, r);
1739b50e4759SMatthew Dillon 
17403660cb22SSepherosa Ziehau 	if (r & IWI_INTR_FATAL_ERROR) {
17410e474d75SJohannes Hofmann 		iwi_fatal_error_intr(sc);
1742c4fe7bb1SImre Vadász 		goto done;
1743b50e4759SMatthew Dillon 	}
1744b50e4759SMatthew Dillon 
1745b50e4759SMatthew Dillon 	if (r & IWI_INTR_FW_INITED) {
1746b50e4759SMatthew Dillon 		if (!(r & (IWI_INTR_FATAL_ERROR | IWI_INTR_PARITY_ERROR)))
17470e474d75SJohannes Hofmann 			wakeup(sc);
1748b50e4759SMatthew Dillon 	}
1749b50e4759SMatthew Dillon 
17500e474d75SJohannes Hofmann 	if (r & IWI_INTR_RADIO_OFF)
17510e474d75SJohannes Hofmann 		iwi_radio_off_intr(sc);
1752b50e4759SMatthew Dillon 
17530e474d75SJohannes Hofmann 	if (r & IWI_INTR_CMD_DONE) {
17540e474d75SJohannes Hofmann 		sc->flags &= ~IWI_FLAG_BUSY;
17550e474d75SJohannes Hofmann 		sc->sc_busy_timer = 0;
17560e474d75SJohannes Hofmann 		wakeup(sc);
17570e474d75SJohannes Hofmann 	}
1758b50e4759SMatthew Dillon 
1759841ab66cSSepherosa Ziehau 	if (r & IWI_INTR_TX1_DONE)
1760841ab66cSSepherosa Ziehau 		iwi_tx_intr(sc, &sc->txq[0]);
1761b50e4759SMatthew Dillon 
1762841ab66cSSepherosa Ziehau 	if (r & IWI_INTR_TX2_DONE)
1763841ab66cSSepherosa Ziehau 		iwi_tx_intr(sc, &sc->txq[1]);
1764b50e4759SMatthew Dillon 
1765841ab66cSSepherosa Ziehau 	if (r & IWI_INTR_TX3_DONE)
1766841ab66cSSepherosa Ziehau 		iwi_tx_intr(sc, &sc->txq[2]);
1767841ab66cSSepherosa Ziehau 
1768841ab66cSSepherosa Ziehau 	if (r & IWI_INTR_TX4_DONE)
1769841ab66cSSepherosa Ziehau 		iwi_tx_intr(sc, &sc->txq[3]);
1770841ab66cSSepherosa Ziehau 
1771841ab66cSSepherosa Ziehau 	if (r & IWI_INTR_RX_DONE)
1772841ab66cSSepherosa Ziehau 		iwi_rx_intr(sc);
1773841ab66cSSepherosa Ziehau 
17740e474d75SJohannes Hofmann 	if (r & IWI_INTR_PARITY_ERROR) {
17750e474d75SJohannes Hofmann 		/* XXX rate-limit */
17760e474d75SJohannes Hofmann 		device_printf(sc->sc_dev, "parity error\n");
17770e474d75SJohannes Hofmann 	}
1778c4fe7bb1SImre Vadász done:
1779c4fe7bb1SImre Vadász 	IWI_UNLOCK(sc);
1780841ab66cSSepherosa Ziehau }
1781b50e4759SMatthew Dillon 
1782841ab66cSSepherosa Ziehau static int
iwi_cmd(struct iwi_softc * sc,uint8_t type,void * data,uint8_t len)17830e474d75SJohannes Hofmann iwi_cmd(struct iwi_softc *sc, uint8_t type, void *data, uint8_t len)
1784841ab66cSSepherosa Ziehau {
1785841ab66cSSepherosa Ziehau 	struct iwi_cmd_desc *desc;
17860e474d75SJohannes Hofmann 
1787c4fe7bb1SImre Vadász 	IWI_LOCK_ASSERT(sc);
1788c4fe7bb1SImre Vadász 
17890e474d75SJohannes Hofmann 	if (sc->flags & IWI_FLAG_BUSY) {
17900e474d75SJohannes Hofmann 		device_printf(sc->sc_dev, "%s: cmd %d not sent, busy\n",
17910e474d75SJohannes Hofmann 			__func__, type);
17920e474d75SJohannes Hofmann 		return EAGAIN;
17930e474d75SJohannes Hofmann 	}
17940e474d75SJohannes Hofmann 	sc->flags |= IWI_FLAG_BUSY;
17950e474d75SJohannes Hofmann 	sc->sc_busy_timer = 2;
1796841ab66cSSepherosa Ziehau 
1797841ab66cSSepherosa Ziehau 	desc = &sc->cmdq.desc[sc->cmdq.cur];
1798841ab66cSSepherosa Ziehau 
1799841ab66cSSepherosa Ziehau 	desc->hdr.type = IWI_HDR_TYPE_COMMAND;
1800841ab66cSSepherosa Ziehau 	desc->hdr.flags = IWI_HDR_FLAG_IRQ;
1801841ab66cSSepherosa Ziehau 	desc->type = type;
1802841ab66cSSepherosa Ziehau 	desc->len = len;
1803841ab66cSSepherosa Ziehau 	memcpy(desc->data, data, len);
1804841ab66cSSepherosa Ziehau 
1805841ab66cSSepherosa Ziehau 	bus_dmamap_sync(sc->cmdq.desc_dmat, sc->cmdq.desc_map,
1806841ab66cSSepherosa Ziehau 	    BUS_DMASYNC_PREWRITE);
1807841ab66cSSepherosa Ziehau 
1808841ab66cSSepherosa Ziehau 	DPRINTFN(2, ("sending command idx=%u type=%u len=%u\n", sc->cmdq.cur,
1809841ab66cSSepherosa Ziehau 	    type, len));
1810841ab66cSSepherosa Ziehau 
1811841ab66cSSepherosa Ziehau 	sc->cmdq.cur = (sc->cmdq.cur + 1) % IWI_CMD_RING_COUNT;
1812841ab66cSSepherosa Ziehau 	CSR_WRITE_4(sc, IWI_CSR_CMD_WIDX, sc->cmdq.cur);
1813841ab66cSSepherosa Ziehau 
1814c4fe7bb1SImre Vadász #if defined(__DragonFly__)
1815c4fe7bb1SImre Vadász 	return lksleep(sc, &sc->sc_lock, 0, "iwicmd", hz);
1816c4fe7bb1SImre Vadász #else
1817c4fe7bb1SImre Vadász 	return msleep(sc, &sc->sc_mtx, 0, "iwicmd", hz);
1818c4fe7bb1SImre Vadász #endif
1819841ab66cSSepherosa Ziehau }
1820841ab66cSSepherosa Ziehau 
1821841ab66cSSepherosa Ziehau static void
iwi_write_ibssnode(struct iwi_softc * sc,const u_int8_t addr[IEEE80211_ADDR_LEN],int entry)18220e474d75SJohannes Hofmann iwi_write_ibssnode(struct iwi_softc *sc,
18230e474d75SJohannes Hofmann 	const u_int8_t addr[IEEE80211_ADDR_LEN], int entry)
1824841ab66cSSepherosa Ziehau {
1825841ab66cSSepherosa Ziehau 	struct iwi_ibssnode node;
1826841ab66cSSepherosa Ziehau 
1827841ab66cSSepherosa Ziehau 	/* write node information into NIC memory */
1828841ab66cSSepherosa Ziehau 	memset(&node, 0, sizeof node);
18290e474d75SJohannes Hofmann 	IEEE80211_ADDR_COPY(node.bssid, addr);
1830c4fe7bb1SImre Vadász #if defined(__DragonFly__)
1831c4fe7bb1SImre Vadász 	DPRINTF(("%s mac %s station %u\n", __func__, ether_sprintf(node.bssid),
1832c4fe7bb1SImre Vadász 	    entry));
1833c4fe7bb1SImre Vadász #else
1834c4fe7bb1SImre Vadász 	DPRINTF(("%s mac %6D station %u\n", __func__, node.bssid, ":", entry));
1835c4fe7bb1SImre Vadász #endif
1836841ab66cSSepherosa Ziehau 
1837841ab66cSSepherosa Ziehau 	CSR_WRITE_REGION_1(sc,
18380e474d75SJohannes Hofmann 	    IWI_CSR_NODE_BASE + entry * sizeof node,
1839841ab66cSSepherosa Ziehau 	    (uint8_t *)&node, sizeof node);
1840b50e4759SMatthew Dillon }
1841b50e4759SMatthew Dillon 
1842b50e4759SMatthew Dillon static int
iwi_tx_start(struct iwi_softc * sc,struct mbuf * m0,struct ieee80211_node * ni,int ac)1843c4fe7bb1SImre Vadász iwi_tx_start(struct iwi_softc *sc, struct mbuf *m0, struct ieee80211_node *ni,
1844841ab66cSSepherosa Ziehau     int ac)
1845b50e4759SMatthew Dillon {
18460e474d75SJohannes Hofmann 	struct ieee80211vap *vap = ni->ni_vap;
18470e474d75SJohannes Hofmann 	struct ieee80211com *ic = ni->ni_ic;
1848841ab66cSSepherosa Ziehau 	struct iwi_node *in = (struct iwi_node *)ni;
18490e474d75SJohannes Hofmann 	const struct ieee80211_frame *wh;
1850841ab66cSSepherosa Ziehau 	struct ieee80211_key *k;
1851841ab66cSSepherosa Ziehau 	const struct chanAccParams *cap;
1852841ab66cSSepherosa Ziehau 	struct iwi_tx_ring *txq = &sc->txq[ac];
1853841ab66cSSepherosa Ziehau 	struct iwi_tx_data *data;
1854b50e4759SMatthew Dillon 	struct iwi_tx_desc *desc;
1855b50e4759SMatthew Dillon 	struct mbuf *mnew;
18560e474d75SJohannes Hofmann 	bus_dma_segment_t segs[IWI_MAX_NSEG];
18570e474d75SJohannes Hofmann 	int error, nsegs, hdrlen, i;
18580e474d75SJohannes Hofmann 	int ismcast, flags, xflags, staid;
1859841ab66cSSepherosa Ziehau 
1860c4fe7bb1SImre Vadász 	IWI_LOCK_ASSERT(sc);
18610e474d75SJohannes Hofmann 	wh = mtod(m0, const struct ieee80211_frame *);
18620e474d75SJohannes Hofmann 	/* NB: only data frames use this path */
18630e474d75SJohannes Hofmann 	hdrlen = ieee80211_hdrsize(wh);
18640e474d75SJohannes Hofmann 	ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1);
18650e474d75SJohannes Hofmann 	flags = xflags = 0;
1866841ab66cSSepherosa Ziehau 
18670e474d75SJohannes Hofmann 	if (!ismcast)
18680e474d75SJohannes Hofmann 		flags |= IWI_DATA_FLAG_NEED_ACK;
18690e474d75SJohannes Hofmann 	if (vap->iv_flags & IEEE80211_F_SHPREAMBLE)
18700e474d75SJohannes Hofmann 		flags |= IWI_DATA_FLAG_SHPREAMBLE;
18710e474d75SJohannes Hofmann 	if (IEEE80211_QOS_HAS_SEQ(wh)) {
18720e474d75SJohannes Hofmann 		xflags |= IWI_DATA_XFLAG_QOS;
1873841ab66cSSepherosa Ziehau 		cap = &ic->ic_wme.wme_chanParams;
18740e474d75SJohannes Hofmann 		if (!cap->cap_wmeParams[ac].wmep_noackPolicy)
18750e474d75SJohannes Hofmann 			flags &= ~IWI_DATA_FLAG_NEED_ACK;
18760e474d75SJohannes Hofmann 	}
1877841ab66cSSepherosa Ziehau 
1878841ab66cSSepherosa Ziehau 	/*
1879841ab66cSSepherosa Ziehau 	 * This is only used in IBSS mode where the firmware expect an index
1880841ab66cSSepherosa Ziehau 	 * in a h/w table instead of a destination address.
1881841ab66cSSepherosa Ziehau 	 */
18820e474d75SJohannes Hofmann 	if (vap->iv_opmode == IEEE80211_M_IBSS) {
18830e474d75SJohannes Hofmann 		if (!ismcast) {
18840e474d75SJohannes Hofmann 			if (in->in_station == -1) {
1885c4fe7bb1SImre Vadász #if defined(__DragonFly__)
18860e474d75SJohannes Hofmann 				in->in_station = devfs_clone_bitmap_get(&sc->sc_unr,
18870e474d75SJohannes Hofmann 					IWI_MAX_IBSSNODE-1);
1888c4fe7bb1SImre Vadász #else
1889c4fe7bb1SImre Vadász 				in->in_station = alloc_unr(sc->sc_unr);
1890c4fe7bb1SImre Vadász #endif
18910e474d75SJohannes Hofmann 				if (in->in_station == -1) {
18920e474d75SJohannes Hofmann 					/* h/w table is full */
1893c4fe7bb1SImre Vadász 					if_inc_counter(ni->ni_vap->iv_ifp,
1894c4fe7bb1SImre Vadász 					    IFCOUNTER_OERRORS, 1);
1895841ab66cSSepherosa Ziehau 					m_freem(m0);
1896841ab66cSSepherosa Ziehau 					ieee80211_free_node(ni);
1897841ab66cSSepherosa Ziehau 					return 0;
1898841ab66cSSepherosa Ziehau 				}
18990e474d75SJohannes Hofmann 				iwi_write_ibssnode(sc,
19000e474d75SJohannes Hofmann 					ni->ni_macaddr, in->in_station);
1901841ab66cSSepherosa Ziehau 			}
19020e474d75SJohannes Hofmann 			staid = in->in_station;
19030e474d75SJohannes Hofmann 		} else {
19040e474d75SJohannes Hofmann 			/*
19050e474d75SJohannes Hofmann 			 * Multicast addresses have no associated node
19060e474d75SJohannes Hofmann 			 * so there will be no station entry.  We reserve
19070e474d75SJohannes Hofmann 			 * entry 0 for one mcast address and use that.
19080e474d75SJohannes Hofmann 			 * If there are many being used this will be
19090e474d75SJohannes Hofmann 			 * expensive and we'll need to do a better job
19100e474d75SJohannes Hofmann 			 * but for now this handles the broadcast case.
19110e474d75SJohannes Hofmann 			 */
19120e474d75SJohannes Hofmann 			if (!IEEE80211_ADDR_EQ(wh->i_addr1, sc->sc_mcast)) {
19130e474d75SJohannes Hofmann 				IEEE80211_ADDR_COPY(sc->sc_mcast, wh->i_addr1);
19140e474d75SJohannes Hofmann 				iwi_write_ibssnode(sc, sc->sc_mcast, 0);
19150e474d75SJohannes Hofmann 			}
19160e474d75SJohannes Hofmann 			staid = 0;
19170e474d75SJohannes Hofmann 		}
19180e474d75SJohannes Hofmann 	} else
19190e474d75SJohannes Hofmann 		staid = 0;
1920841ab66cSSepherosa Ziehau 
1921085ff963SMatthew Dillon 	if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
19220e474d75SJohannes Hofmann 		k = ieee80211_crypto_encap(ni, m0);
1923841ab66cSSepherosa Ziehau 		if (k == NULL) {
1924841ab66cSSepherosa Ziehau 			m_freem(m0);
1925841ab66cSSepherosa Ziehau 			return ENOBUFS;
1926841ab66cSSepherosa Ziehau 		}
19272a0b1ca7SSascha Wildner 
19282a0b1ca7SSascha Wildner 		/* packet header may have moved, reset our local pointer */
19292a0b1ca7SSascha Wildner 		wh = mtod(m0, struct ieee80211_frame *);
1930841ab66cSSepherosa Ziehau 	}
1931b50e4759SMatthew Dillon 
19320e474d75SJohannes Hofmann 	if (ieee80211_radiotap_active_vap(vap)) {
1933b50e4759SMatthew Dillon 		struct iwi_tx_radiotap_header *tap = &sc->sc_txtap;
1934b50e4759SMatthew Dillon 
1935b50e4759SMatthew Dillon 		tap->wt_flags = 0;
1936b50e4759SMatthew Dillon 
19370e474d75SJohannes Hofmann 		ieee80211_radiotap_tx(vap, m0);
1938b50e4759SMatthew Dillon 	}
1939b50e4759SMatthew Dillon 
1940841ab66cSSepherosa Ziehau 	data = &txq->data[txq->cur];
1941841ab66cSSepherosa Ziehau 	desc = &txq->desc[txq->cur];
1942b50e4759SMatthew Dillon 
1943841ab66cSSepherosa Ziehau 	/* save and trim IEEE802.11 header */
1944*05d02a38SAaron LI 	m_copydata(m0, 0, hdrlen, &desc->wh);
1945841ab66cSSepherosa Ziehau 	m_adj(m0, hdrlen);
1946b50e4759SMatthew Dillon 
1947c4fe7bb1SImre Vadász #if defined(__DragonFly__)
19480e474d75SJohannes Hofmann 	error = bus_dmamap_load_mbuf_segment(txq->data_dmat, data->map,
19490e474d75SJohannes Hofmann 	    m0, segs, 1, &nsegs, BUS_DMA_NOWAIT);
1950c4fe7bb1SImre Vadász #else
1951c4fe7bb1SImre Vadász 	error = bus_dmamap_load_mbuf_sg(txq->data_dmat, data->map, m0, segs,
1952c4fe7bb1SImre Vadász 	    &nsegs, 0);
1953c4fe7bb1SImre Vadász #endif
1954b50e4759SMatthew Dillon 	if (error != 0 && error != EFBIG) {
1955b50e4759SMatthew Dillon 		device_printf(sc->sc_dev, "could not map mbuf (error %d)\n",
1956b50e4759SMatthew Dillon 		    error);
1957b50e4759SMatthew Dillon 		m_freem(m0);
1958b50e4759SMatthew Dillon 		return error;
1959b50e4759SMatthew Dillon 	}
1960b50e4759SMatthew Dillon 	if (error != 0) {
1961b5523eacSSascha Wildner 		mnew = m_defrag(m0, M_NOWAIT);
1962b50e4759SMatthew Dillon 		if (mnew == NULL) {
1963b50e4759SMatthew Dillon 			device_printf(sc->sc_dev,
1964b50e4759SMatthew Dillon 			    "could not defragment mbuf\n");
1965b50e4759SMatthew Dillon 			m_freem(m0);
1966b50e4759SMatthew Dillon 			return ENOBUFS;
1967b50e4759SMatthew Dillon 		}
1968b50e4759SMatthew Dillon 		m0 = mnew;
1969b50e4759SMatthew Dillon 
1970c4fe7bb1SImre Vadász #if defined(__DragonFly__)
19710e474d75SJohannes Hofmann 		error = bus_dmamap_load_mbuf_segment(txq->data_dmat,
19720e474d75SJohannes Hofmann 		    data->map, m0, segs, 1, &nsegs, BUS_DMA_NOWAIT);
1973c4fe7bb1SImre Vadász #else
1974c4fe7bb1SImre Vadász 		error = bus_dmamap_load_mbuf_sg(txq->data_dmat, data->map,
1975c4fe7bb1SImre Vadász 		    m0, segs, &nsegs, 0);
1976c4fe7bb1SImre Vadász #endif
1977b50e4759SMatthew Dillon 		if (error != 0) {
1978b50e4759SMatthew Dillon 			device_printf(sc->sc_dev,
1979b50e4759SMatthew Dillon 			    "could not map mbuf (error %d)\n", error);
1980b50e4759SMatthew Dillon 			m_freem(m0);
1981b50e4759SMatthew Dillon 			return error;
1982b50e4759SMatthew Dillon 		}
1983b50e4759SMatthew Dillon 	}
1984b50e4759SMatthew Dillon 
1985841ab66cSSepherosa Ziehau 	data->m = m0;
1986841ab66cSSepherosa Ziehau 	data->ni = ni;
1987b50e4759SMatthew Dillon 
1988b50e4759SMatthew Dillon 	desc->hdr.type = IWI_HDR_TYPE_DATA;
1989b50e4759SMatthew Dillon 	desc->hdr.flags = IWI_HDR_FLAG_IRQ;
19900e474d75SJohannes Hofmann 	desc->station = staid;
1991b50e4759SMatthew Dillon 	desc->cmd = IWI_DATA_CMD_TX;
1992b50e4759SMatthew Dillon 	desc->len = htole16(m0->m_pkthdr.len);
19930e474d75SJohannes Hofmann 	desc->flags = flags;
19940e474d75SJohannes Hofmann 	desc->xflags = xflags;
1995b50e4759SMatthew Dillon 
1996841ab66cSSepherosa Ziehau #if 0
19970e474d75SJohannes Hofmann 	if (vap->iv_flags & IEEE80211_F_PRIVACY)
19980e474d75SJohannes Hofmann 		desc->wep_txkey = vap->iv_def_txkey;
19990e474d75SJohannes Hofmann 	else
2000841ab66cSSepherosa Ziehau #endif
2001b50e4759SMatthew Dillon 		desc->flags |= IWI_DATA_FLAG_NO_WEP;
2002b50e4759SMatthew Dillon 
20030e474d75SJohannes Hofmann 	desc->nseg = htole32(nsegs);
20040e474d75SJohannes Hofmann 	for (i = 0; i < nsegs; i++) {
20050e474d75SJohannes Hofmann 		desc->seg_addr[i] = htole32(segs[i].ds_addr);
20060e474d75SJohannes Hofmann 		desc->seg_len[i]  = htole16(segs[i].ds_len);
2007b50e4759SMatthew Dillon 	}
2008b50e4759SMatthew Dillon 
2009841ab66cSSepherosa Ziehau 	bus_dmamap_sync(txq->data_dmat, data->map, BUS_DMASYNC_PREWRITE);
2010841ab66cSSepherosa Ziehau 	bus_dmamap_sync(txq->desc_dmat, txq->desc_map, BUS_DMASYNC_PREWRITE);
2011b50e4759SMatthew Dillon 
2012841ab66cSSepherosa Ziehau 	DPRINTFN(5, ("sending data frame txq=%u idx=%u len=%u nseg=%u\n",
20130e474d75SJohannes Hofmann 	    ac, txq->cur, le16toh(desc->len), nsegs));
2014b50e4759SMatthew Dillon 
2015841ab66cSSepherosa Ziehau 	txq->queued++;
2016841ab66cSSepherosa Ziehau 	txq->cur = (txq->cur + 1) % IWI_TX_RING_COUNT;
2017841ab66cSSepherosa Ziehau 	CSR_WRITE_4(sc, txq->csr_widx, txq->cur);
2018b50e4759SMatthew Dillon 
2019b50e4759SMatthew Dillon 	return 0;
2020b50e4759SMatthew Dillon }
2021b50e4759SMatthew Dillon 
20220e474d75SJohannes Hofmann static int
iwi_raw_xmit(struct ieee80211_node * ni,struct mbuf * m,const struct ieee80211_bpf_params * params)20230e474d75SJohannes Hofmann iwi_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
20240e474d75SJohannes Hofmann 	const struct ieee80211_bpf_params *params)
20250e474d75SJohannes Hofmann {
20260e474d75SJohannes Hofmann 	/* no support; just discard */
20270e474d75SJohannes Hofmann 	m_freem(m);
20280e474d75SJohannes Hofmann 	ieee80211_free_node(ni);
20290e474d75SJohannes Hofmann 	return 0;
20300e474d75SJohannes Hofmann }
20310e474d75SJohannes Hofmann 
2032c4fe7bb1SImre Vadász static int
iwi_transmit(struct ieee80211com * ic,struct mbuf * m)2033c4fe7bb1SImre Vadász iwi_transmit(struct ieee80211com *ic, struct mbuf *m)
2034b50e4759SMatthew Dillon {
2035c4fe7bb1SImre Vadász 	struct iwi_softc *sc = ic->ic_softc;
2036c4fe7bb1SImre Vadász 	int error;
2037c4fe7bb1SImre Vadász 	IWI_LOCK_DECL;
2038c4fe7bb1SImre Vadász 
2039c4fe7bb1SImre Vadász 	IWI_LOCK(sc);
2040c4fe7bb1SImre Vadász 	if (!sc->sc_running) {
2041c4fe7bb1SImre Vadász 		IWI_UNLOCK(sc);
2042c4fe7bb1SImre Vadász 		return (ENXIO);
2043c4fe7bb1SImre Vadász 	}
2044c4fe7bb1SImre Vadász 	error = mbufq_enqueue(&sc->sc_snd, m);
2045c4fe7bb1SImre Vadász 	if (error) {
2046c4fe7bb1SImre Vadász 		IWI_UNLOCK(sc);
2047c4fe7bb1SImre Vadász 		return (error);
2048c4fe7bb1SImre Vadász 	}
2049c4fe7bb1SImre Vadász 	iwi_start(sc);
2050c4fe7bb1SImre Vadász 	IWI_UNLOCK(sc);
2051c4fe7bb1SImre Vadász 	return (0);
2052c4fe7bb1SImre Vadász }
2053c4fe7bb1SImre Vadász 
2054c4fe7bb1SImre Vadász static void
iwi_start(struct iwi_softc * sc)2055c4fe7bb1SImre Vadász iwi_start(struct iwi_softc *sc)
2056c4fe7bb1SImre Vadász {
20570e474d75SJohannes Hofmann 	struct mbuf *m;
2058b50e4759SMatthew Dillon 	struct ieee80211_node *ni;
2059841ab66cSSepherosa Ziehau 	int ac;
2060b50e4759SMatthew Dillon 
2061c4fe7bb1SImre Vadász 	IWI_LOCK_ASSERT(sc);
2062b866247bSSepherosa Ziehau 
2063c4fe7bb1SImre Vadász 	while ((m =  mbufq_dequeue(&sc->sc_snd)) != NULL) {
20640e474d75SJohannes Hofmann 		ac = M_WME_GETAC(m);
2065841ab66cSSepherosa Ziehau 		if (sc->txq[ac].queued > IWI_TX_RING_COUNT - 8) {
20660e474d75SJohannes Hofmann 			/* there is no place left in this ring; tail drop */
20670e474d75SJohannes Hofmann 			/* XXX tail drop */
2068c4fe7bb1SImre Vadász 			mbufq_prepend(&sc->sc_snd, m);
2069b50e4759SMatthew Dillon 			break;
2070b50e4759SMatthew Dillon 		}
20710e474d75SJohannes Hofmann 		ni = (struct ieee80211_node *) m->m_pkthdr.rcvif;
2072c4fe7bb1SImre Vadász 		if (iwi_tx_start(sc, m, ni, ac) != 0) {
2073841ab66cSSepherosa Ziehau 			ieee80211_free_node(ni);
2074c4fe7bb1SImre Vadász 			if_inc_counter(ni->ni_vap->iv_ifp,
2075c4fe7bb1SImre Vadász 			    IFCOUNTER_OERRORS, 1);
2076b50e4759SMatthew Dillon 			break;
2077b50e4759SMatthew Dillon 		}
2078b50e4759SMatthew Dillon 		sc->sc_tx_timer = 5;
2079b50e4759SMatthew Dillon 	}
2080b50e4759SMatthew Dillon }
2081b50e4759SMatthew Dillon 
2082b50e4759SMatthew Dillon static void
iwi_watchdog(void * arg)20830e474d75SJohannes Hofmann iwi_watchdog(void *arg)
20840e474d75SJohannes Hofmann {
20850e474d75SJohannes Hofmann 	struct iwi_softc *sc = arg;
2086c4fe7bb1SImre Vadász 	struct ieee80211com *ic = &sc->sc_ic;
20870e474d75SJohannes Hofmann 
2088c4fe7bb1SImre Vadász 	IWI_LOCK_ASSERT(sc);
2089c4fe7bb1SImre Vadász 
2090b50e4759SMatthew Dillon 	if (sc->sc_tx_timer > 0) {
2091b50e4759SMatthew Dillon 		if (--sc->sc_tx_timer == 0) {
2092c4fe7bb1SImre Vadász 			device_printf(sc->sc_dev, "device timeout\n");
2093c4fe7bb1SImre Vadász #if defined(__DragonFly__)
2094c4fe7bb1SImre Vadász 			++ic->ic_oerrors;
2095c4fe7bb1SImre Vadász #else
2096c4fe7bb1SImre Vadász 			counter_u64_add(ic->ic_oerrors, 1);
2097c4fe7bb1SImre Vadász #endif
20980e474d75SJohannes Hofmann 			ieee80211_runtask(ic, &sc->sc_restarttask);
2099b50e4759SMatthew Dillon 		}
2100b50e4759SMatthew Dillon 	}
21010e474d75SJohannes Hofmann 	if (sc->sc_state_timer > 0) {
21020e474d75SJohannes Hofmann 		if (--sc->sc_state_timer == 0) {
2103c4fe7bb1SImre Vadász 			device_printf(sc->sc_dev,
2104c4fe7bb1SImre Vadász 			    "firmware stuck in state %d, resetting\n",
21050e474d75SJohannes Hofmann 			    sc->fw_state);
2106c4fe7bb1SImre Vadász 			if (sc->fw_state == IWI_FW_SCANNING)
21070e474d75SJohannes Hofmann 				ieee80211_cancel_scan(TAILQ_FIRST(&ic->ic_vaps));
21080e474d75SJohannes Hofmann 			ieee80211_runtask(ic, &sc->sc_restarttask);
21090e474d75SJohannes Hofmann 			sc->sc_state_timer = 3;
21100e474d75SJohannes Hofmann 		}
21110e474d75SJohannes Hofmann 	}
21120e474d75SJohannes Hofmann 	if (sc->sc_busy_timer > 0) {
21130e474d75SJohannes Hofmann 		if (--sc->sc_busy_timer == 0) {
2114c4fe7bb1SImre Vadász 			device_printf(sc->sc_dev,
2115c4fe7bb1SImre Vadász 			    "firmware command timeout, resetting\n");
21160e474d75SJohannes Hofmann 			ieee80211_runtask(ic, &sc->sc_restarttask);
21170e474d75SJohannes Hofmann 		}
21180e474d75SJohannes Hofmann 	}
2119c4fe7bb1SImre Vadász 	callout_reset(&sc->sc_wdtimer, hz, iwi_watchdog, sc);
2120b50e4759SMatthew Dillon }
2121b50e4759SMatthew Dillon 
2122c4fe7bb1SImre Vadász static void
iwi_parent(struct ieee80211com * ic)2123c4fe7bb1SImre Vadász iwi_parent(struct ieee80211com *ic)
2124b50e4759SMatthew Dillon {
2125c4fe7bb1SImre Vadász 	struct iwi_softc *sc = ic->ic_softc;
2126c4fe7bb1SImre Vadász 	int startall = 0;
2127c4fe7bb1SImre Vadász 	IWI_LOCK_DECL;
2128b50e4759SMatthew Dillon 
2129c4fe7bb1SImre Vadász 	IWI_LOCK(sc);
2130c4fe7bb1SImre Vadász 	if (ic->ic_nrunning > 0) {
2131c4fe7bb1SImre Vadász 		if (!sc->sc_running) {
21320e474d75SJohannes Hofmann 			iwi_init_locked(sc);
21330e474d75SJohannes Hofmann 			startall = 1;
21340e474d75SJohannes Hofmann 		}
2135c4fe7bb1SImre Vadász 	} else if (sc->sc_running)
21360e474d75SJohannes Hofmann 		iwi_stop_locked(sc);
2137c4fe7bb1SImre Vadász 	IWI_UNLOCK(sc);
21380e474d75SJohannes Hofmann 	if (startall)
21390e474d75SJohannes Hofmann 		ieee80211_start_all(ic);
2140b50e4759SMatthew Dillon }
2141b50e4759SMatthew Dillon 
2142b50e4759SMatthew Dillon static void
iwi_stop_master(struct iwi_softc * sc)2143b50e4759SMatthew Dillon iwi_stop_master(struct iwi_softc *sc)
2144b50e4759SMatthew Dillon {
2145841ab66cSSepherosa Ziehau 	uint32_t tmp;
2146b50e4759SMatthew Dillon 	int ntries;
2147b50e4759SMatthew Dillon 
2148841ab66cSSepherosa Ziehau 	/* disable interrupts */
2149b50e4759SMatthew Dillon 	CSR_WRITE_4(sc, IWI_CSR_INTR_MASK, 0);
2150b50e4759SMatthew Dillon 
2151b50e4759SMatthew Dillon 	CSR_WRITE_4(sc, IWI_CSR_RST, IWI_RST_STOP_MASTER);
2152b50e4759SMatthew Dillon 	for (ntries = 0; ntries < 5; ntries++) {
2153b50e4759SMatthew Dillon 		if (CSR_READ_4(sc, IWI_CSR_RST) & IWI_RST_MASTER_DISABLED)
2154b50e4759SMatthew Dillon 			break;
2155b50e4759SMatthew Dillon 		DELAY(10);
2156b50e4759SMatthew Dillon 	}
2157841ab66cSSepherosa Ziehau 	if (ntries == 5)
21580e474d75SJohannes Hofmann 		device_printf(sc->sc_dev, "timeout waiting for master\n");
2159b50e4759SMatthew Dillon 
2160841ab66cSSepherosa Ziehau 	tmp = CSR_READ_4(sc, IWI_CSR_RST);
2161841ab66cSSepherosa Ziehau 	CSR_WRITE_4(sc, IWI_CSR_RST, tmp | IWI_RST_PRINCETON_RESET);
2162b50e4759SMatthew Dillon 
2163b50e4759SMatthew Dillon 	sc->flags &= ~IWI_FLAG_FW_INITED;
2164b50e4759SMatthew Dillon }
2165b50e4759SMatthew Dillon 
2166b50e4759SMatthew Dillon static int
iwi_reset(struct iwi_softc * sc)2167b50e4759SMatthew Dillon iwi_reset(struct iwi_softc *sc)
2168b50e4759SMatthew Dillon {
2169841ab66cSSepherosa Ziehau 	uint32_t tmp;
2170b50e4759SMatthew Dillon 	int i, ntries;
2171b50e4759SMatthew Dillon 
2172b50e4759SMatthew Dillon 	iwi_stop_master(sc);
2173b50e4759SMatthew Dillon 
2174841ab66cSSepherosa Ziehau 	tmp = CSR_READ_4(sc, IWI_CSR_CTL);
2175841ab66cSSepherosa Ziehau 	CSR_WRITE_4(sc, IWI_CSR_CTL, tmp | IWI_CTL_INIT);
2176b50e4759SMatthew Dillon 
2177b50e4759SMatthew Dillon 	CSR_WRITE_4(sc, IWI_CSR_READ_INT, IWI_READ_INT_INIT_HOST);
2178b50e4759SMatthew Dillon 
21790e474d75SJohannes Hofmann 	/* wait for clock stabilization */
2180b50e4759SMatthew Dillon 	for (ntries = 0; ntries < 1000; ntries++) {
2181b50e4759SMatthew Dillon 		if (CSR_READ_4(sc, IWI_CSR_CTL) & IWI_CTL_CLOCK_READY)
2182b50e4759SMatthew Dillon 			break;
2183b50e4759SMatthew Dillon 		DELAY(200);
2184b50e4759SMatthew Dillon 	}
2185b50e4759SMatthew Dillon 	if (ntries == 1000) {
21860e474d75SJohannes Hofmann 		device_printf(sc->sc_dev,
2187841ab66cSSepherosa Ziehau 		    "timeout waiting for clock stabilization\n");
2188b50e4759SMatthew Dillon 		return EIO;
2189b50e4759SMatthew Dillon 	}
2190b50e4759SMatthew Dillon 
2191841ab66cSSepherosa Ziehau 	tmp = CSR_READ_4(sc, IWI_CSR_RST);
2192841ab66cSSepherosa Ziehau 	CSR_WRITE_4(sc, IWI_CSR_RST, tmp | IWI_RST_SOFT_RESET);
2193b50e4759SMatthew Dillon 
2194b50e4759SMatthew Dillon 	DELAY(10);
2195b50e4759SMatthew Dillon 
2196841ab66cSSepherosa Ziehau 	tmp = CSR_READ_4(sc, IWI_CSR_CTL);
2197841ab66cSSepherosa Ziehau 	CSR_WRITE_4(sc, IWI_CSR_CTL, tmp | IWI_CTL_INIT);
2198b50e4759SMatthew Dillon 
21990e474d75SJohannes Hofmann 	/* clear NIC memory */
2200b50e4759SMatthew Dillon 	CSR_WRITE_4(sc, IWI_CSR_AUTOINC_ADDR, 0);
2201841ab66cSSepherosa Ziehau 	for (i = 0; i < 0xc000; i++)
2202b50e4759SMatthew Dillon 		CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, 0);
2203b50e4759SMatthew Dillon 
2204b50e4759SMatthew Dillon 	return 0;
2205b50e4759SMatthew Dillon }
2206b50e4759SMatthew Dillon 
22070e474d75SJohannes Hofmann static const struct iwi_firmware_ohdr *
iwi_setup_ofw(struct iwi_softc * sc,struct iwi_fw * fw)22080e474d75SJohannes Hofmann iwi_setup_ofw(struct iwi_softc *sc, struct iwi_fw *fw)
22090e474d75SJohannes Hofmann {
22100e474d75SJohannes Hofmann 	const struct firmware *fp = fw->fp;
22110e474d75SJohannes Hofmann 	const struct iwi_firmware_ohdr *hdr;
22120e474d75SJohannes Hofmann 
22130e474d75SJohannes Hofmann 	if (fp->datasize < sizeof (struct iwi_firmware_ohdr)) {
22140e474d75SJohannes Hofmann 		device_printf(sc->sc_dev, "image '%s' too small\n", fp->name);
22150e474d75SJohannes Hofmann 		return NULL;
22160e474d75SJohannes Hofmann 	}
22170e474d75SJohannes Hofmann 	hdr = (const struct iwi_firmware_ohdr *)fp->data;
22180e474d75SJohannes Hofmann 	if ((IWI_FW_GET_MAJOR(le32toh(hdr->version)) != IWI_FW_REQ_MAJOR) ||
22190e474d75SJohannes Hofmann 	    (IWI_FW_GET_MINOR(le32toh(hdr->version)) != IWI_FW_REQ_MINOR)) {
22200e474d75SJohannes Hofmann 		device_printf(sc->sc_dev, "version for '%s' %d.%d != %d.%d\n",
22210e474d75SJohannes Hofmann 		    fp->name, IWI_FW_GET_MAJOR(le32toh(hdr->version)),
22220e474d75SJohannes Hofmann 		    IWI_FW_GET_MINOR(le32toh(hdr->version)), IWI_FW_REQ_MAJOR,
22230e474d75SJohannes Hofmann 		    IWI_FW_REQ_MINOR);
22240e474d75SJohannes Hofmann 		return NULL;
22250e474d75SJohannes Hofmann 	}
22260e474d75SJohannes Hofmann 	fw->data = ((const char *) fp->data) + sizeof(struct iwi_firmware_ohdr);
22270e474d75SJohannes Hofmann 	fw->size = fp->datasize - sizeof(struct iwi_firmware_ohdr);
22280e474d75SJohannes Hofmann 	fw->name = fp->name;
22290e474d75SJohannes Hofmann 	return hdr;
22300e474d75SJohannes Hofmann }
22310e474d75SJohannes Hofmann 
22320e474d75SJohannes Hofmann static const struct iwi_firmware_ohdr *
iwi_setup_oucode(struct iwi_softc * sc,struct iwi_fw * fw)22330e474d75SJohannes Hofmann iwi_setup_oucode(struct iwi_softc *sc, struct iwi_fw *fw)
22340e474d75SJohannes Hofmann {
22350e474d75SJohannes Hofmann 	const struct iwi_firmware_ohdr *hdr;
22360e474d75SJohannes Hofmann 
22370e474d75SJohannes Hofmann 	hdr = iwi_setup_ofw(sc, fw);
22380e474d75SJohannes Hofmann 	if (hdr != NULL && le32toh(hdr->mode) != IWI_FW_MODE_UCODE) {
22390e474d75SJohannes Hofmann 		device_printf(sc->sc_dev, "%s is not a ucode image\n",
22400e474d75SJohannes Hofmann 		    fw->name);
22410e474d75SJohannes Hofmann 		hdr = NULL;
22420e474d75SJohannes Hofmann 	}
22430e474d75SJohannes Hofmann 	return hdr;
22440e474d75SJohannes Hofmann }
22450e474d75SJohannes Hofmann 
22460e474d75SJohannes Hofmann static void
iwi_getfw(struct iwi_fw * fw,const char * fwname,struct iwi_fw * uc,const char * ucname)22470e474d75SJohannes Hofmann iwi_getfw(struct iwi_fw *fw, const char *fwname,
22480e474d75SJohannes Hofmann 	  struct iwi_fw *uc, const char *ucname)
22490e474d75SJohannes Hofmann {
22500e474d75SJohannes Hofmann 	if (fw->fp == NULL)
22510e474d75SJohannes Hofmann 		fw->fp = firmware_get(fwname);
22520e474d75SJohannes Hofmann 	/* NB: pre-3.0 ucode is packaged separately */
22530e474d75SJohannes Hofmann 	if (uc->fp == NULL && fw->fp != NULL && fw->fp->version < 300)
22540e474d75SJohannes Hofmann 		uc->fp = firmware_get(ucname);
22550e474d75SJohannes Hofmann }
22560e474d75SJohannes Hofmann 
22570e474d75SJohannes Hofmann /*
22580e474d75SJohannes Hofmann  * Get the required firmware images if not already loaded.
22590e474d75SJohannes Hofmann  * Note that we hold firmware images so long as the device
22600e474d75SJohannes Hofmann  * is marked up in case we need to reload them on device init.
22610e474d75SJohannes Hofmann  * This is necessary because we re-init the device sometimes
22620e474d75SJohannes Hofmann  * from a context where we cannot read from the filesystem
22630e474d75SJohannes Hofmann  * (e.g. from the taskqueue thread when rfkill is re-enabled).
22640e474d75SJohannes Hofmann  * XXX return 0 on success, 1 on error.
22650e474d75SJohannes Hofmann  *
22660e474d75SJohannes Hofmann  * NB: the order of get'ing and put'ing images here is
22670e474d75SJohannes Hofmann  * intentional to support handling firmware images bundled
22680e474d75SJohannes Hofmann  * by operating mode and/or all together in one file with
22690e474d75SJohannes Hofmann  * the boot firmware as "master".
22700e474d75SJohannes Hofmann  */
2271b50e4759SMatthew Dillon static int
iwi_get_firmware(struct iwi_softc * sc,enum ieee80211_opmode opmode)22720e474d75SJohannes Hofmann iwi_get_firmware(struct iwi_softc *sc, enum ieee80211_opmode opmode)
22730e474d75SJohannes Hofmann {
22740e474d75SJohannes Hofmann 	const struct iwi_firmware_hdr *hdr;
22750e474d75SJohannes Hofmann 	const struct firmware *fp;
22760e474d75SJohannes Hofmann 
22770e474d75SJohannes Hofmann 	/* invalidate cached firmware on mode change */
22780e474d75SJohannes Hofmann 	if (sc->fw_mode != opmode)
22790e474d75SJohannes Hofmann 		iwi_put_firmware(sc);
22800e474d75SJohannes Hofmann 
22810e474d75SJohannes Hofmann 	switch (opmode) {
22820e474d75SJohannes Hofmann 	case IEEE80211_M_STA:
22830e474d75SJohannes Hofmann 		iwi_getfw(&sc->fw_fw, "iwi_bss", &sc->fw_uc, "iwi_ucode_bss");
22840e474d75SJohannes Hofmann 		break;
22850e474d75SJohannes Hofmann 	case IEEE80211_M_IBSS:
22860e474d75SJohannes Hofmann 		iwi_getfw(&sc->fw_fw, "iwi_ibss", &sc->fw_uc, "iwi_ucode_ibss");
22870e474d75SJohannes Hofmann 		break;
22880e474d75SJohannes Hofmann 	case IEEE80211_M_MONITOR:
22890e474d75SJohannes Hofmann 		iwi_getfw(&sc->fw_fw, "iwi_monitor",
22900e474d75SJohannes Hofmann 			  &sc->fw_uc, "iwi_ucode_monitor");
22910e474d75SJohannes Hofmann 		break;
22920e474d75SJohannes Hofmann 	default:
22930e474d75SJohannes Hofmann 		device_printf(sc->sc_dev, "unknown opmode %d\n", opmode);
22940e474d75SJohannes Hofmann 		return EINVAL;
22950e474d75SJohannes Hofmann 	}
22960e474d75SJohannes Hofmann 	fp = sc->fw_fw.fp;
22970e474d75SJohannes Hofmann 	if (fp == NULL) {
22980e474d75SJohannes Hofmann 		device_printf(sc->sc_dev, "could not load firmware\n");
22990e474d75SJohannes Hofmann 		goto bad;
23000e474d75SJohannes Hofmann 	}
23010e474d75SJohannes Hofmann 	if (fp->version < 300) {
23020e474d75SJohannes Hofmann 		/*
23030e474d75SJohannes Hofmann 		 * Firmware prior to 3.0 was packaged as separate
23040e474d75SJohannes Hofmann 		 * boot, firmware, and ucode images.  Verify the
23050e474d75SJohannes Hofmann 		 * ucode image was read in, retrieve the boot image
23060e474d75SJohannes Hofmann 		 * if needed, and check version stamps for consistency.
23070e474d75SJohannes Hofmann 		 * The version stamps in the data are also checked
23080e474d75SJohannes Hofmann 		 * above; this is a bit paranoid but is a cheap
23090e474d75SJohannes Hofmann 		 * safeguard against mis-packaging.
23100e474d75SJohannes Hofmann 		 */
23110e474d75SJohannes Hofmann 		if (sc->fw_uc.fp == NULL) {
23120e474d75SJohannes Hofmann 			device_printf(sc->sc_dev, "could not load ucode\n");
23130e474d75SJohannes Hofmann 			goto bad;
23140e474d75SJohannes Hofmann 		}
23150e474d75SJohannes Hofmann 		if (sc->fw_boot.fp == NULL) {
23160e474d75SJohannes Hofmann 			sc->fw_boot.fp = firmware_get("iwi_boot");
23170e474d75SJohannes Hofmann 			if (sc->fw_boot.fp == NULL) {
23180e474d75SJohannes Hofmann 				device_printf(sc->sc_dev,
23190e474d75SJohannes Hofmann 					"could not load boot firmware\n");
23200e474d75SJohannes Hofmann 				goto bad;
23210e474d75SJohannes Hofmann 			}
23220e474d75SJohannes Hofmann 		}
23230e474d75SJohannes Hofmann 		if (sc->fw_boot.fp->version != sc->fw_fw.fp->version ||
23240e474d75SJohannes Hofmann 		    sc->fw_boot.fp->version != sc->fw_uc.fp->version) {
23250e474d75SJohannes Hofmann 			device_printf(sc->sc_dev,
23260e474d75SJohannes Hofmann 			    "firmware version mismatch: "
23270e474d75SJohannes Hofmann 			    "'%s' is %d, '%s' is %d, '%s' is %d\n",
23280e474d75SJohannes Hofmann 			    sc->fw_boot.fp->name, sc->fw_boot.fp->version,
23290e474d75SJohannes Hofmann 			    sc->fw_uc.fp->name, sc->fw_uc.fp->version,
23300e474d75SJohannes Hofmann 			    sc->fw_fw.fp->name, sc->fw_fw.fp->version
23310e474d75SJohannes Hofmann 			);
23320e474d75SJohannes Hofmann 			goto bad;
23330e474d75SJohannes Hofmann 		}
23340e474d75SJohannes Hofmann 		/*
23350e474d75SJohannes Hofmann 		 * Check and setup each image.
23360e474d75SJohannes Hofmann 		 */
23370e474d75SJohannes Hofmann 		if (iwi_setup_oucode(sc, &sc->fw_uc) == NULL ||
23380e474d75SJohannes Hofmann 		    iwi_setup_ofw(sc, &sc->fw_boot) == NULL ||
23390e474d75SJohannes Hofmann 		    iwi_setup_ofw(sc, &sc->fw_fw) == NULL)
23400e474d75SJohannes Hofmann 			goto bad;
23410e474d75SJohannes Hofmann 	} else {
23420e474d75SJohannes Hofmann 		/*
23430e474d75SJohannes Hofmann 		 * Check and setup combined image.
23440e474d75SJohannes Hofmann 		 */
23450e474d75SJohannes Hofmann 		if (fp->datasize < sizeof(struct iwi_firmware_hdr)) {
23460e474d75SJohannes Hofmann 			device_printf(sc->sc_dev, "image '%s' too small\n",
23470e474d75SJohannes Hofmann 			    fp->name);
23480e474d75SJohannes Hofmann 			goto bad;
23490e474d75SJohannes Hofmann 		}
23500e474d75SJohannes Hofmann 		hdr = (const struct iwi_firmware_hdr *)fp->data;
23510e474d75SJohannes Hofmann 		if (fp->datasize < sizeof(*hdr) + le32toh(hdr->bsize) + le32toh(hdr->usize)
23520e474d75SJohannes Hofmann 				+ le32toh(hdr->fsize)) {
23530e474d75SJohannes Hofmann 			device_printf(sc->sc_dev, "image '%s' too small (2)\n",
23540e474d75SJohannes Hofmann 			    fp->name);
23550e474d75SJohannes Hofmann 			goto bad;
23560e474d75SJohannes Hofmann 		}
23570e474d75SJohannes Hofmann 		sc->fw_boot.data = ((const char *) fp->data) + sizeof(*hdr);
23580e474d75SJohannes Hofmann 		sc->fw_boot.size = le32toh(hdr->bsize);
23590e474d75SJohannes Hofmann 		sc->fw_boot.name = fp->name;
23600e474d75SJohannes Hofmann 		sc->fw_uc.data = sc->fw_boot.data + sc->fw_boot.size;
23610e474d75SJohannes Hofmann 		sc->fw_uc.size = le32toh(hdr->usize);
23620e474d75SJohannes Hofmann 		sc->fw_uc.name = fp->name;
23630e474d75SJohannes Hofmann 		sc->fw_fw.data = sc->fw_uc.data + sc->fw_uc.size;
23640e474d75SJohannes Hofmann 		sc->fw_fw.size = le32toh(hdr->fsize);
23650e474d75SJohannes Hofmann 		sc->fw_fw.name = fp->name;
23660e474d75SJohannes Hofmann 	}
23670e474d75SJohannes Hofmann #if 0
23680e474d75SJohannes Hofmann 	device_printf(sc->sc_dev, "boot %d ucode %d fw %d bytes\n",
23690e474d75SJohannes Hofmann 		sc->fw_boot.size, sc->fw_uc.size, sc->fw_fw.size);
23700e474d75SJohannes Hofmann #endif
23710e474d75SJohannes Hofmann 
23720e474d75SJohannes Hofmann 	sc->fw_mode = opmode;
23730e474d75SJohannes Hofmann 	return 0;
23740e474d75SJohannes Hofmann bad:
23750e474d75SJohannes Hofmann 	iwi_put_firmware(sc);
23760e474d75SJohannes Hofmann 	return 1;
23770e474d75SJohannes Hofmann }
23780e474d75SJohannes Hofmann 
23790e474d75SJohannes Hofmann static void
iwi_put_fw(struct iwi_fw * fw)23800e474d75SJohannes Hofmann iwi_put_fw(struct iwi_fw *fw)
23810e474d75SJohannes Hofmann {
23820e474d75SJohannes Hofmann 	if (fw->fp != NULL) {
23830e474d75SJohannes Hofmann 		firmware_put(fw->fp, FIRMWARE_UNLOAD);
23840e474d75SJohannes Hofmann 		fw->fp = NULL;
23850e474d75SJohannes Hofmann 	}
23860e474d75SJohannes Hofmann 	fw->data = NULL;
23870e474d75SJohannes Hofmann 	fw->size = 0;
23880e474d75SJohannes Hofmann 	fw->name = NULL;
23890e474d75SJohannes Hofmann }
23900e474d75SJohannes Hofmann 
23910e474d75SJohannes Hofmann /*
23920e474d75SJohannes Hofmann  * Release any cached firmware images.
23930e474d75SJohannes Hofmann  */
23940e474d75SJohannes Hofmann static void
iwi_put_firmware(struct iwi_softc * sc)23950e474d75SJohannes Hofmann iwi_put_firmware(struct iwi_softc *sc)
23960e474d75SJohannes Hofmann {
23970e474d75SJohannes Hofmann 	iwi_put_fw(&sc->fw_uc);
23980e474d75SJohannes Hofmann 	iwi_put_fw(&sc->fw_fw);
23990e474d75SJohannes Hofmann 	iwi_put_fw(&sc->fw_boot);
24000e474d75SJohannes Hofmann }
24010e474d75SJohannes Hofmann 
24020e474d75SJohannes Hofmann static int
iwi_load_ucode(struct iwi_softc * sc,const struct iwi_fw * fw)24030e474d75SJohannes Hofmann iwi_load_ucode(struct iwi_softc *sc, const struct iwi_fw *fw)
2404b50e4759SMatthew Dillon {
2405841ab66cSSepherosa Ziehau 	uint32_t tmp;
24060e474d75SJohannes Hofmann 	const uint16_t *w;
24070e474d75SJohannes Hofmann 	const char *uc = fw->data;
24080e474d75SJohannes Hofmann 	size_t size = fw->size;
24090e474d75SJohannes Hofmann 	int i, ntries, error;
2410b50e4759SMatthew Dillon 
2411c4fe7bb1SImre Vadász 	IWI_LOCK_ASSERT(sc);
24120e474d75SJohannes Hofmann 	error = 0;
2413b50e4759SMatthew Dillon 	CSR_WRITE_4(sc, IWI_CSR_RST, CSR_READ_4(sc, IWI_CSR_RST) |
2414b50e4759SMatthew Dillon 	    IWI_RST_STOP_MASTER);
2415b50e4759SMatthew Dillon 	for (ntries = 0; ntries < 5; ntries++) {
2416b50e4759SMatthew Dillon 		if (CSR_READ_4(sc, IWI_CSR_RST) & IWI_RST_MASTER_DISABLED)
2417b50e4759SMatthew Dillon 			break;
2418b50e4759SMatthew Dillon 		DELAY(10);
2419b50e4759SMatthew Dillon 	}
2420b50e4759SMatthew Dillon 	if (ntries == 5) {
2421b50e4759SMatthew Dillon 		device_printf(sc->sc_dev, "timeout waiting for master\n");
24220e474d75SJohannes Hofmann 		error = EIO;
24230e474d75SJohannes Hofmann 		goto fail;
2424b50e4759SMatthew Dillon 	}
2425b50e4759SMatthew Dillon 
2426b50e4759SMatthew Dillon 	MEM_WRITE_4(sc, 0x3000e0, 0x80000000);
2427b50e4759SMatthew Dillon 	DELAY(5000);
2428841ab66cSSepherosa Ziehau 
2429841ab66cSSepherosa Ziehau 	tmp = CSR_READ_4(sc, IWI_CSR_RST);
2430841ab66cSSepherosa Ziehau 	tmp &= ~IWI_RST_PRINCETON_RESET;
2431841ab66cSSepherosa Ziehau 	CSR_WRITE_4(sc, IWI_CSR_RST, tmp);
2432841ab66cSSepherosa Ziehau 
2433b50e4759SMatthew Dillon 	DELAY(5000);
2434b50e4759SMatthew Dillon 	MEM_WRITE_4(sc, 0x3000e0, 0);
2435b50e4759SMatthew Dillon 	DELAY(1000);
24360e474d75SJohannes Hofmann 	MEM_WRITE_4(sc, IWI_MEM_EEPROM_EVENT, 1);
2437b50e4759SMatthew Dillon 	DELAY(1000);
24380e474d75SJohannes Hofmann 	MEM_WRITE_4(sc, IWI_MEM_EEPROM_EVENT, 0);
2439b50e4759SMatthew Dillon 	DELAY(1000);
2440b50e4759SMatthew Dillon 	MEM_WRITE_1(sc, 0x200000, 0x00);
2441b50e4759SMatthew Dillon 	MEM_WRITE_1(sc, 0x200000, 0x40);
2442b50e4759SMatthew Dillon 	DELAY(1000);
2443b50e4759SMatthew Dillon 
2444841ab66cSSepherosa Ziehau 	/* write microcode into adapter memory */
24450e474d75SJohannes Hofmann 	for (w = (const uint16_t *)uc; size > 0; w++, size -= 2)
2446841ab66cSSepherosa Ziehau 		MEM_WRITE_2(sc, 0x200010, htole16(*w));
2447b50e4759SMatthew Dillon 
2448b50e4759SMatthew Dillon 	MEM_WRITE_1(sc, 0x200000, 0x00);
2449b50e4759SMatthew Dillon 	MEM_WRITE_1(sc, 0x200000, 0x80);
2450b50e4759SMatthew Dillon 
2451841ab66cSSepherosa Ziehau 	/* wait until we get an answer */
2452b50e4759SMatthew Dillon 	for (ntries = 0; ntries < 100; ntries++) {
2453b50e4759SMatthew Dillon 		if (MEM_READ_1(sc, 0x200000) & 1)
2454b50e4759SMatthew Dillon 			break;
2455b50e4759SMatthew Dillon 		DELAY(100);
2456b50e4759SMatthew Dillon 	}
2457b50e4759SMatthew Dillon 	if (ntries == 100) {
2458b50e4759SMatthew Dillon 		device_printf(sc->sc_dev,
2459b50e4759SMatthew Dillon 		    "timeout waiting for ucode to initialize\n");
24600e474d75SJohannes Hofmann 		error = EIO;
24610e474d75SJohannes Hofmann 		goto fail;
2462b50e4759SMatthew Dillon 	}
2463b50e4759SMatthew Dillon 
2464841ab66cSSepherosa Ziehau 	/* read the answer or the firmware will not initialize properly */
2465b50e4759SMatthew Dillon 	for (i = 0; i < 7; i++)
2466b50e4759SMatthew Dillon 		MEM_READ_4(sc, 0x200004);
2467b50e4759SMatthew Dillon 
2468b50e4759SMatthew Dillon 	MEM_WRITE_1(sc, 0x200000, 0x00);
2469b50e4759SMatthew Dillon 
24700e474d75SJohannes Hofmann fail:
2471114b4cbcSSepherosa Ziehau 	return error;
2472114b4cbcSSepherosa Ziehau }
2473114b4cbcSSepherosa Ziehau 
2474b50e4759SMatthew Dillon /* macro to handle unaligned little endian data in firmware image */
2475b50e4759SMatthew Dillon #define GETLE32(p) ((p)[0] | (p)[1] << 8 | (p)[2] << 16 | (p)[3] << 24)
2476841ab66cSSepherosa Ziehau 
2477b50e4759SMatthew Dillon static int
iwi_load_firmware(struct iwi_softc * sc,const struct iwi_fw * fw)24780e474d75SJohannes Hofmann iwi_load_firmware(struct iwi_softc *sc, const struct iwi_fw *fw)
2479b50e4759SMatthew Dillon {
2480b50e4759SMatthew Dillon 	u_char *p, *end;
2481841ab66cSSepherosa Ziehau 	uint32_t sentinel, ctl, src, dst, sum, len, mlen, tmp;
24820e474d75SJohannes Hofmann 	int ntries, error;
2483b50e4759SMatthew Dillon 
2484c4fe7bb1SImre Vadász 	IWI_LOCK_ASSERT(sc);
2485c4fe7bb1SImre Vadász 
24860e474d75SJohannes Hofmann 	/* copy firmware image to DMA memory */
24870e474d75SJohannes Hofmann 	memcpy(sc->fw_virtaddr, fw->data, fw->size);
2488b50e4759SMatthew Dillon 
24890e474d75SJohannes Hofmann 	/* make sure the adapter will get up-to-date values */
24900e474d75SJohannes Hofmann 	bus_dmamap_sync(sc->fw_dmat, sc->fw_map, BUS_DMASYNC_PREWRITE);
2491b50e4759SMatthew Dillon 
24920e474d75SJohannes Hofmann 	/* tell the adapter where the command blocks are stored */
2493b50e4759SMatthew Dillon 	MEM_WRITE_4(sc, 0x3000a0, 0x27000);
2494b50e4759SMatthew Dillon 
2495b50e4759SMatthew Dillon 	/*
2496b50e4759SMatthew Dillon 	 * Store command blocks into adapter's internal memory using register
2497b50e4759SMatthew Dillon 	 * indirections. The adapter will read the firmware image through DMA
2498b50e4759SMatthew Dillon 	 * using information stored in command blocks.
2499b50e4759SMatthew Dillon 	 */
25000e474d75SJohannes Hofmann 	src = sc->fw_physaddr;
25010e474d75SJohannes Hofmann 	p = sc->fw_virtaddr;
25020e474d75SJohannes Hofmann 	end = p + fw->size;
2503b50e4759SMatthew Dillon 	CSR_WRITE_4(sc, IWI_CSR_AUTOINC_ADDR, 0x27000);
2504b50e4759SMatthew Dillon 
2505b50e4759SMatthew Dillon 	while (p < end) {
2506b50e4759SMatthew Dillon 		dst = GETLE32(p); p += 4; src += 4;
2507b50e4759SMatthew Dillon 		len = GETLE32(p); p += 4; src += 4;
2508b50e4759SMatthew Dillon 		p += len;
2509b50e4759SMatthew Dillon 
2510b50e4759SMatthew Dillon 		while (len > 0) {
2511b50e4759SMatthew Dillon 			mlen = min(len, IWI_CB_MAXDATALEN);
2512b50e4759SMatthew Dillon 
2513b50e4759SMatthew Dillon 			ctl = IWI_CB_DEFAULT_CTL | mlen;
2514b50e4759SMatthew Dillon 			sum = ctl ^ src ^ dst;
2515b50e4759SMatthew Dillon 
25160e474d75SJohannes Hofmann 			/* write a command block */
2517b50e4759SMatthew Dillon 			CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, ctl);
2518b50e4759SMatthew Dillon 			CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, src);
2519b50e4759SMatthew Dillon 			CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, dst);
2520b50e4759SMatthew Dillon 			CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, sum);
2521b50e4759SMatthew Dillon 
2522b50e4759SMatthew Dillon 			src += mlen;
2523b50e4759SMatthew Dillon 			dst += mlen;
2524b50e4759SMatthew Dillon 			len -= mlen;
2525b50e4759SMatthew Dillon 		}
2526b50e4759SMatthew Dillon 	}
2527b50e4759SMatthew Dillon 
25280e474d75SJohannes Hofmann 	/* write a fictive final command block (sentinel) */
2529b50e4759SMatthew Dillon 	sentinel = CSR_READ_4(sc, IWI_CSR_AUTOINC_ADDR);
2530b50e4759SMatthew Dillon 	CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, 0);
2531b50e4759SMatthew Dillon 
2532841ab66cSSepherosa Ziehau 	tmp = CSR_READ_4(sc, IWI_CSR_RST);
2533841ab66cSSepherosa Ziehau 	tmp &= ~(IWI_RST_MASTER_DISABLED | IWI_RST_STOP_MASTER);
2534841ab66cSSepherosa Ziehau 	CSR_WRITE_4(sc, IWI_CSR_RST, tmp);
2535b50e4759SMatthew Dillon 
25360e474d75SJohannes Hofmann 	/* tell the adapter to start processing command blocks */
2537b50e4759SMatthew Dillon 	MEM_WRITE_4(sc, 0x3000a4, 0x540100);
2538b50e4759SMatthew Dillon 
25390e474d75SJohannes Hofmann 	/* wait until the adapter reaches the sentinel */
2540b50e4759SMatthew Dillon 	for (ntries = 0; ntries < 400; ntries++) {
2541b50e4759SMatthew Dillon 		if (MEM_READ_4(sc, 0x3000d0) >= sentinel)
2542b50e4759SMatthew Dillon 			break;
2543b50e4759SMatthew Dillon 		DELAY(100);
2544b50e4759SMatthew Dillon 	}
25450e474d75SJohannes Hofmann 	/* sync dma, just in case */
25460e474d75SJohannes Hofmann 	bus_dmamap_sync(sc->fw_dmat, sc->fw_map, BUS_DMASYNC_POSTWRITE);
2547b50e4759SMatthew Dillon 	if (ntries == 400) {
2548b50e4759SMatthew Dillon 		device_printf(sc->sc_dev,
25490e474d75SJohannes Hofmann 		    "timeout processing command blocks for %s firmware\n",
25500e474d75SJohannes Hofmann 		    fw->name);
25510e474d75SJohannes Hofmann 		return EIO;
2552b50e4759SMatthew Dillon 	}
2553b50e4759SMatthew Dillon 
25540e474d75SJohannes Hofmann 	/* we're done with command blocks processing */
2555b50e4759SMatthew Dillon 	MEM_WRITE_4(sc, 0x3000a4, 0x540c00);
2556b50e4759SMatthew Dillon 
25570e474d75SJohannes Hofmann 	/* allow interrupts so we know when the firmware is ready */
2558b50e4759SMatthew Dillon 	CSR_WRITE_4(sc, IWI_CSR_INTR_MASK, IWI_INTR_MASK);
2559b50e4759SMatthew Dillon 
25600e474d75SJohannes Hofmann 	/* tell the adapter to initialize the firmware */
2561b50e4759SMatthew Dillon 	CSR_WRITE_4(sc, IWI_CSR_RST, 0);
2562b50e4759SMatthew Dillon 
2563841ab66cSSepherosa Ziehau 	tmp = CSR_READ_4(sc, IWI_CSR_CTL);
2564841ab66cSSepherosa Ziehau 	CSR_WRITE_4(sc, IWI_CSR_CTL, tmp | IWI_CTL_ALLOW_STANDBY);
2565841ab66cSSepherosa Ziehau 
2566841ab66cSSepherosa Ziehau 	/* wait at most one second for firmware initialization to complete */
2567c4fe7bb1SImre Vadász #if defined(__DragonFly__)
2568c4fe7bb1SImre Vadász 	if ((error = lksleep(sc, &sc->sc_lock, 0, "iwiinit", hz)) != 0) {
2569c4fe7bb1SImre Vadász #else
2570c4fe7bb1SImre Vadász 	if ((error = msleep(sc, &sc->sc_mtx, 0, "iwiinit", hz)) != 0) {
2571c4fe7bb1SImre Vadász #endif
2572c4fe7bb1SImre Vadász 		device_printf(sc->sc_dev, "timeout waiting for %s firmware "
2573c4fe7bb1SImre Vadász 		    "initialization to complete\n", fw->name);
2574b50e4759SMatthew Dillon 	}
2575b50e4759SMatthew Dillon 
2576b50e4759SMatthew Dillon 	return error;
2577b50e4759SMatthew Dillon }
2578b50e4759SMatthew Dillon 
2579b50e4759SMatthew Dillon static int
25800e474d75SJohannes Hofmann iwi_setpowermode(struct iwi_softc *sc, struct ieee80211vap *vap)
2581b50e4759SMatthew Dillon {
2582841ab66cSSepherosa Ziehau 	uint32_t data;
25830e474d75SJohannes Hofmann 
25840e474d75SJohannes Hofmann 	if (vap->iv_flags & IEEE80211_F_PMGTON) {
25850e474d75SJohannes Hofmann 		/* XXX set more fine-grained operation */
25860e474d75SJohannes Hofmann 		data = htole32(IWI_POWER_MODE_MAX);
25870e474d75SJohannes Hofmann 	} else
25880e474d75SJohannes Hofmann 		data = htole32(IWI_POWER_MODE_CAM);
25890e474d75SJohannes Hofmann 
25900e474d75SJohannes Hofmann 	DPRINTF(("Setting power mode to %u\n", le32toh(data)));
25910e474d75SJohannes Hofmann 	return iwi_cmd(sc, IWI_CMD_SET_POWER_MODE, &data, sizeof data);
25920e474d75SJohannes Hofmann }
25930e474d75SJohannes Hofmann 
25940e474d75SJohannes Hofmann static int
25950e474d75SJohannes Hofmann iwi_setwepkeys(struct iwi_softc *sc, struct ieee80211vap *vap)
25960e474d75SJohannes Hofmann {
25970e474d75SJohannes Hofmann 	struct iwi_wep_key wepkey;
25980e474d75SJohannes Hofmann 	struct ieee80211_key *wk;
2599b50e4759SMatthew Dillon 	int error, i;
2600b50e4759SMatthew Dillon 
2601841ab66cSSepherosa Ziehau 	for (i = 0; i < IEEE80211_WEP_NKID; i++) {
26020e474d75SJohannes Hofmann 		wk = &vap->iv_nw_keys[i];
2603841ab66cSSepherosa Ziehau 
2604b50e4759SMatthew Dillon 		wepkey.cmd = IWI_WEP_KEY_CMD_SETKEY;
2605b50e4759SMatthew Dillon 		wepkey.idx = i;
2606841ab66cSSepherosa Ziehau 		wepkey.len = wk->wk_keylen;
2607841ab66cSSepherosa Ziehau 		memset(wepkey.key, 0, sizeof wepkey.key);
2608841ab66cSSepherosa Ziehau 		memcpy(wepkey.key, wk->wk_key, wk->wk_keylen);
2609841ab66cSSepherosa Ziehau 		DPRINTF(("Setting wep key index %u len %u\n", wepkey.idx,
2610841ab66cSSepherosa Ziehau 		    wepkey.len));
2611b50e4759SMatthew Dillon 		error = iwi_cmd(sc, IWI_CMD_SET_WEP_KEY, &wepkey,
26120e474d75SJohannes Hofmann 		    sizeof wepkey);
26130e474d75SJohannes Hofmann 		if (error != 0)
26140e474d75SJohannes Hofmann 			return error;
26150e474d75SJohannes Hofmann 	}
26160e474d75SJohannes Hofmann 	return 0;
26170e474d75SJohannes Hofmann }
26180e474d75SJohannes Hofmann 
26190e474d75SJohannes Hofmann static int
26200e474d75SJohannes Hofmann iwi_config(struct iwi_softc *sc)
26210e474d75SJohannes Hofmann {
2622c4fe7bb1SImre Vadász 	struct ieee80211com *ic = &sc->sc_ic;
26230e474d75SJohannes Hofmann 	struct iwi_configuration config;
26240e474d75SJohannes Hofmann 	struct iwi_rateset rs;
26250e474d75SJohannes Hofmann 	struct iwi_txpower power;
26260e474d75SJohannes Hofmann 	uint32_t data;
26270e474d75SJohannes Hofmann 	int error, i;
26280e474d75SJohannes Hofmann 
2629c4fe7bb1SImre Vadász 	IWI_LOCK_ASSERT(sc);
2630c4fe7bb1SImre Vadász 
2631c4fe7bb1SImre Vadász #if defined(__DragonFly__)
2632c4fe7bb1SImre Vadász 	DPRINTF(("Setting MAC address to %s\n", ether_sprintf(ic->ic_macaddr)));
2633c4fe7bb1SImre Vadász #else
2634c4fe7bb1SImre Vadász 	DPRINTF(("Setting MAC address to %6D\n", ic->ic_macaddr, ":"));
2635c4fe7bb1SImre Vadász #endif
2636c4fe7bb1SImre Vadász 	error = iwi_cmd(sc, IWI_CMD_SET_MAC_ADDRESS, ic->ic_macaddr,
26370e474d75SJohannes Hofmann 	    IEEE80211_ADDR_LEN);
26380e474d75SJohannes Hofmann 	if (error != 0)
26390e474d75SJohannes Hofmann 		return error;
26400e474d75SJohannes Hofmann 
26410e474d75SJohannes Hofmann 	memset(&config, 0, sizeof config);
26420e474d75SJohannes Hofmann 	config.bluetooth_coexistence = sc->bluetooth;
26430e474d75SJohannes Hofmann 	config.silence_threshold = 0x1e;
26440e474d75SJohannes Hofmann 	config.antenna = sc->antenna;
26450e474d75SJohannes Hofmann 	config.multicast_enabled = 1;
26460e474d75SJohannes Hofmann 	config.answer_pbreq = (ic->ic_opmode == IEEE80211_M_IBSS) ? 1 : 0;
26470e474d75SJohannes Hofmann 	config.disable_unicast_decryption = 1;
26480e474d75SJohannes Hofmann 	config.disable_multicast_decryption = 1;
2649c4fe7bb1SImre Vadász 	if (ic->ic_opmode == IEEE80211_M_MONITOR) {
2650c4fe7bb1SImre Vadász 		config.allow_invalid_frames = 1;
2651c4fe7bb1SImre Vadász 		config.allow_beacon_and_probe_resp = 1;
2652c4fe7bb1SImre Vadász 		config.allow_mgt = 1;
2653c4fe7bb1SImre Vadász 	}
26540e474d75SJohannes Hofmann 	DPRINTF(("Configuring adapter\n"));
26550e474d75SJohannes Hofmann 	error = iwi_cmd(sc, IWI_CMD_SET_CONFIG, &config, sizeof config);
26560e474d75SJohannes Hofmann 	if (error != 0)
26570e474d75SJohannes Hofmann 		return error;
26580e474d75SJohannes Hofmann 	if (ic->ic_opmode == IEEE80211_M_IBSS) {
26590e474d75SJohannes Hofmann 		power.mode = IWI_MODE_11B;
26600e474d75SJohannes Hofmann 		power.nchan = 11;
26610e474d75SJohannes Hofmann 		for (i = 0; i < 11; i++) {
26620e474d75SJohannes Hofmann 			power.chan[i].chan = i + 1;
26630e474d75SJohannes Hofmann 			power.chan[i].power = IWI_TXPOWER_MAX;
26640e474d75SJohannes Hofmann 		}
26650e474d75SJohannes Hofmann 		DPRINTF(("Setting .11b channels tx power\n"));
26660e474d75SJohannes Hofmann 		error = iwi_cmd(sc, IWI_CMD_SET_TX_POWER, &power, sizeof power);
26670e474d75SJohannes Hofmann 		if (error != 0)
26680e474d75SJohannes Hofmann 			return error;
26690e474d75SJohannes Hofmann 
26700e474d75SJohannes Hofmann 		power.mode = IWI_MODE_11G;
26710e474d75SJohannes Hofmann 		DPRINTF(("Setting .11g channels tx power\n"));
26720e474d75SJohannes Hofmann 		error = iwi_cmd(sc, IWI_CMD_SET_TX_POWER, &power, sizeof power);
2673b50e4759SMatthew Dillon 		if (error != 0)
2674b50e4759SMatthew Dillon 			return error;
2675b50e4759SMatthew Dillon 	}
2676b50e4759SMatthew Dillon 
26770e474d75SJohannes Hofmann 	memset(&rs, 0, sizeof rs);
26780e474d75SJohannes Hofmann 	rs.mode = IWI_MODE_11G;
26790e474d75SJohannes Hofmann 	rs.type = IWI_RATESET_TYPE_SUPPORTED;
26800e474d75SJohannes Hofmann 	rs.nrates = ic->ic_sup_rates[IEEE80211_MODE_11G].rs_nrates;
26810e474d75SJohannes Hofmann 	memcpy(rs.rates, ic->ic_sup_rates[IEEE80211_MODE_11G].rs_rates,
26820e474d75SJohannes Hofmann 	    rs.nrates);
26830e474d75SJohannes Hofmann 	DPRINTF(("Setting .11bg supported rates (%u)\n", rs.nrates));
26840e474d75SJohannes Hofmann 	error = iwi_cmd(sc, IWI_CMD_SET_RATES, &rs, sizeof rs);
26850e474d75SJohannes Hofmann 	if (error != 0)
26860e474d75SJohannes Hofmann 		return error;
26870e474d75SJohannes Hofmann 
26880e474d75SJohannes Hofmann 	memset(&rs, 0, sizeof rs);
26890e474d75SJohannes Hofmann 	rs.mode = IWI_MODE_11A;
26900e474d75SJohannes Hofmann 	rs.type = IWI_RATESET_TYPE_SUPPORTED;
26910e474d75SJohannes Hofmann 	rs.nrates = ic->ic_sup_rates[IEEE80211_MODE_11A].rs_nrates;
26920e474d75SJohannes Hofmann 	memcpy(rs.rates, ic->ic_sup_rates[IEEE80211_MODE_11A].rs_rates,
26930e474d75SJohannes Hofmann 	    rs.nrates);
26940e474d75SJohannes Hofmann 	DPRINTF(("Setting .11a supported rates (%u)\n", rs.nrates));
26950e474d75SJohannes Hofmann 	error = iwi_cmd(sc, IWI_CMD_SET_RATES, &rs, sizeof rs);
26960e474d75SJohannes Hofmann 	if (error != 0)
26970e474d75SJohannes Hofmann 		return error;
26980e474d75SJohannes Hofmann 
26990e474d75SJohannes Hofmann 	data = htole32(karc4random());
27000e474d75SJohannes Hofmann 	DPRINTF(("Setting initialization vector to %u\n", le32toh(data)));
27010e474d75SJohannes Hofmann 	error = iwi_cmd(sc, IWI_CMD_SET_IV, &data, sizeof data);
27020e474d75SJohannes Hofmann 	if (error != 0)
27030e474d75SJohannes Hofmann 		return error;
27040e474d75SJohannes Hofmann 
27050e474d75SJohannes Hofmann 	/* enable adapter */
2706b50e4759SMatthew Dillon 	DPRINTF(("Enabling adapter\n"));
27070e474d75SJohannes Hofmann 	return iwi_cmd(sc, IWI_CMD_ENABLE, NULL, 0);
27080e474d75SJohannes Hofmann }
27090e474d75SJohannes Hofmann 
27100e474d75SJohannes Hofmann static __inline void
27110e474d75SJohannes Hofmann set_scan_type(struct iwi_scan_ext *scan, int ix, int scan_type)
27120e474d75SJohannes Hofmann {
27130e474d75SJohannes Hofmann 	uint8_t *st = &scan->scan_type[ix / 2];
27140e474d75SJohannes Hofmann 	if (ix % 2)
27150e474d75SJohannes Hofmann 		*st = (*st & 0xf0) | ((scan_type & 0xf) << 0);
27160e474d75SJohannes Hofmann 	else
27170e474d75SJohannes Hofmann 		*st = (*st & 0x0f) | ((scan_type & 0xf) << 4);
2718b50e4759SMatthew Dillon }
2719b50e4759SMatthew Dillon 
2720b50e4759SMatthew Dillon static int
27210e474d75SJohannes Hofmann scan_type(const struct ieee80211_scan_state *ss,
27220e474d75SJohannes Hofmann 	const struct ieee80211_channel *chan)
2723841ab66cSSepherosa Ziehau {
27240e474d75SJohannes Hofmann 	/* We can only set one essid for a directed scan */
27250e474d75SJohannes Hofmann 	if (ss->ss_nssid != 0)
27260e474d75SJohannes Hofmann 		return IWI_SCAN_TYPE_BDIRECTED;
27270e474d75SJohannes Hofmann 	if ((ss->ss_flags & IEEE80211_SCAN_ACTIVE) &&
27280e474d75SJohannes Hofmann 	    (chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0)
27290e474d75SJohannes Hofmann 		return IWI_SCAN_TYPE_BROADCAST;
27300e474d75SJohannes Hofmann 	return IWI_SCAN_TYPE_PASSIVE;
2731841ab66cSSepherosa Ziehau }
2732841ab66cSSepherosa Ziehau 
27330e474d75SJohannes Hofmann static __inline int
27340e474d75SJohannes Hofmann scan_band(const struct ieee80211_channel *c)
2735b50e4759SMatthew Dillon {
27360e474d75SJohannes Hofmann 	return IEEE80211_IS_CHAN_5GHZ(c) ?  IWI_CHAN_5GHZ : IWI_CHAN_2GHZ;
27370e474d75SJohannes Hofmann }
27380e474d75SJohannes Hofmann 
2739c4fe7bb1SImre Vadász static void
2740c4fe7bb1SImre Vadász iwi_monitor_scan(void *arg, int npending)
2741c4fe7bb1SImre Vadász {
2742c4fe7bb1SImre Vadász 	struct iwi_softc *sc = arg;
2743c4fe7bb1SImre Vadász 	IWI_LOCK_DECL;
2744c4fe7bb1SImre Vadász 
2745c4fe7bb1SImre Vadász 	IWI_LOCK(sc);
2746c4fe7bb1SImre Vadász 	(void) iwi_scanchan(sc, 2000, 0);
2747c4fe7bb1SImre Vadász 	IWI_UNLOCK(sc);
2748c4fe7bb1SImre Vadász }
2749c4fe7bb1SImre Vadász 
27500e474d75SJohannes Hofmann /*
27510e474d75SJohannes Hofmann  * Start a scan on the current channel or all channels.
27520e474d75SJohannes Hofmann  */
27530e474d75SJohannes Hofmann static int
27540e474d75SJohannes Hofmann iwi_scanchan(struct iwi_softc *sc, unsigned long maxdwell, int allchan)
27550e474d75SJohannes Hofmann {
2756c4fe7bb1SImre Vadász 	struct ieee80211com *ic = &sc->sc_ic;
27570e474d75SJohannes Hofmann 	struct ieee80211_channel *chan;
27580e474d75SJohannes Hofmann 	struct ieee80211_scan_state *ss;
27590e474d75SJohannes Hofmann 	struct iwi_scan_ext scan;
27600e474d75SJohannes Hofmann 	int error = 0;
27610e474d75SJohannes Hofmann 
2762c4fe7bb1SImre Vadász 	IWI_LOCK_ASSERT(sc);
27630e474d75SJohannes Hofmann 	if (sc->fw_state == IWI_FW_SCANNING) {
27640e474d75SJohannes Hofmann 		/*
27650e474d75SJohannes Hofmann 		 * This should not happen as we only trigger scan_next after
27660e474d75SJohannes Hofmann 		 * completion
27670e474d75SJohannes Hofmann 		 */
27680e474d75SJohannes Hofmann 		DPRINTF(("%s: called too early - still scanning\n", __func__));
27690e474d75SJohannes Hofmann 		return (EBUSY);
27700e474d75SJohannes Hofmann 	}
27710e474d75SJohannes Hofmann 	IWI_STATE_BEGIN(sc, IWI_FW_SCANNING);
27720e474d75SJohannes Hofmann 
27730e474d75SJohannes Hofmann 	ss = ic->ic_scan;
2774b50e4759SMatthew Dillon 
2775841ab66cSSepherosa Ziehau 	memset(&scan, 0, sizeof scan);
27760e474d75SJohannes Hofmann 	scan.full_scan_index = htole32(++sc->sc_scangen);
27770e474d75SJohannes Hofmann 	scan.dwell_time[IWI_SCAN_TYPE_PASSIVE] = htole16(maxdwell);
27780e474d75SJohannes Hofmann 	if (ic->ic_flags_ext & IEEE80211_FEXT_BGSCAN) {
27790e474d75SJohannes Hofmann 		/*
27800e474d75SJohannes Hofmann 		 * Use very short dwell times for when we send probe request
27810e474d75SJohannes Hofmann 		 * frames.  Without this bg scans hang.  Ideally this should
27820e474d75SJohannes Hofmann 		 * be handled with early-termination as done by net80211 but
27830e474d75SJohannes Hofmann 		 * that's not feasible (aborting a scan is problematic).
27840e474d75SJohannes Hofmann 		 */
27850e474d75SJohannes Hofmann 		scan.dwell_time[IWI_SCAN_TYPE_BROADCAST] = htole16(30);
27860e474d75SJohannes Hofmann 		scan.dwell_time[IWI_SCAN_TYPE_BDIRECTED] = htole16(30);
2787b50e4759SMatthew Dillon 	} else {
27880e474d75SJohannes Hofmann 		scan.dwell_time[IWI_SCAN_TYPE_BROADCAST] = htole16(maxdwell);
27890e474d75SJohannes Hofmann 		scan.dwell_time[IWI_SCAN_TYPE_BDIRECTED] = htole16(maxdwell);
2790b50e4759SMatthew Dillon 	}
2791b50e4759SMatthew Dillon 
27920e474d75SJohannes Hofmann 	/* We can only set one essid for a directed scan */
27930e474d75SJohannes Hofmann 	if (ss->ss_nssid != 0) {
27940e474d75SJohannes Hofmann 		error = iwi_cmd(sc, IWI_CMD_SET_ESSID, ss->ss_ssid[0].ssid,
27950e474d75SJohannes Hofmann 		    ss->ss_ssid[0].len);
27960e474d75SJohannes Hofmann 		if (error)
27970e474d75SJohannes Hofmann 			return (error);
2798b50e4759SMatthew Dillon 	}
2799841ab66cSSepherosa Ziehau 
28000e474d75SJohannes Hofmann 	if (allchan) {
28010e474d75SJohannes Hofmann 		int i, next, band, b, bstart;
28020e474d75SJohannes Hofmann 		/*
28030e474d75SJohannes Hofmann 		 * Convert scan list to run-length encoded channel list
28040e474d75SJohannes Hofmann 		 * the firmware requires (preserving the order setup by
28050e474d75SJohannes Hofmann 		 * net80211).  The first entry in each run specifies the
28060e474d75SJohannes Hofmann 		 * band and the count of items in the run.
28070e474d75SJohannes Hofmann 		 */
28080e474d75SJohannes Hofmann 		next = 0;		/* next open slot */
28090e474d75SJohannes Hofmann 		bstart = 0;		/* NB: not needed, silence compiler */
28100e474d75SJohannes Hofmann 		band = -1;		/* NB: impossible value */
28110e474d75SJohannes Hofmann 		KASSERT(ss->ss_last > 0, ("no channels"));
28120e474d75SJohannes Hofmann 		for (i = 0; i < ss->ss_last; i++) {
28130e474d75SJohannes Hofmann 			chan = ss->ss_chans[i];
28140e474d75SJohannes Hofmann 			b = scan_band(chan);
28150e474d75SJohannes Hofmann 			if (b != band) {
28160e474d75SJohannes Hofmann 				if (band != -1)
28170e474d75SJohannes Hofmann 					scan.channels[bstart] =
28180e474d75SJohannes Hofmann 					    (next - bstart) | band;
28190e474d75SJohannes Hofmann 				/* NB: this allocates a slot for the run-len */
28200e474d75SJohannes Hofmann 				band = b, bstart = next++;
2821b50e4759SMatthew Dillon 			}
28220e474d75SJohannes Hofmann 			if (next >= IWI_SCAN_CHANNELS) {
28230e474d75SJohannes Hofmann 				DPRINTF(("truncating scan list\n"));
28240e474d75SJohannes Hofmann 				break;
2825b50e4759SMatthew Dillon 			}
28260e474d75SJohannes Hofmann 			scan.channels[next] = ieee80211_chan2ieee(ic, chan);
28270e474d75SJohannes Hofmann 			set_scan_type(&scan, next, scan_type(ss, chan));
28280e474d75SJohannes Hofmann 			next++;
28290e474d75SJohannes Hofmann 		}
28300e474d75SJohannes Hofmann 		scan.channels[bstart] = (next - bstart) | band;
28310e474d75SJohannes Hofmann 	} else {
28320e474d75SJohannes Hofmann 		/* Scan the current channel only */
28330e474d75SJohannes Hofmann 		chan = ic->ic_curchan;
28340e474d75SJohannes Hofmann 		scan.channels[0] = 1 | scan_band(chan);
28350e474d75SJohannes Hofmann 		scan.channels[1] = ieee80211_chan2ieee(ic, chan);
28360e474d75SJohannes Hofmann 		set_scan_type(&scan, 1, scan_type(ss, chan));
28370e474d75SJohannes Hofmann 	}
28380e474d75SJohannes Hofmann #ifdef IWI_DEBUG
28390e474d75SJohannes Hofmann 	if (iwi_debug > 0) {
28400e474d75SJohannes Hofmann 		static const char *scantype[8] =
28410e474d75SJohannes Hofmann 		   { "PSTOP", "PASV", "DIR", "BCAST", "BDIR", "5", "6", "7" };
28420e474d75SJohannes Hofmann 		int i;
28430e474d75SJohannes Hofmann 		kprintf("Scan request: index %u dwell %d/%d/%d\n"
28440e474d75SJohannes Hofmann 		    , le32toh(scan.full_scan_index)
28450e474d75SJohannes Hofmann 		    , le16toh(scan.dwell_time[IWI_SCAN_TYPE_PASSIVE])
28460e474d75SJohannes Hofmann 		    , le16toh(scan.dwell_time[IWI_SCAN_TYPE_BROADCAST])
28470e474d75SJohannes Hofmann 		    , le16toh(scan.dwell_time[IWI_SCAN_TYPE_BDIRECTED])
28480e474d75SJohannes Hofmann 		);
28490e474d75SJohannes Hofmann 		i = 0;
28500e474d75SJohannes Hofmann 		do {
28510e474d75SJohannes Hofmann 			int run = scan.channels[i];
28520e474d75SJohannes Hofmann 			if (run == 0)
28530e474d75SJohannes Hofmann 				break;
28540e474d75SJohannes Hofmann 			kprintf("Scan %d %s channels:", run & 0x3f,
28550e474d75SJohannes Hofmann 			    run & IWI_CHAN_2GHZ ? "2.4GHz" : "5GHz");
28560e474d75SJohannes Hofmann 			for (run &= 0x3f, i++; run > 0; run--, i++) {
28570e474d75SJohannes Hofmann 				uint8_t type = scan.scan_type[i/2];
28580e474d75SJohannes Hofmann 				kprintf(" %u/%s", scan.channels[i],
28590e474d75SJohannes Hofmann 				    scantype[(i & 1 ? type : type>>4) & 7]);
28600e474d75SJohannes Hofmann 			}
28610e474d75SJohannes Hofmann 			kprintf("\n");
28620e474d75SJohannes Hofmann 		} while (i < IWI_SCAN_CHANNELS);
28630e474d75SJohannes Hofmann 	}
28640e474d75SJohannes Hofmann #endif
2865841ab66cSSepherosa Ziehau 
28660e474d75SJohannes Hofmann 	return (iwi_cmd(sc, IWI_CMD_SCAN_EXT, &scan, sizeof scan));
2867b50e4759SMatthew Dillon }
2868b50e4759SMatthew Dillon 
2869b50e4759SMatthew Dillon static int
28700e474d75SJohannes Hofmann iwi_set_sensitivity(struct iwi_softc *sc, int8_t rssi_dbm)
2871b50e4759SMatthew Dillon {
28720e474d75SJohannes Hofmann 	struct iwi_sensitivity sens;
28730e474d75SJohannes Hofmann 
28740e474d75SJohannes Hofmann 	DPRINTF(("Setting sensitivity to %d\n", rssi_dbm));
28750e474d75SJohannes Hofmann 
28760e474d75SJohannes Hofmann 	memset(&sens, 0, sizeof sens);
28770e474d75SJohannes Hofmann 	sens.rssi = htole16(rssi_dbm);
28780e474d75SJohannes Hofmann 	return iwi_cmd(sc, IWI_CMD_SET_SENSITIVITY, &sens, sizeof sens);
28790e474d75SJohannes Hofmann }
28800e474d75SJohannes Hofmann 
28810e474d75SJohannes Hofmann static int
28820e474d75SJohannes Hofmann iwi_auth_and_assoc(struct iwi_softc *sc, struct ieee80211vap *vap)
28830e474d75SJohannes Hofmann {
28840e474d75SJohannes Hofmann 	struct ieee80211com *ic = vap->iv_ic;
28850e474d75SJohannes Hofmann 	struct ifnet *ifp = vap->iv_ifp;
2886c4fe7bb1SImre Vadász 	struct ieee80211_node *ni;
2887841ab66cSSepherosa Ziehau 	struct iwi_configuration config;
28880e474d75SJohannes Hofmann 	struct iwi_associate *assoc = &sc->assoc;
2889b50e4759SMatthew Dillon 	struct iwi_rateset rs;
2890841ab66cSSepherosa Ziehau 	uint16_t capinfo;
2891841ab66cSSepherosa Ziehau 	uint32_t data;
28920e474d75SJohannes Hofmann 	int error, mode;
2893c4fe7bb1SImre Vadász 
2894c4fe7bb1SImre Vadász 	IWI_LOCK_ASSERT(sc);
2895c4fe7bb1SImre Vadász 
2896c4fe7bb1SImre Vadász 	ni = ieee80211_ref_node(vap->iv_bss);
2897b50e4759SMatthew Dillon 
28980e474d75SJohannes Hofmann 	if (sc->flags & IWI_FLAG_ASSOCIATED) {
28990e474d75SJohannes Hofmann 		DPRINTF(("Already associated\n"));
29000e474d75SJohannes Hofmann 		return (-1);
29010e474d75SJohannes Hofmann 	}
29020e474d75SJohannes Hofmann 
29030e474d75SJohannes Hofmann 	IWI_STATE_BEGIN(sc, IWI_FW_ASSOCIATING);
29042a0b1ca7SSascha Wildner 	error = 0;
29050e474d75SJohannes Hofmann 	mode = 0;
29060e474d75SJohannes Hofmann 
29070e474d75SJohannes Hofmann 	if (IEEE80211_IS_CHAN_A(ic->ic_curchan))
29080e474d75SJohannes Hofmann 		mode = IWI_MODE_11A;
29090e474d75SJohannes Hofmann 	else if (IEEE80211_IS_CHAN_G(ic->ic_curchan))
29100e474d75SJohannes Hofmann 		mode = IWI_MODE_11G;
29110e474d75SJohannes Hofmann 	if (IEEE80211_IS_CHAN_B(ic->ic_curchan))
29120e474d75SJohannes Hofmann 		mode = IWI_MODE_11B;
29130e474d75SJohannes Hofmann 
29140e474d75SJohannes Hofmann 	if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) {
2915841ab66cSSepherosa Ziehau 		memset(&config, 0, sizeof config);
2916841ab66cSSepherosa Ziehau 		config.bluetooth_coexistence = sc->bluetooth;
2917841ab66cSSepherosa Ziehau 		config.antenna = sc->antenna;
2918841ab66cSSepherosa Ziehau 		config.multicast_enabled = 1;
29190e474d75SJohannes Hofmann 		if (mode == IWI_MODE_11G)
2920841ab66cSSepherosa Ziehau 			config.use_protection = 1;
2921841ab66cSSepherosa Ziehau 		config.answer_pbreq =
29220e474d75SJohannes Hofmann 		    (vap->iv_opmode == IEEE80211_M_IBSS) ? 1 : 0;
2923841ab66cSSepherosa Ziehau 		config.disable_unicast_decryption = 1;
2924841ab66cSSepherosa Ziehau 		config.disable_multicast_decryption = 1;
2925841ab66cSSepherosa Ziehau 		DPRINTF(("Configuring adapter\n"));
29260e474d75SJohannes Hofmann 		error = iwi_cmd(sc, IWI_CMD_SET_CONFIG, &config, sizeof config);
2927841ab66cSSepherosa Ziehau 		if (error != 0)
29280e474d75SJohannes Hofmann 			goto done;
2929b50e4759SMatthew Dillon 	}
2930b50e4759SMatthew Dillon 
2931b50e4759SMatthew Dillon #ifdef IWI_DEBUG
2932841ab66cSSepherosa Ziehau 	if (iwi_debug > 0) {
2933e3869ec7SSascha Wildner 		kprintf("Setting ESSID to ");
2934b50e4759SMatthew Dillon 		ieee80211_print_essid(ni->ni_essid, ni->ni_esslen);
2935e3869ec7SSascha Wildner 		kprintf("\n");
2936b50e4759SMatthew Dillon 	}
2937b50e4759SMatthew Dillon #endif
29380e474d75SJohannes Hofmann 	error = iwi_cmd(sc, IWI_CMD_SET_ESSID, ni->ni_essid, ni->ni_esslen);
2939b50e4759SMatthew Dillon 	if (error != 0)
29400e474d75SJohannes Hofmann 		goto done;
29410e474d75SJohannes Hofmann 
29420e474d75SJohannes Hofmann 	error = iwi_setpowermode(sc, vap);
29430e474d75SJohannes Hofmann 	if (error != 0)
29440e474d75SJohannes Hofmann 		goto done;
29450e474d75SJohannes Hofmann 
29460e474d75SJohannes Hofmann 	data = htole32(vap->iv_rtsthreshold);
29470e474d75SJohannes Hofmann 	DPRINTF(("Setting RTS threshold to %u\n", le32toh(data)));
29480e474d75SJohannes Hofmann 	error = iwi_cmd(sc, IWI_CMD_SET_RTS_THRESHOLD, &data, sizeof data);
29490e474d75SJohannes Hofmann 	if (error != 0)
29500e474d75SJohannes Hofmann 		goto done;
29510e474d75SJohannes Hofmann 
29520e474d75SJohannes Hofmann 	data = htole32(vap->iv_fragthreshold);
29530e474d75SJohannes Hofmann 	DPRINTF(("Setting fragmentation threshold to %u\n", le32toh(data)));
29540e474d75SJohannes Hofmann 	error = iwi_cmd(sc, IWI_CMD_SET_FRAG_THRESHOLD, &data, sizeof data);
29550e474d75SJohannes Hofmann 	if (error != 0)
29560e474d75SJohannes Hofmann 		goto done;
2957b50e4759SMatthew Dillon 
2958b50e4759SMatthew Dillon 	/* the rate set has already been "negotiated" */
29590e474d75SJohannes Hofmann 	memset(&rs, 0, sizeof rs);
29600e474d75SJohannes Hofmann 	rs.mode = mode;
2961b50e4759SMatthew Dillon 	rs.type = IWI_RATESET_TYPE_NEGOTIATED;
2962b50e4759SMatthew Dillon 	rs.nrates = ni->ni_rates.rs_nrates;
29630e474d75SJohannes Hofmann 	if (rs.nrates > IWI_RATESET_SIZE) {
29640e474d75SJohannes Hofmann 		DPRINTF(("Truncating negotiated rate set from %u\n",
29650e474d75SJohannes Hofmann 		    rs.nrates));
29660e474d75SJohannes Hofmann 		rs.nrates = IWI_RATESET_SIZE;
29670e474d75SJohannes Hofmann 	}
2968841ab66cSSepherosa Ziehau 	memcpy(rs.rates, ni->ni_rates.rs_rates, rs.nrates);
29690e474d75SJohannes Hofmann 	DPRINTF(("Setting negotiated rates (%u)\n", rs.nrates));
29700e474d75SJohannes Hofmann 	error = iwi_cmd(sc, IWI_CMD_SET_RATES, &rs, sizeof rs);
2971b50e4759SMatthew Dillon 	if (error != 0)
29720e474d75SJohannes Hofmann 		goto done;
2973b50e4759SMatthew Dillon 
29740e474d75SJohannes Hofmann 	memset(assoc, 0, sizeof *assoc);
2975841ab66cSSepherosa Ziehau 
29760e474d75SJohannes Hofmann 	if ((vap->iv_flags & IEEE80211_F_WME) && ni->ni_ies.wme_ie != NULL) {
29770e474d75SJohannes Hofmann 		/* NB: don't treat WME setup as failure */
2978c4fe7bb1SImre Vadász 		if (iwi_wme_setparams(sc) == 0 && iwi_wme_setie(sc) == 0)
29790e474d75SJohannes Hofmann 			assoc->policy |= htole16(IWI_POLICY_WME);
29800e474d75SJohannes Hofmann 		/* XXX complain on failure? */
2981841ab66cSSepherosa Ziehau 	}
2982841ab66cSSepherosa Ziehau 
29830e474d75SJohannes Hofmann 	if (vap->iv_appie_wpa != NULL) {
29840e474d75SJohannes Hofmann 		struct ieee80211_appie *ie = vap->iv_appie_wpa;
29850e474d75SJohannes Hofmann 
29860e474d75SJohannes Hofmann 		DPRINTF(("Setting optional IE (len=%u)\n", ie->ie_len));
29870e474d75SJohannes Hofmann 		error = iwi_cmd(sc, IWI_CMD_SET_OPTIE, ie->ie_data, ie->ie_len);
2988841ab66cSSepherosa Ziehau 		if (error != 0)
29890e474d75SJohannes Hofmann 			goto done;
2990841ab66cSSepherosa Ziehau 	}
2991841ab66cSSepherosa Ziehau 
29920e474d75SJohannes Hofmann 	error = iwi_set_sensitivity(sc, ic->ic_node_getrssi(ni));
2993b50e4759SMatthew Dillon 	if (error != 0)
29940e474d75SJohannes Hofmann 		goto done;
2995b50e4759SMatthew Dillon 
29960e474d75SJohannes Hofmann 	assoc->mode = mode;
29970e474d75SJohannes Hofmann 	assoc->chan = ic->ic_curchan->ic_ieee;
29980e474d75SJohannes Hofmann 	/*
29990e474d75SJohannes Hofmann 	 * NB: do not arrange for shared key auth w/o privacy
30000e474d75SJohannes Hofmann 	 *     (i.e. a wep key); it causes a firmware error.
30010e474d75SJohannes Hofmann 	 */
30020e474d75SJohannes Hofmann 	if ((vap->iv_flags & IEEE80211_F_PRIVACY) &&
30030e474d75SJohannes Hofmann 	    ni->ni_authmode == IEEE80211_AUTH_SHARED) {
30040e474d75SJohannes Hofmann 		assoc->auth = IWI_AUTH_SHARED;
30050e474d75SJohannes Hofmann 		/*
30060e474d75SJohannes Hofmann 		 * It's possible to have privacy marked but no default
30070e474d75SJohannes Hofmann 		 * key setup.  This typically is due to a user app bug
30080e474d75SJohannes Hofmann 		 * but if we blindly grab the key the firmware will
30090e474d75SJohannes Hofmann 		 * barf so avoid it for now.
30100e474d75SJohannes Hofmann 		 */
30110e474d75SJohannes Hofmann 		if (vap->iv_def_txkey != IEEE80211_KEYIX_NONE)
30120e474d75SJohannes Hofmann 			assoc->auth |= vap->iv_def_txkey << 4;
3013841ab66cSSepherosa Ziehau 
30140e474d75SJohannes Hofmann 		error = iwi_setwepkeys(sc, vap);
30150e474d75SJohannes Hofmann 		if (error != 0)
30160e474d75SJohannes Hofmann 			goto done;
30170e474d75SJohannes Hofmann 	}
30180e474d75SJohannes Hofmann 	if (vap->iv_flags & IEEE80211_F_WPA)
30190e474d75SJohannes Hofmann 		assoc->policy |= htole16(IWI_POLICY_WPA);
30200e474d75SJohannes Hofmann 	if (vap->iv_opmode == IEEE80211_M_IBSS && ni->ni_tstamp.tsf == 0)
30210e474d75SJohannes Hofmann 		assoc->type = IWI_HC_IBSS_START;
30220e474d75SJohannes Hofmann 	else
30230e474d75SJohannes Hofmann 		assoc->type = IWI_HC_ASSOC;
30240e474d75SJohannes Hofmann 	memcpy(assoc->tstamp, ni->ni_tstamp.data, 8);
30250e474d75SJohannes Hofmann 
30260e474d75SJohannes Hofmann 	if (vap->iv_opmode == IEEE80211_M_IBSS)
3027841ab66cSSepherosa Ziehau 		capinfo = IEEE80211_CAPINFO_IBSS;
3028b50e4759SMatthew Dillon 	else
3029841ab66cSSepherosa Ziehau 		capinfo = IEEE80211_CAPINFO_ESS;
30300e474d75SJohannes Hofmann 	if (vap->iv_flags & IEEE80211_F_PRIVACY)
3031841ab66cSSepherosa Ziehau 		capinfo |= IEEE80211_CAPINFO_PRIVACY;
3032841ab66cSSepherosa Ziehau 	if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
30330e474d75SJohannes Hofmann 	    IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan))
3034841ab66cSSepherosa Ziehau 		capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
30350e474d75SJohannes Hofmann 	if (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)
3036841ab66cSSepherosa Ziehau 		capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
30370e474d75SJohannes Hofmann 	assoc->capinfo = htole16(capinfo);
3038841ab66cSSepherosa Ziehau 
30390e474d75SJohannes Hofmann 	assoc->lintval = htole16(ic->ic_lintval);
30400e474d75SJohannes Hofmann 	assoc->intval = htole16(ni->ni_intval);
30410e474d75SJohannes Hofmann 	IEEE80211_ADDR_COPY(assoc->bssid, ni->ni_bssid);
30420e474d75SJohannes Hofmann 	if (vap->iv_opmode == IEEE80211_M_IBSS)
30430e474d75SJohannes Hofmann 		IEEE80211_ADDR_COPY(assoc->dst, ifp->if_broadcastaddr);
3044841ab66cSSepherosa Ziehau 	else
30450e474d75SJohannes Hofmann 		IEEE80211_ADDR_COPY(assoc->dst, ni->ni_bssid);
3046b50e4759SMatthew Dillon 
3047c4fe7bb1SImre Vadász #if defined(__DragonFly__)
30481e290df3SAntonio Huete Jimenez 	DPRINTF(("%s bssid %s dst %s channel %u policy 0x%x "
30490e474d75SJohannes Hofmann 	    "auth %u capinfo 0x%x lintval %u bintval %u\n",
30500e474d75SJohannes Hofmann 	    assoc->type == IWI_HC_IBSS_START ? "Start" : "Join",
3051c4fe7bb1SImre Vadász 	    ether_sprintf(assoc->bssid), ether_sprintf(assoc->dst),
30520e474d75SJohannes Hofmann 	    assoc->chan, le16toh(assoc->policy), assoc->auth,
30530e474d75SJohannes Hofmann 	    le16toh(assoc->capinfo), le16toh(assoc->lintval),
30540e474d75SJohannes Hofmann 	    le16toh(assoc->intval)));
3055c4fe7bb1SImre Vadász #else
3056c4fe7bb1SImre Vadász 	DPRINTF(("%s bssid %6D dst %6D channel %u policy 0x%x "
3057c4fe7bb1SImre Vadász 	    "auth %u capinfo 0x%x lintval %u bintval %u\n",
3058c4fe7bb1SImre Vadász 	    assoc->type == IWI_HC_IBSS_START ? "Start" : "Join",
3059c4fe7bb1SImre Vadász 	    assoc->bssid, ":", assoc->dst, ":",
3060c4fe7bb1SImre Vadász 	    assoc->chan, le16toh(assoc->policy), assoc->auth,
3061c4fe7bb1SImre Vadász 	    le16toh(assoc->capinfo), le16toh(assoc->lintval),
3062c4fe7bb1SImre Vadász 	    le16toh(assoc->intval)));
3063c4fe7bb1SImre Vadász #endif
30640e474d75SJohannes Hofmann 	error = iwi_cmd(sc, IWI_CMD_ASSOCIATE, assoc, sizeof *assoc);
30650e474d75SJohannes Hofmann done:
3066c4fe7bb1SImre Vadász 	ieee80211_free_node(ni);
30670e474d75SJohannes Hofmann 	if (error)
30680e474d75SJohannes Hofmann 		IWI_STATE_END(sc, IWI_FW_ASSOCIATING);
30690e474d75SJohannes Hofmann 
30700e474d75SJohannes Hofmann 	return (error);
3071b50e4759SMatthew Dillon }
3072b50e4759SMatthew Dillon 
3073b50e4759SMatthew Dillon static void
3074c4fe7bb1SImre Vadász iwi_disassoc(void *arg, int pending)
3075b50e4759SMatthew Dillon {
30760e474d75SJohannes Hofmann 	struct iwi_softc *sc = arg;
3077c4fe7bb1SImre Vadász 	IWI_LOCK_DECL;
30780e474d75SJohannes Hofmann 
3079c4fe7bb1SImre Vadász 	IWI_LOCK(sc);
30800e474d75SJohannes Hofmann 	iwi_disassociate(sc, 0);
3081c4fe7bb1SImre Vadász 	IWI_UNLOCK(sc);
30820e474d75SJohannes Hofmann }
30830e474d75SJohannes Hofmann 
30840e474d75SJohannes Hofmann static int
30850e474d75SJohannes Hofmann iwi_disassociate(struct iwi_softc *sc, int quiet)
30860e474d75SJohannes Hofmann {
30870e474d75SJohannes Hofmann 	struct iwi_associate *assoc = &sc->assoc;
30880e474d75SJohannes Hofmann 
30890e474d75SJohannes Hofmann 	if ((sc->flags & IWI_FLAG_ASSOCIATED) == 0) {
30900e474d75SJohannes Hofmann 		DPRINTF(("Not associated\n"));
30910e474d75SJohannes Hofmann 		return (-1);
30920e474d75SJohannes Hofmann 	}
30930e474d75SJohannes Hofmann 
30940e474d75SJohannes Hofmann 	IWI_STATE_BEGIN(sc, IWI_FW_DISASSOCIATING);
30950e474d75SJohannes Hofmann 
30960e474d75SJohannes Hofmann 	if (quiet)
30970e474d75SJohannes Hofmann 		assoc->type = IWI_HC_DISASSOC_QUIET;
30980e474d75SJohannes Hofmann 	else
30990e474d75SJohannes Hofmann 		assoc->type = IWI_HC_DISASSOC;
31000e474d75SJohannes Hofmann 
3101c4fe7bb1SImre Vadász #if defined(__DragonFly__)
31021e290df3SAntonio Huete Jimenez 	DPRINTF(("Trying to disassociate from %s channel %u\n",
3103c4fe7bb1SImre Vadász 	    ether_sprintf(assoc->bssid), assoc->chan));
3104c4fe7bb1SImre Vadász #else
3105c4fe7bb1SImre Vadász 	DPRINTF(("Trying to disassociate from %6D channel %u\n",
3106c4fe7bb1SImre Vadász 	    assoc->bssid, ":", assoc->chan));
3107c4fe7bb1SImre Vadász #endif
31080e474d75SJohannes Hofmann 	return iwi_cmd(sc, IWI_CMD_ASSOCIATE, assoc, sizeof *assoc);
31090e474d75SJohannes Hofmann }
31100e474d75SJohannes Hofmann 
31110e474d75SJohannes Hofmann /*
31120e474d75SJohannes Hofmann  * release dma resources for the firmware
31130e474d75SJohannes Hofmann  */
31140e474d75SJohannes Hofmann static void
31150e474d75SJohannes Hofmann iwi_release_fw_dma(struct iwi_softc *sc)
31160e474d75SJohannes Hofmann {
31170e474d75SJohannes Hofmann 	if (sc->fw_flags & IWI_FW_HAVE_PHY)
31180e474d75SJohannes Hofmann 		bus_dmamap_unload(sc->fw_dmat, sc->fw_map);
31190e474d75SJohannes Hofmann 	if (sc->fw_flags & IWI_FW_HAVE_MAP)
31200e474d75SJohannes Hofmann 		bus_dmamem_free(sc->fw_dmat, sc->fw_virtaddr, sc->fw_map);
31210e474d75SJohannes Hofmann 	if (sc->fw_flags & IWI_FW_HAVE_DMAT)
31220e474d75SJohannes Hofmann 		bus_dma_tag_destroy(sc->fw_dmat);
31230e474d75SJohannes Hofmann 
31240e474d75SJohannes Hofmann 	sc->fw_flags = 0;
31250e474d75SJohannes Hofmann 	sc->fw_dma_size = 0;
31260e474d75SJohannes Hofmann 	sc->fw_dmat = NULL;
31270e474d75SJohannes Hofmann 	sc->fw_map = NULL;
31280e474d75SJohannes Hofmann 	sc->fw_physaddr = 0;
31290e474d75SJohannes Hofmann 	sc->fw_virtaddr = NULL;
31300e474d75SJohannes Hofmann }
31310e474d75SJohannes Hofmann 
31320e474d75SJohannes Hofmann /*
31330e474d75SJohannes Hofmann  * allocate the dma descriptor for the firmware.
31340e474d75SJohannes Hofmann  * Return 0 on success, 1 on error.
31350e474d75SJohannes Hofmann  * Must be called unlocked, protected by IWI_FLAG_FW_LOADING.
31360e474d75SJohannes Hofmann  */
31370e474d75SJohannes Hofmann static int
31380e474d75SJohannes Hofmann iwi_init_fw_dma(struct iwi_softc *sc, int size)
31390e474d75SJohannes Hofmann {
31400e474d75SJohannes Hofmann 	if (sc->fw_dma_size >= size)
31410e474d75SJohannes Hofmann 		return 0;
3142c4fe7bb1SImre Vadász #if defined(__DragonFly__)
31430e474d75SJohannes Hofmann 	if (bus_dma_tag_create(NULL, 4, 0, BUS_SPACE_MAXADDR_32BIT,
3144030b0c8cSMichael Neumann 	    BUS_SPACE_MAXADDR, size, 1, size,
31450e474d75SJohannes Hofmann 	    0, &sc->fw_dmat) != 0) {
3146c4fe7bb1SImre Vadász #else
3147c4fe7bb1SImre Vadász 	if (bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 4, 0,
3148c4fe7bb1SImre Vadász 	    BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
3149c4fe7bb1SImre Vadász 	    size, 1, size, 0, NULL, NULL, &sc->fw_dmat) != 0) {
3150c4fe7bb1SImre Vadász #endif
31510e474d75SJohannes Hofmann 		device_printf(sc->sc_dev,
31520e474d75SJohannes Hofmann 		    "could not create firmware DMA tag\n");
31530e474d75SJohannes Hofmann 		goto error;
31540e474d75SJohannes Hofmann 	}
31550e474d75SJohannes Hofmann 	sc->fw_flags |= IWI_FW_HAVE_DMAT;
31560e474d75SJohannes Hofmann 	if (bus_dmamem_alloc(sc->fw_dmat, &sc->fw_virtaddr, 0,
31570e474d75SJohannes Hofmann 	    &sc->fw_map) != 0) {
31580e474d75SJohannes Hofmann 		device_printf(sc->sc_dev,
31590e474d75SJohannes Hofmann 		    "could not allocate firmware DMA memory\n");
31600e474d75SJohannes Hofmann 		goto error;
31610e474d75SJohannes Hofmann 	}
31620e474d75SJohannes Hofmann 	sc->fw_flags |= IWI_FW_HAVE_MAP;
31630e474d75SJohannes Hofmann 	if (bus_dmamap_load(sc->fw_dmat, sc->fw_map, sc->fw_virtaddr,
31640e474d75SJohannes Hofmann 	    size, iwi_dma_map_addr, &sc->fw_physaddr, 0) != 0) {
31650e474d75SJohannes Hofmann 		device_printf(sc->sc_dev, "could not load firmware DMA map\n");
31660e474d75SJohannes Hofmann 		goto error;
31670e474d75SJohannes Hofmann 	}
31680e474d75SJohannes Hofmann 	sc->fw_flags |= IWI_FW_HAVE_PHY;
31690e474d75SJohannes Hofmann 	sc->fw_dma_size = size;
31700e474d75SJohannes Hofmann 	return 0;
31710e474d75SJohannes Hofmann 
31720e474d75SJohannes Hofmann error:
31730e474d75SJohannes Hofmann 	iwi_release_fw_dma(sc);
31740e474d75SJohannes Hofmann 	return 1;
31750e474d75SJohannes Hofmann }
31760e474d75SJohannes Hofmann 
31770e474d75SJohannes Hofmann static void
31780e474d75SJohannes Hofmann iwi_init_locked(struct iwi_softc *sc)
31790e474d75SJohannes Hofmann {
3180841ab66cSSepherosa Ziehau 	struct iwi_rx_data *data;
3181b50e4759SMatthew Dillon 	int i;
3182b50e4759SMatthew Dillon 
3183c4fe7bb1SImre Vadász 	IWI_LOCK_ASSERT(sc);
3184c4fe7bb1SImre Vadász 
31850e474d75SJohannes Hofmann 	if (sc->fw_state == IWI_FW_LOADING) {
31860e474d75SJohannes Hofmann 		device_printf(sc->sc_dev, "%s: already loading\n", __func__);
31870e474d75SJohannes Hofmann 		return;		/* XXX: condvar? */
31880e474d75SJohannes Hofmann 	}
31890e474d75SJohannes Hofmann 
31900e474d75SJohannes Hofmann 	iwi_stop_locked(sc);
31910e474d75SJohannes Hofmann 
31920e474d75SJohannes Hofmann 	IWI_STATE_BEGIN(sc, IWI_FW_LOADING);
3193b50e4759SMatthew Dillon 
3194b50e4759SMatthew Dillon 	if (iwi_reset(sc) != 0) {
3195b50e4759SMatthew Dillon 		device_printf(sc->sc_dev, "could not reset adapter\n");
3196b50e4759SMatthew Dillon 		goto fail;
3197b50e4759SMatthew Dillon 	}
31980e474d75SJohannes Hofmann 	if (iwi_load_firmware(sc, &sc->fw_boot) != 0) {
31990e474d75SJohannes Hofmann 		device_printf(sc->sc_dev,
32000e474d75SJohannes Hofmann 		    "could not load boot firmware %s\n", sc->fw_boot.name);
3201114b4cbcSSepherosa Ziehau 		goto fail;
3202114b4cbcSSepherosa Ziehau 	}
32030e474d75SJohannes Hofmann 	if (iwi_load_ucode(sc, &sc->fw_uc) != 0) {
32040e474d75SJohannes Hofmann 		device_printf(sc->sc_dev,
32050e474d75SJohannes Hofmann 		    "could not load microcode %s\n", sc->fw_uc.name);
3206b50e4759SMatthew Dillon 		goto fail;
3207b50e4759SMatthew Dillon 	}
3208b50e4759SMatthew Dillon 
3209b50e4759SMatthew Dillon 	iwi_stop_master(sc);
3210b50e4759SMatthew Dillon 
3211841ab66cSSepherosa Ziehau 	CSR_WRITE_4(sc, IWI_CSR_CMD_BASE, sc->cmdq.physaddr);
3212841ab66cSSepherosa Ziehau 	CSR_WRITE_4(sc, IWI_CSR_CMD_SIZE, sc->cmdq.count);
3213841ab66cSSepherosa Ziehau 	CSR_WRITE_4(sc, IWI_CSR_CMD_WIDX, sc->cmdq.cur);
3214b50e4759SMatthew Dillon 
3215841ab66cSSepherosa Ziehau 	CSR_WRITE_4(sc, IWI_CSR_TX1_BASE, sc->txq[0].physaddr);
3216841ab66cSSepherosa Ziehau 	CSR_WRITE_4(sc, IWI_CSR_TX1_SIZE, sc->txq[0].count);
3217841ab66cSSepherosa Ziehau 	CSR_WRITE_4(sc, IWI_CSR_TX1_WIDX, sc->txq[0].cur);
3218b50e4759SMatthew Dillon 
3219841ab66cSSepherosa Ziehau 	CSR_WRITE_4(sc, IWI_CSR_TX2_BASE, sc->txq[1].physaddr);
3220841ab66cSSepherosa Ziehau 	CSR_WRITE_4(sc, IWI_CSR_TX2_SIZE, sc->txq[1].count);
3221841ab66cSSepherosa Ziehau 	CSR_WRITE_4(sc, IWI_CSR_TX2_WIDX, sc->txq[1].cur);
3222b50e4759SMatthew Dillon 
3223841ab66cSSepherosa Ziehau 	CSR_WRITE_4(sc, IWI_CSR_TX3_BASE, sc->txq[2].physaddr);
3224841ab66cSSepherosa Ziehau 	CSR_WRITE_4(sc, IWI_CSR_TX3_SIZE, sc->txq[2].count);
3225841ab66cSSepherosa Ziehau 	CSR_WRITE_4(sc, IWI_CSR_TX3_WIDX, sc->txq[2].cur);
3226b50e4759SMatthew Dillon 
3227841ab66cSSepherosa Ziehau 	CSR_WRITE_4(sc, IWI_CSR_TX4_BASE, sc->txq[3].physaddr);
3228841ab66cSSepherosa Ziehau 	CSR_WRITE_4(sc, IWI_CSR_TX4_SIZE, sc->txq[3].count);
3229841ab66cSSepherosa Ziehau 	CSR_WRITE_4(sc, IWI_CSR_TX4_WIDX, sc->txq[3].cur);
3230b50e4759SMatthew Dillon 
3231841ab66cSSepherosa Ziehau 	for (i = 0; i < sc->rxq.count; i++) {
3232841ab66cSSepherosa Ziehau 		data = &sc->rxq.data[i];
3233841ab66cSSepherosa Ziehau 		CSR_WRITE_4(sc, data->reg, data->physaddr);
3234841ab66cSSepherosa Ziehau 	}
3235b50e4759SMatthew Dillon 
3236841ab66cSSepherosa Ziehau 	CSR_WRITE_4(sc, IWI_CSR_RX_WIDX, sc->rxq.count - 1);
3237b50e4759SMatthew Dillon 
32380e474d75SJohannes Hofmann 	if (iwi_load_firmware(sc, &sc->fw_fw) != 0) {
32390e474d75SJohannes Hofmann 		device_printf(sc->sc_dev,
32400e474d75SJohannes Hofmann 		    "could not load main firmware %s\n", sc->fw_fw.name);
3241b50e4759SMatthew Dillon 		goto fail;
3242b50e4759SMatthew Dillon 	}
3243b50e4759SMatthew Dillon 	sc->flags |= IWI_FLAG_FW_INITED;
3244b50e4759SMatthew Dillon 
32450e474d75SJohannes Hofmann 	IWI_STATE_END(sc, IWI_FW_LOADING);
32460e474d75SJohannes Hofmann 
3247b50e4759SMatthew Dillon 	if (iwi_config(sc) != 0) {
32480e474d75SJohannes Hofmann 		device_printf(sc->sc_dev, "unable to enable adapter\n");
32490e474d75SJohannes Hofmann 		goto fail2;
3250b50e4759SMatthew Dillon 	}
3251b50e4759SMatthew Dillon 
3252c4fe7bb1SImre Vadász 	callout_reset(&sc->sc_wdtimer, hz, iwi_watchdog, sc);
3253c4fe7bb1SImre Vadász 	sc->sc_running = 1;
3254b50e4759SMatthew Dillon 	return;
32550e474d75SJohannes Hofmann fail:
32560e474d75SJohannes Hofmann 	IWI_STATE_END(sc, IWI_FW_LOADING);
32570e474d75SJohannes Hofmann fail2:
32580e474d75SJohannes Hofmann 	iwi_stop_locked(sc);
3259b50e4759SMatthew Dillon }
3260b50e4759SMatthew Dillon 
3261b50e4759SMatthew Dillon static void
32620e474d75SJohannes Hofmann iwi_init(void *priv)
3263b50e4759SMatthew Dillon {
3264b50e4759SMatthew Dillon 	struct iwi_softc *sc = priv;
3265c4fe7bb1SImre Vadász 	struct ieee80211com *ic = &sc->sc_ic;
3266c4fe7bb1SImre Vadász 	IWI_LOCK_DECL;
3267b50e4759SMatthew Dillon 
3268c4fe7bb1SImre Vadász 	IWI_LOCK(sc);
32690e474d75SJohannes Hofmann 	iwi_init_locked(sc);
3270c4fe7bb1SImre Vadász 	IWI_UNLOCK(sc);
32710e474d75SJohannes Hofmann 
3272c4fe7bb1SImre Vadász 	if (sc->sc_running)
32730e474d75SJohannes Hofmann 		ieee80211_start_all(ic);
32740e474d75SJohannes Hofmann }
32750e474d75SJohannes Hofmann 
32760e474d75SJohannes Hofmann static void
32770e474d75SJohannes Hofmann iwi_stop_locked(void *priv)
32780e474d75SJohannes Hofmann {
32790e474d75SJohannes Hofmann 	struct iwi_softc *sc = priv;
32800e474d75SJohannes Hofmann 
3281c4fe7bb1SImre Vadász 	IWI_LOCK_ASSERT(sc);
3282c4fe7bb1SImre Vadász 
3283c4fe7bb1SImre Vadász 	sc->sc_running = 0;
32840e474d75SJohannes Hofmann 
32850e474d75SJohannes Hofmann 	if (sc->sc_softled) {
3286c4fe7bb1SImre Vadász 		callout_stop(&sc->sc_ledtimer);
32870e474d75SJohannes Hofmann 		sc->sc_blinking = 0;
32880e474d75SJohannes Hofmann 	}
3289c4fe7bb1SImre Vadász 	callout_stop(&sc->sc_wdtimer);
3290c4fe7bb1SImre Vadász 	callout_stop(&sc->sc_rftimer);
3291b50e4759SMatthew Dillon 
3292841ab66cSSepherosa Ziehau 	iwi_stop_master(sc);
3293b50e4759SMatthew Dillon 
3294841ab66cSSepherosa Ziehau 	CSR_WRITE_4(sc, IWI_CSR_RST, IWI_RST_SOFT_RESET);
3295b50e4759SMatthew Dillon 
3296841ab66cSSepherosa Ziehau 	/* reset rings */
3297841ab66cSSepherosa Ziehau 	iwi_reset_cmd_ring(sc, &sc->cmdq);
3298841ab66cSSepherosa Ziehau 	iwi_reset_tx_ring(sc, &sc->txq[0]);
3299841ab66cSSepherosa Ziehau 	iwi_reset_tx_ring(sc, &sc->txq[1]);
3300841ab66cSSepherosa Ziehau 	iwi_reset_tx_ring(sc, &sc->txq[2]);
3301841ab66cSSepherosa Ziehau 	iwi_reset_tx_ring(sc, &sc->txq[3]);
3302841ab66cSSepherosa Ziehau 	iwi_reset_rx_ring(sc, &sc->rxq);
3303b50e4759SMatthew Dillon 
3304841ab66cSSepherosa Ziehau 	sc->sc_tx_timer = 0;
33050e474d75SJohannes Hofmann 	sc->sc_state_timer = 0;
33060e474d75SJohannes Hofmann 	sc->sc_busy_timer = 0;
33070e474d75SJohannes Hofmann 	sc->flags &= ~(IWI_FLAG_BUSY | IWI_FLAG_ASSOCIATED);
33080e474d75SJohannes Hofmann 	sc->fw_state = IWI_FW_IDLE;
33090e474d75SJohannes Hofmann 	wakeup(sc);
33100e474d75SJohannes Hofmann }
33110e474d75SJohannes Hofmann 
33120e474d75SJohannes Hofmann static void
33130e474d75SJohannes Hofmann iwi_stop(struct iwi_softc *sc)
33140e474d75SJohannes Hofmann {
3315c4fe7bb1SImre Vadász 	IWI_LOCK_DECL;
3316c4fe7bb1SImre Vadász 
3317c4fe7bb1SImre Vadász 	IWI_LOCK(sc);
33180e474d75SJohannes Hofmann 	iwi_stop_locked(sc);
3319c4fe7bb1SImre Vadász 	IWI_UNLOCK(sc);
33200e474d75SJohannes Hofmann }
33210e474d75SJohannes Hofmann 
33220e474d75SJohannes Hofmann static void
3323c4fe7bb1SImre Vadász iwi_restart(void *arg, int npending)
33240e474d75SJohannes Hofmann {
33250e474d75SJohannes Hofmann 	struct iwi_softc *sc = arg;
33260e474d75SJohannes Hofmann 
33270e474d75SJohannes Hofmann 	iwi_init(sc);
33280e474d75SJohannes Hofmann }
33290e474d75SJohannes Hofmann 
33300e474d75SJohannes Hofmann /*
33310e474d75SJohannes Hofmann  * Return whether or not the radio is enabled in hardware
33320e474d75SJohannes Hofmann  * (i.e. the rfkill switch is "off").
33330e474d75SJohannes Hofmann  */
33340e474d75SJohannes Hofmann static int
33350e474d75SJohannes Hofmann iwi_getrfkill(struct iwi_softc *sc)
33360e474d75SJohannes Hofmann {
33370e474d75SJohannes Hofmann 	return (CSR_READ_4(sc, IWI_CSR_IO) & IWI_IO_RADIO_ENABLED) == 0;
33380e474d75SJohannes Hofmann }
33390e474d75SJohannes Hofmann 
33400e474d75SJohannes Hofmann static void
3341c4fe7bb1SImre Vadász iwi_radio_on(void *arg, int pending)
33420e474d75SJohannes Hofmann {
33430e474d75SJohannes Hofmann 	struct iwi_softc *sc = arg;
3344c4fe7bb1SImre Vadász 	struct ieee80211com *ic = &sc->sc_ic;
33450e474d75SJohannes Hofmann 
33460e474d75SJohannes Hofmann 	device_printf(sc->sc_dev, "radio turned on\n");
33470e474d75SJohannes Hofmann 
33480e474d75SJohannes Hofmann 	iwi_init(sc);
33490e474d75SJohannes Hofmann 	ieee80211_notify_radio(ic, 1);
33500e474d75SJohannes Hofmann }
33510e474d75SJohannes Hofmann 
33520e474d75SJohannes Hofmann static void
33530e474d75SJohannes Hofmann iwi_rfkill_poll(void *arg)
33540e474d75SJohannes Hofmann {
33550e474d75SJohannes Hofmann 	struct iwi_softc *sc = arg;
33560e474d75SJohannes Hofmann 
3357c4fe7bb1SImre Vadász 	IWI_LOCK_ASSERT(sc);
3358c4fe7bb1SImre Vadász 
33590e474d75SJohannes Hofmann 	/*
33600e474d75SJohannes Hofmann 	 * Check for a change in rfkill state.  We get an
33610e474d75SJohannes Hofmann 	 * interrupt when a radio is disabled but not when
33620e474d75SJohannes Hofmann 	 * it is enabled so we must poll for the latter.
33630e474d75SJohannes Hofmann 	 */
33640e474d75SJohannes Hofmann 	if (!iwi_getrfkill(sc)) {
3365c4fe7bb1SImre Vadász 		ieee80211_runtask(&sc->sc_ic, &sc->sc_radiontask);
33660e474d75SJohannes Hofmann 		return;
33670e474d75SJohannes Hofmann 	}
3368c4fe7bb1SImre Vadász 	callout_reset(&sc->sc_rftimer, 2*hz, iwi_rfkill_poll, sc);
33690e474d75SJohannes Hofmann }
33700e474d75SJohannes Hofmann 
33710e474d75SJohannes Hofmann static void
3372c4fe7bb1SImre Vadász iwi_radio_off(void *arg, int pending)
33730e474d75SJohannes Hofmann {
33740e474d75SJohannes Hofmann 	struct iwi_softc *sc = arg;
3375c4fe7bb1SImre Vadász 	struct ieee80211com *ic = &sc->sc_ic;
3376c4fe7bb1SImre Vadász 	IWI_LOCK_DECL;
33770e474d75SJohannes Hofmann 
33780e474d75SJohannes Hofmann 	device_printf(sc->sc_dev, "radio turned off\n");
33790e474d75SJohannes Hofmann 
33800e474d75SJohannes Hofmann 	ieee80211_notify_radio(ic, 0);
33810e474d75SJohannes Hofmann 
3382c4fe7bb1SImre Vadász 	IWI_LOCK(sc);
33830e474d75SJohannes Hofmann 	iwi_stop_locked(sc);
33840e474d75SJohannes Hofmann 	iwi_rfkill_poll(sc);
3385c4fe7bb1SImre Vadász 	IWI_UNLOCK(sc);
3386b50e4759SMatthew Dillon }
3387b50e4759SMatthew Dillon 
3388b50e4759SMatthew Dillon static int
3389b50e4759SMatthew Dillon iwi_sysctl_stats(SYSCTL_HANDLER_ARGS)
3390b50e4759SMatthew Dillon {
3391b50e4759SMatthew Dillon 	struct iwi_softc *sc = arg1;
3392841ab66cSSepherosa Ziehau 	uint32_t size, buf[128];
3393b50e4759SMatthew Dillon 
3394841ab66cSSepherosa Ziehau 	memset(buf, 0, sizeof buf);
33950e474d75SJohannes Hofmann 
33960e474d75SJohannes Hofmann 	if (!(sc->flags & IWI_FLAG_FW_INITED))
33970e474d75SJohannes Hofmann 		return SYSCTL_OUT(req, buf, sizeof buf);
3398b50e4759SMatthew Dillon 
3399b50e4759SMatthew Dillon 	size = min(CSR_READ_4(sc, IWI_CSR_TABLE0_SIZE), 128 - 1);
3400841ab66cSSepherosa Ziehau 	CSR_READ_REGION_4(sc, IWI_CSR_TABLE0_BASE, &buf[1], size);
34010e474d75SJohannes Hofmann 
34020e474d75SJohannes Hofmann 	return SYSCTL_OUT(req, buf, size);
3403b50e4759SMatthew Dillon }
3404b50e4759SMatthew Dillon 
3405b50e4759SMatthew Dillon static int
3406b50e4759SMatthew Dillon iwi_sysctl_radio(SYSCTL_HANDLER_ARGS)
3407b50e4759SMatthew Dillon {
3408b50e4759SMatthew Dillon 	struct iwi_softc *sc = arg1;
34090e474d75SJohannes Hofmann 	int val = !iwi_getrfkill(sc);
3410841ab66cSSepherosa Ziehau 
3411b50e4759SMatthew Dillon 	return SYSCTL_OUT(req, &val, sizeof val);
3412b50e4759SMatthew Dillon }
3413b50e4759SMatthew Dillon 
34140e474d75SJohannes Hofmann /*
34150e474d75SJohannes Hofmann  * Add sysctl knobs.
34160e474d75SJohannes Hofmann  */
34170e474d75SJohannes Hofmann static void
34180e474d75SJohannes Hofmann iwi_sysctlattach(struct iwi_softc *sc)
3419b50e4759SMatthew Dillon {
3420c4fe7bb1SImre Vadász 	struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev);
3421c4fe7bb1SImre Vadász 	struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev);
34220e474d75SJohannes Hofmann 
34230e474d75SJohannes Hofmann 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "radio",
34240e474d75SJohannes Hofmann 	    CTLTYPE_INT | CTLFLAG_RD, sc, 0, iwi_sysctl_radio, "I",
34250e474d75SJohannes Hofmann 	    "radio transmitter switch state (0=off, 1=on)");
34260e474d75SJohannes Hofmann 
34270e474d75SJohannes Hofmann 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "stats",
34280e474d75SJohannes Hofmann 	    CTLTYPE_OPAQUE | CTLFLAG_RD, sc, 0, iwi_sysctl_stats, "S",
34290e474d75SJohannes Hofmann 	    "statistics");
34300e474d75SJohannes Hofmann 
34310e474d75SJohannes Hofmann 	sc->bluetooth = 0;
34320e474d75SJohannes Hofmann 	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "bluetooth",
34330e474d75SJohannes Hofmann 	    CTLFLAG_RW, &sc->bluetooth, 0, "bluetooth coexistence");
34340e474d75SJohannes Hofmann 
34350e474d75SJohannes Hofmann 	sc->antenna = IWI_ANTENNA_AUTO;
34360e474d75SJohannes Hofmann 	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "antenna",
34370e474d75SJohannes Hofmann 	    CTLFLAG_RW, &sc->antenna, 0, "antenna (0=auto)");
3438b50e4759SMatthew Dillon }
34390e474d75SJohannes Hofmann 
34400e474d75SJohannes Hofmann /*
34410e474d75SJohannes Hofmann  * LED support.
34420e474d75SJohannes Hofmann  *
34430e474d75SJohannes Hofmann  * Different cards have different capabilities.  Some have three
34440e474d75SJohannes Hofmann  * led's while others have only one.  The linux ipw driver defines
34450e474d75SJohannes Hofmann  * led's for link state (associated or not), band (11a, 11g, 11b),
34460e474d75SJohannes Hofmann  * and for link activity.  We use one led and vary the blink rate
34470e474d75SJohannes Hofmann  * according to the tx/rx traffic a la the ath driver.
34480e474d75SJohannes Hofmann  */
34490e474d75SJohannes Hofmann 
34500e474d75SJohannes Hofmann static __inline uint32_t
34510e474d75SJohannes Hofmann iwi_toggle_event(uint32_t r)
34520e474d75SJohannes Hofmann {
34530e474d75SJohannes Hofmann 	return r &~ (IWI_RST_STANDBY | IWI_RST_GATE_ODMA |
34540e474d75SJohannes Hofmann 		     IWI_RST_GATE_IDMA | IWI_RST_GATE_ADMA);
34550e474d75SJohannes Hofmann }
34560e474d75SJohannes Hofmann 
34570e474d75SJohannes Hofmann static uint32_t
34580e474d75SJohannes Hofmann iwi_read_event(struct iwi_softc *sc)
34590e474d75SJohannes Hofmann {
34600e474d75SJohannes Hofmann 	return MEM_READ_4(sc, IWI_MEM_EEPROM_EVENT);
3461b50e4759SMatthew Dillon }
3462b50e4759SMatthew Dillon 
3463841ab66cSSepherosa Ziehau static void
34640e474d75SJohannes Hofmann iwi_write_event(struct iwi_softc *sc, uint32_t v)
3465b50e4759SMatthew Dillon {
34660e474d75SJohannes Hofmann 	MEM_WRITE_4(sc, IWI_MEM_EEPROM_EVENT, v);
34670e474d75SJohannes Hofmann }
3468b50e4759SMatthew Dillon 
34690e474d75SJohannes Hofmann static void
34700e474d75SJohannes Hofmann iwi_led_done(void *arg)
34710e474d75SJohannes Hofmann {
34720e474d75SJohannes Hofmann 	struct iwi_softc *sc = arg;
3473b50e4759SMatthew Dillon 
34740e474d75SJohannes Hofmann 	sc->sc_blinking = 0;
34750e474d75SJohannes Hofmann }
3476841ab66cSSepherosa Ziehau 
34770e474d75SJohannes Hofmann /*
34780e474d75SJohannes Hofmann  * Turn the activity LED off: flip the pin and then set a timer so no
34790e474d75SJohannes Hofmann  * update will happen for the specified duration.
34800e474d75SJohannes Hofmann  */
34810e474d75SJohannes Hofmann static void
34820e474d75SJohannes Hofmann iwi_led_off(void *arg)
34830e474d75SJohannes Hofmann {
34840e474d75SJohannes Hofmann 	struct iwi_softc *sc = arg;
34850e474d75SJohannes Hofmann 	uint32_t v;
34860e474d75SJohannes Hofmann 
34870e474d75SJohannes Hofmann 	v = iwi_read_event(sc);
34880e474d75SJohannes Hofmann 	v &= ~sc->sc_ledpin;
34890e474d75SJohannes Hofmann 	iwi_write_event(sc, iwi_toggle_event(v));
3490c4fe7bb1SImre Vadász 	callout_reset(&sc->sc_ledtimer, sc->sc_ledoff, iwi_led_done, sc);
34910e474d75SJohannes Hofmann }
34920e474d75SJohannes Hofmann 
34930e474d75SJohannes Hofmann /*
34940e474d75SJohannes Hofmann  * Blink the LED according to the specified on/off times.
34950e474d75SJohannes Hofmann  */
34960e474d75SJohannes Hofmann static void
34970e474d75SJohannes Hofmann iwi_led_blink(struct iwi_softc *sc, int on, int off)
34980e474d75SJohannes Hofmann {
34990e474d75SJohannes Hofmann 	uint32_t v;
35000e474d75SJohannes Hofmann 
35010e474d75SJohannes Hofmann 	v = iwi_read_event(sc);
35020e474d75SJohannes Hofmann 	v |= sc->sc_ledpin;
35030e474d75SJohannes Hofmann 	iwi_write_event(sc, iwi_toggle_event(v));
35040e474d75SJohannes Hofmann 	sc->sc_blinking = 1;
35050e474d75SJohannes Hofmann 	sc->sc_ledoff = off;
3506c4fe7bb1SImre Vadász 	callout_reset(&sc->sc_ledtimer, on, iwi_led_off, sc);
35070e474d75SJohannes Hofmann }
35080e474d75SJohannes Hofmann 
35090e474d75SJohannes Hofmann static void
35100e474d75SJohannes Hofmann iwi_led_event(struct iwi_softc *sc, int event)
35110e474d75SJohannes Hofmann {
35120e474d75SJohannes Hofmann 	/* NB: on/off times from the Atheros NDIS driver, w/ permission */
35130e474d75SJohannes Hofmann 	static const struct {
35140e474d75SJohannes Hofmann 		u_int		rate;		/* tx/rx iwi rate */
35150e474d75SJohannes Hofmann 		u_int16_t	timeOn;		/* LED on time (ms) */
35160e474d75SJohannes Hofmann 		u_int16_t	timeOff;	/* LED off time (ms) */
35170e474d75SJohannes Hofmann 	} blinkrates[] = {
35180e474d75SJohannes Hofmann 		{ IWI_RATE_OFDM54, 40,  10 },
35190e474d75SJohannes Hofmann 		{ IWI_RATE_OFDM48, 44,  11 },
35200e474d75SJohannes Hofmann 		{ IWI_RATE_OFDM36, 50,  13 },
35210e474d75SJohannes Hofmann 		{ IWI_RATE_OFDM24, 57,  14 },
35220e474d75SJohannes Hofmann 		{ IWI_RATE_OFDM18, 67,  16 },
35230e474d75SJohannes Hofmann 		{ IWI_RATE_OFDM12, 80,  20 },
35240e474d75SJohannes Hofmann 		{ IWI_RATE_DS11,  100,  25 },
35250e474d75SJohannes Hofmann 		{ IWI_RATE_OFDM9, 133,  34 },
35260e474d75SJohannes Hofmann 		{ IWI_RATE_OFDM6, 160,  40 },
35270e474d75SJohannes Hofmann 		{ IWI_RATE_DS5,   200,  50 },
35280e474d75SJohannes Hofmann 		{            6,   240,  58 },	/* XXX 3Mb/s if it existed */
35290e474d75SJohannes Hofmann 		{ IWI_RATE_DS2,   267,  66 },
35300e474d75SJohannes Hofmann 		{ IWI_RATE_DS1,   400, 100 },
35310e474d75SJohannes Hofmann 		{            0,   500, 130 },	/* unknown rate/polling */
35320e474d75SJohannes Hofmann 	};
35330e474d75SJohannes Hofmann 	uint32_t txrate;
35340e474d75SJohannes Hofmann 	int j = 0;			/* XXX silence compiler */
35350e474d75SJohannes Hofmann 
35360e474d75SJohannes Hofmann 	sc->sc_ledevent = ticks;	/* time of last event */
35370e474d75SJohannes Hofmann 	if (sc->sc_blinking)		/* don't interrupt active blink */
35380e474d75SJohannes Hofmann 		return;
35390e474d75SJohannes Hofmann 	switch (event) {
35400e474d75SJohannes Hofmann 	case IWI_LED_POLL:
3541c4fe7bb1SImre Vadász 		j = nitems(blinkrates)-1;
35420e474d75SJohannes Hofmann 		break;
35430e474d75SJohannes Hofmann 	case IWI_LED_TX:
35440e474d75SJohannes Hofmann 		/* read current transmission rate from adapter */
35450e474d75SJohannes Hofmann 		txrate = CSR_READ_4(sc, IWI_CSR_CURRENT_TX_RATE);
35460e474d75SJohannes Hofmann 		if (blinkrates[sc->sc_txrix].rate != txrate) {
3547c4fe7bb1SImre Vadász 			for (j = 0; j < nitems(blinkrates)-1; j++)
35480e474d75SJohannes Hofmann 				if (blinkrates[j].rate == txrate)
35490e474d75SJohannes Hofmann 					break;
35500e474d75SJohannes Hofmann 			sc->sc_txrix = j;
35510e474d75SJohannes Hofmann 		} else
35520e474d75SJohannes Hofmann 			j = sc->sc_txrix;
35530e474d75SJohannes Hofmann 		break;
35540e474d75SJohannes Hofmann 	case IWI_LED_RX:
35550e474d75SJohannes Hofmann 		if (blinkrates[sc->sc_rxrix].rate != sc->sc_rxrate) {
3556c4fe7bb1SImre Vadász 			for (j = 0; j < nitems(blinkrates)-1; j++)
35570e474d75SJohannes Hofmann 				if (blinkrates[j].rate == sc->sc_rxrate)
35580e474d75SJohannes Hofmann 					break;
35590e474d75SJohannes Hofmann 			sc->sc_rxrix = j;
35600e474d75SJohannes Hofmann 		} else
35610e474d75SJohannes Hofmann 			j = sc->sc_rxrix;
35620e474d75SJohannes Hofmann 		break;
35630e474d75SJohannes Hofmann 	}
35640e474d75SJohannes Hofmann 	/* XXX beware of overflow */
35650e474d75SJohannes Hofmann 	iwi_led_blink(sc, (blinkrates[j].timeOn * hz) / 1000,
35660e474d75SJohannes Hofmann 		(blinkrates[j].timeOff * hz) / 1000);
35670e474d75SJohannes Hofmann }
35680e474d75SJohannes Hofmann 
35690e474d75SJohannes Hofmann static int
35700e474d75SJohannes Hofmann iwi_sysctl_softled(SYSCTL_HANDLER_ARGS)
35710e474d75SJohannes Hofmann {
35720e474d75SJohannes Hofmann 	struct iwi_softc *sc = arg1;
35730e474d75SJohannes Hofmann 	int softled = sc->sc_softled;
35740e474d75SJohannes Hofmann 	int error;
35750e474d75SJohannes Hofmann 
35760e474d75SJohannes Hofmann 	error = sysctl_handle_int(oidp, &softled, 0, req);
35770e474d75SJohannes Hofmann 	if (error || !req->newptr)
35780e474d75SJohannes Hofmann 		return error;
35790e474d75SJohannes Hofmann 	softled = (softled != 0);
35800e474d75SJohannes Hofmann 	if (softled != sc->sc_softled) {
35810e474d75SJohannes Hofmann 		if (softled) {
35820e474d75SJohannes Hofmann 			uint32_t v = iwi_read_event(sc);
35830e474d75SJohannes Hofmann 			v &= ~sc->sc_ledpin;
35840e474d75SJohannes Hofmann 			iwi_write_event(sc, iwi_toggle_event(v));
35850e474d75SJohannes Hofmann 		}
35860e474d75SJohannes Hofmann 		sc->sc_softled = softled;
35870e474d75SJohannes Hofmann 	}
35880e474d75SJohannes Hofmann 	return 0;
35890e474d75SJohannes Hofmann }
35900e474d75SJohannes Hofmann 
35910e474d75SJohannes Hofmann static void
35920e474d75SJohannes Hofmann iwi_ledattach(struct iwi_softc *sc)
35930e474d75SJohannes Hofmann {
359426595b18SSascha Wildner 	struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev);
359526595b18SSascha Wildner 	struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev);
35960e474d75SJohannes Hofmann 
35970e474d75SJohannes Hofmann 	sc->sc_blinking = 0;
35980e474d75SJohannes Hofmann 	sc->sc_ledstate = 1;
35990e474d75SJohannes Hofmann 	sc->sc_ledidle = (2700*hz)/1000;	/* 2.7sec */
3600c4fe7bb1SImre Vadász #if defined(__DragonFly__)
3601c4fe7bb1SImre Vadász 	callout_init_lk(&sc->sc_ledtimer, &sc->sc_lock);
3602c4fe7bb1SImre Vadász #else
3603c4fe7bb1SImre Vadász 	callout_init_mtx(&sc->sc_ledtimer, &sc->sc_mtx, 0);
3604c4fe7bb1SImre Vadász #endif
36050e474d75SJohannes Hofmann 
36060e474d75SJohannes Hofmann 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
36070e474d75SJohannes Hofmann 		"softled", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
36080e474d75SJohannes Hofmann 		iwi_sysctl_softled, "I", "enable/disable software LED support");
3609c4fe7bb1SImre Vadász 	SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
36100e474d75SJohannes Hofmann 		"ledpin", CTLFLAG_RW, &sc->sc_ledpin, 0,
36110e474d75SJohannes Hofmann 		"pin setting to turn activity LED on");
3612c4fe7bb1SImre Vadász 	SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
36130e474d75SJohannes Hofmann 		"ledidle", CTLFLAG_RW, &sc->sc_ledidle, 0,
36140e474d75SJohannes Hofmann 		"idle time for inactivity LED (ticks)");
36150e474d75SJohannes Hofmann 	/* XXX for debugging */
3616c4fe7bb1SImre Vadász 	SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
36170e474d75SJohannes Hofmann 		"nictype", CTLFLAG_RD, &sc->sc_nictype, 0,
36180e474d75SJohannes Hofmann 		"NIC type from EEPROM");
36190e474d75SJohannes Hofmann 
36200e474d75SJohannes Hofmann 	sc->sc_ledpin = IWI_RST_LED_ACTIVITY;
36210e474d75SJohannes Hofmann 	sc->sc_softled = 1;
36220e474d75SJohannes Hofmann 
36230e474d75SJohannes Hofmann 	sc->sc_nictype = (iwi_read_prom_word(sc, IWI_EEPROM_NIC) >> 8) & 0xff;
36240e474d75SJohannes Hofmann 	if (sc->sc_nictype == 1) {
36250e474d75SJohannes Hofmann 		/*
36260e474d75SJohannes Hofmann 		 * NB: led's are reversed.
36270e474d75SJohannes Hofmann 		 */
36280e474d75SJohannes Hofmann 		sc->sc_ledpin = IWI_RST_LED_ASSOCIATED;
36290e474d75SJohannes Hofmann 	}
36300e474d75SJohannes Hofmann }
36310e474d75SJohannes Hofmann 
36320e474d75SJohannes Hofmann static void
36330e474d75SJohannes Hofmann iwi_scan_start(struct ieee80211com *ic)
36340e474d75SJohannes Hofmann {
36350e474d75SJohannes Hofmann 	/* ignore */
36360e474d75SJohannes Hofmann }
36370e474d75SJohannes Hofmann 
36380e474d75SJohannes Hofmann static void
36390e474d75SJohannes Hofmann iwi_set_channel(struct ieee80211com *ic)
36400e474d75SJohannes Hofmann {
3641c4fe7bb1SImre Vadász 	struct iwi_softc *sc = ic->ic_softc;
3642c4fe7bb1SImre Vadász 
36430e474d75SJohannes Hofmann 	if (sc->fw_state == IWI_FW_IDLE)
36440e474d75SJohannes Hofmann 		iwi_setcurchan(sc, ic->ic_curchan->ic_ieee);
36450e474d75SJohannes Hofmann }
36460e474d75SJohannes Hofmann 
36470e474d75SJohannes Hofmann static void
36480e474d75SJohannes Hofmann iwi_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell)
36490e474d75SJohannes Hofmann {
36500e474d75SJohannes Hofmann 	struct ieee80211vap *vap = ss->ss_vap;
3651c4fe7bb1SImre Vadász 	struct iwi_softc *sc = vap->iv_ic->ic_softc;
3652c4fe7bb1SImre Vadász 	IWI_LOCK_DECL;
36530e474d75SJohannes Hofmann 
3654c4fe7bb1SImre Vadász 	IWI_LOCK(sc);
36550e474d75SJohannes Hofmann 	if (iwi_scanchan(sc, maxdwell, 0))
36560e474d75SJohannes Hofmann 		ieee80211_cancel_scan(vap);
3657c4fe7bb1SImre Vadász 	IWI_UNLOCK(sc);
36580e474d75SJohannes Hofmann }
36590e474d75SJohannes Hofmann 
36600e474d75SJohannes Hofmann static void
36610e474d75SJohannes Hofmann iwi_scan_mindwell(struct ieee80211_scan_state *ss)
36620e474d75SJohannes Hofmann {
36630e474d75SJohannes Hofmann 	/* NB: don't try to abort scan; wait for firmware to finish */
36640e474d75SJohannes Hofmann }
36650e474d75SJohannes Hofmann 
36660e474d75SJohannes Hofmann static void
36670e474d75SJohannes Hofmann iwi_scan_end(struct ieee80211com *ic)
36680e474d75SJohannes Hofmann {
3669c4fe7bb1SImre Vadász 	struct iwi_softc *sc = ic->ic_softc;
3670c4fe7bb1SImre Vadász 	IWI_LOCK_DECL;
36710e474d75SJohannes Hofmann 
3672c4fe7bb1SImre Vadász 	IWI_LOCK(sc);
36730e474d75SJohannes Hofmann 	sc->flags &= ~IWI_FLAG_CHANNEL_SCAN;
36740e474d75SJohannes Hofmann 	/* NB: make sure we're still scanning */
36750e474d75SJohannes Hofmann 	if (sc->fw_state == IWI_FW_SCANNING)
36760e474d75SJohannes Hofmann 		iwi_cmd(sc, IWI_CMD_ABORT_SCAN, NULL, 0);
3677c4fe7bb1SImre Vadász 	IWI_UNLOCK(sc);
3678b50e4759SMatthew Dillon }
3679