if_wpi.c revision 271849
1173362Sbenjsc/*- 2173362Sbenjsc * Copyright (c) 2006,2007 3173362Sbenjsc * Damien Bergamini <damien.bergamini@free.fr> 4173362Sbenjsc * Benjamin Close <Benjamin.Close@clearchain.com> 5173362Sbenjsc * 6173362Sbenjsc * Permission to use, copy, modify, and distribute this software for any 7173362Sbenjsc * purpose with or without fee is hereby granted, provided that the above 8173362Sbenjsc * copyright notice and this permission notice appear in all copies. 9173362Sbenjsc * 10173362Sbenjsc * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11173362Sbenjsc * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12173362Sbenjsc * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13173362Sbenjsc * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14173362Sbenjsc * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15173362Sbenjsc * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16173362Sbenjsc * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17173362Sbenjsc */ 18173362Sbenjsc 19173977Sbenjsc#define VERSION "20071127" 20173362Sbenjsc 21173362Sbenjsc#include <sys/cdefs.h> 22173362Sbenjsc__FBSDID("$FreeBSD: head/sys/dev/wpi/if_wpi.c 271849 2014-09-19 03:51:26Z glebius $"); 23173362Sbenjsc 24173362Sbenjsc/* 25173362Sbenjsc * Driver for Intel PRO/Wireless 3945ABG 802.11 network adapters. 26173362Sbenjsc * 27173362Sbenjsc * The 3945ABG network adapter doesn't use traditional hardware as 28173362Sbenjsc * many other adaptors do. Instead at run time the eeprom is set into a known 29173362Sbenjsc * state and told to load boot firmware. The boot firmware loads an init and a 30173362Sbenjsc * main binary firmware image into SRAM on the card via DMA. 31173362Sbenjsc * Once the firmware is loaded, the driver/hw then 32218909Sbrucec * communicate by way of circular dma rings via the SRAM to the firmware. 33173362Sbenjsc * 34173362Sbenjsc * There is 6 memory rings. 1 command ring, 1 rx data ring & 4 tx data rings. 35173362Sbenjsc * The 4 tx data rings allow for prioritization QoS. 36173362Sbenjsc * 37173362Sbenjsc * The rx data ring consists of 32 dma buffers. Two registers are used to 38173362Sbenjsc * indicate where in the ring the driver and the firmware are up to. The 39173362Sbenjsc * driver sets the initial read index (reg1) and the initial write index (reg2), 40173362Sbenjsc * the firmware updates the read index (reg1) on rx of a packet and fires an 41173362Sbenjsc * interrupt. The driver then processes the buffers starting at reg1 indicating 42173362Sbenjsc * to the firmware which buffers have been accessed by updating reg2. At the 43173362Sbenjsc * same time allocating new memory for the processed buffer. 44173362Sbenjsc * 45173362Sbenjsc * A similar thing happens with the tx rings. The difference is the firmware 46173362Sbenjsc * stop processing buffers once the queue is full and until confirmation 47173362Sbenjsc * of a successful transmition (tx_intr) has occurred. 48173362Sbenjsc * 49173362Sbenjsc * The command ring operates in the same manner as the tx queues. 50173362Sbenjsc * 51173362Sbenjsc * All communication direct to the card (ie eeprom) is classed as Stage1 52173362Sbenjsc * communication 53173362Sbenjsc * 54173362Sbenjsc * All communication via the firmware to the card is classed as State2. 55173362Sbenjsc * The firmware consists of 2 parts. A bootstrap firmware and a runtime 56173362Sbenjsc * firmware. The bootstrap firmware and runtime firmware are loaded 57173362Sbenjsc * from host memory via dma to the card then told to execute. From this point 58173362Sbenjsc * on the majority of communications between the driver and the card goes 59173362Sbenjsc * via the firmware. 60173362Sbenjsc */ 61173362Sbenjsc 62236381Sadrian#include "opt_wlan.h" 63236381Sadrian 64173362Sbenjsc#include <sys/param.h> 65173362Sbenjsc#include <sys/sysctl.h> 66173362Sbenjsc#include <sys/sockio.h> 67173362Sbenjsc#include <sys/mbuf.h> 68173362Sbenjsc#include <sys/kernel.h> 69173362Sbenjsc#include <sys/socket.h> 70173362Sbenjsc#include <sys/systm.h> 71173362Sbenjsc#include <sys/malloc.h> 72173362Sbenjsc#include <sys/queue.h> 73173362Sbenjsc#include <sys/taskqueue.h> 74173362Sbenjsc#include <sys/module.h> 75173362Sbenjsc#include <sys/bus.h> 76173362Sbenjsc#include <sys/endian.h> 77173362Sbenjsc#include <sys/linker.h> 78173362Sbenjsc#include <sys/firmware.h> 79173362Sbenjsc 80173362Sbenjsc#include <machine/bus.h> 81173362Sbenjsc#include <machine/resource.h> 82173362Sbenjsc#include <sys/rman.h> 83173362Sbenjsc 84173362Sbenjsc#include <dev/pci/pcireg.h> 85173362Sbenjsc#include <dev/pci/pcivar.h> 86173362Sbenjsc 87173362Sbenjsc#include <net/bpf.h> 88173362Sbenjsc#include <net/if.h> 89257176Sglebius#include <net/if_var.h> 90173362Sbenjsc#include <net/if_arp.h> 91173362Sbenjsc#include <net/ethernet.h> 92173362Sbenjsc#include <net/if_dl.h> 93173362Sbenjsc#include <net/if_media.h> 94173362Sbenjsc#include <net/if_types.h> 95173362Sbenjsc 96173362Sbenjsc#include <net80211/ieee80211_var.h> 97173362Sbenjsc#include <net80211/ieee80211_radiotap.h> 98173362Sbenjsc#include <net80211/ieee80211_regdomain.h> 99206358Srpaulo#include <net80211/ieee80211_ratectl.h> 100173362Sbenjsc 101173362Sbenjsc#include <netinet/in.h> 102173362Sbenjsc#include <netinet/in_systm.h> 103173362Sbenjsc#include <netinet/in_var.h> 104173362Sbenjsc#include <netinet/ip.h> 105173362Sbenjsc#include <netinet/if_ether.h> 106173362Sbenjsc 107173362Sbenjsc#include <dev/wpi/if_wpireg.h> 108173362Sbenjsc#include <dev/wpi/if_wpivar.h> 109173362Sbenjsc 110199037Sdougb#define WPI_DEBUG 111199037Sdougb 112173362Sbenjsc#ifdef WPI_DEBUG 113173362Sbenjsc#define DPRINTF(x) do { if (wpi_debug != 0) printf x; } while (0) 114173362Sbenjsc#define DPRINTFN(n, x) do { if (wpi_debug & n) printf x; } while (0) 115179957Sthompsa#define WPI_DEBUG_SET (wpi_debug != 0) 116173362Sbenjsc 117173362Sbenjscenum { 118173362Sbenjsc WPI_DEBUG_UNUSED = 0x00000001, /* Unused */ 119173362Sbenjsc WPI_DEBUG_HW = 0x00000002, /* Stage 1 (eeprom) debugging */ 120173362Sbenjsc WPI_DEBUG_TX = 0x00000004, /* Stage 2 TX intrp debugging*/ 121173362Sbenjsc WPI_DEBUG_RX = 0x00000008, /* Stage 2 RX intrp debugging */ 122173362Sbenjsc WPI_DEBUG_CMD = 0x00000010, /* Stage 2 CMD intrp debugging*/ 123173362Sbenjsc WPI_DEBUG_FIRMWARE = 0x00000020, /* firmware(9) loading debug */ 124173362Sbenjsc WPI_DEBUG_DMA = 0x00000040, /* DMA (de)allocations/syncs */ 125173362Sbenjsc WPI_DEBUG_SCANNING = 0x00000080, /* Stage 2 Scanning debugging */ 126173362Sbenjsc WPI_DEBUG_NOTIFY = 0x00000100, /* State 2 Noftif intr debug */ 127173362Sbenjsc WPI_DEBUG_TEMP = 0x00000200, /* TXPower/Temp Calibration */ 128173362Sbenjsc WPI_DEBUG_OPS = 0x00000400, /* wpi_ops taskq debug */ 129173362Sbenjsc WPI_DEBUG_WATCHDOG = 0x00000800, /* Watch dog debug */ 130173362Sbenjsc WPI_DEBUG_ANY = 0xffffffff 131173362Sbenjsc}; 132173362Sbenjsc 133267992Shselaskystatic int wpi_debug; 134267992ShselaskySYSCTL_INT(_debug, OID_AUTO, wpi, CTLFLAG_RWTUN, &wpi_debug, 0, "wpi debug level"); 135173362Sbenjsc 136173362Sbenjsc#else 137173362Sbenjsc#define DPRINTF(x) 138173362Sbenjsc#define DPRINTFN(n, x) 139179957Sthompsa#define WPI_DEBUG_SET 0 140173362Sbenjsc#endif 141173362Sbenjsc 142173362Sbenjscstruct wpi_ident { 143173362Sbenjsc uint16_t vendor; 144173362Sbenjsc uint16_t device; 145173362Sbenjsc uint16_t subdevice; 146173362Sbenjsc const char *name; 147173362Sbenjsc}; 148173362Sbenjsc 149173362Sbenjscstatic const struct wpi_ident wpi_ident_table[] = { 150173362Sbenjsc /* The below entries support ABG regardless of the subid */ 151173362Sbenjsc { 0x8086, 0x4222, 0x0, "Intel(R) PRO/Wireless 3945ABG" }, 152173362Sbenjsc { 0x8086, 0x4227, 0x0, "Intel(R) PRO/Wireless 3945ABG" }, 153173362Sbenjsc /* The below entries only support BG */ 154182127Sbenjsc { 0x8086, 0x4222, 0x1005, "Intel(R) PRO/Wireless 3945BG" }, 155182127Sbenjsc { 0x8086, 0x4222, 0x1034, "Intel(R) PRO/Wireless 3945BG" }, 156182127Sbenjsc { 0x8086, 0x4227, 0x1014, "Intel(R) PRO/Wireless 3945BG" }, 157182127Sbenjsc { 0x8086, 0x4222, 0x1044, "Intel(R) PRO/Wireless 3945BG" }, 158173362Sbenjsc { 0, 0, 0, NULL } 159173362Sbenjsc}; 160173362Sbenjsc 161178354Ssamstatic struct ieee80211vap *wpi_vap_create(struct ieee80211com *, 162228621Sbschmidt const char [IFNAMSIZ], int, enum ieee80211_opmode, int, 163228621Sbschmidt const uint8_t [IEEE80211_ADDR_LEN], 164228621Sbschmidt const uint8_t [IEEE80211_ADDR_LEN]); 165178354Ssamstatic void wpi_vap_delete(struct ieee80211vap *); 166173362Sbenjscstatic int wpi_dma_contig_alloc(struct wpi_softc *, struct wpi_dma_info *, 167173362Sbenjsc void **, bus_size_t, bus_size_t, int); 168173362Sbenjscstatic void wpi_dma_contig_free(struct wpi_dma_info *); 169173362Sbenjscstatic void wpi_dma_map_addr(void *, bus_dma_segment_t *, int, int); 170173362Sbenjscstatic int wpi_alloc_shared(struct wpi_softc *); 171173362Sbenjscstatic void wpi_free_shared(struct wpi_softc *); 172173362Sbenjscstatic int wpi_alloc_rx_ring(struct wpi_softc *, struct wpi_rx_ring *); 173173362Sbenjscstatic void wpi_reset_rx_ring(struct wpi_softc *, struct wpi_rx_ring *); 174173362Sbenjscstatic void wpi_free_rx_ring(struct wpi_softc *, struct wpi_rx_ring *); 175173362Sbenjscstatic int wpi_alloc_tx_ring(struct wpi_softc *, struct wpi_tx_ring *, 176173362Sbenjsc int, int); 177173362Sbenjscstatic void wpi_reset_tx_ring(struct wpi_softc *, struct wpi_tx_ring *); 178173362Sbenjscstatic void wpi_free_tx_ring(struct wpi_softc *, struct wpi_tx_ring *); 179178354Ssamstatic int wpi_newstate(struct ieee80211vap *, enum ieee80211_state, int); 180173362Sbenjscstatic void wpi_mem_lock(struct wpi_softc *); 181173362Sbenjscstatic void wpi_mem_unlock(struct wpi_softc *); 182173362Sbenjscstatic uint32_t wpi_mem_read(struct wpi_softc *, uint16_t); 183173362Sbenjscstatic void wpi_mem_write(struct wpi_softc *, uint16_t, uint32_t); 184173362Sbenjscstatic void wpi_mem_write_region_4(struct wpi_softc *, uint16_t, 185173362Sbenjsc const uint32_t *, int); 186173362Sbenjscstatic uint16_t wpi_read_prom_data(struct wpi_softc *, uint32_t, void *, int); 187173362Sbenjscstatic int wpi_alloc_fwmem(struct wpi_softc *); 188173362Sbenjscstatic void wpi_free_fwmem(struct wpi_softc *); 189173362Sbenjscstatic int wpi_load_firmware(struct wpi_softc *); 190173362Sbenjscstatic void wpi_unload_firmware(struct wpi_softc *); 191173362Sbenjscstatic int wpi_load_microcode(struct wpi_softc *, const uint8_t *, int); 192173362Sbenjscstatic void wpi_rx_intr(struct wpi_softc *, struct wpi_rx_desc *, 193173362Sbenjsc struct wpi_rx_data *); 194173362Sbenjscstatic void wpi_tx_intr(struct wpi_softc *, struct wpi_rx_desc *); 195173362Sbenjscstatic void wpi_cmd_intr(struct wpi_softc *, struct wpi_rx_desc *); 196173362Sbenjscstatic void wpi_notif_intr(struct wpi_softc *); 197173362Sbenjscstatic void wpi_intr(void *); 198173362Sbenjscstatic uint8_t wpi_plcp_signal(int); 199177043Sthompsastatic void wpi_watchdog(void *); 200173362Sbenjscstatic int wpi_tx_data(struct wpi_softc *, struct mbuf *, 201173362Sbenjsc struct ieee80211_node *, int); 202173362Sbenjscstatic void wpi_start(struct ifnet *); 203178354Ssamstatic void wpi_start_locked(struct ifnet *); 204178354Ssamstatic int wpi_raw_xmit(struct ieee80211_node *, struct mbuf *, 205178354Ssam const struct ieee80211_bpf_params *); 206173362Sbenjscstatic void wpi_scan_start(struct ieee80211com *); 207173362Sbenjscstatic void wpi_scan_end(struct ieee80211com *); 208173362Sbenjscstatic void wpi_set_channel(struct ieee80211com *); 209178354Ssamstatic void wpi_scan_curchan(struct ieee80211_scan_state *, unsigned long); 210178354Ssamstatic void wpi_scan_mindwell(struct ieee80211_scan_state *); 211173362Sbenjscstatic int wpi_ioctl(struct ifnet *, u_long, caddr_t); 212190526Ssamstatic void wpi_read_eeprom(struct wpi_softc *, 213190526Ssam uint8_t macaddr[IEEE80211_ADDR_LEN]); 214173362Sbenjscstatic void wpi_read_eeprom_channels(struct wpi_softc *, int); 215173362Sbenjscstatic void wpi_read_eeprom_group(struct wpi_softc *, int); 216173362Sbenjscstatic int wpi_cmd(struct wpi_softc *, int, const void *, int, int); 217173362Sbenjscstatic int wpi_wme_update(struct ieee80211com *); 218173362Sbenjscstatic int wpi_mrr_setup(struct wpi_softc *); 219173362Sbenjscstatic void wpi_set_led(struct wpi_softc *, uint8_t, uint8_t, uint8_t); 220173362Sbenjscstatic void wpi_enable_tsf(struct wpi_softc *, struct ieee80211_node *); 221173362Sbenjsc#if 0 222173362Sbenjscstatic int wpi_setup_beacon(struct wpi_softc *, struct ieee80211_node *); 223173362Sbenjsc#endif 224178354Ssamstatic int wpi_auth(struct wpi_softc *, struct ieee80211vap *); 225178354Ssamstatic int wpi_run(struct wpi_softc *, struct ieee80211vap *); 226173362Sbenjscstatic int wpi_scan(struct wpi_softc *); 227173362Sbenjscstatic int wpi_config(struct wpi_softc *); 228173362Sbenjscstatic void wpi_stop_master(struct wpi_softc *); 229173362Sbenjscstatic int wpi_power_up(struct wpi_softc *); 230173362Sbenjscstatic int wpi_reset(struct wpi_softc *); 231191746Sthompsastatic void wpi_hwreset(void *, int); 232191746Sthompsastatic void wpi_rfreset(void *, int); 233173362Sbenjscstatic void wpi_hw_config(struct wpi_softc *); 234173362Sbenjscstatic void wpi_init(void *); 235177043Sthompsastatic void wpi_init_locked(struct wpi_softc *, int); 236173362Sbenjscstatic void wpi_stop(struct wpi_softc *); 237173362Sbenjscstatic void wpi_stop_locked(struct wpi_softc *); 238173362Sbenjsc 239173362Sbenjscstatic int wpi_set_txpower(struct wpi_softc *, struct ieee80211_channel *, 240173362Sbenjsc int); 241173362Sbenjscstatic void wpi_calib_timeout(void *); 242173362Sbenjscstatic void wpi_power_calibration(struct wpi_softc *, int); 243173362Sbenjscstatic int wpi_get_power_index(struct wpi_softc *, 244173362Sbenjsc struct wpi_power_group *, struct ieee80211_channel *, int); 245179957Sthompsa#ifdef WPI_DEBUG 246173362Sbenjscstatic const char *wpi_cmd_str(int); 247179957Sthompsa#endif 248173362Sbenjscstatic int wpi_probe(device_t); 249173362Sbenjscstatic int wpi_attach(device_t); 250173362Sbenjscstatic int wpi_detach(device_t); 251173362Sbenjscstatic int wpi_shutdown(device_t); 252173362Sbenjscstatic int wpi_suspend(device_t); 253173362Sbenjscstatic int wpi_resume(device_t); 254173362Sbenjsc 255173362Sbenjscstatic device_method_t wpi_methods[] = { 256173362Sbenjsc /* Device interface */ 257173362Sbenjsc DEVMETHOD(device_probe, wpi_probe), 258173362Sbenjsc DEVMETHOD(device_attach, wpi_attach), 259173362Sbenjsc DEVMETHOD(device_detach, wpi_detach), 260173362Sbenjsc DEVMETHOD(device_shutdown, wpi_shutdown), 261173362Sbenjsc DEVMETHOD(device_suspend, wpi_suspend), 262173362Sbenjsc DEVMETHOD(device_resume, wpi_resume), 263173362Sbenjsc 264260064Smarius DEVMETHOD_END 265173362Sbenjsc}; 266173362Sbenjsc 267173362Sbenjscstatic driver_t wpi_driver = { 268173362Sbenjsc "wpi", 269173362Sbenjsc wpi_methods, 270173362Sbenjsc sizeof (struct wpi_softc) 271173362Sbenjsc}; 272173362Sbenjsc 273173362Sbenjscstatic devclass_t wpi_devclass; 274173362Sbenjsc 275260064SmariusDRIVER_MODULE(wpi, pci, wpi_driver, wpi_devclass, NULL, NULL); 276173362Sbenjsc 277222543SbschmidtMODULE_VERSION(wpi, 1); 278222543Sbschmidt 279173362Sbenjscstatic const uint8_t wpi_ridx_to_plcp[] = { 280173362Sbenjsc /* OFDM: IEEE Std 802.11a-1999, pp. 14 Table 80 */ 281173362Sbenjsc /* R1-R4 (ral/ural is R4-R1) */ 282173362Sbenjsc 0xd, 0xf, 0x5, 0x7, 0x9, 0xb, 0x1, 0x3, 283173362Sbenjsc /* CCK: device-dependent */ 284173362Sbenjsc 10, 20, 55, 110 285173362Sbenjsc}; 286260064Smarius 287173362Sbenjscstatic const uint8_t wpi_ridx_to_rate[] = { 288173362Sbenjsc 12, 18, 24, 36, 48, 72, 96, 108, /* OFDM */ 289173362Sbenjsc 2, 4, 11, 22 /*CCK */ 290173362Sbenjsc}; 291173362Sbenjsc 292173362Sbenjscstatic int 293173362Sbenjscwpi_probe(device_t dev) 294173362Sbenjsc{ 295173362Sbenjsc const struct wpi_ident *ident; 296173362Sbenjsc 297173362Sbenjsc for (ident = wpi_ident_table; ident->name != NULL; ident++) { 298173362Sbenjsc if (pci_get_vendor(dev) == ident->vendor && 299173362Sbenjsc pci_get_device(dev) == ident->device) { 300173362Sbenjsc device_set_desc(dev, ident->name); 301260064Smarius return (BUS_PROBE_DEFAULT); 302173362Sbenjsc } 303173362Sbenjsc } 304173362Sbenjsc return ENXIO; 305173362Sbenjsc} 306173362Sbenjsc 307173362Sbenjsc/** 308173362Sbenjsc * Load the firmare image from disk to the allocated dma buffer. 309173362Sbenjsc * we also maintain the reference to the firmware pointer as there 310173362Sbenjsc * is times where we may need to reload the firmware but we are not 311173362Sbenjsc * in a context that can access the filesystem (ie taskq cause by restart) 312173362Sbenjsc * 313173362Sbenjsc * @return 0 on success, an errno on failure 314173362Sbenjsc */ 315173362Sbenjscstatic int 316173362Sbenjscwpi_load_firmware(struct wpi_softc *sc) 317173362Sbenjsc{ 318178354Ssam const struct firmware *fp; 319173362Sbenjsc struct wpi_dma_info *dma = &sc->fw_dma; 320173362Sbenjsc const struct wpi_firmware_hdr *hdr; 321173362Sbenjsc const uint8_t *itext, *idata, *rtext, *rdata, *btext; 322173362Sbenjsc uint32_t itextsz, idatasz, rtextsz, rdatasz, btextsz; 323173362Sbenjsc int error; 324173362Sbenjsc 325173362Sbenjsc DPRINTFN(WPI_DEBUG_FIRMWARE, 326173362Sbenjsc ("Attempting Loading Firmware from wpi_fw module\n")); 327173362Sbenjsc 328173362Sbenjsc WPI_UNLOCK(sc); 329173362Sbenjsc 330173362Sbenjsc if (sc->fw_fp == NULL && (sc->fw_fp = firmware_get("wpifw")) == NULL) { 331173362Sbenjsc device_printf(sc->sc_dev, 332173362Sbenjsc "could not load firmware image 'wpifw'\n"); 333173362Sbenjsc error = ENOENT; 334173362Sbenjsc WPI_LOCK(sc); 335173362Sbenjsc goto fail; 336173362Sbenjsc } 337173362Sbenjsc 338173362Sbenjsc fp = sc->fw_fp; 339173362Sbenjsc 340173362Sbenjsc WPI_LOCK(sc); 341173362Sbenjsc 342173362Sbenjsc /* Validate the firmware is minimum a particular version */ 343173362Sbenjsc if (fp->version < WPI_FW_MINVERSION) { 344173362Sbenjsc device_printf(sc->sc_dev, 345173362Sbenjsc "firmware version is too old. Need %d, got %d\n", 346173362Sbenjsc WPI_FW_MINVERSION, 347173362Sbenjsc fp->version); 348173362Sbenjsc error = ENXIO; 349173362Sbenjsc goto fail; 350173362Sbenjsc } 351173362Sbenjsc 352173362Sbenjsc if (fp->datasize < sizeof (struct wpi_firmware_hdr)) { 353173362Sbenjsc device_printf(sc->sc_dev, 354173362Sbenjsc "firmware file too short: %zu bytes\n", fp->datasize); 355173362Sbenjsc error = ENXIO; 356173362Sbenjsc goto fail; 357173362Sbenjsc } 358173362Sbenjsc 359173362Sbenjsc hdr = (const struct wpi_firmware_hdr *)fp->data; 360173362Sbenjsc 361173362Sbenjsc /* | RUNTIME FIRMWARE | INIT FIRMWARE | BOOT FW | 362173362Sbenjsc |HDR|<--TEXT-->|<--DATA-->|<--TEXT-->|<--DATA-->|<--TEXT-->| */ 363173362Sbenjsc 364173362Sbenjsc rtextsz = le32toh(hdr->rtextsz); 365173362Sbenjsc rdatasz = le32toh(hdr->rdatasz); 366173362Sbenjsc itextsz = le32toh(hdr->itextsz); 367173362Sbenjsc idatasz = le32toh(hdr->idatasz); 368173362Sbenjsc btextsz = le32toh(hdr->btextsz); 369173362Sbenjsc 370173362Sbenjsc /* check that all firmware segments are present */ 371173362Sbenjsc if (fp->datasize < sizeof (struct wpi_firmware_hdr) + 372173362Sbenjsc rtextsz + rdatasz + itextsz + idatasz + btextsz) { 373173362Sbenjsc device_printf(sc->sc_dev, 374173362Sbenjsc "firmware file too short: %zu bytes\n", fp->datasize); 375173362Sbenjsc error = ENXIO; /* XXX appropriate error code? */ 376173362Sbenjsc goto fail; 377173362Sbenjsc } 378173362Sbenjsc 379173362Sbenjsc /* get pointers to firmware segments */ 380173362Sbenjsc rtext = (const uint8_t *)(hdr + 1); 381173362Sbenjsc rdata = rtext + rtextsz; 382173362Sbenjsc itext = rdata + rdatasz; 383173362Sbenjsc idata = itext + itextsz; 384173362Sbenjsc btext = idata + idatasz; 385173362Sbenjsc 386173362Sbenjsc DPRINTFN(WPI_DEBUG_FIRMWARE, 387173362Sbenjsc ("Firmware Version: Major %d, Minor %d, Driver %d, \n" 388173362Sbenjsc "runtime (text: %u, data: %u) init (text: %u, data %u) boot (text %u)\n", 389173362Sbenjsc (le32toh(hdr->version) & 0xff000000) >> 24, 390173362Sbenjsc (le32toh(hdr->version) & 0x00ff0000) >> 16, 391173362Sbenjsc (le32toh(hdr->version) & 0x0000ffff), 392173362Sbenjsc rtextsz, rdatasz, 393173362Sbenjsc itextsz, idatasz, btextsz)); 394173362Sbenjsc 395173362Sbenjsc DPRINTFN(WPI_DEBUG_FIRMWARE,("rtext 0x%x\n", *(const uint32_t *)rtext)); 396173362Sbenjsc DPRINTFN(WPI_DEBUG_FIRMWARE,("rdata 0x%x\n", *(const uint32_t *)rdata)); 397173362Sbenjsc DPRINTFN(WPI_DEBUG_FIRMWARE,("itext 0x%x\n", *(const uint32_t *)itext)); 398173362Sbenjsc DPRINTFN(WPI_DEBUG_FIRMWARE,("idata 0x%x\n", *(const uint32_t *)idata)); 399173362Sbenjsc DPRINTFN(WPI_DEBUG_FIRMWARE,("btext 0x%x\n", *(const uint32_t *)btext)); 400173362Sbenjsc 401173362Sbenjsc /* sanity checks */ 402173362Sbenjsc if (rtextsz > WPI_FW_MAIN_TEXT_MAXSZ || 403173362Sbenjsc rdatasz > WPI_FW_MAIN_DATA_MAXSZ || 404173362Sbenjsc itextsz > WPI_FW_INIT_TEXT_MAXSZ || 405173362Sbenjsc idatasz > WPI_FW_INIT_DATA_MAXSZ || 406173362Sbenjsc btextsz > WPI_FW_BOOT_TEXT_MAXSZ || 407173362Sbenjsc (btextsz & 3) != 0) { 408173362Sbenjsc device_printf(sc->sc_dev, "firmware invalid\n"); 409173362Sbenjsc error = EINVAL; 410173362Sbenjsc goto fail; 411173362Sbenjsc } 412173362Sbenjsc 413173362Sbenjsc /* copy initialization images into pre-allocated DMA-safe memory */ 414173362Sbenjsc memcpy(dma->vaddr, idata, idatasz); 415173362Sbenjsc memcpy(dma->vaddr + WPI_FW_INIT_DATA_MAXSZ, itext, itextsz); 416173362Sbenjsc 417173362Sbenjsc bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); 418173362Sbenjsc 419173362Sbenjsc /* tell adapter where to find initialization images */ 420173362Sbenjsc wpi_mem_lock(sc); 421173362Sbenjsc wpi_mem_write(sc, WPI_MEM_DATA_BASE, dma->paddr); 422173362Sbenjsc wpi_mem_write(sc, WPI_MEM_DATA_SIZE, idatasz); 423173362Sbenjsc wpi_mem_write(sc, WPI_MEM_TEXT_BASE, 424173362Sbenjsc dma->paddr + WPI_FW_INIT_DATA_MAXSZ); 425173362Sbenjsc wpi_mem_write(sc, WPI_MEM_TEXT_SIZE, itextsz); 426173362Sbenjsc wpi_mem_unlock(sc); 427173362Sbenjsc 428173362Sbenjsc /* load firmware boot code */ 429173362Sbenjsc if ((error = wpi_load_microcode(sc, btext, btextsz)) != 0) { 430173362Sbenjsc device_printf(sc->sc_dev, "Failed to load microcode\n"); 431173362Sbenjsc goto fail; 432173362Sbenjsc } 433173362Sbenjsc 434173362Sbenjsc /* now press "execute" */ 435173362Sbenjsc WPI_WRITE(sc, WPI_RESET, 0); 436173362Sbenjsc 437173362Sbenjsc /* wait at most one second for the first alive notification */ 438173362Sbenjsc if ((error = msleep(sc, &sc->sc_mtx, PCATCH, "wpiinit", hz)) != 0) { 439173362Sbenjsc device_printf(sc->sc_dev, 440173362Sbenjsc "timeout waiting for adapter to initialize\n"); 441173362Sbenjsc goto fail; 442173362Sbenjsc } 443173362Sbenjsc 444173362Sbenjsc /* copy runtime images into pre-allocated DMA-sage memory */ 445173362Sbenjsc memcpy(dma->vaddr, rdata, rdatasz); 446173362Sbenjsc memcpy(dma->vaddr + WPI_FW_MAIN_DATA_MAXSZ, rtext, rtextsz); 447173362Sbenjsc bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); 448173362Sbenjsc 449173362Sbenjsc /* tell adapter where to find runtime images */ 450173362Sbenjsc wpi_mem_lock(sc); 451173362Sbenjsc wpi_mem_write(sc, WPI_MEM_DATA_BASE, dma->paddr); 452173362Sbenjsc wpi_mem_write(sc, WPI_MEM_DATA_SIZE, rdatasz); 453173362Sbenjsc wpi_mem_write(sc, WPI_MEM_TEXT_BASE, 454173362Sbenjsc dma->paddr + WPI_FW_MAIN_DATA_MAXSZ); 455173362Sbenjsc wpi_mem_write(sc, WPI_MEM_TEXT_SIZE, WPI_FW_UPDATED | rtextsz); 456173362Sbenjsc wpi_mem_unlock(sc); 457173362Sbenjsc 458173362Sbenjsc /* wait at most one second for the first alive notification */ 459173362Sbenjsc if ((error = msleep(sc, &sc->sc_mtx, PCATCH, "wpiinit", hz)) != 0) { 460173362Sbenjsc device_printf(sc->sc_dev, 461173362Sbenjsc "timeout waiting for adapter to initialize2\n"); 462173362Sbenjsc goto fail; 463173362Sbenjsc } 464173362Sbenjsc 465173362Sbenjsc DPRINTFN(WPI_DEBUG_FIRMWARE, 466173362Sbenjsc ("Firmware loaded to driver successfully\n")); 467173362Sbenjsc return error; 468173362Sbenjscfail: 469173362Sbenjsc wpi_unload_firmware(sc); 470173362Sbenjsc return error; 471173362Sbenjsc} 472173362Sbenjsc 473173362Sbenjsc/** 474173362Sbenjsc * Free the referenced firmware image 475173362Sbenjsc */ 476173362Sbenjscstatic void 477173362Sbenjscwpi_unload_firmware(struct wpi_softc *sc) 478173362Sbenjsc{ 479173362Sbenjsc 480173362Sbenjsc if (sc->fw_fp) { 481173362Sbenjsc WPI_UNLOCK(sc); 482173362Sbenjsc firmware_put(sc->fw_fp, FIRMWARE_UNLOAD); 483173362Sbenjsc WPI_LOCK(sc); 484173362Sbenjsc sc->fw_fp = NULL; 485173362Sbenjsc } 486173362Sbenjsc} 487173362Sbenjsc 488173362Sbenjscstatic int 489173362Sbenjscwpi_attach(device_t dev) 490173362Sbenjsc{ 491173362Sbenjsc struct wpi_softc *sc = device_get_softc(dev); 492173362Sbenjsc struct ifnet *ifp; 493178354Ssam struct ieee80211com *ic; 494260064Smarius int ac, error, rid, supportsa = 1; 495173362Sbenjsc uint32_t tmp; 496173362Sbenjsc const struct wpi_ident *ident; 497190526Ssam uint8_t macaddr[IEEE80211_ADDR_LEN]; 498173362Sbenjsc 499173362Sbenjsc sc->sc_dev = dev; 500173362Sbenjsc 501179957Sthompsa if (bootverbose || WPI_DEBUG_SET) 502173362Sbenjsc device_printf(sc->sc_dev,"Driver Revision %s\n", VERSION); 503173362Sbenjsc 504173362Sbenjsc /* 505173362Sbenjsc * Some card's only support 802.11b/g not a, check to see if 506173362Sbenjsc * this is one such card. A 0x0 in the subdevice table indicates 507173362Sbenjsc * the entire subdevice range is to be ignored. 508173362Sbenjsc */ 509173362Sbenjsc for (ident = wpi_ident_table; ident->name != NULL; ident++) { 510173362Sbenjsc if (ident->subdevice && 511173362Sbenjsc pci_get_subdevice(dev) == ident->subdevice) { 512173362Sbenjsc supportsa = 0; 513173362Sbenjsc break; 514173362Sbenjsc } 515173362Sbenjsc } 516173362Sbenjsc 517173362Sbenjsc /* Create the tasks that can be queued */ 518191746Sthompsa TASK_INIT(&sc->sc_restarttask, 0, wpi_hwreset, sc); 519191746Sthompsa TASK_INIT(&sc->sc_radiotask, 0, wpi_rfreset, sc); 520173362Sbenjsc 521173362Sbenjsc WPI_LOCK_INIT(sc); 522173362Sbenjsc 523173362Sbenjsc callout_init_mtx(&sc->calib_to, &sc->sc_mtx, 0); 524173362Sbenjsc callout_init_mtx(&sc->watchdog_to, &sc->sc_mtx, 0); 525173362Sbenjsc 526173362Sbenjsc /* disable the retry timeout register */ 527173362Sbenjsc pci_write_config(dev, 0x41, 0, 1); 528173362Sbenjsc 529173362Sbenjsc /* enable bus-mastering */ 530173362Sbenjsc pci_enable_busmaster(dev); 531173362Sbenjsc 532260064Smarius rid = PCIR_BAR(0); 533260064Smarius sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 534173362Sbenjsc RF_ACTIVE); 535173362Sbenjsc if (sc->mem == NULL) { 536173362Sbenjsc device_printf(dev, "could not allocate memory resource\n"); 537173362Sbenjsc error = ENOMEM; 538173362Sbenjsc goto fail; 539173362Sbenjsc } 540173362Sbenjsc 541173362Sbenjsc sc->sc_st = rman_get_bustag(sc->mem); 542173362Sbenjsc sc->sc_sh = rman_get_bushandle(sc->mem); 543173362Sbenjsc 544260064Smarius rid = 0; 545260064Smarius sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 546173362Sbenjsc RF_ACTIVE | RF_SHAREABLE); 547173362Sbenjsc if (sc->irq == NULL) { 548173362Sbenjsc device_printf(dev, "could not allocate interrupt resource\n"); 549173362Sbenjsc error = ENOMEM; 550173362Sbenjsc goto fail; 551173362Sbenjsc } 552173362Sbenjsc 553173362Sbenjsc /* 554173362Sbenjsc * Allocate DMA memory for firmware transfers. 555173362Sbenjsc */ 556173362Sbenjsc if ((error = wpi_alloc_fwmem(sc)) != 0) { 557173362Sbenjsc printf(": could not allocate firmware memory\n"); 558173362Sbenjsc error = ENOMEM; 559173362Sbenjsc goto fail; 560173362Sbenjsc } 561173362Sbenjsc 562173362Sbenjsc /* 563173362Sbenjsc * Put adapter into a known state. 564173362Sbenjsc */ 565173362Sbenjsc if ((error = wpi_reset(sc)) != 0) { 566173362Sbenjsc device_printf(dev, "could not reset adapter\n"); 567173362Sbenjsc goto fail; 568173362Sbenjsc } 569173362Sbenjsc 570173362Sbenjsc wpi_mem_lock(sc); 571173362Sbenjsc tmp = wpi_mem_read(sc, WPI_MEM_PCIDEV); 572179957Sthompsa if (bootverbose || WPI_DEBUG_SET) 573173362Sbenjsc device_printf(sc->sc_dev, "Hardware Revision (0x%X)\n", tmp); 574173362Sbenjsc 575173362Sbenjsc wpi_mem_unlock(sc); 576173362Sbenjsc 577173362Sbenjsc /* Allocate shared page */ 578173362Sbenjsc if ((error = wpi_alloc_shared(sc)) != 0) { 579173362Sbenjsc device_printf(dev, "could not allocate shared page\n"); 580173362Sbenjsc goto fail; 581173362Sbenjsc } 582173362Sbenjsc 583173362Sbenjsc /* tx data queues - 4 for QoS purposes */ 584173362Sbenjsc for (ac = 0; ac < WME_NUM_AC; ac++) { 585173362Sbenjsc error = wpi_alloc_tx_ring(sc, &sc->txq[ac], WPI_TX_RING_COUNT, ac); 586173362Sbenjsc if (error != 0) { 587173362Sbenjsc device_printf(dev, "could not allocate Tx ring %d\n",ac); 588173362Sbenjsc goto fail; 589173362Sbenjsc } 590173362Sbenjsc } 591173362Sbenjsc 592173362Sbenjsc /* command queue to talk to the card's firmware */ 593173362Sbenjsc error = wpi_alloc_tx_ring(sc, &sc->cmdq, WPI_CMD_RING_COUNT, 4); 594173362Sbenjsc if (error != 0) { 595173362Sbenjsc device_printf(dev, "could not allocate command ring\n"); 596173362Sbenjsc goto fail; 597173362Sbenjsc } 598173362Sbenjsc 599173362Sbenjsc /* receive data queue */ 600173362Sbenjsc error = wpi_alloc_rx_ring(sc, &sc->rxq); 601173362Sbenjsc if (error != 0) { 602173362Sbenjsc device_printf(dev, "could not allocate Rx ring\n"); 603173362Sbenjsc goto fail; 604173362Sbenjsc } 605173362Sbenjsc 606178354Ssam ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); 607173362Sbenjsc if (ifp == NULL) { 608173362Sbenjsc device_printf(dev, "can not if_alloc()\n"); 609173362Sbenjsc error = ENOMEM; 610173362Sbenjsc goto fail; 611173362Sbenjsc } 612178354Ssam ic = ifp->if_l2com; 613173362Sbenjsc 614173362Sbenjsc ic->ic_ifp = ifp; 615178354Ssam ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ 616178354Ssam ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ 617173362Sbenjsc 618173362Sbenjsc /* set device capabilities */ 619173362Sbenjsc ic->ic_caps = 620178957Ssam IEEE80211_C_STA /* station mode supported */ 621178957Ssam | IEEE80211_C_MONITOR /* monitor mode supported */ 622173362Sbenjsc | IEEE80211_C_TXPMGT /* tx power management */ 623173362Sbenjsc | IEEE80211_C_SHSLOT /* short slot time supported */ 624173362Sbenjsc | IEEE80211_C_SHPREAMBLE /* short preamble supported */ 625173362Sbenjsc | IEEE80211_C_WPA /* 802.11i */ 626173362Sbenjsc/* XXX looks like WME is partly supported? */ 627173362Sbenjsc#if 0 628173362Sbenjsc | IEEE80211_C_IBSS /* IBSS mode support */ 629173362Sbenjsc | IEEE80211_C_BGSCAN /* capable of bg scanning */ 630173362Sbenjsc | IEEE80211_C_WME /* 802.11e */ 631173362Sbenjsc | IEEE80211_C_HOSTAP /* Host access point mode */ 632173362Sbenjsc#endif 633173362Sbenjsc ; 634173362Sbenjsc 635173362Sbenjsc /* 636173362Sbenjsc * Read in the eeprom and also setup the channels for 637173362Sbenjsc * net80211. We don't set the rates as net80211 does this for us 638173362Sbenjsc */ 639190526Ssam wpi_read_eeprom(sc, macaddr); 640173362Sbenjsc 641179957Sthompsa if (bootverbose || WPI_DEBUG_SET) { 642173362Sbenjsc device_printf(sc->sc_dev, "Regulatory Domain: %.4s\n", sc->domain); 643173362Sbenjsc device_printf(sc->sc_dev, "Hardware Type: %c\n", 644173362Sbenjsc sc->type > 1 ? 'B': '?'); 645173362Sbenjsc device_printf(sc->sc_dev, "Hardware Revision: %c\n", 646173362Sbenjsc ((le16toh(sc->rev) & 0xf0) == 0xd0) ? 'D': '?'); 647173362Sbenjsc device_printf(sc->sc_dev, "SKU %s support 802.11a\n", 648173362Sbenjsc supportsa ? "does" : "does not"); 649173362Sbenjsc 650173362Sbenjsc /* XXX hw_config uses the PCIDEV for the Hardware rev. Must check 651173362Sbenjsc what sc->rev really represents - benjsc 20070615 */ 652173362Sbenjsc } 653173362Sbenjsc 654173362Sbenjsc if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 655173362Sbenjsc ifp->if_softc = sc; 656173362Sbenjsc ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 657173362Sbenjsc ifp->if_init = wpi_init; 658173362Sbenjsc ifp->if_ioctl = wpi_ioctl; 659173362Sbenjsc ifp->if_start = wpi_start; 660207554Ssobomax IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); 661207554Ssobomax ifp->if_snd.ifq_drv_maxlen = ifqmaxlen; 662173362Sbenjsc IFQ_SET_READY(&ifp->if_snd); 663178354Ssam 664190526Ssam ieee80211_ifattach(ic, macaddr); 665173362Sbenjsc /* override default methods */ 666178354Ssam ic->ic_raw_xmit = wpi_raw_xmit; 667173362Sbenjsc ic->ic_wme.wme_update = wpi_wme_update; 668173362Sbenjsc ic->ic_scan_start = wpi_scan_start; 669173362Sbenjsc ic->ic_scan_end = wpi_scan_end; 670173362Sbenjsc ic->ic_set_channel = wpi_set_channel; 671173362Sbenjsc ic->ic_scan_curchan = wpi_scan_curchan; 672173362Sbenjsc ic->ic_scan_mindwell = wpi_scan_mindwell; 673173362Sbenjsc 674178354Ssam ic->ic_vap_create = wpi_vap_create; 675178354Ssam ic->ic_vap_delete = wpi_vap_delete; 676173362Sbenjsc 677192468Ssam ieee80211_radiotap_attach(ic, 678192468Ssam &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), 679192468Ssam WPI_TX_RADIOTAP_PRESENT, 680192468Ssam &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), 681192468Ssam WPI_RX_RADIOTAP_PRESENT); 682173362Sbenjsc 683173362Sbenjsc /* 684173362Sbenjsc * Hook our interrupt after all initialization is complete. 685173362Sbenjsc */ 686177043Sthompsa error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET |INTR_MPSAFE, 687177043Sthompsa NULL, wpi_intr, sc, &sc->sc_ih); 688173362Sbenjsc if (error != 0) { 689173362Sbenjsc device_printf(dev, "could not set up interrupt\n"); 690173362Sbenjsc goto fail; 691173362Sbenjsc } 692173362Sbenjsc 693177043Sthompsa if (bootverbose) 694177043Sthompsa ieee80211_announce(ic); 695173362Sbenjsc#ifdef XXX_DEBUG 696173362Sbenjsc ieee80211_announce_channels(ic); 697173362Sbenjsc#endif 698173362Sbenjsc return 0; 699173362Sbenjsc 700173362Sbenjscfail: wpi_detach(dev); 701173362Sbenjsc return ENXIO; 702173362Sbenjsc} 703173362Sbenjsc 704173362Sbenjscstatic int 705173362Sbenjscwpi_detach(device_t dev) 706173362Sbenjsc{ 707173362Sbenjsc struct wpi_softc *sc = device_get_softc(dev); 708178354Ssam struct ifnet *ifp = sc->sc_ifp; 709200530Sgavin struct ieee80211com *ic; 710173362Sbenjsc int ac; 711173362Sbenjsc 712260064Smarius if (sc->irq != NULL) 713260064Smarius bus_teardown_intr(dev, sc->irq, sc->sc_ih); 714260064Smarius 715200530Sgavin if (ifp != NULL) { 716200530Sgavin ic = ifp->if_l2com; 717191746Sthompsa 718200530Sgavin ieee80211_draintask(ic, &sc->sc_restarttask); 719200530Sgavin ieee80211_draintask(ic, &sc->sc_radiotask); 720173362Sbenjsc wpi_stop(sc); 721173362Sbenjsc callout_drain(&sc->watchdog_to); 722173362Sbenjsc callout_drain(&sc->calib_to); 723173362Sbenjsc ieee80211_ifdetach(ic); 724173362Sbenjsc } 725173362Sbenjsc 726173362Sbenjsc WPI_LOCK(sc); 727173362Sbenjsc if (sc->txq[0].data_dmat) { 728173362Sbenjsc for (ac = 0; ac < WME_NUM_AC; ac++) 729173362Sbenjsc wpi_free_tx_ring(sc, &sc->txq[ac]); 730173362Sbenjsc 731173362Sbenjsc wpi_free_tx_ring(sc, &sc->cmdq); 732173362Sbenjsc wpi_free_rx_ring(sc, &sc->rxq); 733173362Sbenjsc wpi_free_shared(sc); 734173362Sbenjsc } 735173362Sbenjsc 736173362Sbenjsc if (sc->fw_fp != NULL) { 737173362Sbenjsc wpi_unload_firmware(sc); 738173362Sbenjsc } 739173362Sbenjsc 740173362Sbenjsc if (sc->fw_dma.tag) 741173362Sbenjsc wpi_free_fwmem(sc); 742173362Sbenjsc WPI_UNLOCK(sc); 743173362Sbenjsc 744260064Smarius if (sc->irq != NULL) 745260064Smarius bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(sc->irq), 746260064Smarius sc->irq); 747173362Sbenjsc if (sc->mem != NULL) 748260064Smarius bus_release_resource(dev, SYS_RES_MEMORY, 749260064Smarius rman_get_rid(sc->mem), sc->mem); 750173362Sbenjsc 751173362Sbenjsc if (ifp != NULL) 752173362Sbenjsc if_free(ifp); 753173362Sbenjsc 754173362Sbenjsc WPI_LOCK_DESTROY(sc); 755173362Sbenjsc 756173362Sbenjsc return 0; 757173362Sbenjsc} 758173362Sbenjsc 759178354Ssamstatic struct ieee80211vap * 760228621Sbschmidtwpi_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, 761228621Sbschmidt enum ieee80211_opmode opmode, int flags, 762228621Sbschmidt const uint8_t bssid[IEEE80211_ADDR_LEN], 763228621Sbschmidt const uint8_t mac[IEEE80211_ADDR_LEN]) 764178354Ssam{ 765178354Ssam struct wpi_vap *wvp; 766178354Ssam struct ieee80211vap *vap; 767178354Ssam 768178354Ssam if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ 769178354Ssam return NULL; 770178354Ssam wvp = (struct wpi_vap *) malloc(sizeof(struct wpi_vap), 771178354Ssam M_80211_VAP, M_NOWAIT | M_ZERO); 772178354Ssam if (wvp == NULL) 773178354Ssam return NULL; 774178354Ssam vap = &wvp->vap; 775178354Ssam ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac); 776178354Ssam /* override with driver methods */ 777178354Ssam wvp->newstate = vap->iv_newstate; 778178354Ssam vap->iv_newstate = wpi_newstate; 779178354Ssam 780206358Srpaulo ieee80211_ratectl_init(vap); 781178354Ssam /* complete setup */ 782178354Ssam ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status); 783178354Ssam ic->ic_opmode = opmode; 784178354Ssam return vap; 785178354Ssam} 786178354Ssam 787173362Sbenjscstatic void 788178354Ssamwpi_vap_delete(struct ieee80211vap *vap) 789178354Ssam{ 790178354Ssam struct wpi_vap *wvp = WPI_VAP(vap); 791178354Ssam 792206358Srpaulo ieee80211_ratectl_deinit(vap); 793178354Ssam ieee80211_vap_detach(vap); 794178354Ssam free(wvp, M_80211_VAP); 795178354Ssam} 796178354Ssam 797178354Ssamstatic void 798173362Sbenjscwpi_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 799173362Sbenjsc{ 800173362Sbenjsc if (error != 0) 801173362Sbenjsc return; 802173362Sbenjsc 803173362Sbenjsc KASSERT(nsegs == 1, ("too many DMA segments, %d should be 1", nsegs)); 804173362Sbenjsc 805173362Sbenjsc *(bus_addr_t *)arg = segs[0].ds_addr; 806173362Sbenjsc} 807173362Sbenjsc 808177043Sthompsa/* 809177043Sthompsa * Allocates a contiguous block of dma memory of the requested size and 810177043Sthompsa * alignment. Due to limitations of the FreeBSD dma subsystem as of 20071217, 811177043Sthompsa * allocations greater than 4096 may fail. Hence if the requested alignment is 812177043Sthompsa * greater we allocate 'alignment' size extra memory and shift the vaddr and 813177043Sthompsa * paddr after the dma load. This bypasses the problem at the cost of a little 814177043Sthompsa * more memory. 815177043Sthompsa */ 816173362Sbenjscstatic int 817173362Sbenjscwpi_dma_contig_alloc(struct wpi_softc *sc, struct wpi_dma_info *dma, 818173362Sbenjsc void **kvap, bus_size_t size, bus_size_t alignment, int flags) 819173362Sbenjsc{ 820173362Sbenjsc int error; 821177043Sthompsa bus_size_t align; 822177043Sthompsa bus_size_t reqsize; 823173362Sbenjsc 824173362Sbenjsc DPRINTFN(WPI_DEBUG_DMA, 825177043Sthompsa ("Size: %zd - alignment %zd\n", size, alignment)); 826173362Sbenjsc 827173362Sbenjsc dma->size = size; 828173362Sbenjsc dma->tag = NULL; 829173362Sbenjsc 830177043Sthompsa if (alignment > 4096) { 831177043Sthompsa align = PAGE_SIZE; 832177043Sthompsa reqsize = size + alignment; 833177043Sthompsa } else { 834177043Sthompsa align = alignment; 835177043Sthompsa reqsize = size; 836177043Sthompsa } 837177043Sthompsa error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), align, 838173362Sbenjsc 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, 839177043Sthompsa NULL, NULL, reqsize, 840177043Sthompsa 1, reqsize, flags, 841173362Sbenjsc NULL, NULL, &dma->tag); 842173362Sbenjsc if (error != 0) { 843173362Sbenjsc device_printf(sc->sc_dev, 844173362Sbenjsc "could not create shared page DMA tag\n"); 845173362Sbenjsc goto fail; 846173362Sbenjsc } 847177043Sthompsa error = bus_dmamem_alloc(dma->tag, (void **)&dma->vaddr_start, 848173362Sbenjsc flags | BUS_DMA_ZERO, &dma->map); 849173362Sbenjsc if (error != 0) { 850173362Sbenjsc device_printf(sc->sc_dev, 851173362Sbenjsc "could not allocate shared page DMA memory\n"); 852173362Sbenjsc goto fail; 853173362Sbenjsc } 854173362Sbenjsc 855177043Sthompsa error = bus_dmamap_load(dma->tag, dma->map, dma->vaddr_start, 856177043Sthompsa reqsize, wpi_dma_map_addr, &dma->paddr_start, flags); 857177043Sthompsa 858177043Sthompsa /* Save the original pointers so we can free all the memory */ 859177043Sthompsa dma->paddr = dma->paddr_start; 860177043Sthompsa dma->vaddr = dma->vaddr_start; 861177043Sthompsa 862177043Sthompsa /* 863177043Sthompsa * Check the alignment and increment by 4096 until we get the 864177043Sthompsa * requested alignment. Fail if can't obtain the alignment 865177043Sthompsa * we requested. 866173362Sbenjsc */ 867177043Sthompsa if ((dma->paddr & (alignment -1 )) != 0) { 868177043Sthompsa int i; 869173362Sbenjsc 870177043Sthompsa for (i = 0; i < alignment / 4096; i++) { 871177043Sthompsa if ((dma->paddr & (alignment - 1 )) == 0) 872177043Sthompsa break; 873177043Sthompsa dma->paddr += 4096; 874177043Sthompsa dma->vaddr += 4096; 875177043Sthompsa } 876177043Sthompsa if (i == alignment / 4096) { 877177043Sthompsa device_printf(sc->sc_dev, 878177043Sthompsa "alignment requirement was not satisfied\n"); 879177043Sthompsa goto fail; 880177043Sthompsa } 881173362Sbenjsc } 882173362Sbenjsc 883173362Sbenjsc if (error != 0) { 884173362Sbenjsc device_printf(sc->sc_dev, 885173362Sbenjsc "could not load shared page DMA map\n"); 886173362Sbenjsc goto fail; 887173362Sbenjsc } 888173362Sbenjsc 889173362Sbenjsc if (kvap != NULL) 890173362Sbenjsc *kvap = dma->vaddr; 891173362Sbenjsc 892173362Sbenjsc return 0; 893173362Sbenjsc 894173362Sbenjscfail: 895173362Sbenjsc wpi_dma_contig_free(dma); 896173362Sbenjsc return error; 897173362Sbenjsc} 898173362Sbenjsc 899173362Sbenjscstatic void 900173362Sbenjscwpi_dma_contig_free(struct wpi_dma_info *dma) 901173362Sbenjsc{ 902173362Sbenjsc if (dma->tag) { 903267580Sjhb if (dma->vaddr_start != NULL) { 904177043Sthompsa if (dma->paddr_start != 0) { 905173362Sbenjsc bus_dmamap_sync(dma->tag, dma->map, 906173362Sbenjsc BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 907173362Sbenjsc bus_dmamap_unload(dma->tag, dma->map); 908173362Sbenjsc } 909267580Sjhb bus_dmamem_free(dma->tag, dma->vaddr_start, dma->map); 910173362Sbenjsc } 911173362Sbenjsc bus_dma_tag_destroy(dma->tag); 912173362Sbenjsc } 913173362Sbenjsc} 914173362Sbenjsc 915173362Sbenjsc/* 916173362Sbenjsc * Allocate a shared page between host and NIC. 917173362Sbenjsc */ 918173362Sbenjscstatic int 919173362Sbenjscwpi_alloc_shared(struct wpi_softc *sc) 920173362Sbenjsc{ 921173362Sbenjsc int error; 922173362Sbenjsc 923173362Sbenjsc error = wpi_dma_contig_alloc(sc, &sc->shared_dma, 924173362Sbenjsc (void **)&sc->shared, sizeof (struct wpi_shared), 925173362Sbenjsc PAGE_SIZE, 926173362Sbenjsc BUS_DMA_NOWAIT); 927173362Sbenjsc 928173362Sbenjsc if (error != 0) { 929173362Sbenjsc device_printf(sc->sc_dev, 930173362Sbenjsc "could not allocate shared area DMA memory\n"); 931173362Sbenjsc } 932173362Sbenjsc 933173362Sbenjsc return error; 934173362Sbenjsc} 935173362Sbenjsc 936173362Sbenjscstatic void 937173362Sbenjscwpi_free_shared(struct wpi_softc *sc) 938173362Sbenjsc{ 939173362Sbenjsc wpi_dma_contig_free(&sc->shared_dma); 940173362Sbenjsc} 941173362Sbenjsc 942173362Sbenjscstatic int 943173362Sbenjscwpi_alloc_rx_ring(struct wpi_softc *sc, struct wpi_rx_ring *ring) 944173362Sbenjsc{ 945173362Sbenjsc 946173362Sbenjsc int i, error; 947173362Sbenjsc 948173362Sbenjsc ring->cur = 0; 949173362Sbenjsc 950173362Sbenjsc error = wpi_dma_contig_alloc(sc, &ring->desc_dma, 951177043Sthompsa (void **)&ring->desc, WPI_RX_RING_COUNT * sizeof (uint32_t), 952177043Sthompsa WPI_RING_DMA_ALIGN, BUS_DMA_NOWAIT); 953173362Sbenjsc 954173362Sbenjsc if (error != 0) { 955177043Sthompsa device_printf(sc->sc_dev, 956177043Sthompsa "%s: could not allocate rx ring DMA memory, error %d\n", 957177043Sthompsa __func__, error); 958177043Sthompsa goto fail; 959173362Sbenjsc } 960173362Sbenjsc 961177043Sthompsa error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, 962177043Sthompsa BUS_SPACE_MAXADDR_32BIT, 963177043Sthompsa BUS_SPACE_MAXADDR, NULL, NULL, MJUMPAGESIZE, 1, 964177043Sthompsa MJUMPAGESIZE, BUS_DMA_NOWAIT, NULL, NULL, &ring->data_dmat); 965177043Sthompsa if (error != 0) { 966177043Sthompsa device_printf(sc->sc_dev, 967177043Sthompsa "%s: bus_dma_tag_create_failed, error %d\n", 968177043Sthompsa __func__, error); 969177043Sthompsa goto fail; 970177043Sthompsa } 971177043Sthompsa 972173362Sbenjsc /* 973177043Sthompsa * Setup Rx buffers. 974173362Sbenjsc */ 975173362Sbenjsc for (i = 0; i < WPI_RX_RING_COUNT; i++) { 976177043Sthompsa struct wpi_rx_data *data = &ring->data[i]; 977177043Sthompsa struct mbuf *m; 978177043Sthompsa bus_addr_t paddr; 979173362Sbenjsc 980177043Sthompsa error = bus_dmamap_create(ring->data_dmat, 0, &data->map); 981177043Sthompsa if (error != 0) { 982173362Sbenjsc device_printf(sc->sc_dev, 983177043Sthompsa "%s: bus_dmamap_create failed, error %d\n", 984177043Sthompsa __func__, error); 985173362Sbenjsc goto fail; 986173362Sbenjsc } 987243857Sglebius m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE); 988177043Sthompsa if (m == NULL) { 989173362Sbenjsc device_printf(sc->sc_dev, 990177043Sthompsa "%s: could not allocate rx mbuf\n", __func__); 991177043Sthompsa error = ENOMEM; 992173362Sbenjsc goto fail; 993173362Sbenjsc } 994177043Sthompsa /* map page */ 995177043Sthompsa error = bus_dmamap_load(ring->data_dmat, data->map, 996177043Sthompsa mtod(m, caddr_t), MJUMPAGESIZE, 997177043Sthompsa wpi_dma_map_addr, &paddr, BUS_DMA_NOWAIT); 998177043Sthompsa if (error != 0 && error != EFBIG) { 999177043Sthompsa device_printf(sc->sc_dev, 1000177043Sthompsa "%s: bus_dmamap_load failed, error %d\n", 1001177043Sthompsa __func__, error); 1002177043Sthompsa m_freem(m); 1003177043Sthompsa error = ENOMEM; /* XXX unique code */ 1004173362Sbenjsc goto fail; 1005173362Sbenjsc } 1006177043Sthompsa bus_dmamap_sync(ring->data_dmat, data->map, 1007177043Sthompsa BUS_DMASYNC_PREWRITE); 1008177043Sthompsa 1009177043Sthompsa data->m = m; 1010177043Sthompsa ring->desc[i] = htole32(paddr); 1011173362Sbenjsc } 1012173362Sbenjsc bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, 1013173362Sbenjsc BUS_DMASYNC_PREWRITE); 1014173362Sbenjsc return 0; 1015173362Sbenjscfail: 1016173362Sbenjsc wpi_free_rx_ring(sc, ring); 1017173362Sbenjsc return error; 1018173362Sbenjsc} 1019173362Sbenjsc 1020173362Sbenjscstatic void 1021173362Sbenjscwpi_reset_rx_ring(struct wpi_softc *sc, struct wpi_rx_ring *ring) 1022173362Sbenjsc{ 1023173362Sbenjsc int ntries; 1024173362Sbenjsc 1025173362Sbenjsc wpi_mem_lock(sc); 1026173362Sbenjsc 1027173362Sbenjsc WPI_WRITE(sc, WPI_RX_CONFIG, 0); 1028173362Sbenjsc 1029173362Sbenjsc for (ntries = 0; ntries < 100; ntries++) { 1030173362Sbenjsc if (WPI_READ(sc, WPI_RX_STATUS) & WPI_RX_IDLE) 1031173362Sbenjsc break; 1032173362Sbenjsc DELAY(10); 1033173362Sbenjsc } 1034173362Sbenjsc 1035173362Sbenjsc wpi_mem_unlock(sc); 1036173362Sbenjsc 1037173362Sbenjsc#ifdef WPI_DEBUG 1038179957Sthompsa if (ntries == 100 && wpi_debug > 0) 1039173362Sbenjsc device_printf(sc->sc_dev, "timeout resetting Rx ring\n"); 1040173362Sbenjsc#endif 1041173362Sbenjsc 1042173362Sbenjsc ring->cur = 0; 1043173362Sbenjsc} 1044173362Sbenjsc 1045173362Sbenjscstatic void 1046173362Sbenjscwpi_free_rx_ring(struct wpi_softc *sc, struct wpi_rx_ring *ring) 1047173362Sbenjsc{ 1048173362Sbenjsc int i; 1049173362Sbenjsc 1050173362Sbenjsc wpi_dma_contig_free(&ring->desc_dma); 1051173362Sbenjsc 1052216824Sbschmidt for (i = 0; i < WPI_RX_RING_COUNT; i++) { 1053216824Sbschmidt struct wpi_rx_data *data = &ring->data[i]; 1054216824Sbschmidt 1055216824Sbschmidt if (data->m != NULL) { 1056216824Sbschmidt bus_dmamap_sync(ring->data_dmat, data->map, 1057216824Sbschmidt BUS_DMASYNC_POSTREAD); 1058216824Sbschmidt bus_dmamap_unload(ring->data_dmat, data->map); 1059216824Sbschmidt m_freem(data->m); 1060216824Sbschmidt } 1061216824Sbschmidt if (data->map != NULL) 1062216824Sbschmidt bus_dmamap_destroy(ring->data_dmat, data->map); 1063216824Sbschmidt } 1064173362Sbenjsc} 1065173362Sbenjsc 1066173362Sbenjscstatic int 1067173362Sbenjscwpi_alloc_tx_ring(struct wpi_softc *sc, struct wpi_tx_ring *ring, int count, 1068173362Sbenjsc int qid) 1069173362Sbenjsc{ 1070173362Sbenjsc struct wpi_tx_data *data; 1071173362Sbenjsc int i, error; 1072173362Sbenjsc 1073173362Sbenjsc ring->qid = qid; 1074173362Sbenjsc ring->count = count; 1075173362Sbenjsc ring->queued = 0; 1076173362Sbenjsc ring->cur = 0; 1077173362Sbenjsc ring->data = NULL; 1078173362Sbenjsc 1079173362Sbenjsc error = wpi_dma_contig_alloc(sc, &ring->desc_dma, 1080173362Sbenjsc (void **)&ring->desc, count * sizeof (struct wpi_tx_desc), 1081173362Sbenjsc WPI_RING_DMA_ALIGN, BUS_DMA_NOWAIT); 1082173362Sbenjsc 1083173362Sbenjsc if (error != 0) { 1084173362Sbenjsc device_printf(sc->sc_dev, "could not allocate tx dma memory\n"); 1085173362Sbenjsc goto fail; 1086173362Sbenjsc } 1087173362Sbenjsc 1088173362Sbenjsc /* update shared page with ring's base address */ 1089173362Sbenjsc sc->shared->txbase[qid] = htole32(ring->desc_dma.paddr); 1090173362Sbenjsc 1091173362Sbenjsc error = wpi_dma_contig_alloc(sc, &ring->cmd_dma, (void **)&ring->cmd, 1092173362Sbenjsc count * sizeof (struct wpi_tx_cmd), WPI_RING_DMA_ALIGN, 1093173362Sbenjsc BUS_DMA_NOWAIT); 1094173362Sbenjsc 1095173362Sbenjsc if (error != 0) { 1096173362Sbenjsc device_printf(sc->sc_dev, 1097173362Sbenjsc "could not allocate tx command DMA memory\n"); 1098173362Sbenjsc goto fail; 1099173362Sbenjsc } 1100173362Sbenjsc 1101173362Sbenjsc ring->data = malloc(count * sizeof (struct wpi_tx_data), M_DEVBUF, 1102173362Sbenjsc M_NOWAIT | M_ZERO); 1103173362Sbenjsc if (ring->data == NULL) { 1104173362Sbenjsc device_printf(sc->sc_dev, 1105173362Sbenjsc "could not allocate tx data slots\n"); 1106173362Sbenjsc goto fail; 1107173362Sbenjsc } 1108173362Sbenjsc 1109173362Sbenjsc error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, 1110173362Sbenjsc BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, 1111173362Sbenjsc WPI_MAX_SCATTER - 1, MCLBYTES, BUS_DMA_NOWAIT, NULL, NULL, 1112173362Sbenjsc &ring->data_dmat); 1113173362Sbenjsc if (error != 0) { 1114173362Sbenjsc device_printf(sc->sc_dev, "could not create data DMA tag\n"); 1115173362Sbenjsc goto fail; 1116173362Sbenjsc } 1117173362Sbenjsc 1118173362Sbenjsc for (i = 0; i < count; i++) { 1119173362Sbenjsc data = &ring->data[i]; 1120173362Sbenjsc 1121173362Sbenjsc error = bus_dmamap_create(ring->data_dmat, 0, &data->map); 1122173362Sbenjsc if (error != 0) { 1123173362Sbenjsc device_printf(sc->sc_dev, 1124173362Sbenjsc "could not create tx buf DMA map\n"); 1125173362Sbenjsc goto fail; 1126173362Sbenjsc } 1127173362Sbenjsc bus_dmamap_sync(ring->data_dmat, data->map, 1128173362Sbenjsc BUS_DMASYNC_PREWRITE); 1129173362Sbenjsc } 1130173362Sbenjsc 1131173362Sbenjsc return 0; 1132173362Sbenjsc 1133177043Sthompsafail: 1134177043Sthompsa wpi_free_tx_ring(sc, ring); 1135173362Sbenjsc return error; 1136173362Sbenjsc} 1137173362Sbenjsc 1138173362Sbenjscstatic void 1139173362Sbenjscwpi_reset_tx_ring(struct wpi_softc *sc, struct wpi_tx_ring *ring) 1140173362Sbenjsc{ 1141173362Sbenjsc struct wpi_tx_data *data; 1142173362Sbenjsc int i, ntries; 1143173362Sbenjsc 1144173362Sbenjsc wpi_mem_lock(sc); 1145173362Sbenjsc 1146173362Sbenjsc WPI_WRITE(sc, WPI_TX_CONFIG(ring->qid), 0); 1147173362Sbenjsc for (ntries = 0; ntries < 100; ntries++) { 1148173362Sbenjsc if (WPI_READ(sc, WPI_TX_STATUS) & WPI_TX_IDLE(ring->qid)) 1149173362Sbenjsc break; 1150173362Sbenjsc DELAY(10); 1151173362Sbenjsc } 1152173362Sbenjsc#ifdef WPI_DEBUG 1153179957Sthompsa if (ntries == 100 && wpi_debug > 0) 1154173362Sbenjsc device_printf(sc->sc_dev, "timeout resetting Tx ring %d\n", 1155173362Sbenjsc ring->qid); 1156173362Sbenjsc#endif 1157173362Sbenjsc wpi_mem_unlock(sc); 1158173362Sbenjsc 1159173362Sbenjsc for (i = 0; i < ring->count; i++) { 1160173362Sbenjsc data = &ring->data[i]; 1161173362Sbenjsc 1162173362Sbenjsc if (data->m != NULL) { 1163173362Sbenjsc bus_dmamap_unload(ring->data_dmat, data->map); 1164173362Sbenjsc m_freem(data->m); 1165173362Sbenjsc data->m = NULL; 1166173362Sbenjsc } 1167173362Sbenjsc } 1168173362Sbenjsc 1169173362Sbenjsc ring->queued = 0; 1170173362Sbenjsc ring->cur = 0; 1171173362Sbenjsc} 1172173362Sbenjsc 1173173362Sbenjscstatic void 1174173362Sbenjscwpi_free_tx_ring(struct wpi_softc *sc, struct wpi_tx_ring *ring) 1175173362Sbenjsc{ 1176173362Sbenjsc struct wpi_tx_data *data; 1177173362Sbenjsc int i; 1178173362Sbenjsc 1179173362Sbenjsc wpi_dma_contig_free(&ring->desc_dma); 1180173362Sbenjsc wpi_dma_contig_free(&ring->cmd_dma); 1181173362Sbenjsc 1182173362Sbenjsc if (ring->data != NULL) { 1183173362Sbenjsc for (i = 0; i < ring->count; i++) { 1184173362Sbenjsc data = &ring->data[i]; 1185173362Sbenjsc 1186173362Sbenjsc if (data->m != NULL) { 1187173362Sbenjsc bus_dmamap_sync(ring->data_dmat, data->map, 1188173362Sbenjsc BUS_DMASYNC_POSTWRITE); 1189173362Sbenjsc bus_dmamap_unload(ring->data_dmat, data->map); 1190173362Sbenjsc m_freem(data->m); 1191173362Sbenjsc data->m = NULL; 1192173362Sbenjsc } 1193173362Sbenjsc } 1194173362Sbenjsc free(ring->data, M_DEVBUF); 1195173362Sbenjsc } 1196173362Sbenjsc 1197173362Sbenjsc if (ring->data_dmat != NULL) 1198173362Sbenjsc bus_dma_tag_destroy(ring->data_dmat); 1199173362Sbenjsc} 1200173362Sbenjsc 1201173362Sbenjscstatic int 1202173362Sbenjscwpi_shutdown(device_t dev) 1203173362Sbenjsc{ 1204173362Sbenjsc struct wpi_softc *sc = device_get_softc(dev); 1205173362Sbenjsc 1206173362Sbenjsc WPI_LOCK(sc); 1207173362Sbenjsc wpi_stop_locked(sc); 1208173362Sbenjsc wpi_unload_firmware(sc); 1209173362Sbenjsc WPI_UNLOCK(sc); 1210173362Sbenjsc 1211173362Sbenjsc return 0; 1212173362Sbenjsc} 1213173362Sbenjsc 1214173362Sbenjscstatic int 1215173362Sbenjscwpi_suspend(device_t dev) 1216173362Sbenjsc{ 1217173362Sbenjsc struct wpi_softc *sc = device_get_softc(dev); 1218233387Sbschmidt struct ieee80211com *ic = sc->sc_ifp->if_l2com; 1219173362Sbenjsc 1220233387Sbschmidt ieee80211_suspend_all(ic); 1221173362Sbenjsc return 0; 1222173362Sbenjsc} 1223173362Sbenjsc 1224173362Sbenjscstatic int 1225173362Sbenjscwpi_resume(device_t dev) 1226173362Sbenjsc{ 1227173362Sbenjsc struct wpi_softc *sc = device_get_softc(dev); 1228233387Sbschmidt struct ieee80211com *ic = sc->sc_ifp->if_l2com; 1229173362Sbenjsc 1230173362Sbenjsc pci_write_config(dev, 0x41, 0, 1); 1231173362Sbenjsc 1232233387Sbschmidt ieee80211_resume_all(ic); 1233173362Sbenjsc return 0; 1234173362Sbenjsc} 1235173362Sbenjsc 1236173362Sbenjsc/** 1237173362Sbenjsc * Called by net80211 when ever there is a change to 80211 state machine 1238173362Sbenjsc */ 1239173362Sbenjscstatic int 1240178354Ssamwpi_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) 1241173362Sbenjsc{ 1242178354Ssam struct wpi_vap *wvp = WPI_VAP(vap); 1243178354Ssam struct ieee80211com *ic = vap->iv_ic; 1244173362Sbenjsc struct ifnet *ifp = ic->ic_ifp; 1245173362Sbenjsc struct wpi_softc *sc = ifp->if_softc; 1246178354Ssam int error; 1247173362Sbenjsc 1248178354Ssam DPRINTF(("%s: %s -> %s flags 0x%x\n", __func__, 1249178354Ssam ieee80211_state_name[vap->iv_state], 1250178354Ssam ieee80211_state_name[nstate], sc->flags)); 1251173362Sbenjsc 1252191746Sthompsa IEEE80211_UNLOCK(ic); 1253191746Sthompsa WPI_LOCK(sc); 1254216238Sbschmidt if (nstate == IEEE80211_S_SCAN && vap->iv_state != IEEE80211_S_INIT) { 1255216238Sbschmidt /* 1256216238Sbschmidt * On !INIT -> SCAN transitions, we need to clear any possible 1257216238Sbschmidt * knowledge about associations. 1258216238Sbschmidt */ 1259216238Sbschmidt error = wpi_config(sc); 1260216238Sbschmidt if (error != 0) { 1261216238Sbschmidt device_printf(sc->sc_dev, 1262216238Sbschmidt "%s: device config failed, error %d\n", 1263216238Sbschmidt __func__, error); 1264216238Sbschmidt } 1265216238Sbschmidt } 1266216238Sbschmidt if (nstate == IEEE80211_S_AUTH || 1267216238Sbschmidt (nstate == IEEE80211_S_ASSOC && vap->iv_state == IEEE80211_S_RUN)) { 1268216238Sbschmidt /* 1269216238Sbschmidt * The node must be registered in the firmware before auth. 1270216238Sbschmidt * Also the associd must be cleared on RUN -> ASSOC 1271216238Sbschmidt * transitions. 1272216238Sbschmidt */ 1273191746Sthompsa error = wpi_auth(sc, vap); 1274191746Sthompsa if (error != 0) { 1275191746Sthompsa device_printf(sc->sc_dev, 1276191746Sthompsa "%s: could not move to auth state, error %d\n", 1277191746Sthompsa __func__, error); 1278191746Sthompsa } 1279173362Sbenjsc } 1280178354Ssam if (nstate == IEEE80211_S_RUN && vap->iv_state != IEEE80211_S_RUN) { 1281191746Sthompsa error = wpi_run(sc, vap); 1282191746Sthompsa if (error != 0) { 1283191746Sthompsa device_printf(sc->sc_dev, 1284191746Sthompsa "%s: could not move to run state, error %d\n", 1285191746Sthompsa __func__, error); 1286191746Sthompsa } 1287178354Ssam } 1288178354Ssam if (nstate == IEEE80211_S_RUN) { 1289178354Ssam /* RUN -> RUN transition; just restart the timers */ 1290178354Ssam wpi_calib_timeout(sc); 1291178354Ssam /* XXX split out rate control timer */ 1292178354Ssam } 1293191746Sthompsa WPI_UNLOCK(sc); 1294191746Sthompsa IEEE80211_LOCK(ic); 1295178354Ssam return wvp->newstate(vap, nstate, arg); 1296173362Sbenjsc} 1297173362Sbenjsc 1298173362Sbenjsc/* 1299173362Sbenjsc * Grab exclusive access to NIC memory. 1300173362Sbenjsc */ 1301173362Sbenjscstatic void 1302173362Sbenjscwpi_mem_lock(struct wpi_softc *sc) 1303173362Sbenjsc{ 1304173362Sbenjsc int ntries; 1305173362Sbenjsc uint32_t tmp; 1306173362Sbenjsc 1307173362Sbenjsc tmp = WPI_READ(sc, WPI_GPIO_CTL); 1308173362Sbenjsc WPI_WRITE(sc, WPI_GPIO_CTL, tmp | WPI_GPIO_MAC); 1309173362Sbenjsc 1310173362Sbenjsc /* spin until we actually get the lock */ 1311173362Sbenjsc for (ntries = 0; ntries < 100; ntries++) { 1312173362Sbenjsc if ((WPI_READ(sc, WPI_GPIO_CTL) & 1313173362Sbenjsc (WPI_GPIO_CLOCK | WPI_GPIO_SLEEP)) == WPI_GPIO_CLOCK) 1314173362Sbenjsc break; 1315173362Sbenjsc DELAY(10); 1316173362Sbenjsc } 1317173362Sbenjsc if (ntries == 100) 1318173362Sbenjsc device_printf(sc->sc_dev, "could not lock memory\n"); 1319173362Sbenjsc} 1320173362Sbenjsc 1321173362Sbenjsc/* 1322173362Sbenjsc * Release lock on NIC memory. 1323173362Sbenjsc */ 1324173362Sbenjscstatic void 1325173362Sbenjscwpi_mem_unlock(struct wpi_softc *sc) 1326173362Sbenjsc{ 1327173362Sbenjsc uint32_t tmp = WPI_READ(sc, WPI_GPIO_CTL); 1328173362Sbenjsc WPI_WRITE(sc, WPI_GPIO_CTL, tmp & ~WPI_GPIO_MAC); 1329173362Sbenjsc} 1330173362Sbenjsc 1331173362Sbenjscstatic uint32_t 1332173362Sbenjscwpi_mem_read(struct wpi_softc *sc, uint16_t addr) 1333173362Sbenjsc{ 1334173362Sbenjsc WPI_WRITE(sc, WPI_READ_MEM_ADDR, WPI_MEM_4 | addr); 1335173362Sbenjsc return WPI_READ(sc, WPI_READ_MEM_DATA); 1336173362Sbenjsc} 1337173362Sbenjsc 1338173362Sbenjscstatic void 1339173362Sbenjscwpi_mem_write(struct wpi_softc *sc, uint16_t addr, uint32_t data) 1340173362Sbenjsc{ 1341173362Sbenjsc WPI_WRITE(sc, WPI_WRITE_MEM_ADDR, WPI_MEM_4 | addr); 1342173362Sbenjsc WPI_WRITE(sc, WPI_WRITE_MEM_DATA, data); 1343173362Sbenjsc} 1344173362Sbenjsc 1345173362Sbenjscstatic void 1346173362Sbenjscwpi_mem_write_region_4(struct wpi_softc *sc, uint16_t addr, 1347173362Sbenjsc const uint32_t *data, int wlen) 1348173362Sbenjsc{ 1349173362Sbenjsc for (; wlen > 0; wlen--, data++, addr+=4) 1350173362Sbenjsc wpi_mem_write(sc, addr, *data); 1351173362Sbenjsc} 1352173362Sbenjsc 1353173362Sbenjsc/* 1354173362Sbenjsc * Read data from the EEPROM. We access EEPROM through the MAC instead of 1355173362Sbenjsc * using the traditional bit-bang method. Data is read up until len bytes have 1356173362Sbenjsc * been obtained. 1357173362Sbenjsc */ 1358173362Sbenjscstatic uint16_t 1359173362Sbenjscwpi_read_prom_data(struct wpi_softc *sc, uint32_t addr, void *data, int len) 1360173362Sbenjsc{ 1361173362Sbenjsc int ntries; 1362173362Sbenjsc uint32_t val; 1363173362Sbenjsc uint8_t *out = data; 1364173362Sbenjsc 1365173362Sbenjsc wpi_mem_lock(sc); 1366173362Sbenjsc 1367173362Sbenjsc for (; len > 0; len -= 2, addr++) { 1368173362Sbenjsc WPI_WRITE(sc, WPI_EEPROM_CTL, addr << 2); 1369173362Sbenjsc 1370173362Sbenjsc for (ntries = 0; ntries < 10; ntries++) { 1371173362Sbenjsc if ((val = WPI_READ(sc, WPI_EEPROM_CTL)) & WPI_EEPROM_READY) 1372173362Sbenjsc break; 1373173362Sbenjsc DELAY(5); 1374173362Sbenjsc } 1375173362Sbenjsc 1376173362Sbenjsc if (ntries == 10) { 1377173362Sbenjsc device_printf(sc->sc_dev, "could not read EEPROM\n"); 1378173362Sbenjsc return ETIMEDOUT; 1379173362Sbenjsc } 1380173362Sbenjsc 1381173362Sbenjsc *out++= val >> 16; 1382173362Sbenjsc if (len > 1) 1383173362Sbenjsc *out ++= val >> 24; 1384173362Sbenjsc } 1385173362Sbenjsc 1386173362Sbenjsc wpi_mem_unlock(sc); 1387173362Sbenjsc 1388173362Sbenjsc return 0; 1389173362Sbenjsc} 1390173362Sbenjsc 1391173362Sbenjsc/* 1392173362Sbenjsc * The firmware text and data segments are transferred to the NIC using DMA. 1393173362Sbenjsc * The driver just copies the firmware into DMA-safe memory and tells the NIC 1394173362Sbenjsc * where to find it. Once the NIC has copied the firmware into its internal 1395173362Sbenjsc * memory, we can free our local copy in the driver. 1396173362Sbenjsc */ 1397173362Sbenjscstatic int 1398173362Sbenjscwpi_load_microcode(struct wpi_softc *sc, const uint8_t *fw, int size) 1399173362Sbenjsc{ 1400173362Sbenjsc int error, ntries; 1401173362Sbenjsc 1402173362Sbenjsc DPRINTFN(WPI_DEBUG_HW,("Loading microcode size 0x%x\n", size)); 1403173362Sbenjsc 1404173362Sbenjsc size /= sizeof(uint32_t); 1405173362Sbenjsc 1406173362Sbenjsc wpi_mem_lock(sc); 1407173362Sbenjsc 1408173362Sbenjsc wpi_mem_write_region_4(sc, WPI_MEM_UCODE_BASE, 1409173362Sbenjsc (const uint32_t *)fw, size); 1410173362Sbenjsc 1411173362Sbenjsc wpi_mem_write(sc, WPI_MEM_UCODE_SRC, 0); 1412173362Sbenjsc wpi_mem_write(sc, WPI_MEM_UCODE_DST, WPI_FW_TEXT); 1413173362Sbenjsc wpi_mem_write(sc, WPI_MEM_UCODE_SIZE, size); 1414173362Sbenjsc 1415173362Sbenjsc /* run microcode */ 1416173362Sbenjsc wpi_mem_write(sc, WPI_MEM_UCODE_CTL, WPI_UC_RUN); 1417173362Sbenjsc 1418173362Sbenjsc /* wait while the adapter is busy copying the firmware */ 1419173362Sbenjsc for (error = 0, ntries = 0; ntries < 1000; ntries++) { 1420173362Sbenjsc uint32_t status = WPI_READ(sc, WPI_TX_STATUS); 1421173362Sbenjsc DPRINTFN(WPI_DEBUG_HW, 1422173362Sbenjsc ("firmware status=0x%x, val=0x%x, result=0x%x\n", status, 1423173362Sbenjsc WPI_TX_IDLE(6), status & WPI_TX_IDLE(6))); 1424173362Sbenjsc if (status & WPI_TX_IDLE(6)) { 1425173362Sbenjsc DPRINTFN(WPI_DEBUG_HW, 1426173362Sbenjsc ("Status Match! - ntries = %d\n", ntries)); 1427173362Sbenjsc break; 1428173362Sbenjsc } 1429173362Sbenjsc DELAY(10); 1430173362Sbenjsc } 1431173362Sbenjsc if (ntries == 1000) { 1432173362Sbenjsc device_printf(sc->sc_dev, "timeout transferring firmware\n"); 1433173362Sbenjsc error = ETIMEDOUT; 1434173362Sbenjsc } 1435173362Sbenjsc 1436173362Sbenjsc /* start the microcode executing */ 1437173362Sbenjsc wpi_mem_write(sc, WPI_MEM_UCODE_CTL, WPI_UC_ENABLE); 1438173362Sbenjsc 1439173362Sbenjsc wpi_mem_unlock(sc); 1440173362Sbenjsc 1441173362Sbenjsc return (error); 1442173362Sbenjsc} 1443173362Sbenjsc 1444173362Sbenjscstatic void 1445173362Sbenjscwpi_rx_intr(struct wpi_softc *sc, struct wpi_rx_desc *desc, 1446173362Sbenjsc struct wpi_rx_data *data) 1447173362Sbenjsc{ 1448178354Ssam struct ifnet *ifp = sc->sc_ifp; 1449178354Ssam struct ieee80211com *ic = ifp->if_l2com; 1450173362Sbenjsc struct wpi_rx_ring *ring = &sc->rxq; 1451173362Sbenjsc struct wpi_rx_stat *stat; 1452173362Sbenjsc struct wpi_rx_head *head; 1453173362Sbenjsc struct wpi_rx_tail *tail; 1454173362Sbenjsc struct ieee80211_node *ni; 1455173362Sbenjsc struct mbuf *m, *mnew; 1456177043Sthompsa bus_addr_t paddr; 1457177043Sthompsa int error; 1458173362Sbenjsc 1459173362Sbenjsc stat = (struct wpi_rx_stat *)(desc + 1); 1460173362Sbenjsc 1461173362Sbenjsc if (stat->len > WPI_STAT_MAXLEN) { 1462173362Sbenjsc device_printf(sc->sc_dev, "invalid rx statistic header\n"); 1463271849Sglebius if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 1464173362Sbenjsc return; 1465173362Sbenjsc } 1466173362Sbenjsc 1467216824Sbschmidt bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD); 1468173362Sbenjsc head = (struct wpi_rx_head *)((caddr_t)(stat + 1) + stat->len); 1469173362Sbenjsc tail = (struct wpi_rx_tail *)((caddr_t)(head + 1) + le16toh(head->len)); 1470173362Sbenjsc 1471173362Sbenjsc DPRINTFN(WPI_DEBUG_RX, ("rx intr: idx=%d len=%d stat len=%d rssi=%d " 1472173362Sbenjsc "rate=%x chan=%d tstamp=%ju\n", ring->cur, le32toh(desc->len), 1473173362Sbenjsc le16toh(head->len), (int8_t)stat->rssi, head->rate, head->chan, 1474173362Sbenjsc (uintmax_t)le64toh(tail->tstamp))); 1475173362Sbenjsc 1476190458Sjmallett /* discard Rx frames with bad CRC early */ 1477190458Sjmallett if ((le32toh(tail->flags) & WPI_RX_NOERROR) != WPI_RX_NOERROR) { 1478190458Sjmallett DPRINTFN(WPI_DEBUG_RX, ("%s: rx flags error %x\n", __func__, 1479190458Sjmallett le32toh(tail->flags))); 1480271849Sglebius if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 1481190458Sjmallett return; 1482190458Sjmallett } 1483190458Sjmallett if (le16toh(head->len) < sizeof (struct ieee80211_frame)) { 1484190458Sjmallett DPRINTFN(WPI_DEBUG_RX, ("%s: frame too short: %d\n", __func__, 1485190458Sjmallett le16toh(head->len))); 1486271849Sglebius if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 1487190458Sjmallett return; 1488190458Sjmallett } 1489190458Sjmallett 1490177043Sthompsa /* XXX don't need mbuf, just dma buffer */ 1491243857Sglebius mnew = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE); 1492177043Sthompsa if (mnew == NULL) { 1493177043Sthompsa DPRINTFN(WPI_DEBUG_RX, ("%s: no mbuf to restock ring\n", 1494177043Sthompsa __func__)); 1495271849Sglebius if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 1496177043Sthompsa return; 1497177043Sthompsa } 1498216824Sbschmidt bus_dmamap_unload(ring->data_dmat, data->map); 1499216824Sbschmidt 1500177043Sthompsa error = bus_dmamap_load(ring->data_dmat, data->map, 1501177043Sthompsa mtod(mnew, caddr_t), MJUMPAGESIZE, 1502177043Sthompsa wpi_dma_map_addr, &paddr, BUS_DMA_NOWAIT); 1503177043Sthompsa if (error != 0 && error != EFBIG) { 1504177043Sthompsa device_printf(sc->sc_dev, 1505177043Sthompsa "%s: bus_dmamap_load failed, error %d\n", __func__, error); 1506177043Sthompsa m_freem(mnew); 1507271849Sglebius if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 1508177043Sthompsa return; 1509177043Sthompsa } 1510177043Sthompsa bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE); 1511177043Sthompsa 1512177043Sthompsa /* finalize mbuf and swap in new one */ 1513173362Sbenjsc m = data->m; 1514173362Sbenjsc m->m_pkthdr.rcvif = ifp; 1515173362Sbenjsc m->m_data = (caddr_t)(head + 1); 1516173362Sbenjsc m->m_pkthdr.len = m->m_len = le16toh(head->len); 1517173362Sbenjsc 1518177043Sthompsa data->m = mnew; 1519177043Sthompsa /* update Rx descriptor */ 1520177043Sthompsa ring->desc[ring->cur] = htole32(paddr); 1521173362Sbenjsc 1522192468Ssam if (ieee80211_radiotap_active(ic)) { 1523173362Sbenjsc struct wpi_rx_radiotap_header *tap = &sc->sc_rxtap; 1524173362Sbenjsc 1525173362Sbenjsc tap->wr_flags = 0; 1526173362Sbenjsc tap->wr_chan_freq = 1527173362Sbenjsc htole16(ic->ic_channels[head->chan].ic_freq); 1528173362Sbenjsc tap->wr_chan_flags = 1529173362Sbenjsc htole16(ic->ic_channels[head->chan].ic_flags); 1530173362Sbenjsc tap->wr_dbm_antsignal = (int8_t)(stat->rssi - WPI_RSSI_OFFSET); 1531173362Sbenjsc tap->wr_dbm_antnoise = (int8_t)le16toh(stat->noise); 1532173362Sbenjsc tap->wr_tsft = tail->tstamp; 1533173362Sbenjsc tap->wr_antenna = (le16toh(head->flags) >> 4) & 0xf; 1534173362Sbenjsc switch (head->rate) { 1535173362Sbenjsc /* CCK rates */ 1536173362Sbenjsc case 10: tap->wr_rate = 2; break; 1537173362Sbenjsc case 20: tap->wr_rate = 4; break; 1538173362Sbenjsc case 55: tap->wr_rate = 11; break; 1539173362Sbenjsc case 110: tap->wr_rate = 22; break; 1540173362Sbenjsc /* OFDM rates */ 1541173362Sbenjsc case 0xd: tap->wr_rate = 12; break; 1542173362Sbenjsc case 0xf: tap->wr_rate = 18; break; 1543173362Sbenjsc case 0x5: tap->wr_rate = 24; break; 1544173362Sbenjsc case 0x7: tap->wr_rate = 36; break; 1545173362Sbenjsc case 0x9: tap->wr_rate = 48; break; 1546173362Sbenjsc case 0xb: tap->wr_rate = 72; break; 1547173362Sbenjsc case 0x1: tap->wr_rate = 96; break; 1548173362Sbenjsc case 0x3: tap->wr_rate = 108; break; 1549173362Sbenjsc /* unknown rate: should not happen */ 1550173362Sbenjsc default: tap->wr_rate = 0; 1551173362Sbenjsc } 1552173362Sbenjsc if (le16toh(head->flags) & 0x4) 1553173362Sbenjsc tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; 1554173362Sbenjsc } 1555173362Sbenjsc 1556173362Sbenjsc WPI_UNLOCK(sc); 1557173362Sbenjsc 1558177043Sthompsa ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *)); 1559178354Ssam if (ni != NULL) { 1560192468Ssam (void) ieee80211_input(ni, m, stat->rssi, 0); 1561178354Ssam ieee80211_free_node(ni); 1562178354Ssam } else 1563192468Ssam (void) ieee80211_input_all(ic, m, stat->rssi, 0); 1564173362Sbenjsc 1565173362Sbenjsc WPI_LOCK(sc); 1566173362Sbenjsc} 1567173362Sbenjsc 1568173362Sbenjscstatic void 1569173362Sbenjscwpi_tx_intr(struct wpi_softc *sc, struct wpi_rx_desc *desc) 1570173362Sbenjsc{ 1571178354Ssam struct ifnet *ifp = sc->sc_ifp; 1572173362Sbenjsc struct wpi_tx_ring *ring = &sc->txq[desc->qid & 0x3]; 1573173362Sbenjsc struct wpi_tx_data *txdata = &ring->data[desc->idx]; 1574173362Sbenjsc struct wpi_tx_stat *stat = (struct wpi_tx_stat *)(desc + 1); 1575206358Srpaulo struct ieee80211_node *ni = txdata->ni; 1576206358Srpaulo struct ieee80211vap *vap = ni->ni_vap; 1577206358Srpaulo int retrycnt = 0; 1578173362Sbenjsc 1579173362Sbenjsc DPRINTFN(WPI_DEBUG_TX, ("tx done: qid=%d idx=%d retries=%d nkill=%d " 1580173362Sbenjsc "rate=%x duration=%d status=%x\n", desc->qid, desc->idx, 1581173362Sbenjsc stat->ntries, stat->nkill, stat->rate, le32toh(stat->duration), 1582173362Sbenjsc le32toh(stat->status))); 1583173362Sbenjsc 1584173362Sbenjsc /* 1585173362Sbenjsc * Update rate control statistics for the node. 1586173362Sbenjsc * XXX we should not count mgmt frames since they're always sent at 1587173362Sbenjsc * the lowest available bit-rate. 1588173362Sbenjsc * XXX frames w/o ACK shouldn't be used either 1589173362Sbenjsc */ 1590173362Sbenjsc if (stat->ntries > 0) { 1591190462Sjmallett DPRINTFN(WPI_DEBUG_TX, ("%d retries\n", stat->ntries)); 1592206358Srpaulo retrycnt = 1; 1593173362Sbenjsc } 1594206358Srpaulo ieee80211_ratectl_tx_complete(vap, ni, IEEE80211_RATECTL_TX_SUCCESS, 1595206358Srpaulo &retrycnt, NULL); 1596173362Sbenjsc 1597173362Sbenjsc /* XXX oerrors should only count errors !maxtries */ 1598173362Sbenjsc if ((le32toh(stat->status) & 0xff) != 1) 1599271849Sglebius if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 1600173362Sbenjsc else 1601271849Sglebius if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); 1602173362Sbenjsc 1603173362Sbenjsc bus_dmamap_sync(ring->data_dmat, txdata->map, BUS_DMASYNC_POSTWRITE); 1604173362Sbenjsc bus_dmamap_unload(ring->data_dmat, txdata->map); 1605173362Sbenjsc /* XXX handle M_TXCB? */ 1606173362Sbenjsc m_freem(txdata->m); 1607173362Sbenjsc txdata->m = NULL; 1608173362Sbenjsc ieee80211_free_node(txdata->ni); 1609173362Sbenjsc txdata->ni = NULL; 1610173362Sbenjsc 1611173362Sbenjsc ring->queued--; 1612173362Sbenjsc 1613173362Sbenjsc sc->sc_tx_timer = 0; 1614173362Sbenjsc ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 1615178354Ssam wpi_start_locked(ifp); 1616173362Sbenjsc} 1617173362Sbenjsc 1618173362Sbenjscstatic void 1619173362Sbenjscwpi_cmd_intr(struct wpi_softc *sc, struct wpi_rx_desc *desc) 1620173362Sbenjsc{ 1621173362Sbenjsc struct wpi_tx_ring *ring = &sc->cmdq; 1622173362Sbenjsc struct wpi_tx_data *data; 1623173362Sbenjsc 1624173362Sbenjsc DPRINTFN(WPI_DEBUG_CMD, ("cmd notification qid=%x idx=%d flags=%x " 1625173362Sbenjsc "type=%s len=%d\n", desc->qid, desc->idx, 1626173362Sbenjsc desc->flags, wpi_cmd_str(desc->type), 1627173362Sbenjsc le32toh(desc->len))); 1628173362Sbenjsc 1629173362Sbenjsc if ((desc->qid & 7) != 4) 1630173362Sbenjsc return; /* not a command ack */ 1631173362Sbenjsc 1632173362Sbenjsc data = &ring->data[desc->idx]; 1633173362Sbenjsc 1634173362Sbenjsc /* if the command was mapped in a mbuf, free it */ 1635173362Sbenjsc if (data->m != NULL) { 1636173362Sbenjsc bus_dmamap_unload(ring->data_dmat, data->map); 1637173362Sbenjsc m_freem(data->m); 1638173362Sbenjsc data->m = NULL; 1639173362Sbenjsc } 1640173362Sbenjsc 1641173362Sbenjsc sc->flags &= ~WPI_FLAG_BUSY; 1642173362Sbenjsc wakeup(&ring->cmd[desc->idx]); 1643173362Sbenjsc} 1644173362Sbenjsc 1645173362Sbenjscstatic void 1646173362Sbenjscwpi_notif_intr(struct wpi_softc *sc) 1647173362Sbenjsc{ 1648178354Ssam struct ifnet *ifp = sc->sc_ifp; 1649178354Ssam struct ieee80211com *ic = ifp->if_l2com; 1650173362Sbenjsc struct wpi_rx_desc *desc; 1651173362Sbenjsc struct wpi_rx_data *data; 1652173362Sbenjsc uint32_t hw; 1653173362Sbenjsc 1654216523Sbschmidt bus_dmamap_sync(sc->shared_dma.tag, sc->shared_dma.map, 1655216523Sbschmidt BUS_DMASYNC_POSTREAD); 1656216523Sbschmidt 1657173362Sbenjsc hw = le32toh(sc->shared->next); 1658173362Sbenjsc while (sc->rxq.cur != hw) { 1659173362Sbenjsc data = &sc->rxq.data[sc->rxq.cur]; 1660216523Sbschmidt 1661216523Sbschmidt bus_dmamap_sync(sc->rxq.data_dmat, data->map, 1662216523Sbschmidt BUS_DMASYNC_POSTREAD); 1663173362Sbenjsc desc = (void *)data->m->m_ext.ext_buf; 1664173362Sbenjsc 1665173362Sbenjsc DPRINTFN(WPI_DEBUG_NOTIFY, 1666173362Sbenjsc ("notify qid=%x idx=%d flags=%x type=%d len=%d\n", 1667173362Sbenjsc desc->qid, 1668173362Sbenjsc desc->idx, 1669173362Sbenjsc desc->flags, 1670173362Sbenjsc desc->type, 1671173362Sbenjsc le32toh(desc->len))); 1672173362Sbenjsc 1673173362Sbenjsc if (!(desc->qid & 0x80)) /* reply to a command */ 1674173362Sbenjsc wpi_cmd_intr(sc, desc); 1675173362Sbenjsc 1676173362Sbenjsc switch (desc->type) { 1677173362Sbenjsc case WPI_RX_DONE: 1678173362Sbenjsc /* a 802.11 frame was received */ 1679173362Sbenjsc wpi_rx_intr(sc, desc, data); 1680173362Sbenjsc break; 1681173362Sbenjsc 1682173362Sbenjsc case WPI_TX_DONE: 1683173362Sbenjsc /* a 802.11 frame has been transmitted */ 1684173362Sbenjsc wpi_tx_intr(sc, desc); 1685173362Sbenjsc break; 1686173362Sbenjsc 1687173362Sbenjsc case WPI_UC_READY: 1688173362Sbenjsc { 1689173362Sbenjsc struct wpi_ucode_info *uc = 1690173362Sbenjsc (struct wpi_ucode_info *)(desc + 1); 1691173362Sbenjsc 1692173362Sbenjsc /* the microcontroller is ready */ 1693173362Sbenjsc DPRINTF(("microcode alive notification version %x " 1694173362Sbenjsc "alive %x\n", le32toh(uc->version), 1695173362Sbenjsc le32toh(uc->valid))); 1696173362Sbenjsc 1697173362Sbenjsc if (le32toh(uc->valid) != 1) { 1698173362Sbenjsc device_printf(sc->sc_dev, 1699173362Sbenjsc "microcontroller initialization failed\n"); 1700173362Sbenjsc wpi_stop_locked(sc); 1701173362Sbenjsc } 1702173362Sbenjsc break; 1703173362Sbenjsc } 1704173362Sbenjsc case WPI_STATE_CHANGED: 1705173362Sbenjsc { 1706173362Sbenjsc uint32_t *status = (uint32_t *)(desc + 1); 1707173362Sbenjsc 1708173362Sbenjsc /* enabled/disabled notification */ 1709173362Sbenjsc DPRINTF(("state changed to %x\n", le32toh(*status))); 1710173362Sbenjsc 1711173362Sbenjsc if (le32toh(*status) & 1) { 1712173362Sbenjsc device_printf(sc->sc_dev, 1713173362Sbenjsc "Radio transmitter is switched off\n"); 1714173362Sbenjsc sc->flags |= WPI_FLAG_HW_RADIO_OFF; 1715177043Sthompsa ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 1716177043Sthompsa /* Disable firmware commands */ 1717177043Sthompsa WPI_WRITE(sc, WPI_UCODE_SET, WPI_DISABLE_CMD); 1718173362Sbenjsc } 1719173362Sbenjsc break; 1720173362Sbenjsc } 1721173362Sbenjsc case WPI_START_SCAN: 1722173362Sbenjsc { 1723179957Sthompsa#ifdef WPI_DEBUG 1724173362Sbenjsc struct wpi_start_scan *scan = 1725173362Sbenjsc (struct wpi_start_scan *)(desc + 1); 1726179957Sthompsa#endif 1727173362Sbenjsc 1728173362Sbenjsc DPRINTFN(WPI_DEBUG_SCANNING, 1729173362Sbenjsc ("scanning channel %d status %x\n", 1730173362Sbenjsc scan->chan, le32toh(scan->status))); 1731173362Sbenjsc break; 1732173362Sbenjsc } 1733173362Sbenjsc case WPI_STOP_SCAN: 1734173362Sbenjsc { 1735179957Sthompsa#ifdef WPI_DEBUG 1736173362Sbenjsc struct wpi_stop_scan *scan = 1737173362Sbenjsc (struct wpi_stop_scan *)(desc + 1); 1738179957Sthompsa#endif 1739178354Ssam struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 1740173362Sbenjsc 1741173362Sbenjsc DPRINTFN(WPI_DEBUG_SCANNING, 1742173362Sbenjsc ("scan finished nchan=%d status=%d chan=%d\n", 1743173362Sbenjsc scan->nchan, scan->status, scan->chan)); 1744173362Sbenjsc 1745177043Sthompsa sc->sc_scan_timer = 0; 1746178354Ssam ieee80211_scan_next(vap); 1747173362Sbenjsc break; 1748173362Sbenjsc } 1749173976Sbenjsc case WPI_MISSED_BEACON: 1750173976Sbenjsc { 1751178354Ssam struct wpi_missed_beacon *beacon = 1752173976Sbenjsc (struct wpi_missed_beacon *)(desc + 1); 1753178354Ssam struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 1754173976Sbenjsc 1755178354Ssam if (le32toh(beacon->consecutive) >= 1756178354Ssam vap->iv_bmissthreshold) { 1757178354Ssam DPRINTF(("Beacon miss: %u >= %u\n", 1758178354Ssam le32toh(beacon->consecutive), 1759178354Ssam vap->iv_bmissthreshold)); 1760191746Sthompsa ieee80211_beacon_miss(ic); 1761178354Ssam } 1762178354Ssam break; 1763173362Sbenjsc } 1764173976Sbenjsc } 1765173362Sbenjsc 1766173362Sbenjsc sc->rxq.cur = (sc->rxq.cur + 1) % WPI_RX_RING_COUNT; 1767173362Sbenjsc } 1768173362Sbenjsc 1769173362Sbenjsc /* tell the firmware what we have processed */ 1770173362Sbenjsc hw = (hw == 0) ? WPI_RX_RING_COUNT - 1 : hw - 1; 1771173362Sbenjsc WPI_WRITE(sc, WPI_RX_WIDX, hw & ~7); 1772173362Sbenjsc} 1773173362Sbenjsc 1774173362Sbenjscstatic void 1775173362Sbenjscwpi_intr(void *arg) 1776173362Sbenjsc{ 1777173362Sbenjsc struct wpi_softc *sc = arg; 1778173362Sbenjsc uint32_t r; 1779173362Sbenjsc 1780173362Sbenjsc WPI_LOCK(sc); 1781173362Sbenjsc 1782173362Sbenjsc r = WPI_READ(sc, WPI_INTR); 1783173362Sbenjsc if (r == 0 || r == 0xffffffff) { 1784173362Sbenjsc WPI_UNLOCK(sc); 1785173362Sbenjsc return; 1786173362Sbenjsc } 1787173362Sbenjsc 1788173362Sbenjsc /* disable interrupts */ 1789173362Sbenjsc WPI_WRITE(sc, WPI_MASK, 0); 1790173362Sbenjsc /* ack interrupts */ 1791173362Sbenjsc WPI_WRITE(sc, WPI_INTR, r); 1792173362Sbenjsc 1793173362Sbenjsc if (r & (WPI_SW_ERROR | WPI_HW_ERROR)) { 1794191746Sthompsa struct ifnet *ifp = sc->sc_ifp; 1795191746Sthompsa struct ieee80211com *ic = ifp->if_l2com; 1796191956Sthompsa struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 1797191746Sthompsa 1798173362Sbenjsc device_printf(sc->sc_dev, "fatal firmware error\n"); 1799173362Sbenjsc DPRINTFN(6,("(%s)\n", (r & WPI_SW_ERROR) ? "(Software Error)" : 1800173362Sbenjsc "(Hardware Error)")); 1801191956Sthompsa if (vap != NULL) 1802191956Sthompsa ieee80211_cancel_scan(vap); 1803191746Sthompsa ieee80211_runtask(ic, &sc->sc_restarttask); 1804173362Sbenjsc sc->flags &= ~WPI_FLAG_BUSY; 1805173362Sbenjsc WPI_UNLOCK(sc); 1806173362Sbenjsc return; 1807173362Sbenjsc } 1808173362Sbenjsc 1809173362Sbenjsc if (r & WPI_RX_INTR) 1810173362Sbenjsc wpi_notif_intr(sc); 1811173362Sbenjsc 1812173362Sbenjsc if (r & WPI_ALIVE_INTR) /* firmware initialized */ 1813173362Sbenjsc wakeup(sc); 1814173362Sbenjsc 1815173362Sbenjsc /* re-enable interrupts */ 1816173362Sbenjsc if (sc->sc_ifp->if_flags & IFF_UP) 1817173362Sbenjsc WPI_WRITE(sc, WPI_MASK, WPI_INTR_MASK); 1818173362Sbenjsc 1819173362Sbenjsc WPI_UNLOCK(sc); 1820173362Sbenjsc} 1821173362Sbenjsc 1822173362Sbenjscstatic uint8_t 1823173362Sbenjscwpi_plcp_signal(int rate) 1824173362Sbenjsc{ 1825173362Sbenjsc switch (rate) { 1826173362Sbenjsc /* CCK rates (returned values are device-dependent) */ 1827173362Sbenjsc case 2: return 10; 1828173362Sbenjsc case 4: return 20; 1829173362Sbenjsc case 11: return 55; 1830173362Sbenjsc case 22: return 110; 1831173362Sbenjsc 1832173362Sbenjsc /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */ 1833173362Sbenjsc /* R1-R4 (ral/ural is R4-R1) */ 1834173362Sbenjsc case 12: return 0xd; 1835173362Sbenjsc case 18: return 0xf; 1836173362Sbenjsc case 24: return 0x5; 1837173362Sbenjsc case 36: return 0x7; 1838173362Sbenjsc case 48: return 0x9; 1839173362Sbenjsc case 72: return 0xb; 1840173362Sbenjsc case 96: return 0x1; 1841173362Sbenjsc case 108: return 0x3; 1842173362Sbenjsc 1843173362Sbenjsc /* unsupported rates (should not get there) */ 1844173362Sbenjsc default: return 0; 1845173362Sbenjsc } 1846173362Sbenjsc} 1847173362Sbenjsc 1848173362Sbenjsc/* quickly determine if a given rate is CCK or OFDM */ 1849173362Sbenjsc#define WPI_RATE_IS_OFDM(rate) ((rate) >= 12 && (rate) != 22) 1850173362Sbenjsc 1851173362Sbenjsc/* 1852173362Sbenjsc * Construct the data packet for a transmit buffer and acutally put 1853173362Sbenjsc * the buffer onto the transmit ring, kicking the card to process the 1854173362Sbenjsc * the buffer. 1855173362Sbenjsc */ 1856173362Sbenjscstatic int 1857173362Sbenjscwpi_tx_data(struct wpi_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, 1858173362Sbenjsc int ac) 1859173362Sbenjsc{ 1860178354Ssam struct ieee80211vap *vap = ni->ni_vap; 1861178354Ssam struct ifnet *ifp = sc->sc_ifp; 1862178354Ssam struct ieee80211com *ic = ifp->if_l2com; 1863177043Sthompsa const struct chanAccParams *cap = &ic->ic_wme.wme_chanParams; 1864173362Sbenjsc struct wpi_tx_ring *ring = &sc->txq[ac]; 1865173362Sbenjsc struct wpi_tx_desc *desc; 1866173362Sbenjsc struct wpi_tx_data *data; 1867173362Sbenjsc struct wpi_tx_cmd *cmd; 1868173362Sbenjsc struct wpi_cmd_data *tx; 1869173362Sbenjsc struct ieee80211_frame *wh; 1870178354Ssam const struct ieee80211_txparam *tp; 1871173362Sbenjsc struct ieee80211_key *k; 1872173362Sbenjsc struct mbuf *mnew; 1873177043Sthompsa int i, error, nsegs, rate, hdrlen, ismcast; 1874173362Sbenjsc bus_dma_segment_t segs[WPI_MAX_SCATTER]; 1875173362Sbenjsc 1876173362Sbenjsc desc = &ring->desc[ring->cur]; 1877173362Sbenjsc data = &ring->data[ring->cur]; 1878173362Sbenjsc 1879173362Sbenjsc wh = mtod(m0, struct ieee80211_frame *); 1880173362Sbenjsc 1881177043Sthompsa hdrlen = ieee80211_hdrsize(wh); 1882177043Sthompsa ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); 1883173362Sbenjsc 1884260444Skevlo if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { 1885178354Ssam k = ieee80211_crypto_encap(ni, m0); 1886177043Sthompsa if (k == NULL) { 1887173362Sbenjsc m_freem(m0); 1888173362Sbenjsc return ENOBUFS; 1889173362Sbenjsc } 1890173362Sbenjsc /* packet header may have moved, reset our local pointer */ 1891173362Sbenjsc wh = mtod(m0, struct ieee80211_frame *); 1892173362Sbenjsc } 1893173362Sbenjsc 1894173362Sbenjsc cmd = &ring->cmd[ring->cur]; 1895173362Sbenjsc cmd->code = WPI_CMD_TX_DATA; 1896173362Sbenjsc cmd->flags = 0; 1897173362Sbenjsc cmd->qid = ring->qid; 1898173362Sbenjsc cmd->idx = ring->cur; 1899173362Sbenjsc 1900173362Sbenjsc tx = (struct wpi_cmd_data *)cmd->data; 1901177043Sthompsa tx->flags = htole32(WPI_TX_AUTO_SEQ); 1902178354Ssam tx->timeout = htole16(0); 1903177043Sthompsa tx->ofdm_mask = 0xff; 1904177043Sthompsa tx->cck_mask = 0x0f; 1905177043Sthompsa tx->lifetime = htole32(WPI_LIFETIME_INFINITE); 1906177043Sthompsa tx->id = ismcast ? WPI_ID_BROADCAST : WPI_ID_BSS; 1907177043Sthompsa tx->len = htole16(m0->m_pkthdr.len); 1908173362Sbenjsc 1909177119Ssam if (!ismcast) { 1910177043Sthompsa if ((ni->ni_flags & IEEE80211_NODE_QOS) == 0 || 1911177043Sthompsa !cap->cap_wmeParams[ac].wmep_noackPolicy) 1912177043Sthompsa tx->flags |= htole32(WPI_TX_NEED_ACK); 1913178354Ssam if (m0->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) { 1914177043Sthompsa tx->flags |= htole32(WPI_TX_NEED_RTS|WPI_TX_FULL_TXOP); 1915177043Sthompsa tx->rts_ntries = 7; 1916177043Sthompsa } 1917173362Sbenjsc } 1918177043Sthompsa /* pick a rate */ 1919178354Ssam tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)]; 1920178354Ssam if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_MGT) { 1921173362Sbenjsc uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; 1922173362Sbenjsc /* tell h/w to set timestamp in probe responses */ 1923173362Sbenjsc if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP) 1924177043Sthompsa tx->flags |= htole32(WPI_TX_INSERT_TSTAMP); 1925173362Sbenjsc if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ || 1926177043Sthompsa subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) 1927173362Sbenjsc tx->timeout = htole16(3); 1928173362Sbenjsc else 1929173362Sbenjsc tx->timeout = htole16(2); 1930178354Ssam rate = tp->mgmtrate; 1931177043Sthompsa } else if (ismcast) { 1932178354Ssam rate = tp->mcastrate; 1933178354Ssam } else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) { 1934178354Ssam rate = tp->ucastrate; 1935177043Sthompsa } else { 1936206358Srpaulo (void) ieee80211_ratectl_rate(ni, NULL, 0); 1937178354Ssam rate = ni->ni_txrate; 1938177043Sthompsa } 1939173362Sbenjsc tx->rate = wpi_plcp_signal(rate); 1940173362Sbenjsc 1941173362Sbenjsc /* be very persistant at sending frames out */ 1942178354Ssam#if 0 1943178354Ssam tx->data_ntries = tp->maxretry; 1944178354Ssam#else 1945178354Ssam tx->data_ntries = 15; /* XXX way too high */ 1946178354Ssam#endif 1947173362Sbenjsc 1948192468Ssam if (ieee80211_radiotap_active_vap(vap)) { 1949177043Sthompsa struct wpi_tx_radiotap_header *tap = &sc->sc_txtap; 1950177043Sthompsa tap->wt_flags = 0; 1951177043Sthompsa tap->wt_rate = rate; 1952177043Sthompsa tap->wt_hwqueue = ac; 1953260444Skevlo if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) 1954177043Sthompsa tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP; 1955178354Ssam 1956192468Ssam ieee80211_radiotap_tx(vap, m0); 1957177043Sthompsa } 1958173362Sbenjsc 1959173362Sbenjsc /* save and trim IEEE802.11 header */ 1960173362Sbenjsc m_copydata(m0, 0, hdrlen, (caddr_t)&tx->wh); 1961173362Sbenjsc m_adj(m0, hdrlen); 1962173362Sbenjsc 1963173362Sbenjsc error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m0, segs, 1964173362Sbenjsc &nsegs, BUS_DMA_NOWAIT); 1965173362Sbenjsc if (error != 0 && error != EFBIG) { 1966173362Sbenjsc device_printf(sc->sc_dev, "could not map mbuf (error %d)\n", 1967173362Sbenjsc error); 1968173362Sbenjsc m_freem(m0); 1969173362Sbenjsc return error; 1970173362Sbenjsc } 1971173362Sbenjsc if (error != 0) { 1972175418Sjhb /* XXX use m_collapse */ 1973243857Sglebius mnew = m_defrag(m0, M_NOWAIT); 1974173362Sbenjsc if (mnew == NULL) { 1975173362Sbenjsc device_printf(sc->sc_dev, 1976173362Sbenjsc "could not defragment mbuf\n"); 1977173362Sbenjsc m_freem(m0); 1978173362Sbenjsc return ENOBUFS; 1979173362Sbenjsc } 1980173362Sbenjsc m0 = mnew; 1981173362Sbenjsc 1982173362Sbenjsc error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, 1983173362Sbenjsc m0, segs, &nsegs, BUS_DMA_NOWAIT); 1984173362Sbenjsc if (error != 0) { 1985173362Sbenjsc device_printf(sc->sc_dev, 1986173362Sbenjsc "could not map mbuf (error %d)\n", error); 1987173362Sbenjsc m_freem(m0); 1988173362Sbenjsc return error; 1989173362Sbenjsc } 1990173362Sbenjsc } 1991173362Sbenjsc 1992173362Sbenjsc data->m = m0; 1993173362Sbenjsc data->ni = ni; 1994173362Sbenjsc 1995173362Sbenjsc DPRINTFN(WPI_DEBUG_TX, ("sending data: qid=%d idx=%d len=%d nsegs=%d\n", 1996173362Sbenjsc ring->qid, ring->cur, m0->m_pkthdr.len, nsegs)); 1997173362Sbenjsc 1998173362Sbenjsc /* first scatter/gather segment is used by the tx data command */ 1999173362Sbenjsc desc->flags = htole32(WPI_PAD32(m0->m_pkthdr.len) << 28 | 2000173362Sbenjsc (1 + nsegs) << 24); 2001173362Sbenjsc desc->segs[0].addr = htole32(ring->cmd_dma.paddr + 2002173362Sbenjsc ring->cur * sizeof (struct wpi_tx_cmd)); 2003173362Sbenjsc desc->segs[0].len = htole32(4 + sizeof (struct wpi_cmd_data)); 2004173362Sbenjsc for (i = 1; i <= nsegs; i++) { 2005173362Sbenjsc desc->segs[i].addr = htole32(segs[i - 1].ds_addr); 2006173362Sbenjsc desc->segs[i].len = htole32(segs[i - 1].ds_len); 2007173362Sbenjsc } 2008173362Sbenjsc 2009173362Sbenjsc bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE); 2010173362Sbenjsc bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, 2011173362Sbenjsc BUS_DMASYNC_PREWRITE); 2012173362Sbenjsc 2013173362Sbenjsc ring->queued++; 2014173362Sbenjsc 2015173362Sbenjsc /* kick ring */ 2016173362Sbenjsc ring->cur = (ring->cur + 1) % WPI_TX_RING_COUNT; 2017173362Sbenjsc WPI_WRITE(sc, WPI_TX_WIDX, ring->qid << 8 | ring->cur); 2018173362Sbenjsc 2019173362Sbenjsc return 0; 2020173362Sbenjsc} 2021173362Sbenjsc 2022173362Sbenjsc/** 2023173362Sbenjsc * Process data waiting to be sent on the IFNET output queue 2024173362Sbenjsc */ 2025173362Sbenjscstatic void 2026173362Sbenjscwpi_start(struct ifnet *ifp) 2027173362Sbenjsc{ 2028173362Sbenjsc struct wpi_softc *sc = ifp->if_softc; 2029178354Ssam 2030178354Ssam WPI_LOCK(sc); 2031178354Ssam wpi_start_locked(ifp); 2032178354Ssam WPI_UNLOCK(sc); 2033178354Ssam} 2034178354Ssam 2035178354Ssamstatic void 2036178354Ssamwpi_start_locked(struct ifnet *ifp) 2037178354Ssam{ 2038178354Ssam struct wpi_softc *sc = ifp->if_softc; 2039173362Sbenjsc struct ieee80211_node *ni; 2040178354Ssam struct mbuf *m; 2041178354Ssam int ac; 2042173362Sbenjsc 2043178354Ssam WPI_LOCK_ASSERT(sc); 2044178354Ssam 2045177043Sthompsa if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) 2046177043Sthompsa return; 2047173362Sbenjsc 2048173362Sbenjsc for (;;) { 2049190458Sjmallett IFQ_DRV_DEQUEUE(&ifp->if_snd, m); 2050178354Ssam if (m == NULL) 2051178354Ssam break; 2052178354Ssam ac = M_WME_GETAC(m); 2053178354Ssam if (sc->txq[ac].queued > sc->txq[ac].count - 8) { 2054178354Ssam /* there is no place left in this ring */ 2055178354Ssam IFQ_DRV_PREPEND(&ifp->if_snd, m); 2056178354Ssam ifp->if_drv_flags |= IFF_DRV_OACTIVE; 2057178354Ssam break; 2058178354Ssam } 2059178354Ssam ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; 2060178354Ssam if (wpi_tx_data(sc, m, ni, ac) != 0) { 2061178354Ssam ieee80211_free_node(ni); 2062271849Sglebius if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 2063178354Ssam break; 2064178354Ssam } 2065178354Ssam sc->sc_tx_timer = 5; 2066178354Ssam } 2067178354Ssam} 2068173362Sbenjsc 2069178354Ssamstatic int 2070178354Ssamwpi_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, 2071178354Ssam const struct ieee80211_bpf_params *params) 2072178354Ssam{ 2073178354Ssam struct ieee80211com *ic = ni->ni_ic; 2074178354Ssam struct ifnet *ifp = ic->ic_ifp; 2075178354Ssam struct wpi_softc *sc = ifp->if_softc; 2076173362Sbenjsc 2077178354Ssam /* prevent management frames from being sent if we're not ready */ 2078178354Ssam if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { 2079178354Ssam m_freem(m); 2080178354Ssam ieee80211_free_node(ni); 2081178354Ssam return ENETDOWN; 2082178354Ssam } 2083178354Ssam WPI_LOCK(sc); 2084173362Sbenjsc 2085178354Ssam /* management frames go into ring 0 */ 2086178354Ssam if (sc->txq[0].queued > sc->txq[0].count - 8) { 2087178354Ssam ifp->if_drv_flags |= IFF_DRV_OACTIVE; 2088178354Ssam m_freem(m); 2089178354Ssam WPI_UNLOCK(sc); 2090178354Ssam ieee80211_free_node(ni); 2091178354Ssam return ENOBUFS; /* XXX */ 2092178354Ssam } 2093173362Sbenjsc 2094271849Sglebius if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); 2095178354Ssam if (wpi_tx_data(sc, m, ni, 0) != 0) 2096178354Ssam goto bad; 2097178354Ssam sc->sc_tx_timer = 5; 2098178354Ssam callout_reset(&sc->watchdog_to, hz, wpi_watchdog, sc); 2099173362Sbenjsc 2100178354Ssam WPI_UNLOCK(sc); 2101178354Ssam return 0; 2102178354Ssambad: 2103271849Sglebius if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 2104178354Ssam WPI_UNLOCK(sc); 2105178354Ssam ieee80211_free_node(ni); 2106178354Ssam return EIO; /* XXX */ 2107173362Sbenjsc} 2108173362Sbenjsc 2109173362Sbenjscstatic int 2110173362Sbenjscwpi_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 2111173362Sbenjsc{ 2112173362Sbenjsc struct wpi_softc *sc = ifp->if_softc; 2113178354Ssam struct ieee80211com *ic = ifp->if_l2com; 2114178354Ssam struct ifreq *ifr = (struct ifreq *) data; 2115178354Ssam int error = 0, startall = 0; 2116173362Sbenjsc 2117173362Sbenjsc switch (cmd) { 2118173362Sbenjsc case SIOCSIFFLAGS: 2119178704Sthompsa WPI_LOCK(sc); 2120173362Sbenjsc if ((ifp->if_flags & IFF_UP)) { 2121178354Ssam if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { 2122177043Sthompsa wpi_init_locked(sc, 0); 2123178354Ssam startall = 1; 2124178354Ssam } 2125177043Sthompsa } else if ((ifp->if_drv_flags & IFF_DRV_RUNNING) || 2126177043Sthompsa (sc->flags & WPI_FLAG_HW_RADIO_OFF)) 2127173362Sbenjsc wpi_stop_locked(sc); 2128178704Sthompsa WPI_UNLOCK(sc); 2129178704Sthompsa if (startall) 2130178704Sthompsa ieee80211_start_all(ic); 2131173362Sbenjsc break; 2132178354Ssam case SIOCGIFMEDIA: 2133178354Ssam error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); 2134178354Ssam break; 2135178704Sthompsa case SIOCGIFADDR: 2136178354Ssam error = ether_ioctl(ifp, cmd, data); 2137178354Ssam break; 2138178704Sthompsa default: 2139178704Sthompsa error = EINVAL; 2140178704Sthompsa break; 2141173362Sbenjsc } 2142173362Sbenjsc return error; 2143173362Sbenjsc} 2144173362Sbenjsc 2145173362Sbenjsc/* 2146173362Sbenjsc * Extract various information from EEPROM. 2147173362Sbenjsc */ 2148173362Sbenjscstatic void 2149190526Ssamwpi_read_eeprom(struct wpi_softc *sc, uint8_t macaddr[IEEE80211_ADDR_LEN]) 2150173362Sbenjsc{ 2151173362Sbenjsc int i; 2152173362Sbenjsc 2153173362Sbenjsc /* read the hardware capabilities, revision and SKU type */ 2154173362Sbenjsc wpi_read_prom_data(sc, WPI_EEPROM_CAPABILITIES, &sc->cap,1); 2155173362Sbenjsc wpi_read_prom_data(sc, WPI_EEPROM_REVISION, &sc->rev,2); 2156173362Sbenjsc wpi_read_prom_data(sc, WPI_EEPROM_TYPE, &sc->type, 1); 2157173362Sbenjsc 2158173362Sbenjsc /* read the regulatory domain */ 2159173362Sbenjsc wpi_read_prom_data(sc, WPI_EEPROM_DOMAIN, sc->domain, 4); 2160173362Sbenjsc 2161173362Sbenjsc /* read in the hw MAC address */ 2162190526Ssam wpi_read_prom_data(sc, WPI_EEPROM_MAC, macaddr, 6); 2163173362Sbenjsc 2164173362Sbenjsc /* read the list of authorized channels */ 2165173362Sbenjsc for (i = 0; i < WPI_CHAN_BANDS_COUNT; i++) 2166173362Sbenjsc wpi_read_eeprom_channels(sc,i); 2167173362Sbenjsc 2168173362Sbenjsc /* read the power level calibration info for each group */ 2169173362Sbenjsc for (i = 0; i < WPI_POWER_GROUPS_COUNT; i++) 2170173362Sbenjsc wpi_read_eeprom_group(sc,i); 2171173362Sbenjsc} 2172173362Sbenjsc 2173173362Sbenjsc/* 2174173362Sbenjsc * Send a command to the firmware. 2175173362Sbenjsc */ 2176173362Sbenjscstatic int 2177173362Sbenjscwpi_cmd(struct wpi_softc *sc, int code, const void *buf, int size, int async) 2178173362Sbenjsc{ 2179173362Sbenjsc struct wpi_tx_ring *ring = &sc->cmdq; 2180173362Sbenjsc struct wpi_tx_desc *desc; 2181173362Sbenjsc struct wpi_tx_cmd *cmd; 2182173362Sbenjsc 2183173362Sbenjsc#ifdef WPI_DEBUG 2184173362Sbenjsc if (!async) { 2185173362Sbenjsc WPI_LOCK_ASSERT(sc); 2186173362Sbenjsc } 2187173362Sbenjsc#endif 2188173362Sbenjsc 2189173362Sbenjsc DPRINTFN(WPI_DEBUG_CMD,("wpi_cmd %d size %d async %d\n", code, size, 2190173362Sbenjsc async)); 2191173362Sbenjsc 2192173362Sbenjsc if (sc->flags & WPI_FLAG_BUSY) { 2193173362Sbenjsc device_printf(sc->sc_dev, "%s: cmd %d not sent, busy\n", 2194173362Sbenjsc __func__, code); 2195173362Sbenjsc return EAGAIN; 2196173362Sbenjsc } 2197173362Sbenjsc sc->flags|= WPI_FLAG_BUSY; 2198173362Sbenjsc 2199173362Sbenjsc KASSERT(size <= sizeof cmd->data, ("command %d too large: %d bytes", 2200173362Sbenjsc code, size)); 2201173362Sbenjsc 2202173362Sbenjsc desc = &ring->desc[ring->cur]; 2203173362Sbenjsc cmd = &ring->cmd[ring->cur]; 2204173362Sbenjsc 2205173362Sbenjsc cmd->code = code; 2206173362Sbenjsc cmd->flags = 0; 2207173362Sbenjsc cmd->qid = ring->qid; 2208173362Sbenjsc cmd->idx = ring->cur; 2209173362Sbenjsc memcpy(cmd->data, buf, size); 2210173362Sbenjsc 2211173362Sbenjsc desc->flags = htole32(WPI_PAD32(size) << 28 | 1 << 24); 2212173362Sbenjsc desc->segs[0].addr = htole32(ring->cmd_dma.paddr + 2213173362Sbenjsc ring->cur * sizeof (struct wpi_tx_cmd)); 2214173362Sbenjsc desc->segs[0].len = htole32(4 + size); 2215173362Sbenjsc 2216173362Sbenjsc /* kick cmd ring */ 2217173362Sbenjsc ring->cur = (ring->cur + 1) % WPI_CMD_RING_COUNT; 2218173362Sbenjsc WPI_WRITE(sc, WPI_TX_WIDX, ring->qid << 8 | ring->cur); 2219173362Sbenjsc 2220173362Sbenjsc if (async) { 2221173362Sbenjsc sc->flags &= ~ WPI_FLAG_BUSY; 2222173362Sbenjsc return 0; 2223173362Sbenjsc } 2224173362Sbenjsc 2225173362Sbenjsc return msleep(cmd, &sc->sc_mtx, PCATCH, "wpicmd", hz); 2226173362Sbenjsc} 2227173362Sbenjsc 2228173362Sbenjscstatic int 2229173362Sbenjscwpi_wme_update(struct ieee80211com *ic) 2230173362Sbenjsc{ 2231173362Sbenjsc#define WPI_EXP2(v) htole16((1 << (v)) - 1) 2232173362Sbenjsc#define WPI_USEC(v) htole16(IEEE80211_TXOP_TO_US(v)) 2233173362Sbenjsc struct wpi_softc *sc = ic->ic_ifp->if_softc; 2234173362Sbenjsc const struct wmeParams *wmep; 2235173362Sbenjsc struct wpi_wme_setup wme; 2236173362Sbenjsc int ac; 2237173362Sbenjsc 2238173362Sbenjsc /* don't override default WME values if WME is not actually enabled */ 2239173362Sbenjsc if (!(ic->ic_flags & IEEE80211_F_WME)) 2240173362Sbenjsc return 0; 2241173362Sbenjsc 2242173362Sbenjsc wme.flags = 0; 2243173362Sbenjsc for (ac = 0; ac < WME_NUM_AC; ac++) { 2244173362Sbenjsc wmep = &ic->ic_wme.wme_chanParams.cap_wmeParams[ac]; 2245173362Sbenjsc wme.ac[ac].aifsn = wmep->wmep_aifsn; 2246173362Sbenjsc wme.ac[ac].cwmin = WPI_EXP2(wmep->wmep_logcwmin); 2247173362Sbenjsc wme.ac[ac].cwmax = WPI_EXP2(wmep->wmep_logcwmax); 2248173362Sbenjsc wme.ac[ac].txop = WPI_USEC(wmep->wmep_txopLimit); 2249173362Sbenjsc 2250173362Sbenjsc DPRINTF(("setting WME for queue %d aifsn=%d cwmin=%d cwmax=%d " 2251173362Sbenjsc "txop=%d\n", ac, wme.ac[ac].aifsn, wme.ac[ac].cwmin, 2252173362Sbenjsc wme.ac[ac].cwmax, wme.ac[ac].txop)); 2253173362Sbenjsc } 2254173362Sbenjsc return wpi_cmd(sc, WPI_CMD_SET_WME, &wme, sizeof wme, 1); 2255173362Sbenjsc#undef WPI_USEC 2256173362Sbenjsc#undef WPI_EXP2 2257173362Sbenjsc} 2258173362Sbenjsc 2259173362Sbenjsc/* 2260173362Sbenjsc * Configure h/w multi-rate retries. 2261173362Sbenjsc */ 2262173362Sbenjscstatic int 2263173362Sbenjscwpi_mrr_setup(struct wpi_softc *sc) 2264173362Sbenjsc{ 2265178354Ssam struct ifnet *ifp = sc->sc_ifp; 2266178354Ssam struct ieee80211com *ic = ifp->if_l2com; 2267173362Sbenjsc struct wpi_mrr_setup mrr; 2268173362Sbenjsc int i, error; 2269173362Sbenjsc 2270173362Sbenjsc memset(&mrr, 0, sizeof (struct wpi_mrr_setup)); 2271173362Sbenjsc 2272173362Sbenjsc /* CCK rates (not used with 802.11a) */ 2273173362Sbenjsc for (i = WPI_CCK1; i <= WPI_CCK11; i++) { 2274173362Sbenjsc mrr.rates[i].flags = 0; 2275173362Sbenjsc mrr.rates[i].signal = wpi_ridx_to_plcp[i]; 2276173362Sbenjsc /* fallback to the immediate lower CCK rate (if any) */ 2277173362Sbenjsc mrr.rates[i].next = (i == WPI_CCK1) ? WPI_CCK1 : i - 1; 2278173362Sbenjsc /* try one time at this rate before falling back to "next" */ 2279173362Sbenjsc mrr.rates[i].ntries = 1; 2280173362Sbenjsc } 2281173362Sbenjsc 2282173362Sbenjsc /* OFDM rates (not used with 802.11b) */ 2283173362Sbenjsc for (i = WPI_OFDM6; i <= WPI_OFDM54; i++) { 2284173362Sbenjsc mrr.rates[i].flags = 0; 2285173362Sbenjsc mrr.rates[i].signal = wpi_ridx_to_plcp[i]; 2286173362Sbenjsc /* fallback to the immediate lower OFDM rate (if any) */ 2287173362Sbenjsc /* we allow fallback from OFDM/6 to CCK/2 in 11b/g mode */ 2288173362Sbenjsc mrr.rates[i].next = (i == WPI_OFDM6) ? 2289173362Sbenjsc ((ic->ic_curmode == IEEE80211_MODE_11A) ? 2290173362Sbenjsc WPI_OFDM6 : WPI_CCK2) : 2291173362Sbenjsc i - 1; 2292173362Sbenjsc /* try one time at this rate before falling back to "next" */ 2293173362Sbenjsc mrr.rates[i].ntries = 1; 2294173362Sbenjsc } 2295173362Sbenjsc 2296173362Sbenjsc /* setup MRR for control frames */ 2297221299Sbschmidt mrr.which = WPI_MRR_CTL; 2298173362Sbenjsc error = wpi_cmd(sc, WPI_CMD_MRR_SETUP, &mrr, sizeof mrr, 0); 2299173362Sbenjsc if (error != 0) { 2300173362Sbenjsc device_printf(sc->sc_dev, 2301173362Sbenjsc "could not setup MRR for control frames\n"); 2302173362Sbenjsc return error; 2303173362Sbenjsc } 2304173362Sbenjsc 2305173362Sbenjsc /* setup MRR for data frames */ 2306221299Sbschmidt mrr.which = WPI_MRR_DATA; 2307173362Sbenjsc error = wpi_cmd(sc, WPI_CMD_MRR_SETUP, &mrr, sizeof mrr, 0); 2308173362Sbenjsc if (error != 0) { 2309173362Sbenjsc device_printf(sc->sc_dev, 2310173362Sbenjsc "could not setup MRR for data frames\n"); 2311173362Sbenjsc return error; 2312173362Sbenjsc } 2313173362Sbenjsc 2314173362Sbenjsc return 0; 2315173362Sbenjsc} 2316173362Sbenjsc 2317173362Sbenjscstatic void 2318173362Sbenjscwpi_set_led(struct wpi_softc *sc, uint8_t which, uint8_t off, uint8_t on) 2319173362Sbenjsc{ 2320173362Sbenjsc struct wpi_cmd_led led; 2321173362Sbenjsc 2322173362Sbenjsc led.which = which; 2323173362Sbenjsc led.unit = htole32(100000); /* on/off in unit of 100ms */ 2324173362Sbenjsc led.off = off; 2325173362Sbenjsc led.on = on; 2326173362Sbenjsc 2327173362Sbenjsc (void)wpi_cmd(sc, WPI_CMD_SET_LED, &led, sizeof led, 1); 2328173362Sbenjsc} 2329173362Sbenjsc 2330173362Sbenjscstatic void 2331173362Sbenjscwpi_enable_tsf(struct wpi_softc *sc, struct ieee80211_node *ni) 2332173362Sbenjsc{ 2333173362Sbenjsc struct wpi_cmd_tsf tsf; 2334173362Sbenjsc uint64_t val, mod; 2335173362Sbenjsc 2336173362Sbenjsc memset(&tsf, 0, sizeof tsf); 2337173362Sbenjsc memcpy(&tsf.tstamp, ni->ni_tstamp.data, 8); 2338173362Sbenjsc tsf.bintval = htole16(ni->ni_intval); 2339173362Sbenjsc tsf.lintval = htole16(10); 2340173362Sbenjsc 2341173362Sbenjsc /* compute remaining time until next beacon */ 2342173362Sbenjsc val = (uint64_t)ni->ni_intval * 1024; /* msec -> usec */ 2343173362Sbenjsc mod = le64toh(tsf.tstamp) % val; 2344173362Sbenjsc tsf.binitval = htole32((uint32_t)(val - mod)); 2345173362Sbenjsc 2346173362Sbenjsc if (wpi_cmd(sc, WPI_CMD_TSF, &tsf, sizeof tsf, 1) != 0) 2347173362Sbenjsc device_printf(sc->sc_dev, "could not enable TSF\n"); 2348173362Sbenjsc} 2349173362Sbenjsc 2350173362Sbenjsc#if 0 2351173362Sbenjsc/* 2352173362Sbenjsc * Build a beacon frame that the firmware will broadcast periodically in 2353173362Sbenjsc * IBSS or HostAP modes. 2354173362Sbenjsc */ 2355173362Sbenjscstatic int 2356173362Sbenjscwpi_setup_beacon(struct wpi_softc *sc, struct ieee80211_node *ni) 2357173362Sbenjsc{ 2358178354Ssam struct ifnet *ifp = sc->sc_ifp; 2359178354Ssam struct ieee80211com *ic = ifp->if_l2com; 2360173362Sbenjsc struct wpi_tx_ring *ring = &sc->cmdq; 2361173362Sbenjsc struct wpi_tx_desc *desc; 2362173362Sbenjsc struct wpi_tx_data *data; 2363173362Sbenjsc struct wpi_tx_cmd *cmd; 2364173362Sbenjsc struct wpi_cmd_beacon *bcn; 2365173362Sbenjsc struct ieee80211_beacon_offsets bo; 2366173362Sbenjsc struct mbuf *m0; 2367173362Sbenjsc bus_addr_t physaddr; 2368173362Sbenjsc int error; 2369173362Sbenjsc 2370173362Sbenjsc desc = &ring->desc[ring->cur]; 2371173362Sbenjsc data = &ring->data[ring->cur]; 2372173362Sbenjsc 2373173362Sbenjsc m0 = ieee80211_beacon_alloc(ic, ni, &bo); 2374173362Sbenjsc if (m0 == NULL) { 2375173362Sbenjsc device_printf(sc->sc_dev, "could not allocate beacon frame\n"); 2376173362Sbenjsc return ENOMEM; 2377173362Sbenjsc } 2378173362Sbenjsc 2379173362Sbenjsc cmd = &ring->cmd[ring->cur]; 2380173362Sbenjsc cmd->code = WPI_CMD_SET_BEACON; 2381173362Sbenjsc cmd->flags = 0; 2382173362Sbenjsc cmd->qid = ring->qid; 2383173362Sbenjsc cmd->idx = ring->cur; 2384173362Sbenjsc 2385173362Sbenjsc bcn = (struct wpi_cmd_beacon *)cmd->data; 2386173362Sbenjsc memset(bcn, 0, sizeof (struct wpi_cmd_beacon)); 2387173362Sbenjsc bcn->id = WPI_ID_BROADCAST; 2388173362Sbenjsc bcn->ofdm_mask = 0xff; 2389173362Sbenjsc bcn->cck_mask = 0x0f; 2390173362Sbenjsc bcn->lifetime = htole32(WPI_LIFETIME_INFINITE); 2391173362Sbenjsc bcn->len = htole16(m0->m_pkthdr.len); 2392173362Sbenjsc bcn->rate = (ic->ic_curmode == IEEE80211_MODE_11A) ? 2393173362Sbenjsc wpi_plcp_signal(12) : wpi_plcp_signal(2); 2394173362Sbenjsc bcn->flags = htole32(WPI_TX_AUTO_SEQ | WPI_TX_INSERT_TSTAMP); 2395173362Sbenjsc 2396173362Sbenjsc /* save and trim IEEE802.11 header */ 2397173362Sbenjsc m_copydata(m0, 0, sizeof (struct ieee80211_frame), (caddr_t)&bcn->wh); 2398173362Sbenjsc m_adj(m0, sizeof (struct ieee80211_frame)); 2399173362Sbenjsc 2400173362Sbenjsc /* assume beacon frame is contiguous */ 2401173362Sbenjsc error = bus_dmamap_load(ring->data_dmat, data->map, mtod(m0, void *), 2402173362Sbenjsc m0->m_pkthdr.len, wpi_dma_map_addr, &physaddr, 0); 2403173362Sbenjsc if (error != 0) { 2404173362Sbenjsc device_printf(sc->sc_dev, "could not map beacon\n"); 2405173362Sbenjsc m_freem(m0); 2406173362Sbenjsc return error; 2407173362Sbenjsc } 2408173362Sbenjsc 2409173362Sbenjsc data->m = m0; 2410173362Sbenjsc 2411173362Sbenjsc /* first scatter/gather segment is used by the beacon command */ 2412173362Sbenjsc desc->flags = htole32(WPI_PAD32(m0->m_pkthdr.len) << 28 | 2 << 24); 2413173362Sbenjsc desc->segs[0].addr = htole32(ring->cmd_dma.paddr + 2414173362Sbenjsc ring->cur * sizeof (struct wpi_tx_cmd)); 2415173362Sbenjsc desc->segs[0].len = htole32(4 + sizeof (struct wpi_cmd_beacon)); 2416173362Sbenjsc desc->segs[1].addr = htole32(physaddr); 2417173362Sbenjsc desc->segs[1].len = htole32(m0->m_pkthdr.len); 2418173362Sbenjsc 2419173362Sbenjsc /* kick cmd ring */ 2420173362Sbenjsc ring->cur = (ring->cur + 1) % WPI_CMD_RING_COUNT; 2421173362Sbenjsc WPI_WRITE(sc, WPI_TX_WIDX, ring->qid << 8 | ring->cur); 2422173362Sbenjsc 2423173362Sbenjsc return 0; 2424173362Sbenjsc} 2425173362Sbenjsc#endif 2426173362Sbenjsc 2427173362Sbenjscstatic int 2428178354Ssamwpi_auth(struct wpi_softc *sc, struct ieee80211vap *vap) 2429173362Sbenjsc{ 2430178354Ssam struct ieee80211com *ic = vap->iv_ic; 2431178354Ssam struct ieee80211_node *ni = vap->iv_bss; 2432173362Sbenjsc struct wpi_node_info node; 2433173362Sbenjsc int error; 2434173362Sbenjsc 2435177043Sthompsa 2436173362Sbenjsc /* update adapter's configuration */ 2437177043Sthompsa sc->config.associd = 0; 2438177043Sthompsa sc->config.filter &= ~htole32(WPI_FILTER_BSS); 2439173362Sbenjsc IEEE80211_ADDR_COPY(sc->config.bssid, ni->ni_bssid); 2440173362Sbenjsc sc->config.chan = ieee80211_chan2ieee(ic, ni->ni_chan); 2441173362Sbenjsc if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) { 2442173362Sbenjsc sc->config.flags |= htole32(WPI_CONFIG_AUTO | 2443173362Sbenjsc WPI_CONFIG_24GHZ); 2444216522Sbschmidt } else { 2445216522Sbschmidt sc->config.flags &= ~htole32(WPI_CONFIG_AUTO | 2446216522Sbschmidt WPI_CONFIG_24GHZ); 2447173362Sbenjsc } 2448178354Ssam if (IEEE80211_IS_CHAN_A(ni->ni_chan)) { 2449173362Sbenjsc sc->config.cck_mask = 0; 2450173362Sbenjsc sc->config.ofdm_mask = 0x15; 2451178354Ssam } else if (IEEE80211_IS_CHAN_B(ni->ni_chan)) { 2452173362Sbenjsc sc->config.cck_mask = 0x03; 2453173362Sbenjsc sc->config.ofdm_mask = 0; 2454178354Ssam } else { 2455178354Ssam /* XXX assume 802.11b/g */ 2456173362Sbenjsc sc->config.cck_mask = 0x0f; 2457173362Sbenjsc sc->config.ofdm_mask = 0x15; 2458173362Sbenjsc } 2459173362Sbenjsc 2460173362Sbenjsc DPRINTF(("config chan %d flags %x cck %x ofdm %x\n", sc->config.chan, 2461173362Sbenjsc sc->config.flags, sc->config.cck_mask, sc->config.ofdm_mask)); 2462173362Sbenjsc error = wpi_cmd(sc, WPI_CMD_CONFIGURE, &sc->config, 2463173362Sbenjsc sizeof (struct wpi_config), 1); 2464173362Sbenjsc if (error != 0) { 2465173362Sbenjsc device_printf(sc->sc_dev, "could not configure\n"); 2466173362Sbenjsc return error; 2467173362Sbenjsc } 2468173362Sbenjsc 2469173362Sbenjsc /* configuration has changed, set Tx power accordingly */ 2470173362Sbenjsc if ((error = wpi_set_txpower(sc, ni->ni_chan, 1)) != 0) { 2471173362Sbenjsc device_printf(sc->sc_dev, "could not set Tx power\n"); 2472173362Sbenjsc return error; 2473173362Sbenjsc } 2474173362Sbenjsc 2475173362Sbenjsc /* add default node */ 2476173362Sbenjsc memset(&node, 0, sizeof node); 2477173362Sbenjsc IEEE80211_ADDR_COPY(node.bssid, ni->ni_bssid); 2478173362Sbenjsc node.id = WPI_ID_BSS; 2479173362Sbenjsc node.rate = (ic->ic_curmode == IEEE80211_MODE_11A) ? 2480173362Sbenjsc wpi_plcp_signal(12) : wpi_plcp_signal(2); 2481173362Sbenjsc node.action = htole32(WPI_ACTION_SET_RATE); 2482173362Sbenjsc node.antenna = WPI_ANTENNA_BOTH; 2483173362Sbenjsc error = wpi_cmd(sc, WPI_CMD_ADD_NODE, &node, sizeof node, 1); 2484177043Sthompsa if (error != 0) 2485177043Sthompsa device_printf(sc->sc_dev, "could not add BSS node\n"); 2486177043Sthompsa 2487177043Sthompsa return (error); 2488177043Sthompsa} 2489177043Sthompsa 2490177043Sthompsastatic int 2491178354Ssamwpi_run(struct wpi_softc *sc, struct ieee80211vap *vap) 2492177043Sthompsa{ 2493178354Ssam struct ieee80211com *ic = vap->iv_ic; 2494178354Ssam struct ieee80211_node *ni = vap->iv_bss; 2495177043Sthompsa int error; 2496177043Sthompsa 2497178354Ssam if (vap->iv_opmode == IEEE80211_M_MONITOR) { 2498178354Ssam /* link LED blinks while monitoring */ 2499178354Ssam wpi_set_led(sc, WPI_LED_LINK, 5, 5); 2500178354Ssam return 0; 2501178354Ssam } 2502178354Ssam 2503177043Sthompsa wpi_enable_tsf(sc, ni); 2504177043Sthompsa 2505177043Sthompsa /* update adapter's configuration */ 2506177043Sthompsa sc->config.associd = htole16(ni->ni_associd & ~0xc000); 2507177043Sthompsa /* short preamble/slot time are negotiated when associating */ 2508177043Sthompsa sc->config.flags &= ~htole32(WPI_CONFIG_SHPREAMBLE | 2509177043Sthompsa WPI_CONFIG_SHSLOT); 2510177043Sthompsa if (ic->ic_flags & IEEE80211_F_SHSLOT) 2511177043Sthompsa sc->config.flags |= htole32(WPI_CONFIG_SHSLOT); 2512177043Sthompsa if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) 2513177043Sthompsa sc->config.flags |= htole32(WPI_CONFIG_SHPREAMBLE); 2514177043Sthompsa sc->config.filter |= htole32(WPI_FILTER_BSS); 2515177043Sthompsa 2516177043Sthompsa /* XXX put somewhere HC_QOS_SUPPORT_ASSOC + HC_IBSS_START */ 2517177043Sthompsa 2518177043Sthompsa DPRINTF(("config chan %d flags %x\n", sc->config.chan, 2519177043Sthompsa sc->config.flags)); 2520177043Sthompsa error = wpi_cmd(sc, WPI_CMD_CONFIGURE, &sc->config, sizeof (struct 2521177043Sthompsa wpi_config), 1); 2522173362Sbenjsc if (error != 0) { 2523177043Sthompsa device_printf(sc->sc_dev, "could not update configuration\n"); 2524173362Sbenjsc return error; 2525173362Sbenjsc } 2526173362Sbenjsc 2527178354Ssam error = wpi_set_txpower(sc, ni->ni_chan, 1); 2528177043Sthompsa if (error != 0) { 2529177043Sthompsa device_printf(sc->sc_dev, "could set txpower\n"); 2530177043Sthompsa return error; 2531177043Sthompsa } 2532173362Sbenjsc 2533177043Sthompsa /* link LED always on while associated */ 2534177043Sthompsa wpi_set_led(sc, WPI_LED_LINK, 0, 1); 2535177043Sthompsa 2536177043Sthompsa /* start automatic rate control timer */ 2537178354Ssam callout_reset(&sc->calib_to, 60*hz, wpi_calib_timeout, sc); 2538177043Sthompsa 2539177043Sthompsa return (error); 2540173362Sbenjsc} 2541173362Sbenjsc 2542173362Sbenjsc/* 2543173362Sbenjsc * Send a scan request to the firmware. Since this command is huge, we map it 2544173362Sbenjsc * into a mbufcluster instead of using the pre-allocated set of commands. Note, 2545173362Sbenjsc * much of this code is similar to that in wpi_cmd but because we must manually 2546173362Sbenjsc * construct the probe & channels, we duplicate what's needed here. XXX In the 2547173362Sbenjsc * future, this function should be modified to use wpi_cmd to help cleanup the 2548173362Sbenjsc * code base. 2549173362Sbenjsc */ 2550173362Sbenjscstatic int 2551173362Sbenjscwpi_scan(struct wpi_softc *sc) 2552173362Sbenjsc{ 2553178354Ssam struct ifnet *ifp = sc->sc_ifp; 2554178354Ssam struct ieee80211com *ic = ifp->if_l2com; 2555177043Sthompsa struct ieee80211_scan_state *ss = ic->ic_scan; 2556173362Sbenjsc struct wpi_tx_ring *ring = &sc->cmdq; 2557173362Sbenjsc struct wpi_tx_desc *desc; 2558173362Sbenjsc struct wpi_tx_data *data; 2559173362Sbenjsc struct wpi_tx_cmd *cmd; 2560173362Sbenjsc struct wpi_scan_hdr *hdr; 2561173362Sbenjsc struct wpi_scan_chan *chan; 2562173362Sbenjsc struct ieee80211_frame *wh; 2563173362Sbenjsc struct ieee80211_rateset *rs; 2564173362Sbenjsc struct ieee80211_channel *c; 2565173362Sbenjsc enum ieee80211_phymode mode; 2566173362Sbenjsc uint8_t *frm; 2567177043Sthompsa int nrates, pktlen, error, i, nssid; 2568173362Sbenjsc bus_addr_t physaddr; 2569173362Sbenjsc 2570173362Sbenjsc desc = &ring->desc[ring->cur]; 2571173362Sbenjsc data = &ring->data[ring->cur]; 2572173362Sbenjsc 2573243857Sglebius data->m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); 2574173362Sbenjsc if (data->m == NULL) { 2575173362Sbenjsc device_printf(sc->sc_dev, 2576173362Sbenjsc "could not allocate mbuf for scan command\n"); 2577173362Sbenjsc return ENOMEM; 2578173362Sbenjsc } 2579173362Sbenjsc 2580173362Sbenjsc cmd = mtod(data->m, struct wpi_tx_cmd *); 2581173362Sbenjsc cmd->code = WPI_CMD_SCAN; 2582173362Sbenjsc cmd->flags = 0; 2583173362Sbenjsc cmd->qid = ring->qid; 2584173362Sbenjsc cmd->idx = ring->cur; 2585173362Sbenjsc 2586173362Sbenjsc hdr = (struct wpi_scan_hdr *)cmd->data; 2587173362Sbenjsc memset(hdr, 0, sizeof(struct wpi_scan_hdr)); 2588173362Sbenjsc 2589173362Sbenjsc /* 2590173362Sbenjsc * Move to the next channel if no packets are received within 5 msecs 2591173362Sbenjsc * after sending the probe request (this helps to reduce the duration 2592173362Sbenjsc * of active scans). 2593173362Sbenjsc */ 2594173362Sbenjsc hdr->quiet = htole16(5); 2595173362Sbenjsc hdr->threshold = htole16(1); 2596173362Sbenjsc 2597173362Sbenjsc if (IEEE80211_IS_CHAN_A(ic->ic_curchan)) { 2598173362Sbenjsc /* send probe requests at 6Mbps */ 2599173362Sbenjsc hdr->tx.rate = wpi_ridx_to_plcp[WPI_OFDM6]; 2600173362Sbenjsc 2601173362Sbenjsc /* Enable crc checking */ 2602173362Sbenjsc hdr->promotion = htole16(1); 2603173362Sbenjsc } else { 2604173362Sbenjsc hdr->flags = htole32(WPI_CONFIG_24GHZ | WPI_CONFIG_AUTO); 2605173362Sbenjsc /* send probe requests at 1Mbps */ 2606173362Sbenjsc hdr->tx.rate = wpi_ridx_to_plcp[WPI_CCK1]; 2607173362Sbenjsc } 2608173362Sbenjsc hdr->tx.id = WPI_ID_BROADCAST; 2609173362Sbenjsc hdr->tx.lifetime = htole32(WPI_LIFETIME_INFINITE); 2610173362Sbenjsc hdr->tx.flags = htole32(WPI_TX_AUTO_SEQ); 2611173362Sbenjsc 2612178354Ssam memset(hdr->scan_essids, 0, sizeof(hdr->scan_essids)); 2613177043Sthompsa nssid = MIN(ss->ss_nssid, WPI_SCAN_MAX_ESSIDS); 2614178354Ssam for (i = 0; i < nssid; i++) { 2615177043Sthompsa hdr->scan_essids[i].id = IEEE80211_ELEMID_SSID; 2616177043Sthompsa hdr->scan_essids[i].esslen = MIN(ss->ss_ssid[i].len, 32); 2617177043Sthompsa memcpy(hdr->scan_essids[i].essid, ss->ss_ssid[i].ssid, 2618177043Sthompsa hdr->scan_essids[i].esslen); 2619179957Sthompsa#ifdef WPI_DEBUG 2620177043Sthompsa if (wpi_debug & WPI_DEBUG_SCANNING) { 2621177043Sthompsa printf("Scanning Essid: "); 2622178354Ssam ieee80211_print_essid(hdr->scan_essids[i].essid, 2623178354Ssam hdr->scan_essids[i].esslen); 2624177043Sthompsa printf("\n"); 2625177043Sthompsa } 2626179957Sthompsa#endif 2627173362Sbenjsc } 2628173362Sbenjsc 2629173362Sbenjsc /* 2630173362Sbenjsc * Build a probe request frame. Most of the following code is a 2631173362Sbenjsc * copy & paste of what is done in net80211. 2632173362Sbenjsc */ 2633173362Sbenjsc wh = (struct ieee80211_frame *)&hdr->scan_essids[4]; 2634173362Sbenjsc wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | 2635173362Sbenjsc IEEE80211_FC0_SUBTYPE_PROBE_REQ; 2636173362Sbenjsc wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; 2637173362Sbenjsc IEEE80211_ADDR_COPY(wh->i_addr1, ifp->if_broadcastaddr); 2638190526Ssam IEEE80211_ADDR_COPY(wh->i_addr2, IF_LLADDR(ifp)); 2639173362Sbenjsc IEEE80211_ADDR_COPY(wh->i_addr3, ifp->if_broadcastaddr); 2640173362Sbenjsc *(u_int16_t *)&wh->i_dur[0] = 0; /* filled by h/w */ 2641173362Sbenjsc *(u_int16_t *)&wh->i_seq[0] = 0; /* filled by h/w */ 2642173362Sbenjsc 2643173362Sbenjsc frm = (uint8_t *)(wh + 1); 2644173362Sbenjsc 2645173362Sbenjsc /* add essid IE, the hardware will fill this in for us */ 2646173362Sbenjsc *frm++ = IEEE80211_ELEMID_SSID; 2647173362Sbenjsc *frm++ = 0; 2648173362Sbenjsc 2649173362Sbenjsc mode = ieee80211_chan2mode(ic->ic_curchan); 2650173362Sbenjsc rs = &ic->ic_sup_rates[mode]; 2651173362Sbenjsc 2652173362Sbenjsc /* add supported rates IE */ 2653173362Sbenjsc *frm++ = IEEE80211_ELEMID_RATES; 2654173362Sbenjsc nrates = rs->rs_nrates; 2655173362Sbenjsc if (nrates > IEEE80211_RATE_SIZE) 2656173362Sbenjsc nrates = IEEE80211_RATE_SIZE; 2657173362Sbenjsc *frm++ = nrates; 2658173362Sbenjsc memcpy(frm, rs->rs_rates, nrates); 2659173362Sbenjsc frm += nrates; 2660173362Sbenjsc 2661173362Sbenjsc /* add supported xrates IE */ 2662173362Sbenjsc if (rs->rs_nrates > IEEE80211_RATE_SIZE) { 2663173362Sbenjsc nrates = rs->rs_nrates - IEEE80211_RATE_SIZE; 2664173362Sbenjsc *frm++ = IEEE80211_ELEMID_XRATES; 2665173362Sbenjsc *frm++ = nrates; 2666173362Sbenjsc memcpy(frm, rs->rs_rates + IEEE80211_RATE_SIZE, nrates); 2667173362Sbenjsc frm += nrates; 2668173362Sbenjsc } 2669173362Sbenjsc 2670173362Sbenjsc /* setup length of probe request */ 2671173362Sbenjsc hdr->tx.len = htole16(frm - (uint8_t *)wh); 2672173362Sbenjsc 2673173362Sbenjsc /* 2674173362Sbenjsc * Construct information about the channel that we 2675173362Sbenjsc * want to scan. The firmware expects this to be directly 2676173362Sbenjsc * after the scan probe request 2677173362Sbenjsc */ 2678173362Sbenjsc c = ic->ic_curchan; 2679173362Sbenjsc chan = (struct wpi_scan_chan *)frm; 2680173362Sbenjsc chan->chan = ieee80211_chan2ieee(ic, c); 2681173362Sbenjsc chan->flags = 0; 2682173362Sbenjsc if (!(c->ic_flags & IEEE80211_CHAN_PASSIVE)) { 2683173362Sbenjsc chan->flags |= WPI_CHAN_ACTIVE; 2684178354Ssam if (nssid != 0) 2685173362Sbenjsc chan->flags |= WPI_CHAN_DIRECT; 2686173362Sbenjsc } 2687173362Sbenjsc chan->gain_dsp = 0x6e; /* Default level */ 2688173362Sbenjsc if (IEEE80211_IS_CHAN_5GHZ(c)) { 2689173362Sbenjsc chan->active = htole16(10); 2690178354Ssam chan->passive = htole16(ss->ss_maxdwell); 2691173362Sbenjsc chan->gain_radio = 0x3b; 2692173362Sbenjsc } else { 2693173362Sbenjsc chan->active = htole16(20); 2694178354Ssam chan->passive = htole16(ss->ss_maxdwell); 2695173362Sbenjsc chan->gain_radio = 0x28; 2696173362Sbenjsc } 2697173362Sbenjsc 2698173362Sbenjsc DPRINTFN(WPI_DEBUG_SCANNING, 2699173362Sbenjsc ("Scanning %u Passive: %d\n", 2700173362Sbenjsc chan->chan, 2701173362Sbenjsc c->ic_flags & IEEE80211_CHAN_PASSIVE)); 2702173362Sbenjsc 2703173362Sbenjsc hdr->nchan++; 2704173362Sbenjsc chan++; 2705173362Sbenjsc 2706173362Sbenjsc frm += sizeof (struct wpi_scan_chan); 2707173362Sbenjsc#if 0 2708173362Sbenjsc // XXX All Channels.... 2709173362Sbenjsc for (c = &ic->ic_channels[1]; 2710173362Sbenjsc c <= &ic->ic_channels[IEEE80211_CHAN_MAX]; c++) { 2711173362Sbenjsc if ((c->ic_flags & ic->ic_curchan->ic_flags) != ic->ic_curchan->ic_flags) 2712173362Sbenjsc continue; 2713173362Sbenjsc 2714173362Sbenjsc chan->chan = ieee80211_chan2ieee(ic, c); 2715173362Sbenjsc chan->flags = 0; 2716173362Sbenjsc if (!(c->ic_flags & IEEE80211_CHAN_PASSIVE)) { 2717173362Sbenjsc chan->flags |= WPI_CHAN_ACTIVE; 2718173362Sbenjsc if (ic->ic_des_ssid[0].len != 0) 2719173362Sbenjsc chan->flags |= WPI_CHAN_DIRECT; 2720173362Sbenjsc } 2721173362Sbenjsc chan->gain_dsp = 0x6e; /* Default level */ 2722173362Sbenjsc if (IEEE80211_IS_CHAN_5GHZ(c)) { 2723173362Sbenjsc chan->active = htole16(10); 2724173362Sbenjsc chan->passive = htole16(110); 2725173362Sbenjsc chan->gain_radio = 0x3b; 2726173362Sbenjsc } else { 2727173362Sbenjsc chan->active = htole16(20); 2728173362Sbenjsc chan->passive = htole16(120); 2729173362Sbenjsc chan->gain_radio = 0x28; 2730173362Sbenjsc } 2731173362Sbenjsc 2732173362Sbenjsc DPRINTFN(WPI_DEBUG_SCANNING, 2733173362Sbenjsc ("Scanning %u Passive: %d\n", 2734173362Sbenjsc chan->chan, 2735173362Sbenjsc c->ic_flags & IEEE80211_CHAN_PASSIVE)); 2736173362Sbenjsc 2737173362Sbenjsc hdr->nchan++; 2738173362Sbenjsc chan++; 2739173362Sbenjsc 2740173362Sbenjsc frm += sizeof (struct wpi_scan_chan); 2741173362Sbenjsc } 2742173362Sbenjsc#endif 2743173362Sbenjsc 2744173362Sbenjsc hdr->len = htole16(frm - (uint8_t *)hdr); 2745173362Sbenjsc pktlen = frm - (uint8_t *)cmd; 2746173362Sbenjsc 2747173362Sbenjsc error = bus_dmamap_load(ring->data_dmat, data->map, cmd, pktlen, 2748173362Sbenjsc wpi_dma_map_addr, &physaddr, BUS_DMA_NOWAIT); 2749173362Sbenjsc if (error != 0) { 2750173362Sbenjsc device_printf(sc->sc_dev, "could not map scan command\n"); 2751173362Sbenjsc m_freem(data->m); 2752173362Sbenjsc data->m = NULL; 2753173362Sbenjsc return error; 2754173362Sbenjsc } 2755173362Sbenjsc 2756173362Sbenjsc desc->flags = htole32(WPI_PAD32(pktlen) << 28 | 1 << 24); 2757173362Sbenjsc desc->segs[0].addr = htole32(physaddr); 2758173362Sbenjsc desc->segs[0].len = htole32(pktlen); 2759173362Sbenjsc 2760173362Sbenjsc bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, 2761173362Sbenjsc BUS_DMASYNC_PREWRITE); 2762173362Sbenjsc bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE); 2763173362Sbenjsc 2764173362Sbenjsc /* kick cmd ring */ 2765173362Sbenjsc ring->cur = (ring->cur + 1) % WPI_CMD_RING_COUNT; 2766173362Sbenjsc WPI_WRITE(sc, WPI_TX_WIDX, ring->qid << 8 | ring->cur); 2767173362Sbenjsc 2768177043Sthompsa sc->sc_scan_timer = 5; 2769173362Sbenjsc return 0; /* will be notified async. of failure/success */ 2770173362Sbenjsc} 2771173362Sbenjsc 2772173362Sbenjsc/** 2773173362Sbenjsc * Configure the card to listen to a particular channel, this transisions the 2774173362Sbenjsc * card in to being able to receive frames from remote devices. 2775173362Sbenjsc */ 2776173362Sbenjscstatic int 2777173362Sbenjscwpi_config(struct wpi_softc *sc) 2778173362Sbenjsc{ 2779178354Ssam struct ifnet *ifp = sc->sc_ifp; 2780178354Ssam struct ieee80211com *ic = ifp->if_l2com; 2781173362Sbenjsc struct wpi_power power; 2782173362Sbenjsc struct wpi_bluetooth bluetooth; 2783173362Sbenjsc struct wpi_node_info node; 2784173362Sbenjsc int error; 2785173362Sbenjsc 2786173362Sbenjsc /* set power mode */ 2787173362Sbenjsc memset(&power, 0, sizeof power); 2788173362Sbenjsc power.flags = htole32(WPI_POWER_CAM|0x8); 2789173362Sbenjsc error = wpi_cmd(sc, WPI_CMD_SET_POWER_MODE, &power, sizeof power, 0); 2790173362Sbenjsc if (error != 0) { 2791173362Sbenjsc device_printf(sc->sc_dev, "could not set power mode\n"); 2792173362Sbenjsc return error; 2793173362Sbenjsc } 2794173362Sbenjsc 2795173362Sbenjsc /* configure bluetooth coexistence */ 2796173362Sbenjsc memset(&bluetooth, 0, sizeof bluetooth); 2797173362Sbenjsc bluetooth.flags = 3; 2798173362Sbenjsc bluetooth.lead = 0xaa; 2799173362Sbenjsc bluetooth.kill = 1; 2800173362Sbenjsc error = wpi_cmd(sc, WPI_CMD_BLUETOOTH, &bluetooth, sizeof bluetooth, 2801173362Sbenjsc 0); 2802173362Sbenjsc if (error != 0) { 2803173362Sbenjsc device_printf(sc->sc_dev, 2804173362Sbenjsc "could not configure bluetooth coexistence\n"); 2805173362Sbenjsc return error; 2806173362Sbenjsc } 2807173362Sbenjsc 2808173362Sbenjsc /* configure adapter */ 2809173362Sbenjsc memset(&sc->config, 0, sizeof (struct wpi_config)); 2810190526Ssam IEEE80211_ADDR_COPY(sc->config.myaddr, IF_LLADDR(ifp)); 2811173362Sbenjsc /*set default channel*/ 2812173362Sbenjsc sc->config.chan = htole16(ieee80211_chan2ieee(ic, ic->ic_curchan)); 2813173362Sbenjsc sc->config.flags = htole32(WPI_CONFIG_TSF); 2814173362Sbenjsc if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) { 2815173362Sbenjsc sc->config.flags |= htole32(WPI_CONFIG_AUTO | 2816173362Sbenjsc WPI_CONFIG_24GHZ); 2817173362Sbenjsc } 2818173362Sbenjsc sc->config.filter = 0; 2819173362Sbenjsc switch (ic->ic_opmode) { 2820173362Sbenjsc case IEEE80211_M_STA: 2821173362Sbenjsc case IEEE80211_M_WDS: /* No know setup, use STA for now */ 2822173362Sbenjsc sc->config.mode = WPI_MODE_STA; 2823173362Sbenjsc sc->config.filter |= htole32(WPI_FILTER_MULTICAST); 2824173362Sbenjsc break; 2825173362Sbenjsc case IEEE80211_M_IBSS: 2826173362Sbenjsc case IEEE80211_M_AHDEMO: 2827173362Sbenjsc sc->config.mode = WPI_MODE_IBSS; 2828173362Sbenjsc sc->config.filter |= htole32(WPI_FILTER_BEACON | 2829173362Sbenjsc WPI_FILTER_MULTICAST); 2830173362Sbenjsc break; 2831173362Sbenjsc case IEEE80211_M_HOSTAP: 2832173362Sbenjsc sc->config.mode = WPI_MODE_HOSTAP; 2833173362Sbenjsc break; 2834173362Sbenjsc case IEEE80211_M_MONITOR: 2835173362Sbenjsc sc->config.mode = WPI_MODE_MONITOR; 2836173362Sbenjsc sc->config.filter |= htole32(WPI_FILTER_MULTICAST | 2837173362Sbenjsc WPI_FILTER_CTL | WPI_FILTER_PROMISC); 2838173362Sbenjsc break; 2839195562Srpaulo default: 2840195562Srpaulo device_printf(sc->sc_dev, "unknown opmode %d\n", ic->ic_opmode); 2841195562Srpaulo return EINVAL; 2842173362Sbenjsc } 2843173362Sbenjsc sc->config.cck_mask = 0x0f; /* not yet negotiated */ 2844173362Sbenjsc sc->config.ofdm_mask = 0xff; /* not yet negotiated */ 2845173362Sbenjsc error = wpi_cmd(sc, WPI_CMD_CONFIGURE, &sc->config, 2846173362Sbenjsc sizeof (struct wpi_config), 0); 2847173362Sbenjsc if (error != 0) { 2848173362Sbenjsc device_printf(sc->sc_dev, "configure command failed\n"); 2849173362Sbenjsc return error; 2850173362Sbenjsc } 2851173362Sbenjsc 2852173362Sbenjsc /* configuration has changed, set Tx power accordingly */ 2853177043Sthompsa if ((error = wpi_set_txpower(sc, ic->ic_curchan, 0)) != 0) { 2854173362Sbenjsc device_printf(sc->sc_dev, "could not set Tx power\n"); 2855173362Sbenjsc return error; 2856173362Sbenjsc } 2857173362Sbenjsc 2858173362Sbenjsc /* add broadcast node */ 2859173362Sbenjsc memset(&node, 0, sizeof node); 2860173362Sbenjsc IEEE80211_ADDR_COPY(node.bssid, ifp->if_broadcastaddr); 2861173362Sbenjsc node.id = WPI_ID_BROADCAST; 2862173362Sbenjsc node.rate = wpi_plcp_signal(2); 2863173362Sbenjsc error = wpi_cmd(sc, WPI_CMD_ADD_NODE, &node, sizeof node, 0); 2864173362Sbenjsc if (error != 0) { 2865173362Sbenjsc device_printf(sc->sc_dev, "could not add broadcast node\n"); 2866173362Sbenjsc return error; 2867173362Sbenjsc } 2868173362Sbenjsc 2869173362Sbenjsc /* Setup rate scalling */ 2870173362Sbenjsc error = wpi_mrr_setup(sc); 2871173362Sbenjsc if (error != 0) { 2872173362Sbenjsc device_printf(sc->sc_dev, "could not setup MRR\n"); 2873173362Sbenjsc return error; 2874173362Sbenjsc } 2875173362Sbenjsc 2876173362Sbenjsc return 0; 2877173362Sbenjsc} 2878173362Sbenjsc 2879173362Sbenjscstatic void 2880173362Sbenjscwpi_stop_master(struct wpi_softc *sc) 2881173362Sbenjsc{ 2882173362Sbenjsc uint32_t tmp; 2883173362Sbenjsc int ntries; 2884173362Sbenjsc 2885173362Sbenjsc DPRINTFN(WPI_DEBUG_HW,("Disabling Firmware execution\n")); 2886173362Sbenjsc 2887173362Sbenjsc tmp = WPI_READ(sc, WPI_RESET); 2888173362Sbenjsc WPI_WRITE(sc, WPI_RESET, tmp | WPI_STOP_MASTER | WPI_NEVO_RESET); 2889173362Sbenjsc 2890173362Sbenjsc tmp = WPI_READ(sc, WPI_GPIO_CTL); 2891173362Sbenjsc if ((tmp & WPI_GPIO_PWR_STATUS) == WPI_GPIO_PWR_SLEEP) 2892173362Sbenjsc return; /* already asleep */ 2893173362Sbenjsc 2894173362Sbenjsc for (ntries = 0; ntries < 100; ntries++) { 2895173362Sbenjsc if (WPI_READ(sc, WPI_RESET) & WPI_MASTER_DISABLED) 2896173362Sbenjsc break; 2897173362Sbenjsc DELAY(10); 2898173362Sbenjsc } 2899173362Sbenjsc if (ntries == 100) { 2900173362Sbenjsc device_printf(sc->sc_dev, "timeout waiting for master\n"); 2901173362Sbenjsc } 2902173362Sbenjsc} 2903173362Sbenjsc 2904173362Sbenjscstatic int 2905173362Sbenjscwpi_power_up(struct wpi_softc *sc) 2906173362Sbenjsc{ 2907173362Sbenjsc uint32_t tmp; 2908173362Sbenjsc int ntries; 2909173362Sbenjsc 2910173362Sbenjsc wpi_mem_lock(sc); 2911173362Sbenjsc tmp = wpi_mem_read(sc, WPI_MEM_POWER); 2912173362Sbenjsc wpi_mem_write(sc, WPI_MEM_POWER, tmp & ~0x03000000); 2913173362Sbenjsc wpi_mem_unlock(sc); 2914173362Sbenjsc 2915173362Sbenjsc for (ntries = 0; ntries < 5000; ntries++) { 2916173362Sbenjsc if (WPI_READ(sc, WPI_GPIO_STATUS) & WPI_POWERED) 2917173362Sbenjsc break; 2918173362Sbenjsc DELAY(10); 2919173362Sbenjsc } 2920173362Sbenjsc if (ntries == 5000) { 2921173362Sbenjsc device_printf(sc->sc_dev, 2922173362Sbenjsc "timeout waiting for NIC to power up\n"); 2923173362Sbenjsc return ETIMEDOUT; 2924173362Sbenjsc } 2925173362Sbenjsc return 0; 2926173362Sbenjsc} 2927173362Sbenjsc 2928173362Sbenjscstatic int 2929173362Sbenjscwpi_reset(struct wpi_softc *sc) 2930173362Sbenjsc{ 2931173362Sbenjsc uint32_t tmp; 2932173362Sbenjsc int ntries; 2933173362Sbenjsc 2934173362Sbenjsc DPRINTFN(WPI_DEBUG_HW, 2935173362Sbenjsc ("Resetting the card - clearing any uploaded firmware\n")); 2936173362Sbenjsc 2937173362Sbenjsc /* clear any pending interrupts */ 2938173362Sbenjsc WPI_WRITE(sc, WPI_INTR, 0xffffffff); 2939173362Sbenjsc 2940173362Sbenjsc tmp = WPI_READ(sc, WPI_PLL_CTL); 2941173362Sbenjsc WPI_WRITE(sc, WPI_PLL_CTL, tmp | WPI_PLL_INIT); 2942173362Sbenjsc 2943173362Sbenjsc tmp = WPI_READ(sc, WPI_CHICKEN); 2944173362Sbenjsc WPI_WRITE(sc, WPI_CHICKEN, tmp | WPI_CHICKEN_RXNOLOS); 2945173362Sbenjsc 2946173362Sbenjsc tmp = WPI_READ(sc, WPI_GPIO_CTL); 2947173362Sbenjsc WPI_WRITE(sc, WPI_GPIO_CTL, tmp | WPI_GPIO_INIT); 2948173362Sbenjsc 2949173362Sbenjsc /* wait for clock stabilization */ 2950173362Sbenjsc for (ntries = 0; ntries < 25000; ntries++) { 2951173362Sbenjsc if (WPI_READ(sc, WPI_GPIO_CTL) & WPI_GPIO_CLOCK) 2952173362Sbenjsc break; 2953173362Sbenjsc DELAY(10); 2954173362Sbenjsc } 2955173362Sbenjsc if (ntries == 25000) { 2956173362Sbenjsc device_printf(sc->sc_dev, 2957173362Sbenjsc "timeout waiting for clock stabilization\n"); 2958173362Sbenjsc return ETIMEDOUT; 2959173362Sbenjsc } 2960173362Sbenjsc 2961173362Sbenjsc /* initialize EEPROM */ 2962173362Sbenjsc tmp = WPI_READ(sc, WPI_EEPROM_STATUS); 2963173362Sbenjsc 2964173362Sbenjsc if ((tmp & WPI_EEPROM_VERSION) == 0) { 2965173362Sbenjsc device_printf(sc->sc_dev, "EEPROM not found\n"); 2966173362Sbenjsc return EIO; 2967173362Sbenjsc } 2968173362Sbenjsc WPI_WRITE(sc, WPI_EEPROM_STATUS, tmp & ~WPI_EEPROM_LOCKED); 2969173362Sbenjsc 2970173362Sbenjsc return 0; 2971173362Sbenjsc} 2972173362Sbenjsc 2973173362Sbenjscstatic void 2974173362Sbenjscwpi_hw_config(struct wpi_softc *sc) 2975173362Sbenjsc{ 2976173362Sbenjsc uint32_t rev, hw; 2977173362Sbenjsc 2978173362Sbenjsc /* voodoo from the Linux "driver".. */ 2979173362Sbenjsc hw = WPI_READ(sc, WPI_HWCONFIG); 2980173362Sbenjsc 2981173362Sbenjsc rev = pci_read_config(sc->sc_dev, PCIR_REVID, 1); 2982173362Sbenjsc if ((rev & 0xc0) == 0x40) 2983173362Sbenjsc hw |= WPI_HW_ALM_MB; 2984173362Sbenjsc else if (!(rev & 0x80)) 2985173362Sbenjsc hw |= WPI_HW_ALM_MM; 2986173362Sbenjsc 2987173362Sbenjsc if (sc->cap == 0x80) 2988173362Sbenjsc hw |= WPI_HW_SKU_MRC; 2989173362Sbenjsc 2990173362Sbenjsc hw &= ~WPI_HW_REV_D; 2991173362Sbenjsc if ((le16toh(sc->rev) & 0xf0) == 0xd0) 2992173362Sbenjsc hw |= WPI_HW_REV_D; 2993173362Sbenjsc 2994173362Sbenjsc if (sc->type > 1) 2995173362Sbenjsc hw |= WPI_HW_TYPE_B; 2996173362Sbenjsc 2997173362Sbenjsc WPI_WRITE(sc, WPI_HWCONFIG, hw); 2998173362Sbenjsc} 2999173362Sbenjsc 3000173362Sbenjscstatic void 3001177043Sthompsawpi_rfkill_resume(struct wpi_softc *sc) 3002177043Sthompsa{ 3003177043Sthompsa struct ifnet *ifp = sc->sc_ifp; 3004178354Ssam struct ieee80211com *ic = ifp->if_l2com; 3005178354Ssam struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 3006177043Sthompsa int ntries; 3007177043Sthompsa 3008177043Sthompsa /* enable firmware again */ 3009177043Sthompsa WPI_WRITE(sc, WPI_UCODE_CLR, WPI_RADIO_OFF); 3010177043Sthompsa WPI_WRITE(sc, WPI_UCODE_CLR, WPI_DISABLE_CMD); 3011177043Sthompsa 3012177043Sthompsa /* wait for thermal sensors to calibrate */ 3013177043Sthompsa for (ntries = 0; ntries < 1000; ntries++) { 3014177043Sthompsa if ((sc->temp = (int)WPI_READ(sc, WPI_TEMPERATURE)) != 0) 3015177043Sthompsa break; 3016177043Sthompsa DELAY(10); 3017177043Sthompsa } 3018177043Sthompsa 3019177043Sthompsa if (ntries == 1000) { 3020177043Sthompsa device_printf(sc->sc_dev, 3021177043Sthompsa "timeout waiting for thermal calibration\n"); 3022177043Sthompsa return; 3023177043Sthompsa } 3024177043Sthompsa DPRINTFN(WPI_DEBUG_TEMP,("temperature %d\n", sc->temp)); 3025177043Sthompsa 3026177043Sthompsa if (wpi_config(sc) != 0) { 3027177043Sthompsa device_printf(sc->sc_dev, "device config failed\n"); 3028177043Sthompsa return; 3029177043Sthompsa } 3030177043Sthompsa 3031177043Sthompsa ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 3032177043Sthompsa ifp->if_drv_flags |= IFF_DRV_RUNNING; 3033177043Sthompsa sc->flags &= ~WPI_FLAG_HW_RADIO_OFF; 3034177043Sthompsa 3035178354Ssam if (vap != NULL) { 3036178354Ssam if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { 3037178354Ssam if (vap->iv_opmode != IEEE80211_M_MONITOR) { 3038191746Sthompsa ieee80211_beacon_miss(ic); 3039178354Ssam wpi_set_led(sc, WPI_LED_LINK, 0, 1); 3040178354Ssam } else 3041178354Ssam wpi_set_led(sc, WPI_LED_LINK, 5, 5); 3042178354Ssam } else { 3043178354Ssam ieee80211_scan_next(vap); 3044177043Sthompsa wpi_set_led(sc, WPI_LED_LINK, 20, 2); 3045178354Ssam } 3046177043Sthompsa } 3047177043Sthompsa 3048177043Sthompsa callout_reset(&sc->watchdog_to, hz, wpi_watchdog, sc); 3049177043Sthompsa} 3050177043Sthompsa 3051177043Sthompsastatic void 3052177043Sthompsawpi_init_locked(struct wpi_softc *sc, int force) 3053177043Sthompsa{ 3054178354Ssam struct ifnet *ifp = sc->sc_ifp; 3055173362Sbenjsc uint32_t tmp; 3056177043Sthompsa int ntries, qid; 3057173362Sbenjsc 3058173362Sbenjsc wpi_stop_locked(sc); 3059173362Sbenjsc (void)wpi_reset(sc); 3060173362Sbenjsc 3061173362Sbenjsc wpi_mem_lock(sc); 3062173362Sbenjsc wpi_mem_write(sc, WPI_MEM_CLOCK1, 0xa00); 3063173362Sbenjsc DELAY(20); 3064173362Sbenjsc tmp = wpi_mem_read(sc, WPI_MEM_PCIDEV); 3065173362Sbenjsc wpi_mem_write(sc, WPI_MEM_PCIDEV, tmp | 0x800); 3066173362Sbenjsc wpi_mem_unlock(sc); 3067173362Sbenjsc 3068173362Sbenjsc (void)wpi_power_up(sc); 3069173362Sbenjsc wpi_hw_config(sc); 3070173362Sbenjsc 3071173362Sbenjsc /* init Rx ring */ 3072173362Sbenjsc wpi_mem_lock(sc); 3073173362Sbenjsc WPI_WRITE(sc, WPI_RX_BASE, sc->rxq.desc_dma.paddr); 3074173362Sbenjsc WPI_WRITE(sc, WPI_RX_RIDX_PTR, sc->shared_dma.paddr + 3075173362Sbenjsc offsetof(struct wpi_shared, next)); 3076173362Sbenjsc WPI_WRITE(sc, WPI_RX_WIDX, (WPI_RX_RING_COUNT - 1) & ~7); 3077173362Sbenjsc WPI_WRITE(sc, WPI_RX_CONFIG, 0xa9601010); 3078173362Sbenjsc wpi_mem_unlock(sc); 3079173362Sbenjsc 3080173362Sbenjsc /* init Tx rings */ 3081173362Sbenjsc wpi_mem_lock(sc); 3082173362Sbenjsc wpi_mem_write(sc, WPI_MEM_MODE, 2); /* bypass mode */ 3083173362Sbenjsc wpi_mem_write(sc, WPI_MEM_RA, 1); /* enable RA0 */ 3084173362Sbenjsc wpi_mem_write(sc, WPI_MEM_TXCFG, 0x3f); /* enable all 6 Tx rings */ 3085173362Sbenjsc wpi_mem_write(sc, WPI_MEM_BYPASS1, 0x10000); 3086173362Sbenjsc wpi_mem_write(sc, WPI_MEM_BYPASS2, 0x30002); 3087173362Sbenjsc wpi_mem_write(sc, WPI_MEM_MAGIC4, 4); 3088173362Sbenjsc wpi_mem_write(sc, WPI_MEM_MAGIC5, 5); 3089173362Sbenjsc 3090173362Sbenjsc WPI_WRITE(sc, WPI_TX_BASE_PTR, sc->shared_dma.paddr); 3091173362Sbenjsc WPI_WRITE(sc, WPI_MSG_CONFIG, 0xffff05a5); 3092173362Sbenjsc 3093173362Sbenjsc for (qid = 0; qid < 6; qid++) { 3094173362Sbenjsc WPI_WRITE(sc, WPI_TX_CTL(qid), 0); 3095173362Sbenjsc WPI_WRITE(sc, WPI_TX_BASE(qid), 0); 3096173362Sbenjsc WPI_WRITE(sc, WPI_TX_CONFIG(qid), 0x80200008); 3097173362Sbenjsc } 3098173362Sbenjsc wpi_mem_unlock(sc); 3099173362Sbenjsc 3100173362Sbenjsc /* clear "radio off" and "disable command" bits (reversed logic) */ 3101173362Sbenjsc WPI_WRITE(sc, WPI_UCODE_CLR, WPI_RADIO_OFF); 3102173362Sbenjsc WPI_WRITE(sc, WPI_UCODE_CLR, WPI_DISABLE_CMD); 3103173362Sbenjsc sc->flags &= ~WPI_FLAG_HW_RADIO_OFF; 3104173362Sbenjsc 3105173362Sbenjsc /* clear any pending interrupts */ 3106173362Sbenjsc WPI_WRITE(sc, WPI_INTR, 0xffffffff); 3107173362Sbenjsc 3108173362Sbenjsc /* enable interrupts */ 3109173362Sbenjsc WPI_WRITE(sc, WPI_MASK, WPI_INTR_MASK); 3110173362Sbenjsc 3111173362Sbenjsc WPI_WRITE(sc, WPI_UCODE_CLR, WPI_RADIO_OFF); 3112173362Sbenjsc WPI_WRITE(sc, WPI_UCODE_CLR, WPI_RADIO_OFF); 3113173362Sbenjsc 3114177043Sthompsa if ((wpi_load_firmware(sc)) != 0) { 3115173362Sbenjsc device_printf(sc->sc_dev, 3116173362Sbenjsc "A problem occurred loading the firmware to the driver\n"); 3117173362Sbenjsc return; 3118173362Sbenjsc } 3119173362Sbenjsc 3120173362Sbenjsc /* At this point the firmware is up and running. If the hardware 3121173362Sbenjsc * RF switch is turned off thermal calibration will fail, though 3122173362Sbenjsc * the card is still happy to continue to accept commands, catch 3123177043Sthompsa * this case and schedule a task to watch for it to be turned on. 3124173362Sbenjsc */ 3125173362Sbenjsc wpi_mem_lock(sc); 3126173362Sbenjsc tmp = wpi_mem_read(sc, WPI_MEM_HW_RADIO_OFF); 3127173362Sbenjsc wpi_mem_unlock(sc); 3128173362Sbenjsc 3129173362Sbenjsc if (!(tmp & 0x1)) { 3130173362Sbenjsc sc->flags |= WPI_FLAG_HW_RADIO_OFF; 3131173362Sbenjsc device_printf(sc->sc_dev,"Radio Transmitter is switched off\n"); 3132177043Sthompsa goto out; 3133173362Sbenjsc } 3134173362Sbenjsc 3135173362Sbenjsc /* wait for thermal sensors to calibrate */ 3136173362Sbenjsc for (ntries = 0; ntries < 1000; ntries++) { 3137173362Sbenjsc if ((sc->temp = (int)WPI_READ(sc, WPI_TEMPERATURE)) != 0) 3138173362Sbenjsc break; 3139173362Sbenjsc DELAY(10); 3140173362Sbenjsc } 3141173362Sbenjsc 3142173362Sbenjsc if (ntries == 1000) { 3143173362Sbenjsc device_printf(sc->sc_dev, 3144173362Sbenjsc "timeout waiting for thermal sensors calibration\n"); 3145173362Sbenjsc return; 3146173362Sbenjsc } 3147173362Sbenjsc DPRINTFN(WPI_DEBUG_TEMP,("temperature %d\n", sc->temp)); 3148173362Sbenjsc 3149177043Sthompsa if (wpi_config(sc) != 0) { 3150177043Sthompsa device_printf(sc->sc_dev, "device config failed\n"); 3151177043Sthompsa return; 3152177043Sthompsa } 3153177043Sthompsa 3154173362Sbenjsc ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 3155173362Sbenjsc ifp->if_drv_flags |= IFF_DRV_RUNNING; 3156177043Sthompsaout: 3157177043Sthompsa callout_reset(&sc->watchdog_to, hz, wpi_watchdog, sc); 3158173362Sbenjsc} 3159173362Sbenjsc 3160173362Sbenjscstatic void 3161178354Ssamwpi_init(void *arg) 3162173362Sbenjsc{ 3163178354Ssam struct wpi_softc *sc = arg; 3164178354Ssam struct ifnet *ifp = sc->sc_ifp; 3165178354Ssam struct ieee80211com *ic = ifp->if_l2com; 3166173362Sbenjsc 3167173362Sbenjsc WPI_LOCK(sc); 3168178354Ssam wpi_init_locked(sc, 0); 3169173362Sbenjsc WPI_UNLOCK(sc); 3170173362Sbenjsc 3171178354Ssam if (ifp->if_drv_flags & IFF_DRV_RUNNING) 3172178354Ssam ieee80211_start_all(ic); /* start all vaps */ 3173173362Sbenjsc} 3174178354Ssam 3175173362Sbenjscstatic void 3176173362Sbenjscwpi_stop_locked(struct wpi_softc *sc) 3177173362Sbenjsc{ 3178178354Ssam struct ifnet *ifp = sc->sc_ifp; 3179173362Sbenjsc uint32_t tmp; 3180173362Sbenjsc int ac; 3181173362Sbenjsc 3182177043Sthompsa sc->sc_tx_timer = 0; 3183177043Sthompsa sc->sc_scan_timer = 0; 3184173362Sbenjsc ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 3185177043Sthompsa sc->flags &= ~WPI_FLAG_HW_RADIO_OFF; 3186177043Sthompsa callout_stop(&sc->watchdog_to); 3187177043Sthompsa callout_stop(&sc->calib_to); 3188173362Sbenjsc 3189173362Sbenjsc /* disable interrupts */ 3190173362Sbenjsc WPI_WRITE(sc, WPI_MASK, 0); 3191173362Sbenjsc WPI_WRITE(sc, WPI_INTR, WPI_INTR_MASK); 3192173362Sbenjsc WPI_WRITE(sc, WPI_INTR_STATUS, 0xff); 3193173362Sbenjsc WPI_WRITE(sc, WPI_INTR_STATUS, 0x00070000); 3194173362Sbenjsc 3195173362Sbenjsc wpi_mem_lock(sc); 3196173362Sbenjsc wpi_mem_write(sc, WPI_MEM_MODE, 0); 3197173362Sbenjsc wpi_mem_unlock(sc); 3198173362Sbenjsc 3199173362Sbenjsc /* reset all Tx rings */ 3200173362Sbenjsc for (ac = 0; ac < 4; ac++) 3201173362Sbenjsc wpi_reset_tx_ring(sc, &sc->txq[ac]); 3202173362Sbenjsc wpi_reset_tx_ring(sc, &sc->cmdq); 3203173362Sbenjsc 3204173362Sbenjsc /* reset Rx ring */ 3205173362Sbenjsc wpi_reset_rx_ring(sc, &sc->rxq); 3206173362Sbenjsc 3207173362Sbenjsc wpi_mem_lock(sc); 3208173362Sbenjsc wpi_mem_write(sc, WPI_MEM_CLOCK2, 0x200); 3209173362Sbenjsc wpi_mem_unlock(sc); 3210173362Sbenjsc 3211173362Sbenjsc DELAY(5); 3212173362Sbenjsc 3213173362Sbenjsc wpi_stop_master(sc); 3214173362Sbenjsc 3215173362Sbenjsc tmp = WPI_READ(sc, WPI_RESET); 3216173362Sbenjsc WPI_WRITE(sc, WPI_RESET, tmp | WPI_SW_RESET); 3217173362Sbenjsc sc->flags &= ~WPI_FLAG_BUSY; 3218173362Sbenjsc} 3219173362Sbenjsc 3220173362Sbenjscstatic void 3221178354Ssamwpi_stop(struct wpi_softc *sc) 3222173362Sbenjsc{ 3223178354Ssam WPI_LOCK(sc); 3224178354Ssam wpi_stop_locked(sc); 3225178354Ssam WPI_UNLOCK(sc); 3226173362Sbenjsc} 3227173362Sbenjsc 3228173362Sbenjscstatic void 3229173362Sbenjscwpi_calib_timeout(void *arg) 3230173362Sbenjsc{ 3231173362Sbenjsc struct wpi_softc *sc = arg; 3232178354Ssam struct ifnet *ifp = sc->sc_ifp; 3233178354Ssam struct ieee80211com *ic = ifp->if_l2com; 3234178354Ssam struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 3235173362Sbenjsc int temp; 3236173362Sbenjsc 3237178354Ssam if (vap->iv_state != IEEE80211_S_RUN) 3238177043Sthompsa return; 3239177043Sthompsa 3240173362Sbenjsc /* update sensor data */ 3241173362Sbenjsc temp = (int)WPI_READ(sc, WPI_TEMPERATURE); 3242173362Sbenjsc DPRINTFN(WPI_DEBUG_TEMP,("Temp in calibration is: %d\n", temp)); 3243173362Sbenjsc 3244178354Ssam wpi_power_calibration(sc, temp); 3245173362Sbenjsc 3246178354Ssam callout_reset(&sc->calib_to, 60*hz, wpi_calib_timeout, sc); 3247173362Sbenjsc} 3248173362Sbenjsc 3249173362Sbenjsc/* 3250173362Sbenjsc * This function is called periodically (every 60 seconds) to adjust output 3251173362Sbenjsc * power to temperature changes. 3252173362Sbenjsc */ 3253173362Sbenjscstatic void 3254173362Sbenjscwpi_power_calibration(struct wpi_softc *sc, int temp) 3255173362Sbenjsc{ 3256178354Ssam struct ifnet *ifp = sc->sc_ifp; 3257178354Ssam struct ieee80211com *ic = ifp->if_l2com; 3258178354Ssam struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 3259178354Ssam 3260173362Sbenjsc /* sanity-check read value */ 3261173362Sbenjsc if (temp < -260 || temp > 25) { 3262173362Sbenjsc /* this can't be correct, ignore */ 3263173362Sbenjsc DPRINTFN(WPI_DEBUG_TEMP, 3264173362Sbenjsc ("out-of-range temperature reported: %d\n", temp)); 3265173362Sbenjsc return; 3266173362Sbenjsc } 3267173362Sbenjsc 3268173362Sbenjsc DPRINTFN(WPI_DEBUG_TEMP,("temperature %d->%d\n", sc->temp, temp)); 3269173362Sbenjsc 3270173362Sbenjsc /* adjust Tx power if need be */ 3271173362Sbenjsc if (abs(temp - sc->temp) <= 6) 3272173362Sbenjsc return; 3273173362Sbenjsc 3274173362Sbenjsc sc->temp = temp; 3275173362Sbenjsc 3276178354Ssam if (wpi_set_txpower(sc, vap->iv_bss->ni_chan, 1) != 0) { 3277173362Sbenjsc /* just warn, too bad for the automatic calibration... */ 3278173362Sbenjsc device_printf(sc->sc_dev,"could not adjust Tx power\n"); 3279173362Sbenjsc } 3280173362Sbenjsc} 3281173362Sbenjsc 3282173362Sbenjsc/** 3283173362Sbenjsc * Read the eeprom to find out what channels are valid for the given 3284173362Sbenjsc * band and update net80211 with what we find. 3285173362Sbenjsc */ 3286173362Sbenjscstatic void 3287173362Sbenjscwpi_read_eeprom_channels(struct wpi_softc *sc, int n) 3288173362Sbenjsc{ 3289178354Ssam struct ifnet *ifp = sc->sc_ifp; 3290178354Ssam struct ieee80211com *ic = ifp->if_l2com; 3291173362Sbenjsc const struct wpi_chan_band *band = &wpi_bands[n]; 3292173362Sbenjsc struct wpi_eeprom_chan channels[WPI_MAX_CHAN_PER_BAND]; 3293179957Sthompsa struct ieee80211_channel *c; 3294179957Sthompsa int chan, i, passive; 3295173362Sbenjsc 3296173362Sbenjsc wpi_read_prom_data(sc, band->addr, channels, 3297173362Sbenjsc band->nchan * sizeof (struct wpi_eeprom_chan)); 3298173362Sbenjsc 3299173362Sbenjsc for (i = 0; i < band->nchan; i++) { 3300173362Sbenjsc if (!(channels[i].flags & WPI_EEPROM_CHAN_VALID)) { 3301173362Sbenjsc DPRINTFN(WPI_DEBUG_HW, 3302173362Sbenjsc ("Channel Not Valid: %d, band %d\n", 3303173362Sbenjsc band->chan[i],n)); 3304173362Sbenjsc continue; 3305173362Sbenjsc } 3306173362Sbenjsc 3307173362Sbenjsc passive = 0; 3308173362Sbenjsc chan = band->chan[i]; 3309179957Sthompsa c = &ic->ic_channels[ic->ic_nchans++]; 3310173362Sbenjsc 3311173362Sbenjsc /* is active scan allowed on this channel? */ 3312173362Sbenjsc if (!(channels[i].flags & WPI_EEPROM_CHAN_ACTIVE)) { 3313173362Sbenjsc passive = IEEE80211_CHAN_PASSIVE; 3314173362Sbenjsc } 3315173362Sbenjsc 3316173362Sbenjsc if (n == 0) { /* 2GHz band */ 3317179957Sthompsa c->ic_ieee = chan; 3318179957Sthompsa c->ic_freq = ieee80211_ieee2mhz(chan, 3319179957Sthompsa IEEE80211_CHAN_2GHZ); 3320179957Sthompsa c->ic_flags = IEEE80211_CHAN_B | passive; 3321173362Sbenjsc 3322179957Sthompsa c = &ic->ic_channels[ic->ic_nchans++]; 3323179957Sthompsa c->ic_ieee = chan; 3324179957Sthompsa c->ic_freq = ieee80211_ieee2mhz(chan, 3325179957Sthompsa IEEE80211_CHAN_2GHZ); 3326179957Sthompsa c->ic_flags = IEEE80211_CHAN_G | passive; 3327179957Sthompsa 3328173362Sbenjsc } else { /* 5GHz band */ 3329173362Sbenjsc /* 3330173362Sbenjsc * Some 3945ABG adapters support channels 7, 8, 11 3331173362Sbenjsc * and 12 in the 2GHz *and* 5GHz bands. 3332173362Sbenjsc * Because of limitations in our net80211(9) stack, 3333173362Sbenjsc * we can't support these channels in 5GHz band. 3334173362Sbenjsc * XXX not true; just need to map to proper frequency 3335173362Sbenjsc */ 3336173362Sbenjsc if (chan <= 14) 3337173362Sbenjsc continue; 3338173362Sbenjsc 3339179957Sthompsa c->ic_ieee = chan; 3340179957Sthompsa c->ic_freq = ieee80211_ieee2mhz(chan, 3341179957Sthompsa IEEE80211_CHAN_5GHZ); 3342179957Sthompsa c->ic_flags = IEEE80211_CHAN_A | passive; 3343173362Sbenjsc } 3344173362Sbenjsc 3345173362Sbenjsc /* save maximum allowed power for this channel */ 3346173362Sbenjsc sc->maxpwr[chan] = channels[i].maxpwr; 3347173362Sbenjsc 3348173362Sbenjsc#if 0 3349173362Sbenjsc // XXX We can probably use this an get rid of maxpwr - ben 20070617 3350173362Sbenjsc ic->ic_channels[chan].ic_maxpower = channels[i].maxpwr; 3351173362Sbenjsc //ic->ic_channels[chan].ic_minpower... 3352173362Sbenjsc //ic->ic_channels[chan].ic_maxregtxpower... 3353173362Sbenjsc#endif 3354173362Sbenjsc 3355179957Sthompsa DPRINTF(("adding chan %d (%dMHz) flags=0x%x maxpwr=%d" 3356179957Sthompsa " passive=%d, offset %d\n", chan, c->ic_freq, 3357179957Sthompsa channels[i].flags, sc->maxpwr[chan], 3358179957Sthompsa (c->ic_flags & IEEE80211_CHAN_PASSIVE) != 0, 3359179957Sthompsa ic->ic_nchans)); 3360173362Sbenjsc } 3361173362Sbenjsc} 3362173362Sbenjsc 3363173362Sbenjscstatic void 3364173362Sbenjscwpi_read_eeprom_group(struct wpi_softc *sc, int n) 3365173362Sbenjsc{ 3366173362Sbenjsc struct wpi_power_group *group = &sc->groups[n]; 3367173362Sbenjsc struct wpi_eeprom_group rgroup; 3368173362Sbenjsc int i; 3369173362Sbenjsc 3370173362Sbenjsc wpi_read_prom_data(sc, WPI_EEPROM_POWER_GRP + n * 32, &rgroup, 3371173362Sbenjsc sizeof rgroup); 3372173362Sbenjsc 3373173362Sbenjsc /* save power group information */ 3374173362Sbenjsc group->chan = rgroup.chan; 3375173362Sbenjsc group->maxpwr = rgroup.maxpwr; 3376173362Sbenjsc /* temperature at which the samples were taken */ 3377173362Sbenjsc group->temp = (int16_t)le16toh(rgroup.temp); 3378173362Sbenjsc 3379173362Sbenjsc DPRINTF(("power group %d: chan=%d maxpwr=%d temp=%d\n", n, 3380173362Sbenjsc group->chan, group->maxpwr, group->temp)); 3381173362Sbenjsc 3382173362Sbenjsc for (i = 0; i < WPI_SAMPLES_COUNT; i++) { 3383173362Sbenjsc group->samples[i].index = rgroup.samples[i].index; 3384173362Sbenjsc group->samples[i].power = rgroup.samples[i].power; 3385173362Sbenjsc 3386173362Sbenjsc DPRINTF(("\tsample %d: index=%d power=%d\n", i, 3387173362Sbenjsc group->samples[i].index, group->samples[i].power)); 3388173362Sbenjsc } 3389173362Sbenjsc} 3390173362Sbenjsc 3391173362Sbenjsc/* 3392173362Sbenjsc * Update Tx power to match what is defined for channel `c'. 3393173362Sbenjsc */ 3394173362Sbenjscstatic int 3395173362Sbenjscwpi_set_txpower(struct wpi_softc *sc, struct ieee80211_channel *c, int async) 3396173362Sbenjsc{ 3397178354Ssam struct ifnet *ifp = sc->sc_ifp; 3398178354Ssam struct ieee80211com *ic = ifp->if_l2com; 3399173362Sbenjsc struct wpi_power_group *group; 3400173362Sbenjsc struct wpi_cmd_txpower txpower; 3401173362Sbenjsc u_int chan; 3402173362Sbenjsc int i; 3403173362Sbenjsc 3404173362Sbenjsc /* get channel number */ 3405173362Sbenjsc chan = ieee80211_chan2ieee(ic, c); 3406173362Sbenjsc 3407173362Sbenjsc /* find the power group to which this channel belongs */ 3408173362Sbenjsc if (IEEE80211_IS_CHAN_5GHZ(c)) { 3409173362Sbenjsc for (group = &sc->groups[1]; group < &sc->groups[4]; group++) 3410173362Sbenjsc if (chan <= group->chan) 3411173362Sbenjsc break; 3412173362Sbenjsc } else 3413173362Sbenjsc group = &sc->groups[0]; 3414173362Sbenjsc 3415173362Sbenjsc memset(&txpower, 0, sizeof txpower); 3416173362Sbenjsc txpower.band = IEEE80211_IS_CHAN_5GHZ(c) ? 0 : 1; 3417173362Sbenjsc txpower.channel = htole16(chan); 3418173362Sbenjsc 3419173362Sbenjsc /* set Tx power for all OFDM and CCK rates */ 3420173362Sbenjsc for (i = 0; i <= 11 ; i++) { 3421173362Sbenjsc /* retrieve Tx power for this channel/rate combination */ 3422173362Sbenjsc int idx = wpi_get_power_index(sc, group, c, 3423173362Sbenjsc wpi_ridx_to_rate[i]); 3424173362Sbenjsc 3425173362Sbenjsc txpower.rates[i].rate = wpi_ridx_to_plcp[i]; 3426173362Sbenjsc 3427173362Sbenjsc if (IEEE80211_IS_CHAN_5GHZ(c)) { 3428173362Sbenjsc txpower.rates[i].gain_radio = wpi_rf_gain_5ghz[idx]; 3429173362Sbenjsc txpower.rates[i].gain_dsp = wpi_dsp_gain_5ghz[idx]; 3430173362Sbenjsc } else { 3431173362Sbenjsc txpower.rates[i].gain_radio = wpi_rf_gain_2ghz[idx]; 3432173362Sbenjsc txpower.rates[i].gain_dsp = wpi_dsp_gain_2ghz[idx]; 3433173362Sbenjsc } 3434173362Sbenjsc DPRINTFN(WPI_DEBUG_TEMP,("chan %d/rate %d: power index %d\n", 3435173362Sbenjsc chan, wpi_ridx_to_rate[i], idx)); 3436173362Sbenjsc } 3437173362Sbenjsc 3438173362Sbenjsc return wpi_cmd(sc, WPI_CMD_TXPOWER, &txpower, sizeof txpower, async); 3439173362Sbenjsc} 3440173362Sbenjsc 3441173362Sbenjsc/* 3442173362Sbenjsc * Determine Tx power index for a given channel/rate combination. 3443173362Sbenjsc * This takes into account the regulatory information from EEPROM and the 3444173362Sbenjsc * current temperature. 3445173362Sbenjsc */ 3446173362Sbenjscstatic int 3447173362Sbenjscwpi_get_power_index(struct wpi_softc *sc, struct wpi_power_group *group, 3448173362Sbenjsc struct ieee80211_channel *c, int rate) 3449173362Sbenjsc{ 3450173362Sbenjsc/* fixed-point arithmetic division using a n-bit fractional part */ 3451173362Sbenjsc#define fdivround(a, b, n) \ 3452173362Sbenjsc ((((1 << n) * (a)) / (b) + (1 << n) / 2) / (1 << n)) 3453173362Sbenjsc 3454173362Sbenjsc/* linear interpolation */ 3455173362Sbenjsc#define interpolate(x, x1, y1, x2, y2, n) \ 3456173362Sbenjsc ((y1) + fdivround(((x) - (x1)) * ((y2) - (y1)), (x2) - (x1), n)) 3457173362Sbenjsc 3458178354Ssam struct ifnet *ifp = sc->sc_ifp; 3459178354Ssam struct ieee80211com *ic = ifp->if_l2com; 3460173362Sbenjsc struct wpi_power_sample *sample; 3461173362Sbenjsc int pwr, idx; 3462173362Sbenjsc u_int chan; 3463173362Sbenjsc 3464173362Sbenjsc /* get channel number */ 3465173362Sbenjsc chan = ieee80211_chan2ieee(ic, c); 3466173362Sbenjsc 3467173362Sbenjsc /* default power is group's maximum power - 3dB */ 3468173362Sbenjsc pwr = group->maxpwr / 2; 3469173362Sbenjsc 3470173362Sbenjsc /* decrease power for highest OFDM rates to reduce distortion */ 3471173362Sbenjsc switch (rate) { 3472173362Sbenjsc case 72: /* 36Mb/s */ 3473173362Sbenjsc pwr -= IEEE80211_IS_CHAN_2GHZ(c) ? 0 : 5; 3474173362Sbenjsc break; 3475173362Sbenjsc case 96: /* 48Mb/s */ 3476173362Sbenjsc pwr -= IEEE80211_IS_CHAN_2GHZ(c) ? 7 : 10; 3477173362Sbenjsc break; 3478173362Sbenjsc case 108: /* 54Mb/s */ 3479173362Sbenjsc pwr -= IEEE80211_IS_CHAN_2GHZ(c) ? 9 : 12; 3480173362Sbenjsc break; 3481173362Sbenjsc } 3482173362Sbenjsc 3483173362Sbenjsc /* never exceed channel's maximum allowed Tx power */ 3484173362Sbenjsc pwr = min(pwr, sc->maxpwr[chan]); 3485173362Sbenjsc 3486173362Sbenjsc /* retrieve power index into gain tables from samples */ 3487173362Sbenjsc for (sample = group->samples; sample < &group->samples[3]; sample++) 3488173362Sbenjsc if (pwr > sample[1].power) 3489173362Sbenjsc break; 3490173362Sbenjsc /* fixed-point linear interpolation using a 19-bit fractional part */ 3491173362Sbenjsc idx = interpolate(pwr, sample[0].power, sample[0].index, 3492173362Sbenjsc sample[1].power, sample[1].index, 19); 3493173362Sbenjsc 3494173362Sbenjsc /* 3495173362Sbenjsc * Adjust power index based on current temperature 3496173362Sbenjsc * - if colder than factory-calibrated: decreate output power 3497173362Sbenjsc * - if warmer than factory-calibrated: increase output power 3498173362Sbenjsc */ 3499173362Sbenjsc idx -= (sc->temp - group->temp) * 11 / 100; 3500173362Sbenjsc 3501173362Sbenjsc /* decrease power for CCK rates (-5dB) */ 3502173362Sbenjsc if (!WPI_RATE_IS_OFDM(rate)) 3503173362Sbenjsc idx += 10; 3504173362Sbenjsc 3505173362Sbenjsc /* keep power index in a valid range */ 3506173362Sbenjsc if (idx < 0) 3507173362Sbenjsc return 0; 3508173362Sbenjsc if (idx > WPI_MAX_PWR_INDEX) 3509173362Sbenjsc return WPI_MAX_PWR_INDEX; 3510173362Sbenjsc return idx; 3511173362Sbenjsc 3512173362Sbenjsc#undef interpolate 3513173362Sbenjsc#undef fdivround 3514173362Sbenjsc} 3515173362Sbenjsc 3516173362Sbenjsc/** 3517173362Sbenjsc * Called by net80211 framework to indicate that a scan 3518173362Sbenjsc * is starting. This function doesn't actually do the scan, 3519173362Sbenjsc * wpi_scan_curchan starts things off. This function is more 3520173362Sbenjsc * of an early warning from the framework we should get ready 3521173362Sbenjsc * for the scan. 3522173362Sbenjsc */ 3523173362Sbenjscstatic void 3524173362Sbenjscwpi_scan_start(struct ieee80211com *ic) 3525173362Sbenjsc{ 3526173362Sbenjsc struct ifnet *ifp = ic->ic_ifp; 3527173362Sbenjsc struct wpi_softc *sc = ifp->if_softc; 3528173362Sbenjsc 3529191746Sthompsa WPI_LOCK(sc); 3530191746Sthompsa wpi_set_led(sc, WPI_LED_LINK, 20, 2); 3531191746Sthompsa WPI_UNLOCK(sc); 3532173362Sbenjsc} 3533173362Sbenjsc 3534173362Sbenjsc/** 3535173362Sbenjsc * Called by the net80211 framework, indicates that the 3536173362Sbenjsc * scan has ended. If there is a scan in progress on the card 3537173362Sbenjsc * then it should be aborted. 3538173362Sbenjsc */ 3539173362Sbenjscstatic void 3540173362Sbenjscwpi_scan_end(struct ieee80211com *ic) 3541173362Sbenjsc{ 3542191746Sthompsa /* XXX ignore */ 3543173362Sbenjsc} 3544173362Sbenjsc 3545173362Sbenjsc/** 3546173362Sbenjsc * Called by the net80211 framework to indicate to the driver 3547173362Sbenjsc * that the channel should be changed 3548173362Sbenjsc */ 3549173362Sbenjscstatic void 3550173362Sbenjscwpi_set_channel(struct ieee80211com *ic) 3551173362Sbenjsc{ 3552173362Sbenjsc struct ifnet *ifp = ic->ic_ifp; 3553173362Sbenjsc struct wpi_softc *sc = ifp->if_softc; 3554191746Sthompsa int error; 3555173362Sbenjsc 3556177043Sthompsa /* 3557177043Sthompsa * Only need to set the channel in Monitor mode. AP scanning and auth 3558177043Sthompsa * are already taken care of by their respective firmware commands. 3559177043Sthompsa */ 3560191746Sthompsa if (ic->ic_opmode == IEEE80211_M_MONITOR) { 3561216557Sbschmidt WPI_LOCK(sc); 3562191746Sthompsa error = wpi_config(sc); 3563216557Sbschmidt WPI_UNLOCK(sc); 3564191746Sthompsa if (error != 0) 3565191746Sthompsa device_printf(sc->sc_dev, 3566191746Sthompsa "error %d settting channel\n", error); 3567191746Sthompsa } 3568173362Sbenjsc} 3569173362Sbenjsc 3570173362Sbenjsc/** 3571173362Sbenjsc * Called by net80211 to indicate that we need to scan the current 3572173362Sbenjsc * channel. The channel is previously be set via the wpi_set_channel 3573173362Sbenjsc * callback. 3574173362Sbenjsc */ 3575173362Sbenjscstatic void 3576178354Ssamwpi_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell) 3577173362Sbenjsc{ 3578178354Ssam struct ieee80211vap *vap = ss->ss_vap; 3579178354Ssam struct ifnet *ifp = vap->iv_ic->ic_ifp; 3580173362Sbenjsc struct wpi_softc *sc = ifp->if_softc; 3581173362Sbenjsc 3582191746Sthompsa WPI_LOCK(sc); 3583191746Sthompsa if (wpi_scan(sc)) 3584191746Sthompsa ieee80211_cancel_scan(vap); 3585191746Sthompsa WPI_UNLOCK(sc); 3586173362Sbenjsc} 3587173362Sbenjsc 3588173362Sbenjsc/** 3589173362Sbenjsc * Called by the net80211 framework to indicate 3590173362Sbenjsc * the minimum dwell time has been met, terminate the scan. 3591173362Sbenjsc * We don't actually terminate the scan as the firmware will notify 3592173362Sbenjsc * us when it's finished and we have no way to interrupt it. 3593173362Sbenjsc */ 3594173362Sbenjscstatic void 3595178354Ssamwpi_scan_mindwell(struct ieee80211_scan_state *ss) 3596173362Sbenjsc{ 3597173362Sbenjsc /* NB: don't try to abort scan; wait for firmware to finish */ 3598173362Sbenjsc} 3599173362Sbenjsc 3600173362Sbenjscstatic void 3601191746Sthompsawpi_hwreset(void *arg, int pending) 3602173362Sbenjsc{ 3603191746Sthompsa struct wpi_softc *sc = arg; 3604173362Sbenjsc 3605173362Sbenjsc WPI_LOCK(sc); 3606191746Sthompsa wpi_init_locked(sc, 0); 3607173362Sbenjsc WPI_UNLOCK(sc); 3608173362Sbenjsc} 3609173362Sbenjsc 3610191746Sthompsastatic void 3611191746Sthompsawpi_rfreset(void *arg, int pending) 3612173362Sbenjsc{ 3613191746Sthompsa struct wpi_softc *sc = arg; 3614173362Sbenjsc 3615191746Sthompsa WPI_LOCK(sc); 3616191746Sthompsa wpi_rfkill_resume(sc); 3617191746Sthompsa WPI_UNLOCK(sc); 3618173362Sbenjsc} 3619173362Sbenjsc 3620173362Sbenjsc/* 3621173362Sbenjsc * Allocate DMA-safe memory for firmware transfer. 3622173362Sbenjsc */ 3623173362Sbenjscstatic int 3624173362Sbenjscwpi_alloc_fwmem(struct wpi_softc *sc) 3625173362Sbenjsc{ 3626173362Sbenjsc /* allocate enough contiguous space to store text and data */ 3627173362Sbenjsc return wpi_dma_contig_alloc(sc, &sc->fw_dma, NULL, 3628173362Sbenjsc WPI_FW_MAIN_TEXT_MAXSZ + WPI_FW_MAIN_DATA_MAXSZ, 1, 3629173362Sbenjsc BUS_DMA_NOWAIT); 3630173362Sbenjsc} 3631173362Sbenjsc 3632173362Sbenjscstatic void 3633173362Sbenjscwpi_free_fwmem(struct wpi_softc *sc) 3634173362Sbenjsc{ 3635173362Sbenjsc wpi_dma_contig_free(&sc->fw_dma); 3636173362Sbenjsc} 3637173362Sbenjsc 3638173362Sbenjsc/** 3639177043Sthompsa * Called every second, wpi_watchdog used by the watch dog timer 3640173362Sbenjsc * to check that the card is still alive 3641173362Sbenjsc */ 3642173362Sbenjscstatic void 3643177043Sthompsawpi_watchdog(void *arg) 3644173362Sbenjsc{ 3645173362Sbenjsc struct wpi_softc *sc = arg; 3646177043Sthompsa struct ifnet *ifp = sc->sc_ifp; 3647191746Sthompsa struct ieee80211com *ic = ifp->if_l2com; 3648177043Sthompsa uint32_t tmp; 3649173362Sbenjsc 3650173362Sbenjsc DPRINTFN(WPI_DEBUG_WATCHDOG,("Watchdog: tick\n")); 3651173362Sbenjsc 3652177043Sthompsa if (sc->flags & WPI_FLAG_HW_RADIO_OFF) { 3653177043Sthompsa /* No need to lock firmware memory */ 3654177043Sthompsa tmp = wpi_mem_read(sc, WPI_MEM_HW_RADIO_OFF); 3655177043Sthompsa 3656177043Sthompsa if ((tmp & 0x1) == 0) { 3657177043Sthompsa /* Radio kill switch is still off */ 3658177043Sthompsa callout_reset(&sc->watchdog_to, hz, wpi_watchdog, sc); 3659177043Sthompsa return; 3660177043Sthompsa } 3661177043Sthompsa 3662177043Sthompsa device_printf(sc->sc_dev, "Hardware Switch Enabled\n"); 3663191746Sthompsa ieee80211_runtask(ic, &sc->sc_radiotask); 3664177043Sthompsa return; 3665177043Sthompsa } 3666177043Sthompsa 3667177043Sthompsa if (sc->sc_tx_timer > 0) { 3668177043Sthompsa if (--sc->sc_tx_timer == 0) { 3669177043Sthompsa device_printf(sc->sc_dev,"device timeout\n"); 3670271849Sglebius if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 3671191746Sthompsa ieee80211_runtask(ic, &sc->sc_restarttask); 3672177043Sthompsa } 3673177043Sthompsa } 3674177043Sthompsa if (sc->sc_scan_timer > 0) { 3675178354Ssam struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 3676178354Ssam if (--sc->sc_scan_timer == 0 && vap != NULL) { 3677177043Sthompsa device_printf(sc->sc_dev,"scan timeout\n"); 3678178354Ssam ieee80211_cancel_scan(vap); 3679191746Sthompsa ieee80211_runtask(ic, &sc->sc_restarttask); 3680177043Sthompsa } 3681177043Sthompsa } 3682177043Sthompsa 3683177043Sthompsa if (ifp->if_drv_flags & IFF_DRV_RUNNING) 3684177043Sthompsa callout_reset(&sc->watchdog_to, hz, wpi_watchdog, sc); 3685173362Sbenjsc} 3686173362Sbenjsc 3687173362Sbenjsc#ifdef WPI_DEBUG 3688173362Sbenjscstatic const char *wpi_cmd_str(int cmd) 3689173362Sbenjsc{ 3690178354Ssam switch (cmd) { 3691178354Ssam case WPI_DISABLE_CMD: return "WPI_DISABLE_CMD"; 3692178354Ssam case WPI_CMD_CONFIGURE: return "WPI_CMD_CONFIGURE"; 3693178354Ssam case WPI_CMD_ASSOCIATE: return "WPI_CMD_ASSOCIATE"; 3694178354Ssam case WPI_CMD_SET_WME: return "WPI_CMD_SET_WME"; 3695178354Ssam case WPI_CMD_TSF: return "WPI_CMD_TSF"; 3696178354Ssam case WPI_CMD_ADD_NODE: return "WPI_CMD_ADD_NODE"; 3697178354Ssam case WPI_CMD_TX_DATA: return "WPI_CMD_TX_DATA"; 3698178354Ssam case WPI_CMD_MRR_SETUP: return "WPI_CMD_MRR_SETUP"; 3699178354Ssam case WPI_CMD_SET_LED: return "WPI_CMD_SET_LED"; 3700178354Ssam case WPI_CMD_SET_POWER_MODE: return "WPI_CMD_SET_POWER_MODE"; 3701178354Ssam case WPI_CMD_SCAN: return "WPI_CMD_SCAN"; 3702178354Ssam case WPI_CMD_SET_BEACON:return "WPI_CMD_SET_BEACON"; 3703178354Ssam case WPI_CMD_TXPOWER: return "WPI_CMD_TXPOWER"; 3704178354Ssam case WPI_CMD_BLUETOOTH: return "WPI_CMD_BLUETOOTH"; 3705173362Sbenjsc 3706178354Ssam default: 3707173362Sbenjsc KASSERT(1, ("Unknown Command: %d\n", cmd)); 3708178354Ssam return "UNKNOWN CMD"; /* Make the compiler happy */ 3709173362Sbenjsc } 3710173362Sbenjsc} 3711173362Sbenjsc#endif 3712173362Sbenjsc 3713173362SbenjscMODULE_DEPEND(wpi, pci, 1, 1, 1); 3714173362SbenjscMODULE_DEPEND(wpi, wlan, 1, 1, 1); 3715173362SbenjscMODULE_DEPEND(wpi, firmware, 1, 1, 1); 3716