1145247Sdamien/*- 2158089Smlaier * Copyright (c) 2004, 2005 3145247Sdamien * Damien Bergamini <damien.bergamini@free.fr>. All rights reserved. 4158089Smlaier * Copyright (c) 2005-2006 Sam Leffler, Errno Consulting 5170554Sthompsa * Copyright (c) 2007 Andrew Thompson <thompsa@FreeBSD.org> 6145247Sdamien * 7145247Sdamien * Redistribution and use in source and binary forms, with or without 8145247Sdamien * modification, are permitted provided that the following conditions 9145247Sdamien * are met: 10145247Sdamien * 1. Redistributions of source code must retain the above copyright 11145247Sdamien * notice unmodified, this list of conditions, and the following 12145247Sdamien * disclaimer. 13145247Sdamien * 2. Redistributions in binary form must reproduce the above copyright 14145247Sdamien * notice, this list of conditions and the following disclaimer in the 15145247Sdamien * documentation and/or other materials provided with the distribution. 16145247Sdamien * 17145247Sdamien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18145247Sdamien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19145247Sdamien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20145247Sdamien * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21145247Sdamien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22145247Sdamien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23145247Sdamien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24145247Sdamien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25145247Sdamien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26145247Sdamien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27145247Sdamien * SUCH DAMAGE. 28145247Sdamien */ 29145247Sdamien 30145247Sdamien#include <sys/cdefs.h> 31145247Sdamien__FBSDID("$FreeBSD$"); 32145247Sdamien 33145247Sdamien/*- 34145247Sdamien * Intel(R) PRO/Wireless 2200BG/2225BG/2915ABG driver 35145247Sdamien * http://www.intel.com/network/connectivity/products/wireless/prowireless_mobile.htm 36145247Sdamien */ 37145247Sdamien 38145247Sdamien#include <sys/param.h> 39145247Sdamien#include <sys/sysctl.h> 40145247Sdamien#include <sys/sockio.h> 41145247Sdamien#include <sys/mbuf.h> 42145247Sdamien#include <sys/kernel.h> 43145247Sdamien#include <sys/socket.h> 44145247Sdamien#include <sys/systm.h> 45145247Sdamien#include <sys/malloc.h> 46164982Skevlo#include <sys/lock.h> 47164982Skevlo#include <sys/mutex.h> 48145247Sdamien#include <sys/module.h> 49145247Sdamien#include <sys/bus.h> 50145247Sdamien#include <sys/endian.h> 51158089Smlaier#include <sys/proc.h> 52158089Smlaier#include <sys/mount.h> 53158089Smlaier#include <sys/namei.h> 54156546Sdamien#include <sys/linker.h> 55156546Sdamien#include <sys/firmware.h> 56158089Smlaier#include <sys/taskqueue.h> 57145247Sdamien 58145247Sdamien#include <machine/bus.h> 59145247Sdamien#include <machine/resource.h> 60145247Sdamien#include <sys/rman.h> 61145247Sdamien 62145247Sdamien#include <dev/pci/pcireg.h> 63145247Sdamien#include <dev/pci/pcivar.h> 64145247Sdamien 65145247Sdamien#include <net/bpf.h> 66145247Sdamien#include <net/if.h> 67145247Sdamien#include <net/if_arp.h> 68145247Sdamien#include <net/ethernet.h> 69145247Sdamien#include <net/if_dl.h> 70145247Sdamien#include <net/if_media.h> 71145247Sdamien#include <net/if_types.h> 72145247Sdamien 73145247Sdamien#include <net80211/ieee80211_var.h> 74145247Sdamien#include <net80211/ieee80211_radiotap.h> 75178354Ssam#include <net80211/ieee80211_input.h> 76170530Ssam#include <net80211/ieee80211_regdomain.h> 77145247Sdamien 78145247Sdamien#include <netinet/in.h> 79145247Sdamien#include <netinet/in_systm.h> 80145247Sdamien#include <netinet/in_var.h> 81145247Sdamien#include <netinet/ip.h> 82145247Sdamien#include <netinet/if_ether.h> 83145247Sdamien 84158089Smlaier#include <dev/iwi/if_iwireg.h> 85154992Sdamien#include <dev/iwi/if_iwivar.h> 86145247Sdamien 87158089Smlaier#define IWI_DEBUG 88145247Sdamien#ifdef IWI_DEBUG 89145247Sdamien#define DPRINTF(x) do { if (iwi_debug > 0) printf x; } while (0) 90145247Sdamien#define DPRINTFN(n, x) do { if (iwi_debug >= (n)) printf x; } while (0) 91145247Sdamienint iwi_debug = 0; 92145247SdamienSYSCTL_INT(_debug, OID_AUTO, iwi, CTLFLAG_RW, &iwi_debug, 0, "iwi debug level"); 93178354Ssam 94178354Ssamstatic const char *iwi_fw_states[] = { 95178354Ssam "IDLE", /* IWI_FW_IDLE */ 96178354Ssam "LOADING", /* IWI_FW_LOADING */ 97178354Ssam "ASSOCIATING", /* IWI_FW_ASSOCIATING */ 98178354Ssam "DISASSOCIATING", /* IWI_FW_DISASSOCIATING */ 99178354Ssam "SCANNING", /* IWI_FW_SCANNING */ 100178354Ssam}; 101145247Sdamien#else 102145247Sdamien#define DPRINTF(x) 103145247Sdamien#define DPRINTFN(n, x) 104145247Sdamien#endif 105145247Sdamien 106145247SdamienMODULE_DEPEND(iwi, pci, 1, 1, 1); 107145247SdamienMODULE_DEPEND(iwi, wlan, 1, 1, 1); 108156571SdamienMODULE_DEPEND(iwi, firmware, 1, 1, 1); 109145247Sdamien 110158089Smlaierenum { 111158089Smlaier IWI_LED_TX, 112158089Smlaier IWI_LED_RX, 113158089Smlaier IWI_LED_POLL, 114158089Smlaier}; 115158089Smlaier 116145247Sdamienstruct iwi_ident { 117145247Sdamien uint16_t vendor; 118145247Sdamien uint16_t device; 119145247Sdamien const char *name; 120145247Sdamien}; 121145247Sdamien 122145247Sdamienstatic const struct iwi_ident iwi_ident_table[] = { 123145247Sdamien { 0x8086, 0x4220, "Intel(R) PRO/Wireless 2200BG" }, 124145247Sdamien { 0x8086, 0x4221, "Intel(R) PRO/Wireless 2225BG" }, 125145247Sdamien { 0x8086, 0x4223, "Intel(R) PRO/Wireless 2915ABG" }, 126145247Sdamien { 0x8086, 0x4224, "Intel(R) PRO/Wireless 2915ABG" }, 127145247Sdamien 128145247Sdamien { 0, 0, NULL } 129145247Sdamien}; 130145247Sdamien 131178354Ssamstatic struct ieee80211vap *iwi_vap_create(struct ieee80211com *, 132234753Sdim const char [IFNAMSIZ], int, enum ieee80211_opmode, int, 133234753Sdim const uint8_t [IEEE80211_ADDR_LEN], 134234753Sdim const uint8_t [IEEE80211_ADDR_LEN]); 135178354Ssamstatic void iwi_vap_delete(struct ieee80211vap *); 136145247Sdamienstatic void iwi_dma_map_addr(void *, bus_dma_segment_t *, int, int); 137145247Sdamienstatic int iwi_alloc_cmd_ring(struct iwi_softc *, struct iwi_cmd_ring *, 138145247Sdamien int); 139145247Sdamienstatic void iwi_reset_cmd_ring(struct iwi_softc *, struct iwi_cmd_ring *); 140145247Sdamienstatic void iwi_free_cmd_ring(struct iwi_softc *, struct iwi_cmd_ring *); 141145247Sdamienstatic int iwi_alloc_tx_ring(struct iwi_softc *, struct iwi_tx_ring *, 142149338Sdamien int, bus_addr_t, bus_addr_t); 143145247Sdamienstatic void iwi_reset_tx_ring(struct iwi_softc *, struct iwi_tx_ring *); 144145247Sdamienstatic void iwi_free_tx_ring(struct iwi_softc *, struct iwi_tx_ring *); 145145247Sdamienstatic int iwi_alloc_rx_ring(struct iwi_softc *, struct iwi_rx_ring *, 146145247Sdamien int); 147145247Sdamienstatic void iwi_reset_rx_ring(struct iwi_softc *, struct iwi_rx_ring *); 148145247Sdamienstatic void iwi_free_rx_ring(struct iwi_softc *, struct iwi_rx_ring *); 149179643Ssamstatic struct ieee80211_node *iwi_node_alloc(struct ieee80211vap *, 150179643Ssam const uint8_t [IEEE80211_ADDR_LEN]); 151150341Sdamienstatic void iwi_node_free(struct ieee80211_node *); 152145247Sdamienstatic void iwi_media_status(struct ifnet *, struct ifmediareq *); 153178354Ssamstatic int iwi_newstate(struct ieee80211vap *, enum ieee80211_state, int); 154158089Smlaierstatic void iwi_wme_init(struct iwi_softc *); 155178354Ssamstatic int iwi_wme_setparams(struct iwi_softc *, struct ieee80211com *); 156191951Ssamstatic void iwi_update_wme(void *, int); 157149338Sdamienstatic int iwi_wme_update(struct ieee80211com *); 158145247Sdamienstatic uint16_t iwi_read_prom_word(struct iwi_softc *, uint8_t); 159145247Sdamienstatic void iwi_frame_intr(struct iwi_softc *, struct iwi_rx_data *, int, 160145247Sdamien struct iwi_frame *); 161145247Sdamienstatic void iwi_notification_intr(struct iwi_softc *, struct iwi_notif *); 162145247Sdamienstatic void iwi_rx_intr(struct iwi_softc *); 163149338Sdamienstatic void iwi_tx_intr(struct iwi_softc *, struct iwi_tx_ring *); 164145247Sdamienstatic void iwi_intr(void *); 165158089Smlaierstatic int iwi_cmd(struct iwi_softc *, uint8_t, void *, uint8_t); 166158089Smlaierstatic void iwi_write_ibssnode(struct iwi_softc *, const u_int8_t [], int); 167145247Sdamienstatic int iwi_tx_start(struct ifnet *, struct mbuf *, 168150245Sdamien struct ieee80211_node *, int); 169178354Ssamstatic int iwi_raw_xmit(struct ieee80211_node *, struct mbuf *, 170178354Ssam const struct ieee80211_bpf_params *); 171178354Ssamstatic void iwi_start_locked(struct ifnet *); 172145247Sdamienstatic void iwi_start(struct ifnet *); 173170530Ssamstatic void iwi_watchdog(void *); 174145247Sdamienstatic int iwi_ioctl(struct ifnet *, u_long, caddr_t); 175145247Sdamienstatic void iwi_stop_master(struct iwi_softc *); 176145247Sdamienstatic int iwi_reset(struct iwi_softc *); 177158089Smlaierstatic int iwi_load_ucode(struct iwi_softc *, const struct iwi_fw *); 178158089Smlaierstatic int iwi_load_firmware(struct iwi_softc *, const struct iwi_fw *); 179166848Sluigistatic void iwi_release_fw_dma(struct iwi_softc *sc); 180145247Sdamienstatic int iwi_config(struct iwi_softc *); 181178354Ssamstatic int iwi_get_firmware(struct iwi_softc *, enum ieee80211_opmode); 182158089Smlaierstatic void iwi_put_firmware(struct iwi_softc *); 183213729Sbschmidtstatic void iwi_monitor_scan(void *, int); 184170530Ssamstatic int iwi_scanchan(struct iwi_softc *, unsigned long, int); 185170530Ssamstatic void iwi_scan_start(struct ieee80211com *); 186170530Ssamstatic void iwi_scan_end(struct ieee80211com *); 187170530Ssamstatic void iwi_set_channel(struct ieee80211com *); 188178354Ssamstatic void iwi_scan_curchan(struct ieee80211_scan_state *, unsigned long maxdwell); 189178354Ssamstatic void iwi_scan_mindwell(struct ieee80211_scan_state *); 190178354Ssamstatic int iwi_auth_and_assoc(struct iwi_softc *, struct ieee80211vap *); 191191746Sthompsastatic void iwi_disassoc(void *, int); 192158089Smlaierstatic int iwi_disassociate(struct iwi_softc *, int quiet); 193178354Ssamstatic void iwi_init_locked(struct iwi_softc *); 194145247Sdamienstatic void iwi_init(void *); 195178354Ssamstatic int iwi_init_fw_dma(struct iwi_softc *, int); 196178354Ssamstatic void iwi_stop_locked(void *); 197178354Ssamstatic void iwi_stop(struct iwi_softc *); 198158089Smlaierstatic void iwi_restart(void *, int); 199158089Smlaierstatic int iwi_getrfkill(struct iwi_softc *); 200158089Smlaierstatic void iwi_radio_on(void *, int); 201158089Smlaierstatic void iwi_radio_off(void *, int); 202158089Smlaierstatic void iwi_sysctlattach(struct iwi_softc *); 203158089Smlaierstatic void iwi_led_event(struct iwi_softc *, int); 204158089Smlaierstatic void iwi_ledattach(struct iwi_softc *); 205145247Sdamien 206145247Sdamienstatic int iwi_probe(device_t); 207145247Sdamienstatic int iwi_attach(device_t); 208145247Sdamienstatic int iwi_detach(device_t); 209145247Sdamienstatic int iwi_shutdown(device_t); 210145247Sdamienstatic int iwi_suspend(device_t); 211145247Sdamienstatic int iwi_resume(device_t); 212145247Sdamien 213145247Sdamienstatic device_method_t iwi_methods[] = { 214145247Sdamien /* Device interface */ 215145247Sdamien DEVMETHOD(device_probe, iwi_probe), 216145247Sdamien DEVMETHOD(device_attach, iwi_attach), 217145247Sdamien DEVMETHOD(device_detach, iwi_detach), 218145247Sdamien DEVMETHOD(device_shutdown, iwi_shutdown), 219145247Sdamien DEVMETHOD(device_suspend, iwi_suspend), 220145247Sdamien DEVMETHOD(device_resume, iwi_resume), 221145247Sdamien 222264953Smarius DEVMETHOD_END 223145247Sdamien}; 224145247Sdamien 225145247Sdamienstatic driver_t iwi_driver = { 226145247Sdamien "iwi", 227145247Sdamien iwi_methods, 228145247Sdamien sizeof (struct iwi_softc) 229145247Sdamien}; 230145247Sdamien 231145247Sdamienstatic devclass_t iwi_devclass; 232145247Sdamien 233264953SmariusDRIVER_MODULE(iwi, pci, iwi_driver, iwi_devclass, NULL, NULL); 234145247Sdamien 235222543SbschmidtMODULE_VERSION(iwi, 1); 236222543Sbschmidt 237158089Smlaierstatic __inline uint8_t 238158089SmlaierMEM_READ_1(struct iwi_softc *sc, uint32_t addr) 239158089Smlaier{ 240158089Smlaier CSR_WRITE_4(sc, IWI_CSR_INDIRECT_ADDR, addr); 241158089Smlaier return CSR_READ_1(sc, IWI_CSR_INDIRECT_DATA); 242158089Smlaier} 243158089Smlaier 244158089Smlaierstatic __inline uint32_t 245158089SmlaierMEM_READ_4(struct iwi_softc *sc, uint32_t addr) 246158089Smlaier{ 247158089Smlaier CSR_WRITE_4(sc, IWI_CSR_INDIRECT_ADDR, addr); 248158089Smlaier return CSR_READ_4(sc, IWI_CSR_INDIRECT_DATA); 249158089Smlaier} 250158089Smlaier 251145247Sdamienstatic int 252145247Sdamieniwi_probe(device_t dev) 253145247Sdamien{ 254145247Sdamien const struct iwi_ident *ident; 255145247Sdamien 256145247Sdamien for (ident = iwi_ident_table; ident->name != NULL; ident++) { 257145247Sdamien if (pci_get_vendor(dev) == ident->vendor && 258145247Sdamien pci_get_device(dev) == ident->device) { 259145247Sdamien device_set_desc(dev, ident->name); 260264953Smarius return (BUS_PROBE_DEFAULT); 261145247Sdamien } 262145247Sdamien } 263145247Sdamien return ENXIO; 264145247Sdamien} 265145247Sdamien 266145247Sdamienstatic int 267145247Sdamieniwi_attach(device_t dev) 268145247Sdamien{ 269145247Sdamien struct iwi_softc *sc = device_get_softc(dev); 270147256Sbrooks struct ifnet *ifp; 271178354Ssam struct ieee80211com *ic; 272145247Sdamien uint16_t val; 273178354Ssam int i, error; 274178354Ssam uint8_t bands; 275190526Ssam uint8_t macaddr[IEEE80211_ADDR_LEN]; 276145247Sdamien 277145247Sdamien sc->sc_dev = dev; 278145247Sdamien 279178354Ssam ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); 280178354Ssam if (ifp == NULL) { 281178354Ssam device_printf(dev, "can not if_alloc()\n"); 282178354Ssam return ENXIO; 283178354Ssam } 284178354Ssam ic = ifp->if_l2com; 285178354Ssam 286170530Ssam IWI_LOCK_INIT(sc); 287145247Sdamien 288158089Smlaier sc->sc_unr = new_unrhdr(1, IWI_MAX_IBSSNODE-1, &sc->sc_mtx); 289150341Sdamien 290158089Smlaier TASK_INIT(&sc->sc_radiontask, 0, iwi_radio_on, sc); 291158089Smlaier TASK_INIT(&sc->sc_radiofftask, 0, iwi_radio_off, sc); 292170530Ssam TASK_INIT(&sc->sc_restarttask, 0, iwi_restart, sc); 293191746Sthompsa TASK_INIT(&sc->sc_disassoctask, 0, iwi_disassoc, sc); 294191951Ssam TASK_INIT(&sc->sc_wmetask, 0, iwi_update_wme, sc); 295213729Sbschmidt TASK_INIT(&sc->sc_monitortask, 0, iwi_monitor_scan, sc); 296191746Sthompsa 297170530Ssam callout_init_mtx(&sc->sc_wdtimer, &sc->sc_mtx, 0); 298178354Ssam callout_init_mtx(&sc->sc_rftimer, &sc->sc_mtx, 0); 299156598Sdamien 300146500Sdamien pci_write_config(dev, 0x41, 0, 1); 301146500Sdamien 302145247Sdamien /* enable bus-mastering */ 303145247Sdamien pci_enable_busmaster(dev); 304145247Sdamien 305264953Smarius i = PCIR_BAR(0); 306264953Smarius sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &i, RF_ACTIVE); 307145247Sdamien if (sc->mem == NULL) { 308145247Sdamien device_printf(dev, "could not allocate memory resource\n"); 309145247Sdamien goto fail; 310145247Sdamien } 311145247Sdamien 312145247Sdamien sc->sc_st = rman_get_bustag(sc->mem); 313145247Sdamien sc->sc_sh = rman_get_bushandle(sc->mem); 314145247Sdamien 315264953Smarius i = 0; 316264953Smarius sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &i, 317145247Sdamien RF_ACTIVE | RF_SHAREABLE); 318145247Sdamien if (sc->irq == NULL) { 319145247Sdamien device_printf(dev, "could not allocate interrupt resource\n"); 320145247Sdamien goto fail; 321145247Sdamien } 322145247Sdamien 323145247Sdamien if (iwi_reset(sc) != 0) { 324145247Sdamien device_printf(dev, "could not reset adapter\n"); 325145247Sdamien goto fail; 326145247Sdamien } 327145247Sdamien 328145247Sdamien /* 329145247Sdamien * Allocate rings. 330145247Sdamien */ 331145247Sdamien if (iwi_alloc_cmd_ring(sc, &sc->cmdq, IWI_CMD_RING_COUNT) != 0) { 332145247Sdamien device_printf(dev, "could not allocate Cmd ring\n"); 333145247Sdamien goto fail; 334145247Sdamien } 335145247Sdamien 336166848Sluigi for (i = 0; i < 4; i++) { 337166848Sluigi error = iwi_alloc_tx_ring(sc, &sc->txq[i], IWI_TX_RING_COUNT, 338166848Sluigi IWI_CSR_TX1_RIDX + i * 4, 339166848Sluigi IWI_CSR_TX1_WIDX + i * 4); 340166848Sluigi if (error != 0) { 341166848Sluigi device_printf(dev, "could not allocate Tx ring %d\n", 342166848Sluigi i+i); 343166848Sluigi goto fail; 344166848Sluigi } 345145247Sdamien } 346145247Sdamien 347145247Sdamien if (iwi_alloc_rx_ring(sc, &sc->rxq, IWI_RX_RING_COUNT) != 0) { 348145247Sdamien device_printf(dev, "could not allocate Rx ring\n"); 349145247Sdamien goto fail; 350145247Sdamien } 351145247Sdamien 352158089Smlaier iwi_wme_init(sc); 353158089Smlaier 354145247Sdamien ifp->if_softc = sc; 355145247Sdamien if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 356145247Sdamien ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 357145247Sdamien ifp->if_init = iwi_init; 358145247Sdamien ifp->if_ioctl = iwi_ioctl; 359145247Sdamien ifp->if_start = iwi_start; 360207554Ssobomax IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); 361207554Ssobomax ifp->if_snd.ifq_drv_maxlen = ifqmaxlen; 362145247Sdamien IFQ_SET_READY(&ifp->if_snd); 363145247Sdamien 364178354Ssam ic->ic_ifp = ifp; 365178354Ssam ic->ic_opmode = IEEE80211_M_STA; 366145247Sdamien ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ 367145247Sdamien 368145247Sdamien /* set device capabilities */ 369149338Sdamien ic->ic_caps = 370178957Ssam IEEE80211_C_STA /* station mode supported */ 371178957Ssam | IEEE80211_C_IBSS /* IBSS mode supported */ 372170530Ssam | IEEE80211_C_MONITOR /* monitor mode supported */ 373170530Ssam | IEEE80211_C_PMGT /* power save supported */ 374170530Ssam | IEEE80211_C_SHPREAMBLE /* short preamble supported */ 375170530Ssam | IEEE80211_C_WPA /* 802.11i */ 376170530Ssam | IEEE80211_C_WME /* 802.11e */ 377178354Ssam#if 0 378170530Ssam | IEEE80211_C_BGSCAN /* capable of bg scanning */ 379178354Ssam#endif 380170530Ssam ; 381145247Sdamien 382145247Sdamien /* read MAC address from EEPROM */ 383145247Sdamien val = iwi_read_prom_word(sc, IWI_EEPROM_MAC + 0); 384190526Ssam macaddr[0] = val & 0xff; 385190526Ssam macaddr[1] = val >> 8; 386145247Sdamien val = iwi_read_prom_word(sc, IWI_EEPROM_MAC + 1); 387190526Ssam macaddr[2] = val & 0xff; 388190526Ssam macaddr[3] = val >> 8; 389145247Sdamien val = iwi_read_prom_word(sc, IWI_EEPROM_MAC + 2); 390190526Ssam macaddr[4] = val & 0xff; 391190526Ssam macaddr[5] = val >> 8; 392170530Ssam 393170530Ssam bands = 0; 394170530Ssam setbit(&bands, IEEE80211_MODE_11B); 395170530Ssam setbit(&bands, IEEE80211_MODE_11G); 396170530Ssam if (pci_get_device(dev) >= 0x4223) 397170530Ssam setbit(&bands, IEEE80211_MODE_11A); 398178354Ssam ieee80211_init_channels(ic, NULL, &bands); 399145247Sdamien 400190526Ssam ieee80211_ifattach(ic, macaddr); 401150341Sdamien /* override default methods */ 402150341Sdamien ic->ic_node_alloc = iwi_node_alloc; 403150341Sdamien sc->sc_node_free = ic->ic_node_free; 404150341Sdamien ic->ic_node_free = iwi_node_free; 405178354Ssam ic->ic_raw_xmit = iwi_raw_xmit; 406170530Ssam ic->ic_scan_start = iwi_scan_start; 407170530Ssam ic->ic_scan_end = iwi_scan_end; 408170530Ssam ic->ic_set_channel = iwi_set_channel; 409170530Ssam ic->ic_scan_curchan = iwi_scan_curchan; 410170530Ssam ic->ic_scan_mindwell = iwi_scan_mindwell; 411178354Ssam ic->ic_wme.wme_update = iwi_wme_update; 412170530Ssam 413178354Ssam ic->ic_vap_create = iwi_vap_create; 414178354Ssam ic->ic_vap_delete = iwi_vap_delete; 415145247Sdamien 416192468Ssam ieee80211_radiotap_attach(ic, 417192468Ssam &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), 418192468Ssam IWI_TX_RADIOTAP_PRESENT, 419192468Ssam &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), 420192468Ssam IWI_RX_RADIOTAP_PRESENT); 421145247Sdamien 422158089Smlaier iwi_sysctlattach(sc); 423158089Smlaier iwi_ledattach(sc); 424145247Sdamien 425145247Sdamien /* 426145247Sdamien * Hook our interrupt after all initialization is complete. 427145247Sdamien */ 428145247Sdamien error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET | INTR_MPSAFE, 429166901Spiso NULL, iwi_intr, sc, &sc->sc_ih); 430145247Sdamien if (error != 0) { 431145247Sdamien device_printf(dev, "could not set up interrupt\n"); 432145247Sdamien goto fail; 433145247Sdamien } 434145247Sdamien 435145247Sdamien if (bootverbose) 436145247Sdamien ieee80211_announce(ic); 437145247Sdamien 438145247Sdamien return 0; 439178354Ssamfail: 440178354Ssam /* XXX fix */ 441178354Ssam iwi_detach(dev); 442145247Sdamien return ENXIO; 443145247Sdamien} 444145247Sdamien 445145247Sdamienstatic int 446145247Sdamieniwi_detach(device_t dev) 447145247Sdamien{ 448145247Sdamien struct iwi_softc *sc = device_get_softc(dev); 449178354Ssam struct ifnet *ifp = sc->sc_ifp; 450178354Ssam struct ieee80211com *ic = ifp->if_l2com; 451145247Sdamien 452264953Smarius bus_teardown_intr(dev, sc->irq, sc->sc_ih); 453264953Smarius 454178354Ssam /* NB: do early to drain any pending tasks */ 455191746Sthompsa ieee80211_draintask(ic, &sc->sc_radiontask); 456191746Sthompsa ieee80211_draintask(ic, &sc->sc_radiofftask); 457191746Sthompsa ieee80211_draintask(ic, &sc->sc_restarttask); 458191746Sthompsa ieee80211_draintask(ic, &sc->sc_disassoctask); 459213729Sbschmidt ieee80211_draintask(ic, &sc->sc_monitortask); 460178354Ssam 461191912Sthompsa iwi_stop(sc); 462191912Sthompsa 463191912Sthompsa ieee80211_ifdetach(ic); 464191912Sthompsa 465164069Sjhb iwi_put_firmware(sc); 466166848Sluigi iwi_release_fw_dma(sc); 467145247Sdamien 468145247Sdamien iwi_free_cmd_ring(sc, &sc->cmdq); 469149338Sdamien iwi_free_tx_ring(sc, &sc->txq[0]); 470149338Sdamien iwi_free_tx_ring(sc, &sc->txq[1]); 471149338Sdamien iwi_free_tx_ring(sc, &sc->txq[2]); 472149338Sdamien iwi_free_tx_ring(sc, &sc->txq[3]); 473145247Sdamien iwi_free_rx_ring(sc, &sc->rxq); 474145247Sdamien 475264953Smarius bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(sc->irq), sc->irq); 476145247Sdamien 477264953Smarius bus_release_resource(dev, SYS_RES_MEMORY, rman_get_rid(sc->mem), 478264953Smarius sc->mem); 479145247Sdamien 480178354Ssam delete_unrhdr(sc->sc_unr); 481150330Sdamien 482170530Ssam IWI_LOCK_DESTROY(sc); 483145247Sdamien 484178354Ssam if_free(ifp); 485178354Ssam 486145247Sdamien return 0; 487145247Sdamien} 488145247Sdamien 489178354Ssamstatic struct ieee80211vap * 490234753Sdimiwi_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, 491234753Sdim enum ieee80211_opmode opmode, int flags, 492234753Sdim const uint8_t bssid[IEEE80211_ADDR_LEN], 493234753Sdim const uint8_t mac[IEEE80211_ADDR_LEN]) 494178354Ssam{ 495178354Ssam struct ifnet *ifp = ic->ic_ifp; 496178354Ssam struct iwi_softc *sc = ifp->if_softc; 497178354Ssam struct iwi_vap *ivp; 498178354Ssam struct ieee80211vap *vap; 499178354Ssam int i; 500178354Ssam 501178354Ssam if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ 502178354Ssam return NULL; 503178354Ssam /* 504178354Ssam * Get firmware image (and possibly dma memory) on mode change. 505178354Ssam */ 506178354Ssam if (iwi_get_firmware(sc, opmode)) 507178354Ssam return NULL; 508178354Ssam /* allocate DMA memory for mapping firmware image */ 509178354Ssam i = sc->fw_fw.size; 510178354Ssam if (sc->fw_boot.size > i) 511178354Ssam i = sc->fw_boot.size; 512178354Ssam /* XXX do we dma the ucode as well ? */ 513178354Ssam if (sc->fw_uc.size > i) 514178354Ssam i = sc->fw_uc.size; 515178354Ssam if (iwi_init_fw_dma(sc, i)) 516178354Ssam return NULL; 517178354Ssam 518178354Ssam ivp = (struct iwi_vap *) malloc(sizeof(struct iwi_vap), 519178354Ssam M_80211_VAP, M_NOWAIT | M_ZERO); 520178354Ssam if (ivp == NULL) 521178354Ssam return NULL; 522178354Ssam vap = &ivp->iwi_vap; 523178354Ssam ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac); 524178354Ssam /* override the default, the setting comes from the linux driver */ 525178354Ssam vap->iv_bmissthreshold = 24; 526178354Ssam /* override with driver methods */ 527178354Ssam ivp->iwi_newstate = vap->iv_newstate; 528178354Ssam vap->iv_newstate = iwi_newstate; 529178354Ssam 530178354Ssam /* complete setup */ 531178354Ssam ieee80211_vap_attach(vap, ieee80211_media_change, iwi_media_status); 532178354Ssam ic->ic_opmode = opmode; 533178354Ssam return vap; 534178354Ssam} 535178354Ssam 536145247Sdamienstatic void 537178354Ssamiwi_vap_delete(struct ieee80211vap *vap) 538178354Ssam{ 539178354Ssam struct iwi_vap *ivp = IWI_VAP(vap); 540178354Ssam 541178354Ssam ieee80211_vap_detach(vap); 542178354Ssam free(ivp, M_80211_VAP); 543178354Ssam} 544178354Ssam 545178354Ssamstatic void 546145247Sdamieniwi_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nseg, int error) 547145247Sdamien{ 548145247Sdamien if (error != 0) 549145247Sdamien return; 550145247Sdamien 551145247Sdamien KASSERT(nseg == 1, ("too many DMA segments, %d should be 1", nseg)); 552145247Sdamien 553145247Sdamien *(bus_addr_t *)arg = segs[0].ds_addr; 554145247Sdamien} 555145247Sdamien 556145247Sdamienstatic int 557145247Sdamieniwi_alloc_cmd_ring(struct iwi_softc *sc, struct iwi_cmd_ring *ring, int count) 558145247Sdamien{ 559145247Sdamien int error; 560145247Sdamien 561145247Sdamien ring->count = count; 562145247Sdamien ring->queued = 0; 563145247Sdamien ring->cur = ring->next = 0; 564145247Sdamien 565166416Skevlo error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 4, 0, 566166416Skevlo BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, 567166416Skevlo count * IWI_CMD_DESC_SIZE, 1, count * IWI_CMD_DESC_SIZE, 0, 568166416Skevlo NULL, NULL, &ring->desc_dmat); 569145247Sdamien if (error != 0) { 570145247Sdamien device_printf(sc->sc_dev, "could not create desc DMA tag\n"); 571145247Sdamien goto fail; 572145247Sdamien } 573145247Sdamien 574145247Sdamien error = bus_dmamem_alloc(ring->desc_dmat, (void **)&ring->desc, 575145247Sdamien BUS_DMA_NOWAIT | BUS_DMA_ZERO, &ring->desc_map); 576145247Sdamien if (error != 0) { 577145247Sdamien device_printf(sc->sc_dev, "could not allocate DMA memory\n"); 578145247Sdamien goto fail; 579145247Sdamien } 580145247Sdamien 581145247Sdamien error = bus_dmamap_load(ring->desc_dmat, ring->desc_map, ring->desc, 582145247Sdamien count * IWI_CMD_DESC_SIZE, iwi_dma_map_addr, &ring->physaddr, 0); 583145247Sdamien if (error != 0) { 584145247Sdamien device_printf(sc->sc_dev, "could not load desc DMA map\n"); 585145247Sdamien goto fail; 586145247Sdamien } 587145247Sdamien 588145247Sdamien return 0; 589145247Sdamien 590145247Sdamienfail: iwi_free_cmd_ring(sc, ring); 591145247Sdamien return error; 592145247Sdamien} 593145247Sdamien 594145247Sdamienstatic void 595145247Sdamieniwi_reset_cmd_ring(struct iwi_softc *sc, struct iwi_cmd_ring *ring) 596145247Sdamien{ 597145247Sdamien ring->queued = 0; 598145247Sdamien ring->cur = ring->next = 0; 599145247Sdamien} 600145247Sdamien 601145247Sdamienstatic void 602145247Sdamieniwi_free_cmd_ring(struct iwi_softc *sc, struct iwi_cmd_ring *ring) 603145247Sdamien{ 604145247Sdamien if (ring->desc != NULL) { 605145247Sdamien bus_dmamap_sync(ring->desc_dmat, ring->desc_map, 606145247Sdamien BUS_DMASYNC_POSTWRITE); 607145247Sdamien bus_dmamap_unload(ring->desc_dmat, ring->desc_map); 608145247Sdamien bus_dmamem_free(ring->desc_dmat, ring->desc, ring->desc_map); 609145247Sdamien } 610145247Sdamien 611145247Sdamien if (ring->desc_dmat != NULL) 612145247Sdamien bus_dma_tag_destroy(ring->desc_dmat); 613145247Sdamien} 614145247Sdamien 615145247Sdamienstatic int 616149338Sdamieniwi_alloc_tx_ring(struct iwi_softc *sc, struct iwi_tx_ring *ring, int count, 617149338Sdamien bus_addr_t csr_ridx, bus_addr_t csr_widx) 618145247Sdamien{ 619145247Sdamien int i, error; 620145247Sdamien 621145247Sdamien ring->count = count; 622145247Sdamien ring->queued = 0; 623145247Sdamien ring->cur = ring->next = 0; 624149338Sdamien ring->csr_ridx = csr_ridx; 625149338Sdamien ring->csr_widx = csr_widx; 626145247Sdamien 627166416Skevlo error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 4, 0, 628166416Skevlo BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, 629166416Skevlo count * IWI_TX_DESC_SIZE, 1, count * IWI_TX_DESC_SIZE, 0, NULL, 630166416Skevlo NULL, &ring->desc_dmat); 631145247Sdamien if (error != 0) { 632145247Sdamien device_printf(sc->sc_dev, "could not create desc DMA tag\n"); 633145247Sdamien goto fail; 634145247Sdamien } 635145247Sdamien 636145247Sdamien error = bus_dmamem_alloc(ring->desc_dmat, (void **)&ring->desc, 637145247Sdamien BUS_DMA_NOWAIT | BUS_DMA_ZERO, &ring->desc_map); 638145247Sdamien if (error != 0) { 639145247Sdamien device_printf(sc->sc_dev, "could not allocate DMA memory\n"); 640145247Sdamien goto fail; 641145247Sdamien } 642145247Sdamien 643145247Sdamien error = bus_dmamap_load(ring->desc_dmat, ring->desc_map, ring->desc, 644145247Sdamien count * IWI_TX_DESC_SIZE, iwi_dma_map_addr, &ring->physaddr, 0); 645145247Sdamien if (error != 0) { 646145247Sdamien device_printf(sc->sc_dev, "could not load desc DMA map\n"); 647145247Sdamien goto fail; 648145247Sdamien } 649145247Sdamien 650145247Sdamien ring->data = malloc(count * sizeof (struct iwi_tx_data), M_DEVBUF, 651145247Sdamien M_NOWAIT | M_ZERO); 652145247Sdamien if (ring->data == NULL) { 653145247Sdamien device_printf(sc->sc_dev, "could not allocate soft data\n"); 654145247Sdamien error = ENOMEM; 655145247Sdamien goto fail; 656145247Sdamien } 657145247Sdamien 658166416Skevlo error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, 659166416Skevlo BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, 660166416Skevlo IWI_MAX_NSEG, MCLBYTES, 0, NULL, NULL, &ring->data_dmat); 661145247Sdamien if (error != 0) { 662145247Sdamien device_printf(sc->sc_dev, "could not create data DMA tag\n"); 663145247Sdamien goto fail; 664145247Sdamien } 665145247Sdamien 666145247Sdamien for (i = 0; i < count; i++) { 667145247Sdamien error = bus_dmamap_create(ring->data_dmat, 0, 668145247Sdamien &ring->data[i].map); 669145247Sdamien if (error != 0) { 670145247Sdamien device_printf(sc->sc_dev, "could not create DMA map\n"); 671145247Sdamien goto fail; 672145247Sdamien } 673145247Sdamien } 674145247Sdamien 675145247Sdamien return 0; 676145247Sdamien 677145247Sdamienfail: iwi_free_tx_ring(sc, ring); 678145247Sdamien return error; 679145247Sdamien} 680145247Sdamien 681145247Sdamienstatic void 682145247Sdamieniwi_reset_tx_ring(struct iwi_softc *sc, struct iwi_tx_ring *ring) 683145247Sdamien{ 684145247Sdamien struct iwi_tx_data *data; 685145247Sdamien int i; 686145247Sdamien 687145247Sdamien for (i = 0; i < ring->count; i++) { 688145247Sdamien data = &ring->data[i]; 689145247Sdamien 690145247Sdamien if (data->m != NULL) { 691145247Sdamien bus_dmamap_sync(ring->data_dmat, data->map, 692145247Sdamien BUS_DMASYNC_POSTWRITE); 693145247Sdamien bus_dmamap_unload(ring->data_dmat, data->map); 694145247Sdamien m_freem(data->m); 695145247Sdamien data->m = NULL; 696145247Sdamien } 697145247Sdamien 698145247Sdamien if (data->ni != NULL) { 699145247Sdamien ieee80211_free_node(data->ni); 700145247Sdamien data->ni = NULL; 701145247Sdamien } 702145247Sdamien } 703145247Sdamien 704145247Sdamien ring->queued = 0; 705145247Sdamien ring->cur = ring->next = 0; 706145247Sdamien} 707145247Sdamien 708145247Sdamienstatic void 709145247Sdamieniwi_free_tx_ring(struct iwi_softc *sc, struct iwi_tx_ring *ring) 710145247Sdamien{ 711145247Sdamien struct iwi_tx_data *data; 712145247Sdamien int i; 713145247Sdamien 714145247Sdamien if (ring->desc != NULL) { 715145247Sdamien bus_dmamap_sync(ring->desc_dmat, ring->desc_map, 716145247Sdamien BUS_DMASYNC_POSTWRITE); 717145247Sdamien bus_dmamap_unload(ring->desc_dmat, ring->desc_map); 718145247Sdamien bus_dmamem_free(ring->desc_dmat, ring->desc, ring->desc_map); 719145247Sdamien } 720145247Sdamien 721145247Sdamien if (ring->desc_dmat != NULL) 722145247Sdamien bus_dma_tag_destroy(ring->desc_dmat); 723145247Sdamien 724145247Sdamien if (ring->data != NULL) { 725145247Sdamien for (i = 0; i < ring->count; i++) { 726145247Sdamien data = &ring->data[i]; 727145247Sdamien 728145247Sdamien if (data->m != NULL) { 729145247Sdamien bus_dmamap_sync(ring->data_dmat, data->map, 730145247Sdamien BUS_DMASYNC_POSTWRITE); 731145247Sdamien bus_dmamap_unload(ring->data_dmat, data->map); 732145247Sdamien m_freem(data->m); 733145247Sdamien } 734145247Sdamien 735145247Sdamien if (data->ni != NULL) 736145247Sdamien ieee80211_free_node(data->ni); 737145247Sdamien 738145247Sdamien if (data->map != NULL) 739145247Sdamien bus_dmamap_destroy(ring->data_dmat, data->map); 740145247Sdamien } 741145247Sdamien 742145247Sdamien free(ring->data, M_DEVBUF); 743145247Sdamien } 744145247Sdamien 745145247Sdamien if (ring->data_dmat != NULL) 746145247Sdamien bus_dma_tag_destroy(ring->data_dmat); 747145247Sdamien} 748145247Sdamien 749145247Sdamienstatic int 750145247Sdamieniwi_alloc_rx_ring(struct iwi_softc *sc, struct iwi_rx_ring *ring, int count) 751145247Sdamien{ 752145247Sdamien struct iwi_rx_data *data; 753145247Sdamien int i, error; 754145247Sdamien 755145247Sdamien ring->count = count; 756145247Sdamien ring->cur = 0; 757145247Sdamien 758145247Sdamien ring->data = malloc(count * sizeof (struct iwi_rx_data), M_DEVBUF, 759145247Sdamien M_NOWAIT | M_ZERO); 760145247Sdamien if (ring->data == NULL) { 761145247Sdamien device_printf(sc->sc_dev, "could not allocate soft data\n"); 762145247Sdamien error = ENOMEM; 763145247Sdamien goto fail; 764145247Sdamien } 765145247Sdamien 766166416Skevlo error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, 767166416Skevlo BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, 768166416Skevlo 1, MCLBYTES, 0, NULL, NULL, &ring->data_dmat); 769145247Sdamien if (error != 0) { 770145247Sdamien device_printf(sc->sc_dev, "could not create data DMA tag\n"); 771145247Sdamien goto fail; 772145247Sdamien } 773145247Sdamien 774145247Sdamien for (i = 0; i < count; i++) { 775145247Sdamien data = &ring->data[i]; 776145247Sdamien 777145247Sdamien error = bus_dmamap_create(ring->data_dmat, 0, &data->map); 778145247Sdamien if (error != 0) { 779145247Sdamien device_printf(sc->sc_dev, "could not create DMA map\n"); 780145247Sdamien goto fail; 781145247Sdamien } 782145247Sdamien 783248078Smarius data->m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); 784145247Sdamien if (data->m == NULL) { 785145247Sdamien device_printf(sc->sc_dev, 786145247Sdamien "could not allocate rx mbuf\n"); 787145247Sdamien error = ENOMEM; 788145247Sdamien goto fail; 789145247Sdamien } 790145247Sdamien 791145247Sdamien error = bus_dmamap_load(ring->data_dmat, data->map, 792145247Sdamien mtod(data->m, void *), MCLBYTES, iwi_dma_map_addr, 793145247Sdamien &data->physaddr, 0); 794145247Sdamien if (error != 0) { 795145247Sdamien device_printf(sc->sc_dev, 796145247Sdamien "could not load rx buf DMA map"); 797145247Sdamien goto fail; 798145247Sdamien } 799145247Sdamien 800145247Sdamien data->reg = IWI_CSR_RX_BASE + i * 4; 801145247Sdamien } 802145247Sdamien 803145247Sdamien return 0; 804145247Sdamien 805145247Sdamienfail: iwi_free_rx_ring(sc, ring); 806145247Sdamien return error; 807145247Sdamien} 808145247Sdamien 809145247Sdamienstatic void 810145247Sdamieniwi_reset_rx_ring(struct iwi_softc *sc, struct iwi_rx_ring *ring) 811145247Sdamien{ 812145247Sdamien ring->cur = 0; 813145247Sdamien} 814145247Sdamien 815145247Sdamienstatic void 816145247Sdamieniwi_free_rx_ring(struct iwi_softc *sc, struct iwi_rx_ring *ring) 817145247Sdamien{ 818145247Sdamien struct iwi_rx_data *data; 819145247Sdamien int i; 820145247Sdamien 821145247Sdamien if (ring->data != NULL) { 822145247Sdamien for (i = 0; i < ring->count; i++) { 823145247Sdamien data = &ring->data[i]; 824145247Sdamien 825145247Sdamien if (data->m != NULL) { 826145247Sdamien bus_dmamap_sync(ring->data_dmat, data->map, 827145247Sdamien BUS_DMASYNC_POSTREAD); 828145247Sdamien bus_dmamap_unload(ring->data_dmat, data->map); 829145247Sdamien m_freem(data->m); 830145247Sdamien } 831145247Sdamien 832145247Sdamien if (data->map != NULL) 833145247Sdamien bus_dmamap_destroy(ring->data_dmat, data->map); 834145247Sdamien } 835145247Sdamien 836145247Sdamien free(ring->data, M_DEVBUF); 837145247Sdamien } 838145247Sdamien 839145247Sdamien if (ring->data_dmat != NULL) 840145247Sdamien bus_dma_tag_destroy(ring->data_dmat); 841145247Sdamien} 842145247Sdamien 843145247Sdamienstatic int 844145247Sdamieniwi_shutdown(device_t dev) 845145247Sdamien{ 846145247Sdamien struct iwi_softc *sc = device_get_softc(dev); 847145247Sdamien 848145247Sdamien iwi_stop(sc); 849158089Smlaier iwi_put_firmware(sc); /* ??? XXX */ 850145247Sdamien 851145247Sdamien return 0; 852145247Sdamien} 853145247Sdamien 854145247Sdamienstatic int 855145247Sdamieniwi_suspend(device_t dev) 856145247Sdamien{ 857145247Sdamien struct iwi_softc *sc = device_get_softc(dev); 858234570Sbschmidt struct ieee80211com *ic = sc->sc_ifp->if_l2com; 859145247Sdamien 860234570Sbschmidt ieee80211_suspend_all(ic); 861145247Sdamien return 0; 862145247Sdamien} 863145247Sdamien 864145247Sdamienstatic int 865145247Sdamieniwi_resume(device_t dev) 866145247Sdamien{ 867145247Sdamien struct iwi_softc *sc = device_get_softc(dev); 868234570Sbschmidt struct ieee80211com *ic = sc->sc_ifp->if_l2com; 869145247Sdamien 870146500Sdamien pci_write_config(dev, 0x41, 0, 1); 871146500Sdamien 872234570Sbschmidt ieee80211_resume_all(ic); 873145247Sdamien return 0; 874145247Sdamien} 875145247Sdamien 876150341Sdamienstatic struct ieee80211_node * 877179643Ssamiwi_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) 878150341Sdamien{ 879150341Sdamien struct iwi_node *in; 880150341Sdamien 881150341Sdamien in = malloc(sizeof (struct iwi_node), M_80211_NODE, M_NOWAIT | M_ZERO); 882150341Sdamien if (in == NULL) 883150341Sdamien return NULL; 884179643Ssam /* XXX assign sta table entry for adhoc */ 885150341Sdamien in->in_station = -1; 886150341Sdamien 887150341Sdamien return &in->in_node; 888150341Sdamien} 889150341Sdamien 890150341Sdamienstatic void 891150341Sdamieniwi_node_free(struct ieee80211_node *ni) 892150341Sdamien{ 893150341Sdamien struct ieee80211com *ic = ni->ni_ic; 894150341Sdamien struct iwi_softc *sc = ic->ic_ifp->if_softc; 895150341Sdamien struct iwi_node *in = (struct iwi_node *)ni; 896150341Sdamien 897158089Smlaier if (in->in_station != -1) { 898158089Smlaier DPRINTF(("%s mac %6D station %u\n", __func__, 899158089Smlaier ni->ni_macaddr, ":", in->in_station)); 900150341Sdamien free_unr(sc->sc_unr, in->in_station); 901158089Smlaier } 902150341Sdamien 903150341Sdamien sc->sc_node_free(ni); 904150341Sdamien} 905150341Sdamien 906158089Smlaier/* 907158089Smlaier * Convert h/w rate code to IEEE rate code. 908158089Smlaier */ 909158089Smlaierstatic int 910158089Smlaieriwi_cvtrate(int iwirate) 911158089Smlaier{ 912158089Smlaier switch (iwirate) { 913158089Smlaier case IWI_RATE_DS1: return 2; 914158089Smlaier case IWI_RATE_DS2: return 4; 915158089Smlaier case IWI_RATE_DS5: return 11; 916158089Smlaier case IWI_RATE_DS11: return 22; 917158089Smlaier case IWI_RATE_OFDM6: return 12; 918158089Smlaier case IWI_RATE_OFDM9: return 18; 919158089Smlaier case IWI_RATE_OFDM12: return 24; 920158089Smlaier case IWI_RATE_OFDM18: return 36; 921158089Smlaier case IWI_RATE_OFDM24: return 48; 922158089Smlaier case IWI_RATE_OFDM36: return 72; 923158089Smlaier case IWI_RATE_OFDM48: return 96; 924158089Smlaier case IWI_RATE_OFDM54: return 108; 925158089Smlaier } 926145247Sdamien return 0; 927145247Sdamien} 928145247Sdamien 929145247Sdamien/* 930150341Sdamien * The firmware automatically adapts the transmit speed. We report its current 931150341Sdamien * value here. 932145247Sdamien */ 933145247Sdamienstatic void 934145247Sdamieniwi_media_status(struct ifnet *ifp, struct ifmediareq *imr) 935145247Sdamien{ 936178354Ssam struct ieee80211vap *vap = ifp->if_softc; 937178354Ssam struct ieee80211com *ic = vap->iv_ic; 938178354Ssam struct iwi_softc *sc = ic->ic_ifp->if_softc; 939145247Sdamien 940145247Sdamien /* read current transmission rate from adapter */ 941178354Ssam vap->iv_bss->ni_txrate = 942178354Ssam iwi_cvtrate(CSR_READ_4(sc, IWI_CSR_CURRENT_TX_RATE)); 943178354Ssam ieee80211_media_status(ifp, imr); 944145247Sdamien} 945145247Sdamien 946145247Sdamienstatic int 947178354Ssamiwi_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) 948145247Sdamien{ 949178354Ssam struct iwi_vap *ivp = IWI_VAP(vap); 950178354Ssam struct ieee80211com *ic = vap->iv_ic; 951145247Sdamien struct ifnet *ifp = ic->ic_ifp; 952145247Sdamien struct iwi_softc *sc = ifp->if_softc; 953178354Ssam IWI_LOCK_DECL; 954145247Sdamien 955158089Smlaier DPRINTF(("%s: %s -> %s flags 0x%x\n", __func__, 956178354Ssam ieee80211_state_name[vap->iv_state], 957158089Smlaier ieee80211_state_name[nstate], sc->flags)); 958154992Sdamien 959191746Sthompsa IEEE80211_UNLOCK(ic); 960191746Sthompsa IWI_LOCK(sc); 961145247Sdamien switch (nstate) { 962178354Ssam case IEEE80211_S_INIT: 963178354Ssam /* 964178354Ssam * NB: don't try to do this if iwi_stop_master has 965178354Ssam * shutdown the firmware and disabled interrupts. 966178354Ssam */ 967178354Ssam if (vap->iv_state == IEEE80211_S_RUN && 968178354Ssam (sc->flags & IWI_FLAG_FW_INITED)) 969191746Sthompsa iwi_disassociate(sc, 0); 970178354Ssam break; 971145247Sdamien case IEEE80211_S_AUTH: 972191746Sthompsa iwi_auth_and_assoc(sc, vap); 973191746Sthompsa break; 974145247Sdamien case IEEE80211_S_RUN: 975178354Ssam if (vap->iv_opmode == IEEE80211_M_IBSS && 976178354Ssam vap->iv_state == IEEE80211_S_SCAN) { 977158089Smlaier /* 978158089Smlaier * XXX when joining an ibss network we are called 979158089Smlaier * with a SCAN -> RUN transition on scan complete. 980158089Smlaier * Use that to call iwi_auth_and_assoc. On completing 981158089Smlaier * the join we are then called again with an 982158089Smlaier * AUTH -> RUN transition and we want to do nothing. 983158089Smlaier * This is all totally bogus and needs to be redone. 984158089Smlaier */ 985191746Sthompsa iwi_auth_and_assoc(sc, vap); 986213729Sbschmidt } else if (vap->iv_opmode == IEEE80211_M_MONITOR) 987213729Sbschmidt ieee80211_runtask(ic, &sc->sc_monitortask); 988145247Sdamien break; 989170530Ssam case IEEE80211_S_ASSOC: 990170530Ssam /* 991178354Ssam * If we are transitioning from AUTH then just wait 992178354Ssam * for the ASSOC status to come back from the firmware. 993178354Ssam * Otherwise we need to issue the association request. 994170530Ssam */ 995178354Ssam if (vap->iv_state == IEEE80211_S_AUTH) 996178354Ssam break; 997191746Sthompsa iwi_auth_and_assoc(sc, vap); 998191746Sthompsa break; 999170530Ssam default: 1000170530Ssam break; 1001145247Sdamien } 1002191746Sthompsa IWI_UNLOCK(sc); 1003191746Sthompsa IEEE80211_LOCK(ic); 1004178354Ssam return ivp->iwi_newstate(vap, nstate, arg); 1005145247Sdamien} 1006145247Sdamien 1007149346Sdamien/* 1008149346Sdamien * WME parameters coming from IEEE 802.11e specification. These values are 1009149346Sdamien * already declared in ieee80211_proto.c, but they are static so they can't 1010149346Sdamien * be reused here. 1011149346Sdamien */ 1012149346Sdamienstatic const struct wmeParams iwi_wme_cck_params[WME_NUM_AC] = { 1013149346Sdamien { 0, 3, 5, 7, 0 }, /* WME_AC_BE */ 1014149346Sdamien { 0, 3, 5, 10, 0 }, /* WME_AC_BK */ 1015149346Sdamien { 0, 2, 4, 5, 188 }, /* WME_AC_VI */ 1016149346Sdamien { 0, 2, 3, 4, 102 } /* WME_AC_VO */ 1017149346Sdamien}; 1018149346Sdamien 1019149346Sdamienstatic const struct wmeParams iwi_wme_ofdm_params[WME_NUM_AC] = { 1020149346Sdamien { 0, 3, 4, 6, 0 }, /* WME_AC_BE */ 1021149346Sdamien { 0, 3, 4, 10, 0 }, /* WME_AC_BK */ 1022149346Sdamien { 0, 2, 3, 4, 94 }, /* WME_AC_VI */ 1023149346Sdamien { 0, 2, 2, 3, 47 } /* WME_AC_VO */ 1024149346Sdamien}; 1025158089Smlaier#define IWI_EXP2(v) htole16((1 << (v)) - 1) 1026158089Smlaier#define IWI_USEC(v) htole16(IEEE80211_TXOP_TO_US(v)) 1027149346Sdamien 1028158089Smlaierstatic void 1029158089Smlaieriwi_wme_init(struct iwi_softc *sc) 1030149338Sdamien{ 1031149346Sdamien const struct wmeParams *wmep; 1032149346Sdamien int ac; 1033149338Sdamien 1034158089Smlaier memset(sc->wme, 0, sizeof sc->wme); 1035149346Sdamien for (ac = 0; ac < WME_NUM_AC; ac++) { 1036149346Sdamien /* set WME values for CCK modulation */ 1037149346Sdamien wmep = &iwi_wme_cck_params[ac]; 1038158089Smlaier sc->wme[1].aifsn[ac] = wmep->wmep_aifsn; 1039158089Smlaier sc->wme[1].cwmin[ac] = IWI_EXP2(wmep->wmep_logcwmin); 1040158089Smlaier sc->wme[1].cwmax[ac] = IWI_EXP2(wmep->wmep_logcwmax); 1041158089Smlaier sc->wme[1].burst[ac] = IWI_USEC(wmep->wmep_txopLimit); 1042158089Smlaier sc->wme[1].acm[ac] = wmep->wmep_acm; 1043149346Sdamien 1044149346Sdamien /* set WME values for OFDM modulation */ 1045149346Sdamien wmep = &iwi_wme_ofdm_params[ac]; 1046158089Smlaier sc->wme[2].aifsn[ac] = wmep->wmep_aifsn; 1047158089Smlaier sc->wme[2].cwmin[ac] = IWI_EXP2(wmep->wmep_logcwmin); 1048158089Smlaier sc->wme[2].cwmax[ac] = IWI_EXP2(wmep->wmep_logcwmax); 1049158089Smlaier sc->wme[2].burst[ac] = IWI_USEC(wmep->wmep_txopLimit); 1050158089Smlaier sc->wme[2].acm[ac] = wmep->wmep_acm; 1051149346Sdamien } 1052158089Smlaier} 1053149346Sdamien 1054158089Smlaierstatic int 1055178354Ssamiwi_wme_setparams(struct iwi_softc *sc, struct ieee80211com *ic) 1056158089Smlaier{ 1057158089Smlaier const struct wmeParams *wmep; 1058158089Smlaier int ac; 1059158089Smlaier 1060158089Smlaier for (ac = 0; ac < WME_NUM_AC; ac++) { 1061158089Smlaier /* set WME values for current operating mode */ 1062158089Smlaier wmep = &ic->ic_wme.wme_chanParams.cap_wmeParams[ac]; 1063158089Smlaier sc->wme[0].aifsn[ac] = wmep->wmep_aifsn; 1064158089Smlaier sc->wme[0].cwmin[ac] = IWI_EXP2(wmep->wmep_logcwmin); 1065158089Smlaier sc->wme[0].cwmax[ac] = IWI_EXP2(wmep->wmep_logcwmax); 1066158089Smlaier sc->wme[0].burst[ac] = IWI_USEC(wmep->wmep_txopLimit); 1067158089Smlaier sc->wme[0].acm[ac] = wmep->wmep_acm; 1068158089Smlaier } 1069158089Smlaier 1070149346Sdamien DPRINTF(("Setting WME parameters\n")); 1071158089Smlaier return iwi_cmd(sc, IWI_CMD_SET_WME_PARAMS, sc->wme, sizeof sc->wme); 1072158089Smlaier} 1073149346Sdamien#undef IWI_USEC 1074149346Sdamien#undef IWI_EXP2 1075158089Smlaier 1076191951Ssamstatic void 1077191951Ssamiwi_update_wme(void *arg, int npending) 1078191951Ssam{ 1079191951Ssam struct ieee80211com *ic = arg; 1080191951Ssam struct iwi_softc *sc = ic->ic_ifp->if_softc; 1081191951Ssam IWI_LOCK_DECL; 1082191951Ssam 1083191951Ssam IWI_LOCK(sc); 1084191951Ssam (void) iwi_wme_setparams(sc, ic); 1085191951Ssam IWI_UNLOCK(sc); 1086191951Ssam} 1087191951Ssam 1088158089Smlaierstatic int 1089158089Smlaieriwi_wme_update(struct ieee80211com *ic) 1090158089Smlaier{ 1091158089Smlaier struct iwi_softc *sc = ic->ic_ifp->if_softc; 1092191746Sthompsa struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 1093158089Smlaier 1094158089Smlaier /* 1095158089Smlaier * We may be called to update the WME parameters in 1096158089Smlaier * the adapter at various places. If we're already 1097191951Ssam * associated then initiate the request immediately; 1098191951Ssam * otherwise we assume the params will get sent down 1099191951Ssam * to the adapter as part of the work iwi_auth_and_assoc 1100191951Ssam * does. 1101158089Smlaier */ 1102191746Sthompsa if (vap->iv_state == IEEE80211_S_RUN) 1103191951Ssam ieee80211_runtask(ic, &sc->sc_wmetask); 1104191746Sthompsa return (0); 1105149338Sdamien} 1106149338Sdamien 1107158089Smlaierstatic int 1108158089Smlaieriwi_wme_setie(struct iwi_softc *sc) 1109158089Smlaier{ 1110158089Smlaier struct ieee80211_wme_info wme; 1111158089Smlaier 1112158089Smlaier memset(&wme, 0, sizeof wme); 1113158089Smlaier wme.wme_id = IEEE80211_ELEMID_VENDOR; 1114158089Smlaier wme.wme_len = sizeof (struct ieee80211_wme_info) - 2; 1115158089Smlaier wme.wme_oui[0] = 0x00; 1116158089Smlaier wme.wme_oui[1] = 0x50; 1117158089Smlaier wme.wme_oui[2] = 0xf2; 1118158089Smlaier wme.wme_type = WME_OUI_TYPE; 1119158089Smlaier wme.wme_subtype = WME_INFO_OUI_SUBTYPE; 1120158089Smlaier wme.wme_version = WME_VERSION; 1121158089Smlaier wme.wme_info = 0; 1122158089Smlaier 1123158089Smlaier DPRINTF(("Setting WME IE (len=%u)\n", wme.wme_len)); 1124158089Smlaier return iwi_cmd(sc, IWI_CMD_SET_WMEIE, &wme, sizeof wme); 1125158089Smlaier} 1126158089Smlaier 1127145247Sdamien/* 1128145247Sdamien * Read 16 bits at address 'addr' from the serial EEPROM. 1129145247Sdamien */ 1130145247Sdamienstatic uint16_t 1131145247Sdamieniwi_read_prom_word(struct iwi_softc *sc, uint8_t addr) 1132145247Sdamien{ 1133145247Sdamien uint32_t tmp; 1134145247Sdamien uint16_t val; 1135145247Sdamien int n; 1136145247Sdamien 1137145247Sdamien /* clock C once before the first command */ 1138145247Sdamien IWI_EEPROM_CTL(sc, 0); 1139145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_S); 1140145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_C); 1141145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_S); 1142145247Sdamien 1143145247Sdamien /* write start bit (1) */ 1144145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_D); 1145145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_D | IWI_EEPROM_C); 1146145247Sdamien 1147145247Sdamien /* write READ opcode (10) */ 1148145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_D); 1149145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_D | IWI_EEPROM_C); 1150145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_S); 1151145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_C); 1152145247Sdamien 1153145247Sdamien /* write address A7-A0 */ 1154145247Sdamien for (n = 7; n >= 0; n--) { 1155145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_S | 1156145247Sdamien (((addr >> n) & 1) << IWI_EEPROM_SHIFT_D)); 1157145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_S | 1158145247Sdamien (((addr >> n) & 1) << IWI_EEPROM_SHIFT_D) | IWI_EEPROM_C); 1159145247Sdamien } 1160145247Sdamien 1161145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_S); 1162145247Sdamien 1163145247Sdamien /* read data Q15-Q0 */ 1164145247Sdamien val = 0; 1165145247Sdamien for (n = 15; n >= 0; n--) { 1166145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_C); 1167145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_S); 1168145247Sdamien tmp = MEM_READ_4(sc, IWI_MEM_EEPROM_CTL); 1169145247Sdamien val |= ((tmp & IWI_EEPROM_Q) >> IWI_EEPROM_SHIFT_Q) << n; 1170145247Sdamien } 1171145247Sdamien 1172145247Sdamien IWI_EEPROM_CTL(sc, 0); 1173145247Sdamien 1174145247Sdamien /* clear Chip Select and clock C */ 1175145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_S); 1176145247Sdamien IWI_EEPROM_CTL(sc, 0); 1177145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_C); 1178145247Sdamien 1179152389Sdamien return val; 1180145247Sdamien} 1181145247Sdamien 1182145247Sdamienstatic void 1183158089Smlaieriwi_setcurchan(struct iwi_softc *sc, int chan) 1184145247Sdamien{ 1185178354Ssam struct ifnet *ifp = sc->sc_ifp; 1186178354Ssam struct ieee80211com *ic = ifp->if_l2com; 1187145247Sdamien 1188158089Smlaier sc->curchan = chan; 1189192468Ssam ieee80211_radiotap_chan_change(ic); 1190145247Sdamien} 1191145247Sdamien 1192145247Sdamienstatic void 1193145247Sdamieniwi_frame_intr(struct iwi_softc *sc, struct iwi_rx_data *data, int i, 1194145247Sdamien struct iwi_frame *frame) 1195145247Sdamien{ 1196178354Ssam struct ifnet *ifp = sc->sc_ifp; 1197178354Ssam struct ieee80211com *ic = ifp->if_l2com; 1198152384Sdamien struct mbuf *mnew, *m; 1199145247Sdamien struct ieee80211_node *ni; 1200158089Smlaier int type, error, framelen; 1201192468Ssam int8_t rssi, nf; 1202164964Smlaier IWI_LOCK_DECL; 1203145247Sdamien 1204158089Smlaier framelen = le16toh(frame->len); 1205158089Smlaier if (framelen < IEEE80211_MIN_LEN || framelen > MCLBYTES) { 1206158089Smlaier /* 1207158089Smlaier * XXX >MCLBYTES is bogus as it means the h/w dma'd 1208158089Smlaier * out of bounds; need to figure out how to limit 1209158089Smlaier * frame size in the firmware 1210158089Smlaier */ 1211158089Smlaier /* XXX stat */ 1212158089Smlaier DPRINTFN(1, 1213158089Smlaier ("drop rx frame len=%u chan=%u rssi=%u rssi_dbm=%u\n", 1214158089Smlaier le16toh(frame->len), frame->chan, frame->rssi, 1215158089Smlaier frame->rssi_dbm)); 1216146500Sdamien return; 1217158089Smlaier } 1218146500Sdamien 1219158089Smlaier DPRINTFN(5, ("received frame len=%u chan=%u rssi=%u rssi_dbm=%u\n", 1220158089Smlaier le16toh(frame->len), frame->chan, frame->rssi, frame->rssi_dbm)); 1221158089Smlaier 1222158089Smlaier if (frame->chan != sc->curchan) 1223158089Smlaier iwi_setcurchan(sc, frame->chan); 1224158089Smlaier 1225152384Sdamien /* 1226152384Sdamien * Try to allocate a new mbuf for this ring element and load it before 1227152384Sdamien * processing the current mbuf. If the ring element cannot be loaded, 1228152384Sdamien * drop the received packet and reuse the old mbuf. In the unlikely 1229152384Sdamien * case that the old mbuf can't be reloaded either, explicitly panic. 1230152384Sdamien */ 1231248078Smarius mnew = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); 1232152384Sdamien if (mnew == NULL) { 1233152384Sdamien ifp->if_ierrors++; 1234152384Sdamien return; 1235152384Sdamien } 1236152384Sdamien 1237145247Sdamien bus_dmamap_unload(sc->rxq.data_dmat, data->map); 1238145247Sdamien 1239152384Sdamien error = bus_dmamap_load(sc->rxq.data_dmat, data->map, 1240152384Sdamien mtod(mnew, void *), MCLBYTES, iwi_dma_map_addr, &data->physaddr, 1241152384Sdamien 0); 1242152384Sdamien if (error != 0) { 1243152384Sdamien m_freem(mnew); 1244152384Sdamien 1245152384Sdamien /* try to reload the old mbuf */ 1246152384Sdamien error = bus_dmamap_load(sc->rxq.data_dmat, data->map, 1247152384Sdamien mtod(data->m, void *), MCLBYTES, iwi_dma_map_addr, 1248152384Sdamien &data->physaddr, 0); 1249152384Sdamien if (error != 0) { 1250152384Sdamien /* very unlikely that it will fail... */ 1251152384Sdamien panic("%s: could not load old rx mbuf", 1252152384Sdamien device_get_name(sc->sc_dev)); 1253152384Sdamien } 1254152384Sdamien ifp->if_ierrors++; 1255152384Sdamien return; 1256152384Sdamien } 1257152384Sdamien 1258152384Sdamien /* 1259152384Sdamien * New mbuf successfully loaded, update Rx ring and continue 1260152384Sdamien * processing. 1261152384Sdamien */ 1262152384Sdamien m = data->m; 1263152384Sdamien data->m = mnew; 1264152384Sdamien CSR_WRITE_4(sc, data->reg, data->physaddr); 1265152384Sdamien 1266145247Sdamien /* finalize mbuf */ 1267145247Sdamien m->m_pkthdr.rcvif = ifp; 1268145247Sdamien m->m_pkthdr.len = m->m_len = sizeof (struct iwi_hdr) + 1269158089Smlaier sizeof (struct iwi_frame) + framelen; 1270145247Sdamien 1271145247Sdamien m_adj(m, sizeof (struct iwi_hdr) + sizeof (struct iwi_frame)); 1272145247Sdamien 1273192541Ssam rssi = frame->rssi_dbm; 1274192468Ssam nf = -95; 1275192468Ssam if (ieee80211_radiotap_active(ic)) { 1276145247Sdamien struct iwi_rx_radiotap_header *tap = &sc->sc_rxtap; 1277145247Sdamien 1278145247Sdamien tap->wr_flags = 0; 1279192468Ssam tap->wr_antsignal = rssi; 1280192468Ssam tap->wr_antnoise = nf; 1281158089Smlaier tap->wr_rate = iwi_cvtrate(frame->rate); 1282145247Sdamien tap->wr_antenna = frame->antenna; 1283145247Sdamien } 1284164964Smlaier IWI_UNLOCK(sc); 1285145247Sdamien 1286158089Smlaier ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *)); 1287178354Ssam if (ni != NULL) { 1288192468Ssam type = ieee80211_input(ni, m, rssi, nf); 1289178354Ssam ieee80211_free_node(ni); 1290178354Ssam } else 1291192468Ssam type = ieee80211_input_all(ic, m, rssi, nf); 1292145247Sdamien 1293164964Smlaier IWI_LOCK(sc); 1294158089Smlaier if (sc->sc_softled) { 1295158089Smlaier /* 1296158089Smlaier * Blink for any data frame. Otherwise do a 1297158089Smlaier * heartbeat-style blink when idle. The latter 1298158089Smlaier * is mainly for station mode where we depend on 1299158089Smlaier * periodic beacon frames to trigger the poll event. 1300158089Smlaier */ 1301158089Smlaier if (type == IEEE80211_FC0_TYPE_DATA) { 1302158089Smlaier sc->sc_rxrate = frame->rate; 1303158089Smlaier iwi_led_event(sc, IWI_LED_RX); 1304158089Smlaier } else if (ticks - sc->sc_ledevent >= sc->sc_ledidle) 1305158089Smlaier iwi_led_event(sc, IWI_LED_POLL); 1306158089Smlaier } 1307145247Sdamien} 1308145247Sdamien 1309158089Smlaier/* 1310158089Smlaier * Check for an association response frame to see if QoS 1311158089Smlaier * has been negotiated. We parse just enough to figure 1312158089Smlaier * out if we're supposed to use QoS. The proper solution 1313158089Smlaier * is to pass the frame up so ieee80211_input can do the 1314158089Smlaier * work but that's made hard by how things currently are 1315158089Smlaier * done in the driver. 1316158089Smlaier */ 1317145247Sdamienstatic void 1318178354Ssamiwi_checkforqos(struct ieee80211vap *vap, 1319178354Ssam const struct ieee80211_frame *wh, int len) 1320158089Smlaier{ 1321158089Smlaier#define SUBTYPE(wh) ((wh)->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) 1322158089Smlaier const uint8_t *frm, *efrm, *wme; 1323158089Smlaier struct ieee80211_node *ni; 1324170530Ssam uint16_t capinfo, status, associd; 1325158089Smlaier 1326158089Smlaier /* NB: +8 for capinfo, status, associd, and first ie */ 1327158089Smlaier if (!(sizeof(*wh)+8 < len && len < IEEE80211_MAX_LEN) || 1328158089Smlaier SUBTYPE(wh) != IEEE80211_FC0_SUBTYPE_ASSOC_RESP) 1329158089Smlaier return; 1330158089Smlaier /* 1331158089Smlaier * asresp frame format 1332158089Smlaier * [2] capability information 1333158089Smlaier * [2] status 1334158089Smlaier * [2] association ID 1335158089Smlaier * [tlv] supported rates 1336158089Smlaier * [tlv] extended supported rates 1337158089Smlaier * [tlv] WME 1338158089Smlaier */ 1339158089Smlaier frm = (const uint8_t *)&wh[1]; 1340158089Smlaier efrm = ((const uint8_t *) wh) + len; 1341158089Smlaier 1342170530Ssam capinfo = le16toh(*(const uint16_t *)frm); 1343170530Ssam frm += 2; 1344170530Ssam status = le16toh(*(const uint16_t *)frm); 1345170530Ssam frm += 2; 1346170530Ssam associd = le16toh(*(const uint16_t *)frm); 1347170530Ssam frm += 2; 1348170530Ssam 1349158089Smlaier wme = NULL; 1350233077Siwasaki while (efrm - frm > 1) { 1351233077Siwasaki IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return); 1352158089Smlaier switch (*frm) { 1353158089Smlaier case IEEE80211_ELEMID_VENDOR: 1354158089Smlaier if (iswmeoui(frm)) 1355158089Smlaier wme = frm; 1356158089Smlaier break; 1357158089Smlaier } 1358158089Smlaier frm += frm[1] + 2; 1359158089Smlaier } 1360158089Smlaier 1361178354Ssam ni = vap->iv_bss; 1362170530Ssam ni->ni_capinfo = capinfo; 1363214162Sbschmidt ni->ni_associd = associd & 0x3fff; 1364158089Smlaier if (wme != NULL) 1365158089Smlaier ni->ni_flags |= IEEE80211_NODE_QOS; 1366158089Smlaier else 1367158089Smlaier ni->ni_flags &= ~IEEE80211_NODE_QOS; 1368158089Smlaier#undef SUBTYPE 1369158089Smlaier} 1370158089Smlaier 1371178354Ssam/* 1372178354Ssam * Task queue callbacks for iwi_notification_intr used to avoid LOR's. 1373178354Ssam */ 1374178354Ssam 1375158089Smlaierstatic void 1376145247Sdamieniwi_notification_intr(struct iwi_softc *sc, struct iwi_notif *notif) 1377145247Sdamien{ 1378178354Ssam struct ifnet *ifp = sc->sc_ifp; 1379178354Ssam struct ieee80211com *ic = ifp->if_l2com; 1380178354Ssam struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 1381145247Sdamien struct iwi_notif_scan_channel *chan; 1382145247Sdamien struct iwi_notif_scan_complete *scan; 1383145247Sdamien struct iwi_notif_authentication *auth; 1384145247Sdamien struct iwi_notif_association *assoc; 1385158089Smlaier struct iwi_notif_beacon_state *beacon; 1386145247Sdamien 1387145247Sdamien switch (notif->type) { 1388145247Sdamien case IWI_NOTIF_TYPE_SCAN_CHANNEL: 1389145247Sdamien chan = (struct iwi_notif_scan_channel *)(notif + 1); 1390145247Sdamien 1391158089Smlaier DPRINTFN(3, ("Scan of channel %u complete (%u)\n", 1392170530Ssam ieee80211_ieee2mhz(chan->nchan, 0), chan->nchan)); 1393170530Ssam 1394170530Ssam /* Reset the timer, the scan is still going */ 1395170530Ssam sc->sc_state_timer = 3; 1396145247Sdamien break; 1397145247Sdamien 1398145247Sdamien case IWI_NOTIF_TYPE_SCAN_COMPLETE: 1399145247Sdamien scan = (struct iwi_notif_scan_complete *)(notif + 1); 1400145247Sdamien 1401145247Sdamien DPRINTFN(2, ("Scan completed (%u, %u)\n", scan->nchan, 1402145247Sdamien scan->status)); 1403145247Sdamien 1404170530Ssam IWI_STATE_END(sc, IWI_FW_SCANNING); 1405158089Smlaier 1406213729Sbschmidt /* 1407213729Sbschmidt * Monitor mode works by doing a passive scan to set 1408213729Sbschmidt * the channel and enable rx. Because we don't want 1409213729Sbschmidt * to abort a scan lest the firmware crash we scan 1410213729Sbschmidt * for a short period of time and automatically restart 1411213729Sbschmidt * the scan when notified the sweep has completed. 1412213729Sbschmidt */ 1413213729Sbschmidt if (vap->iv_opmode == IEEE80211_M_MONITOR) { 1414213729Sbschmidt ieee80211_runtask(ic, &sc->sc_monitortask); 1415213729Sbschmidt break; 1416213729Sbschmidt } 1417213729Sbschmidt 1418178354Ssam if (scan->status == IWI_SCAN_COMPLETED) { 1419178354Ssam /* NB: don't need to defer, net80211 does it for us */ 1420178354Ssam ieee80211_scan_next(vap); 1421178354Ssam } 1422145247Sdamien break; 1423145247Sdamien 1424145247Sdamien case IWI_NOTIF_TYPE_AUTHENTICATION: 1425145247Sdamien auth = (struct iwi_notif_authentication *)(notif + 1); 1426145247Sdamien switch (auth->state) { 1427158089Smlaier case IWI_AUTH_SUCCESS: 1428158089Smlaier DPRINTFN(2, ("Authentication succeeeded\n")); 1429191746Sthompsa ieee80211_new_state(vap, IEEE80211_S_ASSOC, -1); 1430145247Sdamien break; 1431158089Smlaier case IWI_AUTH_FAIL: 1432178354Ssam /* 1433178354Ssam * These are delivered as an unsolicited deauth 1434178354Ssam * (e.g. due to inactivity) or in response to an 1435178354Ssam * associate request. 1436178354Ssam */ 1437158089Smlaier sc->flags &= ~IWI_FLAG_ASSOCIATED; 1438178354Ssam if (vap->iv_state != IEEE80211_S_RUN) { 1439178354Ssam DPRINTFN(2, ("Authentication failed\n")); 1440178354Ssam vap->iv_stats.is_rx_auth_fail++; 1441178354Ssam IWI_STATE_END(sc, IWI_FW_ASSOCIATING); 1442178354Ssam } else { 1443178354Ssam DPRINTFN(2, ("Deauthenticated\n")); 1444178354Ssam vap->iv_stats.is_rx_deauth++; 1445178354Ssam } 1446191746Sthompsa ieee80211_new_state(vap, IEEE80211_S_SCAN, -1); 1447145247Sdamien break; 1448158089Smlaier case IWI_AUTH_SENT_1: 1449158089Smlaier case IWI_AUTH_RECV_2: 1450158089Smlaier case IWI_AUTH_SEQ1_PASS: 1451158089Smlaier break; 1452158089Smlaier case IWI_AUTH_SEQ1_FAIL: 1453158089Smlaier DPRINTFN(2, ("Initial authentication handshake failed; " 1454158089Smlaier "you probably need shared key\n")); 1455178354Ssam vap->iv_stats.is_rx_auth_fail++; 1456170530Ssam IWI_STATE_END(sc, IWI_FW_ASSOCIATING); 1457158089Smlaier /* XXX retry shared key when in auto */ 1458158089Smlaier break; 1459145247Sdamien default: 1460145247Sdamien device_printf(sc->sc_dev, 1461145247Sdamien "unknown authentication state %u\n", auth->state); 1462178354Ssam break; 1463145247Sdamien } 1464145247Sdamien break; 1465145247Sdamien 1466145247Sdamien case IWI_NOTIF_TYPE_ASSOCIATION: 1467145247Sdamien assoc = (struct iwi_notif_association *)(notif + 1); 1468145247Sdamien switch (assoc->state) { 1469158089Smlaier case IWI_AUTH_SUCCESS: 1470145247Sdamien /* re-association, do nothing */ 1471145247Sdamien break; 1472158089Smlaier case IWI_ASSOC_SUCCESS: 1473158089Smlaier DPRINTFN(2, ("Association succeeded\n")); 1474158089Smlaier sc->flags |= IWI_FLAG_ASSOCIATED; 1475170530Ssam IWI_STATE_END(sc, IWI_FW_ASSOCIATING); 1476178354Ssam iwi_checkforqos(vap, 1477158089Smlaier (const struct ieee80211_frame *)(assoc+1), 1478214236Sbschmidt le16toh(notif->len) - sizeof(*assoc) - 1); 1479191746Sthompsa ieee80211_new_state(vap, IEEE80211_S_RUN, -1); 1480145247Sdamien break; 1481170530Ssam case IWI_ASSOC_INIT: 1482178354Ssam sc->flags &= ~IWI_FLAG_ASSOCIATED; 1483170530Ssam switch (sc->fw_state) { 1484178354Ssam case IWI_FW_ASSOCIATING: 1485178354Ssam DPRINTFN(2, ("Association failed\n")); 1486178354Ssam IWI_STATE_END(sc, IWI_FW_ASSOCIATING); 1487191746Sthompsa ieee80211_new_state(vap, IEEE80211_S_SCAN, -1); 1488178354Ssam break; 1489170530Ssam 1490178354Ssam case IWI_FW_DISASSOCIATING: 1491178354Ssam DPRINTFN(2, ("Dissassociated\n")); 1492178354Ssam IWI_STATE_END(sc, IWI_FW_DISASSOCIATING); 1493178354Ssam vap->iv_stats.is_rx_disassoc++; 1494191746Sthompsa ieee80211_new_state(vap, IEEE80211_S_SCAN, -1); 1495178354Ssam break; 1496170530Ssam } 1497145247Sdamien break; 1498145247Sdamien default: 1499145247Sdamien device_printf(sc->sc_dev, 1500145247Sdamien "unknown association state %u\n", assoc->state); 1501178354Ssam break; 1502145247Sdamien } 1503145247Sdamien break; 1504145247Sdamien 1505158089Smlaier case IWI_NOTIF_TYPE_BEACON: 1506158089Smlaier /* XXX check struct length */ 1507158089Smlaier beacon = (struct iwi_notif_beacon_state *)(notif + 1); 1508158089Smlaier 1509158089Smlaier DPRINTFN(5, ("Beacon state (%u, %u)\n", 1510158089Smlaier beacon->state, le32toh(beacon->number))); 1511158089Smlaier 1512158089Smlaier if (beacon->state == IWI_BEACON_MISS) { 1513158089Smlaier /* 1514158089Smlaier * The firmware notifies us of every beacon miss 1515158089Smlaier * so we need to track the count against the 1516158089Smlaier * configured threshold before notifying the 1517158089Smlaier * 802.11 layer. 1518158089Smlaier * XXX try to roam, drop assoc only on much higher count 1519158089Smlaier */ 1520178354Ssam if (le32toh(beacon->number) >= vap->iv_bmissthreshold) { 1521158089Smlaier DPRINTF(("Beacon miss: %u >= %u\n", 1522158089Smlaier le32toh(beacon->number), 1523178354Ssam vap->iv_bmissthreshold)); 1524178354Ssam vap->iv_stats.is_beacon_miss++; 1525178354Ssam /* 1526178354Ssam * It's pointless to notify the 802.11 layer 1527178354Ssam * as it'll try to send a probe request (which 1528178354Ssam * we'll discard) and then timeout and drop us 1529178354Ssam * into scan state. Instead tell the firmware 1530178354Ssam * to disassociate and then on completion we'll 1531178354Ssam * kick the state machine to scan. 1532178354Ssam */ 1533191746Sthompsa ieee80211_runtask(ic, &sc->sc_disassoctask); 1534158089Smlaier } 1535158089Smlaier } 1536158089Smlaier break; 1537158089Smlaier 1538158089Smlaier case IWI_NOTIF_TYPE_CALIBRATION: 1539158089Smlaier case IWI_NOTIF_TYPE_NOISE: 1540158089Smlaier case IWI_NOTIF_TYPE_LINK_QUALITY: 1541158089Smlaier DPRINTFN(5, ("Notification (%u)\n", notif->type)); 1542158089Smlaier break; 1543158089Smlaier 1544154992Sdamien default: 1545158089Smlaier DPRINTF(("unknown notification type %u flags 0x%x len %u\n", 1546158089Smlaier notif->type, notif->flags, le16toh(notif->len))); 1547178354Ssam break; 1548145247Sdamien } 1549145247Sdamien} 1550145247Sdamien 1551145247Sdamienstatic void 1552145247Sdamieniwi_rx_intr(struct iwi_softc *sc) 1553145247Sdamien{ 1554145247Sdamien struct iwi_rx_data *data; 1555145247Sdamien struct iwi_hdr *hdr; 1556145247Sdamien uint32_t hw; 1557145247Sdamien 1558145247Sdamien hw = CSR_READ_4(sc, IWI_CSR_RX_RIDX); 1559145247Sdamien 1560145247Sdamien for (; sc->rxq.cur != hw;) { 1561145247Sdamien data = &sc->rxq.data[sc->rxq.cur]; 1562145247Sdamien 1563145247Sdamien bus_dmamap_sync(sc->rxq.data_dmat, data->map, 1564145247Sdamien BUS_DMASYNC_POSTREAD); 1565145247Sdamien 1566145247Sdamien hdr = mtod(data->m, struct iwi_hdr *); 1567145247Sdamien 1568145247Sdamien switch (hdr->type) { 1569145247Sdamien case IWI_HDR_TYPE_FRAME: 1570145247Sdamien iwi_frame_intr(sc, data, sc->rxq.cur, 1571145247Sdamien (struct iwi_frame *)(hdr + 1)); 1572145247Sdamien break; 1573145247Sdamien 1574145247Sdamien case IWI_HDR_TYPE_NOTIF: 1575145247Sdamien iwi_notification_intr(sc, 1576145247Sdamien (struct iwi_notif *)(hdr + 1)); 1577145247Sdamien break; 1578145247Sdamien 1579145247Sdamien default: 1580145247Sdamien device_printf(sc->sc_dev, "unknown hdr type %u\n", 1581145247Sdamien hdr->type); 1582145247Sdamien } 1583145247Sdamien 1584145247Sdamien DPRINTFN(15, ("rx done idx=%u\n", sc->rxq.cur)); 1585145247Sdamien 1586145247Sdamien sc->rxq.cur = (sc->rxq.cur + 1) % IWI_RX_RING_COUNT; 1587145247Sdamien } 1588145247Sdamien 1589145247Sdamien /* tell the firmware what we have processed */ 1590145247Sdamien hw = (hw == 0) ? IWI_RX_RING_COUNT - 1 : hw - 1; 1591145247Sdamien CSR_WRITE_4(sc, IWI_CSR_RX_WIDX, hw); 1592145247Sdamien} 1593145247Sdamien 1594145247Sdamienstatic void 1595149338Sdamieniwi_tx_intr(struct iwi_softc *sc, struct iwi_tx_ring *txq) 1596145247Sdamien{ 1597178354Ssam struct ifnet *ifp = sc->sc_ifp; 1598145247Sdamien struct iwi_tx_data *data; 1599145247Sdamien uint32_t hw; 1600145247Sdamien 1601149338Sdamien hw = CSR_READ_4(sc, txq->csr_ridx); 1602145247Sdamien 1603149338Sdamien for (; txq->next != hw;) { 1604149338Sdamien data = &txq->data[txq->next]; 1605145247Sdamien 1606149338Sdamien bus_dmamap_sync(txq->data_dmat, data->map, 1607145247Sdamien BUS_DMASYNC_POSTWRITE); 1608149338Sdamien bus_dmamap_unload(txq->data_dmat, data->map); 1609170530Ssam if (data->m->m_flags & M_TXCB) 1610170530Ssam ieee80211_process_callback(data->ni, data->m, 0/*XXX*/); 1611145247Sdamien m_freem(data->m); 1612145247Sdamien data->m = NULL; 1613145247Sdamien ieee80211_free_node(data->ni); 1614145247Sdamien data->ni = NULL; 1615145247Sdamien 1616149338Sdamien DPRINTFN(15, ("tx done idx=%u\n", txq->next)); 1617145247Sdamien 1618145247Sdamien ifp->if_opackets++; 1619145247Sdamien 1620149338Sdamien txq->queued--; 1621149338Sdamien txq->next = (txq->next + 1) % IWI_TX_RING_COUNT; 1622145247Sdamien } 1623145247Sdamien 1624145247Sdamien sc->sc_tx_timer = 0; 1625148887Srwatson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 1626158089Smlaier 1627158089Smlaier if (sc->sc_softled) 1628158089Smlaier iwi_led_event(sc, IWI_LED_TX); 1629158089Smlaier 1630178354Ssam iwi_start_locked(ifp); 1631145247Sdamien} 1632145247Sdamien 1633145247Sdamienstatic void 1634191746Sthompsaiwi_fatal_error_intr(struct iwi_softc *sc) 1635191746Sthompsa{ 1636191746Sthompsa struct ifnet *ifp = sc->sc_ifp; 1637191746Sthompsa struct ieee80211com *ic = ifp->if_l2com; 1638191956Sthompsa struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 1639191746Sthompsa 1640191746Sthompsa device_printf(sc->sc_dev, "firmware error\n"); 1641191956Sthompsa if (vap != NULL) 1642191956Sthompsa ieee80211_cancel_scan(vap); 1643191746Sthompsa ieee80211_runtask(ic, &sc->sc_restarttask); 1644191746Sthompsa 1645191746Sthompsa sc->flags &= ~IWI_FLAG_BUSY; 1646191746Sthompsa sc->sc_busy_timer = 0; 1647191746Sthompsa wakeup(sc); 1648191746Sthompsa} 1649191746Sthompsa 1650191746Sthompsastatic void 1651191746Sthompsaiwi_radio_off_intr(struct iwi_softc *sc) 1652191746Sthompsa{ 1653191746Sthompsa struct ifnet *ifp = sc->sc_ifp; 1654191746Sthompsa struct ieee80211com *ic = ifp->if_l2com; 1655191746Sthompsa 1656191746Sthompsa ieee80211_runtask(ic, &sc->sc_radiofftask); 1657191746Sthompsa} 1658191746Sthompsa 1659191746Sthompsastatic void 1660145247Sdamieniwi_intr(void *arg) 1661145247Sdamien{ 1662145247Sdamien struct iwi_softc *sc = arg; 1663145247Sdamien uint32_t r; 1664158089Smlaier IWI_LOCK_DECL; 1665145247Sdamien 1666158089Smlaier IWI_LOCK(sc); 1667145247Sdamien 1668145247Sdamien if ((r = CSR_READ_4(sc, IWI_CSR_INTR)) == 0 || r == 0xffffffff) { 1669158089Smlaier IWI_UNLOCK(sc); 1670145247Sdamien return; 1671145247Sdamien } 1672145247Sdamien 1673156547Sdamien /* acknowledge interrupts */ 1674156547Sdamien CSR_WRITE_4(sc, IWI_CSR_INTR, r); 1675145247Sdamien 1676158089Smlaier if (r & IWI_INTR_FATAL_ERROR) { 1677191746Sthompsa iwi_fatal_error_intr(sc); 1678191746Sthompsa goto done; 1679145247Sdamien } 1680145247Sdamien 1681158089Smlaier if (r & IWI_INTR_FW_INITED) { 1682158089Smlaier if (!(r & (IWI_INTR_FATAL_ERROR | IWI_INTR_PARITY_ERROR))) 1683158089Smlaier wakeup(sc); 1684145247Sdamien } 1685145247Sdamien 1686158089Smlaier if (r & IWI_INTR_RADIO_OFF) 1687191746Sthompsa iwi_radio_off_intr(sc); 1688158089Smlaier 1689158089Smlaier if (r & IWI_INTR_CMD_DONE) { 1690158089Smlaier sc->flags &= ~IWI_FLAG_BUSY; 1691170530Ssam sc->sc_busy_timer = 0; 1692145247Sdamien wakeup(sc); 1693158089Smlaier } 1694145247Sdamien 1695145247Sdamien if (r & IWI_INTR_TX1_DONE) 1696149338Sdamien iwi_tx_intr(sc, &sc->txq[0]); 1697145247Sdamien 1698149338Sdamien if (r & IWI_INTR_TX2_DONE) 1699149338Sdamien iwi_tx_intr(sc, &sc->txq[1]); 1700149338Sdamien 1701149338Sdamien if (r & IWI_INTR_TX3_DONE) 1702149338Sdamien iwi_tx_intr(sc, &sc->txq[2]); 1703149338Sdamien 1704149338Sdamien if (r & IWI_INTR_TX4_DONE) 1705149338Sdamien iwi_tx_intr(sc, &sc->txq[3]); 1706149338Sdamien 1707149338Sdamien if (r & IWI_INTR_RX_DONE) 1708149338Sdamien iwi_rx_intr(sc); 1709149338Sdamien 1710158089Smlaier if (r & IWI_INTR_PARITY_ERROR) { 1711158089Smlaier /* XXX rate-limit */ 1712158089Smlaier device_printf(sc->sc_dev, "parity error\n"); 1713158089Smlaier } 1714191746Sthompsadone: 1715158089Smlaier IWI_UNLOCK(sc); 1716145247Sdamien} 1717145247Sdamien 1718145247Sdamienstatic int 1719158089Smlaieriwi_cmd(struct iwi_softc *sc, uint8_t type, void *data, uint8_t len) 1720145247Sdamien{ 1721145247Sdamien struct iwi_cmd_desc *desc; 1722145247Sdamien 1723170530Ssam IWI_LOCK_ASSERT(sc); 1724166848Sluigi 1725158089Smlaier if (sc->flags & IWI_FLAG_BUSY) { 1726158089Smlaier device_printf(sc->sc_dev, "%s: cmd %d not sent, busy\n", 1727158089Smlaier __func__, type); 1728158089Smlaier return EAGAIN; 1729158089Smlaier } 1730158089Smlaier sc->flags |= IWI_FLAG_BUSY; 1731170530Ssam sc->sc_busy_timer = 2; 1732158089Smlaier 1733145247Sdamien desc = &sc->cmdq.desc[sc->cmdq.cur]; 1734145247Sdamien 1735145247Sdamien desc->hdr.type = IWI_HDR_TYPE_COMMAND; 1736145247Sdamien desc->hdr.flags = IWI_HDR_FLAG_IRQ; 1737145247Sdamien desc->type = type; 1738145247Sdamien desc->len = len; 1739145247Sdamien memcpy(desc->data, data, len); 1740145247Sdamien 1741145247Sdamien bus_dmamap_sync(sc->cmdq.desc_dmat, sc->cmdq.desc_map, 1742145247Sdamien BUS_DMASYNC_PREWRITE); 1743145247Sdamien 1744145247Sdamien DPRINTFN(2, ("sending command idx=%u type=%u len=%u\n", sc->cmdq.cur, 1745145247Sdamien type, len)); 1746145247Sdamien 1747145247Sdamien sc->cmdq.cur = (sc->cmdq.cur + 1) % IWI_CMD_RING_COUNT; 1748145247Sdamien CSR_WRITE_4(sc, IWI_CSR_CMD_WIDX, sc->cmdq.cur); 1749145247Sdamien 1750158089Smlaier return msleep(sc, &sc->sc_mtx, 0, "iwicmd", hz); 1751145247Sdamien} 1752145247Sdamien 1753150341Sdamienstatic void 1754158089Smlaieriwi_write_ibssnode(struct iwi_softc *sc, 1755158089Smlaier const u_int8_t addr[IEEE80211_ADDR_LEN], int entry) 1756150341Sdamien{ 1757150341Sdamien struct iwi_ibssnode node; 1758150341Sdamien 1759150341Sdamien /* write node information into NIC memory */ 1760150341Sdamien memset(&node, 0, sizeof node); 1761158089Smlaier IEEE80211_ADDR_COPY(node.bssid, addr); 1762150341Sdamien 1763158089Smlaier DPRINTF(("%s mac %6D station %u\n", __func__, node.bssid, ":", entry)); 1764158089Smlaier 1765150341Sdamien CSR_WRITE_REGION_1(sc, 1766158089Smlaier IWI_CSR_NODE_BASE + entry * sizeof node, 1767150341Sdamien (uint8_t *)&node, sizeof node); 1768150341Sdamien} 1769150341Sdamien 1770145247Sdamienstatic int 1771150245Sdamieniwi_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni, 1772150245Sdamien int ac) 1773145247Sdamien{ 1774145247Sdamien struct iwi_softc *sc = ifp->if_softc; 1775178354Ssam struct ieee80211vap *vap = ni->ni_vap; 1776178354Ssam struct ieee80211com *ic = ni->ni_ic; 1777150341Sdamien struct iwi_node *in = (struct iwi_node *)ni; 1778158089Smlaier const struct ieee80211_frame *wh; 1779146500Sdamien struct ieee80211_key *k; 1780149338Sdamien const struct chanAccParams *cap; 1781150245Sdamien struct iwi_tx_ring *txq = &sc->txq[ac]; 1782145247Sdamien struct iwi_tx_data *data; 1783145247Sdamien struct iwi_tx_desc *desc; 1784145247Sdamien struct mbuf *mnew; 1785145247Sdamien bus_dma_segment_t segs[IWI_MAX_NSEG]; 1786158089Smlaier int error, nsegs, hdrlen, i; 1787158089Smlaier int ismcast, flags, xflags, staid; 1788145247Sdamien 1789170530Ssam IWI_LOCK_ASSERT(sc); 1790158089Smlaier wh = mtod(m0, const struct ieee80211_frame *); 1791158089Smlaier /* NB: only data frames use this path */ 1792158089Smlaier hdrlen = ieee80211_hdrsize(wh); 1793158089Smlaier ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); 1794158089Smlaier flags = xflags = 0; 1795149338Sdamien 1796158089Smlaier if (!ismcast) 1797158089Smlaier flags |= IWI_DATA_FLAG_NEED_ACK; 1798178354Ssam if (vap->iv_flags & IEEE80211_F_SHPREAMBLE) 1799158089Smlaier flags |= IWI_DATA_FLAG_SHPREAMBLE; 1800158089Smlaier if (IEEE80211_QOS_HAS_SEQ(wh)) { 1801158089Smlaier xflags |= IWI_DATA_XFLAG_QOS; 1802149338Sdamien cap = &ic->ic_wme.wme_chanParams; 1803158089Smlaier if (!cap->cap_wmeParams[ac].wmep_noackPolicy) 1804158089Smlaier flags &= ~IWI_DATA_FLAG_NEED_ACK; 1805158089Smlaier } 1806149338Sdamien 1807150341Sdamien /* 1808150341Sdamien * This is only used in IBSS mode where the firmware expect an index 1809150341Sdamien * in a h/w table instead of a destination address. 1810150341Sdamien */ 1811178354Ssam if (vap->iv_opmode == IEEE80211_M_IBSS) { 1812158089Smlaier if (!ismcast) { 1813158089Smlaier if (in->in_station == -1) { 1814158089Smlaier in->in_station = alloc_unr(sc->sc_unr); 1815158089Smlaier if (in->in_station == -1) { 1816158089Smlaier /* h/w table is full */ 1817158089Smlaier m_freem(m0); 1818158089Smlaier ieee80211_free_node(ni); 1819158089Smlaier ifp->if_oerrors++; 1820158089Smlaier return 0; 1821158089Smlaier } 1822158089Smlaier iwi_write_ibssnode(sc, 1823158089Smlaier ni->ni_macaddr, in->in_station); 1824158089Smlaier } 1825158089Smlaier staid = in->in_station; 1826158089Smlaier } else { 1827158089Smlaier /* 1828158089Smlaier * Multicast addresses have no associated node 1829158089Smlaier * so there will be no station entry. We reserve 1830158089Smlaier * entry 0 for one mcast address and use that. 1831158089Smlaier * If there are many being used this will be 1832158089Smlaier * expensive and we'll need to do a better job 1833158089Smlaier * but for now this handles the broadcast case. 1834158089Smlaier */ 1835158089Smlaier if (!IEEE80211_ADDR_EQ(wh->i_addr1, sc->sc_mcast)) { 1836158089Smlaier IEEE80211_ADDR_COPY(sc->sc_mcast, wh->i_addr1); 1837158089Smlaier iwi_write_ibssnode(sc, sc->sc_mcast, 0); 1838158089Smlaier } 1839158089Smlaier staid = 0; 1840150245Sdamien } 1841158089Smlaier } else 1842158089Smlaier staid = 0; 1843149338Sdamien 1844149338Sdamien if (wh->i_fc[1] & IEEE80211_FC1_WEP) { 1845178354Ssam k = ieee80211_crypto_encap(ni, m0); 1846147806Ssam if (k == NULL) { 1847147806Ssam m_freem(m0); 1848146500Sdamien return ENOBUFS; 1849147806Ssam } 1850149338Sdamien 1851149338Sdamien /* packet header may have moved, reset our local pointer */ 1852149338Sdamien wh = mtod(m0, struct ieee80211_frame *); 1853146500Sdamien } 1854146500Sdamien 1855192468Ssam if (ieee80211_radiotap_active_vap(vap)) { 1856145247Sdamien struct iwi_tx_radiotap_header *tap = &sc->sc_txtap; 1857145247Sdamien 1858145247Sdamien tap->wt_flags = 0; 1859145247Sdamien 1860192468Ssam ieee80211_radiotap_tx(vap, m0); 1861145247Sdamien } 1862145247Sdamien 1863149338Sdamien data = &txq->data[txq->cur]; 1864149338Sdamien desc = &txq->desc[txq->cur]; 1865145247Sdamien 1866149338Sdamien /* save and trim IEEE802.11 header */ 1867149338Sdamien m_copydata(m0, 0, hdrlen, (caddr_t)&desc->wh); 1868149338Sdamien m_adj(m0, hdrlen); 1869145247Sdamien 1870149338Sdamien error = bus_dmamap_load_mbuf_sg(txq->data_dmat, data->map, m0, segs, 1871145247Sdamien &nsegs, 0); 1872145247Sdamien if (error != 0 && error != EFBIG) { 1873145247Sdamien device_printf(sc->sc_dev, "could not map mbuf (error %d)\n", 1874145247Sdamien error); 1875145247Sdamien m_freem(m0); 1876145247Sdamien return error; 1877145247Sdamien } 1878145247Sdamien if (error != 0) { 1879248078Smarius mnew = m_defrag(m0, M_NOWAIT); 1880145247Sdamien if (mnew == NULL) { 1881145247Sdamien device_printf(sc->sc_dev, 1882145247Sdamien "could not defragment mbuf\n"); 1883145247Sdamien m_freem(m0); 1884145247Sdamien return ENOBUFS; 1885145247Sdamien } 1886145247Sdamien m0 = mnew; 1887145247Sdamien 1888149338Sdamien error = bus_dmamap_load_mbuf_sg(txq->data_dmat, data->map, 1889145247Sdamien m0, segs, &nsegs, 0); 1890145247Sdamien if (error != 0) { 1891145247Sdamien device_printf(sc->sc_dev, 1892145247Sdamien "could not map mbuf (error %d)\n", error); 1893145247Sdamien m_freem(m0); 1894145247Sdamien return error; 1895145247Sdamien } 1896145247Sdamien } 1897145247Sdamien 1898145247Sdamien data->m = m0; 1899145247Sdamien data->ni = ni; 1900145247Sdamien 1901145247Sdamien desc->hdr.type = IWI_HDR_TYPE_DATA; 1902145247Sdamien desc->hdr.flags = IWI_HDR_FLAG_IRQ; 1903158089Smlaier desc->station = staid; 1904145247Sdamien desc->cmd = IWI_DATA_CMD_TX; 1905145247Sdamien desc->len = htole16(m0->m_pkthdr.len); 1906158089Smlaier desc->flags = flags; 1907158089Smlaier desc->xflags = xflags; 1908145247Sdamien 1909146500Sdamien#if 0 1910178354Ssam if (vap->iv_flags & IEEE80211_F_PRIVACY) 1911178354Ssam desc->wep_txkey = vap->iv_def_txkey; 1912158089Smlaier else 1913146500Sdamien#endif 1914145247Sdamien desc->flags |= IWI_DATA_FLAG_NO_WEP; 1915145247Sdamien 1916145247Sdamien desc->nseg = htole32(nsegs); 1917145247Sdamien for (i = 0; i < nsegs; i++) { 1918145247Sdamien desc->seg_addr[i] = htole32(segs[i].ds_addr); 1919152389Sdamien desc->seg_len[i] = htole16(segs[i].ds_len); 1920145247Sdamien } 1921145247Sdamien 1922149338Sdamien bus_dmamap_sync(txq->data_dmat, data->map, BUS_DMASYNC_PREWRITE); 1923149338Sdamien bus_dmamap_sync(txq->desc_dmat, txq->desc_map, BUS_DMASYNC_PREWRITE); 1924145247Sdamien 1925149338Sdamien DPRINTFN(5, ("sending data frame txq=%u idx=%u len=%u nseg=%u\n", 1926152389Sdamien ac, txq->cur, le16toh(desc->len), nsegs)); 1927145247Sdamien 1928149338Sdamien txq->queued++; 1929149338Sdamien txq->cur = (txq->cur + 1) % IWI_TX_RING_COUNT; 1930149338Sdamien CSR_WRITE_4(sc, txq->csr_widx, txq->cur); 1931145247Sdamien 1932145247Sdamien return 0; 1933145247Sdamien} 1934145247Sdamien 1935178354Ssamstatic int 1936178354Ssamiwi_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, 1937178354Ssam const struct ieee80211_bpf_params *params) 1938178354Ssam{ 1939178354Ssam /* no support; just discard */ 1940178354Ssam m_freem(m); 1941178354Ssam ieee80211_free_node(ni); 1942178354Ssam return 0; 1943178354Ssam} 1944178354Ssam 1945145247Sdamienstatic void 1946178354Ssamiwi_start_locked(struct ifnet *ifp) 1947145247Sdamien{ 1948145247Sdamien struct iwi_softc *sc = ifp->if_softc; 1949178354Ssam struct mbuf *m; 1950145247Sdamien struct ieee80211_node *ni; 1951150245Sdamien int ac; 1952145247Sdamien 1953178354Ssam IWI_LOCK_ASSERT(sc); 1954145247Sdamien 1955178354Ssam if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) 1956145247Sdamien return; 1957145247Sdamien 1958145247Sdamien for (;;) { 1959178354Ssam IFQ_DRV_DEQUEUE(&ifp->if_snd, m); 1960178354Ssam if (m == NULL) 1961178354Ssam break; 1962178354Ssam ac = M_WME_GETAC(m); 1963178354Ssam if (sc->txq[ac].queued > IWI_TX_RING_COUNT - 8) { 1964178354Ssam /* there is no place left in this ring; tail drop */ 1965178354Ssam /* XXX tail drop */ 1966178354Ssam IFQ_DRV_PREPEND(&ifp->if_snd, m); 1967178354Ssam ifp->if_drv_flags |= IFF_DRV_OACTIVE; 1968178354Ssam break; 1969178354Ssam } 1970145247Sdamien 1971178354Ssam ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; 1972178354Ssam if (iwi_tx_start(ifp, m, ni, ac) != 0) { 1973145247Sdamien ieee80211_free_node(ni); 1974145247Sdamien ifp->if_oerrors++; 1975145247Sdamien break; 1976145247Sdamien } 1977145247Sdamien 1978145247Sdamien sc->sc_tx_timer = 5; 1979145247Sdamien } 1980178354Ssam} 1981145247Sdamien 1982178354Ssamstatic void 1983178354Ssamiwi_start(struct ifnet *ifp) 1984178354Ssam{ 1985178354Ssam struct iwi_softc *sc = ifp->if_softc; 1986178354Ssam IWI_LOCK_DECL; 1987178354Ssam 1988178354Ssam IWI_LOCK(sc); 1989178354Ssam iwi_start_locked(ifp); 1990158089Smlaier IWI_UNLOCK(sc); 1991145247Sdamien} 1992145247Sdamien 1993145247Sdamienstatic void 1994170530Ssamiwi_watchdog(void *arg) 1995145247Sdamien{ 1996170530Ssam struct iwi_softc *sc = arg; 1997170530Ssam struct ifnet *ifp = sc->sc_ifp; 1998191746Sthompsa struct ieee80211com *ic = ifp->if_l2com; 1999145247Sdamien 2000170530Ssam IWI_LOCK_ASSERT(sc); 2001145247Sdamien 2002145247Sdamien if (sc->sc_tx_timer > 0) { 2003145247Sdamien if (--sc->sc_tx_timer == 0) { 2004145247Sdamien if_printf(ifp, "device timeout\n"); 2005145247Sdamien ifp->if_oerrors++; 2006191746Sthompsa ieee80211_runtask(ic, &sc->sc_restarttask); 2007145247Sdamien } 2008158089Smlaier } 2009170530Ssam if (sc->sc_state_timer > 0) { 2010170530Ssam if (--sc->sc_state_timer == 0) { 2011170530Ssam if_printf(ifp, "firmware stuck in state %d, resetting\n", 2012170530Ssam sc->fw_state); 2013178354Ssam if (sc->fw_state == IWI_FW_SCANNING) { 2014178354Ssam struct ieee80211com *ic = ifp->if_l2com; 2015178354Ssam ieee80211_cancel_scan(TAILQ_FIRST(&ic->ic_vaps)); 2016178354Ssam } 2017191746Sthompsa ieee80211_runtask(ic, &sc->sc_restarttask); 2018170530Ssam sc->sc_state_timer = 3; 2019158089Smlaier } 2020158089Smlaier } 2021170530Ssam if (sc->sc_busy_timer > 0) { 2022170530Ssam if (--sc->sc_busy_timer == 0) { 2023170530Ssam if_printf(ifp, "firmware command timeout, resetting\n"); 2024191746Sthompsa ieee80211_runtask(ic, &sc->sc_restarttask); 2025170530Ssam } 2026170530Ssam } 2027178354Ssam callout_reset(&sc->sc_wdtimer, hz, iwi_watchdog, sc); 2028145247Sdamien} 2029145247Sdamien 2030145247Sdamienstatic int 2031145247Sdamieniwi_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 2032145247Sdamien{ 2033145247Sdamien struct iwi_softc *sc = ifp->if_softc; 2034178354Ssam struct ieee80211com *ic = ifp->if_l2com; 2035178354Ssam struct ifreq *ifr = (struct ifreq *) data; 2036178354Ssam int error = 0, startall = 0; 2037158089Smlaier IWI_LOCK_DECL; 2038145247Sdamien 2039145247Sdamien switch (cmd) { 2040145247Sdamien case SIOCSIFFLAGS: 2041178704Sthompsa IWI_LOCK(sc); 2042145247Sdamien if (ifp->if_flags & IFF_UP) { 2043178354Ssam if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { 2044178354Ssam iwi_init_locked(sc); 2045178354Ssam startall = 1; 2046178354Ssam } 2047145247Sdamien } else { 2048148887Srwatson if (ifp->if_drv_flags & IFF_DRV_RUNNING) 2049178354Ssam iwi_stop_locked(sc); 2050145247Sdamien } 2051178704Sthompsa IWI_UNLOCK(sc); 2052178704Sthompsa if (startall) 2053178704Sthompsa ieee80211_start_all(ic); 2054145247Sdamien break; 2055178354Ssam case SIOCGIFMEDIA: 2056178354Ssam error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); 2057178354Ssam break; 2058178704Sthompsa case SIOCGIFADDR: 2059178354Ssam error = ether_ioctl(ifp, cmd, data); 2060178354Ssam break; 2061178704Sthompsa default: 2062178704Sthompsa error = EINVAL; 2063178704Sthompsa break; 2064145247Sdamien } 2065145247Sdamien return error; 2066145247Sdamien} 2067145247Sdamien 2068145247Sdamienstatic void 2069145247Sdamieniwi_stop_master(struct iwi_softc *sc) 2070145247Sdamien{ 2071145247Sdamien uint32_t tmp; 2072145247Sdamien int ntries; 2073145247Sdamien 2074145247Sdamien /* disable interrupts */ 2075145247Sdamien CSR_WRITE_4(sc, IWI_CSR_INTR_MASK, 0); 2076145247Sdamien 2077145247Sdamien CSR_WRITE_4(sc, IWI_CSR_RST, IWI_RST_STOP_MASTER); 2078145247Sdamien for (ntries = 0; ntries < 5; ntries++) { 2079145247Sdamien if (CSR_READ_4(sc, IWI_CSR_RST) & IWI_RST_MASTER_DISABLED) 2080145247Sdamien break; 2081145247Sdamien DELAY(10); 2082145247Sdamien } 2083145247Sdamien if (ntries == 5) 2084145247Sdamien device_printf(sc->sc_dev, "timeout waiting for master\n"); 2085145247Sdamien 2086145247Sdamien tmp = CSR_READ_4(sc, IWI_CSR_RST); 2087145247Sdamien CSR_WRITE_4(sc, IWI_CSR_RST, tmp | IWI_RST_PRINCETON_RESET); 2088145247Sdamien 2089145247Sdamien sc->flags &= ~IWI_FLAG_FW_INITED; 2090145247Sdamien} 2091145247Sdamien 2092145247Sdamienstatic int 2093145247Sdamieniwi_reset(struct iwi_softc *sc) 2094145247Sdamien{ 2095145247Sdamien uint32_t tmp; 2096145247Sdamien int i, ntries; 2097145247Sdamien 2098145247Sdamien iwi_stop_master(sc); 2099145247Sdamien 2100145247Sdamien tmp = CSR_READ_4(sc, IWI_CSR_CTL); 2101145247Sdamien CSR_WRITE_4(sc, IWI_CSR_CTL, tmp | IWI_CTL_INIT); 2102145247Sdamien 2103145247Sdamien CSR_WRITE_4(sc, IWI_CSR_READ_INT, IWI_READ_INT_INIT_HOST); 2104145247Sdamien 2105145247Sdamien /* wait for clock stabilization */ 2106145247Sdamien for (ntries = 0; ntries < 1000; ntries++) { 2107145247Sdamien if (CSR_READ_4(sc, IWI_CSR_CTL) & IWI_CTL_CLOCK_READY) 2108145247Sdamien break; 2109145247Sdamien DELAY(200); 2110145247Sdamien } 2111145247Sdamien if (ntries == 1000) { 2112145247Sdamien device_printf(sc->sc_dev, 2113145247Sdamien "timeout waiting for clock stabilization\n"); 2114145247Sdamien return EIO; 2115145247Sdamien } 2116145247Sdamien 2117145247Sdamien tmp = CSR_READ_4(sc, IWI_CSR_RST); 2118145247Sdamien CSR_WRITE_4(sc, IWI_CSR_RST, tmp | IWI_RST_SOFT_RESET); 2119145247Sdamien 2120145247Sdamien DELAY(10); 2121145247Sdamien 2122145247Sdamien tmp = CSR_READ_4(sc, IWI_CSR_CTL); 2123145247Sdamien CSR_WRITE_4(sc, IWI_CSR_CTL, tmp | IWI_CTL_INIT); 2124145247Sdamien 2125145247Sdamien /* clear NIC memory */ 2126145247Sdamien CSR_WRITE_4(sc, IWI_CSR_AUTOINC_ADDR, 0); 2127145247Sdamien for (i = 0; i < 0xc000; i++) 2128145247Sdamien CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, 0); 2129145247Sdamien 2130145247Sdamien return 0; 2131145247Sdamien} 2132145247Sdamien 2133158089Smlaierstatic const struct iwi_firmware_ohdr * 2134158089Smlaieriwi_setup_ofw(struct iwi_softc *sc, struct iwi_fw *fw) 2135158089Smlaier{ 2136166756Sluigi const struct firmware *fp = fw->fp; 2137158089Smlaier const struct iwi_firmware_ohdr *hdr; 2138158089Smlaier 2139158089Smlaier if (fp->datasize < sizeof (struct iwi_firmware_ohdr)) { 2140158089Smlaier device_printf(sc->sc_dev, "image '%s' too small\n", fp->name); 2141158089Smlaier return NULL; 2142158089Smlaier } 2143158089Smlaier hdr = (const struct iwi_firmware_ohdr *)fp->data; 2144158089Smlaier if ((IWI_FW_GET_MAJOR(le32toh(hdr->version)) != IWI_FW_REQ_MAJOR) || 2145158089Smlaier (IWI_FW_GET_MINOR(le32toh(hdr->version)) != IWI_FW_REQ_MINOR)) { 2146158089Smlaier device_printf(sc->sc_dev, "version for '%s' %d.%d != %d.%d\n", 2147158089Smlaier fp->name, IWI_FW_GET_MAJOR(le32toh(hdr->version)), 2148158089Smlaier IWI_FW_GET_MINOR(le32toh(hdr->version)), IWI_FW_REQ_MAJOR, 2149158089Smlaier IWI_FW_REQ_MINOR); 2150158089Smlaier return NULL; 2151158089Smlaier } 2152158089Smlaier fw->data = ((const char *) fp->data) + sizeof(struct iwi_firmware_ohdr); 2153158089Smlaier fw->size = fp->datasize - sizeof(struct iwi_firmware_ohdr); 2154158089Smlaier fw->name = fp->name; 2155158089Smlaier return hdr; 2156158089Smlaier} 2157158089Smlaier 2158158089Smlaierstatic const struct iwi_firmware_ohdr * 2159158089Smlaieriwi_setup_oucode(struct iwi_softc *sc, struct iwi_fw *fw) 2160158089Smlaier{ 2161158089Smlaier const struct iwi_firmware_ohdr *hdr; 2162158089Smlaier 2163158089Smlaier hdr = iwi_setup_ofw(sc, fw); 2164158089Smlaier if (hdr != NULL && le32toh(hdr->mode) != IWI_FW_MODE_UCODE) { 2165158089Smlaier device_printf(sc->sc_dev, "%s is not a ucode image\n", 2166158089Smlaier fw->name); 2167158089Smlaier hdr = NULL; 2168158089Smlaier } 2169158089Smlaier return hdr; 2170158089Smlaier} 2171158089Smlaier 2172158089Smlaierstatic void 2173158089Smlaieriwi_getfw(struct iwi_fw *fw, const char *fwname, 2174158089Smlaier struct iwi_fw *uc, const char *ucname) 2175158089Smlaier{ 2176158089Smlaier if (fw->fp == NULL) 2177158089Smlaier fw->fp = firmware_get(fwname); 2178158089Smlaier /* NB: pre-3.0 ucode is packaged separately */ 2179158089Smlaier if (uc->fp == NULL && fw->fp != NULL && fw->fp->version < 300) 2180158089Smlaier uc->fp = firmware_get(ucname); 2181158089Smlaier} 2182158089Smlaier 2183158089Smlaier/* 2184158089Smlaier * Get the required firmware images if not already loaded. 2185158089Smlaier * Note that we hold firmware images so long as the device 2186158089Smlaier * is marked up in case we need to reload them on device init. 2187158089Smlaier * This is necessary because we re-init the device sometimes 2188158089Smlaier * from a context where we cannot read from the filesystem 2189158089Smlaier * (e.g. from the taskqueue thread when rfkill is re-enabled). 2190166848Sluigi * XXX return 0 on success, 1 on error. 2191158089Smlaier * 2192158089Smlaier * NB: the order of get'ing and put'ing images here is 2193158089Smlaier * intentional to support handling firmware images bundled 2194158089Smlaier * by operating mode and/or all together in one file with 2195158089Smlaier * the boot firmware as "master". 2196158089Smlaier */ 2197145247Sdamienstatic int 2198178354Ssamiwi_get_firmware(struct iwi_softc *sc, enum ieee80211_opmode opmode) 2199145247Sdamien{ 2200158089Smlaier const struct iwi_firmware_hdr *hdr; 2201166756Sluigi const struct firmware *fp; 2202158089Smlaier 2203158089Smlaier /* invalidate cached firmware on mode change */ 2204178354Ssam if (sc->fw_mode != opmode) 2205158089Smlaier iwi_put_firmware(sc); 2206158089Smlaier 2207178354Ssam switch (opmode) { 2208158089Smlaier case IEEE80211_M_STA: 2209158089Smlaier iwi_getfw(&sc->fw_fw, "iwi_bss", &sc->fw_uc, "iwi_ucode_bss"); 2210158089Smlaier break; 2211158089Smlaier case IEEE80211_M_IBSS: 2212158089Smlaier iwi_getfw(&sc->fw_fw, "iwi_ibss", &sc->fw_uc, "iwi_ucode_ibss"); 2213158089Smlaier break; 2214158089Smlaier case IEEE80211_M_MONITOR: 2215158089Smlaier iwi_getfw(&sc->fw_fw, "iwi_monitor", 2216158089Smlaier &sc->fw_uc, "iwi_ucode_monitor"); 2217158089Smlaier break; 2218158089Smlaier default: 2219195562Srpaulo device_printf(sc->sc_dev, "unknown opmode %d\n", opmode); 2220195562Srpaulo return EINVAL; 2221158089Smlaier } 2222158089Smlaier fp = sc->fw_fw.fp; 2223158089Smlaier if (fp == NULL) { 2224158089Smlaier device_printf(sc->sc_dev, "could not load firmware\n"); 2225158089Smlaier goto bad; 2226158089Smlaier } 2227158089Smlaier if (fp->version < 300) { 2228158089Smlaier /* 2229158089Smlaier * Firmware prior to 3.0 was packaged as separate 2230158089Smlaier * boot, firmware, and ucode images. Verify the 2231158089Smlaier * ucode image was read in, retrieve the boot image 2232158089Smlaier * if needed, and check version stamps for consistency. 2233158089Smlaier * The version stamps in the data are also checked 2234158089Smlaier * above; this is a bit paranoid but is a cheap 2235158089Smlaier * safeguard against mis-packaging. 2236158089Smlaier */ 2237158089Smlaier if (sc->fw_uc.fp == NULL) { 2238158089Smlaier device_printf(sc->sc_dev, "could not load ucode\n"); 2239158089Smlaier goto bad; 2240158089Smlaier } 2241158089Smlaier if (sc->fw_boot.fp == NULL) { 2242158089Smlaier sc->fw_boot.fp = firmware_get("iwi_boot"); 2243158089Smlaier if (sc->fw_boot.fp == NULL) { 2244158089Smlaier device_printf(sc->sc_dev, 2245158089Smlaier "could not load boot firmware\n"); 2246158089Smlaier goto bad; 2247158089Smlaier } 2248158089Smlaier } 2249158089Smlaier if (sc->fw_boot.fp->version != sc->fw_fw.fp->version || 2250158089Smlaier sc->fw_boot.fp->version != sc->fw_uc.fp->version) { 2251158089Smlaier device_printf(sc->sc_dev, 2252158089Smlaier "firmware version mismatch: " 2253158089Smlaier "'%s' is %d, '%s' is %d, '%s' is %d\n", 2254158089Smlaier sc->fw_boot.fp->name, sc->fw_boot.fp->version, 2255158089Smlaier sc->fw_uc.fp->name, sc->fw_uc.fp->version, 2256158089Smlaier sc->fw_fw.fp->name, sc->fw_fw.fp->version 2257158089Smlaier ); 2258158089Smlaier goto bad; 2259158089Smlaier } 2260158089Smlaier /* 2261158089Smlaier * Check and setup each image. 2262158089Smlaier */ 2263158089Smlaier if (iwi_setup_oucode(sc, &sc->fw_uc) == NULL || 2264158089Smlaier iwi_setup_ofw(sc, &sc->fw_boot) == NULL || 2265158089Smlaier iwi_setup_ofw(sc, &sc->fw_fw) == NULL) 2266158089Smlaier goto bad; 2267158089Smlaier } else { 2268158089Smlaier /* 2269158089Smlaier * Check and setup combined image. 2270158089Smlaier */ 2271166848Sluigi if (fp->datasize < sizeof(struct iwi_firmware_hdr)) { 2272158089Smlaier device_printf(sc->sc_dev, "image '%s' too small\n", 2273158089Smlaier fp->name); 2274158089Smlaier goto bad; 2275158089Smlaier } 2276158089Smlaier hdr = (const struct iwi_firmware_hdr *)fp->data; 2277166848Sluigi if (fp->datasize < sizeof(*hdr) + le32toh(hdr->bsize) + le32toh(hdr->usize) 2278166848Sluigi + le32toh(hdr->fsize)) { 2279158089Smlaier device_printf(sc->sc_dev, "image '%s' too small (2)\n", 2280158089Smlaier fp->name); 2281158089Smlaier goto bad; 2282158089Smlaier } 2283158089Smlaier sc->fw_boot.data = ((const char *) fp->data) + sizeof(*hdr); 2284166848Sluigi sc->fw_boot.size = le32toh(hdr->bsize); 2285158089Smlaier sc->fw_boot.name = fp->name; 2286158089Smlaier sc->fw_uc.data = sc->fw_boot.data + sc->fw_boot.size; 2287166848Sluigi sc->fw_uc.size = le32toh(hdr->usize); 2288158089Smlaier sc->fw_uc.name = fp->name; 2289158089Smlaier sc->fw_fw.data = sc->fw_uc.data + sc->fw_uc.size; 2290166848Sluigi sc->fw_fw.size = le32toh(hdr->fsize); 2291158089Smlaier sc->fw_fw.name = fp->name; 2292158089Smlaier } 2293166848Sluigi#if 0 2294166848Sluigi device_printf(sc->sc_dev, "boot %d ucode %d fw %d bytes\n", 2295166848Sluigi sc->fw_boot.size, sc->fw_uc.size, sc->fw_fw.size); 2296166848Sluigi#endif 2297158089Smlaier 2298178354Ssam sc->fw_mode = opmode; 2299166848Sluigi return 0; 2300158089Smlaierbad: 2301158089Smlaier iwi_put_firmware(sc); 2302166848Sluigi return 1; 2303158089Smlaier} 2304158089Smlaier 2305158089Smlaierstatic void 2306158089Smlaieriwi_put_fw(struct iwi_fw *fw) 2307158089Smlaier{ 2308158089Smlaier if (fw->fp != NULL) { 2309158089Smlaier firmware_put(fw->fp, FIRMWARE_UNLOAD); 2310158089Smlaier fw->fp = NULL; 2311158089Smlaier } 2312158089Smlaier fw->data = NULL; 2313158089Smlaier fw->size = 0; 2314158089Smlaier fw->name = NULL; 2315158089Smlaier} 2316158089Smlaier 2317158089Smlaier/* 2318158089Smlaier * Release any cached firmware images. 2319158089Smlaier */ 2320158089Smlaierstatic void 2321158089Smlaieriwi_put_firmware(struct iwi_softc *sc) 2322158089Smlaier{ 2323158089Smlaier iwi_put_fw(&sc->fw_uc); 2324158089Smlaier iwi_put_fw(&sc->fw_fw); 2325158089Smlaier iwi_put_fw(&sc->fw_boot); 2326158089Smlaier} 2327158089Smlaier 2328158089Smlaierstatic int 2329158089Smlaieriwi_load_ucode(struct iwi_softc *sc, const struct iwi_fw *fw) 2330158089Smlaier{ 2331158089Smlaier uint32_t tmp; 2332156546Sdamien const uint16_t *w; 2333158089Smlaier const char *uc = fw->data; 2334158089Smlaier size_t size = fw->size; 2335158089Smlaier int i, ntries, error; 2336145247Sdamien 2337170530Ssam IWI_LOCK_ASSERT(sc); 2338158089Smlaier error = 0; 2339145247Sdamien CSR_WRITE_4(sc, IWI_CSR_RST, CSR_READ_4(sc, IWI_CSR_RST) | 2340145247Sdamien IWI_RST_STOP_MASTER); 2341145247Sdamien for (ntries = 0; ntries < 5; ntries++) { 2342145247Sdamien if (CSR_READ_4(sc, IWI_CSR_RST) & IWI_RST_MASTER_DISABLED) 2343145247Sdamien break; 2344145247Sdamien DELAY(10); 2345145247Sdamien } 2346145247Sdamien if (ntries == 5) { 2347145247Sdamien device_printf(sc->sc_dev, "timeout waiting for master\n"); 2348158089Smlaier error = EIO; 2349158089Smlaier goto fail; 2350145247Sdamien } 2351145247Sdamien 2352145247Sdamien MEM_WRITE_4(sc, 0x3000e0, 0x80000000); 2353145247Sdamien DELAY(5000); 2354145247Sdamien 2355145247Sdamien tmp = CSR_READ_4(sc, IWI_CSR_RST); 2356145247Sdamien tmp &= ~IWI_RST_PRINCETON_RESET; 2357145247Sdamien CSR_WRITE_4(sc, IWI_CSR_RST, tmp); 2358145247Sdamien 2359145247Sdamien DELAY(5000); 2360145247Sdamien MEM_WRITE_4(sc, 0x3000e0, 0); 2361145247Sdamien DELAY(1000); 2362158089Smlaier MEM_WRITE_4(sc, IWI_MEM_EEPROM_EVENT, 1); 2363145247Sdamien DELAY(1000); 2364158089Smlaier MEM_WRITE_4(sc, IWI_MEM_EEPROM_EVENT, 0); 2365145247Sdamien DELAY(1000); 2366145247Sdamien MEM_WRITE_1(sc, 0x200000, 0x00); 2367145247Sdamien MEM_WRITE_1(sc, 0x200000, 0x40); 2368145247Sdamien DELAY(1000); 2369145247Sdamien 2370145247Sdamien /* write microcode into adapter memory */ 2371158089Smlaier for (w = (const uint16_t *)uc; size > 0; w++, size -= 2) 2372152389Sdamien MEM_WRITE_2(sc, 0x200010, htole16(*w)); 2373145247Sdamien 2374145247Sdamien MEM_WRITE_1(sc, 0x200000, 0x00); 2375145247Sdamien MEM_WRITE_1(sc, 0x200000, 0x80); 2376145247Sdamien 2377145247Sdamien /* wait until we get an answer */ 2378145247Sdamien for (ntries = 0; ntries < 100; ntries++) { 2379145247Sdamien if (MEM_READ_1(sc, 0x200000) & 1) 2380145247Sdamien break; 2381145247Sdamien DELAY(100); 2382145247Sdamien } 2383145247Sdamien if (ntries == 100) { 2384145247Sdamien device_printf(sc->sc_dev, 2385145247Sdamien "timeout waiting for ucode to initialize\n"); 2386158089Smlaier error = EIO; 2387158089Smlaier goto fail; 2388145247Sdamien } 2389145247Sdamien 2390145247Sdamien /* read the answer or the firmware will not initialize properly */ 2391145247Sdamien for (i = 0; i < 7; i++) 2392145247Sdamien MEM_READ_4(sc, 0x200004); 2393145247Sdamien 2394145247Sdamien MEM_WRITE_1(sc, 0x200000, 0x00); 2395145247Sdamien 2396158089Smlaierfail: 2397158089Smlaier return error; 2398145247Sdamien} 2399145247Sdamien 2400145247Sdamien/* macro to handle unaligned little endian data in firmware image */ 2401145247Sdamien#define GETLE32(p) ((p)[0] | (p)[1] << 8 | (p)[2] << 16 | (p)[3] << 24) 2402145247Sdamien 2403145247Sdamienstatic int 2404158089Smlaieriwi_load_firmware(struct iwi_softc *sc, const struct iwi_fw *fw) 2405145247Sdamien{ 2406145247Sdamien u_char *p, *end; 2407145247Sdamien uint32_t sentinel, ctl, src, dst, sum, len, mlen, tmp; 2408152629Sscottl int ntries, error; 2409145247Sdamien 2410170530Ssam IWI_LOCK_ASSERT(sc); 2411178354Ssam 2412145247Sdamien /* copy firmware image to DMA memory */ 2413158089Smlaier memcpy(sc->fw_virtaddr, fw->data, fw->size); 2414145247Sdamien 2415145247Sdamien /* make sure the adapter will get up-to-date values */ 2416158089Smlaier bus_dmamap_sync(sc->fw_dmat, sc->fw_map, BUS_DMASYNC_PREWRITE); 2417145247Sdamien 2418145247Sdamien /* tell the adapter where the command blocks are stored */ 2419145247Sdamien MEM_WRITE_4(sc, 0x3000a0, 0x27000); 2420145247Sdamien 2421145247Sdamien /* 2422145247Sdamien * Store command blocks into adapter's internal memory using register 2423145247Sdamien * indirections. The adapter will read the firmware image through DMA 2424145247Sdamien * using information stored in command blocks. 2425145247Sdamien */ 2426158089Smlaier src = sc->fw_physaddr; 2427158089Smlaier p = sc->fw_virtaddr; 2428158089Smlaier end = p + fw->size; 2429145247Sdamien CSR_WRITE_4(sc, IWI_CSR_AUTOINC_ADDR, 0x27000); 2430145247Sdamien 2431145247Sdamien while (p < end) { 2432145247Sdamien dst = GETLE32(p); p += 4; src += 4; 2433145247Sdamien len = GETLE32(p); p += 4; src += 4; 2434145247Sdamien p += len; 2435145247Sdamien 2436145247Sdamien while (len > 0) { 2437145247Sdamien mlen = min(len, IWI_CB_MAXDATALEN); 2438145247Sdamien 2439145247Sdamien ctl = IWI_CB_DEFAULT_CTL | mlen; 2440145247Sdamien sum = ctl ^ src ^ dst; 2441145247Sdamien 2442145247Sdamien /* write a command block */ 2443145247Sdamien CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, ctl); 2444145247Sdamien CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, src); 2445145247Sdamien CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, dst); 2446145247Sdamien CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, sum); 2447145247Sdamien 2448145247Sdamien src += mlen; 2449145247Sdamien dst += mlen; 2450145247Sdamien len -= mlen; 2451145247Sdamien } 2452145247Sdamien } 2453145247Sdamien 2454145247Sdamien /* write a fictive final command block (sentinel) */ 2455145247Sdamien sentinel = CSR_READ_4(sc, IWI_CSR_AUTOINC_ADDR); 2456145247Sdamien CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, 0); 2457145247Sdamien 2458145247Sdamien tmp = CSR_READ_4(sc, IWI_CSR_RST); 2459145247Sdamien tmp &= ~(IWI_RST_MASTER_DISABLED | IWI_RST_STOP_MASTER); 2460145247Sdamien CSR_WRITE_4(sc, IWI_CSR_RST, tmp); 2461145247Sdamien 2462145247Sdamien /* tell the adapter to start processing command blocks */ 2463145247Sdamien MEM_WRITE_4(sc, 0x3000a4, 0x540100); 2464145247Sdamien 2465152611Sdamien /* wait until the adapter reaches the sentinel */ 2466145247Sdamien for (ntries = 0; ntries < 400; ntries++) { 2467145247Sdamien if (MEM_READ_4(sc, 0x3000d0) >= sentinel) 2468145247Sdamien break; 2469145247Sdamien DELAY(100); 2470145247Sdamien } 2471166848Sluigi /* sync dma, just in case */ 2472166848Sluigi bus_dmamap_sync(sc->fw_dmat, sc->fw_map, BUS_DMASYNC_POSTWRITE); 2473145247Sdamien if (ntries == 400) { 2474145247Sdamien device_printf(sc->sc_dev, 2475158089Smlaier "timeout processing command blocks for %s firmware\n", 2476158089Smlaier fw->name); 2477166848Sluigi return EIO; 2478145247Sdamien } 2479145247Sdamien 2480145247Sdamien /* we're done with command blocks processing */ 2481145247Sdamien MEM_WRITE_4(sc, 0x3000a4, 0x540c00); 2482145247Sdamien 2483152611Sdamien /* allow interrupts so we know when the firmware is ready */ 2484145247Sdamien CSR_WRITE_4(sc, IWI_CSR_INTR_MASK, IWI_INTR_MASK); 2485145247Sdamien 2486145247Sdamien /* tell the adapter to initialize the firmware */ 2487145247Sdamien CSR_WRITE_4(sc, IWI_CSR_RST, 0); 2488145247Sdamien 2489145247Sdamien tmp = CSR_READ_4(sc, IWI_CSR_CTL); 2490145247Sdamien CSR_WRITE_4(sc, IWI_CSR_CTL, tmp | IWI_CTL_ALLOW_STANDBY); 2491145247Sdamien 2492145247Sdamien /* wait at most one second for firmware initialization to complete */ 2493145247Sdamien if ((error = msleep(sc, &sc->sc_mtx, 0, "iwiinit", hz)) != 0) { 2494158089Smlaier device_printf(sc->sc_dev, "timeout waiting for %s firmware " 2495158089Smlaier "initialization to complete\n", fw->name); 2496145247Sdamien } 2497145247Sdamien 2498145247Sdamien return error; 2499145247Sdamien} 2500145247Sdamien 2501145247Sdamienstatic int 2502178354Ssamiwi_setpowermode(struct iwi_softc *sc, struct ieee80211vap *vap) 2503158089Smlaier{ 2504158089Smlaier uint32_t data; 2505158089Smlaier 2506178354Ssam if (vap->iv_flags & IEEE80211_F_PMGTON) { 2507158089Smlaier /* XXX set more fine-grained operation */ 2508158089Smlaier data = htole32(IWI_POWER_MODE_MAX); 2509158089Smlaier } else 2510158089Smlaier data = htole32(IWI_POWER_MODE_CAM); 2511158089Smlaier 2512158089Smlaier DPRINTF(("Setting power mode to %u\n", le32toh(data))); 2513158089Smlaier return iwi_cmd(sc, IWI_CMD_SET_POWER_MODE, &data, sizeof data); 2514158089Smlaier} 2515158089Smlaier 2516158089Smlaierstatic int 2517178354Ssamiwi_setwepkeys(struct iwi_softc *sc, struct ieee80211vap *vap) 2518158089Smlaier{ 2519158089Smlaier struct iwi_wep_key wepkey; 2520158089Smlaier struct ieee80211_key *wk; 2521158089Smlaier int error, i; 2522158089Smlaier 2523158089Smlaier for (i = 0; i < IEEE80211_WEP_NKID; i++) { 2524178354Ssam wk = &vap->iv_nw_keys[i]; 2525158089Smlaier 2526158089Smlaier wepkey.cmd = IWI_WEP_KEY_CMD_SETKEY; 2527158089Smlaier wepkey.idx = i; 2528158089Smlaier wepkey.len = wk->wk_keylen; 2529158089Smlaier memset(wepkey.key, 0, sizeof wepkey.key); 2530158089Smlaier memcpy(wepkey.key, wk->wk_key, wk->wk_keylen); 2531158089Smlaier DPRINTF(("Setting wep key index %u len %u\n", wepkey.idx, 2532158089Smlaier wepkey.len)); 2533158089Smlaier error = iwi_cmd(sc, IWI_CMD_SET_WEP_KEY, &wepkey, 2534158089Smlaier sizeof wepkey); 2535158089Smlaier if (error != 0) 2536158089Smlaier return error; 2537158089Smlaier } 2538158089Smlaier return 0; 2539158089Smlaier} 2540158089Smlaier 2541158089Smlaierstatic int 2542145247Sdamieniwi_config(struct iwi_softc *sc) 2543145247Sdamien{ 2544178354Ssam struct ifnet *ifp = sc->sc_ifp; 2545178354Ssam struct ieee80211com *ic = ifp->if_l2com; 2546145247Sdamien struct iwi_configuration config; 2547145247Sdamien struct iwi_rateset rs; 2548145247Sdamien struct iwi_txpower power; 2549145247Sdamien uint32_t data; 2550145247Sdamien int error, i; 2551178354Ssam 2552170530Ssam IWI_LOCK_ASSERT(sc); 2553145247Sdamien 2554190526Ssam DPRINTF(("Setting MAC address to %6D\n", IF_LLADDR(ifp), ":")); 2555190526Ssam error = iwi_cmd(sc, IWI_CMD_SET_MAC_ADDRESS, IF_LLADDR(ifp), 2556158089Smlaier IEEE80211_ADDR_LEN); 2557145247Sdamien if (error != 0) 2558145247Sdamien return error; 2559145247Sdamien 2560145247Sdamien memset(&config, 0, sizeof config); 2561145247Sdamien config.bluetooth_coexistence = sc->bluetooth; 2562158089Smlaier config.silence_threshold = 0x1e; 2563146500Sdamien config.antenna = sc->antenna; 2564145247Sdamien config.multicast_enabled = 1; 2565146500Sdamien config.answer_pbreq = (ic->ic_opmode == IEEE80211_M_IBSS) ? 1 : 0; 2566146500Sdamien config.disable_unicast_decryption = 1; 2567146500Sdamien config.disable_multicast_decryption = 1; 2568213729Sbschmidt if (ic->ic_opmode == IEEE80211_M_MONITOR) { 2569213729Sbschmidt config.allow_invalid_frames = 1; 2570213729Sbschmidt config.allow_beacon_and_probe_resp = 1; 2571213729Sbschmidt config.allow_mgt = 1; 2572213729Sbschmidt } 2573145247Sdamien DPRINTF(("Configuring adapter\n")); 2574158089Smlaier error = iwi_cmd(sc, IWI_CMD_SET_CONFIG, &config, sizeof config); 2575145247Sdamien if (error != 0) 2576145247Sdamien return error; 2577145247Sdamien if (ic->ic_opmode == IEEE80211_M_IBSS) { 2578145247Sdamien power.mode = IWI_MODE_11B; 2579145247Sdamien power.nchan = 11; 2580145247Sdamien for (i = 0; i < 11; i++) { 2581145247Sdamien power.chan[i].chan = i + 1; 2582145247Sdamien power.chan[i].power = IWI_TXPOWER_MAX; 2583145247Sdamien } 2584145247Sdamien DPRINTF(("Setting .11b channels tx power\n")); 2585158089Smlaier error = iwi_cmd(sc, IWI_CMD_SET_TX_POWER, &power, sizeof power); 2586145247Sdamien if (error != 0) 2587145247Sdamien return error; 2588145247Sdamien 2589145247Sdamien power.mode = IWI_MODE_11G; 2590145247Sdamien DPRINTF(("Setting .11g channels tx power\n")); 2591158089Smlaier error = iwi_cmd(sc, IWI_CMD_SET_TX_POWER, &power, sizeof power); 2592145247Sdamien if (error != 0) 2593145247Sdamien return error; 2594145247Sdamien } 2595145247Sdamien 2596171034Sthompsa memset(&rs, 0, sizeof rs); 2597145247Sdamien rs.mode = IWI_MODE_11G; 2598145247Sdamien rs.type = IWI_RATESET_TYPE_SUPPORTED; 2599145247Sdamien rs.nrates = ic->ic_sup_rates[IEEE80211_MODE_11G].rs_nrates; 2600145247Sdamien memcpy(rs.rates, ic->ic_sup_rates[IEEE80211_MODE_11G].rs_rates, 2601145247Sdamien rs.nrates); 2602145247Sdamien DPRINTF(("Setting .11bg supported rates (%u)\n", rs.nrates)); 2603158089Smlaier error = iwi_cmd(sc, IWI_CMD_SET_RATES, &rs, sizeof rs); 2604145247Sdamien if (error != 0) 2605145247Sdamien return error; 2606145247Sdamien 2607171034Sthompsa memset(&rs, 0, sizeof rs); 2608145247Sdamien rs.mode = IWI_MODE_11A; 2609145247Sdamien rs.type = IWI_RATESET_TYPE_SUPPORTED; 2610145247Sdamien rs.nrates = ic->ic_sup_rates[IEEE80211_MODE_11A].rs_nrates; 2611145247Sdamien memcpy(rs.rates, ic->ic_sup_rates[IEEE80211_MODE_11A].rs_rates, 2612145247Sdamien rs.nrates); 2613145247Sdamien DPRINTF(("Setting .11a supported rates (%u)\n", rs.nrates)); 2614158089Smlaier error = iwi_cmd(sc, IWI_CMD_SET_RATES, &rs, sizeof rs); 2615145247Sdamien if (error != 0) 2616145247Sdamien return error; 2617145247Sdamien 2618145247Sdamien data = htole32(arc4random()); 2619145247Sdamien DPRINTF(("Setting initialization vector to %u\n", le32toh(data))); 2620158089Smlaier error = iwi_cmd(sc, IWI_CMD_SET_IV, &data, sizeof data); 2621145247Sdamien if (error != 0) 2622145247Sdamien return error; 2623145247Sdamien 2624145247Sdamien /* enable adapter */ 2625145247Sdamien DPRINTF(("Enabling adapter\n")); 2626158089Smlaier return iwi_cmd(sc, IWI_CMD_ENABLE, NULL, 0); 2627145247Sdamien} 2628145247Sdamien 2629158089Smlaierstatic __inline void 2630158089Smlaierset_scan_type(struct iwi_scan_ext *scan, int ix, int scan_type) 2631146500Sdamien{ 2632158089Smlaier uint8_t *st = &scan->scan_type[ix / 2]; 2633158089Smlaier if (ix % 2) 2634158089Smlaier *st = (*st & 0xf0) | ((scan_type & 0xf) << 0); 2635158089Smlaier else 2636158089Smlaier *st = (*st & 0x0f) | ((scan_type & 0xf) << 4); 2637146500Sdamien} 2638146500Sdamien 2639146500Sdamienstatic int 2640170530Ssamscan_type(const struct ieee80211_scan_state *ss, 2641170530Ssam const struct ieee80211_channel *chan) 2642145247Sdamien{ 2643170530Ssam /* We can only set one essid for a directed scan */ 2644170530Ssam if (ss->ss_nssid != 0) 2645170530Ssam return IWI_SCAN_TYPE_BDIRECTED; 2646170530Ssam if ((ss->ss_flags & IEEE80211_SCAN_ACTIVE) && 2647170530Ssam (chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0) 2648170530Ssam return IWI_SCAN_TYPE_BROADCAST; 2649170530Ssam return IWI_SCAN_TYPE_PASSIVE; 2650170530Ssam} 2651170530Ssam 2652170530Ssamstatic __inline int 2653170530Ssamscan_band(const struct ieee80211_channel *c) 2654170530Ssam{ 2655170530Ssam return IEEE80211_IS_CHAN_5GHZ(c) ? IWI_CHAN_5GHZ : IWI_CHAN_2GHZ; 2656170530Ssam} 2657170530Ssam 2658213729Sbschmidtstatic void 2659213729Sbschmidtiwi_monitor_scan(void *arg, int npending) 2660213729Sbschmidt{ 2661213729Sbschmidt struct iwi_softc *sc = arg; 2662213729Sbschmidt IWI_LOCK_DECL; 2663213729Sbschmidt 2664213729Sbschmidt IWI_LOCK(sc); 2665213729Sbschmidt (void) iwi_scanchan(sc, 2000, 0); 2666213729Sbschmidt IWI_UNLOCK(sc); 2667213729Sbschmidt} 2668213729Sbschmidt 2669170530Ssam/* 2670170530Ssam * Start a scan on the current channel or all channels. 2671170530Ssam */ 2672170530Ssamstatic int 2673191746Sthompsaiwi_scanchan(struct iwi_softc *sc, unsigned long maxdwell, int allchan) 2674170530Ssam{ 2675170530Ssam struct ieee80211com *ic; 2676170530Ssam struct ieee80211_channel *chan; 2677170530Ssam struct ieee80211_scan_state *ss; 2678158089Smlaier struct iwi_scan_ext scan; 2679170530Ssam int error = 0; 2680145247Sdamien 2681170530Ssam IWI_LOCK_ASSERT(sc); 2682170530Ssam if (sc->fw_state == IWI_FW_SCANNING) { 2683170530Ssam /* 2684170530Ssam * This should not happen as we only trigger scan_next after 2685170530Ssam * completion 2686170530Ssam */ 2687170530Ssam DPRINTF(("%s: called too early - still scanning\n", __func__)); 2688170530Ssam return (EBUSY); 2689170530Ssam } 2690170530Ssam IWI_STATE_BEGIN(sc, IWI_FW_SCANNING); 2691166848Sluigi 2692178354Ssam ic = sc->sc_ifp->if_l2com; 2693170530Ssam ss = ic->ic_scan; 2694170530Ssam 2695145247Sdamien memset(&scan, 0, sizeof scan); 2696170530Ssam scan.full_scan_index = htole32(++sc->sc_scangen); 2697170530Ssam scan.dwell_time[IWI_SCAN_TYPE_PASSIVE] = htole16(maxdwell); 2698170530Ssam if (ic->ic_flags_ext & IEEE80211_FEXT_BGSCAN) { 2699170530Ssam /* 2700170530Ssam * Use very short dwell times for when we send probe request 2701170530Ssam * frames. Without this bg scans hang. Ideally this should 2702170530Ssam * be handled with early-termination as done by net80211 but 2703170530Ssam * that's not feasible (aborting a scan is problematic). 2704170530Ssam */ 2705170530Ssam scan.dwell_time[IWI_SCAN_TYPE_BROADCAST] = htole16(30); 2706170530Ssam scan.dwell_time[IWI_SCAN_TYPE_BDIRECTED] = htole16(30); 2707170530Ssam } else { 2708170530Ssam scan.dwell_time[IWI_SCAN_TYPE_BROADCAST] = htole16(maxdwell); 2709170530Ssam scan.dwell_time[IWI_SCAN_TYPE_BDIRECTED] = htole16(maxdwell); 2710170530Ssam } 2711145247Sdamien 2712170530Ssam /* We can only set one essid for a directed scan */ 2713170530Ssam if (ss->ss_nssid != 0) { 2714170530Ssam error = iwi_cmd(sc, IWI_CMD_SET_ESSID, ss->ss_ssid[0].ssid, 2715170530Ssam ss->ss_ssid[0].len); 2716170530Ssam if (error) 2717170530Ssam return (error); 2718170530Ssam } 2719151030Sdamien 2720191746Sthompsa if (allchan) { 2721170530Ssam int i, next, band, b, bstart; 2722170530Ssam /* 2723170530Ssam * Convert scan list to run-length encoded channel list 2724170530Ssam * the firmware requires (preserving the order setup by 2725170530Ssam * net80211). The first entry in each run specifies the 2726170530Ssam * band and the count of items in the run. 2727170530Ssam */ 2728170530Ssam next = 0; /* next open slot */ 2729170530Ssam bstart = 0; /* NB: not needed, silence compiler */ 2730170530Ssam band = -1; /* NB: impossible value */ 2731170530Ssam KASSERT(ss->ss_last > 0, ("no channels")); 2732170530Ssam for (i = 0; i < ss->ss_last; i++) { 2733170530Ssam chan = ss->ss_chans[i]; 2734170530Ssam b = scan_band(chan); 2735170530Ssam if (b != band) { 2736170530Ssam if (band != -1) 2737170530Ssam scan.channels[bstart] = 2738170530Ssam (next - bstart) | band; 2739170530Ssam /* NB: this allocates a slot for the run-len */ 2740170530Ssam band = b, bstart = next++; 2741170530Ssam } 2742170530Ssam if (next >= IWI_SCAN_CHANNELS) { 2743170530Ssam DPRINTF(("truncating scan list\n")); 2744170530Ssam break; 2745170530Ssam } 2746170530Ssam scan.channels[next] = ieee80211_chan2ieee(ic, chan); 2747170530Ssam set_scan_type(&scan, next, scan_type(ss, chan)); 2748170530Ssam next++; 2749170530Ssam } 2750170530Ssam scan.channels[bstart] = (next - bstart) | band; 2751170530Ssam } else { 2752170530Ssam /* Scan the current channel only */ 2753170530Ssam chan = ic->ic_curchan; 2754170530Ssam scan.channels[0] = 1 | scan_band(chan); 2755170530Ssam scan.channels[1] = ieee80211_chan2ieee(ic, chan); 2756170530Ssam set_scan_type(&scan, 1, scan_type(ss, chan)); 2757170530Ssam } 2758163209Smlaier#ifdef IWI_DEBUG 2759170530Ssam if (iwi_debug > 0) { 2760170530Ssam static const char *scantype[8] = 2761170530Ssam { "PSTOP", "PASV", "DIR", "BCAST", "BDIR", "5", "6", "7" }; 2762170530Ssam int i; 2763170530Ssam printf("Scan request: index %u dwell %d/%d/%d\n" 2764170530Ssam , le32toh(scan.full_scan_index) 2765170530Ssam , le16toh(scan.dwell_time[IWI_SCAN_TYPE_PASSIVE]) 2766170530Ssam , le16toh(scan.dwell_time[IWI_SCAN_TYPE_BROADCAST]) 2767170530Ssam , le16toh(scan.dwell_time[IWI_SCAN_TYPE_BDIRECTED]) 2768170530Ssam ); 2769170530Ssam i = 0; 2770170530Ssam do { 2771170530Ssam int run = scan.channels[i]; 2772170530Ssam if (run == 0) 2773170530Ssam break; 2774170530Ssam printf("Scan %d %s channels:", run & 0x3f, 2775170530Ssam run & IWI_CHAN_2GHZ ? "2.4GHz" : "5GHz"); 2776170530Ssam for (run &= 0x3f, i++; run > 0; run--, i++) { 2777170530Ssam uint8_t type = scan.scan_type[i/2]; 2778170530Ssam printf(" %u/%s", scan.channels[i], 2779170530Ssam scantype[(i & 1 ? type : type>>4) & 7]); 2780170530Ssam } 2781163209Smlaier printf("\n"); 2782170530Ssam } while (i < IWI_SCAN_CHANNELS); 2783170530Ssam } 2784163209Smlaier#endif 2785158089Smlaier 2786170530Ssam return (iwi_cmd(sc, IWI_CMD_SCAN_EXT, &scan, sizeof scan)); 2787145247Sdamien} 2788145247Sdamien 2789145247Sdamienstatic int 2790158089Smlaieriwi_set_sensitivity(struct iwi_softc *sc, int8_t rssi_dbm) 2791158089Smlaier{ 2792158089Smlaier struct iwi_sensitivity sens; 2793158089Smlaier 2794158089Smlaier DPRINTF(("Setting sensitivity to %d\n", rssi_dbm)); 2795158089Smlaier 2796158089Smlaier memset(&sens, 0, sizeof sens); 2797158089Smlaier sens.rssi = htole16(rssi_dbm); 2798158089Smlaier return iwi_cmd(sc, IWI_CMD_SET_SENSITIVITY, &sens, sizeof sens); 2799158089Smlaier} 2800158089Smlaier 2801158089Smlaierstatic int 2802178354Ssamiwi_auth_and_assoc(struct iwi_softc *sc, struct ieee80211vap *vap) 2803145247Sdamien{ 2804178354Ssam struct ieee80211com *ic = vap->iv_ic; 2805178354Ssam struct ifnet *ifp = vap->iv_ifp; 2806178354Ssam struct ieee80211_node *ni = vap->iv_bss; 2807145247Sdamien struct iwi_configuration config; 2808158089Smlaier struct iwi_associate *assoc = &sc->assoc; 2809145247Sdamien struct iwi_rateset rs; 2810146500Sdamien uint16_t capinfo; 2811178354Ssam uint32_t data; 2812171034Sthompsa int error, mode; 2813170530Ssam 2814170530Ssam IWI_LOCK_ASSERT(sc); 2815170530Ssam 2816170530Ssam if (sc->flags & IWI_FLAG_ASSOCIATED) { 2817170530Ssam DPRINTF(("Already associated\n")); 2818170530Ssam return (-1); 2819170530Ssam } 2820170530Ssam 2821170530Ssam IWI_STATE_BEGIN(sc, IWI_FW_ASSOCIATING); 2822170530Ssam error = 0; 2823171034Sthompsa mode = 0; 2824171034Sthompsa 2825171034Sthompsa if (IEEE80211_IS_CHAN_A(ic->ic_curchan)) 2826171034Sthompsa mode = IWI_MODE_11A; 2827171034Sthompsa else if (IEEE80211_IS_CHAN_G(ic->ic_curchan)) 2828171034Sthompsa mode = IWI_MODE_11G; 2829171034Sthompsa if (IEEE80211_IS_CHAN_B(ic->ic_curchan)) 2830171034Sthompsa mode = IWI_MODE_11B; 2831171034Sthompsa 2832170530Ssam if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) { 2833145247Sdamien memset(&config, 0, sizeof config); 2834145247Sdamien config.bluetooth_coexistence = sc->bluetooth; 2835146500Sdamien config.antenna = sc->antenna; 2836145247Sdamien config.multicast_enabled = 1; 2837171034Sthompsa if (mode == IWI_MODE_11G) 2838171034Sthompsa config.use_protection = 1; 2839146500Sdamien config.answer_pbreq = 2840178354Ssam (vap->iv_opmode == IEEE80211_M_IBSS) ? 1 : 0; 2841146500Sdamien config.disable_unicast_decryption = 1; 2842146500Sdamien config.disable_multicast_decryption = 1; 2843145247Sdamien DPRINTF(("Configuring adapter\n")); 2844158089Smlaier error = iwi_cmd(sc, IWI_CMD_SET_CONFIG, &config, sizeof config); 2845145247Sdamien if (error != 0) 2846170530Ssam goto done; 2847145247Sdamien } 2848145247Sdamien 2849145247Sdamien#ifdef IWI_DEBUG 2850145247Sdamien if (iwi_debug > 0) { 2851145247Sdamien printf("Setting ESSID to "); 2852145247Sdamien ieee80211_print_essid(ni->ni_essid, ni->ni_esslen); 2853145247Sdamien printf("\n"); 2854145247Sdamien } 2855145247Sdamien#endif 2856158089Smlaier error = iwi_cmd(sc, IWI_CMD_SET_ESSID, ni->ni_essid, ni->ni_esslen); 2857145247Sdamien if (error != 0) 2858170530Ssam goto done; 2859145247Sdamien 2860178354Ssam error = iwi_setpowermode(sc, vap); 2861178354Ssam if (error != 0) 2862178354Ssam goto done; 2863178354Ssam 2864178354Ssam data = htole32(vap->iv_rtsthreshold); 2865178354Ssam DPRINTF(("Setting RTS threshold to %u\n", le32toh(data))); 2866178354Ssam error = iwi_cmd(sc, IWI_CMD_SET_RTS_THRESHOLD, &data, sizeof data); 2867178354Ssam if (error != 0) 2868178354Ssam goto done; 2869178354Ssam 2870178354Ssam data = htole32(vap->iv_fragthreshold); 2871178354Ssam DPRINTF(("Setting fragmentation threshold to %u\n", le32toh(data))); 2872178354Ssam error = iwi_cmd(sc, IWI_CMD_SET_FRAG_THRESHOLD, &data, sizeof data); 2873178354Ssam if (error != 0) 2874178354Ssam goto done; 2875178354Ssam 2876151030Sdamien /* the rate set has already been "negotiated" */ 2877171034Sthompsa memset(&rs, 0, sizeof rs); 2878171034Sthompsa rs.mode = mode; 2879151030Sdamien rs.type = IWI_RATESET_TYPE_NEGOTIATED; 2880145247Sdamien rs.nrates = ni->ni_rates.rs_nrates; 2881163618Smlaier if (rs.nrates > IWI_RATESET_SIZE) { 2882163618Smlaier DPRINTF(("Truncating negotiated rate set from %u\n", 2883163618Smlaier rs.nrates)); 2884163618Smlaier rs.nrates = IWI_RATESET_SIZE; 2885163618Smlaier } 2886145247Sdamien memcpy(rs.rates, ni->ni_rates.rs_rates, rs.nrates); 2887158089Smlaier DPRINTF(("Setting negotiated rates (%u)\n", rs.nrates)); 2888158089Smlaier error = iwi_cmd(sc, IWI_CMD_SET_RATES, &rs, sizeof rs); 2889145247Sdamien if (error != 0) 2890170530Ssam goto done; 2891145247Sdamien 2892158089Smlaier memset(assoc, 0, sizeof *assoc); 2893158089Smlaier 2894178354Ssam if ((vap->iv_flags & IEEE80211_F_WME) && ni->ni_ies.wme_ie != NULL) { 2895158089Smlaier /* NB: don't treat WME setup as failure */ 2896178354Ssam if (iwi_wme_setparams(sc, ic) == 0 && iwi_wme_setie(sc) == 0) 2897158089Smlaier assoc->policy |= htole16(IWI_POLICY_WME); 2898158089Smlaier /* XXX complain on failure? */ 2899149338Sdamien } 2900149338Sdamien 2901178354Ssam if (vap->iv_appie_wpa != NULL) { 2902178354Ssam struct ieee80211_appie *ie = vap->iv_appie_wpa; 2903178354Ssam 2904178354Ssam DPRINTF(("Setting optional IE (len=%u)\n", ie->ie_len)); 2905178354Ssam error = iwi_cmd(sc, IWI_CMD_SET_OPTIE, ie->ie_data, ie->ie_len); 2906146500Sdamien if (error != 0) 2907170530Ssam goto done; 2908146500Sdamien } 2909146500Sdamien 2910178354Ssam error = iwi_set_sensitivity(sc, ic->ic_node_getrssi(ni)); 2911145247Sdamien if (error != 0) 2912170530Ssam goto done; 2913145247Sdamien 2914171034Sthompsa assoc->mode = mode; 2915170530Ssam assoc->chan = ic->ic_curchan->ic_ieee; 2916158089Smlaier /* 2917158089Smlaier * NB: do not arrange for shared key auth w/o privacy 2918158089Smlaier * (i.e. a wep key); it causes a firmware error. 2919158089Smlaier */ 2920178354Ssam if ((vap->iv_flags & IEEE80211_F_PRIVACY) && 2921158089Smlaier ni->ni_authmode == IEEE80211_AUTH_SHARED) { 2922158089Smlaier assoc->auth = IWI_AUTH_SHARED; 2923158089Smlaier /* 2924158089Smlaier * It's possible to have privacy marked but no default 2925158089Smlaier * key setup. This typically is due to a user app bug 2926158089Smlaier * but if we blindly grab the key the firmware will 2927158089Smlaier * barf so avoid it for now. 2928158089Smlaier */ 2929178354Ssam if (vap->iv_def_txkey != IEEE80211_KEYIX_NONE) 2930178354Ssam assoc->auth |= vap->iv_def_txkey << 4; 2931158089Smlaier 2932178354Ssam error = iwi_setwepkeys(sc, vap); 2933158089Smlaier if (error != 0) 2934170530Ssam goto done; 2935158089Smlaier } 2936178354Ssam if (vap->iv_flags & IEEE80211_F_WPA) 2937158089Smlaier assoc->policy |= htole16(IWI_POLICY_WPA); 2938178354Ssam if (vap->iv_opmode == IEEE80211_M_IBSS && ni->ni_tstamp.tsf == 0) 2939158089Smlaier assoc->type = IWI_HC_IBSS_START; 2940158089Smlaier else 2941158089Smlaier assoc->type = IWI_HC_ASSOC; 2942158089Smlaier memcpy(assoc->tstamp, ni->ni_tstamp.data, 8); 2943146500Sdamien 2944178354Ssam if (vap->iv_opmode == IEEE80211_M_IBSS) 2945146500Sdamien capinfo = IEEE80211_CAPINFO_IBSS; 2946146500Sdamien else 2947146500Sdamien capinfo = IEEE80211_CAPINFO_ESS; 2948178354Ssam if (vap->iv_flags & IEEE80211_F_PRIVACY) 2949146500Sdamien capinfo |= IEEE80211_CAPINFO_PRIVACY; 2950146500Sdamien if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && 2951170530Ssam IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) 2952146500Sdamien capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE; 2953158089Smlaier if (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) 2954146500Sdamien capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME; 2955158089Smlaier assoc->capinfo = htole16(capinfo); 2956146500Sdamien 2957158089Smlaier assoc->lintval = htole16(ic->ic_lintval); 2958158089Smlaier assoc->intval = htole16(ni->ni_intval); 2959158089Smlaier IEEE80211_ADDR_COPY(assoc->bssid, ni->ni_bssid); 2960178354Ssam if (vap->iv_opmode == IEEE80211_M_IBSS) 2961158089Smlaier IEEE80211_ADDR_COPY(assoc->dst, ifp->if_broadcastaddr); 2962145247Sdamien else 2963158089Smlaier IEEE80211_ADDR_COPY(assoc->dst, ni->ni_bssid); 2964145247Sdamien 2965158089Smlaier DPRINTF(("%s bssid %6D dst %6D channel %u policy 0x%x " 2966158089Smlaier "auth %u capinfo 0x%x lintval %u bintval %u\n", 2967158089Smlaier assoc->type == IWI_HC_IBSS_START ? "Start" : "Join", 2968158089Smlaier assoc->bssid, ":", assoc->dst, ":", 2969158089Smlaier assoc->chan, le16toh(assoc->policy), assoc->auth, 2970158089Smlaier le16toh(assoc->capinfo), le16toh(assoc->lintval), 2971158089Smlaier le16toh(assoc->intval))); 2972170530Ssam error = iwi_cmd(sc, IWI_CMD_ASSOCIATE, assoc, sizeof *assoc); 2973170530Ssamdone: 2974170530Ssam if (error) 2975170530Ssam IWI_STATE_END(sc, IWI_FW_ASSOCIATING); 2976170530Ssam 2977170530Ssam return (error); 2978145247Sdamien} 2979145247Sdamien 2980191746Sthompsastatic void 2981191746Sthompsaiwi_disassoc(void *arg, int pending) 2982191746Sthompsa{ 2983191746Sthompsa struct iwi_softc *sc = arg; 2984191746Sthompsa IWI_LOCK_DECL; 2985191746Sthompsa 2986191746Sthompsa IWI_LOCK(sc); 2987191746Sthompsa iwi_disassociate(sc, 0); 2988191746Sthompsa IWI_UNLOCK(sc); 2989191746Sthompsa} 2990191746Sthompsa 2991158089Smlaierstatic int 2992158089Smlaieriwi_disassociate(struct iwi_softc *sc, int quiet) 2993158089Smlaier{ 2994158089Smlaier struct iwi_associate *assoc = &sc->assoc; 2995158089Smlaier 2996170530Ssam if ((sc->flags & IWI_FLAG_ASSOCIATED) == 0) { 2997170530Ssam DPRINTF(("Not associated\n")); 2998170530Ssam return (-1); 2999170530Ssam } 3000170530Ssam 3001170530Ssam IWI_STATE_BEGIN(sc, IWI_FW_DISASSOCIATING); 3002170530Ssam 3003158089Smlaier if (quiet) 3004158089Smlaier assoc->type = IWI_HC_DISASSOC_QUIET; 3005158089Smlaier else 3006158089Smlaier assoc->type = IWI_HC_DISASSOC; 3007158089Smlaier 3008158089Smlaier DPRINTF(("Trying to disassociate from %6D channel %u\n", 3009158089Smlaier assoc->bssid, ":", assoc->chan)); 3010158089Smlaier return iwi_cmd(sc, IWI_CMD_ASSOCIATE, assoc, sizeof *assoc); 3011158089Smlaier} 3012158089Smlaier 3013166848Sluigi/* 3014166848Sluigi * release dma resources for the firmware 3015166848Sluigi */ 3016158089Smlaierstatic void 3017166848Sluigiiwi_release_fw_dma(struct iwi_softc *sc) 3018166848Sluigi{ 3019166848Sluigi if (sc->fw_flags & IWI_FW_HAVE_PHY) 3020166848Sluigi bus_dmamap_unload(sc->fw_dmat, sc->fw_map); 3021166848Sluigi if (sc->fw_flags & IWI_FW_HAVE_MAP) 3022166848Sluigi bus_dmamem_free(sc->fw_dmat, sc->fw_virtaddr, sc->fw_map); 3023166848Sluigi if (sc->fw_flags & IWI_FW_HAVE_DMAT) 3024166848Sluigi bus_dma_tag_destroy(sc->fw_dmat); 3025166848Sluigi 3026166848Sluigi sc->fw_flags = 0; 3027166848Sluigi sc->fw_dma_size = 0; 3028166848Sluigi sc->fw_dmat = NULL; 3029166848Sluigi sc->fw_map = NULL; 3030166848Sluigi sc->fw_physaddr = 0; 3031166848Sluigi sc->fw_virtaddr = NULL; 3032166848Sluigi} 3033166848Sluigi 3034166848Sluigi/* 3035166848Sluigi * allocate the dma descriptor for the firmware. 3036166848Sluigi * Return 0 on success, 1 on error. 3037166848Sluigi * Must be called unlocked, protected by IWI_FLAG_FW_LOADING. 3038166848Sluigi */ 3039166848Sluigistatic int 3040166848Sluigiiwi_init_fw_dma(struct iwi_softc *sc, int size) 3041166848Sluigi{ 3042167776Sjhb if (sc->fw_dma_size >= size) 3043166848Sluigi return 0; 3044166848Sluigi if (bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 4, 0, 3045166848Sluigi BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, 3046166848Sluigi size, 1, size, 0, NULL, NULL, &sc->fw_dmat) != 0) { 3047166848Sluigi device_printf(sc->sc_dev, 3048166848Sluigi "could not create firmware DMA tag\n"); 3049166848Sluigi goto error; 3050166848Sluigi } 3051166848Sluigi sc->fw_flags |= IWI_FW_HAVE_DMAT; 3052166848Sluigi if (bus_dmamem_alloc(sc->fw_dmat, &sc->fw_virtaddr, 0, 3053166848Sluigi &sc->fw_map) != 0) { 3054166848Sluigi device_printf(sc->sc_dev, 3055166848Sluigi "could not allocate firmware DMA memory\n"); 3056166848Sluigi goto error; 3057166848Sluigi } 3058166848Sluigi sc->fw_flags |= IWI_FW_HAVE_MAP; 3059166848Sluigi if (bus_dmamap_load(sc->fw_dmat, sc->fw_map, sc->fw_virtaddr, 3060166848Sluigi size, iwi_dma_map_addr, &sc->fw_physaddr, 0) != 0) { 3061166848Sluigi device_printf(sc->sc_dev, "could not load firmware DMA map\n"); 3062166848Sluigi goto error; 3063166848Sluigi } 3064166848Sluigi sc->fw_flags |= IWI_FW_HAVE_PHY; 3065166848Sluigi sc->fw_dma_size = size; 3066166848Sluigi return 0; 3067166848Sluigi 3068166848Sluigierror: 3069166848Sluigi iwi_release_fw_dma(sc); 3070166848Sluigi return 1; 3071166848Sluigi} 3072166848Sluigi 3073166848Sluigistatic void 3074178354Ssamiwi_init_locked(struct iwi_softc *sc) 3075158089Smlaier{ 3076178354Ssam struct ifnet *ifp = sc->sc_ifp; 3077145247Sdamien struct iwi_rx_data *data; 3078158089Smlaier int i; 3079145247Sdamien 3080170530Ssam IWI_LOCK_ASSERT(sc); 3081178354Ssam 3082170530Ssam if (sc->fw_state == IWI_FW_LOADING) { 3083166848Sluigi device_printf(sc->sc_dev, "%s: already loading\n", __func__); 3084158089Smlaier return; /* XXX: condvar? */ 3085166848Sluigi } 3086156539Sdamien 3087178354Ssam iwi_stop_locked(sc); 3088178354Ssam 3089170530Ssam IWI_STATE_BEGIN(sc, IWI_FW_LOADING); 3090145247Sdamien 3091145247Sdamien if (iwi_reset(sc) != 0) { 3092145247Sdamien device_printf(sc->sc_dev, "could not reset adapter\n"); 3093158089Smlaier goto fail; 3094145247Sdamien } 3095158089Smlaier if (iwi_load_firmware(sc, &sc->fw_boot) != 0) { 3096156546Sdamien device_printf(sc->sc_dev, 3097158089Smlaier "could not load boot firmware %s\n", sc->fw_boot.name); 3098166848Sluigi goto fail; 3099156546Sdamien } 3100158089Smlaier if (iwi_load_ucode(sc, &sc->fw_uc) != 0) { 3101158089Smlaier device_printf(sc->sc_dev, 3102158089Smlaier "could not load microcode %s\n", sc->fw_uc.name); 3103166848Sluigi goto fail; 3104145247Sdamien } 3105145247Sdamien 3106145247Sdamien iwi_stop_master(sc); 3107145247Sdamien 3108145247Sdamien CSR_WRITE_4(sc, IWI_CSR_CMD_BASE, sc->cmdq.physaddr); 3109145247Sdamien CSR_WRITE_4(sc, IWI_CSR_CMD_SIZE, sc->cmdq.count); 3110145247Sdamien CSR_WRITE_4(sc, IWI_CSR_CMD_WIDX, sc->cmdq.cur); 3111145247Sdamien 3112149338Sdamien CSR_WRITE_4(sc, IWI_CSR_TX1_BASE, sc->txq[0].physaddr); 3113149338Sdamien CSR_WRITE_4(sc, IWI_CSR_TX1_SIZE, sc->txq[0].count); 3114149338Sdamien CSR_WRITE_4(sc, IWI_CSR_TX1_WIDX, sc->txq[0].cur); 3115145247Sdamien 3116149338Sdamien CSR_WRITE_4(sc, IWI_CSR_TX2_BASE, sc->txq[1].physaddr); 3117149338Sdamien CSR_WRITE_4(sc, IWI_CSR_TX2_SIZE, sc->txq[1].count); 3118149338Sdamien CSR_WRITE_4(sc, IWI_CSR_TX2_WIDX, sc->txq[1].cur); 3119145247Sdamien 3120149338Sdamien CSR_WRITE_4(sc, IWI_CSR_TX3_BASE, sc->txq[2].physaddr); 3121149338Sdamien CSR_WRITE_4(sc, IWI_CSR_TX3_SIZE, sc->txq[2].count); 3122149338Sdamien CSR_WRITE_4(sc, IWI_CSR_TX3_WIDX, sc->txq[2].cur); 3123145247Sdamien 3124149338Sdamien CSR_WRITE_4(sc, IWI_CSR_TX4_BASE, sc->txq[3].physaddr); 3125149338Sdamien CSR_WRITE_4(sc, IWI_CSR_TX4_SIZE, sc->txq[3].count); 3126149338Sdamien CSR_WRITE_4(sc, IWI_CSR_TX4_WIDX, sc->txq[3].cur); 3127145247Sdamien 3128145247Sdamien for (i = 0; i < sc->rxq.count; i++) { 3129145247Sdamien data = &sc->rxq.data[i]; 3130145247Sdamien CSR_WRITE_4(sc, data->reg, data->physaddr); 3131145247Sdamien } 3132145247Sdamien 3133145247Sdamien CSR_WRITE_4(sc, IWI_CSR_RX_WIDX, sc->rxq.count - 1); 3134145247Sdamien 3135158089Smlaier if (iwi_load_firmware(sc, &sc->fw_fw) != 0) { 3136158089Smlaier device_printf(sc->sc_dev, 3137158089Smlaier "could not load main firmware %s\n", sc->fw_fw.name); 3138166848Sluigi goto fail; 3139145247Sdamien } 3140145247Sdamien sc->flags |= IWI_FLAG_FW_INITED; 3141145247Sdamien 3142178354Ssam IWI_STATE_END(sc, IWI_FW_LOADING); 3143178354Ssam 3144145247Sdamien if (iwi_config(sc) != 0) { 3145178354Ssam device_printf(sc->sc_dev, "unable to enable adapter\n"); 3146178354Ssam goto fail2; 3147145247Sdamien } 3148145247Sdamien 3149170530Ssam callout_reset(&sc->sc_wdtimer, hz, iwi_watchdog, sc); 3150148887Srwatson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 3151148887Srwatson ifp->if_drv_flags |= IFF_DRV_RUNNING; 3152145247Sdamien return; 3153178354Ssamfail: 3154170530Ssam IWI_STATE_END(sc, IWI_FW_LOADING); 3155178354Ssamfail2: 3156178354Ssam iwi_stop_locked(sc); 3157145247Sdamien} 3158145247Sdamien 3159145247Sdamienstatic void 3160178354Ssamiwi_init(void *priv) 3161145247Sdamien{ 3162145247Sdamien struct iwi_softc *sc = priv; 3163178354Ssam struct ifnet *ifp = sc->sc_ifp; 3164178354Ssam struct ieee80211com *ic = ifp->if_l2com; 3165178354Ssam IWI_LOCK_DECL; 3166145247Sdamien 3167178354Ssam IWI_LOCK(sc); 3168178354Ssam iwi_init_locked(sc); 3169178354Ssam IWI_UNLOCK(sc); 3170178354Ssam 3171178354Ssam if (ifp->if_drv_flags & IFF_DRV_RUNNING) 3172178354Ssam ieee80211_start_all(ic); 3173178354Ssam} 3174178354Ssam 3175178354Ssamstatic void 3176178354Ssamiwi_stop_locked(void *priv) 3177178354Ssam{ 3178178354Ssam struct iwi_softc *sc = priv; 3179178354Ssam struct ifnet *ifp = sc->sc_ifp; 3180178354Ssam 3181170530Ssam IWI_LOCK_ASSERT(sc); 3182178354Ssam 3183178354Ssam ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 3184178354Ssam 3185158089Smlaier if (sc->sc_softled) { 3186158089Smlaier callout_stop(&sc->sc_ledtimer); 3187158089Smlaier sc->sc_blinking = 0; 3188158089Smlaier } 3189178354Ssam callout_stop(&sc->sc_wdtimer); 3190178354Ssam callout_stop(&sc->sc_rftimer); 3191156539Sdamien 3192145247Sdamien iwi_stop_master(sc); 3193145247Sdamien 3194145247Sdamien CSR_WRITE_4(sc, IWI_CSR_RST, IWI_RST_SOFT_RESET); 3195145247Sdamien 3196145247Sdamien /* reset rings */ 3197145247Sdamien iwi_reset_cmd_ring(sc, &sc->cmdq); 3198149338Sdamien iwi_reset_tx_ring(sc, &sc->txq[0]); 3199149338Sdamien iwi_reset_tx_ring(sc, &sc->txq[1]); 3200149338Sdamien iwi_reset_tx_ring(sc, &sc->txq[2]); 3201149338Sdamien iwi_reset_tx_ring(sc, &sc->txq[3]); 3202145247Sdamien iwi_reset_rx_ring(sc, &sc->rxq); 3203145247Sdamien 3204158089Smlaier sc->sc_tx_timer = 0; 3205170530Ssam sc->sc_state_timer = 0; 3206170530Ssam sc->sc_busy_timer = 0; 3207170530Ssam sc->flags &= ~(IWI_FLAG_BUSY | IWI_FLAG_ASSOCIATED); 3208170530Ssam sc->fw_state = IWI_FW_IDLE; 3209170530Ssam wakeup(sc); 3210145247Sdamien} 3211145247Sdamien 3212158089Smlaierstatic void 3213178354Ssamiwi_stop(struct iwi_softc *sc) 3214158089Smlaier{ 3215158089Smlaier IWI_LOCK_DECL; 3216158089Smlaier 3217158089Smlaier IWI_LOCK(sc); 3218178354Ssam iwi_stop_locked(sc); 3219158089Smlaier IWI_UNLOCK(sc); 3220158089Smlaier} 3221158089Smlaier 3222178354Ssamstatic void 3223178354Ssamiwi_restart(void *arg, int npending) 3224178354Ssam{ 3225178354Ssam struct iwi_softc *sc = arg; 3226178354Ssam 3227178354Ssam iwi_init(sc); 3228178354Ssam} 3229178354Ssam 3230158089Smlaier/* 3231158089Smlaier * Return whether or not the radio is enabled in hardware 3232158089Smlaier * (i.e. the rfkill switch is "off"). 3233158089Smlaier */ 3234145247Sdamienstatic int 3235158089Smlaieriwi_getrfkill(struct iwi_softc *sc) 3236158089Smlaier{ 3237158089Smlaier return (CSR_READ_4(sc, IWI_CSR_IO) & IWI_IO_RADIO_ENABLED) == 0; 3238158089Smlaier} 3239158089Smlaier 3240158089Smlaierstatic void 3241158089Smlaieriwi_radio_on(void *arg, int pending) 3242158089Smlaier{ 3243158089Smlaier struct iwi_softc *sc = arg; 3244178354Ssam struct ieee80211com *ic = sc->sc_ifp->if_l2com; 3245158089Smlaier 3246158089Smlaier device_printf(sc->sc_dev, "radio turned on\n"); 3247178354Ssam 3248158089Smlaier iwi_init(sc); 3249178354Ssam ieee80211_notify_radio(ic, 1); 3250158089Smlaier} 3251158089Smlaier 3252158089Smlaierstatic void 3253178354Ssamiwi_rfkill_poll(void *arg) 3254178354Ssam{ 3255178354Ssam struct iwi_softc *sc = arg; 3256178354Ssam 3257178354Ssam IWI_LOCK_ASSERT(sc); 3258178354Ssam 3259178354Ssam /* 3260178354Ssam * Check for a change in rfkill state. We get an 3261178354Ssam * interrupt when a radio is disabled but not when 3262178354Ssam * it is enabled so we must poll for the latter. 3263178354Ssam */ 3264178354Ssam if (!iwi_getrfkill(sc)) { 3265191746Sthompsa struct ifnet *ifp = sc->sc_ifp; 3266191746Sthompsa struct ieee80211com *ic = ifp->if_l2com; 3267191746Sthompsa 3268191746Sthompsa ieee80211_runtask(ic, &sc->sc_radiontask); 3269178354Ssam return; 3270178354Ssam } 3271178354Ssam callout_reset(&sc->sc_rftimer, 2*hz, iwi_rfkill_poll, sc); 3272178354Ssam} 3273178354Ssam 3274178354Ssamstatic void 3275158089Smlaieriwi_radio_off(void *arg, int pending) 3276158089Smlaier{ 3277158089Smlaier struct iwi_softc *sc = arg; 3278178354Ssam struct ieee80211com *ic = sc->sc_ifp->if_l2com; 3279170530Ssam IWI_LOCK_DECL; 3280158089Smlaier 3281158089Smlaier device_printf(sc->sc_dev, "radio turned off\n"); 3282178354Ssam 3283178354Ssam ieee80211_notify_radio(ic, 0); 3284178354Ssam 3285170530Ssam IWI_LOCK(sc); 3286178354Ssam iwi_stop_locked(sc); 3287178354Ssam iwi_rfkill_poll(sc); 3288170530Ssam IWI_UNLOCK(sc); 3289158089Smlaier} 3290158089Smlaier 3291158089Smlaierstatic int 3292145247Sdamieniwi_sysctl_stats(SYSCTL_HANDLER_ARGS) 3293145247Sdamien{ 3294145247Sdamien struct iwi_softc *sc = arg1; 3295145247Sdamien uint32_t size, buf[128]; 3296145247Sdamien 3297174317Sphilip memset(buf, 0, sizeof buf); 3298174317Sphilip 3299174317Sphilip if (!(sc->flags & IWI_FLAG_FW_INITED)) 3300145247Sdamien return SYSCTL_OUT(req, buf, sizeof buf); 3301145247Sdamien 3302145247Sdamien size = min(CSR_READ_4(sc, IWI_CSR_TABLE0_SIZE), 128 - 1); 3303145247Sdamien CSR_READ_REGION_4(sc, IWI_CSR_TABLE0_BASE, &buf[1], size); 3304145247Sdamien 3305174317Sphilip return SYSCTL_OUT(req, buf, size); 3306145247Sdamien} 3307145247Sdamien 3308145247Sdamienstatic int 3309145247Sdamieniwi_sysctl_radio(SYSCTL_HANDLER_ARGS) 3310145247Sdamien{ 3311145247Sdamien struct iwi_softc *sc = arg1; 3312158089Smlaier int val = !iwi_getrfkill(sc); 3313145247Sdamien 3314145247Sdamien return SYSCTL_OUT(req, &val, sizeof val); 3315145247Sdamien} 3316158089Smlaier 3317158089Smlaier/* 3318158089Smlaier * Add sysctl knobs. 3319158089Smlaier */ 3320158089Smlaierstatic void 3321158089Smlaieriwi_sysctlattach(struct iwi_softc *sc) 3322158089Smlaier{ 3323158089Smlaier struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); 3324158089Smlaier struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); 3325158089Smlaier 3326158089Smlaier SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "radio", 3327158089Smlaier CTLTYPE_INT | CTLFLAG_RD, sc, 0, iwi_sysctl_radio, "I", 3328158089Smlaier "radio transmitter switch state (0=off, 1=on)"); 3329158089Smlaier 3330158089Smlaier SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "stats", 3331158089Smlaier CTLTYPE_OPAQUE | CTLFLAG_RD, sc, 0, iwi_sysctl_stats, "S", 3332158089Smlaier "statistics"); 3333158089Smlaier 3334158089Smlaier sc->bluetooth = 0; 3335158089Smlaier SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "bluetooth", 3336158089Smlaier CTLFLAG_RW, &sc->bluetooth, 0, "bluetooth coexistence"); 3337158089Smlaier 3338158089Smlaier sc->antenna = IWI_ANTENNA_AUTO; 3339158089Smlaier SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "antenna", 3340158089Smlaier CTLFLAG_RW, &sc->antenna, 0, "antenna (0=auto)"); 3341158089Smlaier} 3342158089Smlaier 3343158089Smlaier/* 3344158089Smlaier * LED support. 3345158089Smlaier * 3346158089Smlaier * Different cards have different capabilities. Some have three 3347158089Smlaier * led's while others have only one. The linux ipw driver defines 3348158089Smlaier * led's for link state (associated or not), band (11a, 11g, 11b), 3349158089Smlaier * and for link activity. We use one led and vary the blink rate 3350158089Smlaier * according to the tx/rx traffic a la the ath driver. 3351158089Smlaier */ 3352158089Smlaier 3353158089Smlaierstatic __inline uint32_t 3354158089Smlaieriwi_toggle_event(uint32_t r) 3355158089Smlaier{ 3356158089Smlaier return r &~ (IWI_RST_STANDBY | IWI_RST_GATE_ODMA | 3357158089Smlaier IWI_RST_GATE_IDMA | IWI_RST_GATE_ADMA); 3358158089Smlaier} 3359158089Smlaier 3360158089Smlaierstatic uint32_t 3361158089Smlaieriwi_read_event(struct iwi_softc *sc) 3362158089Smlaier{ 3363158089Smlaier return MEM_READ_4(sc, IWI_MEM_EEPROM_EVENT); 3364158089Smlaier} 3365158089Smlaier 3366158089Smlaierstatic void 3367158089Smlaieriwi_write_event(struct iwi_softc *sc, uint32_t v) 3368158089Smlaier{ 3369158089Smlaier MEM_WRITE_4(sc, IWI_MEM_EEPROM_EVENT, v); 3370158089Smlaier} 3371158089Smlaier 3372158089Smlaierstatic void 3373158089Smlaieriwi_led_done(void *arg) 3374158089Smlaier{ 3375158089Smlaier struct iwi_softc *sc = arg; 3376158089Smlaier 3377158089Smlaier sc->sc_blinking = 0; 3378158089Smlaier} 3379158089Smlaier 3380158089Smlaier/* 3381158089Smlaier * Turn the activity LED off: flip the pin and then set a timer so no 3382158089Smlaier * update will happen for the specified duration. 3383158089Smlaier */ 3384158089Smlaierstatic void 3385158089Smlaieriwi_led_off(void *arg) 3386158089Smlaier{ 3387158089Smlaier struct iwi_softc *sc = arg; 3388158089Smlaier uint32_t v; 3389158089Smlaier 3390158089Smlaier v = iwi_read_event(sc); 3391158089Smlaier v &= ~sc->sc_ledpin; 3392158089Smlaier iwi_write_event(sc, iwi_toggle_event(v)); 3393158089Smlaier callout_reset(&sc->sc_ledtimer, sc->sc_ledoff, iwi_led_done, sc); 3394158089Smlaier} 3395158089Smlaier 3396158089Smlaier/* 3397158089Smlaier * Blink the LED according to the specified on/off times. 3398158089Smlaier */ 3399158089Smlaierstatic void 3400158089Smlaieriwi_led_blink(struct iwi_softc *sc, int on, int off) 3401158089Smlaier{ 3402158089Smlaier uint32_t v; 3403158089Smlaier 3404158089Smlaier v = iwi_read_event(sc); 3405158089Smlaier v |= sc->sc_ledpin; 3406158089Smlaier iwi_write_event(sc, iwi_toggle_event(v)); 3407158089Smlaier sc->sc_blinking = 1; 3408158089Smlaier sc->sc_ledoff = off; 3409158089Smlaier callout_reset(&sc->sc_ledtimer, on, iwi_led_off, sc); 3410158089Smlaier} 3411158089Smlaier 3412158089Smlaierstatic void 3413158089Smlaieriwi_led_event(struct iwi_softc *sc, int event) 3414158089Smlaier{ 3415158089Smlaier#define N(a) (sizeof(a)/sizeof(a[0])) 3416158089Smlaier /* NB: on/off times from the Atheros NDIS driver, w/ permission */ 3417158089Smlaier static const struct { 3418158089Smlaier u_int rate; /* tx/rx iwi rate */ 3419158089Smlaier u_int16_t timeOn; /* LED on time (ms) */ 3420158089Smlaier u_int16_t timeOff; /* LED off time (ms) */ 3421158089Smlaier } blinkrates[] = { 3422158089Smlaier { IWI_RATE_OFDM54, 40, 10 }, 3423158089Smlaier { IWI_RATE_OFDM48, 44, 11 }, 3424158089Smlaier { IWI_RATE_OFDM36, 50, 13 }, 3425158089Smlaier { IWI_RATE_OFDM24, 57, 14 }, 3426158089Smlaier { IWI_RATE_OFDM18, 67, 16 }, 3427158089Smlaier { IWI_RATE_OFDM12, 80, 20 }, 3428158089Smlaier { IWI_RATE_DS11, 100, 25 }, 3429158089Smlaier { IWI_RATE_OFDM9, 133, 34 }, 3430158089Smlaier { IWI_RATE_OFDM6, 160, 40 }, 3431158089Smlaier { IWI_RATE_DS5, 200, 50 }, 3432158089Smlaier { 6, 240, 58 }, /* XXX 3Mb/s if it existed */ 3433158089Smlaier { IWI_RATE_DS2, 267, 66 }, 3434158089Smlaier { IWI_RATE_DS1, 400, 100 }, 3435158089Smlaier { 0, 500, 130 }, /* unknown rate/polling */ 3436158089Smlaier }; 3437158089Smlaier uint32_t txrate; 3438158089Smlaier int j = 0; /* XXX silence compiler */ 3439158089Smlaier 3440158089Smlaier sc->sc_ledevent = ticks; /* time of last event */ 3441158089Smlaier if (sc->sc_blinking) /* don't interrupt active blink */ 3442158089Smlaier return; 3443158089Smlaier switch (event) { 3444158089Smlaier case IWI_LED_POLL: 3445158089Smlaier j = N(blinkrates)-1; 3446158089Smlaier break; 3447158089Smlaier case IWI_LED_TX: 3448158089Smlaier /* read current transmission rate from adapter */ 3449158089Smlaier txrate = CSR_READ_4(sc, IWI_CSR_CURRENT_TX_RATE); 3450158089Smlaier if (blinkrates[sc->sc_txrix].rate != txrate) { 3451158089Smlaier for (j = 0; j < N(blinkrates)-1; j++) 3452158089Smlaier if (blinkrates[j].rate == txrate) 3453158089Smlaier break; 3454158089Smlaier sc->sc_txrix = j; 3455158089Smlaier } else 3456158089Smlaier j = sc->sc_txrix; 3457158089Smlaier break; 3458158089Smlaier case IWI_LED_RX: 3459158089Smlaier if (blinkrates[sc->sc_rxrix].rate != sc->sc_rxrate) { 3460158089Smlaier for (j = 0; j < N(blinkrates)-1; j++) 3461158089Smlaier if (blinkrates[j].rate == sc->sc_rxrate) 3462158089Smlaier break; 3463158089Smlaier sc->sc_rxrix = j; 3464158089Smlaier } else 3465158089Smlaier j = sc->sc_rxrix; 3466158089Smlaier break; 3467158089Smlaier } 3468158089Smlaier /* XXX beware of overflow */ 3469158089Smlaier iwi_led_blink(sc, (blinkrates[j].timeOn * hz) / 1000, 3470158089Smlaier (blinkrates[j].timeOff * hz) / 1000); 3471158089Smlaier#undef N 3472158089Smlaier} 3473158089Smlaier 3474158089Smlaierstatic int 3475158089Smlaieriwi_sysctl_softled(SYSCTL_HANDLER_ARGS) 3476158089Smlaier{ 3477158089Smlaier struct iwi_softc *sc = arg1; 3478158089Smlaier int softled = sc->sc_softled; 3479158089Smlaier int error; 3480158089Smlaier 3481158089Smlaier error = sysctl_handle_int(oidp, &softled, 0, req); 3482158089Smlaier if (error || !req->newptr) 3483158089Smlaier return error; 3484158089Smlaier softled = (softled != 0); 3485158089Smlaier if (softled != sc->sc_softled) { 3486158089Smlaier if (softled) { 3487158089Smlaier uint32_t v = iwi_read_event(sc); 3488158089Smlaier v &= ~sc->sc_ledpin; 3489158089Smlaier iwi_write_event(sc, iwi_toggle_event(v)); 3490158089Smlaier } 3491158089Smlaier sc->sc_softled = softled; 3492158089Smlaier } 3493158089Smlaier return 0; 3494158089Smlaier} 3495158089Smlaier 3496158089Smlaierstatic void 3497158089Smlaieriwi_ledattach(struct iwi_softc *sc) 3498158089Smlaier{ 3499158089Smlaier struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); 3500158089Smlaier struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); 3501158089Smlaier 3502158089Smlaier sc->sc_blinking = 0; 3503158089Smlaier sc->sc_ledstate = 1; 3504158089Smlaier sc->sc_ledidle = (2700*hz)/1000; /* 2.7sec */ 3505158089Smlaier callout_init_mtx(&sc->sc_ledtimer, &sc->sc_mtx, 0); 3506158089Smlaier 3507158089Smlaier SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 3508158089Smlaier "softled", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 3509158089Smlaier iwi_sysctl_softled, "I", "enable/disable software LED support"); 3510217323Smdf SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 3511158089Smlaier "ledpin", CTLFLAG_RW, &sc->sc_ledpin, 0, 3512158089Smlaier "pin setting to turn activity LED on"); 3513217323Smdf SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 3514158089Smlaier "ledidle", CTLFLAG_RW, &sc->sc_ledidle, 0, 3515158089Smlaier "idle time for inactivity LED (ticks)"); 3516158089Smlaier /* XXX for debugging */ 3517217323Smdf SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 3518158089Smlaier "nictype", CTLFLAG_RD, &sc->sc_nictype, 0, 3519158089Smlaier "NIC type from EEPROM"); 3520158089Smlaier 3521158089Smlaier sc->sc_ledpin = IWI_RST_LED_ACTIVITY; 3522158089Smlaier sc->sc_softled = 1; 3523158089Smlaier 3524158089Smlaier sc->sc_nictype = (iwi_read_prom_word(sc, IWI_EEPROM_NIC) >> 8) & 0xff; 3525158089Smlaier if (sc->sc_nictype == 1) { 3526158089Smlaier /* 3527158089Smlaier * NB: led's are reversed. 3528158089Smlaier */ 3529158089Smlaier sc->sc_ledpin = IWI_RST_LED_ASSOCIATED; 3530158089Smlaier } 3531158089Smlaier} 3532170530Ssam 3533170530Ssamstatic void 3534170530Ssamiwi_scan_start(struct ieee80211com *ic) 3535170530Ssam{ 3536191746Sthompsa /* ignore */ 3537170530Ssam} 3538170530Ssam 3539170530Ssamstatic void 3540170530Ssamiwi_set_channel(struct ieee80211com *ic) 3541170530Ssam{ 3542170530Ssam struct ifnet *ifp = ic->ic_ifp; 3543170530Ssam struct iwi_softc *sc = ifp->if_softc; 3544170530Ssam if (sc->fw_state == IWI_FW_IDLE) 3545170530Ssam iwi_setcurchan(sc, ic->ic_curchan->ic_ieee); 3546170530Ssam} 3547170530Ssam 3548170530Ssamstatic void 3549178354Ssamiwi_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell) 3550170530Ssam{ 3551178354Ssam struct ieee80211vap *vap = ss->ss_vap; 3552178354Ssam struct ifnet *ifp = vap->iv_ic->ic_ifp; 3553170530Ssam struct iwi_softc *sc = ifp->if_softc; 3554191746Sthompsa IWI_LOCK_DECL; 3555170530Ssam 3556191746Sthompsa IWI_LOCK(sc); 3557191746Sthompsa if (iwi_scanchan(sc, maxdwell, 0)) 3558191746Sthompsa ieee80211_cancel_scan(vap); 3559191746Sthompsa IWI_UNLOCK(sc); 3560170530Ssam} 3561170530Ssam 3562170530Ssamstatic void 3563178354Ssamiwi_scan_mindwell(struct ieee80211_scan_state *ss) 3564170530Ssam{ 3565170530Ssam /* NB: don't try to abort scan; wait for firmware to finish */ 3566170530Ssam} 3567170530Ssam 3568170530Ssamstatic void 3569170530Ssamiwi_scan_end(struct ieee80211com *ic) 3570170530Ssam{ 3571170530Ssam struct ifnet *ifp = ic->ic_ifp; 3572170530Ssam struct iwi_softc *sc = ifp->if_softc; 3573191746Sthompsa IWI_LOCK_DECL; 3574170530Ssam 3575191746Sthompsa IWI_LOCK(sc); 3576191746Sthompsa sc->flags &= ~IWI_FLAG_CHANNEL_SCAN; 3577191746Sthompsa /* NB: make sure we're still scanning */ 3578191746Sthompsa if (sc->fw_state == IWI_FW_SCANNING) 3579191746Sthompsa iwi_cmd(sc, IWI_CMD_ABORT_SCAN, NULL, 0); 3580191746Sthompsa IWI_UNLOCK(sc); 3581170530Ssam} 3582