Commit | Line | Data |
---|---|---|
0e474d75 | 1 | /*- |
b50e4759 | 2 | * Copyright (c) 2004, 2005 |
0e474d75 JH |
3 | * Damien Bergamini <damien.bergamini@free.fr>. All rights reserved. |
4 | * Copyright (c) 2005-2006 Sam Leffler, Errno Consulting | |
5 | * Copyright (c) 2007 Andrew Thompson <thompsa@FreeBSD.org> | |
b50e4759 MD |
6 | * |
7 | * Redistribution and use in source and binary forms, with or without | |
8 | * modification, are permitted provided that the following conditions | |
9 | * are met: | |
10 | * 1. Redistributions of source code must retain the above copyright | |
11 | * notice unmodified, this list of conditions, and the following | |
12 | * disclaimer. | |
13 | * 2. Redistributions in binary form must reproduce the above copyright | |
14 | * notice, this list of conditions and the following disclaimer in the | |
15 | * documentation and/or other materials provided with the distribution. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |
18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |
21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
27 | * SUCH DAMAGE. | |
28 | * | |
c4fe7bb1 | 29 | * $FreeBSD: head/sys/dev/iwi/if_iwi.c 298818 2016-04-29 22:14:11Z avos $ |
b50e4759 MD |
30 | */ |
31 | ||
b50e4759 | 32 | /*- |
841ab66c | 33 | * Intel(R) PRO/Wireless 2200BG/2225BG/2915ABG driver |
b50e4759 MD |
34 | * http://www.intel.com/network/connectivity/products/wireless/prowireless_mobile.htm |
35 | */ | |
36 | ||
37 | #include <sys/param.h> | |
38 | #include <sys/sysctl.h> | |
39 | #include <sys/sockio.h> | |
40 | #include <sys/mbuf.h> | |
41 | #include <sys/kernel.h> | |
b50e4759 MD |
42 | #include <sys/socket.h> |
43 | #include <sys/systm.h> | |
44 | #include <sys/malloc.h> | |
0e474d75 | 45 | #include <sys/lock.h> |
b50e4759 | 46 | #include <sys/module.h> |
0e474d75 | 47 | #include <sys/bus.h> |
b50e4759 MD |
48 | #include <sys/endian.h> |
49 | #include <sys/proc.h> | |
0e474d75 | 50 | #include <sys/mount.h> |
0e474d75 JH |
51 | #include <sys/linker.h> |
52 | #include <sys/firmware.h> | |
53 | #include <sys/taskqueue.h> | |
c4fe7bb1 | 54 | #if defined(__DragonFly__) |
0e474d75 | 55 | #include <sys/devfs.h> |
c4fe7bb1 | 56 | #endif |
0e474d75 | 57 | |
c4fe7bb1 IV |
58 | #if !defined(__DragonFly__) |
59 | #include <machine/bus.h> | |
60 | #include <machine/resource.h> | |
61 | #endif | |
b50e4759 MD |
62 | #include <sys/rman.h> |
63 | ||
c4fe7bb1 | 64 | #if defined(__DragonFly__) |
b50e4759 | 65 | #include <bus/pci/pcivar.h> |
c4fe7bb1 IV |
66 | #include <bus/pci/pcireg.h> |
67 | #else | |
68 | #include <dev/pci/pcireg.h> | |
69 | #include <dev/pci/pcivar.h> | |
70 | #endif | |
b50e4759 MD |
71 | |
72 | #include <net/bpf.h> | |
73 | #include <net/if.h> | |
c4fe7bb1 | 74 | #include <net/if_var.h> |
b50e4759 | 75 | #include <net/if_arp.h> |
b50e4759 MD |
76 | #include <net/ethernet.h> |
77 | #include <net/if_dl.h> | |
78 | #include <net/if_media.h> | |
79 | #include <net/if_types.h> | |
b50e4759 | 80 | |
c4fe7bb1 | 81 | #if defined(__DragonFly__) |
841ab66c SZ |
82 | #include <netproto/802_11/ieee80211_var.h> |
83 | #include <netproto/802_11/ieee80211_radiotap.h> | |
0e474d75 JH |
84 | #include <netproto/802_11/ieee80211_input.h> |
85 | #include <netproto/802_11/ieee80211_regdomain.h> | |
c4fe7bb1 IV |
86 | #else |
87 | #include <net80211/ieee80211_var.h> | |
88 | #include <net80211/ieee80211_radiotap.h> | |
89 | #include <net80211/ieee80211_input.h> | |
90 | #include <net80211/ieee80211_regdomain.h> | |
91 | #endif | |
841ab66c | 92 | |
b50e4759 MD |
93 | #include <netinet/in.h> |
94 | #include <netinet/in_systm.h> | |
95 | #include <netinet/in_var.h> | |
96 | #include <netinet/ip.h> | |
97 | #include <netinet/if_ether.h> | |
98 | ||
c4fe7bb1 IV |
99 | #if defined(__DragonFly__) |
100 | #include "if_iwireg.h" | |
101 | #include "if_iwivar.h" | |
102 | #else | |
103 | #include <dev/iwi/if_iwireg.h> | |
104 | #include <dev/iwi/if_iwivar.h> | |
105 | #endif | |
b50e4759 | 106 | |
0e474d75 | 107 | #define IWI_DEBUG |
b50e4759 | 108 | #ifdef IWI_DEBUG |
e3869ec7 SW |
109 | #define DPRINTF(x) do { if (iwi_debug > 0) kprintf x; } while (0) |
110 | #define DPRINTFN(n, x) do { if (iwi_debug >= (n)) kprintf x; } while (0) | |
841ab66c SZ |
111 | int iwi_debug = 0; |
112 | SYSCTL_INT(_debug, OID_AUTO, iwi, CTLFLAG_RW, &iwi_debug, 0, "iwi debug level"); | |
0e474d75 JH |
113 | |
114 | static const char *iwi_fw_states[] = { | |
115 | "IDLE", /* IWI_FW_IDLE */ | |
116 | "LOADING", /* IWI_FW_LOADING */ | |
117 | "ASSOCIATING", /* IWI_FW_ASSOCIATING */ | |
118 | "DISASSOCIATING", /* IWI_FW_DISASSOCIATING */ | |
119 | "SCANNING", /* IWI_FW_SCANNING */ | |
120 | }; | |
b50e4759 MD |
121 | #else |
122 | #define DPRINTF(x) | |
123 | #define DPRINTFN(n, x) | |
124 | #endif | |
125 | ||
0e474d75 JH |
126 | MODULE_DEPEND(iwi, pci, 1, 1, 1); |
127 | MODULE_DEPEND(iwi, wlan, 1, 1, 1); | |
128 | MODULE_DEPEND(iwi, firmware, 1, 1, 1); | |
129 | ||
130 | enum { | |
131 | IWI_LED_TX, | |
132 | IWI_LED_RX, | |
133 | IWI_LED_POLL, | |
134 | }; | |
135 | ||
136 | struct iwi_ident { | |
841ab66c SZ |
137 | uint16_t vendor; |
138 | uint16_t device; | |
b50e4759 | 139 | const char *name; |
0e474d75 JH |
140 | }; |
141 | ||
142 | static const struct iwi_ident iwi_ident_table[] = { | |
143 | { 0x8086, 0x4220, "Intel(R) PRO/Wireless 2200BG" }, | |
144 | { 0x8086, 0x4221, "Intel(R) PRO/Wireless 2225BG" }, | |
145 | { 0x8086, 0x4223, "Intel(R) PRO/Wireless 2915ABG" }, | |
146 | { 0x8086, 0x4224, "Intel(R) PRO/Wireless 2915ABG" }, | |
147 | ||
b50e4759 MD |
148 | { 0, 0, NULL } |
149 | }; | |
150 | ||
0e474d75 | 151 | static struct ieee80211vap *iwi_vap_create(struct ieee80211com *, |
c4fe7bb1 IV |
152 | const char [IFNAMSIZ], int, enum ieee80211_opmode, int, |
153 | const uint8_t [IEEE80211_ADDR_LEN], | |
154 | const uint8_t [IEEE80211_ADDR_LEN]); | |
0e474d75 | 155 | static void iwi_vap_delete(struct ieee80211vap *); |
841ab66c | 156 | static void iwi_dma_map_addr(void *, bus_dma_segment_t *, int, int); |
841ab66c SZ |
157 | static int iwi_alloc_cmd_ring(struct iwi_softc *, struct iwi_cmd_ring *, |
158 | int); | |
159 | static void iwi_reset_cmd_ring(struct iwi_softc *, struct iwi_cmd_ring *); | |
160 | static void iwi_free_cmd_ring(struct iwi_softc *, struct iwi_cmd_ring *); | |
161 | static int iwi_alloc_tx_ring(struct iwi_softc *, struct iwi_tx_ring *, | |
162 | int, bus_addr_t, bus_addr_t); | |
163 | static void iwi_reset_tx_ring(struct iwi_softc *, struct iwi_tx_ring *); | |
164 | static void iwi_free_tx_ring(struct iwi_softc *, struct iwi_tx_ring *); | |
165 | static int iwi_alloc_rx_ring(struct iwi_softc *, struct iwi_rx_ring *, | |
166 | int); | |
167 | static void iwi_reset_rx_ring(struct iwi_softc *, struct iwi_rx_ring *); | |
168 | static void iwi_free_rx_ring(struct iwi_softc *, struct iwi_rx_ring *); | |
0e474d75 JH |
169 | static struct ieee80211_node *iwi_node_alloc(struct ieee80211vap *, |
170 | const uint8_t [IEEE80211_ADDR_LEN]); | |
841ab66c | 171 | static void iwi_node_free(struct ieee80211_node *); |
841ab66c | 172 | static void iwi_media_status(struct ifnet *, struct ifmediareq *); |
0e474d75 JH |
173 | static int iwi_newstate(struct ieee80211vap *, enum ieee80211_state, int); |
174 | static void iwi_wme_init(struct iwi_softc *); | |
c4fe7bb1 | 175 | static int iwi_wme_setparams(struct iwi_softc *); |
841ab66c SZ |
176 | static int iwi_wme_update(struct ieee80211com *); |
177 | static uint16_t iwi_read_prom_word(struct iwi_softc *, uint8_t); | |
841ab66c SZ |
178 | static void iwi_frame_intr(struct iwi_softc *, struct iwi_rx_data *, int, |
179 | struct iwi_frame *); | |
180 | static void iwi_notification_intr(struct iwi_softc *, struct iwi_notif *); | |
181 | static void iwi_rx_intr(struct iwi_softc *); | |
182 | static void iwi_tx_intr(struct iwi_softc *, struct iwi_tx_ring *); | |
183 | static void iwi_intr(void *); | |
0e474d75 JH |
184 | static int iwi_cmd(struct iwi_softc *, uint8_t, void *, uint8_t); |
185 | static void iwi_write_ibssnode(struct iwi_softc *, const u_int8_t [], int); | |
c4fe7bb1 | 186 | static int iwi_tx_start(struct iwi_softc *, struct mbuf *, |
841ab66c | 187 | struct ieee80211_node *, int); |
0e474d75 JH |
188 | static int iwi_raw_xmit(struct ieee80211_node *, struct mbuf *, |
189 | const struct ieee80211_bpf_params *); | |
c4fe7bb1 IV |
190 | static void iwi_start(struct iwi_softc *); |
191 | static int iwi_transmit(struct ieee80211com *, struct mbuf *); | |
0e474d75 | 192 | static void iwi_watchdog(void *); |
c4fe7bb1 | 193 | static void iwi_parent(struct ieee80211com *); |
841ab66c SZ |
194 | static void iwi_stop_master(struct iwi_softc *); |
195 | static int iwi_reset(struct iwi_softc *); | |
0e474d75 JH |
196 | static int iwi_load_ucode(struct iwi_softc *, const struct iwi_fw *); |
197 | static int iwi_load_firmware(struct iwi_softc *, const struct iwi_fw *); | |
198 | static void iwi_release_fw_dma(struct iwi_softc *sc); | |
841ab66c | 199 | static int iwi_config(struct iwi_softc *); |
0e474d75 JH |
200 | static int iwi_get_firmware(struct iwi_softc *, enum ieee80211_opmode); |
201 | static void iwi_put_firmware(struct iwi_softc *); | |
c4fe7bb1 | 202 | static void iwi_monitor_scan(void *, int); |
0e474d75 JH |
203 | static int iwi_scanchan(struct iwi_softc *, unsigned long, int); |
204 | static void iwi_scan_start(struct ieee80211com *); | |
205 | static void iwi_scan_end(struct ieee80211com *); | |
206 | static void iwi_set_channel(struct ieee80211com *); | |
207 | static void iwi_scan_curchan(struct ieee80211_scan_state *, unsigned long maxdwell); | |
208 | static void iwi_scan_mindwell(struct ieee80211_scan_state *); | |
209 | static int iwi_auth_and_assoc(struct iwi_softc *, struct ieee80211vap *); | |
c4fe7bb1 | 210 | static void iwi_disassoc(void *, int); |
0e474d75 JH |
211 | static int iwi_disassociate(struct iwi_softc *, int quiet); |
212 | static void iwi_init_locked(struct iwi_softc *); | |
841ab66c | 213 | static void iwi_init(void *); |
0e474d75 JH |
214 | static int iwi_init_fw_dma(struct iwi_softc *, int); |
215 | static void iwi_stop_locked(void *); | |
216 | static void iwi_stop(struct iwi_softc *); | |
c4fe7bb1 | 217 | static void iwi_restart(void *, int); |
0e474d75 | 218 | static int iwi_getrfkill(struct iwi_softc *); |
c4fe7bb1 IV |
219 | static void iwi_radio_on(void *, int); |
220 | static void iwi_radio_off(void *, int); | |
0e474d75 JH |
221 | static void iwi_sysctlattach(struct iwi_softc *); |
222 | static void iwi_led_event(struct iwi_softc *, int); | |
223 | static void iwi_ledattach(struct iwi_softc *); | |
224 | ||
225 | static int iwi_probe(device_t); | |
226 | static int iwi_attach(device_t); | |
227 | static int iwi_detach(device_t); | |
228 | static int iwi_shutdown(device_t); | |
229 | static int iwi_suspend(device_t); | |
230 | static int iwi_resume(device_t); | |
b50e4759 MD |
231 | |
232 | static device_method_t iwi_methods[] = { | |
233 | /* Device interface */ | |
234 | DEVMETHOD(device_probe, iwi_probe), | |
235 | DEVMETHOD(device_attach, iwi_attach), | |
236 | DEVMETHOD(device_detach, iwi_detach), | |
237 | DEVMETHOD(device_shutdown, iwi_shutdown), | |
238 | DEVMETHOD(device_suspend, iwi_suspend), | |
239 | DEVMETHOD(device_resume, iwi_resume), | |
240 | ||
d3c9c58e | 241 | DEVMETHOD_END |
b50e4759 MD |
242 | }; |
243 | ||
244 | static driver_t iwi_driver = { | |
245 | "iwi", | |
246 | iwi_methods, | |
841ab66c | 247 | sizeof (struct iwi_softc) |
b50e4759 MD |
248 | }; |
249 | ||
250 | static devclass_t iwi_devclass; | |
251 | ||
aa2b9d05 | 252 | DRIVER_MODULE(iwi, pci, iwi_driver, iwi_devclass, NULL, NULL); |
b50e4759 | 253 | |
c4fe7bb1 IV |
254 | MODULE_VERSION(iwi, 1); |
255 | ||
0e474d75 JH |
256 | static __inline uint8_t |
257 | MEM_READ_1(struct iwi_softc *sc, uint32_t addr) | |
3660cb22 | 258 | { |
0e474d75 JH |
259 | CSR_WRITE_4(sc, IWI_CSR_INDIRECT_ADDR, addr); |
260 | return CSR_READ_1(sc, IWI_CSR_INDIRECT_DATA); | |
261 | } | |
3660cb22 | 262 | |
0e474d75 JH |
263 | static __inline uint32_t |
264 | MEM_READ_4(struct iwi_softc *sc, uint32_t addr) | |
265 | { | |
266 | CSR_WRITE_4(sc, IWI_CSR_INDIRECT_ADDR, addr); | |
267 | return CSR_READ_4(sc, IWI_CSR_INDIRECT_DATA); | |
3660cb22 SZ |
268 | } |
269 | ||
b50e4759 MD |
270 | static int |
271 | iwi_probe(device_t dev) | |
272 | { | |
273 | const struct iwi_ident *ident; | |
274 | ||
275 | for (ident = iwi_ident_table; ident->name != NULL; ident++) { | |
0e474d75 JH |
276 | if (pci_get_vendor(dev) == ident->vendor && |
277 | pci_get_device(dev) == ident->device) { | |
b50e4759 | 278 | device_set_desc(dev, ident->name); |
c4fe7bb1 | 279 | return (BUS_PROBE_DEFAULT); |
b50e4759 MD |
280 | } |
281 | } | |
282 | return ENXIO; | |
283 | } | |
284 | ||
b50e4759 MD |
285 | static int |
286 | iwi_attach(device_t dev) | |
287 | { | |
288 | struct iwi_softc *sc = device_get_softc(dev); | |
c4fe7bb1 | 289 | struct ieee80211com *ic = &sc->sc_ic; |
841ab66c | 290 | uint16_t val; |
c4fe7bb1 | 291 | uint8_t bands[IEEE80211_MODE_BYTES]; |
0e474d75 | 292 | int i, error; |
ef634573 | 293 | |
b50e4759 | 294 | sc->sc_dev = dev; |
0e474d75 | 295 | |
c4fe7bb1 IV |
296 | IWI_LOCK_INIT(sc); |
297 | mbufq_init(&sc->sc_snd, ifqmaxlen); | |
0e474d75 | 298 | |
c4fe7bb1 | 299 | #if defined(__DragonFly__) |
0e474d75 | 300 | devfs_clone_bitmap_init(&sc->sc_unr); |
c4fe7bb1 IV |
301 | #else |
302 | sc->sc_unr = new_unrhdr(1, IWI_MAX_IBSSNODE-1, &sc->sc_mtx); | |
303 | #endif | |
0e474d75 | 304 | |
c4fe7bb1 IV |
305 | TASK_INIT(&sc->sc_radiontask, 0, iwi_radio_on, sc); |
306 | TASK_INIT(&sc->sc_radiofftask, 0, iwi_radio_off, sc); | |
307 | TASK_INIT(&sc->sc_restarttask, 0, iwi_restart, sc); | |
308 | TASK_INIT(&sc->sc_disassoctask, 0, iwi_disassoc, sc); | |
309 | TASK_INIT(&sc->sc_monitortask, 0, iwi_monitor_scan, sc); | |
b50e4759 | 310 | |
c4fe7bb1 IV |
311 | #if defined(__DragonFly__) |
312 | callout_init_lk(&sc->sc_wdtimer, &sc->sc_lock); | |
313 | callout_init_lk(&sc->sc_rftimer, &sc->sc_lock); | |
314 | #else | |
315 | callout_init_mtx(&sc->sc_wdtimer, &sc->sc_mtx, 0); | |
316 | callout_init_mtx(&sc->sc_rftimer, &sc->sc_mtx, 0); | |
317 | #endif | |
b50e4759 MD |
318 | |
319 | pci_write_config(dev, 0x41, 0, 1); | |
320 | ||
321 | /* enable bus-mastering */ | |
322 | pci_enable_busmaster(dev); | |
323 | ||
c4fe7bb1 IV |
324 | i = PCIR_BAR(0); |
325 | sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &i, RF_ACTIVE); | |
b50e4759 MD |
326 | if (sc->mem == NULL) { |
327 | device_printf(dev, "could not allocate memory resource\n"); | |
0e474d75 | 328 | goto fail; |
b50e4759 | 329 | } |
0e474d75 | 330 | |
b50e4759 MD |
331 | sc->sc_st = rman_get_bustag(sc->mem); |
332 | sc->sc_sh = rman_get_bushandle(sc->mem); | |
333 | ||
c4fe7bb1 IV |
334 | i = 0; |
335 | sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &i, | |
0e474d75 | 336 | RF_ACTIVE | RF_SHAREABLE); |
b50e4759 MD |
337 | if (sc->irq == NULL) { |
338 | device_printf(dev, "could not allocate interrupt resource\n"); | |
339 | goto fail; | |
340 | } | |
341 | ||
841ab66c | 342 | if (iwi_reset(sc) != 0) { |
b50e4759 MD |
343 | device_printf(dev, "could not reset adapter\n"); |
344 | goto fail; | |
345 | } | |
346 | ||
841ab66c SZ |
347 | /* |
348 | * Allocate rings. | |
349 | */ | |
0e474d75 | 350 | if (iwi_alloc_cmd_ring(sc, &sc->cmdq, IWI_CMD_RING_COUNT) != 0) { |
841ab66c SZ |
351 | device_printf(dev, "could not allocate Cmd ring\n"); |
352 | goto fail; | |
353 | } | |
354 | ||
0e474d75 JH |
355 | for (i = 0; i < 4; i++) { |
356 | error = iwi_alloc_tx_ring(sc, &sc->txq[i], IWI_TX_RING_COUNT, | |
357 | IWI_CSR_TX1_RIDX + i * 4, | |
358 | IWI_CSR_TX1_WIDX + i * 4); | |
359 | if (error != 0) { | |
360 | device_printf(dev, "could not allocate Tx ring %d\n", | |
361 | i+i); | |
362 | goto fail; | |
363 | } | |
841ab66c SZ |
364 | } |
365 | ||
0e474d75 | 366 | if (iwi_alloc_rx_ring(sc, &sc->rxq, IWI_RX_RING_COUNT) != 0) { |
841ab66c SZ |
367 | device_printf(dev, "could not allocate Rx ring\n"); |
368 | goto fail; | |
369 | } | |
370 | ||
0e474d75 | 371 | iwi_wme_init(sc); |
841ab66c | 372 | |
13f98998 | 373 | ic->ic_softc = sc; |
4f898719 | 374 | ic->ic_name = device_get_nameunit(dev); |
0e474d75 | 375 | ic->ic_opmode = IEEE80211_M_STA; |
841ab66c | 376 | ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ |
b50e4759 MD |
377 | |
378 | /* set device capabilities */ | |
841ab66c | 379 | ic->ic_caps = |
0e474d75 JH |
380 | IEEE80211_C_STA /* station mode supported */ |
381 | | IEEE80211_C_IBSS /* IBSS mode supported */ | |
382 | | IEEE80211_C_MONITOR /* monitor mode supported */ | |
383 | | IEEE80211_C_PMGT /* power save supported */ | |
384 | | IEEE80211_C_SHPREAMBLE /* short preamble supported */ | |
385 | | IEEE80211_C_WPA /* 802.11i */ | |
386 | | IEEE80211_C_WME /* 802.11e */ | |
387 | #if 0 | |
388 | | IEEE80211_C_BGSCAN /* capable of bg scanning */ | |
389 | #endif | |
390 | ; | |
b50e4759 MD |
391 | |
392 | /* read MAC address from EEPROM */ | |
393 | val = iwi_read_prom_word(sc, IWI_EEPROM_MAC + 0); | |
c4fe7bb1 IV |
394 | ic->ic_macaddr[0] = val & 0xff; |
395 | ic->ic_macaddr[1] = val >> 8; | |
b50e4759 | 396 | val = iwi_read_prom_word(sc, IWI_EEPROM_MAC + 1); |
c4fe7bb1 IV |
397 | ic->ic_macaddr[2] = val & 0xff; |
398 | ic->ic_macaddr[3] = val >> 8; | |
b50e4759 | 399 | val = iwi_read_prom_word(sc, IWI_EEPROM_MAC + 2); |
c4fe7bb1 IV |
400 | ic->ic_macaddr[4] = val & 0xff; |
401 | ic->ic_macaddr[5] = val >> 8; | |
402 | ||
403 | memset(bands, 0, sizeof(bands)); | |
404 | setbit(bands, IEEE80211_MODE_11B); | |
405 | setbit(bands, IEEE80211_MODE_11G); | |
0e474d75 | 406 | if (pci_get_device(dev) >= 0x4223) |
c4fe7bb1 IV |
407 | setbit(bands, IEEE80211_MODE_11A); |
408 | ieee80211_init_channels(ic, NULL, bands); | |
0e474d75 | 409 | |
c4fe7bb1 | 410 | ieee80211_ifattach(ic); |
841ab66c SZ |
411 | /* override default methods */ |
412 | ic->ic_node_alloc = iwi_node_alloc; | |
413 | sc->sc_node_free = ic->ic_node_free; | |
414 | ic->ic_node_free = iwi_node_free; | |
0e474d75 JH |
415 | ic->ic_raw_xmit = iwi_raw_xmit; |
416 | ic->ic_scan_start = iwi_scan_start; | |
417 | ic->ic_scan_end = iwi_scan_end; | |
418 | ic->ic_set_channel = iwi_set_channel; | |
419 | ic->ic_scan_curchan = iwi_scan_curchan; | |
420 | ic->ic_scan_mindwell = iwi_scan_mindwell; | |
421 | ic->ic_wme.wme_update = iwi_wme_update; | |
b50e4759 | 422 | |
0e474d75 JH |
423 | ic->ic_vap_create = iwi_vap_create; |
424 | ic->ic_vap_delete = iwi_vap_delete; | |
c4fe7bb1 IV |
425 | ic->ic_transmit = iwi_transmit; |
426 | ic->ic_parent = iwi_parent; | |
b50e4759 | 427 | |
0e474d75 JH |
428 | ieee80211_radiotap_attach(ic, |
429 | &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), | |
430 | IWI_TX_RADIOTAP_PRESENT, | |
431 | &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), | |
432 | IWI_RX_RADIOTAP_PRESENT); | |
b50e4759 | 433 | |
0e474d75 JH |
434 | iwi_sysctlattach(sc); |
435 | iwi_ledattach(sc); | |
3660cb22 | 436 | |
4ff3e076 | 437 | /* |
841ab66c | 438 | * Hook our interrupt after all initialization is complete. |
4ff3e076 | 439 | */ |
c4fe7bb1 | 440 | #if defined(__DragonFly__) |
0e474d75 | 441 | error = bus_setup_intr(dev, sc->irq, INTR_MPSAFE, |
ef634573 | 442 | iwi_intr, sc, &sc->sc_ih, &wlan_global_serializer); |
c4fe7bb1 IV |
443 | #else |
444 | error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET | INTR_MPSAFE, | |
445 | NULL, iwi_intr, sc, &sc->sc_ih); | |
446 | #endif | |
4ff3e076 JS |
447 | if (error != 0) { |
448 | device_printf(dev, "could not set up interrupt\n"); | |
0e474d75 | 449 | goto fail; |
4ff3e076 JS |
450 | } |
451 | ||
841ab66c SZ |
452 | if (bootverbose) |
453 | ieee80211_announce(ic); | |
b50e4759 | 454 | |
841ab66c | 455 | return 0; |
4ff3e076 | 456 | fail: |
0e474d75 | 457 | /* XXX fix */ |
4ff3e076 | 458 | iwi_detach(dev); |
841ab66c | 459 | return ENXIO; |
b50e4759 MD |
460 | } |
461 | ||
462 | static int | |
463 | iwi_detach(device_t dev) | |
464 | { | |
465 | struct iwi_softc *sc = device_get_softc(dev); | |
c4fe7bb1 | 466 | struct ieee80211com *ic = &sc->sc_ic; |
3660cb22 | 467 | |
c4fe7bb1 | 468 | bus_teardown_intr(dev, sc->irq, sc->sc_ih); |
ef634573 | 469 | |
0e474d75 JH |
470 | /* NB: do early to drain any pending tasks */ |
471 | ieee80211_draintask(ic, &sc->sc_radiontask); | |
472 | ieee80211_draintask(ic, &sc->sc_radiofftask); | |
473 | ieee80211_draintask(ic, &sc->sc_restarttask); | |
474 | ieee80211_draintask(ic, &sc->sc_disassoctask); | |
c4fe7bb1 | 475 | ieee80211_draintask(ic, &sc->sc_monitortask); |
841ab66c | 476 | |
0e474d75 | 477 | iwi_stop(sc); |
841ab66c | 478 | |
0e474d75 | 479 | ieee80211_ifdetach(ic); |
cdf89432 | 480 | |
0e474d75 JH |
481 | iwi_put_firmware(sc); |
482 | iwi_release_fw_dma(sc); | |
b50e4759 | 483 | |
841ab66c SZ |
484 | iwi_free_cmd_ring(sc, &sc->cmdq); |
485 | iwi_free_tx_ring(sc, &sc->txq[0]); | |
486 | iwi_free_tx_ring(sc, &sc->txq[1]); | |
487 | iwi_free_tx_ring(sc, &sc->txq[2]); | |
488 | iwi_free_tx_ring(sc, &sc->txq[3]); | |
489 | iwi_free_rx_ring(sc, &sc->rxq); | |
b50e4759 | 490 | |
c4fe7bb1 | 491 | bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(sc->irq), sc->irq); |
0e474d75 | 492 | |
c4fe7bb1 IV |
493 | bus_release_resource(dev, SYS_RES_MEMORY, rman_get_rid(sc->mem), |
494 | sc->mem); | |
0e474d75 | 495 | |
c4fe7bb1 | 496 | #if defined(__DragonFly__) |
0e474d75 | 497 | devfs_clone_bitmap_uninit(&sc->sc_unr); |
c4fe7bb1 IV |
498 | #else |
499 | delete_unrhdr(sc->sc_unr); | |
500 | #endif | |
501 | mbufq_drain(&sc->sc_snd); | |
0e474d75 | 502 | |
c4fe7bb1 | 503 | IWI_LOCK_DESTROY(sc); |
b50e4759 | 504 | |
b50e4759 MD |
505 | return 0; |
506 | } | |
507 | ||
0e474d75 | 508 | static struct ieee80211vap * |
c4fe7bb1 IV |
509 | iwi_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, |
510 | enum ieee80211_opmode opmode, int flags, | |
511 | const uint8_t bssid[IEEE80211_ADDR_LEN], | |
512 | const uint8_t mac[IEEE80211_ADDR_LEN]) | |
513 | { | |
514 | struct iwi_softc *sc = ic->ic_softc; | |
0e474d75 JH |
515 | struct iwi_vap *ivp; |
516 | struct ieee80211vap *vap; | |
517 | int i; | |
518 | ||
519 | if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ | |
520 | return NULL; | |
521 | /* | |
522 | * Get firmware image (and possibly dma memory) on mode change. | |
523 | */ | |
524 | if (iwi_get_firmware(sc, opmode)) | |
525 | return NULL; | |
526 | /* allocate DMA memory for mapping firmware image */ | |
527 | i = sc->fw_fw.size; | |
528 | if (sc->fw_boot.size > i) | |
529 | i = sc->fw_boot.size; | |
530 | /* XXX do we dma the ucode as well ? */ | |
531 | if (sc->fw_uc.size > i) | |
532 | i = sc->fw_uc.size; | |
533 | if (iwi_init_fw_dma(sc, i)) | |
534 | return NULL; | |
535 | ||
c4fe7bb1 | 536 | ivp = kmalloc(sizeof(struct iwi_vap), M_80211_VAP, M_WAITOK | M_ZERO); |
0e474d75 | 537 | vap = &ivp->iwi_vap; |
c4fe7bb1 | 538 | ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid); |
0e474d75 JH |
539 | /* override the default, the setting comes from the linux driver */ |
540 | vap->iv_bmissthreshold = 24; | |
541 | /* override with driver methods */ | |
542 | ivp->iwi_newstate = vap->iv_newstate; | |
543 | vap->iv_newstate = iwi_newstate; | |
544 | ||
545 | /* complete setup */ | |
c4fe7bb1 IV |
546 | ieee80211_vap_attach(vap, ieee80211_media_change, iwi_media_status, |
547 | mac); | |
0e474d75 JH |
548 | ic->ic_opmode = opmode; |
549 | return vap; | |
550 | } | |
551 | ||
552 | static void | |
553 | iwi_vap_delete(struct ieee80211vap *vap) | |
554 | { | |
555 | struct iwi_vap *ivp = IWI_VAP(vap); | |
556 | ||
557 | ieee80211_vap_detach(vap); | |
558 | kfree(ivp, M_80211_VAP); | |
559 | } | |
560 | ||
841ab66c SZ |
561 | static void |
562 | iwi_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nseg, int error) | |
563 | { | |
564 | if (error != 0) | |
565 | return; | |
566 | ||
567 | KASSERT(nseg == 1, ("too many DMA segments, %d should be 1", nseg)); | |
568 | ||
569 | *(bus_addr_t *)arg = segs[0].ds_addr; | |
570 | } | |
571 | ||
b50e4759 | 572 | static int |
841ab66c | 573 | iwi_alloc_cmd_ring(struct iwi_softc *sc, struct iwi_cmd_ring *ring, int count) |
b50e4759 | 574 | { |
841ab66c | 575 | int error; |
b50e4759 | 576 | |
841ab66c SZ |
577 | ring->count = count; |
578 | ring->queued = 0; | |
579 | ring->cur = ring->next = 0; | |
580 | ||
c4fe7bb1 | 581 | #if defined(__DragonFly__) |
0e474d75 | 582 | error = bus_dma_tag_create(NULL, 4, 0, |
030b0c8c | 583 | BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, |
0e474d75 JH |
584 | count * IWI_CMD_DESC_SIZE, 1, count * IWI_CMD_DESC_SIZE, |
585 | 0 , &ring->desc_dmat); | |
c4fe7bb1 IV |
586 | #else |
587 | error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 4, 0, | |
588 | BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, | |
589 | count * IWI_CMD_DESC_SIZE, 1, count * IWI_CMD_DESC_SIZE, 0, | |
590 | NULL, NULL, &ring->desc_dmat); | |
591 | #endif | |
b50e4759 | 592 | if (error != 0) { |
841ab66c | 593 | device_printf(sc->sc_dev, "could not create desc DMA tag\n"); |
b50e4759 MD |
594 | goto fail; |
595 | } | |
841ab66c SZ |
596 | |
597 | error = bus_dmamem_alloc(ring->desc_dmat, (void **)&ring->desc, | |
0e474d75 | 598 | BUS_DMA_NOWAIT | BUS_DMA_ZERO, &ring->desc_map); |
b50e4759 | 599 | if (error != 0) { |
841ab66c | 600 | device_printf(sc->sc_dev, "could not allocate DMA memory\n"); |
b50e4759 MD |
601 | goto fail; |
602 | } | |
603 | ||
841ab66c SZ |
604 | error = bus_dmamap_load(ring->desc_dmat, ring->desc_map, ring->desc, |
605 | count * IWI_CMD_DESC_SIZE, iwi_dma_map_addr, &ring->physaddr, 0); | |
b50e4759 | 606 | if (error != 0) { |
841ab66c | 607 | device_printf(sc->sc_dev, "could not load desc DMA map\n"); |
0e474d75 | 608 | goto fail; |
b50e4759 MD |
609 | } |
610 | ||
841ab66c SZ |
611 | return 0; |
612 | ||
613 | fail: iwi_free_cmd_ring(sc, ring); | |
614 | return error; | |
615 | } | |
616 | ||
617 | static void | |
618 | iwi_reset_cmd_ring(struct iwi_softc *sc, struct iwi_cmd_ring *ring) | |
619 | { | |
620 | ring->queued = 0; | |
621 | ring->cur = ring->next = 0; | |
622 | } | |
623 | ||
624 | static void | |
625 | iwi_free_cmd_ring(struct iwi_softc *sc, struct iwi_cmd_ring *ring) | |
626 | { | |
627 | if (ring->desc != NULL) { | |
628 | bus_dmamap_sync(ring->desc_dmat, ring->desc_map, | |
629 | BUS_DMASYNC_POSTWRITE); | |
630 | bus_dmamap_unload(ring->desc_dmat, ring->desc_map); | |
631 | bus_dmamem_free(ring->desc_dmat, ring->desc, ring->desc_map); | |
b50e4759 MD |
632 | } |
633 | ||
0e474d75 | 634 | if (ring->desc_dmat != NULL) |
841ab66c | 635 | bus_dma_tag_destroy(ring->desc_dmat); |
841ab66c SZ |
636 | } |
637 | ||
638 | static int | |
639 | iwi_alloc_tx_ring(struct iwi_softc *sc, struct iwi_tx_ring *ring, int count, | |
640 | bus_addr_t csr_ridx, bus_addr_t csr_widx) | |
641 | { | |
642 | int i, error; | |
643 | ||
644 | ring->count = count; | |
645 | ring->queued = 0; | |
646 | ring->cur = ring->next = 0; | |
647 | ring->csr_ridx = csr_ridx; | |
648 | ring->csr_widx = csr_widx; | |
649 | ||
c4fe7bb1 | 650 | #if defined(__DragonFly__) |
841ab66c | 651 | error = bus_dma_tag_create(NULL, 4, 0, BUS_SPACE_MAXADDR_32BIT, |
030b0c8c | 652 | BUS_SPACE_MAXADDR, count * IWI_TX_DESC_SIZE, 1, |
841ab66c | 653 | count * IWI_TX_DESC_SIZE, 0, &ring->desc_dmat); |
c4fe7bb1 IV |
654 | #else |
655 | error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 4, 0, | |
656 | BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, | |
657 | count * IWI_TX_DESC_SIZE, 1, count * IWI_TX_DESC_SIZE, 0, NULL, | |
658 | NULL, &ring->desc_dmat); | |
659 | #endif | |
b50e4759 | 660 | if (error != 0) { |
841ab66c | 661 | device_printf(sc->sc_dev, "could not create desc DMA tag\n"); |
b50e4759 MD |
662 | goto fail; |
663 | } | |
664 | ||
841ab66c | 665 | error = bus_dmamem_alloc(ring->desc_dmat, (void **)&ring->desc, |
0e474d75 | 666 | BUS_DMA_NOWAIT | BUS_DMA_ZERO, &ring->desc_map); |
b50e4759 | 667 | if (error != 0) { |
841ab66c | 668 | device_printf(sc->sc_dev, "could not allocate DMA memory\n"); |
b50e4759 MD |
669 | goto fail; |
670 | } | |
671 | ||
841ab66c SZ |
672 | error = bus_dmamap_load(ring->desc_dmat, ring->desc_map, ring->desc, |
673 | count * IWI_TX_DESC_SIZE, iwi_dma_map_addr, &ring->physaddr, 0); | |
b50e4759 | 674 | if (error != 0) { |
841ab66c | 675 | device_printf(sc->sc_dev, "could not load desc DMA map\n"); |
b50e4759 MD |
676 | goto fail; |
677 | } | |
678 | ||
efda3bd0 | 679 | ring->data = kmalloc(count * sizeof (struct iwi_tx_data), M_DEVBUF, |
841ab66c | 680 | M_WAITOK | M_ZERO); |
c4fe7bb1 IV |
681 | if (ring->data == NULL) { |
682 | device_printf(sc->sc_dev, "could not allocate soft data\n"); | |
683 | error = ENOMEM; | |
684 | goto fail; | |
685 | } | |
841ab66c | 686 | |
c4fe7bb1 | 687 | #if defined(__DragonFly__) |
841ab66c | 688 | error = bus_dma_tag_create(NULL, 1, 0, BUS_SPACE_MAXADDR_32BIT, |
030b0c8c | 689 | BUS_SPACE_MAXADDR, MCLBYTES, IWI_MAX_NSEG, |
841ab66c | 690 | MCLBYTES, 0, &ring->data_dmat); |
c4fe7bb1 IV |
691 | #else |
692 | error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, | |
693 | BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, | |
694 | IWI_MAX_NSEG, MCLBYTES, 0, NULL, NULL, &ring->data_dmat); | |
695 | #endif | |
b50e4759 | 696 | if (error != 0) { |
841ab66c | 697 | device_printf(sc->sc_dev, "could not create data DMA tag\n"); |
b50e4759 MD |
698 | goto fail; |
699 | } | |
700 | ||
841ab66c SZ |
701 | for (i = 0; i < count; i++) { |
702 | error = bus_dmamap_create(ring->data_dmat, 0, | |
703 | &ring->data[i].map); | |
b50e4759 | 704 | if (error != 0) { |
841ab66c | 705 | device_printf(sc->sc_dev, "could not create DMA map\n"); |
b50e4759 MD |
706 | goto fail; |
707 | } | |
708 | } | |
709 | ||
841ab66c SZ |
710 | return 0; |
711 | ||
712 | fail: iwi_free_tx_ring(sc, ring); | |
713 | return error; | |
714 | } | |
715 | ||
716 | static void | |
717 | iwi_reset_tx_ring(struct iwi_softc *sc, struct iwi_tx_ring *ring) | |
718 | { | |
719 | struct iwi_tx_data *data; | |
720 | int i; | |
721 | ||
722 | for (i = 0; i < ring->count; i++) { | |
723 | data = &ring->data[i]; | |
724 | ||
725 | if (data->m != NULL) { | |
726 | bus_dmamap_sync(ring->data_dmat, data->map, | |
727 | BUS_DMASYNC_POSTWRITE); | |
728 | bus_dmamap_unload(ring->data_dmat, data->map); | |
729 | m_freem(data->m); | |
730 | data->m = NULL; | |
731 | } | |
732 | ||
733 | if (data->ni != NULL) { | |
734 | ieee80211_free_node(data->ni); | |
735 | data->ni = NULL; | |
736 | } | |
737 | } | |
738 | ||
739 | ring->queued = 0; | |
740 | ring->cur = ring->next = 0; | |
741 | } | |
742 | ||
743 | static void | |
744 | iwi_free_tx_ring(struct iwi_softc *sc, struct iwi_tx_ring *ring) | |
745 | { | |
746 | struct iwi_tx_data *data; | |
747 | int i; | |
748 | ||
749 | if (ring->desc != NULL) { | |
750 | bus_dmamap_sync(ring->desc_dmat, ring->desc_map, | |
751 | BUS_DMASYNC_POSTWRITE); | |
752 | bus_dmamap_unload(ring->desc_dmat, ring->desc_map); | |
753 | bus_dmamem_free(ring->desc_dmat, ring->desc, ring->desc_map); | |
841ab66c SZ |
754 | } |
755 | ||
0e474d75 | 756 | if (ring->desc_dmat != NULL) |
841ab66c | 757 | bus_dma_tag_destroy(ring->desc_dmat); |
841ab66c SZ |
758 | |
759 | if (ring->data != NULL) { | |
760 | for (i = 0; i < ring->count; i++) { | |
761 | data = &ring->data[i]; | |
762 | ||
763 | if (data->m != NULL) { | |
764 | bus_dmamap_sync(ring->data_dmat, data->map, | |
765 | BUS_DMASYNC_POSTWRITE); | |
766 | bus_dmamap_unload(ring->data_dmat, data->map); | |
767 | m_freem(data->m); | |
841ab66c SZ |
768 | } |
769 | ||
0e474d75 | 770 | if (data->ni != NULL) |
841ab66c | 771 | ieee80211_free_node(data->ni); |
841ab66c | 772 | |
0e474d75 | 773 | if (data->map != NULL) |
841ab66c | 774 | bus_dmamap_destroy(ring->data_dmat, data->map); |
841ab66c SZ |
775 | } |
776 | ||
efda3bd0 | 777 | kfree(ring->data, M_DEVBUF); |
841ab66c SZ |
778 | } |
779 | ||
0e474d75 | 780 | if (ring->data_dmat != NULL) |
841ab66c | 781 | bus_dma_tag_destroy(ring->data_dmat); |
841ab66c SZ |
782 | } |
783 | ||
784 | static int | |
785 | iwi_alloc_rx_ring(struct iwi_softc *sc, struct iwi_rx_ring *ring, int count) | |
786 | { | |
787 | struct iwi_rx_data *data; | |
788 | int i, error; | |
789 | ||
790 | ring->count = count; | |
791 | ring->cur = 0; | |
792 | ||
efda3bd0 | 793 | ring->data = kmalloc(count * sizeof (struct iwi_rx_data), M_DEVBUF, |
841ab66c | 794 | M_WAITOK | M_ZERO); |
c4fe7bb1 IV |
795 | if (ring->data == NULL) { |
796 | device_printf(sc->sc_dev, "could not allocate soft data\n"); | |
797 | error = ENOMEM; | |
798 | goto fail; | |
799 | } | |
841ab66c | 800 | |
c4fe7bb1 | 801 | #if defined(__DragonFly__) |
841ab66c | 802 | error = bus_dma_tag_create(NULL, 1, 0, BUS_SPACE_MAXADDR_32BIT, |
030b0c8c | 803 | BUS_SPACE_MAXADDR, MCLBYTES, 1, MCLBYTES, |
0e474d75 | 804 | 0, &ring->data_dmat); |
c4fe7bb1 IV |
805 | #else |
806 | error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, | |
807 | BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, | |
808 | 1, MCLBYTES, 0, NULL, NULL, &ring->data_dmat); | |
809 | #endif | |
b50e4759 | 810 | if (error != 0) { |
841ab66c | 811 | device_printf(sc->sc_dev, "could not create data DMA tag\n"); |
b50e4759 MD |
812 | goto fail; |
813 | } | |
814 | ||
841ab66c SZ |
815 | for (i = 0; i < count; i++) { |
816 | data = &ring->data[i]; | |
b50e4759 | 817 | |
841ab66c | 818 | error = bus_dmamap_create(ring->data_dmat, 0, &data->map); |
b50e4759 | 819 | if (error != 0) { |
841ab66c | 820 | device_printf(sc->sc_dev, "could not create DMA map\n"); |
b50e4759 MD |
821 | goto fail; |
822 | } | |
823 | ||
b5523eac | 824 | data->m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); |
841ab66c | 825 | if (data->m == NULL) { |
b50e4759 MD |
826 | device_printf(sc->sc_dev, |
827 | "could not allocate rx mbuf\n"); | |
828 | error = ENOMEM; | |
829 | goto fail; | |
830 | } | |
831 | ||
841ab66c SZ |
832 | error = bus_dmamap_load(ring->data_dmat, data->map, |
833 | mtod(data->m, void *), MCLBYTES, iwi_dma_map_addr, | |
834 | &data->physaddr, 0); | |
b50e4759 MD |
835 | if (error != 0) { |
836 | device_printf(sc->sc_dev, | |
837 | "could not load rx buf DMA map"); | |
838 | goto fail; | |
839 | } | |
841ab66c SZ |
840 | |
841 | data->reg = IWI_CSR_RX_BASE + i * 4; | |
b50e4759 MD |
842 | } |
843 | ||
844 | return 0; | |
845 | ||
841ab66c | 846 | fail: iwi_free_rx_ring(sc, ring); |
b50e4759 MD |
847 | return error; |
848 | } | |
849 | ||
850 | static void | |
841ab66c | 851 | iwi_reset_rx_ring(struct iwi_softc *sc, struct iwi_rx_ring *ring) |
b50e4759 | 852 | { |
841ab66c SZ |
853 | ring->cur = 0; |
854 | } | |
b50e4759 | 855 | |
841ab66c SZ |
856 | static void |
857 | iwi_free_rx_ring(struct iwi_softc *sc, struct iwi_rx_ring *ring) | |
858 | { | |
859 | struct iwi_rx_data *data; | |
860 | int i; | |
b50e4759 | 861 | |
841ab66c SZ |
862 | if (ring->data != NULL) { |
863 | for (i = 0; i < ring->count; i++) { | |
864 | data = &ring->data[i]; | |
b50e4759 | 865 | |
841ab66c SZ |
866 | if (data->m != NULL) { |
867 | bus_dmamap_sync(ring->data_dmat, data->map, | |
868 | BUS_DMASYNC_POSTREAD); | |
869 | bus_dmamap_unload(ring->data_dmat, data->map); | |
870 | m_freem(data->m); | |
b50e4759 | 871 | } |
b50e4759 | 872 | |
0e474d75 | 873 | if (data->map != NULL) |
841ab66c | 874 | bus_dmamap_destroy(ring->data_dmat, data->map); |
b50e4759 | 875 | } |
841ab66c | 876 | |
efda3bd0 | 877 | kfree(ring->data, M_DEVBUF); |
b50e4759 | 878 | } |
841ab66c | 879 | |
0e474d75 | 880 | if (ring->data_dmat != NULL) |
841ab66c | 881 | bus_dma_tag_destroy(ring->data_dmat); |
b50e4759 MD |
882 | } |
883 | ||
884 | static int | |
885 | iwi_shutdown(device_t dev) | |
886 | { | |
887 | struct iwi_softc *sc = device_get_softc(dev); | |
b50e4759 MD |
888 | |
889 | iwi_stop(sc); | |
0e474d75 | 890 | iwi_put_firmware(sc); /* ??? XXX */ |
b50e4759 MD |
891 | |
892 | return 0; | |
893 | } | |
894 | ||
895 | static int | |
896 | iwi_suspend(device_t dev) | |
897 | { | |
898 | struct iwi_softc *sc = device_get_softc(dev); | |
c4fe7bb1 | 899 | struct ieee80211com *ic = &sc->sc_ic; |
b50e4759 | 900 | |
c4fe7bb1 | 901 | ieee80211_suspend_all(ic); |
b50e4759 MD |
902 | return 0; |
903 | } | |
904 | ||
905 | static int | |
906 | iwi_resume(device_t dev) | |
907 | { | |
908 | struct iwi_softc *sc = device_get_softc(dev); | |
c4fe7bb1 | 909 | struct ieee80211com *ic = &sc->sc_ic; |
841ab66c | 910 | |
b50e4759 MD |
911 | pci_write_config(dev, 0x41, 0, 1); |
912 | ||
c4fe7bb1 | 913 | ieee80211_resume_all(ic); |
b50e4759 MD |
914 | return 0; |
915 | } | |
916 | ||
841ab66c | 917 | static struct ieee80211_node * |
0e474d75 | 918 | iwi_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) |
841ab66c SZ |
919 | { |
920 | struct iwi_node *in; | |
921 | ||
c4fe7bb1 | 922 | in = kmalloc(sizeof (struct iwi_node), M_80211_NODE, M_INTWAIT | M_ZERO); |
841ab66c SZ |
923 | if (in == NULL) |
924 | return NULL; | |
0e474d75 | 925 | /* XXX assign sta table entry for adhoc */ |
841ab66c SZ |
926 | in->in_station = -1; |
927 | ||
928 | return &in->in_node; | |
929 | } | |
930 | ||
931 | static void | |
932 | iwi_node_free(struct ieee80211_node *ni) | |
933 | { | |
934 | struct ieee80211com *ic = ni->ni_ic; | |
c4fe7bb1 | 935 | struct iwi_softc *sc = ic->ic_softc; |
841ab66c SZ |
936 | struct iwi_node *in = (struct iwi_node *)ni; |
937 | ||
0e474d75 | 938 | if (in->in_station != -1) { |
c4fe7bb1 | 939 | #if defined(__DragonFly__) |
1e290df3 | 940 | DPRINTF(("%s mac %s station %u\n", __func__, |
c4fe7bb1 IV |
941 | ether_sprintf(ni->ni_macaddr), in->in_station)); |
942 | #else | |
943 | DPRINTF(("%s mac %6D station %u\n", __func__, | |
944 | ni->ni_macaddr, ":", in->in_station)); | |
945 | #endif | |
946 | #if defined(__DragonFly__) | |
2281ff2e | 947 | devfs_clone_bitmap_put(&sc->sc_unr, in->in_station); |
c4fe7bb1 IV |
948 | #else |
949 | free_unr(sc->sc_unr, in->in_station); | |
950 | #endif | |
0e474d75 | 951 | } |
841ab66c SZ |
952 | |
953 | sc->sc_node_free(ni); | |
954 | } | |
955 | ||
114b4cbc SZ |
956 | /* |
957 | * Convert h/w rate code to IEEE rate code. | |
958 | */ | |
959 | static int | |
960 | iwi_cvtrate(int iwirate) | |
961 | { | |
962 | switch (iwirate) { | |
963 | case IWI_RATE_DS1: return 2; | |
964 | case IWI_RATE_DS2: return 4; | |
965 | case IWI_RATE_DS5: return 11; | |
966 | case IWI_RATE_DS11: return 22; | |
967 | case IWI_RATE_OFDM6: return 12; | |
968 | case IWI_RATE_OFDM9: return 18; | |
969 | case IWI_RATE_OFDM12: return 24; | |
970 | case IWI_RATE_OFDM18: return 36; | |
971 | case IWI_RATE_OFDM24: return 48; | |
972 | case IWI_RATE_OFDM36: return 72; | |
973 | case IWI_RATE_OFDM48: return 96; | |
974 | case IWI_RATE_OFDM54: return 108; | |
975 | } | |
976 | return 0; | |
977 | } | |
978 | ||
841ab66c SZ |
979 | /* |
980 | * The firmware automatically adapts the transmit speed. We report its current | |
981 | * value here. | |
982 | */ | |
b50e4759 MD |
983 | static void |
984 | iwi_media_status(struct ifnet *ifp, struct ifmediareq *imr) | |
985 | { | |
0e474d75 JH |
986 | struct ieee80211vap *vap = ifp->if_softc; |
987 | struct ieee80211com *ic = vap->iv_ic; | |
c4fe7bb1 IV |
988 | struct iwi_softc *sc = ic->ic_softc; |
989 | struct ieee80211_node *ni; | |
b50e4759 MD |
990 | |
991 | /* read current transmission rate from adapter */ | |
c4fe7bb1 IV |
992 | ni = ieee80211_ref_node(vap->iv_bss); |
993 | ni->ni_txrate = | |
0e474d75 | 994 | iwi_cvtrate(CSR_READ_4(sc, IWI_CSR_CURRENT_TX_RATE)); |
c4fe7bb1 | 995 | ieee80211_free_node(ni); |
0e474d75 | 996 | ieee80211_media_status(ifp, imr); |
b50e4759 MD |
997 | } |
998 | ||
999 | static int | |
0e474d75 | 1000 | iwi_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) |
b50e4759 | 1001 | { |
0e474d75 JH |
1002 | struct iwi_vap *ivp = IWI_VAP(vap); |
1003 | struct ieee80211com *ic = vap->iv_ic; | |
c4fe7bb1 IV |
1004 | struct iwi_softc *sc = ic->ic_softc; |
1005 | IWI_LOCK_DECL; | |
904b393f | 1006 | |
0e474d75 JH |
1007 | DPRINTF(("%s: %s -> %s flags 0x%x\n", __func__, |
1008 | ieee80211_state_name[vap->iv_state], | |
1009 | ieee80211_state_name[nstate], sc->flags)); | |
b50e4759 | 1010 | |
c4fe7bb1 IV |
1011 | IEEE80211_UNLOCK(ic); |
1012 | IWI_LOCK(sc); | |
b50e4759 | 1013 | switch (nstate) { |
0e474d75 JH |
1014 | case IEEE80211_S_INIT: |
1015 | /* | |
1016 | * NB: don't try to do this if iwi_stop_master has | |
1017 | * shutdown the firmware and disabled interrupts. | |
1018 | */ | |
1019 | if (vap->iv_state == IEEE80211_S_RUN && | |
1020 | (sc->flags & IWI_FLAG_FW_INITED)) | |
1021 | iwi_disassociate(sc, 0); | |
b50e4759 | 1022 | break; |
b50e4759 | 1023 | case IEEE80211_S_AUTH: |
0e474d75 | 1024 | iwi_auth_and_assoc(sc, vap); |
b50e4759 | 1025 | break; |
b50e4759 | 1026 | case IEEE80211_S_RUN: |
0e474d75 JH |
1027 | if (vap->iv_opmode == IEEE80211_M_IBSS && |
1028 | vap->iv_state == IEEE80211_S_SCAN) { | |
1029 | /* | |
1030 | * XXX when joining an ibss network we are called | |
1031 | * with a SCAN -> RUN transition on scan complete. | |
1032 | * Use that to call iwi_auth_and_assoc. On completing | |
1033 | * the join we are then called again with an | |
1034 | * AUTH -> RUN transition and we want to do nothing. | |
1035 | * This is all totally bogus and needs to be redone. | |
1036 | */ | |
1037 | iwi_auth_and_assoc(sc, vap); | |
c4fe7bb1 IV |
1038 | } else if (vap->iv_opmode == IEEE80211_M_MONITOR) |
1039 | ieee80211_runtask(ic, &sc->sc_monitortask); | |
b50e4759 | 1040 | break; |
0e474d75 JH |
1041 | case IEEE80211_S_ASSOC: |
1042 | /* | |
1043 | * If we are transitioning from AUTH then just wait | |
1044 | * for the ASSOC status to come back from the firmware. | |
1045 | * Otherwise we need to issue the association request. | |
1046 | */ | |
1047 | if (vap->iv_state == IEEE80211_S_AUTH) | |
841ab66c | 1048 | break; |
0e474d75 JH |
1049 | iwi_auth_and_assoc(sc, vap); |
1050 | break; | |
1051 | default: | |
b50e4759 MD |
1052 | break; |
1053 | } | |
c4fe7bb1 IV |
1054 | IWI_UNLOCK(sc); |
1055 | IEEE80211_LOCK(ic); | |
0e474d75 | 1056 | return ivp->iwi_newstate(vap, nstate, arg); |
b50e4759 MD |
1057 | } |
1058 | ||
841ab66c SZ |
1059 | /* |
1060 | * WME parameters coming from IEEE 802.11e specification. These values are | |
1061 | * already declared in ieee80211_proto.c, but they are static so they can't | |
1062 | * be reused here. | |
1063 | */ | |
1064 | static const struct wmeParams iwi_wme_cck_params[WME_NUM_AC] = { | |
1065 | { 0, 3, 5, 7, 0 }, /* WME_AC_BE */ | |
1066 | { 0, 3, 5, 10, 0 }, /* WME_AC_BK */ | |
1067 | { 0, 2, 4, 5, 188 }, /* WME_AC_VI */ | |
1068 | { 0, 2, 3, 4, 102 } /* WME_AC_VO */ | |
1069 | }; | |
1070 | ||
1071 | static const struct wmeParams iwi_wme_ofdm_params[WME_NUM_AC] = { | |
1072 | { 0, 3, 4, 6, 0 }, /* WME_AC_BE */ | |
1073 | { 0, 3, 4, 10, 0 }, /* WME_AC_BK */ | |
1074 | { 0, 2, 3, 4, 94 }, /* WME_AC_VI */ | |
1075 | { 0, 2, 2, 3, 47 } /* WME_AC_VO */ | |
1076 | }; | |
841ab66c SZ |
1077 | #define IWI_EXP2(v) htole16((1 << (v)) - 1) |
1078 | #define IWI_USEC(v) htole16(IEEE80211_TXOP_TO_US(v)) | |
0e474d75 JH |
1079 | |
1080 | static void | |
1081 | iwi_wme_init(struct iwi_softc *sc) | |
1082 | { | |
841ab66c SZ |
1083 | const struct wmeParams *wmep; |
1084 | int ac; | |
1085 | ||
0e474d75 | 1086 | memset(sc->wme, 0, sizeof sc->wme); |
841ab66c | 1087 | for (ac = 0; ac < WME_NUM_AC; ac++) { |
841ab66c SZ |
1088 | /* set WME values for CCK modulation */ |
1089 | wmep = &iwi_wme_cck_params[ac]; | |
0e474d75 JH |
1090 | sc->wme[1].aifsn[ac] = wmep->wmep_aifsn; |
1091 | sc->wme[1].cwmin[ac] = IWI_EXP2(wmep->wmep_logcwmin); | |
1092 | sc->wme[1].cwmax[ac] = IWI_EXP2(wmep->wmep_logcwmax); | |
1093 | sc->wme[1].burst[ac] = IWI_USEC(wmep->wmep_txopLimit); | |
1094 | sc->wme[1].acm[ac] = wmep->wmep_acm; | |
841ab66c SZ |
1095 | |
1096 | /* set WME values for OFDM modulation */ | |
1097 | wmep = &iwi_wme_ofdm_params[ac]; | |
0e474d75 JH |
1098 | sc->wme[2].aifsn[ac] = wmep->wmep_aifsn; |
1099 | sc->wme[2].cwmin[ac] = IWI_EXP2(wmep->wmep_logcwmin); | |
1100 | sc->wme[2].cwmax[ac] = IWI_EXP2(wmep->wmep_logcwmax); | |
1101 | sc->wme[2].burst[ac] = IWI_USEC(wmep->wmep_txopLimit); | |
1102 | sc->wme[2].acm[ac] = wmep->wmep_acm; | |
1103 | } | |
1104 | } | |
1105 | ||
1106 | static int | |
c4fe7bb1 | 1107 | iwi_wme_setparams(struct iwi_softc *sc) |
0e474d75 | 1108 | { |
c4fe7bb1 | 1109 | struct ieee80211com *ic = &sc->sc_ic; |
0e474d75 JH |
1110 | const struct wmeParams *wmep; |
1111 | int ac; | |
1112 | ||
1113 | for (ac = 0; ac < WME_NUM_AC; ac++) { | |
1114 | /* set WME values for current operating mode */ | |
1115 | wmep = &ic->ic_wme.wme_chanParams.cap_wmeParams[ac]; | |
1116 | sc->wme[0].aifsn[ac] = wmep->wmep_aifsn; | |
1117 | sc->wme[0].cwmin[ac] = IWI_EXP2(wmep->wmep_logcwmin); | |
1118 | sc->wme[0].cwmax[ac] = IWI_EXP2(wmep->wmep_logcwmax); | |
1119 | sc->wme[0].burst[ac] = IWI_USEC(wmep->wmep_txopLimit); | |
1120 | sc->wme[0].acm[ac] = wmep->wmep_acm; | |
841ab66c SZ |
1121 | } |
1122 | ||
1123 | DPRINTF(("Setting WME parameters\n")); | |
0e474d75 JH |
1124 | return iwi_cmd(sc, IWI_CMD_SET_WME_PARAMS, sc->wme, sizeof sc->wme); |
1125 | } | |
841ab66c SZ |
1126 | #undef IWI_USEC |
1127 | #undef IWI_EXP2 | |
841ab66c | 1128 | |
0e474d75 JH |
1129 | static int |
1130 | iwi_wme_update(struct ieee80211com *ic) | |
1131 | { | |
c4fe7bb1 | 1132 | struct iwi_softc *sc = ic->ic_softc; |
0e474d75 | 1133 | struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); |
c4fe7bb1 | 1134 | IWI_LOCK_DECL; |
0e474d75 JH |
1135 | |
1136 | /* | |
1137 | * We may be called to update the WME parameters in | |
1138 | * the adapter at various places. If we're already | |
1139 | * associated then initiate the request immediately; | |
1140 | * otherwise we assume the params will get sent down | |
1141 | * to the adapter as part of the work iwi_auth_and_assoc | |
1142 | * does. | |
1143 | */ | |
c4fe7bb1 IV |
1144 | if (vap->iv_state == IEEE80211_S_RUN) { |
1145 | IWI_LOCK(sc); | |
1146 | iwi_wme_setparams(sc); | |
1147 | IWI_UNLOCK(sc); | |
1148 | } | |
0e474d75 JH |
1149 | return (0); |
1150 | } | |
1151 | ||
1152 | static int | |
1153 | iwi_wme_setie(struct iwi_softc *sc) | |
1154 | { | |
1155 | struct ieee80211_wme_info wme; | |
1156 | ||
1157 | memset(&wme, 0, sizeof wme); | |
1158 | wme.wme_id = IEEE80211_ELEMID_VENDOR; | |
1159 | wme.wme_len = sizeof (struct ieee80211_wme_info) - 2; | |
1160 | wme.wme_oui[0] = 0x00; | |
1161 | wme.wme_oui[1] = 0x50; | |
1162 | wme.wme_oui[2] = 0xf2; | |
1163 | wme.wme_type = WME_OUI_TYPE; | |
1164 | wme.wme_subtype = WME_INFO_OUI_SUBTYPE; | |
1165 | wme.wme_version = WME_VERSION; | |
1166 | wme.wme_info = 0; | |
1167 | ||
1168 | DPRINTF(("Setting WME IE (len=%u)\n", wme.wme_len)); | |
1169 | return iwi_cmd(sc, IWI_CMD_SET_WMEIE, &wme, sizeof wme); | |
1170 | } | |
1171 | ||
1172 | /* | |
1173 | * Read 16 bits at address 'addr' from the serial EEPROM. | |
1174 | */ | |
1175 | static uint16_t | |
1176 | iwi_read_prom_word(struct iwi_softc *sc, uint8_t addr) | |
1177 | { | |
1178 | uint32_t tmp; | |
1179 | uint16_t val; | |
1180 | int n; | |
1181 | ||
1182 | /* clock C once before the first command */ | |
b50e4759 MD |
1183 | IWI_EEPROM_CTL(sc, 0); |
1184 | IWI_EEPROM_CTL(sc, IWI_EEPROM_S); | |
1185 | IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_C); | |
1186 | IWI_EEPROM_CTL(sc, IWI_EEPROM_S); | |
1187 | ||
0e474d75 | 1188 | /* write start bit (1) */ |
b50e4759 MD |
1189 | IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_D); |
1190 | IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_D | IWI_EEPROM_C); | |
1191 | ||
0e474d75 | 1192 | /* write READ opcode (10) */ |
b50e4759 MD |
1193 | IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_D); |
1194 | IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_D | IWI_EEPROM_C); | |
1195 | IWI_EEPROM_CTL(sc, IWI_EEPROM_S); | |
1196 | IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_C); | |
1197 | ||
0e474d75 | 1198 | /* write address A7-A0 */ |
b50e4759 MD |
1199 | for (n = 7; n >= 0; n--) { |
1200 | IWI_EEPROM_CTL(sc, IWI_EEPROM_S | | |
1201 | (((addr >> n) & 1) << IWI_EEPROM_SHIFT_D)); | |
1202 | IWI_EEPROM_CTL(sc, IWI_EEPROM_S | | |
1203 | (((addr >> n) & 1) << IWI_EEPROM_SHIFT_D) | IWI_EEPROM_C); | |
1204 | } | |
1205 | ||
1206 | IWI_EEPROM_CTL(sc, IWI_EEPROM_S); | |
1207 | ||
0e474d75 | 1208 | /* read data Q15-Q0 */ |
b50e4759 MD |
1209 | val = 0; |
1210 | for (n = 15; n >= 0; n--) { | |
1211 | IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_C); | |
1212 | IWI_EEPROM_CTL(sc, IWI_EEPROM_S); | |
1213 | tmp = MEM_READ_4(sc, IWI_MEM_EEPROM_CTL); | |
1214 | val |= ((tmp & IWI_EEPROM_Q) >> IWI_EEPROM_SHIFT_Q) << n; | |
1215 | } | |
1216 | ||
1217 | IWI_EEPROM_CTL(sc, 0); | |
1218 | ||
0e474d75 | 1219 | /* clear Chip Select and clock C */ |
b50e4759 MD |
1220 | IWI_EEPROM_CTL(sc, IWI_EEPROM_S); |
1221 | IWI_EEPROM_CTL(sc, 0); | |
1222 | IWI_EEPROM_CTL(sc, IWI_EEPROM_C); | |
1223 | ||
841ab66c | 1224 | return val; |
b50e4759 MD |
1225 | } |
1226 | ||
b50e4759 | 1227 | static void |
0e474d75 | 1228 | iwi_setcurchan(struct iwi_softc *sc, int chan) |
b50e4759 | 1229 | { |
c4fe7bb1 | 1230 | struct ieee80211com *ic = &sc->sc_ic; |
b50e4759 | 1231 | |
0e474d75 JH |
1232 | sc->curchan = chan; |
1233 | ieee80211_radiotap_chan_change(ic); | |
b50e4759 MD |
1234 | } |
1235 | ||
1236 | static void | |
841ab66c | 1237 | iwi_frame_intr(struct iwi_softc *sc, struct iwi_rx_data *data, int i, |
b50e4759 MD |
1238 | struct iwi_frame *frame) |
1239 | { | |
c4fe7bb1 | 1240 | struct ieee80211com *ic = &sc->sc_ic; |
841ab66c | 1241 | struct mbuf *mnew, *m; |
b50e4759 | 1242 | struct ieee80211_node *ni; |
0e474d75 JH |
1243 | int type, error, framelen; |
1244 | int8_t rssi, nf; | |
c4fe7bb1 | 1245 | IWI_LOCK_DECL; |
841ab66c | 1246 | |
0e474d75 JH |
1247 | framelen = le16toh(frame->len); |
1248 | if (framelen < IEEE80211_MIN_LEN || framelen > MCLBYTES) { | |
1249 | /* | |
1250 | * XXX >MCLBYTES is bogus as it means the h/w dma'd | |
1251 | * out of bounds; need to figure out how to limit | |
1252 | * frame size in the firmware | |
1253 | */ | |
1254 | /* XXX stat */ | |
1255 | DPRINTFN(1, | |
1256 | ("drop rx frame len=%u chan=%u rssi=%u rssi_dbm=%u\n", | |
1257 | le16toh(frame->len), frame->chan, frame->rssi, | |
1258 | frame->rssi_dbm)); | |
841ab66c | 1259 | return; |
0e474d75 JH |
1260 | } |
1261 | ||
1262 | DPRINTFN(5, ("received frame len=%u chan=%u rssi=%u rssi_dbm=%u\n", | |
1263 | le16toh(frame->len), frame->chan, frame->rssi, frame->rssi_dbm)); | |
1264 | ||
1265 | if (frame->chan != sc->curchan) | |
1266 | iwi_setcurchan(sc, frame->chan); | |
841ab66c SZ |
1267 | |
1268 | /* | |
1269 | * Try to allocate a new mbuf for this ring element and load it before | |
1270 | * processing the current mbuf. If the ring element cannot be loaded, | |
1271 | * drop the received packet and reuse the old mbuf. In the unlikely | |
1272 | * case that the old mbuf can't be reloaded either, explicitly panic. | |
1273 | */ | |
b5523eac | 1274 | mnew = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); |
841ab66c | 1275 | if (mnew == NULL) { |
c4fe7bb1 IV |
1276 | #if defined(__DragonFly__) |
1277 | ++ic->ic_ierrors; | |
1278 | #else | |
1279 | counter_u64_add(ic->ic_ierrors, 1); | |
1280 | #endif | |
841ab66c SZ |
1281 | return; |
1282 | } | |
1283 | ||
1284 | bus_dmamap_unload(sc->rxq.data_dmat, data->map); | |
b50e4759 | 1285 | |
841ab66c SZ |
1286 | error = bus_dmamap_load(sc->rxq.data_dmat, data->map, |
1287 | mtod(mnew, void *), MCLBYTES, iwi_dma_map_addr, &data->physaddr, | |
1288 | 0); | |
1289 | if (error != 0) { | |
1290 | m_freem(mnew); | |
1291 | ||
1292 | /* try to reload the old mbuf */ | |
1293 | error = bus_dmamap_load(sc->rxq.data_dmat, data->map, | |
1294 | mtod(data->m, void *), MCLBYTES, iwi_dma_map_addr, | |
1295 | &data->physaddr, 0); | |
1296 | if (error != 0) { | |
1297 | /* very unlikely that it will fail... */ | |
1298 | panic("%s: could not load old rx mbuf", | |
1299 | device_get_name(sc->sc_dev)); | |
1300 | } | |
c4fe7bb1 IV |
1301 | #if defined(__DragonFly__) |
1302 | ++ic->ic_ierrors; | |
1303 | #else | |
1304 | counter_u64_add(ic->ic_ierrors, 1); | |
1305 | #endif | |
b50e4759 MD |
1306 | return; |
1307 | } | |
1308 | ||
841ab66c SZ |
1309 | /* |
1310 | * New mbuf successfully loaded, update Rx ring and continue | |
1311 | * processing. | |
1312 | */ | |
1313 | m = data->m; | |
1314 | data->m = mnew; | |
1315 | CSR_WRITE_4(sc, data->reg, data->physaddr); | |
b50e4759 | 1316 | |
0e474d75 | 1317 | /* finalize mbuf */ |
b50e4759 | 1318 | m->m_pkthdr.len = m->m_len = sizeof (struct iwi_hdr) + |
0e474d75 | 1319 | sizeof (struct iwi_frame) + framelen; |
b50e4759 MD |
1320 | |
1321 | m_adj(m, sizeof (struct iwi_hdr) + sizeof (struct iwi_frame)); | |
1322 | ||
0e474d75 JH |
1323 | rssi = frame->rssi_dbm; |
1324 | nf = -95; | |
1325 | if (ieee80211_radiotap_active(ic)) { | |
b50e4759 MD |
1326 | struct iwi_rx_radiotap_header *tap = &sc->sc_rxtap; |
1327 | ||
1328 | tap->wr_flags = 0; | |
0e474d75 JH |
1329 | tap->wr_antsignal = rssi; |
1330 | tap->wr_antnoise = nf; | |
1331 | tap->wr_rate = iwi_cvtrate(frame->rate); | |
b50e4759 | 1332 | tap->wr_antenna = frame->antenna; |
0e474d75 | 1333 | } |
c4fe7bb1 | 1334 | IWI_UNLOCK(sc); |
0e474d75 JH |
1335 | |
1336 | ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *)); | |
1337 | if (ni != NULL) { | |
1338 | type = ieee80211_input(ni, m, rssi, nf); | |
1339 | ieee80211_free_node(ni); | |
1340 | } else | |
1341 | type = ieee80211_input_all(ic, m, rssi, nf); | |
b50e4759 | 1342 | |
c4fe7bb1 | 1343 | IWI_LOCK(sc); |
0e474d75 JH |
1344 | if (sc->sc_softled) { |
1345 | /* | |
1346 | * Blink for any data frame. Otherwise do a | |
1347 | * heartbeat-style blink when idle. The latter | |
1348 | * is mainly for station mode where we depend on | |
1349 | * periodic beacon frames to trigger the poll event. | |
1350 | */ | |
1351 | if (type == IEEE80211_FC0_TYPE_DATA) { | |
1352 | sc->sc_rxrate = frame->rate; | |
1353 | iwi_led_event(sc, IWI_LED_RX); | |
1354 | } else if (ticks - sc->sc_ledevent >= sc->sc_ledidle) | |
1355 | iwi_led_event(sc, IWI_LED_POLL); | |
b50e4759 | 1356 | } |
0e474d75 JH |
1357 | } |
1358 | ||
1359 | /* | |
1360 | * Check for an association response frame to see if QoS | |
1361 | * has been negotiated. We parse just enough to figure | |
1362 | * out if we're supposed to use QoS. The proper solution | |
1363 | * is to pass the frame up so ieee80211_input can do the | |
1364 | * work but that's made hard by how things currently are | |
1365 | * done in the driver. | |
1366 | */ | |
1367 | static void | |
1368 | iwi_checkforqos(struct ieee80211vap *vap, | |
1369 | const struct ieee80211_frame *wh, int len) | |
1370 | { | |
1371 | #define SUBTYPE(wh) ((wh)->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) | |
1372 | const uint8_t *frm, *efrm, *wme; | |
1373 | struct ieee80211_node *ni; | |
c4fe7bb1 | 1374 | uint16_t capinfo, status, associd; |
0e474d75 JH |
1375 | |
1376 | /* NB: +8 for capinfo, status, associd, and first ie */ | |
1377 | if (!(sizeof(*wh)+8 < len && len < IEEE80211_MAX_LEN) || | |
1378 | SUBTYPE(wh) != IEEE80211_FC0_SUBTYPE_ASSOC_RESP) | |
1379 | return; | |
1380 | /* | |
1381 | * asresp frame format | |
1382 | * [2] capability information | |
1383 | * [2] status | |
1384 | * [2] association ID | |
1385 | * [tlv] supported rates | |
1386 | * [tlv] extended supported rates | |
1387 | * [tlv] WME | |
1388 | */ | |
1389 | frm = (const uint8_t *)&wh[1]; | |
1390 | efrm = ((const uint8_t *) wh) + len; | |
b50e4759 | 1391 | |
0e474d75 | 1392 | capinfo = le16toh(*(const uint16_t *)frm); |
2a0b1ca7 | 1393 | frm += 2; |
c4fe7bb1 | 1394 | status = le16toh(*(const uint16_t *)frm); |
2a0b1ca7 | 1395 | frm += 2; |
0e474d75 JH |
1396 | associd = le16toh(*(const uint16_t *)frm); |
1397 | frm += 2; | |
b50e4759 | 1398 | |
0e474d75 | 1399 | wme = NULL; |
c4fe7bb1 IV |
1400 | while (efrm - frm > 1) { |
1401 | IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return); | |
0e474d75 JH |
1402 | switch (*frm) { |
1403 | case IEEE80211_ELEMID_VENDOR: | |
1404 | if (iswmeoui(frm)) | |
1405 | wme = frm; | |
1406 | break; | |
1407 | } | |
1408 | frm += frm[1] + 2; | |
1409 | } | |
b50e4759 | 1410 | |
c4fe7bb1 | 1411 | ni = ieee80211_ref_node(vap->iv_bss); |
0e474d75 | 1412 | ni->ni_capinfo = capinfo; |
c4fe7bb1 | 1413 | ni->ni_associd = associd & 0x3fff; |
0e474d75 JH |
1414 | if (wme != NULL) |
1415 | ni->ni_flags |= IEEE80211_NODE_QOS; | |
1416 | else | |
1417 | ni->ni_flags &= ~IEEE80211_NODE_QOS; | |
c4fe7bb1 | 1418 | ieee80211_free_node(ni); |
0e474d75 | 1419 | #undef SUBTYPE |
b50e4759 MD |
1420 | } |
1421 | ||
c4fe7bb1 IV |
1422 | static void |
1423 | iwi_notif_link_quality(struct iwi_softc *sc, struct iwi_notif *notif) | |
1424 | { | |
1425 | struct iwi_notif_link_quality *lq; | |
1426 | int len; | |
1427 | ||
1428 | len = le16toh(notif->len); | |
1429 | ||
1430 | DPRINTFN(5, ("Notification (%u) - len=%d, sizeof=%zu\n", | |
1431 | notif->type, | |
1432 | len, | |
1433 | sizeof(struct iwi_notif_link_quality) | |
1434 | )); | |
1435 | ||
1436 | /* enforce length */ | |
1437 | if (len != sizeof(struct iwi_notif_link_quality)) { | |
1438 | DPRINTFN(5, ("Notification: (%u) too short (%d)\n", | |
1439 | notif->type, | |
1440 | len)); | |
1441 | return; | |
1442 | } | |
1443 | ||
1444 | lq = (struct iwi_notif_link_quality *)(notif + 1); | |
1445 | memcpy(&sc->sc_linkqual, lq, sizeof(sc->sc_linkqual)); | |
1446 | sc->sc_linkqual_valid = 1; | |
1447 | } | |
1448 | ||
0e474d75 JH |
1449 | /* |
1450 | * Task queue callbacks for iwi_notification_intr used to avoid LOR's. | |
1451 | */ | |
1452 | ||
b50e4759 MD |
1453 | static void |
1454 | iwi_notification_intr(struct iwi_softc *sc, struct iwi_notif *notif) | |
1455 | { | |
c4fe7bb1 | 1456 | struct ieee80211com *ic = &sc->sc_ic; |
0e474d75 | 1457 | struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); |
b50e4759 MD |
1458 | struct iwi_notif_scan_channel *chan; |
1459 | struct iwi_notif_scan_complete *scan; | |
1460 | struct iwi_notif_authentication *auth; | |
1461 | struct iwi_notif_association *assoc; | |
0e474d75 | 1462 | struct iwi_notif_beacon_state *beacon; |
b50e4759 MD |
1463 | |
1464 | switch (notif->type) { | |
1465 | case IWI_NOTIF_TYPE_SCAN_CHANNEL: | |
1466 | chan = (struct iwi_notif_scan_channel *)(notif + 1); | |
1467 | ||
0e474d75 JH |
1468 | DPRINTFN(3, ("Scan of channel %u complete (%u)\n", |
1469 | ieee80211_ieee2mhz(chan->nchan, 0), chan->nchan)); | |
1470 | ||
1471 | /* Reset the timer, the scan is still going */ | |
1472 | sc->sc_state_timer = 3; | |
b50e4759 MD |
1473 | break; |
1474 | ||
1475 | case IWI_NOTIF_TYPE_SCAN_COMPLETE: | |
1476 | scan = (struct iwi_notif_scan_complete *)(notif + 1); | |
1477 | ||
1478 | DPRINTFN(2, ("Scan completed (%u, %u)\n", scan->nchan, | |
1479 | scan->status)); | |
1480 | ||
0e474d75 JH |
1481 | IWI_STATE_END(sc, IWI_FW_SCANNING); |
1482 | ||
c4fe7bb1 IV |
1483 | /* |
1484 | * Monitor mode works by doing a passive scan to set | |
1485 | * the channel and enable rx. Because we don't want | |
1486 | * to abort a scan lest the firmware crash we scan | |
1487 | * for a short period of time and automatically restart | |
1488 | * the scan when notified the sweep has completed. | |
1489 | */ | |
1490 | if (vap->iv_opmode == IEEE80211_M_MONITOR) { | |
1491 | ieee80211_runtask(ic, &sc->sc_monitortask); | |
1492 | break; | |
1493 | } | |
1494 | ||
0e474d75 JH |
1495 | if (scan->status == IWI_SCAN_COMPLETED) { |
1496 | /* NB: don't need to defer, net80211 does it for us */ | |
1497 | ieee80211_scan_next(vap); | |
1498 | } | |
b50e4759 MD |
1499 | break; |
1500 | ||
1501 | case IWI_NOTIF_TYPE_AUTHENTICATION: | |
1502 | auth = (struct iwi_notif_authentication *)(notif + 1); | |
b50e4759 | 1503 | switch (auth->state) { |
0e474d75 JH |
1504 | case IWI_AUTH_SUCCESS: |
1505 | DPRINTFN(2, ("Authentication succeeeded\n")); | |
1506 | ieee80211_new_state(vap, IEEE80211_S_ASSOC, -1); | |
b50e4759 | 1507 | break; |
0e474d75 JH |
1508 | case IWI_AUTH_FAIL: |
1509 | /* | |
1510 | * These are delivered as an unsolicited deauth | |
1511 | * (e.g. due to inactivity) or in response to an | |
1512 | * associate request. | |
1513 | */ | |
1514 | sc->flags &= ~IWI_FLAG_ASSOCIATED; | |
1515 | if (vap->iv_state != IEEE80211_S_RUN) { | |
1516 | DPRINTFN(2, ("Authentication failed\n")); | |
1517 | vap->iv_stats.is_rx_auth_fail++; | |
1518 | IWI_STATE_END(sc, IWI_FW_ASSOCIATING); | |
1519 | } else { | |
1520 | DPRINTFN(2, ("Deauthenticated\n")); | |
1521 | vap->iv_stats.is_rx_deauth++; | |
1522 | } | |
1523 | ieee80211_new_state(vap, IEEE80211_S_SCAN, -1); | |
1524 | break; | |
1525 | case IWI_AUTH_SENT_1: | |
1526 | case IWI_AUTH_RECV_2: | |
1527 | case IWI_AUTH_SEQ1_PASS: | |
1528 | break; | |
1529 | case IWI_AUTH_SEQ1_FAIL: | |
1530 | DPRINTFN(2, ("Initial authentication handshake failed; " | |
1531 | "you probably need shared key\n")); | |
1532 | vap->iv_stats.is_rx_auth_fail++; | |
1533 | IWI_STATE_END(sc, IWI_FW_ASSOCIATING); | |
1534 | /* XXX retry shared key when in auto */ | |
b50e4759 | 1535 | break; |
b50e4759 MD |
1536 | default: |
1537 | device_printf(sc->sc_dev, | |
1538 | "unknown authentication state %u\n", auth->state); | |
0e474d75 | 1539 | break; |
b50e4759 MD |
1540 | } |
1541 | break; | |
1542 | ||
1543 | case IWI_NOTIF_TYPE_ASSOCIATION: | |
1544 | assoc = (struct iwi_notif_association *)(notif + 1); | |
b50e4759 | 1545 | switch (assoc->state) { |
0e474d75 | 1546 | case IWI_AUTH_SUCCESS: |
841ab66c SZ |
1547 | /* re-association, do nothing */ |
1548 | break; | |
0e474d75 JH |
1549 | case IWI_ASSOC_SUCCESS: |
1550 | DPRINTFN(2, ("Association succeeded\n")); | |
1551 | sc->flags |= IWI_FLAG_ASSOCIATED; | |
1552 | IWI_STATE_END(sc, IWI_FW_ASSOCIATING); | |
1553 | iwi_checkforqos(vap, | |
1554 | (const struct ieee80211_frame *)(assoc+1), | |
c4fe7bb1 | 1555 | le16toh(notif->len) - sizeof(*assoc) - 1); |
0e474d75 | 1556 | ieee80211_new_state(vap, IEEE80211_S_RUN, -1); |
b50e4759 | 1557 | break; |
0e474d75 JH |
1558 | case IWI_ASSOC_INIT: |
1559 | sc->flags &= ~IWI_FLAG_ASSOCIATED; | |
1560 | switch (sc->fw_state) { | |
1561 | case IWI_FW_ASSOCIATING: | |
1562 | DPRINTFN(2, ("Association failed\n")); | |
1563 | IWI_STATE_END(sc, IWI_FW_ASSOCIATING); | |
1564 | ieee80211_new_state(vap, IEEE80211_S_SCAN, -1); | |
1565 | break; | |
b50e4759 | 1566 | |
0e474d75 JH |
1567 | case IWI_FW_DISASSOCIATING: |
1568 | DPRINTFN(2, ("Dissassociated\n")); | |
1569 | IWI_STATE_END(sc, IWI_FW_DISASSOCIATING); | |
1570 | vap->iv_stats.is_rx_disassoc++; | |
1571 | ieee80211_new_state(vap, IEEE80211_S_SCAN, -1); | |
1572 | break; | |
1573 | } | |
b50e4759 | 1574 | break; |
b50e4759 MD |
1575 | default: |
1576 | device_printf(sc->sc_dev, | |
1577 | "unknown association state %u\n", assoc->state); | |
0e474d75 | 1578 | break; |
b50e4759 MD |
1579 | } |
1580 | break; | |
1581 | ||
0e474d75 JH |
1582 | case IWI_NOTIF_TYPE_BEACON: |
1583 | /* XXX check struct length */ | |
1584 | beacon = (struct iwi_notif_beacon_state *)(notif + 1); | |
1585 | ||
1586 | DPRINTFN(5, ("Beacon state (%u, %u)\n", | |
1587 | beacon->state, le32toh(beacon->number))); | |
1588 | ||
1589 | if (beacon->state == IWI_BEACON_MISS) { | |
1590 | /* | |
1591 | * The firmware notifies us of every beacon miss | |
1592 | * so we need to track the count against the | |
1593 | * configured threshold before notifying the | |
1594 | * 802.11 layer. | |
1595 | * XXX try to roam, drop assoc only on much higher count | |
1596 | */ | |
1597 | if (le32toh(beacon->number) >= vap->iv_bmissthreshold) { | |
1598 | DPRINTF(("Beacon miss: %u >= %u\n", | |
1599 | le32toh(beacon->number), | |
1600 | vap->iv_bmissthreshold)); | |
1601 | vap->iv_stats.is_beacon_miss++; | |
1602 | /* | |
1603 | * It's pointless to notify the 802.11 layer | |
1604 | * as it'll try to send a probe request (which | |
1605 | * we'll discard) and then timeout and drop us | |
1606 | * into scan state. Instead tell the firmware | |
1607 | * to disassociate and then on completion we'll | |
1608 | * kick the state machine to scan. | |
1609 | */ | |
1610 | ieee80211_runtask(ic, &sc->sc_disassoctask); | |
1611 | } | |
1612 | } | |
1613 | break; | |
1614 | ||
1615 | case IWI_NOTIF_TYPE_CALIBRATION: | |
1616 | case IWI_NOTIF_TYPE_NOISE: | |
c4fe7bb1 | 1617 | /* XXX handle? */ |
841ab66c | 1618 | DPRINTFN(5, ("Notification (%u)\n", notif->type)); |
0e474d75 | 1619 | break; |
c4fe7bb1 IV |
1620 | case IWI_NOTIF_TYPE_LINK_QUALITY: |
1621 | iwi_notif_link_quality(sc, notif); | |
1622 | break; | |
0e474d75 JH |
1623 | |
1624 | default: | |
1625 | DPRINTF(("unknown notification type %u flags 0x%x len %u\n", | |
1626 | notif->type, notif->flags, le16toh(notif->len))); | |
1627 | break; | |
b50e4759 MD |
1628 | } |
1629 | } | |
1630 | ||
1631 | static void | |
1632 | iwi_rx_intr(struct iwi_softc *sc) | |
1633 | { | |
841ab66c | 1634 | struct iwi_rx_data *data; |
b50e4759 | 1635 | struct iwi_hdr *hdr; |
841ab66c | 1636 | uint32_t hw; |
b50e4759 | 1637 | |
841ab66c | 1638 | hw = CSR_READ_4(sc, IWI_CSR_RX_RIDX); |
b50e4759 | 1639 | |
841ab66c SZ |
1640 | for (; sc->rxq.cur != hw;) { |
1641 | data = &sc->rxq.data[sc->rxq.cur]; | |
b50e4759 | 1642 | |
841ab66c | 1643 | bus_dmamap_sync(sc->rxq.data_dmat, data->map, |
b50e4759 MD |
1644 | BUS_DMASYNC_POSTREAD); |
1645 | ||
841ab66c | 1646 | hdr = mtod(data->m, struct iwi_hdr *); |
b50e4759 MD |
1647 | |
1648 | switch (hdr->type) { | |
1649 | case IWI_HDR_TYPE_FRAME: | |
841ab66c | 1650 | iwi_frame_intr(sc, data, sc->rxq.cur, |
b50e4759 MD |
1651 | (struct iwi_frame *)(hdr + 1)); |
1652 | break; | |
1653 | ||
1654 | case IWI_HDR_TYPE_NOTIF: | |
1655 | iwi_notification_intr(sc, | |
1656 | (struct iwi_notif *)(hdr + 1)); | |
1657 | break; | |
1658 | ||
1659 | default: | |
1660 | device_printf(sc->sc_dev, "unknown hdr type %u\n", | |
1661 | hdr->type); | |
1662 | } | |
841ab66c SZ |
1663 | |
1664 | DPRINTFN(15, ("rx done idx=%u\n", sc->rxq.cur)); | |
1665 | ||
1666 | sc->rxq.cur = (sc->rxq.cur + 1) % IWI_RX_RING_COUNT; | |
b50e4759 MD |
1667 | } |
1668 | ||
0e474d75 | 1669 | /* tell the firmware what we have processed */ |
841ab66c SZ |
1670 | hw = (hw == 0) ? IWI_RX_RING_COUNT - 1 : hw - 1; |
1671 | CSR_WRITE_4(sc, IWI_CSR_RX_WIDX, hw); | |
b50e4759 MD |
1672 | } |
1673 | ||
1674 | static void | |
841ab66c | 1675 | iwi_tx_intr(struct iwi_softc *sc, struct iwi_tx_ring *txq) |
b50e4759 | 1676 | { |
841ab66c SZ |
1677 | struct iwi_tx_data *data; |
1678 | uint32_t hw; | |
b50e4759 | 1679 | |
841ab66c | 1680 | hw = CSR_READ_4(sc, txq->csr_ridx); |
b50e4759 | 1681 | |
c4fe7bb1 | 1682 | while (txq->next != hw) { |
841ab66c | 1683 | data = &txq->data[txq->next]; |
c4fe7bb1 | 1684 | DPRINTFN(15, ("tx done idx=%u\n", txq->next)); |
841ab66c | 1685 | bus_dmamap_sync(txq->data_dmat, data->map, |
b50e4759 | 1686 | BUS_DMASYNC_POSTWRITE); |
841ab66c | 1687 | bus_dmamap_unload(txq->data_dmat, data->map); |
c4fe7bb1 | 1688 | ieee80211_tx_complete(data->ni, data->m, 0); |
841ab66c | 1689 | data->ni = NULL; |
c4fe7bb1 | 1690 | data->m = NULL; |
841ab66c SZ |
1691 | txq->queued--; |
1692 | txq->next = (txq->next + 1) % IWI_TX_RING_COUNT; | |
1693 | } | |
841ab66c | 1694 | sc->sc_tx_timer = 0; |
0e474d75 JH |
1695 | if (sc->sc_softled) |
1696 | iwi_led_event(sc, IWI_LED_TX); | |
c4fe7bb1 | 1697 | iwi_start(sc); |
0e474d75 JH |
1698 | } |
1699 | ||
1700 | static void | |
1701 | iwi_fatal_error_intr(struct iwi_softc *sc) | |
1702 | { | |
c4fe7bb1 | 1703 | struct ieee80211com *ic = &sc->sc_ic; |
0e474d75 JH |
1704 | struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); |
1705 | ||
1706 | device_printf(sc->sc_dev, "firmware error\n"); | |
1707 | if (vap != NULL) | |
1708 | ieee80211_cancel_scan(vap); | |
1709 | ieee80211_runtask(ic, &sc->sc_restarttask); | |
1710 | ||
1711 | sc->flags &= ~IWI_FLAG_BUSY; | |
1712 | sc->sc_busy_timer = 0; | |
1713 | wakeup(sc); | |
1714 | } | |
1715 | ||
1716 | static void | |
1717 | iwi_radio_off_intr(struct iwi_softc *sc) | |
1718 | { | |
0e474d75 | 1719 | |
c4fe7bb1 | 1720 | ieee80211_runtask(&sc->sc_ic, &sc->sc_radiofftask); |
b50e4759 MD |
1721 | } |
1722 | ||
1723 | static void | |
1724 | iwi_intr(void *arg) | |
1725 | { | |
1726 | struct iwi_softc *sc = arg; | |
841ab66c | 1727 | uint32_t r; |
c4fe7bb1 IV |
1728 | IWI_LOCK_DECL; |
1729 | ||
1730 | IWI_LOCK(sc); | |
b50e4759 | 1731 | |
0e474d75 | 1732 | if ((r = CSR_READ_4(sc, IWI_CSR_INTR)) == 0 || r == 0xffffffff) { |
c4fe7bb1 | 1733 | IWI_UNLOCK(sc); |
b50e4759 | 1734 | return; |
0e474d75 | 1735 | } |
b50e4759 | 1736 | |
0e474d75 JH |
1737 | /* acknowledge interrupts */ |
1738 | CSR_WRITE_4(sc, IWI_CSR_INTR, r); | |
b50e4759 | 1739 | |
3660cb22 | 1740 | if (r & IWI_INTR_FATAL_ERROR) { |
0e474d75 | 1741 | iwi_fatal_error_intr(sc); |
c4fe7bb1 | 1742 | goto done; |
b50e4759 MD |
1743 | } |
1744 | ||
1745 | if (r & IWI_INTR_FW_INITED) { | |
1746 | if (!(r & (IWI_INTR_FATAL_ERROR | IWI_INTR_PARITY_ERROR))) | |
0e474d75 | 1747 | wakeup(sc); |
b50e4759 MD |
1748 | } |
1749 | ||
0e474d75 JH |
1750 | if (r & IWI_INTR_RADIO_OFF) |
1751 | iwi_radio_off_intr(sc); | |
b50e4759 | 1752 | |
0e474d75 JH |
1753 | if (r & IWI_INTR_CMD_DONE) { |
1754 | sc->flags &= ~IWI_FLAG_BUSY; | |
1755 | sc->sc_busy_timer = 0; | |
1756 | wakeup(sc); | |
1757 | } | |
b50e4759 | 1758 | |
841ab66c SZ |
1759 | if (r & IWI_INTR_TX1_DONE) |
1760 | iwi_tx_intr(sc, &sc->txq[0]); | |
b50e4759 | 1761 | |
841ab66c SZ |
1762 | if (r & IWI_INTR_TX2_DONE) |
1763 | iwi_tx_intr(sc, &sc->txq[1]); | |
b50e4759 | 1764 | |
841ab66c SZ |
1765 | if (r & IWI_INTR_TX3_DONE) |
1766 | iwi_tx_intr(sc, &sc->txq[2]); | |
b50e4759 | 1767 | |
841ab66c SZ |
1768 | if (r & IWI_INTR_TX4_DONE) |
1769 | iwi_tx_intr(sc, &sc->txq[3]); | |
b50e4759 | 1770 | |
841ab66c SZ |
1771 | if (r & IWI_INTR_RX_DONE) |
1772 | iwi_rx_intr(sc); | |
b50e4759 | 1773 | |
0e474d75 JH |
1774 | if (r & IWI_INTR_PARITY_ERROR) { |
1775 | /* XXX rate-limit */ | |
1776 | device_printf(sc->sc_dev, "parity error\n"); | |
1777 | } | |
c4fe7bb1 IV |
1778 | done: |
1779 | IWI_UNLOCK(sc); | |
b50e4759 MD |
1780 | } |
1781 | ||
1782 | static int | |
0e474d75 | 1783 | iwi_cmd(struct iwi_softc *sc, uint8_t type, void *data, uint8_t len) |
b50e4759 MD |
1784 | { |
1785 | struct iwi_cmd_desc *desc; | |
0e474d75 | 1786 | |
c4fe7bb1 IV |
1787 | IWI_LOCK_ASSERT(sc); |
1788 | ||
0e474d75 JH |
1789 | if (sc->flags & IWI_FLAG_BUSY) { |
1790 | device_printf(sc->sc_dev, "%s: cmd %d not sent, busy\n", | |
1791 | __func__, type); | |
1792 | return EAGAIN; | |
1793 | } | |
0e474d75 JH |
1794 | sc->flags |= IWI_FLAG_BUSY; |
1795 | sc->sc_busy_timer = 2; | |
b50e4759 | 1796 | |
841ab66c | 1797 | desc = &sc->cmdq.desc[sc->cmdq.cur]; |
b50e4759 | 1798 | |
b50e4759 MD |
1799 | desc->hdr.type = IWI_HDR_TYPE_COMMAND; |
1800 | desc->hdr.flags = IWI_HDR_FLAG_IRQ; | |
1801 | desc->type = type; | |
1802 | desc->len = len; | |
841ab66c | 1803 | memcpy(desc->data, data, len); |
b50e4759 | 1804 | |
841ab66c | 1805 | bus_dmamap_sync(sc->cmdq.desc_dmat, sc->cmdq.desc_map, |
b50e4759 MD |
1806 | BUS_DMASYNC_PREWRITE); |
1807 | ||
841ab66c SZ |
1808 | DPRINTFN(2, ("sending command idx=%u type=%u len=%u\n", sc->cmdq.cur, |
1809 | type, len)); | |
1810 | ||
1811 | sc->cmdq.cur = (sc->cmdq.cur + 1) % IWI_CMD_RING_COUNT; | |
1812 | CSR_WRITE_4(sc, IWI_CSR_CMD_WIDX, sc->cmdq.cur); | |
c4fe7bb1 IV |
1813 | |
1814 | #if defined(__DragonFly__) | |
1815 | return lksleep(sc, &sc->sc_lock, 0, "iwicmd", hz); | |
1816 | #else | |
1817 | return msleep(sc, &sc->sc_mtx, 0, "iwicmd", hz); | |
1818 | #endif | |
841ab66c SZ |
1819 | } |
1820 | ||
1821 | static void | |
0e474d75 JH |
1822 | iwi_write_ibssnode(struct iwi_softc *sc, |
1823 | const u_int8_t addr[IEEE80211_ADDR_LEN], int entry) | |
841ab66c SZ |
1824 | { |
1825 | struct iwi_ibssnode node; | |
1826 | ||
1827 | /* write node information into NIC memory */ | |
1828 | memset(&node, 0, sizeof node); | |
0e474d75 | 1829 | IEEE80211_ADDR_COPY(node.bssid, addr); |
c4fe7bb1 IV |
1830 | #if defined(__DragonFly__) |
1831 | DPRINTF(("%s mac %s station %u\n", __func__, ether_sprintf(node.bssid), | |
1832 | entry)); | |
1833 | #else | |
1834 | DPRINTF(("%s mac %6D station %u\n", __func__, node.bssid, ":", entry)); | |
1835 | #endif | |
841ab66c SZ |
1836 | |
1837 | CSR_WRITE_REGION_1(sc, | |
0e474d75 | 1838 | IWI_CSR_NODE_BASE + entry * sizeof node, |
841ab66c SZ |
1839 | (uint8_t *)&node, sizeof node); |
1840 | } | |
1841 | ||
b50e4759 | 1842 | static int |
c4fe7bb1 | 1843 | iwi_tx_start(struct iwi_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, |
841ab66c | 1844 | int ac) |
b50e4759 | 1845 | { |
0e474d75 JH |
1846 | struct ieee80211vap *vap = ni->ni_vap; |
1847 | struct ieee80211com *ic = ni->ni_ic; | |
841ab66c | 1848 | struct iwi_node *in = (struct iwi_node *)ni; |
0e474d75 | 1849 | const struct ieee80211_frame *wh; |
841ab66c SZ |
1850 | struct ieee80211_key *k; |
1851 | const struct chanAccParams *cap; | |
1852 | struct iwi_tx_ring *txq = &sc->txq[ac]; | |
1853 | struct iwi_tx_data *data; | |
b50e4759 | 1854 | struct iwi_tx_desc *desc; |
b50e4759 | 1855 | struct mbuf *mnew; |
0e474d75 JH |
1856 | bus_dma_segment_t segs[IWI_MAX_NSEG]; |
1857 | int error, nsegs, hdrlen, i; | |
1858 | int ismcast, flags, xflags, staid; | |
1859 | ||
c4fe7bb1 | 1860 | IWI_LOCK_ASSERT(sc); |
0e474d75 JH |
1861 | wh = mtod(m0, const struct ieee80211_frame *); |
1862 | /* NB: only data frames use this path */ | |
1863 | hdrlen = ieee80211_hdrsize(wh); | |
1864 | ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); | |
1865 | flags = xflags = 0; | |
1866 | ||
1867 | if (!ismcast) | |
1868 | flags |= IWI_DATA_FLAG_NEED_ACK; | |
1869 | if (vap->iv_flags & IEEE80211_F_SHPREAMBLE) | |
1870 | flags |= IWI_DATA_FLAG_SHPREAMBLE; | |
1871 | if (IEEE80211_QOS_HAS_SEQ(wh)) { | |
1872 | xflags |= IWI_DATA_XFLAG_QOS; | |
841ab66c | 1873 | cap = &ic->ic_wme.wme_chanParams; |
0e474d75 JH |
1874 | if (!cap->cap_wmeParams[ac].wmep_noackPolicy) |
1875 | flags &= ~IWI_DATA_FLAG_NEED_ACK; | |
1876 | } | |
841ab66c SZ |
1877 | |
1878 | /* | |
1879 | * This is only used in IBSS mode where the firmware expect an index | |
1880 | * in a h/w table instead of a destination address. | |
1881 | */ | |
0e474d75 JH |
1882 | if (vap->iv_opmode == IEEE80211_M_IBSS) { |
1883 | if (!ismcast) { | |
1884 | if (in->in_station == -1) { | |
c4fe7bb1 | 1885 | #if defined(__DragonFly__) |
0e474d75 JH |
1886 | in->in_station = devfs_clone_bitmap_get(&sc->sc_unr, |
1887 | IWI_MAX_IBSSNODE-1); | |
c4fe7bb1 IV |
1888 | #else |
1889 | in->in_station = alloc_unr(sc->sc_unr); | |
1890 | #endif | |
0e474d75 JH |
1891 | if (in->in_station == -1) { |
1892 | /* h/w table is full */ | |
c4fe7bb1 IV |
1893 | if_inc_counter(ni->ni_vap->iv_ifp, |
1894 | IFCOUNTER_OERRORS, 1); | |
0e474d75 JH |
1895 | m_freem(m0); |
1896 | ieee80211_free_node(ni); | |
0e474d75 JH |
1897 | return 0; |
1898 | } | |
1899 | iwi_write_ibssnode(sc, | |
1900 | ni->ni_macaddr, in->in_station); | |
1901 | } | |
1902 | staid = in->in_station; | |
1903 | } else { | |
1904 | /* | |
1905 | * Multicast addresses have no associated node | |
1906 | * so there will be no station entry. We reserve | |
1907 | * entry 0 for one mcast address and use that. | |
1908 | * If there are many being used this will be | |
1909 | * expensive and we'll need to do a better job | |
1910 | * but for now this handles the broadcast case. | |
1911 | */ | |
1912 | if (!IEEE80211_ADDR_EQ(wh->i_addr1, sc->sc_mcast)) { | |
1913 | IEEE80211_ADDR_COPY(sc->sc_mcast, wh->i_addr1); | |
1914 | iwi_write_ibssnode(sc, sc->sc_mcast, 0); | |
1915 | } | |
1916 | staid = 0; | |
841ab66c | 1917 | } |
0e474d75 JH |
1918 | } else |
1919 | staid = 0; | |
841ab66c | 1920 | |
085ff963 | 1921 | if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { |
0e474d75 | 1922 | k = ieee80211_crypto_encap(ni, m0); |
841ab66c SZ |
1923 | if (k == NULL) { |
1924 | m_freem(m0); | |
1925 | return ENOBUFS; | |
1926 | } | |
2a0b1ca7 SW |
1927 | |
1928 | /* packet header may have moved, reset our local pointer */ | |
1929 | wh = mtod(m0, struct ieee80211_frame *); | |
841ab66c | 1930 | } |
b50e4759 | 1931 | |
0e474d75 | 1932 | if (ieee80211_radiotap_active_vap(vap)) { |
b50e4759 MD |
1933 | struct iwi_tx_radiotap_header *tap = &sc->sc_txtap; |
1934 | ||
1935 | tap->wt_flags = 0; | |
b50e4759 | 1936 | |
0e474d75 | 1937 | ieee80211_radiotap_tx(vap, m0); |
b50e4759 MD |
1938 | } |
1939 | ||
841ab66c SZ |
1940 | data = &txq->data[txq->cur]; |
1941 | desc = &txq->desc[txq->cur]; | |
b50e4759 | 1942 | |
841ab66c | 1943 | /* save and trim IEEE802.11 header */ |
05d02a38 | 1944 | m_copydata(m0, 0, hdrlen, &desc->wh); |
841ab66c | 1945 | m_adj(m0, hdrlen); |
b50e4759 | 1946 | |
c4fe7bb1 | 1947 | #if defined(__DragonFly__) |
0e474d75 JH |
1948 | error = bus_dmamap_load_mbuf_segment(txq->data_dmat, data->map, |
1949 | m0, segs, 1, &nsegs, BUS_DMA_NOWAIT); | |
c4fe7bb1 IV |
1950 | #else |
1951 | error = bus_dmamap_load_mbuf_sg(txq->data_dmat, data->map, m0, segs, | |
1952 | &nsegs, 0); | |
1953 | #endif | |
b50e4759 MD |
1954 | if (error != 0 && error != EFBIG) { |
1955 | device_printf(sc->sc_dev, "could not map mbuf (error %d)\n", | |
1956 | error); | |
1957 | m_freem(m0); | |
b50e4759 MD |
1958 | return error; |
1959 | } | |
1960 | if (error != 0) { | |
b5523eac | 1961 | mnew = m_defrag(m0, M_NOWAIT); |
b50e4759 MD |
1962 | if (mnew == NULL) { |
1963 | device_printf(sc->sc_dev, | |
1964 | "could not defragment mbuf\n"); | |
1965 | m_freem(m0); | |
b50e4759 MD |
1966 | return ENOBUFS; |
1967 | } | |
1968 | m0 = mnew; | |
1969 | ||
c4fe7bb1 | 1970 | #if defined(__DragonFly__) |
0e474d75 JH |
1971 | error = bus_dmamap_load_mbuf_segment(txq->data_dmat, |
1972 | data->map, m0, segs, 1, &nsegs, BUS_DMA_NOWAIT); | |
c4fe7bb1 IV |
1973 | #else |
1974 | error = bus_dmamap_load_mbuf_sg(txq->data_dmat, data->map, | |
1975 | m0, segs, &nsegs, 0); | |
1976 | #endif | |
b50e4759 MD |
1977 | if (error != 0) { |
1978 | device_printf(sc->sc_dev, | |
1979 | "could not map mbuf (error %d)\n", error); | |
1980 | m_freem(m0); | |
b50e4759 MD |
1981 | return error; |
1982 | } | |
1983 | } | |
1984 | ||
841ab66c SZ |
1985 | data->m = m0; |
1986 | data->ni = ni; | |
b50e4759 MD |
1987 | |
1988 | desc->hdr.type = IWI_HDR_TYPE_DATA; | |
1989 | desc->hdr.flags = IWI_HDR_FLAG_IRQ; | |
0e474d75 | 1990 | desc->station = staid; |
b50e4759 MD |
1991 | desc->cmd = IWI_DATA_CMD_TX; |
1992 | desc->len = htole16(m0->m_pkthdr.len); | |
0e474d75 JH |
1993 | desc->flags = flags; |
1994 | desc->xflags = xflags; | |
b50e4759 | 1995 | |
841ab66c | 1996 | #if 0 |
0e474d75 JH |
1997 | if (vap->iv_flags & IEEE80211_F_PRIVACY) |
1998 | desc->wep_txkey = vap->iv_def_txkey; | |
1999 | else | |
841ab66c | 2000 | #endif |
b50e4759 MD |
2001 | desc->flags |= IWI_DATA_FLAG_NO_WEP; |
2002 | ||
0e474d75 JH |
2003 | desc->nseg = htole32(nsegs); |
2004 | for (i = 0; i < nsegs; i++) { | |
2005 | desc->seg_addr[i] = htole32(segs[i].ds_addr); | |
2006 | desc->seg_len[i] = htole16(segs[i].ds_len); | |
b50e4759 MD |
2007 | } |
2008 | ||
841ab66c SZ |
2009 | bus_dmamap_sync(txq->data_dmat, data->map, BUS_DMASYNC_PREWRITE); |
2010 | bus_dmamap_sync(txq->desc_dmat, txq->desc_map, BUS_DMASYNC_PREWRITE); | |
b50e4759 | 2011 | |
841ab66c | 2012 | DPRINTFN(5, ("sending data frame txq=%u idx=%u len=%u nseg=%u\n", |
0e474d75 | 2013 | ac, txq->cur, le16toh(desc->len), nsegs)); |
b50e4759 | 2014 | |
841ab66c SZ |
2015 | txq->queued++; |
2016 | txq->cur = (txq->cur + 1) % IWI_TX_RING_COUNT; | |
2017 | CSR_WRITE_4(sc, txq->csr_widx, txq->cur); | |
b50e4759 | 2018 | |
b50e4759 MD |
2019 | return 0; |
2020 | } | |
2021 | ||
0e474d75 JH |
2022 | static int |
2023 | iwi_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, | |
2024 | const struct ieee80211_bpf_params *params) | |
2025 | { | |
2026 | /* no support; just discard */ | |
2027 | m_freem(m); | |
2028 | ieee80211_free_node(ni); | |
2029 | return 0; | |
2030 | } | |
2031 | ||
c4fe7bb1 IV |
2032 | static int |
2033 | iwi_transmit(struct ieee80211com *ic, struct mbuf *m) | |
2034 | { | |
2035 | struct iwi_softc *sc = ic->ic_softc; | |
2036 | int error; | |
2037 | IWI_LOCK_DECL; | |
2038 | ||
2039 | IWI_LOCK(sc); | |
2040 | if (!sc->sc_running) { | |
2041 | IWI_UNLOCK(sc); | |
2042 | return (ENXIO); | |
2043 | } | |
2044 | error = mbufq_enqueue(&sc->sc_snd, m); | |
2045 | if (error) { | |
2046 | IWI_UNLOCK(sc); | |
2047 | return (error); | |
2048 | } | |
2049 | iwi_start(sc); | |
2050 | IWI_UNLOCK(sc); | |
2051 | return (0); | |
2052 | } | |
2053 | ||
b50e4759 | 2054 | static void |
c4fe7bb1 | 2055 | iwi_start(struct iwi_softc *sc) |
b50e4759 | 2056 | { |
0e474d75 | 2057 | struct mbuf *m; |
b50e4759 | 2058 | struct ieee80211_node *ni; |
841ab66c | 2059 | int ac; |
b50e4759 | 2060 | |
c4fe7bb1 | 2061 | IWI_LOCK_ASSERT(sc); |
b866247b | 2062 | |
c4fe7bb1 | 2063 | while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) { |
0e474d75 | 2064 | ac = M_WME_GETAC(m); |
841ab66c | 2065 | if (sc->txq[ac].queued > IWI_TX_RING_COUNT - 8) { |
0e474d75 JH |
2066 | /* there is no place left in this ring; tail drop */ |
2067 | /* XXX tail drop */ | |
c4fe7bb1 | 2068 | mbufq_prepend(&sc->sc_snd, m); |
b50e4759 MD |
2069 | break; |
2070 | } | |
0e474d75 | 2071 | ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; |
c4fe7bb1 | 2072 | if (iwi_tx_start(sc, m, ni, ac) != 0) { |
841ab66c | 2073 | ieee80211_free_node(ni); |
c4fe7bb1 IV |
2074 | if_inc_counter(ni->ni_vap->iv_ifp, |
2075 | IFCOUNTER_OERRORS, 1); | |
b50e4759 MD |
2076 | break; |
2077 | } | |
b50e4759 | 2078 | sc->sc_tx_timer = 5; |
b50e4759 | 2079 | } |
b50e4759 MD |
2080 | } |
2081 | ||
0e474d75 JH |
2082 | static void |
2083 | iwi_watchdog(void *arg) | |
2084 | { | |
2085 | struct iwi_softc *sc = arg; | |
c4fe7bb1 IV |
2086 | struct ieee80211com *ic = &sc->sc_ic; |
2087 | ||
2088 | IWI_LOCK_ASSERT(sc); | |
b50e4759 | 2089 | |
b50e4759 MD |
2090 | if (sc->sc_tx_timer > 0) { |
2091 | if (--sc->sc_tx_timer == 0) { | |
c4fe7bb1 IV |
2092 | device_printf(sc->sc_dev, "device timeout\n"); |
2093 | #if defined(__DragonFly__) | |
2094 | ++ic->ic_oerrors; | |
2095 | #else | |
2096 | counter_u64_add(ic->ic_oerrors, 1); | |
2097 | #endif | |
0e474d75 | 2098 | ieee80211_runtask(ic, &sc->sc_restarttask); |
b50e4759 | 2099 | } |
b50e4759 | 2100 | } |
0e474d75 JH |
2101 | if (sc->sc_state_timer > 0) { |
2102 | if (--sc->sc_state_timer == 0) { | |
c4fe7bb1 IV |
2103 | device_printf(sc->sc_dev, |
2104 | "firmware stuck in state %d, resetting\n", | |
0e474d75 | 2105 | sc->fw_state); |
c4fe7bb1 | 2106 | if (sc->fw_state == IWI_FW_SCANNING) |
0e474d75 | 2107 | ieee80211_cancel_scan(TAILQ_FIRST(&ic->ic_vaps)); |
0e474d75 JH |
2108 | ieee80211_runtask(ic, &sc->sc_restarttask); |
2109 | sc->sc_state_timer = 3; | |
2110 | } | |
2111 | } | |
2112 | if (sc->sc_busy_timer > 0) { | |
2113 | if (--sc->sc_busy_timer == 0) { | |
c4fe7bb1 IV |
2114 | device_printf(sc->sc_dev, |
2115 | "firmware command timeout, resetting\n"); | |
0e474d75 JH |
2116 | ieee80211_runtask(ic, &sc->sc_restarttask); |
2117 | } | |
2118 | } | |
c4fe7bb1 | 2119 | callout_reset(&sc->sc_wdtimer, hz, iwi_watchdog, sc); |
b50e4759 MD |
2120 | } |
2121 | ||
c4fe7bb1 IV |
2122 | static void |
2123 | iwi_parent(struct ieee80211com *ic) | |
2124 | { | |
2125 | struct iwi_softc *sc = ic->ic_softc; | |
2126 | int startall = 0; | |
2127 | IWI_LOCK_DECL; | |
2128 | ||
2129 | IWI_LOCK(sc); | |
2130 | if (ic->ic_nrunning > 0) { | |
2131 | if (!sc->sc_running) { | |
2132 | iwi_init_locked(sc); | |
2133 | startall = 1; | |
b50e4759 | 2134 | } |
c4fe7bb1 IV |
2135 | } else if (sc->sc_running) |
2136 | iwi_stop_locked(sc); | |
2137 | IWI_UNLOCK(sc); | |
2138 | if (startall) | |
2139 | ieee80211_start_all(ic); | |
b50e4759 MD |
2140 | } |
2141 | ||
2142 | static void | |
2143 | iwi_stop_master(struct iwi_softc *sc) | |
2144 | { | |
841ab66c | 2145 | uint32_t tmp; |
b50e4759 MD |
2146 | int ntries; |
2147 | ||
841ab66c | 2148 | /* disable interrupts */ |
b50e4759 MD |
2149 | CSR_WRITE_4(sc, IWI_CSR_INTR_MASK, 0); |
2150 | ||
2151 | CSR_WRITE_4(sc, IWI_CSR_RST, IWI_RST_STOP_MASTER); | |
2152 | for (ntries = 0; ntries < 5; ntries++) { | |
2153 | if (CSR_READ_4(sc, IWI_CSR_RST) & IWI_RST_MASTER_DISABLED) | |
2154 | break; | |
2155 | DELAY(10); | |
2156 | } | |
841ab66c | 2157 | if (ntries == 5) |
0e474d75 | 2158 | device_printf(sc->sc_dev, "timeout waiting for master\n"); |
b50e4759 | 2159 | |
841ab66c SZ |
2160 | tmp = CSR_READ_4(sc, IWI_CSR_RST); |
2161 | CSR_WRITE_4(sc, IWI_CSR_RST, tmp | IWI_RST_PRINCETON_RESET); | |
b50e4759 MD |
2162 | |
2163 | sc->flags &= ~IWI_FLAG_FW_INITED; | |
2164 | } | |
2165 | ||
2166 | static int | |
2167 | iwi_reset(struct iwi_softc *sc) | |
2168 | { | |
841ab66c | 2169 | uint32_t tmp; |
b50e4759 MD |
2170 | int i, ntries; |
2171 | ||
2172 | iwi_stop_master(sc); | |
2173 | ||
841ab66c SZ |
2174 | tmp = CSR_READ_4(sc, IWI_CSR_CTL); |
2175 | CSR_WRITE_4(sc, IWI_CSR_CTL, tmp | IWI_CTL_INIT); | |
b50e4759 | 2176 | |
b50e4759 MD |
2177 | CSR_WRITE_4(sc, IWI_CSR_READ_INT, IWI_READ_INT_INIT_HOST); |
2178 | ||
0e474d75 | 2179 | /* wait for clock stabilization */ |
b50e4759 MD |
2180 | for (ntries = 0; ntries < 1000; ntries++) { |
2181 | if (CSR_READ_4(sc, IWI_CSR_CTL) & IWI_CTL_CLOCK_READY) | |
2182 | break; | |
2183 | DELAY(200); | |
2184 | } | |
2185 | if (ntries == 1000) { | |
0e474d75 JH |
2186 | device_printf(sc->sc_dev, |
2187 | "timeout waiting for clock stabilization\n"); | |
b50e4759 MD |
2188 | return EIO; |
2189 | } | |
2190 | ||
841ab66c SZ |
2191 | tmp = CSR_READ_4(sc, IWI_CSR_RST); |
2192 | CSR_WRITE_4(sc, IWI_CSR_RST, tmp | IWI_RST_SOFT_RESET); | |
b50e4759 MD |
2193 | |
2194 | DELAY(10); | |
2195 | ||
841ab66c SZ |
2196 | tmp = CSR_READ_4(sc, IWI_CSR_CTL); |
2197 | CSR_WRITE_4(sc, IWI_CSR_CTL, tmp | IWI_CTL_INIT); | |
b50e4759 | 2198 | |
0e474d75 | 2199 | /* clear NIC memory */ |
b50e4759 | 2200 | CSR_WRITE_4(sc, IWI_CSR_AUTOINC_ADDR, 0); |
841ab66c | 2201 | for (i = 0; i < 0xc000; i++) |
b50e4759 | 2202 | CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, 0); |
b50e4759 | 2203 | |
b50e4759 MD |
2204 | return 0; |
2205 | } | |
2206 | ||
0e474d75 JH |
2207 | static const struct iwi_firmware_ohdr * |
2208 | iwi_setup_ofw(struct iwi_softc *sc, struct iwi_fw *fw) | |
b50e4759 | 2209 | { |
0e474d75 JH |
2210 | const struct firmware *fp = fw->fp; |
2211 | const struct iwi_firmware_ohdr *hdr; | |
b50e4759 | 2212 | |
0e474d75 JH |
2213 | if (fp->datasize < sizeof (struct iwi_firmware_ohdr)) { |
2214 | device_printf(sc->sc_dev, "image '%s' too small\n", fp->name); | |
2215 | return NULL; | |
b50e4759 | 2216 | } |
0e474d75 JH |
2217 | hdr = (const struct iwi_firmware_ohdr *)fp->data; |
2218 | if ((IWI_FW_GET_MAJOR(le32toh(hdr->version)) != IWI_FW_REQ_MAJOR) || | |
2219 | (IWI_FW_GET_MINOR(le32toh(hdr->version)) != IWI_FW_REQ_MINOR)) { | |
2220 | device_printf(sc->sc_dev, "version for '%s' %d.%d != %d.%d\n", | |
2221 | fp->name, IWI_FW_GET_MAJOR(le32toh(hdr->version)), | |
2222 | IWI_FW_GET_MINOR(le32toh(hdr->version)), IWI_FW_REQ_MAJOR, | |
2223 | IWI_FW_REQ_MINOR); | |
2224 | return NULL; | |
b50e4759 | 2225 | } |
0e474d75 JH |
2226 | fw->data = ((const char *) fp->data) + sizeof(struct iwi_firmware_ohdr); |
2227 | fw->size = fp->datasize - sizeof(struct iwi_firmware_ohdr); | |
2228 | fw->name = fp->name; | |
2229 | return hdr; | |
2230 | } | |
b50e4759 | 2231 | |
0e474d75 JH |
2232 | static const struct iwi_firmware_ohdr * |
2233 | iwi_setup_oucode(struct iwi_softc *sc, struct iwi_fw *fw) | |
2234 | { | |
2235 | const struct iwi_firmware_ohdr *hdr; | |
841ab66c | 2236 | |
0e474d75 JH |
2237 | hdr = iwi_setup_ofw(sc, fw); |
2238 | if (hdr != NULL && le32toh(hdr->mode) != IWI_FW_MODE_UCODE) { | |
2239 | device_printf(sc->sc_dev, "%s is not a ucode image\n", | |
2240 | fw->name); | |
2241 | hdr = NULL; | |
2242 | } | |
2243 | return hdr; | |
2244 | } | |
2245 | ||
2246 | static void | |
2247 | iwi_getfw(struct iwi_fw *fw, const char *fwname, | |
2248 | struct iwi_fw *uc, const char *ucname) | |
2249 | { | |
2250 | if (fw->fp == NULL) | |
2251 | fw->fp = firmware_get(fwname); | |
0e474d75 JH |
2252 | /* NB: pre-3.0 ucode is packaged separately */ |
2253 | if (uc->fp == NULL && fw->fp != NULL && fw->fp->version < 300) | |
2254 | uc->fp = firmware_get(ucname); | |
2255 | } | |
2256 | ||
2257 | /* | |
2258 | * Get the required firmware images if not already loaded. | |
2259 | * Note that we hold firmware images so long as the device | |
2260 | * is marked up in case we need to reload them on device init. | |
2261 | * This is necessary because we re-init the device sometimes | |
2262 | * from a context where we cannot read from the filesystem | |
2263 | * (e.g. from the taskqueue thread when rfkill is re-enabled). | |
2264 | * XXX return 0 on success, 1 on error. | |
2265 | * | |
2266 | * NB: the order of get'ing and put'ing images here is | |
2267 | * intentional to support handling firmware images bundled | |
2268 | * by operating mode and/or all together in one file with | |
2269 | * the boot firmware as "master". | |
2270 | */ | |
2271 | static int | |
2272 | iwi_get_firmware(struct iwi_softc *sc, enum ieee80211_opmode opmode) | |
2273 | { | |
2274 | const struct iwi_firmware_hdr *hdr; | |
2275 | const struct firmware *fp; | |
2276 | ||
2277 | /* invalidate cached firmware on mode change */ | |
2278 | if (sc->fw_mode != opmode) | |
2279 | iwi_put_firmware(sc); | |
2280 | ||
2281 | switch (opmode) { | |
2282 | case IEEE80211_M_STA: | |
2283 | iwi_getfw(&sc->fw_fw, "iwi_bss", &sc->fw_uc, "iwi_ucode_bss"); | |
2284 | break; | |
2285 | case IEEE80211_M_IBSS: | |
2286 | iwi_getfw(&sc->fw_fw, "iwi_ibss", &sc->fw_uc, "iwi_ucode_ibss"); | |
2287 | break; | |
2288 | case IEEE80211_M_MONITOR: | |
2289 | iwi_getfw(&sc->fw_fw, "iwi_monitor", | |
2290 | &sc->fw_uc, "iwi_ucode_monitor"); | |
2291 | break; | |
2292 | default: | |
2293 | device_printf(sc->sc_dev, "unknown opmode %d\n", opmode); | |
2294 | return EINVAL; | |
2295 | } | |
2296 | fp = sc->fw_fw.fp; | |
2297 | if (fp == NULL) { | |
2298 | device_printf(sc->sc_dev, "could not load firmware\n"); | |
2299 | goto bad; | |
2300 | } | |
2301 | if (fp->version < 300) { | |
2302 | /* | |
2303 | * Firmware prior to 3.0 was packaged as separate | |
2304 | * boot, firmware, and ucode images. Verify the | |
2305 | * ucode image was read in, retrieve the boot image | |
2306 | * if needed, and check version stamps for consistency. | |
2307 | * The version stamps in the data are also checked | |
2308 | * above; this is a bit paranoid but is a cheap | |
2309 | * safeguard against mis-packaging. | |
2310 | */ | |
2311 | if (sc->fw_uc.fp == NULL) { | |
2312 | device_printf(sc->sc_dev, "could not load ucode\n"); | |
2313 | goto bad; | |
2314 | } | |
2315 | if (sc->fw_boot.fp == NULL) { | |
2316 | sc->fw_boot.fp = firmware_get("iwi_boot"); | |
2317 | if (sc->fw_boot.fp == NULL) { | |
2318 | device_printf(sc->sc_dev, | |
2319 | "could not load boot firmware\n"); | |
2320 | goto bad; | |
2321 | } | |
2322 | } | |
2323 | if (sc->fw_boot.fp->version != sc->fw_fw.fp->version || | |
2324 | sc->fw_boot.fp->version != sc->fw_uc.fp->version) { | |
2325 | device_printf(sc->sc_dev, | |
2326 | "firmware version mismatch: " | |
2327 | "'%s' is %d, '%s' is %d, '%s' is %d\n", | |
2328 | sc->fw_boot.fp->name, sc->fw_boot.fp->version, | |
2329 | sc->fw_uc.fp->name, sc->fw_uc.fp->version, | |
2330 | sc->fw_fw.fp->name, sc->fw_fw.fp->version | |
2331 | ); | |
2332 | goto bad; | |
2333 | } | |
2334 | /* | |
2335 | * Check and setup each image. | |
2336 | */ | |
2337 | if (iwi_setup_oucode(sc, &sc->fw_uc) == NULL || | |
2338 | iwi_setup_ofw(sc, &sc->fw_boot) == NULL || | |
2339 | iwi_setup_ofw(sc, &sc->fw_fw) == NULL) | |
2340 | goto bad; | |
2341 | } else { | |
2342 | /* | |
2343 | * Check and setup combined image. | |
2344 | */ | |
2345 | if (fp->datasize < sizeof(struct iwi_firmware_hdr)) { | |
2346 | device_printf(sc->sc_dev, "image '%s' too small\n", | |
2347 | fp->name); | |
2348 | goto bad; | |
2349 | } | |
2350 | hdr = (const struct iwi_firmware_hdr *)fp->data; | |
2351 | if (fp->datasize < sizeof(*hdr) + le32toh(hdr->bsize) + le32toh(hdr->usize) | |
2352 | + le32toh(hdr->fsize)) { | |
2353 | device_printf(sc->sc_dev, "image '%s' too small (2)\n", | |
2354 | fp->name); | |
2355 | goto bad; | |
2356 | } | |
2357 | sc->fw_boot.data = ((const char *) fp->data) + sizeof(*hdr); | |
2358 | sc->fw_boot.size = le32toh(hdr->bsize); | |
2359 | sc->fw_boot.name = fp->name; | |
2360 | sc->fw_uc.data = sc->fw_boot.data + sc->fw_boot.size; | |
2361 | sc->fw_uc.size = le32toh(hdr->usize); | |
2362 | sc->fw_uc.name = fp->name; | |
2363 | sc->fw_fw.data = sc->fw_uc.data + sc->fw_uc.size; | |
2364 | sc->fw_fw.size = le32toh(hdr->fsize); | |
2365 | sc->fw_fw.name = fp->name; | |
2366 | } | |
2367 | #if 0 | |
2368 | device_printf(sc->sc_dev, "boot %d ucode %d fw %d bytes\n", | |
2369 | sc->fw_boot.size, sc->fw_uc.size, sc->fw_fw.size); | |
2370 | #endif | |
2371 | ||
2372 | sc->fw_mode = opmode; | |
2373 | return 0; | |
2374 | bad: | |
2375 | iwi_put_firmware(sc); | |
2376 | return 1; | |
2377 | } | |
2378 | ||
2379 | static void | |
2380 | iwi_put_fw(struct iwi_fw *fw) | |
2381 | { | |
2382 | if (fw->fp != NULL) { | |
2383 | firmware_put(fw->fp, FIRMWARE_UNLOAD); | |
2384 | fw->fp = NULL; | |
2385 | } | |
2386 | fw->data = NULL; | |
2387 | fw->size = 0; | |
2388 | fw->name = NULL; | |
2389 | } | |
2390 | ||
2391 | /* | |
2392 | * Release any cached firmware images. | |
2393 | */ | |
2394 | static void | |
2395 | iwi_put_firmware(struct iwi_softc *sc) | |
2396 | { | |
2397 | iwi_put_fw(&sc->fw_uc); | |
2398 | iwi_put_fw(&sc->fw_fw); | |
2399 | iwi_put_fw(&sc->fw_boot); | |
2400 | } | |
2401 | ||
2402 | static int | |
2403 | iwi_load_ucode(struct iwi_softc *sc, const struct iwi_fw *fw) | |
2404 | { | |
2405 | uint32_t tmp; | |
2406 | const uint16_t *w; | |
2407 | const char *uc = fw->data; | |
2408 | size_t size = fw->size; | |
2409 | int i, ntries, error; | |
2410 | ||
c4fe7bb1 | 2411 | IWI_LOCK_ASSERT(sc); |
0e474d75 JH |
2412 | error = 0; |
2413 | CSR_WRITE_4(sc, IWI_CSR_RST, CSR_READ_4(sc, IWI_CSR_RST) | | |
2414 | IWI_RST_STOP_MASTER); | |
2415 | for (ntries = 0; ntries < 5; ntries++) { | |
2416 | if (CSR_READ_4(sc, IWI_CSR_RST) & IWI_RST_MASTER_DISABLED) | |
2417 | break; | |
2418 | DELAY(10); | |
2419 | } | |
2420 | if (ntries == 5) { | |
2421 | device_printf(sc->sc_dev, "timeout waiting for master\n"); | |
2422 | error = EIO; | |
2423 | goto fail; | |
2424 | } | |
2425 | ||
2426 | MEM_WRITE_4(sc, 0x3000e0, 0x80000000); | |
2427 | DELAY(5000); | |
2428 | ||
2429 | tmp = CSR_READ_4(sc, IWI_CSR_RST); | |
841ab66c SZ |
2430 | tmp &= ~IWI_RST_PRINCETON_RESET; |
2431 | CSR_WRITE_4(sc, IWI_CSR_RST, tmp); | |
2432 | ||
b50e4759 MD |
2433 | DELAY(5000); |
2434 | MEM_WRITE_4(sc, 0x3000e0, 0); | |
2435 | DELAY(1000); | |
0e474d75 | 2436 | MEM_WRITE_4(sc, IWI_MEM_EEPROM_EVENT, 1); |
b50e4759 | 2437 | DELAY(1000); |
0e474d75 | 2438 | MEM_WRITE_4(sc, IWI_MEM_EEPROM_EVENT, 0); |
b50e4759 MD |
2439 | DELAY(1000); |
2440 | MEM_WRITE_1(sc, 0x200000, 0x00); | |
2441 | MEM_WRITE_1(sc, 0x200000, 0x40); | |
2442 | DELAY(1000); | |
2443 | ||
841ab66c | 2444 | /* write microcode into adapter memory */ |
0e474d75 | 2445 | for (w = (const uint16_t *)uc; size > 0; w++, size -= 2) |
841ab66c | 2446 | MEM_WRITE_2(sc, 0x200010, htole16(*w)); |
b50e4759 MD |
2447 | |
2448 | MEM_WRITE_1(sc, 0x200000, 0x00); | |
2449 | MEM_WRITE_1(sc, 0x200000, 0x80); | |
2450 | ||
841ab66c | 2451 | /* wait until we get an answer */ |
b50e4759 MD |
2452 | for (ntries = 0; ntries < 100; ntries++) { |
2453 | if (MEM_READ_1(sc, 0x200000) & 1) | |
2454 | break; | |
2455 | DELAY(100); | |
2456 | } | |
2457 | if (ntries == 100) { | |
2458 | device_printf(sc->sc_dev, | |
2459 | "timeout waiting for ucode to initialize\n"); | |
0e474d75 JH |
2460 | error = EIO; |
2461 | goto fail; | |
b50e4759 MD |
2462 | } |
2463 | ||
841ab66c | 2464 | /* read the answer or the firmware will not initialize properly */ |
b50e4759 MD |
2465 | for (i = 0; i < 7; i++) |
2466 | MEM_READ_4(sc, 0x200004); | |
2467 | ||
2468 | MEM_WRITE_1(sc, 0x200000, 0x00); | |
2469 | ||
0e474d75 | 2470 | fail: |
114b4cbc SZ |
2471 | return error; |
2472 | } | |
2473 | ||
b50e4759 MD |
2474 | /* macro to handle unaligned little endian data in firmware image */ |
2475 | #define GETLE32(p) ((p)[0] | (p)[1] << 8 | (p)[2] << 16 | (p)[3] << 24) | |
841ab66c | 2476 | |
b50e4759 | 2477 | static int |
0e474d75 | 2478 | iwi_load_firmware(struct iwi_softc *sc, const struct iwi_fw *fw) |
b50e4759 | 2479 | { |
b50e4759 | 2480 | u_char *p, *end; |
841ab66c | 2481 | uint32_t sentinel, ctl, src, dst, sum, len, mlen, tmp; |
0e474d75 | 2482 | int ntries, error; |
b50e4759 | 2483 | |
c4fe7bb1 IV |
2484 | IWI_LOCK_ASSERT(sc); |
2485 | ||
0e474d75 JH |
2486 | /* copy firmware image to DMA memory */ |
2487 | memcpy(sc->fw_virtaddr, fw->data, fw->size); | |
841ab66c | 2488 | |
0e474d75 JH |
2489 | /* make sure the adapter will get up-to-date values */ |
2490 | bus_dmamap_sync(sc->fw_dmat, sc->fw_map, BUS_DMASYNC_PREWRITE); | |
b50e4759 | 2491 | |
0e474d75 | 2492 | /* tell the adapter where the command blocks are stored */ |
b50e4759 MD |
2493 | MEM_WRITE_4(sc, 0x3000a0, 0x27000); |
2494 | ||
2495 | /* | |
2496 | * Store command blocks into adapter's internal memory using register | |
2497 | * indirections. The adapter will read the firmware image through DMA | |
2498 | * using information stored in command blocks. | |
2499 | */ | |
0e474d75 JH |
2500 | src = sc->fw_physaddr; |
2501 | p = sc->fw_virtaddr; | |
2502 | end = p + fw->size; | |
b50e4759 MD |
2503 | CSR_WRITE_4(sc, IWI_CSR_AUTOINC_ADDR, 0x27000); |
2504 | ||
2505 | while (p < end) { | |
2506 | dst = GETLE32(p); p += 4; src += 4; | |
2507 | len = GETLE32(p); p += 4; src += 4; | |
2508 | p += len; | |
2509 | ||
2510 | while (len > 0) { | |
2511 | mlen = min(len, IWI_CB_MAXDATALEN); | |
2512 | ||
2513 | ctl = IWI_CB_DEFAULT_CTL | mlen; | |
2514 | sum = ctl ^ src ^ dst; | |
2515 | ||
0e474d75 | 2516 | /* write a command block */ |
b50e4759 MD |
2517 | CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, ctl); |
2518 | CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, src); | |
2519 | CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, dst); | |
2520 | CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, sum); | |
2521 | ||
2522 | src += mlen; | |
2523 | dst += mlen; | |
2524 | len -= mlen; | |
2525 | } | |
2526 | } | |
2527 | ||
0e474d75 | 2528 | /* write a fictive final command block (sentinel) */ |
b50e4759 MD |
2529 | sentinel = CSR_READ_4(sc, IWI_CSR_AUTOINC_ADDR); |
2530 | CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, 0); | |
2531 | ||
841ab66c SZ |
2532 | tmp = CSR_READ_4(sc, IWI_CSR_RST); |
2533 | tmp &= ~(IWI_RST_MASTER_DISABLED | IWI_RST_STOP_MASTER); | |
2534 | CSR_WRITE_4(sc, IWI_CSR_RST, tmp); | |
b50e4759 | 2535 | |
0e474d75 | 2536 | /* tell the adapter to start processing command blocks */ |
b50e4759 MD |
2537 | MEM_WRITE_4(sc, 0x3000a4, 0x540100); |
2538 | ||
0e474d75 | 2539 | /* wait until the adapter reaches the sentinel */ |
b50e4759 MD |
2540 | for (ntries = 0; ntries < 400; ntries++) { |
2541 | if (MEM_READ_4(sc, 0x3000d0) >= sentinel) | |
2542 | break; | |
2543 | DELAY(100); | |
2544 | } | |
0e474d75 JH |
2545 | /* sync dma, just in case */ |
2546 | bus_dmamap_sync(sc->fw_dmat, sc->fw_map, BUS_DMASYNC_POSTWRITE); | |
b50e4759 MD |
2547 | if (ntries == 400) { |
2548 | device_printf(sc->sc_dev, | |
0e474d75 JH |
2549 | "timeout processing command blocks for %s firmware\n", |
2550 | fw->name); | |
2551 | return EIO; | |
b50e4759 MD |
2552 | } |
2553 | ||
0e474d75 | 2554 | /* we're done with command blocks processing */ |
b50e4759 MD |
2555 | MEM_WRITE_4(sc, 0x3000a4, 0x540c00); |
2556 | ||
0e474d75 | 2557 | /* allow interrupts so we know when the firmware is ready */ |
b50e4759 MD |
2558 | CSR_WRITE_4(sc, IWI_CSR_INTR_MASK, IWI_INTR_MASK); |
2559 | ||
0e474d75 | 2560 | /* tell the adapter to initialize the firmware */ |
b50e4759 | 2561 | CSR_WRITE_4(sc, IWI_CSR_RST, 0); |
904b393f | 2562 | |
841ab66c SZ |
2563 | tmp = CSR_READ_4(sc, IWI_CSR_CTL); |
2564 | CSR_WRITE_4(sc, IWI_CSR_CTL, tmp | IWI_CTL_ALLOW_STANDBY); | |
2565 | ||
2566 | /* wait at most one second for firmware initialization to complete */ | |
c4fe7bb1 IV |
2567 | #if defined(__DragonFly__) |
2568 | if ((error = lksleep(sc, &sc->sc_lock, 0, "iwiinit", hz)) != 0) { | |
2569 | #else | |
2570 | if ((error = msleep(sc, &sc->sc_mtx, 0, "iwiinit", hz)) != 0) { | |
2571 | #endif | |
2572 | device_printf(sc->sc_dev, "timeout waiting for %s firmware " | |
2573 | "initialization to complete\n", fw->name); | |
b50e4759 MD |
2574 | } |
2575 | ||
b50e4759 MD |
2576 | return error; |
2577 | } | |
2578 | ||
0e474d75 JH |
2579 | static int |
2580 | iwi_setpowermode(struct iwi_softc *sc, struct ieee80211vap *vap) | |
2581 | { | |
2582 | uint32_t data; | |
2583 | ||
2584 | if (vap->iv_flags & IEEE80211_F_PMGTON) { | |
2585 | /* XXX set more fine-grained operation */ | |
2586 | data = htole32(IWI_POWER_MODE_MAX); | |
2587 | } else | |
2588 | data = htole32(IWI_POWER_MODE_CAM); | |
2589 | ||
2590 | DPRINTF(("Setting power mode to %u\n", le32toh(data))); | |
2591 | return iwi_cmd(sc, IWI_CMD_SET_POWER_MODE, &data, sizeof data); | |
2592 | } | |
2593 | ||
2594 | static int | |
2595 | iwi_setwepkeys(struct iwi_softc *sc, struct ieee80211vap *vap) | |
2596 | { | |
2597 | struct iwi_wep_key wepkey; | |
2598 | struct ieee80211_key *wk; | |
2599 | int error, i; | |
2600 | ||
2601 | for (i = 0; i < IEEE80211_WEP_NKID; i++) { | |
2602 | wk = &vap->iv_nw_keys[i]; | |
2603 | ||
2604 | wepkey.cmd = IWI_WEP_KEY_CMD_SETKEY; | |
2605 | wepkey.idx = i; | |
2606 | wepkey.len = wk->wk_keylen; | |
2607 | memset(wepkey.key, 0, sizeof wepkey.key); | |
2608 | memcpy(wepkey.key, wk->wk_key, wk->wk_keylen); | |
2609 | DPRINTF(("Setting wep key index %u len %u\n", wepkey.idx, | |
2610 | wepkey.len)); | |
2611 | error = iwi_cmd(sc, IWI_CMD_SET_WEP_KEY, &wepkey, | |
2612 | sizeof wepkey); | |
2613 | if (error != 0) | |
2614 | return error; | |
2615 | } | |
2616 | return 0; | |
2617 | } | |
2618 | ||
b50e4759 MD |
2619 | static int |
2620 | iwi_config(struct iwi_softc *sc) | |
2621 | { | |
c4fe7bb1 | 2622 | struct ieee80211com *ic = &sc->sc_ic; |
841ab66c | 2623 | struct iwi_configuration config; |
b50e4759 MD |
2624 | struct iwi_rateset rs; |
2625 | struct iwi_txpower power; | |
841ab66c | 2626 | uint32_t data; |
b50e4759 MD |
2627 | int error, i; |
2628 | ||
c4fe7bb1 IV |
2629 | IWI_LOCK_ASSERT(sc); |
2630 | ||
2631 | #if defined(__DragonFly__) | |
2632 | DPRINTF(("Setting MAC address to %s\n", ether_sprintf(ic->ic_macaddr))); | |
2633 | #else | |
2634 | DPRINTF(("Setting MAC address to %6D\n", ic->ic_macaddr, ":")); | |
2635 | #endif | |
2636 | error = iwi_cmd(sc, IWI_CMD_SET_MAC_ADDRESS, ic->ic_macaddr, | |
0e474d75 | 2637 | IEEE80211_ADDR_LEN); |
b50e4759 MD |
2638 | if (error != 0) |
2639 | return error; | |
2640 | ||
841ab66c SZ |
2641 | memset(&config, 0, sizeof config); |
2642 | config.bluetooth_coexistence = sc->bluetooth; | |
0e474d75 | 2643 | config.silence_threshold = 0x1e; |
841ab66c SZ |
2644 | config.antenna = sc->antenna; |
2645 | config.multicast_enabled = 1; | |
2646 | config.answer_pbreq = (ic->ic_opmode == IEEE80211_M_IBSS) ? 1 : 0; | |
2647 | config.disable_unicast_decryption = 1; | |
2648 | config.disable_multicast_decryption = 1; | |
c4fe7bb1 IV |
2649 | if (ic->ic_opmode == IEEE80211_M_MONITOR) { |
2650 | config.allow_invalid_frames = 1; | |
2651 | config.allow_beacon_and_probe_resp = 1; | |
2652 | config.allow_mgt = 1; | |
2653 | } | |
b50e4759 | 2654 | DPRINTF(("Configuring adapter\n")); |
0e474d75 | 2655 | error = iwi_cmd(sc, IWI_CMD_SET_CONFIG, &config, sizeof config); |
b50e4759 MD |
2656 | if (error != 0) |
2657 | return error; | |
b50e4759 MD |
2658 | if (ic->ic_opmode == IEEE80211_M_IBSS) { |
2659 | power.mode = IWI_MODE_11B; | |
2660 | power.nchan = 11; | |
2661 | for (i = 0; i < 11; i++) { | |
2662 | power.chan[i].chan = i + 1; | |
2663 | power.chan[i].power = IWI_TXPOWER_MAX; | |
2664 | } | |
2665 | DPRINTF(("Setting .11b channels tx power\n")); | |
0e474d75 | 2666 | error = iwi_cmd(sc, IWI_CMD_SET_TX_POWER, &power, sizeof power); |
b50e4759 MD |
2667 | if (error != 0) |
2668 | return error; | |
2669 | ||
2670 | power.mode = IWI_MODE_11G; | |
2671 | DPRINTF(("Setting .11g channels tx power\n")); | |
0e474d75 | 2672 | error = iwi_cmd(sc, IWI_CMD_SET_TX_POWER, &power, sizeof power); |
b50e4759 MD |
2673 | if (error != 0) |
2674 | return error; | |
2675 | } | |
2676 | ||
0e474d75 | 2677 | memset(&rs, 0, sizeof rs); |
b50e4759 MD |
2678 | rs.mode = IWI_MODE_11G; |
2679 | rs.type = IWI_RATESET_TYPE_SUPPORTED; | |
2680 | rs.nrates = ic->ic_sup_rates[IEEE80211_MODE_11G].rs_nrates; | |
841ab66c | 2681 | memcpy(rs.rates, ic->ic_sup_rates[IEEE80211_MODE_11G].rs_rates, |
b50e4759 MD |
2682 | rs.nrates); |
2683 | DPRINTF(("Setting .11bg supported rates (%u)\n", rs.nrates)); | |
0e474d75 | 2684 | error = iwi_cmd(sc, IWI_CMD_SET_RATES, &rs, sizeof rs); |
b50e4759 MD |
2685 | if (error != 0) |
2686 | return error; | |
2687 | ||
0e474d75 | 2688 | memset(&rs, 0, sizeof rs); |
b50e4759 MD |
2689 | rs.mode = IWI_MODE_11A; |
2690 | rs.type = IWI_RATESET_TYPE_SUPPORTED; | |
2691 | rs.nrates = ic->ic_sup_rates[IEEE80211_MODE_11A].rs_nrates; | |
841ab66c | 2692 | memcpy(rs.rates, ic->ic_sup_rates[IEEE80211_MODE_11A].rs_rates, |
b50e4759 MD |
2693 | rs.nrates); |
2694 | DPRINTF(("Setting .11a supported rates (%u)\n", rs.nrates)); | |
0e474d75 | 2695 | error = iwi_cmd(sc, IWI_CMD_SET_RATES, &rs, sizeof rs); |
b50e4759 MD |
2696 | if (error != 0) |
2697 | return error; | |
2698 | ||
0ced1954 | 2699 | data = htole32(karc4random()); |
b50e4759 | 2700 | DPRINTF(("Setting initialization vector to %u\n", le32toh(data))); |
0e474d75 | 2701 | error = iwi_cmd(sc, IWI_CMD_SET_IV, &data, sizeof data); |
b50e4759 MD |
2702 | if (error != 0) |
2703 | return error; | |
2704 | ||
0e474d75 | 2705 | /* enable adapter */ |
b50e4759 | 2706 | DPRINTF(("Enabling adapter\n")); |
0e474d75 | 2707 | return iwi_cmd(sc, IWI_CMD_ENABLE, NULL, 0); |
b50e4759 MD |
2708 | } |
2709 | ||
0e474d75 JH |
2710 | static __inline void |
2711 | set_scan_type(struct iwi_scan_ext *scan, int ix, int scan_type) | |
b50e4759 | 2712 | { |
0e474d75 JH |
2713 | uint8_t *st = &scan->scan_type[ix / 2]; |
2714 | if (ix % 2) | |
2715 | *st = (*st & 0xf0) | ((scan_type & 0xf) << 0); | |
2716 | else | |
2717 | *st = (*st & 0x0f) | ((scan_type & 0xf) << 4); | |
2718 | } | |
b50e4759 | 2719 | |
0e474d75 JH |
2720 | static int |
2721 | scan_type(const struct ieee80211_scan_state *ss, | |
2722 | const struct ieee80211_channel *chan) | |
2723 | { | |
2724 | /* We can only set one essid for a directed scan */ | |
2725 | if (ss->ss_nssid != 0) | |
2726 | return IWI_SCAN_TYPE_BDIRECTED; | |
2727 | if ((ss->ss_flags & IEEE80211_SCAN_ACTIVE) && | |
2728 | (chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0) | |
2729 | return IWI_SCAN_TYPE_BROADCAST; | |
2730 | return IWI_SCAN_TYPE_PASSIVE; | |
2731 | } | |
841ab66c | 2732 | |
0e474d75 JH |
2733 | static __inline int |
2734 | scan_band(const struct ieee80211_channel *c) | |
2735 | { | |
2736 | return IEEE80211_IS_CHAN_5GHZ(c) ? IWI_CHAN_5GHZ : IWI_CHAN_2GHZ; | |
841ab66c | 2737 | } |
b50e4759 | 2738 | |
c4fe7bb1 IV |
2739 | static void |
2740 | iwi_monitor_scan(void *arg, int npending) | |
2741 | { | |
2742 | struct iwi_softc *sc = arg; | |
2743 | IWI_LOCK_DECL; | |
2744 | ||
2745 | IWI_LOCK(sc); | |
2746 | (void) iwi_scanchan(sc, 2000, 0); | |
2747 | IWI_UNLOCK(sc); | |
2748 | } | |
2749 | ||
0e474d75 JH |
2750 | /* |
2751 | * Start a scan on the current channel or all channels. | |
2752 | */ | |
841ab66c | 2753 | static int |
0e474d75 | 2754 | iwi_scanchan(struct iwi_softc *sc, unsigned long maxdwell, int allchan) |
841ab66c | 2755 | { |
c4fe7bb1 | 2756 | struct ieee80211com *ic = &sc->sc_ic; |
0e474d75 JH |
2757 | struct ieee80211_channel *chan; |
2758 | struct ieee80211_scan_state *ss; | |
2759 | struct iwi_scan_ext scan; | |
2760 | int error = 0; | |
b50e4759 | 2761 | |
c4fe7bb1 | 2762 | IWI_LOCK_ASSERT(sc); |
0e474d75 JH |
2763 | if (sc->fw_state == IWI_FW_SCANNING) { |
2764 | /* | |
2765 | * This should not happen as we only trigger scan_next after | |
2766 | * completion | |
2767 | */ | |
2768 | DPRINTF(("%s: called too early - still scanning\n", __func__)); | |
2769 | return (EBUSY); | |
2770 | } | |
2771 | IWI_STATE_BEGIN(sc, IWI_FW_SCANNING); | |
b50e4759 | 2772 | |
0e474d75 JH |
2773 | ss = ic->ic_scan; |
2774 | ||
2775 | memset(&scan, 0, sizeof scan); | |
2776 | scan.full_scan_index = htole32(++sc->sc_scangen); | |
2777 | scan.dwell_time[IWI_SCAN_TYPE_PASSIVE] = htole16(maxdwell); | |
2778 | if (ic->ic_flags_ext & IEEE80211_FEXT_BGSCAN) { | |
2779 | /* | |
2780 | * Use very short dwell times for when we send probe request | |
2781 | * frames. Without this bg scans hang. Ideally this should | |
2782 | * be handled with early-termination as done by net80211 but | |
2783 | * that's not feasible (aborting a scan is problematic). | |
2784 | */ | |
2785 | scan.dwell_time[IWI_SCAN_TYPE_BROADCAST] = htole16(30); | |
2786 | scan.dwell_time[IWI_SCAN_TYPE_BDIRECTED] = htole16(30); | |
b50e4759 | 2787 | } else { |
0e474d75 JH |
2788 | scan.dwell_time[IWI_SCAN_TYPE_BROADCAST] = htole16(maxdwell); |
2789 | scan.dwell_time[IWI_SCAN_TYPE_BDIRECTED] = htole16(maxdwell); | |
b50e4759 MD |
2790 | } |
2791 | ||
0e474d75 JH |
2792 | /* We can only set one essid for a directed scan */ |
2793 | if (ss->ss_nssid != 0) { | |
2794 | error = iwi_cmd(sc, IWI_CMD_SET_ESSID, ss->ss_ssid[0].ssid, | |
2795 | ss->ss_ssid[0].len); | |
2796 | if (error) | |
2797 | return (error); | |
841ab66c | 2798 | } |
841ab66c | 2799 | |
0e474d75 JH |
2800 | if (allchan) { |
2801 | int i, next, band, b, bstart; | |
2802 | /* | |
2803 | * Convert scan list to run-length encoded channel list | |
2804 | * the firmware requires (preserving the order setup by | |
2805 | * net80211). The first entry in each run specifies the | |
2806 | * band and the count of items in the run. | |
2807 | */ | |
2808 | next = 0; /* next open slot */ | |
2809 | bstart = 0; /* NB: not needed, silence compiler */ | |
2810 | band = -1; /* NB: impossible value */ | |
2811 | KASSERT(ss->ss_last > 0, ("no channels")); | |
2812 | for (i = 0; i < ss->ss_last; i++) { | |
2813 | chan = ss->ss_chans[i]; | |
2814 | b = scan_band(chan); | |
2815 | if (b != band) { | |
2816 | if (band != -1) | |
2817 | scan.channels[bstart] = | |
2818 | (next - bstart) | band; | |
2819 | /* NB: this allocates a slot for the run-len */ | |
2820 | band = b, bstart = next++; | |
2821 | } | |
2822 | if (next >= IWI_SCAN_CHANNELS) { | |
2823 | DPRINTF(("truncating scan list\n")); | |
2824 | break; | |
2825 | } | |
2826 | scan.channels[next] = ieee80211_chan2ieee(ic, chan); | |
2827 | set_scan_type(&scan, next, scan_type(ss, chan)); | |
2828 | next++; | |
b50e4759 | 2829 | } |
0e474d75 JH |
2830 | scan.channels[bstart] = (next - bstart) | band; |
2831 | } else { | |
2832 | /* Scan the current channel only */ | |
2833 | chan = ic->ic_curchan; | |
2834 | scan.channels[0] = 1 | scan_band(chan); | |
2835 | scan.channels[1] = ieee80211_chan2ieee(ic, chan); | |
2836 | set_scan_type(&scan, 1, scan_type(ss, chan)); | |
b50e4759 | 2837 | } |
0e474d75 JH |
2838 | #ifdef IWI_DEBUG |
2839 | if (iwi_debug > 0) { | |
2840 | static const char *scantype[8] = | |
2841 | { "PSTOP", "PASV", "DIR", "BCAST", "BDIR", "5", "6", "7" }; | |
2842 | int i; | |
2843 | kprintf("Scan request: index %u dwell %d/%d/%d\n" | |
2844 | , le32toh(scan.full_scan_index) | |
2845 | , le16toh(scan.dwell_time[IWI_SCAN_TYPE_PASSIVE]) | |
2846 | , le16toh(scan.dwell_time[IWI_SCAN_TYPE_BROADCAST]) | |
2847 | , le16toh(scan.dwell_time[IWI_SCAN_TYPE_BDIRECTED]) | |
2848 | ); | |
2849 | i = 0; | |
2850 | do { | |
2851 | int run = scan.channels[i]; | |
2852 | if (run == 0) | |
2853 | break; | |
2854 | kprintf("Scan %d %s channels:", run & 0x3f, | |
2855 | run & IWI_CHAN_2GHZ ? "2.4GHz" : "5GHz"); | |
2856 | for (run &= 0x3f, i++; run > 0; run--, i++) { | |
2857 | uint8_t type = scan.scan_type[i/2]; | |
2858 | kprintf(" %u/%s", scan.channels[i], | |
2859 | scantype[(i & 1 ? type : type>>4) & 7]); | |
2860 | } | |
2861 | kprintf("\n"); | |
2862 | } while (i < IWI_SCAN_CHANNELS); | |
2863 | } | |
2864 | #endif | |
841ab66c | 2865 | |
0e474d75 | 2866 | return (iwi_cmd(sc, IWI_CMD_SCAN_EXT, &scan, sizeof scan)); |
b50e4759 MD |
2867 | } |
2868 | ||
2869 | static int | |
0e474d75 | 2870 | iwi_set_sensitivity(struct iwi_softc *sc, int8_t rssi_dbm) |
b50e4759 | 2871 | { |
0e474d75 JH |
2872 | struct iwi_sensitivity sens; |
2873 | ||
2874 | DPRINTF(("Setting sensitivity to %d\n", rssi_dbm)); | |
2875 | ||
2876 | memset(&sens, 0, sizeof sens); | |
2877 | sens.rssi = htole16(rssi_dbm); | |
2878 | return iwi_cmd(sc, IWI_CMD_SET_SENSITIVITY, &sens, sizeof sens); | |
2879 | } | |
2880 | ||
2881 | static int | |
2882 | iwi_auth_and_assoc(struct iwi_softc *sc, struct ieee80211vap *vap) | |
2883 | { | |
2884 | struct ieee80211com *ic = vap->iv_ic; | |
2885 | struct ifnet *ifp = vap->iv_ifp; | |
c4fe7bb1 | 2886 | struct ieee80211_node *ni; |
841ab66c | 2887 | struct iwi_configuration config; |
0e474d75 | 2888 | struct iwi_associate *assoc = &sc->assoc; |
b50e4759 | 2889 | struct iwi_rateset rs; |
841ab66c SZ |
2890 | uint16_t capinfo; |
2891 | uint32_t data; | |
0e474d75 | 2892 | int error, mode; |
c4fe7bb1 IV |
2893 | |
2894 | IWI_LOCK_ASSERT(sc); | |
2895 | ||
2896 | ni = ieee80211_ref_node(vap->iv_bss); | |
0e474d75 | 2897 | |
0e474d75 JH |
2898 | if (sc->flags & IWI_FLAG_ASSOCIATED) { |
2899 | DPRINTF(("Already associated\n")); | |
2900 | return (-1); | |
2901 | } | |
2902 | ||
2903 | IWI_STATE_BEGIN(sc, IWI_FW_ASSOCIATING); | |
2a0b1ca7 | 2904 | error = 0; |
0e474d75 JH |
2905 | mode = 0; |
2906 | ||
2907 | if (IEEE80211_IS_CHAN_A(ic->ic_curchan)) | |
2908 | mode = IWI_MODE_11A; | |
2909 | else if (IEEE80211_IS_CHAN_G(ic->ic_curchan)) | |
2910 | mode = IWI_MODE_11G; | |
2911 | if (IEEE80211_IS_CHAN_B(ic->ic_curchan)) | |
2912 | mode = IWI_MODE_11B; | |
b50e4759 | 2913 | |
0e474d75 | 2914 | if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) { |
841ab66c SZ |
2915 | memset(&config, 0, sizeof config); |
2916 | config.bluetooth_coexistence = sc->bluetooth; | |
2917 | config.antenna = sc->antenna; | |
2918 | config.multicast_enabled = 1; | |
0e474d75 JH |
2919 | if (mode == IWI_MODE_11G) |
2920 | config.use_protection = 1; | |
841ab66c | 2921 | config.answer_pbreq = |
0e474d75 | 2922 | (vap->iv_opmode == IEEE80211_M_IBSS) ? 1 : 0; |
841ab66c SZ |
2923 | config.disable_unicast_decryption = 1; |
2924 | config.disable_multicast_decryption = 1; | |
2925 | DPRINTF(("Configuring adapter\n")); | |
0e474d75 | 2926 | error = iwi_cmd(sc, IWI_CMD_SET_CONFIG, &config, sizeof config); |
841ab66c | 2927 | if (error != 0) |
0e474d75 | 2928 | goto done; |
841ab66c | 2929 | } |
b50e4759 MD |
2930 | |
2931 | #ifdef IWI_DEBUG | |
841ab66c | 2932 | if (iwi_debug > 0) { |
e3869ec7 | 2933 | kprintf("Setting ESSID to "); |
b50e4759 | 2934 | ieee80211_print_essid(ni->ni_essid, ni->ni_esslen); |
e3869ec7 | 2935 | kprintf("\n"); |
b50e4759 MD |
2936 | } |
2937 | #endif | |
0e474d75 | 2938 | error = iwi_cmd(sc, IWI_CMD_SET_ESSID, ni->ni_essid, ni->ni_esslen); |
b50e4759 | 2939 | if (error != 0) |
0e474d75 JH |
2940 | goto done; |
2941 | ||
2942 | error = iwi_setpowermode(sc, vap); | |
2943 | if (error != 0) | |
2944 | goto done; | |
2945 | ||
2946 | data = htole32(vap->iv_rtsthreshold); | |
2947 | DPRINTF(("Setting RTS threshold to %u\n", le32toh(data))); | |
2948 | error = iwi_cmd(sc, IWI_CMD_SET_RTS_THRESHOLD, &data, sizeof data); | |
2949 | if (error != 0) | |
2950 | goto done; | |
2951 | ||
2952 | data = htole32(vap->iv_fragthreshold); | |
2953 | DPRINTF(("Setting fragmentation threshold to %u\n", le32toh(data))); | |
2954 | error = iwi_cmd(sc, IWI_CMD_SET_FRAG_THRESHOLD, &data, sizeof data); | |
2955 | if (error != 0) | |
2956 | goto done; | |
b50e4759 MD |
2957 | |
2958 | /* the rate set has already been "negotiated" */ | |
0e474d75 JH |
2959 | memset(&rs, 0, sizeof rs); |
2960 | rs.mode = mode; | |
b50e4759 MD |
2961 | rs.type = IWI_RATESET_TYPE_NEGOTIATED; |
2962 | rs.nrates = ni->ni_rates.rs_nrates; | |
0e474d75 JH |
2963 | if (rs.nrates > IWI_RATESET_SIZE) { |
2964 | DPRINTF(("Truncating negotiated rate set from %u\n", | |
2965 | rs.nrates)); | |
2966 | rs.nrates = IWI_RATESET_SIZE; | |
2967 | } | |
841ab66c | 2968 | memcpy(rs.rates, ni->ni_rates.rs_rates, rs.nrates); |
0e474d75 JH |
2969 | DPRINTF(("Setting negotiated rates (%u)\n", rs.nrates)); |
2970 | error = iwi_cmd(sc, IWI_CMD_SET_RATES, &rs, sizeof rs); | |
b50e4759 | 2971 | if (error != 0) |
0e474d75 | 2972 | goto done; |
b50e4759 | 2973 | |
0e474d75 JH |
2974 | memset(assoc, 0, sizeof *assoc); |
2975 | ||
2976 | if ((vap->iv_flags & IEEE80211_F_WME) && ni->ni_ies.wme_ie != NULL) { | |
2977 | /* NB: don't treat WME setup as failure */ | |
c4fe7bb1 | 2978 | if (iwi_wme_setparams(sc) == 0 && iwi_wme_setie(sc) == 0) |
0e474d75 JH |
2979 | assoc->policy |= htole16(IWI_POLICY_WME); |
2980 | /* XXX complain on failure? */ | |
841ab66c SZ |
2981 | } |
2982 | ||
0e474d75 JH |
2983 | if (vap->iv_appie_wpa != NULL) { |
2984 | struct ieee80211_appie *ie = vap->iv_appie_wpa; | |
2985 | ||
2986 | DPRINTF(("Setting optional IE (len=%u)\n", ie->ie_len)); | |
2987 | error = iwi_cmd(sc, IWI_CMD_SET_OPTIE, ie->ie_data, ie->ie_len); | |
841ab66c | 2988 | if (error != 0) |
0e474d75 | 2989 | goto done; |
841ab66c SZ |
2990 | } |
2991 | ||
0e474d75 | 2992 | error = iwi_set_sensitivity(sc, ic->ic_node_getrssi(ni)); |
b50e4759 | 2993 | if (error != 0) |
0e474d75 | 2994 | goto done; |
b50e4759 | 2995 | |
0e474d75 JH |
2996 | assoc->mode = mode; |
2997 | assoc->chan = ic->ic_curchan->ic_ieee; | |
2998 | /* | |
2999 | * NB: do not arrange for shared key auth w/o privacy | |
3000 | * (i.e. a wep key); it causes a firmware error. | |
3001 | */ | |
3002 | if ((vap->iv_flags & IEEE80211_F_PRIVACY) && | |
3003 | ni->ni_authmode == IEEE80211_AUTH_SHARED) { | |
3004 | assoc->auth = IWI_AUTH_SHARED; | |
3005 | /* | |
3006 | * It's possible to have privacy marked but no default | |
3007 | * key setup. This typically is due to a user app bug | |
3008 | * but if we blindly grab the key the firmware will | |
3009 | * barf so avoid it for now. | |
3010 | */ | |
3011 | if (vap->iv_def_txkey != IEEE80211_KEYIX_NONE) | |
3012 | assoc->auth |= vap->iv_def_txkey << 4; | |
3013 | ||
3014 | error = iwi_setwepkeys(sc, vap); | |
3015 | if (error != 0) | |
3016 | goto done; | |
3017 | } | |
3018 | if (vap->iv_flags & IEEE80211_F_WPA) | |
3019 | assoc->policy |= htole16(IWI_POLICY_WPA); | |
3020 | if (vap->iv_opmode == IEEE80211_M_IBSS && ni->ni_tstamp.tsf == 0) | |
3021 | assoc->type = IWI_HC_IBSS_START; | |
3022 | else | |
3023 | assoc->type = IWI_HC_ASSOC; | |
3024 | memcpy(assoc->tstamp, ni->ni_tstamp.data, 8); | |
3025 | ||
3026 | if (vap->iv_opmode == IEEE80211_M_IBSS) | |
841ab66c SZ |
3027 | capinfo = IEEE80211_CAPINFO_IBSS; |
3028 | else | |
3029 | capinfo = IEEE80211_CAPINFO_ESS; | |
0e474d75 | 3030 | if (vap->iv_flags & IEEE80211_F_PRIVACY) |
841ab66c SZ |
3031 | capinfo |= IEEE80211_CAPINFO_PRIVACY; |
3032 | if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && | |
0e474d75 | 3033 | IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) |
841ab66c | 3034 | capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE; |
0e474d75 | 3035 | if (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) |
841ab66c | 3036 | capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME; |
0e474d75 JH |
3037 | assoc->capinfo = htole16(capinfo); |
3038 | ||
3039 | assoc->lintval = htole16(ic->ic_lintval); | |
3040 | assoc->intval = htole16(ni->ni_intval); | |
3041 | IEEE80211_ADDR_COPY(assoc->bssid, ni->ni_bssid); | |
3042 | if (vap->iv_opmode == IEEE80211_M_IBSS) | |
3043 | IEEE80211_ADDR_COPY(assoc->dst, ifp->if_broadcastaddr); | |
3044 | else | |
3045 | IEEE80211_ADDR_COPY(assoc->dst, ni->ni_bssid); | |
3046 | ||
c4fe7bb1 | 3047 | #if defined(__DragonFly__) |
1e290df3 | 3048 | DPRINTF(("%s bssid %s dst %s channel %u policy 0x%x " |
0e474d75 JH |
3049 | "auth %u capinfo 0x%x lintval %u bintval %u\n", |
3050 | assoc->type == IWI_HC_IBSS_START ? "Start" : "Join", | |
c4fe7bb1 | 3051 | ether_sprintf(assoc->bssid), ether_sprintf(assoc->dst), |
0e474d75 JH |
3052 | assoc->chan, le16toh(assoc->policy), assoc->auth, |
3053 | le16toh(assoc->capinfo), le16toh(assoc->lintval), | |
3054 | le16toh(assoc->intval))); | |
c4fe7bb1 IV |
3055 | #else |
3056 | DPRINTF(("%s bssid %6D dst %6D channel %u policy 0x%x " | |
3057 | "auth %u capinfo 0x%x lintval %u bintval %u\n", | |
3058 | assoc->type == IWI_HC_IBSS_START ? "Start" : "Join", | |
3059 | assoc->bssid, ":", assoc->dst, ":", | |
3060 | assoc->chan, le16toh(assoc->policy), assoc->auth, | |
3061 | le16toh(assoc->capinfo), le16toh(assoc->lintval), | |
3062 | le16toh(assoc->intval))); | |
3063 | #endif | |
0e474d75 JH |
3064 | error = iwi_cmd(sc, IWI_CMD_ASSOCIATE, assoc, sizeof *assoc); |
3065 | done: | |
c4fe7bb1 | 3066 | ieee80211_free_node(ni); |
0e474d75 JH |
3067 | if (error) |
3068 | IWI_STATE_END(sc, IWI_FW_ASSOCIATING); | |
3069 | ||
3070 | return (error); | |
3071 | } | |
3072 | ||
3073 | static void | |
c4fe7bb1 | 3074 | iwi_disassoc(void *arg, int pending) |
0e474d75 JH |
3075 | { |
3076 | struct iwi_softc *sc = arg; | |
c4fe7bb1 | 3077 | IWI_LOCK_DECL; |
0e474d75 | 3078 | |
c4fe7bb1 | 3079 | IWI_LOCK(sc); |
0e474d75 | 3080 | iwi_disassociate(sc, 0); |
c4fe7bb1 | 3081 | IWI_UNLOCK(sc); |
0e474d75 JH |
3082 | } |
3083 | ||
3084 | static int | |
3085 | iwi_disassociate(struct iwi_softc *sc, int quiet) | |
3086 | { | |
3087 | struct iwi_associate *assoc = &sc->assoc; | |
3088 | ||
3089 | if ((sc->flags & IWI_FLAG_ASSOCIATED) == 0) { | |
3090 | DPRINTF(("Not associated\n")); | |
3091 | return (-1); | |
3092 | } | |
841ab66c | 3093 | |
0e474d75 JH |
3094 | IWI_STATE_BEGIN(sc, IWI_FW_DISASSOCIATING); |
3095 | ||
3096 | if (quiet) | |
3097 | assoc->type = IWI_HC_DISASSOC_QUIET; | |
b50e4759 | 3098 | else |
0e474d75 | 3099 | assoc->type = IWI_HC_DISASSOC; |
b50e4759 | 3100 | |
c4fe7bb1 | 3101 | #if defined(__DragonFly__) |
1e290df3 | 3102 | DPRINTF(("Trying to disassociate from %s channel %u\n", |
c4fe7bb1 IV |
3103 | ether_sprintf(assoc->bssid), assoc->chan)); |
3104 | #else | |
3105 | DPRINTF(("Trying to disassociate from %6D channel %u\n", | |
3106 | assoc->bssid, ":", assoc->chan)); | |
3107 | #endif | |
0e474d75 | 3108 | return iwi_cmd(sc, IWI_CMD_ASSOCIATE, assoc, sizeof *assoc); |
b50e4759 MD |
3109 | } |
3110 | ||
0e474d75 JH |
3111 | /* |
3112 | * release dma resources for the firmware | |
3113 | */ | |
b50e4759 | 3114 | static void |
0e474d75 | 3115 | iwi_release_fw_dma(struct iwi_softc *sc) |
b50e4759 | 3116 | { |
0e474d75 JH |
3117 | if (sc->fw_flags & IWI_FW_HAVE_PHY) |
3118 | bus_dmamap_unload(sc->fw_dmat, sc->fw_map); | |
3119 | if (sc->fw_flags & IWI_FW_HAVE_MAP) | |
3120 | bus_dmamem_free(sc->fw_dmat, sc->fw_virtaddr, sc->fw_map); | |
3121 | if (sc->fw_flags & IWI_FW_HAVE_DMAT) | |
3122 | bus_dma_tag_destroy(sc->fw_dmat); | |
3123 | ||
3124 | sc->fw_flags = 0; | |
3125 | sc->fw_dma_size = 0; | |
3126 | sc->fw_dmat = NULL; | |
3127 | sc->fw_map = NULL; | |
3128 | sc->fw_physaddr = 0; | |
3129 | sc->fw_virtaddr = NULL; | |
3130 | } | |
3131 | ||
3132 | /* | |
3133 | * allocate the dma descriptor for the firmware. | |
3134 | * Return 0 on success, 1 on error. | |
3135 | * Must be called unlocked, protected by IWI_FLAG_FW_LOADING. | |
3136 | */ | |
3137 | static int | |
3138 | iwi_init_fw_dma(struct iwi_softc *sc, int size) | |
3139 | { | |
3140 | if (sc->fw_dma_size >= size) | |
3141 | return 0; | |
c4fe7bb1 | 3142 | #if defined(__DragonFly__) |
0e474d75 | 3143 | if (bus_dma_tag_create(NULL, 4, 0, BUS_SPACE_MAXADDR_32BIT, |
030b0c8c | 3144 | BUS_SPACE_MAXADDR, size, 1, size, |
0e474d75 | 3145 | 0, &sc->fw_dmat) != 0) { |
c4fe7bb1 IV |
3146 | #else |
3147 | if (bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 4, 0, | |
3148 | BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, | |
3149 | size, 1, size, 0, NULL, NULL, &sc->fw_dmat) != 0) { | |
3150 | #endif | |
0e474d75 JH |
3151 | device_printf(sc->sc_dev, |
3152 | "could not create firmware DMA tag\n"); | |
3153 | goto error; | |
3154 | } | |
3155 | sc->fw_flags |= IWI_FW_HAVE_DMAT; | |
3156 | if (bus_dmamem_alloc(sc->fw_dmat, &sc->fw_virtaddr, 0, | |
3157 | &sc->fw_map) != 0) { | |
3158 | device_printf(sc->sc_dev, | |
3159 | "could not allocate firmware DMA memory\n"); | |
3160 | goto error; | |
3161 | } | |
3162 | sc->fw_flags |= IWI_FW_HAVE_MAP; | |
3163 | if (bus_dmamap_load(sc->fw_dmat, sc->fw_map, sc->fw_virtaddr, | |
3164 | size, iwi_dma_map_addr, &sc->fw_physaddr, 0) != 0) { | |
3165 | device_printf(sc->sc_dev, "could not load firmware DMA map\n"); | |
3166 | goto error; | |
3167 | } | |
3168 | sc->fw_flags |= IWI_FW_HAVE_PHY; | |
3169 | sc->fw_dma_size = size; | |
3170 | return 0; | |
3171 | ||
3172 | error: | |
3173 | iwi_release_fw_dma(sc); | |
3174 | return 1; | |
3175 | } | |
3176 | ||
3177 | static void | |
3178 | iwi_init_locked(struct iwi_softc *sc) | |
3179 | { | |
841ab66c | 3180 | struct iwi_rx_data *data; |
b50e4759 MD |
3181 | int i; |
3182 | ||
c4fe7bb1 IV |
3183 | IWI_LOCK_ASSERT(sc); |
3184 | ||
0e474d75 JH |
3185 | if (sc->fw_state == IWI_FW_LOADING) { |
3186 | device_printf(sc->sc_dev, "%s: already loading\n", __func__); | |
3187 | return; /* XXX: condvar? */ | |
b50e4759 MD |
3188 | } |
3189 | ||
0e474d75 JH |
3190 | iwi_stop_locked(sc); |
3191 | ||
3192 | IWI_STATE_BEGIN(sc, IWI_FW_LOADING); | |
3193 | ||
3194 | if (iwi_reset(sc) != 0) { | |
3195 | device_printf(sc->sc_dev, "could not reset adapter\n"); | |
114b4cbc SZ |
3196 | goto fail; |
3197 | } | |
0e474d75 JH |
3198 | if (iwi_load_firmware(sc, &sc->fw_boot) != 0) { |
3199 | device_printf(sc->sc_dev, | |
3200 | "could not load boot firmware %s\n", sc->fw_boot.name); | |
b50e4759 MD |
3201 | goto fail; |
3202 | } | |
0e474d75 JH |
3203 | if (iwi_load_ucode(sc, &sc->fw_uc) != 0) { |
3204 | device_printf(sc->sc_dev, | |
3205 | "could not load microcode %s\n", sc->fw_uc.name); | |
b50e4759 MD |
3206 | goto fail; |
3207 | } | |
3208 | ||
3209 | iwi_stop_master(sc); | |
3210 | ||
841ab66c SZ |
3211 | CSR_WRITE_4(sc, IWI_CSR_CMD_BASE, sc->cmdq.physaddr); |
3212 | CSR_WRITE_4(sc, IWI_CSR_CMD_SIZE, sc->cmdq.count); | |
3213 | CSR_WRITE_4(sc, IWI_CSR_CMD_WIDX, sc->cmdq.cur); | |
b50e4759 | 3214 | |
841ab66c SZ |
3215 | CSR_WRITE_4(sc, IWI_CSR_TX1_BASE, sc->txq[0].physaddr); |
3216 | CSR_WRITE_4(sc, IWI_CSR_TX1_SIZE, sc->txq[0].count); | |
3217 | CSR_WRITE_4(sc, IWI_CSR_TX1_WIDX, sc->txq[0].cur); | |
3218 | ||
3219 | CSR_WRITE_4(sc, IWI_CSR_TX2_BASE, sc->txq[1].physaddr); | |
3220 | CSR_WRITE_4(sc, IWI_CSR_TX2_SIZE, sc->txq[1].count); | |
3221 | CSR_WRITE_4(sc, IWI_CSR_TX2_WIDX, sc->txq[1].cur); | |
3222 | ||
3223 | CSR_WRITE_4(sc, IWI_CSR_TX3_BASE, sc->txq[2].physaddr); | |
3224 | CSR_WRITE_4(sc, IWI_CSR_TX3_SIZE, sc->txq[2].count); | |
3225 | CSR_WRITE_4(sc, IWI_CSR_TX3_WIDX, sc->txq[2].cur); | |
3226 | ||
3227 | CSR_WRITE_4(sc, IWI_CSR_TX4_BASE, sc->txq[3].physaddr); | |
3228 | CSR_WRITE_4(sc, IWI_CSR_TX4_SIZE, sc->txq[3].count); | |
3229 | CSR_WRITE_4(sc, IWI_CSR_TX4_WIDX, sc->txq[3].cur); | |
3230 | ||
3231 | for (i = 0; i < sc->rxq.count; i++) { | |
3232 | data = &sc->rxq.data[i]; | |
3233 | CSR_WRITE_4(sc, data->reg, data->physaddr); | |
3234 | } | |
3235 | ||
3236 | CSR_WRITE_4(sc, IWI_CSR_RX_WIDX, sc->rxq.count - 1); | |
b50e4759 | 3237 | |
0e474d75 JH |
3238 | if (iwi_load_firmware(sc, &sc->fw_fw) != 0) { |
3239 | device_printf(sc->sc_dev, | |
3240 | "could not load main firmware %s\n", sc->fw_fw.name); | |
b50e4759 MD |
3241 | goto fail; |
3242 | } | |
b50e4759 MD |
3243 | sc->flags |= IWI_FLAG_FW_INITED; |
3244 | ||
0e474d75 JH |
3245 | IWI_STATE_END(sc, IWI_FW_LOADING); |
3246 | ||
b50e4759 | 3247 | if (iwi_config(sc) != 0) { |
0e474d75 JH |
3248 | device_printf(sc->sc_dev, "unable to enable adapter\n"); |
3249 | goto fail2; | |
b50e4759 MD |
3250 | } |
3251 | ||
c4fe7bb1 IV |
3252 | callout_reset(&sc->sc_wdtimer, hz, iwi_watchdog, sc); |
3253 | sc->sc_running = 1; | |
b50e4759 | 3254 | return; |
0e474d75 JH |
3255 | fail: |
3256 | IWI_STATE_END(sc, IWI_FW_LOADING); | |
3257 | fail2: | |
3258 | iwi_stop_locked(sc); | |
3259 | } | |
b50e4759 | 3260 | |
0e474d75 JH |
3261 | static void |
3262 | iwi_init(void *priv) | |
3263 | { | |
3264 | struct iwi_softc *sc = priv; | |
c4fe7bb1 IV |
3265 | struct ieee80211com *ic = &sc->sc_ic; |
3266 | IWI_LOCK_DECL; | |
0e474d75 | 3267 | |
c4fe7bb1 | 3268 | IWI_LOCK(sc); |
0e474d75 | 3269 | iwi_init_locked(sc); |
c4fe7bb1 | 3270 | IWI_UNLOCK(sc); |
0e474d75 | 3271 | |
c4fe7bb1 | 3272 | if (sc->sc_running) |
0e474d75 | 3273 | ieee80211_start_all(ic); |
b50e4759 MD |
3274 | } |
3275 | ||
b50e4759 | 3276 | static void |
0e474d75 | 3277 | iwi_stop_locked(void *priv) |
b50e4759 MD |
3278 | { |
3279 | struct iwi_softc *sc = priv; | |
0e474d75 | 3280 | |
c4fe7bb1 IV |
3281 | IWI_LOCK_ASSERT(sc); |
3282 | ||
3283 | sc->sc_running = 0; | |
0e474d75 JH |
3284 | |
3285 | if (sc->sc_softled) { | |
c4fe7bb1 | 3286 | callout_stop(&sc->sc_ledtimer); |
0e474d75 JH |
3287 | sc->sc_blinking = 0; |
3288 | } | |
c4fe7bb1 IV |
3289 | callout_stop(&sc->sc_wdtimer); |
3290 | callout_stop(&sc->sc_rftimer); | |
b50e4759 MD |
3291 | |
3292 | iwi_stop_master(sc); | |
b50e4759 | 3293 | |
841ab66c | 3294 | CSR_WRITE_4(sc, IWI_CSR_RST, IWI_RST_SOFT_RESET); |
b50e4759 | 3295 | |
841ab66c SZ |
3296 | /* reset rings */ |
3297 | iwi_reset_cmd_ring(sc, &sc->cmdq); | |
3298 | iwi_reset_tx_ring(sc, &sc->txq[0]); | |
3299 | iwi_reset_tx_ring(sc, &sc->txq[1]); | |
3300 | iwi_reset_tx_ring(sc, &sc->txq[2]); | |
3301 | iwi_reset_tx_ring(sc, &sc->txq[3]); | |
3302 | iwi_reset_rx_ring(sc, &sc->rxq); | |
b50e4759 | 3303 | |
841ab66c | 3304 | sc->sc_tx_timer = 0; |
0e474d75 JH |
3305 | sc->sc_state_timer = 0; |
3306 | sc->sc_busy_timer = 0; | |
3307 | sc->flags &= ~(IWI_FLAG_BUSY | IWI_FLAG_ASSOCIATED); | |
3308 | sc->fw_state = IWI_FW_IDLE; | |
3309 | wakeup(sc); | |
3310 | } | |
3311 | ||
3312 | static void | |
3313 | iwi_stop(struct iwi_softc *sc) | |
3314 | { | |
c4fe7bb1 IV |
3315 | IWI_LOCK_DECL; |
3316 | ||
3317 | IWI_LOCK(sc); | |
0e474d75 | 3318 | iwi_stop_locked(sc); |
c4fe7bb1 | 3319 | IWI_UNLOCK(sc); |
0e474d75 JH |
3320 | } |
3321 | ||
3322 | static void | |
c4fe7bb1 | 3323 | iwi_restart(void *arg, int npending) |
0e474d75 JH |
3324 | { |
3325 | struct iwi_softc *sc = arg; | |
3326 | ||
3327 | iwi_init(sc); | |
3328 | } | |
3329 | ||
3330 | /* | |
3331 | * Return whether or not the radio is enabled in hardware | |
3332 | * (i.e. the rfkill switch is "off"). | |
3333 | */ | |
3334 | static int | |
3335 | iwi_getrfkill(struct iwi_softc *sc) | |
3336 | { | |
3337 | return (CSR_READ_4(sc, IWI_CSR_IO) & IWI_IO_RADIO_ENABLED) == 0; | |
3338 | } | |
3339 | ||
3340 | static void | |
c4fe7bb1 | 3341 | iwi_radio_on(void *arg, int pending) |
0e474d75 JH |
3342 | { |
3343 | struct iwi_softc *sc = arg; | |
c4fe7bb1 | 3344 | struct ieee80211com *ic = &sc->sc_ic; |
0e474d75 JH |
3345 | |
3346 | device_printf(sc->sc_dev, "radio turned on\n"); | |
3347 | ||
3348 | iwi_init(sc); | |
3349 | ieee80211_notify_radio(ic, 1); | |
3350 | } | |
3351 | ||
3352 | static void | |
3353 | iwi_rfkill_poll(void *arg) | |
3354 | { | |
3355 | struct iwi_softc *sc = arg; | |
3356 | ||
c4fe7bb1 IV |
3357 | IWI_LOCK_ASSERT(sc); |
3358 | ||
0e474d75 JH |
3359 | /* |
3360 | * Check for a change in rfkill state. We get an | |
3361 | * interrupt when a radio is disabled but not when | |
3362 | * it is enabled so we must poll for the latter. | |
3363 | */ | |
3364 | if (!iwi_getrfkill(sc)) { | |
c4fe7bb1 | 3365 | ieee80211_runtask(&sc->sc_ic, &sc->sc_radiontask); |
0e474d75 JH |
3366 | return; |
3367 | } | |
c4fe7bb1 | 3368 | callout_reset(&sc->sc_rftimer, 2*hz, iwi_rfkill_poll, sc); |
0e474d75 JH |
3369 | } |
3370 | ||
3371 | static void | |
c4fe7bb1 | 3372 | iwi_radio_off(void *arg, int pending) |
0e474d75 JH |
3373 | { |
3374 | struct iwi_softc *sc = arg; | |
c4fe7bb1 IV |
3375 | struct ieee80211com *ic = &sc->sc_ic; |
3376 | IWI_LOCK_DECL; | |
0e474d75 JH |
3377 | |
3378 | device_printf(sc->sc_dev, "radio turned off\n"); | |
3379 | ||
3380 | ieee80211_notify_radio(ic, 0); | |
3381 | ||
c4fe7bb1 | 3382 | IWI_LOCK(sc); |
0e474d75 JH |
3383 | iwi_stop_locked(sc); |
3384 | iwi_rfkill_poll(sc); | |
c4fe7bb1 | 3385 | IWI_UNLOCK(sc); |
b50e4759 MD |
3386 | } |
3387 | ||
3388 | static int | |
3389 | iwi_sysctl_stats(SYSCTL_HANDLER_ARGS) | |
3390 | { | |
3391 | struct iwi_softc *sc = arg1; | |
841ab66c | 3392 | uint32_t size, buf[128]; |
b50e4759 | 3393 | |
0e474d75 | 3394 | memset(buf, 0, sizeof buf); |
b50e4759 | 3395 | |
0e474d75 JH |
3396 | if (!(sc->flags & IWI_FLAG_FW_INITED)) |
3397 | return SYSCTL_OUT(req, buf, sizeof buf); | |
b50e4759 MD |
3398 | |
3399 | size = min(CSR_READ_4(sc, IWI_CSR_TABLE0_SIZE), 128 - 1); | |
841ab66c | 3400 | CSR_READ_REGION_4(sc, IWI_CSR_TABLE0_BASE, &buf[1], size); |
0e474d75 JH |
3401 | |
3402 | return SYSCTL_OUT(req, buf, size); | |
b50e4759 MD |
3403 | } |
3404 | ||
3405 | static int | |
3406 | iwi_sysctl_radio(SYSCTL_HANDLER_ARGS) | |
3407 | { | |
3408 | struct iwi_softc *sc = arg1; | |
0e474d75 | 3409 | int val = !iwi_getrfkill(sc); |
841ab66c | 3410 | |
b50e4759 MD |
3411 | return SYSCTL_OUT(req, &val, sizeof val); |
3412 | } | |
3413 | ||
0e474d75 JH |
3414 | /* |
3415 | * Add sysctl knobs. | |
3416 | */ | |
3417 | static void | |
3418 | iwi_sysctlattach(struct iwi_softc *sc) | |
3419 | { | |
c4fe7bb1 IV |
3420 | struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); |
3421 | struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); | |
0e474d75 JH |
3422 | |
3423 | SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "radio", | |
3424 | CTLTYPE_INT | CTLFLAG_RD, sc, 0, iwi_sysctl_radio, "I", | |
3425 | "radio transmitter switch state (0=off, 1=on)"); | |
3426 | ||
3427 | SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "stats", | |
3428 | CTLTYPE_OPAQUE | CTLFLAG_RD, sc, 0, iwi_sysctl_stats, "S", | |
3429 | "statistics"); | |
3430 | ||
3431 | sc->bluetooth = 0; | |
3432 | SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "bluetooth", | |
3433 | CTLFLAG_RW, &sc->bluetooth, 0, "bluetooth coexistence"); | |
3434 | ||
3435 | sc->antenna = IWI_ANTENNA_AUTO; | |
3436 | SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "antenna", | |
3437 | CTLFLAG_RW, &sc->antenna, 0, "antenna (0=auto)"); | |
3438 | } | |
3439 | ||
3440 | /* | |
3441 | * LED support. | |
3442 | * | |
3443 | * Different cards have different capabilities. Some have three | |
3444 | * led's while others have only one. The linux ipw driver defines | |
3445 | * led's for link state (associated or not), band (11a, 11g, 11b), | |
3446 | * and for link activity. We use one led and vary the blink rate | |
3447 | * according to the tx/rx traffic a la the ath driver. | |
3448 | */ | |
3449 | ||
3450 | static __inline uint32_t | |
3451 | iwi_toggle_event(uint32_t r) | |
b50e4759 | 3452 | { |
0e474d75 JH |
3453 | return r &~ (IWI_RST_STANDBY | IWI_RST_GATE_ODMA | |
3454 | IWI_RST_GATE_IDMA | IWI_RST_GATE_ADMA); | |
3455 | } | |
b50e4759 | 3456 | |
0e474d75 JH |
3457 | static uint32_t |
3458 | iwi_read_event(struct iwi_softc *sc) | |
3459 | { | |
3460 | return MEM_READ_4(sc, IWI_MEM_EEPROM_EVENT); | |
3461 | } | |
b50e4759 | 3462 | |
0e474d75 JH |
3463 | static void |
3464 | iwi_write_event(struct iwi_softc *sc, uint32_t v) | |
3465 | { | |
3466 | MEM_WRITE_4(sc, IWI_MEM_EEPROM_EVENT, v); | |
3467 | } | |
841ab66c | 3468 | |
0e474d75 JH |
3469 | static void |
3470 | iwi_led_done(void *arg) | |
3471 | { | |
3472 | struct iwi_softc *sc = arg; | |
3473 | ||
3474 | sc->sc_blinking = 0; | |
3475 | } | |
3476 | ||
3477 | /* | |
3478 | * Turn the activity LED off: flip the pin and then set a timer so no | |
3479 | * update will happen for the specified duration. | |
3480 | */ | |
3481 | static void | |
3482 | iwi_led_off(void *arg) | |
3483 | { | |
3484 | struct iwi_softc *sc = arg; | |
3485 | uint32_t v; | |
3486 | ||
3487 | v = iwi_read_event(sc); | |
3488 | v &= ~sc->sc_ledpin; | |
3489 | iwi_write_event(sc, iwi_toggle_event(v)); | |
c4fe7bb1 | 3490 | callout_reset(&sc->sc_ledtimer, sc->sc_ledoff, iwi_led_done, sc); |
0e474d75 JH |
3491 | } |
3492 | ||
3493 | /* | |
3494 | * Blink the LED according to the specified on/off times. | |
3495 | */ | |
3496 | static void | |
3497 | iwi_led_blink(struct iwi_softc *sc, int on, int off) | |
3498 | { | |
3499 | uint32_t v; | |
3500 | ||
3501 | v = iwi_read_event(sc); | |
3502 | v |= sc->sc_ledpin; | |
3503 | iwi_write_event(sc, iwi_toggle_event(v)); | |
3504 | sc->sc_blinking = 1; | |
3505 | sc->sc_ledoff = off; | |
c4fe7bb1 | 3506 | callout_reset(&sc->sc_ledtimer, on, iwi_led_off, sc); |
0e474d75 JH |
3507 | } |
3508 | ||
3509 | static void | |
3510 | iwi_led_event(struct iwi_softc *sc, int event) | |
3511 | { | |
0e474d75 JH |
3512 | /* NB: on/off times from the Atheros NDIS driver, w/ permission */ |
3513 | static const struct { | |
3514 | u_int rate; /* tx/rx iwi rate */ | |
3515 | u_int16_t timeOn; /* LED on time (ms) */ | |
3516 | u_int16_t timeOff; /* LED off time (ms) */ | |
3517 | } blinkrates[] = { | |
3518 | { IWI_RATE_OFDM54, 40, 10 }, | |
3519 | { IWI_RATE_OFDM48, 44, 11 }, | |
3520 | { IWI_RATE_OFDM36, 50, 13 }, | |
3521 | { IWI_RATE_OFDM24, 57, 14 }, | |
3522 | { IWI_RATE_OFDM18, 67, 16 }, | |
3523 | { IWI_RATE_OFDM12, 80, 20 }, | |
3524 | { IWI_RATE_DS11, 100, 25 }, | |
3525 | { IWI_RATE_OFDM9, 133, 34 }, | |
3526 | { IWI_RATE_OFDM6, 160, 40 }, | |
3527 | { IWI_RATE_DS5, 200, 50 }, | |
3528 | { 6, 240, 58 }, /* XXX 3Mb/s if it existed */ | |
3529 | { IWI_RATE_DS2, 267, 66 }, | |
3530 | { IWI_RATE_DS1, 400, 100 }, | |
3531 | { 0, 500, 130 }, /* unknown rate/polling */ | |
3532 | }; | |
3533 | uint32_t txrate; | |
3534 | int j = 0; /* XXX silence compiler */ | |
3535 | ||
3536 | sc->sc_ledevent = ticks; /* time of last event */ | |
3537 | if (sc->sc_blinking) /* don't interrupt active blink */ | |
3538 | return; | |
3539 | switch (event) { | |
3540 | case IWI_LED_POLL: | |
c4fe7bb1 | 3541 | j = nitems(blinkrates)-1; |
0e474d75 JH |
3542 | break; |
3543 | case IWI_LED_TX: | |
3544 | /* read current transmission rate from adapter */ | |
3545 | txrate = CSR_READ_4(sc, IWI_CSR_CURRENT_TX_RATE); | |
3546 | if (blinkrates[sc->sc_txrix].rate != txrate) { | |
c4fe7bb1 | 3547 | for (j = 0; j < nitems(blinkrates)-1; j++) |
0e474d75 JH |
3548 | if (blinkrates[j].rate == txrate) |
3549 | break; | |
3550 | sc->sc_txrix = j; | |
3551 | } else | |
3552 | j = sc->sc_txrix; | |
3553 | break; | |
3554 | case IWI_LED_RX: | |
3555 | if (blinkrates[sc->sc_rxrix].rate != sc->sc_rxrate) { | |
c4fe7bb1 | 3556 | for (j = 0; j < nitems(blinkrates)-1; j++) |
0e474d75 JH |
3557 | if (blinkrates[j].rate == sc->sc_rxrate) |
3558 | break; | |
3559 | sc->sc_rxrix = j; | |
3560 | } else | |
3561 | j = sc->sc_rxrix; | |
3562 | break; | |
3563 | } | |
3564 | /* XXX beware of overflow */ | |
3565 | iwi_led_blink(sc, (blinkrates[j].timeOn * hz) / 1000, | |
3566 | (blinkrates[j].timeOff * hz) / 1000); | |
0e474d75 JH |
3567 | } |
3568 | ||
3569 | static int | |
3570 | iwi_sysctl_softled(SYSCTL_HANDLER_ARGS) | |
3571 | { | |
3572 | struct iwi_softc *sc = arg1; | |
3573 | int softled = sc->sc_softled; | |
3574 | int error; | |
3575 | ||
3576 | error = sysctl_handle_int(oidp, &softled, 0, req); | |
3577 | if (error || !req->newptr) | |
3578 | return error; | |
3579 | softled = (softled != 0); | |
3580 | if (softled != sc->sc_softled) { | |
3581 | if (softled) { | |
3582 | uint32_t v = iwi_read_event(sc); | |
3583 | v &= ~sc->sc_ledpin; | |
3584 | iwi_write_event(sc, iwi_toggle_event(v)); | |
b50e4759 | 3585 | } |
0e474d75 | 3586 | sc->sc_softled = softled; |
b50e4759 | 3587 | } |
0e474d75 JH |
3588 | return 0; |
3589 | } | |
3590 | ||
3591 | static void | |
3592 | iwi_ledattach(struct iwi_softc *sc) | |
3593 | { | |
26595b18 SW |
3594 | struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); |
3595 | struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); | |
0e474d75 JH |
3596 | |
3597 | sc->sc_blinking = 0; | |
3598 | sc->sc_ledstate = 1; | |
3599 | sc->sc_ledidle = (2700*hz)/1000; /* 2.7sec */ | |
c4fe7bb1 IV |
3600 | #if defined(__DragonFly__) |
3601 | callout_init_lk(&sc->sc_ledtimer, &sc->sc_lock); | |
3602 | #else | |
3603 | callout_init_mtx(&sc->sc_ledtimer, &sc->sc_mtx, 0); | |
3604 | #endif | |
0e474d75 JH |
3605 | |
3606 | SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, | |
3607 | "softled", CTLTYPE_INT | CTLFLAG_RW, sc, 0, | |
3608 | iwi_sysctl_softled, "I", "enable/disable software LED support"); | |
c4fe7bb1 | 3609 | SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, |
0e474d75 JH |
3610 | "ledpin", CTLFLAG_RW, &sc->sc_ledpin, 0, |
3611 | "pin setting to turn activity LED on"); | |
c4fe7bb1 | 3612 | SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, |
0e474d75 JH |
3613 | "ledidle", CTLFLAG_RW, &sc->sc_ledidle, 0, |
3614 | "idle time for inactivity LED (ticks)"); | |
3615 | /* XXX for debugging */ | |
c4fe7bb1 | 3616 | SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, |
0e474d75 JH |
3617 | "nictype", CTLFLAG_RD, &sc->sc_nictype, 0, |
3618 | "NIC type from EEPROM"); | |
3619 | ||
3620 | sc->sc_ledpin = IWI_RST_LED_ACTIVITY; | |
3621 | sc->sc_softled = 1; | |
3622 | ||
3623 | sc->sc_nictype = (iwi_read_prom_word(sc, IWI_EEPROM_NIC) >> 8) & 0xff; | |
3624 | if (sc->sc_nictype == 1) { | |
3625 | /* | |
3626 | * NB: led's are reversed. | |
3627 | */ | |
3628 | sc->sc_ledpin = IWI_RST_LED_ASSOCIATED; | |
3629 | } | |
3630 | } | |
3631 | ||
3632 | static void | |
3633 | iwi_scan_start(struct ieee80211com *ic) | |
3634 | { | |
3635 | /* ignore */ | |
b50e4759 MD |
3636 | } |
3637 | ||
841ab66c | 3638 | static void |
0e474d75 | 3639 | iwi_set_channel(struct ieee80211com *ic) |
b50e4759 | 3640 | { |
c4fe7bb1 IV |
3641 | struct iwi_softc *sc = ic->ic_softc; |
3642 | ||
0e474d75 JH |
3643 | if (sc->fw_state == IWI_FW_IDLE) |
3644 | iwi_setcurchan(sc, ic->ic_curchan->ic_ieee); | |
3645 | } | |
b50e4759 | 3646 | |
0e474d75 JH |
3647 | static void |
3648 | iwi_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell) | |
3649 | { | |
3650 | struct ieee80211vap *vap = ss->ss_vap; | |
c4fe7bb1 IV |
3651 | struct iwi_softc *sc = vap->iv_ic->ic_softc; |
3652 | IWI_LOCK_DECL; | |
b50e4759 | 3653 | |
c4fe7bb1 | 3654 | IWI_LOCK(sc); |
0e474d75 JH |
3655 | if (iwi_scanchan(sc, maxdwell, 0)) |
3656 | ieee80211_cancel_scan(vap); | |
c4fe7bb1 | 3657 | IWI_UNLOCK(sc); |
0e474d75 | 3658 | } |
b50e4759 | 3659 | |
0e474d75 JH |
3660 | static void |
3661 | iwi_scan_mindwell(struct ieee80211_scan_state *ss) | |
3662 | { | |
3663 | /* NB: don't try to abort scan; wait for firmware to finish */ | |
3664 | } | |
3665 | ||
3666 | static void | |
3667 | iwi_scan_end(struct ieee80211com *ic) | |
3668 | { | |
c4fe7bb1 IV |
3669 | struct iwi_softc *sc = ic->ic_softc; |
3670 | IWI_LOCK_DECL; | |
0e474d75 | 3671 | |
c4fe7bb1 | 3672 | IWI_LOCK(sc); |
0e474d75 JH |
3673 | sc->flags &= ~IWI_FLAG_CHANNEL_SCAN; |
3674 | /* NB: make sure we're still scanning */ | |
3675 | if (sc->fw_state == IWI_FW_SCANNING) | |
3676 | iwi_cmd(sc, IWI_CMD_ABORT_SCAN, NULL, 0); | |
c4fe7bb1 | 3677 | IWI_UNLOCK(sc); |
841ab66c | 3678 | } |