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: stable/11/sys/dev/iwi/if_iwi.c 347511 2019-05-12 12:30:45Z avos $"); 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> 67257176Sglebius#include <net/if_var.h> 68145247Sdamien#include <net/if_arp.h> 69145247Sdamien#include <net/ethernet.h> 70145247Sdamien#include <net/if_dl.h> 71145247Sdamien#include <net/if_media.h> 72145247Sdamien#include <net/if_types.h> 73145247Sdamien 74145247Sdamien#include <net80211/ieee80211_var.h> 75145247Sdamien#include <net80211/ieee80211_radiotap.h> 76178354Ssam#include <net80211/ieee80211_input.h> 77170530Ssam#include <net80211/ieee80211_regdomain.h> 78145247Sdamien 79145247Sdamien#include <netinet/in.h> 80145247Sdamien#include <netinet/in_systm.h> 81145247Sdamien#include <netinet/in_var.h> 82145247Sdamien#include <netinet/ip.h> 83145247Sdamien#include <netinet/if_ether.h> 84145247Sdamien 85158089Smlaier#include <dev/iwi/if_iwireg.h> 86154992Sdamien#include <dev/iwi/if_iwivar.h> 87263920Sadrian#include <dev/iwi/if_iwi_ioctl.h> 88145247Sdamien 89158089Smlaier#define IWI_DEBUG 90145247Sdamien#ifdef IWI_DEBUG 91145247Sdamien#define DPRINTF(x) do { if (iwi_debug > 0) printf x; } while (0) 92145247Sdamien#define DPRINTFN(n, x) do { if (iwi_debug >= (n)) printf x; } while (0) 93145247Sdamienint iwi_debug = 0; 94145247SdamienSYSCTL_INT(_debug, OID_AUTO, iwi, CTLFLAG_RW, &iwi_debug, 0, "iwi debug level"); 95178354Ssam 96178354Ssamstatic const char *iwi_fw_states[] = { 97178354Ssam "IDLE", /* IWI_FW_IDLE */ 98178354Ssam "LOADING", /* IWI_FW_LOADING */ 99178354Ssam "ASSOCIATING", /* IWI_FW_ASSOCIATING */ 100178354Ssam "DISASSOCIATING", /* IWI_FW_DISASSOCIATING */ 101178354Ssam "SCANNING", /* IWI_FW_SCANNING */ 102178354Ssam}; 103145247Sdamien#else 104145247Sdamien#define DPRINTF(x) 105145247Sdamien#define DPRINTFN(n, x) 106145247Sdamien#endif 107145247Sdamien 108145247SdamienMODULE_DEPEND(iwi, pci, 1, 1, 1); 109145247SdamienMODULE_DEPEND(iwi, wlan, 1, 1, 1); 110156571SdamienMODULE_DEPEND(iwi, firmware, 1, 1, 1); 111145247Sdamien 112158089Smlaierenum { 113158089Smlaier IWI_LED_TX, 114158089Smlaier IWI_LED_RX, 115158089Smlaier IWI_LED_POLL, 116158089Smlaier}; 117158089Smlaier 118145247Sdamienstruct iwi_ident { 119145247Sdamien uint16_t vendor; 120145247Sdamien uint16_t device; 121145247Sdamien const char *name; 122145247Sdamien}; 123145247Sdamien 124145247Sdamienstatic const struct iwi_ident iwi_ident_table[] = { 125145247Sdamien { 0x8086, 0x4220, "Intel(R) PRO/Wireless 2200BG" }, 126145247Sdamien { 0x8086, 0x4221, "Intel(R) PRO/Wireless 2225BG" }, 127145247Sdamien { 0x8086, 0x4223, "Intel(R) PRO/Wireless 2915ABG" }, 128145247Sdamien { 0x8086, 0x4224, "Intel(R) PRO/Wireless 2915ABG" }, 129145247Sdamien 130145247Sdamien { 0, 0, NULL } 131145247Sdamien}; 132145247Sdamien 133313921Savosstatic const uint8_t def_chan_5ghz_band1[] = 134313921Savos { 36, 40, 44, 48, 52, 56, 60, 64 }; 135313921Savosstatic const uint8_t def_chan_5ghz_band2[] = 136313921Savos { 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140 }; 137313921Savosstatic const uint8_t def_chan_5ghz_band3[] = 138313921Savos { 149, 153, 157, 161, 165 }; 139313921Savos 140178354Ssamstatic struct ieee80211vap *iwi_vap_create(struct ieee80211com *, 141228621Sbschmidt const char [IFNAMSIZ], int, enum ieee80211_opmode, int, 142228621Sbschmidt const uint8_t [IEEE80211_ADDR_LEN], 143228621Sbschmidt const uint8_t [IEEE80211_ADDR_LEN]); 144178354Ssamstatic void iwi_vap_delete(struct ieee80211vap *); 145145247Sdamienstatic void iwi_dma_map_addr(void *, bus_dma_segment_t *, int, int); 146145247Sdamienstatic int iwi_alloc_cmd_ring(struct iwi_softc *, struct iwi_cmd_ring *, 147145247Sdamien int); 148145247Sdamienstatic void iwi_reset_cmd_ring(struct iwi_softc *, struct iwi_cmd_ring *); 149145247Sdamienstatic void iwi_free_cmd_ring(struct iwi_softc *, struct iwi_cmd_ring *); 150145247Sdamienstatic int iwi_alloc_tx_ring(struct iwi_softc *, struct iwi_tx_ring *, 151149338Sdamien int, bus_addr_t, bus_addr_t); 152145247Sdamienstatic void iwi_reset_tx_ring(struct iwi_softc *, struct iwi_tx_ring *); 153145247Sdamienstatic void iwi_free_tx_ring(struct iwi_softc *, struct iwi_tx_ring *); 154145247Sdamienstatic int iwi_alloc_rx_ring(struct iwi_softc *, struct iwi_rx_ring *, 155145247Sdamien int); 156145247Sdamienstatic void iwi_reset_rx_ring(struct iwi_softc *, struct iwi_rx_ring *); 157145247Sdamienstatic void iwi_free_rx_ring(struct iwi_softc *, struct iwi_rx_ring *); 158179643Ssamstatic struct ieee80211_node *iwi_node_alloc(struct ieee80211vap *, 159179643Ssam const uint8_t [IEEE80211_ADDR_LEN]); 160150341Sdamienstatic void iwi_node_free(struct ieee80211_node *); 161145247Sdamienstatic void iwi_media_status(struct ifnet *, struct ifmediareq *); 162178354Ssamstatic int iwi_newstate(struct ieee80211vap *, enum ieee80211_state, int); 163158089Smlaierstatic void iwi_wme_init(struct iwi_softc *); 164283935Sglebiusstatic int iwi_wme_setparams(struct iwi_softc *); 165149338Sdamienstatic int iwi_wme_update(struct ieee80211com *); 166145247Sdamienstatic uint16_t iwi_read_prom_word(struct iwi_softc *, uint8_t); 167145247Sdamienstatic void iwi_frame_intr(struct iwi_softc *, struct iwi_rx_data *, int, 168145247Sdamien struct iwi_frame *); 169145247Sdamienstatic void iwi_notification_intr(struct iwi_softc *, struct iwi_notif *); 170145247Sdamienstatic void iwi_rx_intr(struct iwi_softc *); 171149338Sdamienstatic void iwi_tx_intr(struct iwi_softc *, struct iwi_tx_ring *); 172145247Sdamienstatic void iwi_intr(void *); 173158089Smlaierstatic int iwi_cmd(struct iwi_softc *, uint8_t, void *, uint8_t); 174158089Smlaierstatic void iwi_write_ibssnode(struct iwi_softc *, const u_int8_t [], int); 175287197Sglebiusstatic int iwi_tx_start(struct iwi_softc *, struct mbuf *, 176150245Sdamien struct ieee80211_node *, int); 177178354Ssamstatic int iwi_raw_xmit(struct ieee80211_node *, struct mbuf *, 178178354Ssam const struct ieee80211_bpf_params *); 179287197Sglebiusstatic void iwi_start(struct iwi_softc *); 180287197Sglebiusstatic int iwi_transmit(struct ieee80211com *, struct mbuf *); 181170530Ssamstatic void iwi_watchdog(void *); 182287197Sglebiusstatic int iwi_ioctl(struct ieee80211com *, u_long, void *); 183287197Sglebiusstatic void iwi_parent(struct ieee80211com *); 184145247Sdamienstatic void iwi_stop_master(struct iwi_softc *); 185145247Sdamienstatic int iwi_reset(struct iwi_softc *); 186158089Smlaierstatic int iwi_load_ucode(struct iwi_softc *, const struct iwi_fw *); 187158089Smlaierstatic int iwi_load_firmware(struct iwi_softc *, const struct iwi_fw *); 188166848Sluigistatic void iwi_release_fw_dma(struct iwi_softc *sc); 189145247Sdamienstatic int iwi_config(struct iwi_softc *); 190178354Ssamstatic int iwi_get_firmware(struct iwi_softc *, enum ieee80211_opmode); 191158089Smlaierstatic void iwi_put_firmware(struct iwi_softc *); 192213729Sbschmidtstatic void iwi_monitor_scan(void *, int); 193170530Ssamstatic int iwi_scanchan(struct iwi_softc *, unsigned long, int); 194170530Ssamstatic void iwi_scan_start(struct ieee80211com *); 195170530Ssamstatic void iwi_scan_end(struct ieee80211com *); 196170530Ssamstatic void iwi_set_channel(struct ieee80211com *); 197178354Ssamstatic void iwi_scan_curchan(struct ieee80211_scan_state *, unsigned long maxdwell); 198178354Ssamstatic void iwi_scan_mindwell(struct ieee80211_scan_state *); 199178354Ssamstatic int iwi_auth_and_assoc(struct iwi_softc *, struct ieee80211vap *); 200191746Sthompsastatic void iwi_disassoc(void *, int); 201158089Smlaierstatic int iwi_disassociate(struct iwi_softc *, int quiet); 202178354Ssamstatic void iwi_init_locked(struct iwi_softc *); 203145247Sdamienstatic void iwi_init(void *); 204178354Ssamstatic int iwi_init_fw_dma(struct iwi_softc *, int); 205178354Ssamstatic void iwi_stop_locked(void *); 206178354Ssamstatic void iwi_stop(struct iwi_softc *); 207158089Smlaierstatic void iwi_restart(void *, int); 208158089Smlaierstatic int iwi_getrfkill(struct iwi_softc *); 209158089Smlaierstatic void iwi_radio_on(void *, int); 210158089Smlaierstatic void iwi_radio_off(void *, int); 211158089Smlaierstatic void iwi_sysctlattach(struct iwi_softc *); 212158089Smlaierstatic void iwi_led_event(struct iwi_softc *, int); 213158089Smlaierstatic void iwi_ledattach(struct iwi_softc *); 214313921Savosstatic void iwi_collect_bands(struct ieee80211com *, uint8_t [], size_t); 215313921Savosstatic void iwi_getradiocaps(struct ieee80211com *, int, int *, 216313921Savos struct ieee80211_channel []); 217145247Sdamien 218145247Sdamienstatic int iwi_probe(device_t); 219145247Sdamienstatic int iwi_attach(device_t); 220145247Sdamienstatic int iwi_detach(device_t); 221145247Sdamienstatic int iwi_shutdown(device_t); 222145247Sdamienstatic int iwi_suspend(device_t); 223145247Sdamienstatic int iwi_resume(device_t); 224145247Sdamien 225145247Sdamienstatic device_method_t iwi_methods[] = { 226145247Sdamien /* Device interface */ 227145247Sdamien DEVMETHOD(device_probe, iwi_probe), 228145247Sdamien DEVMETHOD(device_attach, iwi_attach), 229145247Sdamien DEVMETHOD(device_detach, iwi_detach), 230145247Sdamien DEVMETHOD(device_shutdown, iwi_shutdown), 231145247Sdamien DEVMETHOD(device_suspend, iwi_suspend), 232145247Sdamien DEVMETHOD(device_resume, iwi_resume), 233145247Sdamien 234260063Smarius DEVMETHOD_END 235145247Sdamien}; 236145247Sdamien 237145247Sdamienstatic driver_t iwi_driver = { 238145247Sdamien "iwi", 239145247Sdamien iwi_methods, 240145247Sdamien sizeof (struct iwi_softc) 241145247Sdamien}; 242145247Sdamien 243145247Sdamienstatic devclass_t iwi_devclass; 244145247Sdamien 245260063SmariusDRIVER_MODULE(iwi, pci, iwi_driver, iwi_devclass, NULL, NULL); 246145247Sdamien 247222543SbschmidtMODULE_VERSION(iwi, 1); 248222543Sbschmidt 249158089Smlaierstatic __inline uint8_t 250158089SmlaierMEM_READ_1(struct iwi_softc *sc, uint32_t addr) 251158089Smlaier{ 252158089Smlaier CSR_WRITE_4(sc, IWI_CSR_INDIRECT_ADDR, addr); 253158089Smlaier return CSR_READ_1(sc, IWI_CSR_INDIRECT_DATA); 254158089Smlaier} 255158089Smlaier 256158089Smlaierstatic __inline uint32_t 257158089SmlaierMEM_READ_4(struct iwi_softc *sc, uint32_t addr) 258158089Smlaier{ 259158089Smlaier CSR_WRITE_4(sc, IWI_CSR_INDIRECT_ADDR, addr); 260158089Smlaier return CSR_READ_4(sc, IWI_CSR_INDIRECT_DATA); 261158089Smlaier} 262158089Smlaier 263145247Sdamienstatic int 264145247Sdamieniwi_probe(device_t dev) 265145247Sdamien{ 266145247Sdamien const struct iwi_ident *ident; 267145247Sdamien 268145247Sdamien for (ident = iwi_ident_table; ident->name != NULL; ident++) { 269145247Sdamien if (pci_get_vendor(dev) == ident->vendor && 270145247Sdamien pci_get_device(dev) == ident->device) { 271145247Sdamien device_set_desc(dev, ident->name); 272260063Smarius return (BUS_PROBE_DEFAULT); 273145247Sdamien } 274145247Sdamien } 275145247Sdamien return ENXIO; 276145247Sdamien} 277145247Sdamien 278145247Sdamienstatic int 279145247Sdamieniwi_attach(device_t dev) 280145247Sdamien{ 281145247Sdamien struct iwi_softc *sc = device_get_softc(dev); 282287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 283145247Sdamien uint16_t val; 284178354Ssam int i, error; 285145247Sdamien 286145247Sdamien sc->sc_dev = dev; 287323421Sian sc->sc_ledevent = ticks; 288145247Sdamien 289170530Ssam IWI_LOCK_INIT(sc); 290287197Sglebius mbufq_init(&sc->sc_snd, ifqmaxlen); 291145247Sdamien 292158089Smlaier sc->sc_unr = new_unrhdr(1, IWI_MAX_IBSSNODE-1, &sc->sc_mtx); 293150341Sdamien 294158089Smlaier TASK_INIT(&sc->sc_radiontask, 0, iwi_radio_on, sc); 295158089Smlaier TASK_INIT(&sc->sc_radiofftask, 0, iwi_radio_off, sc); 296170530Ssam TASK_INIT(&sc->sc_restarttask, 0, iwi_restart, sc); 297191746Sthompsa TASK_INIT(&sc->sc_disassoctask, 0, iwi_disassoc, sc); 298213729Sbschmidt TASK_INIT(&sc->sc_monitortask, 0, iwi_monitor_scan, sc); 299191746Sthompsa 300170530Ssam callout_init_mtx(&sc->sc_wdtimer, &sc->sc_mtx, 0); 301178354Ssam callout_init_mtx(&sc->sc_rftimer, &sc->sc_mtx, 0); 302156598Sdamien 303146500Sdamien pci_write_config(dev, 0x41, 0, 1); 304146500Sdamien 305145247Sdamien /* enable bus-mastering */ 306145247Sdamien pci_enable_busmaster(dev); 307145247Sdamien 308260063Smarius i = PCIR_BAR(0); 309260063Smarius sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &i, RF_ACTIVE); 310145247Sdamien if (sc->mem == NULL) { 311145247Sdamien device_printf(dev, "could not allocate memory resource\n"); 312145247Sdamien goto fail; 313145247Sdamien } 314145247Sdamien 315145247Sdamien sc->sc_st = rman_get_bustag(sc->mem); 316145247Sdamien sc->sc_sh = rman_get_bushandle(sc->mem); 317145247Sdamien 318260063Smarius i = 0; 319260063Smarius sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &i, 320145247Sdamien RF_ACTIVE | RF_SHAREABLE); 321145247Sdamien if (sc->irq == NULL) { 322145247Sdamien device_printf(dev, "could not allocate interrupt resource\n"); 323145247Sdamien goto fail; 324145247Sdamien } 325145247Sdamien 326145247Sdamien if (iwi_reset(sc) != 0) { 327145247Sdamien device_printf(dev, "could not reset adapter\n"); 328145247Sdamien goto fail; 329145247Sdamien } 330145247Sdamien 331145247Sdamien /* 332145247Sdamien * Allocate rings. 333145247Sdamien */ 334145247Sdamien if (iwi_alloc_cmd_ring(sc, &sc->cmdq, IWI_CMD_RING_COUNT) != 0) { 335145247Sdamien device_printf(dev, "could not allocate Cmd ring\n"); 336145247Sdamien goto fail; 337145247Sdamien } 338145247Sdamien 339166848Sluigi for (i = 0; i < 4; i++) { 340166848Sluigi error = iwi_alloc_tx_ring(sc, &sc->txq[i], IWI_TX_RING_COUNT, 341166848Sluigi IWI_CSR_TX1_RIDX + i * 4, 342166848Sluigi IWI_CSR_TX1_WIDX + i * 4); 343166848Sluigi if (error != 0) { 344166848Sluigi device_printf(dev, "could not allocate Tx ring %d\n", 345166848Sluigi i+i); 346166848Sluigi goto fail; 347166848Sluigi } 348145247Sdamien } 349145247Sdamien 350145247Sdamien if (iwi_alloc_rx_ring(sc, &sc->rxq, IWI_RX_RING_COUNT) != 0) { 351145247Sdamien device_printf(dev, "could not allocate Rx ring\n"); 352145247Sdamien goto fail; 353145247Sdamien } 354145247Sdamien 355158089Smlaier iwi_wme_init(sc); 356158089Smlaier 357283537Sglebius ic->ic_softc = sc; 358283527Sglebius ic->ic_name = device_get_nameunit(dev); 359178354Ssam ic->ic_opmode = IEEE80211_M_STA; 360145247Sdamien ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ 361145247Sdamien 362145247Sdamien /* set device capabilities */ 363149338Sdamien ic->ic_caps = 364178957Ssam IEEE80211_C_STA /* station mode supported */ 365178957Ssam | IEEE80211_C_IBSS /* IBSS mode supported */ 366170530Ssam | IEEE80211_C_MONITOR /* monitor mode supported */ 367170530Ssam | IEEE80211_C_PMGT /* power save supported */ 368170530Ssam | IEEE80211_C_SHPREAMBLE /* short preamble supported */ 369170530Ssam | IEEE80211_C_WPA /* 802.11i */ 370170530Ssam | IEEE80211_C_WME /* 802.11e */ 371178354Ssam#if 0 372170530Ssam | IEEE80211_C_BGSCAN /* capable of bg scanning */ 373178354Ssam#endif 374170530Ssam ; 375145247Sdamien 376145247Sdamien /* read MAC address from EEPROM */ 377145247Sdamien val = iwi_read_prom_word(sc, IWI_EEPROM_MAC + 0); 378287197Sglebius ic->ic_macaddr[0] = val & 0xff; 379287197Sglebius ic->ic_macaddr[1] = val >> 8; 380145247Sdamien val = iwi_read_prom_word(sc, IWI_EEPROM_MAC + 1); 381287197Sglebius ic->ic_macaddr[2] = val & 0xff; 382287197Sglebius ic->ic_macaddr[3] = val >> 8; 383145247Sdamien val = iwi_read_prom_word(sc, IWI_EEPROM_MAC + 2); 384287197Sglebius ic->ic_macaddr[4] = val & 0xff; 385287197Sglebius ic->ic_macaddr[5] = val >> 8; 386293339Savos 387313921Savos iwi_getradiocaps(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans, 388313921Savos ic->ic_channels); 389145247Sdamien 390287197Sglebius ieee80211_ifattach(ic); 391150341Sdamien /* override default methods */ 392150341Sdamien ic->ic_node_alloc = iwi_node_alloc; 393150341Sdamien sc->sc_node_free = ic->ic_node_free; 394150341Sdamien ic->ic_node_free = iwi_node_free; 395178354Ssam ic->ic_raw_xmit = iwi_raw_xmit; 396170530Ssam ic->ic_scan_start = iwi_scan_start; 397170530Ssam ic->ic_scan_end = iwi_scan_end; 398170530Ssam ic->ic_set_channel = iwi_set_channel; 399170530Ssam ic->ic_scan_curchan = iwi_scan_curchan; 400170530Ssam ic->ic_scan_mindwell = iwi_scan_mindwell; 401178354Ssam ic->ic_wme.wme_update = iwi_wme_update; 402170530Ssam 403178354Ssam ic->ic_vap_create = iwi_vap_create; 404178354Ssam ic->ic_vap_delete = iwi_vap_delete; 405287197Sglebius ic->ic_ioctl = iwi_ioctl; 406287197Sglebius ic->ic_transmit = iwi_transmit; 407287197Sglebius ic->ic_parent = iwi_parent; 408313921Savos ic->ic_getradiocaps = iwi_getradiocaps; 409145247Sdamien 410192468Ssam ieee80211_radiotap_attach(ic, 411192468Ssam &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), 412192468Ssam IWI_TX_RADIOTAP_PRESENT, 413192468Ssam &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), 414192468Ssam IWI_RX_RADIOTAP_PRESENT); 415145247Sdamien 416158089Smlaier iwi_sysctlattach(sc); 417158089Smlaier iwi_ledattach(sc); 418145247Sdamien 419145247Sdamien /* 420145247Sdamien * Hook our interrupt after all initialization is complete. 421145247Sdamien */ 422145247Sdamien error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET | INTR_MPSAFE, 423166901Spiso NULL, iwi_intr, sc, &sc->sc_ih); 424145247Sdamien if (error != 0) { 425145247Sdamien device_printf(dev, "could not set up interrupt\n"); 426145247Sdamien goto fail; 427145247Sdamien } 428145247Sdamien 429145247Sdamien if (bootverbose) 430145247Sdamien ieee80211_announce(ic); 431145247Sdamien 432145247Sdamien return 0; 433178354Ssamfail: 434178354Ssam /* XXX fix */ 435178354Ssam iwi_detach(dev); 436145247Sdamien return ENXIO; 437145247Sdamien} 438145247Sdamien 439145247Sdamienstatic int 440145247Sdamieniwi_detach(device_t dev) 441145247Sdamien{ 442145247Sdamien struct iwi_softc *sc = device_get_softc(dev); 443287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 444145247Sdamien 445260063Smarius bus_teardown_intr(dev, sc->irq, sc->sc_ih); 446260063Smarius 447178354Ssam /* NB: do early to drain any pending tasks */ 448191746Sthompsa ieee80211_draintask(ic, &sc->sc_radiontask); 449191746Sthompsa ieee80211_draintask(ic, &sc->sc_radiofftask); 450191746Sthompsa ieee80211_draintask(ic, &sc->sc_restarttask); 451191746Sthompsa ieee80211_draintask(ic, &sc->sc_disassoctask); 452213729Sbschmidt ieee80211_draintask(ic, &sc->sc_monitortask); 453178354Ssam 454191912Sthompsa iwi_stop(sc); 455191912Sthompsa 456191912Sthompsa ieee80211_ifdetach(ic); 457191912Sthompsa 458164069Sjhb iwi_put_firmware(sc); 459166848Sluigi iwi_release_fw_dma(sc); 460145247Sdamien 461145247Sdamien iwi_free_cmd_ring(sc, &sc->cmdq); 462149338Sdamien iwi_free_tx_ring(sc, &sc->txq[0]); 463149338Sdamien iwi_free_tx_ring(sc, &sc->txq[1]); 464149338Sdamien iwi_free_tx_ring(sc, &sc->txq[2]); 465149338Sdamien iwi_free_tx_ring(sc, &sc->txq[3]); 466145247Sdamien iwi_free_rx_ring(sc, &sc->rxq); 467145247Sdamien 468260063Smarius bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(sc->irq), sc->irq); 469145247Sdamien 470260063Smarius bus_release_resource(dev, SYS_RES_MEMORY, rman_get_rid(sc->mem), 471260063Smarius sc->mem); 472145247Sdamien 473178354Ssam delete_unrhdr(sc->sc_unr); 474287197Sglebius mbufq_drain(&sc->sc_snd); 475150330Sdamien 476170530Ssam IWI_LOCK_DESTROY(sc); 477145247Sdamien 478145247Sdamien return 0; 479145247Sdamien} 480145247Sdamien 481178354Ssamstatic struct ieee80211vap * 482228621Sbschmidtiwi_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, 483228621Sbschmidt enum ieee80211_opmode opmode, int flags, 484228621Sbschmidt const uint8_t bssid[IEEE80211_ADDR_LEN], 485228621Sbschmidt const uint8_t mac[IEEE80211_ADDR_LEN]) 486178354Ssam{ 487287197Sglebius struct iwi_softc *sc = ic->ic_softc; 488178354Ssam struct iwi_vap *ivp; 489178354Ssam struct ieee80211vap *vap; 490178354Ssam int i; 491178354Ssam 492178354Ssam if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ 493178354Ssam return NULL; 494178354Ssam /* 495178354Ssam * Get firmware image (and possibly dma memory) on mode change. 496178354Ssam */ 497178354Ssam if (iwi_get_firmware(sc, opmode)) 498178354Ssam return NULL; 499178354Ssam /* allocate DMA memory for mapping firmware image */ 500178354Ssam i = sc->fw_fw.size; 501178354Ssam if (sc->fw_boot.size > i) 502178354Ssam i = sc->fw_boot.size; 503178354Ssam /* XXX do we dma the ucode as well ? */ 504178354Ssam if (sc->fw_uc.size > i) 505178354Ssam i = sc->fw_uc.size; 506178354Ssam if (iwi_init_fw_dma(sc, i)) 507178354Ssam return NULL; 508178354Ssam 509287197Sglebius ivp = malloc(sizeof(struct iwi_vap), M_80211_VAP, M_WAITOK | M_ZERO); 510178354Ssam vap = &ivp->iwi_vap; 511287197Sglebius ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid); 512178354Ssam /* override the default, the setting comes from the linux driver */ 513178354Ssam vap->iv_bmissthreshold = 24; 514178354Ssam /* override with driver methods */ 515178354Ssam ivp->iwi_newstate = vap->iv_newstate; 516178354Ssam vap->iv_newstate = iwi_newstate; 517178354Ssam 518178354Ssam /* complete setup */ 519287197Sglebius ieee80211_vap_attach(vap, ieee80211_media_change, iwi_media_status, 520287197Sglebius mac); 521178354Ssam ic->ic_opmode = opmode; 522178354Ssam return vap; 523178354Ssam} 524178354Ssam 525145247Sdamienstatic void 526178354Ssamiwi_vap_delete(struct ieee80211vap *vap) 527178354Ssam{ 528178354Ssam struct iwi_vap *ivp = IWI_VAP(vap); 529178354Ssam 530178354Ssam ieee80211_vap_detach(vap); 531178354Ssam free(ivp, M_80211_VAP); 532178354Ssam} 533178354Ssam 534178354Ssamstatic void 535145247Sdamieniwi_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nseg, int error) 536145247Sdamien{ 537145247Sdamien if (error != 0) 538145247Sdamien return; 539145247Sdamien 540145247Sdamien KASSERT(nseg == 1, ("too many DMA segments, %d should be 1", nseg)); 541145247Sdamien 542145247Sdamien *(bus_addr_t *)arg = segs[0].ds_addr; 543145247Sdamien} 544145247Sdamien 545145247Sdamienstatic int 546145247Sdamieniwi_alloc_cmd_ring(struct iwi_softc *sc, struct iwi_cmd_ring *ring, int count) 547145247Sdamien{ 548145247Sdamien int error; 549145247Sdamien 550145247Sdamien ring->count = count; 551145247Sdamien ring->queued = 0; 552145247Sdamien ring->cur = ring->next = 0; 553145247Sdamien 554166416Skevlo error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 4, 0, 555166416Skevlo BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, 556166416Skevlo count * IWI_CMD_DESC_SIZE, 1, count * IWI_CMD_DESC_SIZE, 0, 557166416Skevlo NULL, NULL, &ring->desc_dmat); 558145247Sdamien if (error != 0) { 559145247Sdamien device_printf(sc->sc_dev, "could not create desc DMA tag\n"); 560145247Sdamien goto fail; 561145247Sdamien } 562145247Sdamien 563145247Sdamien error = bus_dmamem_alloc(ring->desc_dmat, (void **)&ring->desc, 564145247Sdamien BUS_DMA_NOWAIT | BUS_DMA_ZERO, &ring->desc_map); 565145247Sdamien if (error != 0) { 566145247Sdamien device_printf(sc->sc_dev, "could not allocate DMA memory\n"); 567145247Sdamien goto fail; 568145247Sdamien } 569145247Sdamien 570145247Sdamien error = bus_dmamap_load(ring->desc_dmat, ring->desc_map, ring->desc, 571145247Sdamien count * IWI_CMD_DESC_SIZE, iwi_dma_map_addr, &ring->physaddr, 0); 572145247Sdamien if (error != 0) { 573145247Sdamien device_printf(sc->sc_dev, "could not load desc DMA map\n"); 574145247Sdamien goto fail; 575145247Sdamien } 576145247Sdamien 577145247Sdamien return 0; 578145247Sdamien 579145247Sdamienfail: iwi_free_cmd_ring(sc, ring); 580145247Sdamien return error; 581145247Sdamien} 582145247Sdamien 583145247Sdamienstatic void 584145247Sdamieniwi_reset_cmd_ring(struct iwi_softc *sc, struct iwi_cmd_ring *ring) 585145247Sdamien{ 586145247Sdamien ring->queued = 0; 587145247Sdamien ring->cur = ring->next = 0; 588145247Sdamien} 589145247Sdamien 590145247Sdamienstatic void 591145247Sdamieniwi_free_cmd_ring(struct iwi_softc *sc, struct iwi_cmd_ring *ring) 592145247Sdamien{ 593145247Sdamien if (ring->desc != NULL) { 594145247Sdamien bus_dmamap_sync(ring->desc_dmat, ring->desc_map, 595145247Sdamien BUS_DMASYNC_POSTWRITE); 596145247Sdamien bus_dmamap_unload(ring->desc_dmat, ring->desc_map); 597145247Sdamien bus_dmamem_free(ring->desc_dmat, ring->desc, ring->desc_map); 598145247Sdamien } 599145247Sdamien 600145247Sdamien if (ring->desc_dmat != NULL) 601145247Sdamien bus_dma_tag_destroy(ring->desc_dmat); 602145247Sdamien} 603145247Sdamien 604145247Sdamienstatic int 605149338Sdamieniwi_alloc_tx_ring(struct iwi_softc *sc, struct iwi_tx_ring *ring, int count, 606149338Sdamien bus_addr_t csr_ridx, bus_addr_t csr_widx) 607145247Sdamien{ 608145247Sdamien int i, error; 609145247Sdamien 610145247Sdamien ring->count = count; 611145247Sdamien ring->queued = 0; 612145247Sdamien ring->cur = ring->next = 0; 613149338Sdamien ring->csr_ridx = csr_ridx; 614149338Sdamien ring->csr_widx = csr_widx; 615145247Sdamien 616166416Skevlo error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 4, 0, 617166416Skevlo BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, 618166416Skevlo count * IWI_TX_DESC_SIZE, 1, count * IWI_TX_DESC_SIZE, 0, NULL, 619166416Skevlo NULL, &ring->desc_dmat); 620145247Sdamien if (error != 0) { 621145247Sdamien device_printf(sc->sc_dev, "could not create desc DMA tag\n"); 622145247Sdamien goto fail; 623145247Sdamien } 624145247Sdamien 625145247Sdamien error = bus_dmamem_alloc(ring->desc_dmat, (void **)&ring->desc, 626145247Sdamien BUS_DMA_NOWAIT | BUS_DMA_ZERO, &ring->desc_map); 627145247Sdamien if (error != 0) { 628145247Sdamien device_printf(sc->sc_dev, "could not allocate DMA memory\n"); 629145247Sdamien goto fail; 630145247Sdamien } 631145247Sdamien 632145247Sdamien error = bus_dmamap_load(ring->desc_dmat, ring->desc_map, ring->desc, 633145247Sdamien count * IWI_TX_DESC_SIZE, iwi_dma_map_addr, &ring->physaddr, 0); 634145247Sdamien if (error != 0) { 635145247Sdamien device_printf(sc->sc_dev, "could not load desc DMA map\n"); 636145247Sdamien goto fail; 637145247Sdamien } 638145247Sdamien 639145247Sdamien ring->data = malloc(count * sizeof (struct iwi_tx_data), M_DEVBUF, 640145247Sdamien M_NOWAIT | M_ZERO); 641145247Sdamien if (ring->data == NULL) { 642145247Sdamien device_printf(sc->sc_dev, "could not allocate soft data\n"); 643145247Sdamien error = ENOMEM; 644145247Sdamien goto fail; 645145247Sdamien } 646145247Sdamien 647166416Skevlo error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, 648166416Skevlo BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, 649166416Skevlo IWI_MAX_NSEG, MCLBYTES, 0, NULL, NULL, &ring->data_dmat); 650145247Sdamien if (error != 0) { 651145247Sdamien device_printf(sc->sc_dev, "could not create data DMA tag\n"); 652145247Sdamien goto fail; 653145247Sdamien } 654145247Sdamien 655145247Sdamien for (i = 0; i < count; i++) { 656145247Sdamien error = bus_dmamap_create(ring->data_dmat, 0, 657145247Sdamien &ring->data[i].map); 658145247Sdamien if (error != 0) { 659145247Sdamien device_printf(sc->sc_dev, "could not create DMA map\n"); 660145247Sdamien goto fail; 661145247Sdamien } 662145247Sdamien } 663145247Sdamien 664145247Sdamien return 0; 665145247Sdamien 666145247Sdamienfail: iwi_free_tx_ring(sc, ring); 667145247Sdamien return error; 668145247Sdamien} 669145247Sdamien 670145247Sdamienstatic void 671145247Sdamieniwi_reset_tx_ring(struct iwi_softc *sc, struct iwi_tx_ring *ring) 672145247Sdamien{ 673145247Sdamien struct iwi_tx_data *data; 674145247Sdamien int i; 675145247Sdamien 676145247Sdamien for (i = 0; i < ring->count; i++) { 677145247Sdamien data = &ring->data[i]; 678145247Sdamien 679145247Sdamien if (data->m != NULL) { 680145247Sdamien bus_dmamap_sync(ring->data_dmat, data->map, 681145247Sdamien BUS_DMASYNC_POSTWRITE); 682145247Sdamien bus_dmamap_unload(ring->data_dmat, data->map); 683145247Sdamien m_freem(data->m); 684145247Sdamien data->m = NULL; 685145247Sdamien } 686145247Sdamien 687145247Sdamien if (data->ni != NULL) { 688145247Sdamien ieee80211_free_node(data->ni); 689145247Sdamien data->ni = NULL; 690145247Sdamien } 691145247Sdamien } 692145247Sdamien 693145247Sdamien ring->queued = 0; 694145247Sdamien ring->cur = ring->next = 0; 695145247Sdamien} 696145247Sdamien 697145247Sdamienstatic void 698145247Sdamieniwi_free_tx_ring(struct iwi_softc *sc, struct iwi_tx_ring *ring) 699145247Sdamien{ 700145247Sdamien struct iwi_tx_data *data; 701145247Sdamien int i; 702145247Sdamien 703145247Sdamien if (ring->desc != NULL) { 704145247Sdamien bus_dmamap_sync(ring->desc_dmat, ring->desc_map, 705145247Sdamien BUS_DMASYNC_POSTWRITE); 706145247Sdamien bus_dmamap_unload(ring->desc_dmat, ring->desc_map); 707145247Sdamien bus_dmamem_free(ring->desc_dmat, ring->desc, ring->desc_map); 708145247Sdamien } 709145247Sdamien 710145247Sdamien if (ring->desc_dmat != NULL) 711145247Sdamien bus_dma_tag_destroy(ring->desc_dmat); 712145247Sdamien 713145247Sdamien if (ring->data != NULL) { 714145247Sdamien for (i = 0; i < ring->count; i++) { 715145247Sdamien data = &ring->data[i]; 716145247Sdamien 717145247Sdamien if (data->m != NULL) { 718145247Sdamien bus_dmamap_sync(ring->data_dmat, data->map, 719145247Sdamien BUS_DMASYNC_POSTWRITE); 720145247Sdamien bus_dmamap_unload(ring->data_dmat, data->map); 721145247Sdamien m_freem(data->m); 722145247Sdamien } 723145247Sdamien 724145247Sdamien if (data->ni != NULL) 725145247Sdamien ieee80211_free_node(data->ni); 726145247Sdamien 727145247Sdamien if (data->map != NULL) 728145247Sdamien bus_dmamap_destroy(ring->data_dmat, data->map); 729145247Sdamien } 730145247Sdamien 731145247Sdamien free(ring->data, M_DEVBUF); 732145247Sdamien } 733145247Sdamien 734145247Sdamien if (ring->data_dmat != NULL) 735145247Sdamien bus_dma_tag_destroy(ring->data_dmat); 736145247Sdamien} 737145247Sdamien 738145247Sdamienstatic int 739145247Sdamieniwi_alloc_rx_ring(struct iwi_softc *sc, struct iwi_rx_ring *ring, int count) 740145247Sdamien{ 741145247Sdamien struct iwi_rx_data *data; 742145247Sdamien int i, error; 743145247Sdamien 744145247Sdamien ring->count = count; 745145247Sdamien ring->cur = 0; 746145247Sdamien 747145247Sdamien ring->data = malloc(count * sizeof (struct iwi_rx_data), M_DEVBUF, 748145247Sdamien M_NOWAIT | M_ZERO); 749145247Sdamien if (ring->data == NULL) { 750145247Sdamien device_printf(sc->sc_dev, "could not allocate soft data\n"); 751145247Sdamien error = ENOMEM; 752145247Sdamien goto fail; 753145247Sdamien } 754145247Sdamien 755166416Skevlo error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, 756166416Skevlo BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, 757166416Skevlo 1, MCLBYTES, 0, NULL, NULL, &ring->data_dmat); 758145247Sdamien if (error != 0) { 759145247Sdamien device_printf(sc->sc_dev, "could not create data DMA tag\n"); 760145247Sdamien goto fail; 761145247Sdamien } 762145247Sdamien 763145247Sdamien for (i = 0; i < count; i++) { 764145247Sdamien data = &ring->data[i]; 765145247Sdamien 766145247Sdamien error = bus_dmamap_create(ring->data_dmat, 0, &data->map); 767145247Sdamien if (error != 0) { 768145247Sdamien device_printf(sc->sc_dev, "could not create DMA map\n"); 769145247Sdamien goto fail; 770145247Sdamien } 771145247Sdamien 772243857Sglebius data->m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); 773145247Sdamien if (data->m == NULL) { 774145247Sdamien device_printf(sc->sc_dev, 775145247Sdamien "could not allocate rx mbuf\n"); 776145247Sdamien error = ENOMEM; 777145247Sdamien goto fail; 778145247Sdamien } 779145247Sdamien 780145247Sdamien error = bus_dmamap_load(ring->data_dmat, data->map, 781145247Sdamien mtod(data->m, void *), MCLBYTES, iwi_dma_map_addr, 782145247Sdamien &data->physaddr, 0); 783145247Sdamien if (error != 0) { 784145247Sdamien device_printf(sc->sc_dev, 785145247Sdamien "could not load rx buf DMA map"); 786145247Sdamien goto fail; 787145247Sdamien } 788145247Sdamien 789145247Sdamien data->reg = IWI_CSR_RX_BASE + i * 4; 790145247Sdamien } 791145247Sdamien 792145247Sdamien return 0; 793145247Sdamien 794145247Sdamienfail: iwi_free_rx_ring(sc, ring); 795145247Sdamien return error; 796145247Sdamien} 797145247Sdamien 798145247Sdamienstatic void 799145247Sdamieniwi_reset_rx_ring(struct iwi_softc *sc, struct iwi_rx_ring *ring) 800145247Sdamien{ 801145247Sdamien ring->cur = 0; 802145247Sdamien} 803145247Sdamien 804145247Sdamienstatic void 805145247Sdamieniwi_free_rx_ring(struct iwi_softc *sc, struct iwi_rx_ring *ring) 806145247Sdamien{ 807145247Sdamien struct iwi_rx_data *data; 808145247Sdamien int i; 809145247Sdamien 810145247Sdamien if (ring->data != NULL) { 811145247Sdamien for (i = 0; i < ring->count; i++) { 812145247Sdamien data = &ring->data[i]; 813145247Sdamien 814145247Sdamien if (data->m != NULL) { 815145247Sdamien bus_dmamap_sync(ring->data_dmat, data->map, 816145247Sdamien BUS_DMASYNC_POSTREAD); 817145247Sdamien bus_dmamap_unload(ring->data_dmat, data->map); 818145247Sdamien m_freem(data->m); 819145247Sdamien } 820145247Sdamien 821145247Sdamien if (data->map != NULL) 822145247Sdamien bus_dmamap_destroy(ring->data_dmat, data->map); 823145247Sdamien } 824145247Sdamien 825145247Sdamien free(ring->data, M_DEVBUF); 826145247Sdamien } 827145247Sdamien 828145247Sdamien if (ring->data_dmat != NULL) 829145247Sdamien bus_dma_tag_destroy(ring->data_dmat); 830145247Sdamien} 831145247Sdamien 832145247Sdamienstatic int 833145247Sdamieniwi_shutdown(device_t dev) 834145247Sdamien{ 835145247Sdamien struct iwi_softc *sc = device_get_softc(dev); 836145247Sdamien 837145247Sdamien iwi_stop(sc); 838158089Smlaier iwi_put_firmware(sc); /* ??? XXX */ 839145247Sdamien 840145247Sdamien return 0; 841145247Sdamien} 842145247Sdamien 843145247Sdamienstatic int 844145247Sdamieniwi_suspend(device_t dev) 845145247Sdamien{ 846145247Sdamien struct iwi_softc *sc = device_get_softc(dev); 847287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 848145247Sdamien 849233387Sbschmidt ieee80211_suspend_all(ic); 850145247Sdamien return 0; 851145247Sdamien} 852145247Sdamien 853145247Sdamienstatic int 854145247Sdamieniwi_resume(device_t dev) 855145247Sdamien{ 856145247Sdamien struct iwi_softc *sc = device_get_softc(dev); 857287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 858145247Sdamien 859146500Sdamien pci_write_config(dev, 0x41, 0, 1); 860146500Sdamien 861233387Sbschmidt ieee80211_resume_all(ic); 862145247Sdamien return 0; 863145247Sdamien} 864145247Sdamien 865150341Sdamienstatic struct ieee80211_node * 866179643Ssamiwi_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) 867150341Sdamien{ 868150341Sdamien struct iwi_node *in; 869150341Sdamien 870150341Sdamien in = malloc(sizeof (struct iwi_node), M_80211_NODE, M_NOWAIT | M_ZERO); 871150341Sdamien if (in == NULL) 872150341Sdamien return NULL; 873179643Ssam /* XXX assign sta table entry for adhoc */ 874150341Sdamien in->in_station = -1; 875150341Sdamien 876150341Sdamien return &in->in_node; 877150341Sdamien} 878150341Sdamien 879150341Sdamienstatic void 880150341Sdamieniwi_node_free(struct ieee80211_node *ni) 881150341Sdamien{ 882150341Sdamien struct ieee80211com *ic = ni->ni_ic; 883287197Sglebius struct iwi_softc *sc = ic->ic_softc; 884150341Sdamien struct iwi_node *in = (struct iwi_node *)ni; 885150341Sdamien 886158089Smlaier if (in->in_station != -1) { 887158089Smlaier DPRINTF(("%s mac %6D station %u\n", __func__, 888158089Smlaier ni->ni_macaddr, ":", in->in_station)); 889150341Sdamien free_unr(sc->sc_unr, in->in_station); 890158089Smlaier } 891150341Sdamien 892150341Sdamien sc->sc_node_free(ni); 893150341Sdamien} 894150341Sdamien 895158089Smlaier/* 896158089Smlaier * Convert h/w rate code to IEEE rate code. 897158089Smlaier */ 898158089Smlaierstatic int 899158089Smlaieriwi_cvtrate(int iwirate) 900158089Smlaier{ 901158089Smlaier switch (iwirate) { 902158089Smlaier case IWI_RATE_DS1: return 2; 903158089Smlaier case IWI_RATE_DS2: return 4; 904158089Smlaier case IWI_RATE_DS5: return 11; 905158089Smlaier case IWI_RATE_DS11: return 22; 906158089Smlaier case IWI_RATE_OFDM6: return 12; 907158089Smlaier case IWI_RATE_OFDM9: return 18; 908158089Smlaier case IWI_RATE_OFDM12: return 24; 909158089Smlaier case IWI_RATE_OFDM18: return 36; 910158089Smlaier case IWI_RATE_OFDM24: return 48; 911158089Smlaier case IWI_RATE_OFDM36: return 72; 912158089Smlaier case IWI_RATE_OFDM48: return 96; 913158089Smlaier case IWI_RATE_OFDM54: return 108; 914158089Smlaier } 915145247Sdamien return 0; 916145247Sdamien} 917145247Sdamien 918145247Sdamien/* 919150341Sdamien * The firmware automatically adapts the transmit speed. We report its current 920150341Sdamien * value here. 921145247Sdamien */ 922145247Sdamienstatic void 923145247Sdamieniwi_media_status(struct ifnet *ifp, struct ifmediareq *imr) 924145247Sdamien{ 925178354Ssam struct ieee80211vap *vap = ifp->if_softc; 926178354Ssam struct ieee80211com *ic = vap->iv_ic; 927287197Sglebius struct iwi_softc *sc = ic->ic_softc; 928258612Sadrian struct ieee80211_node *ni; 929145247Sdamien 930145247Sdamien /* read current transmission rate from adapter */ 931258612Sadrian ni = ieee80211_ref_node(vap->iv_bss); 932258612Sadrian ni->ni_txrate = 933178354Ssam iwi_cvtrate(CSR_READ_4(sc, IWI_CSR_CURRENT_TX_RATE)); 934258612Sadrian ieee80211_free_node(ni); 935178354Ssam ieee80211_media_status(ifp, imr); 936145247Sdamien} 937145247Sdamien 938145247Sdamienstatic int 939178354Ssamiwi_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) 940145247Sdamien{ 941178354Ssam struct iwi_vap *ivp = IWI_VAP(vap); 942178354Ssam struct ieee80211com *ic = vap->iv_ic; 943287197Sglebius struct iwi_softc *sc = ic->ic_softc; 944178354Ssam IWI_LOCK_DECL; 945145247Sdamien 946158089Smlaier DPRINTF(("%s: %s -> %s flags 0x%x\n", __func__, 947178354Ssam ieee80211_state_name[vap->iv_state], 948158089Smlaier ieee80211_state_name[nstate], sc->flags)); 949154992Sdamien 950191746Sthompsa IEEE80211_UNLOCK(ic); 951191746Sthompsa IWI_LOCK(sc); 952145247Sdamien switch (nstate) { 953178354Ssam case IEEE80211_S_INIT: 954178354Ssam /* 955178354Ssam * NB: don't try to do this if iwi_stop_master has 956178354Ssam * shutdown the firmware and disabled interrupts. 957178354Ssam */ 958178354Ssam if (vap->iv_state == IEEE80211_S_RUN && 959178354Ssam (sc->flags & IWI_FLAG_FW_INITED)) 960191746Sthompsa iwi_disassociate(sc, 0); 961178354Ssam break; 962145247Sdamien case IEEE80211_S_AUTH: 963191746Sthompsa iwi_auth_and_assoc(sc, vap); 964191746Sthompsa break; 965145247Sdamien case IEEE80211_S_RUN: 966178354Ssam if (vap->iv_opmode == IEEE80211_M_IBSS && 967178354Ssam vap->iv_state == IEEE80211_S_SCAN) { 968158089Smlaier /* 969158089Smlaier * XXX when joining an ibss network we are called 970158089Smlaier * with a SCAN -> RUN transition on scan complete. 971158089Smlaier * Use that to call iwi_auth_and_assoc. On completing 972158089Smlaier * the join we are then called again with an 973158089Smlaier * AUTH -> RUN transition and we want to do nothing. 974158089Smlaier * This is all totally bogus and needs to be redone. 975158089Smlaier */ 976191746Sthompsa iwi_auth_and_assoc(sc, vap); 977213729Sbschmidt } else if (vap->iv_opmode == IEEE80211_M_MONITOR) 978213729Sbschmidt ieee80211_runtask(ic, &sc->sc_monitortask); 979145247Sdamien break; 980170530Ssam case IEEE80211_S_ASSOC: 981170530Ssam /* 982178354Ssam * If we are transitioning from AUTH then just wait 983178354Ssam * for the ASSOC status to come back from the firmware. 984178354Ssam * Otherwise we need to issue the association request. 985170530Ssam */ 986178354Ssam if (vap->iv_state == IEEE80211_S_AUTH) 987178354Ssam break; 988191746Sthompsa iwi_auth_and_assoc(sc, vap); 989191746Sthompsa break; 990170530Ssam default: 991170530Ssam break; 992145247Sdamien } 993191746Sthompsa IWI_UNLOCK(sc); 994191746Sthompsa IEEE80211_LOCK(ic); 995178354Ssam return ivp->iwi_newstate(vap, nstate, arg); 996145247Sdamien} 997145247Sdamien 998149346Sdamien/* 999149346Sdamien * WME parameters coming from IEEE 802.11e specification. These values are 1000149346Sdamien * already declared in ieee80211_proto.c, but they are static so they can't 1001149346Sdamien * be reused here. 1002149346Sdamien */ 1003149346Sdamienstatic const struct wmeParams iwi_wme_cck_params[WME_NUM_AC] = { 1004149346Sdamien { 0, 3, 5, 7, 0 }, /* WME_AC_BE */ 1005149346Sdamien { 0, 3, 5, 10, 0 }, /* WME_AC_BK */ 1006149346Sdamien { 0, 2, 4, 5, 188 }, /* WME_AC_VI */ 1007149346Sdamien { 0, 2, 3, 4, 102 } /* WME_AC_VO */ 1008149346Sdamien}; 1009149346Sdamien 1010149346Sdamienstatic const struct wmeParams iwi_wme_ofdm_params[WME_NUM_AC] = { 1011149346Sdamien { 0, 3, 4, 6, 0 }, /* WME_AC_BE */ 1012149346Sdamien { 0, 3, 4, 10, 0 }, /* WME_AC_BK */ 1013149346Sdamien { 0, 2, 3, 4, 94 }, /* WME_AC_VI */ 1014149346Sdamien { 0, 2, 2, 3, 47 } /* WME_AC_VO */ 1015149346Sdamien}; 1016158089Smlaier#define IWI_EXP2(v) htole16((1 << (v)) - 1) 1017158089Smlaier#define IWI_USEC(v) htole16(IEEE80211_TXOP_TO_US(v)) 1018149346Sdamien 1019158089Smlaierstatic void 1020158089Smlaieriwi_wme_init(struct iwi_softc *sc) 1021149338Sdamien{ 1022149346Sdamien const struct wmeParams *wmep; 1023149346Sdamien int ac; 1024149338Sdamien 1025158089Smlaier memset(sc->wme, 0, sizeof sc->wme); 1026149346Sdamien for (ac = 0; ac < WME_NUM_AC; ac++) { 1027149346Sdamien /* set WME values for CCK modulation */ 1028149346Sdamien wmep = &iwi_wme_cck_params[ac]; 1029158089Smlaier sc->wme[1].aifsn[ac] = wmep->wmep_aifsn; 1030158089Smlaier sc->wme[1].cwmin[ac] = IWI_EXP2(wmep->wmep_logcwmin); 1031158089Smlaier sc->wme[1].cwmax[ac] = IWI_EXP2(wmep->wmep_logcwmax); 1032158089Smlaier sc->wme[1].burst[ac] = IWI_USEC(wmep->wmep_txopLimit); 1033158089Smlaier sc->wme[1].acm[ac] = wmep->wmep_acm; 1034149346Sdamien 1035149346Sdamien /* set WME values for OFDM modulation */ 1036149346Sdamien wmep = &iwi_wme_ofdm_params[ac]; 1037158089Smlaier sc->wme[2].aifsn[ac] = wmep->wmep_aifsn; 1038158089Smlaier sc->wme[2].cwmin[ac] = IWI_EXP2(wmep->wmep_logcwmin); 1039158089Smlaier sc->wme[2].cwmax[ac] = IWI_EXP2(wmep->wmep_logcwmax); 1040158089Smlaier sc->wme[2].burst[ac] = IWI_USEC(wmep->wmep_txopLimit); 1041158089Smlaier sc->wme[2].acm[ac] = wmep->wmep_acm; 1042149346Sdamien } 1043158089Smlaier} 1044149346Sdamien 1045158089Smlaierstatic int 1046283935Sglebiusiwi_wme_setparams(struct iwi_softc *sc) 1047158089Smlaier{ 1048287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 1049158089Smlaier const struct wmeParams *wmep; 1050158089Smlaier int ac; 1051158089Smlaier 1052158089Smlaier for (ac = 0; ac < WME_NUM_AC; ac++) { 1053158089Smlaier /* set WME values for current operating mode */ 1054158089Smlaier wmep = &ic->ic_wme.wme_chanParams.cap_wmeParams[ac]; 1055158089Smlaier sc->wme[0].aifsn[ac] = wmep->wmep_aifsn; 1056158089Smlaier sc->wme[0].cwmin[ac] = IWI_EXP2(wmep->wmep_logcwmin); 1057158089Smlaier sc->wme[0].cwmax[ac] = IWI_EXP2(wmep->wmep_logcwmax); 1058158089Smlaier sc->wme[0].burst[ac] = IWI_USEC(wmep->wmep_txopLimit); 1059158089Smlaier sc->wme[0].acm[ac] = wmep->wmep_acm; 1060158089Smlaier } 1061158089Smlaier 1062149346Sdamien DPRINTF(("Setting WME parameters\n")); 1063158089Smlaier return iwi_cmd(sc, IWI_CMD_SET_WME_PARAMS, sc->wme, sizeof sc->wme); 1064158089Smlaier} 1065149346Sdamien#undef IWI_USEC 1066149346Sdamien#undef IWI_EXP2 1067158089Smlaier 1068158089Smlaierstatic int 1069158089Smlaieriwi_wme_update(struct ieee80211com *ic) 1070158089Smlaier{ 1071287197Sglebius struct iwi_softc *sc = ic->ic_softc; 1072191746Sthompsa struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 1073290407Savos IWI_LOCK_DECL; 1074158089Smlaier 1075158089Smlaier /* 1076158089Smlaier * We may be called to update the WME parameters in 1077158089Smlaier * the adapter at various places. If we're already 1078191951Ssam * associated then initiate the request immediately; 1079191951Ssam * otherwise we assume the params will get sent down 1080191951Ssam * to the adapter as part of the work iwi_auth_and_assoc 1081191951Ssam * does. 1082158089Smlaier */ 1083290407Savos if (vap->iv_state == IEEE80211_S_RUN) { 1084290407Savos IWI_LOCK(sc); 1085290407Savos iwi_wme_setparams(sc); 1086290407Savos IWI_UNLOCK(sc); 1087290407Savos } 1088191746Sthompsa return (0); 1089149338Sdamien} 1090149338Sdamien 1091158089Smlaierstatic int 1092158089Smlaieriwi_wme_setie(struct iwi_softc *sc) 1093158089Smlaier{ 1094158089Smlaier struct ieee80211_wme_info wme; 1095158089Smlaier 1096158089Smlaier memset(&wme, 0, sizeof wme); 1097158089Smlaier wme.wme_id = IEEE80211_ELEMID_VENDOR; 1098158089Smlaier wme.wme_len = sizeof (struct ieee80211_wme_info) - 2; 1099158089Smlaier wme.wme_oui[0] = 0x00; 1100158089Smlaier wme.wme_oui[1] = 0x50; 1101158089Smlaier wme.wme_oui[2] = 0xf2; 1102158089Smlaier wme.wme_type = WME_OUI_TYPE; 1103158089Smlaier wme.wme_subtype = WME_INFO_OUI_SUBTYPE; 1104158089Smlaier wme.wme_version = WME_VERSION; 1105158089Smlaier wme.wme_info = 0; 1106158089Smlaier 1107158089Smlaier DPRINTF(("Setting WME IE (len=%u)\n", wme.wme_len)); 1108158089Smlaier return iwi_cmd(sc, IWI_CMD_SET_WMEIE, &wme, sizeof wme); 1109158089Smlaier} 1110158089Smlaier 1111145247Sdamien/* 1112145247Sdamien * Read 16 bits at address 'addr' from the serial EEPROM. 1113145247Sdamien */ 1114145247Sdamienstatic uint16_t 1115145247Sdamieniwi_read_prom_word(struct iwi_softc *sc, uint8_t addr) 1116145247Sdamien{ 1117145247Sdamien uint32_t tmp; 1118145247Sdamien uint16_t val; 1119145247Sdamien int n; 1120145247Sdamien 1121145247Sdamien /* clock C once before the first command */ 1122145247Sdamien IWI_EEPROM_CTL(sc, 0); 1123145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_S); 1124145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_C); 1125145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_S); 1126145247Sdamien 1127145247Sdamien /* write start bit (1) */ 1128145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_D); 1129145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_D | IWI_EEPROM_C); 1130145247Sdamien 1131145247Sdamien /* write READ opcode (10) */ 1132145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_D); 1133145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_D | IWI_EEPROM_C); 1134145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_S); 1135145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_C); 1136145247Sdamien 1137145247Sdamien /* write address A7-A0 */ 1138145247Sdamien for (n = 7; n >= 0; n--) { 1139145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_S | 1140145247Sdamien (((addr >> n) & 1) << IWI_EEPROM_SHIFT_D)); 1141145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_S | 1142145247Sdamien (((addr >> n) & 1) << IWI_EEPROM_SHIFT_D) | IWI_EEPROM_C); 1143145247Sdamien } 1144145247Sdamien 1145145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_S); 1146145247Sdamien 1147145247Sdamien /* read data Q15-Q0 */ 1148145247Sdamien val = 0; 1149145247Sdamien for (n = 15; n >= 0; n--) { 1150145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_C); 1151145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_S); 1152145247Sdamien tmp = MEM_READ_4(sc, IWI_MEM_EEPROM_CTL); 1153145247Sdamien val |= ((tmp & IWI_EEPROM_Q) >> IWI_EEPROM_SHIFT_Q) << n; 1154145247Sdamien } 1155145247Sdamien 1156145247Sdamien IWI_EEPROM_CTL(sc, 0); 1157145247Sdamien 1158145247Sdamien /* clear Chip Select and clock C */ 1159145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_S); 1160145247Sdamien IWI_EEPROM_CTL(sc, 0); 1161145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_C); 1162145247Sdamien 1163152389Sdamien return val; 1164145247Sdamien} 1165145247Sdamien 1166145247Sdamienstatic void 1167158089Smlaieriwi_setcurchan(struct iwi_softc *sc, int chan) 1168145247Sdamien{ 1169287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 1170145247Sdamien 1171158089Smlaier sc->curchan = chan; 1172192468Ssam ieee80211_radiotap_chan_change(ic); 1173145247Sdamien} 1174145247Sdamien 1175145247Sdamienstatic void 1176145247Sdamieniwi_frame_intr(struct iwi_softc *sc, struct iwi_rx_data *data, int i, 1177145247Sdamien struct iwi_frame *frame) 1178145247Sdamien{ 1179287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 1180152384Sdamien struct mbuf *mnew, *m; 1181145247Sdamien struct ieee80211_node *ni; 1182158089Smlaier int type, error, framelen; 1183192468Ssam int8_t rssi, nf; 1184164964Smlaier IWI_LOCK_DECL; 1185145247Sdamien 1186158089Smlaier framelen = le16toh(frame->len); 1187158089Smlaier if (framelen < IEEE80211_MIN_LEN || framelen > MCLBYTES) { 1188158089Smlaier /* 1189158089Smlaier * XXX >MCLBYTES is bogus as it means the h/w dma'd 1190158089Smlaier * out of bounds; need to figure out how to limit 1191158089Smlaier * frame size in the firmware 1192158089Smlaier */ 1193158089Smlaier /* XXX stat */ 1194158089Smlaier DPRINTFN(1, 1195158089Smlaier ("drop rx frame len=%u chan=%u rssi=%u rssi_dbm=%u\n", 1196158089Smlaier le16toh(frame->len), frame->chan, frame->rssi, 1197158089Smlaier frame->rssi_dbm)); 1198146500Sdamien return; 1199158089Smlaier } 1200146500Sdamien 1201158089Smlaier DPRINTFN(5, ("received frame len=%u chan=%u rssi=%u rssi_dbm=%u\n", 1202158089Smlaier le16toh(frame->len), frame->chan, frame->rssi, frame->rssi_dbm)); 1203158089Smlaier 1204158089Smlaier if (frame->chan != sc->curchan) 1205158089Smlaier iwi_setcurchan(sc, frame->chan); 1206158089Smlaier 1207152384Sdamien /* 1208152384Sdamien * Try to allocate a new mbuf for this ring element and load it before 1209152384Sdamien * processing the current mbuf. If the ring element cannot be loaded, 1210152384Sdamien * drop the received packet and reuse the old mbuf. In the unlikely 1211152384Sdamien * case that the old mbuf can't be reloaded either, explicitly panic. 1212152384Sdamien */ 1213243857Sglebius mnew = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); 1214152384Sdamien if (mnew == NULL) { 1215287197Sglebius counter_u64_add(ic->ic_ierrors, 1); 1216152384Sdamien return; 1217152384Sdamien } 1218152384Sdamien 1219145247Sdamien bus_dmamap_unload(sc->rxq.data_dmat, data->map); 1220145247Sdamien 1221152384Sdamien error = bus_dmamap_load(sc->rxq.data_dmat, data->map, 1222152384Sdamien mtod(mnew, void *), MCLBYTES, iwi_dma_map_addr, &data->physaddr, 1223152384Sdamien 0); 1224152384Sdamien if (error != 0) { 1225152384Sdamien m_freem(mnew); 1226152384Sdamien 1227152384Sdamien /* try to reload the old mbuf */ 1228152384Sdamien error = bus_dmamap_load(sc->rxq.data_dmat, data->map, 1229152384Sdamien mtod(data->m, void *), MCLBYTES, iwi_dma_map_addr, 1230152384Sdamien &data->physaddr, 0); 1231152384Sdamien if (error != 0) { 1232152384Sdamien /* very unlikely that it will fail... */ 1233152384Sdamien panic("%s: could not load old rx mbuf", 1234152384Sdamien device_get_name(sc->sc_dev)); 1235152384Sdamien } 1236287197Sglebius counter_u64_add(ic->ic_ierrors, 1); 1237152384Sdamien return; 1238152384Sdamien } 1239152384Sdamien 1240152384Sdamien /* 1241152384Sdamien * New mbuf successfully loaded, update Rx ring and continue 1242152384Sdamien * processing. 1243152384Sdamien */ 1244152384Sdamien m = data->m; 1245152384Sdamien data->m = mnew; 1246152384Sdamien CSR_WRITE_4(sc, data->reg, data->physaddr); 1247152384Sdamien 1248145247Sdamien /* finalize mbuf */ 1249145247Sdamien m->m_pkthdr.len = m->m_len = sizeof (struct iwi_hdr) + 1250158089Smlaier sizeof (struct iwi_frame) + framelen; 1251145247Sdamien 1252145247Sdamien m_adj(m, sizeof (struct iwi_hdr) + sizeof (struct iwi_frame)); 1253145247Sdamien 1254192541Ssam rssi = frame->rssi_dbm; 1255192468Ssam nf = -95; 1256192468Ssam if (ieee80211_radiotap_active(ic)) { 1257145247Sdamien struct iwi_rx_radiotap_header *tap = &sc->sc_rxtap; 1258145247Sdamien 1259145247Sdamien tap->wr_flags = 0; 1260192468Ssam tap->wr_antsignal = rssi; 1261192468Ssam tap->wr_antnoise = nf; 1262158089Smlaier tap->wr_rate = iwi_cvtrate(frame->rate); 1263145247Sdamien tap->wr_antenna = frame->antenna; 1264145247Sdamien } 1265164964Smlaier IWI_UNLOCK(sc); 1266145247Sdamien 1267158089Smlaier ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *)); 1268178354Ssam if (ni != NULL) { 1269192468Ssam type = ieee80211_input(ni, m, rssi, nf); 1270178354Ssam ieee80211_free_node(ni); 1271178354Ssam } else 1272192468Ssam type = ieee80211_input_all(ic, m, rssi, nf); 1273145247Sdamien 1274164964Smlaier IWI_LOCK(sc); 1275158089Smlaier if (sc->sc_softled) { 1276158089Smlaier /* 1277158089Smlaier * Blink for any data frame. Otherwise do a 1278158089Smlaier * heartbeat-style blink when idle. The latter 1279158089Smlaier * is mainly for station mode where we depend on 1280158089Smlaier * periodic beacon frames to trigger the poll event. 1281158089Smlaier */ 1282158089Smlaier if (type == IEEE80211_FC0_TYPE_DATA) { 1283158089Smlaier sc->sc_rxrate = frame->rate; 1284158089Smlaier iwi_led_event(sc, IWI_LED_RX); 1285158089Smlaier } else if (ticks - sc->sc_ledevent >= sc->sc_ledidle) 1286158089Smlaier iwi_led_event(sc, IWI_LED_POLL); 1287158089Smlaier } 1288145247Sdamien} 1289145247Sdamien 1290158089Smlaier/* 1291158089Smlaier * Check for an association response frame to see if QoS 1292158089Smlaier * has been negotiated. We parse just enough to figure 1293158089Smlaier * out if we're supposed to use QoS. The proper solution 1294158089Smlaier * is to pass the frame up so ieee80211_input can do the 1295158089Smlaier * work but that's made hard by how things currently are 1296158089Smlaier * done in the driver. 1297158089Smlaier */ 1298145247Sdamienstatic void 1299178354Ssamiwi_checkforqos(struct ieee80211vap *vap, 1300178354Ssam const struct ieee80211_frame *wh, int len) 1301158089Smlaier{ 1302158089Smlaier#define SUBTYPE(wh) ((wh)->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) 1303158089Smlaier const uint8_t *frm, *efrm, *wme; 1304158089Smlaier struct ieee80211_node *ni; 1305170530Ssam uint16_t capinfo, status, associd; 1306158089Smlaier 1307158089Smlaier /* NB: +8 for capinfo, status, associd, and first ie */ 1308158089Smlaier if (!(sizeof(*wh)+8 < len && len < IEEE80211_MAX_LEN) || 1309158089Smlaier SUBTYPE(wh) != IEEE80211_FC0_SUBTYPE_ASSOC_RESP) 1310158089Smlaier return; 1311158089Smlaier /* 1312158089Smlaier * asresp frame format 1313158089Smlaier * [2] capability information 1314158089Smlaier * [2] status 1315158089Smlaier * [2] association ID 1316158089Smlaier * [tlv] supported rates 1317158089Smlaier * [tlv] extended supported rates 1318158089Smlaier * [tlv] WME 1319158089Smlaier */ 1320158089Smlaier frm = (const uint8_t *)&wh[1]; 1321158089Smlaier efrm = ((const uint8_t *) wh) + len; 1322158089Smlaier 1323170530Ssam capinfo = le16toh(*(const uint16_t *)frm); 1324170530Ssam frm += 2; 1325170530Ssam status = le16toh(*(const uint16_t *)frm); 1326170530Ssam frm += 2; 1327170530Ssam associd = le16toh(*(const uint16_t *)frm); 1328170530Ssam frm += 2; 1329170530Ssam 1330158089Smlaier wme = NULL; 1331232785Siwasaki while (efrm - frm > 1) { 1332232785Siwasaki IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return); 1333158089Smlaier switch (*frm) { 1334158089Smlaier case IEEE80211_ELEMID_VENDOR: 1335158089Smlaier if (iswmeoui(frm)) 1336158089Smlaier wme = frm; 1337158089Smlaier break; 1338158089Smlaier } 1339158089Smlaier frm += frm[1] + 2; 1340158089Smlaier } 1341158089Smlaier 1342258612Sadrian ni = ieee80211_ref_node(vap->iv_bss); 1343170530Ssam ni->ni_capinfo = capinfo; 1344214162Sbschmidt ni->ni_associd = associd & 0x3fff; 1345158089Smlaier if (wme != NULL) 1346158089Smlaier ni->ni_flags |= IEEE80211_NODE_QOS; 1347158089Smlaier else 1348158089Smlaier ni->ni_flags &= ~IEEE80211_NODE_QOS; 1349258612Sadrian ieee80211_free_node(ni); 1350158089Smlaier#undef SUBTYPE 1351158089Smlaier} 1352158089Smlaier 1353263920Sadrianstatic void 1354263920Sadrianiwi_notif_link_quality(struct iwi_softc *sc, struct iwi_notif *notif) 1355263920Sadrian{ 1356263920Sadrian struct iwi_notif_link_quality *lq; 1357263920Sadrian int len; 1358263920Sadrian 1359263920Sadrian len = le16toh(notif->len); 1360263920Sadrian 1361263927Seadler DPRINTFN(5, ("Notification (%u) - len=%d, sizeof=%zu\n", 1362263920Sadrian notif->type, 1363263920Sadrian len, 1364263920Sadrian sizeof(struct iwi_notif_link_quality) 1365263920Sadrian )); 1366263920Sadrian 1367263920Sadrian /* enforce length */ 1368263920Sadrian if (len != sizeof(struct iwi_notif_link_quality)) { 1369263920Sadrian DPRINTFN(5, ("Notification: (%u) too short (%d)\n", 1370263920Sadrian notif->type, 1371263920Sadrian len)); 1372263920Sadrian return; 1373263920Sadrian } 1374263920Sadrian 1375263920Sadrian lq = (struct iwi_notif_link_quality *)(notif + 1); 1376263920Sadrian memcpy(&sc->sc_linkqual, lq, sizeof(sc->sc_linkqual)); 1377263920Sadrian sc->sc_linkqual_valid = 1; 1378263920Sadrian} 1379263920Sadrian 1380178354Ssam/* 1381178354Ssam * Task queue callbacks for iwi_notification_intr used to avoid LOR's. 1382178354Ssam */ 1383178354Ssam 1384158089Smlaierstatic void 1385145247Sdamieniwi_notification_intr(struct iwi_softc *sc, struct iwi_notif *notif) 1386145247Sdamien{ 1387287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 1388178354Ssam struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 1389145247Sdamien struct iwi_notif_scan_channel *chan; 1390145247Sdamien struct iwi_notif_scan_complete *scan; 1391145247Sdamien struct iwi_notif_authentication *auth; 1392145247Sdamien struct iwi_notif_association *assoc; 1393158089Smlaier struct iwi_notif_beacon_state *beacon; 1394145247Sdamien 1395145247Sdamien switch (notif->type) { 1396145247Sdamien case IWI_NOTIF_TYPE_SCAN_CHANNEL: 1397145247Sdamien chan = (struct iwi_notif_scan_channel *)(notif + 1); 1398145247Sdamien 1399158089Smlaier DPRINTFN(3, ("Scan of channel %u complete (%u)\n", 1400170530Ssam ieee80211_ieee2mhz(chan->nchan, 0), chan->nchan)); 1401170530Ssam 1402170530Ssam /* Reset the timer, the scan is still going */ 1403170530Ssam sc->sc_state_timer = 3; 1404145247Sdamien break; 1405145247Sdamien 1406145247Sdamien case IWI_NOTIF_TYPE_SCAN_COMPLETE: 1407145247Sdamien scan = (struct iwi_notif_scan_complete *)(notif + 1); 1408145247Sdamien 1409145247Sdamien DPRINTFN(2, ("Scan completed (%u, %u)\n", scan->nchan, 1410145247Sdamien scan->status)); 1411145247Sdamien 1412170530Ssam IWI_STATE_END(sc, IWI_FW_SCANNING); 1413158089Smlaier 1414213729Sbschmidt /* 1415213729Sbschmidt * Monitor mode works by doing a passive scan to set 1416213729Sbschmidt * the channel and enable rx. Because we don't want 1417213729Sbschmidt * to abort a scan lest the firmware crash we scan 1418213729Sbschmidt * for a short period of time and automatically restart 1419213729Sbschmidt * the scan when notified the sweep has completed. 1420213729Sbschmidt */ 1421213729Sbschmidt if (vap->iv_opmode == IEEE80211_M_MONITOR) { 1422213729Sbschmidt ieee80211_runtask(ic, &sc->sc_monitortask); 1423213729Sbschmidt break; 1424213729Sbschmidt } 1425213729Sbschmidt 1426178354Ssam if (scan->status == IWI_SCAN_COMPLETED) { 1427178354Ssam /* NB: don't need to defer, net80211 does it for us */ 1428178354Ssam ieee80211_scan_next(vap); 1429178354Ssam } 1430145247Sdamien break; 1431145247Sdamien 1432145247Sdamien case IWI_NOTIF_TYPE_AUTHENTICATION: 1433145247Sdamien auth = (struct iwi_notif_authentication *)(notif + 1); 1434145247Sdamien switch (auth->state) { 1435158089Smlaier case IWI_AUTH_SUCCESS: 1436158089Smlaier DPRINTFN(2, ("Authentication succeeeded\n")); 1437191746Sthompsa ieee80211_new_state(vap, IEEE80211_S_ASSOC, -1); 1438145247Sdamien break; 1439158089Smlaier case IWI_AUTH_FAIL: 1440178354Ssam /* 1441178354Ssam * These are delivered as an unsolicited deauth 1442178354Ssam * (e.g. due to inactivity) or in response to an 1443178354Ssam * associate request. 1444178354Ssam */ 1445158089Smlaier sc->flags &= ~IWI_FLAG_ASSOCIATED; 1446178354Ssam if (vap->iv_state != IEEE80211_S_RUN) { 1447178354Ssam DPRINTFN(2, ("Authentication failed\n")); 1448178354Ssam vap->iv_stats.is_rx_auth_fail++; 1449178354Ssam IWI_STATE_END(sc, IWI_FW_ASSOCIATING); 1450178354Ssam } else { 1451178354Ssam DPRINTFN(2, ("Deauthenticated\n")); 1452178354Ssam vap->iv_stats.is_rx_deauth++; 1453178354Ssam } 1454191746Sthompsa ieee80211_new_state(vap, IEEE80211_S_SCAN, -1); 1455145247Sdamien break; 1456158089Smlaier case IWI_AUTH_SENT_1: 1457158089Smlaier case IWI_AUTH_RECV_2: 1458158089Smlaier case IWI_AUTH_SEQ1_PASS: 1459158089Smlaier break; 1460158089Smlaier case IWI_AUTH_SEQ1_FAIL: 1461158089Smlaier DPRINTFN(2, ("Initial authentication handshake failed; " 1462158089Smlaier "you probably need shared key\n")); 1463178354Ssam vap->iv_stats.is_rx_auth_fail++; 1464170530Ssam IWI_STATE_END(sc, IWI_FW_ASSOCIATING); 1465158089Smlaier /* XXX retry shared key when in auto */ 1466158089Smlaier break; 1467145247Sdamien default: 1468145247Sdamien device_printf(sc->sc_dev, 1469145247Sdamien "unknown authentication state %u\n", auth->state); 1470178354Ssam break; 1471145247Sdamien } 1472145247Sdamien break; 1473145247Sdamien 1474145247Sdamien case IWI_NOTIF_TYPE_ASSOCIATION: 1475145247Sdamien assoc = (struct iwi_notif_association *)(notif + 1); 1476145247Sdamien switch (assoc->state) { 1477158089Smlaier case IWI_AUTH_SUCCESS: 1478145247Sdamien /* re-association, do nothing */ 1479145247Sdamien break; 1480158089Smlaier case IWI_ASSOC_SUCCESS: 1481158089Smlaier DPRINTFN(2, ("Association succeeded\n")); 1482158089Smlaier sc->flags |= IWI_FLAG_ASSOCIATED; 1483170530Ssam IWI_STATE_END(sc, IWI_FW_ASSOCIATING); 1484178354Ssam iwi_checkforqos(vap, 1485158089Smlaier (const struct ieee80211_frame *)(assoc+1), 1486214236Sbschmidt le16toh(notif->len) - sizeof(*assoc) - 1); 1487191746Sthompsa ieee80211_new_state(vap, IEEE80211_S_RUN, -1); 1488145247Sdamien break; 1489170530Ssam case IWI_ASSOC_INIT: 1490178354Ssam sc->flags &= ~IWI_FLAG_ASSOCIATED; 1491170530Ssam switch (sc->fw_state) { 1492178354Ssam case IWI_FW_ASSOCIATING: 1493178354Ssam DPRINTFN(2, ("Association failed\n")); 1494178354Ssam IWI_STATE_END(sc, IWI_FW_ASSOCIATING); 1495191746Sthompsa ieee80211_new_state(vap, IEEE80211_S_SCAN, -1); 1496178354Ssam break; 1497170530Ssam 1498178354Ssam case IWI_FW_DISASSOCIATING: 1499178354Ssam DPRINTFN(2, ("Dissassociated\n")); 1500178354Ssam IWI_STATE_END(sc, IWI_FW_DISASSOCIATING); 1501178354Ssam vap->iv_stats.is_rx_disassoc++; 1502191746Sthompsa ieee80211_new_state(vap, IEEE80211_S_SCAN, -1); 1503178354Ssam break; 1504170530Ssam } 1505145247Sdamien break; 1506145247Sdamien default: 1507145247Sdamien device_printf(sc->sc_dev, 1508145247Sdamien "unknown association state %u\n", assoc->state); 1509178354Ssam break; 1510145247Sdamien } 1511145247Sdamien break; 1512145247Sdamien 1513158089Smlaier case IWI_NOTIF_TYPE_BEACON: 1514158089Smlaier /* XXX check struct length */ 1515158089Smlaier beacon = (struct iwi_notif_beacon_state *)(notif + 1); 1516158089Smlaier 1517158089Smlaier DPRINTFN(5, ("Beacon state (%u, %u)\n", 1518158089Smlaier beacon->state, le32toh(beacon->number))); 1519158089Smlaier 1520158089Smlaier if (beacon->state == IWI_BEACON_MISS) { 1521158089Smlaier /* 1522158089Smlaier * The firmware notifies us of every beacon miss 1523158089Smlaier * so we need to track the count against the 1524158089Smlaier * configured threshold before notifying the 1525158089Smlaier * 802.11 layer. 1526158089Smlaier * XXX try to roam, drop assoc only on much higher count 1527158089Smlaier */ 1528178354Ssam if (le32toh(beacon->number) >= vap->iv_bmissthreshold) { 1529158089Smlaier DPRINTF(("Beacon miss: %u >= %u\n", 1530158089Smlaier le32toh(beacon->number), 1531178354Ssam vap->iv_bmissthreshold)); 1532178354Ssam vap->iv_stats.is_beacon_miss++; 1533178354Ssam /* 1534178354Ssam * It's pointless to notify the 802.11 layer 1535178354Ssam * as it'll try to send a probe request (which 1536178354Ssam * we'll discard) and then timeout and drop us 1537178354Ssam * into scan state. Instead tell the firmware 1538178354Ssam * to disassociate and then on completion we'll 1539178354Ssam * kick the state machine to scan. 1540178354Ssam */ 1541191746Sthompsa ieee80211_runtask(ic, &sc->sc_disassoctask); 1542158089Smlaier } 1543158089Smlaier } 1544158089Smlaier break; 1545158089Smlaier 1546158089Smlaier case IWI_NOTIF_TYPE_CALIBRATION: 1547158089Smlaier case IWI_NOTIF_TYPE_NOISE: 1548263920Sadrian /* XXX handle? */ 1549158089Smlaier DPRINTFN(5, ("Notification (%u)\n", notif->type)); 1550158089Smlaier break; 1551263920Sadrian case IWI_NOTIF_TYPE_LINK_QUALITY: 1552263920Sadrian iwi_notif_link_quality(sc, notif); 1553263920Sadrian break; 1554158089Smlaier 1555154992Sdamien default: 1556158089Smlaier DPRINTF(("unknown notification type %u flags 0x%x len %u\n", 1557158089Smlaier notif->type, notif->flags, le16toh(notif->len))); 1558178354Ssam break; 1559145247Sdamien } 1560145247Sdamien} 1561145247Sdamien 1562145247Sdamienstatic void 1563145247Sdamieniwi_rx_intr(struct iwi_softc *sc) 1564145247Sdamien{ 1565145247Sdamien struct iwi_rx_data *data; 1566145247Sdamien struct iwi_hdr *hdr; 1567145247Sdamien uint32_t hw; 1568145247Sdamien 1569145247Sdamien hw = CSR_READ_4(sc, IWI_CSR_RX_RIDX); 1570145247Sdamien 1571145247Sdamien for (; sc->rxq.cur != hw;) { 1572145247Sdamien data = &sc->rxq.data[sc->rxq.cur]; 1573145247Sdamien 1574145247Sdamien bus_dmamap_sync(sc->rxq.data_dmat, data->map, 1575145247Sdamien BUS_DMASYNC_POSTREAD); 1576145247Sdamien 1577145247Sdamien hdr = mtod(data->m, struct iwi_hdr *); 1578145247Sdamien 1579145247Sdamien switch (hdr->type) { 1580145247Sdamien case IWI_HDR_TYPE_FRAME: 1581145247Sdamien iwi_frame_intr(sc, data, sc->rxq.cur, 1582145247Sdamien (struct iwi_frame *)(hdr + 1)); 1583145247Sdamien break; 1584145247Sdamien 1585145247Sdamien case IWI_HDR_TYPE_NOTIF: 1586145247Sdamien iwi_notification_intr(sc, 1587145247Sdamien (struct iwi_notif *)(hdr + 1)); 1588145247Sdamien break; 1589145247Sdamien 1590145247Sdamien default: 1591145247Sdamien device_printf(sc->sc_dev, "unknown hdr type %u\n", 1592145247Sdamien hdr->type); 1593145247Sdamien } 1594145247Sdamien 1595145247Sdamien DPRINTFN(15, ("rx done idx=%u\n", sc->rxq.cur)); 1596145247Sdamien 1597145247Sdamien sc->rxq.cur = (sc->rxq.cur + 1) % IWI_RX_RING_COUNT; 1598145247Sdamien } 1599145247Sdamien 1600145247Sdamien /* tell the firmware what we have processed */ 1601145247Sdamien hw = (hw == 0) ? IWI_RX_RING_COUNT - 1 : hw - 1; 1602145247Sdamien CSR_WRITE_4(sc, IWI_CSR_RX_WIDX, hw); 1603145247Sdamien} 1604145247Sdamien 1605145247Sdamienstatic void 1606149338Sdamieniwi_tx_intr(struct iwi_softc *sc, struct iwi_tx_ring *txq) 1607145247Sdamien{ 1608145247Sdamien struct iwi_tx_data *data; 1609145247Sdamien uint32_t hw; 1610145247Sdamien 1611149338Sdamien hw = CSR_READ_4(sc, txq->csr_ridx); 1612145247Sdamien 1613287197Sglebius while (txq->next != hw) { 1614149338Sdamien data = &txq->data[txq->next]; 1615287197Sglebius DPRINTFN(15, ("tx done idx=%u\n", txq->next)); 1616149338Sdamien bus_dmamap_sync(txq->data_dmat, data->map, 1617145247Sdamien BUS_DMASYNC_POSTWRITE); 1618149338Sdamien bus_dmamap_unload(txq->data_dmat, data->map); 1619287197Sglebius ieee80211_tx_complete(data->ni, data->m, 0); 1620287197Sglebius data->ni = NULL; 1621286437Sadrian data->m = NULL; 1622149338Sdamien txq->queued--; 1623149338Sdamien txq->next = (txq->next + 1) % IWI_TX_RING_COUNT; 1624145247Sdamien } 1625145247Sdamien sc->sc_tx_timer = 0; 1626158089Smlaier if (sc->sc_softled) 1627158089Smlaier iwi_led_event(sc, IWI_LED_TX); 1628287197Sglebius iwi_start(sc); 1629145247Sdamien} 1630145247Sdamien 1631145247Sdamienstatic void 1632191746Sthompsaiwi_fatal_error_intr(struct iwi_softc *sc) 1633191746Sthompsa{ 1634287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 1635191956Sthompsa struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 1636191746Sthompsa 1637191746Sthompsa device_printf(sc->sc_dev, "firmware error\n"); 1638191956Sthompsa if (vap != NULL) 1639191956Sthompsa ieee80211_cancel_scan(vap); 1640191746Sthompsa ieee80211_runtask(ic, &sc->sc_restarttask); 1641191746Sthompsa 1642191746Sthompsa sc->flags &= ~IWI_FLAG_BUSY; 1643191746Sthompsa sc->sc_busy_timer = 0; 1644191746Sthompsa wakeup(sc); 1645191746Sthompsa} 1646191746Sthompsa 1647191746Sthompsastatic void 1648191746Sthompsaiwi_radio_off_intr(struct iwi_softc *sc) 1649191746Sthompsa{ 1650191746Sthompsa 1651287197Sglebius ieee80211_runtask(&sc->sc_ic, &sc->sc_radiofftask); 1652191746Sthompsa} 1653191746Sthompsa 1654191746Sthompsastatic void 1655145247Sdamieniwi_intr(void *arg) 1656145247Sdamien{ 1657145247Sdamien struct iwi_softc *sc = arg; 1658145247Sdamien uint32_t r; 1659158089Smlaier IWI_LOCK_DECL; 1660145247Sdamien 1661158089Smlaier IWI_LOCK(sc); 1662145247Sdamien 1663145247Sdamien if ((r = CSR_READ_4(sc, IWI_CSR_INTR)) == 0 || r == 0xffffffff) { 1664158089Smlaier IWI_UNLOCK(sc); 1665145247Sdamien return; 1666145247Sdamien } 1667145247Sdamien 1668156547Sdamien /* acknowledge interrupts */ 1669156547Sdamien CSR_WRITE_4(sc, IWI_CSR_INTR, r); 1670145247Sdamien 1671158089Smlaier if (r & IWI_INTR_FATAL_ERROR) { 1672191746Sthompsa iwi_fatal_error_intr(sc); 1673191746Sthompsa goto done; 1674145247Sdamien } 1675145247Sdamien 1676158089Smlaier if (r & IWI_INTR_FW_INITED) { 1677158089Smlaier if (!(r & (IWI_INTR_FATAL_ERROR | IWI_INTR_PARITY_ERROR))) 1678158089Smlaier wakeup(sc); 1679145247Sdamien } 1680145247Sdamien 1681158089Smlaier if (r & IWI_INTR_RADIO_OFF) 1682191746Sthompsa iwi_radio_off_intr(sc); 1683158089Smlaier 1684158089Smlaier if (r & IWI_INTR_CMD_DONE) { 1685158089Smlaier sc->flags &= ~IWI_FLAG_BUSY; 1686170530Ssam sc->sc_busy_timer = 0; 1687145247Sdamien wakeup(sc); 1688158089Smlaier } 1689145247Sdamien 1690145247Sdamien if (r & IWI_INTR_TX1_DONE) 1691149338Sdamien iwi_tx_intr(sc, &sc->txq[0]); 1692145247Sdamien 1693149338Sdamien if (r & IWI_INTR_TX2_DONE) 1694149338Sdamien iwi_tx_intr(sc, &sc->txq[1]); 1695149338Sdamien 1696149338Sdamien if (r & IWI_INTR_TX3_DONE) 1697149338Sdamien iwi_tx_intr(sc, &sc->txq[2]); 1698149338Sdamien 1699149338Sdamien if (r & IWI_INTR_TX4_DONE) 1700149338Sdamien iwi_tx_intr(sc, &sc->txq[3]); 1701149338Sdamien 1702149338Sdamien if (r & IWI_INTR_RX_DONE) 1703149338Sdamien iwi_rx_intr(sc); 1704149338Sdamien 1705158089Smlaier if (r & IWI_INTR_PARITY_ERROR) { 1706158089Smlaier /* XXX rate-limit */ 1707158089Smlaier device_printf(sc->sc_dev, "parity error\n"); 1708158089Smlaier } 1709191746Sthompsadone: 1710158089Smlaier IWI_UNLOCK(sc); 1711145247Sdamien} 1712145247Sdamien 1713145247Sdamienstatic int 1714158089Smlaieriwi_cmd(struct iwi_softc *sc, uint8_t type, void *data, uint8_t len) 1715145247Sdamien{ 1716145247Sdamien struct iwi_cmd_desc *desc; 1717145247Sdamien 1718170530Ssam IWI_LOCK_ASSERT(sc); 1719166848Sluigi 1720158089Smlaier if (sc->flags & IWI_FLAG_BUSY) { 1721158089Smlaier device_printf(sc->sc_dev, "%s: cmd %d not sent, busy\n", 1722158089Smlaier __func__, type); 1723158089Smlaier return EAGAIN; 1724158089Smlaier } 1725158089Smlaier sc->flags |= IWI_FLAG_BUSY; 1726170530Ssam sc->sc_busy_timer = 2; 1727158089Smlaier 1728145247Sdamien desc = &sc->cmdq.desc[sc->cmdq.cur]; 1729145247Sdamien 1730145247Sdamien desc->hdr.type = IWI_HDR_TYPE_COMMAND; 1731145247Sdamien desc->hdr.flags = IWI_HDR_FLAG_IRQ; 1732145247Sdamien desc->type = type; 1733145247Sdamien desc->len = len; 1734145247Sdamien memcpy(desc->data, data, len); 1735145247Sdamien 1736145247Sdamien bus_dmamap_sync(sc->cmdq.desc_dmat, sc->cmdq.desc_map, 1737145247Sdamien BUS_DMASYNC_PREWRITE); 1738145247Sdamien 1739145247Sdamien DPRINTFN(2, ("sending command idx=%u type=%u len=%u\n", sc->cmdq.cur, 1740145247Sdamien type, len)); 1741145247Sdamien 1742145247Sdamien sc->cmdq.cur = (sc->cmdq.cur + 1) % IWI_CMD_RING_COUNT; 1743145247Sdamien CSR_WRITE_4(sc, IWI_CSR_CMD_WIDX, sc->cmdq.cur); 1744145247Sdamien 1745158089Smlaier return msleep(sc, &sc->sc_mtx, 0, "iwicmd", hz); 1746145247Sdamien} 1747145247Sdamien 1748150341Sdamienstatic void 1749158089Smlaieriwi_write_ibssnode(struct iwi_softc *sc, 1750158089Smlaier const u_int8_t addr[IEEE80211_ADDR_LEN], int entry) 1751150341Sdamien{ 1752150341Sdamien struct iwi_ibssnode node; 1753150341Sdamien 1754150341Sdamien /* write node information into NIC memory */ 1755150341Sdamien memset(&node, 0, sizeof node); 1756158089Smlaier IEEE80211_ADDR_COPY(node.bssid, addr); 1757150341Sdamien 1758158089Smlaier DPRINTF(("%s mac %6D station %u\n", __func__, node.bssid, ":", entry)); 1759158089Smlaier 1760150341Sdamien CSR_WRITE_REGION_1(sc, 1761158089Smlaier IWI_CSR_NODE_BASE + entry * sizeof node, 1762150341Sdamien (uint8_t *)&node, sizeof node); 1763150341Sdamien} 1764150341Sdamien 1765145247Sdamienstatic int 1766287197Sglebiusiwi_tx_start(struct iwi_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, 1767150245Sdamien int ac) 1768145247Sdamien{ 1769178354Ssam struct ieee80211vap *vap = ni->ni_vap; 1770178354Ssam struct ieee80211com *ic = ni->ni_ic; 1771150341Sdamien struct iwi_node *in = (struct iwi_node *)ni; 1772158089Smlaier const struct ieee80211_frame *wh; 1773146500Sdamien struct ieee80211_key *k; 1774149338Sdamien const struct chanAccParams *cap; 1775150245Sdamien struct iwi_tx_ring *txq = &sc->txq[ac]; 1776145247Sdamien struct iwi_tx_data *data; 1777145247Sdamien struct iwi_tx_desc *desc; 1778145247Sdamien struct mbuf *mnew; 1779145247Sdamien bus_dma_segment_t segs[IWI_MAX_NSEG]; 1780158089Smlaier int error, nsegs, hdrlen, i; 1781158089Smlaier int ismcast, flags, xflags, staid; 1782145247Sdamien 1783170530Ssam IWI_LOCK_ASSERT(sc); 1784158089Smlaier wh = mtod(m0, const struct ieee80211_frame *); 1785158089Smlaier /* NB: only data frames use this path */ 1786158089Smlaier hdrlen = ieee80211_hdrsize(wh); 1787158089Smlaier ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); 1788158089Smlaier flags = xflags = 0; 1789149338Sdamien 1790158089Smlaier if (!ismcast) 1791158089Smlaier flags |= IWI_DATA_FLAG_NEED_ACK; 1792178354Ssam if (vap->iv_flags & IEEE80211_F_SHPREAMBLE) 1793158089Smlaier flags |= IWI_DATA_FLAG_SHPREAMBLE; 1794158089Smlaier if (IEEE80211_QOS_HAS_SEQ(wh)) { 1795158089Smlaier xflags |= IWI_DATA_XFLAG_QOS; 1796149338Sdamien cap = &ic->ic_wme.wme_chanParams; 1797158089Smlaier if (!cap->cap_wmeParams[ac].wmep_noackPolicy) 1798158089Smlaier flags &= ~IWI_DATA_FLAG_NEED_ACK; 1799158089Smlaier } 1800149338Sdamien 1801150341Sdamien /* 1802150341Sdamien * This is only used in IBSS mode where the firmware expect an index 1803150341Sdamien * in a h/w table instead of a destination address. 1804150341Sdamien */ 1805178354Ssam if (vap->iv_opmode == IEEE80211_M_IBSS) { 1806158089Smlaier if (!ismcast) { 1807158089Smlaier if (in->in_station == -1) { 1808158089Smlaier in->in_station = alloc_unr(sc->sc_unr); 1809158089Smlaier if (in->in_station == -1) { 1810158089Smlaier /* h/w table is full */ 1811287197Sglebius if_inc_counter(ni->ni_vap->iv_ifp, 1812287197Sglebius IFCOUNTER_OERRORS, 1); 1813158089Smlaier m_freem(m0); 1814158089Smlaier ieee80211_free_node(ni); 1815158089Smlaier return 0; 1816158089Smlaier } 1817158089Smlaier iwi_write_ibssnode(sc, 1818158089Smlaier ni->ni_macaddr, in->in_station); 1819158089Smlaier } 1820158089Smlaier staid = in->in_station; 1821158089Smlaier } else { 1822158089Smlaier /* 1823158089Smlaier * Multicast addresses have no associated node 1824158089Smlaier * so there will be no station entry. We reserve 1825158089Smlaier * entry 0 for one mcast address and use that. 1826158089Smlaier * If there are many being used this will be 1827158089Smlaier * expensive and we'll need to do a better job 1828158089Smlaier * but for now this handles the broadcast case. 1829158089Smlaier */ 1830158089Smlaier if (!IEEE80211_ADDR_EQ(wh->i_addr1, sc->sc_mcast)) { 1831158089Smlaier IEEE80211_ADDR_COPY(sc->sc_mcast, wh->i_addr1); 1832158089Smlaier iwi_write_ibssnode(sc, sc->sc_mcast, 0); 1833158089Smlaier } 1834158089Smlaier staid = 0; 1835150245Sdamien } 1836158089Smlaier } else 1837158089Smlaier staid = 0; 1838149338Sdamien 1839260444Skevlo if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { 1840178354Ssam k = ieee80211_crypto_encap(ni, m0); 1841147806Ssam if (k == NULL) { 1842147806Ssam m_freem(m0); 1843146500Sdamien return ENOBUFS; 1844147806Ssam } 1845149338Sdamien 1846149338Sdamien /* packet header may have moved, reset our local pointer */ 1847149338Sdamien wh = mtod(m0, struct ieee80211_frame *); 1848146500Sdamien } 1849146500Sdamien 1850192468Ssam if (ieee80211_radiotap_active_vap(vap)) { 1851145247Sdamien struct iwi_tx_radiotap_header *tap = &sc->sc_txtap; 1852145247Sdamien 1853145247Sdamien tap->wt_flags = 0; 1854145247Sdamien 1855192468Ssam ieee80211_radiotap_tx(vap, m0); 1856145247Sdamien } 1857145247Sdamien 1858149338Sdamien data = &txq->data[txq->cur]; 1859149338Sdamien desc = &txq->desc[txq->cur]; 1860145247Sdamien 1861149338Sdamien /* save and trim IEEE802.11 header */ 1862149338Sdamien m_copydata(m0, 0, hdrlen, (caddr_t)&desc->wh); 1863149338Sdamien m_adj(m0, hdrlen); 1864145247Sdamien 1865149338Sdamien error = bus_dmamap_load_mbuf_sg(txq->data_dmat, data->map, m0, segs, 1866145247Sdamien &nsegs, 0); 1867145247Sdamien if (error != 0 && error != EFBIG) { 1868145247Sdamien device_printf(sc->sc_dev, "could not map mbuf (error %d)\n", 1869145247Sdamien error); 1870145247Sdamien m_freem(m0); 1871145247Sdamien return error; 1872145247Sdamien } 1873145247Sdamien if (error != 0) { 1874243857Sglebius mnew = m_defrag(m0, M_NOWAIT); 1875145247Sdamien if (mnew == NULL) { 1876145247Sdamien device_printf(sc->sc_dev, 1877145247Sdamien "could not defragment mbuf\n"); 1878145247Sdamien m_freem(m0); 1879145247Sdamien return ENOBUFS; 1880145247Sdamien } 1881145247Sdamien m0 = mnew; 1882145247Sdamien 1883149338Sdamien error = bus_dmamap_load_mbuf_sg(txq->data_dmat, data->map, 1884145247Sdamien m0, segs, &nsegs, 0); 1885145247Sdamien if (error != 0) { 1886145247Sdamien device_printf(sc->sc_dev, 1887145247Sdamien "could not map mbuf (error %d)\n", error); 1888145247Sdamien m_freem(m0); 1889145247Sdamien return error; 1890145247Sdamien } 1891145247Sdamien } 1892145247Sdamien 1893145247Sdamien data->m = m0; 1894145247Sdamien data->ni = ni; 1895145247Sdamien 1896145247Sdamien desc->hdr.type = IWI_HDR_TYPE_DATA; 1897145247Sdamien desc->hdr.flags = IWI_HDR_FLAG_IRQ; 1898158089Smlaier desc->station = staid; 1899145247Sdamien desc->cmd = IWI_DATA_CMD_TX; 1900145247Sdamien desc->len = htole16(m0->m_pkthdr.len); 1901158089Smlaier desc->flags = flags; 1902158089Smlaier desc->xflags = xflags; 1903145247Sdamien 1904146500Sdamien#if 0 1905178354Ssam if (vap->iv_flags & IEEE80211_F_PRIVACY) 1906178354Ssam desc->wep_txkey = vap->iv_def_txkey; 1907158089Smlaier else 1908146500Sdamien#endif 1909145247Sdamien desc->flags |= IWI_DATA_FLAG_NO_WEP; 1910145247Sdamien 1911145247Sdamien desc->nseg = htole32(nsegs); 1912145247Sdamien for (i = 0; i < nsegs; i++) { 1913145247Sdamien desc->seg_addr[i] = htole32(segs[i].ds_addr); 1914152389Sdamien desc->seg_len[i] = htole16(segs[i].ds_len); 1915145247Sdamien } 1916145247Sdamien 1917149338Sdamien bus_dmamap_sync(txq->data_dmat, data->map, BUS_DMASYNC_PREWRITE); 1918149338Sdamien bus_dmamap_sync(txq->desc_dmat, txq->desc_map, BUS_DMASYNC_PREWRITE); 1919145247Sdamien 1920149338Sdamien DPRINTFN(5, ("sending data frame txq=%u idx=%u len=%u nseg=%u\n", 1921152389Sdamien ac, txq->cur, le16toh(desc->len), nsegs)); 1922145247Sdamien 1923149338Sdamien txq->queued++; 1924149338Sdamien txq->cur = (txq->cur + 1) % IWI_TX_RING_COUNT; 1925149338Sdamien CSR_WRITE_4(sc, txq->csr_widx, txq->cur); 1926145247Sdamien 1927145247Sdamien return 0; 1928145247Sdamien} 1929145247Sdamien 1930178354Ssamstatic int 1931178354Ssamiwi_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, 1932178354Ssam const struct ieee80211_bpf_params *params) 1933178354Ssam{ 1934178354Ssam /* no support; just discard */ 1935178354Ssam m_freem(m); 1936178354Ssam ieee80211_free_node(ni); 1937178354Ssam return 0; 1938178354Ssam} 1939178354Ssam 1940287197Sglebiusstatic int 1941287197Sglebiusiwi_transmit(struct ieee80211com *ic, struct mbuf *m) 1942287197Sglebius{ 1943287197Sglebius struct iwi_softc *sc = ic->ic_softc; 1944287197Sglebius int error; 1945287197Sglebius IWI_LOCK_DECL; 1946287197Sglebius 1947287197Sglebius IWI_LOCK(sc); 1948287197Sglebius if (!sc->sc_running) { 1949287197Sglebius IWI_UNLOCK(sc); 1950287197Sglebius return (ENXIO); 1951287197Sglebius } 1952287197Sglebius error = mbufq_enqueue(&sc->sc_snd, m); 1953287197Sglebius if (error) { 1954287197Sglebius IWI_UNLOCK(sc); 1955287197Sglebius return (error); 1956287197Sglebius } 1957287197Sglebius iwi_start(sc); 1958287197Sglebius IWI_UNLOCK(sc); 1959287197Sglebius return (0); 1960287197Sglebius} 1961287197Sglebius 1962145247Sdamienstatic void 1963287197Sglebiusiwi_start(struct iwi_softc *sc) 1964145247Sdamien{ 1965178354Ssam struct mbuf *m; 1966145247Sdamien struct ieee80211_node *ni; 1967150245Sdamien int ac; 1968145247Sdamien 1969178354Ssam IWI_LOCK_ASSERT(sc); 1970145247Sdamien 1971287197Sglebius while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) { 1972178354Ssam ac = M_WME_GETAC(m); 1973178354Ssam if (sc->txq[ac].queued > IWI_TX_RING_COUNT - 8) { 1974178354Ssam /* there is no place left in this ring; tail drop */ 1975178354Ssam /* XXX tail drop */ 1976287197Sglebius mbufq_prepend(&sc->sc_snd, m); 1977178354Ssam break; 1978178354Ssam } 1979178354Ssam ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; 1980287197Sglebius if (iwi_tx_start(sc, m, ni, ac) != 0) { 1981287197Sglebius if_inc_counter(ni->ni_vap->iv_ifp, 1982287197Sglebius IFCOUNTER_OERRORS, 1); 1983314222Savos ieee80211_free_node(ni); 1984145247Sdamien break; 1985145247Sdamien } 1986145247Sdamien sc->sc_tx_timer = 5; 1987145247Sdamien } 1988178354Ssam} 1989145247Sdamien 1990178354Ssamstatic void 1991170530Ssamiwi_watchdog(void *arg) 1992145247Sdamien{ 1993170530Ssam struct iwi_softc *sc = arg; 1994287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 1995145247Sdamien 1996170530Ssam IWI_LOCK_ASSERT(sc); 1997145247Sdamien 1998145247Sdamien if (sc->sc_tx_timer > 0) { 1999145247Sdamien if (--sc->sc_tx_timer == 0) { 2000287197Sglebius device_printf(sc->sc_dev, "device timeout\n"); 2001287197Sglebius counter_u64_add(ic->ic_oerrors, 1); 2002191746Sthompsa ieee80211_runtask(ic, &sc->sc_restarttask); 2003145247Sdamien } 2004158089Smlaier } 2005170530Ssam if (sc->sc_state_timer > 0) { 2006170530Ssam if (--sc->sc_state_timer == 0) { 2007287197Sglebius device_printf(sc->sc_dev, 2008287197Sglebius "firmware stuck in state %d, resetting\n", 2009170530Ssam sc->fw_state); 2010287197Sglebius if (sc->fw_state == IWI_FW_SCANNING) 2011178354Ssam ieee80211_cancel_scan(TAILQ_FIRST(&ic->ic_vaps)); 2012191746Sthompsa ieee80211_runtask(ic, &sc->sc_restarttask); 2013170530Ssam sc->sc_state_timer = 3; 2014158089Smlaier } 2015158089Smlaier } 2016170530Ssam if (sc->sc_busy_timer > 0) { 2017170530Ssam if (--sc->sc_busy_timer == 0) { 2018287197Sglebius device_printf(sc->sc_dev, 2019287197Sglebius "firmware command timeout, resetting\n"); 2020191746Sthompsa ieee80211_runtask(ic, &sc->sc_restarttask); 2021170530Ssam } 2022170530Ssam } 2023178354Ssam callout_reset(&sc->sc_wdtimer, hz, iwi_watchdog, sc); 2024145247Sdamien} 2025145247Sdamien 2026287197Sglebiusstatic void 2027287197Sglebiusiwi_parent(struct ieee80211com *ic) 2028287197Sglebius{ 2029287197Sglebius struct iwi_softc *sc = ic->ic_softc; 2030287197Sglebius int startall = 0; 2031287197Sglebius IWI_LOCK_DECL; 2032287197Sglebius 2033287197Sglebius IWI_LOCK(sc); 2034287197Sglebius if (ic->ic_nrunning > 0) { 2035287197Sglebius if (!sc->sc_running) { 2036287197Sglebius iwi_init_locked(sc); 2037287197Sglebius startall = 1; 2038287197Sglebius } 2039287197Sglebius } else if (sc->sc_running) 2040287197Sglebius iwi_stop_locked(sc); 2041287197Sglebius IWI_UNLOCK(sc); 2042287197Sglebius if (startall) 2043287197Sglebius ieee80211_start_all(ic); 2044287197Sglebius} 2045287197Sglebius 2046145247Sdamienstatic int 2047287197Sglebiusiwi_ioctl(struct ieee80211com *ic, u_long cmd, void *data) 2048145247Sdamien{ 2049287197Sglebius struct ifreq *ifr = data; 2050287197Sglebius struct iwi_softc *sc = ic->ic_softc; 2051287197Sglebius int error; 2052158089Smlaier IWI_LOCK_DECL; 2053145247Sdamien 2054287197Sglebius IWI_LOCK(sc); 2055145247Sdamien switch (cmd) { 2056263920Sadrian case SIOCGIWISTATS: 2057263920Sadrian /* XXX validate permissions/memory/etc? */ 2058332288Sbrooks error = copyout(&sc->sc_linkqual, ifr_data_get_ptr(ifr), 2059263920Sadrian sizeof(struct iwi_notif_link_quality)); 2060263920Sadrian break; 2061263920Sadrian case SIOCZIWISTATS: 2062263920Sadrian memset(&sc->sc_linkqual, 0, 2063263920Sadrian sizeof(struct iwi_notif_link_quality)); 2064263920Sadrian error = 0; 2065263920Sadrian break; 2066178704Sthompsa default: 2067287197Sglebius error = ENOTTY; 2068178704Sthompsa break; 2069145247Sdamien } 2070287197Sglebius IWI_UNLOCK(sc); 2071287197Sglebius 2072287197Sglebius return (error); 2073145247Sdamien} 2074145247Sdamien 2075145247Sdamienstatic void 2076145247Sdamieniwi_stop_master(struct iwi_softc *sc) 2077145247Sdamien{ 2078145247Sdamien uint32_t tmp; 2079145247Sdamien int ntries; 2080145247Sdamien 2081145247Sdamien /* disable interrupts */ 2082145247Sdamien CSR_WRITE_4(sc, IWI_CSR_INTR_MASK, 0); 2083145247Sdamien 2084145247Sdamien CSR_WRITE_4(sc, IWI_CSR_RST, IWI_RST_STOP_MASTER); 2085145247Sdamien for (ntries = 0; ntries < 5; ntries++) { 2086145247Sdamien if (CSR_READ_4(sc, IWI_CSR_RST) & IWI_RST_MASTER_DISABLED) 2087145247Sdamien break; 2088145247Sdamien DELAY(10); 2089145247Sdamien } 2090145247Sdamien if (ntries == 5) 2091145247Sdamien device_printf(sc->sc_dev, "timeout waiting for master\n"); 2092145247Sdamien 2093145247Sdamien tmp = CSR_READ_4(sc, IWI_CSR_RST); 2094145247Sdamien CSR_WRITE_4(sc, IWI_CSR_RST, tmp | IWI_RST_PRINCETON_RESET); 2095145247Sdamien 2096145247Sdamien sc->flags &= ~IWI_FLAG_FW_INITED; 2097145247Sdamien} 2098145247Sdamien 2099145247Sdamienstatic int 2100145247Sdamieniwi_reset(struct iwi_softc *sc) 2101145247Sdamien{ 2102145247Sdamien uint32_t tmp; 2103145247Sdamien int i, ntries; 2104145247Sdamien 2105145247Sdamien iwi_stop_master(sc); 2106145247Sdamien 2107145247Sdamien tmp = CSR_READ_4(sc, IWI_CSR_CTL); 2108145247Sdamien CSR_WRITE_4(sc, IWI_CSR_CTL, tmp | IWI_CTL_INIT); 2109145247Sdamien 2110145247Sdamien CSR_WRITE_4(sc, IWI_CSR_READ_INT, IWI_READ_INT_INIT_HOST); 2111145247Sdamien 2112145247Sdamien /* wait for clock stabilization */ 2113145247Sdamien for (ntries = 0; ntries < 1000; ntries++) { 2114145247Sdamien if (CSR_READ_4(sc, IWI_CSR_CTL) & IWI_CTL_CLOCK_READY) 2115145247Sdamien break; 2116145247Sdamien DELAY(200); 2117145247Sdamien } 2118145247Sdamien if (ntries == 1000) { 2119145247Sdamien device_printf(sc->sc_dev, 2120145247Sdamien "timeout waiting for clock stabilization\n"); 2121145247Sdamien return EIO; 2122145247Sdamien } 2123145247Sdamien 2124145247Sdamien tmp = CSR_READ_4(sc, IWI_CSR_RST); 2125145247Sdamien CSR_WRITE_4(sc, IWI_CSR_RST, tmp | IWI_RST_SOFT_RESET); 2126145247Sdamien 2127145247Sdamien DELAY(10); 2128145247Sdamien 2129145247Sdamien tmp = CSR_READ_4(sc, IWI_CSR_CTL); 2130145247Sdamien CSR_WRITE_4(sc, IWI_CSR_CTL, tmp | IWI_CTL_INIT); 2131145247Sdamien 2132145247Sdamien /* clear NIC memory */ 2133145247Sdamien CSR_WRITE_4(sc, IWI_CSR_AUTOINC_ADDR, 0); 2134145247Sdamien for (i = 0; i < 0xc000; i++) 2135145247Sdamien CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, 0); 2136145247Sdamien 2137145247Sdamien return 0; 2138145247Sdamien} 2139145247Sdamien 2140158089Smlaierstatic const struct iwi_firmware_ohdr * 2141158089Smlaieriwi_setup_ofw(struct iwi_softc *sc, struct iwi_fw *fw) 2142158089Smlaier{ 2143166756Sluigi const struct firmware *fp = fw->fp; 2144158089Smlaier const struct iwi_firmware_ohdr *hdr; 2145158089Smlaier 2146158089Smlaier if (fp->datasize < sizeof (struct iwi_firmware_ohdr)) { 2147158089Smlaier device_printf(sc->sc_dev, "image '%s' too small\n", fp->name); 2148158089Smlaier return NULL; 2149158089Smlaier } 2150158089Smlaier hdr = (const struct iwi_firmware_ohdr *)fp->data; 2151158089Smlaier if ((IWI_FW_GET_MAJOR(le32toh(hdr->version)) != IWI_FW_REQ_MAJOR) || 2152158089Smlaier (IWI_FW_GET_MINOR(le32toh(hdr->version)) != IWI_FW_REQ_MINOR)) { 2153158089Smlaier device_printf(sc->sc_dev, "version for '%s' %d.%d != %d.%d\n", 2154158089Smlaier fp->name, IWI_FW_GET_MAJOR(le32toh(hdr->version)), 2155158089Smlaier IWI_FW_GET_MINOR(le32toh(hdr->version)), IWI_FW_REQ_MAJOR, 2156158089Smlaier IWI_FW_REQ_MINOR); 2157158089Smlaier return NULL; 2158158089Smlaier } 2159158089Smlaier fw->data = ((const char *) fp->data) + sizeof(struct iwi_firmware_ohdr); 2160158089Smlaier fw->size = fp->datasize - sizeof(struct iwi_firmware_ohdr); 2161158089Smlaier fw->name = fp->name; 2162158089Smlaier return hdr; 2163158089Smlaier} 2164158089Smlaier 2165158089Smlaierstatic const struct iwi_firmware_ohdr * 2166158089Smlaieriwi_setup_oucode(struct iwi_softc *sc, struct iwi_fw *fw) 2167158089Smlaier{ 2168158089Smlaier const struct iwi_firmware_ohdr *hdr; 2169158089Smlaier 2170158089Smlaier hdr = iwi_setup_ofw(sc, fw); 2171158089Smlaier if (hdr != NULL && le32toh(hdr->mode) != IWI_FW_MODE_UCODE) { 2172158089Smlaier device_printf(sc->sc_dev, "%s is not a ucode image\n", 2173158089Smlaier fw->name); 2174158089Smlaier hdr = NULL; 2175158089Smlaier } 2176158089Smlaier return hdr; 2177158089Smlaier} 2178158089Smlaier 2179158089Smlaierstatic void 2180158089Smlaieriwi_getfw(struct iwi_fw *fw, const char *fwname, 2181158089Smlaier struct iwi_fw *uc, const char *ucname) 2182158089Smlaier{ 2183158089Smlaier if (fw->fp == NULL) 2184158089Smlaier fw->fp = firmware_get(fwname); 2185158089Smlaier /* NB: pre-3.0 ucode is packaged separately */ 2186158089Smlaier if (uc->fp == NULL && fw->fp != NULL && fw->fp->version < 300) 2187158089Smlaier uc->fp = firmware_get(ucname); 2188158089Smlaier} 2189158089Smlaier 2190158089Smlaier/* 2191158089Smlaier * Get the required firmware images if not already loaded. 2192158089Smlaier * Note that we hold firmware images so long as the device 2193158089Smlaier * is marked up in case we need to reload them on device init. 2194158089Smlaier * This is necessary because we re-init the device sometimes 2195158089Smlaier * from a context where we cannot read from the filesystem 2196158089Smlaier * (e.g. from the taskqueue thread when rfkill is re-enabled). 2197166848Sluigi * XXX return 0 on success, 1 on error. 2198158089Smlaier * 2199158089Smlaier * NB: the order of get'ing and put'ing images here is 2200158089Smlaier * intentional to support handling firmware images bundled 2201158089Smlaier * by operating mode and/or all together in one file with 2202158089Smlaier * the boot firmware as "master". 2203158089Smlaier */ 2204145247Sdamienstatic int 2205178354Ssamiwi_get_firmware(struct iwi_softc *sc, enum ieee80211_opmode opmode) 2206145247Sdamien{ 2207158089Smlaier const struct iwi_firmware_hdr *hdr; 2208166756Sluigi const struct firmware *fp; 2209158089Smlaier 2210158089Smlaier /* invalidate cached firmware on mode change */ 2211178354Ssam if (sc->fw_mode != opmode) 2212158089Smlaier iwi_put_firmware(sc); 2213158089Smlaier 2214178354Ssam switch (opmode) { 2215158089Smlaier case IEEE80211_M_STA: 2216158089Smlaier iwi_getfw(&sc->fw_fw, "iwi_bss", &sc->fw_uc, "iwi_ucode_bss"); 2217158089Smlaier break; 2218158089Smlaier case IEEE80211_M_IBSS: 2219158089Smlaier iwi_getfw(&sc->fw_fw, "iwi_ibss", &sc->fw_uc, "iwi_ucode_ibss"); 2220158089Smlaier break; 2221158089Smlaier case IEEE80211_M_MONITOR: 2222158089Smlaier iwi_getfw(&sc->fw_fw, "iwi_monitor", 2223158089Smlaier &sc->fw_uc, "iwi_ucode_monitor"); 2224158089Smlaier break; 2225158089Smlaier default: 2226195562Srpaulo device_printf(sc->sc_dev, "unknown opmode %d\n", opmode); 2227195562Srpaulo return EINVAL; 2228158089Smlaier } 2229158089Smlaier fp = sc->fw_fw.fp; 2230158089Smlaier if (fp == NULL) { 2231158089Smlaier device_printf(sc->sc_dev, "could not load firmware\n"); 2232158089Smlaier goto bad; 2233158089Smlaier } 2234158089Smlaier if (fp->version < 300) { 2235158089Smlaier /* 2236158089Smlaier * Firmware prior to 3.0 was packaged as separate 2237158089Smlaier * boot, firmware, and ucode images. Verify the 2238158089Smlaier * ucode image was read in, retrieve the boot image 2239158089Smlaier * if needed, and check version stamps for consistency. 2240158089Smlaier * The version stamps in the data are also checked 2241158089Smlaier * above; this is a bit paranoid but is a cheap 2242158089Smlaier * safeguard against mis-packaging. 2243158089Smlaier */ 2244158089Smlaier if (sc->fw_uc.fp == NULL) { 2245158089Smlaier device_printf(sc->sc_dev, "could not load ucode\n"); 2246158089Smlaier goto bad; 2247158089Smlaier } 2248158089Smlaier if (sc->fw_boot.fp == NULL) { 2249158089Smlaier sc->fw_boot.fp = firmware_get("iwi_boot"); 2250158089Smlaier if (sc->fw_boot.fp == NULL) { 2251158089Smlaier device_printf(sc->sc_dev, 2252158089Smlaier "could not load boot firmware\n"); 2253158089Smlaier goto bad; 2254158089Smlaier } 2255158089Smlaier } 2256158089Smlaier if (sc->fw_boot.fp->version != sc->fw_fw.fp->version || 2257158089Smlaier sc->fw_boot.fp->version != sc->fw_uc.fp->version) { 2258158089Smlaier device_printf(sc->sc_dev, 2259158089Smlaier "firmware version mismatch: " 2260158089Smlaier "'%s' is %d, '%s' is %d, '%s' is %d\n", 2261158089Smlaier sc->fw_boot.fp->name, sc->fw_boot.fp->version, 2262158089Smlaier sc->fw_uc.fp->name, sc->fw_uc.fp->version, 2263158089Smlaier sc->fw_fw.fp->name, sc->fw_fw.fp->version 2264158089Smlaier ); 2265158089Smlaier goto bad; 2266158089Smlaier } 2267158089Smlaier /* 2268158089Smlaier * Check and setup each image. 2269158089Smlaier */ 2270158089Smlaier if (iwi_setup_oucode(sc, &sc->fw_uc) == NULL || 2271158089Smlaier iwi_setup_ofw(sc, &sc->fw_boot) == NULL || 2272158089Smlaier iwi_setup_ofw(sc, &sc->fw_fw) == NULL) 2273158089Smlaier goto bad; 2274158089Smlaier } else { 2275158089Smlaier /* 2276158089Smlaier * Check and setup combined image. 2277158089Smlaier */ 2278166848Sluigi if (fp->datasize < sizeof(struct iwi_firmware_hdr)) { 2279158089Smlaier device_printf(sc->sc_dev, "image '%s' too small\n", 2280158089Smlaier fp->name); 2281158089Smlaier goto bad; 2282158089Smlaier } 2283158089Smlaier hdr = (const struct iwi_firmware_hdr *)fp->data; 2284166848Sluigi if (fp->datasize < sizeof(*hdr) + le32toh(hdr->bsize) + le32toh(hdr->usize) 2285166848Sluigi + le32toh(hdr->fsize)) { 2286158089Smlaier device_printf(sc->sc_dev, "image '%s' too small (2)\n", 2287158089Smlaier fp->name); 2288158089Smlaier goto bad; 2289158089Smlaier } 2290158089Smlaier sc->fw_boot.data = ((const char *) fp->data) + sizeof(*hdr); 2291166848Sluigi sc->fw_boot.size = le32toh(hdr->bsize); 2292158089Smlaier sc->fw_boot.name = fp->name; 2293158089Smlaier sc->fw_uc.data = sc->fw_boot.data + sc->fw_boot.size; 2294166848Sluigi sc->fw_uc.size = le32toh(hdr->usize); 2295158089Smlaier sc->fw_uc.name = fp->name; 2296158089Smlaier sc->fw_fw.data = sc->fw_uc.data + sc->fw_uc.size; 2297166848Sluigi sc->fw_fw.size = le32toh(hdr->fsize); 2298158089Smlaier sc->fw_fw.name = fp->name; 2299158089Smlaier } 2300166848Sluigi#if 0 2301166848Sluigi device_printf(sc->sc_dev, "boot %d ucode %d fw %d bytes\n", 2302166848Sluigi sc->fw_boot.size, sc->fw_uc.size, sc->fw_fw.size); 2303166848Sluigi#endif 2304158089Smlaier 2305178354Ssam sc->fw_mode = opmode; 2306166848Sluigi return 0; 2307158089Smlaierbad: 2308158089Smlaier iwi_put_firmware(sc); 2309166848Sluigi return 1; 2310158089Smlaier} 2311158089Smlaier 2312158089Smlaierstatic void 2313158089Smlaieriwi_put_fw(struct iwi_fw *fw) 2314158089Smlaier{ 2315158089Smlaier if (fw->fp != NULL) { 2316158089Smlaier firmware_put(fw->fp, FIRMWARE_UNLOAD); 2317158089Smlaier fw->fp = NULL; 2318158089Smlaier } 2319158089Smlaier fw->data = NULL; 2320158089Smlaier fw->size = 0; 2321158089Smlaier fw->name = NULL; 2322158089Smlaier} 2323158089Smlaier 2324158089Smlaier/* 2325158089Smlaier * Release any cached firmware images. 2326158089Smlaier */ 2327158089Smlaierstatic void 2328158089Smlaieriwi_put_firmware(struct iwi_softc *sc) 2329158089Smlaier{ 2330158089Smlaier iwi_put_fw(&sc->fw_uc); 2331158089Smlaier iwi_put_fw(&sc->fw_fw); 2332158089Smlaier iwi_put_fw(&sc->fw_boot); 2333158089Smlaier} 2334158089Smlaier 2335158089Smlaierstatic int 2336158089Smlaieriwi_load_ucode(struct iwi_softc *sc, const struct iwi_fw *fw) 2337158089Smlaier{ 2338158089Smlaier uint32_t tmp; 2339156546Sdamien const uint16_t *w; 2340158089Smlaier const char *uc = fw->data; 2341158089Smlaier size_t size = fw->size; 2342158089Smlaier int i, ntries, error; 2343145247Sdamien 2344170530Ssam IWI_LOCK_ASSERT(sc); 2345158089Smlaier error = 0; 2346145247Sdamien CSR_WRITE_4(sc, IWI_CSR_RST, CSR_READ_4(sc, IWI_CSR_RST) | 2347145247Sdamien IWI_RST_STOP_MASTER); 2348145247Sdamien for (ntries = 0; ntries < 5; ntries++) { 2349145247Sdamien if (CSR_READ_4(sc, IWI_CSR_RST) & IWI_RST_MASTER_DISABLED) 2350145247Sdamien break; 2351145247Sdamien DELAY(10); 2352145247Sdamien } 2353145247Sdamien if (ntries == 5) { 2354145247Sdamien device_printf(sc->sc_dev, "timeout waiting for master\n"); 2355158089Smlaier error = EIO; 2356158089Smlaier goto fail; 2357145247Sdamien } 2358145247Sdamien 2359145247Sdamien MEM_WRITE_4(sc, 0x3000e0, 0x80000000); 2360145247Sdamien DELAY(5000); 2361145247Sdamien 2362145247Sdamien tmp = CSR_READ_4(sc, IWI_CSR_RST); 2363145247Sdamien tmp &= ~IWI_RST_PRINCETON_RESET; 2364145247Sdamien CSR_WRITE_4(sc, IWI_CSR_RST, tmp); 2365145247Sdamien 2366145247Sdamien DELAY(5000); 2367145247Sdamien MEM_WRITE_4(sc, 0x3000e0, 0); 2368145247Sdamien DELAY(1000); 2369158089Smlaier MEM_WRITE_4(sc, IWI_MEM_EEPROM_EVENT, 1); 2370145247Sdamien DELAY(1000); 2371158089Smlaier MEM_WRITE_4(sc, IWI_MEM_EEPROM_EVENT, 0); 2372145247Sdamien DELAY(1000); 2373145247Sdamien MEM_WRITE_1(sc, 0x200000, 0x00); 2374145247Sdamien MEM_WRITE_1(sc, 0x200000, 0x40); 2375145247Sdamien DELAY(1000); 2376145247Sdamien 2377145247Sdamien /* write microcode into adapter memory */ 2378158089Smlaier for (w = (const uint16_t *)uc; size > 0; w++, size -= 2) 2379152389Sdamien MEM_WRITE_2(sc, 0x200010, htole16(*w)); 2380145247Sdamien 2381145247Sdamien MEM_WRITE_1(sc, 0x200000, 0x00); 2382145247Sdamien MEM_WRITE_1(sc, 0x200000, 0x80); 2383145247Sdamien 2384145247Sdamien /* wait until we get an answer */ 2385145247Sdamien for (ntries = 0; ntries < 100; ntries++) { 2386145247Sdamien if (MEM_READ_1(sc, 0x200000) & 1) 2387145247Sdamien break; 2388145247Sdamien DELAY(100); 2389145247Sdamien } 2390145247Sdamien if (ntries == 100) { 2391145247Sdamien device_printf(sc->sc_dev, 2392145247Sdamien "timeout waiting for ucode to initialize\n"); 2393158089Smlaier error = EIO; 2394158089Smlaier goto fail; 2395145247Sdamien } 2396145247Sdamien 2397145247Sdamien /* read the answer or the firmware will not initialize properly */ 2398145247Sdamien for (i = 0; i < 7; i++) 2399145247Sdamien MEM_READ_4(sc, 0x200004); 2400145247Sdamien 2401145247Sdamien MEM_WRITE_1(sc, 0x200000, 0x00); 2402145247Sdamien 2403158089Smlaierfail: 2404158089Smlaier return error; 2405145247Sdamien} 2406145247Sdamien 2407145247Sdamien/* macro to handle unaligned little endian data in firmware image */ 2408145247Sdamien#define GETLE32(p) ((p)[0] | (p)[1] << 8 | (p)[2] << 16 | (p)[3] << 24) 2409145247Sdamien 2410145247Sdamienstatic int 2411158089Smlaieriwi_load_firmware(struct iwi_softc *sc, const struct iwi_fw *fw) 2412145247Sdamien{ 2413145247Sdamien u_char *p, *end; 2414145247Sdamien uint32_t sentinel, ctl, src, dst, sum, len, mlen, tmp; 2415152629Sscottl int ntries, error; 2416145247Sdamien 2417170530Ssam IWI_LOCK_ASSERT(sc); 2418178354Ssam 2419145247Sdamien /* copy firmware image to DMA memory */ 2420158089Smlaier memcpy(sc->fw_virtaddr, fw->data, fw->size); 2421145247Sdamien 2422145247Sdamien /* make sure the adapter will get up-to-date values */ 2423158089Smlaier bus_dmamap_sync(sc->fw_dmat, sc->fw_map, BUS_DMASYNC_PREWRITE); 2424145247Sdamien 2425145247Sdamien /* tell the adapter where the command blocks are stored */ 2426145247Sdamien MEM_WRITE_4(sc, 0x3000a0, 0x27000); 2427145247Sdamien 2428145247Sdamien /* 2429145247Sdamien * Store command blocks into adapter's internal memory using register 2430145247Sdamien * indirections. The adapter will read the firmware image through DMA 2431145247Sdamien * using information stored in command blocks. 2432145247Sdamien */ 2433158089Smlaier src = sc->fw_physaddr; 2434158089Smlaier p = sc->fw_virtaddr; 2435158089Smlaier end = p + fw->size; 2436145247Sdamien CSR_WRITE_4(sc, IWI_CSR_AUTOINC_ADDR, 0x27000); 2437145247Sdamien 2438145247Sdamien while (p < end) { 2439145247Sdamien dst = GETLE32(p); p += 4; src += 4; 2440145247Sdamien len = GETLE32(p); p += 4; src += 4; 2441145247Sdamien p += len; 2442145247Sdamien 2443145247Sdamien while (len > 0) { 2444145247Sdamien mlen = min(len, IWI_CB_MAXDATALEN); 2445145247Sdamien 2446145247Sdamien ctl = IWI_CB_DEFAULT_CTL | mlen; 2447145247Sdamien sum = ctl ^ src ^ dst; 2448145247Sdamien 2449145247Sdamien /* write a command block */ 2450145247Sdamien CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, ctl); 2451145247Sdamien CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, src); 2452145247Sdamien CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, dst); 2453145247Sdamien CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, sum); 2454145247Sdamien 2455145247Sdamien src += mlen; 2456145247Sdamien dst += mlen; 2457145247Sdamien len -= mlen; 2458145247Sdamien } 2459145247Sdamien } 2460145247Sdamien 2461145247Sdamien /* write a fictive final command block (sentinel) */ 2462145247Sdamien sentinel = CSR_READ_4(sc, IWI_CSR_AUTOINC_ADDR); 2463145247Sdamien CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, 0); 2464145247Sdamien 2465145247Sdamien tmp = CSR_READ_4(sc, IWI_CSR_RST); 2466145247Sdamien tmp &= ~(IWI_RST_MASTER_DISABLED | IWI_RST_STOP_MASTER); 2467145247Sdamien CSR_WRITE_4(sc, IWI_CSR_RST, tmp); 2468145247Sdamien 2469145247Sdamien /* tell the adapter to start processing command blocks */ 2470145247Sdamien MEM_WRITE_4(sc, 0x3000a4, 0x540100); 2471145247Sdamien 2472152611Sdamien /* wait until the adapter reaches the sentinel */ 2473145247Sdamien for (ntries = 0; ntries < 400; ntries++) { 2474145247Sdamien if (MEM_READ_4(sc, 0x3000d0) >= sentinel) 2475145247Sdamien break; 2476145247Sdamien DELAY(100); 2477145247Sdamien } 2478166848Sluigi /* sync dma, just in case */ 2479166848Sluigi bus_dmamap_sync(sc->fw_dmat, sc->fw_map, BUS_DMASYNC_POSTWRITE); 2480145247Sdamien if (ntries == 400) { 2481145247Sdamien device_printf(sc->sc_dev, 2482158089Smlaier "timeout processing command blocks for %s firmware\n", 2483158089Smlaier fw->name); 2484166848Sluigi return EIO; 2485145247Sdamien } 2486145247Sdamien 2487145247Sdamien /* we're done with command blocks processing */ 2488145247Sdamien MEM_WRITE_4(sc, 0x3000a4, 0x540c00); 2489145247Sdamien 2490152611Sdamien /* allow interrupts so we know when the firmware is ready */ 2491145247Sdamien CSR_WRITE_4(sc, IWI_CSR_INTR_MASK, IWI_INTR_MASK); 2492145247Sdamien 2493145247Sdamien /* tell the adapter to initialize the firmware */ 2494145247Sdamien CSR_WRITE_4(sc, IWI_CSR_RST, 0); 2495145247Sdamien 2496145247Sdamien tmp = CSR_READ_4(sc, IWI_CSR_CTL); 2497145247Sdamien CSR_WRITE_4(sc, IWI_CSR_CTL, tmp | IWI_CTL_ALLOW_STANDBY); 2498145247Sdamien 2499145247Sdamien /* wait at most one second for firmware initialization to complete */ 2500145247Sdamien if ((error = msleep(sc, &sc->sc_mtx, 0, "iwiinit", hz)) != 0) { 2501158089Smlaier device_printf(sc->sc_dev, "timeout waiting for %s firmware " 2502158089Smlaier "initialization to complete\n", fw->name); 2503145247Sdamien } 2504145247Sdamien 2505145247Sdamien return error; 2506145247Sdamien} 2507145247Sdamien 2508145247Sdamienstatic int 2509178354Ssamiwi_setpowermode(struct iwi_softc *sc, struct ieee80211vap *vap) 2510158089Smlaier{ 2511158089Smlaier uint32_t data; 2512158089Smlaier 2513178354Ssam if (vap->iv_flags & IEEE80211_F_PMGTON) { 2514158089Smlaier /* XXX set more fine-grained operation */ 2515158089Smlaier data = htole32(IWI_POWER_MODE_MAX); 2516158089Smlaier } else 2517158089Smlaier data = htole32(IWI_POWER_MODE_CAM); 2518158089Smlaier 2519158089Smlaier DPRINTF(("Setting power mode to %u\n", le32toh(data))); 2520158089Smlaier return iwi_cmd(sc, IWI_CMD_SET_POWER_MODE, &data, sizeof data); 2521158089Smlaier} 2522158089Smlaier 2523158089Smlaierstatic int 2524178354Ssamiwi_setwepkeys(struct iwi_softc *sc, struct ieee80211vap *vap) 2525158089Smlaier{ 2526158089Smlaier struct iwi_wep_key wepkey; 2527158089Smlaier struct ieee80211_key *wk; 2528158089Smlaier int error, i; 2529158089Smlaier 2530158089Smlaier for (i = 0; i < IEEE80211_WEP_NKID; i++) { 2531178354Ssam wk = &vap->iv_nw_keys[i]; 2532158089Smlaier 2533158089Smlaier wepkey.cmd = IWI_WEP_KEY_CMD_SETKEY; 2534158089Smlaier wepkey.idx = i; 2535158089Smlaier wepkey.len = wk->wk_keylen; 2536158089Smlaier memset(wepkey.key, 0, sizeof wepkey.key); 2537158089Smlaier memcpy(wepkey.key, wk->wk_key, wk->wk_keylen); 2538158089Smlaier DPRINTF(("Setting wep key index %u len %u\n", wepkey.idx, 2539158089Smlaier wepkey.len)); 2540158089Smlaier error = iwi_cmd(sc, IWI_CMD_SET_WEP_KEY, &wepkey, 2541158089Smlaier sizeof wepkey); 2542158089Smlaier if (error != 0) 2543158089Smlaier return error; 2544158089Smlaier } 2545158089Smlaier return 0; 2546158089Smlaier} 2547158089Smlaier 2548158089Smlaierstatic int 2549145247Sdamieniwi_config(struct iwi_softc *sc) 2550145247Sdamien{ 2551287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 2552347511Savos struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 2553145247Sdamien struct iwi_configuration config; 2554145247Sdamien struct iwi_rateset rs; 2555145247Sdamien struct iwi_txpower power; 2556347511Savos uint8_t *macaddr; 2557145247Sdamien uint32_t data; 2558145247Sdamien int error, i; 2559178354Ssam 2560170530Ssam IWI_LOCK_ASSERT(sc); 2561145247Sdamien 2562347511Savos macaddr = vap ? vap->iv_myaddr : ic->ic_macaddr; 2563347511Savos DPRINTF(("Setting MAC address to %6D\n", macaddr, ":")); 2564347511Savos error = iwi_cmd(sc, IWI_CMD_SET_MAC_ADDRESS, macaddr, 2565158089Smlaier IEEE80211_ADDR_LEN); 2566145247Sdamien if (error != 0) 2567145247Sdamien return error; 2568145247Sdamien 2569145247Sdamien memset(&config, 0, sizeof config); 2570145247Sdamien config.bluetooth_coexistence = sc->bluetooth; 2571158089Smlaier config.silence_threshold = 0x1e; 2572146500Sdamien config.antenna = sc->antenna; 2573145247Sdamien config.multicast_enabled = 1; 2574146500Sdamien config.answer_pbreq = (ic->ic_opmode == IEEE80211_M_IBSS) ? 1 : 0; 2575146500Sdamien config.disable_unicast_decryption = 1; 2576146500Sdamien config.disable_multicast_decryption = 1; 2577213729Sbschmidt if (ic->ic_opmode == IEEE80211_M_MONITOR) { 2578213729Sbschmidt config.allow_invalid_frames = 1; 2579213729Sbschmidt config.allow_beacon_and_probe_resp = 1; 2580213729Sbschmidt config.allow_mgt = 1; 2581213729Sbschmidt } 2582145247Sdamien DPRINTF(("Configuring adapter\n")); 2583158089Smlaier error = iwi_cmd(sc, IWI_CMD_SET_CONFIG, &config, sizeof config); 2584145247Sdamien if (error != 0) 2585145247Sdamien return error; 2586145247Sdamien if (ic->ic_opmode == IEEE80211_M_IBSS) { 2587145247Sdamien power.mode = IWI_MODE_11B; 2588145247Sdamien power.nchan = 11; 2589145247Sdamien for (i = 0; i < 11; i++) { 2590145247Sdamien power.chan[i].chan = i + 1; 2591145247Sdamien power.chan[i].power = IWI_TXPOWER_MAX; 2592145247Sdamien } 2593145247Sdamien DPRINTF(("Setting .11b channels tx power\n")); 2594158089Smlaier error = iwi_cmd(sc, IWI_CMD_SET_TX_POWER, &power, sizeof power); 2595145247Sdamien if (error != 0) 2596145247Sdamien return error; 2597145247Sdamien 2598145247Sdamien power.mode = IWI_MODE_11G; 2599145247Sdamien DPRINTF(("Setting .11g channels tx power\n")); 2600158089Smlaier error = iwi_cmd(sc, IWI_CMD_SET_TX_POWER, &power, sizeof power); 2601145247Sdamien if (error != 0) 2602145247Sdamien return error; 2603145247Sdamien } 2604145247Sdamien 2605171034Sthompsa memset(&rs, 0, sizeof rs); 2606145247Sdamien rs.mode = IWI_MODE_11G; 2607145247Sdamien rs.type = IWI_RATESET_TYPE_SUPPORTED; 2608145247Sdamien rs.nrates = ic->ic_sup_rates[IEEE80211_MODE_11G].rs_nrates; 2609145247Sdamien memcpy(rs.rates, ic->ic_sup_rates[IEEE80211_MODE_11G].rs_rates, 2610145247Sdamien rs.nrates); 2611145247Sdamien DPRINTF(("Setting .11bg supported rates (%u)\n", rs.nrates)); 2612158089Smlaier error = iwi_cmd(sc, IWI_CMD_SET_RATES, &rs, sizeof rs); 2613145247Sdamien if (error != 0) 2614145247Sdamien return error; 2615145247Sdamien 2616171034Sthompsa memset(&rs, 0, sizeof rs); 2617145247Sdamien rs.mode = IWI_MODE_11A; 2618145247Sdamien rs.type = IWI_RATESET_TYPE_SUPPORTED; 2619145247Sdamien rs.nrates = ic->ic_sup_rates[IEEE80211_MODE_11A].rs_nrates; 2620145247Sdamien memcpy(rs.rates, ic->ic_sup_rates[IEEE80211_MODE_11A].rs_rates, 2621145247Sdamien rs.nrates); 2622145247Sdamien DPRINTF(("Setting .11a supported rates (%u)\n", rs.nrates)); 2623158089Smlaier error = iwi_cmd(sc, IWI_CMD_SET_RATES, &rs, sizeof rs); 2624145247Sdamien if (error != 0) 2625145247Sdamien return error; 2626145247Sdamien 2627145247Sdamien data = htole32(arc4random()); 2628145247Sdamien DPRINTF(("Setting initialization vector to %u\n", le32toh(data))); 2629158089Smlaier error = iwi_cmd(sc, IWI_CMD_SET_IV, &data, sizeof data); 2630145247Sdamien if (error != 0) 2631145247Sdamien return error; 2632145247Sdamien 2633145247Sdamien /* enable adapter */ 2634145247Sdamien DPRINTF(("Enabling adapter\n")); 2635158089Smlaier return iwi_cmd(sc, IWI_CMD_ENABLE, NULL, 0); 2636145247Sdamien} 2637145247Sdamien 2638158089Smlaierstatic __inline void 2639158089Smlaierset_scan_type(struct iwi_scan_ext *scan, int ix, int scan_type) 2640146500Sdamien{ 2641158089Smlaier uint8_t *st = &scan->scan_type[ix / 2]; 2642158089Smlaier if (ix % 2) 2643158089Smlaier *st = (*st & 0xf0) | ((scan_type & 0xf) << 0); 2644158089Smlaier else 2645158089Smlaier *st = (*st & 0x0f) | ((scan_type & 0xf) << 4); 2646146500Sdamien} 2647146500Sdamien 2648146500Sdamienstatic int 2649170530Ssamscan_type(const struct ieee80211_scan_state *ss, 2650170530Ssam const struct ieee80211_channel *chan) 2651145247Sdamien{ 2652170530Ssam /* We can only set one essid for a directed scan */ 2653170530Ssam if (ss->ss_nssid != 0) 2654170530Ssam return IWI_SCAN_TYPE_BDIRECTED; 2655170530Ssam if ((ss->ss_flags & IEEE80211_SCAN_ACTIVE) && 2656170530Ssam (chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0) 2657170530Ssam return IWI_SCAN_TYPE_BROADCAST; 2658170530Ssam return IWI_SCAN_TYPE_PASSIVE; 2659170530Ssam} 2660170530Ssam 2661170530Ssamstatic __inline int 2662170530Ssamscan_band(const struct ieee80211_channel *c) 2663170530Ssam{ 2664170530Ssam return IEEE80211_IS_CHAN_5GHZ(c) ? IWI_CHAN_5GHZ : IWI_CHAN_2GHZ; 2665170530Ssam} 2666170530Ssam 2667213729Sbschmidtstatic void 2668213729Sbschmidtiwi_monitor_scan(void *arg, int npending) 2669213729Sbschmidt{ 2670213729Sbschmidt struct iwi_softc *sc = arg; 2671213729Sbschmidt IWI_LOCK_DECL; 2672213729Sbschmidt 2673213729Sbschmidt IWI_LOCK(sc); 2674213729Sbschmidt (void) iwi_scanchan(sc, 2000, 0); 2675213729Sbschmidt IWI_UNLOCK(sc); 2676213729Sbschmidt} 2677213729Sbschmidt 2678170530Ssam/* 2679170530Ssam * Start a scan on the current channel or all channels. 2680170530Ssam */ 2681170530Ssamstatic int 2682191746Sthompsaiwi_scanchan(struct iwi_softc *sc, unsigned long maxdwell, int allchan) 2683170530Ssam{ 2684287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 2685170530Ssam struct ieee80211_channel *chan; 2686170530Ssam struct ieee80211_scan_state *ss; 2687158089Smlaier struct iwi_scan_ext scan; 2688170530Ssam int error = 0; 2689145247Sdamien 2690170530Ssam IWI_LOCK_ASSERT(sc); 2691170530Ssam if (sc->fw_state == IWI_FW_SCANNING) { 2692170530Ssam /* 2693170530Ssam * This should not happen as we only trigger scan_next after 2694170530Ssam * completion 2695170530Ssam */ 2696170530Ssam DPRINTF(("%s: called too early - still scanning\n", __func__)); 2697170530Ssam return (EBUSY); 2698170530Ssam } 2699170530Ssam IWI_STATE_BEGIN(sc, IWI_FW_SCANNING); 2700166848Sluigi 2701170530Ssam ss = ic->ic_scan; 2702170530Ssam 2703145247Sdamien memset(&scan, 0, sizeof scan); 2704170530Ssam scan.full_scan_index = htole32(++sc->sc_scangen); 2705170530Ssam scan.dwell_time[IWI_SCAN_TYPE_PASSIVE] = htole16(maxdwell); 2706170530Ssam if (ic->ic_flags_ext & IEEE80211_FEXT_BGSCAN) { 2707170530Ssam /* 2708170530Ssam * Use very short dwell times for when we send probe request 2709170530Ssam * frames. Without this bg scans hang. Ideally this should 2710170530Ssam * be handled with early-termination as done by net80211 but 2711170530Ssam * that's not feasible (aborting a scan is problematic). 2712170530Ssam */ 2713170530Ssam scan.dwell_time[IWI_SCAN_TYPE_BROADCAST] = htole16(30); 2714170530Ssam scan.dwell_time[IWI_SCAN_TYPE_BDIRECTED] = htole16(30); 2715170530Ssam } else { 2716170530Ssam scan.dwell_time[IWI_SCAN_TYPE_BROADCAST] = htole16(maxdwell); 2717170530Ssam scan.dwell_time[IWI_SCAN_TYPE_BDIRECTED] = htole16(maxdwell); 2718170530Ssam } 2719145247Sdamien 2720170530Ssam /* We can only set one essid for a directed scan */ 2721170530Ssam if (ss->ss_nssid != 0) { 2722170530Ssam error = iwi_cmd(sc, IWI_CMD_SET_ESSID, ss->ss_ssid[0].ssid, 2723170530Ssam ss->ss_ssid[0].len); 2724170530Ssam if (error) 2725170530Ssam return (error); 2726170530Ssam } 2727151030Sdamien 2728191746Sthompsa if (allchan) { 2729170530Ssam int i, next, band, b, bstart; 2730170530Ssam /* 2731170530Ssam * Convert scan list to run-length encoded channel list 2732170530Ssam * the firmware requires (preserving the order setup by 2733170530Ssam * net80211). The first entry in each run specifies the 2734170530Ssam * band and the count of items in the run. 2735170530Ssam */ 2736170530Ssam next = 0; /* next open slot */ 2737170530Ssam bstart = 0; /* NB: not needed, silence compiler */ 2738170530Ssam band = -1; /* NB: impossible value */ 2739170530Ssam KASSERT(ss->ss_last > 0, ("no channels")); 2740170530Ssam for (i = 0; i < ss->ss_last; i++) { 2741170530Ssam chan = ss->ss_chans[i]; 2742170530Ssam b = scan_band(chan); 2743170530Ssam if (b != band) { 2744170530Ssam if (band != -1) 2745170530Ssam scan.channels[bstart] = 2746170530Ssam (next - bstart) | band; 2747170530Ssam /* NB: this allocates a slot for the run-len */ 2748170530Ssam band = b, bstart = next++; 2749170530Ssam } 2750170530Ssam if (next >= IWI_SCAN_CHANNELS) { 2751170530Ssam DPRINTF(("truncating scan list\n")); 2752170530Ssam break; 2753170530Ssam } 2754170530Ssam scan.channels[next] = ieee80211_chan2ieee(ic, chan); 2755170530Ssam set_scan_type(&scan, next, scan_type(ss, chan)); 2756170530Ssam next++; 2757170530Ssam } 2758170530Ssam scan.channels[bstart] = (next - bstart) | band; 2759170530Ssam } else { 2760170530Ssam /* Scan the current channel only */ 2761170530Ssam chan = ic->ic_curchan; 2762170530Ssam scan.channels[0] = 1 | scan_band(chan); 2763170530Ssam scan.channels[1] = ieee80211_chan2ieee(ic, chan); 2764170530Ssam set_scan_type(&scan, 1, scan_type(ss, chan)); 2765170530Ssam } 2766163209Smlaier#ifdef IWI_DEBUG 2767170530Ssam if (iwi_debug > 0) { 2768170530Ssam static const char *scantype[8] = 2769170530Ssam { "PSTOP", "PASV", "DIR", "BCAST", "BDIR", "5", "6", "7" }; 2770170530Ssam int i; 2771170530Ssam printf("Scan request: index %u dwell %d/%d/%d\n" 2772170530Ssam , le32toh(scan.full_scan_index) 2773170530Ssam , le16toh(scan.dwell_time[IWI_SCAN_TYPE_PASSIVE]) 2774170530Ssam , le16toh(scan.dwell_time[IWI_SCAN_TYPE_BROADCAST]) 2775170530Ssam , le16toh(scan.dwell_time[IWI_SCAN_TYPE_BDIRECTED]) 2776170530Ssam ); 2777170530Ssam i = 0; 2778170530Ssam do { 2779170530Ssam int run = scan.channels[i]; 2780170530Ssam if (run == 0) 2781170530Ssam break; 2782170530Ssam printf("Scan %d %s channels:", run & 0x3f, 2783170530Ssam run & IWI_CHAN_2GHZ ? "2.4GHz" : "5GHz"); 2784170530Ssam for (run &= 0x3f, i++; run > 0; run--, i++) { 2785170530Ssam uint8_t type = scan.scan_type[i/2]; 2786170530Ssam printf(" %u/%s", scan.channels[i], 2787170530Ssam scantype[(i & 1 ? type : type>>4) & 7]); 2788170530Ssam } 2789163209Smlaier printf("\n"); 2790170530Ssam } while (i < IWI_SCAN_CHANNELS); 2791170530Ssam } 2792163209Smlaier#endif 2793158089Smlaier 2794170530Ssam return (iwi_cmd(sc, IWI_CMD_SCAN_EXT, &scan, sizeof scan)); 2795145247Sdamien} 2796145247Sdamien 2797145247Sdamienstatic int 2798158089Smlaieriwi_set_sensitivity(struct iwi_softc *sc, int8_t rssi_dbm) 2799158089Smlaier{ 2800158089Smlaier struct iwi_sensitivity sens; 2801158089Smlaier 2802158089Smlaier DPRINTF(("Setting sensitivity to %d\n", rssi_dbm)); 2803158089Smlaier 2804158089Smlaier memset(&sens, 0, sizeof sens); 2805158089Smlaier sens.rssi = htole16(rssi_dbm); 2806158089Smlaier return iwi_cmd(sc, IWI_CMD_SET_SENSITIVITY, &sens, sizeof sens); 2807158089Smlaier} 2808158089Smlaier 2809158089Smlaierstatic int 2810178354Ssamiwi_auth_and_assoc(struct iwi_softc *sc, struct ieee80211vap *vap) 2811145247Sdamien{ 2812178354Ssam struct ieee80211com *ic = vap->iv_ic; 2813178354Ssam struct ifnet *ifp = vap->iv_ifp; 2814258612Sadrian struct ieee80211_node *ni; 2815145247Sdamien struct iwi_configuration config; 2816158089Smlaier struct iwi_associate *assoc = &sc->assoc; 2817145247Sdamien struct iwi_rateset rs; 2818146500Sdamien uint16_t capinfo; 2819178354Ssam uint32_t data; 2820171034Sthompsa int error, mode; 2821170530Ssam 2822170530Ssam IWI_LOCK_ASSERT(sc); 2823170530Ssam 2824170530Ssam if (sc->flags & IWI_FLAG_ASSOCIATED) { 2825170530Ssam DPRINTF(("Already associated\n")); 2826170530Ssam return (-1); 2827170530Ssam } 2828170530Ssam 2829342462Savos ni = ieee80211_ref_node(vap->iv_bss); 2830342462Savos 2831170530Ssam IWI_STATE_BEGIN(sc, IWI_FW_ASSOCIATING); 2832170530Ssam error = 0; 2833171034Sthompsa mode = 0; 2834171034Sthompsa 2835171034Sthompsa if (IEEE80211_IS_CHAN_A(ic->ic_curchan)) 2836171034Sthompsa mode = IWI_MODE_11A; 2837171034Sthompsa else if (IEEE80211_IS_CHAN_G(ic->ic_curchan)) 2838171034Sthompsa mode = IWI_MODE_11G; 2839171034Sthompsa if (IEEE80211_IS_CHAN_B(ic->ic_curchan)) 2840171034Sthompsa mode = IWI_MODE_11B; 2841171034Sthompsa 2842170530Ssam if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) { 2843145247Sdamien memset(&config, 0, sizeof config); 2844145247Sdamien config.bluetooth_coexistence = sc->bluetooth; 2845146500Sdamien config.antenna = sc->antenna; 2846145247Sdamien config.multicast_enabled = 1; 2847171034Sthompsa if (mode == IWI_MODE_11G) 2848171034Sthompsa config.use_protection = 1; 2849146500Sdamien config.answer_pbreq = 2850178354Ssam (vap->iv_opmode == IEEE80211_M_IBSS) ? 1 : 0; 2851146500Sdamien config.disable_unicast_decryption = 1; 2852146500Sdamien config.disable_multicast_decryption = 1; 2853145247Sdamien DPRINTF(("Configuring adapter\n")); 2854158089Smlaier error = iwi_cmd(sc, IWI_CMD_SET_CONFIG, &config, sizeof config); 2855145247Sdamien if (error != 0) 2856170530Ssam goto done; 2857145247Sdamien } 2858145247Sdamien 2859145247Sdamien#ifdef IWI_DEBUG 2860145247Sdamien if (iwi_debug > 0) { 2861145247Sdamien printf("Setting ESSID to "); 2862145247Sdamien ieee80211_print_essid(ni->ni_essid, ni->ni_esslen); 2863145247Sdamien printf("\n"); 2864145247Sdamien } 2865145247Sdamien#endif 2866158089Smlaier error = iwi_cmd(sc, IWI_CMD_SET_ESSID, ni->ni_essid, ni->ni_esslen); 2867145247Sdamien if (error != 0) 2868170530Ssam goto done; 2869145247Sdamien 2870178354Ssam error = iwi_setpowermode(sc, vap); 2871178354Ssam if (error != 0) 2872178354Ssam goto done; 2873178354Ssam 2874178354Ssam data = htole32(vap->iv_rtsthreshold); 2875178354Ssam DPRINTF(("Setting RTS threshold to %u\n", le32toh(data))); 2876178354Ssam error = iwi_cmd(sc, IWI_CMD_SET_RTS_THRESHOLD, &data, sizeof data); 2877178354Ssam if (error != 0) 2878178354Ssam goto done; 2879178354Ssam 2880178354Ssam data = htole32(vap->iv_fragthreshold); 2881178354Ssam DPRINTF(("Setting fragmentation threshold to %u\n", le32toh(data))); 2882178354Ssam error = iwi_cmd(sc, IWI_CMD_SET_FRAG_THRESHOLD, &data, sizeof data); 2883178354Ssam if (error != 0) 2884178354Ssam goto done; 2885178354Ssam 2886151030Sdamien /* the rate set has already been "negotiated" */ 2887171034Sthompsa memset(&rs, 0, sizeof rs); 2888171034Sthompsa rs.mode = mode; 2889151030Sdamien rs.type = IWI_RATESET_TYPE_NEGOTIATED; 2890145247Sdamien rs.nrates = ni->ni_rates.rs_nrates; 2891163618Smlaier if (rs.nrates > IWI_RATESET_SIZE) { 2892163618Smlaier DPRINTF(("Truncating negotiated rate set from %u\n", 2893163618Smlaier rs.nrates)); 2894163618Smlaier rs.nrates = IWI_RATESET_SIZE; 2895163618Smlaier } 2896145247Sdamien memcpy(rs.rates, ni->ni_rates.rs_rates, rs.nrates); 2897158089Smlaier DPRINTF(("Setting negotiated rates (%u)\n", rs.nrates)); 2898158089Smlaier error = iwi_cmd(sc, IWI_CMD_SET_RATES, &rs, sizeof rs); 2899145247Sdamien if (error != 0) 2900170530Ssam goto done; 2901145247Sdamien 2902158089Smlaier memset(assoc, 0, sizeof *assoc); 2903158089Smlaier 2904178354Ssam if ((vap->iv_flags & IEEE80211_F_WME) && ni->ni_ies.wme_ie != NULL) { 2905158089Smlaier /* NB: don't treat WME setup as failure */ 2906283935Sglebius if (iwi_wme_setparams(sc) == 0 && iwi_wme_setie(sc) == 0) 2907158089Smlaier assoc->policy |= htole16(IWI_POLICY_WME); 2908158089Smlaier /* XXX complain on failure? */ 2909149338Sdamien } 2910149338Sdamien 2911178354Ssam if (vap->iv_appie_wpa != NULL) { 2912178354Ssam struct ieee80211_appie *ie = vap->iv_appie_wpa; 2913178354Ssam 2914178354Ssam DPRINTF(("Setting optional IE (len=%u)\n", ie->ie_len)); 2915178354Ssam error = iwi_cmd(sc, IWI_CMD_SET_OPTIE, ie->ie_data, ie->ie_len); 2916146500Sdamien if (error != 0) 2917170530Ssam goto done; 2918146500Sdamien } 2919146500Sdamien 2920178354Ssam error = iwi_set_sensitivity(sc, ic->ic_node_getrssi(ni)); 2921145247Sdamien if (error != 0) 2922170530Ssam goto done; 2923145247Sdamien 2924171034Sthompsa assoc->mode = mode; 2925170530Ssam assoc->chan = ic->ic_curchan->ic_ieee; 2926158089Smlaier /* 2927158089Smlaier * NB: do not arrange for shared key auth w/o privacy 2928158089Smlaier * (i.e. a wep key); it causes a firmware error. 2929158089Smlaier */ 2930178354Ssam if ((vap->iv_flags & IEEE80211_F_PRIVACY) && 2931158089Smlaier ni->ni_authmode == IEEE80211_AUTH_SHARED) { 2932158089Smlaier assoc->auth = IWI_AUTH_SHARED; 2933158089Smlaier /* 2934158089Smlaier * It's possible to have privacy marked but no default 2935158089Smlaier * key setup. This typically is due to a user app bug 2936158089Smlaier * but if we blindly grab the key the firmware will 2937158089Smlaier * barf so avoid it for now. 2938158089Smlaier */ 2939178354Ssam if (vap->iv_def_txkey != IEEE80211_KEYIX_NONE) 2940178354Ssam assoc->auth |= vap->iv_def_txkey << 4; 2941158089Smlaier 2942178354Ssam error = iwi_setwepkeys(sc, vap); 2943158089Smlaier if (error != 0) 2944170530Ssam goto done; 2945158089Smlaier } 2946178354Ssam if (vap->iv_flags & IEEE80211_F_WPA) 2947158089Smlaier assoc->policy |= htole16(IWI_POLICY_WPA); 2948178354Ssam if (vap->iv_opmode == IEEE80211_M_IBSS && ni->ni_tstamp.tsf == 0) 2949158089Smlaier assoc->type = IWI_HC_IBSS_START; 2950158089Smlaier else 2951158089Smlaier assoc->type = IWI_HC_ASSOC; 2952158089Smlaier memcpy(assoc->tstamp, ni->ni_tstamp.data, 8); 2953146500Sdamien 2954178354Ssam if (vap->iv_opmode == IEEE80211_M_IBSS) 2955146500Sdamien capinfo = IEEE80211_CAPINFO_IBSS; 2956146500Sdamien else 2957146500Sdamien capinfo = IEEE80211_CAPINFO_ESS; 2958178354Ssam if (vap->iv_flags & IEEE80211_F_PRIVACY) 2959146500Sdamien capinfo |= IEEE80211_CAPINFO_PRIVACY; 2960146500Sdamien if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && 2961170530Ssam IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) 2962146500Sdamien capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE; 2963158089Smlaier if (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) 2964146500Sdamien capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME; 2965158089Smlaier assoc->capinfo = htole16(capinfo); 2966146500Sdamien 2967158089Smlaier assoc->lintval = htole16(ic->ic_lintval); 2968158089Smlaier assoc->intval = htole16(ni->ni_intval); 2969158089Smlaier IEEE80211_ADDR_COPY(assoc->bssid, ni->ni_bssid); 2970178354Ssam if (vap->iv_opmode == IEEE80211_M_IBSS) 2971158089Smlaier IEEE80211_ADDR_COPY(assoc->dst, ifp->if_broadcastaddr); 2972145247Sdamien else 2973158089Smlaier IEEE80211_ADDR_COPY(assoc->dst, ni->ni_bssid); 2974145247Sdamien 2975158089Smlaier DPRINTF(("%s bssid %6D dst %6D channel %u policy 0x%x " 2976158089Smlaier "auth %u capinfo 0x%x lintval %u bintval %u\n", 2977158089Smlaier assoc->type == IWI_HC_IBSS_START ? "Start" : "Join", 2978158089Smlaier assoc->bssid, ":", assoc->dst, ":", 2979158089Smlaier assoc->chan, le16toh(assoc->policy), assoc->auth, 2980158089Smlaier le16toh(assoc->capinfo), le16toh(assoc->lintval), 2981158089Smlaier le16toh(assoc->intval))); 2982170530Ssam error = iwi_cmd(sc, IWI_CMD_ASSOCIATE, assoc, sizeof *assoc); 2983170530Ssamdone: 2984258612Sadrian ieee80211_free_node(ni); 2985170530Ssam if (error) 2986170530Ssam IWI_STATE_END(sc, IWI_FW_ASSOCIATING); 2987170530Ssam 2988170530Ssam return (error); 2989145247Sdamien} 2990145247Sdamien 2991191746Sthompsastatic void 2992191746Sthompsaiwi_disassoc(void *arg, int pending) 2993191746Sthompsa{ 2994191746Sthompsa struct iwi_softc *sc = arg; 2995191746Sthompsa IWI_LOCK_DECL; 2996191746Sthompsa 2997191746Sthompsa IWI_LOCK(sc); 2998191746Sthompsa iwi_disassociate(sc, 0); 2999191746Sthompsa IWI_UNLOCK(sc); 3000191746Sthompsa} 3001191746Sthompsa 3002158089Smlaierstatic int 3003158089Smlaieriwi_disassociate(struct iwi_softc *sc, int quiet) 3004158089Smlaier{ 3005158089Smlaier struct iwi_associate *assoc = &sc->assoc; 3006158089Smlaier 3007170530Ssam if ((sc->flags & IWI_FLAG_ASSOCIATED) == 0) { 3008170530Ssam DPRINTF(("Not associated\n")); 3009170530Ssam return (-1); 3010170530Ssam } 3011170530Ssam 3012170530Ssam IWI_STATE_BEGIN(sc, IWI_FW_DISASSOCIATING); 3013170530Ssam 3014158089Smlaier if (quiet) 3015158089Smlaier assoc->type = IWI_HC_DISASSOC_QUIET; 3016158089Smlaier else 3017158089Smlaier assoc->type = IWI_HC_DISASSOC; 3018158089Smlaier 3019158089Smlaier DPRINTF(("Trying to disassociate from %6D channel %u\n", 3020158089Smlaier assoc->bssid, ":", assoc->chan)); 3021158089Smlaier return iwi_cmd(sc, IWI_CMD_ASSOCIATE, assoc, sizeof *assoc); 3022158089Smlaier} 3023158089Smlaier 3024166848Sluigi/* 3025166848Sluigi * release dma resources for the firmware 3026166848Sluigi */ 3027158089Smlaierstatic void 3028166848Sluigiiwi_release_fw_dma(struct iwi_softc *sc) 3029166848Sluigi{ 3030166848Sluigi if (sc->fw_flags & IWI_FW_HAVE_PHY) 3031166848Sluigi bus_dmamap_unload(sc->fw_dmat, sc->fw_map); 3032166848Sluigi if (sc->fw_flags & IWI_FW_HAVE_MAP) 3033166848Sluigi bus_dmamem_free(sc->fw_dmat, sc->fw_virtaddr, sc->fw_map); 3034166848Sluigi if (sc->fw_flags & IWI_FW_HAVE_DMAT) 3035166848Sluigi bus_dma_tag_destroy(sc->fw_dmat); 3036166848Sluigi 3037166848Sluigi sc->fw_flags = 0; 3038166848Sluigi sc->fw_dma_size = 0; 3039166848Sluigi sc->fw_dmat = NULL; 3040166848Sluigi sc->fw_map = NULL; 3041166848Sluigi sc->fw_physaddr = 0; 3042166848Sluigi sc->fw_virtaddr = NULL; 3043166848Sluigi} 3044166848Sluigi 3045166848Sluigi/* 3046166848Sluigi * allocate the dma descriptor for the firmware. 3047166848Sluigi * Return 0 on success, 1 on error. 3048166848Sluigi * Must be called unlocked, protected by IWI_FLAG_FW_LOADING. 3049166848Sluigi */ 3050166848Sluigistatic int 3051166848Sluigiiwi_init_fw_dma(struct iwi_softc *sc, int size) 3052166848Sluigi{ 3053167776Sjhb if (sc->fw_dma_size >= size) 3054166848Sluigi return 0; 3055166848Sluigi if (bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 4, 0, 3056166848Sluigi BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, 3057166848Sluigi size, 1, size, 0, NULL, NULL, &sc->fw_dmat) != 0) { 3058166848Sluigi device_printf(sc->sc_dev, 3059166848Sluigi "could not create firmware DMA tag\n"); 3060166848Sluigi goto error; 3061166848Sluigi } 3062166848Sluigi sc->fw_flags |= IWI_FW_HAVE_DMAT; 3063166848Sluigi if (bus_dmamem_alloc(sc->fw_dmat, &sc->fw_virtaddr, 0, 3064166848Sluigi &sc->fw_map) != 0) { 3065166848Sluigi device_printf(sc->sc_dev, 3066166848Sluigi "could not allocate firmware DMA memory\n"); 3067166848Sluigi goto error; 3068166848Sluigi } 3069166848Sluigi sc->fw_flags |= IWI_FW_HAVE_MAP; 3070166848Sluigi if (bus_dmamap_load(sc->fw_dmat, sc->fw_map, sc->fw_virtaddr, 3071166848Sluigi size, iwi_dma_map_addr, &sc->fw_physaddr, 0) != 0) { 3072166848Sluigi device_printf(sc->sc_dev, "could not load firmware DMA map\n"); 3073166848Sluigi goto error; 3074166848Sluigi } 3075166848Sluigi sc->fw_flags |= IWI_FW_HAVE_PHY; 3076166848Sluigi sc->fw_dma_size = size; 3077166848Sluigi return 0; 3078166848Sluigi 3079166848Sluigierror: 3080166848Sluigi iwi_release_fw_dma(sc); 3081166848Sluigi return 1; 3082166848Sluigi} 3083166848Sluigi 3084166848Sluigistatic void 3085178354Ssamiwi_init_locked(struct iwi_softc *sc) 3086158089Smlaier{ 3087145247Sdamien struct iwi_rx_data *data; 3088158089Smlaier int i; 3089145247Sdamien 3090170530Ssam IWI_LOCK_ASSERT(sc); 3091178354Ssam 3092170530Ssam if (sc->fw_state == IWI_FW_LOADING) { 3093166848Sluigi device_printf(sc->sc_dev, "%s: already loading\n", __func__); 3094158089Smlaier return; /* XXX: condvar? */ 3095166848Sluigi } 3096156539Sdamien 3097178354Ssam iwi_stop_locked(sc); 3098178354Ssam 3099170530Ssam IWI_STATE_BEGIN(sc, IWI_FW_LOADING); 3100145247Sdamien 3101145247Sdamien if (iwi_reset(sc) != 0) { 3102145247Sdamien device_printf(sc->sc_dev, "could not reset adapter\n"); 3103158089Smlaier goto fail; 3104145247Sdamien } 3105158089Smlaier if (iwi_load_firmware(sc, &sc->fw_boot) != 0) { 3106156546Sdamien device_printf(sc->sc_dev, 3107158089Smlaier "could not load boot firmware %s\n", sc->fw_boot.name); 3108166848Sluigi goto fail; 3109156546Sdamien } 3110158089Smlaier if (iwi_load_ucode(sc, &sc->fw_uc) != 0) { 3111158089Smlaier device_printf(sc->sc_dev, 3112158089Smlaier "could not load microcode %s\n", sc->fw_uc.name); 3113166848Sluigi goto fail; 3114145247Sdamien } 3115145247Sdamien 3116145247Sdamien iwi_stop_master(sc); 3117145247Sdamien 3118145247Sdamien CSR_WRITE_4(sc, IWI_CSR_CMD_BASE, sc->cmdq.physaddr); 3119145247Sdamien CSR_WRITE_4(sc, IWI_CSR_CMD_SIZE, sc->cmdq.count); 3120145247Sdamien CSR_WRITE_4(sc, IWI_CSR_CMD_WIDX, sc->cmdq.cur); 3121145247Sdamien 3122149338Sdamien CSR_WRITE_4(sc, IWI_CSR_TX1_BASE, sc->txq[0].physaddr); 3123149338Sdamien CSR_WRITE_4(sc, IWI_CSR_TX1_SIZE, sc->txq[0].count); 3124149338Sdamien CSR_WRITE_4(sc, IWI_CSR_TX1_WIDX, sc->txq[0].cur); 3125145247Sdamien 3126149338Sdamien CSR_WRITE_4(sc, IWI_CSR_TX2_BASE, sc->txq[1].physaddr); 3127149338Sdamien CSR_WRITE_4(sc, IWI_CSR_TX2_SIZE, sc->txq[1].count); 3128149338Sdamien CSR_WRITE_4(sc, IWI_CSR_TX2_WIDX, sc->txq[1].cur); 3129145247Sdamien 3130149338Sdamien CSR_WRITE_4(sc, IWI_CSR_TX3_BASE, sc->txq[2].physaddr); 3131149338Sdamien CSR_WRITE_4(sc, IWI_CSR_TX3_SIZE, sc->txq[2].count); 3132149338Sdamien CSR_WRITE_4(sc, IWI_CSR_TX3_WIDX, sc->txq[2].cur); 3133145247Sdamien 3134149338Sdamien CSR_WRITE_4(sc, IWI_CSR_TX4_BASE, sc->txq[3].physaddr); 3135149338Sdamien CSR_WRITE_4(sc, IWI_CSR_TX4_SIZE, sc->txq[3].count); 3136149338Sdamien CSR_WRITE_4(sc, IWI_CSR_TX4_WIDX, sc->txq[3].cur); 3137145247Sdamien 3138145247Sdamien for (i = 0; i < sc->rxq.count; i++) { 3139145247Sdamien data = &sc->rxq.data[i]; 3140145247Sdamien CSR_WRITE_4(sc, data->reg, data->physaddr); 3141145247Sdamien } 3142145247Sdamien 3143145247Sdamien CSR_WRITE_4(sc, IWI_CSR_RX_WIDX, sc->rxq.count - 1); 3144145247Sdamien 3145158089Smlaier if (iwi_load_firmware(sc, &sc->fw_fw) != 0) { 3146158089Smlaier device_printf(sc->sc_dev, 3147158089Smlaier "could not load main firmware %s\n", sc->fw_fw.name); 3148166848Sluigi goto fail; 3149145247Sdamien } 3150145247Sdamien sc->flags |= IWI_FLAG_FW_INITED; 3151145247Sdamien 3152178354Ssam IWI_STATE_END(sc, IWI_FW_LOADING); 3153178354Ssam 3154145247Sdamien if (iwi_config(sc) != 0) { 3155178354Ssam device_printf(sc->sc_dev, "unable to enable adapter\n"); 3156178354Ssam goto fail2; 3157145247Sdamien } 3158145247Sdamien 3159170530Ssam callout_reset(&sc->sc_wdtimer, hz, iwi_watchdog, sc); 3160287197Sglebius sc->sc_running = 1; 3161145247Sdamien return; 3162178354Ssamfail: 3163170530Ssam IWI_STATE_END(sc, IWI_FW_LOADING); 3164178354Ssamfail2: 3165178354Ssam iwi_stop_locked(sc); 3166145247Sdamien} 3167145247Sdamien 3168145247Sdamienstatic void 3169178354Ssamiwi_init(void *priv) 3170145247Sdamien{ 3171145247Sdamien struct iwi_softc *sc = priv; 3172287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 3173178354Ssam IWI_LOCK_DECL; 3174145247Sdamien 3175178354Ssam IWI_LOCK(sc); 3176178354Ssam iwi_init_locked(sc); 3177178354Ssam IWI_UNLOCK(sc); 3178178354Ssam 3179287197Sglebius if (sc->sc_running) 3180178354Ssam ieee80211_start_all(ic); 3181178354Ssam} 3182178354Ssam 3183178354Ssamstatic void 3184178354Ssamiwi_stop_locked(void *priv) 3185178354Ssam{ 3186178354Ssam struct iwi_softc *sc = priv; 3187178354Ssam 3188170530Ssam IWI_LOCK_ASSERT(sc); 3189178354Ssam 3190287197Sglebius sc->sc_running = 0; 3191178354Ssam 3192158089Smlaier if (sc->sc_softled) { 3193158089Smlaier callout_stop(&sc->sc_ledtimer); 3194158089Smlaier sc->sc_blinking = 0; 3195158089Smlaier } 3196178354Ssam callout_stop(&sc->sc_wdtimer); 3197178354Ssam callout_stop(&sc->sc_rftimer); 3198156539Sdamien 3199145247Sdamien iwi_stop_master(sc); 3200145247Sdamien 3201145247Sdamien CSR_WRITE_4(sc, IWI_CSR_RST, IWI_RST_SOFT_RESET); 3202145247Sdamien 3203145247Sdamien /* reset rings */ 3204145247Sdamien iwi_reset_cmd_ring(sc, &sc->cmdq); 3205149338Sdamien iwi_reset_tx_ring(sc, &sc->txq[0]); 3206149338Sdamien iwi_reset_tx_ring(sc, &sc->txq[1]); 3207149338Sdamien iwi_reset_tx_ring(sc, &sc->txq[2]); 3208149338Sdamien iwi_reset_tx_ring(sc, &sc->txq[3]); 3209145247Sdamien iwi_reset_rx_ring(sc, &sc->rxq); 3210145247Sdamien 3211158089Smlaier sc->sc_tx_timer = 0; 3212170530Ssam sc->sc_state_timer = 0; 3213170530Ssam sc->sc_busy_timer = 0; 3214170530Ssam sc->flags &= ~(IWI_FLAG_BUSY | IWI_FLAG_ASSOCIATED); 3215170530Ssam sc->fw_state = IWI_FW_IDLE; 3216170530Ssam wakeup(sc); 3217145247Sdamien} 3218145247Sdamien 3219158089Smlaierstatic void 3220178354Ssamiwi_stop(struct iwi_softc *sc) 3221158089Smlaier{ 3222158089Smlaier IWI_LOCK_DECL; 3223158089Smlaier 3224158089Smlaier IWI_LOCK(sc); 3225178354Ssam iwi_stop_locked(sc); 3226158089Smlaier IWI_UNLOCK(sc); 3227158089Smlaier} 3228158089Smlaier 3229178354Ssamstatic void 3230178354Ssamiwi_restart(void *arg, int npending) 3231178354Ssam{ 3232178354Ssam struct iwi_softc *sc = arg; 3233178354Ssam 3234178354Ssam iwi_init(sc); 3235178354Ssam} 3236178354Ssam 3237158089Smlaier/* 3238158089Smlaier * Return whether or not the radio is enabled in hardware 3239158089Smlaier * (i.e. the rfkill switch is "off"). 3240158089Smlaier */ 3241145247Sdamienstatic int 3242158089Smlaieriwi_getrfkill(struct iwi_softc *sc) 3243158089Smlaier{ 3244158089Smlaier return (CSR_READ_4(sc, IWI_CSR_IO) & IWI_IO_RADIO_ENABLED) == 0; 3245158089Smlaier} 3246158089Smlaier 3247158089Smlaierstatic void 3248158089Smlaieriwi_radio_on(void *arg, int pending) 3249158089Smlaier{ 3250158089Smlaier struct iwi_softc *sc = arg; 3251287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 3252158089Smlaier 3253158089Smlaier device_printf(sc->sc_dev, "radio turned on\n"); 3254178354Ssam 3255158089Smlaier iwi_init(sc); 3256178354Ssam ieee80211_notify_radio(ic, 1); 3257158089Smlaier} 3258158089Smlaier 3259158089Smlaierstatic void 3260178354Ssamiwi_rfkill_poll(void *arg) 3261178354Ssam{ 3262178354Ssam struct iwi_softc *sc = arg; 3263178354Ssam 3264178354Ssam IWI_LOCK_ASSERT(sc); 3265178354Ssam 3266178354Ssam /* 3267178354Ssam * Check for a change in rfkill state. We get an 3268178354Ssam * interrupt when a radio is disabled but not when 3269178354Ssam * it is enabled so we must poll for the latter. 3270178354Ssam */ 3271178354Ssam if (!iwi_getrfkill(sc)) { 3272287197Sglebius ieee80211_runtask(&sc->sc_ic, &sc->sc_radiontask); 3273178354Ssam return; 3274178354Ssam } 3275178354Ssam callout_reset(&sc->sc_rftimer, 2*hz, iwi_rfkill_poll, sc); 3276178354Ssam} 3277178354Ssam 3278178354Ssamstatic void 3279158089Smlaieriwi_radio_off(void *arg, int pending) 3280158089Smlaier{ 3281158089Smlaier struct iwi_softc *sc = arg; 3282287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 3283170530Ssam IWI_LOCK_DECL; 3284158089Smlaier 3285158089Smlaier device_printf(sc->sc_dev, "radio turned off\n"); 3286178354Ssam 3287178354Ssam ieee80211_notify_radio(ic, 0); 3288178354Ssam 3289170530Ssam IWI_LOCK(sc); 3290178354Ssam iwi_stop_locked(sc); 3291178354Ssam iwi_rfkill_poll(sc); 3292170530Ssam IWI_UNLOCK(sc); 3293158089Smlaier} 3294158089Smlaier 3295158089Smlaierstatic int 3296145247Sdamieniwi_sysctl_stats(SYSCTL_HANDLER_ARGS) 3297145247Sdamien{ 3298145247Sdamien struct iwi_softc *sc = arg1; 3299145247Sdamien uint32_t size, buf[128]; 3300145247Sdamien 3301174317Sphilip memset(buf, 0, sizeof buf); 3302174317Sphilip 3303174317Sphilip if (!(sc->flags & IWI_FLAG_FW_INITED)) 3304145247Sdamien return SYSCTL_OUT(req, buf, sizeof buf); 3305145247Sdamien 3306145247Sdamien size = min(CSR_READ_4(sc, IWI_CSR_TABLE0_SIZE), 128 - 1); 3307145247Sdamien CSR_READ_REGION_4(sc, IWI_CSR_TABLE0_BASE, &buf[1], size); 3308145247Sdamien 3309174317Sphilip return SYSCTL_OUT(req, buf, size); 3310145247Sdamien} 3311145247Sdamien 3312145247Sdamienstatic int 3313145247Sdamieniwi_sysctl_radio(SYSCTL_HANDLER_ARGS) 3314145247Sdamien{ 3315145247Sdamien struct iwi_softc *sc = arg1; 3316158089Smlaier int val = !iwi_getrfkill(sc); 3317145247Sdamien 3318145247Sdamien return SYSCTL_OUT(req, &val, sizeof val); 3319145247Sdamien} 3320158089Smlaier 3321158089Smlaier/* 3322158089Smlaier * Add sysctl knobs. 3323158089Smlaier */ 3324158089Smlaierstatic void 3325158089Smlaieriwi_sysctlattach(struct iwi_softc *sc) 3326158089Smlaier{ 3327158089Smlaier struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); 3328158089Smlaier struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); 3329158089Smlaier 3330158089Smlaier SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "radio", 3331158089Smlaier CTLTYPE_INT | CTLFLAG_RD, sc, 0, iwi_sysctl_radio, "I", 3332158089Smlaier "radio transmitter switch state (0=off, 1=on)"); 3333158089Smlaier 3334158089Smlaier SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "stats", 3335158089Smlaier CTLTYPE_OPAQUE | CTLFLAG_RD, sc, 0, iwi_sysctl_stats, "S", 3336158089Smlaier "statistics"); 3337158089Smlaier 3338158089Smlaier sc->bluetooth = 0; 3339158089Smlaier SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "bluetooth", 3340158089Smlaier CTLFLAG_RW, &sc->bluetooth, 0, "bluetooth coexistence"); 3341158089Smlaier 3342158089Smlaier sc->antenna = IWI_ANTENNA_AUTO; 3343158089Smlaier SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "antenna", 3344158089Smlaier CTLFLAG_RW, &sc->antenna, 0, "antenna (0=auto)"); 3345158089Smlaier} 3346158089Smlaier 3347158089Smlaier/* 3348158089Smlaier * LED support. 3349158089Smlaier * 3350158089Smlaier * Different cards have different capabilities. Some have three 3351158089Smlaier * led's while others have only one. The linux ipw driver defines 3352158089Smlaier * led's for link state (associated or not), band (11a, 11g, 11b), 3353158089Smlaier * and for link activity. We use one led and vary the blink rate 3354158089Smlaier * according to the tx/rx traffic a la the ath driver. 3355158089Smlaier */ 3356158089Smlaier 3357158089Smlaierstatic __inline uint32_t 3358158089Smlaieriwi_toggle_event(uint32_t r) 3359158089Smlaier{ 3360158089Smlaier return r &~ (IWI_RST_STANDBY | IWI_RST_GATE_ODMA | 3361158089Smlaier IWI_RST_GATE_IDMA | IWI_RST_GATE_ADMA); 3362158089Smlaier} 3363158089Smlaier 3364158089Smlaierstatic uint32_t 3365158089Smlaieriwi_read_event(struct iwi_softc *sc) 3366158089Smlaier{ 3367158089Smlaier return MEM_READ_4(sc, IWI_MEM_EEPROM_EVENT); 3368158089Smlaier} 3369158089Smlaier 3370158089Smlaierstatic void 3371158089Smlaieriwi_write_event(struct iwi_softc *sc, uint32_t v) 3372158089Smlaier{ 3373158089Smlaier MEM_WRITE_4(sc, IWI_MEM_EEPROM_EVENT, v); 3374158089Smlaier} 3375158089Smlaier 3376158089Smlaierstatic void 3377158089Smlaieriwi_led_done(void *arg) 3378158089Smlaier{ 3379158089Smlaier struct iwi_softc *sc = arg; 3380158089Smlaier 3381158089Smlaier sc->sc_blinking = 0; 3382158089Smlaier} 3383158089Smlaier 3384158089Smlaier/* 3385158089Smlaier * Turn the activity LED off: flip the pin and then set a timer so no 3386158089Smlaier * update will happen for the specified duration. 3387158089Smlaier */ 3388158089Smlaierstatic void 3389158089Smlaieriwi_led_off(void *arg) 3390158089Smlaier{ 3391158089Smlaier struct iwi_softc *sc = arg; 3392158089Smlaier uint32_t v; 3393158089Smlaier 3394158089Smlaier v = iwi_read_event(sc); 3395158089Smlaier v &= ~sc->sc_ledpin; 3396158089Smlaier iwi_write_event(sc, iwi_toggle_event(v)); 3397158089Smlaier callout_reset(&sc->sc_ledtimer, sc->sc_ledoff, iwi_led_done, sc); 3398158089Smlaier} 3399158089Smlaier 3400158089Smlaier/* 3401158089Smlaier * Blink the LED according to the specified on/off times. 3402158089Smlaier */ 3403158089Smlaierstatic void 3404158089Smlaieriwi_led_blink(struct iwi_softc *sc, int on, int off) 3405158089Smlaier{ 3406158089Smlaier uint32_t v; 3407158089Smlaier 3408158089Smlaier v = iwi_read_event(sc); 3409158089Smlaier v |= sc->sc_ledpin; 3410158089Smlaier iwi_write_event(sc, iwi_toggle_event(v)); 3411158089Smlaier sc->sc_blinking = 1; 3412158089Smlaier sc->sc_ledoff = off; 3413158089Smlaier callout_reset(&sc->sc_ledtimer, on, iwi_led_off, sc); 3414158089Smlaier} 3415158089Smlaier 3416158089Smlaierstatic void 3417158089Smlaieriwi_led_event(struct iwi_softc *sc, int event) 3418158089Smlaier{ 3419158089Smlaier /* NB: on/off times from the Atheros NDIS driver, w/ permission */ 3420158089Smlaier static const struct { 3421158089Smlaier u_int rate; /* tx/rx iwi rate */ 3422158089Smlaier u_int16_t timeOn; /* LED on time (ms) */ 3423158089Smlaier u_int16_t timeOff; /* LED off time (ms) */ 3424158089Smlaier } blinkrates[] = { 3425158089Smlaier { IWI_RATE_OFDM54, 40, 10 }, 3426158089Smlaier { IWI_RATE_OFDM48, 44, 11 }, 3427158089Smlaier { IWI_RATE_OFDM36, 50, 13 }, 3428158089Smlaier { IWI_RATE_OFDM24, 57, 14 }, 3429158089Smlaier { IWI_RATE_OFDM18, 67, 16 }, 3430158089Smlaier { IWI_RATE_OFDM12, 80, 20 }, 3431158089Smlaier { IWI_RATE_DS11, 100, 25 }, 3432158089Smlaier { IWI_RATE_OFDM9, 133, 34 }, 3433158089Smlaier { IWI_RATE_OFDM6, 160, 40 }, 3434158089Smlaier { IWI_RATE_DS5, 200, 50 }, 3435158089Smlaier { 6, 240, 58 }, /* XXX 3Mb/s if it existed */ 3436158089Smlaier { IWI_RATE_DS2, 267, 66 }, 3437158089Smlaier { IWI_RATE_DS1, 400, 100 }, 3438158089Smlaier { 0, 500, 130 }, /* unknown rate/polling */ 3439158089Smlaier }; 3440158089Smlaier uint32_t txrate; 3441158089Smlaier int j = 0; /* XXX silence compiler */ 3442158089Smlaier 3443158089Smlaier sc->sc_ledevent = ticks; /* time of last event */ 3444158089Smlaier if (sc->sc_blinking) /* don't interrupt active blink */ 3445158089Smlaier return; 3446158089Smlaier switch (event) { 3447158089Smlaier case IWI_LED_POLL: 3448288087Sadrian j = nitems(blinkrates)-1; 3449158089Smlaier break; 3450158089Smlaier case IWI_LED_TX: 3451158089Smlaier /* read current transmission rate from adapter */ 3452158089Smlaier txrate = CSR_READ_4(sc, IWI_CSR_CURRENT_TX_RATE); 3453158089Smlaier if (blinkrates[sc->sc_txrix].rate != txrate) { 3454288087Sadrian for (j = 0; j < nitems(blinkrates)-1; j++) 3455158089Smlaier if (blinkrates[j].rate == txrate) 3456158089Smlaier break; 3457158089Smlaier sc->sc_txrix = j; 3458158089Smlaier } else 3459158089Smlaier j = sc->sc_txrix; 3460158089Smlaier break; 3461158089Smlaier case IWI_LED_RX: 3462158089Smlaier if (blinkrates[sc->sc_rxrix].rate != sc->sc_rxrate) { 3463288087Sadrian for (j = 0; j < nitems(blinkrates)-1; j++) 3464158089Smlaier if (blinkrates[j].rate == sc->sc_rxrate) 3465158089Smlaier break; 3466158089Smlaier sc->sc_rxrix = j; 3467158089Smlaier } else 3468158089Smlaier j = sc->sc_rxrix; 3469158089Smlaier break; 3470158089Smlaier } 3471158089Smlaier /* XXX beware of overflow */ 3472158089Smlaier iwi_led_blink(sc, (blinkrates[j].timeOn * hz) / 1000, 3473158089Smlaier (blinkrates[j].timeOff * hz) / 1000); 3474158089Smlaier} 3475158089Smlaier 3476158089Smlaierstatic int 3477158089Smlaieriwi_sysctl_softled(SYSCTL_HANDLER_ARGS) 3478158089Smlaier{ 3479158089Smlaier struct iwi_softc *sc = arg1; 3480158089Smlaier int softled = sc->sc_softled; 3481158089Smlaier int error; 3482158089Smlaier 3483158089Smlaier error = sysctl_handle_int(oidp, &softled, 0, req); 3484158089Smlaier if (error || !req->newptr) 3485158089Smlaier return error; 3486158089Smlaier softled = (softled != 0); 3487158089Smlaier if (softled != sc->sc_softled) { 3488158089Smlaier if (softled) { 3489158089Smlaier uint32_t v = iwi_read_event(sc); 3490158089Smlaier v &= ~sc->sc_ledpin; 3491158089Smlaier iwi_write_event(sc, iwi_toggle_event(v)); 3492158089Smlaier } 3493158089Smlaier sc->sc_softled = softled; 3494158089Smlaier } 3495158089Smlaier return 0; 3496158089Smlaier} 3497158089Smlaier 3498158089Smlaierstatic void 3499158089Smlaieriwi_ledattach(struct iwi_softc *sc) 3500158089Smlaier{ 3501158089Smlaier struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); 3502158089Smlaier struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); 3503158089Smlaier 3504158089Smlaier sc->sc_blinking = 0; 3505158089Smlaier sc->sc_ledstate = 1; 3506158089Smlaier sc->sc_ledidle = (2700*hz)/1000; /* 2.7sec */ 3507158089Smlaier callout_init_mtx(&sc->sc_ledtimer, &sc->sc_mtx, 0); 3508158089Smlaier 3509158089Smlaier SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 3510158089Smlaier "softled", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 3511158089Smlaier iwi_sysctl_softled, "I", "enable/disable software LED support"); 3512217323Smdf SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 3513158089Smlaier "ledpin", CTLFLAG_RW, &sc->sc_ledpin, 0, 3514158089Smlaier "pin setting to turn activity LED on"); 3515217323Smdf SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 3516158089Smlaier "ledidle", CTLFLAG_RW, &sc->sc_ledidle, 0, 3517158089Smlaier "idle time for inactivity LED (ticks)"); 3518158089Smlaier /* XXX for debugging */ 3519217323Smdf SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 3520158089Smlaier "nictype", CTLFLAG_RD, &sc->sc_nictype, 0, 3521158089Smlaier "NIC type from EEPROM"); 3522158089Smlaier 3523158089Smlaier sc->sc_ledpin = IWI_RST_LED_ACTIVITY; 3524158089Smlaier sc->sc_softled = 1; 3525158089Smlaier 3526158089Smlaier sc->sc_nictype = (iwi_read_prom_word(sc, IWI_EEPROM_NIC) >> 8) & 0xff; 3527158089Smlaier if (sc->sc_nictype == 1) { 3528158089Smlaier /* 3529158089Smlaier * NB: led's are reversed. 3530158089Smlaier */ 3531158089Smlaier sc->sc_ledpin = IWI_RST_LED_ASSOCIATED; 3532158089Smlaier } 3533158089Smlaier} 3534170530Ssam 3535170530Ssamstatic void 3536170530Ssamiwi_scan_start(struct ieee80211com *ic) 3537170530Ssam{ 3538191746Sthompsa /* ignore */ 3539170530Ssam} 3540170530Ssam 3541170530Ssamstatic void 3542170530Ssamiwi_set_channel(struct ieee80211com *ic) 3543170530Ssam{ 3544287197Sglebius struct iwi_softc *sc = ic->ic_softc; 3545287197Sglebius 3546170530Ssam if (sc->fw_state == IWI_FW_IDLE) 3547170530Ssam iwi_setcurchan(sc, ic->ic_curchan->ic_ieee); 3548170530Ssam} 3549170530Ssam 3550170530Ssamstatic void 3551178354Ssamiwi_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell) 3552170530Ssam{ 3553178354Ssam struct ieee80211vap *vap = ss->ss_vap; 3554287197Sglebius struct iwi_softc *sc = vap->iv_ic->ic_softc; 3555191746Sthompsa IWI_LOCK_DECL; 3556170530Ssam 3557191746Sthompsa IWI_LOCK(sc); 3558191746Sthompsa if (iwi_scanchan(sc, maxdwell, 0)) 3559191746Sthompsa ieee80211_cancel_scan(vap); 3560191746Sthompsa IWI_UNLOCK(sc); 3561170530Ssam} 3562170530Ssam 3563170530Ssamstatic void 3564178354Ssamiwi_scan_mindwell(struct ieee80211_scan_state *ss) 3565170530Ssam{ 3566170530Ssam /* NB: don't try to abort scan; wait for firmware to finish */ 3567170530Ssam} 3568170530Ssam 3569170530Ssamstatic void 3570170530Ssamiwi_scan_end(struct ieee80211com *ic) 3571170530Ssam{ 3572287197Sglebius struct iwi_softc *sc = ic->ic_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} 3582313921Savos 3583313921Savosstatic void 3584313921Savosiwi_collect_bands(struct ieee80211com *ic, uint8_t bands[], size_t bands_sz) 3585313921Savos{ 3586313921Savos struct iwi_softc *sc = ic->ic_softc; 3587313921Savos device_t dev = sc->sc_dev; 3588313921Savos 3589313921Savos memset(bands, 0, bands_sz); 3590313921Savos setbit(bands, IEEE80211_MODE_11B); 3591313921Savos setbit(bands, IEEE80211_MODE_11G); 3592313921Savos if (pci_get_device(dev) >= 0x4223) 3593313921Savos setbit(bands, IEEE80211_MODE_11A); 3594313921Savos} 3595313921Savos 3596313921Savosstatic void 3597313921Savosiwi_getradiocaps(struct ieee80211com *ic, 3598313921Savos int maxchans, int *nchans, struct ieee80211_channel chans[]) 3599313921Savos{ 3600313921Savos uint8_t bands[IEEE80211_MODE_BYTES]; 3601313921Savos 3602313921Savos iwi_collect_bands(ic, bands, sizeof(bands)); 3603313921Savos *nchans = 0; 3604313921Savos if (isset(bands, IEEE80211_MODE_11B) || isset(bands, IEEE80211_MODE_11G)) 3605343976Savos ieee80211_add_channels_default_2ghz(chans, maxchans, nchans, 3606343976Savos bands, 0); 3607313921Savos if (isset(bands, IEEE80211_MODE_11A)) { 3608313921Savos ieee80211_add_channel_list_5ghz(chans, maxchans, nchans, 3609313921Savos def_chan_5ghz_band1, nitems(def_chan_5ghz_band1), 3610313921Savos bands, 0); 3611313921Savos ieee80211_add_channel_list_5ghz(chans, maxchans, nchans, 3612313921Savos def_chan_5ghz_band2, nitems(def_chan_5ghz_band2), 3613313921Savos bands, 0); 3614313921Savos ieee80211_add_channel_list_5ghz(chans, maxchans, nchans, 3615313921Savos def_chan_5ghz_band3, nitems(def_chan_5ghz_band3), 3616313921Savos bands, 0); 3617313921Savos } 3618313921Savos} 3619