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