if_iwi.c revision 195562
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: head/sys/dev/iwi/if_iwi.c 195562 2009-07-10 15:28:33Z rpaulo $"); 32145247Sdamien 33145247Sdamien/*- 34145247Sdamien * Intel(R) PRO/Wireless 2200BG/2225BG/2915ABG driver 35145247Sdamien * http://www.intel.com/network/connectivity/products/wireless/prowireless_mobile.htm 36145247Sdamien */ 37145247Sdamien 38145247Sdamien#include <sys/param.h> 39145247Sdamien#include <sys/sysctl.h> 40145247Sdamien#include <sys/sockio.h> 41145247Sdamien#include <sys/mbuf.h> 42145247Sdamien#include <sys/kernel.h> 43145247Sdamien#include <sys/socket.h> 44145247Sdamien#include <sys/systm.h> 45145247Sdamien#include <sys/malloc.h> 46164982Skevlo#include <sys/lock.h> 47164982Skevlo#include <sys/mutex.h> 48145247Sdamien#include <sys/module.h> 49145247Sdamien#include <sys/bus.h> 50145247Sdamien#include <sys/endian.h> 51158089Smlaier#include <sys/proc.h> 52158089Smlaier#include <sys/mount.h> 53158089Smlaier#include <sys/namei.h> 54156546Sdamien#include <sys/linker.h> 55156546Sdamien#include <sys/firmware.h> 56158089Smlaier#include <sys/taskqueue.h> 57145247Sdamien 58145247Sdamien#include <machine/bus.h> 59145247Sdamien#include <machine/resource.h> 60145247Sdamien#include <sys/rman.h> 61145247Sdamien 62145247Sdamien#include <dev/pci/pcireg.h> 63145247Sdamien#include <dev/pci/pcivar.h> 64145247Sdamien 65145247Sdamien#include <net/bpf.h> 66145247Sdamien#include <net/if.h> 67145247Sdamien#include <net/if_arp.h> 68145247Sdamien#include <net/ethernet.h> 69145247Sdamien#include <net/if_dl.h> 70145247Sdamien#include <net/if_media.h> 71145247Sdamien#include <net/if_types.h> 72145247Sdamien 73145247Sdamien#include <net80211/ieee80211_var.h> 74145247Sdamien#include <net80211/ieee80211_radiotap.h> 75178354Ssam#include <net80211/ieee80211_input.h> 76170530Ssam#include <net80211/ieee80211_regdomain.h> 77145247Sdamien 78145247Sdamien#include <netinet/in.h> 79145247Sdamien#include <netinet/in_systm.h> 80145247Sdamien#include <netinet/in_var.h> 81145247Sdamien#include <netinet/ip.h> 82145247Sdamien#include <netinet/if_ether.h> 83145247Sdamien 84158089Smlaier#include <dev/iwi/if_iwireg.h> 85154992Sdamien#include <dev/iwi/if_iwivar.h> 86145247Sdamien 87158089Smlaier#define IWI_DEBUG 88145247Sdamien#ifdef IWI_DEBUG 89145247Sdamien#define DPRINTF(x) do { if (iwi_debug > 0) printf x; } while (0) 90145247Sdamien#define DPRINTFN(n, x) do { if (iwi_debug >= (n)) printf x; } while (0) 91145247Sdamienint iwi_debug = 0; 92145247SdamienSYSCTL_INT(_debug, OID_AUTO, iwi, CTLFLAG_RW, &iwi_debug, 0, "iwi debug level"); 93178354Ssam 94178354Ssamstatic const char *iwi_fw_states[] = { 95178354Ssam "IDLE", /* IWI_FW_IDLE */ 96178354Ssam "LOADING", /* IWI_FW_LOADING */ 97178354Ssam "ASSOCIATING", /* IWI_FW_ASSOCIATING */ 98178354Ssam "DISASSOCIATING", /* IWI_FW_DISASSOCIATING */ 99178354Ssam "SCANNING", /* IWI_FW_SCANNING */ 100178354Ssam}; 101145247Sdamien#else 102145247Sdamien#define DPRINTF(x) 103145247Sdamien#define DPRINTFN(n, x) 104145247Sdamien#endif 105145247Sdamien 106145247SdamienMODULE_DEPEND(iwi, pci, 1, 1, 1); 107145247SdamienMODULE_DEPEND(iwi, wlan, 1, 1, 1); 108156571SdamienMODULE_DEPEND(iwi, firmware, 1, 1, 1); 109145247Sdamien 110158089Smlaierenum { 111158089Smlaier IWI_LED_TX, 112158089Smlaier IWI_LED_RX, 113158089Smlaier IWI_LED_POLL, 114158089Smlaier}; 115158089Smlaier 116145247Sdamienstruct iwi_ident { 117145247Sdamien uint16_t vendor; 118145247Sdamien uint16_t device; 119145247Sdamien const char *name; 120145247Sdamien}; 121145247Sdamien 122145247Sdamienstatic const struct iwi_ident iwi_ident_table[] = { 123145247Sdamien { 0x8086, 0x4220, "Intel(R) PRO/Wireless 2200BG" }, 124145247Sdamien { 0x8086, 0x4221, "Intel(R) PRO/Wireless 2225BG" }, 125145247Sdamien { 0x8086, 0x4223, "Intel(R) PRO/Wireless 2915ABG" }, 126145247Sdamien { 0x8086, 0x4224, "Intel(R) PRO/Wireless 2915ABG" }, 127145247Sdamien 128145247Sdamien { 0, 0, NULL } 129145247Sdamien}; 130145247Sdamien 131178354Ssamstatic struct ieee80211vap *iwi_vap_create(struct ieee80211com *, 132178354Ssam const char name[IFNAMSIZ], int unit, int opmode, int flags, 133178354Ssam const uint8_t bssid[IEEE80211_ADDR_LEN], 134178354Ssam const uint8_t mac[IEEE80211_ADDR_LEN]); 135178354Ssamstatic void iwi_vap_delete(struct ieee80211vap *); 136145247Sdamienstatic void iwi_dma_map_addr(void *, bus_dma_segment_t *, int, int); 137145247Sdamienstatic int iwi_alloc_cmd_ring(struct iwi_softc *, struct iwi_cmd_ring *, 138145247Sdamien int); 139145247Sdamienstatic void iwi_reset_cmd_ring(struct iwi_softc *, struct iwi_cmd_ring *); 140145247Sdamienstatic void iwi_free_cmd_ring(struct iwi_softc *, struct iwi_cmd_ring *); 141145247Sdamienstatic int iwi_alloc_tx_ring(struct iwi_softc *, struct iwi_tx_ring *, 142149338Sdamien int, bus_addr_t, bus_addr_t); 143145247Sdamienstatic void iwi_reset_tx_ring(struct iwi_softc *, struct iwi_tx_ring *); 144145247Sdamienstatic void iwi_free_tx_ring(struct iwi_softc *, struct iwi_tx_ring *); 145145247Sdamienstatic int iwi_alloc_rx_ring(struct iwi_softc *, struct iwi_rx_ring *, 146145247Sdamien int); 147145247Sdamienstatic void iwi_reset_rx_ring(struct iwi_softc *, struct iwi_rx_ring *); 148145247Sdamienstatic void iwi_free_rx_ring(struct iwi_softc *, struct iwi_rx_ring *); 149179643Ssamstatic struct ieee80211_node *iwi_node_alloc(struct ieee80211vap *, 150179643Ssam const uint8_t [IEEE80211_ADDR_LEN]); 151150341Sdamienstatic void iwi_node_free(struct ieee80211_node *); 152145247Sdamienstatic void iwi_media_status(struct ifnet *, struct ifmediareq *); 153178354Ssamstatic int iwi_newstate(struct ieee80211vap *, enum ieee80211_state, int); 154158089Smlaierstatic void iwi_wme_init(struct iwi_softc *); 155178354Ssamstatic int iwi_wme_setparams(struct iwi_softc *, struct ieee80211com *); 156191951Ssamstatic void iwi_update_wme(void *, int); 157149338Sdamienstatic int iwi_wme_update(struct ieee80211com *); 158145247Sdamienstatic uint16_t iwi_read_prom_word(struct iwi_softc *, uint8_t); 159145247Sdamienstatic void iwi_frame_intr(struct iwi_softc *, struct iwi_rx_data *, int, 160145247Sdamien struct iwi_frame *); 161145247Sdamienstatic void iwi_notification_intr(struct iwi_softc *, struct iwi_notif *); 162145247Sdamienstatic void iwi_rx_intr(struct iwi_softc *); 163149338Sdamienstatic void iwi_tx_intr(struct iwi_softc *, struct iwi_tx_ring *); 164145247Sdamienstatic void iwi_intr(void *); 165158089Smlaierstatic int iwi_cmd(struct iwi_softc *, uint8_t, void *, uint8_t); 166158089Smlaierstatic void iwi_write_ibssnode(struct iwi_softc *, const u_int8_t [], int); 167145247Sdamienstatic int iwi_tx_start(struct ifnet *, struct mbuf *, 168150245Sdamien struct ieee80211_node *, int); 169178354Ssamstatic int iwi_raw_xmit(struct ieee80211_node *, struct mbuf *, 170178354Ssam const struct ieee80211_bpf_params *); 171178354Ssamstatic void iwi_start_locked(struct ifnet *); 172145247Sdamienstatic void iwi_start(struct ifnet *); 173170530Ssamstatic void iwi_watchdog(void *); 174145247Sdamienstatic int iwi_ioctl(struct ifnet *, u_long, caddr_t); 175145247Sdamienstatic void iwi_stop_master(struct iwi_softc *); 176145247Sdamienstatic int iwi_reset(struct iwi_softc *); 177158089Smlaierstatic int iwi_load_ucode(struct iwi_softc *, const struct iwi_fw *); 178158089Smlaierstatic int iwi_load_firmware(struct iwi_softc *, const struct iwi_fw *); 179166848Sluigistatic void iwi_release_fw_dma(struct iwi_softc *sc); 180145247Sdamienstatic int iwi_config(struct iwi_softc *); 181178354Ssamstatic int iwi_get_firmware(struct iwi_softc *, enum ieee80211_opmode); 182158089Smlaierstatic void iwi_put_firmware(struct iwi_softc *); 183170530Ssamstatic int iwi_scanchan(struct iwi_softc *, unsigned long, int); 184170530Ssamstatic void iwi_scan_start(struct ieee80211com *); 185170530Ssamstatic void iwi_scan_end(struct ieee80211com *); 186170530Ssamstatic void iwi_set_channel(struct ieee80211com *); 187178354Ssamstatic void iwi_scan_curchan(struct ieee80211_scan_state *, unsigned long maxdwell); 188178354Ssamstatic void iwi_scan_mindwell(struct ieee80211_scan_state *); 189178354Ssamstatic int iwi_auth_and_assoc(struct iwi_softc *, struct ieee80211vap *); 190191746Sthompsastatic void iwi_disassoc(void *, int); 191158089Smlaierstatic int iwi_disassociate(struct iwi_softc *, int quiet); 192178354Ssamstatic void iwi_init_locked(struct iwi_softc *); 193145247Sdamienstatic void iwi_init(void *); 194178354Ssamstatic int iwi_init_fw_dma(struct iwi_softc *, int); 195178354Ssamstatic void iwi_stop_locked(void *); 196178354Ssamstatic void iwi_stop(struct iwi_softc *); 197158089Smlaierstatic void iwi_restart(void *, int); 198158089Smlaierstatic int iwi_getrfkill(struct iwi_softc *); 199158089Smlaierstatic void iwi_radio_on(void *, int); 200158089Smlaierstatic void iwi_radio_off(void *, int); 201158089Smlaierstatic void iwi_sysctlattach(struct iwi_softc *); 202158089Smlaierstatic void iwi_led_event(struct iwi_softc *, int); 203158089Smlaierstatic void iwi_ledattach(struct iwi_softc *); 204145247Sdamien 205145247Sdamienstatic int iwi_probe(device_t); 206145247Sdamienstatic int iwi_attach(device_t); 207145247Sdamienstatic int iwi_detach(device_t); 208145247Sdamienstatic int iwi_shutdown(device_t); 209145247Sdamienstatic int iwi_suspend(device_t); 210145247Sdamienstatic int iwi_resume(device_t); 211145247Sdamien 212145247Sdamienstatic device_method_t iwi_methods[] = { 213145247Sdamien /* Device interface */ 214145247Sdamien DEVMETHOD(device_probe, iwi_probe), 215145247Sdamien DEVMETHOD(device_attach, iwi_attach), 216145247Sdamien DEVMETHOD(device_detach, iwi_detach), 217145247Sdamien DEVMETHOD(device_shutdown, iwi_shutdown), 218145247Sdamien DEVMETHOD(device_suspend, iwi_suspend), 219145247Sdamien DEVMETHOD(device_resume, iwi_resume), 220145247Sdamien 221145247Sdamien { 0, 0 } 222145247Sdamien}; 223145247Sdamien 224145247Sdamienstatic driver_t iwi_driver = { 225145247Sdamien "iwi", 226145247Sdamien iwi_methods, 227145247Sdamien sizeof (struct iwi_softc) 228145247Sdamien}; 229145247Sdamien 230145247Sdamienstatic devclass_t iwi_devclass; 231145247Sdamien 232145247SdamienDRIVER_MODULE(iwi, pci, iwi_driver, iwi_devclass, 0, 0); 233145247Sdamien 234158089Smlaierstatic __inline uint8_t 235158089SmlaierMEM_READ_1(struct iwi_softc *sc, uint32_t addr) 236158089Smlaier{ 237158089Smlaier CSR_WRITE_4(sc, IWI_CSR_INDIRECT_ADDR, addr); 238158089Smlaier return CSR_READ_1(sc, IWI_CSR_INDIRECT_DATA); 239158089Smlaier} 240158089Smlaier 241158089Smlaierstatic __inline uint32_t 242158089SmlaierMEM_READ_4(struct iwi_softc *sc, uint32_t addr) 243158089Smlaier{ 244158089Smlaier CSR_WRITE_4(sc, IWI_CSR_INDIRECT_ADDR, addr); 245158089Smlaier return CSR_READ_4(sc, IWI_CSR_INDIRECT_DATA); 246158089Smlaier} 247158089Smlaier 248145247Sdamienstatic int 249145247Sdamieniwi_probe(device_t dev) 250145247Sdamien{ 251145247Sdamien const struct iwi_ident *ident; 252145247Sdamien 253145247Sdamien for (ident = iwi_ident_table; ident->name != NULL; ident++) { 254145247Sdamien if (pci_get_vendor(dev) == ident->vendor && 255145247Sdamien pci_get_device(dev) == ident->device) { 256145247Sdamien device_set_desc(dev, ident->name); 257145247Sdamien return 0; 258145247Sdamien } 259145247Sdamien } 260145247Sdamien return ENXIO; 261145247Sdamien} 262145247Sdamien 263145247Sdamien/* Base Address Register */ 264145247Sdamien#define IWI_PCI_BAR0 0x10 265145247Sdamien 266145247Sdamienstatic int 267145247Sdamieniwi_attach(device_t dev) 268145247Sdamien{ 269145247Sdamien struct iwi_softc *sc = device_get_softc(dev); 270147256Sbrooks struct ifnet *ifp; 271178354Ssam struct ieee80211com *ic; 272145247Sdamien uint16_t val; 273178354Ssam int i, error; 274178354Ssam uint8_t bands; 275190526Ssam uint8_t macaddr[IEEE80211_ADDR_LEN]; 276145247Sdamien 277145247Sdamien sc->sc_dev = dev; 278145247Sdamien 279178354Ssam ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); 280178354Ssam if (ifp == NULL) { 281178354Ssam device_printf(dev, "can not if_alloc()\n"); 282178354Ssam return ENXIO; 283178354Ssam } 284178354Ssam ic = ifp->if_l2com; 285178354Ssam 286170530Ssam IWI_LOCK_INIT(sc); 287145247Sdamien 288158089Smlaier sc->sc_unr = new_unrhdr(1, IWI_MAX_IBSSNODE-1, &sc->sc_mtx); 289150341Sdamien 290158089Smlaier TASK_INIT(&sc->sc_radiontask, 0, iwi_radio_on, sc); 291158089Smlaier TASK_INIT(&sc->sc_radiofftask, 0, iwi_radio_off, sc); 292170530Ssam TASK_INIT(&sc->sc_restarttask, 0, iwi_restart, sc); 293191746Sthompsa TASK_INIT(&sc->sc_disassoctask, 0, iwi_disassoc, sc); 294191951Ssam TASK_INIT(&sc->sc_wmetask, 0, iwi_update_wme, sc); 295191746Sthompsa 296170530Ssam callout_init_mtx(&sc->sc_wdtimer, &sc->sc_mtx, 0); 297178354Ssam callout_init_mtx(&sc->sc_rftimer, &sc->sc_mtx, 0); 298156598Sdamien 299145247Sdamien if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { 300145247Sdamien device_printf(dev, "chip is in D%d power mode " 301145247Sdamien "-- setting to D0\n", pci_get_powerstate(dev)); 302145247Sdamien pci_set_powerstate(dev, PCI_POWERSTATE_D0); 303145247Sdamien } 304145247Sdamien 305146500Sdamien pci_write_config(dev, 0x41, 0, 1); 306146500Sdamien 307145247Sdamien /* enable bus-mastering */ 308145247Sdamien pci_enable_busmaster(dev); 309145247Sdamien 310145247Sdamien sc->mem_rid = IWI_PCI_BAR0; 311145247Sdamien sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid, 312145247Sdamien RF_ACTIVE); 313145247Sdamien if (sc->mem == NULL) { 314145247Sdamien device_printf(dev, "could not allocate memory resource\n"); 315145247Sdamien goto fail; 316145247Sdamien } 317145247Sdamien 318145247Sdamien sc->sc_st = rman_get_bustag(sc->mem); 319145247Sdamien sc->sc_sh = rman_get_bushandle(sc->mem); 320145247Sdamien 321145247Sdamien sc->irq_rid = 0; 322145247Sdamien sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid, 323145247Sdamien RF_ACTIVE | RF_SHAREABLE); 324145247Sdamien if (sc->irq == NULL) { 325145247Sdamien device_printf(dev, "could not allocate interrupt resource\n"); 326145247Sdamien goto fail; 327145247Sdamien } 328145247Sdamien 329145247Sdamien if (iwi_reset(sc) != 0) { 330145247Sdamien device_printf(dev, "could not reset adapter\n"); 331145247Sdamien goto fail; 332145247Sdamien } 333145247Sdamien 334145247Sdamien /* 335145247Sdamien * Allocate rings. 336145247Sdamien */ 337145247Sdamien if (iwi_alloc_cmd_ring(sc, &sc->cmdq, IWI_CMD_RING_COUNT) != 0) { 338145247Sdamien device_printf(dev, "could not allocate Cmd ring\n"); 339145247Sdamien goto fail; 340145247Sdamien } 341145247Sdamien 342166848Sluigi for (i = 0; i < 4; i++) { 343166848Sluigi error = iwi_alloc_tx_ring(sc, &sc->txq[i], IWI_TX_RING_COUNT, 344166848Sluigi IWI_CSR_TX1_RIDX + i * 4, 345166848Sluigi IWI_CSR_TX1_WIDX + i * 4); 346166848Sluigi if (error != 0) { 347166848Sluigi device_printf(dev, "could not allocate Tx ring %d\n", 348166848Sluigi i+i); 349166848Sluigi goto fail; 350166848Sluigi } 351145247Sdamien } 352145247Sdamien 353145247Sdamien if (iwi_alloc_rx_ring(sc, &sc->rxq, IWI_RX_RING_COUNT) != 0) { 354145247Sdamien device_printf(dev, "could not allocate Rx ring\n"); 355145247Sdamien goto fail; 356145247Sdamien } 357145247Sdamien 358158089Smlaier iwi_wme_init(sc); 359158089Smlaier 360145247Sdamien ifp->if_softc = sc; 361145247Sdamien if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 362145247Sdamien ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 363145247Sdamien ifp->if_init = iwi_init; 364145247Sdamien ifp->if_ioctl = iwi_ioctl; 365145247Sdamien ifp->if_start = iwi_start; 366145247Sdamien IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); 367145247Sdamien ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; 368145247Sdamien IFQ_SET_READY(&ifp->if_snd); 369145247Sdamien 370178354Ssam ic->ic_ifp = ifp; 371178354Ssam ic->ic_opmode = IEEE80211_M_STA; 372145247Sdamien ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ 373145247Sdamien 374145247Sdamien /* set device capabilities */ 375149338Sdamien ic->ic_caps = 376178957Ssam IEEE80211_C_STA /* station mode supported */ 377178957Ssam | IEEE80211_C_IBSS /* IBSS mode supported */ 378170530Ssam | IEEE80211_C_MONITOR /* monitor mode supported */ 379170530Ssam | IEEE80211_C_PMGT /* power save supported */ 380170530Ssam | IEEE80211_C_SHPREAMBLE /* short preamble supported */ 381170530Ssam | IEEE80211_C_WPA /* 802.11i */ 382170530Ssam | IEEE80211_C_WME /* 802.11e */ 383178354Ssam#if 0 384170530Ssam | IEEE80211_C_BGSCAN /* capable of bg scanning */ 385178354Ssam#endif 386170530Ssam ; 387145247Sdamien 388145247Sdamien /* read MAC address from EEPROM */ 389145247Sdamien val = iwi_read_prom_word(sc, IWI_EEPROM_MAC + 0); 390190526Ssam macaddr[0] = val & 0xff; 391190526Ssam macaddr[1] = val >> 8; 392145247Sdamien val = iwi_read_prom_word(sc, IWI_EEPROM_MAC + 1); 393190526Ssam macaddr[2] = val & 0xff; 394190526Ssam macaddr[3] = val >> 8; 395145247Sdamien val = iwi_read_prom_word(sc, IWI_EEPROM_MAC + 2); 396190526Ssam macaddr[4] = val & 0xff; 397190526Ssam macaddr[5] = val >> 8; 398170530Ssam 399170530Ssam bands = 0; 400170530Ssam setbit(&bands, IEEE80211_MODE_11B); 401170530Ssam setbit(&bands, IEEE80211_MODE_11G); 402170530Ssam if (pci_get_device(dev) >= 0x4223) 403170530Ssam setbit(&bands, IEEE80211_MODE_11A); 404178354Ssam ieee80211_init_channels(ic, NULL, &bands); 405145247Sdamien 406190526Ssam ieee80211_ifattach(ic, macaddr); 407150341Sdamien /* override default methods */ 408150341Sdamien ic->ic_node_alloc = iwi_node_alloc; 409150341Sdamien sc->sc_node_free = ic->ic_node_free; 410150341Sdamien ic->ic_node_free = iwi_node_free; 411178354Ssam ic->ic_raw_xmit = iwi_raw_xmit; 412170530Ssam ic->ic_scan_start = iwi_scan_start; 413170530Ssam ic->ic_scan_end = iwi_scan_end; 414170530Ssam ic->ic_set_channel = iwi_set_channel; 415170530Ssam ic->ic_scan_curchan = iwi_scan_curchan; 416170530Ssam ic->ic_scan_mindwell = iwi_scan_mindwell; 417178354Ssam ic->ic_wme.wme_update = iwi_wme_update; 418170530Ssam 419178354Ssam ic->ic_vap_create = iwi_vap_create; 420178354Ssam ic->ic_vap_delete = iwi_vap_delete; 421145247Sdamien 422192468Ssam ieee80211_radiotap_attach(ic, 423192468Ssam &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), 424192468Ssam IWI_TX_RADIOTAP_PRESENT, 425192468Ssam &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), 426192468Ssam IWI_RX_RADIOTAP_PRESENT); 427145247Sdamien 428158089Smlaier iwi_sysctlattach(sc); 429158089Smlaier iwi_ledattach(sc); 430145247Sdamien 431145247Sdamien /* 432145247Sdamien * Hook our interrupt after all initialization is complete. 433145247Sdamien */ 434145247Sdamien error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET | INTR_MPSAFE, 435166901Spiso NULL, iwi_intr, sc, &sc->sc_ih); 436145247Sdamien if (error != 0) { 437145247Sdamien device_printf(dev, "could not set up interrupt\n"); 438145247Sdamien goto fail; 439145247Sdamien } 440145247Sdamien 441145247Sdamien if (bootverbose) 442145247Sdamien ieee80211_announce(ic); 443145247Sdamien 444145247Sdamien return 0; 445178354Ssamfail: 446178354Ssam /* XXX fix */ 447178354Ssam iwi_detach(dev); 448145247Sdamien return ENXIO; 449145247Sdamien} 450145247Sdamien 451145247Sdamienstatic int 452145247Sdamieniwi_detach(device_t dev) 453145247Sdamien{ 454145247Sdamien struct iwi_softc *sc = device_get_softc(dev); 455178354Ssam struct ifnet *ifp = sc->sc_ifp; 456178354Ssam struct ieee80211com *ic = ifp->if_l2com; 457145247Sdamien 458178354Ssam /* NB: do early to drain any pending tasks */ 459191746Sthompsa ieee80211_draintask(ic, &sc->sc_radiontask); 460191746Sthompsa ieee80211_draintask(ic, &sc->sc_radiofftask); 461191746Sthompsa ieee80211_draintask(ic, &sc->sc_restarttask); 462191746Sthompsa ieee80211_draintask(ic, &sc->sc_disassoctask); 463178354Ssam 464191912Sthompsa iwi_stop(sc); 465191912Sthompsa 466191912Sthompsa ieee80211_ifdetach(ic); 467191912Sthompsa 468164069Sjhb iwi_put_firmware(sc); 469166848Sluigi iwi_release_fw_dma(sc); 470145247Sdamien 471145247Sdamien iwi_free_cmd_ring(sc, &sc->cmdq); 472149338Sdamien iwi_free_tx_ring(sc, &sc->txq[0]); 473149338Sdamien iwi_free_tx_ring(sc, &sc->txq[1]); 474149338Sdamien iwi_free_tx_ring(sc, &sc->txq[2]); 475149338Sdamien iwi_free_tx_ring(sc, &sc->txq[3]); 476145247Sdamien iwi_free_rx_ring(sc, &sc->rxq); 477145247Sdamien 478178354Ssam bus_teardown_intr(dev, sc->irq, sc->sc_ih); 479178354Ssam bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq); 480145247Sdamien 481178354Ssam bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem); 482145247Sdamien 483178354Ssam delete_unrhdr(sc->sc_unr); 484150330Sdamien 485170530Ssam IWI_LOCK_DESTROY(sc); 486145247Sdamien 487178354Ssam if_free(ifp); 488178354Ssam 489145247Sdamien return 0; 490145247Sdamien} 491145247Sdamien 492178354Ssamstatic struct ieee80211vap * 493178354Ssamiwi_vap_create(struct ieee80211com *ic, 494178354Ssam const char name[IFNAMSIZ], int unit, int opmode, int flags, 495178354Ssam const uint8_t bssid[IEEE80211_ADDR_LEN], 496178354Ssam const uint8_t mac[IEEE80211_ADDR_LEN]) 497178354Ssam{ 498178354Ssam struct ifnet *ifp = ic->ic_ifp; 499178354Ssam struct iwi_softc *sc = ifp->if_softc; 500178354Ssam struct iwi_vap *ivp; 501178354Ssam struct ieee80211vap *vap; 502178354Ssam int i; 503178354Ssam 504178354Ssam if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ 505178354Ssam return NULL; 506178354Ssam /* 507178354Ssam * Get firmware image (and possibly dma memory) on mode change. 508178354Ssam */ 509178354Ssam if (iwi_get_firmware(sc, opmode)) 510178354Ssam return NULL; 511178354Ssam /* allocate DMA memory for mapping firmware image */ 512178354Ssam i = sc->fw_fw.size; 513178354Ssam if (sc->fw_boot.size > i) 514178354Ssam i = sc->fw_boot.size; 515178354Ssam /* XXX do we dma the ucode as well ? */ 516178354Ssam if (sc->fw_uc.size > i) 517178354Ssam i = sc->fw_uc.size; 518178354Ssam if (iwi_init_fw_dma(sc, i)) 519178354Ssam return NULL; 520178354Ssam 521178354Ssam ivp = (struct iwi_vap *) malloc(sizeof(struct iwi_vap), 522178354Ssam M_80211_VAP, M_NOWAIT | M_ZERO); 523178354Ssam if (ivp == NULL) 524178354Ssam return NULL; 525178354Ssam vap = &ivp->iwi_vap; 526178354Ssam ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac); 527178354Ssam /* override the default, the setting comes from the linux driver */ 528178354Ssam vap->iv_bmissthreshold = 24; 529178354Ssam /* override with driver methods */ 530178354Ssam ivp->iwi_newstate = vap->iv_newstate; 531178354Ssam vap->iv_newstate = iwi_newstate; 532178354Ssam 533178354Ssam /* complete setup */ 534178354Ssam ieee80211_vap_attach(vap, ieee80211_media_change, iwi_media_status); 535178354Ssam ic->ic_opmode = opmode; 536178354Ssam return vap; 537178354Ssam} 538178354Ssam 539145247Sdamienstatic void 540178354Ssamiwi_vap_delete(struct ieee80211vap *vap) 541178354Ssam{ 542178354Ssam struct iwi_vap *ivp = IWI_VAP(vap); 543178354Ssam 544178354Ssam ieee80211_vap_detach(vap); 545178354Ssam free(ivp, M_80211_VAP); 546178354Ssam} 547178354Ssam 548178354Ssamstatic void 549145247Sdamieniwi_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nseg, int error) 550145247Sdamien{ 551145247Sdamien if (error != 0) 552145247Sdamien return; 553145247Sdamien 554145247Sdamien KASSERT(nseg == 1, ("too many DMA segments, %d should be 1", nseg)); 555145247Sdamien 556145247Sdamien *(bus_addr_t *)arg = segs[0].ds_addr; 557145247Sdamien} 558145247Sdamien 559145247Sdamienstatic int 560145247Sdamieniwi_alloc_cmd_ring(struct iwi_softc *sc, struct iwi_cmd_ring *ring, int count) 561145247Sdamien{ 562145247Sdamien int error; 563145247Sdamien 564145247Sdamien ring->count = count; 565145247Sdamien ring->queued = 0; 566145247Sdamien ring->cur = ring->next = 0; 567145247Sdamien 568166416Skevlo error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 4, 0, 569166416Skevlo BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, 570166416Skevlo count * IWI_CMD_DESC_SIZE, 1, count * IWI_CMD_DESC_SIZE, 0, 571166416Skevlo NULL, NULL, &ring->desc_dmat); 572145247Sdamien if (error != 0) { 573145247Sdamien device_printf(sc->sc_dev, "could not create desc DMA tag\n"); 574145247Sdamien goto fail; 575145247Sdamien } 576145247Sdamien 577145247Sdamien error = bus_dmamem_alloc(ring->desc_dmat, (void **)&ring->desc, 578145247Sdamien BUS_DMA_NOWAIT | BUS_DMA_ZERO, &ring->desc_map); 579145247Sdamien if (error != 0) { 580145247Sdamien device_printf(sc->sc_dev, "could not allocate DMA memory\n"); 581145247Sdamien goto fail; 582145247Sdamien } 583145247Sdamien 584145247Sdamien error = bus_dmamap_load(ring->desc_dmat, ring->desc_map, ring->desc, 585145247Sdamien count * IWI_CMD_DESC_SIZE, iwi_dma_map_addr, &ring->physaddr, 0); 586145247Sdamien if (error != 0) { 587145247Sdamien device_printf(sc->sc_dev, "could not load desc DMA map\n"); 588145247Sdamien goto fail; 589145247Sdamien } 590145247Sdamien 591145247Sdamien return 0; 592145247Sdamien 593145247Sdamienfail: iwi_free_cmd_ring(sc, ring); 594145247Sdamien return error; 595145247Sdamien} 596145247Sdamien 597145247Sdamienstatic void 598145247Sdamieniwi_reset_cmd_ring(struct iwi_softc *sc, struct iwi_cmd_ring *ring) 599145247Sdamien{ 600145247Sdamien ring->queued = 0; 601145247Sdamien ring->cur = ring->next = 0; 602145247Sdamien} 603145247Sdamien 604145247Sdamienstatic void 605145247Sdamieniwi_free_cmd_ring(struct iwi_softc *sc, struct iwi_cmd_ring *ring) 606145247Sdamien{ 607145247Sdamien if (ring->desc != NULL) { 608145247Sdamien bus_dmamap_sync(ring->desc_dmat, ring->desc_map, 609145247Sdamien BUS_DMASYNC_POSTWRITE); 610145247Sdamien bus_dmamap_unload(ring->desc_dmat, ring->desc_map); 611145247Sdamien bus_dmamem_free(ring->desc_dmat, ring->desc, ring->desc_map); 612145247Sdamien } 613145247Sdamien 614145247Sdamien if (ring->desc_dmat != NULL) 615145247Sdamien bus_dma_tag_destroy(ring->desc_dmat); 616145247Sdamien} 617145247Sdamien 618145247Sdamienstatic int 619149338Sdamieniwi_alloc_tx_ring(struct iwi_softc *sc, struct iwi_tx_ring *ring, int count, 620149338Sdamien bus_addr_t csr_ridx, bus_addr_t csr_widx) 621145247Sdamien{ 622145247Sdamien int i, error; 623145247Sdamien 624145247Sdamien ring->count = count; 625145247Sdamien ring->queued = 0; 626145247Sdamien ring->cur = ring->next = 0; 627149338Sdamien ring->csr_ridx = csr_ridx; 628149338Sdamien ring->csr_widx = csr_widx; 629145247Sdamien 630166416Skevlo error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 4, 0, 631166416Skevlo BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, 632166416Skevlo count * IWI_TX_DESC_SIZE, 1, count * IWI_TX_DESC_SIZE, 0, NULL, 633166416Skevlo NULL, &ring->desc_dmat); 634145247Sdamien if (error != 0) { 635145247Sdamien device_printf(sc->sc_dev, "could not create desc DMA tag\n"); 636145247Sdamien goto fail; 637145247Sdamien } 638145247Sdamien 639145247Sdamien error = bus_dmamem_alloc(ring->desc_dmat, (void **)&ring->desc, 640145247Sdamien BUS_DMA_NOWAIT | BUS_DMA_ZERO, &ring->desc_map); 641145247Sdamien if (error != 0) { 642145247Sdamien device_printf(sc->sc_dev, "could not allocate DMA memory\n"); 643145247Sdamien goto fail; 644145247Sdamien } 645145247Sdamien 646145247Sdamien error = bus_dmamap_load(ring->desc_dmat, ring->desc_map, ring->desc, 647145247Sdamien count * IWI_TX_DESC_SIZE, iwi_dma_map_addr, &ring->physaddr, 0); 648145247Sdamien if (error != 0) { 649145247Sdamien device_printf(sc->sc_dev, "could not load desc DMA map\n"); 650145247Sdamien goto fail; 651145247Sdamien } 652145247Sdamien 653145247Sdamien ring->data = malloc(count * sizeof (struct iwi_tx_data), M_DEVBUF, 654145247Sdamien M_NOWAIT | M_ZERO); 655145247Sdamien if (ring->data == NULL) { 656145247Sdamien device_printf(sc->sc_dev, "could not allocate soft data\n"); 657145247Sdamien error = ENOMEM; 658145247Sdamien goto fail; 659145247Sdamien } 660145247Sdamien 661166416Skevlo error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, 662166416Skevlo BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, 663166416Skevlo IWI_MAX_NSEG, MCLBYTES, 0, NULL, NULL, &ring->data_dmat); 664145247Sdamien if (error != 0) { 665145247Sdamien device_printf(sc->sc_dev, "could not create data DMA tag\n"); 666145247Sdamien goto fail; 667145247Sdamien } 668145247Sdamien 669145247Sdamien for (i = 0; i < count; i++) { 670145247Sdamien error = bus_dmamap_create(ring->data_dmat, 0, 671145247Sdamien &ring->data[i].map); 672145247Sdamien if (error != 0) { 673145247Sdamien device_printf(sc->sc_dev, "could not create DMA map\n"); 674145247Sdamien goto fail; 675145247Sdamien } 676145247Sdamien } 677145247Sdamien 678145247Sdamien return 0; 679145247Sdamien 680145247Sdamienfail: iwi_free_tx_ring(sc, ring); 681145247Sdamien return error; 682145247Sdamien} 683145247Sdamien 684145247Sdamienstatic void 685145247Sdamieniwi_reset_tx_ring(struct iwi_softc *sc, struct iwi_tx_ring *ring) 686145247Sdamien{ 687145247Sdamien struct iwi_tx_data *data; 688145247Sdamien int i; 689145247Sdamien 690145247Sdamien for (i = 0; i < ring->count; i++) { 691145247Sdamien data = &ring->data[i]; 692145247Sdamien 693145247Sdamien if (data->m != NULL) { 694145247Sdamien bus_dmamap_sync(ring->data_dmat, data->map, 695145247Sdamien BUS_DMASYNC_POSTWRITE); 696145247Sdamien bus_dmamap_unload(ring->data_dmat, data->map); 697145247Sdamien m_freem(data->m); 698145247Sdamien data->m = NULL; 699145247Sdamien } 700145247Sdamien 701145247Sdamien if (data->ni != NULL) { 702145247Sdamien ieee80211_free_node(data->ni); 703145247Sdamien data->ni = NULL; 704145247Sdamien } 705145247Sdamien } 706145247Sdamien 707145247Sdamien ring->queued = 0; 708145247Sdamien ring->cur = ring->next = 0; 709145247Sdamien} 710145247Sdamien 711145247Sdamienstatic void 712145247Sdamieniwi_free_tx_ring(struct iwi_softc *sc, struct iwi_tx_ring *ring) 713145247Sdamien{ 714145247Sdamien struct iwi_tx_data *data; 715145247Sdamien int i; 716145247Sdamien 717145247Sdamien if (ring->desc != NULL) { 718145247Sdamien bus_dmamap_sync(ring->desc_dmat, ring->desc_map, 719145247Sdamien BUS_DMASYNC_POSTWRITE); 720145247Sdamien bus_dmamap_unload(ring->desc_dmat, ring->desc_map); 721145247Sdamien bus_dmamem_free(ring->desc_dmat, ring->desc, ring->desc_map); 722145247Sdamien } 723145247Sdamien 724145247Sdamien if (ring->desc_dmat != NULL) 725145247Sdamien bus_dma_tag_destroy(ring->desc_dmat); 726145247Sdamien 727145247Sdamien if (ring->data != NULL) { 728145247Sdamien for (i = 0; i < ring->count; i++) { 729145247Sdamien data = &ring->data[i]; 730145247Sdamien 731145247Sdamien if (data->m != NULL) { 732145247Sdamien bus_dmamap_sync(ring->data_dmat, data->map, 733145247Sdamien BUS_DMASYNC_POSTWRITE); 734145247Sdamien bus_dmamap_unload(ring->data_dmat, data->map); 735145247Sdamien m_freem(data->m); 736145247Sdamien } 737145247Sdamien 738145247Sdamien if (data->ni != NULL) 739145247Sdamien ieee80211_free_node(data->ni); 740145247Sdamien 741145247Sdamien if (data->map != NULL) 742145247Sdamien bus_dmamap_destroy(ring->data_dmat, data->map); 743145247Sdamien } 744145247Sdamien 745145247Sdamien free(ring->data, M_DEVBUF); 746145247Sdamien } 747145247Sdamien 748145247Sdamien if (ring->data_dmat != NULL) 749145247Sdamien bus_dma_tag_destroy(ring->data_dmat); 750145247Sdamien} 751145247Sdamien 752145247Sdamienstatic int 753145247Sdamieniwi_alloc_rx_ring(struct iwi_softc *sc, struct iwi_rx_ring *ring, int count) 754145247Sdamien{ 755145247Sdamien struct iwi_rx_data *data; 756145247Sdamien int i, error; 757145247Sdamien 758145247Sdamien ring->count = count; 759145247Sdamien ring->cur = 0; 760145247Sdamien 761145247Sdamien ring->data = malloc(count * sizeof (struct iwi_rx_data), M_DEVBUF, 762145247Sdamien M_NOWAIT | M_ZERO); 763145247Sdamien if (ring->data == NULL) { 764145247Sdamien device_printf(sc->sc_dev, "could not allocate soft data\n"); 765145247Sdamien error = ENOMEM; 766145247Sdamien goto fail; 767145247Sdamien } 768145247Sdamien 769166416Skevlo error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, 770166416Skevlo BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, 771166416Skevlo 1, MCLBYTES, 0, NULL, NULL, &ring->data_dmat); 772145247Sdamien if (error != 0) { 773145247Sdamien device_printf(sc->sc_dev, "could not create data DMA tag\n"); 774145247Sdamien goto fail; 775145247Sdamien } 776145247Sdamien 777145247Sdamien for (i = 0; i < count; i++) { 778145247Sdamien data = &ring->data[i]; 779145247Sdamien 780145247Sdamien error = bus_dmamap_create(ring->data_dmat, 0, &data->map); 781145247Sdamien if (error != 0) { 782145247Sdamien device_printf(sc->sc_dev, "could not create DMA map\n"); 783145247Sdamien goto fail; 784145247Sdamien } 785145247Sdamien 786145247Sdamien data->m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); 787145247Sdamien if (data->m == NULL) { 788145247Sdamien device_printf(sc->sc_dev, 789145247Sdamien "could not allocate rx mbuf\n"); 790145247Sdamien error = ENOMEM; 791145247Sdamien goto fail; 792145247Sdamien } 793145247Sdamien 794145247Sdamien error = bus_dmamap_load(ring->data_dmat, data->map, 795145247Sdamien mtod(data->m, void *), MCLBYTES, iwi_dma_map_addr, 796145247Sdamien &data->physaddr, 0); 797145247Sdamien if (error != 0) { 798145247Sdamien device_printf(sc->sc_dev, 799145247Sdamien "could not load rx buf DMA map"); 800145247Sdamien goto fail; 801145247Sdamien } 802145247Sdamien 803145247Sdamien data->reg = IWI_CSR_RX_BASE + i * 4; 804145247Sdamien } 805145247Sdamien 806145247Sdamien return 0; 807145247Sdamien 808145247Sdamienfail: iwi_free_rx_ring(sc, ring); 809145247Sdamien return error; 810145247Sdamien} 811145247Sdamien 812145247Sdamienstatic void 813145247Sdamieniwi_reset_rx_ring(struct iwi_softc *sc, struct iwi_rx_ring *ring) 814145247Sdamien{ 815145247Sdamien ring->cur = 0; 816145247Sdamien} 817145247Sdamien 818145247Sdamienstatic void 819145247Sdamieniwi_free_rx_ring(struct iwi_softc *sc, struct iwi_rx_ring *ring) 820145247Sdamien{ 821145247Sdamien struct iwi_rx_data *data; 822145247Sdamien int i; 823145247Sdamien 824145247Sdamien if (ring->data != NULL) { 825145247Sdamien for (i = 0; i < ring->count; i++) { 826145247Sdamien data = &ring->data[i]; 827145247Sdamien 828145247Sdamien if (data->m != NULL) { 829145247Sdamien bus_dmamap_sync(ring->data_dmat, data->map, 830145247Sdamien BUS_DMASYNC_POSTREAD); 831145247Sdamien bus_dmamap_unload(ring->data_dmat, data->map); 832145247Sdamien m_freem(data->m); 833145247Sdamien } 834145247Sdamien 835145247Sdamien if (data->map != NULL) 836145247Sdamien bus_dmamap_destroy(ring->data_dmat, data->map); 837145247Sdamien } 838145247Sdamien 839145247Sdamien free(ring->data, M_DEVBUF); 840145247Sdamien } 841145247Sdamien 842145247Sdamien if (ring->data_dmat != NULL) 843145247Sdamien bus_dma_tag_destroy(ring->data_dmat); 844145247Sdamien} 845145247Sdamien 846145247Sdamienstatic int 847145247Sdamieniwi_shutdown(device_t dev) 848145247Sdamien{ 849145247Sdamien struct iwi_softc *sc = device_get_softc(dev); 850145247Sdamien 851145247Sdamien iwi_stop(sc); 852158089Smlaier iwi_put_firmware(sc); /* ??? XXX */ 853145247Sdamien 854145247Sdamien return 0; 855145247Sdamien} 856145247Sdamien 857145247Sdamienstatic int 858145247Sdamieniwi_suspend(device_t dev) 859145247Sdamien{ 860145247Sdamien struct iwi_softc *sc = device_get_softc(dev); 861145247Sdamien 862145247Sdamien iwi_stop(sc); 863145247Sdamien 864145247Sdamien return 0; 865145247Sdamien} 866145247Sdamien 867145247Sdamienstatic int 868145247Sdamieniwi_resume(device_t dev) 869145247Sdamien{ 870145247Sdamien struct iwi_softc *sc = device_get_softc(dev); 871178354Ssam struct ifnet *ifp = sc->sc_ifp; 872145247Sdamien 873146500Sdamien pci_write_config(dev, 0x41, 0, 1); 874146500Sdamien 875178354Ssam if (ifp->if_flags & IFF_UP) 876178354Ssam iwi_init(sc); 877145247Sdamien 878145247Sdamien return 0; 879145247Sdamien} 880145247Sdamien 881150341Sdamienstatic struct ieee80211_node * 882179643Ssamiwi_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) 883150341Sdamien{ 884150341Sdamien struct iwi_node *in; 885150341Sdamien 886150341Sdamien in = malloc(sizeof (struct iwi_node), M_80211_NODE, M_NOWAIT | M_ZERO); 887150341Sdamien if (in == NULL) 888150341Sdamien return NULL; 889179643Ssam /* XXX assign sta table entry for adhoc */ 890150341Sdamien in->in_station = -1; 891150341Sdamien 892150341Sdamien return &in->in_node; 893150341Sdamien} 894150341Sdamien 895150341Sdamienstatic void 896150341Sdamieniwi_node_free(struct ieee80211_node *ni) 897150341Sdamien{ 898150341Sdamien struct ieee80211com *ic = ni->ni_ic; 899150341Sdamien struct iwi_softc *sc = ic->ic_ifp->if_softc; 900150341Sdamien struct iwi_node *in = (struct iwi_node *)ni; 901150341Sdamien 902158089Smlaier if (in->in_station != -1) { 903158089Smlaier DPRINTF(("%s mac %6D station %u\n", __func__, 904158089Smlaier ni->ni_macaddr, ":", in->in_station)); 905150341Sdamien free_unr(sc->sc_unr, in->in_station); 906158089Smlaier } 907150341Sdamien 908150341Sdamien sc->sc_node_free(ni); 909150341Sdamien} 910150341Sdamien 911158089Smlaier/* 912158089Smlaier * Convert h/w rate code to IEEE rate code. 913158089Smlaier */ 914158089Smlaierstatic int 915158089Smlaieriwi_cvtrate(int iwirate) 916158089Smlaier{ 917158089Smlaier switch (iwirate) { 918158089Smlaier case IWI_RATE_DS1: return 2; 919158089Smlaier case IWI_RATE_DS2: return 4; 920158089Smlaier case IWI_RATE_DS5: return 11; 921158089Smlaier case IWI_RATE_DS11: return 22; 922158089Smlaier case IWI_RATE_OFDM6: return 12; 923158089Smlaier case IWI_RATE_OFDM9: return 18; 924158089Smlaier case IWI_RATE_OFDM12: return 24; 925158089Smlaier case IWI_RATE_OFDM18: return 36; 926158089Smlaier case IWI_RATE_OFDM24: return 48; 927158089Smlaier case IWI_RATE_OFDM36: return 72; 928158089Smlaier case IWI_RATE_OFDM48: return 96; 929158089Smlaier case IWI_RATE_OFDM54: return 108; 930158089Smlaier } 931145247Sdamien return 0; 932145247Sdamien} 933145247Sdamien 934145247Sdamien/* 935150341Sdamien * The firmware automatically adapts the transmit speed. We report its current 936150341Sdamien * value here. 937145247Sdamien */ 938145247Sdamienstatic void 939145247Sdamieniwi_media_status(struct ifnet *ifp, struct ifmediareq *imr) 940145247Sdamien{ 941178354Ssam struct ieee80211vap *vap = ifp->if_softc; 942178354Ssam struct ieee80211com *ic = vap->iv_ic; 943178354Ssam struct iwi_softc *sc = ic->ic_ifp->if_softc; 944145247Sdamien 945145247Sdamien /* read current transmission rate from adapter */ 946178354Ssam vap->iv_bss->ni_txrate = 947178354Ssam iwi_cvtrate(CSR_READ_4(sc, IWI_CSR_CURRENT_TX_RATE)); 948178354Ssam ieee80211_media_status(ifp, imr); 949145247Sdamien} 950145247Sdamien 951145247Sdamienstatic int 952178354Ssamiwi_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) 953145247Sdamien{ 954178354Ssam struct iwi_vap *ivp = IWI_VAP(vap); 955178354Ssam struct ieee80211com *ic = vap->iv_ic; 956145247Sdamien struct ifnet *ifp = ic->ic_ifp; 957145247Sdamien struct iwi_softc *sc = ifp->if_softc; 958178354Ssam IWI_LOCK_DECL; 959145247Sdamien 960158089Smlaier DPRINTF(("%s: %s -> %s flags 0x%x\n", __func__, 961178354Ssam ieee80211_state_name[vap->iv_state], 962158089Smlaier ieee80211_state_name[nstate], sc->flags)); 963154992Sdamien 964191746Sthompsa IEEE80211_UNLOCK(ic); 965191746Sthompsa IWI_LOCK(sc); 966145247Sdamien switch (nstate) { 967178354Ssam case IEEE80211_S_INIT: 968178354Ssam /* 969178354Ssam * NB: don't try to do this if iwi_stop_master has 970178354Ssam * shutdown the firmware and disabled interrupts. 971178354Ssam */ 972178354Ssam if (vap->iv_state == IEEE80211_S_RUN && 973178354Ssam (sc->flags & IWI_FLAG_FW_INITED)) 974191746Sthompsa iwi_disassociate(sc, 0); 975178354Ssam break; 976145247Sdamien case IEEE80211_S_AUTH: 977191746Sthompsa iwi_auth_and_assoc(sc, vap); 978191746Sthompsa break; 979145247Sdamien case IEEE80211_S_RUN: 980178354Ssam if (vap->iv_opmode == IEEE80211_M_IBSS && 981178354Ssam vap->iv_state == IEEE80211_S_SCAN) { 982158089Smlaier /* 983158089Smlaier * XXX when joining an ibss network we are called 984158089Smlaier * with a SCAN -> RUN transition on scan complete. 985158089Smlaier * Use that to call iwi_auth_and_assoc. On completing 986158089Smlaier * the join we are then called again with an 987158089Smlaier * AUTH -> RUN transition and we want to do nothing. 988158089Smlaier * This is all totally bogus and needs to be redone. 989158089Smlaier */ 990191746Sthompsa iwi_auth_and_assoc(sc, vap); 991178354Ssam } 992145247Sdamien break; 993170530Ssam case IEEE80211_S_ASSOC: 994170530Ssam /* 995178354Ssam * If we are transitioning from AUTH then just wait 996178354Ssam * for the ASSOC status to come back from the firmware. 997178354Ssam * Otherwise we need to issue the association request. 998170530Ssam */ 999178354Ssam if (vap->iv_state == IEEE80211_S_AUTH) 1000178354Ssam break; 1001191746Sthompsa iwi_auth_and_assoc(sc, vap); 1002191746Sthompsa break; 1003170530Ssam default: 1004170530Ssam break; 1005145247Sdamien } 1006191746Sthompsa IWI_UNLOCK(sc); 1007191746Sthompsa IEEE80211_LOCK(ic); 1008178354Ssam return ivp->iwi_newstate(vap, nstate, arg); 1009145247Sdamien} 1010145247Sdamien 1011149346Sdamien/* 1012149346Sdamien * WME parameters coming from IEEE 802.11e specification. These values are 1013149346Sdamien * already declared in ieee80211_proto.c, but they are static so they can't 1014149346Sdamien * be reused here. 1015149346Sdamien */ 1016149346Sdamienstatic const struct wmeParams iwi_wme_cck_params[WME_NUM_AC] = { 1017149346Sdamien { 0, 3, 5, 7, 0 }, /* WME_AC_BE */ 1018149346Sdamien { 0, 3, 5, 10, 0 }, /* WME_AC_BK */ 1019149346Sdamien { 0, 2, 4, 5, 188 }, /* WME_AC_VI */ 1020149346Sdamien { 0, 2, 3, 4, 102 } /* WME_AC_VO */ 1021149346Sdamien}; 1022149346Sdamien 1023149346Sdamienstatic const struct wmeParams iwi_wme_ofdm_params[WME_NUM_AC] = { 1024149346Sdamien { 0, 3, 4, 6, 0 }, /* WME_AC_BE */ 1025149346Sdamien { 0, 3, 4, 10, 0 }, /* WME_AC_BK */ 1026149346Sdamien { 0, 2, 3, 4, 94 }, /* WME_AC_VI */ 1027149346Sdamien { 0, 2, 2, 3, 47 } /* WME_AC_VO */ 1028149346Sdamien}; 1029158089Smlaier#define IWI_EXP2(v) htole16((1 << (v)) - 1) 1030158089Smlaier#define IWI_USEC(v) htole16(IEEE80211_TXOP_TO_US(v)) 1031149346Sdamien 1032158089Smlaierstatic void 1033158089Smlaieriwi_wme_init(struct iwi_softc *sc) 1034149338Sdamien{ 1035149346Sdamien const struct wmeParams *wmep; 1036149346Sdamien int ac; 1037149338Sdamien 1038158089Smlaier memset(sc->wme, 0, sizeof sc->wme); 1039149346Sdamien for (ac = 0; ac < WME_NUM_AC; ac++) { 1040149346Sdamien /* set WME values for CCK modulation */ 1041149346Sdamien wmep = &iwi_wme_cck_params[ac]; 1042158089Smlaier sc->wme[1].aifsn[ac] = wmep->wmep_aifsn; 1043158089Smlaier sc->wme[1].cwmin[ac] = IWI_EXP2(wmep->wmep_logcwmin); 1044158089Smlaier sc->wme[1].cwmax[ac] = IWI_EXP2(wmep->wmep_logcwmax); 1045158089Smlaier sc->wme[1].burst[ac] = IWI_USEC(wmep->wmep_txopLimit); 1046158089Smlaier sc->wme[1].acm[ac] = wmep->wmep_acm; 1047149346Sdamien 1048149346Sdamien /* set WME values for OFDM modulation */ 1049149346Sdamien wmep = &iwi_wme_ofdm_params[ac]; 1050158089Smlaier sc->wme[2].aifsn[ac] = wmep->wmep_aifsn; 1051158089Smlaier sc->wme[2].cwmin[ac] = IWI_EXP2(wmep->wmep_logcwmin); 1052158089Smlaier sc->wme[2].cwmax[ac] = IWI_EXP2(wmep->wmep_logcwmax); 1053158089Smlaier sc->wme[2].burst[ac] = IWI_USEC(wmep->wmep_txopLimit); 1054158089Smlaier sc->wme[2].acm[ac] = wmep->wmep_acm; 1055149346Sdamien } 1056158089Smlaier} 1057149346Sdamien 1058158089Smlaierstatic int 1059178354Ssamiwi_wme_setparams(struct iwi_softc *sc, struct ieee80211com *ic) 1060158089Smlaier{ 1061158089Smlaier const struct wmeParams *wmep; 1062158089Smlaier int ac; 1063158089Smlaier 1064158089Smlaier for (ac = 0; ac < WME_NUM_AC; ac++) { 1065158089Smlaier /* set WME values for current operating mode */ 1066158089Smlaier wmep = &ic->ic_wme.wme_chanParams.cap_wmeParams[ac]; 1067158089Smlaier sc->wme[0].aifsn[ac] = wmep->wmep_aifsn; 1068158089Smlaier sc->wme[0].cwmin[ac] = IWI_EXP2(wmep->wmep_logcwmin); 1069158089Smlaier sc->wme[0].cwmax[ac] = IWI_EXP2(wmep->wmep_logcwmax); 1070158089Smlaier sc->wme[0].burst[ac] = IWI_USEC(wmep->wmep_txopLimit); 1071158089Smlaier sc->wme[0].acm[ac] = wmep->wmep_acm; 1072158089Smlaier } 1073158089Smlaier 1074149346Sdamien DPRINTF(("Setting WME parameters\n")); 1075158089Smlaier return iwi_cmd(sc, IWI_CMD_SET_WME_PARAMS, sc->wme, sizeof sc->wme); 1076158089Smlaier} 1077149346Sdamien#undef IWI_USEC 1078149346Sdamien#undef IWI_EXP2 1079158089Smlaier 1080191951Ssamstatic void 1081191951Ssamiwi_update_wme(void *arg, int npending) 1082191951Ssam{ 1083191951Ssam struct ieee80211com *ic = arg; 1084191951Ssam struct iwi_softc *sc = ic->ic_ifp->if_softc; 1085191951Ssam IWI_LOCK_DECL; 1086191951Ssam 1087191951Ssam IWI_LOCK(sc); 1088191951Ssam (void) iwi_wme_setparams(sc, ic); 1089191951Ssam IWI_UNLOCK(sc); 1090191951Ssam} 1091191951Ssam 1092158089Smlaierstatic int 1093158089Smlaieriwi_wme_update(struct ieee80211com *ic) 1094158089Smlaier{ 1095158089Smlaier struct iwi_softc *sc = ic->ic_ifp->if_softc; 1096191746Sthompsa struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 1097158089Smlaier 1098158089Smlaier /* 1099158089Smlaier * We may be called to update the WME parameters in 1100158089Smlaier * the adapter at various places. If we're already 1101191951Ssam * associated then initiate the request immediately; 1102191951Ssam * otherwise we assume the params will get sent down 1103191951Ssam * to the adapter as part of the work iwi_auth_and_assoc 1104191951Ssam * does. 1105158089Smlaier */ 1106191746Sthompsa if (vap->iv_state == IEEE80211_S_RUN) 1107191951Ssam ieee80211_runtask(ic, &sc->sc_wmetask); 1108191746Sthompsa return (0); 1109149338Sdamien} 1110149338Sdamien 1111158089Smlaierstatic int 1112158089Smlaieriwi_wme_setie(struct iwi_softc *sc) 1113158089Smlaier{ 1114158089Smlaier struct ieee80211_wme_info wme; 1115158089Smlaier 1116158089Smlaier memset(&wme, 0, sizeof wme); 1117158089Smlaier wme.wme_id = IEEE80211_ELEMID_VENDOR; 1118158089Smlaier wme.wme_len = sizeof (struct ieee80211_wme_info) - 2; 1119158089Smlaier wme.wme_oui[0] = 0x00; 1120158089Smlaier wme.wme_oui[1] = 0x50; 1121158089Smlaier wme.wme_oui[2] = 0xf2; 1122158089Smlaier wme.wme_type = WME_OUI_TYPE; 1123158089Smlaier wme.wme_subtype = WME_INFO_OUI_SUBTYPE; 1124158089Smlaier wme.wme_version = WME_VERSION; 1125158089Smlaier wme.wme_info = 0; 1126158089Smlaier 1127158089Smlaier DPRINTF(("Setting WME IE (len=%u)\n", wme.wme_len)); 1128158089Smlaier return iwi_cmd(sc, IWI_CMD_SET_WMEIE, &wme, sizeof wme); 1129158089Smlaier} 1130158089Smlaier 1131145247Sdamien/* 1132145247Sdamien * Read 16 bits at address 'addr' from the serial EEPROM. 1133145247Sdamien */ 1134145247Sdamienstatic uint16_t 1135145247Sdamieniwi_read_prom_word(struct iwi_softc *sc, uint8_t addr) 1136145247Sdamien{ 1137145247Sdamien uint32_t tmp; 1138145247Sdamien uint16_t val; 1139145247Sdamien int n; 1140145247Sdamien 1141145247Sdamien /* clock C once before the first command */ 1142145247Sdamien IWI_EEPROM_CTL(sc, 0); 1143145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_S); 1144145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_C); 1145145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_S); 1146145247Sdamien 1147145247Sdamien /* write start bit (1) */ 1148145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_D); 1149145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_D | IWI_EEPROM_C); 1150145247Sdamien 1151145247Sdamien /* write READ opcode (10) */ 1152145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_D); 1153145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_D | IWI_EEPROM_C); 1154145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_S); 1155145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_C); 1156145247Sdamien 1157145247Sdamien /* write address A7-A0 */ 1158145247Sdamien for (n = 7; n >= 0; n--) { 1159145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_S | 1160145247Sdamien (((addr >> n) & 1) << IWI_EEPROM_SHIFT_D)); 1161145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_S | 1162145247Sdamien (((addr >> n) & 1) << IWI_EEPROM_SHIFT_D) | IWI_EEPROM_C); 1163145247Sdamien } 1164145247Sdamien 1165145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_S); 1166145247Sdamien 1167145247Sdamien /* read data Q15-Q0 */ 1168145247Sdamien val = 0; 1169145247Sdamien for (n = 15; n >= 0; n--) { 1170145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_S | IWI_EEPROM_C); 1171145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_S); 1172145247Sdamien tmp = MEM_READ_4(sc, IWI_MEM_EEPROM_CTL); 1173145247Sdamien val |= ((tmp & IWI_EEPROM_Q) >> IWI_EEPROM_SHIFT_Q) << n; 1174145247Sdamien } 1175145247Sdamien 1176145247Sdamien IWI_EEPROM_CTL(sc, 0); 1177145247Sdamien 1178145247Sdamien /* clear Chip Select and clock C */ 1179145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_S); 1180145247Sdamien IWI_EEPROM_CTL(sc, 0); 1181145247Sdamien IWI_EEPROM_CTL(sc, IWI_EEPROM_C); 1182145247Sdamien 1183152389Sdamien return val; 1184145247Sdamien} 1185145247Sdamien 1186145247Sdamienstatic void 1187158089Smlaieriwi_setcurchan(struct iwi_softc *sc, int chan) 1188145247Sdamien{ 1189178354Ssam struct ifnet *ifp = sc->sc_ifp; 1190178354Ssam struct ieee80211com *ic = ifp->if_l2com; 1191145247Sdamien 1192158089Smlaier sc->curchan = chan; 1193192468Ssam ieee80211_radiotap_chan_change(ic); 1194145247Sdamien} 1195145247Sdamien 1196145247Sdamienstatic void 1197145247Sdamieniwi_frame_intr(struct iwi_softc *sc, struct iwi_rx_data *data, int i, 1198145247Sdamien struct iwi_frame *frame) 1199145247Sdamien{ 1200178354Ssam struct ifnet *ifp = sc->sc_ifp; 1201178354Ssam struct ieee80211com *ic = ifp->if_l2com; 1202152384Sdamien struct mbuf *mnew, *m; 1203145247Sdamien struct ieee80211_node *ni; 1204158089Smlaier int type, error, framelen; 1205192468Ssam int8_t rssi, nf; 1206164964Smlaier IWI_LOCK_DECL; 1207145247Sdamien 1208158089Smlaier framelen = le16toh(frame->len); 1209158089Smlaier if (framelen < IEEE80211_MIN_LEN || framelen > MCLBYTES) { 1210158089Smlaier /* 1211158089Smlaier * XXX >MCLBYTES is bogus as it means the h/w dma'd 1212158089Smlaier * out of bounds; need to figure out how to limit 1213158089Smlaier * frame size in the firmware 1214158089Smlaier */ 1215158089Smlaier /* XXX stat */ 1216158089Smlaier DPRINTFN(1, 1217158089Smlaier ("drop rx frame len=%u chan=%u rssi=%u rssi_dbm=%u\n", 1218158089Smlaier le16toh(frame->len), frame->chan, frame->rssi, 1219158089Smlaier frame->rssi_dbm)); 1220146500Sdamien return; 1221158089Smlaier } 1222146500Sdamien 1223158089Smlaier DPRINTFN(5, ("received frame len=%u chan=%u rssi=%u rssi_dbm=%u\n", 1224158089Smlaier le16toh(frame->len), frame->chan, frame->rssi, frame->rssi_dbm)); 1225158089Smlaier 1226158089Smlaier if (frame->chan != sc->curchan) 1227158089Smlaier iwi_setcurchan(sc, frame->chan); 1228158089Smlaier 1229152384Sdamien /* 1230152384Sdamien * Try to allocate a new mbuf for this ring element and load it before 1231152384Sdamien * processing the current mbuf. If the ring element cannot be loaded, 1232152384Sdamien * drop the received packet and reuse the old mbuf. In the unlikely 1233152384Sdamien * case that the old mbuf can't be reloaded either, explicitly panic. 1234152384Sdamien */ 1235152384Sdamien mnew = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); 1236152384Sdamien if (mnew == NULL) { 1237152384Sdamien ifp->if_ierrors++; 1238152384Sdamien return; 1239152384Sdamien } 1240152384Sdamien 1241145247Sdamien bus_dmamap_unload(sc->rxq.data_dmat, data->map); 1242145247Sdamien 1243152384Sdamien error = bus_dmamap_load(sc->rxq.data_dmat, data->map, 1244152384Sdamien mtod(mnew, void *), MCLBYTES, iwi_dma_map_addr, &data->physaddr, 1245152384Sdamien 0); 1246152384Sdamien if (error != 0) { 1247152384Sdamien m_freem(mnew); 1248152384Sdamien 1249152384Sdamien /* try to reload the old mbuf */ 1250152384Sdamien error = bus_dmamap_load(sc->rxq.data_dmat, data->map, 1251152384Sdamien mtod(data->m, void *), MCLBYTES, iwi_dma_map_addr, 1252152384Sdamien &data->physaddr, 0); 1253152384Sdamien if (error != 0) { 1254152384Sdamien /* very unlikely that it will fail... */ 1255152384Sdamien panic("%s: could not load old rx mbuf", 1256152384Sdamien device_get_name(sc->sc_dev)); 1257152384Sdamien } 1258152384Sdamien ifp->if_ierrors++; 1259152384Sdamien return; 1260152384Sdamien } 1261152384Sdamien 1262152384Sdamien /* 1263152384Sdamien * New mbuf successfully loaded, update Rx ring and continue 1264152384Sdamien * processing. 1265152384Sdamien */ 1266152384Sdamien m = data->m; 1267152384Sdamien data->m = mnew; 1268152384Sdamien CSR_WRITE_4(sc, data->reg, data->physaddr); 1269152384Sdamien 1270145247Sdamien /* finalize mbuf */ 1271145247Sdamien m->m_pkthdr.rcvif = ifp; 1272145247Sdamien m->m_pkthdr.len = m->m_len = sizeof (struct iwi_hdr) + 1273158089Smlaier sizeof (struct iwi_frame) + framelen; 1274145247Sdamien 1275145247Sdamien m_adj(m, sizeof (struct iwi_hdr) + sizeof (struct iwi_frame)); 1276145247Sdamien 1277192541Ssam rssi = frame->rssi_dbm; 1278192468Ssam nf = -95; 1279192468Ssam if (ieee80211_radiotap_active(ic)) { 1280145247Sdamien struct iwi_rx_radiotap_header *tap = &sc->sc_rxtap; 1281145247Sdamien 1282145247Sdamien tap->wr_flags = 0; 1283192468Ssam tap->wr_antsignal = rssi; 1284192468Ssam tap->wr_antnoise = nf; 1285158089Smlaier tap->wr_rate = iwi_cvtrate(frame->rate); 1286145247Sdamien tap->wr_antenna = frame->antenna; 1287145247Sdamien } 1288164964Smlaier IWI_UNLOCK(sc); 1289145247Sdamien 1290158089Smlaier ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *)); 1291178354Ssam if (ni != NULL) { 1292192468Ssam type = ieee80211_input(ni, m, rssi, nf); 1293178354Ssam ieee80211_free_node(ni); 1294178354Ssam } else 1295192468Ssam type = ieee80211_input_all(ic, m, rssi, nf); 1296145247Sdamien 1297164964Smlaier IWI_LOCK(sc); 1298158089Smlaier if (sc->sc_softled) { 1299158089Smlaier /* 1300158089Smlaier * Blink for any data frame. Otherwise do a 1301158089Smlaier * heartbeat-style blink when idle. The latter 1302158089Smlaier * is mainly for station mode where we depend on 1303158089Smlaier * periodic beacon frames to trigger the poll event. 1304158089Smlaier */ 1305158089Smlaier if (type == IEEE80211_FC0_TYPE_DATA) { 1306158089Smlaier sc->sc_rxrate = frame->rate; 1307158089Smlaier iwi_led_event(sc, IWI_LED_RX); 1308158089Smlaier } else if (ticks - sc->sc_ledevent >= sc->sc_ledidle) 1309158089Smlaier iwi_led_event(sc, IWI_LED_POLL); 1310158089Smlaier } 1311145247Sdamien} 1312145247Sdamien 1313158089Smlaier/* 1314158089Smlaier * Check for an association response frame to see if QoS 1315158089Smlaier * has been negotiated. We parse just enough to figure 1316158089Smlaier * out if we're supposed to use QoS. The proper solution 1317158089Smlaier * is to pass the frame up so ieee80211_input can do the 1318158089Smlaier * work but that's made hard by how things currently are 1319158089Smlaier * done in the driver. 1320158089Smlaier */ 1321145247Sdamienstatic void 1322178354Ssamiwi_checkforqos(struct ieee80211vap *vap, 1323178354Ssam const struct ieee80211_frame *wh, int len) 1324158089Smlaier{ 1325158089Smlaier#define SUBTYPE(wh) ((wh)->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) 1326158089Smlaier const uint8_t *frm, *efrm, *wme; 1327158089Smlaier struct ieee80211_node *ni; 1328170530Ssam uint16_t capinfo, status, associd; 1329158089Smlaier 1330158089Smlaier /* NB: +8 for capinfo, status, associd, and first ie */ 1331158089Smlaier if (!(sizeof(*wh)+8 < len && len < IEEE80211_MAX_LEN) || 1332158089Smlaier SUBTYPE(wh) != IEEE80211_FC0_SUBTYPE_ASSOC_RESP) 1333158089Smlaier return; 1334158089Smlaier /* 1335158089Smlaier * asresp frame format 1336158089Smlaier * [2] capability information 1337158089Smlaier * [2] status 1338158089Smlaier * [2] association ID 1339158089Smlaier * [tlv] supported rates 1340158089Smlaier * [tlv] extended supported rates 1341158089Smlaier * [tlv] WME 1342158089Smlaier */ 1343158089Smlaier frm = (const uint8_t *)&wh[1]; 1344158089Smlaier efrm = ((const uint8_t *) wh) + len; 1345158089Smlaier 1346170530Ssam capinfo = le16toh(*(const uint16_t *)frm); 1347170530Ssam frm += 2; 1348170530Ssam status = le16toh(*(const uint16_t *)frm); 1349170530Ssam frm += 2; 1350170530Ssam associd = le16toh(*(const uint16_t *)frm); 1351170530Ssam frm += 2; 1352170530Ssam 1353158089Smlaier wme = NULL; 1354158089Smlaier while (frm < efrm) { 1355178354Ssam IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1], return); 1356158089Smlaier switch (*frm) { 1357158089Smlaier case IEEE80211_ELEMID_VENDOR: 1358158089Smlaier if (iswmeoui(frm)) 1359158089Smlaier wme = frm; 1360158089Smlaier break; 1361158089Smlaier } 1362158089Smlaier frm += frm[1] + 2; 1363158089Smlaier } 1364158089Smlaier 1365178354Ssam ni = vap->iv_bss; 1366170530Ssam ni->ni_capinfo = capinfo; 1367170530Ssam ni->ni_associd = associd; 1368158089Smlaier if (wme != NULL) 1369158089Smlaier ni->ni_flags |= IEEE80211_NODE_QOS; 1370158089Smlaier else 1371158089Smlaier ni->ni_flags &= ~IEEE80211_NODE_QOS; 1372158089Smlaier#undef SUBTYPE 1373158089Smlaier} 1374158089Smlaier 1375178354Ssam/* 1376178354Ssam * Task queue callbacks for iwi_notification_intr used to avoid LOR's. 1377178354Ssam */ 1378178354Ssam 1379158089Smlaierstatic void 1380145247Sdamieniwi_notification_intr(struct iwi_softc *sc, struct iwi_notif *notif) 1381145247Sdamien{ 1382178354Ssam struct ifnet *ifp = sc->sc_ifp; 1383178354Ssam struct ieee80211com *ic = ifp->if_l2com; 1384178354Ssam struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 1385145247Sdamien struct iwi_notif_scan_channel *chan; 1386145247Sdamien struct iwi_notif_scan_complete *scan; 1387145247Sdamien struct iwi_notif_authentication *auth; 1388145247Sdamien struct iwi_notif_association *assoc; 1389158089Smlaier struct iwi_notif_beacon_state *beacon; 1390145247Sdamien 1391145247Sdamien switch (notif->type) { 1392145247Sdamien case IWI_NOTIF_TYPE_SCAN_CHANNEL: 1393145247Sdamien chan = (struct iwi_notif_scan_channel *)(notif + 1); 1394145247Sdamien 1395158089Smlaier DPRINTFN(3, ("Scan of channel %u complete (%u)\n", 1396170530Ssam ieee80211_ieee2mhz(chan->nchan, 0), chan->nchan)); 1397170530Ssam 1398170530Ssam /* Reset the timer, the scan is still going */ 1399170530Ssam sc->sc_state_timer = 3; 1400145247Sdamien break; 1401145247Sdamien 1402145247Sdamien case IWI_NOTIF_TYPE_SCAN_COMPLETE: 1403145247Sdamien scan = (struct iwi_notif_scan_complete *)(notif + 1); 1404145247Sdamien 1405145247Sdamien DPRINTFN(2, ("Scan completed (%u, %u)\n", scan->nchan, 1406145247Sdamien scan->status)); 1407145247Sdamien 1408170530Ssam IWI_STATE_END(sc, IWI_FW_SCANNING); 1409158089Smlaier 1410178354Ssam if (scan->status == IWI_SCAN_COMPLETED) { 1411178354Ssam /* NB: don't need to defer, net80211 does it for us */ 1412178354Ssam ieee80211_scan_next(vap); 1413178354Ssam } 1414145247Sdamien break; 1415145247Sdamien 1416145247Sdamien case IWI_NOTIF_TYPE_AUTHENTICATION: 1417145247Sdamien auth = (struct iwi_notif_authentication *)(notif + 1); 1418145247Sdamien switch (auth->state) { 1419158089Smlaier case IWI_AUTH_SUCCESS: 1420158089Smlaier DPRINTFN(2, ("Authentication succeeeded\n")); 1421191746Sthompsa ieee80211_new_state(vap, IEEE80211_S_ASSOC, -1); 1422145247Sdamien break; 1423158089Smlaier case IWI_AUTH_FAIL: 1424178354Ssam /* 1425178354Ssam * These are delivered as an unsolicited deauth 1426178354Ssam * (e.g. due to inactivity) or in response to an 1427178354Ssam * associate request. 1428178354Ssam */ 1429158089Smlaier sc->flags &= ~IWI_FLAG_ASSOCIATED; 1430178354Ssam if (vap->iv_state != IEEE80211_S_RUN) { 1431178354Ssam DPRINTFN(2, ("Authentication failed\n")); 1432178354Ssam vap->iv_stats.is_rx_auth_fail++; 1433178354Ssam IWI_STATE_END(sc, IWI_FW_ASSOCIATING); 1434178354Ssam } else { 1435178354Ssam DPRINTFN(2, ("Deauthenticated\n")); 1436178354Ssam vap->iv_stats.is_rx_deauth++; 1437178354Ssam } 1438191746Sthompsa ieee80211_new_state(vap, IEEE80211_S_SCAN, -1); 1439145247Sdamien break; 1440158089Smlaier case IWI_AUTH_SENT_1: 1441158089Smlaier case IWI_AUTH_RECV_2: 1442158089Smlaier case IWI_AUTH_SEQ1_PASS: 1443158089Smlaier break; 1444158089Smlaier case IWI_AUTH_SEQ1_FAIL: 1445158089Smlaier DPRINTFN(2, ("Initial authentication handshake failed; " 1446158089Smlaier "you probably need shared key\n")); 1447178354Ssam vap->iv_stats.is_rx_auth_fail++; 1448170530Ssam IWI_STATE_END(sc, IWI_FW_ASSOCIATING); 1449158089Smlaier /* XXX retry shared key when in auto */ 1450158089Smlaier break; 1451145247Sdamien default: 1452145247Sdamien device_printf(sc->sc_dev, 1453145247Sdamien "unknown authentication state %u\n", auth->state); 1454178354Ssam break; 1455145247Sdamien } 1456145247Sdamien break; 1457145247Sdamien 1458145247Sdamien case IWI_NOTIF_TYPE_ASSOCIATION: 1459145247Sdamien assoc = (struct iwi_notif_association *)(notif + 1); 1460145247Sdamien switch (assoc->state) { 1461158089Smlaier case IWI_AUTH_SUCCESS: 1462145247Sdamien /* re-association, do nothing */ 1463145247Sdamien break; 1464158089Smlaier case IWI_ASSOC_SUCCESS: 1465158089Smlaier DPRINTFN(2, ("Association succeeded\n")); 1466158089Smlaier sc->flags |= IWI_FLAG_ASSOCIATED; 1467170530Ssam IWI_STATE_END(sc, IWI_FW_ASSOCIATING); 1468178354Ssam iwi_checkforqos(vap, 1469158089Smlaier (const struct ieee80211_frame *)(assoc+1), 1470158089Smlaier le16toh(notif->len) - sizeof(*assoc)); 1471191746Sthompsa ieee80211_new_state(vap, IEEE80211_S_RUN, -1); 1472145247Sdamien break; 1473170530Ssam case IWI_ASSOC_INIT: 1474178354Ssam sc->flags &= ~IWI_FLAG_ASSOCIATED; 1475170530Ssam switch (sc->fw_state) { 1476178354Ssam case IWI_FW_ASSOCIATING: 1477178354Ssam DPRINTFN(2, ("Association failed\n")); 1478178354Ssam IWI_STATE_END(sc, IWI_FW_ASSOCIATING); 1479191746Sthompsa ieee80211_new_state(vap, IEEE80211_S_SCAN, -1); 1480178354Ssam break; 1481170530Ssam 1482178354Ssam case IWI_FW_DISASSOCIATING: 1483178354Ssam DPRINTFN(2, ("Dissassociated\n")); 1484178354Ssam IWI_STATE_END(sc, IWI_FW_DISASSOCIATING); 1485178354Ssam vap->iv_stats.is_rx_disassoc++; 1486191746Sthompsa ieee80211_new_state(vap, IEEE80211_S_SCAN, -1); 1487178354Ssam break; 1488170530Ssam } 1489145247Sdamien break; 1490145247Sdamien default: 1491145247Sdamien device_printf(sc->sc_dev, 1492145247Sdamien "unknown association state %u\n", assoc->state); 1493178354Ssam break; 1494145247Sdamien } 1495145247Sdamien break; 1496145247Sdamien 1497158089Smlaier case IWI_NOTIF_TYPE_BEACON: 1498158089Smlaier /* XXX check struct length */ 1499158089Smlaier beacon = (struct iwi_notif_beacon_state *)(notif + 1); 1500158089Smlaier 1501158089Smlaier DPRINTFN(5, ("Beacon state (%u, %u)\n", 1502158089Smlaier beacon->state, le32toh(beacon->number))); 1503158089Smlaier 1504158089Smlaier if (beacon->state == IWI_BEACON_MISS) { 1505158089Smlaier /* 1506158089Smlaier * The firmware notifies us of every beacon miss 1507158089Smlaier * so we need to track the count against the 1508158089Smlaier * configured threshold before notifying the 1509158089Smlaier * 802.11 layer. 1510158089Smlaier * XXX try to roam, drop assoc only on much higher count 1511158089Smlaier */ 1512178354Ssam if (le32toh(beacon->number) >= vap->iv_bmissthreshold) { 1513158089Smlaier DPRINTF(("Beacon miss: %u >= %u\n", 1514158089Smlaier le32toh(beacon->number), 1515178354Ssam vap->iv_bmissthreshold)); 1516178354Ssam vap->iv_stats.is_beacon_miss++; 1517178354Ssam /* 1518178354Ssam * It's pointless to notify the 802.11 layer 1519178354Ssam * as it'll try to send a probe request (which 1520178354Ssam * we'll discard) and then timeout and drop us 1521178354Ssam * into scan state. Instead tell the firmware 1522178354Ssam * to disassociate and then on completion we'll 1523178354Ssam * kick the state machine to scan. 1524178354Ssam */ 1525191746Sthompsa ieee80211_runtask(ic, &sc->sc_disassoctask); 1526158089Smlaier } 1527158089Smlaier } 1528158089Smlaier break; 1529158089Smlaier 1530158089Smlaier case IWI_NOTIF_TYPE_CALIBRATION: 1531158089Smlaier case IWI_NOTIF_TYPE_NOISE: 1532158089Smlaier case IWI_NOTIF_TYPE_LINK_QUALITY: 1533158089Smlaier DPRINTFN(5, ("Notification (%u)\n", notif->type)); 1534158089Smlaier break; 1535158089Smlaier 1536154992Sdamien default: 1537158089Smlaier DPRINTF(("unknown notification type %u flags 0x%x len %u\n", 1538158089Smlaier notif->type, notif->flags, le16toh(notif->len))); 1539178354Ssam break; 1540145247Sdamien } 1541145247Sdamien} 1542145247Sdamien 1543145247Sdamienstatic void 1544145247Sdamieniwi_rx_intr(struct iwi_softc *sc) 1545145247Sdamien{ 1546145247Sdamien struct iwi_rx_data *data; 1547145247Sdamien struct iwi_hdr *hdr; 1548145247Sdamien uint32_t hw; 1549145247Sdamien 1550145247Sdamien hw = CSR_READ_4(sc, IWI_CSR_RX_RIDX); 1551145247Sdamien 1552145247Sdamien for (; sc->rxq.cur != hw;) { 1553145247Sdamien data = &sc->rxq.data[sc->rxq.cur]; 1554145247Sdamien 1555145247Sdamien bus_dmamap_sync(sc->rxq.data_dmat, data->map, 1556145247Sdamien BUS_DMASYNC_POSTREAD); 1557145247Sdamien 1558145247Sdamien hdr = mtod(data->m, struct iwi_hdr *); 1559145247Sdamien 1560145247Sdamien switch (hdr->type) { 1561145247Sdamien case IWI_HDR_TYPE_FRAME: 1562145247Sdamien iwi_frame_intr(sc, data, sc->rxq.cur, 1563145247Sdamien (struct iwi_frame *)(hdr + 1)); 1564145247Sdamien break; 1565145247Sdamien 1566145247Sdamien case IWI_HDR_TYPE_NOTIF: 1567145247Sdamien iwi_notification_intr(sc, 1568145247Sdamien (struct iwi_notif *)(hdr + 1)); 1569145247Sdamien break; 1570145247Sdamien 1571145247Sdamien default: 1572145247Sdamien device_printf(sc->sc_dev, "unknown hdr type %u\n", 1573145247Sdamien hdr->type); 1574145247Sdamien } 1575145247Sdamien 1576145247Sdamien DPRINTFN(15, ("rx done idx=%u\n", sc->rxq.cur)); 1577145247Sdamien 1578145247Sdamien sc->rxq.cur = (sc->rxq.cur + 1) % IWI_RX_RING_COUNT; 1579145247Sdamien } 1580145247Sdamien 1581145247Sdamien /* tell the firmware what we have processed */ 1582145247Sdamien hw = (hw == 0) ? IWI_RX_RING_COUNT - 1 : hw - 1; 1583145247Sdamien CSR_WRITE_4(sc, IWI_CSR_RX_WIDX, hw); 1584145247Sdamien} 1585145247Sdamien 1586145247Sdamienstatic void 1587149338Sdamieniwi_tx_intr(struct iwi_softc *sc, struct iwi_tx_ring *txq) 1588145247Sdamien{ 1589178354Ssam struct ifnet *ifp = sc->sc_ifp; 1590145247Sdamien struct iwi_tx_data *data; 1591145247Sdamien uint32_t hw; 1592145247Sdamien 1593149338Sdamien hw = CSR_READ_4(sc, txq->csr_ridx); 1594145247Sdamien 1595149338Sdamien for (; txq->next != hw;) { 1596149338Sdamien data = &txq->data[txq->next]; 1597145247Sdamien 1598149338Sdamien bus_dmamap_sync(txq->data_dmat, data->map, 1599145247Sdamien BUS_DMASYNC_POSTWRITE); 1600149338Sdamien bus_dmamap_unload(txq->data_dmat, data->map); 1601170530Ssam if (data->m->m_flags & M_TXCB) 1602170530Ssam ieee80211_process_callback(data->ni, data->m, 0/*XXX*/); 1603145247Sdamien m_freem(data->m); 1604145247Sdamien data->m = NULL; 1605145247Sdamien ieee80211_free_node(data->ni); 1606145247Sdamien data->ni = NULL; 1607145247Sdamien 1608149338Sdamien DPRINTFN(15, ("tx done idx=%u\n", txq->next)); 1609145247Sdamien 1610145247Sdamien ifp->if_opackets++; 1611145247Sdamien 1612149338Sdamien txq->queued--; 1613149338Sdamien txq->next = (txq->next + 1) % IWI_TX_RING_COUNT; 1614145247Sdamien } 1615145247Sdamien 1616145247Sdamien sc->sc_tx_timer = 0; 1617148887Srwatson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 1618158089Smlaier 1619158089Smlaier if (sc->sc_softled) 1620158089Smlaier iwi_led_event(sc, IWI_LED_TX); 1621158089Smlaier 1622178354Ssam iwi_start_locked(ifp); 1623145247Sdamien} 1624145247Sdamien 1625145247Sdamienstatic void 1626191746Sthompsaiwi_fatal_error_intr(struct iwi_softc *sc) 1627191746Sthompsa{ 1628191746Sthompsa struct ifnet *ifp = sc->sc_ifp; 1629191746Sthompsa struct ieee80211com *ic = ifp->if_l2com; 1630191956Sthompsa struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 1631191746Sthompsa 1632191746Sthompsa device_printf(sc->sc_dev, "firmware error\n"); 1633191956Sthompsa if (vap != NULL) 1634191956Sthompsa ieee80211_cancel_scan(vap); 1635191746Sthompsa ieee80211_runtask(ic, &sc->sc_restarttask); 1636191746Sthompsa 1637191746Sthompsa sc->flags &= ~IWI_FLAG_BUSY; 1638191746Sthompsa sc->sc_busy_timer = 0; 1639191746Sthompsa wakeup(sc); 1640191746Sthompsa} 1641191746Sthompsa 1642191746Sthompsastatic void 1643191746Sthompsaiwi_radio_off_intr(struct iwi_softc *sc) 1644191746Sthompsa{ 1645191746Sthompsa struct ifnet *ifp = sc->sc_ifp; 1646191746Sthompsa struct ieee80211com *ic = ifp->if_l2com; 1647191746Sthompsa 1648191746Sthompsa ieee80211_runtask(ic, &sc->sc_radiofftask); 1649191746Sthompsa} 1650191746Sthompsa 1651191746Sthompsastatic void 1652145247Sdamieniwi_intr(void *arg) 1653145247Sdamien{ 1654145247Sdamien struct iwi_softc *sc = arg; 1655145247Sdamien uint32_t r; 1656158089Smlaier IWI_LOCK_DECL; 1657145247Sdamien 1658158089Smlaier IWI_LOCK(sc); 1659145247Sdamien 1660145247Sdamien if ((r = CSR_READ_4(sc, IWI_CSR_INTR)) == 0 || r == 0xffffffff) { 1661158089Smlaier IWI_UNLOCK(sc); 1662145247Sdamien return; 1663145247Sdamien } 1664145247Sdamien 1665156547Sdamien /* acknowledge interrupts */ 1666156547Sdamien CSR_WRITE_4(sc, IWI_CSR_INTR, r); 1667145247Sdamien 1668158089Smlaier if (r & IWI_INTR_FATAL_ERROR) { 1669191746Sthompsa iwi_fatal_error_intr(sc); 1670191746Sthompsa goto done; 1671145247Sdamien } 1672145247Sdamien 1673158089Smlaier if (r & IWI_INTR_FW_INITED) { 1674158089Smlaier if (!(r & (IWI_INTR_FATAL_ERROR | IWI_INTR_PARITY_ERROR))) 1675158089Smlaier wakeup(sc); 1676145247Sdamien } 1677145247Sdamien 1678158089Smlaier if (r & IWI_INTR_RADIO_OFF) 1679191746Sthompsa iwi_radio_off_intr(sc); 1680158089Smlaier 1681158089Smlaier if (r & IWI_INTR_CMD_DONE) { 1682158089Smlaier sc->flags &= ~IWI_FLAG_BUSY; 1683170530Ssam sc->sc_busy_timer = 0; 1684145247Sdamien wakeup(sc); 1685158089Smlaier } 1686145247Sdamien 1687145247Sdamien if (r & IWI_INTR_TX1_DONE) 1688149338Sdamien iwi_tx_intr(sc, &sc->txq[0]); 1689145247Sdamien 1690149338Sdamien if (r & IWI_INTR_TX2_DONE) 1691149338Sdamien iwi_tx_intr(sc, &sc->txq[1]); 1692149338Sdamien 1693149338Sdamien if (r & IWI_INTR_TX3_DONE) 1694149338Sdamien iwi_tx_intr(sc, &sc->txq[2]); 1695149338Sdamien 1696149338Sdamien if (r & IWI_INTR_TX4_DONE) 1697149338Sdamien iwi_tx_intr(sc, &sc->txq[3]); 1698149338Sdamien 1699149338Sdamien if (r & IWI_INTR_RX_DONE) 1700149338Sdamien iwi_rx_intr(sc); 1701149338Sdamien 1702158089Smlaier if (r & IWI_INTR_PARITY_ERROR) { 1703158089Smlaier /* XXX rate-limit */ 1704158089Smlaier device_printf(sc->sc_dev, "parity error\n"); 1705158089Smlaier } 1706191746Sthompsadone: 1707158089Smlaier IWI_UNLOCK(sc); 1708145247Sdamien} 1709145247Sdamien 1710145247Sdamienstatic int 1711158089Smlaieriwi_cmd(struct iwi_softc *sc, uint8_t type, void *data, uint8_t len) 1712145247Sdamien{ 1713145247Sdamien struct iwi_cmd_desc *desc; 1714145247Sdamien 1715170530Ssam IWI_LOCK_ASSERT(sc); 1716166848Sluigi 1717158089Smlaier if (sc->flags & IWI_FLAG_BUSY) { 1718158089Smlaier device_printf(sc->sc_dev, "%s: cmd %d not sent, busy\n", 1719158089Smlaier __func__, type); 1720158089Smlaier return EAGAIN; 1721158089Smlaier } 1722158089Smlaier sc->flags |= IWI_FLAG_BUSY; 1723170530Ssam sc->sc_busy_timer = 2; 1724158089Smlaier 1725145247Sdamien desc = &sc->cmdq.desc[sc->cmdq.cur]; 1726145247Sdamien 1727145247Sdamien desc->hdr.type = IWI_HDR_TYPE_COMMAND; 1728145247Sdamien desc->hdr.flags = IWI_HDR_FLAG_IRQ; 1729145247Sdamien desc->type = type; 1730145247Sdamien desc->len = len; 1731145247Sdamien memcpy(desc->data, data, len); 1732145247Sdamien 1733145247Sdamien bus_dmamap_sync(sc->cmdq.desc_dmat, sc->cmdq.desc_map, 1734145247Sdamien BUS_DMASYNC_PREWRITE); 1735145247Sdamien 1736145247Sdamien DPRINTFN(2, ("sending command idx=%u type=%u len=%u\n", sc->cmdq.cur, 1737145247Sdamien type, len)); 1738145247Sdamien 1739145247Sdamien sc->cmdq.cur = (sc->cmdq.cur + 1) % IWI_CMD_RING_COUNT; 1740145247Sdamien CSR_WRITE_4(sc, IWI_CSR_CMD_WIDX, sc->cmdq.cur); 1741145247Sdamien 1742158089Smlaier return msleep(sc, &sc->sc_mtx, 0, "iwicmd", hz); 1743145247Sdamien} 1744145247Sdamien 1745150341Sdamienstatic void 1746158089Smlaieriwi_write_ibssnode(struct iwi_softc *sc, 1747158089Smlaier const u_int8_t addr[IEEE80211_ADDR_LEN], int entry) 1748150341Sdamien{ 1749150341Sdamien struct iwi_ibssnode node; 1750150341Sdamien 1751150341Sdamien /* write node information into NIC memory */ 1752150341Sdamien memset(&node, 0, sizeof node); 1753158089Smlaier IEEE80211_ADDR_COPY(node.bssid, addr); 1754150341Sdamien 1755158089Smlaier DPRINTF(("%s mac %6D station %u\n", __func__, node.bssid, ":", entry)); 1756158089Smlaier 1757150341Sdamien CSR_WRITE_REGION_1(sc, 1758158089Smlaier IWI_CSR_NODE_BASE + entry * sizeof node, 1759150341Sdamien (uint8_t *)&node, sizeof node); 1760150341Sdamien} 1761150341Sdamien 1762145247Sdamienstatic int 1763150245Sdamieniwi_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni, 1764150245Sdamien int ac) 1765145247Sdamien{ 1766145247Sdamien struct iwi_softc *sc = ifp->if_softc; 1767178354Ssam struct ieee80211vap *vap = ni->ni_vap; 1768178354Ssam struct ieee80211com *ic = ni->ni_ic; 1769150341Sdamien struct iwi_node *in = (struct iwi_node *)ni; 1770158089Smlaier const struct ieee80211_frame *wh; 1771146500Sdamien struct ieee80211_key *k; 1772149338Sdamien const struct chanAccParams *cap; 1773150245Sdamien struct iwi_tx_ring *txq = &sc->txq[ac]; 1774145247Sdamien struct iwi_tx_data *data; 1775145247Sdamien struct iwi_tx_desc *desc; 1776145247Sdamien struct mbuf *mnew; 1777145247Sdamien bus_dma_segment_t segs[IWI_MAX_NSEG]; 1778158089Smlaier int error, nsegs, hdrlen, i; 1779158089Smlaier int ismcast, flags, xflags, staid; 1780145247Sdamien 1781170530Ssam IWI_LOCK_ASSERT(sc); 1782158089Smlaier wh = mtod(m0, const struct ieee80211_frame *); 1783158089Smlaier /* NB: only data frames use this path */ 1784158089Smlaier hdrlen = ieee80211_hdrsize(wh); 1785158089Smlaier ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); 1786158089Smlaier flags = xflags = 0; 1787149338Sdamien 1788158089Smlaier if (!ismcast) 1789158089Smlaier flags |= IWI_DATA_FLAG_NEED_ACK; 1790178354Ssam if (vap->iv_flags & IEEE80211_F_SHPREAMBLE) 1791158089Smlaier flags |= IWI_DATA_FLAG_SHPREAMBLE; 1792158089Smlaier if (IEEE80211_QOS_HAS_SEQ(wh)) { 1793158089Smlaier xflags |= IWI_DATA_XFLAG_QOS; 1794149338Sdamien cap = &ic->ic_wme.wme_chanParams; 1795158089Smlaier if (!cap->cap_wmeParams[ac].wmep_noackPolicy) 1796158089Smlaier flags &= ~IWI_DATA_FLAG_NEED_ACK; 1797158089Smlaier } 1798149338Sdamien 1799150341Sdamien /* 1800150341Sdamien * This is only used in IBSS mode where the firmware expect an index 1801150341Sdamien * in a h/w table instead of a destination address. 1802150341Sdamien */ 1803178354Ssam if (vap->iv_opmode == IEEE80211_M_IBSS) { 1804158089Smlaier if (!ismcast) { 1805158089Smlaier if (in->in_station == -1) { 1806158089Smlaier in->in_station = alloc_unr(sc->sc_unr); 1807158089Smlaier if (in->in_station == -1) { 1808158089Smlaier /* h/w table is full */ 1809158089Smlaier m_freem(m0); 1810158089Smlaier ieee80211_free_node(ni); 1811158089Smlaier ifp->if_oerrors++; 1812158089Smlaier return 0; 1813158089Smlaier } 1814158089Smlaier iwi_write_ibssnode(sc, 1815158089Smlaier ni->ni_macaddr, in->in_station); 1816158089Smlaier } 1817158089Smlaier staid = in->in_station; 1818158089Smlaier } else { 1819158089Smlaier /* 1820158089Smlaier * Multicast addresses have no associated node 1821158089Smlaier * so there will be no station entry. We reserve 1822158089Smlaier * entry 0 for one mcast address and use that. 1823158089Smlaier * If there are many being used this will be 1824158089Smlaier * expensive and we'll need to do a better job 1825158089Smlaier * but for now this handles the broadcast case. 1826158089Smlaier */ 1827158089Smlaier if (!IEEE80211_ADDR_EQ(wh->i_addr1, sc->sc_mcast)) { 1828158089Smlaier IEEE80211_ADDR_COPY(sc->sc_mcast, wh->i_addr1); 1829158089Smlaier iwi_write_ibssnode(sc, sc->sc_mcast, 0); 1830158089Smlaier } 1831158089Smlaier staid = 0; 1832150245Sdamien } 1833158089Smlaier } else 1834158089Smlaier staid = 0; 1835149338Sdamien 1836149338Sdamien if (wh->i_fc[1] & IEEE80211_FC1_WEP) { 1837178354Ssam k = ieee80211_crypto_encap(ni, m0); 1838147806Ssam if (k == NULL) { 1839147806Ssam m_freem(m0); 1840146500Sdamien return ENOBUFS; 1841147806Ssam } 1842149338Sdamien 1843149338Sdamien /* packet header may have moved, reset our local pointer */ 1844149338Sdamien wh = mtod(m0, struct ieee80211_frame *); 1845146500Sdamien } 1846146500Sdamien 1847192468Ssam if (ieee80211_radiotap_active_vap(vap)) { 1848145247Sdamien struct iwi_tx_radiotap_header *tap = &sc->sc_txtap; 1849145247Sdamien 1850145247Sdamien tap->wt_flags = 0; 1851145247Sdamien 1852192468Ssam ieee80211_radiotap_tx(vap, m0); 1853145247Sdamien } 1854145247Sdamien 1855149338Sdamien data = &txq->data[txq->cur]; 1856149338Sdamien desc = &txq->desc[txq->cur]; 1857145247Sdamien 1858149338Sdamien /* save and trim IEEE802.11 header */ 1859149338Sdamien m_copydata(m0, 0, hdrlen, (caddr_t)&desc->wh); 1860149338Sdamien m_adj(m0, hdrlen); 1861145247Sdamien 1862149338Sdamien error = bus_dmamap_load_mbuf_sg(txq->data_dmat, data->map, m0, segs, 1863145247Sdamien &nsegs, 0); 1864145247Sdamien if (error != 0 && error != EFBIG) { 1865145247Sdamien device_printf(sc->sc_dev, "could not map mbuf (error %d)\n", 1866145247Sdamien error); 1867145247Sdamien m_freem(m0); 1868145247Sdamien return error; 1869145247Sdamien } 1870145247Sdamien if (error != 0) { 1871145247Sdamien mnew = m_defrag(m0, M_DONTWAIT); 1872145247Sdamien if (mnew == NULL) { 1873145247Sdamien device_printf(sc->sc_dev, 1874145247Sdamien "could not defragment mbuf\n"); 1875145247Sdamien m_freem(m0); 1876145247Sdamien return ENOBUFS; 1877145247Sdamien } 1878145247Sdamien m0 = mnew; 1879145247Sdamien 1880149338Sdamien error = bus_dmamap_load_mbuf_sg(txq->data_dmat, data->map, 1881145247Sdamien m0, segs, &nsegs, 0); 1882145247Sdamien if (error != 0) { 1883145247Sdamien device_printf(sc->sc_dev, 1884145247Sdamien "could not map mbuf (error %d)\n", error); 1885145247Sdamien m_freem(m0); 1886145247Sdamien return error; 1887145247Sdamien } 1888145247Sdamien } 1889145247Sdamien 1890145247Sdamien data->m = m0; 1891145247Sdamien data->ni = ni; 1892145247Sdamien 1893145247Sdamien desc->hdr.type = IWI_HDR_TYPE_DATA; 1894145247Sdamien desc->hdr.flags = IWI_HDR_FLAG_IRQ; 1895158089Smlaier desc->station = staid; 1896145247Sdamien desc->cmd = IWI_DATA_CMD_TX; 1897145247Sdamien desc->len = htole16(m0->m_pkthdr.len); 1898158089Smlaier desc->flags = flags; 1899158089Smlaier desc->xflags = xflags; 1900145247Sdamien 1901146500Sdamien#if 0 1902178354Ssam if (vap->iv_flags & IEEE80211_F_PRIVACY) 1903178354Ssam desc->wep_txkey = vap->iv_def_txkey; 1904158089Smlaier else 1905146500Sdamien#endif 1906145247Sdamien desc->flags |= IWI_DATA_FLAG_NO_WEP; 1907145247Sdamien 1908145247Sdamien desc->nseg = htole32(nsegs); 1909145247Sdamien for (i = 0; i < nsegs; i++) { 1910145247Sdamien desc->seg_addr[i] = htole32(segs[i].ds_addr); 1911152389Sdamien desc->seg_len[i] = htole16(segs[i].ds_len); 1912145247Sdamien } 1913145247Sdamien 1914149338Sdamien bus_dmamap_sync(txq->data_dmat, data->map, BUS_DMASYNC_PREWRITE); 1915149338Sdamien bus_dmamap_sync(txq->desc_dmat, txq->desc_map, BUS_DMASYNC_PREWRITE); 1916145247Sdamien 1917149338Sdamien DPRINTFN(5, ("sending data frame txq=%u idx=%u len=%u nseg=%u\n", 1918152389Sdamien ac, txq->cur, le16toh(desc->len), nsegs)); 1919145247Sdamien 1920149338Sdamien txq->queued++; 1921149338Sdamien txq->cur = (txq->cur + 1) % IWI_TX_RING_COUNT; 1922149338Sdamien CSR_WRITE_4(sc, txq->csr_widx, txq->cur); 1923145247Sdamien 1924145247Sdamien return 0; 1925145247Sdamien} 1926145247Sdamien 1927178354Ssamstatic int 1928178354Ssamiwi_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, 1929178354Ssam const struct ieee80211_bpf_params *params) 1930178354Ssam{ 1931178354Ssam /* no support; just discard */ 1932178354Ssam m_freem(m); 1933178354Ssam ieee80211_free_node(ni); 1934178354Ssam return 0; 1935178354Ssam} 1936178354Ssam 1937145247Sdamienstatic void 1938178354Ssamiwi_start_locked(struct ifnet *ifp) 1939145247Sdamien{ 1940145247Sdamien struct iwi_softc *sc = ifp->if_softc; 1941178354Ssam struct mbuf *m; 1942145247Sdamien struct ieee80211_node *ni; 1943150245Sdamien int ac; 1944145247Sdamien 1945178354Ssam IWI_LOCK_ASSERT(sc); 1946145247Sdamien 1947178354Ssam if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) 1948145247Sdamien return; 1949145247Sdamien 1950145247Sdamien for (;;) { 1951178354Ssam IFQ_DRV_DEQUEUE(&ifp->if_snd, m); 1952178354Ssam if (m == NULL) 1953178354Ssam break; 1954178354Ssam ac = M_WME_GETAC(m); 1955178354Ssam if (sc->txq[ac].queued > IWI_TX_RING_COUNT - 8) { 1956178354Ssam /* there is no place left in this ring; tail drop */ 1957178354Ssam /* XXX tail drop */ 1958178354Ssam IFQ_DRV_PREPEND(&ifp->if_snd, m); 1959178354Ssam ifp->if_drv_flags |= IFF_DRV_OACTIVE; 1960178354Ssam break; 1961178354Ssam } 1962145247Sdamien 1963178354Ssam ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; 1964178354Ssam if (iwi_tx_start(ifp, m, ni, ac) != 0) { 1965145247Sdamien ieee80211_free_node(ni); 1966145247Sdamien ifp->if_oerrors++; 1967145247Sdamien break; 1968145247Sdamien } 1969145247Sdamien 1970145247Sdamien sc->sc_tx_timer = 5; 1971145247Sdamien } 1972178354Ssam} 1973145247Sdamien 1974178354Ssamstatic void 1975178354Ssamiwi_start(struct ifnet *ifp) 1976178354Ssam{ 1977178354Ssam struct iwi_softc *sc = ifp->if_softc; 1978178354Ssam IWI_LOCK_DECL; 1979178354Ssam 1980178354Ssam IWI_LOCK(sc); 1981178354Ssam iwi_start_locked(ifp); 1982158089Smlaier IWI_UNLOCK(sc); 1983145247Sdamien} 1984145247Sdamien 1985145247Sdamienstatic void 1986170530Ssamiwi_watchdog(void *arg) 1987145247Sdamien{ 1988170530Ssam struct iwi_softc *sc = arg; 1989170530Ssam struct ifnet *ifp = sc->sc_ifp; 1990191746Sthompsa struct ieee80211com *ic = ifp->if_l2com; 1991145247Sdamien 1992170530Ssam IWI_LOCK_ASSERT(sc); 1993145247Sdamien 1994145247Sdamien if (sc->sc_tx_timer > 0) { 1995145247Sdamien if (--sc->sc_tx_timer == 0) { 1996145247Sdamien if_printf(ifp, "device timeout\n"); 1997145247Sdamien ifp->if_oerrors++; 1998191746Sthompsa ieee80211_runtask(ic, &sc->sc_restarttask); 1999145247Sdamien } 2000158089Smlaier } 2001170530Ssam if (sc->sc_state_timer > 0) { 2002170530Ssam if (--sc->sc_state_timer == 0) { 2003170530Ssam if_printf(ifp, "firmware stuck in state %d, resetting\n", 2004170530Ssam sc->fw_state); 2005178354Ssam if (sc->fw_state == IWI_FW_SCANNING) { 2006178354Ssam struct ieee80211com *ic = ifp->if_l2com; 2007178354Ssam ieee80211_cancel_scan(TAILQ_FIRST(&ic->ic_vaps)); 2008178354Ssam } 2009191746Sthompsa ieee80211_runtask(ic, &sc->sc_restarttask); 2010170530Ssam sc->sc_state_timer = 3; 2011158089Smlaier } 2012158089Smlaier } 2013170530Ssam if (sc->sc_busy_timer > 0) { 2014170530Ssam if (--sc->sc_busy_timer == 0) { 2015170530Ssam if_printf(ifp, "firmware command timeout, resetting\n"); 2016191746Sthompsa ieee80211_runtask(ic, &sc->sc_restarttask); 2017170530Ssam } 2018170530Ssam } 2019178354Ssam callout_reset(&sc->sc_wdtimer, hz, iwi_watchdog, sc); 2020145247Sdamien} 2021145247Sdamien 2022145247Sdamienstatic int 2023145247Sdamieniwi_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 2024145247Sdamien{ 2025145247Sdamien struct iwi_softc *sc = ifp->if_softc; 2026178354Ssam struct ieee80211com *ic = ifp->if_l2com; 2027178354Ssam struct ifreq *ifr = (struct ifreq *) data; 2028178354Ssam int error = 0, startall = 0; 2029158089Smlaier IWI_LOCK_DECL; 2030145247Sdamien 2031145247Sdamien switch (cmd) { 2032145247Sdamien case SIOCSIFFLAGS: 2033178704Sthompsa IWI_LOCK(sc); 2034145247Sdamien if (ifp->if_flags & IFF_UP) { 2035178354Ssam if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { 2036178354Ssam iwi_init_locked(sc); 2037178354Ssam startall = 1; 2038178354Ssam } 2039145247Sdamien } else { 2040148887Srwatson if (ifp->if_drv_flags & IFF_DRV_RUNNING) 2041178354Ssam iwi_stop_locked(sc); 2042145247Sdamien } 2043178704Sthompsa IWI_UNLOCK(sc); 2044178704Sthompsa if (startall) 2045178704Sthompsa ieee80211_start_all(ic); 2046145247Sdamien break; 2047178354Ssam case SIOCGIFMEDIA: 2048178354Ssam error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); 2049178354Ssam break; 2050178704Sthompsa case SIOCGIFADDR: 2051178354Ssam error = ether_ioctl(ifp, cmd, data); 2052178354Ssam break; 2053178704Sthompsa default: 2054178704Sthompsa error = EINVAL; 2055178704Sthompsa break; 2056145247Sdamien } 2057145247Sdamien return error; 2058145247Sdamien} 2059145247Sdamien 2060145247Sdamienstatic void 2061145247Sdamieniwi_stop_master(struct iwi_softc *sc) 2062145247Sdamien{ 2063145247Sdamien uint32_t tmp; 2064145247Sdamien int ntries; 2065145247Sdamien 2066145247Sdamien /* disable interrupts */ 2067145247Sdamien CSR_WRITE_4(sc, IWI_CSR_INTR_MASK, 0); 2068145247Sdamien 2069145247Sdamien CSR_WRITE_4(sc, IWI_CSR_RST, IWI_RST_STOP_MASTER); 2070145247Sdamien for (ntries = 0; ntries < 5; ntries++) { 2071145247Sdamien if (CSR_READ_4(sc, IWI_CSR_RST) & IWI_RST_MASTER_DISABLED) 2072145247Sdamien break; 2073145247Sdamien DELAY(10); 2074145247Sdamien } 2075145247Sdamien if (ntries == 5) 2076145247Sdamien device_printf(sc->sc_dev, "timeout waiting for master\n"); 2077145247Sdamien 2078145247Sdamien tmp = CSR_READ_4(sc, IWI_CSR_RST); 2079145247Sdamien CSR_WRITE_4(sc, IWI_CSR_RST, tmp | IWI_RST_PRINCETON_RESET); 2080145247Sdamien 2081145247Sdamien sc->flags &= ~IWI_FLAG_FW_INITED; 2082145247Sdamien} 2083145247Sdamien 2084145247Sdamienstatic int 2085145247Sdamieniwi_reset(struct iwi_softc *sc) 2086145247Sdamien{ 2087145247Sdamien uint32_t tmp; 2088145247Sdamien int i, ntries; 2089145247Sdamien 2090145247Sdamien iwi_stop_master(sc); 2091145247Sdamien 2092145247Sdamien tmp = CSR_READ_4(sc, IWI_CSR_CTL); 2093145247Sdamien CSR_WRITE_4(sc, IWI_CSR_CTL, tmp | IWI_CTL_INIT); 2094145247Sdamien 2095145247Sdamien CSR_WRITE_4(sc, IWI_CSR_READ_INT, IWI_READ_INT_INIT_HOST); 2096145247Sdamien 2097145247Sdamien /* wait for clock stabilization */ 2098145247Sdamien for (ntries = 0; ntries < 1000; ntries++) { 2099145247Sdamien if (CSR_READ_4(sc, IWI_CSR_CTL) & IWI_CTL_CLOCK_READY) 2100145247Sdamien break; 2101145247Sdamien DELAY(200); 2102145247Sdamien } 2103145247Sdamien if (ntries == 1000) { 2104145247Sdamien device_printf(sc->sc_dev, 2105145247Sdamien "timeout waiting for clock stabilization\n"); 2106145247Sdamien return EIO; 2107145247Sdamien } 2108145247Sdamien 2109145247Sdamien tmp = CSR_READ_4(sc, IWI_CSR_RST); 2110145247Sdamien CSR_WRITE_4(sc, IWI_CSR_RST, tmp | IWI_RST_SOFT_RESET); 2111145247Sdamien 2112145247Sdamien DELAY(10); 2113145247Sdamien 2114145247Sdamien tmp = CSR_READ_4(sc, IWI_CSR_CTL); 2115145247Sdamien CSR_WRITE_4(sc, IWI_CSR_CTL, tmp | IWI_CTL_INIT); 2116145247Sdamien 2117145247Sdamien /* clear NIC memory */ 2118145247Sdamien CSR_WRITE_4(sc, IWI_CSR_AUTOINC_ADDR, 0); 2119145247Sdamien for (i = 0; i < 0xc000; i++) 2120145247Sdamien CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, 0); 2121145247Sdamien 2122145247Sdamien return 0; 2123145247Sdamien} 2124145247Sdamien 2125158089Smlaierstatic const struct iwi_firmware_ohdr * 2126158089Smlaieriwi_setup_ofw(struct iwi_softc *sc, struct iwi_fw *fw) 2127158089Smlaier{ 2128166756Sluigi const struct firmware *fp = fw->fp; 2129158089Smlaier const struct iwi_firmware_ohdr *hdr; 2130158089Smlaier 2131158089Smlaier if (fp->datasize < sizeof (struct iwi_firmware_ohdr)) { 2132158089Smlaier device_printf(sc->sc_dev, "image '%s' too small\n", fp->name); 2133158089Smlaier return NULL; 2134158089Smlaier } 2135158089Smlaier hdr = (const struct iwi_firmware_ohdr *)fp->data; 2136158089Smlaier if ((IWI_FW_GET_MAJOR(le32toh(hdr->version)) != IWI_FW_REQ_MAJOR) || 2137158089Smlaier (IWI_FW_GET_MINOR(le32toh(hdr->version)) != IWI_FW_REQ_MINOR)) { 2138158089Smlaier device_printf(sc->sc_dev, "version for '%s' %d.%d != %d.%d\n", 2139158089Smlaier fp->name, IWI_FW_GET_MAJOR(le32toh(hdr->version)), 2140158089Smlaier IWI_FW_GET_MINOR(le32toh(hdr->version)), IWI_FW_REQ_MAJOR, 2141158089Smlaier IWI_FW_REQ_MINOR); 2142158089Smlaier return NULL; 2143158089Smlaier } 2144158089Smlaier fw->data = ((const char *) fp->data) + sizeof(struct iwi_firmware_ohdr); 2145158089Smlaier fw->size = fp->datasize - sizeof(struct iwi_firmware_ohdr); 2146158089Smlaier fw->name = fp->name; 2147158089Smlaier return hdr; 2148158089Smlaier} 2149158089Smlaier 2150158089Smlaierstatic const struct iwi_firmware_ohdr * 2151158089Smlaieriwi_setup_oucode(struct iwi_softc *sc, struct iwi_fw *fw) 2152158089Smlaier{ 2153158089Smlaier const struct iwi_firmware_ohdr *hdr; 2154158089Smlaier 2155158089Smlaier hdr = iwi_setup_ofw(sc, fw); 2156158089Smlaier if (hdr != NULL && le32toh(hdr->mode) != IWI_FW_MODE_UCODE) { 2157158089Smlaier device_printf(sc->sc_dev, "%s is not a ucode image\n", 2158158089Smlaier fw->name); 2159158089Smlaier hdr = NULL; 2160158089Smlaier } 2161158089Smlaier return hdr; 2162158089Smlaier} 2163158089Smlaier 2164158089Smlaierstatic void 2165158089Smlaieriwi_getfw(struct iwi_fw *fw, const char *fwname, 2166158089Smlaier struct iwi_fw *uc, const char *ucname) 2167158089Smlaier{ 2168158089Smlaier if (fw->fp == NULL) 2169158089Smlaier fw->fp = firmware_get(fwname); 2170158089Smlaier /* NB: pre-3.0 ucode is packaged separately */ 2171158089Smlaier if (uc->fp == NULL && fw->fp != NULL && fw->fp->version < 300) 2172158089Smlaier uc->fp = firmware_get(ucname); 2173158089Smlaier} 2174158089Smlaier 2175158089Smlaier/* 2176158089Smlaier * Get the required firmware images if not already loaded. 2177158089Smlaier * Note that we hold firmware images so long as the device 2178158089Smlaier * is marked up in case we need to reload them on device init. 2179158089Smlaier * This is necessary because we re-init the device sometimes 2180158089Smlaier * from a context where we cannot read from the filesystem 2181158089Smlaier * (e.g. from the taskqueue thread when rfkill is re-enabled). 2182166848Sluigi * XXX return 0 on success, 1 on error. 2183158089Smlaier * 2184158089Smlaier * NB: the order of get'ing and put'ing images here is 2185158089Smlaier * intentional to support handling firmware images bundled 2186158089Smlaier * by operating mode and/or all together in one file with 2187158089Smlaier * the boot firmware as "master". 2188158089Smlaier */ 2189145247Sdamienstatic int 2190178354Ssamiwi_get_firmware(struct iwi_softc *sc, enum ieee80211_opmode opmode) 2191145247Sdamien{ 2192158089Smlaier const struct iwi_firmware_hdr *hdr; 2193166756Sluigi const struct firmware *fp; 2194158089Smlaier 2195158089Smlaier /* invalidate cached firmware on mode change */ 2196178354Ssam if (sc->fw_mode != opmode) 2197158089Smlaier iwi_put_firmware(sc); 2198158089Smlaier 2199178354Ssam switch (opmode) { 2200158089Smlaier case IEEE80211_M_STA: 2201158089Smlaier iwi_getfw(&sc->fw_fw, "iwi_bss", &sc->fw_uc, "iwi_ucode_bss"); 2202158089Smlaier break; 2203158089Smlaier case IEEE80211_M_IBSS: 2204158089Smlaier iwi_getfw(&sc->fw_fw, "iwi_ibss", &sc->fw_uc, "iwi_ucode_ibss"); 2205158089Smlaier break; 2206158089Smlaier case IEEE80211_M_MONITOR: 2207158089Smlaier iwi_getfw(&sc->fw_fw, "iwi_monitor", 2208158089Smlaier &sc->fw_uc, "iwi_ucode_monitor"); 2209158089Smlaier break; 2210158089Smlaier default: 2211195562Srpaulo device_printf(sc->sc_dev, "unknown opmode %d\n", opmode); 2212195562Srpaulo return EINVAL; 2213158089Smlaier } 2214158089Smlaier fp = sc->fw_fw.fp; 2215158089Smlaier if (fp == NULL) { 2216158089Smlaier device_printf(sc->sc_dev, "could not load firmware\n"); 2217158089Smlaier goto bad; 2218158089Smlaier } 2219158089Smlaier if (fp->version < 300) { 2220158089Smlaier /* 2221158089Smlaier * Firmware prior to 3.0 was packaged as separate 2222158089Smlaier * boot, firmware, and ucode images. Verify the 2223158089Smlaier * ucode image was read in, retrieve the boot image 2224158089Smlaier * if needed, and check version stamps for consistency. 2225158089Smlaier * The version stamps in the data are also checked 2226158089Smlaier * above; this is a bit paranoid but is a cheap 2227158089Smlaier * safeguard against mis-packaging. 2228158089Smlaier */ 2229158089Smlaier if (sc->fw_uc.fp == NULL) { 2230158089Smlaier device_printf(sc->sc_dev, "could not load ucode\n"); 2231158089Smlaier goto bad; 2232158089Smlaier } 2233158089Smlaier if (sc->fw_boot.fp == NULL) { 2234158089Smlaier sc->fw_boot.fp = firmware_get("iwi_boot"); 2235158089Smlaier if (sc->fw_boot.fp == NULL) { 2236158089Smlaier device_printf(sc->sc_dev, 2237158089Smlaier "could not load boot firmware\n"); 2238158089Smlaier goto bad; 2239158089Smlaier } 2240158089Smlaier } 2241158089Smlaier if (sc->fw_boot.fp->version != sc->fw_fw.fp->version || 2242158089Smlaier sc->fw_boot.fp->version != sc->fw_uc.fp->version) { 2243158089Smlaier device_printf(sc->sc_dev, 2244158089Smlaier "firmware version mismatch: " 2245158089Smlaier "'%s' is %d, '%s' is %d, '%s' is %d\n", 2246158089Smlaier sc->fw_boot.fp->name, sc->fw_boot.fp->version, 2247158089Smlaier sc->fw_uc.fp->name, sc->fw_uc.fp->version, 2248158089Smlaier sc->fw_fw.fp->name, sc->fw_fw.fp->version 2249158089Smlaier ); 2250158089Smlaier goto bad; 2251158089Smlaier } 2252158089Smlaier /* 2253158089Smlaier * Check and setup each image. 2254158089Smlaier */ 2255158089Smlaier if (iwi_setup_oucode(sc, &sc->fw_uc) == NULL || 2256158089Smlaier iwi_setup_ofw(sc, &sc->fw_boot) == NULL || 2257158089Smlaier iwi_setup_ofw(sc, &sc->fw_fw) == NULL) 2258158089Smlaier goto bad; 2259158089Smlaier } else { 2260158089Smlaier /* 2261158089Smlaier * Check and setup combined image. 2262158089Smlaier */ 2263166848Sluigi if (fp->datasize < sizeof(struct iwi_firmware_hdr)) { 2264158089Smlaier device_printf(sc->sc_dev, "image '%s' too small\n", 2265158089Smlaier fp->name); 2266158089Smlaier goto bad; 2267158089Smlaier } 2268158089Smlaier hdr = (const struct iwi_firmware_hdr *)fp->data; 2269166848Sluigi if (fp->datasize < sizeof(*hdr) + le32toh(hdr->bsize) + le32toh(hdr->usize) 2270166848Sluigi + le32toh(hdr->fsize)) { 2271158089Smlaier device_printf(sc->sc_dev, "image '%s' too small (2)\n", 2272158089Smlaier fp->name); 2273158089Smlaier goto bad; 2274158089Smlaier } 2275158089Smlaier sc->fw_boot.data = ((const char *) fp->data) + sizeof(*hdr); 2276166848Sluigi sc->fw_boot.size = le32toh(hdr->bsize); 2277158089Smlaier sc->fw_boot.name = fp->name; 2278158089Smlaier sc->fw_uc.data = sc->fw_boot.data + sc->fw_boot.size; 2279166848Sluigi sc->fw_uc.size = le32toh(hdr->usize); 2280158089Smlaier sc->fw_uc.name = fp->name; 2281158089Smlaier sc->fw_fw.data = sc->fw_uc.data + sc->fw_uc.size; 2282166848Sluigi sc->fw_fw.size = le32toh(hdr->fsize); 2283158089Smlaier sc->fw_fw.name = fp->name; 2284158089Smlaier } 2285166848Sluigi#if 0 2286166848Sluigi device_printf(sc->sc_dev, "boot %d ucode %d fw %d bytes\n", 2287166848Sluigi sc->fw_boot.size, sc->fw_uc.size, sc->fw_fw.size); 2288166848Sluigi#endif 2289158089Smlaier 2290178354Ssam sc->fw_mode = opmode; 2291166848Sluigi return 0; 2292158089Smlaierbad: 2293158089Smlaier iwi_put_firmware(sc); 2294166848Sluigi return 1; 2295158089Smlaier} 2296158089Smlaier 2297158089Smlaierstatic void 2298158089Smlaieriwi_put_fw(struct iwi_fw *fw) 2299158089Smlaier{ 2300158089Smlaier if (fw->fp != NULL) { 2301158089Smlaier firmware_put(fw->fp, FIRMWARE_UNLOAD); 2302158089Smlaier fw->fp = NULL; 2303158089Smlaier } 2304158089Smlaier fw->data = NULL; 2305158089Smlaier fw->size = 0; 2306158089Smlaier fw->name = NULL; 2307158089Smlaier} 2308158089Smlaier 2309158089Smlaier/* 2310158089Smlaier * Release any cached firmware images. 2311158089Smlaier */ 2312158089Smlaierstatic void 2313158089Smlaieriwi_put_firmware(struct iwi_softc *sc) 2314158089Smlaier{ 2315158089Smlaier iwi_put_fw(&sc->fw_uc); 2316158089Smlaier iwi_put_fw(&sc->fw_fw); 2317158089Smlaier iwi_put_fw(&sc->fw_boot); 2318158089Smlaier} 2319158089Smlaier 2320158089Smlaierstatic int 2321158089Smlaieriwi_load_ucode(struct iwi_softc *sc, const struct iwi_fw *fw) 2322158089Smlaier{ 2323158089Smlaier uint32_t tmp; 2324156546Sdamien const uint16_t *w; 2325158089Smlaier const char *uc = fw->data; 2326158089Smlaier size_t size = fw->size; 2327158089Smlaier int i, ntries, error; 2328145247Sdamien 2329170530Ssam IWI_LOCK_ASSERT(sc); 2330158089Smlaier error = 0; 2331145247Sdamien CSR_WRITE_4(sc, IWI_CSR_RST, CSR_READ_4(sc, IWI_CSR_RST) | 2332145247Sdamien IWI_RST_STOP_MASTER); 2333145247Sdamien for (ntries = 0; ntries < 5; ntries++) { 2334145247Sdamien if (CSR_READ_4(sc, IWI_CSR_RST) & IWI_RST_MASTER_DISABLED) 2335145247Sdamien break; 2336145247Sdamien DELAY(10); 2337145247Sdamien } 2338145247Sdamien if (ntries == 5) { 2339145247Sdamien device_printf(sc->sc_dev, "timeout waiting for master\n"); 2340158089Smlaier error = EIO; 2341158089Smlaier goto fail; 2342145247Sdamien } 2343145247Sdamien 2344145247Sdamien MEM_WRITE_4(sc, 0x3000e0, 0x80000000); 2345145247Sdamien DELAY(5000); 2346145247Sdamien 2347145247Sdamien tmp = CSR_READ_4(sc, IWI_CSR_RST); 2348145247Sdamien tmp &= ~IWI_RST_PRINCETON_RESET; 2349145247Sdamien CSR_WRITE_4(sc, IWI_CSR_RST, tmp); 2350145247Sdamien 2351145247Sdamien DELAY(5000); 2352145247Sdamien MEM_WRITE_4(sc, 0x3000e0, 0); 2353145247Sdamien DELAY(1000); 2354158089Smlaier MEM_WRITE_4(sc, IWI_MEM_EEPROM_EVENT, 1); 2355145247Sdamien DELAY(1000); 2356158089Smlaier MEM_WRITE_4(sc, IWI_MEM_EEPROM_EVENT, 0); 2357145247Sdamien DELAY(1000); 2358145247Sdamien MEM_WRITE_1(sc, 0x200000, 0x00); 2359145247Sdamien MEM_WRITE_1(sc, 0x200000, 0x40); 2360145247Sdamien DELAY(1000); 2361145247Sdamien 2362145247Sdamien /* write microcode into adapter memory */ 2363158089Smlaier for (w = (const uint16_t *)uc; size > 0; w++, size -= 2) 2364152389Sdamien MEM_WRITE_2(sc, 0x200010, htole16(*w)); 2365145247Sdamien 2366145247Sdamien MEM_WRITE_1(sc, 0x200000, 0x00); 2367145247Sdamien MEM_WRITE_1(sc, 0x200000, 0x80); 2368145247Sdamien 2369145247Sdamien /* wait until we get an answer */ 2370145247Sdamien for (ntries = 0; ntries < 100; ntries++) { 2371145247Sdamien if (MEM_READ_1(sc, 0x200000) & 1) 2372145247Sdamien break; 2373145247Sdamien DELAY(100); 2374145247Sdamien } 2375145247Sdamien if (ntries == 100) { 2376145247Sdamien device_printf(sc->sc_dev, 2377145247Sdamien "timeout waiting for ucode to initialize\n"); 2378158089Smlaier error = EIO; 2379158089Smlaier goto fail; 2380145247Sdamien } 2381145247Sdamien 2382145247Sdamien /* read the answer or the firmware will not initialize properly */ 2383145247Sdamien for (i = 0; i < 7; i++) 2384145247Sdamien MEM_READ_4(sc, 0x200004); 2385145247Sdamien 2386145247Sdamien MEM_WRITE_1(sc, 0x200000, 0x00); 2387145247Sdamien 2388158089Smlaierfail: 2389158089Smlaier return error; 2390145247Sdamien} 2391145247Sdamien 2392145247Sdamien/* macro to handle unaligned little endian data in firmware image */ 2393145247Sdamien#define GETLE32(p) ((p)[0] | (p)[1] << 8 | (p)[2] << 16 | (p)[3] << 24) 2394145247Sdamien 2395145247Sdamienstatic int 2396158089Smlaieriwi_load_firmware(struct iwi_softc *sc, const struct iwi_fw *fw) 2397145247Sdamien{ 2398145247Sdamien u_char *p, *end; 2399145247Sdamien uint32_t sentinel, ctl, src, dst, sum, len, mlen, tmp; 2400152629Sscottl int ntries, error; 2401145247Sdamien 2402170530Ssam IWI_LOCK_ASSERT(sc); 2403178354Ssam 2404145247Sdamien /* copy firmware image to DMA memory */ 2405158089Smlaier memcpy(sc->fw_virtaddr, fw->data, fw->size); 2406145247Sdamien 2407145247Sdamien /* make sure the adapter will get up-to-date values */ 2408158089Smlaier bus_dmamap_sync(sc->fw_dmat, sc->fw_map, BUS_DMASYNC_PREWRITE); 2409145247Sdamien 2410145247Sdamien /* tell the adapter where the command blocks are stored */ 2411145247Sdamien MEM_WRITE_4(sc, 0x3000a0, 0x27000); 2412145247Sdamien 2413145247Sdamien /* 2414145247Sdamien * Store command blocks into adapter's internal memory using register 2415145247Sdamien * indirections. The adapter will read the firmware image through DMA 2416145247Sdamien * using information stored in command blocks. 2417145247Sdamien */ 2418158089Smlaier src = sc->fw_physaddr; 2419158089Smlaier p = sc->fw_virtaddr; 2420158089Smlaier end = p + fw->size; 2421145247Sdamien CSR_WRITE_4(sc, IWI_CSR_AUTOINC_ADDR, 0x27000); 2422145247Sdamien 2423145247Sdamien while (p < end) { 2424145247Sdamien dst = GETLE32(p); p += 4; src += 4; 2425145247Sdamien len = GETLE32(p); p += 4; src += 4; 2426145247Sdamien p += len; 2427145247Sdamien 2428145247Sdamien while (len > 0) { 2429145247Sdamien mlen = min(len, IWI_CB_MAXDATALEN); 2430145247Sdamien 2431145247Sdamien ctl = IWI_CB_DEFAULT_CTL | mlen; 2432145247Sdamien sum = ctl ^ src ^ dst; 2433145247Sdamien 2434145247Sdamien /* write a command block */ 2435145247Sdamien CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, ctl); 2436145247Sdamien CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, src); 2437145247Sdamien CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, dst); 2438145247Sdamien CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, sum); 2439145247Sdamien 2440145247Sdamien src += mlen; 2441145247Sdamien dst += mlen; 2442145247Sdamien len -= mlen; 2443145247Sdamien } 2444145247Sdamien } 2445145247Sdamien 2446145247Sdamien /* write a fictive final command block (sentinel) */ 2447145247Sdamien sentinel = CSR_READ_4(sc, IWI_CSR_AUTOINC_ADDR); 2448145247Sdamien CSR_WRITE_4(sc, IWI_CSR_AUTOINC_DATA, 0); 2449145247Sdamien 2450145247Sdamien tmp = CSR_READ_4(sc, IWI_CSR_RST); 2451145247Sdamien tmp &= ~(IWI_RST_MASTER_DISABLED | IWI_RST_STOP_MASTER); 2452145247Sdamien CSR_WRITE_4(sc, IWI_CSR_RST, tmp); 2453145247Sdamien 2454145247Sdamien /* tell the adapter to start processing command blocks */ 2455145247Sdamien MEM_WRITE_4(sc, 0x3000a4, 0x540100); 2456145247Sdamien 2457152611Sdamien /* wait until the adapter reaches the sentinel */ 2458145247Sdamien for (ntries = 0; ntries < 400; ntries++) { 2459145247Sdamien if (MEM_READ_4(sc, 0x3000d0) >= sentinel) 2460145247Sdamien break; 2461145247Sdamien DELAY(100); 2462145247Sdamien } 2463166848Sluigi /* sync dma, just in case */ 2464166848Sluigi bus_dmamap_sync(sc->fw_dmat, sc->fw_map, BUS_DMASYNC_POSTWRITE); 2465145247Sdamien if (ntries == 400) { 2466145247Sdamien device_printf(sc->sc_dev, 2467158089Smlaier "timeout processing command blocks for %s firmware\n", 2468158089Smlaier fw->name); 2469166848Sluigi return EIO; 2470145247Sdamien } 2471145247Sdamien 2472145247Sdamien /* we're done with command blocks processing */ 2473145247Sdamien MEM_WRITE_4(sc, 0x3000a4, 0x540c00); 2474145247Sdamien 2475152611Sdamien /* allow interrupts so we know when the firmware is ready */ 2476145247Sdamien CSR_WRITE_4(sc, IWI_CSR_INTR_MASK, IWI_INTR_MASK); 2477145247Sdamien 2478145247Sdamien /* tell the adapter to initialize the firmware */ 2479145247Sdamien CSR_WRITE_4(sc, IWI_CSR_RST, 0); 2480145247Sdamien 2481145247Sdamien tmp = CSR_READ_4(sc, IWI_CSR_CTL); 2482145247Sdamien CSR_WRITE_4(sc, IWI_CSR_CTL, tmp | IWI_CTL_ALLOW_STANDBY); 2483145247Sdamien 2484145247Sdamien /* wait at most one second for firmware initialization to complete */ 2485145247Sdamien if ((error = msleep(sc, &sc->sc_mtx, 0, "iwiinit", hz)) != 0) { 2486158089Smlaier device_printf(sc->sc_dev, "timeout waiting for %s firmware " 2487158089Smlaier "initialization to complete\n", fw->name); 2488145247Sdamien } 2489145247Sdamien 2490145247Sdamien return error; 2491145247Sdamien} 2492145247Sdamien 2493145247Sdamienstatic int 2494178354Ssamiwi_setpowermode(struct iwi_softc *sc, struct ieee80211vap *vap) 2495158089Smlaier{ 2496158089Smlaier uint32_t data; 2497158089Smlaier 2498178354Ssam if (vap->iv_flags & IEEE80211_F_PMGTON) { 2499158089Smlaier /* XXX set more fine-grained operation */ 2500158089Smlaier data = htole32(IWI_POWER_MODE_MAX); 2501158089Smlaier } else 2502158089Smlaier data = htole32(IWI_POWER_MODE_CAM); 2503158089Smlaier 2504158089Smlaier DPRINTF(("Setting power mode to %u\n", le32toh(data))); 2505158089Smlaier return iwi_cmd(sc, IWI_CMD_SET_POWER_MODE, &data, sizeof data); 2506158089Smlaier} 2507158089Smlaier 2508158089Smlaierstatic int 2509178354Ssamiwi_setwepkeys(struct iwi_softc *sc, struct ieee80211vap *vap) 2510158089Smlaier{ 2511158089Smlaier struct iwi_wep_key wepkey; 2512158089Smlaier struct ieee80211_key *wk; 2513158089Smlaier int error, i; 2514158089Smlaier 2515158089Smlaier for (i = 0; i < IEEE80211_WEP_NKID; i++) { 2516178354Ssam wk = &vap->iv_nw_keys[i]; 2517158089Smlaier 2518158089Smlaier wepkey.cmd = IWI_WEP_KEY_CMD_SETKEY; 2519158089Smlaier wepkey.idx = i; 2520158089Smlaier wepkey.len = wk->wk_keylen; 2521158089Smlaier memset(wepkey.key, 0, sizeof wepkey.key); 2522158089Smlaier memcpy(wepkey.key, wk->wk_key, wk->wk_keylen); 2523158089Smlaier DPRINTF(("Setting wep key index %u len %u\n", wepkey.idx, 2524158089Smlaier wepkey.len)); 2525158089Smlaier error = iwi_cmd(sc, IWI_CMD_SET_WEP_KEY, &wepkey, 2526158089Smlaier sizeof wepkey); 2527158089Smlaier if (error != 0) 2528158089Smlaier return error; 2529158089Smlaier } 2530158089Smlaier return 0; 2531158089Smlaier} 2532158089Smlaier 2533158089Smlaierstatic int 2534145247Sdamieniwi_config(struct iwi_softc *sc) 2535145247Sdamien{ 2536178354Ssam struct ifnet *ifp = sc->sc_ifp; 2537178354Ssam struct ieee80211com *ic = ifp->if_l2com; 2538145247Sdamien struct iwi_configuration config; 2539145247Sdamien struct iwi_rateset rs; 2540145247Sdamien struct iwi_txpower power; 2541145247Sdamien uint32_t data; 2542145247Sdamien int error, i; 2543178354Ssam 2544170530Ssam IWI_LOCK_ASSERT(sc); 2545145247Sdamien 2546190526Ssam DPRINTF(("Setting MAC address to %6D\n", IF_LLADDR(ifp), ":")); 2547190526Ssam error = iwi_cmd(sc, IWI_CMD_SET_MAC_ADDRESS, IF_LLADDR(ifp), 2548158089Smlaier IEEE80211_ADDR_LEN); 2549145247Sdamien if (error != 0) 2550145247Sdamien return error; 2551145247Sdamien 2552145247Sdamien memset(&config, 0, sizeof config); 2553145247Sdamien config.bluetooth_coexistence = sc->bluetooth; 2554158089Smlaier config.silence_threshold = 0x1e; 2555146500Sdamien config.antenna = sc->antenna; 2556145247Sdamien config.multicast_enabled = 1; 2557146500Sdamien config.answer_pbreq = (ic->ic_opmode == IEEE80211_M_IBSS) ? 1 : 0; 2558146500Sdamien config.disable_unicast_decryption = 1; 2559146500Sdamien config.disable_multicast_decryption = 1; 2560145247Sdamien DPRINTF(("Configuring adapter\n")); 2561158089Smlaier error = iwi_cmd(sc, IWI_CMD_SET_CONFIG, &config, sizeof config); 2562145247Sdamien if (error != 0) 2563145247Sdamien return error; 2564145247Sdamien if (ic->ic_opmode == IEEE80211_M_IBSS) { 2565145247Sdamien power.mode = IWI_MODE_11B; 2566145247Sdamien power.nchan = 11; 2567145247Sdamien for (i = 0; i < 11; i++) { 2568145247Sdamien power.chan[i].chan = i + 1; 2569145247Sdamien power.chan[i].power = IWI_TXPOWER_MAX; 2570145247Sdamien } 2571145247Sdamien DPRINTF(("Setting .11b channels tx power\n")); 2572158089Smlaier error = iwi_cmd(sc, IWI_CMD_SET_TX_POWER, &power, sizeof power); 2573145247Sdamien if (error != 0) 2574145247Sdamien return error; 2575145247Sdamien 2576145247Sdamien power.mode = IWI_MODE_11G; 2577145247Sdamien DPRINTF(("Setting .11g channels tx power\n")); 2578158089Smlaier error = iwi_cmd(sc, IWI_CMD_SET_TX_POWER, &power, sizeof power); 2579145247Sdamien if (error != 0) 2580145247Sdamien return error; 2581145247Sdamien } 2582145247Sdamien 2583171034Sthompsa memset(&rs, 0, sizeof rs); 2584145247Sdamien rs.mode = IWI_MODE_11G; 2585145247Sdamien rs.type = IWI_RATESET_TYPE_SUPPORTED; 2586145247Sdamien rs.nrates = ic->ic_sup_rates[IEEE80211_MODE_11G].rs_nrates; 2587145247Sdamien memcpy(rs.rates, ic->ic_sup_rates[IEEE80211_MODE_11G].rs_rates, 2588145247Sdamien rs.nrates); 2589145247Sdamien DPRINTF(("Setting .11bg supported rates (%u)\n", rs.nrates)); 2590158089Smlaier error = iwi_cmd(sc, IWI_CMD_SET_RATES, &rs, sizeof rs); 2591145247Sdamien if (error != 0) 2592145247Sdamien return error; 2593145247Sdamien 2594171034Sthompsa memset(&rs, 0, sizeof rs); 2595145247Sdamien rs.mode = IWI_MODE_11A; 2596145247Sdamien rs.type = IWI_RATESET_TYPE_SUPPORTED; 2597145247Sdamien rs.nrates = ic->ic_sup_rates[IEEE80211_MODE_11A].rs_nrates; 2598145247Sdamien memcpy(rs.rates, ic->ic_sup_rates[IEEE80211_MODE_11A].rs_rates, 2599145247Sdamien rs.nrates); 2600145247Sdamien DPRINTF(("Setting .11a supported rates (%u)\n", rs.nrates)); 2601158089Smlaier error = iwi_cmd(sc, IWI_CMD_SET_RATES, &rs, sizeof rs); 2602145247Sdamien if (error != 0) 2603145247Sdamien return error; 2604145247Sdamien 2605145247Sdamien data = htole32(arc4random()); 2606145247Sdamien DPRINTF(("Setting initialization vector to %u\n", le32toh(data))); 2607158089Smlaier error = iwi_cmd(sc, IWI_CMD_SET_IV, &data, sizeof data); 2608145247Sdamien if (error != 0) 2609145247Sdamien return error; 2610145247Sdamien 2611145247Sdamien /* enable adapter */ 2612145247Sdamien DPRINTF(("Enabling adapter\n")); 2613158089Smlaier return iwi_cmd(sc, IWI_CMD_ENABLE, NULL, 0); 2614145247Sdamien} 2615145247Sdamien 2616158089Smlaierstatic __inline void 2617158089Smlaierset_scan_type(struct iwi_scan_ext *scan, int ix, int scan_type) 2618146500Sdamien{ 2619158089Smlaier uint8_t *st = &scan->scan_type[ix / 2]; 2620158089Smlaier if (ix % 2) 2621158089Smlaier *st = (*st & 0xf0) | ((scan_type & 0xf) << 0); 2622158089Smlaier else 2623158089Smlaier *st = (*st & 0x0f) | ((scan_type & 0xf) << 4); 2624146500Sdamien} 2625146500Sdamien 2626146500Sdamienstatic int 2627170530Ssamscan_type(const struct ieee80211_scan_state *ss, 2628170530Ssam const struct ieee80211_channel *chan) 2629145247Sdamien{ 2630170530Ssam /* We can only set one essid for a directed scan */ 2631170530Ssam if (ss->ss_nssid != 0) 2632170530Ssam return IWI_SCAN_TYPE_BDIRECTED; 2633170530Ssam if ((ss->ss_flags & IEEE80211_SCAN_ACTIVE) && 2634170530Ssam (chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0) 2635170530Ssam return IWI_SCAN_TYPE_BROADCAST; 2636170530Ssam return IWI_SCAN_TYPE_PASSIVE; 2637170530Ssam} 2638170530Ssam 2639170530Ssamstatic __inline int 2640170530Ssamscan_band(const struct ieee80211_channel *c) 2641170530Ssam{ 2642170530Ssam return IEEE80211_IS_CHAN_5GHZ(c) ? IWI_CHAN_5GHZ : IWI_CHAN_2GHZ; 2643170530Ssam} 2644170530Ssam 2645170530Ssam/* 2646170530Ssam * Start a scan on the current channel or all channels. 2647170530Ssam */ 2648170530Ssamstatic int 2649191746Sthompsaiwi_scanchan(struct iwi_softc *sc, unsigned long maxdwell, int allchan) 2650170530Ssam{ 2651170530Ssam struct ieee80211com *ic; 2652170530Ssam struct ieee80211_channel *chan; 2653170530Ssam struct ieee80211_scan_state *ss; 2654158089Smlaier struct iwi_scan_ext scan; 2655170530Ssam int error = 0; 2656145247Sdamien 2657170530Ssam IWI_LOCK_ASSERT(sc); 2658170530Ssam if (sc->fw_state == IWI_FW_SCANNING) { 2659170530Ssam /* 2660170530Ssam * This should not happen as we only trigger scan_next after 2661170530Ssam * completion 2662170530Ssam */ 2663170530Ssam DPRINTF(("%s: called too early - still scanning\n", __func__)); 2664170530Ssam return (EBUSY); 2665170530Ssam } 2666170530Ssam IWI_STATE_BEGIN(sc, IWI_FW_SCANNING); 2667166848Sluigi 2668178354Ssam ic = sc->sc_ifp->if_l2com; 2669170530Ssam ss = ic->ic_scan; 2670170530Ssam 2671145247Sdamien memset(&scan, 0, sizeof scan); 2672170530Ssam scan.full_scan_index = htole32(++sc->sc_scangen); 2673170530Ssam scan.dwell_time[IWI_SCAN_TYPE_PASSIVE] = htole16(maxdwell); 2674170530Ssam if (ic->ic_flags_ext & IEEE80211_FEXT_BGSCAN) { 2675170530Ssam /* 2676170530Ssam * Use very short dwell times for when we send probe request 2677170530Ssam * frames. Without this bg scans hang. Ideally this should 2678170530Ssam * be handled with early-termination as done by net80211 but 2679170530Ssam * that's not feasible (aborting a scan is problematic). 2680170530Ssam */ 2681170530Ssam scan.dwell_time[IWI_SCAN_TYPE_BROADCAST] = htole16(30); 2682170530Ssam scan.dwell_time[IWI_SCAN_TYPE_BDIRECTED] = htole16(30); 2683170530Ssam } else { 2684170530Ssam scan.dwell_time[IWI_SCAN_TYPE_BROADCAST] = htole16(maxdwell); 2685170530Ssam scan.dwell_time[IWI_SCAN_TYPE_BDIRECTED] = htole16(maxdwell); 2686170530Ssam } 2687145247Sdamien 2688170530Ssam /* We can only set one essid for a directed scan */ 2689170530Ssam if (ss->ss_nssid != 0) { 2690170530Ssam error = iwi_cmd(sc, IWI_CMD_SET_ESSID, ss->ss_ssid[0].ssid, 2691170530Ssam ss->ss_ssid[0].len); 2692170530Ssam if (error) 2693170530Ssam return (error); 2694170530Ssam } 2695151030Sdamien 2696191746Sthompsa if (allchan) { 2697170530Ssam int i, next, band, b, bstart; 2698170530Ssam /* 2699170530Ssam * Convert scan list to run-length encoded channel list 2700170530Ssam * the firmware requires (preserving the order setup by 2701170530Ssam * net80211). The first entry in each run specifies the 2702170530Ssam * band and the count of items in the run. 2703170530Ssam */ 2704170530Ssam next = 0; /* next open slot */ 2705170530Ssam bstart = 0; /* NB: not needed, silence compiler */ 2706170530Ssam band = -1; /* NB: impossible value */ 2707170530Ssam KASSERT(ss->ss_last > 0, ("no channels")); 2708170530Ssam for (i = 0; i < ss->ss_last; i++) { 2709170530Ssam chan = ss->ss_chans[i]; 2710170530Ssam b = scan_band(chan); 2711170530Ssam if (b != band) { 2712170530Ssam if (band != -1) 2713170530Ssam scan.channels[bstart] = 2714170530Ssam (next - bstart) | band; 2715170530Ssam /* NB: this allocates a slot for the run-len */ 2716170530Ssam band = b, bstart = next++; 2717170530Ssam } 2718170530Ssam if (next >= IWI_SCAN_CHANNELS) { 2719170530Ssam DPRINTF(("truncating scan list\n")); 2720170530Ssam break; 2721170530Ssam } 2722170530Ssam scan.channels[next] = ieee80211_chan2ieee(ic, chan); 2723170530Ssam set_scan_type(&scan, next, scan_type(ss, chan)); 2724170530Ssam next++; 2725170530Ssam } 2726170530Ssam scan.channels[bstart] = (next - bstart) | band; 2727170530Ssam } else { 2728170530Ssam /* Scan the current channel only */ 2729170530Ssam chan = ic->ic_curchan; 2730170530Ssam scan.channels[0] = 1 | scan_band(chan); 2731170530Ssam scan.channels[1] = ieee80211_chan2ieee(ic, chan); 2732170530Ssam set_scan_type(&scan, 1, scan_type(ss, chan)); 2733170530Ssam } 2734163209Smlaier#ifdef IWI_DEBUG 2735170530Ssam if (iwi_debug > 0) { 2736170530Ssam static const char *scantype[8] = 2737170530Ssam { "PSTOP", "PASV", "DIR", "BCAST", "BDIR", "5", "6", "7" }; 2738170530Ssam int i; 2739170530Ssam printf("Scan request: index %u dwell %d/%d/%d\n" 2740170530Ssam , le32toh(scan.full_scan_index) 2741170530Ssam , le16toh(scan.dwell_time[IWI_SCAN_TYPE_PASSIVE]) 2742170530Ssam , le16toh(scan.dwell_time[IWI_SCAN_TYPE_BROADCAST]) 2743170530Ssam , le16toh(scan.dwell_time[IWI_SCAN_TYPE_BDIRECTED]) 2744170530Ssam ); 2745170530Ssam i = 0; 2746170530Ssam do { 2747170530Ssam int run = scan.channels[i]; 2748170530Ssam if (run == 0) 2749170530Ssam break; 2750170530Ssam printf("Scan %d %s channels:", run & 0x3f, 2751170530Ssam run & IWI_CHAN_2GHZ ? "2.4GHz" : "5GHz"); 2752170530Ssam for (run &= 0x3f, i++; run > 0; run--, i++) { 2753170530Ssam uint8_t type = scan.scan_type[i/2]; 2754170530Ssam printf(" %u/%s", scan.channels[i], 2755170530Ssam scantype[(i & 1 ? type : type>>4) & 7]); 2756170530Ssam } 2757163209Smlaier printf("\n"); 2758170530Ssam } while (i < IWI_SCAN_CHANNELS); 2759170530Ssam } 2760163209Smlaier#endif 2761158089Smlaier 2762170530Ssam return (iwi_cmd(sc, IWI_CMD_SCAN_EXT, &scan, sizeof scan)); 2763145247Sdamien} 2764145247Sdamien 2765145247Sdamienstatic int 2766158089Smlaieriwi_set_sensitivity(struct iwi_softc *sc, int8_t rssi_dbm) 2767158089Smlaier{ 2768158089Smlaier struct iwi_sensitivity sens; 2769158089Smlaier 2770158089Smlaier DPRINTF(("Setting sensitivity to %d\n", rssi_dbm)); 2771158089Smlaier 2772158089Smlaier memset(&sens, 0, sizeof sens); 2773158089Smlaier sens.rssi = htole16(rssi_dbm); 2774158089Smlaier return iwi_cmd(sc, IWI_CMD_SET_SENSITIVITY, &sens, sizeof sens); 2775158089Smlaier} 2776158089Smlaier 2777158089Smlaierstatic int 2778178354Ssamiwi_auth_and_assoc(struct iwi_softc *sc, struct ieee80211vap *vap) 2779145247Sdamien{ 2780178354Ssam struct ieee80211com *ic = vap->iv_ic; 2781178354Ssam struct ifnet *ifp = vap->iv_ifp; 2782178354Ssam struct ieee80211_node *ni = vap->iv_bss; 2783145247Sdamien struct iwi_configuration config; 2784158089Smlaier struct iwi_associate *assoc = &sc->assoc; 2785145247Sdamien struct iwi_rateset rs; 2786146500Sdamien uint16_t capinfo; 2787178354Ssam uint32_t data; 2788171034Sthompsa int error, mode; 2789170530Ssam 2790170530Ssam IWI_LOCK_ASSERT(sc); 2791170530Ssam 2792170530Ssam if (sc->flags & IWI_FLAG_ASSOCIATED) { 2793170530Ssam DPRINTF(("Already associated\n")); 2794170530Ssam return (-1); 2795170530Ssam } 2796170530Ssam 2797170530Ssam IWI_STATE_BEGIN(sc, IWI_FW_ASSOCIATING); 2798170530Ssam error = 0; 2799171034Sthompsa mode = 0; 2800171034Sthompsa 2801171034Sthompsa if (IEEE80211_IS_CHAN_A(ic->ic_curchan)) 2802171034Sthompsa mode = IWI_MODE_11A; 2803171034Sthompsa else if (IEEE80211_IS_CHAN_G(ic->ic_curchan)) 2804171034Sthompsa mode = IWI_MODE_11G; 2805171034Sthompsa if (IEEE80211_IS_CHAN_B(ic->ic_curchan)) 2806171034Sthompsa mode = IWI_MODE_11B; 2807171034Sthompsa 2808170530Ssam if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) { 2809145247Sdamien memset(&config, 0, sizeof config); 2810145247Sdamien config.bluetooth_coexistence = sc->bluetooth; 2811146500Sdamien config.antenna = sc->antenna; 2812145247Sdamien config.multicast_enabled = 1; 2813171034Sthompsa if (mode == IWI_MODE_11G) 2814171034Sthompsa config.use_protection = 1; 2815146500Sdamien config.answer_pbreq = 2816178354Ssam (vap->iv_opmode == IEEE80211_M_IBSS) ? 1 : 0; 2817146500Sdamien config.disable_unicast_decryption = 1; 2818146500Sdamien config.disable_multicast_decryption = 1; 2819145247Sdamien DPRINTF(("Configuring adapter\n")); 2820158089Smlaier error = iwi_cmd(sc, IWI_CMD_SET_CONFIG, &config, sizeof config); 2821145247Sdamien if (error != 0) 2822170530Ssam goto done; 2823145247Sdamien } 2824145247Sdamien 2825145247Sdamien#ifdef IWI_DEBUG 2826145247Sdamien if (iwi_debug > 0) { 2827145247Sdamien printf("Setting ESSID to "); 2828145247Sdamien ieee80211_print_essid(ni->ni_essid, ni->ni_esslen); 2829145247Sdamien printf("\n"); 2830145247Sdamien } 2831145247Sdamien#endif 2832158089Smlaier error = iwi_cmd(sc, IWI_CMD_SET_ESSID, ni->ni_essid, ni->ni_esslen); 2833145247Sdamien if (error != 0) 2834170530Ssam goto done; 2835145247Sdamien 2836178354Ssam error = iwi_setpowermode(sc, vap); 2837178354Ssam if (error != 0) 2838178354Ssam goto done; 2839178354Ssam 2840178354Ssam data = htole32(vap->iv_rtsthreshold); 2841178354Ssam DPRINTF(("Setting RTS threshold to %u\n", le32toh(data))); 2842178354Ssam error = iwi_cmd(sc, IWI_CMD_SET_RTS_THRESHOLD, &data, sizeof data); 2843178354Ssam if (error != 0) 2844178354Ssam goto done; 2845178354Ssam 2846178354Ssam data = htole32(vap->iv_fragthreshold); 2847178354Ssam DPRINTF(("Setting fragmentation threshold to %u\n", le32toh(data))); 2848178354Ssam error = iwi_cmd(sc, IWI_CMD_SET_FRAG_THRESHOLD, &data, sizeof data); 2849178354Ssam if (error != 0) 2850178354Ssam goto done; 2851178354Ssam 2852151030Sdamien /* the rate set has already been "negotiated" */ 2853171034Sthompsa memset(&rs, 0, sizeof rs); 2854171034Sthompsa rs.mode = mode; 2855151030Sdamien rs.type = IWI_RATESET_TYPE_NEGOTIATED; 2856145247Sdamien rs.nrates = ni->ni_rates.rs_nrates; 2857163618Smlaier if (rs.nrates > IWI_RATESET_SIZE) { 2858163618Smlaier DPRINTF(("Truncating negotiated rate set from %u\n", 2859163618Smlaier rs.nrates)); 2860163618Smlaier rs.nrates = IWI_RATESET_SIZE; 2861163618Smlaier } 2862145247Sdamien memcpy(rs.rates, ni->ni_rates.rs_rates, rs.nrates); 2863158089Smlaier DPRINTF(("Setting negotiated rates (%u)\n", rs.nrates)); 2864158089Smlaier error = iwi_cmd(sc, IWI_CMD_SET_RATES, &rs, sizeof rs); 2865145247Sdamien if (error != 0) 2866170530Ssam goto done; 2867145247Sdamien 2868158089Smlaier memset(assoc, 0, sizeof *assoc); 2869158089Smlaier 2870178354Ssam if ((vap->iv_flags & IEEE80211_F_WME) && ni->ni_ies.wme_ie != NULL) { 2871158089Smlaier /* NB: don't treat WME setup as failure */ 2872178354Ssam if (iwi_wme_setparams(sc, ic) == 0 && iwi_wme_setie(sc) == 0) 2873158089Smlaier assoc->policy |= htole16(IWI_POLICY_WME); 2874158089Smlaier /* XXX complain on failure? */ 2875149338Sdamien } 2876149338Sdamien 2877178354Ssam if (vap->iv_appie_wpa != NULL) { 2878178354Ssam struct ieee80211_appie *ie = vap->iv_appie_wpa; 2879178354Ssam 2880178354Ssam DPRINTF(("Setting optional IE (len=%u)\n", ie->ie_len)); 2881178354Ssam error = iwi_cmd(sc, IWI_CMD_SET_OPTIE, ie->ie_data, ie->ie_len); 2882146500Sdamien if (error != 0) 2883170530Ssam goto done; 2884146500Sdamien } 2885146500Sdamien 2886178354Ssam error = iwi_set_sensitivity(sc, ic->ic_node_getrssi(ni)); 2887145247Sdamien if (error != 0) 2888170530Ssam goto done; 2889145247Sdamien 2890171034Sthompsa assoc->mode = mode; 2891170530Ssam assoc->chan = ic->ic_curchan->ic_ieee; 2892158089Smlaier /* 2893158089Smlaier * NB: do not arrange for shared key auth w/o privacy 2894158089Smlaier * (i.e. a wep key); it causes a firmware error. 2895158089Smlaier */ 2896178354Ssam if ((vap->iv_flags & IEEE80211_F_PRIVACY) && 2897158089Smlaier ni->ni_authmode == IEEE80211_AUTH_SHARED) { 2898158089Smlaier assoc->auth = IWI_AUTH_SHARED; 2899158089Smlaier /* 2900158089Smlaier * It's possible to have privacy marked but no default 2901158089Smlaier * key setup. This typically is due to a user app bug 2902158089Smlaier * but if we blindly grab the key the firmware will 2903158089Smlaier * barf so avoid it for now. 2904158089Smlaier */ 2905178354Ssam if (vap->iv_def_txkey != IEEE80211_KEYIX_NONE) 2906178354Ssam assoc->auth |= vap->iv_def_txkey << 4; 2907158089Smlaier 2908178354Ssam error = iwi_setwepkeys(sc, vap); 2909158089Smlaier if (error != 0) 2910170530Ssam goto done; 2911158089Smlaier } 2912178354Ssam if (vap->iv_flags & IEEE80211_F_WPA) 2913158089Smlaier assoc->policy |= htole16(IWI_POLICY_WPA); 2914178354Ssam if (vap->iv_opmode == IEEE80211_M_IBSS && ni->ni_tstamp.tsf == 0) 2915158089Smlaier assoc->type = IWI_HC_IBSS_START; 2916158089Smlaier else 2917158089Smlaier assoc->type = IWI_HC_ASSOC; 2918158089Smlaier memcpy(assoc->tstamp, ni->ni_tstamp.data, 8); 2919146500Sdamien 2920178354Ssam if (vap->iv_opmode == IEEE80211_M_IBSS) 2921146500Sdamien capinfo = IEEE80211_CAPINFO_IBSS; 2922146500Sdamien else 2923146500Sdamien capinfo = IEEE80211_CAPINFO_ESS; 2924178354Ssam if (vap->iv_flags & IEEE80211_F_PRIVACY) 2925146500Sdamien capinfo |= IEEE80211_CAPINFO_PRIVACY; 2926146500Sdamien if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && 2927170530Ssam IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) 2928146500Sdamien capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE; 2929158089Smlaier if (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) 2930146500Sdamien capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME; 2931158089Smlaier assoc->capinfo = htole16(capinfo); 2932146500Sdamien 2933158089Smlaier assoc->lintval = htole16(ic->ic_lintval); 2934158089Smlaier assoc->intval = htole16(ni->ni_intval); 2935158089Smlaier IEEE80211_ADDR_COPY(assoc->bssid, ni->ni_bssid); 2936178354Ssam if (vap->iv_opmode == IEEE80211_M_IBSS) 2937158089Smlaier IEEE80211_ADDR_COPY(assoc->dst, ifp->if_broadcastaddr); 2938145247Sdamien else 2939158089Smlaier IEEE80211_ADDR_COPY(assoc->dst, ni->ni_bssid); 2940145247Sdamien 2941158089Smlaier DPRINTF(("%s bssid %6D dst %6D channel %u policy 0x%x " 2942158089Smlaier "auth %u capinfo 0x%x lintval %u bintval %u\n", 2943158089Smlaier assoc->type == IWI_HC_IBSS_START ? "Start" : "Join", 2944158089Smlaier assoc->bssid, ":", assoc->dst, ":", 2945158089Smlaier assoc->chan, le16toh(assoc->policy), assoc->auth, 2946158089Smlaier le16toh(assoc->capinfo), le16toh(assoc->lintval), 2947158089Smlaier le16toh(assoc->intval))); 2948170530Ssam error = iwi_cmd(sc, IWI_CMD_ASSOCIATE, assoc, sizeof *assoc); 2949170530Ssamdone: 2950170530Ssam if (error) 2951170530Ssam IWI_STATE_END(sc, IWI_FW_ASSOCIATING); 2952170530Ssam 2953170530Ssam return (error); 2954145247Sdamien} 2955145247Sdamien 2956191746Sthompsastatic void 2957191746Sthompsaiwi_disassoc(void *arg, int pending) 2958191746Sthompsa{ 2959191746Sthompsa struct iwi_softc *sc = arg; 2960191746Sthompsa IWI_LOCK_DECL; 2961191746Sthompsa 2962191746Sthompsa IWI_LOCK(sc); 2963191746Sthompsa iwi_disassociate(sc, 0); 2964191746Sthompsa IWI_UNLOCK(sc); 2965191746Sthompsa} 2966191746Sthompsa 2967158089Smlaierstatic int 2968158089Smlaieriwi_disassociate(struct iwi_softc *sc, int quiet) 2969158089Smlaier{ 2970158089Smlaier struct iwi_associate *assoc = &sc->assoc; 2971158089Smlaier 2972170530Ssam if ((sc->flags & IWI_FLAG_ASSOCIATED) == 0) { 2973170530Ssam DPRINTF(("Not associated\n")); 2974170530Ssam return (-1); 2975170530Ssam } 2976170530Ssam 2977170530Ssam IWI_STATE_BEGIN(sc, IWI_FW_DISASSOCIATING); 2978170530Ssam 2979158089Smlaier if (quiet) 2980158089Smlaier assoc->type = IWI_HC_DISASSOC_QUIET; 2981158089Smlaier else 2982158089Smlaier assoc->type = IWI_HC_DISASSOC; 2983158089Smlaier 2984158089Smlaier DPRINTF(("Trying to disassociate from %6D channel %u\n", 2985158089Smlaier assoc->bssid, ":", assoc->chan)); 2986158089Smlaier return iwi_cmd(sc, IWI_CMD_ASSOCIATE, assoc, sizeof *assoc); 2987158089Smlaier} 2988158089Smlaier 2989166848Sluigi/* 2990166848Sluigi * release dma resources for the firmware 2991166848Sluigi */ 2992158089Smlaierstatic void 2993166848Sluigiiwi_release_fw_dma(struct iwi_softc *sc) 2994166848Sluigi{ 2995166848Sluigi if (sc->fw_flags & IWI_FW_HAVE_PHY) 2996166848Sluigi bus_dmamap_unload(sc->fw_dmat, sc->fw_map); 2997166848Sluigi if (sc->fw_flags & IWI_FW_HAVE_MAP) 2998166848Sluigi bus_dmamem_free(sc->fw_dmat, sc->fw_virtaddr, sc->fw_map); 2999166848Sluigi if (sc->fw_flags & IWI_FW_HAVE_DMAT) 3000166848Sluigi bus_dma_tag_destroy(sc->fw_dmat); 3001166848Sluigi 3002166848Sluigi sc->fw_flags = 0; 3003166848Sluigi sc->fw_dma_size = 0; 3004166848Sluigi sc->fw_dmat = NULL; 3005166848Sluigi sc->fw_map = NULL; 3006166848Sluigi sc->fw_physaddr = 0; 3007166848Sluigi sc->fw_virtaddr = NULL; 3008166848Sluigi} 3009166848Sluigi 3010166848Sluigi/* 3011166848Sluigi * allocate the dma descriptor for the firmware. 3012166848Sluigi * Return 0 on success, 1 on error. 3013166848Sluigi * Must be called unlocked, protected by IWI_FLAG_FW_LOADING. 3014166848Sluigi */ 3015166848Sluigistatic int 3016166848Sluigiiwi_init_fw_dma(struct iwi_softc *sc, int size) 3017166848Sluigi{ 3018167776Sjhb if (sc->fw_dma_size >= size) 3019166848Sluigi return 0; 3020166848Sluigi if (bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 4, 0, 3021166848Sluigi BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, 3022166848Sluigi size, 1, size, 0, NULL, NULL, &sc->fw_dmat) != 0) { 3023166848Sluigi device_printf(sc->sc_dev, 3024166848Sluigi "could not create firmware DMA tag\n"); 3025166848Sluigi goto error; 3026166848Sluigi } 3027166848Sluigi sc->fw_flags |= IWI_FW_HAVE_DMAT; 3028166848Sluigi if (bus_dmamem_alloc(sc->fw_dmat, &sc->fw_virtaddr, 0, 3029166848Sluigi &sc->fw_map) != 0) { 3030166848Sluigi device_printf(sc->sc_dev, 3031166848Sluigi "could not allocate firmware DMA memory\n"); 3032166848Sluigi goto error; 3033166848Sluigi } 3034166848Sluigi sc->fw_flags |= IWI_FW_HAVE_MAP; 3035166848Sluigi if (bus_dmamap_load(sc->fw_dmat, sc->fw_map, sc->fw_virtaddr, 3036166848Sluigi size, iwi_dma_map_addr, &sc->fw_physaddr, 0) != 0) { 3037166848Sluigi device_printf(sc->sc_dev, "could not load firmware DMA map\n"); 3038166848Sluigi goto error; 3039166848Sluigi } 3040166848Sluigi sc->fw_flags |= IWI_FW_HAVE_PHY; 3041166848Sluigi sc->fw_dma_size = size; 3042166848Sluigi return 0; 3043166848Sluigi 3044166848Sluigierror: 3045166848Sluigi iwi_release_fw_dma(sc); 3046166848Sluigi return 1; 3047166848Sluigi} 3048166848Sluigi 3049166848Sluigistatic void 3050178354Ssamiwi_init_locked(struct iwi_softc *sc) 3051158089Smlaier{ 3052178354Ssam struct ifnet *ifp = sc->sc_ifp; 3053145247Sdamien struct iwi_rx_data *data; 3054158089Smlaier int i; 3055145247Sdamien 3056170530Ssam IWI_LOCK_ASSERT(sc); 3057178354Ssam 3058170530Ssam if (sc->fw_state == IWI_FW_LOADING) { 3059166848Sluigi device_printf(sc->sc_dev, "%s: already loading\n", __func__); 3060158089Smlaier return; /* XXX: condvar? */ 3061166848Sluigi } 3062156539Sdamien 3063178354Ssam iwi_stop_locked(sc); 3064178354Ssam 3065170530Ssam IWI_STATE_BEGIN(sc, IWI_FW_LOADING); 3066145247Sdamien 3067145247Sdamien if (iwi_reset(sc) != 0) { 3068145247Sdamien device_printf(sc->sc_dev, "could not reset adapter\n"); 3069158089Smlaier goto fail; 3070145247Sdamien } 3071158089Smlaier if (iwi_load_firmware(sc, &sc->fw_boot) != 0) { 3072156546Sdamien device_printf(sc->sc_dev, 3073158089Smlaier "could not load boot firmware %s\n", sc->fw_boot.name); 3074166848Sluigi goto fail; 3075156546Sdamien } 3076158089Smlaier if (iwi_load_ucode(sc, &sc->fw_uc) != 0) { 3077158089Smlaier device_printf(sc->sc_dev, 3078158089Smlaier "could not load microcode %s\n", sc->fw_uc.name); 3079166848Sluigi goto fail; 3080145247Sdamien } 3081145247Sdamien 3082145247Sdamien iwi_stop_master(sc); 3083145247Sdamien 3084145247Sdamien CSR_WRITE_4(sc, IWI_CSR_CMD_BASE, sc->cmdq.physaddr); 3085145247Sdamien CSR_WRITE_4(sc, IWI_CSR_CMD_SIZE, sc->cmdq.count); 3086145247Sdamien CSR_WRITE_4(sc, IWI_CSR_CMD_WIDX, sc->cmdq.cur); 3087145247Sdamien 3088149338Sdamien CSR_WRITE_4(sc, IWI_CSR_TX1_BASE, sc->txq[0].physaddr); 3089149338Sdamien CSR_WRITE_4(sc, IWI_CSR_TX1_SIZE, sc->txq[0].count); 3090149338Sdamien CSR_WRITE_4(sc, IWI_CSR_TX1_WIDX, sc->txq[0].cur); 3091145247Sdamien 3092149338Sdamien CSR_WRITE_4(sc, IWI_CSR_TX2_BASE, sc->txq[1].physaddr); 3093149338Sdamien CSR_WRITE_4(sc, IWI_CSR_TX2_SIZE, sc->txq[1].count); 3094149338Sdamien CSR_WRITE_4(sc, IWI_CSR_TX2_WIDX, sc->txq[1].cur); 3095145247Sdamien 3096149338Sdamien CSR_WRITE_4(sc, IWI_CSR_TX3_BASE, sc->txq[2].physaddr); 3097149338Sdamien CSR_WRITE_4(sc, IWI_CSR_TX3_SIZE, sc->txq[2].count); 3098149338Sdamien CSR_WRITE_4(sc, IWI_CSR_TX3_WIDX, sc->txq[2].cur); 3099145247Sdamien 3100149338Sdamien CSR_WRITE_4(sc, IWI_CSR_TX4_BASE, sc->txq[3].physaddr); 3101149338Sdamien CSR_WRITE_4(sc, IWI_CSR_TX4_SIZE, sc->txq[3].count); 3102149338Sdamien CSR_WRITE_4(sc, IWI_CSR_TX4_WIDX, sc->txq[3].cur); 3103145247Sdamien 3104145247Sdamien for (i = 0; i < sc->rxq.count; i++) { 3105145247Sdamien data = &sc->rxq.data[i]; 3106145247Sdamien CSR_WRITE_4(sc, data->reg, data->physaddr); 3107145247Sdamien } 3108145247Sdamien 3109145247Sdamien CSR_WRITE_4(sc, IWI_CSR_RX_WIDX, sc->rxq.count - 1); 3110145247Sdamien 3111158089Smlaier if (iwi_load_firmware(sc, &sc->fw_fw) != 0) { 3112158089Smlaier device_printf(sc->sc_dev, 3113158089Smlaier "could not load main firmware %s\n", sc->fw_fw.name); 3114166848Sluigi goto fail; 3115145247Sdamien } 3116145247Sdamien sc->flags |= IWI_FLAG_FW_INITED; 3117145247Sdamien 3118178354Ssam IWI_STATE_END(sc, IWI_FW_LOADING); 3119178354Ssam 3120145247Sdamien if (iwi_config(sc) != 0) { 3121178354Ssam device_printf(sc->sc_dev, "unable to enable adapter\n"); 3122178354Ssam goto fail2; 3123145247Sdamien } 3124145247Sdamien 3125170530Ssam callout_reset(&sc->sc_wdtimer, hz, iwi_watchdog, sc); 3126148887Srwatson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 3127148887Srwatson ifp->if_drv_flags |= IFF_DRV_RUNNING; 3128145247Sdamien return; 3129178354Ssamfail: 3130170530Ssam IWI_STATE_END(sc, IWI_FW_LOADING); 3131178354Ssamfail2: 3132178354Ssam iwi_stop_locked(sc); 3133145247Sdamien} 3134145247Sdamien 3135145247Sdamienstatic void 3136178354Ssamiwi_init(void *priv) 3137145247Sdamien{ 3138145247Sdamien struct iwi_softc *sc = priv; 3139178354Ssam struct ifnet *ifp = sc->sc_ifp; 3140178354Ssam struct ieee80211com *ic = ifp->if_l2com; 3141178354Ssam IWI_LOCK_DECL; 3142145247Sdamien 3143178354Ssam IWI_LOCK(sc); 3144178354Ssam iwi_init_locked(sc); 3145178354Ssam IWI_UNLOCK(sc); 3146178354Ssam 3147178354Ssam if (ifp->if_drv_flags & IFF_DRV_RUNNING) 3148178354Ssam ieee80211_start_all(ic); 3149178354Ssam} 3150178354Ssam 3151178354Ssamstatic void 3152178354Ssamiwi_stop_locked(void *priv) 3153178354Ssam{ 3154178354Ssam struct iwi_softc *sc = priv; 3155178354Ssam struct ifnet *ifp = sc->sc_ifp; 3156178354Ssam 3157170530Ssam IWI_LOCK_ASSERT(sc); 3158178354Ssam 3159178354Ssam ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 3160178354Ssam 3161158089Smlaier if (sc->sc_softled) { 3162158089Smlaier callout_stop(&sc->sc_ledtimer); 3163158089Smlaier sc->sc_blinking = 0; 3164158089Smlaier } 3165178354Ssam callout_stop(&sc->sc_wdtimer); 3166178354Ssam callout_stop(&sc->sc_rftimer); 3167156539Sdamien 3168145247Sdamien iwi_stop_master(sc); 3169145247Sdamien 3170145247Sdamien CSR_WRITE_4(sc, IWI_CSR_RST, IWI_RST_SOFT_RESET); 3171145247Sdamien 3172145247Sdamien /* reset rings */ 3173145247Sdamien iwi_reset_cmd_ring(sc, &sc->cmdq); 3174149338Sdamien iwi_reset_tx_ring(sc, &sc->txq[0]); 3175149338Sdamien iwi_reset_tx_ring(sc, &sc->txq[1]); 3176149338Sdamien iwi_reset_tx_ring(sc, &sc->txq[2]); 3177149338Sdamien iwi_reset_tx_ring(sc, &sc->txq[3]); 3178145247Sdamien iwi_reset_rx_ring(sc, &sc->rxq); 3179145247Sdamien 3180158089Smlaier sc->sc_tx_timer = 0; 3181170530Ssam sc->sc_state_timer = 0; 3182170530Ssam sc->sc_busy_timer = 0; 3183170530Ssam sc->flags &= ~(IWI_FLAG_BUSY | IWI_FLAG_ASSOCIATED); 3184170530Ssam sc->fw_state = IWI_FW_IDLE; 3185170530Ssam wakeup(sc); 3186145247Sdamien} 3187145247Sdamien 3188158089Smlaierstatic void 3189178354Ssamiwi_stop(struct iwi_softc *sc) 3190158089Smlaier{ 3191158089Smlaier IWI_LOCK_DECL; 3192158089Smlaier 3193158089Smlaier IWI_LOCK(sc); 3194178354Ssam iwi_stop_locked(sc); 3195158089Smlaier IWI_UNLOCK(sc); 3196158089Smlaier} 3197158089Smlaier 3198178354Ssamstatic void 3199178354Ssamiwi_restart(void *arg, int npending) 3200178354Ssam{ 3201178354Ssam struct iwi_softc *sc = arg; 3202178354Ssam 3203178354Ssam iwi_init(sc); 3204178354Ssam} 3205178354Ssam 3206158089Smlaier/* 3207158089Smlaier * Return whether or not the radio is enabled in hardware 3208158089Smlaier * (i.e. the rfkill switch is "off"). 3209158089Smlaier */ 3210145247Sdamienstatic int 3211158089Smlaieriwi_getrfkill(struct iwi_softc *sc) 3212158089Smlaier{ 3213158089Smlaier return (CSR_READ_4(sc, IWI_CSR_IO) & IWI_IO_RADIO_ENABLED) == 0; 3214158089Smlaier} 3215158089Smlaier 3216158089Smlaierstatic void 3217158089Smlaieriwi_radio_on(void *arg, int pending) 3218158089Smlaier{ 3219158089Smlaier struct iwi_softc *sc = arg; 3220178354Ssam struct ieee80211com *ic = sc->sc_ifp->if_l2com; 3221158089Smlaier 3222158089Smlaier device_printf(sc->sc_dev, "radio turned on\n"); 3223178354Ssam 3224158089Smlaier iwi_init(sc); 3225178354Ssam ieee80211_notify_radio(ic, 1); 3226158089Smlaier} 3227158089Smlaier 3228158089Smlaierstatic void 3229178354Ssamiwi_rfkill_poll(void *arg) 3230178354Ssam{ 3231178354Ssam struct iwi_softc *sc = arg; 3232178354Ssam 3233178354Ssam IWI_LOCK_ASSERT(sc); 3234178354Ssam 3235178354Ssam /* 3236178354Ssam * Check for a change in rfkill state. We get an 3237178354Ssam * interrupt when a radio is disabled but not when 3238178354Ssam * it is enabled so we must poll for the latter. 3239178354Ssam */ 3240178354Ssam if (!iwi_getrfkill(sc)) { 3241191746Sthompsa struct ifnet *ifp = sc->sc_ifp; 3242191746Sthompsa struct ieee80211com *ic = ifp->if_l2com; 3243191746Sthompsa 3244191746Sthompsa ieee80211_runtask(ic, &sc->sc_radiontask); 3245178354Ssam return; 3246178354Ssam } 3247178354Ssam callout_reset(&sc->sc_rftimer, 2*hz, iwi_rfkill_poll, sc); 3248178354Ssam} 3249178354Ssam 3250178354Ssamstatic void 3251158089Smlaieriwi_radio_off(void *arg, int pending) 3252158089Smlaier{ 3253158089Smlaier struct iwi_softc *sc = arg; 3254178354Ssam struct ieee80211com *ic = sc->sc_ifp->if_l2com; 3255170530Ssam IWI_LOCK_DECL; 3256158089Smlaier 3257158089Smlaier device_printf(sc->sc_dev, "radio turned off\n"); 3258178354Ssam 3259178354Ssam ieee80211_notify_radio(ic, 0); 3260178354Ssam 3261170530Ssam IWI_LOCK(sc); 3262178354Ssam iwi_stop_locked(sc); 3263178354Ssam iwi_rfkill_poll(sc); 3264170530Ssam IWI_UNLOCK(sc); 3265158089Smlaier} 3266158089Smlaier 3267158089Smlaierstatic int 3268145247Sdamieniwi_sysctl_stats(SYSCTL_HANDLER_ARGS) 3269145247Sdamien{ 3270145247Sdamien struct iwi_softc *sc = arg1; 3271145247Sdamien uint32_t size, buf[128]; 3272145247Sdamien 3273174317Sphilip memset(buf, 0, sizeof buf); 3274174317Sphilip 3275174317Sphilip if (!(sc->flags & IWI_FLAG_FW_INITED)) 3276145247Sdamien return SYSCTL_OUT(req, buf, sizeof buf); 3277145247Sdamien 3278145247Sdamien size = min(CSR_READ_4(sc, IWI_CSR_TABLE0_SIZE), 128 - 1); 3279145247Sdamien CSR_READ_REGION_4(sc, IWI_CSR_TABLE0_BASE, &buf[1], size); 3280145247Sdamien 3281174317Sphilip return SYSCTL_OUT(req, buf, size); 3282145247Sdamien} 3283145247Sdamien 3284145247Sdamienstatic int 3285145247Sdamieniwi_sysctl_radio(SYSCTL_HANDLER_ARGS) 3286145247Sdamien{ 3287145247Sdamien struct iwi_softc *sc = arg1; 3288158089Smlaier int val = !iwi_getrfkill(sc); 3289145247Sdamien 3290145247Sdamien return SYSCTL_OUT(req, &val, sizeof val); 3291145247Sdamien} 3292158089Smlaier 3293158089Smlaier/* 3294158089Smlaier * Add sysctl knobs. 3295158089Smlaier */ 3296158089Smlaierstatic void 3297158089Smlaieriwi_sysctlattach(struct iwi_softc *sc) 3298158089Smlaier{ 3299158089Smlaier struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); 3300158089Smlaier struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); 3301158089Smlaier 3302158089Smlaier SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "radio", 3303158089Smlaier CTLTYPE_INT | CTLFLAG_RD, sc, 0, iwi_sysctl_radio, "I", 3304158089Smlaier "radio transmitter switch state (0=off, 1=on)"); 3305158089Smlaier 3306158089Smlaier SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "stats", 3307158089Smlaier CTLTYPE_OPAQUE | CTLFLAG_RD, sc, 0, iwi_sysctl_stats, "S", 3308158089Smlaier "statistics"); 3309158089Smlaier 3310158089Smlaier sc->bluetooth = 0; 3311158089Smlaier SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "bluetooth", 3312158089Smlaier CTLFLAG_RW, &sc->bluetooth, 0, "bluetooth coexistence"); 3313158089Smlaier 3314158089Smlaier sc->antenna = IWI_ANTENNA_AUTO; 3315158089Smlaier SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "antenna", 3316158089Smlaier CTLFLAG_RW, &sc->antenna, 0, "antenna (0=auto)"); 3317158089Smlaier} 3318158089Smlaier 3319158089Smlaier/* 3320158089Smlaier * LED support. 3321158089Smlaier * 3322158089Smlaier * Different cards have different capabilities. Some have three 3323158089Smlaier * led's while others have only one. The linux ipw driver defines 3324158089Smlaier * led's for link state (associated or not), band (11a, 11g, 11b), 3325158089Smlaier * and for link activity. We use one led and vary the blink rate 3326158089Smlaier * according to the tx/rx traffic a la the ath driver. 3327158089Smlaier */ 3328158089Smlaier 3329158089Smlaierstatic __inline uint32_t 3330158089Smlaieriwi_toggle_event(uint32_t r) 3331158089Smlaier{ 3332158089Smlaier return r &~ (IWI_RST_STANDBY | IWI_RST_GATE_ODMA | 3333158089Smlaier IWI_RST_GATE_IDMA | IWI_RST_GATE_ADMA); 3334158089Smlaier} 3335158089Smlaier 3336158089Smlaierstatic uint32_t 3337158089Smlaieriwi_read_event(struct iwi_softc *sc) 3338158089Smlaier{ 3339158089Smlaier return MEM_READ_4(sc, IWI_MEM_EEPROM_EVENT); 3340158089Smlaier} 3341158089Smlaier 3342158089Smlaierstatic void 3343158089Smlaieriwi_write_event(struct iwi_softc *sc, uint32_t v) 3344158089Smlaier{ 3345158089Smlaier MEM_WRITE_4(sc, IWI_MEM_EEPROM_EVENT, v); 3346158089Smlaier} 3347158089Smlaier 3348158089Smlaierstatic void 3349158089Smlaieriwi_led_done(void *arg) 3350158089Smlaier{ 3351158089Smlaier struct iwi_softc *sc = arg; 3352158089Smlaier 3353158089Smlaier sc->sc_blinking = 0; 3354158089Smlaier} 3355158089Smlaier 3356158089Smlaier/* 3357158089Smlaier * Turn the activity LED off: flip the pin and then set a timer so no 3358158089Smlaier * update will happen for the specified duration. 3359158089Smlaier */ 3360158089Smlaierstatic void 3361158089Smlaieriwi_led_off(void *arg) 3362158089Smlaier{ 3363158089Smlaier struct iwi_softc *sc = arg; 3364158089Smlaier uint32_t v; 3365158089Smlaier 3366158089Smlaier v = iwi_read_event(sc); 3367158089Smlaier v &= ~sc->sc_ledpin; 3368158089Smlaier iwi_write_event(sc, iwi_toggle_event(v)); 3369158089Smlaier callout_reset(&sc->sc_ledtimer, sc->sc_ledoff, iwi_led_done, sc); 3370158089Smlaier} 3371158089Smlaier 3372158089Smlaier/* 3373158089Smlaier * Blink the LED according to the specified on/off times. 3374158089Smlaier */ 3375158089Smlaierstatic void 3376158089Smlaieriwi_led_blink(struct iwi_softc *sc, int on, int off) 3377158089Smlaier{ 3378158089Smlaier uint32_t v; 3379158089Smlaier 3380158089Smlaier v = iwi_read_event(sc); 3381158089Smlaier v |= sc->sc_ledpin; 3382158089Smlaier iwi_write_event(sc, iwi_toggle_event(v)); 3383158089Smlaier sc->sc_blinking = 1; 3384158089Smlaier sc->sc_ledoff = off; 3385158089Smlaier callout_reset(&sc->sc_ledtimer, on, iwi_led_off, sc); 3386158089Smlaier} 3387158089Smlaier 3388158089Smlaierstatic void 3389158089Smlaieriwi_led_event(struct iwi_softc *sc, int event) 3390158089Smlaier{ 3391158089Smlaier#define N(a) (sizeof(a)/sizeof(a[0])) 3392158089Smlaier /* NB: on/off times from the Atheros NDIS driver, w/ permission */ 3393158089Smlaier static const struct { 3394158089Smlaier u_int rate; /* tx/rx iwi rate */ 3395158089Smlaier u_int16_t timeOn; /* LED on time (ms) */ 3396158089Smlaier u_int16_t timeOff; /* LED off time (ms) */ 3397158089Smlaier } blinkrates[] = { 3398158089Smlaier { IWI_RATE_OFDM54, 40, 10 }, 3399158089Smlaier { IWI_RATE_OFDM48, 44, 11 }, 3400158089Smlaier { IWI_RATE_OFDM36, 50, 13 }, 3401158089Smlaier { IWI_RATE_OFDM24, 57, 14 }, 3402158089Smlaier { IWI_RATE_OFDM18, 67, 16 }, 3403158089Smlaier { IWI_RATE_OFDM12, 80, 20 }, 3404158089Smlaier { IWI_RATE_DS11, 100, 25 }, 3405158089Smlaier { IWI_RATE_OFDM9, 133, 34 }, 3406158089Smlaier { IWI_RATE_OFDM6, 160, 40 }, 3407158089Smlaier { IWI_RATE_DS5, 200, 50 }, 3408158089Smlaier { 6, 240, 58 }, /* XXX 3Mb/s if it existed */ 3409158089Smlaier { IWI_RATE_DS2, 267, 66 }, 3410158089Smlaier { IWI_RATE_DS1, 400, 100 }, 3411158089Smlaier { 0, 500, 130 }, /* unknown rate/polling */ 3412158089Smlaier }; 3413158089Smlaier uint32_t txrate; 3414158089Smlaier int j = 0; /* XXX silence compiler */ 3415158089Smlaier 3416158089Smlaier sc->sc_ledevent = ticks; /* time of last event */ 3417158089Smlaier if (sc->sc_blinking) /* don't interrupt active blink */ 3418158089Smlaier return; 3419158089Smlaier switch (event) { 3420158089Smlaier case IWI_LED_POLL: 3421158089Smlaier j = N(blinkrates)-1; 3422158089Smlaier break; 3423158089Smlaier case IWI_LED_TX: 3424158089Smlaier /* read current transmission rate from adapter */ 3425158089Smlaier txrate = CSR_READ_4(sc, IWI_CSR_CURRENT_TX_RATE); 3426158089Smlaier if (blinkrates[sc->sc_txrix].rate != txrate) { 3427158089Smlaier for (j = 0; j < N(blinkrates)-1; j++) 3428158089Smlaier if (blinkrates[j].rate == txrate) 3429158089Smlaier break; 3430158089Smlaier sc->sc_txrix = j; 3431158089Smlaier } else 3432158089Smlaier j = sc->sc_txrix; 3433158089Smlaier break; 3434158089Smlaier case IWI_LED_RX: 3435158089Smlaier if (blinkrates[sc->sc_rxrix].rate != sc->sc_rxrate) { 3436158089Smlaier for (j = 0; j < N(blinkrates)-1; j++) 3437158089Smlaier if (blinkrates[j].rate == sc->sc_rxrate) 3438158089Smlaier break; 3439158089Smlaier sc->sc_rxrix = j; 3440158089Smlaier } else 3441158089Smlaier j = sc->sc_rxrix; 3442158089Smlaier break; 3443158089Smlaier } 3444158089Smlaier /* XXX beware of overflow */ 3445158089Smlaier iwi_led_blink(sc, (blinkrates[j].timeOn * hz) / 1000, 3446158089Smlaier (blinkrates[j].timeOff * hz) / 1000); 3447158089Smlaier#undef N 3448158089Smlaier} 3449158089Smlaier 3450158089Smlaierstatic int 3451158089Smlaieriwi_sysctl_softled(SYSCTL_HANDLER_ARGS) 3452158089Smlaier{ 3453158089Smlaier struct iwi_softc *sc = arg1; 3454158089Smlaier int softled = sc->sc_softled; 3455158089Smlaier int error; 3456158089Smlaier 3457158089Smlaier error = sysctl_handle_int(oidp, &softled, 0, req); 3458158089Smlaier if (error || !req->newptr) 3459158089Smlaier return error; 3460158089Smlaier softled = (softled != 0); 3461158089Smlaier if (softled != sc->sc_softled) { 3462158089Smlaier if (softled) { 3463158089Smlaier uint32_t v = iwi_read_event(sc); 3464158089Smlaier v &= ~sc->sc_ledpin; 3465158089Smlaier iwi_write_event(sc, iwi_toggle_event(v)); 3466158089Smlaier } 3467158089Smlaier sc->sc_softled = softled; 3468158089Smlaier } 3469158089Smlaier return 0; 3470158089Smlaier} 3471158089Smlaier 3472158089Smlaierstatic void 3473158089Smlaieriwi_ledattach(struct iwi_softc *sc) 3474158089Smlaier{ 3475158089Smlaier struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); 3476158089Smlaier struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); 3477158089Smlaier 3478158089Smlaier sc->sc_blinking = 0; 3479158089Smlaier sc->sc_ledstate = 1; 3480158089Smlaier sc->sc_ledidle = (2700*hz)/1000; /* 2.7sec */ 3481158089Smlaier callout_init_mtx(&sc->sc_ledtimer, &sc->sc_mtx, 0); 3482158089Smlaier 3483158089Smlaier SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 3484158089Smlaier "softled", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 3485158089Smlaier iwi_sysctl_softled, "I", "enable/disable software LED support"); 3486158089Smlaier SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 3487158089Smlaier "ledpin", CTLFLAG_RW, &sc->sc_ledpin, 0, 3488158089Smlaier "pin setting to turn activity LED on"); 3489158089Smlaier SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 3490158089Smlaier "ledidle", CTLFLAG_RW, &sc->sc_ledidle, 0, 3491158089Smlaier "idle time for inactivity LED (ticks)"); 3492158089Smlaier /* XXX for debugging */ 3493158089Smlaier SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 3494158089Smlaier "nictype", CTLFLAG_RD, &sc->sc_nictype, 0, 3495158089Smlaier "NIC type from EEPROM"); 3496158089Smlaier 3497158089Smlaier sc->sc_ledpin = IWI_RST_LED_ACTIVITY; 3498158089Smlaier sc->sc_softled = 1; 3499158089Smlaier 3500158089Smlaier sc->sc_nictype = (iwi_read_prom_word(sc, IWI_EEPROM_NIC) >> 8) & 0xff; 3501158089Smlaier if (sc->sc_nictype == 1) { 3502158089Smlaier /* 3503158089Smlaier * NB: led's are reversed. 3504158089Smlaier */ 3505158089Smlaier sc->sc_ledpin = IWI_RST_LED_ASSOCIATED; 3506158089Smlaier } 3507158089Smlaier} 3508170530Ssam 3509170530Ssamstatic void 3510170530Ssamiwi_scan_start(struct ieee80211com *ic) 3511170530Ssam{ 3512191746Sthompsa /* ignore */ 3513170530Ssam} 3514170530Ssam 3515170530Ssamstatic void 3516170530Ssamiwi_set_channel(struct ieee80211com *ic) 3517170530Ssam{ 3518170530Ssam struct ifnet *ifp = ic->ic_ifp; 3519170530Ssam struct iwi_softc *sc = ifp->if_softc; 3520170530Ssam if (sc->fw_state == IWI_FW_IDLE) 3521170530Ssam iwi_setcurchan(sc, ic->ic_curchan->ic_ieee); 3522170530Ssam} 3523170530Ssam 3524170530Ssamstatic void 3525178354Ssamiwi_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell) 3526170530Ssam{ 3527178354Ssam struct ieee80211vap *vap = ss->ss_vap; 3528178354Ssam struct ifnet *ifp = vap->iv_ic->ic_ifp; 3529170530Ssam struct iwi_softc *sc = ifp->if_softc; 3530191746Sthompsa IWI_LOCK_DECL; 3531170530Ssam 3532191746Sthompsa IWI_LOCK(sc); 3533191746Sthompsa if (iwi_scanchan(sc, maxdwell, 0)) 3534191746Sthompsa ieee80211_cancel_scan(vap); 3535191746Sthompsa IWI_UNLOCK(sc); 3536170530Ssam} 3537170530Ssam 3538170530Ssamstatic void 3539178354Ssamiwi_scan_mindwell(struct ieee80211_scan_state *ss) 3540170530Ssam{ 3541170530Ssam /* NB: don't try to abort scan; wait for firmware to finish */ 3542170530Ssam} 3543170530Ssam 3544170530Ssamstatic void 3545170530Ssamiwi_scan_end(struct ieee80211com *ic) 3546170530Ssam{ 3547170530Ssam struct ifnet *ifp = ic->ic_ifp; 3548170530Ssam struct iwi_softc *sc = ifp->if_softc; 3549191746Sthompsa IWI_LOCK_DECL; 3550170530Ssam 3551191746Sthompsa IWI_LOCK(sc); 3552191746Sthompsa sc->flags &= ~IWI_FLAG_CHANNEL_SCAN; 3553191746Sthompsa /* NB: make sure we're still scanning */ 3554191746Sthompsa if (sc->fw_state == IWI_FW_SCANNING) 3555191746Sthompsa iwi_cmd(sc, IWI_CMD_ABORT_SCAN, NULL, 0); 3556191746Sthompsa IWI_UNLOCK(sc); 3557170530Ssam} 3558