if_iwi.c revision 148887
1145247Sdamien/*	$FreeBSD: head/sys/dev/iwi/if_iwi.c 148887 2005-08-09 10:20:02Z rwatson $	*/
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 148887 2005-08-09 10:20:02Z rwatson $");
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 *,
112145247Sdamien		    int);
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);
122145247Sdamienstatic uint16_t	iwi_read_prom_word(struct iwi_softc *, uint8_t);
123145247Sdamienstatic void	iwi_fix_channel(struct ieee80211com *, struct mbuf *);
124145247Sdamienstatic void	iwi_frame_intr(struct iwi_softc *, struct iwi_rx_data *, int,
125145247Sdamien		    struct iwi_frame *);
126145247Sdamienstatic void	iwi_notification_intr(struct iwi_softc *, struct iwi_notif *);
127145247Sdamienstatic void	iwi_rx_intr(struct iwi_softc *);
128145247Sdamienstatic void	iwi_tx_intr(struct iwi_softc *);
129145247Sdamienstatic void	iwi_intr(void *);
130145247Sdamienstatic int	iwi_cmd(struct iwi_softc *, uint8_t, void *, uint8_t, int);
131145247Sdamienstatic int	iwi_tx_start(struct ifnet *, struct mbuf *,
132145247Sdamien		    struct ieee80211_node *);
133145247Sdamienstatic void	iwi_start(struct ifnet *);
134145247Sdamienstatic void	iwi_watchdog(struct ifnet *);
135145247Sdamienstatic int	iwi_ioctl(struct ifnet *, u_long, caddr_t);
136145247Sdamienstatic void	iwi_stop_master(struct iwi_softc *);
137145247Sdamienstatic int	iwi_reset(struct iwi_softc *);
138145247Sdamienstatic int	iwi_load_ucode(struct iwi_softc *, void *, int);
139145247Sdamienstatic int	iwi_load_firmware(struct iwi_softc *, void *, int);
140145247Sdamienstatic int	iwi_cache_firmware(struct iwi_softc *, void *);
141145247Sdamienstatic void	iwi_free_firmware(struct iwi_softc *);
142145247Sdamienstatic int	iwi_config(struct iwi_softc *);
143146500Sdamienstatic int	iwi_set_chan(struct iwi_softc *, struct ieee80211_channel *);
144145247Sdamienstatic int	iwi_scan(struct iwi_softc *);
145145247Sdamienstatic int	iwi_auth_and_assoc(struct iwi_softc *);
146145247Sdamienstatic void	iwi_init(void *);
147145247Sdamienstatic void	iwi_stop(void *);
148145247Sdamien#ifdef IWI_DEBUG
149145247Sdamienstatic int	iwi_sysctl_stats(SYSCTL_HANDLER_ARGS);
150145247Sdamien#endif
151145247Sdamienstatic int	iwi_sysctl_radio(SYSCTL_HANDLER_ARGS);
152145247Sdamien
153145247Sdamienstatic int iwi_probe(device_t);
154145247Sdamienstatic int iwi_attach(device_t);
155145247Sdamienstatic int iwi_detach(device_t);
156145247Sdamienstatic int iwi_shutdown(device_t);
157145247Sdamienstatic int iwi_suspend(device_t);
158145247Sdamienstatic int iwi_resume(device_t);
159145247Sdamien
160145247Sdamienstatic device_method_t iwi_methods[] = {
161145247Sdamien	/* Device interface */
162145247Sdamien	DEVMETHOD(device_probe,		iwi_probe),
163145247Sdamien	DEVMETHOD(device_attach,	iwi_attach),
164145247Sdamien	DEVMETHOD(device_detach,	iwi_detach),
165145247Sdamien	DEVMETHOD(device_shutdown,	iwi_shutdown),
166145247Sdamien	DEVMETHOD(device_suspend,	iwi_suspend),
167145247Sdamien	DEVMETHOD(device_resume,	iwi_resume),
168145247Sdamien
169145247Sdamien	{ 0, 0 }
170145247Sdamien};
171145247Sdamien
172145247Sdamienstatic driver_t iwi_driver = {
173145247Sdamien	"iwi",
174145247Sdamien	iwi_methods,
175145247Sdamien	sizeof (struct iwi_softc)
176145247Sdamien};
177145247Sdamien
178145247Sdamienstatic devclass_t iwi_devclass;
179145247Sdamien
180145247SdamienDRIVER_MODULE(iwi, pci, iwi_driver, iwi_devclass, 0, 0);
181145247Sdamien
182145247Sdamien/*
183145247Sdamien * Supported rates for 802.11a/b/g modes (in 500Kbps unit).
184145247Sdamien */
185145247Sdamienstatic const struct ieee80211_rateset iwi_rateset_11a =
186145247Sdamien	{ 8, { 12, 18, 24, 36, 48, 72, 96, 108 } };
187145247Sdamien
188145247Sdamienstatic const struct ieee80211_rateset iwi_rateset_11b =
189145247Sdamien	{ 4, { 2, 4, 11, 22 } };
190145247Sdamien
191145247Sdamienstatic const struct ieee80211_rateset iwi_rateset_11g =
192145247Sdamien	{ 12, { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 } };
193145247Sdamien
194145247Sdamienstatic __inline uint8_t
195145247SdamienMEM_READ_1(struct iwi_softc *sc, uint32_t addr)
196145247Sdamien{
197145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_INDIRECT_ADDR, addr);
198145247Sdamien	return CSR_READ_1(sc, IWI_CSR_INDIRECT_DATA);
199145247Sdamien}
200145247Sdamien
201145247Sdamienstatic __inline uint32_t
202145247SdamienMEM_READ_4(struct iwi_softc *sc, uint32_t addr)
203145247Sdamien{
204145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_INDIRECT_ADDR, addr);
205145247Sdamien	return CSR_READ_4(sc, IWI_CSR_INDIRECT_DATA);
206145247Sdamien}
207145247Sdamien
208145247Sdamienstatic int
209145247Sdamieniwi_probe(device_t dev)
210145247Sdamien{
211145247Sdamien	const struct iwi_ident *ident;
212145247Sdamien
213145247Sdamien	for (ident = iwi_ident_table; ident->name != NULL; ident++) {
214145247Sdamien		if (pci_get_vendor(dev) == ident->vendor &&
215145247Sdamien		    pci_get_device(dev) == ident->device) {
216145247Sdamien			device_set_desc(dev, ident->name);
217145247Sdamien			return 0;
218145247Sdamien		}
219145247Sdamien	}
220145247Sdamien	return ENXIO;
221145247Sdamien}
222145247Sdamien
223145247Sdamien/* Base Address Register */
224145247Sdamien#define IWI_PCI_BAR0	0x10
225145247Sdamien
226145247Sdamienstatic int
227145247Sdamieniwi_attach(device_t dev)
228145247Sdamien{
229145247Sdamien	struct iwi_softc *sc = device_get_softc(dev);
230147256Sbrooks	struct ifnet *ifp;
231145247Sdamien	struct ieee80211com *ic = &sc->sc_ic;
232145247Sdamien	uint16_t val;
233145247Sdamien	int error, i;
234145247Sdamien
235145247Sdamien	sc->sc_dev = dev;
236145247Sdamien
237145247Sdamien	mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
238145247Sdamien	    MTX_DEF | MTX_RECURSE);
239145247Sdamien
240145247Sdamien	if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) {
241145247Sdamien		device_printf(dev, "chip is in D%d power mode "
242145247Sdamien		    "-- setting to D0\n", pci_get_powerstate(dev));
243145247Sdamien		pci_set_powerstate(dev, PCI_POWERSTATE_D0);
244145247Sdamien	}
245145247Sdamien
246146500Sdamien	pci_write_config(dev, 0x41, 0, 1);
247146500Sdamien
248145247Sdamien	/* enable bus-mastering */
249145247Sdamien	pci_enable_busmaster(dev);
250145247Sdamien
251145247Sdamien	sc->mem_rid = IWI_PCI_BAR0;
252145247Sdamien	sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid,
253145247Sdamien	    RF_ACTIVE);
254145247Sdamien	if (sc->mem == NULL) {
255145247Sdamien		device_printf(dev, "could not allocate memory resource\n");
256145247Sdamien		goto fail;
257145247Sdamien	}
258145247Sdamien
259145247Sdamien	sc->sc_st = rman_get_bustag(sc->mem);
260145247Sdamien	sc->sc_sh = rman_get_bushandle(sc->mem);
261145247Sdamien
262145247Sdamien	sc->irq_rid = 0;
263145247Sdamien	sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid,
264145247Sdamien	    RF_ACTIVE | RF_SHAREABLE);
265145247Sdamien	if (sc->irq == NULL) {
266145247Sdamien		device_printf(dev, "could not allocate interrupt resource\n");
267145247Sdamien		goto fail;
268145247Sdamien	}
269145247Sdamien
270145247Sdamien	if (iwi_reset(sc) != 0) {
271145247Sdamien		device_printf(dev, "could not reset adapter\n");
272145247Sdamien		goto fail;
273145247Sdamien	}
274145247Sdamien
275145247Sdamien	/*
276145247Sdamien	 * Allocate rings.
277145247Sdamien	 */
278145247Sdamien	if (iwi_alloc_cmd_ring(sc, &sc->cmdq, IWI_CMD_RING_COUNT) != 0) {
279145247Sdamien		device_printf(dev, "could not allocate Cmd ring\n");
280145247Sdamien		goto fail;
281145247Sdamien	}
282145247Sdamien
283145247Sdamien	if (iwi_alloc_tx_ring(sc, &sc->txq, IWI_TX_RING_COUNT) != 0) {
284145247Sdamien		device_printf(dev, "could not allocate Tx ring\n");
285145247Sdamien		goto fail;
286145247Sdamien	}
287145247Sdamien
288145247Sdamien	if (iwi_alloc_rx_ring(sc, &sc->rxq, IWI_RX_RING_COUNT) != 0) {
289145247Sdamien		device_printf(dev, "could not allocate Rx ring\n");
290145247Sdamien		goto fail;
291145247Sdamien	}
292145247Sdamien
293147256Sbrooks	ifp = sc->sc_ifp = if_alloc(IFT_ETHER);
294147256Sbrooks	if (ifp == NULL) {
295147256Sbrooks		device_printf(dev, "can not if_alloc()\n");
296147256Sbrooks		goto fail;
297147256Sbrooks		return (ENOSPC);
298147256Sbrooks	}
299145247Sdamien	ifp->if_softc = sc;
300145247Sdamien	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
301145247Sdamien	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
302145247Sdamien	ifp->if_init = iwi_init;
303145247Sdamien	ifp->if_ioctl = iwi_ioctl;
304145247Sdamien	ifp->if_start = iwi_start;
305145247Sdamien	ifp->if_watchdog = iwi_watchdog;
306145247Sdamien	IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
307145247Sdamien	ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN;
308145247Sdamien	IFQ_SET_READY(&ifp->if_snd);
309145247Sdamien
310145247Sdamien	ic->ic_ifp = ifp;
311145247Sdamien	ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
312145247Sdamien	ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */
313145247Sdamien	ic->ic_state = IEEE80211_S_INIT;
314145247Sdamien
315145247Sdamien	/* set device capabilities */
316146500Sdamien	ic->ic_caps = IEEE80211_C_WPA | IEEE80211_C_PMGT | IEEE80211_C_TXPMGT |
317146500Sdamien	    IEEE80211_C_SHPREAMBLE | IEEE80211_C_MONITOR;
318145247Sdamien
319145247Sdamien	/* read MAC address from EEPROM */
320145247Sdamien	val = iwi_read_prom_word(sc, IWI_EEPROM_MAC + 0);
321145247Sdamien	ic->ic_myaddr[0] = val >> 8;
322145247Sdamien	ic->ic_myaddr[1] = val & 0xff;
323145247Sdamien	val = iwi_read_prom_word(sc, IWI_EEPROM_MAC + 1);
324145247Sdamien	ic->ic_myaddr[2] = val >> 8;
325145247Sdamien	ic->ic_myaddr[3] = val & 0xff;
326145247Sdamien	val = iwi_read_prom_word(sc, IWI_EEPROM_MAC + 2);
327145247Sdamien	ic->ic_myaddr[4] = val >> 8;
328145247Sdamien	ic->ic_myaddr[5] = val & 0xff;
329145247Sdamien
330146500Sdamien#if 0
331145247Sdamien	if (pci_get_device(dev) >= 0x4223) {
332145247Sdamien		/* set supported .11a rates (2915ABG only) */
333145247Sdamien		ic->ic_sup_rates[IEEE80211_MODE_11A] = iwi_rateset_11a;
334145247Sdamien
335145247Sdamien		/* set supported .11a channels */
336145247Sdamien		for (i = 36; i <= 64; i += 4) {
337145247Sdamien			ic->ic_channels[i].ic_freq =
338145247Sdamien			    ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ);
339145247Sdamien			ic->ic_channels[i].ic_flags = IEEE80211_CHAN_A;
340145247Sdamien		}
341145247Sdamien		for (i = 149; i <= 165; i += 4) {
342145247Sdamien			ic->ic_channels[i].ic_freq =
343145247Sdamien			    ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ);
344145247Sdamien			ic->ic_channels[i].ic_flags = IEEE80211_CHAN_A;
345145247Sdamien		}
346145247Sdamien	}
347146500Sdamien#endif
348145247Sdamien
349145247Sdamien	/* set supported .11b and .11g rates */
350145247Sdamien	ic->ic_sup_rates[IEEE80211_MODE_11B] = iwi_rateset_11b;
351145247Sdamien	ic->ic_sup_rates[IEEE80211_MODE_11G] = iwi_rateset_11g;
352145247Sdamien
353145247Sdamien	/* set supported .11b and .11g channels (1 through 14) */
354145247Sdamien	for (i = 1; i <= 14; i++) {
355145247Sdamien		ic->ic_channels[i].ic_freq =
356145247Sdamien		    ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ);
357145247Sdamien		ic->ic_channels[i].ic_flags =
358145247Sdamien		    IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM |
359145247Sdamien		    IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ;
360145247Sdamien	}
361145247Sdamien
362145247Sdamien	ieee80211_ifattach(ic);
363145247Sdamien	/* override state transition machine */
364145247Sdamien	sc->sc_newstate = ic->ic_newstate;
365145247Sdamien	ic->ic_newstate = iwi_newstate;
366145247Sdamien	ieee80211_media_init(ic, iwi_media_change, iwi_media_status);
367145247Sdamien
368145247Sdamien	bpfattach2(ifp, DLT_IEEE802_11_RADIO,
369145247Sdamien	    sizeof (struct ieee80211_frame) + 64, &sc->sc_drvbpf);
370145247Sdamien
371145247Sdamien	sc->sc_rxtap_len = sizeof sc->sc_rxtapu;
372145247Sdamien	sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len);
373145247Sdamien	sc->sc_rxtap.wr_ihdr.it_present = htole32(IWI_RX_RADIOTAP_PRESENT);
374145247Sdamien
375145247Sdamien	sc->sc_txtap_len = sizeof sc->sc_txtapu;
376145247Sdamien	sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len);
377145247Sdamien	sc->sc_txtap.wt_ihdr.it_present = htole32(IWI_TX_RADIOTAP_PRESENT);
378145247Sdamien
379145247Sdamien	/*
380145247Sdamien	 * Add a few sysctl knobs.
381145247Sdamien	 */
382145247Sdamien	sc->dwelltime = 100;
383145247Sdamien	sc->bluetooth = 1;
384146500Sdamien	sc->antenna = 0;
385145247Sdamien
386145247Sdamien	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
387145247Sdamien	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "radio",
388145247Sdamien	    CTLTYPE_INT | CTLFLAG_RD, sc, 0, iwi_sysctl_radio, "I",
389145247Sdamien	    "radio transmitter switch state (0=off, 1=on)");
390145247Sdamien
391145247Sdamien#ifdef IWI_DEBUG
392145247Sdamien	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
393145247Sdamien	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "stats",
394145247Sdamien	    CTLTYPE_OPAQUE | CTLFLAG_RD, sc, 0, iwi_sysctl_stats, "S",
395145247Sdamien	    "statistics");
396145247Sdamien#endif
397145247Sdamien
398145247Sdamien	SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
399145247Sdamien	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "dwell",
400145247Sdamien	    CTLFLAG_RW, &sc->dwelltime, 0,
401145247Sdamien	    "channel dwell time (ms) for AP/station scanning");
402145247Sdamien
403145247Sdamien	SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
404145247Sdamien	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "bluetooth",
405145247Sdamien	    CTLFLAG_RW, &sc->bluetooth, 0, "bluetooth coexistence");
406145247Sdamien
407146500Sdamien	SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
408146500Sdamien	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "antenna",
409146500Sdamien	    CTLFLAG_RW, &sc->antenna, 0, "antenna (0=auto)");
410146500Sdamien
411145247Sdamien	/*
412145247Sdamien	 * Hook our interrupt after all initialization is complete.
413145247Sdamien	 */
414145247Sdamien	error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET | INTR_MPSAFE,
415145247Sdamien	    iwi_intr, sc, &sc->sc_ih);
416145247Sdamien	if (error != 0) {
417145247Sdamien		device_printf(dev, "could not set up interrupt\n");
418145247Sdamien		goto fail;
419145247Sdamien	}
420145247Sdamien
421145247Sdamien	if (bootverbose)
422145247Sdamien		ieee80211_announce(ic);
423145247Sdamien
424145247Sdamien	return 0;
425145247Sdamien
426145247Sdamienfail:	iwi_detach(dev);
427145247Sdamien	return ENXIO;
428145247Sdamien}
429145247Sdamien
430145247Sdamienstatic int
431145247Sdamieniwi_detach(device_t dev)
432145247Sdamien{
433145247Sdamien	struct iwi_softc *sc = device_get_softc(dev);
434145247Sdamien	struct ieee80211com *ic = &sc->sc_ic;
435145247Sdamien	struct ifnet *ifp = ic->ic_ifp;
436145247Sdamien
437145247Sdamien	iwi_stop(sc);
438145247Sdamien
439145247Sdamien	iwi_free_firmware(sc);
440145247Sdamien
441147256Sbrooks	if (ifp != NULL)
442147256Sbrooks		bpfdetach(ifp);
443145247Sdamien	ieee80211_ifdetach(ic);
444147256Sbrooks	if (ifp != NULL)
445147256Sbrooks		if_free(ifp);
446145247Sdamien
447145247Sdamien	iwi_free_cmd_ring(sc, &sc->cmdq);
448145247Sdamien	iwi_free_tx_ring(sc, &sc->txq);
449145247Sdamien	iwi_free_rx_ring(sc, &sc->rxq);
450145247Sdamien
451145247Sdamien	if (sc->irq != NULL) {
452145247Sdamien		bus_teardown_intr(dev, sc->irq, sc->sc_ih);
453145247Sdamien		bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq);
454145247Sdamien	}
455145247Sdamien
456145247Sdamien	if (sc->mem != NULL)
457145247Sdamien		bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem);
458145247Sdamien
459145247Sdamien	mtx_destroy(&sc->sc_mtx);
460145247Sdamien
461145247Sdamien	return 0;
462145247Sdamien}
463145247Sdamien
464145247Sdamienstatic void
465145247Sdamieniwi_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nseg, int error)
466145247Sdamien{
467145247Sdamien	if (error != 0)
468145247Sdamien		return;
469145247Sdamien
470145247Sdamien	KASSERT(nseg == 1, ("too many DMA segments, %d should be 1", nseg));
471145247Sdamien
472145247Sdamien	*(bus_addr_t *)arg = segs[0].ds_addr;
473145247Sdamien}
474145247Sdamien
475145247Sdamienstatic int
476145247Sdamieniwi_alloc_cmd_ring(struct iwi_softc *sc, struct iwi_cmd_ring *ring, int count)
477145247Sdamien{
478145247Sdamien	int error;
479145247Sdamien
480145247Sdamien	ring->count = count;
481145247Sdamien	ring->queued = 0;
482145247Sdamien	ring->cur = ring->next = 0;
483145247Sdamien
484145247Sdamien	error = bus_dma_tag_create(NULL, 4, 0, BUS_SPACE_MAXADDR_32BIT,
485145247Sdamien	    BUS_SPACE_MAXADDR, NULL, NULL, count * IWI_CMD_DESC_SIZE, 1,
486145247Sdamien	    count * IWI_CMD_DESC_SIZE, 0, NULL, NULL, &ring->desc_dmat);
487145247Sdamien	if (error != 0) {
488145247Sdamien		device_printf(sc->sc_dev, "could not create desc DMA tag\n");
489145247Sdamien		goto fail;
490145247Sdamien	}
491145247Sdamien
492145247Sdamien	error = bus_dmamem_alloc(ring->desc_dmat, (void **)&ring->desc,
493145247Sdamien	    BUS_DMA_NOWAIT | BUS_DMA_ZERO, &ring->desc_map);
494145247Sdamien	if (error != 0) {
495145247Sdamien		device_printf(sc->sc_dev, "could not allocate DMA memory\n");
496145247Sdamien		goto fail;
497145247Sdamien	}
498145247Sdamien
499145247Sdamien	error = bus_dmamap_load(ring->desc_dmat, ring->desc_map, ring->desc,
500145247Sdamien	    count * IWI_CMD_DESC_SIZE, iwi_dma_map_addr, &ring->physaddr, 0);
501145247Sdamien	if (error != 0) {
502145247Sdamien		device_printf(sc->sc_dev, "could not load desc DMA map\n");
503145247Sdamien		goto fail;
504145247Sdamien	}
505145247Sdamien
506145247Sdamien	return 0;
507145247Sdamien
508145247Sdamienfail:	iwi_free_cmd_ring(sc, ring);
509145247Sdamien	return error;
510145247Sdamien}
511145247Sdamien
512145247Sdamienstatic void
513145247Sdamieniwi_reset_cmd_ring(struct iwi_softc *sc, struct iwi_cmd_ring *ring)
514145247Sdamien{
515145247Sdamien	ring->queued = 0;
516145247Sdamien	ring->cur = ring->next = 0;
517145247Sdamien}
518145247Sdamien
519145247Sdamienstatic void
520145247Sdamieniwi_free_cmd_ring(struct iwi_softc *sc, struct iwi_cmd_ring *ring)
521145247Sdamien{
522145247Sdamien	if (ring->desc != NULL) {
523145247Sdamien		bus_dmamap_sync(ring->desc_dmat, ring->desc_map,
524145247Sdamien		    BUS_DMASYNC_POSTWRITE);
525145247Sdamien		bus_dmamap_unload(ring->desc_dmat, ring->desc_map);
526145247Sdamien		bus_dmamem_free(ring->desc_dmat, ring->desc, ring->desc_map);
527145247Sdamien	}
528145247Sdamien
529145247Sdamien	if (ring->desc_dmat != NULL)
530145247Sdamien		bus_dma_tag_destroy(ring->desc_dmat);
531145247Sdamien}
532145247Sdamien
533145247Sdamienstatic int
534145247Sdamieniwi_alloc_tx_ring(struct iwi_softc *sc, struct iwi_tx_ring *ring, int count)
535145247Sdamien{
536145247Sdamien	int i, error;
537145247Sdamien
538145247Sdamien	ring->count = count;
539145247Sdamien	ring->queued = 0;
540145247Sdamien	ring->cur = ring->next = 0;
541145247Sdamien
542145247Sdamien	error = bus_dma_tag_create(NULL, 4, 0, BUS_SPACE_MAXADDR_32BIT,
543145247Sdamien	    BUS_SPACE_MAXADDR, NULL, NULL, count * IWI_TX_DESC_SIZE, 1,
544145247Sdamien	    count * IWI_TX_DESC_SIZE, 0, NULL, NULL, &ring->desc_dmat);
545145247Sdamien	if (error != 0) {
546145247Sdamien		device_printf(sc->sc_dev, "could not create desc DMA tag\n");
547145247Sdamien		goto fail;
548145247Sdamien	}
549145247Sdamien
550145247Sdamien	error = bus_dmamem_alloc(ring->desc_dmat, (void **)&ring->desc,
551145247Sdamien	    BUS_DMA_NOWAIT | BUS_DMA_ZERO, &ring->desc_map);
552145247Sdamien	if (error != 0) {
553145247Sdamien		device_printf(sc->sc_dev, "could not allocate DMA memory\n");
554145247Sdamien		goto fail;
555145247Sdamien	}
556145247Sdamien
557145247Sdamien	error = bus_dmamap_load(ring->desc_dmat, ring->desc_map, ring->desc,
558145247Sdamien	    count * IWI_TX_DESC_SIZE, iwi_dma_map_addr, &ring->physaddr, 0);
559145247Sdamien	if (error != 0) {
560145247Sdamien		device_printf(sc->sc_dev, "could not load desc DMA map\n");
561145247Sdamien		goto fail;
562145247Sdamien	}
563145247Sdamien
564145247Sdamien	ring->data = malloc(count * sizeof (struct iwi_tx_data), M_DEVBUF,
565145247Sdamien	    M_NOWAIT | M_ZERO);
566145247Sdamien	if (ring->data == NULL) {
567145247Sdamien		device_printf(sc->sc_dev, "could not allocate soft data\n");
568145247Sdamien		error = ENOMEM;
569145247Sdamien		goto fail;
570145247Sdamien	}
571145247Sdamien
572145247Sdamien	error = bus_dma_tag_create(NULL, 1, 0, BUS_SPACE_MAXADDR_32BIT,
573145247Sdamien	    BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, 1, MCLBYTES, 0, NULL,
574145247Sdamien	    NULL, &ring->data_dmat);
575145247Sdamien	if (error != 0) {
576145247Sdamien		device_printf(sc->sc_dev, "could not create data DMA tag\n");
577145247Sdamien		goto fail;
578145247Sdamien	}
579145247Sdamien
580145247Sdamien	for (i = 0; i < count; i++) {
581145247Sdamien		error = bus_dmamap_create(ring->data_dmat, 0,
582145247Sdamien		    &ring->data[i].map);
583145247Sdamien		if (error != 0) {
584145247Sdamien			device_printf(sc->sc_dev, "could not create DMA map\n");
585145247Sdamien			goto fail;
586145247Sdamien		}
587145247Sdamien	}
588145247Sdamien
589145247Sdamien	return 0;
590145247Sdamien
591145247Sdamienfail:	iwi_free_tx_ring(sc, ring);
592145247Sdamien	return error;
593145247Sdamien}
594145247Sdamien
595145247Sdamienstatic void
596145247Sdamieniwi_reset_tx_ring(struct iwi_softc *sc, struct iwi_tx_ring *ring)
597145247Sdamien{
598145247Sdamien	struct iwi_tx_data *data;
599145247Sdamien	int i;
600145247Sdamien
601145247Sdamien	for (i = 0; i < ring->count; i++) {
602145247Sdamien		data = &ring->data[i];
603145247Sdamien
604145247Sdamien		if (data->m != NULL) {
605145247Sdamien			bus_dmamap_sync(ring->data_dmat, data->map,
606145247Sdamien			    BUS_DMASYNC_POSTWRITE);
607145247Sdamien			bus_dmamap_unload(ring->data_dmat, data->map);
608145247Sdamien			m_freem(data->m);
609145247Sdamien			data->m = NULL;
610145247Sdamien		}
611145247Sdamien
612145247Sdamien		if (data->ni != NULL) {
613145247Sdamien			ieee80211_free_node(data->ni);
614145247Sdamien			data->ni = NULL;
615145247Sdamien		}
616145247Sdamien	}
617145247Sdamien
618145247Sdamien	ring->queued = 0;
619145247Sdamien	ring->cur = ring->next = 0;
620145247Sdamien}
621145247Sdamien
622145247Sdamienstatic void
623145247Sdamieniwi_free_tx_ring(struct iwi_softc *sc, struct iwi_tx_ring *ring)
624145247Sdamien{
625145247Sdamien	struct iwi_tx_data *data;
626145247Sdamien	int i;
627145247Sdamien
628145247Sdamien	if (ring->desc != NULL) {
629145247Sdamien		bus_dmamap_sync(ring->desc_dmat, ring->desc_map,
630145247Sdamien		    BUS_DMASYNC_POSTWRITE);
631145247Sdamien		bus_dmamap_unload(ring->desc_dmat, ring->desc_map);
632145247Sdamien		bus_dmamem_free(ring->desc_dmat, ring->desc, ring->desc_map);
633145247Sdamien	}
634145247Sdamien
635145247Sdamien	if (ring->desc_dmat != NULL)
636145247Sdamien		bus_dma_tag_destroy(ring->desc_dmat);
637145247Sdamien
638145247Sdamien	if (ring->data != NULL) {
639145247Sdamien		for (i = 0; i < ring->count; i++) {
640145247Sdamien			data = &ring->data[i];
641145247Sdamien
642145247Sdamien			if (data->m != NULL) {
643145247Sdamien				bus_dmamap_sync(ring->data_dmat, data->map,
644145247Sdamien				    BUS_DMASYNC_POSTWRITE);
645145247Sdamien				bus_dmamap_unload(ring->data_dmat, data->map);
646145247Sdamien				m_freem(data->m);
647145247Sdamien			}
648145247Sdamien
649145247Sdamien			if (data->ni != NULL)
650145247Sdamien				ieee80211_free_node(data->ni);
651145247Sdamien
652145247Sdamien			if (data->map != NULL)
653145247Sdamien				bus_dmamap_destroy(ring->data_dmat, data->map);
654145247Sdamien		}
655145247Sdamien
656145247Sdamien		free(ring->data, M_DEVBUF);
657145247Sdamien	}
658145247Sdamien
659145247Sdamien	if (ring->data_dmat != NULL)
660145247Sdamien		bus_dma_tag_destroy(ring->data_dmat);
661145247Sdamien}
662145247Sdamien
663145247Sdamienstatic int
664145247Sdamieniwi_alloc_rx_ring(struct iwi_softc *sc, struct iwi_rx_ring *ring, int count)
665145247Sdamien{
666145247Sdamien	struct iwi_rx_data *data;
667145247Sdamien	int i, error;
668145247Sdamien
669145247Sdamien	ring->count = count;
670145247Sdamien	ring->cur = 0;
671145247Sdamien
672145247Sdamien	ring->data = malloc(count * sizeof (struct iwi_rx_data), M_DEVBUF,
673145247Sdamien	    M_NOWAIT | M_ZERO);
674145247Sdamien	if (ring->data == NULL) {
675145247Sdamien		device_printf(sc->sc_dev, "could not allocate soft data\n");
676145247Sdamien		error = ENOMEM;
677145247Sdamien		goto fail;
678145247Sdamien	}
679145247Sdamien
680145247Sdamien	error = bus_dma_tag_create(NULL, 1, 0, BUS_SPACE_MAXADDR_32BIT,
681145247Sdamien	    BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, 1, MCLBYTES, 0, NULL,
682145247Sdamien	    NULL, &ring->data_dmat);
683145247Sdamien	if (error != 0) {
684145247Sdamien		device_printf(sc->sc_dev, "could not create data DMA tag\n");
685145247Sdamien		goto fail;
686145247Sdamien	}
687145247Sdamien
688145247Sdamien	for (i = 0; i < count; i++) {
689145247Sdamien		data = &ring->data[i];
690145247Sdamien
691145247Sdamien		error = bus_dmamap_create(ring->data_dmat, 0, &data->map);
692145247Sdamien		if (error != 0) {
693145247Sdamien			device_printf(sc->sc_dev, "could not create DMA map\n");
694145247Sdamien			goto fail;
695145247Sdamien		}
696145247Sdamien
697145247Sdamien		data->m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
698145247Sdamien		if (data->m == NULL) {
699145247Sdamien			device_printf(sc->sc_dev,
700145247Sdamien			    "could not allocate rx mbuf\n");
701145247Sdamien			error = ENOMEM;
702145247Sdamien			goto fail;
703145247Sdamien		}
704145247Sdamien
705145247Sdamien		error = bus_dmamap_load(ring->data_dmat, data->map,
706145247Sdamien		    mtod(data->m, void *), MCLBYTES, iwi_dma_map_addr,
707145247Sdamien		    &data->physaddr, 0);
708145247Sdamien		if (error != 0) {
709145247Sdamien			device_printf(sc->sc_dev,
710145247Sdamien			    "could not load rx buf DMA map");
711145247Sdamien			goto fail;
712145247Sdamien		}
713145247Sdamien
714145247Sdamien		data->reg = IWI_CSR_RX_BASE + i * 4;
715145247Sdamien	}
716145247Sdamien
717145247Sdamien	return 0;
718145247Sdamien
719145247Sdamienfail:	iwi_free_rx_ring(sc, ring);
720145247Sdamien	return error;
721145247Sdamien}
722145247Sdamien
723145247Sdamienstatic void
724145247Sdamieniwi_reset_rx_ring(struct iwi_softc *sc, struct iwi_rx_ring *ring)
725145247Sdamien{
726145247Sdamien	ring->cur = 0;
727145247Sdamien}
728145247Sdamien
729145247Sdamienstatic void
730145247Sdamieniwi_free_rx_ring(struct iwi_softc *sc, struct iwi_rx_ring *ring)
731145247Sdamien{
732145247Sdamien	struct iwi_rx_data *data;
733145247Sdamien	int i;
734145247Sdamien
735145247Sdamien	if (ring->data != NULL) {
736145247Sdamien		for (i = 0; i < ring->count; i++) {
737145247Sdamien			data = &ring->data[i];
738145247Sdamien
739145247Sdamien			if (data->m != NULL) {
740145247Sdamien				bus_dmamap_sync(ring->data_dmat, data->map,
741145247Sdamien				    BUS_DMASYNC_POSTREAD);
742145247Sdamien				bus_dmamap_unload(ring->data_dmat, data->map);
743145247Sdamien				m_freem(data->m);
744145247Sdamien			}
745145247Sdamien
746145247Sdamien			if (data->map != NULL)
747145247Sdamien				bus_dmamap_destroy(ring->data_dmat, data->map);
748145247Sdamien		}
749145247Sdamien
750145247Sdamien		free(ring->data, M_DEVBUF);
751145247Sdamien	}
752145247Sdamien
753145247Sdamien	if (ring->data_dmat != NULL)
754145247Sdamien		bus_dma_tag_destroy(ring->data_dmat);
755145247Sdamien}
756145247Sdamien
757145247Sdamienstatic int
758145247Sdamieniwi_shutdown(device_t dev)
759145247Sdamien{
760145247Sdamien	struct iwi_softc *sc = device_get_softc(dev);
761145247Sdamien
762145247Sdamien	iwi_stop(sc);
763145247Sdamien
764145247Sdamien	return 0;
765145247Sdamien}
766145247Sdamien
767145247Sdamienstatic int
768145247Sdamieniwi_suspend(device_t dev)
769145247Sdamien{
770145247Sdamien	struct iwi_softc *sc = device_get_softc(dev);
771145247Sdamien
772145247Sdamien	iwi_stop(sc);
773145247Sdamien
774145247Sdamien	return 0;
775145247Sdamien}
776145247Sdamien
777145247Sdamienstatic int
778145247Sdamieniwi_resume(device_t dev)
779145247Sdamien{
780145247Sdamien	struct iwi_softc *sc = device_get_softc(dev);
781145247Sdamien	struct ifnet *ifp = sc->sc_ic.ic_ifp;
782145247Sdamien
783145247Sdamien	IWI_LOCK(sc);
784145247Sdamien
785146500Sdamien	pci_write_config(dev, 0x41, 0, 1);
786146500Sdamien
787145247Sdamien	if (ifp->if_flags & IFF_UP) {
788145247Sdamien		ifp->if_init(ifp->if_softc);
789148887Srwatson		if (ifp->if_drv_flags & IFF_DRV_RUNNING)
790145247Sdamien			ifp->if_start(ifp);
791145247Sdamien	}
792145247Sdamien
793145247Sdamien	IWI_UNLOCK(sc);
794145247Sdamien
795145247Sdamien	return 0;
796145247Sdamien}
797145247Sdamien
798145247Sdamienstatic int
799145247Sdamieniwi_media_change(struct ifnet *ifp)
800145247Sdamien{
801145247Sdamien	struct iwi_softc *sc = ifp->if_softc;
802145247Sdamien	int error;
803145247Sdamien
804145247Sdamien	IWI_LOCK(sc);
805145247Sdamien
806145247Sdamien	error = ieee80211_media_change(ifp);
807145247Sdamien	if (error != ENETRESET) {
808145247Sdamien		IWI_UNLOCK(sc);
809145247Sdamien		return error;
810145247Sdamien	}
811145247Sdamien
812148887Srwatson	if ((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING))
813145247Sdamien		iwi_init(sc);
814145247Sdamien
815145247Sdamien	IWI_UNLOCK(sc);
816145247Sdamien
817145247Sdamien	return 0;
818145247Sdamien}
819145247Sdamien
820145247Sdamien/*
821145247Sdamien * The firmware automaticly adapt the transmit speed. We report the current
822145247Sdamien * transmit speed here.
823145247Sdamien */
824145247Sdamienstatic void
825145247Sdamieniwi_media_status(struct ifnet *ifp, struct ifmediareq *imr)
826145247Sdamien{
827145247Sdamien	struct iwi_softc *sc = ifp->if_softc;
828145247Sdamien	struct ieee80211com *ic = &sc->sc_ic;
829145247Sdamien#define N(a)	(sizeof (a) / sizeof (a[0]))
830145247Sdamien	static const struct {
831145247Sdamien		uint32_t	val;
832145247Sdamien		int		rate;
833145247Sdamien	} rates[] = {
834145247Sdamien		{ IWI_RATE_DS1,      2 },
835145247Sdamien		{ IWI_RATE_DS2,      4 },
836145247Sdamien		{ IWI_RATE_DS5,     11 },
837145247Sdamien		{ IWI_RATE_DS11,    22 },
838145247Sdamien		{ IWI_RATE_OFDM6,   12 },
839145247Sdamien		{ IWI_RATE_OFDM9,   18 },
840145247Sdamien		{ IWI_RATE_OFDM12,  24 },
841145247Sdamien		{ IWI_RATE_OFDM18,  36 },
842145247Sdamien		{ IWI_RATE_OFDM24,  48 },
843145247Sdamien		{ IWI_RATE_OFDM36,  72 },
844145247Sdamien		{ IWI_RATE_OFDM48,  96 },
845145247Sdamien		{ IWI_RATE_OFDM54, 108 },
846145247Sdamien	};
847145247Sdamien	uint32_t val;
848145247Sdamien	int rate, i;
849145247Sdamien
850145247Sdamien	imr->ifm_status = IFM_AVALID;
851145247Sdamien	imr->ifm_active = IFM_IEEE80211;
852145247Sdamien	if (ic->ic_state == IEEE80211_S_RUN)
853145247Sdamien		imr->ifm_status |= IFM_ACTIVE;
854145247Sdamien
855145247Sdamien	/* read current transmission rate from adapter */
856145247Sdamien	val = CSR_READ_4(sc, IWI_CSR_CURRENT_TX_RATE);
857145247Sdamien
858145247Sdamien	/* convert rate to 802.11 rate */
859145247Sdamien	for (i = 0; i < N(rates) && rates[i].val != val; i++);
860145247Sdamien	rate = (i < N(rates)) ? rates[i].rate : 0;
861145247Sdamien
862145247Sdamien	imr->ifm_active |= ieee80211_rate2media(ic, rate, ic->ic_curmode);
863145247Sdamien	switch (ic->ic_opmode) {
864145247Sdamien	case IEEE80211_M_STA:
865145247Sdamien		break;
866145247Sdamien
867145247Sdamien	case IEEE80211_M_IBSS:
868145247Sdamien		imr->ifm_active |= IFM_IEEE80211_ADHOC;
869145247Sdamien		break;
870145247Sdamien
871145247Sdamien	case IEEE80211_M_MONITOR:
872145247Sdamien		imr->ifm_active |= IFM_IEEE80211_MONITOR;
873145247Sdamien		break;
874145247Sdamien
875145247Sdamien	case IEEE80211_M_AHDEMO:
876145247Sdamien	case IEEE80211_M_HOSTAP:
877145247Sdamien		/* should not get there */
878145247Sdamien		break;
879145247Sdamien	}
880145247Sdamien#undef N
881145247Sdamien}
882145247Sdamien
883145247Sdamienstatic int
884145247Sdamieniwi_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
885145247Sdamien{
886145247Sdamien	struct ifnet *ifp = ic->ic_ifp;
887145247Sdamien	struct iwi_softc *sc = ifp->if_softc;
888145247Sdamien
889145247Sdamien	switch (nstate) {
890145247Sdamien	case IEEE80211_S_SCAN:
891146500Sdamien		if (sc->flags & IWI_FLAG_SCANNING)
892146500Sdamien			break;
893146500Sdamien
894146500Sdamien		ieee80211_node_table_reset(&ic->ic_scan);
895146500Sdamien		ic->ic_flags |= IEEE80211_F_SCAN | IEEE80211_F_ASCAN;
896146500Sdamien		sc->flags |= IWI_FLAG_SCANNING;
897145247Sdamien		iwi_scan(sc);
898145247Sdamien		break;
899145247Sdamien
900145247Sdamien	case IEEE80211_S_AUTH:
901145247Sdamien		iwi_auth_and_assoc(sc);
902145247Sdamien		break;
903145247Sdamien
904145247Sdamien	case IEEE80211_S_RUN:
905145247Sdamien		if (ic->ic_opmode == IEEE80211_M_IBSS)
906145247Sdamien			ieee80211_new_state(ic, IEEE80211_S_AUTH, -1);
907146500Sdamien		else if (ic->ic_opmode == IEEE80211_M_MONITOR)
908146500Sdamien			iwi_set_chan(sc, ic->ic_ibss_chan);
909146500Sdamien
910146500Sdamien		return sc->sc_newstate(ic, nstate,
911146500Sdamien		    IEEE80211_FC0_SUBTYPE_ASSOC_RESP);
912146500Sdamien
913146500Sdamien	case IEEE80211_S_ASSOC:
914145247Sdamien		break;
915145247Sdamien
916145247Sdamien	case IEEE80211_S_INIT:
917146500Sdamien		sc->flags &= ~IWI_FLAG_SCANNING;
918145247Sdamien		break;
919145247Sdamien	}
920145247Sdamien
921145247Sdamien	ic->ic_state = nstate;
922145247Sdamien	return 0;
923145247Sdamien}
924145247Sdamien
925145247Sdamien/*
926145247Sdamien * Read 16 bits at address 'addr' from the serial EEPROM.
927145247Sdamien */
928145247Sdamienstatic uint16_t
929145247Sdamieniwi_read_prom_word(struct iwi_softc *sc, uint8_t addr)
930145247Sdamien{
931145247Sdamien	uint32_t tmp;
932145247Sdamien	uint16_t val;
933145247Sdamien	int n;
934145247Sdamien
935145247Sdamien	/* clock C once before the first command */
936145247Sdamien	IWI_EEPROM_CTL(sc, 0);
937145247Sdamien	IWI_EEPROM_CTL(sc, IWI_EEPROM_S);
938145247Sdamien	IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_C);
939145247Sdamien	IWI_EEPROM_CTL(sc, IWI_EEPROM_S);
940145247Sdamien
941145247Sdamien	/* write start bit (1) */
942145247Sdamien	IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_D);
943145247Sdamien	IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_D | IWI_EEPROM_C);
944145247Sdamien
945145247Sdamien	/* write READ opcode (10) */
946145247Sdamien	IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_D);
947145247Sdamien	IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_D | IWI_EEPROM_C);
948145247Sdamien	IWI_EEPROM_CTL(sc, IWI_EEPROM_S);
949145247Sdamien	IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_C);
950145247Sdamien
951145247Sdamien	/* write address A7-A0 */
952145247Sdamien	for (n = 7; n >= 0; n--) {
953145247Sdamien		IWI_EEPROM_CTL(sc, IWI_EEPROM_S |
954145247Sdamien		    (((addr >> n) & 1) << IWI_EEPROM_SHIFT_D));
955145247Sdamien		IWI_EEPROM_CTL(sc, IWI_EEPROM_S |
956145247Sdamien		    (((addr >> n) & 1) << IWI_EEPROM_SHIFT_D) | IWI_EEPROM_C);
957145247Sdamien	}
958145247Sdamien
959145247Sdamien	IWI_EEPROM_CTL(sc, IWI_EEPROM_S);
960145247Sdamien
961145247Sdamien	/* read data Q15-Q0 */
962145247Sdamien	val = 0;
963145247Sdamien	for (n = 15; n >= 0; n--) {
964145247Sdamien		IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_C);
965145247Sdamien		IWI_EEPROM_CTL(sc, IWI_EEPROM_S);
966145247Sdamien		tmp = MEM_READ_4(sc, IWI_MEM_EEPROM_CTL);
967145247Sdamien		val |= ((tmp & IWI_EEPROM_Q) >> IWI_EEPROM_SHIFT_Q) << n;
968145247Sdamien	}
969145247Sdamien
970145247Sdamien	IWI_EEPROM_CTL(sc, 0);
971145247Sdamien
972145247Sdamien	/* clear Chip Select and clock C */
973145247Sdamien	IWI_EEPROM_CTL(sc, IWI_EEPROM_S);
974145247Sdamien	IWI_EEPROM_CTL(sc, 0);
975145247Sdamien	IWI_EEPROM_CTL(sc, IWI_EEPROM_C);
976145247Sdamien
977145247Sdamien	return be16toh(val);
978145247Sdamien}
979145247Sdamien
980145247Sdamien/*
981145247Sdamien * XXX: Hack to set the current channel to the value advertised in beacons or
982145247Sdamien * probe responses. Only used during AP detection.
983145247Sdamien */
984145247Sdamienstatic void
985145247Sdamieniwi_fix_channel(struct ieee80211com *ic, struct mbuf *m)
986145247Sdamien{
987145247Sdamien	struct ieee80211_frame *wh;
988145247Sdamien	uint8_t subtype;
989145247Sdamien	uint8_t *frm, *efrm;
990145247Sdamien
991145247Sdamien	wh = mtod(m, struct ieee80211_frame *);
992145247Sdamien
993145247Sdamien	if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_MGT)
994145247Sdamien		return;
995145247Sdamien
996145247Sdamien	subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
997145247Sdamien
998145247Sdamien	if (subtype != IEEE80211_FC0_SUBTYPE_BEACON &&
999145247Sdamien	    subtype != IEEE80211_FC0_SUBTYPE_PROBE_RESP)
1000145247Sdamien		return;
1001145247Sdamien
1002145247Sdamien	frm = (uint8_t *)(wh + 1);
1003145247Sdamien	efrm = mtod(m, uint8_t *) + m->m_len;
1004145247Sdamien
1005145247Sdamien	frm += 12;	/* skip tstamp, bintval and capinfo fields */
1006145247Sdamien	while (frm < efrm) {
1007145247Sdamien		if (*frm == IEEE80211_ELEMID_DSPARMS)
1008145247Sdamien#if IEEE80211_CHAN_MAX < 255
1009145247Sdamien		if (frm[2] <= IEEE80211_CHAN_MAX)
1010145247Sdamien#endif
1011145247Sdamien			ic->ic_bss->ni_chan = &ic->ic_channels[frm[2]];
1012145247Sdamien
1013145247Sdamien		frm += frm[1] + 2;
1014145247Sdamien	}
1015145247Sdamien}
1016145247Sdamien
1017145247Sdamienstatic void
1018145247Sdamieniwi_frame_intr(struct iwi_softc *sc, struct iwi_rx_data *data, int i,
1019145247Sdamien    struct iwi_frame *frame)
1020145247Sdamien{
1021145247Sdamien	struct ieee80211com *ic = &sc->sc_ic;
1022145247Sdamien	struct ifnet *ifp = ic->ic_ifp;
1023145247Sdamien	struct mbuf *m;
1024145247Sdamien	struct ieee80211_frame *wh;
1025145247Sdamien	struct ieee80211_node *ni;
1026145247Sdamien	int error;
1027145247Sdamien
1028145247Sdamien	DPRINTFN(5, ("received frame len=%u chan=%u rssi=%u\n",
1029145247Sdamien	    le16toh(frame->len), frame->chan, frame->rssi_dbm));
1030145247Sdamien
1031146500Sdamien	if (le16toh(frame->len) < sizeof (struct ieee80211_frame))
1032146500Sdamien		return;
1033146500Sdamien
1034145247Sdamien	bus_dmamap_unload(sc->rxq.data_dmat, data->map);
1035145247Sdamien
1036145247Sdamien	/* finalize mbuf */
1037145247Sdamien	m = data->m;
1038145247Sdamien	m->m_pkthdr.rcvif = ifp;
1039145247Sdamien	m->m_pkthdr.len = m->m_len = sizeof (struct iwi_hdr) +
1040145247Sdamien	    sizeof (struct iwi_frame) + le16toh(frame->len);
1041145247Sdamien
1042145247Sdamien	m_adj(m, sizeof (struct iwi_hdr) + sizeof (struct iwi_frame));
1043145247Sdamien
1044145247Sdamien	if (ic->ic_state == IEEE80211_S_SCAN)
1045145247Sdamien		iwi_fix_channel(ic, m);
1046145247Sdamien
1047145247Sdamien	if (sc->sc_drvbpf != NULL) {
1048145247Sdamien		struct iwi_rx_radiotap_header *tap = &sc->sc_rxtap;
1049145247Sdamien
1050145247Sdamien		tap->wr_flags = 0;
1051145247Sdamien		tap->wr_rate = frame->rate;
1052145247Sdamien		tap->wr_chan_freq =
1053145247Sdamien		    htole16(ic->ic_channels[frame->chan].ic_freq);
1054145247Sdamien		tap->wr_chan_flags =
1055145247Sdamien		    htole16(ic->ic_channels[frame->chan].ic_flags);
1056145247Sdamien		tap->wr_antsignal = frame->signal;
1057145247Sdamien		tap->wr_antenna = frame->antenna;
1058145247Sdamien
1059145247Sdamien		bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_rxtap_len, m);
1060145247Sdamien	}
1061145247Sdamien
1062145247Sdamien	wh = mtod(m, struct ieee80211_frame *);
1063145247Sdamien	ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh);
1064145247Sdamien
1065145247Sdamien	/* send the frame to the 802.11 layer */
1066146500Sdamien	ieee80211_input(ic, m, ni, frame->rssi_dbm, 0);
1067145247Sdamien
1068145247Sdamien	/* node is no longer needed */
1069145247Sdamien	ieee80211_free_node(ni);
1070145247Sdamien
1071145247Sdamien	data->m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
1072145247Sdamien	if (data->m == NULL) {
1073145247Sdamien		device_printf(sc->sc_dev, "could not allocate rx mbuf\n");
1074145247Sdamien		return;
1075145247Sdamien	}
1076145247Sdamien
1077145247Sdamien	error = bus_dmamap_load(sc->rxq.data_dmat, data->map,
1078145247Sdamien	    mtod(data->m, void *), MCLBYTES, iwi_dma_map_addr, &data->physaddr,
1079145247Sdamien	    0);
1080145247Sdamien	if (error != 0) {
1081145247Sdamien		device_printf(sc->sc_dev, "could not load rx buf DMA map\n");
1082145247Sdamien		m_freem(data->m);
1083145247Sdamien		data->m = NULL;
1084145247Sdamien		return;
1085145247Sdamien	}
1086145247Sdamien
1087145247Sdamien	CSR_WRITE_4(sc, data->reg, data->physaddr);
1088145247Sdamien}
1089145247Sdamien
1090145247Sdamienstatic void
1091145247Sdamieniwi_notification_intr(struct iwi_softc *sc, struct iwi_notif *notif)
1092145247Sdamien{
1093145247Sdamien	struct ieee80211com *ic = &sc->sc_ic;
1094145247Sdamien	struct iwi_notif_scan_channel *chan;
1095145247Sdamien	struct iwi_notif_scan_complete *scan;
1096145247Sdamien	struct iwi_notif_authentication *auth;
1097145247Sdamien	struct iwi_notif_association *assoc;
1098145247Sdamien
1099145247Sdamien	switch (notif->type) {
1100145247Sdamien	case IWI_NOTIF_TYPE_SCAN_CHANNEL:
1101145247Sdamien		chan = (struct iwi_notif_scan_channel *)(notif + 1);
1102145247Sdamien
1103145247Sdamien		DPRINTFN(2, ("Scanning channel (%u)\n", chan->nchan));
1104145247Sdamien		break;
1105145247Sdamien
1106145247Sdamien	case IWI_NOTIF_TYPE_SCAN_COMPLETE:
1107145247Sdamien		scan = (struct iwi_notif_scan_complete *)(notif + 1);
1108145247Sdamien
1109145247Sdamien		DPRINTFN(2, ("Scan completed (%u, %u)\n", scan->nchan,
1110145247Sdamien		    scan->status));
1111145247Sdamien
1112146500Sdamien		/* monitor mode uses scan to set the channel ... */
1113146500Sdamien		if (ic->ic_opmode != IEEE80211_M_MONITOR) {
1114146500Sdamien			sc->flags &= ~IWI_FLAG_SCANNING;
1115146500Sdamien			ieee80211_end_scan(ic);
1116146500Sdamien		} else
1117146500Sdamien			iwi_set_chan(sc, ic->ic_ibss_chan);
1118145247Sdamien		break;
1119145247Sdamien
1120145247Sdamien	case IWI_NOTIF_TYPE_AUTHENTICATION:
1121145247Sdamien		auth = (struct iwi_notif_authentication *)(notif + 1);
1122145247Sdamien
1123145247Sdamien		DPRINTFN(2, ("Authentication (%u)\n", auth->state));
1124145247Sdamien
1125145247Sdamien		switch (auth->state) {
1126145247Sdamien		case IWI_AUTHENTICATED:
1127148302Ssam			ieee80211_node_authorize(ic->ic_bss);
1128145247Sdamien			ieee80211_new_state(ic, IEEE80211_S_ASSOC, -1);
1129145247Sdamien			break;
1130145247Sdamien
1131145247Sdamien		case IWI_DEAUTHENTICATED:
1132145247Sdamien			break;
1133145247Sdamien
1134145247Sdamien		default:
1135145247Sdamien			device_printf(sc->sc_dev,
1136145247Sdamien			    "unknown authentication state %u\n", auth->state);
1137145247Sdamien		}
1138145247Sdamien		break;
1139145247Sdamien
1140145247Sdamien	case IWI_NOTIF_TYPE_ASSOCIATION:
1141145247Sdamien		assoc = (struct iwi_notif_association *)(notif + 1);
1142145247Sdamien
1143145247Sdamien		DPRINTFN(2, ("Association (%u, %u)\n", assoc->state,
1144145247Sdamien		    assoc->status));
1145145247Sdamien
1146145247Sdamien		switch (assoc->state) {
1147145247Sdamien		case IWI_AUTHENTICATED:
1148145247Sdamien			/* re-association, do nothing */
1149145247Sdamien			break;
1150145247Sdamien
1151145247Sdamien		case IWI_ASSOCIATED:
1152145247Sdamien			ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
1153145247Sdamien			break;
1154145247Sdamien
1155145247Sdamien		case IWI_DEASSOCIATED:
1156145247Sdamien			ieee80211_begin_scan(ic, 1);
1157145247Sdamien			break;
1158145247Sdamien
1159145247Sdamien		default:
1160145247Sdamien			device_printf(sc->sc_dev,
1161145247Sdamien			    "unknown association state %u\n", assoc->state);
1162145247Sdamien		}
1163145247Sdamien		break;
1164145247Sdamien
1165145247Sdamien	case IWI_NOTIF_TYPE_CALIBRATION:
1166145247Sdamien	case IWI_NOTIF_TYPE_BEACON:
1167145247Sdamien	case IWI_NOTIF_TYPE_NOISE:
1168145247Sdamien		DPRINTFN(5, ("Notification (%u)\n", notif->type));
1169145247Sdamien		break;
1170145247Sdamien
1171145247Sdamien	default:
1172145247Sdamien		device_printf(sc->sc_dev, "unknown notification type %u\n",
1173145247Sdamien		    notif->type);
1174145247Sdamien	}
1175145247Sdamien}
1176145247Sdamien
1177145247Sdamienstatic void
1178145247Sdamieniwi_rx_intr(struct iwi_softc *sc)
1179145247Sdamien{
1180145247Sdamien	struct iwi_rx_data *data;
1181145247Sdamien	struct iwi_hdr *hdr;
1182145247Sdamien	uint32_t hw;
1183145247Sdamien
1184145247Sdamien	hw = CSR_READ_4(sc, IWI_CSR_RX_RIDX);
1185145247Sdamien
1186145247Sdamien	for (; sc->rxq.cur != hw;) {
1187145247Sdamien		data = &sc->rxq.data[sc->rxq.cur];
1188145247Sdamien
1189145247Sdamien		bus_dmamap_sync(sc->rxq.data_dmat, data->map,
1190145247Sdamien		    BUS_DMASYNC_POSTREAD);
1191145247Sdamien
1192145247Sdamien		hdr = mtod(data->m, struct iwi_hdr *);
1193145247Sdamien
1194145247Sdamien		switch (hdr->type) {
1195145247Sdamien		case IWI_HDR_TYPE_FRAME:
1196145247Sdamien			iwi_frame_intr(sc, data, sc->rxq.cur,
1197145247Sdamien			    (struct iwi_frame *)(hdr + 1));
1198145247Sdamien			break;
1199145247Sdamien
1200145247Sdamien		case IWI_HDR_TYPE_NOTIF:
1201145247Sdamien			iwi_notification_intr(sc,
1202145247Sdamien			    (struct iwi_notif *)(hdr + 1));
1203145247Sdamien			break;
1204145247Sdamien
1205145247Sdamien		default:
1206145247Sdamien			device_printf(sc->sc_dev, "unknown hdr type %u\n",
1207145247Sdamien			    hdr->type);
1208145247Sdamien		}
1209145247Sdamien
1210145247Sdamien		DPRINTFN(15, ("rx done idx=%u\n", sc->rxq.cur));
1211145247Sdamien
1212145247Sdamien		sc->rxq.cur = (sc->rxq.cur + 1) % IWI_RX_RING_COUNT;
1213145247Sdamien	}
1214145247Sdamien
1215145247Sdamien	/* tell the firmware what we have processed */
1216145247Sdamien	hw = (hw == 0) ? IWI_RX_RING_COUNT - 1 : hw - 1;
1217145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_RX_WIDX, hw);
1218145247Sdamien}
1219145247Sdamien
1220145247Sdamienstatic void
1221145247Sdamieniwi_tx_intr(struct iwi_softc *sc)
1222145247Sdamien{
1223145247Sdamien	struct ieee80211com *ic = &sc->sc_ic;
1224145247Sdamien	struct ifnet *ifp = ic->ic_ifp;
1225145247Sdamien	struct iwi_tx_data *data;
1226145247Sdamien	uint32_t hw;
1227145247Sdamien
1228145247Sdamien	hw = CSR_READ_4(sc, IWI_CSR_TX1_RIDX);
1229145247Sdamien
1230145247Sdamien	for (; sc->txq.next != hw;) {
1231145247Sdamien		data = &sc->txq.data[sc->txq.next];
1232145247Sdamien
1233145247Sdamien		bus_dmamap_sync(sc->txq.data_dmat, data->map,
1234145247Sdamien		    BUS_DMASYNC_POSTWRITE);
1235145247Sdamien		bus_dmamap_unload(sc->txq.data_dmat, data->map);
1236145247Sdamien		m_freem(data->m);
1237145247Sdamien		data->m = NULL;
1238145247Sdamien		ieee80211_free_node(data->ni);
1239145247Sdamien		data->ni = NULL;
1240145247Sdamien
1241145247Sdamien		DPRINTFN(15, ("tx done idx=%u\n", sc->txq.next));
1242145247Sdamien
1243145247Sdamien		ifp->if_opackets++;
1244145247Sdamien
1245145247Sdamien		sc->txq.queued--;
1246145247Sdamien		sc->txq.next = (sc->txq.next + 1) % IWI_TX_RING_COUNT;
1247145247Sdamien	}
1248145247Sdamien
1249145247Sdamien	sc->sc_tx_timer = 0;
1250148887Srwatson	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
1251145247Sdamien	iwi_start(ifp);
1252145247Sdamien}
1253145247Sdamien
1254145247Sdamienstatic void
1255145247Sdamieniwi_intr(void *arg)
1256145247Sdamien{
1257145247Sdamien	struct iwi_softc *sc = arg;
1258145247Sdamien	uint32_t r;
1259145247Sdamien
1260145247Sdamien	IWI_LOCK(sc);
1261145247Sdamien
1262145247Sdamien	if ((r = CSR_READ_4(sc, IWI_CSR_INTR)) == 0 || r == 0xffffffff) {
1263145247Sdamien		IWI_UNLOCK(sc);
1264145247Sdamien		return;
1265145247Sdamien	}
1266145247Sdamien
1267145247Sdamien	/* disable interrupts */
1268145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_INTR_MASK, 0);
1269145247Sdamien
1270145247Sdamien	if (r & (IWI_INTR_FATAL_ERROR | IWI_INTR_PARITY_ERROR)) {
1271145247Sdamien		device_printf(sc->sc_dev, "fatal error\n");
1272145247Sdamien		sc->sc_ic.ic_ifp->if_flags &= ~IFF_UP;
1273145247Sdamien		iwi_stop(sc);
1274145247Sdamien	}
1275145247Sdamien
1276145247Sdamien	if (r & IWI_INTR_FW_INITED) {
1277145247Sdamien		if (!(r & (IWI_INTR_FATAL_ERROR | IWI_INTR_PARITY_ERROR)))
1278145247Sdamien			wakeup(sc);
1279145247Sdamien	}
1280145247Sdamien
1281145247Sdamien	if (r & IWI_INTR_RADIO_OFF) {
1282145247Sdamien		DPRINTF(("radio transmitter turned off\n"));
1283145247Sdamien		sc->sc_ic.ic_ifp->if_flags &= ~IFF_UP;
1284145247Sdamien		iwi_stop(sc);
1285145247Sdamien	}
1286145247Sdamien
1287145247Sdamien	if (r & IWI_INTR_RX_DONE)
1288145247Sdamien		iwi_rx_intr(sc);
1289145247Sdamien
1290145247Sdamien	if (r & IWI_INTR_CMD_DONE)
1291145247Sdamien		wakeup(sc);
1292145247Sdamien
1293145247Sdamien	if (r & IWI_INTR_TX1_DONE)
1294145247Sdamien		iwi_tx_intr(sc);
1295145247Sdamien
1296145247Sdamien	/* acknowledge interrupts */
1297145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_INTR, r);
1298145247Sdamien
1299145247Sdamien	/* re-enable interrupts */
1300145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_INTR_MASK, IWI_INTR_MASK);
1301145247Sdamien
1302145247Sdamien	IWI_UNLOCK(sc);
1303145247Sdamien}
1304145247Sdamien
1305145247Sdamienstatic int
1306145247Sdamieniwi_cmd(struct iwi_softc *sc, uint8_t type, void *data, uint8_t len, int async)
1307145247Sdamien{
1308145247Sdamien	struct iwi_cmd_desc *desc;
1309145247Sdamien
1310145247Sdamien	desc = &sc->cmdq.desc[sc->cmdq.cur];
1311145247Sdamien
1312145247Sdamien	desc->hdr.type = IWI_HDR_TYPE_COMMAND;
1313145247Sdamien	desc->hdr.flags = IWI_HDR_FLAG_IRQ;
1314145247Sdamien	desc->type = type;
1315145247Sdamien	desc->len = len;
1316145247Sdamien	memcpy(desc->data, data, len);
1317145247Sdamien
1318145247Sdamien	bus_dmamap_sync(sc->cmdq.desc_dmat, sc->cmdq.desc_map,
1319145247Sdamien	    BUS_DMASYNC_PREWRITE);
1320145247Sdamien
1321145247Sdamien	DPRINTFN(2, ("sending command idx=%u type=%u len=%u\n", sc->cmdq.cur,
1322145247Sdamien	    type, len));
1323145247Sdamien
1324145247Sdamien	sc->cmdq.cur = (sc->cmdq.cur + 1) % IWI_CMD_RING_COUNT;
1325145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_CMD_WIDX, sc->cmdq.cur);
1326145247Sdamien
1327145247Sdamien	return async ? 0 : msleep(sc, &sc->sc_mtx, 0, "iwicmd", hz);
1328145247Sdamien}
1329145247Sdamien
1330145247Sdamienstatic int
1331145247Sdamieniwi_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni)
1332145247Sdamien{
1333145247Sdamien	struct iwi_softc *sc = ifp->if_softc;
1334145247Sdamien	struct ieee80211com *ic = &sc->sc_ic;
1335147538Ssilby	struct ieee80211_frame wh;
1336146500Sdamien	struct ieee80211_key *k;
1337145247Sdamien	struct iwi_tx_data *data;
1338145247Sdamien	struct iwi_tx_desc *desc;
1339145247Sdamien	struct mbuf *mnew;
1340145247Sdamien	bus_dma_segment_t segs[IWI_MAX_NSEG];
1341145247Sdamien	int nsegs, error, i;
1342145247Sdamien
1343147538Ssilby	bcopy(mtod(m0, struct ieee80211_frame *), &wh, sizeof (struct ieee80211_frame));
1344147538Ssilby	if (wh.i_fc[1] & IEEE80211_FC1_WEP) {
1345146500Sdamien		k = ieee80211_crypto_encap(ic, ni, m0);
1346147806Ssam		if (k == NULL) {
1347147806Ssam			m_freem(m0);
1348146500Sdamien			return ENOBUFS;
1349147806Ssam		}
1350146500Sdamien	}
1351146500Sdamien
1352145247Sdamien	if (sc->sc_drvbpf != NULL) {
1353145247Sdamien		struct iwi_tx_radiotap_header *tap = &sc->sc_txtap;
1354145247Sdamien
1355145247Sdamien		tap->wt_flags = 0;
1356146500Sdamien		tap->wt_chan_freq = htole16(ic->ic_ibss_chan->ic_freq);
1357146500Sdamien		tap->wt_chan_flags = htole16(ic->ic_ibss_chan->ic_flags);
1358145247Sdamien
1359145247Sdamien		bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m0);
1360145247Sdamien	}
1361145247Sdamien
1362145247Sdamien	data = &sc->txq.data[sc->txq.cur];
1363145247Sdamien	desc = &sc->txq.desc[sc->txq.cur];
1364145247Sdamien
1365145247Sdamien	/* trim IEEE802.11 header */
1366145247Sdamien	m_adj(m0, sizeof (struct ieee80211_frame));
1367145247Sdamien
1368145247Sdamien	error = bus_dmamap_load_mbuf_sg(sc->txq.data_dmat, data->map, m0, segs,
1369145247Sdamien	    &nsegs, 0);
1370145247Sdamien	if (error != 0 && error != EFBIG) {
1371145247Sdamien		device_printf(sc->sc_dev, "could not map mbuf (error %d)\n",
1372145247Sdamien		    error);
1373145247Sdamien		m_freem(m0);
1374145247Sdamien		return error;
1375145247Sdamien	}
1376145247Sdamien	if (error != 0) {
1377145247Sdamien		mnew = m_defrag(m0, M_DONTWAIT);
1378145247Sdamien		if (mnew == NULL) {
1379145247Sdamien			device_printf(sc->sc_dev,
1380145247Sdamien			    "could not defragment mbuf\n");
1381145247Sdamien			m_freem(m0);
1382145247Sdamien			return ENOBUFS;
1383145247Sdamien		}
1384145247Sdamien		m0 = mnew;
1385145247Sdamien
1386145247Sdamien		error = bus_dmamap_load_mbuf_sg(sc->txq.data_dmat, data->map,
1387145247Sdamien		    m0, segs, &nsegs, 0);
1388145247Sdamien		if (error != 0) {
1389145247Sdamien			device_printf(sc->sc_dev,
1390145247Sdamien			    "could not map mbuf (error %d)\n", error);
1391145247Sdamien			m_freem(m0);
1392145247Sdamien			return error;
1393145247Sdamien		}
1394145247Sdamien	}
1395145247Sdamien
1396145247Sdamien	data->m = m0;
1397145247Sdamien	data->ni = ni;
1398145247Sdamien
1399145247Sdamien	desc->hdr.type = IWI_HDR_TYPE_DATA;
1400145247Sdamien	desc->hdr.flags = IWI_HDR_FLAG_IRQ;
1401145247Sdamien	desc->cmd = IWI_DATA_CMD_TX;
1402145247Sdamien	desc->len = htole16(m0->m_pkthdr.len);
1403147538Ssilby	memcpy(&desc->wh, &wh, sizeof (struct ieee80211_frame));
1404145247Sdamien	desc->flags = 0;
1405145247Sdamien
1406147538Ssilby	if (!IEEE80211_IS_MULTICAST(wh.i_addr1))
1407145247Sdamien		desc->flags |= IWI_DATA_FLAG_NEED_ACK;
1408145247Sdamien
1409146500Sdamien#if 0
1410145247Sdamien	if (ic->ic_flags & IEEE80211_F_PRIVACY) {
1411147538Ssilby		wh.i_fc[1] |= IEEE80211_FC1_WEP;
1412145247Sdamien		desc->wep_txkey = ic->ic_crypto.cs_def_txkey;
1413145247Sdamien	} else
1414146500Sdamien#endif
1415145247Sdamien		desc->flags |= IWI_DATA_FLAG_NO_WEP;
1416145247Sdamien
1417145247Sdamien	if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
1418145247Sdamien		desc->flags |= IWI_DATA_FLAG_SHPREAMBLE;
1419145247Sdamien
1420145247Sdamien	desc->nseg = htole32(nsegs);
1421145247Sdamien	for (i = 0; i < nsegs; i++) {
1422145247Sdamien		desc->seg_addr[i] = htole32(segs[i].ds_addr);
1423145247Sdamien		desc->seg_len[i]  = htole32(segs[i].ds_len);
1424145247Sdamien	}
1425145247Sdamien
1426145247Sdamien	bus_dmamap_sync(sc->txq.data_dmat, data->map, BUS_DMASYNC_PREWRITE);
1427145247Sdamien	bus_dmamap_sync(sc->txq.desc_dmat, sc->txq.desc_map,
1428145247Sdamien	    BUS_DMASYNC_PREWRITE);
1429145247Sdamien
1430145247Sdamien	DPRINTFN(5, ("sending data frame idx=%u len=%u nseg=%u\n", sc->txq.cur,
1431145247Sdamien	    desc->len, desc->nseg));
1432145247Sdamien
1433145247Sdamien	sc->txq.queued++;
1434145247Sdamien	sc->txq.cur = (sc->txq.cur + 1) % IWI_TX_RING_COUNT;
1435145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_TX1_WIDX, sc->txq.cur);
1436145247Sdamien
1437145247Sdamien	return 0;
1438145247Sdamien}
1439145247Sdamien
1440145247Sdamienstatic void
1441145247Sdamieniwi_start(struct ifnet *ifp)
1442145247Sdamien{
1443145247Sdamien	struct iwi_softc *sc = ifp->if_softc;
1444145247Sdamien	struct ieee80211com *ic = &sc->sc_ic;
1445145247Sdamien	struct mbuf *m0;
1446145247Sdamien	struct ether_header *eh;
1447145247Sdamien	struct ieee80211_node *ni;
1448145247Sdamien
1449145247Sdamien	IWI_LOCK(sc);
1450145247Sdamien
1451145247Sdamien	if (ic->ic_state != IEEE80211_S_RUN) {
1452145247Sdamien		IWI_UNLOCK(sc);
1453145247Sdamien		return;
1454145247Sdamien	}
1455145247Sdamien
1456145247Sdamien	for (;;) {
1457145247Sdamien		IFQ_DRV_DEQUEUE(&ifp->if_snd, m0);
1458145247Sdamien		if (m0 == NULL)
1459145247Sdamien			break;
1460145247Sdamien
1461145247Sdamien		if (sc->txq.queued >= IWI_TX_RING_COUNT - 4) {
1462145247Sdamien			IFQ_DRV_PREPEND(&ifp->if_snd, m0);
1463148887Srwatson			ifp->if_drv_flags |= IFF_DRV_OACTIVE;
1464145247Sdamien			break;
1465145247Sdamien		}
1466145247Sdamien
1467145247Sdamien		if (m0->m_len < sizeof (struct ether_header) &&
1468145247Sdamien		    (m0 = m_pullup(m0, sizeof (struct ether_header))) == NULL)
1469145247Sdamien			continue;
1470145247Sdamien
1471145247Sdamien		eh = mtod(m0, struct ether_header *);
1472145247Sdamien		ni = ieee80211_find_txnode(ic, eh->ether_dhost);
1473145247Sdamien		if (ni == NULL) {
1474145247Sdamien			m_freem(m0);
1475145247Sdamien			continue;
1476145247Sdamien		}
1477145247Sdamien		BPF_MTAP(ifp, m0);
1478145247Sdamien
1479145247Sdamien		m0 = ieee80211_encap(ic, m0, ni);
1480147834Ssam		if (m0 == NULL) {
1481147834Ssam			ieee80211_free_node(ni);
1482145247Sdamien			continue;
1483147834Ssam		}
1484145247Sdamien
1485145247Sdamien		if (ic->ic_rawbpf != NULL)
1486145247Sdamien			bpf_mtap(ic->ic_rawbpf, m0);
1487145247Sdamien
1488145247Sdamien		if (iwi_tx_start(ifp, m0, ni) != 0) {
1489145247Sdamien			ieee80211_free_node(ni);
1490145247Sdamien			ifp->if_oerrors++;
1491145247Sdamien			break;
1492145247Sdamien		}
1493145247Sdamien
1494145247Sdamien		sc->sc_tx_timer = 5;
1495145247Sdamien		ifp->if_timer = 1;
1496145247Sdamien	}
1497145247Sdamien
1498145247Sdamien	IWI_UNLOCK(sc);
1499145247Sdamien}
1500145247Sdamien
1501145247Sdamienstatic void
1502145247Sdamieniwi_watchdog(struct ifnet *ifp)
1503145247Sdamien{
1504145247Sdamien	struct iwi_softc *sc = ifp->if_softc;
1505145247Sdamien	struct ieee80211com *ic = &sc->sc_ic;
1506145247Sdamien
1507145247Sdamien	IWI_LOCK(sc);
1508145247Sdamien
1509145247Sdamien	ifp->if_timer = 0;
1510145247Sdamien
1511145247Sdamien	if (sc->sc_tx_timer > 0) {
1512145247Sdamien		if (--sc->sc_tx_timer == 0) {
1513145247Sdamien			if_printf(ifp, "device timeout\n");
1514145247Sdamien			ifp->if_oerrors++;
1515145247Sdamien			ifp->if_flags &= ~IFF_UP;
1516145247Sdamien			iwi_stop(sc);
1517145247Sdamien			IWI_UNLOCK(sc);
1518145247Sdamien			return;
1519145247Sdamien		}
1520145247Sdamien		ifp->if_timer = 1;
1521145247Sdamien	}
1522145247Sdamien
1523145247Sdamien	ieee80211_watchdog(ic);
1524145247Sdamien
1525145247Sdamien	IWI_UNLOCK(sc);
1526145247Sdamien}
1527145247Sdamien
1528145247Sdamienstatic int
1529145247Sdamieniwi_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
1530145247Sdamien{
1531145247Sdamien	struct iwi_softc *sc = ifp->if_softc;
1532145247Sdamien	struct ieee80211com *ic = &sc->sc_ic;
1533145247Sdamien	struct ifreq *ifr;
1534145247Sdamien	int error = 0;
1535145247Sdamien
1536145247Sdamien	IWI_LOCK(sc);
1537145247Sdamien
1538145247Sdamien	switch (cmd) {
1539145247Sdamien	case SIOCSIFFLAGS:
1540145247Sdamien		if (ifp->if_flags & IFF_UP) {
1541148887Srwatson			if (!(ifp->if_drv_flags & IFF_DRV_RUNNING))
1542145247Sdamien				iwi_init(sc);
1543145247Sdamien		} else {
1544148887Srwatson			if (ifp->if_drv_flags & IFF_DRV_RUNNING)
1545145247Sdamien				iwi_stop(sc);
1546145247Sdamien		}
1547145247Sdamien		break;
1548145247Sdamien
1549145247Sdamien	case SIOCSLOADFW:
1550145247Sdamien		/* only super-user can do that! */
1551145247Sdamien		if ((error = suser(curthread)) != 0)
1552145247Sdamien			break;
1553145247Sdamien
1554145247Sdamien		ifr = (struct ifreq *)data;
1555145247Sdamien		error = iwi_cache_firmware(sc, ifr->ifr_data);
1556145247Sdamien		break;
1557145247Sdamien
1558145247Sdamien	case SIOCSKILLFW:
1559145247Sdamien		/* only super-user can do that! */
1560145247Sdamien		if ((error = suser(curthread)) != 0)
1561145247Sdamien			break;
1562145247Sdamien
1563145247Sdamien		ifp->if_flags &= ~IFF_UP;
1564145247Sdamien		iwi_stop(sc);
1565145247Sdamien		iwi_free_firmware(sc);
1566145247Sdamien		break;
1567145247Sdamien
1568145247Sdamien	default:
1569145247Sdamien		error = ieee80211_ioctl(ic, cmd, data);
1570145247Sdamien	}
1571145247Sdamien
1572145247Sdamien	if (error == ENETRESET) {
1573148887Srwatson		if ((ifp->if_flags & IFF_UP) &&
1574148887Srwatson		    (ifp->if_drv_flags & IFF_DRV_RUNNING))
1575145247Sdamien			iwi_init(sc);
1576145247Sdamien		error = 0;
1577145247Sdamien	}
1578145247Sdamien
1579145247Sdamien	IWI_UNLOCK(sc);
1580145247Sdamien
1581145247Sdamien	return error;
1582145247Sdamien}
1583145247Sdamien
1584145247Sdamienstatic void
1585145247Sdamieniwi_stop_master(struct iwi_softc *sc)
1586145247Sdamien{
1587145247Sdamien	uint32_t tmp;
1588145247Sdamien	int ntries;
1589145247Sdamien
1590145247Sdamien	/* disable interrupts */
1591145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_INTR_MASK, 0);
1592145247Sdamien
1593145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_RST, IWI_RST_STOP_MASTER);
1594145247Sdamien	for (ntries = 0; ntries < 5; ntries++) {
1595145247Sdamien		if (CSR_READ_4(sc, IWI_CSR_RST) & IWI_RST_MASTER_DISABLED)
1596145247Sdamien			break;
1597145247Sdamien		DELAY(10);
1598145247Sdamien	}
1599145247Sdamien	if (ntries == 5)
1600145247Sdamien		device_printf(sc->sc_dev, "timeout waiting for master\n");
1601145247Sdamien
1602145247Sdamien	tmp = CSR_READ_4(sc, IWI_CSR_RST);
1603145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_RST, tmp | IWI_RST_PRINCETON_RESET);
1604145247Sdamien
1605145247Sdamien	sc->flags &= ~IWI_FLAG_FW_INITED;
1606145247Sdamien}
1607145247Sdamien
1608145247Sdamienstatic int
1609145247Sdamieniwi_reset(struct iwi_softc *sc)
1610145247Sdamien{
1611145247Sdamien	uint32_t tmp;
1612145247Sdamien	int i, ntries;
1613145247Sdamien
1614145247Sdamien	iwi_stop_master(sc);
1615145247Sdamien
1616145247Sdamien	tmp = CSR_READ_4(sc, IWI_CSR_CTL);
1617145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_CTL, tmp | IWI_CTL_INIT);
1618145247Sdamien
1619145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_READ_INT, IWI_READ_INT_INIT_HOST);
1620145247Sdamien
1621145247Sdamien	/* wait for clock stabilization */
1622145247Sdamien	for (ntries = 0; ntries < 1000; ntries++) {
1623145247Sdamien		if (CSR_READ_4(sc, IWI_CSR_CTL) & IWI_CTL_CLOCK_READY)
1624145247Sdamien			break;
1625145247Sdamien		DELAY(200);
1626145247Sdamien	}
1627145247Sdamien	if (ntries == 1000) {
1628145247Sdamien		device_printf(sc->sc_dev,
1629145247Sdamien		    "timeout waiting for clock stabilization\n");
1630145247Sdamien		return EIO;
1631145247Sdamien	}
1632145247Sdamien
1633145247Sdamien	tmp = CSR_READ_4(sc, IWI_CSR_RST);
1634145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_RST, tmp | IWI_RST_SOFT_RESET);
1635145247Sdamien
1636145247Sdamien	DELAY(10);
1637145247Sdamien
1638145247Sdamien	tmp = CSR_READ_4(sc, IWI_CSR_CTL);
1639145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_CTL, tmp | IWI_CTL_INIT);
1640145247Sdamien
1641145247Sdamien	/* clear NIC memory */
1642145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_AUTOINC_ADDR, 0);
1643145247Sdamien	for (i = 0; i < 0xc000; i++)
1644145247Sdamien		CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, 0);
1645145247Sdamien
1646145247Sdamien	return 0;
1647145247Sdamien}
1648145247Sdamien
1649145247Sdamienstatic int
1650145247Sdamieniwi_load_ucode(struct iwi_softc *sc, void *uc, int size)
1651145247Sdamien{
1652145247Sdamien	uint32_t tmp;
1653145247Sdamien	uint16_t *w;
1654145247Sdamien	int ntries, i;
1655145247Sdamien
1656145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_RST, CSR_READ_4(sc, IWI_CSR_RST) |
1657145247Sdamien	    IWI_RST_STOP_MASTER);
1658145247Sdamien	for (ntries = 0; ntries < 5; ntries++) {
1659145247Sdamien		if (CSR_READ_4(sc, IWI_CSR_RST) & IWI_RST_MASTER_DISABLED)
1660145247Sdamien			break;
1661145247Sdamien		DELAY(10);
1662145247Sdamien	}
1663145247Sdamien	if (ntries == 5) {
1664145247Sdamien		device_printf(sc->sc_dev, "timeout waiting for master\n");
1665145247Sdamien		return EIO;
1666145247Sdamien	}
1667145247Sdamien
1668145247Sdamien	MEM_WRITE_4(sc, 0x3000e0, 0x80000000);
1669145247Sdamien	DELAY(5000);
1670145247Sdamien
1671145247Sdamien	tmp = CSR_READ_4(sc, IWI_CSR_RST);
1672145247Sdamien	tmp &= ~IWI_RST_PRINCETON_RESET;
1673145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_RST, tmp);
1674145247Sdamien
1675145247Sdamien	DELAY(5000);
1676145247Sdamien	MEM_WRITE_4(sc, 0x3000e0, 0);
1677145247Sdamien	DELAY(1000);
1678145247Sdamien	MEM_WRITE_4(sc, 0x300004, 1);
1679145247Sdamien	DELAY(1000);
1680145247Sdamien	MEM_WRITE_4(sc, 0x300004, 0);
1681145247Sdamien	DELAY(1000);
1682145247Sdamien	MEM_WRITE_1(sc, 0x200000, 0x00);
1683145247Sdamien	MEM_WRITE_1(sc, 0x200000, 0x40);
1684145247Sdamien	DELAY(1000);
1685145247Sdamien
1686145247Sdamien	/* write microcode into adapter memory */
1687145247Sdamien	for (w = uc; size > 0; w++, size -= 2)
1688145247Sdamien		MEM_WRITE_2(sc, 0x200010, *w);
1689145247Sdamien
1690145247Sdamien	MEM_WRITE_1(sc, 0x200000, 0x00);
1691145247Sdamien	MEM_WRITE_1(sc, 0x200000, 0x80);
1692145247Sdamien
1693145247Sdamien	/* wait until we get an answer */
1694145247Sdamien	for (ntries = 0; ntries < 100; ntries++) {
1695145247Sdamien		if (MEM_READ_1(sc, 0x200000) & 1)
1696145247Sdamien			break;
1697145247Sdamien		DELAY(100);
1698145247Sdamien	}
1699145247Sdamien	if (ntries == 100) {
1700145247Sdamien		device_printf(sc->sc_dev,
1701145247Sdamien		    "timeout waiting for ucode to initialize\n");
1702145247Sdamien		return EIO;
1703145247Sdamien	}
1704145247Sdamien
1705145247Sdamien	/* read the answer or the firmware will not initialize properly */
1706145247Sdamien	for (i = 0; i < 7; i++)
1707145247Sdamien		MEM_READ_4(sc, 0x200004);
1708145247Sdamien
1709145247Sdamien	MEM_WRITE_1(sc, 0x200000, 0x00);
1710145247Sdamien
1711145247Sdamien	return 0;
1712145247Sdamien}
1713145247Sdamien
1714145247Sdamien/* macro to handle unaligned little endian data in firmware image */
1715145247Sdamien#define GETLE32(p) ((p)[0] | (p)[1] << 8 | (p)[2] << 16 | (p)[3] << 24)
1716145247Sdamien
1717145247Sdamienstatic int
1718145247Sdamieniwi_load_firmware(struct iwi_softc *sc, void *fw, int size)
1719145247Sdamien{
1720145247Sdamien	bus_dma_tag_t dmat;
1721145247Sdamien	bus_dmamap_t map;
1722145247Sdamien	bus_addr_t physaddr;
1723145247Sdamien	void *virtaddr;
1724145247Sdamien	u_char *p, *end;
1725145247Sdamien	uint32_t sentinel, ctl, src, dst, sum, len, mlen, tmp;
1726145247Sdamien	int ntries, error = 0;
1727145247Sdamien
1728145247Sdamien	/* allocate DMA memory for mapping firmware image */
1729145247Sdamien	error = bus_dma_tag_create(NULL, 4, 0, BUS_SPACE_MAXADDR_32BIT,
1730145247Sdamien	    BUS_SPACE_MAXADDR, NULL, NULL, size, 1, size, 0, NULL, NULL, &dmat);
1731145247Sdamien	if (error != 0) {
1732145247Sdamien		device_printf(sc->sc_dev,
1733145247Sdamien		    "could not create firmware DMA tag\n");
1734145247Sdamien		goto fail1;
1735145247Sdamien	}
1736145247Sdamien
1737145247Sdamien	error = bus_dmamem_alloc(dmat, &virtaddr, BUS_DMA_NOWAIT, &map);
1738145247Sdamien	if (error != 0) {
1739145247Sdamien		device_printf(sc->sc_dev,
1740145247Sdamien		    "could not allocate firmware DMA memory\n");
1741145247Sdamien		goto fail2;
1742145247Sdamien	}
1743145247Sdamien
1744145247Sdamien	error = bus_dmamap_load(dmat, map, virtaddr, size, iwi_dma_map_addr,
1745145247Sdamien	    &physaddr, 0);
1746145247Sdamien	if (error != 0) {
1747145247Sdamien		device_printf(sc->sc_dev, "could not load firmware DMA map\n");
1748145247Sdamien		goto fail3;
1749145247Sdamien	}
1750145247Sdamien
1751145247Sdamien	/* copy firmware image to DMA memory */
1752145247Sdamien	memcpy(virtaddr, fw, size);
1753145247Sdamien
1754145247Sdamien	/* make sure the adapter will get up-to-date values */
1755145247Sdamien	bus_dmamap_sync(dmat, map, BUS_DMASYNC_PREWRITE);
1756145247Sdamien
1757145247Sdamien	/* tell the adapter where the command blocks are stored */
1758145247Sdamien	MEM_WRITE_4(sc, 0x3000a0, 0x27000);
1759145247Sdamien
1760145247Sdamien	/*
1761145247Sdamien	 * Store command blocks into adapter's internal memory using register
1762145247Sdamien	 * indirections. The adapter will read the firmware image through DMA
1763145247Sdamien	 * using information stored in command blocks.
1764145247Sdamien	 */
1765145247Sdamien	src = physaddr;
1766145247Sdamien	p = virtaddr;
1767145247Sdamien	end = p + size;
1768145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_AUTOINC_ADDR, 0x27000);
1769145247Sdamien
1770145247Sdamien	while (p < end) {
1771145247Sdamien		dst = GETLE32(p); p += 4; src += 4;
1772145247Sdamien		len = GETLE32(p); p += 4; src += 4;
1773145247Sdamien		p += len;
1774145247Sdamien
1775145247Sdamien		while (len > 0) {
1776145247Sdamien			mlen = min(len, IWI_CB_MAXDATALEN);
1777145247Sdamien
1778145247Sdamien			ctl = IWI_CB_DEFAULT_CTL | mlen;
1779145247Sdamien			sum = ctl ^ src ^ dst;
1780145247Sdamien
1781145247Sdamien			/* write a command block */
1782145247Sdamien			CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, ctl);
1783145247Sdamien			CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, src);
1784145247Sdamien			CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, dst);
1785145247Sdamien			CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, sum);
1786145247Sdamien
1787145247Sdamien			src += mlen;
1788145247Sdamien			dst += mlen;
1789145247Sdamien			len -= mlen;
1790145247Sdamien		}
1791145247Sdamien	}
1792145247Sdamien
1793145247Sdamien	/* write a fictive final command block (sentinel) */
1794145247Sdamien	sentinel = CSR_READ_4(sc, IWI_CSR_AUTOINC_ADDR);
1795145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, 0);
1796145247Sdamien
1797145247Sdamien	tmp = CSR_READ_4(sc, IWI_CSR_RST);
1798145247Sdamien	tmp &= ~(IWI_RST_MASTER_DISABLED | IWI_RST_STOP_MASTER);
1799145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_RST, tmp);
1800145247Sdamien
1801145247Sdamien	/* tell the adapter to start processing command blocks */
1802145247Sdamien	MEM_WRITE_4(sc, 0x3000a4, 0x540100);
1803145247Sdamien
1804145247Sdamien	/* wait until the adapter reach the sentinel */
1805145247Sdamien	for (ntries = 0; ntries < 400; ntries++) {
1806145247Sdamien		if (MEM_READ_4(sc, 0x3000d0) >= sentinel)
1807145247Sdamien			break;
1808145247Sdamien		DELAY(100);
1809145247Sdamien	}
1810145247Sdamien	if (ntries == 400) {
1811145247Sdamien		device_printf(sc->sc_dev,
1812145247Sdamien		    "timeout processing command blocks\n");
1813145247Sdamien		error = EIO;
1814145247Sdamien		goto fail4;
1815145247Sdamien	}
1816145247Sdamien
1817145247Sdamien	/* we're done with command blocks processing */
1818145247Sdamien	MEM_WRITE_4(sc, 0x3000a4, 0x540c00);
1819145247Sdamien
1820145247Sdamien	/* allow interrupts so we know when the firmware is inited */
1821145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_INTR_MASK, IWI_INTR_MASK);
1822145247Sdamien
1823145247Sdamien	/* tell the adapter to initialize the firmware */
1824145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_RST, 0);
1825145247Sdamien
1826145247Sdamien	tmp = CSR_READ_4(sc, IWI_CSR_CTL);
1827145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_CTL, tmp | IWI_CTL_ALLOW_STANDBY);
1828145247Sdamien
1829145247Sdamien	/* wait at most one second for firmware initialization to complete */
1830145247Sdamien	if ((error = msleep(sc, &sc->sc_mtx, 0, "iwiinit", hz)) != 0) {
1831145247Sdamien		device_printf(sc->sc_dev, "timeout waiting for firmware "
1832145247Sdamien		    "initialization to complete\n");
1833145247Sdamien		goto fail4;
1834145247Sdamien	}
1835145247Sdamien
1836145247Sdamienfail4:	bus_dmamap_sync(dmat, map, BUS_DMASYNC_POSTWRITE);
1837145247Sdamien	bus_dmamap_unload(dmat, map);
1838145247Sdamienfail3:	bus_dmamem_free(dmat, virtaddr, map);
1839145247Sdamienfail2:	bus_dma_tag_destroy(dmat);
1840145247Sdamienfail1:
1841145247Sdamien	return error;
1842145247Sdamien}
1843145247Sdamien
1844145247Sdamien/*
1845145247Sdamien * Store firmware into kernel memory so we can download it when we need to,
1846145247Sdamien * e.g when the adapter wakes up from suspend mode.
1847145247Sdamien */
1848145247Sdamienstatic int
1849145247Sdamieniwi_cache_firmware(struct iwi_softc *sc, void *data)
1850145247Sdamien{
1851145247Sdamien	struct iwi_firmware *kfw = &sc->fw;
1852145247Sdamien	struct iwi_firmware ufw;
1853145247Sdamien	int error;
1854145247Sdamien
1855145247Sdamien	iwi_free_firmware(sc);
1856145247Sdamien
1857145247Sdamien	IWI_UNLOCK(sc);
1858145247Sdamien
1859145247Sdamien	if ((error = copyin(data, &ufw, sizeof ufw)) != 0)
1860145247Sdamien		goto fail1;
1861145247Sdamien
1862145247Sdamien	kfw->boot_size  = ufw.boot_size;
1863145247Sdamien	kfw->ucode_size = ufw.ucode_size;
1864145247Sdamien	kfw->main_size  = ufw.main_size;
1865145247Sdamien
1866145247Sdamien	kfw->boot = malloc(kfw->boot_size, M_DEVBUF, M_NOWAIT);
1867145247Sdamien	if (kfw->boot == NULL) {
1868145247Sdamien		error = ENOMEM;
1869145247Sdamien		goto fail1;
1870145247Sdamien	}
1871145247Sdamien
1872145247Sdamien	kfw->ucode = malloc(kfw->ucode_size, M_DEVBUF, M_NOWAIT);
1873145247Sdamien	if (kfw->ucode == NULL) {
1874145247Sdamien		error = ENOMEM;
1875145247Sdamien		goto fail2;
1876145247Sdamien	}
1877145247Sdamien
1878145247Sdamien	kfw->main = malloc(kfw->main_size, M_DEVBUF, M_NOWAIT);
1879145247Sdamien	if (kfw->main == NULL) {
1880145247Sdamien		error = ENOMEM;
1881145247Sdamien		goto fail3;
1882145247Sdamien	}
1883145247Sdamien
1884145247Sdamien	if ((error = copyin(ufw.boot, kfw->boot, kfw->boot_size)) != 0)
1885145247Sdamien		goto fail4;
1886145247Sdamien
1887145247Sdamien	if ((error = copyin(ufw.ucode, kfw->ucode, kfw->ucode_size)) != 0)
1888145247Sdamien		goto fail4;
1889145247Sdamien
1890145247Sdamien	if ((error = copyin(ufw.main, kfw->main, kfw->main_size)) != 0)
1891145247Sdamien		goto fail4;
1892145247Sdamien
1893145247Sdamien	DPRINTF(("Firmware cached: boot %u, ucode %u, main %u\n",
1894145247Sdamien	    kfw->boot_size, kfw->ucode_size, kfw->main_size));
1895145247Sdamien
1896145247Sdamien	IWI_LOCK(sc);
1897145247Sdamien
1898145247Sdamien	sc->flags |= IWI_FLAG_FW_CACHED;
1899145247Sdamien
1900145247Sdamien	return 0;
1901145247Sdamien
1902145247Sdamienfail4:	free(kfw->boot, M_DEVBUF);
1903145247Sdamienfail3:	free(kfw->ucode, M_DEVBUF);
1904145247Sdamienfail2:	free(kfw->main, M_DEVBUF);
1905145247Sdamienfail1:	IWI_LOCK(sc);
1906145247Sdamien
1907145247Sdamien	return error;
1908145247Sdamien}
1909145247Sdamien
1910145247Sdamienstatic void
1911145247Sdamieniwi_free_firmware(struct iwi_softc *sc)
1912145247Sdamien{
1913145247Sdamien	if (!(sc->flags & IWI_FLAG_FW_CACHED))
1914145247Sdamien		return;
1915145247Sdamien
1916145247Sdamien	free(sc->fw.boot, M_DEVBUF);
1917145247Sdamien	free(sc->fw.ucode, M_DEVBUF);
1918145247Sdamien	free(sc->fw.main, M_DEVBUF);
1919145247Sdamien
1920145247Sdamien	sc->flags &= ~IWI_FLAG_FW_CACHED;
1921145247Sdamien}
1922145247Sdamien
1923145247Sdamienstatic int
1924145247Sdamieniwi_config(struct iwi_softc *sc)
1925145247Sdamien{
1926145247Sdamien	struct ieee80211com *ic = &sc->sc_ic;
1927145247Sdamien	struct ifnet *ifp = ic->ic_ifp;
1928145247Sdamien	struct iwi_configuration config;
1929145247Sdamien	struct iwi_rateset rs;
1930145247Sdamien	struct iwi_txpower power;
1931145247Sdamien	struct ieee80211_key *wk;
1932145247Sdamien	struct iwi_wep_key wepkey;
1933145247Sdamien	uint32_t data;
1934145247Sdamien	int error, i;
1935145247Sdamien
1936145247Sdamien	IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp));
1937145247Sdamien	DPRINTF(("Setting MAC address to %6D\n", ic->ic_myaddr, ":"));
1938145247Sdamien	error = iwi_cmd(sc, IWI_CMD_SET_MAC_ADDRESS, ic->ic_myaddr,
1939145247Sdamien	    IEEE80211_ADDR_LEN, 0);
1940145247Sdamien	if (error != 0)
1941145247Sdamien		return error;
1942145247Sdamien
1943145247Sdamien	memset(&config, 0, sizeof config);
1944145247Sdamien	config.bluetooth_coexistence = sc->bluetooth;
1945146500Sdamien	config.antenna = sc->antenna;
1946145247Sdamien	config.multicast_enabled = 1;
1947146500Sdamien	config.answer_pbreq = (ic->ic_opmode == IEEE80211_M_IBSS) ? 1 : 0;
1948146500Sdamien	config.disable_unicast_decryption = 1;
1949146500Sdamien	config.disable_multicast_decryption = 1;
1950145247Sdamien	DPRINTF(("Configuring adapter\n"));
1951145247Sdamien	error = iwi_cmd(sc, IWI_CMD_SET_CONFIG, &config, sizeof config, 0);
1952145247Sdamien	if (error != 0)
1953145247Sdamien		return error;
1954145247Sdamien
1955145247Sdamien	data = htole32(IWI_POWER_MODE_CAM);
1956145247Sdamien	DPRINTF(("Setting power mode to %u\n", le32toh(data)));
1957145247Sdamien	error = iwi_cmd(sc, IWI_CMD_SET_POWER_MODE, &data, sizeof data, 0);
1958145247Sdamien	if (error != 0)
1959145247Sdamien		return error;
1960145247Sdamien
1961145247Sdamien	data = htole32(ic->ic_rtsthreshold);
1962145247Sdamien	DPRINTF(("Setting RTS threshold to %u\n", le32toh(data)));
1963145247Sdamien	error = iwi_cmd(sc, IWI_CMD_SET_RTS_THRESHOLD, &data, sizeof data, 0);
1964145247Sdamien	if (error != 0)
1965145247Sdamien		return error;
1966145247Sdamien
1967146500Sdamien	data = htole32(ic->ic_fragthreshold);
1968146500Sdamien	DPRINTF(("Setting fragmentation threshold to %u\n", le32toh(data)));
1969146500Sdamien	error = iwi_cmd(sc, IWI_CMD_SET_FRAG_THRESHOLD, &data, sizeof data, 0);
1970146500Sdamien	if (error != 0)
1971146500Sdamien		return error;
1972146500Sdamien
1973145247Sdamien	if (ic->ic_opmode == IEEE80211_M_IBSS) {
1974145247Sdamien		power.mode = IWI_MODE_11B;
1975145247Sdamien		power.nchan = 11;
1976145247Sdamien		for (i = 0; i < 11; i++) {
1977145247Sdamien			power.chan[i].chan = i + 1;
1978145247Sdamien			power.chan[i].power = IWI_TXPOWER_MAX;
1979145247Sdamien		}
1980145247Sdamien		DPRINTF(("Setting .11b channels tx power\n"));
1981145247Sdamien		error = iwi_cmd(sc, IWI_CMD_SET_TX_POWER, &power, sizeof power,
1982145247Sdamien		    0);
1983145247Sdamien		if (error != 0)
1984145247Sdamien			return error;
1985145247Sdamien
1986145247Sdamien		power.mode = IWI_MODE_11G;
1987145247Sdamien		DPRINTF(("Setting .11g channels tx power\n"));
1988145247Sdamien		error = iwi_cmd(sc, IWI_CMD_SET_TX_POWER, &power, sizeof power,
1989145247Sdamien		    0);
1990145247Sdamien		if (error != 0)
1991145247Sdamien			return error;
1992145247Sdamien	}
1993145247Sdamien
1994145247Sdamien	rs.mode = IWI_MODE_11G;
1995145247Sdamien	rs.type = IWI_RATESET_TYPE_SUPPORTED;
1996145247Sdamien	rs.nrates = ic->ic_sup_rates[IEEE80211_MODE_11G].rs_nrates;
1997145247Sdamien	memcpy(rs.rates, ic->ic_sup_rates[IEEE80211_MODE_11G].rs_rates,
1998145247Sdamien	    rs.nrates);
1999145247Sdamien	DPRINTF(("Setting .11bg supported rates (%u)\n", rs.nrates));
2000145247Sdamien	error = iwi_cmd(sc, IWI_CMD_SET_RATES, &rs, sizeof rs, 0);
2001145247Sdamien	if (error != 0)
2002145247Sdamien		return error;
2003145247Sdamien
2004145247Sdamien	rs.mode = IWI_MODE_11A;
2005145247Sdamien	rs.type = IWI_RATESET_TYPE_SUPPORTED;
2006145247Sdamien	rs.nrates = ic->ic_sup_rates[IEEE80211_MODE_11A].rs_nrates;
2007145247Sdamien	memcpy(rs.rates, ic->ic_sup_rates[IEEE80211_MODE_11A].rs_rates,
2008145247Sdamien	    rs.nrates);
2009145247Sdamien	DPRINTF(("Setting .11a supported rates (%u)\n", rs.nrates));
2010145247Sdamien	error = iwi_cmd(sc, IWI_CMD_SET_RATES, &rs, sizeof rs, 0);
2011145247Sdamien	if (error != 0)
2012145247Sdamien		return error;
2013145247Sdamien
2014145247Sdamien	data = htole32(arc4random());
2015145247Sdamien	DPRINTF(("Setting initialization vector to %u\n", le32toh(data)));
2016145247Sdamien	error = iwi_cmd(sc, IWI_CMD_SET_IV, &data, sizeof data, 0);
2017145247Sdamien	if (error != 0)
2018145247Sdamien		return error;
2019145247Sdamien
2020145247Sdamien	for (i = 0; i < IEEE80211_WEP_NKID; i++) {
2021145247Sdamien		wk = &ic->ic_crypto.cs_nw_keys[i];
2022145247Sdamien
2023145247Sdamien		wepkey.cmd = IWI_WEP_KEY_CMD_SETKEY;
2024145247Sdamien		wepkey.idx = i;
2025145247Sdamien		wepkey.len = wk->wk_keylen;
2026145247Sdamien		memset(wepkey.key, 0, sizeof wepkey.key);
2027145247Sdamien		memcpy(wepkey.key, wk->wk_key, wk->wk_keylen);
2028145247Sdamien		DPRINTF(("Setting wep key index %u len %u\n", wepkey.idx,
2029145247Sdamien		    wepkey.len));
2030145247Sdamien		error = iwi_cmd(sc, IWI_CMD_SET_WEP_KEY, &wepkey,
2031145247Sdamien		    sizeof wepkey, 0);
2032145247Sdamien		if (error != 0)
2033145247Sdamien			return error;
2034145247Sdamien	}
2035145247Sdamien
2036145247Sdamien	/* enable adapter */
2037145247Sdamien	DPRINTF(("Enabling adapter\n"));
2038145247Sdamien	return iwi_cmd(sc, IWI_CMD_ENABLE, NULL, 0, 0);
2039145247Sdamien}
2040145247Sdamien
2041145247Sdamienstatic int
2042146500Sdamieniwi_set_chan(struct iwi_softc *sc, struct ieee80211_channel *chan)
2043146500Sdamien{
2044146500Sdamien	struct ieee80211com *ic = &sc->sc_ic;
2045146500Sdamien	struct iwi_scan scan;
2046146500Sdamien
2047146500Sdamien	memset(&scan, 0, sizeof scan);
2048146500Sdamien	scan.type = IWI_SCAN_TYPE_PASSIVE;
2049146500Sdamien	scan.dwelltime = htole16(2000);
2050146500Sdamien	scan.channels[0] = 1 | (IEEE80211_IS_CHAN_5GHZ(chan) ? IWI_CHAN_5GHZ :
2051146500Sdamien	    IWI_CHAN_2GHZ);
2052146500Sdamien	scan.channels[1] = ieee80211_chan2ieee(ic, chan);
2053146500Sdamien
2054146500Sdamien	DPRINTF(("Setting channel to %u\n", ieee80211_chan2ieee(ic, chan)));
2055146500Sdamien	return iwi_cmd(sc, IWI_CMD_SCAN, &scan, sizeof scan, 1);
2056146500Sdamien}
2057146500Sdamien
2058146500Sdamienstatic int
2059145247Sdamieniwi_scan(struct iwi_softc *sc)
2060145247Sdamien{
2061145247Sdamien	struct ieee80211com *ic = &sc->sc_ic;
2062145247Sdamien	struct iwi_scan scan;
2063145247Sdamien	uint8_t *p;
2064145247Sdamien	int i, count;
2065145247Sdamien
2066145247Sdamien	memset(&scan, 0, sizeof scan);
2067145247Sdamien	scan.type = IWI_SCAN_TYPE_BROADCAST;
2068146500Sdamien	scan.dwelltime = htole16(sc->dwelltime);
2069145247Sdamien
2070145247Sdamien	p = scan.channels;
2071145247Sdamien	count = 0;
2072145247Sdamien	for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
2073145247Sdamien		if (IEEE80211_IS_CHAN_5GHZ(&ic->ic_channels[i]) &&
2074145247Sdamien		    isset(ic->ic_chan_active, i)) {
2075145247Sdamien			*++p = i;
2076145247Sdamien			count++;
2077145247Sdamien		}
2078145247Sdamien	}
2079145247Sdamien	*(p - count) = IWI_CHAN_5GHZ | count;
2080145247Sdamien
2081145247Sdamien	count = 0;
2082145247Sdamien	for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
2083145247Sdamien		if (IEEE80211_IS_CHAN_2GHZ(&ic->ic_channels[i]) &&
2084145247Sdamien		    isset(ic->ic_chan_active, i)) {
2085145247Sdamien			*++p = i;
2086145247Sdamien			count++;
2087145247Sdamien		}
2088145247Sdamien	}
2089145247Sdamien	*(p - count) = IWI_CHAN_2GHZ | count;
2090145247Sdamien
2091145247Sdamien	DPRINTF(("Start scanning\n"));
2092145247Sdamien	return iwi_cmd(sc, IWI_CMD_SCAN, &scan, sizeof scan, 1);
2093145247Sdamien}
2094145247Sdamien
2095145247Sdamienstatic int
2096145247Sdamieniwi_auth_and_assoc(struct iwi_softc *sc)
2097145247Sdamien{
2098145247Sdamien	struct ieee80211com *ic = &sc->sc_ic;
2099145247Sdamien	struct ifnet *ifp = ic->ic_ifp;
2100145247Sdamien	struct ieee80211_node *ni = ic->ic_bss;
2101145247Sdamien	struct iwi_configuration config;
2102145247Sdamien	struct iwi_associate assoc;
2103145247Sdamien	struct iwi_rateset rs;
2104146500Sdamien	uint16_t capinfo;
2105145247Sdamien	uint32_t data;
2106145247Sdamien	int error;
2107145247Sdamien
2108145247Sdamien	if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) {
2109145247Sdamien		memset(&config, 0, sizeof config);
2110145247Sdamien		config.bluetooth_coexistence = sc->bluetooth;
2111146500Sdamien		config.antenna = sc->antenna;
2112145247Sdamien		config.multicast_enabled = 1;
2113145247Sdamien		config.use_protection = 1;
2114146500Sdamien		config.answer_pbreq =
2115146500Sdamien		    (ic->ic_opmode == IEEE80211_M_IBSS) ? 1 : 0;
2116146500Sdamien		config.disable_unicast_decryption = 1;
2117146500Sdamien		config.disable_multicast_decryption = 1;
2118145247Sdamien		DPRINTF(("Configuring adapter\n"));
2119145247Sdamien		error = iwi_cmd(sc, IWI_CMD_SET_CONFIG, &config, sizeof config,
2120145247Sdamien		    1);
2121145247Sdamien		if (error != 0)
2122145247Sdamien			return error;
2123145247Sdamien	}
2124145247Sdamien
2125145247Sdamien#ifdef IWI_DEBUG
2126145247Sdamien	if (iwi_debug > 0) {
2127145247Sdamien		printf("Setting ESSID to ");
2128145247Sdamien		ieee80211_print_essid(ni->ni_essid, ni->ni_esslen);
2129145247Sdamien		printf("\n");
2130145247Sdamien	}
2131145247Sdamien#endif
2132145247Sdamien	error = iwi_cmd(sc, IWI_CMD_SET_ESSID, ni->ni_essid, ni->ni_esslen, 1);
2133145247Sdamien	if (error != 0)
2134145247Sdamien		return error;
2135145247Sdamien
2136145247Sdamien	/* the rate set has already been "negociated" */
2137145247Sdamien	rs.mode = IEEE80211_IS_CHAN_5GHZ(ni->ni_chan) ? IWI_MODE_11A :
2138145247Sdamien	    IWI_MODE_11G;
2139145247Sdamien	rs.type = IWI_RATESET_TYPE_NEGOCIATED;
2140145247Sdamien	rs.nrates = ni->ni_rates.rs_nrates;
2141145247Sdamien	memcpy(rs.rates, ni->ni_rates.rs_rates, rs.nrates);
2142145247Sdamien	DPRINTF(("Setting negociated rates (%u)\n", rs.nrates));
2143145247Sdamien	error = iwi_cmd(sc, IWI_CMD_SET_RATES, &rs, sizeof rs, 1);
2144145247Sdamien	if (error != 0)
2145145247Sdamien		return error;
2146145247Sdamien
2147146500Sdamien	if (ic->ic_opt_ie != NULL) {
2148146500Sdamien		DPRINTF(("Setting optional IE (len=%u)\n", ic->ic_opt_ie_len));
2149146500Sdamien		error = iwi_cmd(sc, IWI_CMD_SET_OPTIE, ic->ic_opt_ie,
2150146500Sdamien		    ic->ic_opt_ie_len, 1);
2151146500Sdamien		if (error != 0)
2152146500Sdamien			return error;
2153146500Sdamien	}
2154146500Sdamien
2155145247Sdamien	data = htole32(ni->ni_rssi);
2156145247Sdamien	DPRINTF(("Setting sensitivity to %d\n", (int8_t)ni->ni_rssi));
2157145247Sdamien	error = iwi_cmd(sc, IWI_CMD_SET_SENSITIVITY, &data, sizeof data, 1);
2158145247Sdamien	if (error != 0)
2159145247Sdamien		return error;
2160145247Sdamien
2161145247Sdamien	memset(&assoc, 0, sizeof assoc);
2162145247Sdamien	assoc.mode = IEEE80211_IS_CHAN_5GHZ(ni->ni_chan) ? IWI_MODE_11A :
2163145247Sdamien	    IWI_MODE_11G;
2164145247Sdamien	assoc.chan = ieee80211_chan2ieee(ic, ni->ni_chan);
2165145247Sdamien	if (ni->ni_authmode == IEEE80211_AUTH_SHARED)
2166145247Sdamien		assoc.auth = ic->ic_crypto.cs_def_txkey << 4 | IWI_AUTH_SHARED;
2167146500Sdamien	if (ic->ic_opt_ie != NULL)
2168146500Sdamien		assoc.policy |= htole16(IWI_POLICY_OPTIE);
2169145247Sdamien	memcpy(assoc.tstamp, ni->ni_tstamp.data, 8);
2170146500Sdamien
2171146500Sdamien	if (ic->ic_opmode == IEEE80211_M_IBSS)
2172146500Sdamien		capinfo = IEEE80211_CAPINFO_IBSS;
2173146500Sdamien	else
2174146500Sdamien		capinfo = IEEE80211_CAPINFO_ESS;
2175146500Sdamien	if (ic->ic_flags & IEEE80211_F_PRIVACY)
2176146500Sdamien		capinfo |= IEEE80211_CAPINFO_PRIVACY;
2177146500Sdamien	if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
2178146500Sdamien	    IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
2179146500Sdamien		capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
2180146500Sdamien	if (ic->ic_flags & IEEE80211_F_SHSLOT)
2181146500Sdamien		capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
2182146500Sdamien	assoc.capinfo = htole16(capinfo);
2183146500Sdamien
2184145247Sdamien	assoc.lintval = htole16(ic->ic_lintval);
2185145247Sdamien	assoc.intval = htole16(ni->ni_intval);
2186145247Sdamien	IEEE80211_ADDR_COPY(assoc.bssid, ni->ni_bssid);
2187145247Sdamien	if (ic->ic_opmode == IEEE80211_M_IBSS)
2188145247Sdamien		IEEE80211_ADDR_COPY(assoc.dst, ifp->if_broadcastaddr);
2189145247Sdamien	else
2190145247Sdamien		IEEE80211_ADDR_COPY(assoc.dst, ni->ni_bssid);
2191145247Sdamien
2192145247Sdamien	DPRINTF(("Trying to associate to %6D channel %u auth %u\n",
2193145247Sdamien	    assoc.bssid, ":", assoc.chan, assoc.auth));
2194145247Sdamien	return iwi_cmd(sc, IWI_CMD_ASSOCIATE, &assoc, sizeof assoc, 1);
2195145247Sdamien}
2196145247Sdamien
2197145247Sdamienstatic void
2198145247Sdamieniwi_init(void *priv)
2199145247Sdamien{
2200145247Sdamien	struct iwi_softc *sc = priv;
2201145247Sdamien	struct ieee80211com *ic = &sc->sc_ic;
2202145247Sdamien	struct ifnet *ifp = ic->ic_ifp;
2203145247Sdamien	struct iwi_firmware *fw = &sc->fw;
2204145247Sdamien	struct iwi_rx_data *data;
2205145247Sdamien	int i;
2206145247Sdamien
2207145247Sdamien	/* exit immediately if firmware has not been ioctl'd */
2208145247Sdamien	if (!(sc->flags & IWI_FLAG_FW_CACHED)) {
2209146247Simp		if (!(sc->flags & IWI_FLAG_FW_WARNED))
2210146247Simp			device_printf(sc->sc_dev, "Please load firmware\n");
2211146247Simp		sc->flags |= IWI_FLAG_FW_WARNED;
2212145247Sdamien		ifp->if_flags &= ~IFF_UP;
2213145247Sdamien		return;
2214145247Sdamien	}
2215145247Sdamien
2216145247Sdamien	iwi_stop(sc);
2217145247Sdamien
2218145247Sdamien	if (iwi_reset(sc) != 0) {
2219145247Sdamien		device_printf(sc->sc_dev, "could not reset adapter\n");
2220145247Sdamien		goto fail;
2221145247Sdamien	}
2222145247Sdamien
2223145247Sdamien	if (iwi_load_firmware(sc, fw->boot, fw->boot_size) != 0) {
2224145247Sdamien		device_printf(sc->sc_dev, "could not load boot firmware\n");
2225145247Sdamien		goto fail;
2226145247Sdamien	}
2227145247Sdamien
2228145247Sdamien	if (iwi_load_ucode(sc, fw->ucode, fw->ucode_size) != 0) {
2229145247Sdamien		device_printf(sc->sc_dev, "could not load microcode\n");
2230145247Sdamien		goto fail;
2231145247Sdamien	}
2232145247Sdamien
2233145247Sdamien	iwi_stop_master(sc);
2234145247Sdamien
2235145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_CMD_BASE, sc->cmdq.physaddr);
2236145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_CMD_SIZE, sc->cmdq.count);
2237145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_CMD_WIDX, sc->cmdq.cur);
2238145247Sdamien
2239145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_TX1_BASE, sc->txq.physaddr);
2240145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_TX1_SIZE, sc->txq.count);
2241145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_TX1_WIDX, sc->txq.cur);
2242145247Sdamien
2243145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_TX2_BASE, sc->txq.physaddr);
2244145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_TX2_SIZE, sc->txq.count);
2245145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_TX2_WIDX, sc->txq.cur);
2246145247Sdamien
2247145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_TX3_BASE, sc->txq.physaddr);
2248145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_TX3_SIZE, sc->txq.count);
2249145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_TX3_WIDX, sc->txq.cur);
2250145247Sdamien
2251145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_TX4_BASE, sc->txq.physaddr);
2252145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_TX4_SIZE, sc->txq.count);
2253145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_TX4_WIDX, sc->txq.cur);
2254145247Sdamien
2255145247Sdamien	for (i = 0; i < sc->rxq.count; i++) {
2256145247Sdamien		data = &sc->rxq.data[i];
2257145247Sdamien		CSR_WRITE_4(sc, data->reg, data->physaddr);
2258145247Sdamien	}
2259145247Sdamien
2260145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_RX_WIDX, sc->rxq.count - 1);
2261145247Sdamien
2262145247Sdamien	if (iwi_load_firmware(sc, fw->main, fw->main_size) != 0) {
2263145247Sdamien		device_printf(sc->sc_dev, "could not load main firmware\n");
2264145247Sdamien		goto fail;
2265145247Sdamien	}
2266145247Sdamien
2267145247Sdamien	sc->flags |= IWI_FLAG_FW_INITED;
2268145247Sdamien
2269145247Sdamien	if (iwi_config(sc) != 0) {
2270145247Sdamien		device_printf(sc->sc_dev, "device configuration failed\n");
2271145247Sdamien		goto fail;
2272145247Sdamien	}
2273145247Sdamien
2274146500Sdamien	if (ic->ic_opmode == IEEE80211_M_MONITOR)
2275146500Sdamien		ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
2276146500Sdamien	else
2277146500Sdamien		ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
2278145247Sdamien
2279148887Srwatson	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
2280148887Srwatson	ifp->if_drv_flags |= IFF_DRV_RUNNING;
2281145247Sdamien
2282145247Sdamien	return;
2283145247Sdamien
2284145247Sdamienfail:	ifp->if_flags &= ~IFF_UP;
2285145247Sdamien	iwi_stop(sc);
2286145247Sdamien}
2287145247Sdamien
2288145247Sdamienstatic void
2289145247Sdamieniwi_stop(void *priv)
2290145247Sdamien{
2291145247Sdamien	struct iwi_softc *sc = priv;
2292145247Sdamien	struct ieee80211com *ic = &sc->sc_ic;
2293145247Sdamien	struct ifnet *ifp = ic->ic_ifp;
2294145247Sdamien
2295145247Sdamien	iwi_stop_master(sc);
2296145247Sdamien
2297145247Sdamien	CSR_WRITE_4(sc, IWI_CSR_RST, IWI_RST_SOFT_RESET);
2298145247Sdamien
2299145247Sdamien	/* reset rings */
2300145247Sdamien	iwi_reset_cmd_ring(sc, &sc->cmdq);
2301145247Sdamien	iwi_reset_tx_ring(sc, &sc->txq);
2302145247Sdamien	iwi_reset_rx_ring(sc, &sc->rxq);
2303145247Sdamien
2304145247Sdamien	sc->sc_tx_timer = 0;
2305145247Sdamien	ifp->if_timer = 0;
2306148887Srwatson	ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
2307145247Sdamien
2308145247Sdamien	ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
2309145247Sdamien}
2310145247Sdamien
2311145247Sdamien#ifdef IWI_DEBUG
2312145247Sdamienstatic int
2313145247Sdamieniwi_sysctl_stats(SYSCTL_HANDLER_ARGS)
2314145247Sdamien{
2315145247Sdamien	struct iwi_softc *sc = arg1;
2316145247Sdamien	uint32_t size, buf[128];
2317145247Sdamien
2318145247Sdamien	if (!(sc->flags & IWI_FLAG_FW_INITED)) {
2319145247Sdamien		memset(buf, 0, sizeof buf);
2320145247Sdamien		return SYSCTL_OUT(req, buf, sizeof buf);
2321145247Sdamien	}
2322145247Sdamien
2323145247Sdamien	size = min(CSR_READ_4(sc, IWI_CSR_TABLE0_SIZE), 128 - 1);
2324145247Sdamien	CSR_READ_REGION_4(sc, IWI_CSR_TABLE0_BASE, &buf[1], size);
2325145247Sdamien
2326145247Sdamien	return SYSCTL_OUT(req, buf, sizeof buf);
2327145247Sdamien}
2328145247Sdamien#endif
2329145247Sdamien
2330145247Sdamienstatic int
2331145247Sdamieniwi_sysctl_radio(SYSCTL_HANDLER_ARGS)
2332145247Sdamien{
2333145247Sdamien	struct iwi_softc *sc = arg1;
2334145247Sdamien	int val;
2335145247Sdamien
2336145247Sdamien	val = (CSR_READ_4(sc, IWI_CSR_IO) & IWI_IO_RADIO_ENABLED) ? 1 : 0;
2337145247Sdamien
2338145247Sdamien	return SYSCTL_OUT(req, &val, sizeof val);
2339145247Sdamien}
2340