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