if_ipw.c revision 283527
1178354Ssam/*- 2178354Ssam * Copyright (c) 2004-2006 3178354Ssam * Damien Bergamini <damien.bergamini@free.fr>. All rights reserved. 4178354Ssam * Copyright (c) 2006 Sam Leffler, Errno Consulting 5178354Ssam * Copyright (c) 2007 Andrew Thompson <thompsa@FreeBSD.org> 6178354Ssam * 7178354Ssam * Redistribution and use in source and binary forms, with or without 8178354Ssam * modification, are permitted provided that the following conditions 9178354Ssam * are met: 10178354Ssam * 1. Redistributions of source code must retain the above copyright 11178354Ssam * notice unmodified, this list of conditions, and the following 12178354Ssam * disclaimer. 13178354Ssam * 2. Redistributions in binary form must reproduce the above copyright 14178354Ssam * notice, this list of conditions and the following disclaimer in the 15178354Ssam * documentation and/or other materials provided with the distribution. 16178354Ssam * 17178354Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18178354Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19178354Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20178354Ssam * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21178354Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22178354Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23178354Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24178354Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25178354Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26178354Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27178354Ssam * SUCH DAMAGE. 28178354Ssam */ 29178354Ssam 30178354Ssam#include <sys/cdefs.h> 31178354Ssam__FBSDID("$FreeBSD: head/sys/dev/ipw/if_ipw.c 283527 2015-05-25 13:51:13Z glebius $"); 32178354Ssam 33178354Ssam/*- 34178354Ssam * Intel(R) PRO/Wireless 2100 MiniPCI driver 35178354Ssam * http://www.intel.com/network/connectivity/products/wireless/prowireless_mobile.htm 36178354Ssam */ 37178354Ssam 38178354Ssam#include <sys/param.h> 39178354Ssam#include <sys/sysctl.h> 40178354Ssam#include <sys/sockio.h> 41178354Ssam#include <sys/mbuf.h> 42178354Ssam#include <sys/kernel.h> 43178354Ssam#include <sys/socket.h> 44178354Ssam#include <sys/systm.h> 45178354Ssam#include <sys/malloc.h> 46178354Ssam#include <sys/queue.h> 47178354Ssam#include <sys/taskqueue.h> 48178354Ssam#include <sys/module.h> 49178354Ssam#include <sys/bus.h> 50178354Ssam#include <sys/endian.h> 51178354Ssam#include <sys/linker.h> 52178354Ssam#include <sys/firmware.h> 53227339Sadrian 54227339Sadrian#include <machine/bus.h> 55178354Ssam#include <machine/resource.h> 56178354Ssam#include <sys/rman.h> 57178354Ssam 58178354Ssam#include <dev/pci/pcireg.h> 59178354Ssam#include <dev/pci/pcivar.h> 60178354Ssam 61178354Ssam#include <net/bpf.h> 62190391Ssam#include <net/if.h> 63190391Ssam#include <net/if_var.h> 64190391Ssam#include <net/if_arp.h> 65211295Sbschmidt#include <net/ethernet.h> 66244060Sadrian#include <net/if_dl.h> 67178354Ssam#include <net/if_media.h> 68178354Ssam#include <net/if_types.h> 69178354Ssam 70178354Ssam#include <net80211/ieee80211_var.h> 71178354Ssam#include <net80211/ieee80211_radiotap.h> 72178354Ssam 73283535Sadrian#include <netinet/in.h> 74283535Sadrian#include <netinet/in_systm.h> 75178354Ssam#include <netinet/in_var.h> 76283535Sadrian#include <netinet/ip.h> 77191546Ssam#include <netinet/if_ether.h> 78178354Ssam 79178354Ssam#include <dev/ipw/if_ipwreg.h> 80178354Ssam#include <dev/ipw/if_ipwvar.h> 81178354Ssam 82178354Ssam#define IPW_DEBUG 83178354Ssam#ifdef IPW_DEBUG 84178354Ssam#define DPRINTF(x) do { if (ipw_debug > 0) printf x; } while (0) 85178354Ssam#define DPRINTFN(n, x) do { if (ipw_debug >= (n)) printf x; } while (0) 86178354Ssamint ipw_debug = 0; 87178354SsamSYSCTL_INT(_debug, OID_AUTO, ipw, CTLFLAG_RW, &ipw_debug, 0, "ipw debug level"); 88178354Ssam#else 89178354Ssam#define DPRINTF(x) 90178354Ssam#define DPRINTFN(n, x) 91178354Ssam#endif 92178354Ssam 93178354SsamMODULE_DEPEND(ipw, pci, 1, 1, 1); 94178354SsamMODULE_DEPEND(ipw, wlan, 1, 1, 1); 95178354SsamMODULE_DEPEND(ipw, firmware, 1, 1, 1); 96178354Ssam 97178354Ssamstruct ipw_ident { 98178354Ssam uint16_t vendor; 99178354Ssam uint16_t device; 100178354Ssam const char *name; 101191546Ssam}; 102178354Ssam 103178354Ssamstatic const struct ipw_ident ipw_ident_table[] = { 104178354Ssam { 0x8086, 0x1043, "Intel(R) PRO/Wireless 2100 MiniPCI" }, 105178354Ssam 106178354Ssam { 0, 0, NULL } 107178354Ssam}; 108178354Ssam 109178354Ssamstatic struct ieee80211vap *ipw_vap_create(struct ieee80211com *, 110178354Ssam const char [IFNAMSIZ], int, enum ieee80211_opmode, int, 111178354Ssam const uint8_t [IEEE80211_ADDR_LEN], 112178354Ssam const uint8_t [IEEE80211_ADDR_LEN]); 113178354Ssamstatic void ipw_vap_delete(struct ieee80211vap *); 114193439Ssamstatic int ipw_dma_alloc(struct ipw_softc *); 115178354Ssamstatic void ipw_release(struct ipw_softc *); 116225913Sadrianstatic void ipw_media_status(struct ifnet *, struct ifmediareq *); 117225913Sadrianstatic int ipw_newstate(struct ieee80211vap *, enum ieee80211_state, int); 118193439Ssamstatic uint16_t ipw_read_prom_word(struct ipw_softc *, uint8_t); 119193439Ssamstatic void ipw_rx_cmd_intr(struct ipw_softc *, struct ipw_soft_buf *); 120193439Ssamstatic void ipw_rx_newstate_intr(struct ipw_softc *, struct ipw_soft_buf *); 121178354Ssamstatic void ipw_rx_data_intr(struct ipw_softc *, struct ipw_status *, 122193439Ssam struct ipw_soft_bd *, struct ipw_soft_buf *); 123193439Ssamstatic void ipw_rx_intr(struct ipw_softc *); 124193439Ssamstatic void ipw_release_sbd(struct ipw_softc *, struct ipw_soft_bd *); 125193439Ssamstatic void ipw_tx_intr(struct ipw_softc *); 126193439Ssamstatic void ipw_intr(void *); 127193439Ssamstatic void ipw_dma_map_addr(void *, bus_dma_segment_t *, int, int); 128193439Ssamstatic const char * ipw_cmdname(int); 129193439Ssamstatic int ipw_cmd(struct ipw_softc *, uint32_t, void *, uint32_t); 130193439Ssamstatic int ipw_tx_start(struct ifnet *, struct mbuf *, 131193439Ssam struct ieee80211_node *); 132193439Ssamstatic int ipw_raw_xmit(struct ieee80211_node *, struct mbuf *, 133193439Ssam const struct ieee80211_bpf_params *); 134193439Ssamstatic void ipw_start(struct ifnet *); 135193439Ssamstatic void ipw_start_locked(struct ifnet *); 136193439Ssamstatic void ipw_watchdog(void *); 137193439Ssamstatic int ipw_ioctl(struct ifnet *, u_long, caddr_t); 138178354Ssamstatic void ipw_stop_master(struct ipw_softc *); 139178354Ssamstatic int ipw_enable(struct ipw_softc *); 140178354Ssamstatic int ipw_disable(struct ipw_softc *); 141178354Ssamstatic int ipw_reset(struct ipw_softc *); 142178354Ssamstatic int ipw_load_ucode(struct ipw_softc *, const char *, int); 143178354Ssamstatic int ipw_load_firmware(struct ipw_softc *, const char *, int); 144178354Ssamstatic int ipw_config(struct ipw_softc *); 145178354Ssamstatic void ipw_assoc(struct ieee80211com *, struct ieee80211vap *); 146178354Ssamstatic void ipw_disassoc(struct ieee80211com *, struct ieee80211vap *); 147178354Ssamstatic void ipw_init_task(void *, int); 148178354Ssamstatic void ipw_init(void *); 149178354Ssamstatic void ipw_init_locked(struct ipw_softc *); 150178354Ssamstatic void ipw_stop(void *); 151205140Sweongyostatic void ipw_stop_locked(struct ipw_softc *); 152205140Sweongyostatic int ipw_sysctl_stats(SYSCTL_HANDLER_ARGS); 153178354Ssamstatic int ipw_sysctl_radio(SYSCTL_HANDLER_ARGS); 154178354Ssamstatic uint32_t ipw_read_table1(struct ipw_softc *, uint32_t); 155178354Ssamstatic void ipw_write_table1(struct ipw_softc *, uint32_t, uint32_t); 156190391Ssam#if 0 157190402Ssamstatic int ipw_read_table2(struct ipw_softc *, uint32_t, void *, 158190402Ssam uint32_t *); 159178354Ssamstatic void ipw_read_mem_1(struct ipw_softc *, bus_size_t, uint8_t *, 160178354Ssam bus_size_t); 161178354Ssam#endif 162178354Ssamstatic void ipw_write_mem_1(struct ipw_softc *, bus_size_t, 163178354Ssam const uint8_t *, bus_size_t); 164178354Ssamstatic int ipw_scan(struct ipw_softc *); 165178354Ssamstatic void ipw_scan_start(struct ieee80211com *); 166178354Ssamstatic void ipw_scan_end(struct ieee80211com *); 167190391Ssamstatic void ipw_set_channel(struct ieee80211com *); 168178354Ssamstatic void ipw_scan_curchan(struct ieee80211_scan_state *, 169178354Ssam unsigned long maxdwell); 170178354Ssamstatic void ipw_scan_mindwell(struct ieee80211_scan_state *); 171178354Ssam 172178354Ssamstatic int ipw_probe(device_t); 173178354Ssamstatic int ipw_attach(device_t); 174178354Ssamstatic int ipw_detach(device_t); 175178354Ssamstatic int ipw_shutdown(device_t); 176178354Ssamstatic int ipw_suspend(device_t); 177178354Ssamstatic int ipw_resume(device_t); 178178354Ssam 179178354Ssamstatic device_method_t ipw_methods[] = { 180178354Ssam /* Device interface */ 181178354Ssam DEVMETHOD(device_probe, ipw_probe), 182178354Ssam DEVMETHOD(device_attach, ipw_attach), 183178354Ssam DEVMETHOD(device_detach, ipw_detach), 184178354Ssam DEVMETHOD(device_shutdown, ipw_shutdown), 185178354Ssam DEVMETHOD(device_suspend, ipw_suspend), 186178354Ssam DEVMETHOD(device_resume, ipw_resume), 187178354Ssam 188178354Ssam DEVMETHOD_END 189178354Ssam}; 190178354Ssam 191178354Ssamstatic driver_t ipw_driver = { 192178354Ssam "ipw", 193178354Ssam ipw_methods, 194178354Ssam sizeof (struct ipw_softc) 195178354Ssam}; 196178354Ssam 197178354Ssamstatic devclass_t ipw_devclass; 198178354Ssam 199178354SsamDRIVER_MODULE(ipw, pci, ipw_driver, ipw_devclass, NULL, NULL); 200178354Ssam 201178354SsamMODULE_VERSION(ipw, 1); 202178354Ssam 203178354Ssamstatic int 204178354Ssamipw_probe(device_t dev) 205178354Ssam{ 206178354Ssam const struct ipw_ident *ident; 207178354Ssam 208178354Ssam for (ident = ipw_ident_table; ident->name != NULL; ident++) { 209297164Savos if (pci_get_vendor(dev) == ident->vendor && 210297164Savos pci_get_device(dev) == ident->device) { 211297164Savos device_set_desc(dev, ident->name); 212297164Savos return (BUS_PROBE_DEFAULT); 213297164Savos } 214297164Savos } 215297164Savos return ENXIO; 216297164Savos} 217297164Savos 218297164Savos/* Base Address Register */ 219297164Savosstatic int 220297164Savosipw_attach(device_t dev) 221297164Savos{ 222297164Savos struct ipw_softc *sc = device_get_softc(dev); 223297164Savos struct ifnet *ifp; 224297164Savos struct ieee80211com *ic; 225297164Savos struct ieee80211_channel *c; 226297164Savos uint16_t val; 227178354Ssam int error, i; 228178354Ssam uint8_t macaddr[IEEE80211_ADDR_LEN]; 229178354Ssam 230178354Ssam sc->sc_dev = dev; 231178354Ssam 232178354Ssam mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, 233178354Ssam MTX_DEF | MTX_RECURSE); 234178354Ssam 235178354Ssam TASK_INIT(&sc->sc_init_task, 0, ipw_init_task, sc); 236178354Ssam callout_init_mtx(&sc->sc_wdtimer, &sc->sc_mtx, 0); 237178354Ssam 238178354Ssam pci_write_config(dev, 0x41, 0, 1); 239178354Ssam 240178354Ssam /* enable bus-mastering */ 241178354Ssam pci_enable_busmaster(dev); 242178354Ssam 243178354Ssam i = PCIR_BAR(0); 244178354Ssam sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &i, RF_ACTIVE); 245178354Ssam if (sc->mem == NULL) { 246178354Ssam device_printf(dev, "could not allocate memory resource\n"); 247178354Ssam goto fail; 248178354Ssam } 249178354Ssam 250178354Ssam sc->sc_st = rman_get_bustag(sc->mem); 251178354Ssam sc->sc_sh = rman_get_bushandle(sc->mem); 252178354Ssam 253178354Ssam i = 0; 254178354Ssam sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &i, 255178354Ssam RF_ACTIVE | RF_SHAREABLE); 256264855Sadrian if (sc->irq == NULL) { 257178354Ssam device_printf(dev, "could not allocate interrupt resource\n"); 258178354Ssam goto fail1; 259178354Ssam } 260178354Ssam 261178354Ssam if (ipw_reset(sc) != 0) { 262178354Ssam device_printf(dev, "could not reset adapter\n"); 263178354Ssam goto fail2; 264178354Ssam } 265178354Ssam 266178354Ssam if (ipw_dma_alloc(sc) != 0) { 267178354Ssam device_printf(dev, "could not allocate DMA resources\n"); 268178354Ssam goto fail2; 269178354Ssam } 270178354Ssam 271178354Ssam ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); 272296355Savos if (ifp == NULL) { 273178354Ssam device_printf(dev, "can not if_alloc()\n"); 274178354Ssam goto fail3; 275178354Ssam } 276178354Ssam ic = ifp->if_l2com; 277178354Ssam 278178354Ssam ifp->if_softc = sc; 279178354Ssam if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 280178354Ssam ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 281178354Ssam ifp->if_init = ipw_init; 282178354Ssam ifp->if_ioctl = ipw_ioctl; 283178354Ssam ifp->if_start = ipw_start; 284178354Ssam IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); 285178354Ssam ifp->if_snd.ifq_drv_maxlen = ifqmaxlen; 286178354Ssam IFQ_SET_READY(&ifp->if_snd); 287178354Ssam 288178354Ssam ic->ic_ifp = ifp; 289178354Ssam ic->ic_name = device_get_nameunit(dev); 290178354Ssam ic->ic_opmode = IEEE80211_M_STA; 291178354Ssam ic->ic_phytype = IEEE80211_T_DS; 292178354Ssam 293178354Ssam /* set device capabilities */ 294178354Ssam ic->ic_caps = 295178354Ssam IEEE80211_C_STA /* station mode supported */ 296178354Ssam | IEEE80211_C_IBSS /* IBSS mode supported */ 297178354Ssam | IEEE80211_C_MONITOR /* monitor mode supported */ 298178354Ssam | IEEE80211_C_PMGT /* power save supported */ 299178354Ssam | IEEE80211_C_SHPREAMBLE /* short preamble supported */ 300178354Ssam | IEEE80211_C_WPA /* 802.11i supported */ 301178354Ssam ; 302178354Ssam 303178354Ssam /* read MAC address from EEPROM */ 304178354Ssam val = ipw_read_prom_word(sc, IPW_EEPROM_MAC + 0); 305178354Ssam macaddr[0] = val >> 8; 306178354Ssam macaddr[1] = val & 0xff; 307178354Ssam val = ipw_read_prom_word(sc, IPW_EEPROM_MAC + 1); 308178354Ssam macaddr[2] = val >> 8; 309178354Ssam macaddr[3] = val & 0xff; 310178354Ssam val = ipw_read_prom_word(sc, IPW_EEPROM_MAC + 2); 311178354Ssam macaddr[4] = val >> 8; 312178354Ssam macaddr[5] = val & 0xff; 313178354Ssam 314178354Ssam /* set supported .11b channels (read from EEPROM) */ 315178354Ssam if ((val = ipw_read_prom_word(sc, IPW_EEPROM_CHANNEL_LIST)) == 0) 316178354Ssam val = 0x7ff; /* default to channels 1-11 */ 317178354Ssam val <<= 1; 318178354Ssam for (i = 1; i < 16; i++) { 319178354Ssam if (val & (1 << i)) { 320178354Ssam c = &ic->ic_channels[ic->ic_nchans++]; 321178354Ssam c->ic_freq = ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ); 322264906Sadrian c->ic_flags = IEEE80211_CHAN_B; 323264906Sadrian c->ic_ieee = i; 324264906Sadrian } 325264906Sadrian } 326264906Sadrian 327178354Ssam /* check support for radio transmitter switch in EEPROM */ 328178354Ssam if (!(ipw_read_prom_word(sc, IPW_EEPROM_RADIO) & 8)) 329178354Ssam sc->flags |= IPW_FLAG_HAS_RADIO_SWITCH; 330178354Ssam 331178354Ssam ieee80211_ifattach(ic, macaddr); 332178354Ssam ic->ic_scan_start = ipw_scan_start; 333264906Sadrian ic->ic_scan_end = ipw_scan_end; 334178354Ssam ic->ic_set_channel = ipw_set_channel; 335178354Ssam ic->ic_scan_curchan = ipw_scan_curchan; 336178354Ssam ic->ic_scan_mindwell = ipw_scan_mindwell; 337178354Ssam ic->ic_raw_xmit = ipw_raw_xmit; 338178354Ssam 339178354Ssam ic->ic_vap_create = ipw_vap_create; 340178354Ssam ic->ic_vap_delete = ipw_vap_delete; 341178354Ssam 342178354Ssam ieee80211_radiotap_attach(ic, 343178354Ssam &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), 344178354Ssam IPW_TX_RADIOTAP_PRESENT, 345178354Ssam &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), 346178354Ssam IPW_RX_RADIOTAP_PRESENT); 347178354Ssam 348178354Ssam /* 349178354Ssam * Add a few sysctl knobs. 350178354Ssam */ 351178354Ssam SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 352178354Ssam SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "radio", 353178354Ssam CTLTYPE_INT | CTLFLAG_RD, sc, 0, ipw_sysctl_radio, "I", 354178354Ssam "radio transmitter switch state (0=off, 1=on)"); 355178354Ssam 356178354Ssam SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 357178354Ssam SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "stats", 358178354Ssam CTLTYPE_OPAQUE | CTLFLAG_RD, sc, 0, ipw_sysctl_stats, "S", 359178354Ssam "statistics"); 360178354Ssam 361178354Ssam /* 362297162Savos * Hook our interrupt after all initialization is complete. 363178354Ssam */ 364178354Ssam error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET | INTR_MPSAFE, 365178354Ssam NULL, ipw_intr, sc, &sc->sc_ih); 366178354Ssam if (error != 0) { 367178354Ssam device_printf(dev, "could not set up interrupt\n"); 368297162Savos goto fail4; 369178354Ssam } 370178354Ssam 371178354Ssam if (bootverbose) 372178354Ssam ieee80211_announce(ic); 373178354Ssam 374178354Ssam return 0; 375178354Ssamfail4: 376178354Ssam if_free(ifp); 377178354Ssamfail3: 378178354Ssam ipw_release(sc); 379178354Ssamfail2: 380178354Ssam bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(sc->irq), sc->irq); 381178354Ssamfail1: 382178354Ssam bus_release_resource(dev, SYS_RES_MEMORY, rman_get_rid(sc->mem), 383178354Ssam sc->mem); 384178354Ssamfail: 385178354Ssam mtx_destroy(&sc->sc_mtx); 386178354Ssam return ENXIO; 387178354Ssam} 388178354Ssam 389178354Ssamstatic int 390178354Ssamipw_detach(device_t dev) 391178354Ssam{ 392178354Ssam struct ipw_softc *sc = device_get_softc(dev); 393178354Ssam struct ifnet *ifp = sc->sc_ifp; 394178354Ssam struct ieee80211com *ic = ifp->if_l2com; 395178354Ssam 396178354Ssam bus_teardown_intr(dev, sc->irq, sc->sc_ih); 397178354Ssam 398178354Ssam ieee80211_draintask(ic, &sc->sc_init_task); 399178354Ssam ipw_stop(sc); 400178354Ssam 401178354Ssam ieee80211_ifdetach(ic); 402178354Ssam 403178354Ssam callout_drain(&sc->sc_wdtimer); 404178354Ssam 405178354Ssam ipw_release(sc); 406178354Ssam 407178354Ssam bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(sc->irq), sc->irq); 408178354Ssam 409178354Ssam bus_release_resource(dev, SYS_RES_MEMORY, rman_get_rid(sc->mem), 410193439Ssam sc->mem); 411178354Ssam 412178354Ssam if_free(ifp); 413178354Ssam 414178354Ssam if (sc->sc_firmware != NULL) { 415178354Ssam firmware_put(sc->sc_firmware, FIRMWARE_UNLOAD); 416178354Ssam sc->sc_firmware = NULL; 417178354Ssam } 418178354Ssam 419178354Ssam mtx_destroy(&sc->sc_mtx); 420178354Ssam 421178354Ssam return 0; 422178354Ssam} 423178354Ssam 424178354Ssamstatic struct ieee80211vap * 425178354Ssamipw_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, 426178354Ssam enum ieee80211_opmode opmode, int flags, 427178354Ssam const uint8_t bssid[IEEE80211_ADDR_LEN], 428178354Ssam const uint8_t mac[IEEE80211_ADDR_LEN]) 429178354Ssam{ 430178354Ssam struct ifnet *ifp = ic->ic_ifp; 431178354Ssam struct ipw_softc *sc = ifp->if_softc; 432178354Ssam struct ipw_vap *ivp; 433264855Sadrian struct ieee80211vap *vap; 434241138Sadrian const struct firmware *fp; 435178354Ssam const struct ipw_firmware_hdr *hdr; 436178354Ssam const char *imagename; 437178354Ssam 438178354Ssam if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ 439178354Ssam return NULL; 440297164Savos 441297164Savos switch (opmode) { 442178354Ssam case IEEE80211_M_STA: 443178354Ssam imagename = "ipw_bss"; 444178354Ssam break; 445178354Ssam case IEEE80211_M_IBSS: 446178354Ssam imagename = "ipw_ibss"; 447178354Ssam break; 448184345Ssam case IEEE80211_M_MONITOR: 449184345Ssam imagename = "ipw_monitor"; 450264855Sadrian break; 451264855Sadrian default: 452184345Ssam return NULL; 453264855Sadrian } 454264855Sadrian 455178354Ssam /* 456193439Ssam * Load firmware image using the firmware(9) subsystem. Doing 457193439Ssam * this unlocked is ok since we're single-threaded by the 458193439Ssam * 802.11 layer. 459193439Ssam */ 460178354Ssam if (sc->sc_firmware == NULL || 461297164Savos strcmp(sc->sc_firmware->name, imagename) != 0) { 462241138Sadrian if (sc->sc_firmware != NULL) 463178354Ssam firmware_put(sc->sc_firmware, FIRMWARE_UNLOAD); 464178354Ssam sc->sc_firmware = firmware_get(imagename); 465178354Ssam } 466184268Ssam if (sc->sc_firmware == NULL) { 467184268Ssam device_printf(sc->sc_dev, 468178354Ssam "could not load firmware image '%s'\n", imagename); 469178354Ssam return NULL; 470178354Ssam } 471178354Ssam fp = sc->sc_firmware; 472178354Ssam if (fp->datasize < sizeof *hdr) { 473178354Ssam device_printf(sc->sc_dev, 474178354Ssam "firmware image too short %zu\n", fp->datasize); 475178354Ssam firmware_put(sc->sc_firmware, FIRMWARE_UNLOAD); 476178354Ssam sc->sc_firmware = NULL; 477178354Ssam return NULL; 478178354Ssam } 479178354Ssam hdr = (const struct ipw_firmware_hdr *)fp->data; 480178354Ssam if (fp->datasize < sizeof *hdr + le32toh(hdr->mainsz) + 481178354Ssam le32toh(hdr->ucodesz)) { 482178354Ssam device_printf(sc->sc_dev, 483178354Ssam "firmware image too short %zu\n", fp->datasize); 484178354Ssam firmware_put(sc->sc_firmware, FIRMWARE_UNLOAD); 485178354Ssam sc->sc_firmware = NULL; 486178354Ssam return NULL; 487178354Ssam } 488178354Ssam 489178354Ssam ivp = (struct ipw_vap *) malloc(sizeof(struct ipw_vap), 490178354Ssam M_80211_VAP, M_NOWAIT | M_ZERO); 491178354Ssam if (ivp == NULL) 492178354Ssam return NULL; 493178354Ssam vap = &ivp->vap; 494178354Ssam 495178354Ssam ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac); 496178354Ssam /* override with driver methods */ 497178354Ssam ivp->newstate = vap->iv_newstate; 498178354Ssam vap->iv_newstate = ipw_newstate; 499178354Ssam 500178354Ssam /* complete setup */ 501178354Ssam ieee80211_vap_attach(vap, ieee80211_media_change, ipw_media_status); 502178354Ssam ic->ic_opmode = opmode; 503178354Ssam return vap; 504178354Ssam} 505178354Ssam 506178354Ssamstatic void 507178354Ssamipw_vap_delete(struct ieee80211vap *vap) 508178354Ssam{ 509178354Ssam struct ipw_vap *ivp = IPW_VAP(vap); 510178354Ssam 511178354Ssam ieee80211_vap_detach(vap); 512178354Ssam free(ivp, M_80211_VAP); 513178354Ssam} 514178354Ssam 515178354Ssamstatic int 516178354Ssamipw_dma_alloc(struct ipw_softc *sc) 517178354Ssam{ 518178354Ssam struct ipw_soft_bd *sbd; 519178354Ssam struct ipw_soft_hdr *shdr; 520178354Ssam struct ipw_soft_buf *sbuf; 521178354Ssam bus_addr_t physaddr; 522178354Ssam int error, i; 523178354Ssam 524178354Ssam /* 525178354Ssam * Allocate parent DMA tag for subsequent allocations. 526178354Ssam */ 527178354Ssam error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, 528178354Ssam BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, 529178354Ssam BUS_SPACE_MAXSIZE_32BIT, BUS_SPACE_UNRESTRICTED, 530178354Ssam BUS_SPACE_MAXSIZE_32BIT, 0, NULL, NULL, &sc->parent_dmat); 531178354Ssam if (error != 0) { 532178354Ssam device_printf(sc->sc_dev, "could not create parent DMA tag\n"); 533178354Ssam goto fail; 534178354Ssam } 535178354Ssam 536178354Ssam /* 537178354Ssam * Allocate and map tx ring. 538283535Sadrian */ 539283535Sadrian error = bus_dma_tag_create(sc->parent_dmat, 4, 0, BUS_SPACE_MAXADDR_32BIT, 540178354Ssam BUS_SPACE_MAXADDR, NULL, NULL, IPW_TBD_SZ, 1, IPW_TBD_SZ, 0, NULL, 541178354Ssam NULL, &sc->tbd_dmat); 542178354Ssam if (error != 0) { 543178354Ssam device_printf(sc->sc_dev, "could not create tx ring DMA tag\n"); 544178354Ssam goto fail; 545178354Ssam } 546178354Ssam 547203422Srpaulo error = bus_dmamem_alloc(sc->tbd_dmat, (void **)&sc->tbd_list, 548178354Ssam BUS_DMA_NOWAIT | BUS_DMA_ZERO, &sc->tbd_map); 549178354Ssam if (error != 0) { 550178354Ssam device_printf(sc->sc_dev, 551183247Ssam "could not allocate tx ring DMA memory\n"); 552178354Ssam goto fail; 553178354Ssam } 554183247Ssam 555183247Ssam error = bus_dmamap_load(sc->tbd_dmat, sc->tbd_map, sc->tbd_list, 556183247Ssam IPW_TBD_SZ, ipw_dma_map_addr, &sc->tbd_phys, 0); 557183247Ssam if (error != 0) { 558183247Ssam device_printf(sc->sc_dev, "could not map tx ring DMA memory\n"); 559178354Ssam goto fail; 560178354Ssam } 561178354Ssam 562178354Ssam /* 563178354Ssam * Allocate and map rx ring. 564178354Ssam */ 565178354Ssam error = bus_dma_tag_create(sc->parent_dmat, 4, 0, BUS_SPACE_MAXADDR_32BIT, 566178354Ssam BUS_SPACE_MAXADDR, NULL, NULL, IPW_RBD_SZ, 1, IPW_RBD_SZ, 0, NULL, 567178354Ssam NULL, &sc->rbd_dmat); 568178354Ssam if (error != 0) { 569178354Ssam device_printf(sc->sc_dev, "could not create rx ring DMA tag\n"); 570178354Ssam goto fail; 571178354Ssam } 572178354Ssam 573178354Ssam error = bus_dmamem_alloc(sc->rbd_dmat, (void **)&sc->rbd_list, 574178354Ssam BUS_DMA_NOWAIT | BUS_DMA_ZERO, &sc->rbd_map); 575178354Ssam if (error != 0) { 576178354Ssam device_printf(sc->sc_dev, 577178354Ssam "could not allocate rx ring DMA memory\n"); 578178354Ssam goto fail; 579178354Ssam } 580178354Ssam 581178354Ssam error = bus_dmamap_load(sc->rbd_dmat, sc->rbd_map, sc->rbd_list, 582178354Ssam IPW_RBD_SZ, ipw_dma_map_addr, &sc->rbd_phys, 0); 583178354Ssam if (error != 0) { 584178354Ssam device_printf(sc->sc_dev, "could not map rx ring DMA memory\n"); 585178354Ssam goto fail; 586178354Ssam } 587178354Ssam 588178354Ssam /* 589178354Ssam * Allocate and map status ring. 590178354Ssam */ 591191547Ssam error = bus_dma_tag_create(sc->parent_dmat, 4, 0, BUS_SPACE_MAXADDR_32BIT, 592191547Ssam BUS_SPACE_MAXADDR, NULL, NULL, IPW_STATUS_SZ, 1, IPW_STATUS_SZ, 0, 593178354Ssam NULL, NULL, &sc->status_dmat); 594178354Ssam if (error != 0) { 595178354Ssam device_printf(sc->sc_dev, 596178354Ssam "could not create status ring DMA tag\n"); 597178354Ssam goto fail; 598178354Ssam } 599178354Ssam 600178354Ssam error = bus_dmamem_alloc(sc->status_dmat, (void **)&sc->status_list, 601178354Ssam BUS_DMA_NOWAIT | BUS_DMA_ZERO, &sc->status_map); 602178354Ssam if (error != 0) { 603178354Ssam device_printf(sc->sc_dev, 604178354Ssam "could not allocate status ring DMA memory\n"); 605178354Ssam goto fail; 606178354Ssam } 607178354Ssam 608178354Ssam error = bus_dmamap_load(sc->status_dmat, sc->status_map, 609227338Sadrian sc->status_list, IPW_STATUS_SZ, ipw_dma_map_addr, &sc->status_phys, 610227338Sadrian 0); 611227338Sadrian if (error != 0) { 612227338Sadrian device_printf(sc->sc_dev, 613227338Sadrian "could not map status ring DMA memory\n"); 614227338Sadrian goto fail; 615227338Sadrian } 616227338Sadrian 617227338Sadrian /* 618227338Sadrian * Allocate command DMA map. 619227338Sadrian */ 620227338Sadrian error = bus_dma_tag_create(sc->parent_dmat, 1, 0, BUS_SPACE_MAXADDR_32BIT, 621227338Sadrian BUS_SPACE_MAXADDR, NULL, NULL, sizeof (struct ipw_cmd), 1, 622227338Sadrian sizeof (struct ipw_cmd), 0, NULL, NULL, &sc->cmd_dmat); 623227338Sadrian if (error != 0) { 624227338Sadrian device_printf(sc->sc_dev, "could not create command DMA tag\n"); 625227338Sadrian goto fail; 626227338Sadrian } 627227338Sadrian 628227338Sadrian error = bus_dmamap_create(sc->cmd_dmat, 0, &sc->cmd_map); 629227338Sadrian if (error != 0) { 630227338Sadrian device_printf(sc->sc_dev, 631227338Sadrian "could not create command DMA map\n"); 632227338Sadrian goto fail; 633178354Ssam } 634192468Ssam 635282820Sadrian /* 636282820Sadrian * Allocate headers DMA maps. 637178354Ssam */ 638178354Ssam error = bus_dma_tag_create(sc->parent_dmat, 1, 0, BUS_SPACE_MAXADDR_32BIT, 639178354Ssam BUS_SPACE_MAXADDR, NULL, NULL, sizeof (struct ipw_hdr), 1, 640178354Ssam sizeof (struct ipw_hdr), 0, NULL, NULL, &sc->hdr_dmat); 641296254Savos if (error != 0) { 642178354Ssam device_printf(sc->sc_dev, "could not create header DMA tag\n"); 643178354Ssam goto fail; 644178354Ssam } 645178354Ssam 646178354Ssam SLIST_INIT(&sc->free_shdr); 647178354Ssam for (i = 0; i < IPW_NDATA; i++) { 648178354Ssam shdr = &sc->shdr_list[i]; 649178354Ssam error = bus_dmamap_create(sc->hdr_dmat, 0, &shdr->map); 650178354Ssam if (error != 0) { 651178354Ssam device_printf(sc->sc_dev, 652178354Ssam "could not create header DMA map\n"); 653178354Ssam goto fail; 654178354Ssam } 655178354Ssam SLIST_INSERT_HEAD(&sc->free_shdr, shdr, next); 656178354Ssam } 657178354Ssam 658183247Ssam /* 659183247Ssam * Allocate tx buffers DMA maps. 660178354Ssam */ 661178354Ssam error = bus_dma_tag_create(sc->parent_dmat, 1, 0, BUS_SPACE_MAXADDR_32BIT, 662178354Ssam BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, IPW_MAX_NSEG, MCLBYTES, 0, 663183247Ssam NULL, NULL, &sc->txbuf_dmat); 664178354Ssam if (error != 0) { 665178354Ssam device_printf(sc->sc_dev, "could not create tx DMA tag\n"); 666178354Ssam goto fail; 667178354Ssam } 668178354Ssam 669178354Ssam SLIST_INIT(&sc->free_sbuf); 670178354Ssam for (i = 0; i < IPW_NDATA; i++) { 671178354Ssam sbuf = &sc->tx_sbuf_list[i]; 672178354Ssam error = bus_dmamap_create(sc->txbuf_dmat, 0, &sbuf->map); 673178354Ssam if (error != 0) { 674178354Ssam device_printf(sc->sc_dev, 675178354Ssam "could not create tx DMA map\n"); 676178354Ssam goto fail; 677178354Ssam } 678178354Ssam SLIST_INSERT_HEAD(&sc->free_sbuf, sbuf, next); 679178354Ssam } 680178354Ssam 681178354Ssam /* 682178354Ssam * Initialize tx ring. 683178354Ssam */ 684178354Ssam for (i = 0; i < IPW_NTBD; i++) { 685178354Ssam sbd = &sc->stbd_list[i]; 686178354Ssam sbd->bd = &sc->tbd_list[i]; 687178354Ssam sbd->type = IPW_SBD_TYPE_NOASSOC; 688178354Ssam } 689178354Ssam 690178354Ssam /* 691178354Ssam * Pre-allocate rx buffers and DMA maps. 692178354Ssam */ 693178354Ssam error = bus_dma_tag_create(sc->parent_dmat, 1, 0, BUS_SPACE_MAXADDR_32BIT, 694178354Ssam BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, 1, MCLBYTES, 0, NULL, 695178354Ssam NULL, &sc->rxbuf_dmat); 696178354Ssam if (error != 0) { 697178354Ssam device_printf(sc->sc_dev, "could not create rx DMA tag\n"); 698178354Ssam goto fail; 699178354Ssam } 700178354Ssam 701178354Ssam for (i = 0; i < IPW_NRBD; i++) { 702178354Ssam sbd = &sc->srbd_list[i]; 703178354Ssam sbuf = &sc->rx_sbuf_list[i]; 704178354Ssam sbd->bd = &sc->rbd_list[i]; 705178354Ssam 706178354Ssam sbuf->m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); 707178354Ssam if (sbuf->m == NULL) { 708178354Ssam device_printf(sc->sc_dev, 709178354Ssam "could not allocate rx mbuf\n"); 710178354Ssam error = ENOMEM; 711178354Ssam goto fail; 712178354Ssam } 713178354Ssam 714178354Ssam error = bus_dmamap_create(sc->rxbuf_dmat, 0, &sbuf->map); 715178354Ssam if (error != 0) { 716178354Ssam device_printf(sc->sc_dev, 717178354Ssam "could not create rx DMA map\n"); 718178354Ssam goto fail; 719178354Ssam } 720178354Ssam 721178354Ssam error = bus_dmamap_load(sc->rxbuf_dmat, sbuf->map, 722178354Ssam mtod(sbuf->m, void *), MCLBYTES, ipw_dma_map_addr, 723178354Ssam &physaddr, 0); 724178354Ssam if (error != 0) { 725178354Ssam device_printf(sc->sc_dev, 726178354Ssam "could not map rx DMA memory\n"); 727178354Ssam goto fail; 728178354Ssam } 729178354Ssam 730178354Ssam sbd->type = IPW_SBD_TYPE_DATA; 731178354Ssam sbd->priv = sbuf; 732178354Ssam sbd->bd->physaddr = htole32(physaddr); 733178354Ssam sbd->bd->len = htole32(MCLBYTES); 734178354Ssam } 735260444Skevlo 736178354Ssam bus_dmamap_sync(sc->rbd_dmat, sc->rbd_map, BUS_DMASYNC_PREWRITE); 737178354Ssam 738178354Ssam return 0; 739178354Ssam 740178354Ssamfail: ipw_release(sc); 741178354Ssam return error; 742178354Ssam} 743178354Ssam 744178354Ssamstatic void 745178354Ssamipw_release(struct ipw_softc *sc) 746178354Ssam{ 747178354Ssam struct ipw_soft_buf *sbuf; 748178354Ssam int i; 749178354Ssam 750178354Ssam if (sc->parent_dmat != NULL) { 751178354Ssam bus_dma_tag_destroy(sc->parent_dmat); 752178354Ssam } 753260444Skevlo 754178354Ssam if (sc->tbd_dmat != NULL) { 755178354Ssam if (sc->stbd_list != NULL) { 756178354Ssam bus_dmamap_unload(sc->tbd_dmat, sc->tbd_map); 757178354Ssam bus_dmamem_free(sc->tbd_dmat, sc->tbd_list, 758178354Ssam sc->tbd_map); 759178354Ssam } 760178354Ssam bus_dma_tag_destroy(sc->tbd_dmat); 761178354Ssam } 762178354Ssam 763178354Ssam if (sc->rbd_dmat != NULL) { 764178354Ssam if (sc->rbd_list != NULL) { 765178354Ssam bus_dmamap_unload(sc->rbd_dmat, sc->rbd_map); 766178354Ssam bus_dmamem_free(sc->rbd_dmat, sc->rbd_list, 767178354Ssam sc->rbd_map); 768178354Ssam } 769178354Ssam bus_dma_tag_destroy(sc->rbd_dmat); 770178354Ssam } 771178354Ssam 772178354Ssam if (sc->status_dmat != NULL) { 773178354Ssam if (sc->status_list != NULL) { 774178354Ssam bus_dmamap_unload(sc->status_dmat, sc->status_map); 775178354Ssam bus_dmamem_free(sc->status_dmat, sc->status_list, 776178354Ssam sc->status_map); 777178354Ssam } 778178354Ssam bus_dma_tag_destroy(sc->status_dmat); 779178354Ssam } 780178354Ssam 781178354Ssam for (i = 0; i < IPW_NTBD; i++) 782178354Ssam ipw_release_sbd(sc, &sc->stbd_list[i]); 783178354Ssam 784178354Ssam if (sc->cmd_dmat != NULL) { 785178354Ssam bus_dmamap_destroy(sc->cmd_dmat, sc->cmd_map); 786178354Ssam bus_dma_tag_destroy(sc->cmd_dmat); 787178354Ssam } 788178354Ssam 789178354Ssam if (sc->hdr_dmat != NULL) { 790178354Ssam for (i = 0; i < IPW_NDATA; i++) 791178354Ssam bus_dmamap_destroy(sc->hdr_dmat, sc->shdr_list[i].map); 792178354Ssam bus_dma_tag_destroy(sc->hdr_dmat); 793192468Ssam } 794202967Srpaulo 795178354Ssam if (sc->txbuf_dmat != NULL) { 796178354Ssam for (i = 0; i < IPW_NDATA; i++) { 797178354Ssam bus_dmamap_destroy(sc->txbuf_dmat, 798178354Ssam sc->tx_sbuf_list[i].map); 799178354Ssam } 800178354Ssam bus_dma_tag_destroy(sc->txbuf_dmat); 801178354Ssam } 802178354Ssam 803178354Ssam if (sc->rxbuf_dmat != NULL) { 804178354Ssam for (i = 0; i < IPW_NRBD; i++) { 805178354Ssam sbuf = &sc->rx_sbuf_list[i]; 806178354Ssam if (sbuf->m != NULL) { 807178354Ssam bus_dmamap_sync(sc->rxbuf_dmat, sbuf->map, 808178354Ssam BUS_DMASYNC_POSTREAD); 809178354Ssam bus_dmamap_unload(sc->rxbuf_dmat, sbuf->map); 810178354Ssam m_freem(sbuf->m); 811178354Ssam } 812178354Ssam bus_dmamap_destroy(sc->rxbuf_dmat, sbuf->map); 813178354Ssam } 814178354Ssam bus_dma_tag_destroy(sc->rxbuf_dmat); 815178354Ssam } 816178354Ssam} 817178354Ssam 818178354Ssamstatic int 819178354Ssamipw_shutdown(device_t dev) 820178354Ssam{ 821178354Ssam struct ipw_softc *sc = device_get_softc(dev); 822178354Ssam 823178354Ssam ipw_stop(sc); 824178354Ssam 825178354Ssam return 0; 826178354Ssam} 827178354Ssam 828178354Ssamstatic int 829178354Ssamipw_suspend(device_t dev) 830178354Ssam{ 831178354Ssam struct ipw_softc *sc = device_get_softc(dev); 832178354Ssam struct ieee80211com *ic = sc->sc_ifp->if_l2com; 833178354Ssam 834178354Ssam ieee80211_suspend_all(ic); 835178354Ssam return 0; 836178354Ssam} 837178354Ssam 838178354Ssamstatic int 839178354Ssamipw_resume(device_t dev) 840178354Ssam{ 841178354Ssam struct ipw_softc *sc = device_get_softc(dev); 842178354Ssam struct ieee80211com *ic = sc->sc_ifp->if_l2com; 843178354Ssam 844178354Ssam pci_write_config(dev, 0x41, 0, 1); 845178354Ssam 846178354Ssam ieee80211_resume_all(ic); 847178354Ssam return 0; 848178354Ssam} 849178354Ssam 850178354Ssamstatic int 851178354Ssamipw_cvtrate(int ipwrate) 852178354Ssam{ 853190391Ssam switch (ipwrate) { 854190391Ssam case IPW_RATE_DS1: return 2; 855190391Ssam case IPW_RATE_DS2: return 4; 856190391Ssam case IPW_RATE_DS5: return 11; 857178354Ssam case IPW_RATE_DS11: return 22; 858190391Ssam } 859178354Ssam return 0; 860178354Ssam} 861178354Ssam 862178354Ssam/* 863178354Ssam * The firmware automatically adapts the transmit speed. We report its current 864178354Ssam * value here. 865178354Ssam */ 866178354Ssamstatic void 867178354Ssamipw_media_status(struct ifnet *ifp, struct ifmediareq *imr) 868178354Ssam{ 869178354Ssam struct ieee80211vap *vap = ifp->if_softc; 870178354Ssam struct ieee80211com *ic = vap->iv_ic; 871178354Ssam struct ipw_softc *sc = ic->ic_ifp->if_softc; 872178354Ssam 873178354Ssam /* read current transmission rate from adapter */ 874178354Ssam vap->iv_bss->ni_txrate = ipw_cvtrate( 875178354Ssam ipw_read_table1(sc, IPW_INFO_CURRENT_TX_RATE) & 0xf); 876178354Ssam ieee80211_media_status(ifp, imr); 877178354Ssam} 878178354Ssam 879178354Ssamstatic int 880178354Ssamipw_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) 881178354Ssam{ 882178354Ssam struct ipw_vap *ivp = IPW_VAP(vap); 883298376Savos struct ieee80211com *ic = vap->iv_ic; 884178354Ssam struct ifnet *ifp = ic->ic_ifp; 885178354Ssam struct ipw_softc *sc = ifp->if_softc; 886178354Ssam enum ieee80211_state ostate; 887260444Skevlo 888178354Ssam DPRINTF(("%s: %s -> %s flags 0x%x\n", __func__, 889178354Ssam ieee80211_state_name[vap->iv_state], 890178354Ssam ieee80211_state_name[nstate], sc->flags)); 891178354Ssam 892178354Ssam ostate = vap->iv_state; 893178354Ssam IEEE80211_UNLOCK(ic); 894298376Savos 895178354Ssam switch (nstate) { 896178354Ssam case IEEE80211_S_RUN: 897178354Ssam if (ic->ic_opmode == IEEE80211_M_IBSS) { 898178354Ssam /* 899178354Ssam * XXX when joining an ibss network we are called 900178354Ssam * with a SCAN -> RUN transition on scan complete. 901178354Ssam * Use that to call ipw_assoc. On completing the 902178354Ssam * join we are then called again with an AUTH -> RUN 903178354Ssam * transition and we want to do nothing. This is 904178354Ssam * all totally bogus and needs to be redone. 905178354Ssam */ 906178354Ssam if (ostate == IEEE80211_S_SCAN) 907178354Ssam ipw_assoc(ic, vap); 908178354Ssam } 909178354Ssam break; 910178354Ssam 911178354Ssam case IEEE80211_S_INIT: 912178354Ssam if (sc->flags & IPW_FLAG_ASSOCIATED) 913178354Ssam ipw_disassoc(ic, vap); 914178354Ssam break; 915260444Skevlo 916178354Ssam case IEEE80211_S_AUTH: 917283535Sadrian /* 918191534Ssam * Move to ASSOC state after the ipw_assoc() call. Firmware 919178354Ssam * takes care of authentication, after the call we'll receive 920178354Ssam * only an assoc response which would otherwise be discared 921178354Ssam * if we are still in AUTH state. 922178354Ssam */ 923191546Ssam nstate = IEEE80211_S_ASSOC; 924178354Ssam ipw_assoc(ic, vap); 925191549Ssam break; 926178354Ssam 927178354Ssam case IEEE80211_S_ASSOC: 928178354Ssam /* 929178354Ssam * If we are not transitioning from AUTH then resend the 930178354Ssam * association request. 931178354Ssam */ 932178354Ssam if (ostate != IEEE80211_S_AUTH) 933271861Sglebius ipw_assoc(ic, vap); 934178354Ssam break; 935178354Ssam 936192765Ssam default: 937192468Ssam break; 938178354Ssam } 939178354Ssam IEEE80211_LOCK(ic); 940178354Ssam return ivp->newstate(vap, nstate, arg); 941178354Ssam} 942178354Ssam 943178354Ssam/* 944178354Ssam * Read 16 bits at address 'addr' from the serial EEPROM. 945192468Ssam */ 946178354Ssamstatic uint16_t 947178354Ssamipw_read_prom_word(struct ipw_softc *sc, uint8_t addr) 948178354Ssam{ 949178354Ssam uint32_t tmp; 950178354Ssam uint16_t val; 951178354Ssam int n; 952178354Ssam 953178354Ssam /* clock C once before the first command */ 954178354Ssam IPW_EEPROM_CTL(sc, 0); 955178354Ssam IPW_EEPROM_CTL(sc, IPW_EEPROM_S); 956178354Ssam IPW_EEPROM_CTL(sc, IPW_EEPROM_S | IPW_EEPROM_C); 957178354Ssam IPW_EEPROM_CTL(sc, IPW_EEPROM_S); 958178354Ssam 959178354Ssam /* write start bit (1) */ 960178354Ssam IPW_EEPROM_CTL(sc, IPW_EEPROM_S | IPW_EEPROM_D); 961178354Ssam IPW_EEPROM_CTL(sc, IPW_EEPROM_S | IPW_EEPROM_D | IPW_EEPROM_C); 962178354Ssam 963178354Ssam /* write READ opcode (10) */ 964178354Ssam IPW_EEPROM_CTL(sc, IPW_EEPROM_S | IPW_EEPROM_D); 965178354Ssam IPW_EEPROM_CTL(sc, IPW_EEPROM_S | IPW_EEPROM_D | IPW_EEPROM_C); 966178354Ssam IPW_EEPROM_CTL(sc, IPW_EEPROM_S); 967178354Ssam IPW_EEPROM_CTL(sc, IPW_EEPROM_S | IPW_EEPROM_C); 968178354Ssam 969178354Ssam /* write address A7-A0 */ 970178354Ssam for (n = 7; n >= 0; n--) { 971178354Ssam IPW_EEPROM_CTL(sc, IPW_EEPROM_S | 972178354Ssam (((addr >> n) & 1) << IPW_EEPROM_SHIFT_D)); 973178354Ssam IPW_EEPROM_CTL(sc, IPW_EEPROM_S | 974192468Ssam (((addr >> n) & 1) << IPW_EEPROM_SHIFT_D) | IPW_EEPROM_C); 975178354Ssam } 976178354Ssam 977178354Ssam IPW_EEPROM_CTL(sc, IPW_EEPROM_S); 978178354Ssam 979178354Ssam /* read data Q15-Q0 */ 980178354Ssam val = 0; 981178354Ssam for (n = 15; n >= 0; n--) { 982178354Ssam IPW_EEPROM_CTL(sc, IPW_EEPROM_S | IPW_EEPROM_C); 983178354Ssam IPW_EEPROM_CTL(sc, IPW_EEPROM_S); 984178354Ssam tmp = MEM_READ_4(sc, IPW_MEM_EEPROM_CTL); 985178354Ssam val |= ((tmp & IPW_EEPROM_Q) >> IPW_EEPROM_SHIFT_Q) << n; 986178354Ssam } 987178354Ssam 988178354Ssam IPW_EEPROM_CTL(sc, 0); 989178354Ssam 990178354Ssam /* clear Chip Select and clock C */ 991178354Ssam IPW_EEPROM_CTL(sc, IPW_EEPROM_S); 992178354Ssam IPW_EEPROM_CTL(sc, 0); 993178354Ssam IPW_EEPROM_CTL(sc, IPW_EEPROM_C); 994178354Ssam 995178354Ssam return le16toh(val); 996178354Ssam} 997178354Ssam 998178354Ssamstatic void 999178354Ssamipw_rx_cmd_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf) 1000178354Ssam{ 1001178354Ssam struct ipw_cmd *cmd; 1002178354Ssam 1003178354Ssam bus_dmamap_sync(sc->rxbuf_dmat, sbuf->map, BUS_DMASYNC_POSTREAD); 1004178354Ssam 1005178354Ssam cmd = mtod(sbuf->m, struct ipw_cmd *); 1006178354Ssam 1007178354Ssam DPRINTFN(9, ("cmd ack'ed %s(%u, %u, %u, %u, %u)\n", 1008178354Ssam ipw_cmdname(le32toh(cmd->type)), le32toh(cmd->type), 1009178354Ssam le32toh(cmd->subtype), le32toh(cmd->seq), le32toh(cmd->len), 1010178354Ssam le32toh(cmd->status))); 1011178354Ssam 1012178354Ssam sc->flags &= ~IPW_FLAG_BUSY; 1013178354Ssam wakeup(sc); 1014178354Ssam} 1015178354Ssam 1016178354Ssamstatic void 1017178354Ssamipw_rx_newstate_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf) 1018178354Ssam{ 1019178354Ssam#define IEEESTATE(vap) ieee80211_state_name[vap->iv_state] 1020178354Ssam struct ifnet *ifp = sc->sc_ifp; 1021178354Ssam struct ieee80211com *ic = ifp->if_l2com; 1022178354Ssam struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 1023178354Ssam uint32_t state; 1024178354Ssam 1025178354Ssam bus_dmamap_sync(sc->rxbuf_dmat, sbuf->map, BUS_DMASYNC_POSTREAD); 1026178354Ssam 1027178354Ssam state = le32toh(*mtod(sbuf->m, uint32_t *)); 1028178354Ssam 1029178354Ssam switch (state) { 1030178354Ssam case IPW_STATE_ASSOCIATED: 1031178354Ssam DPRINTFN(2, ("Association succeeded (%s flags 0x%x)\n", 1032178354Ssam IEEESTATE(vap), sc->flags)); 1033178354Ssam /* XXX suppress state change in case the fw auto-associates */ 1034178354Ssam if ((sc->flags & IPW_FLAG_ASSOCIATING) == 0) { 1035178354Ssam DPRINTF(("Unexpected association (%s, flags 0x%x)\n", 1036178354Ssam IEEESTATE(vap), sc->flags)); 1037178354Ssam break; 1038178354Ssam } 1039178354Ssam sc->flags &= ~IPW_FLAG_ASSOCIATING; 1040178354Ssam sc->flags |= IPW_FLAG_ASSOCIATED; 1041178354Ssam break; 1042178354Ssam 1043178354Ssam case IPW_STATE_SCANNING: 1044178354Ssam DPRINTFN(3, ("Scanning (%s flags 0x%x)\n", 1045178354Ssam IEEESTATE(vap), sc->flags)); 1046178354Ssam /* 1047178354Ssam * NB: Check driver state for association on assoc 1048178354Ssam * loss as the firmware will immediately start to 1049178354Ssam * scan and we would treat it as a beacon miss if 1050178354Ssam * we checked the 802.11 layer state. 1051178354Ssam */ 1052178354Ssam if (sc->flags & IPW_FLAG_ASSOCIATED) { 1053283538Sadrian IPW_UNLOCK(sc); 1054178354Ssam /* XXX probably need to issue disassoc to fw */ 1055178354Ssam ieee80211_beacon_miss(ic); 1056178354Ssam IPW_LOCK(sc); 1057178354Ssam } 1058178354Ssam break; 1059178354Ssam 1060178354Ssam case IPW_STATE_SCAN_COMPLETE: 1061178354Ssam /* 1062178354Ssam * XXX For some reason scan requests generate scan 1063178354Ssam * started + scan done events before any traffic is 1064178354Ssam * received (e.g. probe response frames). We work 1065178354Ssam * around this by marking the HACK flag and skipping 1066178354Ssam * the first scan complete event. 1067178354Ssam */ 1068178354Ssam DPRINTFN(3, ("Scan complete (%s flags 0x%x)\n", 1069178354Ssam IEEESTATE(vap), sc->flags)); 1070178354Ssam if (sc->flags & IPW_FLAG_HACK) { 1071178354Ssam sc->flags &= ~IPW_FLAG_HACK; 1072178354Ssam break; 1073178354Ssam } 1074178354Ssam if (sc->flags & IPW_FLAG_SCANNING) { 1075178354Ssam IPW_UNLOCK(sc); 1076178354Ssam ieee80211_scan_done(vap); 1077178354Ssam IPW_LOCK(sc); 1078178354Ssam sc->flags &= ~IPW_FLAG_SCANNING; 1079178354Ssam sc->sc_scan_timer = 0; 1080178354Ssam } 1081178354Ssam break; 1082178354Ssam 1083178354Ssam case IPW_STATE_ASSOCIATION_LOST: 1084178354Ssam DPRINTFN(2, ("Association lost (%s flags 0x%x)\n", 1085178354Ssam IEEESTATE(vap), sc->flags)); 1086178354Ssam sc->flags &= ~(IPW_FLAG_ASSOCIATING | IPW_FLAG_ASSOCIATED); 1087178354Ssam if (vap->iv_state == IEEE80211_S_RUN) { 1088178354Ssam IPW_UNLOCK(sc); 1089178354Ssam ieee80211_new_state(vap, IEEE80211_S_SCAN, -1); 1090178354Ssam IPW_LOCK(sc); 1091178354Ssam } 1092244060Sadrian break; 1093178354Ssam 1094178354Ssam case IPW_STATE_DISABLED: 1095178354Ssam /* XXX? is this right? */ 1096178354Ssam sc->flags &= ~(IPW_FLAG_HACK | IPW_FLAG_SCANNING | 1097178354Ssam IPW_FLAG_ASSOCIATING | IPW_FLAG_ASSOCIATED); 1098178354Ssam DPRINTFN(2, ("Firmware disabled (%s flags 0x%x)\n", 1099178354Ssam IEEESTATE(vap), sc->flags)); 1100178354Ssam break; 1101178354Ssam 1102178354Ssam case IPW_STATE_RADIO_DISABLED: 1103178354Ssam device_printf(sc->sc_dev, "radio turned off\n"); 1104178354Ssam ieee80211_notify_radio(ic, 0); 1105178354Ssam ipw_stop_locked(sc); 1106178354Ssam /* XXX start polling thread to detect radio on */ 1107178354Ssam break; 1108178354Ssam 1109178354Ssam default: 1110178354Ssam DPRINTFN(2, ("%s: unhandled state %u %s flags 0x%x\n", 1111178354Ssam __func__, state, IEEESTATE(vap), sc->flags)); 1112178354Ssam break; 1113178354Ssam } 1114178354Ssam#undef IEEESTATE 1115178354Ssam} 1116178354Ssam 1117178354Ssam/* 1118178354Ssam * Set driver state for current channel. 1119178354Ssam */ 1120178354Ssamstatic void 1121298359Savosipw_setcurchan(struct ipw_softc *sc, struct ieee80211_channel *chan) 1122178354Ssam{ 1123178354Ssam struct ifnet *ifp = sc->sc_ifp; 1124178354Ssam struct ieee80211com *ic = ifp->if_l2com; 1125178354Ssam 1126178354Ssam ic->ic_curchan = chan; 1127178354Ssam ieee80211_radiotap_chan_change(ic); 1128178354Ssam} 1129178354Ssam 1130193439Ssam/* 1131193439Ssam * XXX: Hack to set the current channel to the value advertised in beacons or 1132193439Ssam * probe responses. Only used during AP detection. 1133193439Ssam */ 1134193439Ssamstatic void 1135193439Ssamipw_fix_channel(struct ipw_softc *sc, struct mbuf *m) 1136193439Ssam{ 1137193439Ssam struct ifnet *ifp = sc->sc_ifp; 1138193439Ssam struct ieee80211com *ic = ifp->if_l2com; 1139193439Ssam struct ieee80211_channel *c; 1140193439Ssam struct ieee80211_frame *wh; 1141193439Ssam uint8_t subtype; 1142193439Ssam uint8_t *frm, *efrm; 1143193439Ssam 1144193439Ssam wh = mtod(m, struct ieee80211_frame *); 1145193439Ssam 1146193439Ssam if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_MGT) 1147193439Ssam return; 1148193439Ssam 1149193439Ssam subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; 1150193439Ssam 1151193439Ssam if (subtype != IEEE80211_FC0_SUBTYPE_BEACON && 1152193439Ssam subtype != IEEE80211_FC0_SUBTYPE_PROBE_RESP) 1153193439Ssam return; 1154193439Ssam 1155193439Ssam /* XXX use ieee80211_parse_beacon */ 1156193439Ssam frm = (uint8_t *)(wh + 1); 1157193439Ssam efrm = mtod(m, uint8_t *) + m->m_len; 1158193439Ssam 1159193439Ssam frm += 12; /* skip tstamp, bintval and capinfo fields */ 1160193439Ssam while (frm < efrm) { 1161193439Ssam if (*frm == IEEE80211_ELEMID_DSPARMS) 1162193439Ssam#if IEEE80211_CHAN_MAX < 255 1163193439Ssam if (frm[2] <= IEEE80211_CHAN_MAX) 1164193439Ssam#endif 1165193439Ssam { 1166193439Ssam DPRINTF(("Fixing channel to %d\n", frm[2])); 1167193439Ssam c = ieee80211_find_channel(ic, 1168193439Ssam ieee80211_ieee2mhz(frm[2], 0), 1169193439Ssam IEEE80211_CHAN_B); 1170193439Ssam if (c == NULL) 1171193439Ssam c = &ic->ic_channels[0]; 1172193439Ssam ipw_setcurchan(sc, c); 1173193439Ssam } 1174193439Ssam 1175193439Ssam frm += frm[1] + 2; 1176193439Ssam } 1177193439Ssam} 1178193439Ssam 1179193439Ssamstatic void 1180193439Ssamipw_rx_data_intr(struct ipw_softc *sc, struct ipw_status *status, 1181193439Ssam struct ipw_soft_bd *sbd, struct ipw_soft_buf *sbuf) 1182193439Ssam{ 1183193439Ssam struct ifnet *ifp = sc->sc_ifp; 1184193439Ssam struct ieee80211com *ic = ifp->if_l2com; 1185193439Ssam struct mbuf *mnew, *m; 1186193439Ssam struct ieee80211_node *ni; 1187193439Ssam bus_addr_t physaddr; 1188193439Ssam int error; 1189193439Ssam int8_t rssi, nf; 1190193439Ssam 1191193439Ssam DPRINTFN(5, ("received frame len=%u, rssi=%u\n", le32toh(status->len), 1192193439Ssam status->rssi)); 1193193439Ssam 1194193439Ssam if (le32toh(status->len) < sizeof (struct ieee80211_frame_min) || 1195193439Ssam le32toh(status->len) > MCLBYTES) 1196193439Ssam return; 1197193439Ssam 1198193439Ssam /* 1199193439Ssam * Try to allocate a new mbuf for this ring element and load it before 1200193439Ssam * processing the current mbuf. If the ring element cannot be loaded, 1201193439Ssam * drop the received packet and reuse the old mbuf. In the unlikely 1202193439Ssam * case that the old mbuf can't be reloaded either, explicitly panic. 1203193439Ssam */ 1204193439Ssam mnew = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); 1205193439Ssam if (mnew == NULL) { 1206193439Ssam if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 1207193439Ssam return; 1208193439Ssam } 1209193439Ssam 1210193439Ssam bus_dmamap_sync(sc->rxbuf_dmat, sbuf->map, BUS_DMASYNC_POSTREAD); 1211193439Ssam bus_dmamap_unload(sc->rxbuf_dmat, sbuf->map); 1212193439Ssam 1213193439Ssam error = bus_dmamap_load(sc->rxbuf_dmat, sbuf->map, mtod(mnew, void *), 1214193439Ssam MCLBYTES, ipw_dma_map_addr, &physaddr, 0); 1215193439Ssam if (error != 0) { 1216193439Ssam m_freem(mnew); 1217193439Ssam 1218193439Ssam /* try to reload the old mbuf */ 1219193439Ssam error = bus_dmamap_load(sc->rxbuf_dmat, sbuf->map, 1220193439Ssam mtod(sbuf->m, void *), MCLBYTES, ipw_dma_map_addr, 1221193439Ssam &physaddr, 0); 1222193439Ssam if (error != 0) { 1223193439Ssam /* very unlikely that it will fail... */ 1224193439Ssam panic("%s: could not load old rx mbuf", 1225193439Ssam device_get_name(sc->sc_dev)); 1226193439Ssam } 1227193439Ssam if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 1228193439Ssam return; 1229193439Ssam } 1230193439Ssam 1231193439Ssam /* 1232193439Ssam * New mbuf successfully loaded, update Rx ring and continue 1233193439Ssam * processing. 1234193439Ssam */ 1235193439Ssam m = sbuf->m; 1236178354Ssam sbuf->m = mnew; 1237178354Ssam sbd->bd->physaddr = htole32(physaddr); 1238178354Ssam 1239178354Ssam /* finalize mbuf */ 1240178354Ssam m->m_pkthdr.rcvif = ifp; 1241178354Ssam m->m_pkthdr.len = m->m_len = le32toh(status->len); 1242178354Ssam 1243178354Ssam rssi = status->rssi + IPW_RSSI_TO_DBM; 1244178354Ssam nf = -95; 1245178354Ssam if (ieee80211_radiotap_active(ic)) { 1246178354Ssam struct ipw_rx_radiotap_header *tap = &sc->sc_rxtap; 1247178354Ssam 1248178354Ssam tap->wr_flags = 0; 1249178354Ssam tap->wr_antsignal = rssi; 1250178354Ssam tap->wr_antnoise = nf; 1251178354Ssam } 1252178354Ssam 1253178354Ssam if (sc->flags & IPW_FLAG_SCANNING) 1254297405Sadrian ipw_fix_channel(sc, m); 1255178354Ssam 1256178354Ssam IPW_UNLOCK(sc); 1257178354Ssam ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *)); 1258178354Ssam if (ni != NULL) { 1259178354Ssam (void) ieee80211_input(ni, m, rssi - nf, nf); 1260178354Ssam ieee80211_free_node(ni); 1261178354Ssam } else 1262178354Ssam (void) ieee80211_input_all(ic, m, rssi - nf, nf); 1263178354Ssam IPW_LOCK(sc); 1264178354Ssam 1265178354Ssam bus_dmamap_sync(sc->rbd_dmat, sc->rbd_map, BUS_DMASYNC_PREWRITE); 1266178354Ssam} 1267178354Ssam 1268178354Ssamstatic void 1269178354Ssamipw_rx_intr(struct ipw_softc *sc) 1270178354Ssam{ 1271178354Ssam struct ipw_status *status; 1272190391Ssam struct ipw_soft_bd *sbd; 1273178354Ssam struct ipw_soft_buf *sbuf; 1274190391Ssam uint32_t r, i; 1275297405Sadrian 1276297405Sadrian if (!(sc->flags & IPW_FLAG_FW_INITED)) 1277178354Ssam return; 1278178354Ssam 1279178354Ssam r = CSR_READ_4(sc, IPW_CSR_RX_READ); 1280283535Sadrian 1281283535Sadrian bus_dmamap_sync(sc->status_dmat, sc->status_map, BUS_DMASYNC_POSTREAD); 1282283535Sadrian 1283178354Ssam for (i = (sc->rxcur + 1) % IPW_NRBD; i != r; i = (i + 1) % IPW_NRBD) { 1284178354Ssam status = &sc->status_list[i]; 1285178354Ssam sbd = &sc->srbd_list[i]; 1286178354Ssam sbuf = sbd->priv; 1287178354Ssam 1288283535Sadrian switch (le16toh(status->code) & 0xf) { 1289178354Ssam case IPW_STATUS_CODE_COMMAND: 1290178354Ssam ipw_rx_cmd_intr(sc, sbuf); 1291178354Ssam break; 1292178354Ssam 1293233452Sadrian case IPW_STATUS_CODE_NEWSTATE: 1294178354Ssam ipw_rx_newstate_intr(sc, sbuf); 1295178354Ssam break; 1296178354Ssam 1297178354Ssam case IPW_STATUS_CODE_DATA_802_3: 1298178354Ssam case IPW_STATUS_CODE_DATA_802_11: 1299178354Ssam ipw_rx_data_intr(sc, status, sbd, sbuf); 1300178354Ssam break; 1301178354Ssam 1302283535Sadrian case IPW_STATUS_CODE_NOTIFICATION: 1303178354Ssam DPRINTFN(2, ("notification status, len %u flags 0x%x\n", 1304178354Ssam le32toh(status->len), status->flags)); 1305178354Ssam /* XXX maybe drive state machine AUTH->ASSOC? */ 1306178354Ssam break; 1307191444Srpaulo 1308178354Ssam default: 1309178354Ssam device_printf(sc->sc_dev, "unexpected status code %u\n", 1310178354Ssam le16toh(status->code)); 1311178354Ssam } 1312178354Ssam 1313178354Ssam /* firmware was killed, stop processing received frames */ 1314283535Sadrian if (!(sc->flags & IPW_FLAG_FW_INITED)) 1315283535Sadrian return; 1316283535Sadrian 1317283535Sadrian sbd->bd->flags = 0; 1318283535Sadrian } 1319283535Sadrian 1320283535Sadrian bus_dmamap_sync(sc->rbd_dmat, sc->rbd_map, BUS_DMASYNC_PREWRITE); 1321283535Sadrian 1322178354Ssam /* kick the firmware */ 1323283535Sadrian sc->rxcur = (r == 0) ? IPW_NRBD - 1 : r - 1; 1324232270Sadrian CSR_WRITE_4(sc, IPW_CSR_RX_WRITE, sc->rxcur); 1325232270Sadrian} 1326178354Ssam 1327232244Sadrianstatic void 1328264855Sadrianipw_release_sbd(struct ipw_softc *sc, struct ipw_soft_bd *sbd) 1329178354Ssam{ 1330178354Ssam struct ipw_soft_hdr *shdr; 1331178354Ssam struct ipw_soft_buf *sbuf; 1332178354Ssam 1333178354Ssam switch (sbd->type) { 1334178354Ssam case IPW_SBD_TYPE_COMMAND: 1335178354Ssam bus_dmamap_sync(sc->cmd_dmat, sc->cmd_map, 1336178354Ssam BUS_DMASYNC_POSTWRITE); 1337178354Ssam bus_dmamap_unload(sc->cmd_dmat, sc->cmd_map); 1338178354Ssam break; 1339178354Ssam 1340178354Ssam case IPW_SBD_TYPE_HEADER: 1341178354Ssam shdr = sbd->priv; 1342178354Ssam bus_dmamap_sync(sc->hdr_dmat, shdr->map, BUS_DMASYNC_POSTWRITE); 1343178354Ssam bus_dmamap_unload(sc->hdr_dmat, shdr->map); 1344178354Ssam SLIST_INSERT_HEAD(&sc->free_shdr, shdr, next); 1345178354Ssam break; 1346178354Ssam 1347178354Ssam case IPW_SBD_TYPE_DATA: 1348178354Ssam sbuf = sbd->priv; 1349178354Ssam bus_dmamap_sync(sc->txbuf_dmat, sbuf->map, 1350178354Ssam BUS_DMASYNC_POSTWRITE); 1351178354Ssam bus_dmamap_unload(sc->txbuf_dmat, sbuf->map); 1352178354Ssam SLIST_INSERT_HEAD(&sc->free_sbuf, sbuf, next); 1353178354Ssam 1354178354Ssam if (sbuf->m->m_flags & M_TXCB) 1355178354Ssam ieee80211_process_callback(sbuf->ni, sbuf->m, 0/*XXX*/); 1356178354Ssam m_freem(sbuf->m); 1357178354Ssam ieee80211_free_node(sbuf->ni); 1358178354Ssam 1359178354Ssam sc->sc_tx_timer = 0; 1360178354Ssam break; 1361178354Ssam } 1362178354Ssam 1363178354Ssam sbd->type = IPW_SBD_TYPE_NOASSOC; 1364178354Ssam} 1365178354Ssam 1366178354Ssamstatic void 1367178354Ssamipw_tx_intr(struct ipw_softc *sc) 1368178354Ssam{ 1369178354Ssam struct ifnet *ifp = sc->sc_ifp; 1370178354Ssam struct ipw_soft_bd *sbd; 1371178354Ssam uint32_t r, i; 1372178354Ssam 1373178354Ssam if (!(sc->flags & IPW_FLAG_FW_INITED)) 1374178354Ssam return; 1375178354Ssam 1376178354Ssam r = CSR_READ_4(sc, IPW_CSR_TX_READ); 1377178354Ssam 1378178354Ssam for (i = (sc->txold + 1) % IPW_NTBD; i != r; i = (i + 1) % IPW_NTBD) { 1379178354Ssam sbd = &sc->stbd_list[i]; 1380178354Ssam 1381178354Ssam if (sbd->type == IPW_SBD_TYPE_DATA) 1382178354Ssam if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); 1383178354Ssam 1384178354Ssam ipw_release_sbd(sc, sbd); 1385190391Ssam sc->txfree++; 1386178354Ssam } 1387178354Ssam 1388190391Ssam /* remember what the firmware has processed */ 1389183254Ssam sc->txold = (r == 0) ? IPW_NTBD - 1 : r - 1; 1390193655Ssam 1391178354Ssam ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 1392233452Sadrian ipw_start_locked(ifp); 1393233452Sadrian} 1394233452Sadrian 1395178354Ssamstatic void 1396227331Sadrianipw_fatal_error_intr(struct ipw_softc *sc) 1397227331Sadrian{ 1398264855Sadrian struct ifnet *ifp = sc->sc_ifp; 1399178354Ssam struct ieee80211com *ic = ifp->if_l2com; 1400178354Ssam struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 1401178354Ssam 1402264855Sadrian device_printf(sc->sc_dev, "firmware error\n"); 1403264855Sadrian if (vap != NULL) { 1404264855Sadrian IPW_UNLOCK(sc); 1405264855Sadrian ieee80211_cancel_scan(vap); 1406264855Sadrian IPW_LOCK(sc); 1407178354Ssam } 1408178354Ssam ieee80211_runtask(ic, &sc->sc_init_task); 1409178354Ssam} 1410178354Ssam 1411275983Sadrianstatic void 1412264855Sadrianipw_intr(void *arg) 1413264855Sadrian{ 1414264855Sadrian struct ipw_softc *sc = arg; 1415264855Sadrian uint32_t r; 1416264855Sadrian 1417264855Sadrian IPW_LOCK(sc); 1418264855Sadrian 1419264855Sadrian r = CSR_READ_4(sc, IPW_CSR_INTR); 1420264855Sadrian if (r == 0 || r == 0xffffffff) 1421275983Sadrian goto done; 1422264855Sadrian 1423264855Sadrian /* disable interrupts */ 1424264855Sadrian CSR_WRITE_4(sc, IPW_CSR_INTR_MASK, 0); 1425275983Sadrian 1426264855Sadrian /* acknowledge all interrupts */ 1427264855Sadrian CSR_WRITE_4(sc, IPW_CSR_INTR, r); 1428264906Sadrian 1429275983Sadrian if (r & (IPW_INTR_FATAL_ERROR | IPW_INTR_PARITY_ERROR)) { 1430275983Sadrian ipw_fatal_error_intr(sc); 1431275983Sadrian goto done; 1432275983Sadrian } 1433275983Sadrian 1434275983Sadrian if (r & IPW_INTR_FW_INIT_DONE) 1435275983Sadrian wakeup(sc); 1436275983Sadrian 1437275983Sadrian if (r & IPW_INTR_RX_TRANSFER) 1438275983Sadrian ipw_rx_intr(sc); 1439275983Sadrian 1440275983Sadrian if (r & IPW_INTR_TX_TRANSFER) 1441275983Sadrian ipw_tx_intr(sc); 1442275983Sadrian 1443264855Sadrian /* re-enable interrupts */ 1444275983Sadrian CSR_WRITE_4(sc, IPW_CSR_INTR_MASK, IPW_INTR_MASK); 1445275983Sadriandone: 1446275983Sadrian IPW_UNLOCK(sc); 1447275983Sadrian} 1448275983Sadrian 1449275983Sadrianstatic void 1450275983Sadrianipw_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nseg, int error) 1451275983Sadrian{ 1452275983Sadrian if (error != 0) 1453275983Sadrian return; 1454275983Sadrian 1455178354Ssam KASSERT(nseg == 1, ("too many DMA segments, %d should be 1", nseg)); 1456178354Ssam 1457264906Sadrian *(bus_addr_t *)arg = segs[0].ds_addr; 1458178354Ssam} 1459178354Ssam 1460178354Ssamstatic const char * 1461193439Ssamipw_cmdname(int cmd) 1462193439Ssam{ 1463193439Ssam#define N(a) (sizeof(a) / sizeof(a[0])) 1464193439Ssam static const struct { 1465193439Ssam int cmd; 1466193439Ssam const char *name; 1467193439Ssam } cmds[] = { 1468193439Ssam { IPW_CMD_ADD_MULTICAST, "ADD_MULTICAST" }, 1469193439Ssam { IPW_CMD_BROADCAST_SCAN, "BROADCAST_SCAN" }, 1470193439Ssam { IPW_CMD_DISABLE, "DISABLE" }, 1471193439Ssam { IPW_CMD_DISABLE_PHY, "DISABLE_PHY" }, 1472193439Ssam { IPW_CMD_ENABLE, "ENABLE" }, 1473193439Ssam { IPW_CMD_PREPARE_POWER_DOWN, "PREPARE_POWER_DOWN" }, 1474193439Ssam { IPW_CMD_SET_BASIC_TX_RATES, "SET_BASIC_TX_RATES" }, 1475178354Ssam { IPW_CMD_SET_BEACON_INTERVAL, "SET_BEACON_INTERVAL" }, 1476178354Ssam { IPW_CMD_SET_CHANNEL, "SET_CHANNEL" }, 1477178354Ssam { IPW_CMD_SET_CONFIGURATION, "SET_CONFIGURATION" }, 1478178354Ssam { IPW_CMD_SET_DESIRED_BSSID, "SET_DESIRED_BSSID" }, 1479178354Ssam { IPW_CMD_SET_ESSID, "SET_ESSID" }, 1480178354Ssam { IPW_CMD_SET_FRAG_THRESHOLD, "SET_FRAG_THRESHOLD" }, 1481178354Ssam { IPW_CMD_SET_MAC_ADDRESS, "SET_MAC_ADDRESS" }, 1482178354Ssam { IPW_CMD_SET_MANDATORY_BSSID, "SET_MANDATORY_BSSID" }, 1483178354Ssam { IPW_CMD_SET_MODE, "SET_MODE" }, 1484178354Ssam { IPW_CMD_SET_MSDU_TX_RATES, "SET_MSDU_TX_RATES" }, 1485178354Ssam { IPW_CMD_SET_POWER_MODE, "SET_POWER_MODE" }, 1486178354Ssam { IPW_CMD_SET_RTS_THRESHOLD, "SET_RTS_THRESHOLD" }, 1487178354Ssam { IPW_CMD_SET_SCAN_OPTIONS, "SET_SCAN_OPTIONS" }, 1488178354Ssam { IPW_CMD_SET_SECURITY_INFO, "SET_SECURITY_INFO" }, 1489178354Ssam { IPW_CMD_SET_TX_POWER_INDEX, "SET_TX_POWER_INDEX" }, 1490283535Sadrian { IPW_CMD_SET_TX_RATES, "SET_TX_RATES" }, 1491282742Sadrian { IPW_CMD_SET_WEP_FLAGS, "SET_WEP_FLAGS" }, 1492178354Ssam { IPW_CMD_SET_WEP_KEY, "SET_WEP_KEY" }, 1493178354Ssam { IPW_CMD_SET_WEP_KEY_INDEX, "SET_WEP_KEY_INDEX" }, 1494178354Ssam { IPW_CMD_SET_WPA_IE, "SET_WPA_IE" }, 1495178354Ssam 1496178354Ssam }; 1497178354Ssam static char buf[12]; 1498178354Ssam int i; 1499178354Ssam 1500178354Ssam for (i = 0; i < N(cmds); i++) 1501178354Ssam if (cmds[i].cmd == cmd) 1502233452Sadrian return cmds[i].name; 1503233452Sadrian snprintf(buf, sizeof(buf), "%u", cmd); 1504264855Sadrian return buf; 1505264855Sadrian#undef N 1506264855Sadrian} 1507264855Sadrian 1508264855Sadrian/* 1509264855Sadrian * Send a command to the firmware and wait for the acknowledgement. 1510264855Sadrian */ 1511264855Sadrianstatic int 1512233452Sadrianipw_cmd(struct ipw_softc *sc, uint32_t type, void *data, uint32_t len) 1513233452Sadrian{ 1514233452Sadrian struct ipw_soft_bd *sbd; 1515233452Sadrian bus_addr_t physaddr; 1516233452Sadrian int error; 1517178354Ssam 1518178354Ssam IPW_LOCK_ASSERT(sc); 1519178354Ssam 1520178354Ssam if (sc->flags & IPW_FLAG_BUSY) { 1521178354Ssam device_printf(sc->sc_dev, "%s: %s not sent, busy\n", 1522178354Ssam __func__, ipw_cmdname(type)); 1523178354Ssam return EAGAIN; 1524178354Ssam } 1525178354Ssam sc->flags |= IPW_FLAG_BUSY; 1526178354Ssam 1527178354Ssam sbd = &sc->stbd_list[sc->txcur]; 1528178354Ssam 1529178354Ssam error = bus_dmamap_load(sc->cmd_dmat, sc->cmd_map, &sc->cmd, 1530178354Ssam sizeof (struct ipw_cmd), ipw_dma_map_addr, &physaddr, 0); 1531178354Ssam if (error != 0) { 1532178354Ssam device_printf(sc->sc_dev, "could not map command DMA memory\n"); 1533178354Ssam sc->flags &= ~IPW_FLAG_BUSY; 1534178354Ssam return error; 1535283535Sadrian } 1536282742Sadrian 1537178354Ssam sc->cmd.type = htole32(type); 1538178354Ssam sc->cmd.subtype = 0; 1539178354Ssam sc->cmd.len = htole32(len); 1540178354Ssam sc->cmd.seq = 0; 1541178354Ssam memcpy(sc->cmd.data, data, len); 1542178354Ssam 1543178354Ssam sbd->type = IPW_SBD_TYPE_COMMAND; 1544178354Ssam sbd->bd->physaddr = htole32(physaddr); 1545178354Ssam sbd->bd->len = htole32(sizeof (struct ipw_cmd)); 1546178354Ssam sbd->bd->nfrag = 1; 1547178354Ssam sbd->bd->flags = IPW_BD_FLAG_TX_FRAME_COMMAND | 1548178354Ssam IPW_BD_FLAG_TX_LAST_FRAGMENT; 1549178354Ssam 1550178354Ssam bus_dmamap_sync(sc->cmd_dmat, sc->cmd_map, BUS_DMASYNC_PREWRITE); 1551178354Ssam bus_dmamap_sync(sc->tbd_dmat, sc->tbd_map, BUS_DMASYNC_PREWRITE); 1552178354Ssam 1553178354Ssam#ifdef IPW_DEBUG 1554178354Ssam if (ipw_debug >= 4) { 1555178354Ssam printf("sending %s(%u, %u, %u, %u)", ipw_cmdname(type), type, 1556178354Ssam 0, 0, len); 1557178354Ssam /* Print the data buffer in the higher debug level */ 1558178354Ssam if (ipw_debug >= 9 && len > 0) { 1559178354Ssam printf(" data: 0x"); 1560178354Ssam for (int i = 1; i <= len; i++) 1561178354Ssam printf("%1D", (u_char *)data + len - i, ""); 1562178354Ssam } 1563178354Ssam printf("\n"); 1564178354Ssam } 1565178354Ssam#endif 1566178354Ssam 1567178354Ssam /* kick firmware */ 1568178354Ssam sc->txfree--; 1569178354Ssam sc->txcur = (sc->txcur + 1) % IPW_NTBD; 1570178354Ssam CSR_WRITE_4(sc, IPW_CSR_TX_WRITE, sc->txcur); 1571192468Ssam 1572192468Ssam /* wait at most one second for command to complete */ 1573178354Ssam error = msleep(sc, &sc->sc_mtx, 0, "ipwcmd", hz); 1574192468Ssam if (error != 0) { 1575178354Ssam device_printf(sc->sc_dev, "%s: %s failed, timeout (error %u)\n", 1576178354Ssam __func__, ipw_cmdname(type), error); 1577178354Ssam sc->flags &= ~IPW_FLAG_BUSY; 1578178354Ssam return (error); 1579178354Ssam } 1580178354Ssam return (0); 1581178354Ssam} 1582178354Ssam 1583178354Ssamstatic int 1584178354Ssamipw_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni) 1585178354Ssam{ 1586178354Ssam struct ipw_softc *sc = ifp->if_softc; 1587178354Ssam struct ieee80211com *ic = ifp->if_l2com; 1588178354Ssam struct ieee80211vap *vap = ni->ni_vap; 1589178354Ssam struct ieee80211_frame *wh; 1590178354Ssam struct ipw_soft_bd *sbd; 1591178354Ssam struct ipw_soft_hdr *shdr; 1592178354Ssam struct ipw_soft_buf *sbuf; 1593178354Ssam struct ieee80211_key *k; 1594178354Ssam struct mbuf *mnew; 1595178354Ssam bus_dma_segment_t segs[IPW_MAX_NSEG]; 1596178354Ssam bus_addr_t physaddr; 1597178354Ssam int nsegs, error, i; 1598178354Ssam 1599178354Ssam wh = mtod(m0, struct ieee80211_frame *); 1600178354Ssam 1601178354Ssam if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { 1602178354Ssam k = ieee80211_crypto_encap(ni, m0); 1603178354Ssam if (k == NULL) { 1604178354Ssam m_freem(m0); 1605178354Ssam return ENOBUFS; 1606178354Ssam } 1607178354Ssam /* packet header may have moved, reset our local pointer */ 1608178354Ssam wh = mtod(m0, struct ieee80211_frame *); 1609178354Ssam } 1610178354Ssam 1611178354Ssam if (ieee80211_radiotap_active_vap(vap)) { 1612178354Ssam struct ipw_tx_radiotap_header *tap = &sc->sc_txtap; 1613178354Ssam 1614178354Ssam tap->wt_flags = 0; 1615178354Ssam 1616178354Ssam ieee80211_radiotap_tx(vap, m0); 1617178354Ssam } 1618178354Ssam 1619178354Ssam shdr = SLIST_FIRST(&sc->free_shdr); 1620178354Ssam sbuf = SLIST_FIRST(&sc->free_sbuf); 1621178354Ssam KASSERT(shdr != NULL && sbuf != NULL, ("empty sw hdr/buf pool")); 1622178354Ssam 1623178354Ssam shdr->hdr.type = htole32(IPW_HDR_TYPE_SEND); 1624178354Ssam shdr->hdr.subtype = 0; 1625178354Ssam shdr->hdr.encrypted = (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) ? 1 : 0; 1626178354Ssam shdr->hdr.encrypt = 0; 1627178354Ssam shdr->hdr.keyidx = 0; 1628178354Ssam shdr->hdr.keysz = 0; 1629178354Ssam shdr->hdr.fragmentsz = 0; 1630178354Ssam IEEE80211_ADDR_COPY(shdr->hdr.src_addr, wh->i_addr2); 1631178354Ssam if (ic->ic_opmode == IEEE80211_M_STA) 1632178354Ssam IEEE80211_ADDR_COPY(shdr->hdr.dst_addr, wh->i_addr3); 1633178354Ssam else 1634178354Ssam IEEE80211_ADDR_COPY(shdr->hdr.dst_addr, wh->i_addr1); 1635178354Ssam 1636178354Ssam /* trim IEEE802.11 header */ 1637178354Ssam m_adj(m0, sizeof (struct ieee80211_frame)); 1638178354Ssam 1639178354Ssam error = bus_dmamap_load_mbuf_sg(sc->txbuf_dmat, sbuf->map, m0, segs, 1640193655Ssam &nsegs, 0); 1641178354Ssam if (error != 0 && error != EFBIG) { 1642178354Ssam device_printf(sc->sc_dev, "could not map mbuf (error %d)\n", 1643178354Ssam error); 1644178354Ssam m_freem(m0); 1645178354Ssam return error; 1646178354Ssam } 1647178354Ssam if (error != 0) { 1648178354Ssam mnew = m_defrag(m0, M_NOWAIT); 1649178354Ssam if (mnew == NULL) { 1650219603Sbschmidt device_printf(sc->sc_dev, 1651178354Ssam "could not defragment mbuf\n"); 1652178354Ssam m_freem(m0); 1653178354Ssam return ENOBUFS; 1654178354Ssam } 1655178354Ssam m0 = mnew; 1656178354Ssam 1657178354Ssam error = bus_dmamap_load_mbuf_sg(sc->txbuf_dmat, sbuf->map, m0, 1658178354Ssam segs, &nsegs, 0); 1659178354Ssam if (error != 0) { 1660178354Ssam device_printf(sc->sc_dev, 1661178354Ssam "could not map mbuf (error %d)\n", error); 1662178354Ssam m_freem(m0); 1663178354Ssam return error; 1664178354Ssam } 1665178354Ssam } 1666178354Ssam 1667178354Ssam error = bus_dmamap_load(sc->hdr_dmat, shdr->map, &shdr->hdr, 1668178354Ssam sizeof (struct ipw_hdr), ipw_dma_map_addr, &physaddr, 0); 1669178354Ssam if (error != 0) { 1670178354Ssam device_printf(sc->sc_dev, "could not map header DMA memory\n"); 1671178354Ssam bus_dmamap_unload(sc->txbuf_dmat, sbuf->map); 1672178354Ssam m_freem(m0); 1673178354Ssam return error; 1674178354Ssam } 1675178354Ssam 1676178354Ssam SLIST_REMOVE_HEAD(&sc->free_sbuf, next); 1677178354Ssam SLIST_REMOVE_HEAD(&sc->free_shdr, next); 1678178354Ssam 1679178354Ssam sbd = &sc->stbd_list[sc->txcur]; 1680178354Ssam sbd->type = IPW_SBD_TYPE_HEADER; 1681178354Ssam sbd->priv = shdr; 1682178354Ssam sbd->bd->physaddr = htole32(physaddr); 1683178354Ssam sbd->bd->len = htole32(sizeof (struct ipw_hdr)); 1684178354Ssam sbd->bd->nfrag = 1 + nsegs; 1685178354Ssam sbd->bd->flags = IPW_BD_FLAG_TX_FRAME_802_3 | 1686178354Ssam IPW_BD_FLAG_TX_NOT_LAST_FRAGMENT; 1687178354Ssam 1688178354Ssam DPRINTFN(5, ("sending tx hdr (%u, %u, %u, %u, %6D, %6D)\n", 1689178354Ssam shdr->hdr.type, shdr->hdr.subtype, shdr->hdr.encrypted, 1690178354Ssam shdr->hdr.encrypt, shdr->hdr.src_addr, ":", shdr->hdr.dst_addr, 1691178354Ssam ":")); 1692178354Ssam 1693178354Ssam sc->txfree--; 1694178354Ssam sc->txcur = (sc->txcur + 1) % IPW_NTBD; 1695178354Ssam 1696193655Ssam sbuf->m = m0; 1697183254Ssam sbuf->ni = ni; 1698183254Ssam 1699178354Ssam for (i = 0; i < nsegs; i++) { 1700178354Ssam sbd = &sc->stbd_list[sc->txcur]; 1701178354Ssam 1702193966Ssam sbd->bd->physaddr = htole32(segs[i].ds_addr); 1703214894Sbschmidt sbd->bd->len = htole32(segs[i].ds_len); 1704178354Ssam sbd->bd->nfrag = 0; 1705297603Sadrian sbd->bd->flags = IPW_BD_FLAG_TX_FRAME_802_3; 1706178354Ssam if (i == nsegs - 1) { 1707297768Sadrian sbd->type = IPW_SBD_TYPE_DATA; 1708297768Sadrian sbd->priv = sbuf; 1709297768Sadrian sbd->bd->flags |= IPW_BD_FLAG_TX_LAST_FRAGMENT; 1710297768Sadrian } else { 1711297768Sadrian sbd->type = IPW_SBD_TYPE_NOASSOC; 1712297768Sadrian sbd->bd->flags |= IPW_BD_FLAG_TX_NOT_LAST_FRAGMENT; 1713297768Sadrian } 1714297768Sadrian 1715178354Ssam DPRINTFN(5, ("sending fragment (%d)\n", i)); 1716178354Ssam 1717178354Ssam sc->txfree--; 1718178354Ssam sc->txcur = (sc->txcur + 1) % IPW_NTBD; 1719178354Ssam } 1720178354Ssam 1721178354Ssam bus_dmamap_sync(sc->hdr_dmat, shdr->map, BUS_DMASYNC_PREWRITE); 1722178354Ssam bus_dmamap_sync(sc->txbuf_dmat, sbuf->map, BUS_DMASYNC_PREWRITE); 1723178354Ssam bus_dmamap_sync(sc->tbd_dmat, sc->tbd_map, BUS_DMASYNC_PREWRITE); 1724178354Ssam 1725178354Ssam /* kick firmware */ 1726178354Ssam CSR_WRITE_4(sc, IPW_CSR_TX_WRITE, sc->txcur); 1727178354Ssam 1728178354Ssam return 0; 1729178354Ssam} 1730178354Ssam 1731178354Ssamstatic int 1732178354Ssamipw_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, 1733178354Ssam const struct ieee80211_bpf_params *params) 1734178354Ssam{ 1735178354Ssam /* no support; just discard */ 1736178354Ssam m_freem(m); 1737178354Ssam ieee80211_free_node(ni); 1738178354Ssam return 0; 1739178354Ssam} 1740178354Ssam 1741178354Ssamstatic void 1742183256Ssamipw_start(struct ifnet *ifp) 1743178354Ssam{ 1744178354Ssam struct ipw_softc *sc = ifp->if_softc; 1745178354Ssam 1746178354Ssam IPW_LOCK(sc); 1747178354Ssam ipw_start_locked(ifp); 1748178354Ssam IPW_UNLOCK(sc); 1749178354Ssam} 1750182834Ssam 1751178354Ssamstatic void 1752183255Ssamipw_start_locked(struct ifnet *ifp) 1753183255Ssam{ 1754183256Ssam struct ipw_softc *sc = ifp->if_softc; 1755178354Ssam struct ieee80211_node *ni; 1756178354Ssam struct mbuf *m; 1757178354Ssam 1758178354Ssam IPW_LOCK_ASSERT(sc); 1759178354Ssam 1760178354Ssam for (;;) { 1761178354Ssam IFQ_DRV_DEQUEUE(&ifp->if_snd, m); 1762178354Ssam if (m == NULL) 1763178354Ssam break; 1764178354Ssam if (sc->txfree < 1 + IPW_MAX_NSEG) { 1765178354Ssam IFQ_DRV_PREPEND(&ifp->if_snd, m); 1766178354Ssam ifp->if_drv_flags |= IFF_DRV_OACTIVE; 1767178354Ssam break; 1768178354Ssam } 1769178354Ssam ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; 1770178354Ssam if (ipw_tx_start(ifp, m, ni) != 0) { 1771178354Ssam ieee80211_free_node(ni); 1772178354Ssam if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 1773178354Ssam break; 1774178354Ssam } 1775178354Ssam /* start watchdog timer */ 1776178354Ssam sc->sc_tx_timer = 5; 1777178354Ssam } 1778178354Ssam} 1779178354Ssam 1780178354Ssamstatic void 1781178354Ssamipw_watchdog(void *arg) 1782178354Ssam{ 1783178354Ssam struct ipw_softc *sc = arg; 1784178354Ssam struct ifnet *ifp = sc->sc_ifp; 1785178354Ssam struct ieee80211com *ic = ifp->if_l2com; 1786178354Ssam 1787178354Ssam IPW_LOCK_ASSERT(sc); 1788178354Ssam 1789298364Savos if (sc->sc_tx_timer > 0) { 1790298364Savos if (--sc->sc_tx_timer == 0) { 1791178354Ssam if_printf(ifp, "device timeout\n"); 1792178354Ssam if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 1793178354Ssam taskqueue_enqueue(taskqueue_swi, &sc->sc_init_task); 1794178354Ssam } 1795178354Ssam } 1796178354Ssam if (sc->sc_scan_timer > 0) { 1797178354Ssam if (--sc->sc_scan_timer == 0) { 1798178354Ssam DPRINTFN(3, ("Scan timeout\n")); 1799178354Ssam /* End the scan */ 1800178354Ssam if (sc->flags & IPW_FLAG_SCANNING) { 1801178354Ssam IPW_UNLOCK(sc); 1802178354Ssam ieee80211_scan_done(TAILQ_FIRST(&ic->ic_vaps)); 1803178354Ssam IPW_LOCK(sc); 1804178354Ssam sc->flags &= ~IPW_FLAG_SCANNING; 1805178354Ssam } 1806178354Ssam } 1807178354Ssam } 1808178354Ssam if (ifp->if_drv_flags & IFF_DRV_RUNNING) 1809178354Ssam callout_reset(&sc->sc_wdtimer, hz, ipw_watchdog, sc); 1810178354Ssam} 1811178354Ssam 1812178354Ssamstatic int 1813178354Ssamipw_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 1814178354Ssam{ 1815178354Ssam struct ipw_softc *sc = ifp->if_softc; 1816178354Ssam struct ieee80211com *ic = ifp->if_l2com; 1817178354Ssam struct ifreq *ifr = (struct ifreq *) data; 1818178354Ssam int error = 0, startall = 0; 1819178354Ssam 1820178354Ssam switch (cmd) { 1821178354Ssam case SIOCSIFFLAGS: 1822178354Ssam IPW_LOCK(sc); 1823298364Savos if (ifp->if_flags & IFF_UP) { 1824298364Savos if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { 1825178354Ssam ipw_init_locked(sc); 1826178354Ssam startall = 1; 1827178354Ssam } 1828178354Ssam } else { 1829178354Ssam if (ifp->if_drv_flags & IFF_DRV_RUNNING) 1830218927Sbschmidt ipw_stop_locked(sc); 1831218958Sbschmidt } 1832218958Sbschmidt IPW_UNLOCK(sc); 1833218927Sbschmidt if (startall) 1834218958Sbschmidt ieee80211_start_all(ic); 1835218958Sbschmidt break; 1836218958Sbschmidt case SIOCGIFMEDIA: 1837218958Sbschmidt error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); 1838218927Sbschmidt break; 1839218927Sbschmidt case SIOCGIFADDR: 1840178354Ssam error = ether_ioctl(ifp, cmd, data); 1841218958Sbschmidt break; 1842218958Sbschmidt default: 1843218958Sbschmidt error = EINVAL; 1844218927Sbschmidt break; 1845178354Ssam } 1846178354Ssam return error; 1847178354Ssam} 1848178354Ssam 1849218927Sbschmidtstatic void 1850295795Savosipw_stop_master(struct ipw_softc *sc) 1851218927Sbschmidt{ 1852218927Sbschmidt uint32_t tmp; 1853218927Sbschmidt int ntries; 1854178354Ssam 1855218927Sbschmidt /* disable interrupts */ 1856218927Sbschmidt CSR_WRITE_4(sc, IPW_CSR_INTR_MASK, 0); 1857178354Ssam 1858178354Ssam CSR_WRITE_4(sc, IPW_CSR_RST, IPW_RST_STOP_MASTER); 1859218927Sbschmidt for (ntries = 0; ntries < 50; ntries++) { 1860178354Ssam if (CSR_READ_4(sc, IPW_CSR_RST) & IPW_RST_MASTER_DISABLED) 1861178354Ssam break; 1862178354Ssam DELAY(10); 1863178354Ssam } 1864178354Ssam if (ntries == 50) 1865178354Ssam device_printf(sc->sc_dev, "timeout waiting for master\n"); 1866191546Ssam 1867191546Ssam tmp = CSR_READ_4(sc, IPW_CSR_RST); 1868205277Srpaulo CSR_WRITE_4(sc, IPW_CSR_RST, tmp | IPW_RST_PRINCETON_RESET); 1869191546Ssam 1870205277Srpaulo /* Clear all flags except the following */ 1871205277Srpaulo sc->flags &= IPW_FLAG_HAS_RADIO_SWITCH; 1872205277Srpaulo} 1873205277Srpaulo 1874205277Srpaulostatic int 1875191546Ssamipw_reset(struct ipw_softc *sc) 1876{ 1877 uint32_t tmp; 1878 int ntries; 1879 1880 ipw_stop_master(sc); 1881 1882 /* move adapter to D0 state */ 1883 tmp = CSR_READ_4(sc, IPW_CSR_CTL); 1884 CSR_WRITE_4(sc, IPW_CSR_CTL, tmp | IPW_CTL_INIT); 1885 1886 /* wait for clock stabilization */ 1887 for (ntries = 0; ntries < 1000; ntries++) { 1888 if (CSR_READ_4(sc, IPW_CSR_CTL) & IPW_CTL_CLOCK_READY) 1889 break; 1890 DELAY(200); 1891 } 1892 if (ntries == 1000) 1893 return EIO; 1894 1895 tmp = CSR_READ_4(sc, IPW_CSR_RST); 1896 CSR_WRITE_4(sc, IPW_CSR_RST, tmp | IPW_RST_SW_RESET); 1897 1898 DELAY(10); 1899 1900 tmp = CSR_READ_4(sc, IPW_CSR_CTL); 1901 CSR_WRITE_4(sc, IPW_CSR_CTL, tmp | IPW_CTL_INIT); 1902 1903 return 0; 1904} 1905 1906static int 1907ipw_waitfordisable(struct ipw_softc *sc, int waitfor) 1908{ 1909 int ms = hz < 1000 ? 1 : hz/10; 1910 int i, error; 1911 1912 for (i = 0; i < 100; i++) { 1913 if (ipw_read_table1(sc, IPW_INFO_CARD_DISABLED) == waitfor) 1914 return 0; 1915 error = msleep(sc, &sc->sc_mtx, PCATCH, __func__, ms); 1916 if (error == 0 || error != EWOULDBLOCK) 1917 return 0; 1918 } 1919 DPRINTF(("%s: timeout waiting for %s\n", 1920 __func__, waitfor ? "disable" : "enable")); 1921 return ETIMEDOUT; 1922} 1923 1924static int 1925ipw_enable(struct ipw_softc *sc) 1926{ 1927 int error; 1928 1929 if ((sc->flags & IPW_FLAG_ENABLED) == 0) { 1930 DPRINTF(("Enable adapter\n")); 1931 error = ipw_cmd(sc, IPW_CMD_ENABLE, NULL, 0); 1932 if (error != 0) 1933 return error; 1934 error = ipw_waitfordisable(sc, 0); 1935 if (error != 0) 1936 return error; 1937 sc->flags |= IPW_FLAG_ENABLED; 1938 } 1939 return 0; 1940} 1941 1942static int 1943ipw_disable(struct ipw_softc *sc) 1944{ 1945 int error; 1946 1947 if (sc->flags & IPW_FLAG_ENABLED) { 1948 DPRINTF(("Disable adapter\n")); 1949 error = ipw_cmd(sc, IPW_CMD_DISABLE, NULL, 0); 1950 if (error != 0) 1951 return error; 1952 error = ipw_waitfordisable(sc, 1); 1953 if (error != 0) 1954 return error; 1955 sc->flags &= ~IPW_FLAG_ENABLED; 1956 } 1957 return 0; 1958} 1959 1960/* 1961 * Upload the microcode to the device. 1962 */ 1963static int 1964ipw_load_ucode(struct ipw_softc *sc, const char *uc, int size) 1965{ 1966 int ntries; 1967 1968 MEM_WRITE_4(sc, 0x3000e0, 0x80000000); 1969 CSR_WRITE_4(sc, IPW_CSR_RST, 0); 1970 1971 MEM_WRITE_2(sc, 0x220000, 0x0703); 1972 MEM_WRITE_2(sc, 0x220000, 0x0707); 1973 1974 MEM_WRITE_1(sc, 0x210014, 0x72); 1975 MEM_WRITE_1(sc, 0x210014, 0x72); 1976 1977 MEM_WRITE_1(sc, 0x210000, 0x40); 1978 MEM_WRITE_1(sc, 0x210000, 0x00); 1979 MEM_WRITE_1(sc, 0x210000, 0x40); 1980 1981 MEM_WRITE_MULTI_1(sc, 0x210010, uc, size); 1982 1983 MEM_WRITE_1(sc, 0x210000, 0x00); 1984 MEM_WRITE_1(sc, 0x210000, 0x00); 1985 MEM_WRITE_1(sc, 0x210000, 0x80); 1986 1987 MEM_WRITE_2(sc, 0x220000, 0x0703); 1988 MEM_WRITE_2(sc, 0x220000, 0x0707); 1989 1990 MEM_WRITE_1(sc, 0x210014, 0x72); 1991 MEM_WRITE_1(sc, 0x210014, 0x72); 1992 1993 MEM_WRITE_1(sc, 0x210000, 0x00); 1994 MEM_WRITE_1(sc, 0x210000, 0x80); 1995 1996 for (ntries = 0; ntries < 10; ntries++) { 1997 if (MEM_READ_1(sc, 0x210000) & 1) 1998 break; 1999 DELAY(10); 2000 } 2001 if (ntries == 10) { 2002 device_printf(sc->sc_dev, 2003 "timeout waiting for ucode to initialize\n"); 2004 return EIO; 2005 } 2006 2007 MEM_WRITE_4(sc, 0x3000e0, 0); 2008 2009 return 0; 2010} 2011 2012/* set of macros to handle unaligned little endian data in firmware image */ 2013#define GETLE32(p) ((p)[0] | (p)[1] << 8 | (p)[2] << 16 | (p)[3] << 24) 2014#define GETLE16(p) ((p)[0] | (p)[1] << 8) 2015static int 2016ipw_load_firmware(struct ipw_softc *sc, const char *fw, int size) 2017{ 2018 const uint8_t *p, *end; 2019 uint32_t tmp, dst; 2020 uint16_t len; 2021 int error; 2022 2023 p = fw; 2024 end = fw + size; 2025 while (p < end) { 2026 dst = GETLE32(p); p += 4; 2027 len = GETLE16(p); p += 2; 2028 2029 ipw_write_mem_1(sc, dst, p, len); 2030 p += len; 2031 } 2032 2033 CSR_WRITE_4(sc, IPW_CSR_IO, IPW_IO_GPIO1_ENABLE | IPW_IO_GPIO3_MASK | 2034 IPW_IO_LED_OFF); 2035 2036 /* enable interrupts */ 2037 CSR_WRITE_4(sc, IPW_CSR_INTR_MASK, IPW_INTR_MASK); 2038 2039 /* kick the firmware */ 2040 CSR_WRITE_4(sc, IPW_CSR_RST, 0); 2041 2042 tmp = CSR_READ_4(sc, IPW_CSR_CTL); 2043 CSR_WRITE_4(sc, IPW_CSR_CTL, tmp | IPW_CTL_ALLOW_STANDBY); 2044 2045 /* wait at most one second for firmware initialization to complete */ 2046 if ((error = msleep(sc, &sc->sc_mtx, 0, "ipwinit", hz)) != 0) { 2047 device_printf(sc->sc_dev, "timeout waiting for firmware " 2048 "initialization to complete\n"); 2049 return error; 2050 } 2051 2052 tmp = CSR_READ_4(sc, IPW_CSR_IO); 2053 CSR_WRITE_4(sc, IPW_CSR_IO, tmp | IPW_IO_GPIO1_MASK | 2054 IPW_IO_GPIO3_MASK); 2055 2056 return 0; 2057} 2058 2059static int 2060ipw_setwepkeys(struct ipw_softc *sc) 2061{ 2062 struct ifnet *ifp = sc->sc_ifp; 2063 struct ieee80211com *ic = ifp->if_l2com; 2064 struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 2065 struct ipw_wep_key wepkey; 2066 struct ieee80211_key *wk; 2067 int error, i; 2068 2069 for (i = 0; i < IEEE80211_WEP_NKID; i++) { 2070 wk = &vap->iv_nw_keys[i]; 2071 2072 if (wk->wk_cipher == NULL || 2073 wk->wk_cipher->ic_cipher != IEEE80211_CIPHER_WEP) 2074 continue; 2075 2076 wepkey.idx = i; 2077 wepkey.len = wk->wk_keylen; 2078 memset(wepkey.key, 0, sizeof wepkey.key); 2079 memcpy(wepkey.key, wk->wk_key, wk->wk_keylen); 2080 DPRINTF(("Setting wep key index %u len %u\n", wepkey.idx, 2081 wepkey.len)); 2082 error = ipw_cmd(sc, IPW_CMD_SET_WEP_KEY, &wepkey, 2083 sizeof wepkey); 2084 if (error != 0) 2085 return error; 2086 } 2087 return 0; 2088} 2089 2090static int 2091ipw_setwpaie(struct ipw_softc *sc, const void *ie, int ielen) 2092{ 2093 struct ipw_wpa_ie wpaie; 2094 2095 memset(&wpaie, 0, sizeof(wpaie)); 2096 wpaie.len = htole32(ielen); 2097 /* XXX verify length */ 2098 memcpy(&wpaie.ie, ie, ielen); 2099 DPRINTF(("Setting WPA IE\n")); 2100 return ipw_cmd(sc, IPW_CMD_SET_WPA_IE, &wpaie, sizeof(wpaie)); 2101} 2102 2103static int 2104ipw_setbssid(struct ipw_softc *sc, uint8_t *bssid) 2105{ 2106 static const uint8_t zerobssid[IEEE80211_ADDR_LEN]; 2107 2108 if (bssid == NULL || bcmp(bssid, zerobssid, IEEE80211_ADDR_LEN) == 0) { 2109 DPRINTF(("Setting mandatory BSSID to null\n")); 2110 return ipw_cmd(sc, IPW_CMD_SET_MANDATORY_BSSID, NULL, 0); 2111 } else { 2112 DPRINTF(("Setting mandatory BSSID to %6D\n", bssid, ":")); 2113 return ipw_cmd(sc, IPW_CMD_SET_MANDATORY_BSSID, 2114 bssid, IEEE80211_ADDR_LEN); 2115 } 2116} 2117 2118static int 2119ipw_setssid(struct ipw_softc *sc, void *ssid, size_t ssidlen) 2120{ 2121 if (ssidlen == 0) { 2122 /* 2123 * A bug in the firmware breaks the ``don't associate'' 2124 * bit in the scan options command. To compensate for 2125 * this install a bogus ssid when no ssid is specified 2126 * so the firmware won't try to associate. 2127 */ 2128 DPRINTF(("Setting bogus ESSID to WAR firmware bug\n")); 2129 return ipw_cmd(sc, IPW_CMD_SET_ESSID, 2130 "\x18\x19\x20\x21\x22\x23\x24\x25\x26\x27" 2131 "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31" 2132 "\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b" 2133 "\x3c\x3d", IEEE80211_NWID_LEN); 2134 } else { 2135#ifdef IPW_DEBUG 2136 if (ipw_debug > 0) { 2137 printf("Setting ESSID to "); 2138 ieee80211_print_essid(ssid, ssidlen); 2139 printf("\n"); 2140 } 2141#endif 2142 return ipw_cmd(sc, IPW_CMD_SET_ESSID, ssid, ssidlen); 2143 } 2144} 2145 2146static int 2147ipw_setscanopts(struct ipw_softc *sc, uint32_t chanmask, uint32_t flags) 2148{ 2149 struct ipw_scan_options opts; 2150 2151 DPRINTF(("Scan options: mask 0x%x flags 0x%x\n", chanmask, flags)); 2152 opts.channels = htole32(chanmask); 2153 opts.flags = htole32(flags); 2154 return ipw_cmd(sc, IPW_CMD_SET_SCAN_OPTIONS, &opts, sizeof(opts)); 2155} 2156 2157static int 2158ipw_scan(struct ipw_softc *sc) 2159{ 2160 uint32_t params; 2161 int error; 2162 2163 DPRINTF(("%s: flags 0x%x\n", __func__, sc->flags)); 2164 2165 if (sc->flags & IPW_FLAG_SCANNING) 2166 return (EBUSY); 2167 sc->flags |= IPW_FLAG_SCANNING | IPW_FLAG_HACK; 2168 2169 /* NB: IPW_SCAN_DO_NOT_ASSOCIATE does not work (we set it anyway) */ 2170 error = ipw_setscanopts(sc, 0x3fff, IPW_SCAN_DO_NOT_ASSOCIATE); 2171 if (error != 0) 2172 goto done; 2173 2174 /* 2175 * Setup null/bogus ssid so firmware doesn't use any previous 2176 * ssid to try and associate. This is because the ``don't 2177 * associate'' option bit is broken (sigh). 2178 */ 2179 error = ipw_setssid(sc, NULL, 0); 2180 if (error != 0) 2181 goto done; 2182 2183 /* 2184 * NB: the adapter may be disabled on association lost; 2185 * if so just re-enable it to kick off scanning. 2186 */ 2187 DPRINTF(("Starting scan\n")); 2188 sc->sc_scan_timer = 3; 2189 if (sc->flags & IPW_FLAG_ENABLED) { 2190 params = 0; /* XXX? */ 2191 error = ipw_cmd(sc, IPW_CMD_BROADCAST_SCAN, 2192 ¶ms, sizeof(params)); 2193 } else 2194 error = ipw_enable(sc); 2195done: 2196 if (error != 0) { 2197 DPRINTF(("Scan failed\n")); 2198 sc->flags &= ~(IPW_FLAG_SCANNING | IPW_FLAG_HACK); 2199 } 2200 return (error); 2201} 2202 2203static int 2204ipw_setchannel(struct ipw_softc *sc, struct ieee80211_channel *chan) 2205{ 2206 struct ifnet *ifp = sc->sc_ifp; 2207 struct ieee80211com *ic = ifp->if_l2com; 2208 uint32_t data; 2209 int error; 2210 2211 data = htole32(ieee80211_chan2ieee(ic, chan)); 2212 DPRINTF(("Setting channel to %u\n", le32toh(data))); 2213 error = ipw_cmd(sc, IPW_CMD_SET_CHANNEL, &data, sizeof data); 2214 if (error == 0) 2215 ipw_setcurchan(sc, chan); 2216 return error; 2217} 2218 2219static void 2220ipw_assoc(struct ieee80211com *ic, struct ieee80211vap *vap) 2221{ 2222 struct ifnet *ifp = vap->iv_ic->ic_ifp; 2223 struct ipw_softc *sc = ifp->if_softc; 2224 struct ieee80211_node *ni = vap->iv_bss; 2225 struct ipw_security security; 2226 uint32_t data; 2227 int error; 2228 2229 IPW_LOCK(sc); 2230 error = ipw_disable(sc); 2231 if (error != 0) 2232 goto done; 2233 2234 memset(&security, 0, sizeof security); 2235 security.authmode = (ni->ni_authmode == IEEE80211_AUTH_SHARED) ? 2236 IPW_AUTH_SHARED : IPW_AUTH_OPEN; 2237 security.ciphers = htole32(IPW_CIPHER_NONE); 2238 DPRINTF(("Setting authmode to %u\n", security.authmode)); 2239 error = ipw_cmd(sc, IPW_CMD_SET_SECURITY_INFO, &security, 2240 sizeof security); 2241 if (error != 0) 2242 goto done; 2243 2244 data = htole32(vap->iv_rtsthreshold); 2245 DPRINTF(("Setting RTS threshold to %u\n", le32toh(data))); 2246 error = ipw_cmd(sc, IPW_CMD_SET_RTS_THRESHOLD, &data, sizeof data); 2247 if (error != 0) 2248 goto done; 2249 2250 data = htole32(vap->iv_fragthreshold); 2251 DPRINTF(("Setting frag threshold to %u\n", le32toh(data))); 2252 error = ipw_cmd(sc, IPW_CMD_SET_FRAG_THRESHOLD, &data, sizeof data); 2253 if (error != 0) 2254 goto done; 2255 2256 if (vap->iv_flags & IEEE80211_F_PRIVACY) { 2257 error = ipw_setwepkeys(sc); 2258 if (error != 0) 2259 goto done; 2260 2261 if (vap->iv_def_txkey != IEEE80211_KEYIX_NONE) { 2262 data = htole32(vap->iv_def_txkey); 2263 DPRINTF(("Setting wep tx key index to %u\n", 2264 le32toh(data))); 2265 error = ipw_cmd(sc, IPW_CMD_SET_WEP_KEY_INDEX, &data, 2266 sizeof data); 2267 if (error != 0) 2268 goto done; 2269 } 2270 } 2271 2272 data = htole32((vap->iv_flags & IEEE80211_F_PRIVACY) ? IPW_WEPON : 0); 2273 DPRINTF(("Setting wep flags to 0x%x\n", le32toh(data))); 2274 error = ipw_cmd(sc, IPW_CMD_SET_WEP_FLAGS, &data, sizeof data); 2275 if (error != 0) 2276 goto done; 2277 2278 error = ipw_setssid(sc, ni->ni_essid, ni->ni_esslen); 2279 if (error != 0) 2280 goto done; 2281 2282 error = ipw_setbssid(sc, ni->ni_bssid); 2283 if (error != 0) 2284 goto done; 2285 2286 if (vap->iv_appie_wpa != NULL) { 2287 struct ieee80211_appie *ie = vap->iv_appie_wpa; 2288 error = ipw_setwpaie(sc, ie->ie_data, ie->ie_len); 2289 if (error != 0) 2290 goto done; 2291 } 2292 if (ic->ic_opmode == IEEE80211_M_IBSS) { 2293 error = ipw_setchannel(sc, ni->ni_chan); 2294 if (error != 0) 2295 goto done; 2296 } 2297 2298 /* lock scan to ap's channel and enable associate */ 2299 error = ipw_setscanopts(sc, 2300 1<<(ieee80211_chan2ieee(ic, ni->ni_chan)-1), 0); 2301 if (error != 0) 2302 goto done; 2303 2304 error = ipw_enable(sc); /* finally, enable adapter */ 2305 if (error == 0) 2306 sc->flags |= IPW_FLAG_ASSOCIATING; 2307done: 2308 IPW_UNLOCK(sc); 2309} 2310 2311static void 2312ipw_disassoc(struct ieee80211com *ic, struct ieee80211vap *vap) 2313{ 2314 struct ifnet *ifp = vap->iv_ic->ic_ifp; 2315 struct ieee80211_node *ni = vap->iv_bss; 2316 struct ipw_softc *sc = ifp->if_softc; 2317 2318 IPW_LOCK(sc); 2319 DPRINTF(("Disassociate from %6D\n", ni->ni_bssid, ":")); 2320 /* 2321 * NB: don't try to do this if ipw_stop_master has 2322 * shutdown the firmware and disabled interrupts. 2323 */ 2324 if (sc->flags & IPW_FLAG_FW_INITED) { 2325 sc->flags &= ~IPW_FLAG_ASSOCIATED; 2326 /* 2327 * NB: firmware currently ignores bssid parameter, but 2328 * supply it in case this changes (follow linux driver). 2329 */ 2330 (void) ipw_cmd(sc, IPW_CMD_DISASSOCIATE, 2331 ni->ni_bssid, IEEE80211_ADDR_LEN); 2332 } 2333 IPW_UNLOCK(sc); 2334} 2335 2336/* 2337 * Handler for sc_init_task. This is a simple wrapper around ipw_init(). 2338 * It is called on firmware panics or on watchdog timeouts. 2339 */ 2340static void 2341ipw_init_task(void *context, int pending) 2342{ 2343 ipw_init(context); 2344} 2345 2346static void 2347ipw_init(void *priv) 2348{ 2349 struct ipw_softc *sc = priv; 2350 struct ifnet *ifp = sc->sc_ifp; 2351 struct ieee80211com *ic = ifp->if_l2com; 2352 2353 IPW_LOCK(sc); 2354 ipw_init_locked(sc); 2355 IPW_UNLOCK(sc); 2356 2357 if (ifp->if_drv_flags & IFF_DRV_RUNNING) 2358 ieee80211_start_all(ic); /* start all vap's */ 2359} 2360 2361static void 2362ipw_init_locked(struct ipw_softc *sc) 2363{ 2364 struct ifnet *ifp = sc->sc_ifp; 2365 struct ieee80211com *ic = ifp->if_l2com; 2366 struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 2367 const struct firmware *fp; 2368 const struct ipw_firmware_hdr *hdr; 2369 const char *fw; 2370 2371 IPW_LOCK_ASSERT(sc); 2372 2373 DPRINTF(("%s: state %s flags 0x%x\n", __func__, 2374 ieee80211_state_name[vap->iv_state], sc->flags)); 2375 2376 /* 2377 * Avoid re-entrant calls. We need to release the mutex in ipw_init() 2378 * when loading the firmware and we don't want to be called during this 2379 * operation. 2380 */ 2381 if (sc->flags & IPW_FLAG_INIT_LOCKED) 2382 return; 2383 sc->flags |= IPW_FLAG_INIT_LOCKED; 2384 2385 ipw_stop_locked(sc); 2386 2387 if (ipw_reset(sc) != 0) { 2388 device_printf(sc->sc_dev, "could not reset adapter\n"); 2389 goto fail; 2390 } 2391 2392 if (sc->sc_firmware == NULL) { 2393 device_printf(sc->sc_dev, "no firmware\n"); 2394 goto fail; 2395 } 2396 /* NB: consistency already checked on load */ 2397 fp = sc->sc_firmware; 2398 hdr = (const struct ipw_firmware_hdr *)fp->data; 2399 2400 DPRINTF(("Loading firmware image '%s'\n", fp->name)); 2401 fw = (const char *)fp->data + sizeof *hdr + le32toh(hdr->mainsz); 2402 if (ipw_load_ucode(sc, fw, le32toh(hdr->ucodesz)) != 0) { 2403 device_printf(sc->sc_dev, "could not load microcode\n"); 2404 goto fail; 2405 } 2406 2407 ipw_stop_master(sc); 2408 2409 /* 2410 * Setup tx, rx and status rings. 2411 */ 2412 sc->txold = IPW_NTBD - 1; 2413 sc->txcur = 0; 2414 sc->txfree = IPW_NTBD - 2; 2415 sc->rxcur = IPW_NRBD - 1; 2416 2417 CSR_WRITE_4(sc, IPW_CSR_TX_BASE, sc->tbd_phys); 2418 CSR_WRITE_4(sc, IPW_CSR_TX_SIZE, IPW_NTBD); 2419 CSR_WRITE_4(sc, IPW_CSR_TX_READ, 0); 2420 CSR_WRITE_4(sc, IPW_CSR_TX_WRITE, sc->txcur); 2421 2422 CSR_WRITE_4(sc, IPW_CSR_RX_BASE, sc->rbd_phys); 2423 CSR_WRITE_4(sc, IPW_CSR_RX_SIZE, IPW_NRBD); 2424 CSR_WRITE_4(sc, IPW_CSR_RX_READ, 0); 2425 CSR_WRITE_4(sc, IPW_CSR_RX_WRITE, sc->rxcur); 2426 2427 CSR_WRITE_4(sc, IPW_CSR_STATUS_BASE, sc->status_phys); 2428 2429 fw = (const char *)fp->data + sizeof *hdr; 2430 if (ipw_load_firmware(sc, fw, le32toh(hdr->mainsz)) != 0) { 2431 device_printf(sc->sc_dev, "could not load firmware\n"); 2432 goto fail; 2433 } 2434 2435 sc->flags |= IPW_FLAG_FW_INITED; 2436 2437 /* retrieve information tables base addresses */ 2438 sc->table1_base = CSR_READ_4(sc, IPW_CSR_TABLE1_BASE); 2439 sc->table2_base = CSR_READ_4(sc, IPW_CSR_TABLE2_BASE); 2440 2441 ipw_write_table1(sc, IPW_INFO_LOCK, 0); 2442 2443 if (ipw_config(sc) != 0) { 2444 device_printf(sc->sc_dev, "device configuration failed\n"); 2445 goto fail; 2446 } 2447 2448 callout_reset(&sc->sc_wdtimer, hz, ipw_watchdog, sc); 2449 ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 2450 ifp->if_drv_flags |= IFF_DRV_RUNNING; 2451 2452 sc->flags &=~ IPW_FLAG_INIT_LOCKED; 2453 return; 2454 2455fail: 2456 ipw_stop_locked(sc); 2457 sc->flags &=~ IPW_FLAG_INIT_LOCKED; 2458} 2459 2460static int 2461ipw_config(struct ipw_softc *sc) 2462{ 2463 struct ifnet *ifp = sc->sc_ifp; 2464 struct ieee80211com *ic = ifp->if_l2com; 2465 struct ipw_configuration config; 2466 uint32_t data; 2467 int error; 2468 2469 error = ipw_disable(sc); 2470 if (error != 0) 2471 return error; 2472 2473 switch (ic->ic_opmode) { 2474 case IEEE80211_M_STA: 2475 case IEEE80211_M_HOSTAP: 2476 case IEEE80211_M_WDS: /* XXX */ 2477 data = htole32(IPW_MODE_BSS); 2478 break; 2479 case IEEE80211_M_IBSS: 2480 case IEEE80211_M_AHDEMO: 2481 data = htole32(IPW_MODE_IBSS); 2482 break; 2483 case IEEE80211_M_MONITOR: 2484 data = htole32(IPW_MODE_MONITOR); 2485 break; 2486 default: 2487 device_printf(sc->sc_dev, "unknown opmode %d\n", ic->ic_opmode); 2488 return EINVAL; 2489 } 2490 DPRINTF(("Setting mode to %u\n", le32toh(data))); 2491 error = ipw_cmd(sc, IPW_CMD_SET_MODE, &data, sizeof data); 2492 if (error != 0) 2493 return error; 2494 2495 if (ic->ic_opmode == IEEE80211_M_IBSS || 2496 ic->ic_opmode == IEEE80211_M_MONITOR) { 2497 error = ipw_setchannel(sc, ic->ic_curchan); 2498 if (error != 0) 2499 return error; 2500 } 2501 2502 if (ic->ic_opmode == IEEE80211_M_MONITOR) 2503 return ipw_enable(sc); 2504 2505 config.flags = htole32(IPW_CFG_BSS_MASK | IPW_CFG_IBSS_MASK | 2506 IPW_CFG_PREAMBLE_AUTO | IPW_CFG_802_1x_ENABLE); 2507 if (ic->ic_opmode == IEEE80211_M_IBSS) 2508 config.flags |= htole32(IPW_CFG_IBSS_AUTO_START); 2509 if (ifp->if_flags & IFF_PROMISC) 2510 config.flags |= htole32(IPW_CFG_PROMISCUOUS); 2511 config.bss_chan = htole32(0x3fff); /* channels 1-14 */ 2512 config.ibss_chan = htole32(0x7ff); /* channels 1-11 */ 2513 DPRINTF(("Setting configuration to 0x%x\n", le32toh(config.flags))); 2514 error = ipw_cmd(sc, IPW_CMD_SET_CONFIGURATION, &config, sizeof config); 2515 if (error != 0) 2516 return error; 2517 2518 data = htole32(0xf); /* 1, 2, 5.5, 11 */ 2519 DPRINTF(("Setting basic tx rates to 0x%x\n", le32toh(data))); 2520 error = ipw_cmd(sc, IPW_CMD_SET_BASIC_TX_RATES, &data, sizeof data); 2521 if (error != 0) 2522 return error; 2523 2524 /* Use the same rate set */ 2525 DPRINTF(("Setting msdu tx rates to 0x%x\n", le32toh(data))); 2526 error = ipw_cmd(sc, IPW_CMD_SET_MSDU_TX_RATES, &data, sizeof data); 2527 if (error != 0) 2528 return error; 2529 2530 /* Use the same rate set */ 2531 DPRINTF(("Setting tx rates to 0x%x\n", le32toh(data))); 2532 error = ipw_cmd(sc, IPW_CMD_SET_TX_RATES, &data, sizeof data); 2533 if (error != 0) 2534 return error; 2535 2536 data = htole32(IPW_POWER_MODE_CAM); 2537 DPRINTF(("Setting power mode to %u\n", le32toh(data))); 2538 error = ipw_cmd(sc, IPW_CMD_SET_POWER_MODE, &data, sizeof data); 2539 if (error != 0) 2540 return error; 2541 2542 if (ic->ic_opmode == IEEE80211_M_IBSS) { 2543 data = htole32(32); /* default value */ 2544 DPRINTF(("Setting tx power index to %u\n", le32toh(data))); 2545 error = ipw_cmd(sc, IPW_CMD_SET_TX_POWER_INDEX, &data, 2546 sizeof data); 2547 if (error != 0) 2548 return error; 2549 } 2550 2551 return 0; 2552} 2553 2554static void 2555ipw_stop(void *priv) 2556{ 2557 struct ipw_softc *sc = priv; 2558 2559 IPW_LOCK(sc); 2560 ipw_stop_locked(sc); 2561 IPW_UNLOCK(sc); 2562} 2563 2564static void 2565ipw_stop_locked(struct ipw_softc *sc) 2566{ 2567 struct ifnet *ifp = sc->sc_ifp; 2568 int i; 2569 2570 IPW_LOCK_ASSERT(sc); 2571 2572 callout_stop(&sc->sc_wdtimer); 2573 ipw_stop_master(sc); 2574 2575 CSR_WRITE_4(sc, IPW_CSR_RST, IPW_RST_SW_RESET); 2576 2577 /* 2578 * Release tx buffers. 2579 */ 2580 for (i = 0; i < IPW_NTBD; i++) 2581 ipw_release_sbd(sc, &sc->stbd_list[i]); 2582 2583 sc->sc_tx_timer = 0; 2584 ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 2585} 2586 2587static int 2588ipw_sysctl_stats(SYSCTL_HANDLER_ARGS) 2589{ 2590 struct ipw_softc *sc = arg1; 2591 uint32_t i, size, buf[256]; 2592 2593 memset(buf, 0, sizeof buf); 2594 2595 if (!(sc->flags & IPW_FLAG_FW_INITED)) 2596 return SYSCTL_OUT(req, buf, sizeof buf); 2597 2598 CSR_WRITE_4(sc, IPW_CSR_AUTOINC_ADDR, sc->table1_base); 2599 2600 size = min(CSR_READ_4(sc, IPW_CSR_AUTOINC_DATA), 256); 2601 for (i = 1; i < size; i++) 2602 buf[i] = MEM_READ_4(sc, CSR_READ_4(sc, IPW_CSR_AUTOINC_DATA)); 2603 2604 return SYSCTL_OUT(req, buf, size); 2605} 2606 2607static int 2608ipw_sysctl_radio(SYSCTL_HANDLER_ARGS) 2609{ 2610 struct ipw_softc *sc = arg1; 2611 int val; 2612 2613 val = !((sc->flags & IPW_FLAG_HAS_RADIO_SWITCH) && 2614 (CSR_READ_4(sc, IPW_CSR_IO) & IPW_IO_RADIO_DISABLED)); 2615 2616 return SYSCTL_OUT(req, &val, sizeof val); 2617} 2618 2619static uint32_t 2620ipw_read_table1(struct ipw_softc *sc, uint32_t off) 2621{ 2622 return MEM_READ_4(sc, MEM_READ_4(sc, sc->table1_base + off)); 2623} 2624 2625static void 2626ipw_write_table1(struct ipw_softc *sc, uint32_t off, uint32_t info) 2627{ 2628 MEM_WRITE_4(sc, MEM_READ_4(sc, sc->table1_base + off), info); 2629} 2630 2631#if 0 2632static int 2633ipw_read_table2(struct ipw_softc *sc, uint32_t off, void *buf, uint32_t *len) 2634{ 2635 uint32_t addr, info; 2636 uint16_t count, size; 2637 uint32_t total; 2638 2639 /* addr[4] + count[2] + size[2] */ 2640 addr = MEM_READ_4(sc, sc->table2_base + off); 2641 info = MEM_READ_4(sc, sc->table2_base + off + 4); 2642 2643 count = info >> 16; 2644 size = info & 0xffff; 2645 total = count * size; 2646 2647 if (total > *len) { 2648 *len = total; 2649 return EINVAL; 2650 } 2651 2652 *len = total; 2653 ipw_read_mem_1(sc, addr, buf, total); 2654 2655 return 0; 2656} 2657 2658static void 2659ipw_read_mem_1(struct ipw_softc *sc, bus_size_t offset, uint8_t *datap, 2660 bus_size_t count) 2661{ 2662 for (; count > 0; offset++, datap++, count--) { 2663 CSR_WRITE_4(sc, IPW_CSR_INDIRECT_ADDR, offset & ~3); 2664 *datap = CSR_READ_1(sc, IPW_CSR_INDIRECT_DATA + (offset & 3)); 2665 } 2666} 2667#endif 2668 2669static void 2670ipw_write_mem_1(struct ipw_softc *sc, bus_size_t offset, const uint8_t *datap, 2671 bus_size_t count) 2672{ 2673 for (; count > 0; offset++, datap++, count--) { 2674 CSR_WRITE_4(sc, IPW_CSR_INDIRECT_ADDR, offset & ~3); 2675 CSR_WRITE_1(sc, IPW_CSR_INDIRECT_DATA + (offset & 3), *datap); 2676 } 2677} 2678 2679static void 2680ipw_scan_start(struct ieee80211com *ic) 2681{ 2682 struct ifnet *ifp = ic->ic_ifp; 2683 struct ipw_softc *sc = ifp->if_softc; 2684 2685 IPW_LOCK(sc); 2686 ipw_scan(sc); 2687 IPW_UNLOCK(sc); 2688} 2689 2690static void 2691ipw_set_channel(struct ieee80211com *ic) 2692{ 2693 struct ifnet *ifp = ic->ic_ifp; 2694 struct ipw_softc *sc = ifp->if_softc; 2695 2696 IPW_LOCK(sc); 2697 if (ic->ic_opmode == IEEE80211_M_MONITOR) { 2698 ipw_disable(sc); 2699 ipw_setchannel(sc, ic->ic_curchan); 2700 ipw_enable(sc); 2701 } 2702 IPW_UNLOCK(sc); 2703} 2704 2705static void 2706ipw_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell) 2707{ 2708 /* NB: all channels are scanned at once */ 2709} 2710 2711static void 2712ipw_scan_mindwell(struct ieee80211_scan_state *ss) 2713{ 2714 /* NB: don't try to abort scan; wait for firmware to finish */ 2715} 2716 2717static void 2718ipw_scan_end(struct ieee80211com *ic) 2719{ 2720 struct ifnet *ifp = ic->ic_ifp; 2721 struct ipw_softc *sc = ifp->if_softc; 2722 2723 IPW_LOCK(sc); 2724 sc->flags &= ~IPW_FLAG_SCANNING; 2725 IPW_UNLOCK(sc); 2726} 2727