if_iwi.c revision 149346
1145247Sdamien/*	$FreeBSD: head/sys/dev/iwi/if_iwi.c 149346 2005-08-21 09:52:18Z damien $	*/
2145247Sdamien
3145247Sdamien/*-
4145247Sdamien * Copyright (c) 2004, 2005
5145247Sdamien *      Damien Bergamini <damien.bergamini@free.fr>. All rights reserved.
6145247Sdamien *
7145247Sdamien * Redistribution and use in source and binary forms, with or without
8145247Sdamien * modification, are permitted provided that the following conditions
9145247Sdamien * are met:
10145247Sdamien * 1. Redistributions of source code must retain the above copyright
11145247Sdamien *    notice unmodified, this list of conditions, and the following
12145247Sdamien *    disclaimer.
13145247Sdamien * 2. Redistributions in binary form must reproduce the above copyright
14145247Sdamien *    notice, this list of conditions and the following disclaimer in the
15145247Sdamien *    documentation and/or other materials provided with the distribution.
16145247Sdamien *
17145247Sdamien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18145247Sdamien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19145247Sdamien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20145247Sdamien * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21145247Sdamien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22145247Sdamien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23145247Sdamien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24145247Sdamien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25145247Sdamien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26145247Sdamien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27145247Sdamien * SUCH DAMAGE.
28145247Sdamien */
29145247Sdamien
30145247Sdamien#include <sys/cdefs.h>
31145247Sdamien__FBSDID("$FreeBSD: head/sys/dev/iwi/if_iwi.c 149346 2005-08-21 09:52:18Z damien $");
32145247Sdamien
33145247Sdamien/*-
34145247Sdamien * Intel(R) PRO/Wireless 2200BG/2225BG/2915ABG driver
35145247Sdamien * http://www.intel.com/network/connectivity/products/wireless/prowireless_mobile.htm
36145247Sdamien */
37145247Sdamien
38145247Sdamien#include <sys/param.h>
39145247Sdamien#include <sys/sysctl.h>
40145247Sdamien#include <sys/sockio.h>
41145247Sdamien#include <sys/mbuf.h>
42145247Sdamien#include <sys/kernel.h>
43145247Sdamien#include <sys/socket.h>
44145247Sdamien#include <sys/systm.h>
45145247Sdamien#include <sys/malloc.h>
46145247Sdamien#include <sys/module.h>
47145247Sdamien#include <sys/bus.h>
48145247Sdamien#include <sys/endian.h>
49145247Sdamien
50145247Sdamien#include <machine/bus.h>
51145247Sdamien#include <machine/resource.h>
52145247Sdamien#include <machine/clock.h>
53145247Sdamien#include <sys/rman.h>
54145247Sdamien
55145247Sdamien#include <dev/pci/pcireg.h>
56145247Sdamien#include <dev/pci/pcivar.h>
57145247Sdamien
58145247Sdamien#include <net/bpf.h>
59145247Sdamien#include <net/if.h>
60145247Sdamien#include <net/if_arp.h>
61145247Sdamien#include <net/ethernet.h>
62145247Sdamien#include <net/if_dl.h>
63145247Sdamien#include <net/if_media.h>
64145247Sdamien#include <net/if_types.h>
65145247Sdamien
66145247Sdamien#include <net80211/ieee80211_var.h>
67145247Sdamien#include <net80211/ieee80211_radiotap.h>
68145247Sdamien
69145247Sdamien#include <netinet/in.h>
70145247Sdamien#include <netinet/in_systm.h>
71145247Sdamien#include <netinet/in_var.h>
72145247Sdamien#include <netinet/ip.h>
73145247Sdamien#include <netinet/if_ether.h>
74145247Sdamien
75145247Sdamien#include <dev/iwi/if_iwireg.h>
76145247Sdamien#include <dev/iwi/if_iwivar.h>
77145247Sdamien
78145247Sdamien#ifdef IWI_DEBUG
79145247Sdamien#define DPRINTF(x)	do { if (iwi_debug > 0) printf x; } while (0)
80145247Sdamien#define DPRINTFN(n, x)	do { if (iwi_debug >= (n)) printf x; } while (0)
81145247Sdamienint iwi_debug = 0;
82145247SdamienSYSCTL_INT(_debug, OID_AUTO, iwi, CTLFLAG_RW, &iwi_debug, 0, "iwi debug level");
83145247Sdamien#else
84145247Sdamien#define DPRINTF(x)
85145247Sdamien#define DPRINTFN(n, x)
86145247Sdamien#endif
87145247Sdamien
88145247SdamienMODULE_DEPEND(iwi, pci,  1, 1, 1);
89145247SdamienMODULE_DEPEND(iwi, wlan, 1, 1, 1);
90145247Sdamien
91145247Sdamienstruct iwi_ident {
92145247Sdamien	uint16_t	vendor;
93145247Sdamien	uint16_t	device;
94145247Sdamien	const char	*name;
95145247Sdamien};
96145247Sdamien
97145247Sdamienstatic const struct iwi_ident iwi_ident_table[] = {
98145247Sdamien	{ 0x8086, 0x4220, "Intel(R) PRO/Wireless 2200BG" },
99145247Sdamien	{ 0x8086, 0x4221, "Intel(R) PRO/Wireless 2225BG" },
100145247Sdamien	{ 0x8086, 0x4223, "Intel(R) PRO/Wireless 2915ABG" },
101145247Sdamien	{ 0x8086, 0x4224, "Intel(R) PRO/Wireless 2915ABG" },
102145247Sdamien
103145247Sdamien	{ 0, 0, NULL }
104145247Sdamien};
105145247Sdamien
106145247Sdamienstatic void	iwi_dma_map_addr(void *, bus_dma_segment_t *, int, int);
107145247Sdamienstatic int	iwi_alloc_cmd_ring(struct iwi_softc *, struct iwi_cmd_ring *,
108145247Sdamien		    int);
109145247Sdamienstatic void	iwi_reset_cmd_ring(struct iwi_softc *, struct iwi_cmd_ring *);
110145247Sdamienstatic void	iwi_free_cmd_ring(struct iwi_softc *, struct iwi_cmd_ring *);
111145247Sdamienstatic int	iwi_alloc_tx_ring(struct iwi_softc *, struct iwi_tx_ring *,
112149338Sdamien		    int, bus_addr_t, bus_addr_t);
113145247Sdamienstatic void	iwi_reset_tx_ring(struct iwi_softc *, struct iwi_tx_ring *);
114145247Sdamienstatic void	iwi_free_tx_ring(struct iwi_softc *, struct iwi_tx_ring *);
115145247Sdamienstatic int	iwi_alloc_rx_ring(struct iwi_softc *, struct iwi_rx_ring *,
116145247Sdamien		    int);
117145247Sdamienstatic void	iwi_reset_rx_ring(struct iwi_softc *, struct iwi_rx_ring *);
118145247Sdamienstatic void	iwi_free_rx_ring(struct iwi_softc *, struct iwi_rx_ring *);
119145247Sdamienstatic int	iwi_media_change(struct ifnet *);
120145247Sdamienstatic void	iwi_media_status(struct ifnet *, struct ifmediareq *);
121145247Sdamienstatic int	iwi_newstate(struct ieee80211com *, enum ieee80211_state, int);
122149338Sdamienstatic int	iwi_wme_update(struct ieee80211com *);
123145247Sdamienstatic uint16_t	iwi_read_prom_word(struct iwi_softc *, uint8_t);
124145247Sdamienstatic void	iwi_fix_channel(struct ieee80211com *, struct mbuf *);
125145247Sdamienstatic void	iwi_frame_intr(struct iwi_softc *, struct iwi_rx_data *, int,
126145247Sdamien		    struct iwi_frame *);
127145247Sdamienstatic void	iwi_notification_intr(struct iwi_softc *, struct iwi_notif *);
128145247Sdamienstatic void	iwi_rx_intr(struct iwi_softc *);
129149338Sdamienstatic void	iwi_tx_intr(struct iwi_softc *, struct iwi_tx_ring *);
130145247Sdamienstatic void	iwi_intr(void *);
131145247Sdamienstatic int	iwi_cmd(struct iwi_softc *, uint8_t, void *, uint8_t, int);
132145247Sdamienstatic int	iwi_tx_start(struct ifnet *, struct mbuf *,
133145247Sdamien		    struct ieee80211_node *);
134145247Sdamienstatic void	iwi_start(struct ifnet *);
135145247Sdamienstatic void	iwi_watchdog(struct ifnet *);
136145247Sdamienstatic int	iwi_ioctl(struct ifnet *, u_long, caddr_t);
137145247Sdamienstatic void	iwi_stop_master(struct iwi_softc *);
138145247Sdamienstatic int	iwi_reset(struct iwi_softc *);
139145247Sdamienstatic int	iwi_load_ucode(struct iwi_softc *, void *, int);
140145247Sdamienstatic int	iwi_load_firmware(struct iwi_softc *, void *, int);
141145247Sdamienstatic int	iwi_cache_firmware(struct iwi_softc *, void *);
142145247Sdamienstatic void	iwi_free_firmware(struct iwi_softc *);
143145247Sdamienstatic int	iwi_config(struct iwi_softc *);
144146500Sdamienstatic int	iwi_set_chan(struct iwi_softc *, struct ieee80211_channel *);
145145247Sdamienstatic int	iwi_scan(struct iwi_softc *);
146145247Sdamienstatic int	iwi_auth_and_assoc(struct iwi_softc *);
147145247Sdamienstatic void	iwi_init(void *);
148145247Sdamienstatic void	iwi_stop(void *);
149145247Sdamienstatic int	iwi_sysctl_stats(SYSCTL_HANDLER_ARGS);
150145247Sdamienstatic int	iwi_sysctl_radio(SYSCTL_HANDLER_ARGS);
151145247Sdamien
152145247Sdamienstatic int iwi_probe(device_t);
153145247Sdamienstatic int iwi_attach(device_t);
154145247Sdamienstatic int iwi_detach(device_t);
155145247Sdamienstatic int iwi_shutdown(device_t);
156145247Sdamienstatic int iwi_suspend(device_t);
157145247Sdamienstatic int iwi_resume(device_t);
158145247Sdamien
159145247Sdamienstatic device_method_t iwi_methods[] = {
160145247Sdamien	/* Device interface */
161145247Sdamien	DEVMETHOD(device_probe,		iwi_probe),
162145247Sdamien	DEVMETHOD(device_attach,	iwi_attach),
163145247Sdamien	DEVMETHOD(device_detach,	iwi_detach),
164145247Sdamien	DEVMETHOD(device_shutdown,	iwi_shutdown),
165145247Sdamien	DEVMETHOD(device_suspend,	iwi_suspend),
166145247Sdamien	DEVMETHOD(device_resume,	iwi_resume),
167145247Sdamien
168145247Sdamien	{ 0, 0 }
169145247Sdamien};
170145247Sdamien
171145247Sdamienstatic driver_t iwi_driver = {
172145247Sdamien	"iwi",
173145247Sdamien	iwi_methods,
174145247Sdamien	sizeof (struct iwi_softc)
175145247Sdamien};
176145247Sdamien
177145247Sdamienstatic devclass_t iwi_devclass;
178145247Sdamien
179145247SdamienDRIVER_MODULE(iwi, pci, iwi_driver, iwi_devclass, 0, 0);
180145247Sdamien
181145247Sdamien/*
182145247Sdamien * Supported rates for 802.11a/b/g modes (in 500Kbps unit).
183145247Sdamien */
184145247Sdamienstatic const struct ieee80211_rateset iwi_rateset_11a =
185145247Sdamien	{ 8, { 12, 18, 24, 36, 48, 72, 96, 108 } };
186145247Sdamien
187145247Sdamienstatic const struct ieee80211_rateset iwi_rateset_11b =
188145247Sdamien	{ 4, { 2, 4, 11, 22 } };
189145247Sdamien
190145247Sdamienstatic const struct ieee80211_rateset iwi_rateset_11g =
191145247Sdamien	{ 12, { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 } };
192145247Sdamien
193145247Sdamienstatic __inline uint8_t
194145247SdamienMEM_READ_1(struct iwi_softc *sc, uint32_t addr)
195145247Sdamien{
196145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_INDIRECT_ADDR, addr);
197145247Sdamien	return CSR_READ_1(sc, IWI_CSR_INDIRECT_DATA);
198145247Sdamien}
199145247Sdamien
200145247Sdamienstatic __inline uint32_t
201145247SdamienMEM_READ_4(struct iwi_softc *sc, uint32_t addr)
202145247Sdamien{
203145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_INDIRECT_ADDR, addr);
204145247Sdamien	return CSR_READ_4(sc, IWI_CSR_INDIRECT_DATA);
205145247Sdamien}
206145247Sdamien
207145247Sdamienstatic int
208145247Sdamieniwi_probe(device_t dev)
209145247Sdamien{
210145247Sdamien	const struct iwi_ident *ident;
211145247Sdamien
212145247Sdamien	for (ident = iwi_ident_table; ident->name != NULL; ident++) {
213145247Sdamien		if (pci_get_vendor(dev) == ident->vendor &&
214145247Sdamien		    pci_get_device(dev) == ident->device) {
215145247Sdamien			device_set_desc(dev, ident->name);
216145247Sdamien			return 0;
217145247Sdamien		}
218145247Sdamien	}
219145247Sdamien	return ENXIO;
220145247Sdamien}
221145247Sdamien
222145247Sdamien/* Base Address Register */
223145247Sdamien#define IWI_PCI_BAR0	0x10
224145247Sdamien
225145247Sdamienstatic int
226145247Sdamieniwi_attach(device_t dev)
227145247Sdamien{
228145247Sdamien	struct iwi_softc *sc = device_get_softc(dev);
229147256Sbrooks	struct ifnet *ifp;
230145247Sdamien	struct ieee80211com *ic = &sc->sc_ic;
231145247Sdamien	uint16_t val;
232145247Sdamien	int error, i;
233145247Sdamien
234145247Sdamien	sc->sc_dev = dev;
235145247Sdamien
236145247Sdamien	mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
237145247Sdamien	    MTX_DEF | MTX_RECURSE);
238145247Sdamien
239145247Sdamien	if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) {
240145247Sdamien		device_printf(dev, "chip is in D%d power mode "
241145247Sdamien		    "-- setting to D0\n", pci_get_powerstate(dev));
242145247Sdamien		pci_set_powerstate(dev, PCI_POWERSTATE_D0);
243145247Sdamien	}
244145247Sdamien
245146500Sdamien	pci_write_config(dev, 0x41, 0, 1);
246146500Sdamien
247145247Sdamien	/* enable bus-mastering */
248145247Sdamien	pci_enable_busmaster(dev);
249145247Sdamien
250145247Sdamien	sc->mem_rid = IWI_PCI_BAR0;
251145247Sdamien	sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid,
252145247Sdamien	    RF_ACTIVE);
253145247Sdamien	if (sc->mem == NULL) {
254145247Sdamien		device_printf(dev, "could not allocate memory resource\n");
255145247Sdamien		goto fail;
256145247Sdamien	}
257145247Sdamien
258145247Sdamien	sc->sc_st = rman_get_bustag(sc->mem);
259145247Sdamien	sc->sc_sh = rman_get_bushandle(sc->mem);
260145247Sdamien
261145247Sdamien	sc->irq_rid = 0;
262145247Sdamien	sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid,
263145247Sdamien	    RF_ACTIVE | RF_SHAREABLE);
264145247Sdamien	if (sc->irq == NULL) {
265145247Sdamien		device_printf(dev, "could not allocate interrupt resource\n");
266145247Sdamien		goto fail;
267145247Sdamien	}
268145247Sdamien
269145247Sdamien	if (iwi_reset(sc) != 0) {
270145247Sdamien		device_printf(dev, "could not reset adapter\n");
271145247Sdamien		goto fail;
272145247Sdamien	}
273145247Sdamien
274145247Sdamien	/*
275145247Sdamien	 * Allocate rings.
276145247Sdamien	 */
277145247Sdamien	if (iwi_alloc_cmd_ring(sc, &sc->cmdq, IWI_CMD_RING_COUNT) != 0) {
278145247Sdamien		device_printf(dev, "could not allocate Cmd ring\n");
279145247Sdamien		goto fail;
280145247Sdamien	}
281145247Sdamien
282149338Sdamien	error = iwi_alloc_tx_ring(sc, &sc->txq[0], IWI_TX_RING_COUNT,
283149338Sdamien	    IWI_CSR_TX1_RIDX, IWI_CSR_TX1_WIDX);
284149338Sdamien	if (error != 0) {
285149338Sdamien		device_printf(dev, "could not allocate Tx ring 1\n");
286145247Sdamien		goto fail;
287145247Sdamien	}
288145247Sdamien
289149338Sdamien	error = iwi_alloc_tx_ring(sc, &sc->txq[1], IWI_TX_RING_COUNT,
290149338Sdamien	    IWI_CSR_TX2_RIDX, IWI_CSR_TX2_WIDX);
291149338Sdamien	if (error != 0) {
292149338Sdamien		device_printf(dev, "could not allocate Tx ring 2\n");
293149338Sdamien		goto fail;
294149338Sdamien	}
295149338Sdamien
296149338Sdamien	error = iwi_alloc_tx_ring(sc, &sc->txq[2], IWI_TX_RING_COUNT,
297149338Sdamien	    IWI_CSR_TX3_RIDX, IWI_CSR_TX3_WIDX);
298149338Sdamien	if (error != 0) {
299149338Sdamien		device_printf(dev, "could not allocate Tx ring 3\n");
300149338Sdamien		goto fail;
301149338Sdamien	}
302149338Sdamien
303149338Sdamien	error = iwi_alloc_tx_ring(sc, &sc->txq[3], IWI_TX_RING_COUNT,
304149338Sdamien	    IWI_CSR_TX4_RIDX, IWI_CSR_TX4_WIDX);
305149338Sdamien	if (error != 0) {
306149338Sdamien		device_printf(dev, "could not allocate Tx ring 4\n");
307149338Sdamien		goto fail;
308149338Sdamien	}
309149338Sdamien
310145247Sdamien	if (iwi_alloc_rx_ring(sc, &sc->rxq, IWI_RX_RING_COUNT) != 0) {
311145247Sdamien		device_printf(dev, "could not allocate Rx ring\n");
312145247Sdamien		goto fail;
313145247Sdamien	}
314145247Sdamien
315147256Sbrooks	ifp = sc->sc_ifp = if_alloc(IFT_ETHER);
316147256Sbrooks	if (ifp == NULL) {
317147256Sbrooks		device_printf(dev, "can not if_alloc()\n");
318147256Sbrooks		goto fail;
319147256Sbrooks		return (ENOSPC);
320147256Sbrooks	}
321145247Sdamien	ifp->if_softc = sc;
322145247Sdamien	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
323145247Sdamien	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
324145247Sdamien	ifp->if_init = iwi_init;
325145247Sdamien	ifp->if_ioctl = iwi_ioctl;
326145247Sdamien	ifp->if_start = iwi_start;
327145247Sdamien	ifp->if_watchdog = iwi_watchdog;
328145247Sdamien	IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
329145247Sdamien	ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN;
330145247Sdamien	IFQ_SET_READY(&ifp->if_snd);
331145247Sdamien
332145247Sdamien	ic->ic_ifp = ifp;
333149338Sdamien	ic->ic_wme.wme_update = iwi_wme_update;
334145247Sdamien	ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
335145247Sdamien	ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */
336145247Sdamien	ic->ic_state = IEEE80211_S_INIT;
337145247Sdamien
338145247Sdamien	/* set device capabilities */
339149338Sdamien	ic->ic_caps =
340149338Sdamien	    IEEE80211_C_MONITOR |	/* monitor mode supported */
341149338Sdamien	    IEEE80211_C_TXPMGT |	/* tx power management */
342149338Sdamien	    IEEE80211_C_SHPREAMBLE |	/* short preamble supported */
343149338Sdamien	    IEEE80211_C_WPA |		/* 802.11i */
344149338Sdamien	    IEEE80211_C_WME;		/* 802.11e */
345145247Sdamien
346145247Sdamien	/* read MAC address from EEPROM */
347145247Sdamien	val = iwi_read_prom_word(sc, IWI_EEPROM_MAC + 0);
348145247Sdamien	ic->ic_myaddr[0] = val >> 8;
349145247Sdamien	ic->ic_myaddr[1] = val & 0xff;
350145247Sdamien	val = iwi_read_prom_word(sc, IWI_EEPROM_MAC + 1);
351145247Sdamien	ic->ic_myaddr[2] = val >> 8;
352145247Sdamien	ic->ic_myaddr[3] = val & 0xff;
353145247Sdamien	val = iwi_read_prom_word(sc, IWI_EEPROM_MAC + 2);
354145247Sdamien	ic->ic_myaddr[4] = val >> 8;
355145247Sdamien	ic->ic_myaddr[5] = val & 0xff;
356145247Sdamien
357146500Sdamien#if 0
358145247Sdamien	if (pci_get_device(dev) >= 0x4223) {
359145247Sdamien		/* set supported .11a rates (2915ABG only) */
360145247Sdamien		ic->ic_sup_rates[IEEE80211_MODE_11A] = iwi_rateset_11a;
361145247Sdamien
362145247Sdamien		/* set supported .11a channels */
363145247Sdamien		for (i = 36; i <= 64; i += 4) {
364145247Sdamien			ic->ic_channels[i].ic_freq =
365145247Sdamien			    ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ);
366145247Sdamien			ic->ic_channels[i].ic_flags = IEEE80211_CHAN_A;
367145247Sdamien		}
368145247Sdamien		for (i = 149; i <= 165; i += 4) {
369145247Sdamien			ic->ic_channels[i].ic_freq =
370145247Sdamien			    ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ);
371145247Sdamien			ic->ic_channels[i].ic_flags = IEEE80211_CHAN_A;
372145247Sdamien		}
373145247Sdamien	}
374146500Sdamien#endif
375145247Sdamien
376145247Sdamien	/* set supported .11b and .11g rates */
377145247Sdamien	ic->ic_sup_rates[IEEE80211_MODE_11B] = iwi_rateset_11b;
378145247Sdamien	ic->ic_sup_rates[IEEE80211_MODE_11G] = iwi_rateset_11g;
379145247Sdamien
380145247Sdamien	/* set supported .11b and .11g channels (1 through 14) */
381145247Sdamien	for (i = 1; i <= 14; i++) {
382145247Sdamien		ic->ic_channels[i].ic_freq =
383145247Sdamien		    ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ);
384145247Sdamien		ic->ic_channels[i].ic_flags =
385145247Sdamien		    IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM |
386145247Sdamien		    IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ;
387145247Sdamien	}
388145247Sdamien
389145247Sdamien	ieee80211_ifattach(ic);
390145247Sdamien	/* override state transition machine */
391145247Sdamien	sc->sc_newstate = ic->ic_newstate;
392145247Sdamien	ic->ic_newstate = iwi_newstate;
393145247Sdamien	ieee80211_media_init(ic, iwi_media_change, iwi_media_status);
394145247Sdamien
395145247Sdamien	bpfattach2(ifp, DLT_IEEE802_11_RADIO,
396145247Sdamien	    sizeof (struct ieee80211_frame) + 64, &sc->sc_drvbpf);
397145247Sdamien
398145247Sdamien	sc->sc_rxtap_len = sizeof sc->sc_rxtapu;
399145247Sdamien	sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len);
400145247Sdamien	sc->sc_rxtap.wr_ihdr.it_present = htole32(IWI_RX_RADIOTAP_PRESENT);
401145247Sdamien
402145247Sdamien	sc->sc_txtap_len = sizeof sc->sc_txtapu;
403145247Sdamien	sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len);
404145247Sdamien	sc->sc_txtap.wt_ihdr.it_present = htole32(IWI_TX_RADIOTAP_PRESENT);
405145247Sdamien
406145247Sdamien	/*
407145247Sdamien	 * Add a few sysctl knobs.
408145247Sdamien	 */
409145247Sdamien	sc->dwelltime = 100;
410145247Sdamien	sc->bluetooth = 1;
411146500Sdamien	sc->antenna = 0;
412145247Sdamien
413145247Sdamien	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
414145247Sdamien	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "radio",
415145247Sdamien	    CTLTYPE_INT | CTLFLAG_RD, sc, 0, iwi_sysctl_radio, "I",
416145247Sdamien	    "radio transmitter switch state (0=off, 1=on)");
417145247Sdamien
418145247Sdamien	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
419145247Sdamien	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "stats",
420145247Sdamien	    CTLTYPE_OPAQUE | CTLFLAG_RD, sc, 0, iwi_sysctl_stats, "S",
421145247Sdamien	    "statistics");
422145247Sdamien
423145247Sdamien	SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
424145247Sdamien	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "dwell",
425145247Sdamien	    CTLFLAG_RW, &sc->dwelltime, 0,
426145247Sdamien	    "channel dwell time (ms) for AP/station scanning");
427145247Sdamien
428145247Sdamien	SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
429145247Sdamien	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "bluetooth",
430145247Sdamien	    CTLFLAG_RW, &sc->bluetooth, 0, "bluetooth coexistence");
431145247Sdamien
432146500Sdamien	SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
433146500Sdamien	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "antenna",
434146500Sdamien	    CTLFLAG_RW, &sc->antenna, 0, "antenna (0=auto)");
435146500Sdamien
436145247Sdamien	/*
437145247Sdamien	 * Hook our interrupt after all initialization is complete.
438145247Sdamien	 */
439145247Sdamien	error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET | INTR_MPSAFE,
440145247Sdamien	    iwi_intr, sc, &sc->sc_ih);
441145247Sdamien	if (error != 0) {
442145247Sdamien		device_printf(dev, "could not set up interrupt\n");
443145247Sdamien		goto fail;
444145247Sdamien	}
445145247Sdamien
446145247Sdamien	if (bootverbose)
447145247Sdamien		ieee80211_announce(ic);
448145247Sdamien
449145247Sdamien	return 0;
450145247Sdamien
451145247Sdamienfail:	iwi_detach(dev);
452145247Sdamien	return ENXIO;
453145247Sdamien}
454145247Sdamien
455145247Sdamienstatic int
456145247Sdamieniwi_detach(device_t dev)
457145247Sdamien{
458145247Sdamien	struct iwi_softc *sc = device_get_softc(dev);
459145247Sdamien	struct ieee80211com *ic = &sc->sc_ic;
460145247Sdamien	struct ifnet *ifp = ic->ic_ifp;
461145247Sdamien
462145247Sdamien	iwi_stop(sc);
463145247Sdamien
464145247Sdamien	iwi_free_firmware(sc);
465145247Sdamien
466147256Sbrooks	if (ifp != NULL)
467147256Sbrooks		bpfdetach(ifp);
468145247Sdamien	ieee80211_ifdetach(ic);
469147256Sbrooks	if (ifp != NULL)
470147256Sbrooks		if_free(ifp);
471145247Sdamien
472145247Sdamien	iwi_free_cmd_ring(sc, &sc->cmdq);
473149338Sdamien	iwi_free_tx_ring(sc, &sc->txq[0]);
474149338Sdamien	iwi_free_tx_ring(sc, &sc->txq[1]);
475149338Sdamien	iwi_free_tx_ring(sc, &sc->txq[2]);
476149338Sdamien	iwi_free_tx_ring(sc, &sc->txq[3]);
477145247Sdamien	iwi_free_rx_ring(sc, &sc->rxq);
478145247Sdamien
479145247Sdamien	if (sc->irq != NULL) {
480145247Sdamien		bus_teardown_intr(dev, sc->irq, sc->sc_ih);
481145247Sdamien		bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq);
482145247Sdamien	}
483145247Sdamien
484145247Sdamien	if (sc->mem != NULL)
485145247Sdamien		bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem);
486145247Sdamien
487145247Sdamien	mtx_destroy(&sc->sc_mtx);
488145247Sdamien
489145247Sdamien	return 0;
490145247Sdamien}
491145247Sdamien
492145247Sdamienstatic void
493145247Sdamieniwi_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nseg, int error)
494145247Sdamien{
495145247Sdamien	if (error != 0)
496145247Sdamien		return;
497145247Sdamien
498145247Sdamien	KASSERT(nseg == 1, ("too many DMA segments, %d should be 1", nseg));
499145247Sdamien
500145247Sdamien	*(bus_addr_t *)arg = segs[0].ds_addr;
501145247Sdamien}
502145247Sdamien
503145247Sdamienstatic int
504145247Sdamieniwi_alloc_cmd_ring(struct iwi_softc *sc, struct iwi_cmd_ring *ring, int count)
505145247Sdamien{
506145247Sdamien	int error;
507145247Sdamien
508145247Sdamien	ring->count = count;
509145247Sdamien	ring->queued = 0;
510145247Sdamien	ring->cur = ring->next = 0;
511145247Sdamien
512145247Sdamien	error = bus_dma_tag_create(NULL, 4, 0, BUS_SPACE_MAXADDR_32BIT,
513145247Sdamien	    BUS_SPACE_MAXADDR, NULL, NULL, count * IWI_CMD_DESC_SIZE, 1,
514145247Sdamien	    count * IWI_CMD_DESC_SIZE, 0, NULL, NULL, &ring->desc_dmat);
515145247Sdamien	if (error != 0) {
516145247Sdamien		device_printf(sc->sc_dev, "could not create desc DMA tag\n");
517145247Sdamien		goto fail;
518145247Sdamien	}
519145247Sdamien
520145247Sdamien	error = bus_dmamem_alloc(ring->desc_dmat, (void **)&ring->desc,
521145247Sdamien	    BUS_DMA_NOWAIT | BUS_DMA_ZERO, &ring->desc_map);
522145247Sdamien	if (error != 0) {
523145247Sdamien		device_printf(sc->sc_dev, "could not allocate DMA memory\n");
524145247Sdamien		goto fail;
525145247Sdamien	}
526145247Sdamien
527145247Sdamien	error = bus_dmamap_load(ring->desc_dmat, ring->desc_map, ring->desc,
528145247Sdamien	    count * IWI_CMD_DESC_SIZE, iwi_dma_map_addr, &ring->physaddr, 0);
529145247Sdamien	if (error != 0) {
530145247Sdamien		device_printf(sc->sc_dev, "could not load desc DMA map\n");
531145247Sdamien		goto fail;
532145247Sdamien	}
533145247Sdamien
534145247Sdamien	return 0;
535145247Sdamien
536145247Sdamienfail:	iwi_free_cmd_ring(sc, ring);
537145247Sdamien	return error;
538145247Sdamien}
539145247Sdamien
540145247Sdamienstatic void
541145247Sdamieniwi_reset_cmd_ring(struct iwi_softc *sc, struct iwi_cmd_ring *ring)
542145247Sdamien{
543145247Sdamien	ring->queued = 0;
544145247Sdamien	ring->cur = ring->next = 0;
545145247Sdamien}
546145247Sdamien
547145247Sdamienstatic void
548145247Sdamieniwi_free_cmd_ring(struct iwi_softc *sc, struct iwi_cmd_ring *ring)
549145247Sdamien{
550145247Sdamien	if (ring->desc != NULL) {
551145247Sdamien		bus_dmamap_sync(ring->desc_dmat, ring->desc_map,
552145247Sdamien		    BUS_DMASYNC_POSTWRITE);
553145247Sdamien		bus_dmamap_unload(ring->desc_dmat, ring->desc_map);
554145247Sdamien		bus_dmamem_free(ring->desc_dmat, ring->desc, ring->desc_map);
555145247Sdamien	}
556145247Sdamien
557145247Sdamien	if (ring->desc_dmat != NULL)
558145247Sdamien		bus_dma_tag_destroy(ring->desc_dmat);
559145247Sdamien}
560145247Sdamien
561145247Sdamienstatic int
562149338Sdamieniwi_alloc_tx_ring(struct iwi_softc *sc, struct iwi_tx_ring *ring, int count,
563149338Sdamien    bus_addr_t csr_ridx, bus_addr_t csr_widx)
564145247Sdamien{
565145247Sdamien	int i, error;
566145247Sdamien
567145247Sdamien	ring->count = count;
568145247Sdamien	ring->queued = 0;
569145247Sdamien	ring->cur = ring->next = 0;
570149338Sdamien	ring->csr_ridx = csr_ridx;
571149338Sdamien	ring->csr_widx = csr_widx;
572145247Sdamien
573145247Sdamien	error = bus_dma_tag_create(NULL, 4, 0, BUS_SPACE_MAXADDR_32BIT,
574145247Sdamien	    BUS_SPACE_MAXADDR, NULL, NULL, count * IWI_TX_DESC_SIZE, 1,
575145247Sdamien	    count * IWI_TX_DESC_SIZE, 0, NULL, NULL, &ring->desc_dmat);
576145247Sdamien	if (error != 0) {
577145247Sdamien		device_printf(sc->sc_dev, "could not create desc DMA tag\n");
578145247Sdamien		goto fail;
579145247Sdamien	}
580145247Sdamien
581145247Sdamien	error = bus_dmamem_alloc(ring->desc_dmat, (void **)&ring->desc,
582145247Sdamien	    BUS_DMA_NOWAIT | BUS_DMA_ZERO, &ring->desc_map);
583145247Sdamien	if (error != 0) {
584145247Sdamien		device_printf(sc->sc_dev, "could not allocate DMA memory\n");
585145247Sdamien		goto fail;
586145247Sdamien	}
587145247Sdamien
588145247Sdamien	error = bus_dmamap_load(ring->desc_dmat, ring->desc_map, ring->desc,
589145247Sdamien	    count * IWI_TX_DESC_SIZE, iwi_dma_map_addr, &ring->physaddr, 0);
590145247Sdamien	if (error != 0) {
591145247Sdamien		device_printf(sc->sc_dev, "could not load desc DMA map\n");
592145247Sdamien		goto fail;
593145247Sdamien	}
594145247Sdamien
595145247Sdamien	ring->data = malloc(count * sizeof (struct iwi_tx_data), M_DEVBUF,
596145247Sdamien	    M_NOWAIT | M_ZERO);
597145247Sdamien	if (ring->data == NULL) {
598145247Sdamien		device_printf(sc->sc_dev, "could not allocate soft data\n");
599145247Sdamien		error = ENOMEM;
600145247Sdamien		goto fail;
601145247Sdamien	}
602145247Sdamien
603145247Sdamien	error = bus_dma_tag_create(NULL, 1, 0, BUS_SPACE_MAXADDR_32BIT,
604145247Sdamien	    BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, 1, MCLBYTES, 0, NULL,
605145247Sdamien	    NULL, &ring->data_dmat);
606145247Sdamien	if (error != 0) {
607145247Sdamien		device_printf(sc->sc_dev, "could not create data DMA tag\n");
608145247Sdamien		goto fail;
609145247Sdamien	}
610145247Sdamien
611145247Sdamien	for (i = 0; i < count; i++) {
612145247Sdamien		error = bus_dmamap_create(ring->data_dmat, 0,
613145247Sdamien		    &ring->data[i].map);
614145247Sdamien		if (error != 0) {
615145247Sdamien			device_printf(sc->sc_dev, "could not create DMA map\n");
616145247Sdamien			goto fail;
617145247Sdamien		}
618145247Sdamien	}
619145247Sdamien
620145247Sdamien	return 0;
621145247Sdamien
622145247Sdamienfail:	iwi_free_tx_ring(sc, ring);
623145247Sdamien	return error;
624145247Sdamien}
625145247Sdamien
626145247Sdamienstatic void
627145247Sdamieniwi_reset_tx_ring(struct iwi_softc *sc, struct iwi_tx_ring *ring)
628145247Sdamien{
629145247Sdamien	struct iwi_tx_data *data;
630145247Sdamien	int i;
631145247Sdamien
632145247Sdamien	for (i = 0; i < ring->count; i++) {
633145247Sdamien		data = &ring->data[i];
634145247Sdamien
635145247Sdamien		if (data->m != NULL) {
636145247Sdamien			bus_dmamap_sync(ring->data_dmat, data->map,
637145247Sdamien			    BUS_DMASYNC_POSTWRITE);
638145247Sdamien			bus_dmamap_unload(ring->data_dmat, data->map);
639145247Sdamien			m_freem(data->m);
640145247Sdamien			data->m = NULL;
641145247Sdamien		}
642145247Sdamien
643145247Sdamien		if (data->ni != NULL) {
644145247Sdamien			ieee80211_free_node(data->ni);
645145247Sdamien			data->ni = NULL;
646145247Sdamien		}
647145247Sdamien	}
648145247Sdamien
649145247Sdamien	ring->queued = 0;
650145247Sdamien	ring->cur = ring->next = 0;
651145247Sdamien}
652145247Sdamien
653145247Sdamienstatic void
654145247Sdamieniwi_free_tx_ring(struct iwi_softc *sc, struct iwi_tx_ring *ring)
655145247Sdamien{
656145247Sdamien	struct iwi_tx_data *data;
657145247Sdamien	int i;
658145247Sdamien
659145247Sdamien	if (ring->desc != NULL) {
660145247Sdamien		bus_dmamap_sync(ring->desc_dmat, ring->desc_map,
661145247Sdamien		    BUS_DMASYNC_POSTWRITE);
662145247Sdamien		bus_dmamap_unload(ring->desc_dmat, ring->desc_map);
663145247Sdamien		bus_dmamem_free(ring->desc_dmat, ring->desc, ring->desc_map);
664145247Sdamien	}
665145247Sdamien
666145247Sdamien	if (ring->desc_dmat != NULL)
667145247Sdamien		bus_dma_tag_destroy(ring->desc_dmat);
668145247Sdamien
669145247Sdamien	if (ring->data != NULL) {
670145247Sdamien		for (i = 0; i < ring->count; i++) {
671145247Sdamien			data = &ring->data[i];
672145247Sdamien
673145247Sdamien			if (data->m != NULL) {
674145247Sdamien				bus_dmamap_sync(ring->data_dmat, data->map,
675145247Sdamien				    BUS_DMASYNC_POSTWRITE);
676145247Sdamien				bus_dmamap_unload(ring->data_dmat, data->map);
677145247Sdamien				m_freem(data->m);
678145247Sdamien			}
679145247Sdamien
680145247Sdamien			if (data->ni != NULL)
681145247Sdamien				ieee80211_free_node(data->ni);
682145247Sdamien
683145247Sdamien			if (data->map != NULL)
684145247Sdamien				bus_dmamap_destroy(ring->data_dmat, data->map);
685145247Sdamien		}
686145247Sdamien
687145247Sdamien		free(ring->data, M_DEVBUF);
688145247Sdamien	}
689145247Sdamien
690145247Sdamien	if (ring->data_dmat != NULL)
691145247Sdamien		bus_dma_tag_destroy(ring->data_dmat);
692145247Sdamien}
693145247Sdamien
694145247Sdamienstatic int
695145247Sdamieniwi_alloc_rx_ring(struct iwi_softc *sc, struct iwi_rx_ring *ring, int count)
696145247Sdamien{
697145247Sdamien	struct iwi_rx_data *data;
698145247Sdamien	int i, error;
699145247Sdamien
700145247Sdamien	ring->count = count;
701145247Sdamien	ring->cur = 0;
702145247Sdamien
703145247Sdamien	ring->data = malloc(count * sizeof (struct iwi_rx_data), M_DEVBUF,
704145247Sdamien	    M_NOWAIT | M_ZERO);
705145247Sdamien	if (ring->data == NULL) {
706145247Sdamien		device_printf(sc->sc_dev, "could not allocate soft data\n");
707145247Sdamien		error = ENOMEM;
708145247Sdamien		goto fail;
709145247Sdamien	}
710145247Sdamien
711145247Sdamien	error = bus_dma_tag_create(NULL, 1, 0, BUS_SPACE_MAXADDR_32BIT,
712145247Sdamien	    BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, 1, MCLBYTES, 0, NULL,
713145247Sdamien	    NULL, &ring->data_dmat);
714145247Sdamien	if (error != 0) {
715145247Sdamien		device_printf(sc->sc_dev, "could not create data DMA tag\n");
716145247Sdamien		goto fail;
717145247Sdamien	}
718145247Sdamien
719145247Sdamien	for (i = 0; i < count; i++) {
720145247Sdamien		data = &ring->data[i];
721145247Sdamien
722145247Sdamien		error = bus_dmamap_create(ring->data_dmat, 0, &data->map);
723145247Sdamien		if (error != 0) {
724145247Sdamien			device_printf(sc->sc_dev, "could not create DMA map\n");
725145247Sdamien			goto fail;
726145247Sdamien		}
727145247Sdamien
728145247Sdamien		data->m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
729145247Sdamien		if (data->m == NULL) {
730145247Sdamien			device_printf(sc->sc_dev,
731145247Sdamien			    "could not allocate rx mbuf\n");
732145247Sdamien			error = ENOMEM;
733145247Sdamien			goto fail;
734145247Sdamien		}
735145247Sdamien
736145247Sdamien		error = bus_dmamap_load(ring->data_dmat, data->map,
737145247Sdamien		    mtod(data->m, void *), MCLBYTES, iwi_dma_map_addr,
738145247Sdamien		    &data->physaddr, 0);
739145247Sdamien		if (error != 0) {
740145247Sdamien			device_printf(sc->sc_dev,
741145247Sdamien			    "could not load rx buf DMA map");
742145247Sdamien			goto fail;
743145247Sdamien		}
744145247Sdamien
745145247Sdamien		data->reg = IWI_CSR_RX_BASE + i * 4;
746145247Sdamien	}
747145247Sdamien
748145247Sdamien	return 0;
749145247Sdamien
750145247Sdamienfail:	iwi_free_rx_ring(sc, ring);
751145247Sdamien	return error;
752145247Sdamien}
753145247Sdamien
754145247Sdamienstatic void
755145247Sdamieniwi_reset_rx_ring(struct iwi_softc *sc, struct iwi_rx_ring *ring)
756145247Sdamien{
757145247Sdamien	ring->cur = 0;
758145247Sdamien}
759145247Sdamien
760145247Sdamienstatic void
761145247Sdamieniwi_free_rx_ring(struct iwi_softc *sc, struct iwi_rx_ring *ring)
762145247Sdamien{
763145247Sdamien	struct iwi_rx_data *data;
764145247Sdamien	int i;
765145247Sdamien
766145247Sdamien	if (ring->data != NULL) {
767145247Sdamien		for (i = 0; i < ring->count; i++) {
768145247Sdamien			data = &ring->data[i];
769145247Sdamien
770145247Sdamien			if (data->m != NULL) {
771145247Sdamien				bus_dmamap_sync(ring->data_dmat, data->map,
772145247Sdamien				    BUS_DMASYNC_POSTREAD);
773145247Sdamien				bus_dmamap_unload(ring->data_dmat, data->map);
774145247Sdamien				m_freem(data->m);
775145247Sdamien			}
776145247Sdamien
777145247Sdamien			if (data->map != NULL)
778145247Sdamien				bus_dmamap_destroy(ring->data_dmat, data->map);
779145247Sdamien		}
780145247Sdamien
781145247Sdamien		free(ring->data, M_DEVBUF);
782145247Sdamien	}
783145247Sdamien
784145247Sdamien	if (ring->data_dmat != NULL)
785145247Sdamien		bus_dma_tag_destroy(ring->data_dmat);
786145247Sdamien}
787145247Sdamien
788145247Sdamienstatic int
789145247Sdamieniwi_shutdown(device_t dev)
790145247Sdamien{
791145247Sdamien	struct iwi_softc *sc = device_get_softc(dev);
792145247Sdamien
793145247Sdamien	iwi_stop(sc);
794145247Sdamien
795145247Sdamien	return 0;
796145247Sdamien}
797145247Sdamien
798145247Sdamienstatic int
799145247Sdamieniwi_suspend(device_t dev)
800145247Sdamien{
801145247Sdamien	struct iwi_softc *sc = device_get_softc(dev);
802145247Sdamien
803145247Sdamien	iwi_stop(sc);
804145247Sdamien
805145247Sdamien	return 0;
806145247Sdamien}
807145247Sdamien
808145247Sdamienstatic int
809145247Sdamieniwi_resume(device_t dev)
810145247Sdamien{
811145247Sdamien	struct iwi_softc *sc = device_get_softc(dev);
812145247Sdamien	struct ifnet *ifp = sc->sc_ic.ic_ifp;
813145247Sdamien
814145247Sdamien	IWI_LOCK(sc);
815145247Sdamien
816146500Sdamien	pci_write_config(dev, 0x41, 0, 1);
817146500Sdamien
818145247Sdamien	if (ifp->if_flags & IFF_UP) {
819145247Sdamien		ifp->if_init(ifp->if_softc);
820148887Srwatson		if (ifp->if_drv_flags & IFF_DRV_RUNNING)
821145247Sdamien			ifp->if_start(ifp);
822145247Sdamien	}
823145247Sdamien
824145247Sdamien	IWI_UNLOCK(sc);
825145247Sdamien
826145247Sdamien	return 0;
827145247Sdamien}
828145247Sdamien
829145247Sdamienstatic int
830145247Sdamieniwi_media_change(struct ifnet *ifp)
831145247Sdamien{
832145247Sdamien	struct iwi_softc *sc = ifp->if_softc;
833145247Sdamien	int error;
834145247Sdamien
835145247Sdamien	IWI_LOCK(sc);
836145247Sdamien
837145247Sdamien	error = ieee80211_media_change(ifp);
838145247Sdamien	if (error != ENETRESET) {
839145247Sdamien		IWI_UNLOCK(sc);
840145247Sdamien		return error;
841145247Sdamien	}
842145247Sdamien
843148887Srwatson	if ((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING))
844145247Sdamien		iwi_init(sc);
845145247Sdamien
846145247Sdamien	IWI_UNLOCK(sc);
847145247Sdamien
848145247Sdamien	return 0;
849145247Sdamien}
850145247Sdamien
851145247Sdamien/*
852145247Sdamien * The firmware automaticly adapt the transmit speed. We report the current
853145247Sdamien * transmit speed here.
854145247Sdamien */
855145247Sdamienstatic void
856145247Sdamieniwi_media_status(struct ifnet *ifp, struct ifmediareq *imr)
857145247Sdamien{
858145247Sdamien	struct iwi_softc *sc = ifp->if_softc;
859145247Sdamien	struct ieee80211com *ic = &sc->sc_ic;
860145247Sdamien#define N(a)	(sizeof (a) / sizeof (a[0]))
861145247Sdamien	static const struct {
862145247Sdamien		uint32_t	val;
863145247Sdamien		int		rate;
864145247Sdamien	} rates[] = {
865145247Sdamien		{ IWI_RATE_DS1,      2 },
866145247Sdamien		{ IWI_RATE_DS2,      4 },
867145247Sdamien		{ IWI_RATE_DS5,     11 },
868145247Sdamien		{ IWI_RATE_DS11,    22 },
869145247Sdamien		{ IWI_RATE_OFDM6,   12 },
870145247Sdamien		{ IWI_RATE_OFDM9,   18 },
871145247Sdamien		{ IWI_RATE_OFDM12,  24 },
872145247Sdamien		{ IWI_RATE_OFDM18,  36 },
873145247Sdamien		{ IWI_RATE_OFDM24,  48 },
874145247Sdamien		{ IWI_RATE_OFDM36,  72 },
875145247Sdamien		{ IWI_RATE_OFDM48,  96 },
876145247Sdamien		{ IWI_RATE_OFDM54, 108 },
877145247Sdamien	};
878145247Sdamien	uint32_t val;
879145247Sdamien	int rate, i;
880145247Sdamien
881145247Sdamien	imr->ifm_status = IFM_AVALID;
882145247Sdamien	imr->ifm_active = IFM_IEEE80211;
883145247Sdamien	if (ic->ic_state == IEEE80211_S_RUN)
884145247Sdamien		imr->ifm_status |= IFM_ACTIVE;
885145247Sdamien
886145247Sdamien	/* read current transmission rate from adapter */
887145247Sdamien	val = CSR_READ_4(sc, IWI_CSR_CURRENT_TX_RATE);
888145247Sdamien
889145247Sdamien	/* convert rate to 802.11 rate */
890145247Sdamien	for (i = 0; i < N(rates) && rates[i].val != val; i++);
891145247Sdamien	rate = (i < N(rates)) ? rates[i].rate : 0;
892145247Sdamien
893145247Sdamien	imr->ifm_active |= ieee80211_rate2media(ic, rate, ic->ic_curmode);
894145247Sdamien	switch (ic->ic_opmode) {
895145247Sdamien	case IEEE80211_M_STA:
896145247Sdamien		break;
897145247Sdamien
898145247Sdamien	case IEEE80211_M_IBSS:
899145247Sdamien		imr->ifm_active |= IFM_IEEE80211_ADHOC;
900145247Sdamien		break;
901145247Sdamien
902145247Sdamien	case IEEE80211_M_MONITOR:
903145247Sdamien		imr->ifm_active |= IFM_IEEE80211_MONITOR;
904145247Sdamien		break;
905145247Sdamien
906145247Sdamien	case IEEE80211_M_AHDEMO:
907145247Sdamien	case IEEE80211_M_HOSTAP:
908145247Sdamien		/* should not get there */
909145247Sdamien		break;
910145247Sdamien	}
911145247Sdamien#undef N
912145247Sdamien}
913145247Sdamien
914145247Sdamienstatic int
915145247Sdamieniwi_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
916145247Sdamien{
917145247Sdamien	struct ifnet *ifp = ic->ic_ifp;
918145247Sdamien	struct iwi_softc *sc = ifp->if_softc;
919145247Sdamien
920145247Sdamien	switch (nstate) {
921145247Sdamien	case IEEE80211_S_SCAN:
922146500Sdamien		if (sc->flags & IWI_FLAG_SCANNING)
923146500Sdamien			break;
924146500Sdamien
925146500Sdamien		ieee80211_node_table_reset(&ic->ic_scan);
926146500Sdamien		ic->ic_flags |= IEEE80211_F_SCAN | IEEE80211_F_ASCAN;
927146500Sdamien		sc->flags |= IWI_FLAG_SCANNING;
928145247Sdamien		iwi_scan(sc);
929145247Sdamien		break;
930145247Sdamien
931145247Sdamien	case IEEE80211_S_AUTH:
932145247Sdamien		iwi_auth_and_assoc(sc);
933145247Sdamien		break;
934145247Sdamien
935145247Sdamien	case IEEE80211_S_RUN:
936145247Sdamien		if (ic->ic_opmode == IEEE80211_M_IBSS)
937145247Sdamien			ieee80211_new_state(ic, IEEE80211_S_AUTH, -1);
938146500Sdamien		else if (ic->ic_opmode == IEEE80211_M_MONITOR)
939146500Sdamien			iwi_set_chan(sc, ic->ic_ibss_chan);
940146500Sdamien
941146500Sdamien		return sc->sc_newstate(ic, nstate,
942146500Sdamien		    IEEE80211_FC0_SUBTYPE_ASSOC_RESP);
943146500Sdamien
944146500Sdamien	case IEEE80211_S_ASSOC:
945145247Sdamien		break;
946145247Sdamien
947145247Sdamien	case IEEE80211_S_INIT:
948146500Sdamien		sc->flags &= ~IWI_FLAG_SCANNING;
949145247Sdamien		break;
950145247Sdamien	}
951145247Sdamien
952145247Sdamien	ic->ic_state = nstate;
953145247Sdamien	return 0;
954145247Sdamien}
955145247Sdamien
956149346Sdamien/*
957149346Sdamien * WME parameters coming from IEEE 802.11e specification.  These values are
958149346Sdamien * already declared in ieee80211_proto.c, but they are static so they can't
959149346Sdamien * be reused here.
960149346Sdamien */
961149346Sdamienstatic const struct wmeParams iwi_wme_cck_params[WME_NUM_AC] = {
962149346Sdamien	{ 0, 3, 5,  7,   0 },	/* WME_AC_BE */
963149346Sdamien	{ 0, 3, 5, 10,   0 },	/* WME_AC_BK */
964149346Sdamien	{ 0, 2, 4,  5, 188 },	/* WME_AC_VI */
965149346Sdamien	{ 0, 2, 3,  4, 102 }	/* WME_AC_VO */
966149346Sdamien};
967149346Sdamien
968149346Sdamienstatic const struct wmeParams iwi_wme_ofdm_params[WME_NUM_AC] = {
969149346Sdamien	{ 0, 3, 4,  6,   0 },	/* WME_AC_BE */
970149346Sdamien	{ 0, 3, 4, 10,   0 },	/* WME_AC_BK */
971149346Sdamien	{ 0, 2, 3,  4,  94 },	/* WME_AC_VI */
972149346Sdamien	{ 0, 2, 2,  3,  47 }	/* WME_AC_VO */
973149346Sdamien};
974149346Sdamien
975149338Sdamienstatic int
976149338Sdamieniwi_wme_update(struct ieee80211com *ic)
977149338Sdamien{
978149346Sdamien#define IWI_EXP2(v)	htole16((1 << (v)) - 1)
979149346Sdamien#define IWI_USEC(v)	htole16(IEEE80211_TXOP_TO_US(v))
980149346Sdamien	struct iwi_softc *sc = ic->ic_ifp->if_softc;
981149346Sdamien	struct iwi_wme_params wme[3];
982149346Sdamien	const struct wmeParams *wmep;
983149346Sdamien	int ac;
984149338Sdamien
985149346Sdamien	/*
986149346Sdamien	 * We shall not override firmware default WME values if WME is not
987149346Sdamien	 * actually enabled.
988149346Sdamien	 */
989149346Sdamien	if (!(ic->ic_flags & IEEE80211_F_WME))
990149346Sdamien		return 0;
991149346Sdamien
992149346Sdamien	for (ac = 0; ac < WME_NUM_AC; ac++) {
993149346Sdamien		/* set WME values for current operating mode */
994149346Sdamien		wmep = &ic->ic_wme.wme_chanParams.cap_wmeParams[ac];
995149346Sdamien		wme[0].aifsn[ac] = wmep->wmep_aifsn;
996149346Sdamien		wme[0].cwmin[ac] = IWI_EXP2(wmep->wmep_logcwmin);
997149346Sdamien		wme[0].cwmax[ac] = IWI_EXP2(wmep->wmep_logcwmax);
998149346Sdamien		wme[0].burst[ac] = IWI_USEC(wmep->wmep_txopLimit);
999149346Sdamien		wme[0].acm[ac]   = wmep->wmep_acm;
1000149346Sdamien
1001149346Sdamien		/* set WME values for CCK modulation */
1002149346Sdamien		wmep = &iwi_wme_cck_params[ac];
1003149346Sdamien		wme[1].aifsn[ac] = wmep->wmep_aifsn;
1004149346Sdamien		wme[1].cwmin[ac] = IWI_EXP2(wmep->wmep_logcwmin);
1005149346Sdamien		wme[1].cwmax[ac] = IWI_EXP2(wmep->wmep_logcwmax);
1006149346Sdamien		wme[1].burst[ac] = IWI_USEC(wmep->wmep_txopLimit);
1007149346Sdamien		wme[1].acm[ac]   = wmep->wmep_acm;
1008149346Sdamien
1009149346Sdamien		/* set WME values for OFDM modulation */
1010149346Sdamien		wmep = &iwi_wme_ofdm_params[ac];
1011149346Sdamien		wme[2].aifsn[ac] = wmep->wmep_aifsn;
1012149346Sdamien		wme[2].cwmin[ac] = IWI_EXP2(wmep->wmep_logcwmin);
1013149346Sdamien		wme[2].cwmax[ac] = IWI_EXP2(wmep->wmep_logcwmax);
1014149346Sdamien		wme[2].burst[ac] = IWI_USEC(wmep->wmep_txopLimit);
1015149346Sdamien		wme[2].acm[ac]   = wmep->wmep_acm;
1016149346Sdamien	}
1017149346Sdamien
1018149346Sdamien	DPRINTF(("Setting WME parameters\n"));
1019149346Sdamien	return iwi_cmd(sc, IWI_CMD_SET_WME_PARAMS, wme, sizeof wme, 1);
1020149346Sdamien#undef IWI_USEC
1021149346Sdamien#undef IWI_EXP2
1022149338Sdamien}
1023149338Sdamien
1024145247Sdamien/*
1025145247Sdamien * Read 16 bits at address 'addr' from the serial EEPROM.
1026145247Sdamien */
1027145247Sdamienstatic uint16_t
1028145247Sdamieniwi_read_prom_word(struct iwi_softc *sc, uint8_t addr)
1029145247Sdamien{
1030145247Sdamien	uint32_t tmp;
1031145247Sdamien	uint16_t val;
1032145247Sdamien	int n;
1033145247Sdamien
1034145247Sdamien	/* clock C once before the first command */
1035145247Sdamien	IWI_EEPROM_CTL(sc, 0);
1036145247Sdamien	IWI_EEPROM_CTL(sc, IWI_EEPROM_S);
1037145247Sdamien	IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_C);
1038145247Sdamien	IWI_EEPROM_CTL(sc, IWI_EEPROM_S);
1039145247Sdamien
1040145247Sdamien	/* write start bit (1) */
1041145247Sdamien	IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_D);
1042145247Sdamien	IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_D | IWI_EEPROM_C);
1043145247Sdamien
1044145247Sdamien	/* write READ opcode (10) */
1045145247Sdamien	IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_D);
1046145247Sdamien	IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_D | IWI_EEPROM_C);
1047145247Sdamien	IWI_EEPROM_CTL(sc, IWI_EEPROM_S);
1048145247Sdamien	IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_C);
1049145247Sdamien
1050145247Sdamien	/* write address A7-A0 */
1051145247Sdamien	for (n = 7; n >= 0; n--) {
1052145247Sdamien		IWI_EEPROM_CTL(sc, IWI_EEPROM_S |
1053145247Sdamien		    (((addr >> n) & 1) << IWI_EEPROM_SHIFT_D));
1054145247Sdamien		IWI_EEPROM_CTL(sc, IWI_EEPROM_S |
1055145247Sdamien		    (((addr >> n) & 1) << IWI_EEPROM_SHIFT_D) | IWI_EEPROM_C);
1056145247Sdamien	}
1057145247Sdamien
1058145247Sdamien	IWI_EEPROM_CTL(sc, IWI_EEPROM_S);
1059145247Sdamien
1060145247Sdamien	/* read data Q15-Q0 */
1061145247Sdamien	val = 0;
1062145247Sdamien	for (n = 15; n >= 0; n--) {
1063145247Sdamien		IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_C);
1064145247Sdamien		IWI_EEPROM_CTL(sc, IWI_EEPROM_S);
1065145247Sdamien		tmp = MEM_READ_4(sc, IWI_MEM_EEPROM_CTL);
1066145247Sdamien		val |= ((tmp & IWI_EEPROM_Q) >> IWI_EEPROM_SHIFT_Q) << n;
1067145247Sdamien	}
1068145247Sdamien
1069145247Sdamien	IWI_EEPROM_CTL(sc, 0);
1070145247Sdamien
1071145247Sdamien	/* clear Chip Select and clock C */
1072145247Sdamien	IWI_EEPROM_CTL(sc, IWI_EEPROM_S);
1073145247Sdamien	IWI_EEPROM_CTL(sc, 0);
1074145247Sdamien	IWI_EEPROM_CTL(sc, IWI_EEPROM_C);
1075145247Sdamien
1076145247Sdamien	return be16toh(val);
1077145247Sdamien}
1078145247Sdamien
1079145247Sdamien/*
1080145247Sdamien * XXX: Hack to set the current channel to the value advertised in beacons or
1081145247Sdamien * probe responses. Only used during AP detection.
1082145247Sdamien */
1083145247Sdamienstatic void
1084145247Sdamieniwi_fix_channel(struct ieee80211com *ic, struct mbuf *m)
1085145247Sdamien{
1086145247Sdamien	struct ieee80211_frame *wh;
1087145247Sdamien	uint8_t subtype;
1088145247Sdamien	uint8_t *frm, *efrm;
1089145247Sdamien
1090145247Sdamien	wh = mtod(m, struct ieee80211_frame *);
1091145247Sdamien
1092145247Sdamien	if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_MGT)
1093145247Sdamien		return;
1094145247Sdamien
1095145247Sdamien	subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
1096145247Sdamien
1097145247Sdamien	if (subtype != IEEE80211_FC0_SUBTYPE_BEACON &&
1098145247Sdamien	    subtype != IEEE80211_FC0_SUBTYPE_PROBE_RESP)
1099145247Sdamien		return;
1100145247Sdamien
1101145247Sdamien	frm = (uint8_t *)(wh + 1);
1102145247Sdamien	efrm = mtod(m, uint8_t *) + m->m_len;
1103145247Sdamien
1104145247Sdamien	frm += 12;	/* skip tstamp, bintval and capinfo fields */
1105145247Sdamien	while (frm < efrm) {
1106145247Sdamien		if (*frm == IEEE80211_ELEMID_DSPARMS)
1107145247Sdamien#if IEEE80211_CHAN_MAX < 255
1108145247Sdamien		if (frm[2] <= IEEE80211_CHAN_MAX)
1109145247Sdamien#endif
1110149186Sjhb			ic->ic_curchan = &ic->ic_channels[frm[2]];
1111145247Sdamien
1112145247Sdamien		frm += frm[1] + 2;
1113145247Sdamien	}
1114145247Sdamien}
1115145247Sdamien
1116145247Sdamienstatic void
1117145247Sdamieniwi_frame_intr(struct iwi_softc *sc, struct iwi_rx_data *data, int i,
1118145247Sdamien    struct iwi_frame *frame)
1119145247Sdamien{
1120145247Sdamien	struct ieee80211com *ic = &sc->sc_ic;
1121145247Sdamien	struct ifnet *ifp = ic->ic_ifp;
1122145247Sdamien	struct mbuf *m;
1123145247Sdamien	struct ieee80211_frame *wh;
1124145247Sdamien	struct ieee80211_node *ni;
1125145247Sdamien	int error;
1126145247Sdamien
1127145247Sdamien	DPRINTFN(5, ("received frame len=%u chan=%u rssi=%u\n",
1128145247Sdamien	    le16toh(frame->len), frame->chan, frame->rssi_dbm));
1129145247Sdamien
1130146500Sdamien	if (le16toh(frame->len) < sizeof (struct ieee80211_frame))
1131146500Sdamien		return;
1132146500Sdamien
1133145247Sdamien	bus_dmamap_unload(sc->rxq.data_dmat, data->map);
1134145247Sdamien
1135145247Sdamien	/* finalize mbuf */
1136145247Sdamien	m = data->m;
1137145247Sdamien	m->m_pkthdr.rcvif = ifp;
1138145247Sdamien	m->m_pkthdr.len = m->m_len = sizeof (struct iwi_hdr) +
1139145247Sdamien	    sizeof (struct iwi_frame) + le16toh(frame->len);
1140145247Sdamien
1141145247Sdamien	m_adj(m, sizeof (struct iwi_hdr) + sizeof (struct iwi_frame));
1142145247Sdamien
1143145247Sdamien	if (ic->ic_state == IEEE80211_S_SCAN)
1144145247Sdamien		iwi_fix_channel(ic, m);
1145145247Sdamien
1146145247Sdamien	if (sc->sc_drvbpf != NULL) {
1147145247Sdamien		struct iwi_rx_radiotap_header *tap = &sc->sc_rxtap;
1148145247Sdamien
1149145247Sdamien		tap->wr_flags = 0;
1150145247Sdamien		tap->wr_rate = frame->rate;
1151145247Sdamien		tap->wr_chan_freq =
1152145247Sdamien		    htole16(ic->ic_channels[frame->chan].ic_freq);
1153145247Sdamien		tap->wr_chan_flags =
1154145247Sdamien		    htole16(ic->ic_channels[frame->chan].ic_flags);
1155145247Sdamien		tap->wr_antsignal = frame->signal;
1156145247Sdamien		tap->wr_antenna = frame->antenna;
1157145247Sdamien
1158145247Sdamien		bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_rxtap_len, m);
1159145247Sdamien	}
1160145247Sdamien
1161145247Sdamien	wh = mtod(m, struct ieee80211_frame *);
1162145247Sdamien	ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh);
1163145247Sdamien
1164145247Sdamien	/* send the frame to the 802.11 layer */
1165146500Sdamien	ieee80211_input(ic, m, ni, frame->rssi_dbm, 0);
1166145247Sdamien
1167145247Sdamien	/* node is no longer needed */
1168145247Sdamien	ieee80211_free_node(ni);
1169145247Sdamien
1170145247Sdamien	data->m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
1171145247Sdamien	if (data->m == NULL) {
1172145247Sdamien		device_printf(sc->sc_dev, "could not allocate rx mbuf\n");
1173145247Sdamien		return;
1174145247Sdamien	}
1175145247Sdamien
1176145247Sdamien	error = bus_dmamap_load(sc->rxq.data_dmat, data->map,
1177145247Sdamien	    mtod(data->m, void *), MCLBYTES, iwi_dma_map_addr, &data->physaddr,
1178145247Sdamien	    0);
1179145247Sdamien	if (error != 0) {
1180145247Sdamien		device_printf(sc->sc_dev, "could not load rx buf DMA map\n");
1181145247Sdamien		m_freem(data->m);
1182145247Sdamien		data->m = NULL;
1183145247Sdamien		return;
1184145247Sdamien	}
1185145247Sdamien
1186145247Sdamien	CSR_WRITE_4(sc, data->reg, data->physaddr);
1187145247Sdamien}
1188145247Sdamien
1189145247Sdamienstatic void
1190145247Sdamieniwi_notification_intr(struct iwi_softc *sc, struct iwi_notif *notif)
1191145247Sdamien{
1192145247Sdamien	struct ieee80211com *ic = &sc->sc_ic;
1193145247Sdamien	struct iwi_notif_scan_channel *chan;
1194145247Sdamien	struct iwi_notif_scan_complete *scan;
1195145247Sdamien	struct iwi_notif_authentication *auth;
1196145247Sdamien	struct iwi_notif_association *assoc;
1197145247Sdamien
1198145247Sdamien	switch (notif->type) {
1199145247Sdamien	case IWI_NOTIF_TYPE_SCAN_CHANNEL:
1200145247Sdamien		chan = (struct iwi_notif_scan_channel *)(notif + 1);
1201145247Sdamien
1202145247Sdamien		DPRINTFN(2, ("Scanning channel (%u)\n", chan->nchan));
1203145247Sdamien		break;
1204145247Sdamien
1205145247Sdamien	case IWI_NOTIF_TYPE_SCAN_COMPLETE:
1206145247Sdamien		scan = (struct iwi_notif_scan_complete *)(notif + 1);
1207145247Sdamien
1208145247Sdamien		DPRINTFN(2, ("Scan completed (%u, %u)\n", scan->nchan,
1209145247Sdamien		    scan->status));
1210145247Sdamien
1211146500Sdamien		/* monitor mode uses scan to set the channel ... */
1212146500Sdamien		if (ic->ic_opmode != IEEE80211_M_MONITOR) {
1213146500Sdamien			sc->flags &= ~IWI_FLAG_SCANNING;
1214146500Sdamien			ieee80211_end_scan(ic);
1215146500Sdamien		} else
1216146500Sdamien			iwi_set_chan(sc, ic->ic_ibss_chan);
1217145247Sdamien		break;
1218145247Sdamien
1219145247Sdamien	case IWI_NOTIF_TYPE_AUTHENTICATION:
1220145247Sdamien		auth = (struct iwi_notif_authentication *)(notif + 1);
1221145247Sdamien
1222145247Sdamien		DPRINTFN(2, ("Authentication (%u)\n", auth->state));
1223145247Sdamien
1224145247Sdamien		switch (auth->state) {
1225145247Sdamien		case IWI_AUTHENTICATED:
1226148302Ssam			ieee80211_node_authorize(ic->ic_bss);
1227145247Sdamien			ieee80211_new_state(ic, IEEE80211_S_ASSOC, -1);
1228145247Sdamien			break;
1229145247Sdamien
1230145247Sdamien		case IWI_DEAUTHENTICATED:
1231145247Sdamien			break;
1232145247Sdamien
1233145247Sdamien		default:
1234145247Sdamien			device_printf(sc->sc_dev,
1235145247Sdamien			    "unknown authentication state %u\n", auth->state);
1236145247Sdamien		}
1237145247Sdamien		break;
1238145247Sdamien
1239145247Sdamien	case IWI_NOTIF_TYPE_ASSOCIATION:
1240145247Sdamien		assoc = (struct iwi_notif_association *)(notif + 1);
1241145247Sdamien
1242145247Sdamien		DPRINTFN(2, ("Association (%u, %u)\n", assoc->state,
1243145247Sdamien		    assoc->status));
1244145247Sdamien
1245145247Sdamien		switch (assoc->state) {
1246145247Sdamien		case IWI_AUTHENTICATED:
1247145247Sdamien			/* re-association, do nothing */
1248145247Sdamien			break;
1249145247Sdamien
1250145247Sdamien		case IWI_ASSOCIATED:
1251145247Sdamien			ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
1252145247Sdamien			break;
1253145247Sdamien
1254145247Sdamien		case IWI_DEASSOCIATED:
1255145247Sdamien			ieee80211_begin_scan(ic, 1);
1256145247Sdamien			break;
1257145247Sdamien
1258145247Sdamien		default:
1259145247Sdamien			device_printf(sc->sc_dev,
1260145247Sdamien			    "unknown association state %u\n", assoc->state);
1261145247Sdamien		}
1262145247Sdamien		break;
1263145247Sdamien
1264145247Sdamien	case IWI_NOTIF_TYPE_CALIBRATION:
1265145247Sdamien	case IWI_NOTIF_TYPE_BEACON:
1266145247Sdamien	case IWI_NOTIF_TYPE_NOISE:
1267145247Sdamien		DPRINTFN(5, ("Notification (%u)\n", notif->type));
1268145247Sdamien		break;
1269145247Sdamien
1270145247Sdamien	default:
1271145247Sdamien		device_printf(sc->sc_dev, "unknown notification type %u\n",
1272145247Sdamien		    notif->type);
1273145247Sdamien	}
1274145247Sdamien}
1275145247Sdamien
1276145247Sdamienstatic void
1277145247Sdamieniwi_rx_intr(struct iwi_softc *sc)
1278145247Sdamien{
1279145247Sdamien	struct iwi_rx_data *data;
1280145247Sdamien	struct iwi_hdr *hdr;
1281145247Sdamien	uint32_t hw;
1282145247Sdamien
1283145247Sdamien	hw = CSR_READ_4(sc, IWI_CSR_RX_RIDX);
1284145247Sdamien
1285145247Sdamien	for (; sc->rxq.cur != hw;) {
1286145247Sdamien		data = &sc->rxq.data[sc->rxq.cur];
1287145247Sdamien
1288145247Sdamien		bus_dmamap_sync(sc->rxq.data_dmat, data->map,
1289145247Sdamien		    BUS_DMASYNC_POSTREAD);
1290145247Sdamien
1291145247Sdamien		hdr = mtod(data->m, struct iwi_hdr *);
1292145247Sdamien
1293145247Sdamien		switch (hdr->type) {
1294145247Sdamien		case IWI_HDR_TYPE_FRAME:
1295145247Sdamien			iwi_frame_intr(sc, data, sc->rxq.cur,
1296145247Sdamien			    (struct iwi_frame *)(hdr + 1));
1297145247Sdamien			break;
1298145247Sdamien
1299145247Sdamien		case IWI_HDR_TYPE_NOTIF:
1300145247Sdamien			iwi_notification_intr(sc,
1301145247Sdamien			    (struct iwi_notif *)(hdr + 1));
1302145247Sdamien			break;
1303145247Sdamien
1304145247Sdamien		default:
1305145247Sdamien			device_printf(sc->sc_dev, "unknown hdr type %u\n",
1306145247Sdamien			    hdr->type);
1307145247Sdamien		}
1308145247Sdamien
1309145247Sdamien		DPRINTFN(15, ("rx done idx=%u\n", sc->rxq.cur));
1310145247Sdamien
1311145247Sdamien		sc->rxq.cur = (sc->rxq.cur + 1) % IWI_RX_RING_COUNT;
1312145247Sdamien	}
1313145247Sdamien
1314145247Sdamien	/* tell the firmware what we have processed */
1315145247Sdamien	hw = (hw == 0) ? IWI_RX_RING_COUNT - 1 : hw - 1;
1316145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_RX_WIDX, hw);
1317145247Sdamien}
1318145247Sdamien
1319145247Sdamienstatic void
1320149338Sdamieniwi_tx_intr(struct iwi_softc *sc, struct iwi_tx_ring *txq)
1321145247Sdamien{
1322145247Sdamien	struct ieee80211com *ic = &sc->sc_ic;
1323145247Sdamien	struct ifnet *ifp = ic->ic_ifp;
1324145247Sdamien	struct iwi_tx_data *data;
1325145247Sdamien	uint32_t hw;
1326145247Sdamien
1327149338Sdamien	hw = CSR_READ_4(sc, txq->csr_ridx);
1328145247Sdamien
1329149338Sdamien	for (; txq->next != hw;) {
1330149338Sdamien		data = &txq->data[txq->next];
1331145247Sdamien
1332149338Sdamien		bus_dmamap_sync(txq->data_dmat, data->map,
1333145247Sdamien		    BUS_DMASYNC_POSTWRITE);
1334149338Sdamien		bus_dmamap_unload(txq->data_dmat, data->map);
1335145247Sdamien		m_freem(data->m);
1336145247Sdamien		data->m = NULL;
1337145247Sdamien		ieee80211_free_node(data->ni);
1338145247Sdamien		data->ni = NULL;
1339145247Sdamien
1340149338Sdamien		DPRINTFN(15, ("tx done idx=%u\n", txq->next));
1341145247Sdamien
1342145247Sdamien		ifp->if_opackets++;
1343145247Sdamien
1344149338Sdamien		txq->queued--;
1345149338Sdamien		txq->next = (txq->next + 1) % IWI_TX_RING_COUNT;
1346145247Sdamien	}
1347145247Sdamien
1348145247Sdamien	sc->sc_tx_timer = 0;
1349148887Srwatson	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
1350145247Sdamien	iwi_start(ifp);
1351145247Sdamien}
1352145247Sdamien
1353145247Sdamienstatic void
1354145247Sdamieniwi_intr(void *arg)
1355145247Sdamien{
1356145247Sdamien	struct iwi_softc *sc = arg;
1357145247Sdamien	uint32_t r;
1358145247Sdamien
1359145247Sdamien	IWI_LOCK(sc);
1360145247Sdamien
1361145247Sdamien	if ((r = CSR_READ_4(sc, IWI_CSR_INTR)) == 0 || r == 0xffffffff) {
1362145247Sdamien		IWI_UNLOCK(sc);
1363145247Sdamien		return;
1364145247Sdamien	}
1365145247Sdamien
1366145247Sdamien	/* disable interrupts */
1367145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_INTR_MASK, 0);
1368145247Sdamien
1369145247Sdamien	if (r & (IWI_INTR_FATAL_ERROR | IWI_INTR_PARITY_ERROR)) {
1370145247Sdamien		device_printf(sc->sc_dev, "fatal error\n");
1371145247Sdamien		sc->sc_ic.ic_ifp->if_flags &= ~IFF_UP;
1372145247Sdamien		iwi_stop(sc);
1373145247Sdamien	}
1374145247Sdamien
1375145247Sdamien	if (r & IWI_INTR_FW_INITED) {
1376145247Sdamien		if (!(r & (IWI_INTR_FATAL_ERROR | IWI_INTR_PARITY_ERROR)))
1377145247Sdamien			wakeup(sc);
1378145247Sdamien	}
1379145247Sdamien
1380145247Sdamien	if (r & IWI_INTR_RADIO_OFF) {
1381145247Sdamien		DPRINTF(("radio transmitter turned off\n"));
1382145247Sdamien		sc->sc_ic.ic_ifp->if_flags &= ~IFF_UP;
1383145247Sdamien		iwi_stop(sc);
1384145247Sdamien	}
1385145247Sdamien
1386145247Sdamien	if (r & IWI_INTR_CMD_DONE)
1387145247Sdamien		wakeup(sc);
1388145247Sdamien
1389145247Sdamien	if (r & IWI_INTR_TX1_DONE)
1390149338Sdamien		iwi_tx_intr(sc, &sc->txq[0]);
1391145247Sdamien
1392149338Sdamien	if (r & IWI_INTR_TX2_DONE)
1393149338Sdamien		iwi_tx_intr(sc, &sc->txq[1]);
1394149338Sdamien
1395149338Sdamien	if (r & IWI_INTR_TX3_DONE)
1396149338Sdamien		iwi_tx_intr(sc, &sc->txq[2]);
1397149338Sdamien
1398149338Sdamien	if (r & IWI_INTR_TX4_DONE)
1399149338Sdamien		iwi_tx_intr(sc, &sc->txq[3]);
1400149338Sdamien
1401149338Sdamien	if (r & IWI_INTR_RX_DONE)
1402149338Sdamien		iwi_rx_intr(sc);
1403149338Sdamien
1404145247Sdamien	/* acknowledge interrupts */
1405145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_INTR, r);
1406145247Sdamien
1407145247Sdamien	/* re-enable interrupts */
1408145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_INTR_MASK, IWI_INTR_MASK);
1409145247Sdamien
1410145247Sdamien	IWI_UNLOCK(sc);
1411145247Sdamien}
1412145247Sdamien
1413145247Sdamienstatic int
1414145247Sdamieniwi_cmd(struct iwi_softc *sc, uint8_t type, void *data, uint8_t len, int async)
1415145247Sdamien{
1416145247Sdamien	struct iwi_cmd_desc *desc;
1417145247Sdamien
1418145247Sdamien	desc = &sc->cmdq.desc[sc->cmdq.cur];
1419145247Sdamien
1420145247Sdamien	desc->hdr.type = IWI_HDR_TYPE_COMMAND;
1421145247Sdamien	desc->hdr.flags = IWI_HDR_FLAG_IRQ;
1422145247Sdamien	desc->type = type;
1423145247Sdamien	desc->len = len;
1424145247Sdamien	memcpy(desc->data, data, len);
1425145247Sdamien
1426145247Sdamien	bus_dmamap_sync(sc->cmdq.desc_dmat, sc->cmdq.desc_map,
1427145247Sdamien	    BUS_DMASYNC_PREWRITE);
1428145247Sdamien
1429145247Sdamien	DPRINTFN(2, ("sending command idx=%u type=%u len=%u\n", sc->cmdq.cur,
1430145247Sdamien	    type, len));
1431145247Sdamien
1432145247Sdamien	sc->cmdq.cur = (sc->cmdq.cur + 1) % IWI_CMD_RING_COUNT;
1433145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_CMD_WIDX, sc->cmdq.cur);
1434145247Sdamien
1435145247Sdamien	return async ? 0 : msleep(sc, &sc->sc_mtx, 0, "iwicmd", hz);
1436145247Sdamien}
1437145247Sdamien
1438145247Sdamienstatic int
1439145247Sdamieniwi_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni)
1440145247Sdamien{
1441145247Sdamien	struct iwi_softc *sc = ifp->if_softc;
1442145247Sdamien	struct ieee80211com *ic = &sc->sc_ic;
1443149338Sdamien	struct ieee80211_frame *wh;
1444146500Sdamien	struct ieee80211_key *k;
1445149338Sdamien	const struct chanAccParams *cap;
1446149338Sdamien	struct iwi_tx_ring *txq;
1447145247Sdamien	struct iwi_tx_data *data;
1448145247Sdamien	struct iwi_tx_desc *desc;
1449145247Sdamien	struct mbuf *mnew;
1450145247Sdamien	bus_dma_segment_t segs[IWI_MAX_NSEG];
1451149338Sdamien	int error, nsegs, hdrlen, ac, i, noack = 0;
1452145247Sdamien
1453149338Sdamien	wh = mtod(m0, struct ieee80211_frame *);
1454149338Sdamien
1455149338Sdamien	if (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_QOS) {
1456149338Sdamien		hdrlen = sizeof (struct ieee80211_qosframe);
1457149338Sdamien		ac = M_WME_GETAC(m0);
1458149338Sdamien		cap = &ic->ic_wme.wme_chanParams;
1459149338Sdamien		noack = cap->cap_wmeParams[ac].wmep_noackPolicy;
1460149338Sdamien	} else {
1461149338Sdamien		hdrlen = sizeof (struct ieee80211_frame);
1462149338Sdamien		ac = WME_AC_BE;
1463149338Sdamien	}
1464149338Sdamien
1465149338Sdamien	txq = &sc->txq[ac];
1466149338Sdamien	if (txq->queued >= IWI_TX_RING_COUNT - 4) {
1467149338Sdamien		/*
1468149338Sdamien		 * There is no place left in this ring.  Perhaps in 802.11e,
1469149338Sdamien		 * we should try to fallback to a lowest priority ring?
1470149338Sdamien		 */
1471149338Sdamien		ifp->if_drv_flags |= IFF_DRV_OACTIVE;
1472149338Sdamien		m_freem(m0);
1473149338Sdamien		return 0;
1474149338Sdamien	}
1475149338Sdamien
1476149338Sdamien	if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
1477146500Sdamien		k = ieee80211_crypto_encap(ic, ni, m0);
1478147806Ssam		if (k == NULL) {
1479147806Ssam			m_freem(m0);
1480146500Sdamien			return ENOBUFS;
1481147806Ssam		}
1482149338Sdamien
1483149338Sdamien		/* packet header may have moved, reset our local pointer */
1484149338Sdamien		wh = mtod(m0, struct ieee80211_frame *);
1485146500Sdamien	}
1486146500Sdamien
1487145247Sdamien	if (sc->sc_drvbpf != NULL) {
1488145247Sdamien		struct iwi_tx_radiotap_header *tap = &sc->sc_txtap;
1489145247Sdamien
1490145247Sdamien		tap->wt_flags = 0;
1491146500Sdamien		tap->wt_chan_freq = htole16(ic->ic_ibss_chan->ic_freq);
1492146500Sdamien		tap->wt_chan_flags = htole16(ic->ic_ibss_chan->ic_flags);
1493145247Sdamien
1494145247Sdamien		bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m0);
1495145247Sdamien	}
1496145247Sdamien
1497149338Sdamien	data = &txq->data[txq->cur];
1498149338Sdamien	desc = &txq->desc[txq->cur];
1499145247Sdamien
1500149338Sdamien	/* save and trim IEEE802.11 header */
1501149338Sdamien	m_copydata(m0, 0, hdrlen, (caddr_t)&desc->wh);
1502149338Sdamien	m_adj(m0, hdrlen);
1503145247Sdamien
1504149338Sdamien	error = bus_dmamap_load_mbuf_sg(txq->data_dmat, data->map, m0, segs,
1505145247Sdamien	    &nsegs, 0);
1506145247Sdamien	if (error != 0 && error != EFBIG) {
1507145247Sdamien		device_printf(sc->sc_dev, "could not map mbuf (error %d)\n",
1508145247Sdamien		    error);
1509145247Sdamien		m_freem(m0);
1510145247Sdamien		return error;
1511145247Sdamien	}
1512145247Sdamien	if (error != 0) {
1513145247Sdamien		mnew = m_defrag(m0, M_DONTWAIT);
1514145247Sdamien		if (mnew == NULL) {
1515145247Sdamien			device_printf(sc->sc_dev,
1516145247Sdamien			    "could not defragment mbuf\n");
1517145247Sdamien			m_freem(m0);
1518145247Sdamien			return ENOBUFS;
1519145247Sdamien		}
1520145247Sdamien		m0 = mnew;
1521145247Sdamien
1522149338Sdamien		error = bus_dmamap_load_mbuf_sg(txq->data_dmat, data->map,
1523145247Sdamien		    m0, segs, &nsegs, 0);
1524145247Sdamien		if (error != 0) {
1525145247Sdamien			device_printf(sc->sc_dev,
1526145247Sdamien			    "could not map mbuf (error %d)\n", error);
1527145247Sdamien			m_freem(m0);
1528145247Sdamien			return error;
1529145247Sdamien		}
1530145247Sdamien	}
1531145247Sdamien
1532145247Sdamien	data->m = m0;
1533145247Sdamien	data->ni = ni;
1534145247Sdamien
1535145247Sdamien	desc->hdr.type = IWI_HDR_TYPE_DATA;
1536145247Sdamien	desc->hdr.flags = IWI_HDR_FLAG_IRQ;
1537145247Sdamien	desc->cmd = IWI_DATA_CMD_TX;
1538145247Sdamien	desc->len = htole16(m0->m_pkthdr.len);
1539145247Sdamien	desc->flags = 0;
1540149338Sdamien	desc->xflags = 0;
1541145247Sdamien
1542149338Sdamien	if (!noack && !IEEE80211_IS_MULTICAST(desc->wh.i_addr1))
1543145247Sdamien		desc->flags |= IWI_DATA_FLAG_NEED_ACK;
1544145247Sdamien
1545146500Sdamien#if 0
1546145247Sdamien	if (ic->ic_flags & IEEE80211_F_PRIVACY) {
1547149338Sdamien		desc->wh.i_fc[1] |= IEEE80211_FC1_WEP;
1548145247Sdamien		desc->wep_txkey = ic->ic_crypto.cs_def_txkey;
1549145247Sdamien	} else
1550146500Sdamien#endif
1551145247Sdamien		desc->flags |= IWI_DATA_FLAG_NO_WEP;
1552145247Sdamien
1553145247Sdamien	if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
1554145247Sdamien		desc->flags |= IWI_DATA_FLAG_SHPREAMBLE;
1555145247Sdamien
1556149338Sdamien	if (desc->wh.i_fc[0] & IEEE80211_FC0_SUBTYPE_QOS)
1557149338Sdamien		desc->xflags |= IWI_DATA_XFLAG_QOS;
1558149338Sdamien
1559145247Sdamien	desc->nseg = htole32(nsegs);
1560145247Sdamien	for (i = 0; i < nsegs; i++) {
1561145247Sdamien		desc->seg_addr[i] = htole32(segs[i].ds_addr);
1562145247Sdamien		desc->seg_len[i]  = htole32(segs[i].ds_len);
1563145247Sdamien	}
1564145247Sdamien
1565149338Sdamien	bus_dmamap_sync(txq->data_dmat, data->map, BUS_DMASYNC_PREWRITE);
1566149338Sdamien	bus_dmamap_sync(txq->desc_dmat, txq->desc_map, BUS_DMASYNC_PREWRITE);
1567145247Sdamien
1568149338Sdamien	DPRINTFN(5, ("sending data frame txq=%u idx=%u len=%u nseg=%u\n",
1569149338Sdamien	    ac, txq->cur, desc->len, desc->nseg));
1570145247Sdamien
1571149338Sdamien	txq->queued++;
1572149338Sdamien	txq->cur = (txq->cur + 1) % IWI_TX_RING_COUNT;
1573149338Sdamien	CSR_WRITE_4(sc, txq->csr_widx, txq->cur);
1574145247Sdamien
1575145247Sdamien	return 0;
1576145247Sdamien}
1577145247Sdamien
1578145247Sdamienstatic void
1579145247Sdamieniwi_start(struct ifnet *ifp)
1580145247Sdamien{
1581145247Sdamien	struct iwi_softc *sc = ifp->if_softc;
1582145247Sdamien	struct ieee80211com *ic = &sc->sc_ic;
1583145247Sdamien	struct mbuf *m0;
1584145247Sdamien	struct ether_header *eh;
1585145247Sdamien	struct ieee80211_node *ni;
1586145247Sdamien
1587145247Sdamien	IWI_LOCK(sc);
1588145247Sdamien
1589145247Sdamien	if (ic->ic_state != IEEE80211_S_RUN) {
1590145247Sdamien		IWI_UNLOCK(sc);
1591145247Sdamien		return;
1592145247Sdamien	}
1593145247Sdamien
1594145247Sdamien	for (;;) {
1595145247Sdamien		IFQ_DRV_DEQUEUE(&ifp->if_snd, m0);
1596145247Sdamien		if (m0 == NULL)
1597145247Sdamien			break;
1598145247Sdamien
1599145247Sdamien		if (m0->m_len < sizeof (struct ether_header) &&
1600145247Sdamien		    (m0 = m_pullup(m0, sizeof (struct ether_header))) == NULL)
1601145247Sdamien			continue;
1602145247Sdamien
1603145247Sdamien		eh = mtod(m0, struct ether_header *);
1604145247Sdamien		ni = ieee80211_find_txnode(ic, eh->ether_dhost);
1605145247Sdamien		if (ni == NULL) {
1606145247Sdamien			m_freem(m0);
1607145247Sdamien			continue;
1608145247Sdamien		}
1609149338Sdamien		if (ieee80211_classify(ic, m0, ni) != 0) {
1610149338Sdamien			m_freem(m0);
1611149338Sdamien			continue;
1612149338Sdamien		}
1613145247Sdamien		BPF_MTAP(ifp, m0);
1614145247Sdamien
1615145247Sdamien		m0 = ieee80211_encap(ic, m0, ni);
1616147834Ssam		if (m0 == NULL) {
1617147834Ssam			ieee80211_free_node(ni);
1618145247Sdamien			continue;
1619147834Ssam		}
1620145247Sdamien
1621145247Sdamien		if (ic->ic_rawbpf != NULL)
1622145247Sdamien			bpf_mtap(ic->ic_rawbpf, m0);
1623145247Sdamien
1624145247Sdamien		if (iwi_tx_start(ifp, m0, ni) != 0) {
1625145247Sdamien			ieee80211_free_node(ni);
1626145247Sdamien			ifp->if_oerrors++;
1627145247Sdamien			break;
1628145247Sdamien		}
1629145247Sdamien
1630145247Sdamien		sc->sc_tx_timer = 5;
1631145247Sdamien		ifp->if_timer = 1;
1632145247Sdamien	}
1633145247Sdamien
1634145247Sdamien	IWI_UNLOCK(sc);
1635145247Sdamien}
1636145247Sdamien
1637145247Sdamienstatic void
1638145247Sdamieniwi_watchdog(struct ifnet *ifp)
1639145247Sdamien{
1640145247Sdamien	struct iwi_softc *sc = ifp->if_softc;
1641145247Sdamien	struct ieee80211com *ic = &sc->sc_ic;
1642145247Sdamien
1643145247Sdamien	IWI_LOCK(sc);
1644145247Sdamien
1645145247Sdamien	ifp->if_timer = 0;
1646145247Sdamien
1647145247Sdamien	if (sc->sc_tx_timer > 0) {
1648145247Sdamien		if (--sc->sc_tx_timer == 0) {
1649145247Sdamien			if_printf(ifp, "device timeout\n");
1650145247Sdamien			ifp->if_oerrors++;
1651145247Sdamien			ifp->if_flags &= ~IFF_UP;
1652145247Sdamien			iwi_stop(sc);
1653145247Sdamien			IWI_UNLOCK(sc);
1654145247Sdamien			return;
1655145247Sdamien		}
1656145247Sdamien		ifp->if_timer = 1;
1657145247Sdamien	}
1658145247Sdamien
1659145247Sdamien	ieee80211_watchdog(ic);
1660145247Sdamien
1661145247Sdamien	IWI_UNLOCK(sc);
1662145247Sdamien}
1663145247Sdamien
1664145247Sdamienstatic int
1665145247Sdamieniwi_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
1666145247Sdamien{
1667145247Sdamien	struct iwi_softc *sc = ifp->if_softc;
1668145247Sdamien	struct ieee80211com *ic = &sc->sc_ic;
1669145247Sdamien	struct ifreq *ifr;
1670145247Sdamien	int error = 0;
1671145247Sdamien
1672145247Sdamien	IWI_LOCK(sc);
1673145247Sdamien
1674145247Sdamien	switch (cmd) {
1675145247Sdamien	case SIOCSIFFLAGS:
1676145247Sdamien		if (ifp->if_flags & IFF_UP) {
1677148887Srwatson			if (!(ifp->if_drv_flags & IFF_DRV_RUNNING))
1678145247Sdamien				iwi_init(sc);
1679145247Sdamien		} else {
1680148887Srwatson			if (ifp->if_drv_flags & IFF_DRV_RUNNING)
1681145247Sdamien				iwi_stop(sc);
1682145247Sdamien		}
1683145247Sdamien		break;
1684145247Sdamien
1685145247Sdamien	case SIOCSLOADFW:
1686145247Sdamien		/* only super-user can do that! */
1687145247Sdamien		if ((error = suser(curthread)) != 0)
1688145247Sdamien			break;
1689145247Sdamien
1690145247Sdamien		ifr = (struct ifreq *)data;
1691145247Sdamien		error = iwi_cache_firmware(sc, ifr->ifr_data);
1692145247Sdamien		break;
1693145247Sdamien
1694145247Sdamien	case SIOCSKILLFW:
1695145247Sdamien		/* only super-user can do that! */
1696145247Sdamien		if ((error = suser(curthread)) != 0)
1697145247Sdamien			break;
1698145247Sdamien
1699145247Sdamien		ifp->if_flags &= ~IFF_UP;
1700145247Sdamien		iwi_stop(sc);
1701145247Sdamien		iwi_free_firmware(sc);
1702145247Sdamien		break;
1703145247Sdamien
1704145247Sdamien	default:
1705145247Sdamien		error = ieee80211_ioctl(ic, cmd, data);
1706145247Sdamien	}
1707145247Sdamien
1708145247Sdamien	if (error == ENETRESET) {
1709148887Srwatson		if ((ifp->if_flags & IFF_UP) &&
1710149333Sdamien		    (ifp->if_drv_flags & IFF_DRV_RUNNING) &&
1711149333Sdamien		    (ic->ic_roaming != IEEE80211_ROAMING_MANUAL))
1712145247Sdamien			iwi_init(sc);
1713145247Sdamien		error = 0;
1714145247Sdamien	}
1715145247Sdamien
1716145247Sdamien	IWI_UNLOCK(sc);
1717145247Sdamien
1718145247Sdamien	return error;
1719145247Sdamien}
1720145247Sdamien
1721145247Sdamienstatic void
1722145247Sdamieniwi_stop_master(struct iwi_softc *sc)
1723145247Sdamien{
1724145247Sdamien	uint32_t tmp;
1725145247Sdamien	int ntries;
1726145247Sdamien
1727145247Sdamien	/* disable interrupts */
1728145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_INTR_MASK, 0);
1729145247Sdamien
1730145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_RST, IWI_RST_STOP_MASTER);
1731145247Sdamien	for (ntries = 0; ntries < 5; ntries++) {
1732145247Sdamien		if (CSR_READ_4(sc, IWI_CSR_RST) & IWI_RST_MASTER_DISABLED)
1733145247Sdamien			break;
1734145247Sdamien		DELAY(10);
1735145247Sdamien	}
1736145247Sdamien	if (ntries == 5)
1737145247Sdamien		device_printf(sc->sc_dev, "timeout waiting for master\n");
1738145247Sdamien
1739145247Sdamien	tmp = CSR_READ_4(sc, IWI_CSR_RST);
1740145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_RST, tmp | IWI_RST_PRINCETON_RESET);
1741145247Sdamien
1742145247Sdamien	sc->flags &= ~IWI_FLAG_FW_INITED;
1743145247Sdamien}
1744145247Sdamien
1745145247Sdamienstatic int
1746145247Sdamieniwi_reset(struct iwi_softc *sc)
1747145247Sdamien{
1748145247Sdamien	uint32_t tmp;
1749145247Sdamien	int i, ntries;
1750145247Sdamien
1751145247Sdamien	iwi_stop_master(sc);
1752145247Sdamien
1753145247Sdamien	tmp = CSR_READ_4(sc, IWI_CSR_CTL);
1754145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_CTL, tmp | IWI_CTL_INIT);
1755145247Sdamien
1756145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_READ_INT, IWI_READ_INT_INIT_HOST);
1757145247Sdamien
1758145247Sdamien	/* wait for clock stabilization */
1759145247Sdamien	for (ntries = 0; ntries < 1000; ntries++) {
1760145247Sdamien		if (CSR_READ_4(sc, IWI_CSR_CTL) & IWI_CTL_CLOCK_READY)
1761145247Sdamien			break;
1762145247Sdamien		DELAY(200);
1763145247Sdamien	}
1764145247Sdamien	if (ntries == 1000) {
1765145247Sdamien		device_printf(sc->sc_dev,
1766145247Sdamien		    "timeout waiting for clock stabilization\n");
1767145247Sdamien		return EIO;
1768145247Sdamien	}
1769145247Sdamien
1770145247Sdamien	tmp = CSR_READ_4(sc, IWI_CSR_RST);
1771145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_RST, tmp | IWI_RST_SOFT_RESET);
1772145247Sdamien
1773145247Sdamien	DELAY(10);
1774145247Sdamien
1775145247Sdamien	tmp = CSR_READ_4(sc, IWI_CSR_CTL);
1776145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_CTL, tmp | IWI_CTL_INIT);
1777145247Sdamien
1778145247Sdamien	/* clear NIC memory */
1779145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_AUTOINC_ADDR, 0);
1780145247Sdamien	for (i = 0; i < 0xc000; i++)
1781145247Sdamien		CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, 0);
1782145247Sdamien
1783145247Sdamien	return 0;
1784145247Sdamien}
1785145247Sdamien
1786145247Sdamienstatic int
1787145247Sdamieniwi_load_ucode(struct iwi_softc *sc, void *uc, int size)
1788145247Sdamien{
1789145247Sdamien	uint32_t tmp;
1790145247Sdamien	uint16_t *w;
1791145247Sdamien	int ntries, i;
1792145247Sdamien
1793145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_RST, CSR_READ_4(sc, IWI_CSR_RST) |
1794145247Sdamien	    IWI_RST_STOP_MASTER);
1795145247Sdamien	for (ntries = 0; ntries < 5; ntries++) {
1796145247Sdamien		if (CSR_READ_4(sc, IWI_CSR_RST) & IWI_RST_MASTER_DISABLED)
1797145247Sdamien			break;
1798145247Sdamien		DELAY(10);
1799145247Sdamien	}
1800145247Sdamien	if (ntries == 5) {
1801145247Sdamien		device_printf(sc->sc_dev, "timeout waiting for master\n");
1802145247Sdamien		return EIO;
1803145247Sdamien	}
1804145247Sdamien
1805145247Sdamien	MEM_WRITE_4(sc, 0x3000e0, 0x80000000);
1806145247Sdamien	DELAY(5000);
1807145247Sdamien
1808145247Sdamien	tmp = CSR_READ_4(sc, IWI_CSR_RST);
1809145247Sdamien	tmp &= ~IWI_RST_PRINCETON_RESET;
1810145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_RST, tmp);
1811145247Sdamien
1812145247Sdamien	DELAY(5000);
1813145247Sdamien	MEM_WRITE_4(sc, 0x3000e0, 0);
1814145247Sdamien	DELAY(1000);
1815145247Sdamien	MEM_WRITE_4(sc, 0x300004, 1);
1816145247Sdamien	DELAY(1000);
1817145247Sdamien	MEM_WRITE_4(sc, 0x300004, 0);
1818145247Sdamien	DELAY(1000);
1819145247Sdamien	MEM_WRITE_1(sc, 0x200000, 0x00);
1820145247Sdamien	MEM_WRITE_1(sc, 0x200000, 0x40);
1821145247Sdamien	DELAY(1000);
1822145247Sdamien
1823145247Sdamien	/* write microcode into adapter memory */
1824145247Sdamien	for (w = uc; size > 0; w++, size -= 2)
1825145247Sdamien		MEM_WRITE_2(sc, 0x200010, *w);
1826145247Sdamien
1827145247Sdamien	MEM_WRITE_1(sc, 0x200000, 0x00);
1828145247Sdamien	MEM_WRITE_1(sc, 0x200000, 0x80);
1829145247Sdamien
1830145247Sdamien	/* wait until we get an answer */
1831145247Sdamien	for (ntries = 0; ntries < 100; ntries++) {
1832145247Sdamien		if (MEM_READ_1(sc, 0x200000) & 1)
1833145247Sdamien			break;
1834145247Sdamien		DELAY(100);
1835145247Sdamien	}
1836145247Sdamien	if (ntries == 100) {
1837145247Sdamien		device_printf(sc->sc_dev,
1838145247Sdamien		    "timeout waiting for ucode to initialize\n");
1839145247Sdamien		return EIO;
1840145247Sdamien	}
1841145247Sdamien
1842145247Sdamien	/* read the answer or the firmware will not initialize properly */
1843145247Sdamien	for (i = 0; i < 7; i++)
1844145247Sdamien		MEM_READ_4(sc, 0x200004);
1845145247Sdamien
1846145247Sdamien	MEM_WRITE_1(sc, 0x200000, 0x00);
1847145247Sdamien
1848145247Sdamien	return 0;
1849145247Sdamien}
1850145247Sdamien
1851145247Sdamien/* macro to handle unaligned little endian data in firmware image */
1852145247Sdamien#define GETLE32(p) ((p)[0] | (p)[1] << 8 | (p)[2] << 16 | (p)[3] << 24)
1853145247Sdamien
1854145247Sdamienstatic int
1855145247Sdamieniwi_load_firmware(struct iwi_softc *sc, void *fw, int size)
1856145247Sdamien{
1857145247Sdamien	bus_dma_tag_t dmat;
1858145247Sdamien	bus_dmamap_t map;
1859145247Sdamien	bus_addr_t physaddr;
1860145247Sdamien	void *virtaddr;
1861145247Sdamien	u_char *p, *end;
1862145247Sdamien	uint32_t sentinel, ctl, src, dst, sum, len, mlen, tmp;
1863145247Sdamien	int ntries, error = 0;
1864145247Sdamien
1865145247Sdamien	/* allocate DMA memory for mapping firmware image */
1866145247Sdamien	error = bus_dma_tag_create(NULL, 4, 0, BUS_SPACE_MAXADDR_32BIT,
1867145247Sdamien	    BUS_SPACE_MAXADDR, NULL, NULL, size, 1, size, 0, NULL, NULL, &dmat);
1868145247Sdamien	if (error != 0) {
1869145247Sdamien		device_printf(sc->sc_dev,
1870145247Sdamien		    "could not create firmware DMA tag\n");
1871145247Sdamien		goto fail1;
1872145247Sdamien	}
1873145247Sdamien
1874145247Sdamien	error = bus_dmamem_alloc(dmat, &virtaddr, BUS_DMA_NOWAIT, &map);
1875145247Sdamien	if (error != 0) {
1876145247Sdamien		device_printf(sc->sc_dev,
1877145247Sdamien		    "could not allocate firmware DMA memory\n");
1878145247Sdamien		goto fail2;
1879145247Sdamien	}
1880145247Sdamien
1881145247Sdamien	error = bus_dmamap_load(dmat, map, virtaddr, size, iwi_dma_map_addr,
1882145247Sdamien	    &physaddr, 0);
1883145247Sdamien	if (error != 0) {
1884145247Sdamien		device_printf(sc->sc_dev, "could not load firmware DMA map\n");
1885145247Sdamien		goto fail3;
1886145247Sdamien	}
1887145247Sdamien
1888145247Sdamien	/* copy firmware image to DMA memory */
1889145247Sdamien	memcpy(virtaddr, fw, size);
1890145247Sdamien
1891145247Sdamien	/* make sure the adapter will get up-to-date values */
1892145247Sdamien	bus_dmamap_sync(dmat, map, BUS_DMASYNC_PREWRITE);
1893145247Sdamien
1894145247Sdamien	/* tell the adapter where the command blocks are stored */
1895145247Sdamien	MEM_WRITE_4(sc, 0x3000a0, 0x27000);
1896145247Sdamien
1897145247Sdamien	/*
1898145247Sdamien	 * Store command blocks into adapter's internal memory using register
1899145247Sdamien	 * indirections. The adapter will read the firmware image through DMA
1900145247Sdamien	 * using information stored in command blocks.
1901145247Sdamien	 */
1902145247Sdamien	src = physaddr;
1903145247Sdamien	p = virtaddr;
1904145247Sdamien	end = p + size;
1905145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_AUTOINC_ADDR, 0x27000);
1906145247Sdamien
1907145247Sdamien	while (p < end) {
1908145247Sdamien		dst = GETLE32(p); p += 4; src += 4;
1909145247Sdamien		len = GETLE32(p); p += 4; src += 4;
1910145247Sdamien		p += len;
1911145247Sdamien
1912145247Sdamien		while (len > 0) {
1913145247Sdamien			mlen = min(len, IWI_CB_MAXDATALEN);
1914145247Sdamien
1915145247Sdamien			ctl = IWI_CB_DEFAULT_CTL | mlen;
1916145247Sdamien			sum = ctl ^ src ^ dst;
1917145247Sdamien
1918145247Sdamien			/* write a command block */
1919145247Sdamien			CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, ctl);
1920145247Sdamien			CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, src);
1921145247Sdamien			CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, dst);
1922145247Sdamien			CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, sum);
1923145247Sdamien
1924145247Sdamien			src += mlen;
1925145247Sdamien			dst += mlen;
1926145247Sdamien			len -= mlen;
1927145247Sdamien		}
1928145247Sdamien	}
1929145247Sdamien
1930145247Sdamien	/* write a fictive final command block (sentinel) */
1931145247Sdamien	sentinel = CSR_READ_4(sc, IWI_CSR_AUTOINC_ADDR);
1932145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, 0);
1933145247Sdamien
1934145247Sdamien	tmp = CSR_READ_4(sc, IWI_CSR_RST);
1935145247Sdamien	tmp &= ~(IWI_RST_MASTER_DISABLED | IWI_RST_STOP_MASTER);
1936145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_RST, tmp);
1937145247Sdamien
1938145247Sdamien	/* tell the adapter to start processing command blocks */
1939145247Sdamien	MEM_WRITE_4(sc, 0x3000a4, 0x540100);
1940145247Sdamien
1941145247Sdamien	/* wait until the adapter reach the sentinel */
1942145247Sdamien	for (ntries = 0; ntries < 400; ntries++) {
1943145247Sdamien		if (MEM_READ_4(sc, 0x3000d0) >= sentinel)
1944145247Sdamien			break;
1945145247Sdamien		DELAY(100);
1946145247Sdamien	}
1947145247Sdamien	if (ntries == 400) {
1948145247Sdamien		device_printf(sc->sc_dev,
1949145247Sdamien		    "timeout processing command blocks\n");
1950145247Sdamien		error = EIO;
1951145247Sdamien		goto fail4;
1952145247Sdamien	}
1953145247Sdamien
1954145247Sdamien	/* we're done with command blocks processing */
1955145247Sdamien	MEM_WRITE_4(sc, 0x3000a4, 0x540c00);
1956145247Sdamien
1957145247Sdamien	/* allow interrupts so we know when the firmware is inited */
1958145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_INTR_MASK, IWI_INTR_MASK);
1959145247Sdamien
1960145247Sdamien	/* tell the adapter to initialize the firmware */
1961145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_RST, 0);
1962145247Sdamien
1963145247Sdamien	tmp = CSR_READ_4(sc, IWI_CSR_CTL);
1964145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_CTL, tmp | IWI_CTL_ALLOW_STANDBY);
1965145247Sdamien
1966145247Sdamien	/* wait at most one second for firmware initialization to complete */
1967145247Sdamien	if ((error = msleep(sc, &sc->sc_mtx, 0, "iwiinit", hz)) != 0) {
1968145247Sdamien		device_printf(sc->sc_dev, "timeout waiting for firmware "
1969145247Sdamien		    "initialization to complete\n");
1970145247Sdamien		goto fail4;
1971145247Sdamien	}
1972145247Sdamien
1973145247Sdamienfail4:	bus_dmamap_sync(dmat, map, BUS_DMASYNC_POSTWRITE);
1974145247Sdamien	bus_dmamap_unload(dmat, map);
1975145247Sdamienfail3:	bus_dmamem_free(dmat, virtaddr, map);
1976145247Sdamienfail2:	bus_dma_tag_destroy(dmat);
1977145247Sdamienfail1:
1978145247Sdamien	return error;
1979145247Sdamien}
1980145247Sdamien
1981145247Sdamien/*
1982145247Sdamien * Store firmware into kernel memory so we can download it when we need to,
1983145247Sdamien * e.g when the adapter wakes up from suspend mode.
1984145247Sdamien */
1985145247Sdamienstatic int
1986145247Sdamieniwi_cache_firmware(struct iwi_softc *sc, void *data)
1987145247Sdamien{
1988145247Sdamien	struct iwi_firmware *kfw = &sc->fw;
1989145247Sdamien	struct iwi_firmware ufw;
1990145247Sdamien	int error;
1991145247Sdamien
1992145247Sdamien	iwi_free_firmware(sc);
1993145247Sdamien
1994145247Sdamien	IWI_UNLOCK(sc);
1995145247Sdamien
1996145247Sdamien	if ((error = copyin(data, &ufw, sizeof ufw)) != 0)
1997145247Sdamien		goto fail1;
1998145247Sdamien
1999145247Sdamien	kfw->boot_size  = ufw.boot_size;
2000145247Sdamien	kfw->ucode_size = ufw.ucode_size;
2001145247Sdamien	kfw->main_size  = ufw.main_size;
2002145247Sdamien
2003145247Sdamien	kfw->boot = malloc(kfw->boot_size, M_DEVBUF, M_NOWAIT);
2004145247Sdamien	if (kfw->boot == NULL) {
2005145247Sdamien		error = ENOMEM;
2006145247Sdamien		goto fail1;
2007145247Sdamien	}
2008145247Sdamien
2009145247Sdamien	kfw->ucode = malloc(kfw->ucode_size, M_DEVBUF, M_NOWAIT);
2010145247Sdamien	if (kfw->ucode == NULL) {
2011145247Sdamien		error = ENOMEM;
2012145247Sdamien		goto fail2;
2013145247Sdamien	}
2014145247Sdamien
2015145247Sdamien	kfw->main = malloc(kfw->main_size, M_DEVBUF, M_NOWAIT);
2016145247Sdamien	if (kfw->main == NULL) {
2017145247Sdamien		error = ENOMEM;
2018145247Sdamien		goto fail3;
2019145247Sdamien	}
2020145247Sdamien
2021145247Sdamien	if ((error = copyin(ufw.boot, kfw->boot, kfw->boot_size)) != 0)
2022145247Sdamien		goto fail4;
2023145247Sdamien
2024145247Sdamien	if ((error = copyin(ufw.ucode, kfw->ucode, kfw->ucode_size)) != 0)
2025145247Sdamien		goto fail4;
2026145247Sdamien
2027145247Sdamien	if ((error = copyin(ufw.main, kfw->main, kfw->main_size)) != 0)
2028145247Sdamien		goto fail4;
2029145247Sdamien
2030145247Sdamien	DPRINTF(("Firmware cached: boot %u, ucode %u, main %u\n",
2031145247Sdamien	    kfw->boot_size, kfw->ucode_size, kfw->main_size));
2032145247Sdamien
2033145247Sdamien	IWI_LOCK(sc);
2034145247Sdamien
2035145247Sdamien	sc->flags |= IWI_FLAG_FW_CACHED;
2036145247Sdamien
2037145247Sdamien	return 0;
2038145247Sdamien
2039145247Sdamienfail4:	free(kfw->boot, M_DEVBUF);
2040145247Sdamienfail3:	free(kfw->ucode, M_DEVBUF);
2041145247Sdamienfail2:	free(kfw->main, M_DEVBUF);
2042145247Sdamienfail1:	IWI_LOCK(sc);
2043145247Sdamien
2044145247Sdamien	return error;
2045145247Sdamien}
2046145247Sdamien
2047145247Sdamienstatic void
2048145247Sdamieniwi_free_firmware(struct iwi_softc *sc)
2049145247Sdamien{
2050145247Sdamien	if (!(sc->flags & IWI_FLAG_FW_CACHED))
2051145247Sdamien		return;
2052145247Sdamien
2053145247Sdamien	free(sc->fw.boot, M_DEVBUF);
2054145247Sdamien	free(sc->fw.ucode, M_DEVBUF);
2055145247Sdamien	free(sc->fw.main, M_DEVBUF);
2056145247Sdamien
2057145247Sdamien	sc->flags &= ~IWI_FLAG_FW_CACHED;
2058145247Sdamien}
2059145247Sdamien
2060145247Sdamienstatic int
2061145247Sdamieniwi_config(struct iwi_softc *sc)
2062145247Sdamien{
2063145247Sdamien	struct ieee80211com *ic = &sc->sc_ic;
2064145247Sdamien	struct ifnet *ifp = ic->ic_ifp;
2065145247Sdamien	struct iwi_configuration config;
2066145247Sdamien	struct iwi_rateset rs;
2067145247Sdamien	struct iwi_txpower power;
2068145247Sdamien	struct ieee80211_key *wk;
2069145247Sdamien	struct iwi_wep_key wepkey;
2070145247Sdamien	uint32_t data;
2071145247Sdamien	int error, i;
2072145247Sdamien
2073145247Sdamien	IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp));
2074145247Sdamien	DPRINTF(("Setting MAC address to %6D\n", ic->ic_myaddr, ":"));
2075145247Sdamien	error = iwi_cmd(sc, IWI_CMD_SET_MAC_ADDRESS, ic->ic_myaddr,
2076145247Sdamien	    IEEE80211_ADDR_LEN, 0);
2077145247Sdamien	if (error != 0)
2078145247Sdamien		return error;
2079145247Sdamien
2080145247Sdamien	memset(&config, 0, sizeof config);
2081145247Sdamien	config.bluetooth_coexistence = sc->bluetooth;
2082146500Sdamien	config.antenna = sc->antenna;
2083145247Sdamien	config.multicast_enabled = 1;
2084146500Sdamien	config.answer_pbreq = (ic->ic_opmode == IEEE80211_M_IBSS) ? 1 : 0;
2085146500Sdamien	config.disable_unicast_decryption = 1;
2086146500Sdamien	config.disable_multicast_decryption = 1;
2087145247Sdamien	DPRINTF(("Configuring adapter\n"));
2088145247Sdamien	error = iwi_cmd(sc, IWI_CMD_SET_CONFIG, &config, sizeof config, 0);
2089145247Sdamien	if (error != 0)
2090145247Sdamien		return error;
2091145247Sdamien
2092145247Sdamien	data = htole32(IWI_POWER_MODE_CAM);
2093145247Sdamien	DPRINTF(("Setting power mode to %u\n", le32toh(data)));
2094145247Sdamien	error = iwi_cmd(sc, IWI_CMD_SET_POWER_MODE, &data, sizeof data, 0);
2095145247Sdamien	if (error != 0)
2096145247Sdamien		return error;
2097145247Sdamien
2098145247Sdamien	data = htole32(ic->ic_rtsthreshold);
2099145247Sdamien	DPRINTF(("Setting RTS threshold to %u\n", le32toh(data)));
2100145247Sdamien	error = iwi_cmd(sc, IWI_CMD_SET_RTS_THRESHOLD, &data, sizeof data, 0);
2101145247Sdamien	if (error != 0)
2102145247Sdamien		return error;
2103145247Sdamien
2104146500Sdamien	data = htole32(ic->ic_fragthreshold);
2105146500Sdamien	DPRINTF(("Setting fragmentation threshold to %u\n", le32toh(data)));
2106146500Sdamien	error = iwi_cmd(sc, IWI_CMD_SET_FRAG_THRESHOLD, &data, sizeof data, 0);
2107146500Sdamien	if (error != 0)
2108146500Sdamien		return error;
2109146500Sdamien
2110145247Sdamien	if (ic->ic_opmode == IEEE80211_M_IBSS) {
2111145247Sdamien		power.mode = IWI_MODE_11B;
2112145247Sdamien		power.nchan = 11;
2113145247Sdamien		for (i = 0; i < 11; i++) {
2114145247Sdamien			power.chan[i].chan = i + 1;
2115145247Sdamien			power.chan[i].power = IWI_TXPOWER_MAX;
2116145247Sdamien		}
2117145247Sdamien		DPRINTF(("Setting .11b channels tx power\n"));
2118145247Sdamien		error = iwi_cmd(sc, IWI_CMD_SET_TX_POWER, &power, sizeof power,
2119145247Sdamien		    0);
2120145247Sdamien		if (error != 0)
2121145247Sdamien			return error;
2122145247Sdamien
2123145247Sdamien		power.mode = IWI_MODE_11G;
2124145247Sdamien		DPRINTF(("Setting .11g channels tx power\n"));
2125145247Sdamien		error = iwi_cmd(sc, IWI_CMD_SET_TX_POWER, &power, sizeof power,
2126145247Sdamien		    0);
2127145247Sdamien		if (error != 0)
2128145247Sdamien			return error;
2129145247Sdamien	}
2130145247Sdamien
2131145247Sdamien	rs.mode = IWI_MODE_11G;
2132145247Sdamien	rs.type = IWI_RATESET_TYPE_SUPPORTED;
2133145247Sdamien	rs.nrates = ic->ic_sup_rates[IEEE80211_MODE_11G].rs_nrates;
2134145247Sdamien	memcpy(rs.rates, ic->ic_sup_rates[IEEE80211_MODE_11G].rs_rates,
2135145247Sdamien	    rs.nrates);
2136145247Sdamien	DPRINTF(("Setting .11bg supported rates (%u)\n", rs.nrates));
2137145247Sdamien	error = iwi_cmd(sc, IWI_CMD_SET_RATES, &rs, sizeof rs, 0);
2138145247Sdamien	if (error != 0)
2139145247Sdamien		return error;
2140145247Sdamien
2141145247Sdamien	rs.mode = IWI_MODE_11A;
2142145247Sdamien	rs.type = IWI_RATESET_TYPE_SUPPORTED;
2143145247Sdamien	rs.nrates = ic->ic_sup_rates[IEEE80211_MODE_11A].rs_nrates;
2144145247Sdamien	memcpy(rs.rates, ic->ic_sup_rates[IEEE80211_MODE_11A].rs_rates,
2145145247Sdamien	    rs.nrates);
2146145247Sdamien	DPRINTF(("Setting .11a supported rates (%u)\n", rs.nrates));
2147145247Sdamien	error = iwi_cmd(sc, IWI_CMD_SET_RATES, &rs, sizeof rs, 0);
2148145247Sdamien	if (error != 0)
2149145247Sdamien		return error;
2150145247Sdamien
2151145247Sdamien	data = htole32(arc4random());
2152145247Sdamien	DPRINTF(("Setting initialization vector to %u\n", le32toh(data)));
2153145247Sdamien	error = iwi_cmd(sc, IWI_CMD_SET_IV, &data, sizeof data, 0);
2154145247Sdamien	if (error != 0)
2155145247Sdamien		return error;
2156145247Sdamien
2157145247Sdamien	for (i = 0; i < IEEE80211_WEP_NKID; i++) {
2158145247Sdamien		wk = &ic->ic_crypto.cs_nw_keys[i];
2159145247Sdamien
2160145247Sdamien		wepkey.cmd = IWI_WEP_KEY_CMD_SETKEY;
2161145247Sdamien		wepkey.idx = i;
2162145247Sdamien		wepkey.len = wk->wk_keylen;
2163145247Sdamien		memset(wepkey.key, 0, sizeof wepkey.key);
2164145247Sdamien		memcpy(wepkey.key, wk->wk_key, wk->wk_keylen);
2165145247Sdamien		DPRINTF(("Setting wep key index %u len %u\n", wepkey.idx,
2166145247Sdamien		    wepkey.len));
2167145247Sdamien		error = iwi_cmd(sc, IWI_CMD_SET_WEP_KEY, &wepkey,
2168145247Sdamien		    sizeof wepkey, 0);
2169145247Sdamien		if (error != 0)
2170145247Sdamien			return error;
2171145247Sdamien	}
2172145247Sdamien
2173145247Sdamien	/* enable adapter */
2174145247Sdamien	DPRINTF(("Enabling adapter\n"));
2175145247Sdamien	return iwi_cmd(sc, IWI_CMD_ENABLE, NULL, 0, 0);
2176145247Sdamien}
2177145247Sdamien
2178145247Sdamienstatic int
2179146500Sdamieniwi_set_chan(struct iwi_softc *sc, struct ieee80211_channel *chan)
2180146500Sdamien{
2181146500Sdamien	struct ieee80211com *ic = &sc->sc_ic;
2182146500Sdamien	struct iwi_scan scan;
2183146500Sdamien
2184146500Sdamien	memset(&scan, 0, sizeof scan);
2185146500Sdamien	scan.type = IWI_SCAN_TYPE_PASSIVE;
2186146500Sdamien	scan.dwelltime = htole16(2000);
2187146500Sdamien	scan.channels[0] = 1 | (IEEE80211_IS_CHAN_5GHZ(chan) ? IWI_CHAN_5GHZ :
2188146500Sdamien	    IWI_CHAN_2GHZ);
2189146500Sdamien	scan.channels[1] = ieee80211_chan2ieee(ic, chan);
2190146500Sdamien
2191146500Sdamien	DPRINTF(("Setting channel to %u\n", ieee80211_chan2ieee(ic, chan)));
2192146500Sdamien	return iwi_cmd(sc, IWI_CMD_SCAN, &scan, sizeof scan, 1);
2193146500Sdamien}
2194146500Sdamien
2195146500Sdamienstatic int
2196145247Sdamieniwi_scan(struct iwi_softc *sc)
2197145247Sdamien{
2198145247Sdamien	struct ieee80211com *ic = &sc->sc_ic;
2199145247Sdamien	struct iwi_scan scan;
2200145247Sdamien	uint8_t *p;
2201145247Sdamien	int i, count;
2202145247Sdamien
2203145247Sdamien	memset(&scan, 0, sizeof scan);
2204145247Sdamien	scan.type = IWI_SCAN_TYPE_BROADCAST;
2205146500Sdamien	scan.dwelltime = htole16(sc->dwelltime);
2206145247Sdamien
2207145247Sdamien	p = scan.channels;
2208145247Sdamien	count = 0;
2209145247Sdamien	for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
2210145247Sdamien		if (IEEE80211_IS_CHAN_5GHZ(&ic->ic_channels[i]) &&
2211145247Sdamien		    isset(ic->ic_chan_active, i)) {
2212145247Sdamien			*++p = i;
2213145247Sdamien			count++;
2214145247Sdamien		}
2215145247Sdamien	}
2216145247Sdamien	*(p - count) = IWI_CHAN_5GHZ | count;
2217145247Sdamien
2218145247Sdamien	count = 0;
2219145247Sdamien	for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
2220145247Sdamien		if (IEEE80211_IS_CHAN_2GHZ(&ic->ic_channels[i]) &&
2221145247Sdamien		    isset(ic->ic_chan_active, i)) {
2222145247Sdamien			*++p = i;
2223145247Sdamien			count++;
2224145247Sdamien		}
2225145247Sdamien	}
2226145247Sdamien	*(p - count) = IWI_CHAN_2GHZ | count;
2227145247Sdamien
2228145247Sdamien	DPRINTF(("Start scanning\n"));
2229145247Sdamien	return iwi_cmd(sc, IWI_CMD_SCAN, &scan, sizeof scan, 1);
2230145247Sdamien}
2231145247Sdamien
2232145247Sdamienstatic int
2233145247Sdamieniwi_auth_and_assoc(struct iwi_softc *sc)
2234145247Sdamien{
2235145247Sdamien	struct ieee80211com *ic = &sc->sc_ic;
2236145247Sdamien	struct ifnet *ifp = ic->ic_ifp;
2237145247Sdamien	struct ieee80211_node *ni = ic->ic_bss;
2238149338Sdamien	struct ieee80211_wme_info wme;
2239145247Sdamien	struct iwi_configuration config;
2240145247Sdamien	struct iwi_associate assoc;
2241145247Sdamien	struct iwi_rateset rs;
2242146500Sdamien	uint16_t capinfo;
2243145247Sdamien	uint32_t data;
2244145247Sdamien	int error;
2245145247Sdamien
2246145247Sdamien	if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) {
2247145247Sdamien		memset(&config, 0, sizeof config);
2248145247Sdamien		config.bluetooth_coexistence = sc->bluetooth;
2249146500Sdamien		config.antenna = sc->antenna;
2250145247Sdamien		config.multicast_enabled = 1;
2251145247Sdamien		config.use_protection = 1;
2252146500Sdamien		config.answer_pbreq =
2253146500Sdamien		    (ic->ic_opmode == IEEE80211_M_IBSS) ? 1 : 0;
2254146500Sdamien		config.disable_unicast_decryption = 1;
2255146500Sdamien		config.disable_multicast_decryption = 1;
2256145247Sdamien		DPRINTF(("Configuring adapter\n"));
2257145247Sdamien		error = iwi_cmd(sc, IWI_CMD_SET_CONFIG, &config, sizeof config,
2258145247Sdamien		    1);
2259145247Sdamien		if (error != 0)
2260145247Sdamien			return error;
2261145247Sdamien	}
2262145247Sdamien
2263145247Sdamien#ifdef IWI_DEBUG
2264145247Sdamien	if (iwi_debug > 0) {
2265145247Sdamien		printf("Setting ESSID to ");
2266145247Sdamien		ieee80211_print_essid(ni->ni_essid, ni->ni_esslen);
2267145247Sdamien		printf("\n");
2268145247Sdamien	}
2269145247Sdamien#endif
2270145247Sdamien	error = iwi_cmd(sc, IWI_CMD_SET_ESSID, ni->ni_essid, ni->ni_esslen, 1);
2271145247Sdamien	if (error != 0)
2272145247Sdamien		return error;
2273145247Sdamien
2274145247Sdamien	/* the rate set has already been "negociated" */
2275145247Sdamien	rs.mode = IEEE80211_IS_CHAN_5GHZ(ni->ni_chan) ? IWI_MODE_11A :
2276145247Sdamien	    IWI_MODE_11G;
2277145247Sdamien	rs.type = IWI_RATESET_TYPE_NEGOCIATED;
2278145247Sdamien	rs.nrates = ni->ni_rates.rs_nrates;
2279145247Sdamien	memcpy(rs.rates, ni->ni_rates.rs_rates, rs.nrates);
2280145247Sdamien	DPRINTF(("Setting negociated rates (%u)\n", rs.nrates));
2281145247Sdamien	error = iwi_cmd(sc, IWI_CMD_SET_RATES, &rs, sizeof rs, 1);
2282145247Sdamien	if (error != 0)
2283145247Sdamien		return error;
2284145247Sdamien
2285149338Sdamien	if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_wme_ie != NULL) {
2286149338Sdamien		wme.wme_id = IEEE80211_ELEMID_VENDOR;
2287149338Sdamien		wme.wme_len = sizeof (struct ieee80211_wme_info) - 2;
2288149338Sdamien		wme.wme_oui[0] = 0x00;
2289149338Sdamien		wme.wme_oui[1] = 0x50;
2290149338Sdamien		wme.wme_oui[2] = 0xf2;
2291149338Sdamien		wme.wme_type = WME_OUI_TYPE;
2292149338Sdamien		wme.wme_subtype = WME_INFO_OUI_SUBTYPE;
2293149338Sdamien		wme.wme_version = WME_VERSION;
2294149338Sdamien		wme.wme_info = 0;
2295149338Sdamien
2296149338Sdamien		DPRINTF(("Setting WME IE (len=%u)\n", wme.wme_len));
2297149338Sdamien		error = iwi_cmd(sc, IWI_CMD_SET_WMEIE, &wme, sizeof wme, 1);
2298149338Sdamien		if (error != 0)
2299149338Sdamien			return error;
2300149338Sdamien	}
2301149338Sdamien
2302146500Sdamien	if (ic->ic_opt_ie != NULL) {
2303146500Sdamien		DPRINTF(("Setting optional IE (len=%u)\n", ic->ic_opt_ie_len));
2304146500Sdamien		error = iwi_cmd(sc, IWI_CMD_SET_OPTIE, ic->ic_opt_ie,
2305146500Sdamien		    ic->ic_opt_ie_len, 1);
2306146500Sdamien		if (error != 0)
2307146500Sdamien			return error;
2308146500Sdamien	}
2309146500Sdamien
2310145247Sdamien	data = htole32(ni->ni_rssi);
2311145247Sdamien	DPRINTF(("Setting sensitivity to %d\n", (int8_t)ni->ni_rssi));
2312145247Sdamien	error = iwi_cmd(sc, IWI_CMD_SET_SENSITIVITY, &data, sizeof data, 1);
2313145247Sdamien	if (error != 0)
2314145247Sdamien		return error;
2315145247Sdamien
2316145247Sdamien	memset(&assoc, 0, sizeof assoc);
2317145247Sdamien	assoc.mode = IEEE80211_IS_CHAN_5GHZ(ni->ni_chan) ? IWI_MODE_11A :
2318145247Sdamien	    IWI_MODE_11G;
2319145247Sdamien	assoc.chan = ieee80211_chan2ieee(ic, ni->ni_chan);
2320145247Sdamien	if (ni->ni_authmode == IEEE80211_AUTH_SHARED)
2321145247Sdamien		assoc.auth = ic->ic_crypto.cs_def_txkey << 4 | IWI_AUTH_SHARED;
2322149338Sdamien	if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_wme_ie != NULL)
2323149338Sdamien		assoc.policy |= htole16(IWI_POLICY_WME);
2324146500Sdamien	if (ic->ic_opt_ie != NULL)
2325149338Sdamien		assoc.policy |= htole16(IWI_POLICY_WPA);
2326145247Sdamien	memcpy(assoc.tstamp, ni->ni_tstamp.data, 8);
2327146500Sdamien
2328146500Sdamien	if (ic->ic_opmode == IEEE80211_M_IBSS)
2329146500Sdamien		capinfo = IEEE80211_CAPINFO_IBSS;
2330146500Sdamien	else
2331146500Sdamien		capinfo = IEEE80211_CAPINFO_ESS;
2332146500Sdamien	if (ic->ic_flags & IEEE80211_F_PRIVACY)
2333146500Sdamien		capinfo |= IEEE80211_CAPINFO_PRIVACY;
2334146500Sdamien	if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
2335146500Sdamien	    IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
2336146500Sdamien		capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
2337146500Sdamien	if (ic->ic_flags & IEEE80211_F_SHSLOT)
2338146500Sdamien		capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
2339146500Sdamien	assoc.capinfo = htole16(capinfo);
2340146500Sdamien
2341145247Sdamien	assoc.lintval = htole16(ic->ic_lintval);
2342145247Sdamien	assoc.intval = htole16(ni->ni_intval);
2343145247Sdamien	IEEE80211_ADDR_COPY(assoc.bssid, ni->ni_bssid);
2344145247Sdamien	if (ic->ic_opmode == IEEE80211_M_IBSS)
2345145247Sdamien		IEEE80211_ADDR_COPY(assoc.dst, ifp->if_broadcastaddr);
2346145247Sdamien	else
2347145247Sdamien		IEEE80211_ADDR_COPY(assoc.dst, ni->ni_bssid);
2348145247Sdamien
2349145247Sdamien	DPRINTF(("Trying to associate to %6D channel %u auth %u\n",
2350145247Sdamien	    assoc.bssid, ":", assoc.chan, assoc.auth));
2351145247Sdamien	return iwi_cmd(sc, IWI_CMD_ASSOCIATE, &assoc, sizeof assoc, 1);
2352145247Sdamien}
2353145247Sdamien
2354145247Sdamienstatic void
2355145247Sdamieniwi_init(void *priv)
2356145247Sdamien{
2357145247Sdamien	struct iwi_softc *sc = priv;
2358145247Sdamien	struct ieee80211com *ic = &sc->sc_ic;
2359145247Sdamien	struct ifnet *ifp = ic->ic_ifp;
2360145247Sdamien	struct iwi_firmware *fw = &sc->fw;
2361145247Sdamien	struct iwi_rx_data *data;
2362145247Sdamien	int i;
2363145247Sdamien
2364145247Sdamien	/* exit immediately if firmware has not been ioctl'd */
2365145247Sdamien	if (!(sc->flags & IWI_FLAG_FW_CACHED)) {
2366146247Simp		if (!(sc->flags & IWI_FLAG_FW_WARNED))
2367146247Simp			device_printf(sc->sc_dev, "Please load firmware\n");
2368146247Simp		sc->flags |= IWI_FLAG_FW_WARNED;
2369145247Sdamien		ifp->if_flags &= ~IFF_UP;
2370145247Sdamien		return;
2371145247Sdamien	}
2372145247Sdamien
2373145247Sdamien	iwi_stop(sc);
2374145247Sdamien
2375145247Sdamien	if (iwi_reset(sc) != 0) {
2376145247Sdamien		device_printf(sc->sc_dev, "could not reset adapter\n");
2377145247Sdamien		goto fail;
2378145247Sdamien	}
2379145247Sdamien
2380145247Sdamien	if (iwi_load_firmware(sc, fw->boot, fw->boot_size) != 0) {
2381145247Sdamien		device_printf(sc->sc_dev, "could not load boot firmware\n");
2382145247Sdamien		goto fail;
2383145247Sdamien	}
2384145247Sdamien
2385145247Sdamien	if (iwi_load_ucode(sc, fw->ucode, fw->ucode_size) != 0) {
2386145247Sdamien		device_printf(sc->sc_dev, "could not load microcode\n");
2387145247Sdamien		goto fail;
2388145247Sdamien	}
2389145247Sdamien
2390145247Sdamien	iwi_stop_master(sc);
2391145247Sdamien
2392145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_CMD_BASE, sc->cmdq.physaddr);
2393145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_CMD_SIZE, sc->cmdq.count);
2394145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_CMD_WIDX, sc->cmdq.cur);
2395145247Sdamien
2396149338Sdamien	CSR_WRITE_4(sc, IWI_CSR_TX1_BASE, sc->txq[0].physaddr);
2397149338Sdamien	CSR_WRITE_4(sc, IWI_CSR_TX1_SIZE, sc->txq[0].count);
2398149338Sdamien	CSR_WRITE_4(sc, IWI_CSR_TX1_WIDX, sc->txq[0].cur);
2399145247Sdamien
2400149338Sdamien	CSR_WRITE_4(sc, IWI_CSR_TX2_BASE, sc->txq[1].physaddr);
2401149338Sdamien	CSR_WRITE_4(sc, IWI_CSR_TX2_SIZE, sc->txq[1].count);
2402149338Sdamien	CSR_WRITE_4(sc, IWI_CSR_TX2_WIDX, sc->txq[1].cur);
2403145247Sdamien
2404149338Sdamien	CSR_WRITE_4(sc, IWI_CSR_TX3_BASE, sc->txq[2].physaddr);
2405149338Sdamien	CSR_WRITE_4(sc, IWI_CSR_TX3_SIZE, sc->txq[2].count);
2406149338Sdamien	CSR_WRITE_4(sc, IWI_CSR_TX3_WIDX, sc->txq[2].cur);
2407145247Sdamien
2408149338Sdamien	CSR_WRITE_4(sc, IWI_CSR_TX4_BASE, sc->txq[3].physaddr);
2409149338Sdamien	CSR_WRITE_4(sc, IWI_CSR_TX4_SIZE, sc->txq[3].count);
2410149338Sdamien	CSR_WRITE_4(sc, IWI_CSR_TX4_WIDX, sc->txq[3].cur);
2411145247Sdamien
2412145247Sdamien	for (i = 0; i < sc->rxq.count; i++) {
2413145247Sdamien		data = &sc->rxq.data[i];
2414145247Sdamien		CSR_WRITE_4(sc, data->reg, data->physaddr);
2415145247Sdamien	}
2416145247Sdamien
2417145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_RX_WIDX, sc->rxq.count - 1);
2418145247Sdamien
2419145247Sdamien	if (iwi_load_firmware(sc, fw->main, fw->main_size) != 0) {
2420145247Sdamien		device_printf(sc->sc_dev, "could not load main firmware\n");
2421145247Sdamien		goto fail;
2422145247Sdamien	}
2423145247Sdamien
2424145247Sdamien	sc->flags |= IWI_FLAG_FW_INITED;
2425145247Sdamien
2426145247Sdamien	if (iwi_config(sc) != 0) {
2427145247Sdamien		device_printf(sc->sc_dev, "device configuration failed\n");
2428145247Sdamien		goto fail;
2429145247Sdamien	}
2430145247Sdamien
2431149333Sdamien	if (ic->ic_opmode != IEEE80211_M_MONITOR) {
2432149333Sdamien		if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL)
2433149333Sdamien			ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
2434149333Sdamien	} else
2435146500Sdamien		ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
2436145247Sdamien
2437148887Srwatson	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
2438148887Srwatson	ifp->if_drv_flags |= IFF_DRV_RUNNING;
2439145247Sdamien
2440145247Sdamien	return;
2441145247Sdamien
2442145247Sdamienfail:	ifp->if_flags &= ~IFF_UP;
2443145247Sdamien	iwi_stop(sc);
2444145247Sdamien}
2445145247Sdamien
2446145247Sdamienstatic void
2447145247Sdamieniwi_stop(void *priv)
2448145247Sdamien{
2449145247Sdamien	struct iwi_softc *sc = priv;
2450145247Sdamien	struct ieee80211com *ic = &sc->sc_ic;
2451145247Sdamien	struct ifnet *ifp = ic->ic_ifp;
2452145247Sdamien
2453145247Sdamien	iwi_stop_master(sc);
2454145247Sdamien
2455145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_RST, IWI_RST_SOFT_RESET);
2456145247Sdamien
2457145247Sdamien	/* reset rings */
2458145247Sdamien	iwi_reset_cmd_ring(sc, &sc->cmdq);
2459149338Sdamien	iwi_reset_tx_ring(sc, &sc->txq[0]);
2460149338Sdamien	iwi_reset_tx_ring(sc, &sc->txq[1]);
2461149338Sdamien	iwi_reset_tx_ring(sc, &sc->txq[2]);
2462149338Sdamien	iwi_reset_tx_ring(sc, &sc->txq[3]);
2463145247Sdamien	iwi_reset_rx_ring(sc, &sc->rxq);
2464145247Sdamien
2465145247Sdamien	sc->sc_tx_timer = 0;
2466145247Sdamien	ifp->if_timer = 0;
2467148887Srwatson	ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
2468145247Sdamien
2469145247Sdamien	ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
2470145247Sdamien}
2471145247Sdamien
2472145247Sdamienstatic int
2473145247Sdamieniwi_sysctl_stats(SYSCTL_HANDLER_ARGS)
2474145247Sdamien{
2475145247Sdamien	struct iwi_softc *sc = arg1;
2476145247Sdamien	uint32_t size, buf[128];
2477145247Sdamien
2478145247Sdamien	if (!(sc->flags & IWI_FLAG_FW_INITED)) {
2479145247Sdamien		memset(buf, 0, sizeof buf);
2480145247Sdamien		return SYSCTL_OUT(req, buf, sizeof buf);
2481145247Sdamien	}
2482145247Sdamien
2483145247Sdamien	size = min(CSR_READ_4(sc, IWI_CSR_TABLE0_SIZE), 128 - 1);
2484145247Sdamien	CSR_READ_REGION_4(sc, IWI_CSR_TABLE0_BASE, &buf[1], size);
2485145247Sdamien
2486145247Sdamien	return SYSCTL_OUT(req, buf, sizeof buf);
2487145247Sdamien}
2488145247Sdamien
2489145247Sdamienstatic int
2490145247Sdamieniwi_sysctl_radio(SYSCTL_HANDLER_ARGS)
2491145247Sdamien{
2492145247Sdamien	struct iwi_softc *sc = arg1;
2493145247Sdamien	int val;
2494145247Sdamien
2495145247Sdamien	val = (CSR_READ_4(sc, IWI_CSR_IO) & IWI_IO_RADIO_ENABLED) ? 1 : 0;
2496145247Sdamien
2497145247Sdamien	return SYSCTL_OUT(req, &val, sizeof val);
2498145247Sdamien}
2499