From 7370bffcb64eb03375b862bc589756a04c51b43e Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Mon, 23 May 2016 13:57:50 -0700 Subject: [PATCH] wlan - Sync dev/netif/wpi from FreeBSD * Sync dev/netif/wpi from FreeBSD, fbsd git dd885b9a0a0e, May 11 2016. * Straight re-port from FreeBSD using new infrastructure, with DragonFly mods. --- sys/contrib/dev/wpi/LICENSE | 78 +- sys/dev/netif/wpi/Makefile | 2 +- sys/dev/netif/wpi/if_wpi.c | 9610 ++++++++++++++++++++++---------------- sys/dev/netif/wpi/if_wpi_debug.h | 144 + sys/dev/netif/wpi/if_wpireg.h | 1023 ++-- sys/dev/netif/wpi/if_wpivar.h | 289 +- 6 files changed, 6778 insertions(+), 4368 deletions(-) rewrite sys/dev/netif/wpi/if_wpi.c (65%) create mode 100644 sys/dev/netif/wpi/if_wpi_debug.h diff --git a/sys/contrib/dev/wpi/LICENSE b/sys/contrib/dev/wpi/LICENSE index e86fd69225..938004c43e 100644 --- a/sys/contrib/dev/wpi/LICENSE +++ b/sys/contrib/dev/wpi/LICENSE @@ -1,39 +1,39 @@ -Copyright (c) 2006, Intel Corporation. -All rights reserved. - -Redistribution. Redistribution and use in binary form, without -modification, are permitted provided that the following conditions are -met: - -* Redistributions must reproduce the above copyright notice and the - following disclaimer in the documentation and/or other materials - provided with the distribution. -* Neither the name of Intel Corporation nor the names of its suppliers - may be used to endorse or promote products derived from this software - without specific prior written permission. -* No reverse engineering, decompilation, or disassembly of this software - is permitted. - -Limited patent license. Intel Corporation grants a world-wide, -royalty-free, non-exclusive license under patents it now or hereafter -owns or controls to make, have made, use, import, offer to sell and -sell ("Utilize") this software, but solely to the extent that any -such patent is necessary to Utilize the software alone, or in -combination with an operating system licensed under an approved Open -Source license as listed by the Open Source Initiative at -http://opensource.org/licenses. The patent license shall not apply to -any other combinations which include this software. No hardware per -se is licensed hereunder. - -DISCLAIMER. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND -CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS -OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR -TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. +Copyright (c) 2006-2009, Intel Corporation. +All rights reserved. + +Redistribution. Redistribution and use in binary form, without +modification, are permitted provided that the following conditions are +met: + +* Redistributions must reproduce the above copyright notice and the + following disclaimer in the documentation and/or other materials + provided with the distribution. +* Neither the name of Intel Corporation nor the names of its suppliers + may be used to endorse or promote products derived from this software + without specific prior written permission. +* No reverse engineering, decompilation, or disassembly of this software + is permitted. + +Limited patent license. Intel Corporation grants a world-wide, +royalty-free, non-exclusive license under patents it now or hereafter +owns or controls to make, have made, use, import, offer to sell and +sell ("Utilize") this software, but solely to the extent that any +such patent is necessary to Utilize the software alone, or in +combination with an operating system licensed under an approved Open +Source license as listed by the Open Source Initiative at +http://opensource.org/licenses. The patent license shall not apply to +any other combinations which include this software. No hardware per +se is licensed hereunder. + +DISCLAIMER. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. diff --git a/sys/dev/netif/wpi/Makefile b/sys/dev/netif/wpi/Makefile index f2d5319da7..6dec9cf22c 100644 --- a/sys/dev/netif/wpi/Makefile +++ b/sys/dev/netif/wpi/Makefile @@ -1,5 +1,5 @@ KMOD = if_wpi -SRCS = if_wpi.c device_if.h bus_if.h pci_if.h opt_wlan.h +SRCS = if_wpi.c device_if.h bus_if.h pci_if.h opt_wlan.h opt_wpi.h SRCS += miibus_if.h SUBDIR = fw diff --git a/sys/dev/netif/wpi/if_wpi.c b/sys/dev/netif/wpi/if_wpi.c dissimilarity index 65% index 90834579ed..36079aa735 100644 --- a/sys/dev/netif/wpi/if_wpi.c +++ b/sys/dev/netif/wpi/if_wpi.c @@ -1,3901 +1,5709 @@ -/*- - * Copyright (c) 2006,2007 - * Damien Bergamini - * Benjamin Close - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#define VERSION "20071127" - -#include -__FBSDID("$FreeBSD$"); - -/* - * Driver for Intel PRO/Wireless 3945ABG 802.11 network adapters. - * - * The 3945ABG network adapter doesn't use traditional hardware as - * many other adaptors do. Instead at run time the eeprom is set into a known - * state and told to load boot firmware. The boot firmware loads an init and a - * main binary firmware image into SRAM on the card via DMA. - * Once the firmware is loaded, the driver/hw then - * communicate by way of circular dma rings via the SRAM to the firmware. - * - * There is 6 memory rings. 1 command ring, 1 rx data ring & 4 tx data rings. - * The 4 tx data rings allow for prioritization QoS. - * - * The rx data ring consists of 32 dma buffers. Two registers are used to - * indicate where in the ring the driver and the firmware are up to. The - * driver sets the initial read index (reg1) and the initial write index (reg2), - * the firmware updates the read index (reg1) on rx of a packet and fires an - * interrupt. The driver then processes the buffers starting at reg1 indicating - * to the firmware which buffers have been accessed by updating reg2. At the - * same time allocating new memory for the processed buffer. - * - * A similar thing happens with the tx rings. The difference is the firmware - * stop processing buffers once the queue is full and until confirmation - * of a successful transmition (tx_intr) has occurred. - * - * The command ring operates in the same manner as the tx queues. - * - * All communication direct to the card (ie eeprom) is classed as Stage1 - * communication - * - * All communication via the firmware to the card is classed as State2. - * The firmware consists of 2 parts. A bootstrap firmware and a runtime - * firmware. The bootstrap firmware and runtime firmware are loaded - * from host memory via dma to the card then told to execute. From this point - * on the majority of communications between the driver and the card goes - * via the firmware. - */ - -#include "opt_wlan.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include - -#define WPI_DEBUG - -#ifdef WPI_DEBUG -#define DPRINTF(x) do { if (wpi_debug != 0) kprintf x; } while (0) -#define DPRINTFN(n, x) do { if (wpi_debug & n) kprintf x; } while (0) -#define WPI_DEBUG_SET (wpi_debug != 0) - -enum { - WPI_DEBUG_UNUSED = 0x00000001, /* Unused */ - WPI_DEBUG_HW = 0x00000002, /* Stage 1 (eeprom) debugging */ - WPI_DEBUG_TX = 0x00000004, /* Stage 2 TX intrp debugging*/ - WPI_DEBUG_RX = 0x00000008, /* Stage 2 RX intrp debugging */ - WPI_DEBUG_CMD = 0x00000010, /* Stage 2 CMD intrp debugging*/ - WPI_DEBUG_FIRMWARE = 0x00000020, /* firmware(9) loading debug */ - WPI_DEBUG_DMA = 0x00000040, /* DMA (de)allocations/syncs */ - WPI_DEBUG_SCANNING = 0x00000080, /* Stage 2 Scanning debugging */ - WPI_DEBUG_NOTIFY = 0x00000100, /* State 2 Noftif intr debug */ - WPI_DEBUG_TEMP = 0x00000200, /* TXPower/Temp Calibration */ - WPI_DEBUG_OPS = 0x00000400, /* wpi_ops taskq debug */ - WPI_DEBUG_WATCHDOG = 0x00000800, /* Watch dog debug */ - WPI_DEBUG_ANY = 0xffffffff -}; - -static int wpi_debug; -SYSCTL_INT(_debug, OID_AUTO, wpi, CTLFLAG_RW, &wpi_debug, 0, "wpi debug level"); -TUNABLE_INT("debug.wpi", &wpi_debug); - -#else -#define DPRINTF(x) -#define DPRINTFN(n, x) -#define WPI_DEBUG_SET 0 -#endif - -struct wpi_ident { - uint16_t vendor; - uint16_t device; - uint16_t subdevice; - const char *name; -}; - -static const struct wpi_ident wpi_ident_table[] = { - /* The below entries support ABG regardless of the subid */ - { 0x8086, 0x4222, 0x0, "Intel(R) PRO/Wireless 3945ABG" }, - { 0x8086, 0x4227, 0x0, "Intel(R) PRO/Wireless 3945ABG" }, - /* The below entries only support BG */ - { 0x8086, 0x4222, 0x1005, "Intel(R) PRO/Wireless 3945BG" }, - { 0x8086, 0x4222, 0x1034, "Intel(R) PRO/Wireless 3945BG" }, - { 0x8086, 0x4227, 0x1014, "Intel(R) PRO/Wireless 3945BG" }, - { 0x8086, 0x4222, 0x1044, "Intel(R) PRO/Wireless 3945BG" }, - { 0, 0, 0, NULL } -}; - -static struct ieee80211vap *wpi_vap_create(struct ieee80211com *, - const char [IFNAMSIZ], int, enum ieee80211_opmode, int, - const uint8_t [IEEE80211_ADDR_LEN], - const uint8_t [IEEE80211_ADDR_LEN]); -static void wpi_vap_delete(struct ieee80211vap *); -static int wpi_dma_contig_alloc(struct wpi_softc *, struct wpi_dma_info *, - void **, bus_size_t, bus_size_t, int); -static void wpi_dma_contig_free(struct wpi_dma_info *); -static void wpi_dma_map_addr(void *, bus_dma_segment_t *, int, int); -static int wpi_alloc_shared(struct wpi_softc *); -static void wpi_free_shared(struct wpi_softc *); -static int wpi_alloc_rx_ring(struct wpi_softc *, struct wpi_rx_ring *); -static void wpi_reset_rx_ring(struct wpi_softc *, struct wpi_rx_ring *); -static void wpi_free_rx_ring(struct wpi_softc *, struct wpi_rx_ring *); -static int wpi_alloc_tx_ring(struct wpi_softc *, struct wpi_tx_ring *, - int, int); -static void wpi_reset_tx_ring(struct wpi_softc *, struct wpi_tx_ring *); -static void wpi_free_tx_ring(struct wpi_softc *, struct wpi_tx_ring *); -static int wpi_newstate(struct ieee80211vap *, enum ieee80211_state, int); -static void wpi_mem_lock(struct wpi_softc *); -static void wpi_mem_unlock(struct wpi_softc *); -static uint32_t wpi_mem_read(struct wpi_softc *, uint16_t); -static void wpi_mem_write(struct wpi_softc *, uint16_t, uint32_t); -static void wpi_mem_write_region_4(struct wpi_softc *, uint16_t, - const uint32_t *, int); -static uint16_t wpi_read_prom_data(struct wpi_softc *, uint32_t, void *, int); -static int wpi_alloc_fwmem(struct wpi_softc *); -static void wpi_free_fwmem(struct wpi_softc *); -static int wpi_load_firmware(struct wpi_softc *); -static void wpi_unload_firmware(struct wpi_softc *); -static int wpi_load_microcode(struct wpi_softc *, const uint8_t *, int); -static void wpi_rx_intr(struct wpi_softc *, struct wpi_rx_desc *, - struct wpi_rx_data *); -static void wpi_tx_intr(struct wpi_softc *, struct wpi_rx_desc *); -static void wpi_cmd_intr(struct wpi_softc *, struct wpi_rx_desc *); -static void wpi_notif_intr(struct wpi_softc *); -static void wpi_intr(void *); -static uint8_t wpi_plcp_signal(int); -static void wpi_watchdog(void *); -static int wpi_tx_data(struct wpi_softc *, struct mbuf *, - struct ieee80211_node *, int); -static void wpi_start(struct ifnet *, struct ifaltq_subque *); -static void wpi_start_locked(struct ifnet *); -static int wpi_raw_xmit(struct ieee80211_node *, struct mbuf *, - const struct ieee80211_bpf_params *); -static void wpi_scan_start(struct ieee80211com *); -static void wpi_scan_end(struct ieee80211com *); -static void wpi_set_channel(struct ieee80211com *); -static void wpi_scan_curchan(struct ieee80211_scan_state *, unsigned long); -static void wpi_scan_mindwell(struct ieee80211_scan_state *); -static int wpi_ioctl(struct ifnet *, u_long, caddr_t, struct ucred *); -static void wpi_read_eeprom(struct wpi_softc *, - uint8_t macaddr[IEEE80211_ADDR_LEN]); -static void wpi_read_eeprom_channels(struct wpi_softc *, int); -static void wpi_read_eeprom_group(struct wpi_softc *, int); -static int wpi_cmd(struct wpi_softc *, int, const void *, int, int); -static int wpi_wme_update(struct ieee80211com *); -static int wpi_mrr_setup(struct wpi_softc *); -static void wpi_set_led(struct wpi_softc *, uint8_t, uint8_t, uint8_t); -static void wpi_enable_tsf(struct wpi_softc *, struct ieee80211_node *); -#if 0 -static int wpi_setup_beacon(struct wpi_softc *, struct ieee80211_node *); -#endif -static int wpi_auth(struct wpi_softc *, struct ieee80211vap *); -static int wpi_run(struct wpi_softc *, struct ieee80211vap *); -static int wpi_scan(struct wpi_softc *); -static int wpi_config(struct wpi_softc *); -static void wpi_stop_master(struct wpi_softc *); -static int wpi_power_up(struct wpi_softc *); -static int wpi_reset(struct wpi_softc *); -static void wpi_hwreset(void *, int); -static void wpi_rfreset(void *, int); -static void wpi_hw_config(struct wpi_softc *); -static void wpi_init(void *); -static void wpi_init_locked(struct wpi_softc *, int); -static void wpi_stop(struct wpi_softc *); -static void wpi_stop_locked(struct wpi_softc *); - -static int wpi_set_txpower(struct wpi_softc *, struct ieee80211_channel *, - int); -static void wpi_calib_timeout(void *); -static void wpi_power_calibration(struct wpi_softc *, int); -static int wpi_get_power_index(struct wpi_softc *, - struct wpi_power_group *, struct ieee80211_channel *, int); -#ifdef WPI_DEBUG -static const char *wpi_cmd_str(int); -#endif -static int wpi_probe(device_t); -static int wpi_attach(device_t); -static int wpi_detach(device_t); -static int wpi_shutdown(device_t); -static int wpi_suspend(device_t); -static int wpi_resume(device_t); - -#if defined(__DragonFly__) -static int wpi_sleep(struct wpi_softc *sc, void *wchan, - int flags, const char *wmsg, int timo); -#endif - -static device_method_t wpi_methods[] = { - /* Device interface */ - DEVMETHOD(device_probe, wpi_probe), - DEVMETHOD(device_attach, wpi_attach), - DEVMETHOD(device_detach, wpi_detach), - DEVMETHOD(device_shutdown, wpi_shutdown), - DEVMETHOD(device_suspend, wpi_suspend), - DEVMETHOD(device_resume, wpi_resume), - - DEVMETHOD_END -}; - -static driver_t wpi_driver = { - "wpi", - wpi_methods, - sizeof (struct wpi_softc) -}; - -static devclass_t wpi_devclass; - -DRIVER_MODULE(wpi, pci, wpi_driver, wpi_devclass, NULL, NULL); - -MODULE_VERSION(wpi, 1); - -static const uint8_t wpi_ridx_to_plcp[] = { - /* OFDM: IEEE Std 802.11a-1999, pp. 14 Table 80 */ - /* R1-R4 (ral/ural is R4-R1) */ - 0xd, 0xf, 0x5, 0x7, 0x9, 0xb, 0x1, 0x3, - /* CCK: device-dependent */ - 10, 20, 55, 110 -}; - -static const uint8_t wpi_ridx_to_rate[] = { - 12, 18, 24, 36, 48, 72, 96, 108, /* OFDM */ - 2, 4, 11, 22 /*CCK */ -}; - -static int -wpi_probe(device_t dev) -{ - const struct wpi_ident *ident; - - for (ident = wpi_ident_table; ident->name != NULL; ident++) { - if (pci_get_vendor(dev) == ident->vendor && - pci_get_device(dev) == ident->device) { - device_set_desc(dev, ident->name); - return (BUS_PROBE_DEFAULT); - } - } - return ENXIO; -} - -/** - * Load the firmare image from disk to the allocated dma buffer. - * we also maintain the reference to the firmware pointer as there - * is times where we may need to reload the firmware but we are not - * in a context that can access the filesystem (ie taskq cause by restart) - * - * @return 0 on success, an errno on failure - */ -static int -wpi_load_firmware(struct wpi_softc *sc) -{ - const struct firmware *fp; - struct wpi_dma_info *dma = &sc->fw_dma; - const struct wpi_firmware_hdr *hdr; - const uint8_t *itext, *idata, *rtext, *rdata, *btext; - uint32_t itextsz, idatasz, rtextsz, rdatasz, btextsz; - int error; - - DPRINTFN(WPI_DEBUG_FIRMWARE, - ("Attempting Loading Firmware from wpi_fw module\n")); - - WPI_UNLOCK(sc); - - if (sc->fw_fp == NULL && (sc->fw_fp = firmware_get("wpifw")) == NULL) { - device_printf(sc->sc_dev, - "could not load firmware image 'wpifw'\n"); - error = ENOENT; - WPI_LOCK(sc); - goto fail; - } - - fp = sc->fw_fp; - - WPI_LOCK(sc); - - /* Validate the firmware is minimum a particular version */ - if (fp->version < WPI_FW_MINVERSION) { - device_printf(sc->sc_dev, - "firmware version is too old. Need %d, got %d\n", - WPI_FW_MINVERSION, - fp->version); - error = ENXIO; - goto fail; - } - - if (fp->datasize < sizeof (struct wpi_firmware_hdr)) { - device_printf(sc->sc_dev, - "firmware file too short: %zu bytes\n", fp->datasize); - error = ENXIO; - goto fail; - } - - hdr = (const struct wpi_firmware_hdr *)fp->data; - - /* | RUNTIME FIRMWARE | INIT FIRMWARE | BOOT FW | - |HDR|<--TEXT-->|<--DATA-->|<--TEXT-->|<--DATA-->|<--TEXT-->| */ - - rtextsz = le32toh(hdr->rtextsz); - rdatasz = le32toh(hdr->rdatasz); - itextsz = le32toh(hdr->itextsz); - idatasz = le32toh(hdr->idatasz); - btextsz = le32toh(hdr->btextsz); - - /* check that all firmware segments are present */ - if (fp->datasize < sizeof (struct wpi_firmware_hdr) + - rtextsz + rdatasz + itextsz + idatasz + btextsz) { - device_printf(sc->sc_dev, - "firmware file too short: %zu bytes\n", fp->datasize); - error = ENXIO; /* XXX appropriate error code? */ - goto fail; - } - - /* get pointers to firmware segments */ - rtext = (const uint8_t *)(hdr + 1); - rdata = rtext + rtextsz; - itext = rdata + rdatasz; - idata = itext + itextsz; - btext = idata + idatasz; - - DPRINTFN(WPI_DEBUG_FIRMWARE, - ("Firmware Version: Major %d, Minor %d, Driver %d, \n" - "runtime (text: %u, data: %u) init (text: %u, data %u) boot (text %u)\n", - (le32toh(hdr->version) & 0xff000000) >> 24, - (le32toh(hdr->version) & 0x00ff0000) >> 16, - (le32toh(hdr->version) & 0x0000ffff), - rtextsz, rdatasz, - itextsz, idatasz, btextsz)); - - DPRINTFN(WPI_DEBUG_FIRMWARE,("rtext 0x%x\n", *(const uint32_t *)rtext)); - DPRINTFN(WPI_DEBUG_FIRMWARE,("rdata 0x%x\n", *(const uint32_t *)rdata)); - DPRINTFN(WPI_DEBUG_FIRMWARE,("itext 0x%x\n", *(const uint32_t *)itext)); - DPRINTFN(WPI_DEBUG_FIRMWARE,("idata 0x%x\n", *(const uint32_t *)idata)); - DPRINTFN(WPI_DEBUG_FIRMWARE,("btext 0x%x\n", *(const uint32_t *)btext)); - - /* sanity checks */ - if (rtextsz > WPI_FW_MAIN_TEXT_MAXSZ || - rdatasz > WPI_FW_MAIN_DATA_MAXSZ || - itextsz > WPI_FW_INIT_TEXT_MAXSZ || - idatasz > WPI_FW_INIT_DATA_MAXSZ || - btextsz > WPI_FW_BOOT_TEXT_MAXSZ || - (btextsz & 3) != 0) { - device_printf(sc->sc_dev, "firmware invalid\n"); - error = EINVAL; - goto fail; - } - - /* copy initialization images into pre-allocated DMA-safe memory */ - memcpy(dma->vaddr, idata, idatasz); - memcpy(dma->vaddr + WPI_FW_INIT_DATA_MAXSZ, itext, itextsz); - - bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); - - /* tell adapter where to find initialization images */ - wpi_mem_lock(sc); - wpi_mem_write(sc, WPI_MEM_DATA_BASE, dma->paddr); - wpi_mem_write(sc, WPI_MEM_DATA_SIZE, idatasz); - wpi_mem_write(sc, WPI_MEM_TEXT_BASE, - dma->paddr + WPI_FW_INIT_DATA_MAXSZ); - wpi_mem_write(sc, WPI_MEM_TEXT_SIZE, itextsz); - wpi_mem_unlock(sc); - - /* load firmware boot code */ - if ((error = wpi_load_microcode(sc, btext, btextsz)) != 0) { - device_printf(sc->sc_dev, "Failed to load microcode\n"); - goto fail; - } - - /* now press "execute" */ - WPI_WRITE(sc, WPI_RESET, 0); - - /* wait at most one second for the first alive notification */ -#if defined(__DragonFly__) - if ((error = wpi_sleep(sc, sc, PCATCH, "wpiinit", hz)) != 0) { - device_printf(sc->sc_dev, - "timeout waiting for adapter to initialize\n"); - goto fail; - } -#else - if ((error = msleep(sc, &sc->sc_mtx, PCATCH, "wpiinit", hz)) != 0) { - device_printf(sc->sc_dev, - "timeout waiting for adapter to initialize\n"); - goto fail; - } -#endif - - /* copy runtime images into pre-allocated DMA-sage memory */ - memcpy(dma->vaddr, rdata, rdatasz); - memcpy(dma->vaddr + WPI_FW_MAIN_DATA_MAXSZ, rtext, rtextsz); - bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); - - /* tell adapter where to find runtime images */ - wpi_mem_lock(sc); - wpi_mem_write(sc, WPI_MEM_DATA_BASE, dma->paddr); - wpi_mem_write(sc, WPI_MEM_DATA_SIZE, rdatasz); - wpi_mem_write(sc, WPI_MEM_TEXT_BASE, - dma->paddr + WPI_FW_MAIN_DATA_MAXSZ); - wpi_mem_write(sc, WPI_MEM_TEXT_SIZE, WPI_FW_UPDATED | rtextsz); - wpi_mem_unlock(sc); - - /* wait at most one second for the first alive notification */ -#if defined(__DragonFly__) - if ((error = wpi_sleep(sc, sc, PCATCH, "wpiinit", hz)) != 0) { - device_printf(sc->sc_dev, - "timeout waiting for adapter to initialize2\n"); - goto fail; - } -#else - if ((error = msleep(sc, &sc->sc_mtx, PCATCH, "wpiinit", hz)) != 0) { - device_printf(sc->sc_dev, - "timeout waiting for adapter to initialize2\n"); - goto fail; - } -#endif - - DPRINTFN(WPI_DEBUG_FIRMWARE, - ("Firmware loaded to driver successfully\n")); - return error; -fail: - wpi_unload_firmware(sc); - return error; -} - -/** - * Free the referenced firmware image - */ -static void -wpi_unload_firmware(struct wpi_softc *sc) -{ - - if (sc->fw_fp) { - WPI_UNLOCK(sc); - firmware_put(sc->fw_fp, FIRMWARE_UNLOAD); - WPI_LOCK(sc); - sc->fw_fp = NULL; - } -} - -static int -wpi_attach(device_t dev) -{ - struct wpi_softc *sc = device_get_softc(dev); - struct ifnet *ifp; - struct ieee80211com *ic; - int ac, error, rid, supportsa = 1; - uint32_t tmp; - const struct wpi_ident *ident; - uint8_t macaddr[IEEE80211_ADDR_LEN]; - - sc->sc_dev = dev; - - if (bootverbose || WPI_DEBUG_SET) - device_printf(sc->sc_dev,"Driver Revision %s\n", VERSION); - - /* - * Some card's only support 802.11b/g not a, check to see if - * this is one such card. A 0x0 in the subdevice table indicates - * the entire subdevice range is to be ignored. - */ - for (ident = wpi_ident_table; ident->name != NULL; ident++) { - if (ident->subdevice && - pci_get_subdevice(dev) == ident->subdevice) { - supportsa = 0; - break; - } - } - - /* Create the tasks that can be queued */ - TASK_INIT(&sc->sc_restarttask, 0, wpi_hwreset, sc); - TASK_INIT(&sc->sc_radiotask, 0, wpi_rfreset, sc); - - WPI_LOCK_INIT(sc); - - callout_init_mtx(&sc->calib_to, &sc->sc_mtx, 0); - callout_init_mtx(&sc->watchdog_to, &sc->sc_mtx, 0); - - /* disable the retry timeout register */ - pci_write_config(dev, 0x41, 0, 1); - - /* enable bus-mastering */ - pci_enable_busmaster(dev); - - rid = PCIR_BAR(0); - sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, - RF_ACTIVE); - if (sc->mem == NULL) { - device_printf(dev, "could not allocate memory resource\n"); - error = ENOMEM; - goto fail; - } - - sc->sc_st = rman_get_bustag(sc->mem); - sc->sc_sh = rman_get_bushandle(sc->mem); - - rid = 0; - sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, - RF_ACTIVE | RF_SHAREABLE); - if (sc->irq == NULL) { - device_printf(dev, "could not allocate interrupt resource\n"); - error = ENOMEM; - goto fail; - } - - /* - * Allocate DMA memory for firmware transfers. - */ - if ((error = wpi_alloc_fwmem(sc)) != 0) { - kprintf(": could not allocate firmware memory\n"); - error = ENOMEM; - goto fail; - } - - /* - * Put adapter into a known state. - */ - if ((error = wpi_reset(sc)) != 0) { - device_printf(dev, "could not reset adapter\n"); - goto fail; - } - - wpi_mem_lock(sc); - tmp = wpi_mem_read(sc, WPI_MEM_PCIDEV); - if (bootverbose || WPI_DEBUG_SET) - device_printf(sc->sc_dev, "Hardware Revision (0x%X)\n", tmp); - - wpi_mem_unlock(sc); - - /* Allocate shared page */ - if ((error = wpi_alloc_shared(sc)) != 0) { - device_printf(dev, "could not allocate shared page\n"); - goto fail; - } - - /* tx data queues - 4 for QoS purposes */ - for (ac = 0; ac < WME_NUM_AC; ac++) { - error = wpi_alloc_tx_ring(sc, &sc->txq[ac], WPI_TX_RING_COUNT, ac); - if (error != 0) { - device_printf(dev, "could not allocate Tx ring %d\n",ac); - goto fail; - } - } - - /* command queue to talk to the card's firmware */ - error = wpi_alloc_tx_ring(sc, &sc->cmdq, WPI_CMD_RING_COUNT, 4); - if (error != 0) { - device_printf(dev, "could not allocate command ring\n"); - goto fail; - } - - /* receive data queue */ - error = wpi_alloc_rx_ring(sc, &sc->rxq); - if (error != 0) { - device_printf(dev, "could not allocate Rx ring\n"); - goto fail; - } - - ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); - if (ifp == NULL) { - device_printf(dev, "can not if_alloc()\n"); - error = ENOMEM; - goto fail; - } - ic = ifp->if_l2com; - - ic->ic_ifp = ifp; - ic->ic_softc = sc; - ic->ic_name = device_get_nameunit(dev); - ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ - ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ - - /* set device capabilities */ - ic->ic_caps = - IEEE80211_C_STA /* station mode supported */ - | IEEE80211_C_MONITOR /* monitor mode supported */ - | IEEE80211_C_TXPMGT /* tx power management */ - | IEEE80211_C_SHSLOT /* short slot time supported */ - | IEEE80211_C_SHPREAMBLE /* short preamble supported */ - | IEEE80211_C_WPA /* 802.11i */ -/* XXX looks like WME is partly supported? */ -#if 0 - | IEEE80211_C_IBSS /* IBSS mode support */ - | IEEE80211_C_BGSCAN /* capable of bg scanning */ - | IEEE80211_C_WME /* 802.11e */ - | IEEE80211_C_HOSTAP /* Host access point mode */ -#endif - ; - - /* - * Read in the eeprom and also setup the channels for - * net80211. We don't set the rates as net80211 does this for us - */ - wpi_read_eeprom(sc, macaddr); - - if (bootverbose || WPI_DEBUG_SET) { - device_printf(sc->sc_dev, "Regulatory Domain: %.4s\n", sc->domain); - device_printf(sc->sc_dev, "Hardware Type: %c\n", - sc->type > 1 ? 'B': '?'); - device_printf(sc->sc_dev, "Hardware Revision: %c\n", - ((le16toh(sc->rev) & 0xf0) == 0xd0) ? 'D': '?'); - device_printf(sc->sc_dev, "SKU %s support 802.11a\n", - supportsa ? "does" : "does not"); - - /* XXX hw_config uses the PCIDEV for the Hardware rev. Must check - what sc->rev really represents - benjsc 20070615 */ - } - - if_initname(ifp, device_get_name(dev), device_get_unit(dev)); - ifp->if_softc = sc; - ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; - ifp->if_init = wpi_init; - ifp->if_ioctl = wpi_ioctl; - ifp->if_start = wpi_start; -#if defined(__DragonFly__) - ifp->if_nmbjclusters = WPI_RX_RING_COUNT; - ifq_set_maxlen(&ifp->if_snd, ifqmaxlen); -#else - IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); - ifp->if_snd.ifq_drv_maxlen = ifqmaxlen; - IFQ_SET_READY(&ifp->if_snd); -#endif - - /* ieee80211_ifattach() assumes that WLAN serializer is held */ - wlan_serialize_enter(); - ieee80211_ifattach(ic, macaddr); - wlan_serialize_exit(); - /* override default methods */ - ic->ic_raw_xmit = wpi_raw_xmit; - ic->ic_wme.wme_update = wpi_wme_update; - ic->ic_scan_start = wpi_scan_start; - ic->ic_scan_end = wpi_scan_end; - ic->ic_set_channel = wpi_set_channel; - ic->ic_scan_curchan = wpi_scan_curchan; - ic->ic_scan_mindwell = wpi_scan_mindwell; - - ic->ic_vap_create = wpi_vap_create; - ic->ic_vap_delete = wpi_vap_delete; - - ieee80211_radiotap_attach(ic, - &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), - WPI_TX_RADIOTAP_PRESENT, - &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), - WPI_RX_RADIOTAP_PRESENT); - - /* - * Hook our interrupt after all initialization is complete. - */ -#if defined (__DragonFly__) - error = bus_setup_intr(dev, sc->irq, INTR_MPSAFE, - wpi_intr, sc, &sc->sc_ih, &wlan_global_serializer); -#else - error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET |INTR_MPSAFE, - NULL, wpi_intr, sc, &sc->sc_ih); -#endif - if (error != 0) { - device_printf(dev, "could not set up interrupt\n"); - goto fail; - } - - if (bootverbose) - ieee80211_announce(ic); -#ifdef XXX_DEBUG - ieee80211_announce_channels(ic); -#endif - return 0; - -fail: wpi_detach(dev); - return ENXIO; -} - -static int -wpi_detach(device_t dev) -{ - struct wpi_softc *sc = device_get_softc(dev); - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic; - int ac; - - if (sc->irq != NULL) - bus_teardown_intr(dev, sc->irq, sc->sc_ih); - - if (ifp != NULL) { - ic = ifp->if_l2com; - - ieee80211_draintask(ic, &sc->sc_restarttask); - ieee80211_draintask(ic, &sc->sc_radiotask); - wpi_stop(sc); - callout_drain(&sc->watchdog_to); - callout_drain(&sc->calib_to); - ieee80211_ifdetach(ic); - } - - WPI_LOCK(sc); - if (sc->txq[0].data_dmat) { - for (ac = 0; ac < WME_NUM_AC; ac++) - wpi_free_tx_ring(sc, &sc->txq[ac]); - - wpi_free_tx_ring(sc, &sc->cmdq); - wpi_free_rx_ring(sc, &sc->rxq); - wpi_free_shared(sc); - } - - if (sc->fw_fp != NULL) { - wpi_unload_firmware(sc); - } - - if (sc->fw_dma.tag) - wpi_free_fwmem(sc); - WPI_UNLOCK(sc); - - if (sc->irq != NULL) - bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(sc->irq), - sc->irq); - if (sc->mem != NULL) - bus_release_resource(dev, SYS_RES_MEMORY, - rman_get_rid(sc->mem), sc->mem); - - if (ifp != NULL) - if_free(ifp); - - WPI_LOCK_DESTROY(sc); - - return 0; -} - -static struct ieee80211vap * -wpi_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, - enum ieee80211_opmode opmode, int flags, - const uint8_t bssid[IEEE80211_ADDR_LEN], - const uint8_t mac[IEEE80211_ADDR_LEN]) -{ - struct wpi_vap *wvp; - struct ieee80211vap *vap; - - if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ - return NULL; - wvp = (struct wpi_vap *) kmalloc(sizeof(struct wpi_vap), - M_80211_VAP, M_INTWAIT | M_ZERO); - if (wvp == NULL) - return NULL; - vap = &wvp->vap; - ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac); - /* override with driver methods */ - wvp->newstate = vap->iv_newstate; - vap->iv_newstate = wpi_newstate; - - ieee80211_ratectl_init(vap); - /* complete setup */ - ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status); - ic->ic_opmode = opmode; - return vap; -} - -static void -wpi_vap_delete(struct ieee80211vap *vap) -{ - struct wpi_vap *wvp = WPI_VAP(vap); - - ieee80211_ratectl_deinit(vap); - ieee80211_vap_detach(vap); - kfree(wvp, M_80211_VAP); -} - -static void -wpi_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) -{ - if (error != 0) - return; - - KASSERT(nsegs == 1, ("too many DMA segments, %d should be 1", nsegs)); - - *(bus_addr_t *)arg = segs[0].ds_addr; -} - -/* - * Allocates a contiguous block of dma memory of the requested size and - * alignment. Due to limitations of the FreeBSD dma subsystem as of 20071217, - * allocations greater than 4096 may fail. Hence if the requested alignment is - * greater we allocate 'alignment' size extra memory and shift the vaddr and - * paddr after the dma load. This bypasses the problem at the cost of a little - * more memory. - */ -static int -wpi_dma_contig_alloc(struct wpi_softc *sc, struct wpi_dma_info *dma, - void **kvap, bus_size_t size, bus_size_t alignment, int flags) -{ - int error; - bus_size_t align; - bus_size_t reqsize; - - DPRINTFN(WPI_DEBUG_DMA, - ("Size: %zd - alignment %zd\n", size, alignment)); - - dma->size = size; - dma->tag = NULL; - - if (alignment > 4096) { - align = PAGE_SIZE; - reqsize = size + alignment; - } else { - align = alignment; - reqsize = size; - } -#if defined(__DragonFly__) - error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), align, - 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, - NULL, NULL, reqsize, - 1, reqsize, flags, - &dma->tag); -#else - error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), align, - 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, - NULL, NULL, reqsize, - 1, reqsize, flags, - NULL, NULL, &dma->tag); -#endif - if (error != 0) { - device_printf(sc->sc_dev, - "could not create shared page DMA tag\n"); - goto fail; - } - error = bus_dmamem_alloc(dma->tag, (void **)&dma->vaddr_start, - flags | BUS_DMA_ZERO, &dma->map); - if (error != 0) { - device_printf(sc->sc_dev, - "could not allocate shared page DMA memory\n"); - goto fail; - } - - error = bus_dmamap_load(dma->tag, dma->map, dma->vaddr_start, - reqsize, wpi_dma_map_addr, &dma->paddr_start, flags); - - /* Save the original pointers so we can free all the memory */ - dma->paddr = dma->paddr_start; - dma->vaddr = dma->vaddr_start; - - /* - * Check the alignment and increment by 4096 until we get the - * requested alignment. Fail if can't obtain the alignment - * we requested. - */ - if ((dma->paddr & (alignment -1 )) != 0) { - int i; - - for (i = 0; i < alignment / 4096; i++) { - if ((dma->paddr & (alignment - 1 )) == 0) - break; - dma->paddr += 4096; - dma->vaddr += 4096; - } - if (i == alignment / 4096) { - device_printf(sc->sc_dev, - "alignment requirement was not satisfied\n"); - goto fail; - } - } - - if (error != 0) { - device_printf(sc->sc_dev, - "could not load shared page DMA map\n"); - goto fail; - } - - if (kvap != NULL) - *kvap = dma->vaddr; - - return 0; - -fail: - wpi_dma_contig_free(dma); - return error; -} - -static void -wpi_dma_contig_free(struct wpi_dma_info *dma) -{ - if (dma->tag) { - if (dma->vaddr_start != NULL) { - if (dma->paddr_start != 0) { - bus_dmamap_sync(dma->tag, dma->map, - BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); - bus_dmamap_unload(dma->tag, dma->map); - } - bus_dmamem_free(dma->tag, dma->vaddr_start, dma->map); - } - bus_dma_tag_destroy(dma->tag); - } -} - -/* - * Allocate a shared page between host and NIC. - */ -static int -wpi_alloc_shared(struct wpi_softc *sc) -{ - int error; - - error = wpi_dma_contig_alloc(sc, &sc->shared_dma, - (void **)&sc->shared, sizeof (struct wpi_shared), - PAGE_SIZE, - BUS_DMA_NOWAIT); - - if (error != 0) { - device_printf(sc->sc_dev, - "could not allocate shared area DMA memory\n"); - } - - return error; -} - -static void -wpi_free_shared(struct wpi_softc *sc) -{ - wpi_dma_contig_free(&sc->shared_dma); -} - -static int -wpi_alloc_rx_ring(struct wpi_softc *sc, struct wpi_rx_ring *ring) -{ - - int i, error; - - ring->cur = 0; - - error = wpi_dma_contig_alloc(sc, &ring->desc_dma, - (void **)&ring->desc, WPI_RX_RING_COUNT * sizeof (uint32_t), - WPI_RING_DMA_ALIGN, BUS_DMA_NOWAIT); - - if (error != 0) { - device_printf(sc->sc_dev, - "%s: could not allocate rx ring DMA memory, error %d\n", - __func__, error); - goto fail; - } - -#if defined(__DragonFly__) - error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, - BUS_SPACE_MAXADDR_32BIT, - BUS_SPACE_MAXADDR, NULL, NULL, MJUMPAGESIZE, 1, - MJUMPAGESIZE, BUS_DMA_NOWAIT, &ring->data_dmat); -#else - error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, - BUS_SPACE_MAXADDR_32BIT, - BUS_SPACE_MAXADDR, NULL, NULL, MJUMPAGESIZE, 1, - MJUMPAGESIZE, BUS_DMA_NOWAIT, NULL, NULL, &ring->data_dmat); -#endif - if (error != 0) { - device_printf(sc->sc_dev, - "%s: bus_dma_tag_create_failed, error %d\n", - __func__, error); - goto fail; - } - - /* - * Setup Rx buffers. - */ - for (i = 0; i < WPI_RX_RING_COUNT; i++) { - struct wpi_rx_data *data = &ring->data[i]; - struct mbuf *m; - bus_addr_t paddr; - - error = bus_dmamap_create(ring->data_dmat, 0, &data->map); - if (error != 0) { - device_printf(sc->sc_dev, - "%s: bus_dmamap_create failed, error %d\n", - __func__, error); - goto fail; - } - m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE); - if (m == NULL) { - device_printf(sc->sc_dev, - "%s: could not allocate rx mbuf\n", __func__); - error = ENOMEM; - goto fail; - } - /* map page */ - error = bus_dmamap_load(ring->data_dmat, data->map, - mtod(m, caddr_t), MJUMPAGESIZE, - wpi_dma_map_addr, &paddr, BUS_DMA_NOWAIT); - if (error != 0 && error != EFBIG) { - device_printf(sc->sc_dev, - "%s: bus_dmamap_load failed, error %d\n", - __func__, error); - m_freem(m); - error = ENOMEM; /* XXX unique code */ - goto fail; - } - bus_dmamap_sync(ring->data_dmat, data->map, - BUS_DMASYNC_PREWRITE); - - data->m = m; - ring->desc[i] = htole32(paddr); - } - bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, - BUS_DMASYNC_PREWRITE); - return 0; -fail: - wpi_free_rx_ring(sc, ring); - return error; -} - -static void -wpi_reset_rx_ring(struct wpi_softc *sc, struct wpi_rx_ring *ring) -{ - int ntries; - - wpi_mem_lock(sc); - - WPI_WRITE(sc, WPI_RX_CONFIG, 0); - - for (ntries = 0; ntries < 100; ntries++) { - if (WPI_READ(sc, WPI_RX_STATUS) & WPI_RX_IDLE) - break; - DELAY(10); - } - - wpi_mem_unlock(sc); - -#ifdef WPI_DEBUG - if (ntries == 100 && wpi_debug > 0) - device_printf(sc->sc_dev, "timeout resetting Rx ring\n"); -#endif - - ring->cur = 0; -} - -static void -wpi_free_rx_ring(struct wpi_softc *sc, struct wpi_rx_ring *ring) -{ - int i; - - wpi_dma_contig_free(&ring->desc_dma); - - for (i = 0; i < WPI_RX_RING_COUNT; i++) { - struct wpi_rx_data *data = &ring->data[i]; - - if (data->m != NULL) { - bus_dmamap_sync(ring->data_dmat, data->map, - BUS_DMASYNC_POSTREAD); - bus_dmamap_unload(ring->data_dmat, data->map); - m_freem(data->m); - } - if (data->map != NULL) - bus_dmamap_destroy(ring->data_dmat, data->map); - } -} - -static int -wpi_alloc_tx_ring(struct wpi_softc *sc, struct wpi_tx_ring *ring, int count, - int qid) -{ - struct wpi_tx_data *data; - int i, error; - - ring->qid = qid; - ring->count = count; - ring->queued = 0; - ring->cur = 0; - ring->data = NULL; - - error = wpi_dma_contig_alloc(sc, &ring->desc_dma, - (void **)&ring->desc, count * sizeof (struct wpi_tx_desc), - WPI_RING_DMA_ALIGN, BUS_DMA_NOWAIT); - - if (error != 0) { - device_printf(sc->sc_dev, "could not allocate tx dma memory\n"); - goto fail; - } - - /* update shared page with ring's base address */ - sc->shared->txbase[qid] = htole32(ring->desc_dma.paddr); - - error = wpi_dma_contig_alloc(sc, &ring->cmd_dma, (void **)&ring->cmd, - count * sizeof (struct wpi_tx_cmd), WPI_RING_DMA_ALIGN, - BUS_DMA_NOWAIT); - - if (error != 0) { - device_printf(sc->sc_dev, - "could not allocate tx command DMA memory\n"); - goto fail; - } - - ring->data = kmalloc(count * sizeof (struct wpi_tx_data), M_DEVBUF, - M_INTWAIT | M_ZERO); - if (ring->data == NULL) { - device_printf(sc->sc_dev, - "could not allocate tx data slots\n"); - goto fail; - } - -#if defined(__DragonFly__) - error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, - BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, - WPI_MAX_SCATTER - 1, MCLBYTES, BUS_DMA_NOWAIT, - &ring->data_dmat); -#else - error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, - BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, - WPI_MAX_SCATTER - 1, MCLBYTES, BUS_DMA_NOWAIT, NULL, NULL, - &ring->data_dmat); -#endif - if (error != 0) { - device_printf(sc->sc_dev, "could not create data DMA tag\n"); - goto fail; - } - - for (i = 0; i < count; i++) { - data = &ring->data[i]; - - error = bus_dmamap_create(ring->data_dmat, 0, &data->map); - if (error != 0) { - device_printf(sc->sc_dev, - "could not create tx buf DMA map\n"); - goto fail; - } - bus_dmamap_sync(ring->data_dmat, data->map, - BUS_DMASYNC_PREWRITE); - } - - return 0; - -fail: - wpi_free_tx_ring(sc, ring); - return error; -} - -static void -wpi_reset_tx_ring(struct wpi_softc *sc, struct wpi_tx_ring *ring) -{ - struct wpi_tx_data *data; - int i, ntries; - - wpi_mem_lock(sc); - - WPI_WRITE(sc, WPI_TX_CONFIG(ring->qid), 0); - for (ntries = 0; ntries < 100; ntries++) { - if (WPI_READ(sc, WPI_TX_STATUS) & WPI_TX_IDLE(ring->qid)) - break; - DELAY(10); - } -#ifdef WPI_DEBUG - if (ntries == 100 && wpi_debug > 0) - device_printf(sc->sc_dev, "timeout resetting Tx ring %d\n", - ring->qid); -#endif - wpi_mem_unlock(sc); - - for (i = 0; i < ring->count; i++) { - data = &ring->data[i]; - - if (data->m != NULL) { - bus_dmamap_unload(ring->data_dmat, data->map); - m_freem(data->m); - data->m = NULL; - } - } - - ring->queued = 0; - ring->cur = 0; -} - -static void -wpi_free_tx_ring(struct wpi_softc *sc, struct wpi_tx_ring *ring) -{ - struct wpi_tx_data *data; - int i; - - wpi_dma_contig_free(&ring->desc_dma); - wpi_dma_contig_free(&ring->cmd_dma); - - if (ring->data != NULL) { - for (i = 0; i < ring->count; i++) { - data = &ring->data[i]; - - if (data->m != NULL) { - bus_dmamap_sync(ring->data_dmat, data->map, - BUS_DMASYNC_POSTWRITE); - bus_dmamap_unload(ring->data_dmat, data->map); - m_freem(data->m); - data->m = NULL; - } - } - kfree(ring->data, M_DEVBUF); - } - - if (ring->data_dmat != NULL) - bus_dma_tag_destroy(ring->data_dmat); -} - -static int -wpi_shutdown(device_t dev) -{ - struct wpi_softc *sc = device_get_softc(dev); - - WPI_LOCK(sc); - wpi_stop_locked(sc); - wpi_unload_firmware(sc); - WPI_UNLOCK(sc); - - return 0; -} - -static int -wpi_suspend(device_t dev) -{ - struct wpi_softc *sc = device_get_softc(dev); - struct ieee80211com *ic = sc->sc_ifp->if_l2com; - - ieee80211_suspend_all(ic); - return 0; -} - -static int -wpi_resume(device_t dev) -{ - struct wpi_softc *sc = device_get_softc(dev); - struct ieee80211com *ic = sc->sc_ifp->if_l2com; - - pci_write_config(dev, 0x41, 0, 1); - - ieee80211_resume_all(ic); - return 0; -} - -/** - * Called by net80211 when ever there is a change to 80211 state machine - */ -static int -wpi_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) -{ - struct wpi_vap *wvp = WPI_VAP(vap); - struct ieee80211com *ic = vap->iv_ic; - struct wpi_softc *sc = ic->ic_softc; - int error; - - DPRINTF(("%s: %s -> %s flags 0x%x\n", __func__, - ieee80211_state_name[vap->iv_state], - ieee80211_state_name[nstate], sc->flags)); - - IEEE80211_UNLOCK(ic); - WPI_LOCK(sc); - if (nstate == IEEE80211_S_SCAN && vap->iv_state != IEEE80211_S_INIT) { - /* - * On !INIT -> SCAN transitions, we need to clear any possible - * knowledge about associations. - */ - error = wpi_config(sc); - if (error != 0) { - device_printf(sc->sc_dev, - "%s: device config failed, error %d\n", - __func__, error); - } - } - if (nstate == IEEE80211_S_AUTH || - (nstate == IEEE80211_S_ASSOC && vap->iv_state == IEEE80211_S_RUN)) { - /* - * The node must be registered in the firmware before auth. - * Also the associd must be cleared on RUN -> ASSOC - * transitions. - */ - error = wpi_auth(sc, vap); - if (error != 0) { - device_printf(sc->sc_dev, - "%s: could not move to auth state, error %d\n", - __func__, error); - } - } - if (nstate == IEEE80211_S_RUN && vap->iv_state != IEEE80211_S_RUN) { - error = wpi_run(sc, vap); - if (error != 0) { - device_printf(sc->sc_dev, - "%s: could not move to run state, error %d\n", - __func__, error); - } - } - if (nstate == IEEE80211_S_RUN) { - /* RUN -> RUN transition; just restart the timers */ - wpi_calib_timeout(sc); - /* XXX split out rate control timer */ - } - WPI_UNLOCK(sc); - IEEE80211_LOCK(ic); - return wvp->newstate(vap, nstate, arg); -} - -/* - * Grab exclusive access to NIC memory. - */ -static void -wpi_mem_lock(struct wpi_softc *sc) -{ - int ntries; - uint32_t tmp; - - tmp = WPI_READ(sc, WPI_GPIO_CTL); - WPI_WRITE(sc, WPI_GPIO_CTL, tmp | WPI_GPIO_MAC); - - /* spin until we actually get the lock */ - for (ntries = 0; ntries < 100; ntries++) { - if ((WPI_READ(sc, WPI_GPIO_CTL) & - (WPI_GPIO_CLOCK | WPI_GPIO_SLEEP)) == WPI_GPIO_CLOCK) - break; - DELAY(10); - } - if (ntries == 100) - device_printf(sc->sc_dev, "could not lock memory\n"); -} - -/* - * Release lock on NIC memory. - */ -static void -wpi_mem_unlock(struct wpi_softc *sc) -{ - uint32_t tmp = WPI_READ(sc, WPI_GPIO_CTL); - WPI_WRITE(sc, WPI_GPIO_CTL, tmp & ~WPI_GPIO_MAC); -} - -static uint32_t -wpi_mem_read(struct wpi_softc *sc, uint16_t addr) -{ - WPI_WRITE(sc, WPI_READ_MEM_ADDR, WPI_MEM_4 | addr); - return WPI_READ(sc, WPI_READ_MEM_DATA); -} - -static void -wpi_mem_write(struct wpi_softc *sc, uint16_t addr, uint32_t data) -{ - WPI_WRITE(sc, WPI_WRITE_MEM_ADDR, WPI_MEM_4 | addr); - WPI_WRITE(sc, WPI_WRITE_MEM_DATA, data); -} - -static void -wpi_mem_write_region_4(struct wpi_softc *sc, uint16_t addr, - const uint32_t *data, int wlen) -{ - for (; wlen > 0; wlen--, data++, addr+=4) - wpi_mem_write(sc, addr, *data); -} - -/* - * Read data from the EEPROM. We access EEPROM through the MAC instead of - * using the traditional bit-bang method. Data is read up until len bytes have - * been obtained. - */ -static uint16_t -wpi_read_prom_data(struct wpi_softc *sc, uint32_t addr, void *data, int len) -{ - int ntries; - uint32_t val; - uint8_t *out = data; - - wpi_mem_lock(sc); - - for (; len > 0; len -= 2, addr++) { - WPI_WRITE(sc, WPI_EEPROM_CTL, addr << 2); - - for (ntries = 0; ntries < 10; ntries++) { - if ((val = WPI_READ(sc, WPI_EEPROM_CTL)) & WPI_EEPROM_READY) - break; - DELAY(5); - } - - if (ntries == 10) { - device_printf(sc->sc_dev, "could not read EEPROM\n"); - return ETIMEDOUT; - } - - *out++= val >> 16; - if (len > 1) - *out ++= val >> 24; - } - - wpi_mem_unlock(sc); - - return 0; -} - -/* - * The firmware text and data segments are transferred to the NIC using DMA. - * The driver just copies the firmware into DMA-safe memory and tells the NIC - * where to find it. Once the NIC has copied the firmware into its internal - * memory, we can free our local copy in the driver. - */ -static int -wpi_load_microcode(struct wpi_softc *sc, const uint8_t *fw, int size) -{ - int error, ntries; - - DPRINTFN(WPI_DEBUG_HW,("Loading microcode size 0x%x\n", size)); - - size /= sizeof(uint32_t); - - wpi_mem_lock(sc); - - wpi_mem_write_region_4(sc, WPI_MEM_UCODE_BASE, - (const uint32_t *)fw, size); - - wpi_mem_write(sc, WPI_MEM_UCODE_SRC, 0); - wpi_mem_write(sc, WPI_MEM_UCODE_DST, WPI_FW_TEXT); - wpi_mem_write(sc, WPI_MEM_UCODE_SIZE, size); - - /* run microcode */ - wpi_mem_write(sc, WPI_MEM_UCODE_CTL, WPI_UC_RUN); - - /* wait while the adapter is busy copying the firmware */ - for (error = 0, ntries = 0; ntries < 1000; ntries++) { - uint32_t status = WPI_READ(sc, WPI_TX_STATUS); - DPRINTFN(WPI_DEBUG_HW, - ("firmware status=0x%x, val=0x%x, result=0x%x\n", status, - WPI_TX_IDLE(6), status & WPI_TX_IDLE(6))); - if (status & WPI_TX_IDLE(6)) { - DPRINTFN(WPI_DEBUG_HW, - ("Status Match! - ntries = %d\n", ntries)); - break; - } - DELAY(10); - } - if (ntries == 1000) { - device_printf(sc->sc_dev, "timeout transferring firmware\n"); - error = ETIMEDOUT; - } - - /* start the microcode executing */ - wpi_mem_write(sc, WPI_MEM_UCODE_CTL, WPI_UC_ENABLE); - - wpi_mem_unlock(sc); - - return (error); -} - -static void -wpi_rx_intr(struct wpi_softc *sc, struct wpi_rx_desc *desc, - struct wpi_rx_data *data) -{ - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - struct wpi_rx_ring *ring = &sc->rxq; - struct wpi_rx_stat *stat; - struct wpi_rx_head *head; - struct wpi_rx_tail *tail; - struct ieee80211_node *ni; - struct mbuf *m, *mnew; - bus_addr_t paddr; - int error; - - stat = (struct wpi_rx_stat *)(desc + 1); - - if (stat->len > WPI_STAT_MAXLEN) { - device_printf(sc->sc_dev, "invalid rx statistic header\n"); -#if defined(__DragonFly__) - IFNET_STAT_INC(ifp, ierrors, 1); -#else - if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); -#endif - return; - } - - bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD); - head = (struct wpi_rx_head *)((caddr_t)(stat + 1) + stat->len); - tail = (struct wpi_rx_tail *)((caddr_t)(head + 1) + le16toh(head->len)); - - DPRINTFN(WPI_DEBUG_RX, ("rx intr: idx=%d len=%d stat len=%d rssi=%d " - "rate=%x chan=%d tstamp=%ju\n", ring->cur, le32toh(desc->len), - le16toh(head->len), (int8_t)stat->rssi, head->rate, head->chan, - (uintmax_t)le64toh(tail->tstamp))); - - /* discard Rx frames with bad CRC early */ - if ((le32toh(tail->flags) & WPI_RX_NOERROR) != WPI_RX_NOERROR) { - DPRINTFN(WPI_DEBUG_RX, ("%s: rx flags error %x\n", __func__, - le32toh(tail->flags))); -#if defined(__DragonFly__) - IFNET_STAT_INC(ifp, ierrors, 1); -#else - if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); -#endif - return; - } - if (le16toh(head->len) < sizeof (struct ieee80211_frame)) { - DPRINTFN(WPI_DEBUG_RX, ("%s: frame too short: %d\n", __func__, - le16toh(head->len))); -#if defined(__DragonFly__) - IFNET_STAT_INC(ifp, ierrors, 1); -#else - if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); -#endif - return; - } - - /* XXX don't need mbuf, just dma buffer */ - mnew = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE); - if (mnew == NULL) { - DPRINTFN(WPI_DEBUG_RX, ("%s: no mbuf to restock ring\n", - __func__)); -#if defined(__DragonFly__) - IFNET_STAT_INC(ifp, ierrors, 1); -#else - if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); -#endif - return; - } - bus_dmamap_unload(ring->data_dmat, data->map); - - error = bus_dmamap_load(ring->data_dmat, data->map, - mtod(mnew, caddr_t), MJUMPAGESIZE, - wpi_dma_map_addr, &paddr, BUS_DMA_NOWAIT); - if (error != 0 && error != EFBIG) { - device_printf(sc->sc_dev, - "%s: bus_dmamap_load failed, error %d\n", __func__, error); - m_freem(mnew); -#if defined(__DragonFly__) - IFNET_STAT_INC(ifp, ierrors, 1); -#else - if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); -#endif - return; - } - bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE); - - /* finalize mbuf and swap in new one */ - m = data->m; - m->m_pkthdr.rcvif = ifp; - m->m_data = (caddr_t)(head + 1); - m->m_pkthdr.len = m->m_len = le16toh(head->len); - - data->m = mnew; - /* update Rx descriptor */ - ring->desc[ring->cur] = htole32(paddr); - - if (ieee80211_radiotap_active(ic)) { - struct wpi_rx_radiotap_header *tap = &sc->sc_rxtap; - - tap->wr_flags = 0; - tap->wr_chan_freq = - htole16(ic->ic_channels[head->chan].ic_freq); - tap->wr_chan_flags = - htole16(ic->ic_channels[head->chan].ic_flags); - tap->wr_dbm_antsignal = (int8_t)(stat->rssi - WPI_RSSI_OFFSET); - tap->wr_dbm_antnoise = (int8_t)le16toh(stat->noise); - tap->wr_tsft = tail->tstamp; - tap->wr_antenna = (le16toh(head->flags) >> 4) & 0xf; - switch (head->rate) { - /* CCK rates */ - case 10: tap->wr_rate = 2; break; - case 20: tap->wr_rate = 4; break; - case 55: tap->wr_rate = 11; break; - case 110: tap->wr_rate = 22; break; - /* OFDM rates */ - case 0xd: tap->wr_rate = 12; break; - case 0xf: tap->wr_rate = 18; break; - case 0x5: tap->wr_rate = 24; break; - case 0x7: tap->wr_rate = 36; break; - case 0x9: tap->wr_rate = 48; break; - case 0xb: tap->wr_rate = 72; break; - case 0x1: tap->wr_rate = 96; break; - case 0x3: tap->wr_rate = 108; break; - /* unknown rate: should not happen */ - default: tap->wr_rate = 0; - } - if (le16toh(head->flags) & 0x4) - tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; - } - - WPI_UNLOCK(sc); - - ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *)); - if (ni != NULL) { - (void) ieee80211_input(ni, m, stat->rssi, 0); - ieee80211_free_node(ni); - } else - (void) ieee80211_input_all(ic, m, stat->rssi, 0); - - WPI_LOCK(sc); -} - -static void -wpi_tx_intr(struct wpi_softc *sc, struct wpi_rx_desc *desc) -{ - struct ifnet *ifp = sc->sc_ifp; - struct wpi_tx_ring *ring = &sc->txq[desc->qid & 0x3]; - struct wpi_tx_data *txdata = &ring->data[desc->idx]; - struct wpi_tx_stat *stat = (struct wpi_tx_stat *)(desc + 1); - struct ieee80211_node *ni = txdata->ni; - struct ieee80211vap *vap = ni->ni_vap; - int retrycnt = 0; - - DPRINTFN(WPI_DEBUG_TX, ("tx done: qid=%d idx=%d retries=%d nkill=%d " - "rate=%x duration=%d status=%x\n", desc->qid, desc->idx, - stat->ntries, stat->nkill, stat->rate, le32toh(stat->duration), - le32toh(stat->status))); - - /* - * Update rate control statistics for the node. - * XXX we should not count mgmt frames since they're always sent at - * the lowest available bit-rate. - * XXX frames w/o ACK shouldn't be used either - */ - if (stat->ntries > 0) { - DPRINTFN(WPI_DEBUG_TX, ("%d retries\n", stat->ntries)); - retrycnt = 1; - } - ieee80211_ratectl_tx_complete(vap, ni, IEEE80211_RATECTL_TX_SUCCESS, - &retrycnt, NULL); - - /* XXX oerrors should only count errors !maxtries */ -#if defined(__DragonFly__) - if ((le32toh(stat->status) & 0xff) != 1) - IFNET_STAT_INC(ifp, oerrors, 1); - else - IFNET_STAT_INC(ifp, opackets, 1); -#else - if ((le32toh(stat->status) & 0xff) != 1) - if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); - else - if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); -#endif - - bus_dmamap_sync(ring->data_dmat, txdata->map, BUS_DMASYNC_POSTWRITE); - bus_dmamap_unload(ring->data_dmat, txdata->map); - /* XXX handle M_TXCB? */ - m_freem(txdata->m); - txdata->m = NULL; - ieee80211_free_node(txdata->ni); - txdata->ni = NULL; - - ring->queued--; - - sc->sc_tx_timer = 0; -#if defined(__DragonFly__) - ifq_clr_oactive(&ifp->if_snd); -#else - ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; -#endif - wpi_start_locked(ifp); -} - -static void -wpi_cmd_intr(struct wpi_softc *sc, struct wpi_rx_desc *desc) -{ - struct wpi_tx_ring *ring = &sc->cmdq; - struct wpi_tx_data *data; - - DPRINTFN(WPI_DEBUG_CMD, ("cmd notification qid=%x idx=%d flags=%x " - "type=%s len=%d\n", desc->qid, desc->idx, - desc->flags, wpi_cmd_str(desc->type), - le32toh(desc->len))); - - if ((desc->qid & 7) != 4) - return; /* not a command ack */ - - data = &ring->data[desc->idx]; - - /* if the command was mapped in a mbuf, free it */ - if (data->m != NULL) { - bus_dmamap_unload(ring->data_dmat, data->map); - m_freem(data->m); - data->m = NULL; - } - - sc->flags &= ~WPI_FLAG_BUSY; - wakeup(&ring->cmd[desc->idx]); -} - -static void -wpi_notif_intr(struct wpi_softc *sc) -{ - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - struct wpi_rx_desc *desc; - struct wpi_rx_data *data; - uint32_t hw; - - bus_dmamap_sync(sc->shared_dma.tag, sc->shared_dma.map, - BUS_DMASYNC_POSTREAD); - - hw = le32toh(sc->shared->next); - while (sc->rxq.cur != hw) { - data = &sc->rxq.data[sc->rxq.cur]; - - bus_dmamap_sync(sc->rxq.data_dmat, data->map, - BUS_DMASYNC_POSTREAD); - desc = (void *)data->m->m_ext.ext_buf; - - DPRINTFN(WPI_DEBUG_NOTIFY, - ("notify qid=%x idx=%d flags=%x type=%d len=%d\n", - desc->qid, - desc->idx, - desc->flags, - desc->type, - le32toh(desc->len))); - - if (!(desc->qid & 0x80)) /* reply to a command */ - wpi_cmd_intr(sc, desc); - - switch (desc->type) { - case WPI_RX_DONE: - /* a 802.11 frame was received */ - wpi_rx_intr(sc, desc, data); - break; - - case WPI_TX_DONE: - /* a 802.11 frame has been transmitted */ - wpi_tx_intr(sc, desc); - break; - - case WPI_UC_READY: - { - struct wpi_ucode_info *uc = - (struct wpi_ucode_info *)(desc + 1); - - /* the microcontroller is ready */ - DPRINTF(("microcode alive notification version %x " - "alive %x\n", le32toh(uc->version), - le32toh(uc->valid))); - - if (le32toh(uc->valid) != 1) { - device_printf(sc->sc_dev, - "microcontroller initialization failed\n"); - wpi_stop_locked(sc); - } - break; - } - case WPI_STATE_CHANGED: - { - uint32_t *status = (uint32_t *)(desc + 1); - - /* enabled/disabled notification */ - DPRINTF(("state changed to %x\n", le32toh(*status))); - - if (le32toh(*status) & 1) { - device_printf(sc->sc_dev, - "Radio transmitter is switched off\n"); - sc->flags |= WPI_FLAG_HW_RADIO_OFF; -#if defined(__DragonFly__) - ifp->if_flags &= ~IFF_RUNNING; -#else - ifp->if_drv_flags &= ~IFF_DRV_RUNNING; -#endif - /* Disable firmware commands */ - WPI_WRITE(sc, WPI_UCODE_SET, WPI_DISABLE_CMD); - } - break; - } - case WPI_START_SCAN: - { -#ifdef WPI_DEBUG - struct wpi_start_scan *scan = - (struct wpi_start_scan *)(desc + 1); -#endif - - DPRINTFN(WPI_DEBUG_SCANNING, - ("scanning channel %d status %x\n", - scan->chan, le32toh(scan->status))); - break; - } - case WPI_STOP_SCAN: - { -#ifdef WPI_DEBUG - struct wpi_stop_scan *scan = - (struct wpi_stop_scan *)(desc + 1); -#endif - struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); - - DPRINTFN(WPI_DEBUG_SCANNING, - ("scan finished nchan=%d status=%d chan=%d\n", - scan->nchan, scan->status, scan->chan)); - - sc->sc_scan_timer = 0; - ieee80211_scan_next(vap); - break; - } - case WPI_MISSED_BEACON: - { - struct wpi_missed_beacon *beacon = - (struct wpi_missed_beacon *)(desc + 1); - struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); - - if (le32toh(beacon->consecutive) >= - vap->iv_bmissthreshold) { - DPRINTF(("Beacon miss: %u >= %u\n", - le32toh(beacon->consecutive), - vap->iv_bmissthreshold)); - ieee80211_beacon_miss(ic); - } - break; - } - } - - sc->rxq.cur = (sc->rxq.cur + 1) % WPI_RX_RING_COUNT; - } - - /* tell the firmware what we have processed */ - hw = (hw == 0) ? WPI_RX_RING_COUNT - 1 : hw - 1; - WPI_WRITE(sc, WPI_RX_WIDX, hw & ~7); -} - -static void -wpi_intr(void *arg) -{ - struct wpi_softc *sc = arg; - uint32_t r; - - WPI_LOCK(sc); - - r = WPI_READ(sc, WPI_INTR); - if (r == 0 || r == 0xffffffff) { - WPI_UNLOCK(sc); - return; - } - - /* disable interrupts */ - WPI_WRITE(sc, WPI_MASK, 0); - /* ack interrupts */ - WPI_WRITE(sc, WPI_INTR, r); - - if (r & (WPI_SW_ERROR | WPI_HW_ERROR)) { - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); - - device_printf(sc->sc_dev, "fatal firmware error\n"); - DPRINTFN(6,("(%s)\n", (r & WPI_SW_ERROR) ? "(Software Error)" : - "(Hardware Error)")); - if (vap != NULL) - ieee80211_cancel_scan(vap); - ieee80211_runtask(ic, &sc->sc_restarttask); - sc->flags &= ~WPI_FLAG_BUSY; - WPI_UNLOCK(sc); - return; - } - - if (r & WPI_RX_INTR) - wpi_notif_intr(sc); - - if (r & WPI_ALIVE_INTR) /* firmware initialized */ - wakeup(sc); - - /* re-enable interrupts */ - if (sc->sc_ifp->if_flags & IFF_UP) - WPI_WRITE(sc, WPI_MASK, WPI_INTR_MASK); - - WPI_UNLOCK(sc); -} - -static uint8_t -wpi_plcp_signal(int rate) -{ - switch (rate) { - /* CCK rates (returned values are device-dependent) */ - case 2: return 10; - case 4: return 20; - case 11: return 55; - case 22: return 110; - - /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */ - /* R1-R4 (ral/ural is R4-R1) */ - case 12: return 0xd; - case 18: return 0xf; - case 24: return 0x5; - case 36: return 0x7; - case 48: return 0x9; - case 72: return 0xb; - case 96: return 0x1; - case 108: return 0x3; - - /* unsupported rates (should not get there) */ - default: return 0; - } -} - -/* quickly determine if a given rate is CCK or OFDM */ -#define WPI_RATE_IS_OFDM(rate) ((rate) >= 12 && (rate) != 22) - -/* - * Construct the data packet for a transmit buffer and acutally put - * the buffer onto the transmit ring, kicking the card to process the - * the buffer. - */ -static int -wpi_tx_data(struct wpi_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, - int ac) -{ - struct ieee80211vap *vap = ni->ni_vap; - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - const struct chanAccParams *cap = &ic->ic_wme.wme_chanParams; - struct wpi_tx_ring *ring = &sc->txq[ac]; - struct wpi_tx_desc *desc; - struct wpi_tx_data *data; - struct wpi_tx_cmd *cmd; - struct wpi_cmd_data *tx; - struct ieee80211_frame *wh; - const struct ieee80211_txparam *tp; - struct ieee80211_key *k; - struct mbuf *mnew; - int i, error, nsegs, rate, hdrlen, ismcast; - bus_dma_segment_t segs[WPI_MAX_SCATTER]; - - desc = &ring->desc[ring->cur]; - data = &ring->data[ring->cur]; - - wh = mtod(m0, struct ieee80211_frame *); - - hdrlen = ieee80211_hdrsize(wh); - ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); - - if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { - k = ieee80211_crypto_encap(ni, m0); - if (k == NULL) { - m_freem(m0); - return ENOBUFS; - } - /* packet header may have moved, reset our local pointer */ - wh = mtod(m0, struct ieee80211_frame *); - } - - cmd = &ring->cmd[ring->cur]; - cmd->code = WPI_CMD_TX_DATA; - cmd->flags = 0; - cmd->qid = ring->qid; - cmd->idx = ring->cur; - - tx = (struct wpi_cmd_data *)cmd->data; - tx->flags = htole32(WPI_TX_AUTO_SEQ); - tx->timeout = htole16(0); - tx->ofdm_mask = 0xff; - tx->cck_mask = 0x0f; - tx->lifetime = htole32(WPI_LIFETIME_INFINITE); - tx->id = ismcast ? WPI_ID_BROADCAST : WPI_ID_BSS; - tx->len = htole16(m0->m_pkthdr.len); - - if (!ismcast) { - if ((ni->ni_flags & IEEE80211_NODE_QOS) == 0 || - !cap->cap_wmeParams[ac].wmep_noackPolicy) - tx->flags |= htole32(WPI_TX_NEED_ACK); - if (m0->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) { - tx->flags |= htole32(WPI_TX_NEED_RTS|WPI_TX_FULL_TXOP); - tx->rts_ntries = 7; - } - } - /* pick a rate */ - tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)]; - if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_MGT) { - uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; - /* tell h/w to set timestamp in probe responses */ - if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP) - tx->flags |= htole32(WPI_TX_INSERT_TSTAMP); - if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ || - subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) - tx->timeout = htole16(3); - else - tx->timeout = htole16(2); - rate = tp->mgmtrate; - } else if (ismcast) { - rate = tp->mcastrate; - } else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) { - rate = tp->ucastrate; - } else { - (void) ieee80211_ratectl_rate(ni, NULL, 0); - rate = ni->ni_txrate; - } - tx->rate = wpi_plcp_signal(rate); - - /* be very persistant at sending frames out */ -#if 0 - tx->data_ntries = tp->maxretry; -#else - tx->data_ntries = 15; /* XXX way too high */ -#endif - - if (ieee80211_radiotap_active_vap(vap)) { - struct wpi_tx_radiotap_header *tap = &sc->sc_txtap; - tap->wt_flags = 0; - tap->wt_rate = rate; - tap->wt_hwqueue = ac; - if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) - tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP; - - ieee80211_radiotap_tx(vap, m0); - } - - /* save and trim IEEE802.11 header */ - m_copydata(m0, 0, hdrlen, (caddr_t)&tx->wh); - m_adj(m0, hdrlen); - -#if defined(__DragonFly__) - error = bus_dmamap_load_mbuf_segment(ring->data_dmat, data->map, - m0, segs, 1, &nsegs, BUS_DMA_NOWAIT); -#else - error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m0, segs, - &nsegs, BUS_DMA_NOWAIT); -#endif - if (error != 0 && error != EFBIG) { - device_printf(sc->sc_dev, "could not map mbuf (error %d)\n", - error); - m_freem(m0); - return error; - } - if (error != 0) { - /* XXX use m_collapse */ - mnew = m_defrag(m0, M_NOWAIT); - if (mnew == NULL) { - device_printf(sc->sc_dev, - "could not defragment mbuf\n"); - m_freem(m0); - return ENOBUFS; - } - m0 = mnew; - -#if defined(__DragonFly__) - error = bus_dmamap_load_mbuf_segment(ring->data_dmat, - data->map, m0, segs, 1, &nsegs, BUS_DMA_NOWAIT); -#else - error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, - m0, segs, &nsegs, BUS_DMA_NOWAIT); -#endif - if (error != 0) { - device_printf(sc->sc_dev, - "could not map mbuf (error %d)\n", error); - m_freem(m0); - return error; - } - } - - data->m = m0; - data->ni = ni; - - DPRINTFN(WPI_DEBUG_TX, ("sending data: qid=%d idx=%d len=%d nsegs=%d\n", - ring->qid, ring->cur, m0->m_pkthdr.len, nsegs)); - - /* first scatter/gather segment is used by the tx data command */ - desc->flags = htole32(WPI_PAD32(m0->m_pkthdr.len) << 28 | - (1 + nsegs) << 24); - desc->segs[0].addr = htole32(ring->cmd_dma.paddr + - ring->cur * sizeof (struct wpi_tx_cmd)); - desc->segs[0].len = htole32(4 + sizeof (struct wpi_cmd_data)); - for (i = 1; i <= nsegs; i++) { - desc->segs[i].addr = htole32(segs[i - 1].ds_addr); - desc->segs[i].len = htole32(segs[i - 1].ds_len); - } - - bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE); - bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, - BUS_DMASYNC_PREWRITE); - - ring->queued++; - - /* kick ring */ - ring->cur = (ring->cur + 1) % WPI_TX_RING_COUNT; - WPI_WRITE(sc, WPI_TX_WIDX, ring->qid << 8 | ring->cur); - - return 0; -} - -/** - * Process data waiting to be sent on the IFNET output queue - */ -static void -wpi_start(struct ifnet *ifp, struct ifaltq_subque *ifsq) -{ - struct wpi_softc *sc = ifp->if_softc; - - ASSERT_ALTQ_SQ_DEFAULT(ifp, ifsq); - - WPI_LOCK(sc); - wpi_start_locked(ifp); - WPI_UNLOCK(sc); -} - -static void -wpi_start_locked(struct ifnet *ifp) -{ - struct wpi_softc *sc = ifp->if_softc; - struct ieee80211_node *ni; - struct mbuf *m; - int ac; - - WPI_LOCK_ASSERT(sc); - -#if defined(__DragonFly__) - if ((ifp->if_flags & IFF_RUNNING) == 0) - return; -#else - if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) - return; -#endif - - for (;;) { -#if defined(__DragonFly__) - m = ifq_dequeue(&ifp->if_snd); -#else - IFQ_DRV_DEQUEUE(&ifp->if_snd, m); -#endif - if (m == NULL) - break; - ac = M_WME_GETAC(m); - if (sc->txq[ac].queued > sc->txq[ac].count - 8) { - /* there is no place left in this ring */ -#if defined(__DragonFly__) - ifq_prepend(&ifp->if_snd, m); - ifq_set_oactive(&ifp->if_snd); -#else - IFQ_DRV_PREPEND(&ifp->if_snd, m); - ifp->if_drv_flags |= IFF_DRV_OACTIVE; -#endif - break; - } - ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; - if (wpi_tx_data(sc, m, ni, ac) != 0) { - ieee80211_free_node(ni); -#if defined(__DragonFly__) - IFNET_STAT_INC(ifp, oerrors, 1); -#else - if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); -#endif - break; - } - sc->sc_tx_timer = 5; - } -} - -static int -wpi_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, - const struct ieee80211_bpf_params *params) -{ - struct ieee80211com *ic = ni->ni_ic; - struct ifnet *ifp = ic->ic_ifp; - struct wpi_softc *sc = ic->ic_softc; - - /* prevent management frames from being sent if we're not ready */ -#if defined(__DragonFly__) - if (!(ifp->if_flags & IFF_RUNNING)) { - m_freem(m); - ieee80211_free_node(ni); - return ENETDOWN; - } -#else - if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { - m_freem(m); - ieee80211_free_node(ni); - return ENETDOWN; - } -#endif - WPI_LOCK(sc); - - /* management frames go into ring 0 */ - if (sc->txq[0].queued > sc->txq[0].count - 8) { -#if defined(__DragonFly__) - ifq_set_oactive(&ifp->if_snd); -#else - ifp->if_drv_flags |= IFF_DRV_OACTIVE; -#endif - m_freem(m); - WPI_UNLOCK(sc); - ieee80211_free_node(ni); - return ENOBUFS; /* XXX */ - } - -#if defined(__DragonFly__) - IFNET_STAT_INC(ifp, opackets, 1); -#else - if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); -#endif - if (wpi_tx_data(sc, m, ni, 0) != 0) - goto bad; - sc->sc_tx_timer = 5; - callout_reset(&sc->watchdog_to, hz, wpi_watchdog, sc); - - WPI_UNLOCK(sc); - return 0; -bad: -#if defined(__DragonFly__) - IFNET_STAT_INC(ifp, oerrors, 1); -#else - if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); -#endif - WPI_UNLOCK(sc); - ieee80211_free_node(ni); - return EIO; /* XXX */ -} - -static int -wpi_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data, - struct ucred *cred __unused) -{ - struct wpi_softc *sc = ifp->if_softc; - struct ieee80211com *ic = ifp->if_l2com; - struct ifreq *ifr = (struct ifreq *) data; - int error = 0, startall = 0; - - switch (cmd) { - case SIOCSIFFLAGS: - WPI_LOCK(sc); -#if defined(__DragonFly__) - if ((ifp->if_flags & IFF_UP)) { - if (!(ifp->if_flags & IFF_RUNNING)) { - wpi_init_locked(sc, 0); - startall = 1; - } - } else if ((ifp->if_flags & IFF_RUNNING) || - (sc->flags & WPI_FLAG_HW_RADIO_OFF)) - wpi_stop_locked(sc); -#else - if ((ifp->if_flags & IFF_UP)) { - if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { - wpi_init_locked(sc, 0); - startall = 1; - } - } else if ((ifp->if_drv_flags & IFF_DRV_RUNNING) || - (sc->flags & WPI_FLAG_HW_RADIO_OFF)) - wpi_stop_locked(sc); -#endif - WPI_UNLOCK(sc); - if (startall) - ieee80211_start_all(ic); - break; - case SIOCGIFMEDIA: - error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); - break; - case SIOCGIFADDR: - error = ether_ioctl(ifp, cmd, data); - break; - default: - error = EINVAL; - break; - } - return error; -} - -/* - * Extract various information from EEPROM. - */ -static void -wpi_read_eeprom(struct wpi_softc *sc, uint8_t macaddr[IEEE80211_ADDR_LEN]) -{ - int i; - - /* read the hardware capabilities, revision and SKU type */ - wpi_read_prom_data(sc, WPI_EEPROM_CAPABILITIES, &sc->cap,1); - wpi_read_prom_data(sc, WPI_EEPROM_REVISION, &sc->rev,2); - wpi_read_prom_data(sc, WPI_EEPROM_TYPE, &sc->type, 1); - - /* read the regulatory domain */ - wpi_read_prom_data(sc, WPI_EEPROM_DOMAIN, sc->domain, 4); - - /* read in the hw MAC address */ - wpi_read_prom_data(sc, WPI_EEPROM_MAC, macaddr, 6); - - /* read the list of authorized channels */ - for (i = 0; i < WPI_CHAN_BANDS_COUNT; i++) - wpi_read_eeprom_channels(sc,i); - - /* read the power level calibration info for each group */ - for (i = 0; i < WPI_POWER_GROUPS_COUNT; i++) - wpi_read_eeprom_group(sc,i); -} - -/* - * Send a command to the firmware. - */ -static int -wpi_cmd(struct wpi_softc *sc, int code, const void *buf, int size, int async) -{ - struct wpi_tx_ring *ring = &sc->cmdq; - struct wpi_tx_desc *desc; - struct wpi_tx_cmd *cmd; - -#ifdef WPI_DEBUG - if (!async) { - WPI_LOCK_ASSERT(sc); - } -#endif - - DPRINTFN(WPI_DEBUG_CMD,("wpi_cmd %d size %d async %d\n", code, size, - async)); - - if (sc->flags & WPI_FLAG_BUSY) { - device_printf(sc->sc_dev, "%s: cmd %d not sent, busy\n", - __func__, code); - return EAGAIN; - } - sc->flags|= WPI_FLAG_BUSY; - - KASSERT(size <= sizeof cmd->data, ("command %d too large: %d bytes", - code, size)); - - desc = &ring->desc[ring->cur]; - cmd = &ring->cmd[ring->cur]; - - cmd->code = code; - cmd->flags = 0; - cmd->qid = ring->qid; - cmd->idx = ring->cur; - memcpy(cmd->data, buf, size); - - desc->flags = htole32(WPI_PAD32(size) << 28 | 1 << 24); - desc->segs[0].addr = htole32(ring->cmd_dma.paddr + - ring->cur * sizeof (struct wpi_tx_cmd)); - desc->segs[0].len = htole32(4 + size); - - /* kick cmd ring */ - ring->cur = (ring->cur + 1) % WPI_CMD_RING_COUNT; - WPI_WRITE(sc, WPI_TX_WIDX, ring->qid << 8 | ring->cur); - - if (async) { - sc->flags &= ~ WPI_FLAG_BUSY; - return 0; - } - -#if defined(__DragonFly__) - return wpi_sleep(sc, cmd, PCATCH, "wpicmd", hz); -#else - return msleep(cmd, &sc->sc_mtx, PCATCH, "wpicmd", hz); -#endif -} - -static int -wpi_wme_update(struct ieee80211com *ic) -{ -#define WPI_EXP2(v) htole16((1 << (v)) - 1) -#define WPI_USEC(v) htole16(IEEE80211_TXOP_TO_US(v)) - struct wpi_softc *sc = ic->ic_softc; - const struct wmeParams *wmep; - struct wpi_wme_setup wme; - int ac; - - /* don't override default WME values if WME is not actually enabled */ - if (!(ic->ic_flags & IEEE80211_F_WME)) - return 0; - - wme.flags = 0; - for (ac = 0; ac < WME_NUM_AC; ac++) { - wmep = &ic->ic_wme.wme_chanParams.cap_wmeParams[ac]; - wme.ac[ac].aifsn = wmep->wmep_aifsn; - wme.ac[ac].cwmin = WPI_EXP2(wmep->wmep_logcwmin); - wme.ac[ac].cwmax = WPI_EXP2(wmep->wmep_logcwmax); - wme.ac[ac].txop = WPI_USEC(wmep->wmep_txopLimit); - - DPRINTF(("setting WME for queue %d aifsn=%d cwmin=%d cwmax=%d " - "txop=%d\n", ac, wme.ac[ac].aifsn, wme.ac[ac].cwmin, - wme.ac[ac].cwmax, wme.ac[ac].txop)); - } - return wpi_cmd(sc, WPI_CMD_SET_WME, &wme, sizeof wme, 1); -#undef WPI_USEC -#undef WPI_EXP2 -} - -/* - * Configure h/w multi-rate retries. - */ -static int -wpi_mrr_setup(struct wpi_softc *sc) -{ - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - struct wpi_mrr_setup mrr; - int i, error; - - memset(&mrr, 0, sizeof (struct wpi_mrr_setup)); - - /* CCK rates (not used with 802.11a) */ - for (i = WPI_CCK1; i <= WPI_CCK11; i++) { - mrr.rates[i].flags = 0; - mrr.rates[i].signal = wpi_ridx_to_plcp[i]; - /* fallback to the immediate lower CCK rate (if any) */ - mrr.rates[i].next = (i == WPI_CCK1) ? WPI_CCK1 : i - 1; - /* try one time at this rate before falling back to "next" */ - mrr.rates[i].ntries = 1; - } - - /* OFDM rates (not used with 802.11b) */ - for (i = WPI_OFDM6; i <= WPI_OFDM54; i++) { - mrr.rates[i].flags = 0; - mrr.rates[i].signal = wpi_ridx_to_plcp[i]; - /* fallback to the immediate lower OFDM rate (if any) */ - /* we allow fallback from OFDM/6 to CCK/2 in 11b/g mode */ - mrr.rates[i].next = (i == WPI_OFDM6) ? - ((ic->ic_curmode == IEEE80211_MODE_11A) ? - WPI_OFDM6 : WPI_CCK2) : - i - 1; - /* try one time at this rate before falling back to "next" */ - mrr.rates[i].ntries = 1; - } - - /* setup MRR for control frames */ - mrr.which = WPI_MRR_CTL; - error = wpi_cmd(sc, WPI_CMD_MRR_SETUP, &mrr, sizeof mrr, 0); - if (error != 0) { - device_printf(sc->sc_dev, - "could not setup MRR for control frames\n"); - return error; - } - - /* setup MRR for data frames */ - mrr.which = WPI_MRR_DATA; - error = wpi_cmd(sc, WPI_CMD_MRR_SETUP, &mrr, sizeof mrr, 0); - if (error != 0) { - device_printf(sc->sc_dev, - "could not setup MRR for data frames\n"); - return error; - } - - return 0; -} - -static void -wpi_set_led(struct wpi_softc *sc, uint8_t which, uint8_t off, uint8_t on) -{ - struct wpi_cmd_led led; - - led.which = which; - led.unit = htole32(100000); /* on/off in unit of 100ms */ - led.off = off; - led.on = on; - - (void)wpi_cmd(sc, WPI_CMD_SET_LED, &led, sizeof led, 1); -} - -static void -wpi_enable_tsf(struct wpi_softc *sc, struct ieee80211_node *ni) -{ - struct wpi_cmd_tsf tsf; - uint64_t val, mod; - - memset(&tsf, 0, sizeof tsf); - memcpy(&tsf.tstamp, ni->ni_tstamp.data, 8); - tsf.bintval = htole16(ni->ni_intval); - tsf.lintval = htole16(10); - - /* compute remaining time until next beacon */ - val = (uint64_t)ni->ni_intval * 1024; /* msec -> usec */ - mod = le64toh(tsf.tstamp) % val; - tsf.binitval = htole32((uint32_t)(val - mod)); - - if (wpi_cmd(sc, WPI_CMD_TSF, &tsf, sizeof tsf, 1) != 0) - device_printf(sc->sc_dev, "could not enable TSF\n"); -} - -#if 0 -/* - * Build a beacon frame that the firmware will broadcast periodically in - * IBSS or HostAP modes. - */ -static int -wpi_setup_beacon(struct wpi_softc *sc, struct ieee80211_node *ni) -{ - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - struct wpi_tx_ring *ring = &sc->cmdq; - struct wpi_tx_desc *desc; - struct wpi_tx_data *data; - struct wpi_tx_cmd *cmd; - struct wpi_cmd_beacon *bcn; - struct ieee80211_beacon_offsets bo; - struct mbuf *m0; - bus_addr_t physaddr; - int error; - - desc = &ring->desc[ring->cur]; - data = &ring->data[ring->cur]; - - m0 = ieee80211_beacon_alloc(ic, ni, &bo); - if (m0 == NULL) { - device_printf(sc->sc_dev, "could not allocate beacon frame\n"); - return ENOMEM; - } - - cmd = &ring->cmd[ring->cur]; - cmd->code = WPI_CMD_SET_BEACON; - cmd->flags = 0; - cmd->qid = ring->qid; - cmd->idx = ring->cur; - - bcn = (struct wpi_cmd_beacon *)cmd->data; - memset(bcn, 0, sizeof (struct wpi_cmd_beacon)); - bcn->id = WPI_ID_BROADCAST; - bcn->ofdm_mask = 0xff; - bcn->cck_mask = 0x0f; - bcn->lifetime = htole32(WPI_LIFETIME_INFINITE); - bcn->len = htole16(m0->m_pkthdr.len); - bcn->rate = (ic->ic_curmode == IEEE80211_MODE_11A) ? - wpi_plcp_signal(12) : wpi_plcp_signal(2); - bcn->flags = htole32(WPI_TX_AUTO_SEQ | WPI_TX_INSERT_TSTAMP); - - /* save and trim IEEE802.11 header */ - m_copydata(m0, 0, sizeof (struct ieee80211_frame), (caddr_t)&bcn->wh); - m_adj(m0, sizeof (struct ieee80211_frame)); - - /* assume beacon frame is contiguous */ - error = bus_dmamap_load(ring->data_dmat, data->map, mtod(m0, void *), - m0->m_pkthdr.len, wpi_dma_map_addr, &physaddr, 0); - if (error != 0) { - device_printf(sc->sc_dev, "could not map beacon\n"); - m_freem(m0); - return error; - } - - data->m = m0; - - /* first scatter/gather segment is used by the beacon command */ - desc->flags = htole32(WPI_PAD32(m0->m_pkthdr.len) << 28 | 2 << 24); - desc->segs[0].addr = htole32(ring->cmd_dma.paddr + - ring->cur * sizeof (struct wpi_tx_cmd)); - desc->segs[0].len = htole32(4 + sizeof (struct wpi_cmd_beacon)); - desc->segs[1].addr = htole32(physaddr); - desc->segs[1].len = htole32(m0->m_pkthdr.len); - - /* kick cmd ring */ - ring->cur = (ring->cur + 1) % WPI_CMD_RING_COUNT; - WPI_WRITE(sc, WPI_TX_WIDX, ring->qid << 8 | ring->cur); - - return 0; -} -#endif - -static int -wpi_auth(struct wpi_softc *sc, struct ieee80211vap *vap) -{ - struct ieee80211com *ic = vap->iv_ic; - struct ieee80211_node *ni = vap->iv_bss; - struct wpi_node_info node; - int error; - - - /* update adapter's configuration */ - sc->config.associd = 0; - sc->config.filter &= ~htole32(WPI_FILTER_BSS); - IEEE80211_ADDR_COPY(sc->config.bssid, ni->ni_bssid); - sc->config.chan = ieee80211_chan2ieee(ic, ni->ni_chan); - if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) { - sc->config.flags |= htole32(WPI_CONFIG_AUTO | - WPI_CONFIG_24GHZ); - } else { - sc->config.flags &= ~htole32(WPI_CONFIG_AUTO | - WPI_CONFIG_24GHZ); - } - if (IEEE80211_IS_CHAN_A(ni->ni_chan)) { - sc->config.cck_mask = 0; - sc->config.ofdm_mask = 0x15; - } else if (IEEE80211_IS_CHAN_B(ni->ni_chan)) { - sc->config.cck_mask = 0x03; - sc->config.ofdm_mask = 0; - } else { - /* XXX assume 802.11b/g */ - sc->config.cck_mask = 0x0f; - sc->config.ofdm_mask = 0x15; - } - - DPRINTF(("config chan %d flags %x cck %x ofdm %x\n", sc->config.chan, - sc->config.flags, sc->config.cck_mask, sc->config.ofdm_mask)); - error = wpi_cmd(sc, WPI_CMD_CONFIGURE, &sc->config, - sizeof (struct wpi_config), 1); - if (error != 0) { - device_printf(sc->sc_dev, "could not configure\n"); - return error; - } - - /* configuration has changed, set Tx power accordingly */ - if ((error = wpi_set_txpower(sc, ni->ni_chan, 1)) != 0) { - device_printf(sc->sc_dev, "could not set Tx power\n"); - return error; - } - - /* add default node */ - memset(&node, 0, sizeof node); - IEEE80211_ADDR_COPY(node.bssid, ni->ni_bssid); - node.id = WPI_ID_BSS; - node.rate = (ic->ic_curmode == IEEE80211_MODE_11A) ? - wpi_plcp_signal(12) : wpi_plcp_signal(2); - node.action = htole32(WPI_ACTION_SET_RATE); - node.antenna = WPI_ANTENNA_BOTH; - error = wpi_cmd(sc, WPI_CMD_ADD_NODE, &node, sizeof node, 1); - if (error != 0) - device_printf(sc->sc_dev, "could not add BSS node\n"); - - return (error); -} - -static int -wpi_run(struct wpi_softc *sc, struct ieee80211vap *vap) -{ - struct ieee80211com *ic = vap->iv_ic; - struct ieee80211_node *ni = vap->iv_bss; - int error; - - if (vap->iv_opmode == IEEE80211_M_MONITOR) { - /* link LED blinks while monitoring */ - wpi_set_led(sc, WPI_LED_LINK, 5, 5); - return 0; - } - - wpi_enable_tsf(sc, ni); - - /* update adapter's configuration */ - sc->config.associd = htole16(ni->ni_associd & ~0xc000); - /* short preamble/slot time are negotiated when associating */ - sc->config.flags &= ~htole32(WPI_CONFIG_SHPREAMBLE | - WPI_CONFIG_SHSLOT); - if (ic->ic_flags & IEEE80211_F_SHSLOT) - sc->config.flags |= htole32(WPI_CONFIG_SHSLOT); - if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) - sc->config.flags |= htole32(WPI_CONFIG_SHPREAMBLE); - sc->config.filter |= htole32(WPI_FILTER_BSS); - - /* XXX put somewhere HC_QOS_SUPPORT_ASSOC + HC_IBSS_START */ - - DPRINTF(("config chan %d flags %x\n", sc->config.chan, - sc->config.flags)); - error = wpi_cmd(sc, WPI_CMD_CONFIGURE, &sc->config, sizeof (struct - wpi_config), 1); - if (error != 0) { - device_printf(sc->sc_dev, "could not update configuration\n"); - return error; - } - - error = wpi_set_txpower(sc, ni->ni_chan, 1); - if (error != 0) { - device_printf(sc->sc_dev, "could set txpower\n"); - return error; - } - - /* link LED always on while associated */ - wpi_set_led(sc, WPI_LED_LINK, 0, 1); - - /* start automatic rate control timer */ - callout_reset(&sc->calib_to, 60*hz, wpi_calib_timeout, sc); - - return (error); -} - -/* - * Send a scan request to the firmware. Since this command is huge, we map it - * into a mbufcluster instead of using the pre-allocated set of commands. Note, - * much of this code is similar to that in wpi_cmd but because we must manually - * construct the probe & channels, we duplicate what's needed here. XXX In the - * future, this function should be modified to use wpi_cmd to help cleanup the - * code base. - */ -static int -wpi_scan(struct wpi_softc *sc) -{ - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - struct ieee80211_scan_state *ss = ic->ic_scan; - struct wpi_tx_ring *ring = &sc->cmdq; - struct wpi_tx_desc *desc; - struct wpi_tx_data *data; - struct wpi_tx_cmd *cmd; - struct wpi_scan_hdr *hdr; - struct wpi_scan_chan *chan; - struct ieee80211_frame *wh; - struct ieee80211_rateset *rs; - struct ieee80211_channel *c; - enum ieee80211_phymode mode; - uint8_t *frm; - int pktlen, error, i, nssid; - bus_addr_t physaddr; - - desc = &ring->desc[ring->cur]; - data = &ring->data[ring->cur]; - - data->m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); - if (data->m == NULL) { - device_printf(sc->sc_dev, - "could not allocate mbuf for scan command\n"); - return ENOMEM; - } - - cmd = mtod(data->m, struct wpi_tx_cmd *); - cmd->code = WPI_CMD_SCAN; - cmd->flags = 0; - cmd->qid = ring->qid; - cmd->idx = ring->cur; - - hdr = (struct wpi_scan_hdr *)cmd->data; - memset(hdr, 0, sizeof(struct wpi_scan_hdr)); - - /* - * Move to the next channel if no packets are received within 5 msecs - * after sending the probe request (this helps to reduce the duration - * of active scans). - */ - hdr->quiet = htole16(5); - hdr->threshold = htole16(1); - - if (IEEE80211_IS_CHAN_A(ic->ic_curchan)) { - /* send probe requests at 6Mbps */ - hdr->tx.rate = wpi_ridx_to_plcp[WPI_OFDM6]; - - /* Enable crc checking */ - hdr->promotion = htole16(1); - } else { - hdr->flags = htole32(WPI_CONFIG_24GHZ | WPI_CONFIG_AUTO); - /* send probe requests at 1Mbps */ - hdr->tx.rate = wpi_ridx_to_plcp[WPI_CCK1]; - } - hdr->tx.id = WPI_ID_BROADCAST; - hdr->tx.lifetime = htole32(WPI_LIFETIME_INFINITE); - hdr->tx.flags = htole32(WPI_TX_AUTO_SEQ); - - memset(hdr->scan_essids, 0, sizeof(hdr->scan_essids)); - nssid = MIN(ss->ss_nssid, WPI_SCAN_MAX_ESSIDS); - for (i = 0; i < nssid; i++) { - hdr->scan_essids[i].id = IEEE80211_ELEMID_SSID; - hdr->scan_essids[i].esslen = MIN(ss->ss_ssid[i].len, IEEE80211_NWID_LEN); - memcpy(hdr->scan_essids[i].essid, ss->ss_ssid[i].ssid, - hdr->scan_essids[i].esslen); -#ifdef WPI_DEBUG - if (wpi_debug & WPI_DEBUG_SCANNING) { - kprintf("Scanning Essid: "); - ieee80211_print_essid(hdr->scan_essids[i].essid, - hdr->scan_essids[i].esslen); - kprintf("\n"); - } -#endif - } - - /* - * Build a probe request frame. Most of the following code is a - * copy & paste of what is done in net80211. - */ - wh = (struct ieee80211_frame *)&hdr->scan_essids[WPI_SCAN_MAX_ESSIDS]; - wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | - IEEE80211_FC0_SUBTYPE_PROBE_REQ; - wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; - IEEE80211_ADDR_COPY(wh->i_addr1, ifp->if_broadcastaddr); - IEEE80211_ADDR_COPY(wh->i_addr2, IF_LLADDR(ifp)); - IEEE80211_ADDR_COPY(wh->i_addr3, ifp->if_broadcastaddr); - *(u_int16_t *)&wh->i_dur[0] = 0; /* filled by h/w */ - *(u_int16_t *)&wh->i_seq[0] = 0; /* filled by h/w */ - - frm = (uint8_t *)(wh + 1); - - mode = ieee80211_chan2mode(ic->ic_curchan); - rs = &ic->ic_sup_rates[mode]; - - frm = ieee80211_add_ssid(frm, NULL, 0); - frm = ieee80211_add_rates(frm, rs); - frm = ieee80211_add_xrates(frm, rs); - - /* setup length of probe request */ - hdr->tx.len = htole16(frm - (uint8_t *)wh); - - /* - * Construct information about the channel that we - * want to scan. The firmware expects this to be directly - * after the scan probe request - */ - c = ic->ic_curchan; - chan = (struct wpi_scan_chan *)frm; - chan->chan = ieee80211_chan2ieee(ic, c); - chan->flags = 0; - if (!(c->ic_flags & IEEE80211_CHAN_PASSIVE)) { - chan->flags |= WPI_CHAN_ACTIVE; - if (nssid != 0) - chan->flags |= WPI_CHAN_DIRECT; - } - chan->gain_dsp = 0x6e; /* Default level */ - if (IEEE80211_IS_CHAN_5GHZ(c)) { - chan->active = htole16(10); - chan->passive = htole16(ss->ss_maxdwell); - chan->gain_radio = 0x3b; - } else { - chan->active = htole16(20); - chan->passive = htole16(ss->ss_maxdwell); - chan->gain_radio = 0x28; - } - - DPRINTFN(WPI_DEBUG_SCANNING, - ("Scanning %u Passive: %d\n", - chan->chan, - c->ic_flags & IEEE80211_CHAN_PASSIVE)); - - hdr->nchan++; - chan++; - - frm += sizeof (struct wpi_scan_chan); -#if 0 - // XXX All Channels.... - for (c = &ic->ic_channels[1]; - c <= &ic->ic_channels[IEEE80211_CHAN_MAX]; c++) { - if ((c->ic_flags & ic->ic_curchan->ic_flags) != ic->ic_curchan->ic_flags) - continue; - - chan->chan = ieee80211_chan2ieee(ic, c); - chan->flags = 0; - if (!(c->ic_flags & IEEE80211_CHAN_PASSIVE)) { - chan->flags |= WPI_CHAN_ACTIVE; - if (ic->ic_des_ssid[0].len != 0) - chan->flags |= WPI_CHAN_DIRECT; - } - chan->gain_dsp = 0x6e; /* Default level */ - if (IEEE80211_IS_CHAN_5GHZ(c)) { - chan->active = htole16(10); - chan->passive = htole16(110); - chan->gain_radio = 0x3b; - } else { - chan->active = htole16(20); - chan->passive = htole16(120); - chan->gain_radio = 0x28; - } - - DPRINTFN(WPI_DEBUG_SCANNING, - ("Scanning %u Passive: %d\n", - chan->chan, - c->ic_flags & IEEE80211_CHAN_PASSIVE)); - - hdr->nchan++; - chan++; - - frm += sizeof (struct wpi_scan_chan); - } -#endif - - hdr->len = htole16(frm - (uint8_t *)hdr); - pktlen = frm - (uint8_t *)cmd; - - error = bus_dmamap_load(ring->data_dmat, data->map, cmd, pktlen, - wpi_dma_map_addr, &physaddr, BUS_DMA_NOWAIT); - if (error != 0) { - device_printf(sc->sc_dev, "could not map scan command\n"); - m_freem(data->m); - data->m = NULL; - return error; - } - - desc->flags = htole32(WPI_PAD32(pktlen) << 28 | 1 << 24); - desc->segs[0].addr = htole32(physaddr); - desc->segs[0].len = htole32(pktlen); - - bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, - BUS_DMASYNC_PREWRITE); - bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE); - - /* kick cmd ring */ - ring->cur = (ring->cur + 1) % WPI_CMD_RING_COUNT; - WPI_WRITE(sc, WPI_TX_WIDX, ring->qid << 8 | ring->cur); - - sc->sc_scan_timer = 5; - return 0; /* will be notified async. of failure/success */ -} - -/** - * Configure the card to listen to a particular channel, this transisions the - * card in to being able to receive frames from remote devices. - */ -static int -wpi_config(struct wpi_softc *sc) -{ - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - struct wpi_power power; - struct wpi_bluetooth bluetooth; - struct wpi_node_info node; - int error; - - /* set power mode */ - memset(&power, 0, sizeof power); - power.flags = htole32(WPI_POWER_CAM|0x8); - error = wpi_cmd(sc, WPI_CMD_SET_POWER_MODE, &power, sizeof power, 0); - if (error != 0) { - device_printf(sc->sc_dev, "could not set power mode\n"); - return error; - } - - /* configure bluetooth coexistence */ - memset(&bluetooth, 0, sizeof bluetooth); - bluetooth.flags = 3; - bluetooth.lead = 0xaa; - bluetooth.kill = 1; - error = wpi_cmd(sc, WPI_CMD_BLUETOOTH, &bluetooth, sizeof bluetooth, - 0); - if (error != 0) { - device_printf(sc->sc_dev, - "could not configure bluetooth coexistence\n"); - return error; - } - - /* configure adapter */ - memset(&sc->config, 0, sizeof (struct wpi_config)); - IEEE80211_ADDR_COPY(sc->config.myaddr, IF_LLADDR(ifp)); - /*set default channel*/ - sc->config.chan = htole16(ieee80211_chan2ieee(ic, ic->ic_curchan)); - sc->config.flags = htole32(WPI_CONFIG_TSF); - if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) { - sc->config.flags |= htole32(WPI_CONFIG_AUTO | - WPI_CONFIG_24GHZ); - } - sc->config.filter = 0; - switch (ic->ic_opmode) { - case IEEE80211_M_STA: - case IEEE80211_M_WDS: /* No know setup, use STA for now */ - sc->config.mode = WPI_MODE_STA; - sc->config.filter |= htole32(WPI_FILTER_MULTICAST); - break; - case IEEE80211_M_IBSS: - case IEEE80211_M_AHDEMO: - sc->config.mode = WPI_MODE_IBSS; - sc->config.filter |= htole32(WPI_FILTER_BEACON | - WPI_FILTER_MULTICAST); - break; - case IEEE80211_M_HOSTAP: - sc->config.mode = WPI_MODE_HOSTAP; - break; - case IEEE80211_M_MONITOR: - sc->config.mode = WPI_MODE_MONITOR; - sc->config.filter |= htole32(WPI_FILTER_MULTICAST | - WPI_FILTER_CTL | WPI_FILTER_PROMISC); - break; - default: - device_printf(sc->sc_dev, "unknown opmode %d\n", ic->ic_opmode); - return EINVAL; - } - sc->config.cck_mask = 0x0f; /* not yet negotiated */ - sc->config.ofdm_mask = 0xff; /* not yet negotiated */ - error = wpi_cmd(sc, WPI_CMD_CONFIGURE, &sc->config, - sizeof (struct wpi_config), 0); - if (error != 0) { - device_printf(sc->sc_dev, "configure command failed\n"); - return error; - } - - /* configuration has changed, set Tx power accordingly */ - if ((error = wpi_set_txpower(sc, ic->ic_curchan, 0)) != 0) { - device_printf(sc->sc_dev, "could not set Tx power\n"); - return error; - } - - /* add broadcast node */ - memset(&node, 0, sizeof node); - IEEE80211_ADDR_COPY(node.bssid, ifp->if_broadcastaddr); - node.id = WPI_ID_BROADCAST; - node.rate = wpi_plcp_signal(2); - error = wpi_cmd(sc, WPI_CMD_ADD_NODE, &node, sizeof node, 0); - if (error != 0) { - device_printf(sc->sc_dev, "could not add broadcast node\n"); - return error; - } - - /* Setup rate scalling */ - error = wpi_mrr_setup(sc); - if (error != 0) { - device_printf(sc->sc_dev, "could not setup MRR\n"); - return error; - } - - return 0; -} - -static void -wpi_stop_master(struct wpi_softc *sc) -{ - uint32_t tmp; - int ntries; - - DPRINTFN(WPI_DEBUG_HW,("Disabling Firmware execution\n")); - - tmp = WPI_READ(sc, WPI_RESET); - WPI_WRITE(sc, WPI_RESET, tmp | WPI_STOP_MASTER | WPI_NEVO_RESET); - - tmp = WPI_READ(sc, WPI_GPIO_CTL); - if ((tmp & WPI_GPIO_PWR_STATUS) == WPI_GPIO_PWR_SLEEP) - return; /* already asleep */ - - for (ntries = 0; ntries < 100; ntries++) { - if (WPI_READ(sc, WPI_RESET) & WPI_MASTER_DISABLED) - break; - DELAY(10); - } - if (ntries == 100) { - device_printf(sc->sc_dev, "timeout waiting for master\n"); - } -} - -static int -wpi_power_up(struct wpi_softc *sc) -{ - uint32_t tmp; - int ntries; - - wpi_mem_lock(sc); - tmp = wpi_mem_read(sc, WPI_MEM_POWER); - wpi_mem_write(sc, WPI_MEM_POWER, tmp & ~0x03000000); - wpi_mem_unlock(sc); - - for (ntries = 0; ntries < 5000; ntries++) { - if (WPI_READ(sc, WPI_GPIO_STATUS) & WPI_POWERED) - break; - DELAY(10); - } - if (ntries == 5000) { - device_printf(sc->sc_dev, - "timeout waiting for NIC to power up\n"); - return ETIMEDOUT; - } - return 0; -} - -static int -wpi_reset(struct wpi_softc *sc) -{ - uint32_t tmp; - int ntries; - - DPRINTFN(WPI_DEBUG_HW, - ("Resetting the card - clearing any uploaded firmware\n")); - - /* clear any pending interrupts */ - WPI_WRITE(sc, WPI_INTR, 0xffffffff); - - tmp = WPI_READ(sc, WPI_PLL_CTL); - WPI_WRITE(sc, WPI_PLL_CTL, tmp | WPI_PLL_INIT); - - tmp = WPI_READ(sc, WPI_CHICKEN); - WPI_WRITE(sc, WPI_CHICKEN, tmp | WPI_CHICKEN_RXNOLOS); - - tmp = WPI_READ(sc, WPI_GPIO_CTL); - WPI_WRITE(sc, WPI_GPIO_CTL, tmp | WPI_GPIO_INIT); - - /* wait for clock stabilization */ - for (ntries = 0; ntries < 25000; ntries++) { - if (WPI_READ(sc, WPI_GPIO_CTL) & WPI_GPIO_CLOCK) - break; - DELAY(10); - } - if (ntries == 25000) { - device_printf(sc->sc_dev, - "timeout waiting for clock stabilization\n"); - return ETIMEDOUT; - } - - /* initialize EEPROM */ - tmp = WPI_READ(sc, WPI_EEPROM_STATUS); - - if ((tmp & WPI_EEPROM_VERSION) == 0) { - device_printf(sc->sc_dev, "EEPROM not found\n"); - return EIO; - } - WPI_WRITE(sc, WPI_EEPROM_STATUS, tmp & ~WPI_EEPROM_LOCKED); - - return 0; -} - -static void -wpi_hw_config(struct wpi_softc *sc) -{ - uint32_t rev, hw; - - /* voodoo from the Linux "driver".. */ - hw = WPI_READ(sc, WPI_HWCONFIG); - - rev = pci_read_config(sc->sc_dev, PCIR_REVID, 1); - if ((rev & 0xc0) == 0x40) - hw |= WPI_HW_ALM_MB; - else if (!(rev & 0x80)) - hw |= WPI_HW_ALM_MM; - - if (sc->cap == 0x80) - hw |= WPI_HW_SKU_MRC; - - hw &= ~WPI_HW_REV_D; - if ((le16toh(sc->rev) & 0xf0) == 0xd0) - hw |= WPI_HW_REV_D; - - if (sc->type > 1) - hw |= WPI_HW_TYPE_B; - - WPI_WRITE(sc, WPI_HWCONFIG, hw); -} - -static void -wpi_rfkill_resume(struct wpi_softc *sc) -{ - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); - int ntries; - - /* enable firmware again */ - WPI_WRITE(sc, WPI_UCODE_CLR, WPI_RADIO_OFF); - WPI_WRITE(sc, WPI_UCODE_CLR, WPI_DISABLE_CMD); - - /* wait for thermal sensors to calibrate */ - for (ntries = 0; ntries < 1000; ntries++) { - if ((sc->temp = (int)WPI_READ(sc, WPI_TEMPERATURE)) != 0) - break; - DELAY(10); - } - - if (ntries == 1000) { - device_printf(sc->sc_dev, - "timeout waiting for thermal calibration\n"); - return; - } - DPRINTFN(WPI_DEBUG_TEMP,("temperature %d\n", sc->temp)); - - if (wpi_config(sc) != 0) { - device_printf(sc->sc_dev, "device config failed\n"); - return; - } - -#if defined(__DragonFly__) - ifq_clr_oactive(&ifp->if_snd); - ifp->if_flags |= IFF_RUNNING; -#else - ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; - ifp->if_drv_flags |= IFF_DRV_RUNNING; -#endif - sc->flags &= ~WPI_FLAG_HW_RADIO_OFF; - - if (vap != NULL) { - if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { - if (vap->iv_opmode != IEEE80211_M_MONITOR) { - ieee80211_beacon_miss(ic); - wpi_set_led(sc, WPI_LED_LINK, 0, 1); - } else - wpi_set_led(sc, WPI_LED_LINK, 5, 5); - } else { - ieee80211_scan_next(vap); - wpi_set_led(sc, WPI_LED_LINK, 20, 2); - } - } - - callout_reset(&sc->watchdog_to, hz, wpi_watchdog, sc); -} - -static void -wpi_init_locked(struct wpi_softc *sc, int force) -{ - struct ifnet *ifp = sc->sc_ifp; - uint32_t tmp; - int ntries, qid; - - wpi_stop_locked(sc); - (void)wpi_reset(sc); - - wpi_mem_lock(sc); - wpi_mem_write(sc, WPI_MEM_CLOCK1, 0xa00); - DELAY(20); - tmp = wpi_mem_read(sc, WPI_MEM_PCIDEV); - wpi_mem_write(sc, WPI_MEM_PCIDEV, tmp | 0x800); - wpi_mem_unlock(sc); - - (void)wpi_power_up(sc); - wpi_hw_config(sc); - - /* init Rx ring */ - wpi_mem_lock(sc); - WPI_WRITE(sc, WPI_RX_BASE, sc->rxq.desc_dma.paddr); - WPI_WRITE(sc, WPI_RX_RIDX_PTR, sc->shared_dma.paddr + - offsetof(struct wpi_shared, next)); - WPI_WRITE(sc, WPI_RX_WIDX, (WPI_RX_RING_COUNT - 1) & ~7); - WPI_WRITE(sc, WPI_RX_CONFIG, 0xa9601010); - wpi_mem_unlock(sc); - - /* init Tx rings */ - wpi_mem_lock(sc); - wpi_mem_write(sc, WPI_MEM_MODE, 2); /* bypass mode */ - wpi_mem_write(sc, WPI_MEM_RA, 1); /* enable RA0 */ - wpi_mem_write(sc, WPI_MEM_TXCFG, 0x3f); /* enable all 6 Tx rings */ - wpi_mem_write(sc, WPI_MEM_BYPASS1, 0x10000); - wpi_mem_write(sc, WPI_MEM_BYPASS2, 0x30002); - wpi_mem_write(sc, WPI_MEM_MAGIC4, 4); - wpi_mem_write(sc, WPI_MEM_MAGIC5, 5); - - WPI_WRITE(sc, WPI_TX_BASE_PTR, sc->shared_dma.paddr); - WPI_WRITE(sc, WPI_MSG_CONFIG, 0xffff05a5); - - for (qid = 0; qid < 6; qid++) { - WPI_WRITE(sc, WPI_TX_CTL(qid), 0); - WPI_WRITE(sc, WPI_TX_BASE(qid), 0); - WPI_WRITE(sc, WPI_TX_CONFIG(qid), 0x80200008); - } - wpi_mem_unlock(sc); - - /* clear "radio off" and "disable command" bits (reversed logic) */ - WPI_WRITE(sc, WPI_UCODE_CLR, WPI_RADIO_OFF); - WPI_WRITE(sc, WPI_UCODE_CLR, WPI_DISABLE_CMD); - sc->flags &= ~WPI_FLAG_HW_RADIO_OFF; - - /* clear any pending interrupts */ - WPI_WRITE(sc, WPI_INTR, 0xffffffff); - - /* enable interrupts */ - WPI_WRITE(sc, WPI_MASK, WPI_INTR_MASK); - - WPI_WRITE(sc, WPI_UCODE_CLR, WPI_RADIO_OFF); - WPI_WRITE(sc, WPI_UCODE_CLR, WPI_RADIO_OFF); - - if ((wpi_load_firmware(sc)) != 0) { - device_printf(sc->sc_dev, - "A problem occurred loading the firmware to the driver\n"); - return; - } - - /* At this point the firmware is up and running. If the hardware - * RF switch is turned off thermal calibration will fail, though - * the card is still happy to continue to accept commands, catch - * this case and schedule a task to watch for it to be turned on. - */ - wpi_mem_lock(sc); - tmp = wpi_mem_read(sc, WPI_MEM_HW_RADIO_OFF); - wpi_mem_unlock(sc); - - if (!(tmp & 0x1)) { - sc->flags |= WPI_FLAG_HW_RADIO_OFF; - device_printf(sc->sc_dev,"Radio Transmitter is switched off\n"); - goto out; - } - - /* wait for thermal sensors to calibrate */ - for (ntries = 0; ntries < 1000; ntries++) { - if ((sc->temp = (int)WPI_READ(sc, WPI_TEMPERATURE)) != 0) - break; - DELAY(10); - } - - if (ntries == 1000) { - device_printf(sc->sc_dev, - "timeout waiting for thermal sensors calibration\n"); - return; - } - DPRINTFN(WPI_DEBUG_TEMP,("temperature %d\n", sc->temp)); - - if (wpi_config(sc) != 0) { - device_printf(sc->sc_dev, "device config failed\n"); - return; - } - -#if defined(__DragonFly__) - ifq_clr_oactive(&ifp->if_snd); - ifp->if_flags |= IFF_RUNNING; -#else - ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; - ifp->if_drv_flags |= IFF_DRV_RUNNING; -#endif -out: - callout_reset(&sc->watchdog_to, hz, wpi_watchdog, sc); -} - -static void -wpi_init(void *arg) -{ - struct wpi_softc *sc = arg; - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - - WPI_LOCK(sc); - wpi_init_locked(sc, 0); - WPI_UNLOCK(sc); - -#if defined(__DragonFly__) - if (ifp->if_flags & IFF_RUNNING) - ieee80211_start_all(ic); /* start all vaps */ -#else - if (ifp->if_drv_flags & IFF_DRV_RUNNING) - ieee80211_start_all(ic); /* start all vaps */ -#endif -} - -static void -wpi_stop_locked(struct wpi_softc *sc) -{ - struct ifnet *ifp = sc->sc_ifp; - uint32_t tmp; - int ac; - - sc->sc_tx_timer = 0; - sc->sc_scan_timer = 0; -#if defined(__DragonFly__) - ifq_clr_oactive(&ifp->if_snd); - ifp->if_flags &= ~IFF_RUNNING; -#else - ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); -#endif - sc->flags &= ~WPI_FLAG_HW_RADIO_OFF; - callout_stop_sync(&sc->watchdog_to); - callout_stop_sync(&sc->calib_to); - - /* disable interrupts */ - WPI_WRITE(sc, WPI_MASK, 0); - WPI_WRITE(sc, WPI_INTR, WPI_INTR_MASK); - WPI_WRITE(sc, WPI_INTR_STATUS, 0xff); - WPI_WRITE(sc, WPI_INTR_STATUS, 0x00070000); - - wpi_mem_lock(sc); - wpi_mem_write(sc, WPI_MEM_MODE, 0); - wpi_mem_unlock(sc); - - /* reset all Tx rings */ - for (ac = 0; ac < 4; ac++) - wpi_reset_tx_ring(sc, &sc->txq[ac]); - wpi_reset_tx_ring(sc, &sc->cmdq); - - /* reset Rx ring */ - wpi_reset_rx_ring(sc, &sc->rxq); - - wpi_mem_lock(sc); - wpi_mem_write(sc, WPI_MEM_CLOCK2, 0x200); - wpi_mem_unlock(sc); - - DELAY(5); - - wpi_stop_master(sc); - - tmp = WPI_READ(sc, WPI_RESET); - WPI_WRITE(sc, WPI_RESET, tmp | WPI_SW_RESET); - sc->flags &= ~WPI_FLAG_BUSY; -} - -static void -wpi_stop(struct wpi_softc *sc) -{ - WPI_LOCK(sc); - wpi_stop_locked(sc); - WPI_UNLOCK(sc); -} - -static void -wpi_calib_timeout(void *arg) -{ - struct wpi_softc *sc = arg; - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); - int temp; - - if (vap->iv_state != IEEE80211_S_RUN) - return; - - /* update sensor data */ - temp = (int)WPI_READ(sc, WPI_TEMPERATURE); - DPRINTFN(WPI_DEBUG_TEMP,("Temp in calibration is: %d\n", temp)); - - wpi_power_calibration(sc, temp); - - callout_reset(&sc->calib_to, 60*hz, wpi_calib_timeout, sc); -} - -/* - * This function is called periodically (every 60 seconds) to adjust output - * power to temperature changes. - */ -static void -wpi_power_calibration(struct wpi_softc *sc, int temp) -{ - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); - - /* sanity-check read value */ - if (temp < -260 || temp > 25) { - /* this can't be correct, ignore */ - DPRINTFN(WPI_DEBUG_TEMP, - ("out-of-range temperature reported: %d\n", temp)); - return; - } - - DPRINTFN(WPI_DEBUG_TEMP,("temperature %d->%d\n", sc->temp, temp)); - - /* adjust Tx power if need be */ - if (abs(temp - sc->temp) <= 6) - return; - - sc->temp = temp; - - if (wpi_set_txpower(sc, vap->iv_bss->ni_chan, 1) != 0) { - /* just warn, too bad for the automatic calibration... */ - device_printf(sc->sc_dev,"could not adjust Tx power\n"); - } -} - -/** - * Read the eeprom to find out what channels are valid for the given - * band and update net80211 with what we find. - */ -static void -wpi_read_eeprom_channels(struct wpi_softc *sc, int n) -{ - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - const struct wpi_chan_band *band = &wpi_bands[n]; - struct wpi_eeprom_chan channels[WPI_MAX_CHAN_PER_BAND]; - struct ieee80211_channel *c; - int chan, i, passive; - - wpi_read_prom_data(sc, band->addr, channels, - band->nchan * sizeof (struct wpi_eeprom_chan)); - - for (i = 0; i < band->nchan; i++) { - if (!(channels[i].flags & WPI_EEPROM_CHAN_VALID)) { - DPRINTFN(WPI_DEBUG_HW, - ("Channel Not Valid: %d, band %d\n", - band->chan[i],n)); - continue; - } - - passive = 0; - chan = band->chan[i]; - c = &ic->ic_channels[ic->ic_nchans++]; - - /* is active scan allowed on this channel? */ - if (!(channels[i].flags & WPI_EEPROM_CHAN_ACTIVE)) { - passive = IEEE80211_CHAN_PASSIVE; - } - - if (n == 0) { /* 2GHz band */ - c->ic_ieee = chan; - c->ic_freq = ieee80211_ieee2mhz(chan, - IEEE80211_CHAN_2GHZ); - c->ic_flags = IEEE80211_CHAN_B | passive; - - c = &ic->ic_channels[ic->ic_nchans++]; - c->ic_ieee = chan; - c->ic_freq = ieee80211_ieee2mhz(chan, - IEEE80211_CHAN_2GHZ); - c->ic_flags = IEEE80211_CHAN_G | passive; - - } else { /* 5GHz band */ - /* - * Some 3945ABG adapters support channels 7, 8, 11 - * and 12 in the 2GHz *and* 5GHz bands. - * Because of limitations in our net80211(9) stack, - * we can't support these channels in 5GHz band. - * XXX not true; just need to map to proper frequency - */ - if (chan <= 14) - continue; - - c->ic_ieee = chan; - c->ic_freq = ieee80211_ieee2mhz(chan, - IEEE80211_CHAN_5GHZ); - c->ic_flags = IEEE80211_CHAN_A | passive; - } - - /* save maximum allowed power for this channel */ - sc->maxpwr[chan] = channels[i].maxpwr; - -#if 0 - // XXX We can probably use this an get rid of maxpwr - ben 20070617 - ic->ic_channels[chan].ic_maxpower = channels[i].maxpwr; - //ic->ic_channels[chan].ic_minpower... - //ic->ic_channels[chan].ic_maxregtxpower... -#endif - - DPRINTF(("adding chan %d (%dMHz) flags=0x%x maxpwr=%d" - " passive=%d, offset %d\n", chan, c->ic_freq, - channels[i].flags, sc->maxpwr[chan], - (c->ic_flags & IEEE80211_CHAN_PASSIVE) != 0, - ic->ic_nchans)); - } -} - -static void -wpi_read_eeprom_group(struct wpi_softc *sc, int n) -{ - struct wpi_power_group *group = &sc->groups[n]; - struct wpi_eeprom_group rgroup; - int i; - - wpi_read_prom_data(sc, WPI_EEPROM_POWER_GRP + n * 32, &rgroup, - sizeof rgroup); - - /* save power group information */ - group->chan = rgroup.chan; - group->maxpwr = rgroup.maxpwr; - /* temperature at which the samples were taken */ - group->temp = (int16_t)le16toh(rgroup.temp); - - DPRINTF(("power group %d: chan=%d maxpwr=%d temp=%d\n", n, - group->chan, group->maxpwr, group->temp)); - - for (i = 0; i < WPI_SAMPLES_COUNT; i++) { - group->samples[i].index = rgroup.samples[i].index; - group->samples[i].power = rgroup.samples[i].power; - - DPRINTF(("\tsample %d: index=%d power=%d\n", i, - group->samples[i].index, group->samples[i].power)); - } -} - -/* - * Update Tx power to match what is defined for channel `c'. - */ -static int -wpi_set_txpower(struct wpi_softc *sc, struct ieee80211_channel *c, int async) -{ - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - struct wpi_power_group *group; - struct wpi_cmd_txpower txpower; - u_int chan; - int i; - - /* get channel number */ - chan = ieee80211_chan2ieee(ic, c); - - /* find the power group to which this channel belongs */ - if (IEEE80211_IS_CHAN_5GHZ(c)) { - for (group = &sc->groups[1]; group < &sc->groups[4]; group++) - if (chan <= group->chan) - break; - } else - group = &sc->groups[0]; - - memset(&txpower, 0, sizeof txpower); - txpower.band = IEEE80211_IS_CHAN_5GHZ(c) ? 0 : 1; - txpower.channel = htole16(chan); - - /* set Tx power for all OFDM and CCK rates */ - for (i = 0; i <= 11 ; i++) { - /* retrieve Tx power for this channel/rate combination */ - int idx = wpi_get_power_index(sc, group, c, - wpi_ridx_to_rate[i]); - - txpower.rates[i].rate = wpi_ridx_to_plcp[i]; - - if (IEEE80211_IS_CHAN_5GHZ(c)) { - txpower.rates[i].gain_radio = wpi_rf_gain_5ghz[idx]; - txpower.rates[i].gain_dsp = wpi_dsp_gain_5ghz[idx]; - } else { - txpower.rates[i].gain_radio = wpi_rf_gain_2ghz[idx]; - txpower.rates[i].gain_dsp = wpi_dsp_gain_2ghz[idx]; - } - DPRINTFN(WPI_DEBUG_TEMP,("chan %d/rate %d: power index %d\n", - chan, wpi_ridx_to_rate[i], idx)); - } - - return wpi_cmd(sc, WPI_CMD_TXPOWER, &txpower, sizeof txpower, async); -} - -/* - * Determine Tx power index for a given channel/rate combination. - * This takes into account the regulatory information from EEPROM and the - * current temperature. - */ -static int -wpi_get_power_index(struct wpi_softc *sc, struct wpi_power_group *group, - struct ieee80211_channel *c, int rate) -{ -/* fixed-point arithmetic division using a n-bit fractional part */ -#define fdivround(a, b, n) \ - ((((1 << n) * (a)) / (b) + (1 << n) / 2) / (1 << n)) - -/* linear interpolation */ -#define interpolate(x, x1, y1, x2, y2, n) \ - ((y1) + fdivround(((x) - (x1)) * ((y2) - (y1)), (x2) - (x1), n)) - - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - struct wpi_power_sample *sample; - int pwr, idx; - u_int chan; - - /* get channel number */ - chan = ieee80211_chan2ieee(ic, c); - - /* default power is group's maximum power - 3dB */ - pwr = group->maxpwr / 2; - - /* decrease power for highest OFDM rates to reduce distortion */ - switch (rate) { - case 72: /* 36Mb/s */ - pwr -= IEEE80211_IS_CHAN_2GHZ(c) ? 0 : 5; - break; - case 96: /* 48Mb/s */ - pwr -= IEEE80211_IS_CHAN_2GHZ(c) ? 7 : 10; - break; - case 108: /* 54Mb/s */ - pwr -= IEEE80211_IS_CHAN_2GHZ(c) ? 9 : 12; - break; - } - - /* never exceed channel's maximum allowed Tx power */ - pwr = min(pwr, sc->maxpwr[chan]); - - /* retrieve power index into gain tables from samples */ - for (sample = group->samples; sample < &group->samples[3]; sample++) - if (pwr > sample[1].power) - break; - /* fixed-point linear interpolation using a 19-bit fractional part */ - idx = interpolate(pwr, sample[0].power, sample[0].index, - sample[1].power, sample[1].index, 19); - - /* - * Adjust power index based on current temperature - * - if colder than factory-calibrated: decreate output power - * - if warmer than factory-calibrated: increase output power - */ - idx -= (sc->temp - group->temp) * 11 / 100; - - /* decrease power for CCK rates (-5dB) */ - if (!WPI_RATE_IS_OFDM(rate)) - idx += 10; - - /* keep power index in a valid range */ - if (idx < 0) - return 0; - if (idx > WPI_MAX_PWR_INDEX) - return WPI_MAX_PWR_INDEX; - return idx; - -#undef interpolate -#undef fdivround -} - -/** - * Called by net80211 framework to indicate that a scan - * is starting. This function doesn't actually do the scan, - * wpi_scan_curchan starts things off. This function is more - * of an early warning from the framework we should get ready - * for the scan. - */ -static void -wpi_scan_start(struct ieee80211com *ic) -{ - struct wpi_softc *sc = ic->ic_softc; - - WPI_LOCK(sc); - wpi_set_led(sc, WPI_LED_LINK, 20, 2); - WPI_UNLOCK(sc); -} - -/** - * Called by the net80211 framework, indicates that the - * scan has ended. If there is a scan in progress on the card - * then it should be aborted. - */ -static void -wpi_scan_end(struct ieee80211com *ic) -{ - /* XXX ignore */ -} - -/** - * Called by the net80211 framework to indicate to the driver - * that the channel should be changed - */ -static void -wpi_set_channel(struct ieee80211com *ic) -{ - struct wpi_softc *sc = ic->ic_softc; - int error; - - /* - * Only need to set the channel in Monitor mode. AP scanning and auth - * are already taken care of by their respective firmware commands. - */ - if (ic->ic_opmode == IEEE80211_M_MONITOR) { - WPI_LOCK(sc); - error = wpi_config(sc); - WPI_UNLOCK(sc); - if (error != 0) - device_printf(sc->sc_dev, - "error %d settting channel\n", error); - } -} - -/** - * Called by net80211 to indicate that we need to scan the current - * channel. The channel is previously be set via the wpi_set_channel - * callback. - */ -static void -wpi_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell) -{ - struct ieee80211vap *vap = ss->ss_vap; - struct ieee80211com *ic = vap->iv_ic; - struct wpi_softc *sc = ic->ic_softc; - - WPI_LOCK(sc); - if (wpi_scan(sc)) - ieee80211_cancel_scan(vap); - WPI_UNLOCK(sc); -} - -/** - * Called by the net80211 framework to indicate - * the minimum dwell time has been met, terminate the scan. - * We don't actually terminate the scan as the firmware will notify - * us when it's finished and we have no way to interrupt it. - */ -static void -wpi_scan_mindwell(struct ieee80211_scan_state *ss) -{ - /* NB: don't try to abort scan; wait for firmware to finish */ -} - -static void -wpi_hwreset(void *arg, int pending) -{ - struct wpi_softc *sc = arg; - - WPI_LOCK(sc); - wpi_init_locked(sc, 0); - WPI_UNLOCK(sc); -} - -static void -wpi_rfreset(void *arg, int pending) -{ - struct wpi_softc *sc = arg; - - WPI_LOCK(sc); - wpi_rfkill_resume(sc); - WPI_UNLOCK(sc); -} - -/* - * Allocate DMA-safe memory for firmware transfer. - */ -static int -wpi_alloc_fwmem(struct wpi_softc *sc) -{ - /* allocate enough contiguous space to store text and data */ - return wpi_dma_contig_alloc(sc, &sc->fw_dma, NULL, - WPI_FW_MAIN_TEXT_MAXSZ + WPI_FW_MAIN_DATA_MAXSZ, 1, - BUS_DMA_NOWAIT); -} - -static void -wpi_free_fwmem(struct wpi_softc *sc) -{ - wpi_dma_contig_free(&sc->fw_dma); -} - -/** - * Called every second, wpi_watchdog used by the watch dog timer - * to check that the card is still alive - */ -static void -wpi_watchdog(void *arg) -{ - struct wpi_softc *sc = arg; - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - uint32_t tmp; - - DPRINTFN(WPI_DEBUG_WATCHDOG,("Watchdog: tick\n")); - - if (sc->flags & WPI_FLAG_HW_RADIO_OFF) { - /* No need to lock firmware memory */ - tmp = wpi_mem_read(sc, WPI_MEM_HW_RADIO_OFF); - - if ((tmp & 0x1) == 0) { - /* Radio kill switch is still off */ - callout_reset(&sc->watchdog_to, hz, wpi_watchdog, sc); - return; - } - - device_printf(sc->sc_dev, "Hardware Switch Enabled\n"); - ieee80211_runtask(ic, &sc->sc_radiotask); - return; - } - - if (sc->sc_tx_timer > 0) { - if (--sc->sc_tx_timer == 0) { - device_printf(sc->sc_dev,"device timeout\n"); -#if defined(__DragonFly__) - IFNET_STAT_INC(ifp, oerrors, 1); -#else - if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); -#endif - ieee80211_runtask(ic, &sc->sc_restarttask); - } - } - if (sc->sc_scan_timer > 0) { - struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); - if (--sc->sc_scan_timer == 0 && vap != NULL) { - device_printf(sc->sc_dev,"scan timeout\n"); - ieee80211_cancel_scan(vap); - ieee80211_runtask(ic, &sc->sc_restarttask); - } - } - -#if defined(__DragonFly__) - if (ifp->if_flags & IFF_RUNNING) - callout_reset(&sc->watchdog_to, hz, wpi_watchdog, sc); -#else - if (ifp->if_drv_flags & IFF_DRV_RUNNING) - callout_reset(&sc->watchdog_to, hz, wpi_watchdog, sc); -#endif -} - -#if defined(__DragonFly__) -static int -wpi_sleep(struct wpi_softc *sc, void *wchan, - int flags, const char *wmsg, int timo) -{ - int iws; - int error; - iws = wlan_is_serialized(); - if (iws) - wlan_serialize_exit(); - error = lksleep(wchan, &sc->sc_mtx, flags, wmsg, timo); - if (iws) - wlan_serialize_enter(); - return error; -} -#endif - - -#ifdef WPI_DEBUG -static const char *wpi_cmd_str(int cmd) -{ - switch (cmd) { - case WPI_DISABLE_CMD: return "WPI_DISABLE_CMD"; - case WPI_CMD_CONFIGURE: return "WPI_CMD_CONFIGURE"; - case WPI_CMD_ASSOCIATE: return "WPI_CMD_ASSOCIATE"; - case WPI_CMD_SET_WME: return "WPI_CMD_SET_WME"; - case WPI_CMD_TSF: return "WPI_CMD_TSF"; - case WPI_CMD_ADD_NODE: return "WPI_CMD_ADD_NODE"; - case WPI_CMD_TX_DATA: return "WPI_CMD_TX_DATA"; - case WPI_CMD_MRR_SETUP: return "WPI_CMD_MRR_SETUP"; - case WPI_CMD_SET_LED: return "WPI_CMD_SET_LED"; - case WPI_CMD_SET_POWER_MODE: return "WPI_CMD_SET_POWER_MODE"; - case WPI_CMD_SCAN: return "WPI_CMD_SCAN"; - case WPI_CMD_SET_BEACON:return "WPI_CMD_SET_BEACON"; - case WPI_CMD_TXPOWER: return "WPI_CMD_TXPOWER"; - case WPI_CMD_BLUETOOTH: return "WPI_CMD_BLUETOOTH"; - - default: - KASSERT(1, ("Unknown Command: %d", cmd)); - return "UNKNOWN CMD"; /* Make the compiler happy */ - } -} -#endif - -MODULE_DEPEND(wpi, pci, 1, 1, 1); -MODULE_DEPEND(wpi, wlan, 1, 1, 1); -MODULE_DEPEND(wpi, firmware, 1, 1, 1); +/*- + * Copyright (c) 2006,2007 + * Damien Bergamini + * Benjamin Close + * Copyright (c) 2015 Andriy Voskoboinyk + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +__FBSDID("$FreeBSD$"); + +/* + * Driver for Intel PRO/Wireless 3945ABG 802.11 network adapters. + * + * The 3945ABG network adapter doesn't use traditional hardware as + * many other adaptors do. Instead at run time the eeprom is set into a known + * state and told to load boot firmware. The boot firmware loads an init and a + * main binary firmware image into SRAM on the card via DMA. + * Once the firmware is loaded, the driver/hw then + * communicate by way of circular dma rings via the SRAM to the firmware. + * + * There is 6 memory rings. 1 command ring, 1 rx data ring & 4 tx data rings. + * The 4 tx data rings allow for prioritization QoS. + * + * The rx data ring consists of 32 dma buffers. Two registers are used to + * indicate where in the ring the driver and the firmware are up to. The + * driver sets the initial read index (reg1) and the initial write index (reg2), + * the firmware updates the read index (reg1) on rx of a packet and fires an + * interrupt. The driver then processes the buffers starting at reg1 indicating + * to the firmware which buffers have been accessed by updating reg2. At the + * same time allocating new memory for the processed buffer. + * + * A similar thing happens with the tx rings. The difference is the firmware + * stop processing buffers once the queue is full and until confirmation + * of a successful transmition (tx_done) has occurred. + * + * The command ring operates in the same manner as the tx queues. + * + * All communication direct to the card (ie eeprom) is classed as Stage1 + * communication + * + * All communication via the firmware to the card is classed as State2. + * The firmware consists of 2 parts. A bootstrap firmware and a runtime + * firmware. The bootstrap firmware and runtime firmware are loaded + * from host memory via dma to the card then told to execute. From this point + * on the majority of communications between the driver and the card goes + * via the firmware. + */ + +#include "opt_wlan.h" +#include "opt_wpi.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__DragonFly__) +/* empty */ +#else +#include +#include +#endif +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +struct wpi_ident { + uint16_t vendor; + uint16_t device; + uint16_t subdevice; + const char *name; +}; + +static const struct wpi_ident wpi_ident_table[] = { + /* The below entries support ABG regardless of the subid */ + { 0x8086, 0x4222, 0x0, "Intel(R) PRO/Wireless 3945ABG" }, + { 0x8086, 0x4227, 0x0, "Intel(R) PRO/Wireless 3945ABG" }, + /* The below entries only support BG */ + { 0x8086, 0x4222, 0x1005, "Intel(R) PRO/Wireless 3945BG" }, + { 0x8086, 0x4222, 0x1034, "Intel(R) PRO/Wireless 3945BG" }, + { 0x8086, 0x4227, 0x1014, "Intel(R) PRO/Wireless 3945BG" }, + { 0x8086, 0x4222, 0x1044, "Intel(R) PRO/Wireless 3945BG" }, + { 0, 0, 0, NULL } +}; + +static int wpi_probe(device_t); +static int wpi_attach(device_t); +static void wpi_radiotap_attach(struct wpi_softc *); +static void wpi_sysctlattach(struct wpi_softc *); +static void wpi_init_beacon(struct wpi_vap *); +static struct ieee80211vap *wpi_vap_create(struct ieee80211com *, + const char [IFNAMSIZ], int, enum ieee80211_opmode, int, + const uint8_t [IEEE80211_ADDR_LEN], + const uint8_t [IEEE80211_ADDR_LEN]); +static void wpi_vap_delete(struct ieee80211vap *); +static int wpi_detach(device_t); +static int wpi_shutdown(device_t); +static int wpi_suspend(device_t); +static int wpi_resume(device_t); +static int wpi_nic_lock(struct wpi_softc *); +static int wpi_read_prom_data(struct wpi_softc *, uint32_t, void *, int); +static void wpi_dma_map_addr(void *, bus_dma_segment_t *, int, int); +static int wpi_dma_contig_alloc(struct wpi_softc *, struct wpi_dma_info *, + void **, bus_size_t, bus_size_t); +static void wpi_dma_contig_free(struct wpi_dma_info *); +static int wpi_alloc_shared(struct wpi_softc *); +static void wpi_free_shared(struct wpi_softc *); +static int wpi_alloc_fwmem(struct wpi_softc *); +static void wpi_free_fwmem(struct wpi_softc *); +static int wpi_alloc_rx_ring(struct wpi_softc *); +static void wpi_update_rx_ring(struct wpi_softc *); +static void wpi_update_rx_ring_ps(struct wpi_softc *); +static void wpi_reset_rx_ring(struct wpi_softc *); +static void wpi_free_rx_ring(struct wpi_softc *); +static int wpi_alloc_tx_ring(struct wpi_softc *, struct wpi_tx_ring *, + uint8_t); +static void wpi_update_tx_ring(struct wpi_softc *, struct wpi_tx_ring *); +static void wpi_update_tx_ring_ps(struct wpi_softc *, + struct wpi_tx_ring *); +static void wpi_reset_tx_ring(struct wpi_softc *, struct wpi_tx_ring *); +static void wpi_free_tx_ring(struct wpi_softc *, struct wpi_tx_ring *); +static int wpi_read_eeprom(struct wpi_softc *, + uint8_t macaddr[IEEE80211_ADDR_LEN]); +static uint32_t wpi_eeprom_channel_flags(struct wpi_eeprom_chan *); +static void wpi_read_eeprom_band(struct wpi_softc *, uint8_t, int, int *, + struct ieee80211_channel[]); +static int wpi_read_eeprom_channels(struct wpi_softc *, uint8_t); +static struct wpi_eeprom_chan *wpi_find_eeprom_channel(struct wpi_softc *, + struct ieee80211_channel *); +static void wpi_getradiocaps(struct ieee80211com *, int, int *, + struct ieee80211_channel[]); +static int wpi_setregdomain(struct ieee80211com *, + struct ieee80211_regdomain *, int, + struct ieee80211_channel[]); +static int wpi_read_eeprom_group(struct wpi_softc *, uint8_t); +static struct ieee80211_node *wpi_node_alloc(struct ieee80211vap *, + const uint8_t mac[IEEE80211_ADDR_LEN]); +static void wpi_node_free(struct ieee80211_node *); +static void wpi_ibss_recv_mgmt(struct ieee80211_node *, struct mbuf *, int, + const struct ieee80211_rx_stats *, + int, int); +static void wpi_restore_node(void *, struct ieee80211_node *); +static void wpi_restore_node_table(struct wpi_softc *, struct wpi_vap *); +static int wpi_newstate(struct ieee80211vap *, enum ieee80211_state, int); +static void wpi_calib_timeout(void *); +static void wpi_rx_done(struct wpi_softc *, struct wpi_rx_desc *, + struct wpi_rx_data *); +static void wpi_rx_statistics(struct wpi_softc *, struct wpi_rx_desc *, + struct wpi_rx_data *); +static void wpi_tx_done(struct wpi_softc *, struct wpi_rx_desc *); +static void wpi_cmd_done(struct wpi_softc *, struct wpi_rx_desc *); +static void wpi_notif_intr(struct wpi_softc *); +static void wpi_wakeup_intr(struct wpi_softc *); +#ifdef WPI_DEBUG +static void wpi_debug_registers(struct wpi_softc *); +#endif +static void wpi_fatal_intr(struct wpi_softc *); +static void wpi_intr(void *); +static void wpi_free_txfrags(struct wpi_softc *, uint16_t); +static int wpi_cmd2(struct wpi_softc *, struct wpi_buf *); +static int wpi_tx_data(struct wpi_softc *, struct mbuf *, + struct ieee80211_node *); +static int wpi_tx_data_raw(struct wpi_softc *, struct mbuf *, + struct ieee80211_node *, + const struct ieee80211_bpf_params *); +static int wpi_raw_xmit(struct ieee80211_node *, struct mbuf *, + const struct ieee80211_bpf_params *); +static int wpi_transmit(struct ieee80211com *, struct mbuf *); +static void wpi_watchdog_rfkill(void *); +static void wpi_scan_timeout(void *); +static void wpi_tx_timeout(void *); +static void wpi_parent(struct ieee80211com *); +static int wpi_cmd(struct wpi_softc *, uint8_t, const void *, uint16_t, + int); +static int wpi_mrr_setup(struct wpi_softc *); +static int wpi_add_node(struct wpi_softc *, struct ieee80211_node *); +static int wpi_add_broadcast_node(struct wpi_softc *, int); +static int wpi_add_ibss_node(struct wpi_softc *, struct ieee80211_node *); +static void wpi_del_node(struct wpi_softc *, struct ieee80211_node *); +static int wpi_updateedca(struct ieee80211com *); +static void wpi_set_promisc(struct wpi_softc *); +static void wpi_update_promisc(struct ieee80211com *); +static void wpi_update_mcast(struct ieee80211com *); +static void wpi_set_led(struct wpi_softc *, uint8_t, uint8_t, uint8_t); +static int wpi_set_timing(struct wpi_softc *, struct ieee80211_node *); +static void wpi_power_calibration(struct wpi_softc *); +static int wpi_set_txpower(struct wpi_softc *, int); +static int wpi_get_power_index(struct wpi_softc *, + struct wpi_power_group *, uint8_t, int, int); +static int wpi_set_pslevel(struct wpi_softc *, uint8_t, int, int); +static int wpi_send_btcoex(struct wpi_softc *); +static int wpi_send_rxon(struct wpi_softc *, int, int); +static int wpi_config(struct wpi_softc *); +static uint16_t wpi_get_active_dwell_time(struct wpi_softc *, + struct ieee80211_channel *, uint8_t); +static uint16_t wpi_limit_dwell(struct wpi_softc *, uint16_t); +static uint16_t wpi_get_passive_dwell_time(struct wpi_softc *, + struct ieee80211_channel *); +static uint32_t wpi_get_scan_pause_time(uint32_t, uint16_t); +static int wpi_scan(struct wpi_softc *, struct ieee80211_channel *); +static int wpi_auth(struct wpi_softc *, struct ieee80211vap *); +static int wpi_config_beacon(struct wpi_vap *); +static int wpi_setup_beacon(struct wpi_softc *, struct ieee80211_node *); +static void wpi_update_beacon(struct ieee80211vap *, int); +static void wpi_newassoc(struct ieee80211_node *, int); +static int wpi_run(struct wpi_softc *, struct ieee80211vap *); +static int wpi_load_key(struct ieee80211_node *, + const struct ieee80211_key *); +static void wpi_load_key_cb(void *, struct ieee80211_node *); +static int wpi_set_global_keys(struct ieee80211_node *); +static int wpi_del_key(struct ieee80211_node *, + const struct ieee80211_key *); +static void wpi_del_key_cb(void *, struct ieee80211_node *); +static int wpi_process_key(struct ieee80211vap *, + const struct ieee80211_key *, int); +static int wpi_key_set(struct ieee80211vap *, + const struct ieee80211_key *); +static int wpi_key_delete(struct ieee80211vap *, + const struct ieee80211_key *); +static int wpi_post_alive(struct wpi_softc *); +static int wpi_load_bootcode(struct wpi_softc *, const uint8_t *, + uint32_t); +static int wpi_load_firmware(struct wpi_softc *); +static int wpi_read_firmware(struct wpi_softc *); +static void wpi_unload_firmware(struct wpi_softc *); +static int wpi_clock_wait(struct wpi_softc *); +static int wpi_apm_init(struct wpi_softc *); +static void wpi_apm_stop_master(struct wpi_softc *); +static void wpi_apm_stop(struct wpi_softc *); +static void wpi_nic_config(struct wpi_softc *); +static int wpi_hw_init(struct wpi_softc *); +static void wpi_hw_stop(struct wpi_softc *); +static void wpi_radio_on(void *, int); +static void wpi_radio_off(void *, int); +static int wpi_init(struct wpi_softc *); +static void wpi_stop_locked(struct wpi_softc *); +static void wpi_stop(struct wpi_softc *); +static void wpi_scan_start(struct ieee80211com *); +static void wpi_scan_end(struct ieee80211com *); +static void wpi_set_channel(struct ieee80211com *); +static void wpi_scan_curchan(struct ieee80211_scan_state *, unsigned long); +static void wpi_scan_mindwell(struct ieee80211_scan_state *); + +static device_method_t wpi_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, wpi_probe), + DEVMETHOD(device_attach, wpi_attach), + DEVMETHOD(device_detach, wpi_detach), + DEVMETHOD(device_shutdown, wpi_shutdown), + DEVMETHOD(device_suspend, wpi_suspend), + DEVMETHOD(device_resume, wpi_resume), + + DEVMETHOD_END +}; + +static driver_t wpi_driver = { + "wpi", + wpi_methods, + sizeof (struct wpi_softc) +}; +static devclass_t wpi_devclass; + +DRIVER_MODULE(wpi, pci, wpi_driver, wpi_devclass, NULL, NULL); + +MODULE_VERSION(wpi, 1); + +MODULE_DEPEND(wpi, pci, 1, 1, 1); +MODULE_DEPEND(wpi, wlan, 1, 1, 1); +MODULE_DEPEND(wpi, firmware, 1, 1, 1); + +static int +wpi_probe(device_t dev) +{ + const struct wpi_ident *ident; + + for (ident = wpi_ident_table; ident->name != NULL; ident++) { + if (pci_get_vendor(dev) == ident->vendor && + pci_get_device(dev) == ident->device) { + device_set_desc(dev, ident->name); + return (BUS_PROBE_DEFAULT); + } + } + return ENXIO; +} + +static int +wpi_attach(device_t dev) +{ + struct wpi_softc *sc = (struct wpi_softc *)device_get_softc(dev); + struct ieee80211com *ic; + uint8_t i; + int error, rid; +#ifdef WPI_DEBUG + int supportsa = 1; + const struct wpi_ident *ident; +#endif +#if defined(__DragonFly__) + int irq_flags; +#endif + + sc->sc_dev = dev; + +#ifdef WPI_DEBUG + error = resource_int_value(device_get_name(sc->sc_dev), + device_get_unit(sc->sc_dev), "debug", &(sc->sc_debug)); + if (error != 0) + sc->sc_debug = 0; +#else + sc->sc_debug = 0; +#endif + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); + + /* + * Get the offset of the PCI Express Capability Structure in PCI + * Configuration Space. + */ +#if defined(__DragonFly__) + error = pci_find_extcap(dev, PCIY_EXPRESS, &sc->sc_cap_off); +#else + error = pci_find_cap(dev, PCIY_EXPRESS, &sc->sc_cap_off); +#endif + if (error != 0) { + device_printf(dev, "PCIe capability structure not found!\n"); + return error; + } + + /* + * Some card's only support 802.11b/g not a, check to see if + * this is one such card. A 0x0 in the subdevice table indicates + * the entire subdevice range is to be ignored. + */ +#ifdef WPI_DEBUG + for (ident = wpi_ident_table; ident->name != NULL; ident++) { + if (ident->subdevice && + pci_get_subdevice(dev) == ident->subdevice) { + supportsa = 0; + break; + } + } +#endif + + /* Clear device-specific "PCI retry timeout" register (41h). */ + pci_write_config(dev, 0x41, 0, 1); + + /* Enable bus-mastering. */ + pci_enable_busmaster(dev); + + rid = PCIR_BAR(0); + sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->mem == NULL) { + device_printf(dev, "can't map mem space\n"); + return ENOMEM; + } + sc->sc_st = rman_get_bustag(sc->mem); + sc->sc_sh = rman_get_bushandle(sc->mem); + +#if defined(__DragonFly__) + pci_alloc_1intr(dev, 1, &rid, &irq_flags); + sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, irq_flags); +#else + rid = 1; + if (pci_alloc_msi(dev, &rid) == 0) + rid = 1; + else + rid = 0; + /* Install interrupt handler. */ + sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE | + (rid != 0 ? 0 : RF_SHAREABLE)); +#endif + if (sc->irq == NULL) { + device_printf(dev, "can't map interrupt\n"); + error = ENOMEM; + goto fail; + } + + WPI_LOCK_INIT(sc); + WPI_TX_LOCK_INIT(sc); + WPI_RXON_LOCK_INIT(sc); + WPI_NT_LOCK_INIT(sc); + WPI_TXQ_LOCK_INIT(sc); + WPI_TXQ_STATE_LOCK_INIT(sc); + + /* Allocate DMA memory for firmware transfers. */ + if ((error = wpi_alloc_fwmem(sc)) != 0) { + device_printf(dev, + "could not allocate memory for firmware, error %d\n", + error); + goto fail; + } + + /* Allocate shared page. */ + if ((error = wpi_alloc_shared(sc)) != 0) { + device_printf(dev, "could not allocate shared page\n"); + goto fail; + } + + /* Allocate TX rings - 4 for QoS purposes, 1 for commands. */ + for (i = 0; i < WPI_DRV_NTXQUEUES; i++) { + if ((error = wpi_alloc_tx_ring(sc, &sc->txq[i], i)) != 0) { + device_printf(dev, + "could not allocate TX ring %d, error %d\n", i, + error); + goto fail; + } + } + + /* Allocate RX ring. */ + if ((error = wpi_alloc_rx_ring(sc)) != 0) { + device_printf(dev, "could not allocate RX ring, error %d\n", + error); + goto fail; + } + + /* Clear pending interrupts. */ + WPI_WRITE(sc, WPI_INT, 0xffffffff); + + ic = &sc->sc_ic; + ic->ic_softc = sc; + ic->ic_name = device_get_nameunit(dev); + ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ + ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ + + /* Set device capabilities. */ + ic->ic_caps = + IEEE80211_C_STA /* station mode supported */ + | IEEE80211_C_IBSS /* IBSS mode supported */ + | IEEE80211_C_HOSTAP /* Host access point mode */ + | IEEE80211_C_MONITOR /* monitor mode supported */ + | IEEE80211_C_AHDEMO /* adhoc demo mode */ + | IEEE80211_C_BGSCAN /* capable of bg scanning */ + | IEEE80211_C_TXFRAG /* handle tx frags */ + | IEEE80211_C_TXPMGT /* tx power management */ + | IEEE80211_C_SHSLOT /* short slot time supported */ + | IEEE80211_C_WPA /* 802.11i */ + | IEEE80211_C_SHPREAMBLE /* short preamble supported */ + | IEEE80211_C_WME /* 802.11e */ + | IEEE80211_C_PMGT /* Station-side power mgmt */ + ; + + ic->ic_cryptocaps = + IEEE80211_CRYPTO_AES_CCM; + + /* + * Read in the eeprom and also setup the channels for + * net80211. We don't set the rates as net80211 does this for us + */ + if ((error = wpi_read_eeprom(sc, ic->ic_macaddr)) != 0) { + device_printf(dev, "could not read EEPROM, error %d\n", + error); + goto fail; + } + +#ifdef WPI_DEBUG + if (bootverbose) { + device_printf(sc->sc_dev, "Regulatory Domain: %.4s\n", + sc->domain); + device_printf(sc->sc_dev, "Hardware Type: %c\n", + sc->type > 1 ? 'B': '?'); + device_printf(sc->sc_dev, "Hardware Revision: %c\n", + ((sc->rev & 0xf0) == 0xd0) ? 'D': '?'); + device_printf(sc->sc_dev, "SKU %s support 802.11a\n", + supportsa ? "does" : "does not"); + + /* XXX hw_config uses the PCIDEV for the Hardware rev. Must + check what sc->rev really represents - benjsc 20070615 */ + } +#endif + + ieee80211_ifattach(ic); + ic->ic_vap_create = wpi_vap_create; + ic->ic_vap_delete = wpi_vap_delete; + ic->ic_parent = wpi_parent; + ic->ic_raw_xmit = wpi_raw_xmit; + ic->ic_transmit = wpi_transmit; + ic->ic_node_alloc = wpi_node_alloc; + sc->sc_node_free = ic->ic_node_free; + ic->ic_node_free = wpi_node_free; + ic->ic_wme.wme_update = wpi_updateedca; + ic->ic_update_promisc = wpi_update_promisc; + ic->ic_update_mcast = wpi_update_mcast; + ic->ic_newassoc = wpi_newassoc; + ic->ic_scan_start = wpi_scan_start; + ic->ic_scan_end = wpi_scan_end; + ic->ic_set_channel = wpi_set_channel; + ic->ic_scan_curchan = wpi_scan_curchan; + ic->ic_scan_mindwell = wpi_scan_mindwell; + ic->ic_getradiocaps = wpi_getradiocaps; + ic->ic_setregdomain = wpi_setregdomain; + + sc->sc_update_rx_ring = wpi_update_rx_ring; + sc->sc_update_tx_ring = wpi_update_tx_ring; + + wpi_radiotap_attach(sc); + + callout_init_mtx(&sc->calib_to, &sc->rxon_mtx, 0); + callout_init_mtx(&sc->scan_timeout, &sc->rxon_mtx, 0); + callout_init_mtx(&sc->tx_timeout, &sc->txq_state_mtx, 0); + callout_init_mtx(&sc->watchdog_rfkill, &sc->sc_mtx, 0); + TASK_INIT(&sc->sc_radiooff_task, 0, wpi_radio_off, sc); + TASK_INIT(&sc->sc_radioon_task, 0, wpi_radio_on, sc); + + wpi_sysctlattach(sc); + + /* + * Hook our interrupt after all initialization is complete. + */ +#if defined(__DragonFly__) + error = bus_setup_intr(dev, sc->irq, INTR_MPSAFE, + wpi_intr, sc, &sc->sc_ih, &wlan_global_serializer); +#else + error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET | INTR_MPSAFE, + NULL, wpi_intr, sc, &sc->sc_ih); +#endif + if (error != 0) { + device_printf(dev, "can't establish interrupt, error %d\n", + error); + goto fail; + } + + if (bootverbose) + ieee80211_announce(ic); + +#ifdef WPI_DEBUG + if (sc->sc_debug & WPI_DEBUG_HW) + ieee80211_announce_channels(ic); +#endif + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); + return 0; + +fail: wpi_detach(dev); + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__); + return error; +} + +/* + * Attach the interface to 802.11 radiotap. + */ +static void +wpi_radiotap_attach(struct wpi_softc *sc) +{ + struct wpi_rx_radiotap_header *rxtap = &sc->sc_rxtap; + struct wpi_tx_radiotap_header *txtap = &sc->sc_txtap; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); + ieee80211_radiotap_attach(&sc->sc_ic, + &txtap->wt_ihdr, sizeof(*txtap), WPI_TX_RADIOTAP_PRESENT, + &rxtap->wr_ihdr, sizeof(*rxtap), WPI_RX_RADIOTAP_PRESENT); + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); +} + +static void +wpi_sysctlattach(struct wpi_softc *sc) +{ +#ifdef WPI_DEBUG + struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); + struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "debug", CTLFLAG_RW, &sc->sc_debug, sc->sc_debug, + "control debugging printfs"); +#endif +} + +static void +wpi_init_beacon(struct wpi_vap *wvp) +{ + struct wpi_buf *bcn = &wvp->wv_bcbuf; + struct wpi_cmd_beacon *cmd = (struct wpi_cmd_beacon *)&bcn->data; + + cmd->id = WPI_ID_BROADCAST; + cmd->ofdm_mask = 0xff; + cmd->cck_mask = 0x0f; + cmd->lifetime = htole32(WPI_LIFETIME_INFINITE); + + /* + * XXX WPI_TX_AUTO_SEQ seems to be ignored - workaround this issue + * XXX by using WPI_TX_NEED_ACK instead (with some side effects). + */ + cmd->flags = htole32(WPI_TX_NEED_ACK | WPI_TX_INSERT_TSTAMP); + + bcn->code = WPI_CMD_SET_BEACON; + bcn->ac = WPI_CMD_QUEUE_NUM; + bcn->size = sizeof(struct wpi_cmd_beacon); +} + +static struct ieee80211vap * +wpi_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, + enum ieee80211_opmode opmode, int flags, + const uint8_t bssid[IEEE80211_ADDR_LEN], + const uint8_t mac[IEEE80211_ADDR_LEN]) +{ + struct wpi_vap *wvp; + struct ieee80211vap *vap; + + if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ + return NULL; + + wvp = kmalloc(sizeof(struct wpi_vap), M_80211_VAP, M_WAITOK | M_ZERO); + vap = &wvp->wv_vap; + ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid); + + if (opmode == IEEE80211_M_IBSS || opmode == IEEE80211_M_HOSTAP) { + WPI_VAP_LOCK_INIT(wvp); + wpi_init_beacon(wvp); + } + + /* Override with driver methods. */ + vap->iv_key_set = wpi_key_set; + vap->iv_key_delete = wpi_key_delete; + if (opmode == IEEE80211_M_IBSS) { + wvp->wv_recv_mgmt = vap->iv_recv_mgmt; + vap->iv_recv_mgmt = wpi_ibss_recv_mgmt; + } + wvp->wv_newstate = vap->iv_newstate; + vap->iv_newstate = wpi_newstate; + vap->iv_update_beacon = wpi_update_beacon; + vap->iv_max_aid = WPI_ID_IBSS_MAX - WPI_ID_IBSS_MIN + 1; + + ieee80211_ratectl_init(vap); + /* Complete setup. */ + ieee80211_vap_attach(vap, ieee80211_media_change, + ieee80211_media_status, mac); + ic->ic_opmode = opmode; + return vap; +} + +static void +wpi_vap_delete(struct ieee80211vap *vap) +{ + struct wpi_vap *wvp = WPI_VAP(vap); + struct wpi_buf *bcn = &wvp->wv_bcbuf; + enum ieee80211_opmode opmode = vap->iv_opmode; + + ieee80211_ratectl_deinit(vap); + ieee80211_vap_detach(vap); + + if (opmode == IEEE80211_M_IBSS || opmode == IEEE80211_M_HOSTAP) { + if (bcn->m != NULL) + m_freem(bcn->m); + + WPI_VAP_LOCK_DESTROY(wvp); + } + + kfree(wvp, M_80211_VAP); +} + +static int +wpi_detach(device_t dev) +{ + struct wpi_softc *sc = device_get_softc(dev); + struct ieee80211com *ic = &sc->sc_ic; + uint8_t qid; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); + + if (ic->ic_vap_create == wpi_vap_create) { + ieee80211_draintask(ic, &sc->sc_radioon_task); + ieee80211_draintask(ic, &sc->sc_radiooff_task); + + wpi_stop(sc); + + callout_drain(&sc->watchdog_rfkill); + callout_drain(&sc->tx_timeout); + callout_drain(&sc->scan_timeout); + callout_drain(&sc->calib_to); + ieee80211_ifdetach(ic); + } + + /* Uninstall interrupt handler. */ + if (sc->irq != NULL) { + bus_teardown_intr(dev, sc->irq, sc->sc_ih); + bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(sc->irq), + sc->irq); + pci_release_msi(dev); + } + + if (sc->txq[0].data_dmat) { + /* Free DMA resources. */ + for (qid = 0; qid < WPI_DRV_NTXQUEUES; qid++) + wpi_free_tx_ring(sc, &sc->txq[qid]); + + wpi_free_rx_ring(sc); + wpi_free_shared(sc); + } + + if (sc->fw_dma.tag) + wpi_free_fwmem(sc); + + if (sc->mem != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, + rman_get_rid(sc->mem), sc->mem); + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); + WPI_TXQ_STATE_LOCK_DESTROY(sc); + WPI_TXQ_LOCK_DESTROY(sc); + WPI_NT_LOCK_DESTROY(sc); + WPI_RXON_LOCK_DESTROY(sc); + WPI_TX_LOCK_DESTROY(sc); + WPI_LOCK_DESTROY(sc); + return 0; +} + +static int +wpi_shutdown(device_t dev) +{ + struct wpi_softc *sc = device_get_softc(dev); + + wpi_stop(sc); + return 0; +} + +static int +wpi_suspend(device_t dev) +{ + struct wpi_softc *sc = device_get_softc(dev); + struct ieee80211com *ic = &sc->sc_ic; + + ieee80211_suspend_all(ic); + return 0; +} + +static int +wpi_resume(device_t dev) +{ + struct wpi_softc *sc = device_get_softc(dev); + struct ieee80211com *ic = &sc->sc_ic; + + /* Clear device-specific "PCI retry timeout" register (41h). */ + pci_write_config(dev, 0x41, 0, 1); + + ieee80211_resume_all(ic); + return 0; +} + +/* + * Grab exclusive access to NIC memory. + */ +static int +wpi_nic_lock(struct wpi_softc *sc) +{ + int ntries; + + /* Request exclusive access to NIC. */ + WPI_SETBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_MAC_ACCESS_REQ); + + /* Spin until we actually get the lock. */ + for (ntries = 0; ntries < 1000; ntries++) { + if ((WPI_READ(sc, WPI_GP_CNTRL) & + (WPI_GP_CNTRL_MAC_ACCESS_ENA | WPI_GP_CNTRL_SLEEP)) == + WPI_GP_CNTRL_MAC_ACCESS_ENA) + return 0; + DELAY(10); + } + + device_printf(sc->sc_dev, "could not lock memory\n"); + + return ETIMEDOUT; +} + +/* + * Release lock on NIC memory. + */ +static __inline void +wpi_nic_unlock(struct wpi_softc *sc) +{ + WPI_CLRBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_MAC_ACCESS_REQ); +} + +static __inline uint32_t +wpi_prph_read(struct wpi_softc *sc, uint32_t addr) +{ + WPI_WRITE(sc, WPI_PRPH_RADDR, WPI_PRPH_DWORD | addr); + WPI_BARRIER_READ_WRITE(sc); + return WPI_READ(sc, WPI_PRPH_RDATA); +} + +static __inline void +wpi_prph_write(struct wpi_softc *sc, uint32_t addr, uint32_t data) +{ + WPI_WRITE(sc, WPI_PRPH_WADDR, WPI_PRPH_DWORD | addr); + WPI_BARRIER_WRITE(sc); + WPI_WRITE(sc, WPI_PRPH_WDATA, data); +} + +static __inline void +wpi_prph_setbits(struct wpi_softc *sc, uint32_t addr, uint32_t mask) +{ + wpi_prph_write(sc, addr, wpi_prph_read(sc, addr) | mask); +} + +static __inline void +wpi_prph_clrbits(struct wpi_softc *sc, uint32_t addr, uint32_t mask) +{ + wpi_prph_write(sc, addr, wpi_prph_read(sc, addr) & ~mask); +} + +static __inline void +wpi_prph_write_region_4(struct wpi_softc *sc, uint32_t addr, + const uint32_t *data, uint32_t count) +{ + for (; count != 0; count--, data++, addr += 4) + wpi_prph_write(sc, addr, *data); +} + +static __inline uint32_t +wpi_mem_read(struct wpi_softc *sc, uint32_t addr) +{ + WPI_WRITE(sc, WPI_MEM_RADDR, addr); + WPI_BARRIER_READ_WRITE(sc); + return WPI_READ(sc, WPI_MEM_RDATA); +} + +static __inline void +wpi_mem_read_region_4(struct wpi_softc *sc, uint32_t addr, uint32_t *data, + int count) +{ + for (; count > 0; count--, addr += 4) + *data++ = wpi_mem_read(sc, addr); +} + +static int +wpi_read_prom_data(struct wpi_softc *sc, uint32_t addr, void *data, int count) +{ + uint8_t *out = data; + uint32_t val; + int error, ntries; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); + + if ((error = wpi_nic_lock(sc)) != 0) + return error; + + for (; count > 0; count -= 2, addr++) { + WPI_WRITE(sc, WPI_EEPROM, addr << 2); + for (ntries = 0; ntries < 10; ntries++) { + val = WPI_READ(sc, WPI_EEPROM); + if (val & WPI_EEPROM_READ_VALID) + break; + DELAY(5); + } + if (ntries == 10) { + device_printf(sc->sc_dev, + "timeout reading ROM at 0x%x\n", addr); + return ETIMEDOUT; + } + *out++= val >> 16; + if (count > 1) + *out ++= val >> 24; + } + + wpi_nic_unlock(sc); + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); + + return 0; +} + +static void +wpi_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) +{ + if (error != 0) + return; + KASSERT(nsegs == 1, ("too many DMA segments, %d should be 1", nsegs)); + *(bus_addr_t *)arg = segs[0].ds_addr; +} + +/* + * Allocates a contiguous block of dma memory of the requested size and + * alignment. + */ +static int +wpi_dma_contig_alloc(struct wpi_softc *sc, struct wpi_dma_info *dma, + void **kvap, bus_size_t size, bus_size_t alignment) +{ + int error; + + dma->tag = NULL; + dma->size = size; + +#if defined(__DragonFly__) + error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), alignment, + 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, size, + 1, size, 0, &dma->tag); +#else + error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), alignment, + 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, size, + 1, size, 0, NULL, NULL, &dma->tag); +#endif + if (error != 0) + goto fail; + + error = bus_dmamem_alloc(dma->tag, (void **)&dma->vaddr, + BUS_DMA_NOWAIT | BUS_DMA_ZERO | BUS_DMA_COHERENT, &dma->map); + if (error != 0) + goto fail; + + error = bus_dmamap_load(dma->tag, dma->map, dma->vaddr, size, + wpi_dma_map_addr, &dma->paddr, BUS_DMA_NOWAIT); + if (error != 0) + goto fail; + + bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); + + if (kvap != NULL) + *kvap = dma->vaddr; + + return 0; + +fail: wpi_dma_contig_free(dma); + return error; +} + +static void +wpi_dma_contig_free(struct wpi_dma_info *dma) +{ + if (dma->vaddr != NULL) { + bus_dmamap_sync(dma->tag, dma->map, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(dma->tag, dma->map); + bus_dmamem_free(dma->tag, dma->vaddr, dma->map); + dma->vaddr = NULL; + } + if (dma->tag != NULL) { + bus_dma_tag_destroy(dma->tag); + dma->tag = NULL; + } +} + +/* + * Allocate a shared page between host and NIC. + */ +static int +wpi_alloc_shared(struct wpi_softc *sc) +{ + /* Shared buffer must be aligned on a 4KB boundary. */ + return wpi_dma_contig_alloc(sc, &sc->shared_dma, + (void **)&sc->shared, sizeof (struct wpi_shared), 4096); +} + +static void +wpi_free_shared(struct wpi_softc *sc) +{ + wpi_dma_contig_free(&sc->shared_dma); +} + +/* + * Allocate DMA-safe memory for firmware transfer. + */ +static int +wpi_alloc_fwmem(struct wpi_softc *sc) +{ + /* Must be aligned on a 16-byte boundary. */ + return wpi_dma_contig_alloc(sc, &sc->fw_dma, NULL, + WPI_FW_TEXT_MAXSZ + WPI_FW_DATA_MAXSZ, 16); +} + +static void +wpi_free_fwmem(struct wpi_softc *sc) +{ + wpi_dma_contig_free(&sc->fw_dma); +} + +static int +wpi_alloc_rx_ring(struct wpi_softc *sc) +{ + struct wpi_rx_ring *ring = &sc->rxq; + bus_size_t size; + int i, error; + + ring->cur = 0; + ring->update = 0; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); + + /* Allocate RX descriptors (16KB aligned.) */ + size = WPI_RX_RING_COUNT * sizeof (uint32_t); + error = wpi_dma_contig_alloc(sc, &ring->desc_dma, + (void **)&ring->desc, size, WPI_RING_DMA_ALIGN); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: could not allocate RX ring DMA memory, error %d\n", + __func__, error); + goto fail; + } + + /* Create RX buffer DMA tag. */ +#if defined(__DragonFly__) + error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, + MJUMPAGESIZE, 1, MJUMPAGESIZE, 0, &ring->data_dmat); +#else + error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, + MJUMPAGESIZE, 1, MJUMPAGESIZE, 0, NULL, NULL, &ring->data_dmat); +#endif + if (error != 0) { + device_printf(sc->sc_dev, + "%s: could not create RX buf DMA tag, error %d\n", + __func__, error); + goto fail; + } + + /* + * Allocate and map RX buffers. + */ + for (i = 0; i < WPI_RX_RING_COUNT; i++) { + struct wpi_rx_data *data = &ring->data[i]; + bus_addr_t paddr; + + error = bus_dmamap_create(ring->data_dmat, 0, &data->map); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: could not create RX buf DMA map, error %d\n", + __func__, error); + goto fail; + } + + data->m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE); + if (data->m == NULL) { + device_printf(sc->sc_dev, + "%s: could not allocate RX mbuf\n", __func__); + error = ENOBUFS; + goto fail; + } + + error = bus_dmamap_load(ring->data_dmat, data->map, + mtod(data->m, void *), MJUMPAGESIZE, wpi_dma_map_addr, + &paddr, BUS_DMA_NOWAIT); + if (error != 0 && error != EFBIG) { + device_printf(sc->sc_dev, + "%s: can't map mbuf (error %d)\n", __func__, + error); + goto fail; + } + + /* Set physical address of RX buffer. */ + ring->desc[i] = htole32(paddr); + } + + bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, + BUS_DMASYNC_PREWRITE); + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); + + return 0; + +fail: wpi_free_rx_ring(sc); + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__); + + return error; +} + +static void +wpi_update_rx_ring(struct wpi_softc *sc) +{ + WPI_WRITE(sc, WPI_FH_RX_WPTR, sc->rxq.cur & ~7); +} + +static void +wpi_update_rx_ring_ps(struct wpi_softc *sc) +{ + struct wpi_rx_ring *ring = &sc->rxq; + + if (ring->update != 0) { + /* Wait for INT_WAKEUP event. */ + return; + } + + WPI_TXQ_LOCK(sc); + WPI_SETBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_MAC_ACCESS_REQ); + if (WPI_READ(sc, WPI_GP_CNTRL) & WPI_GP_CNTRL_SLEEP) { + DPRINTF(sc, WPI_DEBUG_PWRSAVE, "%s: wakeup request\n", + __func__); + ring->update = 1; + } else { + wpi_update_rx_ring(sc); + WPI_CLRBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_MAC_ACCESS_REQ); + } + WPI_TXQ_UNLOCK(sc); +} + +static void +wpi_reset_rx_ring(struct wpi_softc *sc) +{ + struct wpi_rx_ring *ring = &sc->rxq; + int ntries; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + + if (wpi_nic_lock(sc) == 0) { + WPI_WRITE(sc, WPI_FH_RX_CONFIG, 0); + for (ntries = 0; ntries < 1000; ntries++) { + if (WPI_READ(sc, WPI_FH_RX_STATUS) & + WPI_FH_RX_STATUS_IDLE) + break; + DELAY(10); + } + wpi_nic_unlock(sc); + } + + ring->cur = 0; + ring->update = 0; +} + +static void +wpi_free_rx_ring(struct wpi_softc *sc) +{ + struct wpi_rx_ring *ring = &sc->rxq; + int i; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + + wpi_dma_contig_free(&ring->desc_dma); + + for (i = 0; i < WPI_RX_RING_COUNT; i++) { + struct wpi_rx_data *data = &ring->data[i]; + + if (data->m != NULL) { + bus_dmamap_sync(ring->data_dmat, data->map, + BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(ring->data_dmat, data->map); + m_freem(data->m); + data->m = NULL; + } + if (data->map != NULL) + bus_dmamap_destroy(ring->data_dmat, data->map); + } + if (ring->data_dmat != NULL) { + bus_dma_tag_destroy(ring->data_dmat); + ring->data_dmat = NULL; + } +} + +static int +wpi_alloc_tx_ring(struct wpi_softc *sc, struct wpi_tx_ring *ring, uint8_t qid) +{ + bus_addr_t paddr; + bus_size_t size; + int i, error; + + ring->qid = qid; + ring->queued = 0; + ring->cur = 0; + ring->pending = 0; + ring->update = 0; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); + + /* Allocate TX descriptors (16KB aligned.) */ + size = WPI_TX_RING_COUNT * sizeof (struct wpi_tx_desc); + error = wpi_dma_contig_alloc(sc, &ring->desc_dma, (void **)&ring->desc, + size, WPI_RING_DMA_ALIGN); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: could not allocate TX ring DMA memory, error %d\n", + __func__, error); + goto fail; + } + + /* Update shared area with ring physical address. */ + sc->shared->txbase[qid] = htole32(ring->desc_dma.paddr); + bus_dmamap_sync(sc->shared_dma.tag, sc->shared_dma.map, + BUS_DMASYNC_PREWRITE); + + size = WPI_TX_RING_COUNT * sizeof (struct wpi_tx_cmd); + error = wpi_dma_contig_alloc(sc, &ring->cmd_dma, (void **)&ring->cmd, + size, 4); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: could not allocate TX cmd DMA memory, error %d\n", + __func__, error); + goto fail; + } + +#if defined(__DragonFly__) + error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, + WPI_MAX_SCATTER - 1, MCLBYTES, 0, &ring->data_dmat); +#else + error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, + WPI_MAX_SCATTER - 1, MCLBYTES, 0, NULL, NULL, &ring->data_dmat); +#endif + if (error != 0) { + device_printf(sc->sc_dev, + "%s: could not create TX buf DMA tag, error %d\n", + __func__, error); + goto fail; + } + + paddr = ring->cmd_dma.paddr; + for (i = 0; i < WPI_TX_RING_COUNT; i++) { + struct wpi_tx_data *data = &ring->data[i]; + + data->cmd_paddr = paddr; + paddr += sizeof (struct wpi_tx_cmd); + + error = bus_dmamap_create(ring->data_dmat, 0, &data->map); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: could not create TX buf DMA map, error %d\n", + __func__, error); + goto fail; + } + } + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); + + return 0; + +fail: wpi_free_tx_ring(sc, ring); + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__); + return error; +} + +static void +wpi_update_tx_ring(struct wpi_softc *sc, struct wpi_tx_ring *ring) +{ + WPI_WRITE(sc, WPI_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur); +} + +static void +wpi_update_tx_ring_ps(struct wpi_softc *sc, struct wpi_tx_ring *ring) +{ + + if (ring->update != 0) { + /* Wait for INT_WAKEUP event. */ + return; + } + + WPI_SETBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_MAC_ACCESS_REQ); + if (WPI_READ(sc, WPI_GP_CNTRL) & WPI_GP_CNTRL_SLEEP) { + DPRINTF(sc, WPI_DEBUG_PWRSAVE, "%s (%d): requesting wakeup\n", + __func__, ring->qid); + ring->update = 1; + } else { + wpi_update_tx_ring(sc, ring); + WPI_CLRBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_MAC_ACCESS_REQ); + } +} + +static void +wpi_reset_tx_ring(struct wpi_softc *sc, struct wpi_tx_ring *ring) +{ + int i; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + + for (i = 0; i < WPI_TX_RING_COUNT; i++) { + struct wpi_tx_data *data = &ring->data[i]; + + if (data->m != NULL) { + bus_dmamap_sync(ring->data_dmat, data->map, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(ring->data_dmat, data->map); + m_freem(data->m); + data->m = NULL; + } + if (data->ni != NULL) { + ieee80211_free_node(data->ni); + data->ni = NULL; + } + } + /* Clear TX descriptors. */ + memset(ring->desc, 0, ring->desc_dma.size); + bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, + BUS_DMASYNC_PREWRITE); + ring->queued = 0; + ring->cur = 0; + ring->pending = 0; + ring->update = 0; +} + +static void +wpi_free_tx_ring(struct wpi_softc *sc, struct wpi_tx_ring *ring) +{ + int i; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + + wpi_dma_contig_free(&ring->desc_dma); + wpi_dma_contig_free(&ring->cmd_dma); + + for (i = 0; i < WPI_TX_RING_COUNT; i++) { + struct wpi_tx_data *data = &ring->data[i]; + + if (data->m != NULL) { + bus_dmamap_sync(ring->data_dmat, data->map, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(ring->data_dmat, data->map); + m_freem(data->m); + } + if (data->map != NULL) + bus_dmamap_destroy(ring->data_dmat, data->map); + } + if (ring->data_dmat != NULL) { + bus_dma_tag_destroy(ring->data_dmat); + ring->data_dmat = NULL; + } +} + +/* + * Extract various information from EEPROM. + */ +static int +wpi_read_eeprom(struct wpi_softc *sc, uint8_t macaddr[IEEE80211_ADDR_LEN]) +{ +#define WPI_CHK(res) do { \ + if ((error = res) != 0) \ + goto fail; \ +} while (0) + uint8_t i; + int error; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); + + /* Adapter has to be powered on for EEPROM access to work. */ + if ((error = wpi_apm_init(sc)) != 0) { + device_printf(sc->sc_dev, + "%s: could not power ON adapter, error %d\n", __func__, + error); + return error; + } + + if ((WPI_READ(sc, WPI_EEPROM_GP) & 0x6) == 0) { + device_printf(sc->sc_dev, "bad EEPROM signature\n"); + error = EIO; + goto fail; + } + /* Clear HW ownership of EEPROM. */ + WPI_CLRBITS(sc, WPI_EEPROM_GP, WPI_EEPROM_GP_IF_OWNER); + + /* Read the hardware capabilities, revision and SKU type. */ + WPI_CHK(wpi_read_prom_data(sc, WPI_EEPROM_SKU_CAP, &sc->cap, + sizeof(sc->cap))); + WPI_CHK(wpi_read_prom_data(sc, WPI_EEPROM_REVISION, &sc->rev, + sizeof(sc->rev))); + WPI_CHK(wpi_read_prom_data(sc, WPI_EEPROM_TYPE, &sc->type, + sizeof(sc->type))); + + sc->rev = le16toh(sc->rev); + DPRINTF(sc, WPI_DEBUG_EEPROM, "cap=%x rev=%x type=%x\n", sc->cap, + sc->rev, sc->type); + + /* Read the regulatory domain (4 ASCII characters.) */ + WPI_CHK(wpi_read_prom_data(sc, WPI_EEPROM_DOMAIN, sc->domain, + sizeof(sc->domain))); + + /* Read MAC address. */ + WPI_CHK(wpi_read_prom_data(sc, WPI_EEPROM_MAC, macaddr, + IEEE80211_ADDR_LEN)); + + /* Read the list of authorized channels. */ + for (i = 0; i < WPI_CHAN_BANDS_COUNT; i++) + WPI_CHK(wpi_read_eeprom_channels(sc, i)); + + /* Read the list of TX power groups. */ + for (i = 0; i < WPI_POWER_GROUPS_COUNT; i++) + WPI_CHK(wpi_read_eeprom_group(sc, i)); + +fail: wpi_apm_stop(sc); /* Power OFF adapter. */ + + DPRINTF(sc, WPI_DEBUG_TRACE, error ? TRACE_STR_END_ERR : TRACE_STR_END, + __func__); + + return error; +#undef WPI_CHK +} + +/* + * Translate EEPROM flags to net80211. + */ +static uint32_t +wpi_eeprom_channel_flags(struct wpi_eeprom_chan *channel) +{ + uint32_t nflags; + + nflags = 0; + if ((channel->flags & WPI_EEPROM_CHAN_ACTIVE) == 0) + nflags |= IEEE80211_CHAN_PASSIVE; + if ((channel->flags & WPI_EEPROM_CHAN_IBSS) == 0) + nflags |= IEEE80211_CHAN_NOADHOC; + if (channel->flags & WPI_EEPROM_CHAN_RADAR) { + nflags |= IEEE80211_CHAN_DFS; + /* XXX apparently IBSS may still be marked */ + nflags |= IEEE80211_CHAN_NOADHOC; + } + + /* XXX HOSTAP uses WPI_MODE_IBSS */ + if (nflags & IEEE80211_CHAN_NOADHOC) + nflags |= IEEE80211_CHAN_NOHOSTAP; + + return nflags; +} + +static void +wpi_read_eeprom_band(struct wpi_softc *sc, uint8_t n, int maxchans, + int *nchans, struct ieee80211_channel chans[]) +{ + struct wpi_eeprom_chan *channels = sc->eeprom_channels[n]; + const struct wpi_chan_band *band = &wpi_bands[n]; + uint32_t nflags; + uint8_t bands[IEEE80211_MODE_BYTES]; + uint8_t chan, i; + int error; + + memset(bands, 0, sizeof(bands)); + + if (n == 0) { + setbit(bands, IEEE80211_MODE_11B); + setbit(bands, IEEE80211_MODE_11G); + } else + setbit(bands, IEEE80211_MODE_11A); + + for (i = 0; i < band->nchan; i++) { + if (!(channels[i].flags & WPI_EEPROM_CHAN_VALID)) { + DPRINTF(sc, WPI_DEBUG_EEPROM, + "Channel Not Valid: %d, band %d\n", + band->chan[i],n); + continue; + } + + chan = band->chan[i]; + nflags = wpi_eeprom_channel_flags(&channels[i]); + error = ieee80211_add_channel(chans, maxchans, nchans, + chan, 0, channels[i].maxpwr, nflags, bands); + if (error != 0) + break; + + /* Save maximum allowed TX power for this channel. */ + sc->maxpwr[chan] = channels[i].maxpwr; + + DPRINTF(sc, WPI_DEBUG_EEPROM, + "adding chan %d flags=0x%x maxpwr=%d, offset %d\n", + chan, channels[i].flags, sc->maxpwr[chan], *nchans); + } +} + +/** + * Read the eeprom to find out what channels are valid for the given + * band and update net80211 with what we find. + */ +static int +wpi_read_eeprom_channels(struct wpi_softc *sc, uint8_t n) +{ + struct ieee80211com *ic = &sc->sc_ic; + const struct wpi_chan_band *band = &wpi_bands[n]; + int error; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); + + error = wpi_read_prom_data(sc, band->addr, &sc->eeprom_channels[n], + band->nchan * sizeof (struct wpi_eeprom_chan)); + if (error != 0) { + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__); + return error; + } + + wpi_read_eeprom_band(sc, n, IEEE80211_CHAN_MAX, &ic->ic_nchans, + ic->ic_channels); + + ieee80211_sort_channels(ic->ic_channels, ic->ic_nchans); + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); + + return 0; +} + +static struct wpi_eeprom_chan * +wpi_find_eeprom_channel(struct wpi_softc *sc, struct ieee80211_channel *c) +{ + int i, j; + + for (j = 0; j < WPI_CHAN_BANDS_COUNT; j++) + for (i = 0; i < wpi_bands[j].nchan; i++) + if (wpi_bands[j].chan[i] == c->ic_ieee && + ((j == 0) ^ IEEE80211_IS_CHAN_A(c)) == 1) + return &sc->eeprom_channels[j][i]; + + return NULL; +} + +static void +wpi_getradiocaps(struct ieee80211com *ic, + int maxchans, int *nchans, struct ieee80211_channel chans[]) +{ + struct wpi_softc *sc = ic->ic_softc; + int i; + + /* Parse the list of authorized channels. */ + for (i = 0; i < WPI_CHAN_BANDS_COUNT && *nchans < maxchans; i++) + wpi_read_eeprom_band(sc, i, maxchans, nchans, chans); +} + +/* + * Enforce flags read from EEPROM. + */ +static int +wpi_setregdomain(struct ieee80211com *ic, struct ieee80211_regdomain *rd, + int nchan, struct ieee80211_channel chans[]) +{ + struct wpi_softc *sc = ic->ic_softc; + int i; + + for (i = 0; i < nchan; i++) { + struct ieee80211_channel *c = &chans[i]; + struct wpi_eeprom_chan *channel; + + channel = wpi_find_eeprom_channel(sc, c); + if (channel == NULL) { + ic_printf(ic, "%s: invalid channel %u freq %u/0x%x\n", + __func__, c->ic_ieee, c->ic_freq, c->ic_flags); + return EINVAL; + } + c->ic_flags |= wpi_eeprom_channel_flags(channel); + } + + return 0; +} + +static int +wpi_read_eeprom_group(struct wpi_softc *sc, uint8_t n) +{ + struct wpi_power_group *group = &sc->groups[n]; + struct wpi_eeprom_group rgroup; + int i, error; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); + + if ((error = wpi_read_prom_data(sc, WPI_EEPROM_POWER_GRP + n * 32, + &rgroup, sizeof rgroup)) != 0) { + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__); + return error; + } + + /* Save TX power group information. */ + group->chan = rgroup.chan; + group->maxpwr = rgroup.maxpwr; + /* Retrieve temperature at which the samples were taken. */ + group->temp = (int16_t)le16toh(rgroup.temp); + + DPRINTF(sc, WPI_DEBUG_EEPROM, + "power group %d: chan=%d maxpwr=%d temp=%d\n", n, group->chan, + group->maxpwr, group->temp); + + for (i = 0; i < WPI_SAMPLES_COUNT; i++) { + group->samples[i].index = rgroup.samples[i].index; + group->samples[i].power = rgroup.samples[i].power; + + DPRINTF(sc, WPI_DEBUG_EEPROM, + "\tsample %d: index=%d power=%d\n", i, + group->samples[i].index, group->samples[i].power); + } + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); + + return 0; +} + +static __inline uint8_t +wpi_add_node_entry_adhoc(struct wpi_softc *sc) +{ + uint8_t newid = WPI_ID_IBSS_MIN; + + for (; newid <= WPI_ID_IBSS_MAX; newid++) { + if ((sc->nodesmsk & (1 << newid)) == 0) { + sc->nodesmsk |= 1 << newid; + return newid; + } + } + + return WPI_ID_UNDEFINED; +} + +static __inline uint8_t +wpi_add_node_entry_sta(struct wpi_softc *sc) +{ + sc->nodesmsk |= 1 << WPI_ID_BSS; + + return WPI_ID_BSS; +} + +static __inline int +wpi_check_node_entry(struct wpi_softc *sc, uint8_t id) +{ + if (id == WPI_ID_UNDEFINED) + return 0; + + return (sc->nodesmsk >> id) & 1; +} + +static __inline void +wpi_clear_node_table(struct wpi_softc *sc) +{ + sc->nodesmsk = 0; +} + +static __inline void +wpi_del_node_entry(struct wpi_softc *sc, uint8_t id) +{ + sc->nodesmsk &= ~(1 << id); +} + +static struct ieee80211_node * +wpi_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) +{ + struct wpi_node *wn; + + wn = kmalloc(sizeof (struct wpi_node), M_80211_NODE, + M_INTWAIT | M_ZERO); + + if (wn == NULL) + return NULL; + + wn->id = WPI_ID_UNDEFINED; + + return &wn->ni; +} + +static void +wpi_node_free(struct ieee80211_node *ni) +{ + struct wpi_softc *sc = ni->ni_ic->ic_softc; + struct wpi_node *wn = WPI_NODE(ni); + + if (wn->id != WPI_ID_UNDEFINED) { + WPI_NT_LOCK(sc); + if (wpi_check_node_entry(sc, wn->id)) { + wpi_del_node_entry(sc, wn->id); + wpi_del_node(sc, ni); + } + WPI_NT_UNLOCK(sc); + } + + sc->sc_node_free(ni); +} + +static __inline int +wpi_check_bss_filter(struct wpi_softc *sc) +{ + return (sc->rxon.filter & htole32(WPI_FILTER_BSS)) != 0; +} + +static void +wpi_ibss_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, int subtype, + const struct ieee80211_rx_stats *rxs, + int rssi, int nf) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct wpi_softc *sc = vap->iv_ic->ic_softc; + struct wpi_vap *wvp = WPI_VAP(vap); + uint64_t ni_tstamp, rx_tstamp; + + wvp->wv_recv_mgmt(ni, m, subtype, rxs, rssi, nf); + + if (vap->iv_state == IEEE80211_S_RUN && + (subtype == IEEE80211_FC0_SUBTYPE_BEACON || + subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)) { + ni_tstamp = le64toh(ni->ni_tstamp.tsf); + rx_tstamp = le64toh(sc->rx_tstamp); + + if (ni_tstamp >= rx_tstamp) { + DPRINTF(sc, WPI_DEBUG_STATE, + "ibss merge, tsf %ju tstamp %ju\n", + (uintmax_t)rx_tstamp, (uintmax_t)ni_tstamp); + (void) ieee80211_ibss_merge(ni); + } + } +} + +static void +wpi_restore_node(void *arg, struct ieee80211_node *ni) +{ + struct wpi_softc *sc = arg; + struct wpi_node *wn = WPI_NODE(ni); + int error; + + WPI_NT_LOCK(sc); + if (wn->id != WPI_ID_UNDEFINED) { + wn->id = WPI_ID_UNDEFINED; + if ((error = wpi_add_ibss_node(sc, ni)) != 0) { + device_printf(sc->sc_dev, + "%s: could not add IBSS node, error %d\n", + __func__, error); + } + } + WPI_NT_UNLOCK(sc); +} + +static void +wpi_restore_node_table(struct wpi_softc *sc, struct wpi_vap *wvp) +{ + struct ieee80211com *ic = &sc->sc_ic; + + /* Set group keys once. */ + WPI_NT_LOCK(sc); + wvp->wv_gtk = 0; + WPI_NT_UNLOCK(sc); + + ieee80211_iterate_nodes(&ic->ic_sta, wpi_restore_node, sc); + ieee80211_crypto_reload_keys(ic); +} + +/** + * Called by net80211 when ever there is a change to 80211 state machine + */ +static int +wpi_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + struct wpi_vap *wvp = WPI_VAP(vap); + struct ieee80211com *ic = vap->iv_ic; + struct wpi_softc *sc = ic->ic_softc; + int error = 0; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); + + WPI_TXQ_LOCK(sc); + if (nstate > IEEE80211_S_INIT && sc->sc_running == 0) { + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__); + WPI_TXQ_UNLOCK(sc); + + return ENXIO; + } + WPI_TXQ_UNLOCK(sc); + + DPRINTF(sc, WPI_DEBUG_STATE, "%s: %s -> %s\n", __func__, + ieee80211_state_name[vap->iv_state], + ieee80211_state_name[nstate]); + + if (vap->iv_state == IEEE80211_S_RUN && nstate < IEEE80211_S_RUN) { + if ((error = wpi_set_pslevel(sc, 0, 0, 1)) != 0) { + device_printf(sc->sc_dev, + "%s: could not set power saving level\n", + __func__); + return error; + } + + wpi_set_led(sc, WPI_LED_LINK, 1, 0); + } + + switch (nstate) { + case IEEE80211_S_SCAN: + WPI_RXON_LOCK(sc); + if (wpi_check_bss_filter(sc) != 0) { + sc->rxon.filter &= ~htole32(WPI_FILTER_BSS); + if ((error = wpi_send_rxon(sc, 0, 1)) != 0) { + device_printf(sc->sc_dev, + "%s: could not send RXON\n", __func__); + } + } + WPI_RXON_UNLOCK(sc); + break; + + case IEEE80211_S_ASSOC: + if (vap->iv_state != IEEE80211_S_RUN) + break; + /* FALLTHROUGH */ + case IEEE80211_S_AUTH: + /* + * NB: do not optimize AUTH -> AUTH state transmission - + * this will break powersave with non-QoS AP! + */ + + /* + * The node must be registered in the firmware before auth. + * Also the associd must be cleared on RUN -> ASSOC + * transitions. + */ + if ((error = wpi_auth(sc, vap)) != 0) { + device_printf(sc->sc_dev, + "%s: could not move to AUTH state, error %d\n", + __func__, error); + } + break; + + case IEEE80211_S_RUN: + /* + * RUN -> RUN transition: + * STA mode: Just restart the timers. + * IBSS mode: Process IBSS merge. + */ + if (vap->iv_state == IEEE80211_S_RUN) { + if (vap->iv_opmode != IEEE80211_M_IBSS) { + WPI_RXON_LOCK(sc); + wpi_calib_timeout(sc); + WPI_RXON_UNLOCK(sc); + break; + } else { + /* + * Drop the BSS_FILTER bit + * (there is no another way to change bssid). + */ + WPI_RXON_LOCK(sc); + sc->rxon.filter &= ~htole32(WPI_FILTER_BSS); + if ((error = wpi_send_rxon(sc, 0, 1)) != 0) { + device_printf(sc->sc_dev, + "%s: could not send RXON\n", + __func__); + } + WPI_RXON_UNLOCK(sc); + + /* Restore all what was lost. */ + wpi_restore_node_table(sc, wvp); + + /* XXX set conditionally? */ + wpi_updateedca(ic); + } + } + + /* + * !RUN -> RUN requires setting the association id + * which is done with a firmware cmd. We also defer + * starting the timers until that work is done. + */ + if ((error = wpi_run(sc, vap)) != 0) { + device_printf(sc->sc_dev, + "%s: could not move to RUN state\n", __func__); + } + break; + + default: + break; + } + if (error != 0) { + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__); + return error; + } + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); + + return wvp->wv_newstate(vap, nstate, arg); +} + +static void +wpi_calib_timeout(void *arg) +{ + struct wpi_softc *sc = arg; + + if (wpi_check_bss_filter(sc) == 0) + return; + + wpi_power_calibration(sc); + + callout_reset(&sc->calib_to, 60*hz, wpi_calib_timeout, sc); +} + +static __inline uint8_t +rate2plcp(const uint8_t rate) +{ + switch (rate) { + case 12: return 0xd; + case 18: return 0xf; + case 24: return 0x5; + case 36: return 0x7; + case 48: return 0x9; + case 72: return 0xb; + case 96: return 0x1; + case 108: return 0x3; + case 2: return 10; + case 4: return 20; + case 11: return 55; + case 22: return 110; + default: return 0; + } +} + +static __inline uint8_t +plcp2rate(const uint8_t plcp) +{ + switch (plcp) { + case 0xd: return 12; + case 0xf: return 18; + case 0x5: return 24; + case 0x7: return 36; + case 0x9: return 48; + case 0xb: return 72; + case 0x1: return 96; + case 0x3: return 108; + case 10: return 2; + case 20: return 4; + case 55: return 11; + case 110: return 22; + default: return 0; + } +} + +/* Quickly determine if a given rate is CCK or OFDM. */ +#define WPI_RATE_IS_OFDM(rate) ((rate) >= 12 && (rate) != 22) + +static void +wpi_rx_done(struct wpi_softc *sc, struct wpi_rx_desc *desc, + struct wpi_rx_data *data) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct wpi_rx_ring *ring = &sc->rxq; + struct wpi_rx_stat *stat; + struct wpi_rx_head *head; + struct wpi_rx_tail *tail; + struct ieee80211_frame *wh; + struct ieee80211_node *ni; + struct mbuf *m, *m1; + bus_addr_t paddr; + uint32_t flags; + uint16_t len; + int error; + + stat = (struct wpi_rx_stat *)(desc + 1); + + if (__predict_false(stat->len > WPI_STAT_MAXLEN)) { + device_printf(sc->sc_dev, "invalid RX statistic header\n"); + goto fail1; + } + + bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD); + head = (struct wpi_rx_head *)((caddr_t)(stat + 1) + stat->len); + len = le16toh(head->len); + tail = (struct wpi_rx_tail *)((caddr_t)(head + 1) + len); + flags = le32toh(tail->flags); + + DPRINTF(sc, WPI_DEBUG_RECV, "%s: idx %d len %d stat len %u rssi %d" + " rate %x chan %d tstamp %ju\n", __func__, ring->cur, + le32toh(desc->len), len, (int8_t)stat->rssi, + head->plcp, head->chan, (uintmax_t)le64toh(tail->tstamp)); + + /* Discard frames with a bad FCS early. */ + if ((flags & WPI_RX_NOERROR) != WPI_RX_NOERROR) { + DPRINTF(sc, WPI_DEBUG_RECV, "%s: RX flags error %x\n", + __func__, flags); + goto fail1; + } + /* Discard frames that are too short. */ + if (len < sizeof (struct ieee80211_frame_ack)) { + DPRINTF(sc, WPI_DEBUG_RECV, "%s: frame too short: %d\n", + __func__, len); + goto fail1; + } + + m1 = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE); + if (__predict_false(m1 == NULL)) { + DPRINTF(sc, WPI_DEBUG_ANY, "%s: no mbuf to restock ring\n", + __func__); + goto fail1; + } + bus_dmamap_unload(ring->data_dmat, data->map); + + error = bus_dmamap_load(ring->data_dmat, data->map, mtod(m1, void *), + MJUMPAGESIZE, wpi_dma_map_addr, &paddr, BUS_DMA_NOWAIT); + if (__predict_false(error != 0 && error != EFBIG)) { + device_printf(sc->sc_dev, + "%s: bus_dmamap_load failed, error %d\n", __func__, error); + m_freem(m1); + + /* Try to reload the old mbuf. */ + error = bus_dmamap_load(ring->data_dmat, data->map, + mtod(data->m, void *), MJUMPAGESIZE, wpi_dma_map_addr, + &paddr, BUS_DMA_NOWAIT); + if (error != 0 && error != EFBIG) { + panic("%s: could not load old RX mbuf", __func__); + } + /* Physical address may have changed. */ + ring->desc[ring->cur] = htole32(paddr); + bus_dmamap_sync(ring->data_dmat, ring->desc_dma.map, + BUS_DMASYNC_PREWRITE); + goto fail1; + } + + m = data->m; + data->m = m1; + /* Update RX descriptor. */ + ring->desc[ring->cur] = htole32(paddr); + bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, + BUS_DMASYNC_PREWRITE); + + /* Finalize mbuf. */ + m->m_data = (caddr_t)(head + 1); + m->m_pkthdr.len = m->m_len = len; + + /* Grab a reference to the source node. */ + wh = mtod(m, struct ieee80211_frame *); + + if ((wh->i_fc[1] & IEEE80211_FC1_PROTECTED) && + (flags & WPI_RX_CIPHER_MASK) == WPI_RX_CIPHER_CCMP) { + /* Check whether decryption was successful or not. */ + if ((flags & WPI_RX_DECRYPT_MASK) != WPI_RX_DECRYPT_OK) { + DPRINTF(sc, WPI_DEBUG_RECV, + "CCMP decryption failed 0x%x\n", flags); + goto fail2; + } + m->m_flags |= M_WEP; + } + + if (len >= sizeof(struct ieee80211_frame_min)) + ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh); + else + ni = NULL; + + sc->rx_tstamp = tail->tstamp; + + if (ieee80211_radiotap_active(ic)) { + struct wpi_rx_radiotap_header *tap = &sc->sc_rxtap; + + tap->wr_flags = 0; + if (head->flags & htole16(WPI_STAT_FLAG_SHPREAMBLE)) + tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; + tap->wr_dbm_antsignal = (int8_t)(stat->rssi + WPI_RSSI_OFFSET); + tap->wr_dbm_antnoise = WPI_RSSI_OFFSET; + tap->wr_tsft = tail->tstamp; + tap->wr_antenna = (le16toh(head->flags) >> 4) & 0xf; + tap->wr_rate = plcp2rate(head->plcp); + } + + WPI_UNLOCK(sc); + + /* Send the frame to the 802.11 layer. */ + if (ni != NULL) { + (void)ieee80211_input(ni, m, stat->rssi, WPI_RSSI_OFFSET); + /* Node is no longer needed. */ + ieee80211_free_node(ni); + } else + (void)ieee80211_input_all(ic, m, stat->rssi, WPI_RSSI_OFFSET); + + WPI_LOCK(sc); + + return; + +fail2: m_freem(m); + +#if defined(__DragonFly__) +fail1: ; /* not implemented */ +#else +fail1: counter_u64_add(ic->ic_ierrors, 1); +#endif +} + +static void +wpi_rx_statistics(struct wpi_softc *sc, struct wpi_rx_desc *desc, + struct wpi_rx_data *data) +{ + /* Ignore */ +} + +static void +wpi_tx_done(struct wpi_softc *sc, struct wpi_rx_desc *desc) +{ + struct wpi_tx_ring *ring = &sc->txq[desc->qid & 0x3]; + struct wpi_tx_data *data = &ring->data[desc->idx]; + struct wpi_tx_stat *stat = (struct wpi_tx_stat *)(desc + 1); + struct mbuf *m; + struct ieee80211_node *ni; + struct ieee80211vap *vap; + uint32_t status = le32toh(stat->status); + int ackfailcnt = stat->ackfailcnt / WPI_NTRIES_DEFAULT; + + KASSERT(data->ni != NULL, ("no node")); + KASSERT(data->m != NULL, ("no mbuf")); + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); + + DPRINTF(sc, WPI_DEBUG_XMIT, "%s: " + "qid %d idx %d retries %d btkillcnt %d rate %x duration %d " + "status %x\n", __func__, desc->qid, desc->idx, stat->ackfailcnt, + stat->btkillcnt, stat->rate, le32toh(stat->duration), status); + + /* Unmap and free mbuf. */ + bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(ring->data_dmat, data->map); + m = data->m, data->m = NULL; + ni = data->ni, data->ni = NULL; + vap = ni->ni_vap; + + /* + * Update rate control statistics for the node. + */ + if (status & WPI_TX_STATUS_FAIL) { + ieee80211_ratectl_tx_complete(vap, ni, + IEEE80211_RATECTL_TX_FAILURE, &ackfailcnt, NULL); + } else + ieee80211_ratectl_tx_complete(vap, ni, + IEEE80211_RATECTL_TX_SUCCESS, &ackfailcnt, NULL); + + ieee80211_tx_complete(ni, m, (status & WPI_TX_STATUS_FAIL) != 0); + + WPI_TXQ_STATE_LOCK(sc); + if (--ring->queued > 0) + callout_reset(&sc->tx_timeout, 5*hz, wpi_tx_timeout, sc); + else + callout_stop(&sc->tx_timeout); + WPI_TXQ_STATE_UNLOCK(sc); + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); +} + +/* + * Process a "command done" firmware notification. This is where we wakeup + * processes waiting for a synchronous command completion. + */ +static void +wpi_cmd_done(struct wpi_softc *sc, struct wpi_rx_desc *desc) +{ + struct wpi_tx_ring *ring = &sc->txq[WPI_CMD_QUEUE_NUM]; + struct wpi_tx_data *data; + struct wpi_tx_cmd *cmd; + + DPRINTF(sc, WPI_DEBUG_CMD, "cmd notification qid %x idx %d flags %x " + "type %s len %d\n", desc->qid, desc->idx, + desc->flags, wpi_cmd_str(desc->type), + le32toh(desc->len)); + + if ((desc->qid & WPI_RX_DESC_QID_MSK) != WPI_CMD_QUEUE_NUM) + return; /* Not a command ack. */ + + KASSERT(ring->queued == 0, ("ring->queued must be 0")); + + data = &ring->data[desc->idx]; + cmd = &ring->cmd[desc->idx]; + + /* If the command was mapped in an mbuf, free it. */ + if (data->m != NULL) { + bus_dmamap_sync(ring->data_dmat, data->map, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(ring->data_dmat, data->map); + m_freem(data->m); + data->m = NULL; + } + + wakeup(cmd); + + if (desc->type == WPI_CMD_SET_POWER_MODE) { + struct wpi_pmgt_cmd *pcmd = (struct wpi_pmgt_cmd *)cmd->data; + + bus_dmamap_sync(ring->data_dmat, ring->cmd_dma.map, + BUS_DMASYNC_POSTREAD); + + WPI_TXQ_LOCK(sc); + if (le16toh(pcmd->flags) & WPI_PS_ALLOW_SLEEP) { + sc->sc_update_rx_ring = wpi_update_rx_ring_ps; + sc->sc_update_tx_ring = wpi_update_tx_ring_ps; + } else { + sc->sc_update_rx_ring = wpi_update_rx_ring; + sc->sc_update_tx_ring = wpi_update_tx_ring; + } + WPI_TXQ_UNLOCK(sc); + } +} + +static void +wpi_notif_intr(struct wpi_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + uint32_t hw; + + bus_dmamap_sync(sc->shared_dma.tag, sc->shared_dma.map, + BUS_DMASYNC_POSTREAD); + + hw = le32toh(sc->shared->next) & 0xfff; + hw = (hw == 0) ? WPI_RX_RING_COUNT - 1 : hw - 1; + + while (sc->rxq.cur != hw) { + sc->rxq.cur = (sc->rxq.cur + 1) % WPI_RX_RING_COUNT; + + struct wpi_rx_data *data = &sc->rxq.data[sc->rxq.cur]; + struct wpi_rx_desc *desc; + + bus_dmamap_sync(sc->rxq.data_dmat, data->map, + BUS_DMASYNC_POSTREAD); + desc = mtod(data->m, struct wpi_rx_desc *); + + DPRINTF(sc, WPI_DEBUG_NOTIFY, + "%s: cur=%d; qid %x idx %d flags %x type %d(%s) len %d\n", + __func__, sc->rxq.cur, desc->qid, desc->idx, desc->flags, + desc->type, wpi_cmd_str(desc->type), le32toh(desc->len)); + + if (!(desc->qid & WPI_UNSOLICITED_RX_NOTIF)) { + /* Reply to a command. */ + wpi_cmd_done(sc, desc); + } + + switch (desc->type) { + case WPI_RX_DONE: + /* An 802.11 frame has been received. */ + wpi_rx_done(sc, desc, data); + + if (__predict_false(sc->sc_running == 0)) { + /* wpi_stop() was called. */ + return; + } + + break; + + case WPI_TX_DONE: + /* An 802.11 frame has been transmitted. */ + wpi_tx_done(sc, desc); + break; + + case WPI_RX_STATISTICS: + case WPI_BEACON_STATISTICS: + wpi_rx_statistics(sc, desc, data); + break; + + case WPI_BEACON_MISSED: + { + struct wpi_beacon_missed *miss = + (struct wpi_beacon_missed *)(desc + 1); + uint32_t expected, misses, received, threshold; + + bus_dmamap_sync(sc->rxq.data_dmat, data->map, + BUS_DMASYNC_POSTREAD); + + misses = le32toh(miss->consecutive); + expected = le32toh(miss->expected); + received = le32toh(miss->received); + threshold = MAX(2, vap->iv_bmissthreshold); + + DPRINTF(sc, WPI_DEBUG_BMISS, + "%s: beacons missed %u(%u) (received %u/%u)\n", + __func__, misses, le32toh(miss->total), received, + expected); + + if (misses >= threshold || + (received == 0 && expected >= threshold)) { + WPI_RXON_LOCK(sc); + if (callout_pending(&sc->scan_timeout)) { + wpi_cmd(sc, WPI_CMD_SCAN_ABORT, NULL, + 0, 1); + } + WPI_RXON_UNLOCK(sc); + if (vap->iv_state == IEEE80211_S_RUN && + (ic->ic_flags & IEEE80211_F_SCAN) == 0) + ieee80211_beacon_miss(ic); + } + + break; + } +#ifdef WPI_DEBUG + case WPI_BEACON_SENT: + { + struct wpi_tx_stat *stat = + (struct wpi_tx_stat *)(desc + 1); + uint64_t *tsf = (uint64_t *)(stat + 1); + uint32_t *mode = (uint32_t *)(tsf + 1); + + bus_dmamap_sync(sc->rxq.data_dmat, data->map, + BUS_DMASYNC_POSTREAD); + + DPRINTF(sc, WPI_DEBUG_BEACON, + "beacon sent: rts %u, ack %u, btkill %u, rate %u, " + "duration %u, status %x, tsf %ju, mode %x\n", + stat->rtsfailcnt, stat->ackfailcnt, + stat->btkillcnt, stat->rate, le32toh(stat->duration), + le32toh(stat->status), le64toh(*tsf), + le32toh(*mode)); + + break; + } +#endif + case WPI_UC_READY: + { + struct wpi_ucode_info *uc = + (struct wpi_ucode_info *)(desc + 1); + + /* The microcontroller is ready. */ + bus_dmamap_sync(sc->rxq.data_dmat, data->map, + BUS_DMASYNC_POSTREAD); + DPRINTF(sc, WPI_DEBUG_RESET, + "microcode alive notification version=%d.%d " + "subtype=%x alive=%x\n", uc->major, uc->minor, + uc->subtype, le32toh(uc->valid)); + + if (le32toh(uc->valid) != 1) { + device_printf(sc->sc_dev, + "microcontroller initialization failed\n"); + wpi_stop_locked(sc); + return; + } + /* Save the address of the error log in SRAM. */ + sc->errptr = le32toh(uc->errptr); + break; + } + case WPI_STATE_CHANGED: + { + bus_dmamap_sync(sc->rxq.data_dmat, data->map, + BUS_DMASYNC_POSTREAD); + + uint32_t *status = (uint32_t *)(desc + 1); + + DPRINTF(sc, WPI_DEBUG_STATE, "state changed to %x\n", + le32toh(*status)); + + if (le32toh(*status) & 1) { + WPI_NT_LOCK(sc); + wpi_clear_node_table(sc); + WPI_NT_UNLOCK(sc); + ieee80211_runtask(ic, + &sc->sc_radiooff_task); + return; + } + break; + } +#ifdef WPI_DEBUG + case WPI_START_SCAN: + { + bus_dmamap_sync(sc->rxq.data_dmat, data->map, + BUS_DMASYNC_POSTREAD); + + struct wpi_start_scan *scan = + (struct wpi_start_scan *)(desc + 1); + DPRINTF(sc, WPI_DEBUG_SCAN, + "%s: scanning channel %d status %x\n", + __func__, scan->chan, le32toh(scan->status)); + + break; + } +#endif + case WPI_STOP_SCAN: + { + bus_dmamap_sync(sc->rxq.data_dmat, data->map, + BUS_DMASYNC_POSTREAD); + + struct wpi_stop_scan *scan = + (struct wpi_stop_scan *)(desc + 1); + + DPRINTF(sc, WPI_DEBUG_SCAN, + "scan finished nchan=%d status=%d chan=%d\n", + scan->nchan, scan->status, scan->chan); + + WPI_RXON_LOCK(sc); + callout_stop(&sc->scan_timeout); + WPI_RXON_UNLOCK(sc); + if (scan->status == WPI_SCAN_ABORTED) + ieee80211_cancel_scan(vap); + else + ieee80211_scan_next(vap); + break; + } + } + + if (sc->rxq.cur % 8 == 0) { + /* Tell the firmware what we have processed. */ + sc->sc_update_rx_ring(sc); + } + } +} + +/* + * Process an INT_WAKEUP interrupt raised when the microcontroller wakes up + * from power-down sleep mode. + */ +static void +wpi_wakeup_intr(struct wpi_softc *sc) +{ + int qid; + + DPRINTF(sc, WPI_DEBUG_PWRSAVE, + "%s: ucode wakeup from power-down sleep\n", __func__); + + /* Wakeup RX and TX rings. */ + if (sc->rxq.update) { + sc->rxq.update = 0; + wpi_update_rx_ring(sc); + } + WPI_TXQ_LOCK(sc); + for (qid = 0; qid < WPI_DRV_NTXQUEUES; qid++) { + struct wpi_tx_ring *ring = &sc->txq[qid]; + + if (ring->update) { + ring->update = 0; + wpi_update_tx_ring(sc, ring); + } + } + WPI_CLRBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_MAC_ACCESS_REQ); + WPI_TXQ_UNLOCK(sc); +} + +/* + * This function prints firmware registers + */ +#ifdef WPI_DEBUG +static void +wpi_debug_registers(struct wpi_softc *sc) +{ + size_t i; + static const uint32_t csr_tbl[] = { + WPI_HW_IF_CONFIG, + WPI_INT, + WPI_INT_MASK, + WPI_FH_INT, + WPI_GPIO_IN, + WPI_RESET, + WPI_GP_CNTRL, + WPI_EEPROM, + WPI_EEPROM_GP, + WPI_GIO, + WPI_UCODE_GP1, + WPI_UCODE_GP2, + WPI_GIO_CHICKEN, + WPI_ANA_PLL, + WPI_DBG_HPET_MEM, + }; + static const uint32_t prph_tbl[] = { + WPI_APMG_CLK_CTRL, + WPI_APMG_PS, + WPI_APMG_PCI_STT, + WPI_APMG_RFKILL, + }; + + DPRINTF(sc, WPI_DEBUG_REGISTER,"%s","\n"); + + for (i = 0; i < nitems(csr_tbl); i++) { + DPRINTF(sc, WPI_DEBUG_REGISTER, " %-18s: 0x%08x ", + wpi_get_csr_string(csr_tbl[i]), WPI_READ(sc, csr_tbl[i])); + + if ((i + 1) % 2 == 0) + DPRINTF(sc, WPI_DEBUG_REGISTER, "\n"); + } + DPRINTF(sc, WPI_DEBUG_REGISTER, "\n\n"); + + if (wpi_nic_lock(sc) == 0) { + for (i = 0; i < nitems(prph_tbl); i++) { + DPRINTF(sc, WPI_DEBUG_REGISTER, " %-18s: 0x%08x ", + wpi_get_prph_string(prph_tbl[i]), + wpi_prph_read(sc, prph_tbl[i])); + + if ((i + 1) % 2 == 0) + DPRINTF(sc, WPI_DEBUG_REGISTER, "\n"); + } + DPRINTF(sc, WPI_DEBUG_REGISTER, "\n"); + wpi_nic_unlock(sc); + } else { + DPRINTF(sc, WPI_DEBUG_REGISTER, + "Cannot access internal registers.\n"); + } +} +#endif + +/* + * Dump the error log of the firmware when a firmware panic occurs. Although + * we can't debug the firmware because it is neither open source nor free, it + * can help us to identify certain classes of problems. + */ +static void +wpi_fatal_intr(struct wpi_softc *sc) +{ + struct wpi_fw_dump dump; + uint32_t i, offset, count; + + /* Check that the error log address is valid. */ + if (sc->errptr < WPI_FW_DATA_BASE || + sc->errptr + sizeof (dump) > + WPI_FW_DATA_BASE + WPI_FW_DATA_MAXSZ) { + kprintf("%s: bad firmware error log address 0x%08x\n", __func__, + sc->errptr); + return; + } + if (wpi_nic_lock(sc) != 0) { + kprintf("%s: could not read firmware error log\n", __func__); + return; + } + /* Read number of entries in the log. */ + count = wpi_mem_read(sc, sc->errptr); + if (count == 0 || count * sizeof (dump) > WPI_FW_DATA_MAXSZ) { + kprintf("%s: invalid count field (count = %u)\n", __func__, + count); + wpi_nic_unlock(sc); + return; + } + /* Skip "count" field. */ + offset = sc->errptr + sizeof (uint32_t); + kprintf("firmware error log (count = %u):\n", count); + for (i = 0; i < count; i++) { + wpi_mem_read_region_4(sc, offset, (uint32_t *)&dump, + sizeof (dump) / sizeof (uint32_t)); + + kprintf(" error type = \"%s\" (0x%08X)\n", + (dump.desc < nitems(wpi_fw_errmsg)) ? + wpi_fw_errmsg[dump.desc] : "UNKNOWN", + dump.desc); + kprintf(" error data = 0x%08X\n", + dump.data); + kprintf(" branch link = 0x%08X%08X\n", + dump.blink[0], dump.blink[1]); + kprintf(" interrupt link = 0x%08X%08X\n", + dump.ilink[0], dump.ilink[1]); + kprintf(" time = %u\n", dump.time); + + offset += sizeof (dump); + } + wpi_nic_unlock(sc); + /* Dump driver status (TX and RX rings) while we're here. */ + kprintf("driver status:\n"); + WPI_TXQ_LOCK(sc); + for (i = 0; i < WPI_DRV_NTXQUEUES; i++) { + struct wpi_tx_ring *ring = &sc->txq[i]; + kprintf(" tx ring %2d: qid=%-2d cur=%-3d queued=%-3d\n", + i, ring->qid, ring->cur, ring->queued); + } + WPI_TXQ_UNLOCK(sc); + kprintf(" rx ring: cur=%d\n", sc->rxq.cur); +} + +static void +wpi_intr(void *arg) +{ + struct wpi_softc *sc = arg; + uint32_t r1, r2; + + WPI_LOCK(sc); + + /* Disable interrupts. */ + WPI_WRITE(sc, WPI_INT_MASK, 0); + + r1 = WPI_READ(sc, WPI_INT); + + if (__predict_false(r1 == 0xffffffff || + (r1 & 0xfffffff0) == 0xa5a5a5a0)) + goto end; /* Hardware gone! */ + + r2 = WPI_READ(sc, WPI_FH_INT); + + DPRINTF(sc, WPI_DEBUG_INTR, "%s: reg1=0x%08x reg2=0x%08x\n", __func__, + r1, r2); + + if (r1 == 0 && r2 == 0) + goto done; /* Interrupt not for us. */ + + /* Acknowledge interrupts. */ + WPI_WRITE(sc, WPI_INT, r1); + WPI_WRITE(sc, WPI_FH_INT, r2); + + if (__predict_false(r1 & (WPI_INT_SW_ERR | WPI_INT_HW_ERR))) { + struct ieee80211com *ic = &sc->sc_ic; + + device_printf(sc->sc_dev, "fatal firmware error\n"); +#ifdef WPI_DEBUG + wpi_debug_registers(sc); +#endif + wpi_fatal_intr(sc); + DPRINTF(sc, WPI_DEBUG_HW, + "(%s)\n", (r1 & WPI_INT_SW_ERR) ? "(Software Error)" : + "(Hardware Error)"); + ieee80211_restart_all(ic); + goto end; + } + + if ((r1 & (WPI_INT_FH_RX | WPI_INT_SW_RX)) || + (r2 & WPI_FH_INT_RX)) + wpi_notif_intr(sc); + + if (r1 & WPI_INT_ALIVE) + wakeup(sc); /* Firmware is alive. */ + + if (r1 & WPI_INT_WAKEUP) + wpi_wakeup_intr(sc); + +done: + /* Re-enable interrupts. */ + if (__predict_true(sc->sc_running)) + WPI_WRITE(sc, WPI_INT_MASK, WPI_INT_MASK_DEF); + +end: WPI_UNLOCK(sc); +} + +static void +wpi_free_txfrags(struct wpi_softc *sc, uint16_t ac) +{ + struct wpi_tx_ring *ring; + struct wpi_tx_data *data; + uint8_t cur; + + WPI_TXQ_LOCK(sc); + ring = &sc->txq[ac]; + + while (ring->pending != 0) { + ring->pending--; + cur = (ring->cur + ring->pending) % WPI_TX_RING_COUNT; + data = &ring->data[cur]; + + bus_dmamap_sync(ring->data_dmat, data->map, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(ring->data_dmat, data->map); + m_freem(data->m); + data->m = NULL; + + ieee80211_node_decref(data->ni); + data->ni = NULL; + } + + WPI_TXQ_UNLOCK(sc); +} + +static int +wpi_cmd2(struct wpi_softc *sc, struct wpi_buf *buf) +{ + struct ieee80211_frame *wh; + struct wpi_tx_cmd *cmd; + struct wpi_tx_data *data; + struct wpi_tx_desc *desc; + struct wpi_tx_ring *ring; + struct mbuf *m1; + bus_dma_segment_t *seg, segs[WPI_MAX_SCATTER]; + uint8_t cur, pad; + uint16_t hdrlen; + int error, i, nsegs, totlen, frag; + + WPI_TXQ_LOCK(sc); + + KASSERT(buf->size <= sizeof(buf->data), ("buffer overflow")); + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); + + if (__predict_false(sc->sc_running == 0)) { + /* wpi_stop() was called */ + error = ENETDOWN; + goto end; + } + + wh = mtod(buf->m, struct ieee80211_frame *); + hdrlen = ieee80211_anyhdrsize(wh); + totlen = buf->m->m_pkthdr.len; + frag = ((buf->m->m_flags & (M_FRAG | M_LASTFRAG)) == M_FRAG); + + if (__predict_false(totlen < sizeof(struct ieee80211_frame_min))) { + error = EINVAL; + goto end; + } + + if (hdrlen & 3) { + /* First segment length must be a multiple of 4. */ + pad = 4 - (hdrlen & 3); + } else + pad = 0; + + ring = &sc->txq[buf->ac]; + cur = (ring->cur + ring->pending) % WPI_TX_RING_COUNT; + desc = &ring->desc[cur]; + data = &ring->data[cur]; + + /* Prepare TX firmware command. */ + cmd = &ring->cmd[cur]; + cmd->code = buf->code; + cmd->flags = 0; + cmd->qid = ring->qid; + cmd->idx = cur; + + memcpy(cmd->data, buf->data, buf->size); + + /* Save and trim IEEE802.11 header. */ + memcpy((uint8_t *)(cmd->data + buf->size), wh, hdrlen); + m_adj(buf->m, hdrlen); + +#if defined(__DragonFly__) + error = bus_dmamap_load_mbuf_segment(ring->data_dmat, data->map, buf->m, + segs, 1, &nsegs, BUS_DMA_NOWAIT); +#else + error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, buf->m, + segs, &nsegs, BUS_DMA_NOWAIT); +#endif + if (error != 0 && error != EFBIG) { + device_printf(sc->sc_dev, + "%s: can't map mbuf (error %d)\n", __func__, error); + goto end; + } + if (error != 0) { + /* Too many DMA segments, linearize mbuf. */ +#if defined(__DragonFly__) + m1 = m_defrag(buf->m, M_NOWAIT); +#else + m1 = m_collapse(buf->m, M_NOWAIT, WPI_MAX_SCATTER - 1); +#endif + if (m1 == NULL) { + device_printf(sc->sc_dev, + "%s: could not defrag mbuf\n", __func__); + error = ENOBUFS; + goto end; + } + buf->m = m1; + +#if defined(__DragonFly__) + error = bus_dmamap_load_mbuf_segment(ring->data_dmat, data->map, + buf->m, segs, 1, &nsegs, BUS_DMA_NOWAIT); +#else + error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, + buf->m, segs, &nsegs, BUS_DMA_NOWAIT); +#endif + if (__predict_false(error != 0)) { + /* XXX fix this (applicable to the iwn(4) too) */ + /* + * NB: Do not return error; + * original mbuf does not exist anymore. + */ + device_printf(sc->sc_dev, + "%s: can't map mbuf (error %d)\n", __func__, + error); + if (ring->qid < WPI_CMD_QUEUE_NUM) { + if_inc_counter(buf->ni->ni_vap->iv_ifp, + IFCOUNTER_OERRORS, 1); + if (!frag) + ieee80211_free_node(buf->ni); + } + m_freem(buf->m); + error = 0; + goto end; + } + } + + KASSERT(nsegs < WPI_MAX_SCATTER, + ("too many DMA segments, nsegs (%d) should be less than %d", + nsegs, WPI_MAX_SCATTER)); + + data->m = buf->m; + data->ni = buf->ni; + + DPRINTF(sc, WPI_DEBUG_XMIT, "%s: qid %d idx %d len %d nsegs %d\n", + __func__, ring->qid, cur, totlen, nsegs); + + /* Fill TX descriptor. */ + desc->nsegs = WPI_PAD32(totlen + pad) << 4 | (1 + nsegs); + /* First DMA segment is used by the TX command. */ + desc->segs[0].addr = htole32(data->cmd_paddr); + desc->segs[0].len = htole32(4 + buf->size + hdrlen + pad); + /* Other DMA segments are for data payload. */ + seg = &segs[0]; + for (i = 1; i <= nsegs; i++) { + desc->segs[i].addr = htole32(seg->ds_addr); + desc->segs[i].len = htole32(seg->ds_len); + seg++; + } + + bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(ring->data_dmat, ring->cmd_dma.map, + BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, + BUS_DMASYNC_PREWRITE); + + ring->pending += 1; + + if (!frag) { + if (ring->qid < WPI_CMD_QUEUE_NUM) { + WPI_TXQ_STATE_LOCK(sc); + ring->queued += ring->pending; + callout_reset(&sc->tx_timeout, 5*hz, wpi_tx_timeout, + sc); + WPI_TXQ_STATE_UNLOCK(sc); + } + + /* Kick TX ring. */ + ring->cur = (ring->cur + ring->pending) % WPI_TX_RING_COUNT; + ring->pending = 0; + sc->sc_update_tx_ring(sc, ring); + } else + ieee80211_node_incref(data->ni); + +end: DPRINTF(sc, WPI_DEBUG_TRACE, error ? TRACE_STR_END_ERR : TRACE_STR_END, + __func__); + + WPI_TXQ_UNLOCK(sc); + + return (error); +} + +/* + * Construct the data packet for a transmit buffer. + */ +static int +wpi_tx_data(struct wpi_softc *sc, struct mbuf *m, struct ieee80211_node *ni) +{ + const struct ieee80211_txparam *tp; + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + struct wpi_node *wn = WPI_NODE(ni); + struct ieee80211_channel *chan; + struct ieee80211_frame *wh; + struct ieee80211_key *k = NULL; + struct wpi_buf tx_data; + struct wpi_cmd_data *tx = (struct wpi_cmd_data *)&tx_data.data; + uint32_t flags; + uint16_t ac, qos; + uint8_t tid, type, rate; + int swcrypt, ismcast, totlen; + + wh = mtod(m, struct ieee80211_frame *); + type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); + swcrypt = 1; + + /* Select EDCA Access Category and TX ring for this frame. */ + if (IEEE80211_QOS_HAS_SEQ(wh)) { + qos = ((const struct ieee80211_qosframe *)wh)->i_qos[0]; + tid = qos & IEEE80211_QOS_TID; + } else { + qos = 0; + tid = 0; + } + ac = M_WME_GETAC(m); + + chan = (ni->ni_chan != IEEE80211_CHAN_ANYC) ? + ni->ni_chan : ic->ic_curchan; + tp = &vap->iv_txparms[ieee80211_chan2mode(chan)]; + + /* Choose a TX rate index. */ + if (type == IEEE80211_FC0_TYPE_MGT) + rate = tp->mgmtrate; + else if (ismcast) + rate = tp->mcastrate; + else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) + rate = tp->ucastrate; + else if (m->m_flags & M_EAPOL) + rate = tp->mgmtrate; + else { + /* XXX pass pktlen */ + (void) ieee80211_ratectl_rate(ni, NULL, 0); + rate = ni->ni_txrate; + } + + /* Encrypt the frame if need be. */ + if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { + /* Retrieve key for TX. */ + k = ieee80211_crypto_encap(ni, m); + if (k == NULL) + return (ENOBUFS); + + swcrypt = k->wk_flags & IEEE80211_KEY_SWCRYPT; + + /* 802.11 header may have moved. */ + wh = mtod(m, struct ieee80211_frame *); + } + totlen = m->m_pkthdr.len; + + if (ieee80211_radiotap_active_vap(vap)) { + struct wpi_tx_radiotap_header *tap = &sc->sc_txtap; + + tap->wt_flags = 0; + tap->wt_rate = rate; + if (k != NULL) + tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP; + if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG) + tap->wt_flags |= IEEE80211_RADIOTAP_F_FRAG; + + ieee80211_radiotap_tx(vap, m); + } + + flags = 0; + if (!ismcast) { + /* Unicast frame, check if an ACK is expected. */ + if (!qos || (qos & IEEE80211_QOS_ACKPOLICY) != + IEEE80211_QOS_ACKPOLICY_NOACK) + flags |= WPI_TX_NEED_ACK; + } + + if (!IEEE80211_QOS_HAS_SEQ(wh)) + flags |= WPI_TX_AUTO_SEQ; + if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG) + flags |= WPI_TX_MORE_FRAG; + + /* Check if frame must be protected using RTS/CTS or CTS-to-self. */ + if (!ismcast) { + /* NB: Group frames are sent using CCK in 802.11b/g. */ + if (totlen + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) { + flags |= WPI_TX_NEED_RTS; + } else if ((ic->ic_flags & IEEE80211_F_USEPROT) && + WPI_RATE_IS_OFDM(rate)) { + if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) + flags |= WPI_TX_NEED_CTS; + else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) + flags |= WPI_TX_NEED_RTS; + } + + if (flags & (WPI_TX_NEED_RTS | WPI_TX_NEED_CTS)) + flags |= WPI_TX_FULL_TXOP; + } + + memset(tx, 0, sizeof (struct wpi_cmd_data)); + if (type == IEEE80211_FC0_TYPE_MGT) { + uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; + + /* Tell HW to set timestamp in probe responses. */ + if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP) + flags |= WPI_TX_INSERT_TSTAMP; + if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ || + subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) + tx->timeout = htole16(3); + else + tx->timeout = htole16(2); + } + + if (ismcast || type != IEEE80211_FC0_TYPE_DATA) + tx->id = WPI_ID_BROADCAST; + else { + if (wn->id == WPI_ID_UNDEFINED) { + device_printf(sc->sc_dev, + "%s: undefined node id\n", __func__); + return (EINVAL); + } + + tx->id = wn->id; + } + + if (!swcrypt) { + switch (k->wk_cipher->ic_cipher) { + case IEEE80211_CIPHER_AES_CCM: + tx->security = WPI_CIPHER_CCMP; + break; + + default: + break; + } + + memcpy(tx->key, k->wk_key, k->wk_keylen); + } + + if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG) { + struct mbuf *next = m->m_nextpkt; + + tx->lnext = htole16(next->m_pkthdr.len); + tx->fnext = htole32(tx->security | + (flags & WPI_TX_NEED_ACK) | + WPI_NEXT_STA_ID(tx->id)); + } + + tx->len = htole16(totlen); + tx->flags = htole32(flags); + tx->plcp = rate2plcp(rate); + tx->tid = tid; + tx->lifetime = htole32(WPI_LIFETIME_INFINITE); + tx->ofdm_mask = 0xff; + tx->cck_mask = 0x0f; + tx->rts_ntries = 7; + tx->data_ntries = tp->maxretry; + + tx_data.ni = ni; + tx_data.m = m; + tx_data.size = sizeof(struct wpi_cmd_data); + tx_data.code = WPI_CMD_TX_DATA; + tx_data.ac = ac; + + return wpi_cmd2(sc, &tx_data); +} + +static int +wpi_tx_data_raw(struct wpi_softc *sc, struct mbuf *m, + struct ieee80211_node *ni, const struct ieee80211_bpf_params *params) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211_key *k = NULL; + struct ieee80211_frame *wh; + struct wpi_buf tx_data; + struct wpi_cmd_data *tx = (struct wpi_cmd_data *)&tx_data.data; + uint32_t flags; + uint8_t ac, type, rate; + int swcrypt, totlen; + + wh = mtod(m, struct ieee80211_frame *); + type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + swcrypt = 1; + + ac = params->ibp_pri & 3; + + /* Choose a TX rate index. */ + rate = params->ibp_rate0; + + flags = 0; + if (!IEEE80211_QOS_HAS_SEQ(wh)) + flags |= WPI_TX_AUTO_SEQ; + if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0) + flags |= WPI_TX_NEED_ACK; + if (params->ibp_flags & IEEE80211_BPF_RTS) + flags |= WPI_TX_NEED_RTS; + if (params->ibp_flags & IEEE80211_BPF_CTS) + flags |= WPI_TX_NEED_CTS; + if (flags & (WPI_TX_NEED_RTS | WPI_TX_NEED_CTS)) + flags |= WPI_TX_FULL_TXOP; + + /* Encrypt the frame if need be. */ + if (params->ibp_flags & IEEE80211_BPF_CRYPTO) { + /* Retrieve key for TX. */ + k = ieee80211_crypto_encap(ni, m); + if (k == NULL) + return (ENOBUFS); + + swcrypt = k->wk_flags & IEEE80211_KEY_SWCRYPT; + + /* 802.11 header may have moved. */ + wh = mtod(m, struct ieee80211_frame *); + } + totlen = m->m_pkthdr.len; + + if (ieee80211_radiotap_active_vap(vap)) { + struct wpi_tx_radiotap_header *tap = &sc->sc_txtap; + + tap->wt_flags = 0; + tap->wt_rate = rate; + if (params->ibp_flags & IEEE80211_BPF_CRYPTO) + tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP; + + ieee80211_radiotap_tx(vap, m); + } + + memset(tx, 0, sizeof (struct wpi_cmd_data)); + if (type == IEEE80211_FC0_TYPE_MGT) { + uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; + + /* Tell HW to set timestamp in probe responses. */ + if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP) + flags |= WPI_TX_INSERT_TSTAMP; + if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ || + subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) + tx->timeout = htole16(3); + else + tx->timeout = htole16(2); + } + + if (!swcrypt) { + switch (k->wk_cipher->ic_cipher) { + case IEEE80211_CIPHER_AES_CCM: + tx->security = WPI_CIPHER_CCMP; + break; + + default: + break; + } + + memcpy(tx->key, k->wk_key, k->wk_keylen); + } + + tx->len = htole16(totlen); + tx->flags = htole32(flags); + tx->plcp = rate2plcp(rate); + tx->id = WPI_ID_BROADCAST; + tx->lifetime = htole32(WPI_LIFETIME_INFINITE); + tx->rts_ntries = params->ibp_try1; + tx->data_ntries = params->ibp_try0; + + tx_data.ni = ni; + tx_data.m = m; + tx_data.size = sizeof(struct wpi_cmd_data); + tx_data.code = WPI_CMD_TX_DATA; + tx_data.ac = ac; + + return wpi_cmd2(sc, &tx_data); +} + +static __inline int +wpi_tx_ring_free_space(struct wpi_softc *sc, uint16_t ac) +{ + struct wpi_tx_ring *ring = &sc->txq[ac]; + int retval; + + WPI_TXQ_STATE_LOCK(sc); + retval = WPI_TX_RING_HIMARK - ring->queued; + WPI_TXQ_STATE_UNLOCK(sc); + + return retval; +} + +static int +wpi_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, + const struct ieee80211_bpf_params *params) +{ + struct ieee80211com *ic = ni->ni_ic; + struct wpi_softc *sc = ic->ic_softc; + uint16_t ac; + int error = 0; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); + + ac = M_WME_GETAC(m); + + WPI_TX_LOCK(sc); + + /* NB: no fragments here */ + if (sc->sc_running == 0 || wpi_tx_ring_free_space(sc, ac) < 1) { + error = sc->sc_running ? ENOBUFS : ENETDOWN; + goto unlock; + } + + if (params == NULL) { + /* + * Legacy path; interpret frame contents to decide + * precisely how to send the frame. + */ + error = wpi_tx_data(sc, m, ni); + } else { + /* + * Caller supplied explicit parameters to use in + * sending the frame. + */ + error = wpi_tx_data_raw(sc, m, ni, params); + } + +unlock: WPI_TX_UNLOCK(sc); + + if (error != 0) { + m_freem(m); + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__); + + return error; + } + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); + + return 0; +} + +static int +wpi_transmit(struct ieee80211com *ic, struct mbuf *m) +{ + struct wpi_softc *sc = ic->ic_softc; + struct ieee80211_node *ni; + struct mbuf *mnext; + uint16_t ac; + int error, nmbufs; + + WPI_TX_LOCK(sc); + DPRINTF(sc, WPI_DEBUG_XMIT, "%s: called\n", __func__); + + /* Check if interface is up & running. */ + if (__predict_false(sc->sc_running == 0)) { + error = ENXIO; + goto unlock; + } + + nmbufs = 1; + for (mnext = m->m_nextpkt; mnext != NULL; mnext = mnext->m_nextpkt) + nmbufs++; + + /* Check for available space. */ + ac = M_WME_GETAC(m); + if (wpi_tx_ring_free_space(sc, ac) < nmbufs) { + error = ENOBUFS; + goto unlock; + } + + error = 0; + ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; + do { + mnext = m->m_nextpkt; + if (wpi_tx_data(sc, m, ni) != 0) { + if_inc_counter(ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, + nmbufs); + wpi_free_txfrags(sc, ac); + ieee80211_free_mbuf(m); + ieee80211_free_node(ni); + break; + } + } while((m = mnext) != NULL); + + DPRINTF(sc, WPI_DEBUG_XMIT, "%s: done\n", __func__); + +unlock: WPI_TX_UNLOCK(sc); + + return (error); +} + +static void +wpi_watchdog_rfkill(void *arg) +{ + struct wpi_softc *sc = arg; + struct ieee80211com *ic = &sc->sc_ic; + + DPRINTF(sc, WPI_DEBUG_WATCHDOG, "RFkill Watchdog: tick\n"); + + /* No need to lock firmware memory. */ + if ((wpi_prph_read(sc, WPI_APMG_RFKILL) & 0x1) == 0) { + /* Radio kill switch is still off. */ + callout_reset(&sc->watchdog_rfkill, hz, wpi_watchdog_rfkill, + sc); + } else + ieee80211_runtask(ic, &sc->sc_radioon_task); +} + +static void +wpi_scan_timeout(void *arg) +{ + struct wpi_softc *sc = arg; + struct ieee80211com *ic = &sc->sc_ic; + + ic_printf(ic, "scan timeout\n"); + ieee80211_restart_all(ic); +} + +static void +wpi_tx_timeout(void *arg) +{ + struct wpi_softc *sc = arg; + struct ieee80211com *ic = &sc->sc_ic; + + ic_printf(ic, "device timeout\n"); + ieee80211_restart_all(ic); +} + +static void +wpi_parent(struct ieee80211com *ic) +{ + struct wpi_softc *sc = ic->ic_softc; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + + if (ic->ic_nrunning > 0) { + if (wpi_init(sc) == 0) { + ieee80211_notify_radio(ic, 1); + ieee80211_start_all(ic); + } else { + ieee80211_notify_radio(ic, 0); + ieee80211_stop(vap); + } + } else { + ieee80211_notify_radio(ic, 0); + wpi_stop(sc); + } +} + +/* + * Send a command to the firmware. + */ +static int +wpi_cmd(struct wpi_softc *sc, uint8_t code, const void *buf, uint16_t size, + int async) +{ + struct wpi_tx_ring *ring = &sc->txq[WPI_CMD_QUEUE_NUM]; + struct wpi_tx_desc *desc; + struct wpi_tx_data *data; + struct wpi_tx_cmd *cmd; + struct mbuf *m; + bus_addr_t paddr; + uint16_t totlen; + int error; + + WPI_TXQ_LOCK(sc); + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); + + if (__predict_false(sc->sc_running == 0)) { + /* wpi_stop() was called */ + if (code == WPI_CMD_SCAN) + error = ENETDOWN; + else + error = 0; + + goto fail; + } + + if (async == 0) + WPI_LOCK_ASSERT(sc); + + DPRINTF(sc, WPI_DEBUG_CMD, "%s: cmd %s size %u async %d\n", + __func__, wpi_cmd_str(code), size, async); + + desc = &ring->desc[ring->cur]; + data = &ring->data[ring->cur]; + totlen = 4 + size; + + if (size > sizeof cmd->data) { + /* Command is too large to fit in a descriptor. */ + if (totlen > MCLBYTES) { + error = EINVAL; + goto fail; + } + m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE); + if (m == NULL) { + error = ENOMEM; + goto fail; + } + cmd = mtod(m, struct wpi_tx_cmd *); + error = bus_dmamap_load(ring->data_dmat, data->map, cmd, + totlen, wpi_dma_map_addr, &paddr, BUS_DMA_NOWAIT); + if (error != 0) { + m_freem(m); + goto fail; + } + data->m = m; + } else { + cmd = &ring->cmd[ring->cur]; + paddr = data->cmd_paddr; + } + + cmd->code = code; + cmd->flags = 0; + cmd->qid = ring->qid; + cmd->idx = ring->cur; + memcpy(cmd->data, buf, size); + + desc->nsegs = 1 + (WPI_PAD32(size) << 4); + desc->segs[0].addr = htole32(paddr); + desc->segs[0].len = htole32(totlen); + + if (size > sizeof cmd->data) { + bus_dmamap_sync(ring->data_dmat, data->map, + BUS_DMASYNC_PREWRITE); + } else { + bus_dmamap_sync(ring->data_dmat, ring->cmd_dma.map, + BUS_DMASYNC_PREWRITE); + } + bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, + BUS_DMASYNC_PREWRITE); + + /* Kick command ring. */ + ring->cur = (ring->cur + 1) % WPI_TX_RING_COUNT; + sc->sc_update_tx_ring(sc, ring); + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); + + WPI_TXQ_UNLOCK(sc); + +#if defined(__DragonFly__) + return async ? 0 : lksleep(cmd, &sc->sc_mtx, PCATCH, "wpicmd", hz); +#else + return async ? 0 : mtx_sleep(cmd, &sc->sc_mtx, PCATCH, "wpicmd", hz); +#endif + +fail: DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__); + + WPI_TXQ_UNLOCK(sc); + + return error; +} + +/* + * Configure HW multi-rate retries. + */ +static int +wpi_mrr_setup(struct wpi_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct wpi_mrr_setup mrr; + uint8_t i; + int error; + + /* CCK rates (not used with 802.11a). */ + for (i = WPI_RIDX_CCK1; i <= WPI_RIDX_CCK11; i++) { + mrr.rates[i].flags = 0; + mrr.rates[i].plcp = wpi_ridx_to_plcp[i]; + /* Fallback to the immediate lower CCK rate (if any.) */ + mrr.rates[i].next = + (i == WPI_RIDX_CCK1) ? WPI_RIDX_CCK1 : i - 1; + /* Try twice at this rate before falling back to "next". */ + mrr.rates[i].ntries = WPI_NTRIES_DEFAULT; + } + /* OFDM rates (not used with 802.11b). */ + for (i = WPI_RIDX_OFDM6; i <= WPI_RIDX_OFDM54; i++) { + mrr.rates[i].flags = 0; + mrr.rates[i].plcp = wpi_ridx_to_plcp[i]; + /* Fallback to the immediate lower rate (if any.) */ + /* We allow fallback from OFDM/6 to CCK/2 in 11b/g mode. */ + mrr.rates[i].next = (i == WPI_RIDX_OFDM6) ? + ((ic->ic_curmode == IEEE80211_MODE_11A) ? + WPI_RIDX_OFDM6 : WPI_RIDX_CCK2) : + i - 1; + /* Try twice at this rate before falling back to "next". */ + mrr.rates[i].ntries = WPI_NTRIES_DEFAULT; + } + /* Setup MRR for control frames. */ + mrr.which = htole32(WPI_MRR_CTL); + error = wpi_cmd(sc, WPI_CMD_MRR_SETUP, &mrr, sizeof mrr, 0); + if (error != 0) { + device_printf(sc->sc_dev, + "could not setup MRR for control frames\n"); + return error; + } + /* Setup MRR for data frames. */ + mrr.which = htole32(WPI_MRR_DATA); + error = wpi_cmd(sc, WPI_CMD_MRR_SETUP, &mrr, sizeof mrr, 0); + if (error != 0) { + device_printf(sc->sc_dev, + "could not setup MRR for data frames\n"); + return error; + } + return 0; +} + +static int +wpi_add_node(struct wpi_softc *sc, struct ieee80211_node *ni) +{ + struct ieee80211com *ic = ni->ni_ic; + struct wpi_vap *wvp = WPI_VAP(ni->ni_vap); + struct wpi_node *wn = WPI_NODE(ni); + struct wpi_node_info node; + int error; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + + if (wn->id == WPI_ID_UNDEFINED) + return EINVAL; + + memset(&node, 0, sizeof node); + IEEE80211_ADDR_COPY(node.macaddr, ni->ni_macaddr); + node.id = wn->id; + node.plcp = (ic->ic_curmode == IEEE80211_MODE_11A) ? + wpi_ridx_to_plcp[WPI_RIDX_OFDM6] : wpi_ridx_to_plcp[WPI_RIDX_CCK1]; + node.action = htole32(WPI_ACTION_SET_RATE); + node.antenna = WPI_ANTENNA_BOTH; + + DPRINTF(sc, WPI_DEBUG_NODE, "%s: adding node %d (%s)\n", __func__, + wn->id, ether_sprintf(ni->ni_macaddr)); + + error = wpi_cmd(sc, WPI_CMD_ADD_NODE, &node, sizeof node, 1); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: wpi_cmd() call failed with error code %d\n", __func__, + error); + return error; + } + + if (wvp->wv_gtk != 0) { + error = wpi_set_global_keys(ni); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: error while setting global keys\n", __func__); + return ENXIO; + } + } + + return 0; +} + +/* + * Broadcast node is used to send group-addressed and management frames. + */ +static int +wpi_add_broadcast_node(struct wpi_softc *sc, int async) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct wpi_node_info node; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + + memset(&node, 0, sizeof node); + IEEE80211_ADDR_COPY(node.macaddr, ieee80211broadcastaddr); + node.id = WPI_ID_BROADCAST; + node.plcp = (ic->ic_curmode == IEEE80211_MODE_11A) ? + wpi_ridx_to_plcp[WPI_RIDX_OFDM6] : wpi_ridx_to_plcp[WPI_RIDX_CCK1]; + node.action = htole32(WPI_ACTION_SET_RATE); + node.antenna = WPI_ANTENNA_BOTH; + + DPRINTF(sc, WPI_DEBUG_NODE, "%s: adding broadcast node\n", __func__); + + return wpi_cmd(sc, WPI_CMD_ADD_NODE, &node, sizeof node, async); +} + +static int +wpi_add_sta_node(struct wpi_softc *sc, struct ieee80211_node *ni) +{ + struct wpi_node *wn = WPI_NODE(ni); + int error; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + + wn->id = wpi_add_node_entry_sta(sc); + + if ((error = wpi_add_node(sc, ni)) != 0) { + wpi_del_node_entry(sc, wn->id); + wn->id = WPI_ID_UNDEFINED; + return error; + } + + return 0; +} + +static int +wpi_add_ibss_node(struct wpi_softc *sc, struct ieee80211_node *ni) +{ + struct wpi_node *wn = WPI_NODE(ni); + int error; + + KASSERT(wn->id == WPI_ID_UNDEFINED, + ("the node %d was added before", wn->id)); + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + + if ((wn->id = wpi_add_node_entry_adhoc(sc)) == WPI_ID_UNDEFINED) { + device_printf(sc->sc_dev, "%s: h/w table is full\n", __func__); + return ENOMEM; + } + + if ((error = wpi_add_node(sc, ni)) != 0) { + wpi_del_node_entry(sc, wn->id); + wn->id = WPI_ID_UNDEFINED; + return error; + } + + return 0; +} + +static void +wpi_del_node(struct wpi_softc *sc, struct ieee80211_node *ni) +{ + struct wpi_node *wn = WPI_NODE(ni); + struct wpi_cmd_del_node node; + int error; + + KASSERT(wn->id != WPI_ID_UNDEFINED, ("undefined node id passed")); + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + + memset(&node, 0, sizeof node); + IEEE80211_ADDR_COPY(node.macaddr, ni->ni_macaddr); + node.count = 1; + + DPRINTF(sc, WPI_DEBUG_NODE, "%s: deleting node %d (%s)\n", __func__, + wn->id, ether_sprintf(ni->ni_macaddr)); + + error = wpi_cmd(sc, WPI_CMD_DEL_NODE, &node, sizeof node, 1); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: could not delete node %u, error %d\n", __func__, + wn->id, error); + } +} + +static int +wpi_updateedca(struct ieee80211com *ic) +{ +#define WPI_EXP2(x) ((1 << (x)) - 1) /* CWmin = 2^ECWmin - 1 */ + struct wpi_softc *sc = ic->ic_softc; + struct wpi_edca_params cmd; + int aci, error; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); + + memset(&cmd, 0, sizeof cmd); + cmd.flags = htole32(WPI_EDCA_UPDATE); + for (aci = 0; aci < WME_NUM_AC; aci++) { + const struct wmeParams *ac = + &ic->ic_wme.wme_chanParams.cap_wmeParams[aci]; + cmd.ac[aci].aifsn = ac->wmep_aifsn; + cmd.ac[aci].cwmin = htole16(WPI_EXP2(ac->wmep_logcwmin)); + cmd.ac[aci].cwmax = htole16(WPI_EXP2(ac->wmep_logcwmax)); + cmd.ac[aci].txoplimit = + htole16(IEEE80211_TXOP_TO_US(ac->wmep_txopLimit)); + + DPRINTF(sc, WPI_DEBUG_EDCA, + "setting WME for queue %d aifsn=%d cwmin=%d cwmax=%d " + "txoplimit=%d\n", aci, cmd.ac[aci].aifsn, + cmd.ac[aci].cwmin, cmd.ac[aci].cwmax, + cmd.ac[aci].txoplimit); + } + error = wpi_cmd(sc, WPI_CMD_EDCA_PARAMS, &cmd, sizeof cmd, 1); + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); + + return error; +#undef WPI_EXP2 +} + +static void +wpi_set_promisc(struct wpi_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + uint32_t promisc_filter; + + promisc_filter = WPI_FILTER_CTL; + if (vap != NULL && vap->iv_opmode != IEEE80211_M_HOSTAP) + promisc_filter |= WPI_FILTER_PROMISC; + + if (ic->ic_promisc > 0) + sc->rxon.filter |= htole32(promisc_filter); + else + sc->rxon.filter &= ~htole32(promisc_filter); +} + +static void +wpi_update_promisc(struct ieee80211com *ic) +{ + struct wpi_softc *sc = ic->ic_softc; + + WPI_LOCK(sc); + if (sc->sc_running == 0) { + WPI_UNLOCK(sc); + return; + } + WPI_UNLOCK(sc); + + WPI_RXON_LOCK(sc); + wpi_set_promisc(sc); + + if (wpi_send_rxon(sc, 1, 1) != 0) { + device_printf(sc->sc_dev, "%s: could not send RXON\n", + __func__); + } + WPI_RXON_UNLOCK(sc); +} + +static void +wpi_update_mcast(struct ieee80211com *ic) +{ + /* Ignore */ +} + +static void +wpi_set_led(struct wpi_softc *sc, uint8_t which, uint8_t off, uint8_t on) +{ + struct wpi_cmd_led led; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + + led.which = which; + led.unit = htole32(100000); /* on/off in unit of 100ms */ + led.off = off; + led.on = on; + (void)wpi_cmd(sc, WPI_CMD_SET_LED, &led, sizeof led, 1); +} + +static int +wpi_set_timing(struct wpi_softc *sc, struct ieee80211_node *ni) +{ + struct wpi_cmd_timing cmd; + uint64_t val, mod; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + + memset(&cmd, 0, sizeof cmd); + memcpy(&cmd.tstamp, ni->ni_tstamp.data, sizeof (uint64_t)); + cmd.bintval = htole16(ni->ni_intval); + cmd.lintval = htole16(10); + + /* Compute remaining time until next beacon. */ + val = (uint64_t)ni->ni_intval * IEEE80211_DUR_TU; + mod = le64toh(cmd.tstamp) % val; + cmd.binitval = htole32((uint32_t)(val - mod)); + + DPRINTF(sc, WPI_DEBUG_RESET, "timing bintval=%u tstamp=%ju, init=%u\n", + ni->ni_intval, le64toh(cmd.tstamp), (uint32_t)(val - mod)); + + return wpi_cmd(sc, WPI_CMD_TIMING, &cmd, sizeof cmd, 1); +} + +/* + * This function is called periodically (every 60 seconds) to adjust output + * power to temperature changes. + */ +static void +wpi_power_calibration(struct wpi_softc *sc) +{ + int temp; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + + /* Update sensor data. */ + temp = (int)WPI_READ(sc, WPI_UCODE_GP2); + DPRINTF(sc, WPI_DEBUG_TEMP, "Temp in calibration is: %d\n", temp); + + /* Sanity-check read value. */ + if (temp < -260 || temp > 25) { + /* This can't be correct, ignore. */ + DPRINTF(sc, WPI_DEBUG_TEMP, + "out-of-range temperature reported: %d\n", temp); + return; + } + + DPRINTF(sc, WPI_DEBUG_TEMP, "temperature %d->%d\n", sc->temp, temp); + + /* Adjust Tx power if need be. */ + if (abs(temp - sc->temp) <= 6) + return; + + sc->temp = temp; + + if (wpi_set_txpower(sc, 1) != 0) { + /* just warn, too bad for the automatic calibration... */ + device_printf(sc->sc_dev,"could not adjust Tx power\n"); + } +} + +/* + * Set TX power for current channel. + */ +static int +wpi_set_txpower(struct wpi_softc *sc, int async) +{ + struct wpi_power_group *group; + struct wpi_cmd_txpower cmd; + uint8_t chan; + int idx, is_chan_5ghz, i; + + /* Retrieve current channel from last RXON. */ + chan = sc->rxon.chan; + is_chan_5ghz = (sc->rxon.flags & htole32(WPI_RXON_24GHZ)) == 0; + + /* Find the TX power group to which this channel belongs. */ + if (is_chan_5ghz) { + for (group = &sc->groups[1]; group < &sc->groups[4]; group++) + if (chan <= group->chan) + break; + } else + group = &sc->groups[0]; + + memset(&cmd, 0, sizeof cmd); + cmd.band = is_chan_5ghz ? WPI_BAND_5GHZ : WPI_BAND_2GHZ; + cmd.chan = htole16(chan); + + /* Set TX power for all OFDM and CCK rates. */ + for (i = 0; i <= WPI_RIDX_MAX ; i++) { + /* Retrieve TX power for this channel/rate. */ + idx = wpi_get_power_index(sc, group, chan, is_chan_5ghz, i); + + cmd.rates[i].plcp = wpi_ridx_to_plcp[i]; + + if (is_chan_5ghz) { + cmd.rates[i].rf_gain = wpi_rf_gain_5ghz[idx]; + cmd.rates[i].dsp_gain = wpi_dsp_gain_5ghz[idx]; + } else { + cmd.rates[i].rf_gain = wpi_rf_gain_2ghz[idx]; + cmd.rates[i].dsp_gain = wpi_dsp_gain_2ghz[idx]; + } + DPRINTF(sc, WPI_DEBUG_TEMP, + "chan %d/ridx %d: power index %d\n", chan, i, idx); + } + + return wpi_cmd(sc, WPI_CMD_TXPOWER, &cmd, sizeof cmd, async); +} + +/* + * Determine Tx power index for a given channel/rate combination. + * This takes into account the regulatory information from EEPROM and the + * current temperature. + */ +static int +wpi_get_power_index(struct wpi_softc *sc, struct wpi_power_group *group, + uint8_t chan, int is_chan_5ghz, int ridx) +{ +/* Fixed-point arithmetic division using a n-bit fractional part. */ +#define fdivround(a, b, n) \ + ((((1 << n) * (a)) / (b) + (1 << n) / 2) / (1 << n)) + +/* Linear interpolation. */ +#define interpolate(x, x1, y1, x2, y2, n) \ + ((y1) + fdivround(((x) - (x1)) * ((y2) - (y1)), (x2) - (x1), n)) + + struct wpi_power_sample *sample; + int pwr, idx; + + /* Default TX power is group maximum TX power minus 3dB. */ + pwr = group->maxpwr / 2; + + /* Decrease TX power for highest OFDM rates to reduce distortion. */ + switch (ridx) { + case WPI_RIDX_OFDM36: + pwr -= is_chan_5ghz ? 5 : 0; + break; + case WPI_RIDX_OFDM48: + pwr -= is_chan_5ghz ? 10 : 7; + break; + case WPI_RIDX_OFDM54: + pwr -= is_chan_5ghz ? 12 : 9; + break; + } + + /* Never exceed the channel maximum allowed TX power. */ + pwr = min(pwr, sc->maxpwr[chan]); + + /* Retrieve TX power index into gain tables from samples. */ + for (sample = group->samples; sample < &group->samples[3]; sample++) + if (pwr > sample[1].power) + break; + /* Fixed-point linear interpolation using a 19-bit fractional part. */ + idx = interpolate(pwr, sample[0].power, sample[0].index, + sample[1].power, sample[1].index, 19); + + /*- + * Adjust power index based on current temperature: + * - if cooler than factory-calibrated: decrease output power + * - if warmer than factory-calibrated: increase output power + */ + idx -= (sc->temp - group->temp) * 11 / 100; + + /* Decrease TX power for CCK rates (-5dB). */ + if (ridx >= WPI_RIDX_CCK1) + idx += 10; + + /* Make sure idx stays in a valid range. */ + if (idx < 0) + return 0; + if (idx > WPI_MAX_PWR_INDEX) + return WPI_MAX_PWR_INDEX; + return idx; + +#undef interpolate +#undef fdivround +} + +/* + * Set STA mode power saving level (between 0 and 5). + * Level 0 is CAM (Continuously Aware Mode), 5 is for maximum power saving. + */ +static int +wpi_set_pslevel(struct wpi_softc *sc, uint8_t dtim, int level, int async) +{ + struct wpi_pmgt_cmd cmd; + const struct wpi_pmgt *pmgt; + uint32_t max, reg; + uint8_t skip_dtim; + int i; + + DPRINTF(sc, WPI_DEBUG_PWRSAVE, + "%s: dtim=%d, level=%d, async=%d\n", + __func__, dtim, level, async); + + /* Select which PS parameters to use. */ + if (dtim <= 10) + pmgt = &wpi_pmgt[0][level]; + else + pmgt = &wpi_pmgt[1][level]; + + memset(&cmd, 0, sizeof cmd); + if (level != 0) /* not CAM */ + cmd.flags |= htole16(WPI_PS_ALLOW_SLEEP); + /* Retrieve PCIe Active State Power Management (ASPM). */ +#if defined(__DragonFly__) + reg = pci_read_config(sc->sc_dev, sc->sc_cap_off + PCIER_LINKCTRL, 1); + if (!(reg & PCIEM_LNKCTL_ASPM_L0S)) /* L0s Entry disabled. */ + cmd.flags |= htole16(WPI_PS_PCI_PMGT); +#else + reg = pci_read_config(sc->sc_dev, sc->sc_cap_off + PCIER_LINK_CTL, 1); + if (!(reg & PCIEM_LINK_CTL_ASPMC_L0S)) /* L0s Entry disabled. */ + cmd.flags |= htole16(WPI_PS_PCI_PMGT); +#endif + + cmd.rxtimeout = htole32(pmgt->rxtimeout * IEEE80211_DUR_TU); + cmd.txtimeout = htole32(pmgt->txtimeout * IEEE80211_DUR_TU); + + if (dtim == 0) { + dtim = 1; + skip_dtim = 0; + } else + skip_dtim = pmgt->skip_dtim; + + if (skip_dtim != 0) { + cmd.flags |= htole16(WPI_PS_SLEEP_OVER_DTIM); + max = pmgt->intval[4]; + if (max == (uint32_t)-1) + max = dtim * (skip_dtim + 1); + else if (max > dtim) + max = rounddown(max, dtim); + } else + max = dtim; + + for (i = 0; i < 5; i++) + cmd.intval[i] = htole32(MIN(max, pmgt->intval[i])); + + return wpi_cmd(sc, WPI_CMD_SET_POWER_MODE, &cmd, sizeof cmd, async); +} + +static int +wpi_send_btcoex(struct wpi_softc *sc) +{ + struct wpi_bluetooth cmd; + + memset(&cmd, 0, sizeof cmd); + cmd.flags = WPI_BT_COEX_MODE_4WIRE; + cmd.lead_time = WPI_BT_LEAD_TIME_DEF; + cmd.max_kill = WPI_BT_MAX_KILL_DEF; + DPRINTF(sc, WPI_DEBUG_RESET, "%s: configuring bluetooth coexistence\n", + __func__); + return wpi_cmd(sc, WPI_CMD_BT_COEX, &cmd, sizeof(cmd), 0); +} + +static int +wpi_send_rxon(struct wpi_softc *sc, int assoc, int async) +{ + int error; + + if (async) + WPI_RXON_LOCK_ASSERT(sc); + + if (assoc && wpi_check_bss_filter(sc) != 0) { + struct wpi_assoc rxon_assoc; + + rxon_assoc.flags = sc->rxon.flags; + rxon_assoc.filter = sc->rxon.filter; + rxon_assoc.ofdm_mask = sc->rxon.ofdm_mask; + rxon_assoc.cck_mask = sc->rxon.cck_mask; + rxon_assoc.reserved = 0; + + error = wpi_cmd(sc, WPI_CMD_RXON_ASSOC, &rxon_assoc, + sizeof (struct wpi_assoc), async); + if (error != 0) { + device_printf(sc->sc_dev, + "RXON_ASSOC command failed, error %d\n", error); + return error; + } + } else { + if (async) { + WPI_NT_LOCK(sc); + error = wpi_cmd(sc, WPI_CMD_RXON, &sc->rxon, + sizeof (struct wpi_rxon), async); + if (error == 0) + wpi_clear_node_table(sc); + WPI_NT_UNLOCK(sc); + } else { + error = wpi_cmd(sc, WPI_CMD_RXON, &sc->rxon, + sizeof (struct wpi_rxon), async); + if (error == 0) + wpi_clear_node_table(sc); + } + + if (error != 0) { + device_printf(sc->sc_dev, + "RXON command failed, error %d\n", error); + return error; + } + + /* Add broadcast node. */ + error = wpi_add_broadcast_node(sc, async); + if (error != 0) { + device_printf(sc->sc_dev, + "could not add broadcast node, error %d\n", error); + return error; + } + } + + /* Configuration has changed, set Tx power accordingly. */ + if ((error = wpi_set_txpower(sc, async)) != 0) { + device_printf(sc->sc_dev, + "%s: could not set TX power, error %d\n", __func__, error); + return error; + } + + return 0; +} + +/** + * Configure the card to listen to a particular channel, this transisions the + * card in to being able to receive frames from remote devices. + */ +static int +wpi_config(struct wpi_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + struct ieee80211_channel *c = ic->ic_curchan; + int error; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); + + /* Set power saving level to CAM during initialization. */ + if ((error = wpi_set_pslevel(sc, 0, 0, 0)) != 0) { + device_printf(sc->sc_dev, + "%s: could not set power saving level\n", __func__); + return error; + } + + /* Configure bluetooth coexistence. */ + if ((error = wpi_send_btcoex(sc)) != 0) { + device_printf(sc->sc_dev, + "could not configure bluetooth coexistence\n"); + return error; + } + + /* Configure adapter. */ + memset(&sc->rxon, 0, sizeof (struct wpi_rxon)); + IEEE80211_ADDR_COPY(sc->rxon.myaddr, vap->iv_myaddr); + + /* Set default channel. */ + sc->rxon.chan = ieee80211_chan2ieee(ic, c); + sc->rxon.flags = htole32(WPI_RXON_TSF | WPI_RXON_CTS_TO_SELF); + if (IEEE80211_IS_CHAN_2GHZ(c)) + sc->rxon.flags |= htole32(WPI_RXON_AUTO | WPI_RXON_24GHZ); + + sc->rxon.filter = WPI_FILTER_MULTICAST; + switch (ic->ic_opmode) { + case IEEE80211_M_STA: + sc->rxon.mode = WPI_MODE_STA; + break; + case IEEE80211_M_IBSS: + sc->rxon.mode = WPI_MODE_IBSS; + sc->rxon.filter |= WPI_FILTER_BEACON; + break; + case IEEE80211_M_HOSTAP: + /* XXX workaround for beaconing */ + sc->rxon.mode = WPI_MODE_IBSS; + sc->rxon.filter |= WPI_FILTER_ASSOC | WPI_FILTER_PROMISC; + break; + case IEEE80211_M_AHDEMO: + sc->rxon.mode = WPI_MODE_HOSTAP; + break; + case IEEE80211_M_MONITOR: + sc->rxon.mode = WPI_MODE_MONITOR; + break; + default: + device_printf(sc->sc_dev, "unknown opmode %d\n", + ic->ic_opmode); + return EINVAL; + } + sc->rxon.filter = htole32(sc->rxon.filter); + wpi_set_promisc(sc); + sc->rxon.cck_mask = 0x0f; /* not yet negotiated */ + sc->rxon.ofdm_mask = 0xff; /* not yet negotiated */ + + if ((error = wpi_send_rxon(sc, 0, 0)) != 0) { + device_printf(sc->sc_dev, "%s: could not send RXON\n", + __func__); + return error; + } + + /* Setup rate scalling. */ + if ((error = wpi_mrr_setup(sc)) != 0) { + device_printf(sc->sc_dev, "could not setup MRR, error %d\n", + error); + return error; + } + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); + + return 0; +} + +static uint16_t +wpi_get_active_dwell_time(struct wpi_softc *sc, + struct ieee80211_channel *c, uint8_t n_probes) +{ + /* No channel? Default to 2GHz settings. */ + if (c == NULL || IEEE80211_IS_CHAN_2GHZ(c)) { + return (WPI_ACTIVE_DWELL_TIME_2GHZ + + WPI_ACTIVE_DWELL_FACTOR_2GHZ * (n_probes + 1)); + } + + /* 5GHz dwell time. */ + return (WPI_ACTIVE_DWELL_TIME_5GHZ + + WPI_ACTIVE_DWELL_FACTOR_5GHZ * (n_probes + 1)); +} + +/* + * Limit the total dwell time. + * + * Returns the dwell time in milliseconds. + */ +static uint16_t +wpi_limit_dwell(struct wpi_softc *sc, uint16_t dwell_time) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + uint16_t bintval = 0; + + /* bintval is in TU (1.024mS) */ + if (vap != NULL) + bintval = vap->iv_bss->ni_intval; + + /* + * If it's non-zero, we should calculate the minimum of + * it and the DWELL_BASE. + * + * XXX Yes, the math should take into account that bintval + * is 1.024mS, not 1mS.. + */ + if (bintval > 0) { + DPRINTF(sc, WPI_DEBUG_SCAN, "%s: bintval=%d\n", __func__, + bintval); + return (MIN(dwell_time, bintval - WPI_CHANNEL_TUNE_TIME * 2)); + } + + /* No association context? Default. */ + return dwell_time; +} + +static uint16_t +wpi_get_passive_dwell_time(struct wpi_softc *sc, struct ieee80211_channel *c) +{ + uint16_t passive; + + if (c == NULL || IEEE80211_IS_CHAN_2GHZ(c)) + passive = WPI_PASSIVE_DWELL_BASE + WPI_PASSIVE_DWELL_TIME_2GHZ; + else + passive = WPI_PASSIVE_DWELL_BASE + WPI_PASSIVE_DWELL_TIME_5GHZ; + + /* Clamp to the beacon interval if we're associated. */ + return (wpi_limit_dwell(sc, passive)); +} + +static uint32_t +wpi_get_scan_pause_time(uint32_t time, uint16_t bintval) +{ + uint32_t mod = (time % bintval) * IEEE80211_DUR_TU; + uint32_t nbeacons = time / bintval; + + if (mod > WPI_PAUSE_MAX_TIME) + mod = WPI_PAUSE_MAX_TIME; + + return WPI_PAUSE_SCAN(nbeacons, mod); +} + +/* + * Send a scan request to the firmware. + */ +static int +wpi_scan(struct wpi_softc *sc, struct ieee80211_channel *c) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_scan_state *ss = ic->ic_scan; + struct ieee80211vap *vap = ss->ss_vap; + struct wpi_scan_hdr *hdr; + struct wpi_cmd_data *tx; + struct wpi_scan_essid *essids; + struct wpi_scan_chan *chan; + struct ieee80211_frame *wh; + struct ieee80211_rateset *rs; + uint16_t bintval, buflen, dwell_active, dwell_passive; + uint8_t *buf, *frm, i, nssid; + int bgscan, error; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); + + /* + * We are absolutely not allowed to send a scan command when another + * scan command is pending. + */ + if (callout_pending(&sc->scan_timeout)) { + device_printf(sc->sc_dev, "%s: called whilst scanning!\n", + __func__); + error = EAGAIN; + goto fail; + } + + bgscan = wpi_check_bss_filter(sc); + bintval = vap->iv_bss->ni_intval; + if (bgscan != 0 && + bintval < WPI_QUIET_TIME_DEFAULT + WPI_CHANNEL_TUNE_TIME * 2) { + error = EOPNOTSUPP; + goto fail; + } + + buf = kmalloc(WPI_SCAN_MAXSZ, M_DEVBUF, M_INTWAIT | M_ZERO); + if (buf == NULL) { + device_printf(sc->sc_dev, + "%s: could not allocate buffer for scan command\n", + __func__); + error = ENOMEM; + goto fail; + } + hdr = (struct wpi_scan_hdr *)buf; + + /* + * Move to the next channel if no packets are received within 10 msecs + * after sending the probe request. + */ + hdr->quiet_time = htole16(WPI_QUIET_TIME_DEFAULT); + hdr->quiet_threshold = htole16(1); + + if (bgscan != 0) { + /* + * Max needs to be greater than active and passive and quiet! + * It's also in microseconds! + */ + hdr->max_svc = htole32(250 * IEEE80211_DUR_TU); + hdr->pause_svc = htole32(wpi_get_scan_pause_time(100, + bintval)); + } + + hdr->filter = htole32(WPI_FILTER_MULTICAST | WPI_FILTER_BEACON); + + tx = (struct wpi_cmd_data *)(hdr + 1); + tx->flags = htole32(WPI_TX_AUTO_SEQ); + tx->id = WPI_ID_BROADCAST; + tx->lifetime = htole32(WPI_LIFETIME_INFINITE); + + if (IEEE80211_IS_CHAN_5GHZ(c)) { + /* Send probe requests at 6Mbps. */ + tx->plcp = wpi_ridx_to_plcp[WPI_RIDX_OFDM6]; + rs = &ic->ic_sup_rates[IEEE80211_MODE_11A]; + } else { + hdr->flags = htole32(WPI_RXON_24GHZ | WPI_RXON_AUTO); + /* Send probe requests at 1Mbps. */ + tx->plcp = wpi_ridx_to_plcp[WPI_RIDX_CCK1]; + rs = &ic->ic_sup_rates[IEEE80211_MODE_11G]; + } + + essids = (struct wpi_scan_essid *)(tx + 1); + nssid = MIN(ss->ss_nssid, WPI_SCAN_MAX_ESSIDS); + for (i = 0; i < nssid; i++) { + essids[i].id = IEEE80211_ELEMID_SSID; + essids[i].len = MIN(ss->ss_ssid[i].len, IEEE80211_NWID_LEN); + memcpy(essids[i].data, ss->ss_ssid[i].ssid, essids[i].len); +#ifdef WPI_DEBUG + if (sc->sc_debug & WPI_DEBUG_SCAN) { + printf("Scanning Essid: "); + ieee80211_print_essid(essids[i].data, essids[i].len); + printf("\n"); + } +#endif + } + + /* + * Build a probe request frame. Most of the following code is a + * copy & paste of what is done in net80211. + */ + wh = (struct ieee80211_frame *)(essids + WPI_SCAN_MAX_ESSIDS); + wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | + IEEE80211_FC0_SUBTYPE_PROBE_REQ; + wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; + IEEE80211_ADDR_COPY(wh->i_addr1, ieee80211broadcastaddr); + IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); + IEEE80211_ADDR_COPY(wh->i_addr3, ieee80211broadcastaddr); + + frm = (uint8_t *)(wh + 1); + frm = ieee80211_add_ssid(frm, NULL, 0); + frm = ieee80211_add_rates(frm, rs); + if (rs->rs_nrates > IEEE80211_RATE_SIZE) + frm = ieee80211_add_xrates(frm, rs); + + /* Set length of probe request. */ + tx->len = htole16(frm - (uint8_t *)wh); + + /* + * Construct information about the channel that we + * want to scan. The firmware expects this to be directly + * after the scan probe request + */ + chan = (struct wpi_scan_chan *)frm; + chan->chan = ieee80211_chan2ieee(ic, c); + chan->flags = 0; + if (nssid) { + hdr->crc_threshold = WPI_SCAN_CRC_TH_DEFAULT; + chan->flags |= WPI_CHAN_NPBREQS(nssid); + } else + hdr->crc_threshold = WPI_SCAN_CRC_TH_NEVER; + + if (!IEEE80211_IS_CHAN_PASSIVE(c)) + chan->flags |= WPI_CHAN_ACTIVE; + + /* + * Calculate the active/passive dwell times. + */ + dwell_active = wpi_get_active_dwell_time(sc, c, nssid); + dwell_passive = wpi_get_passive_dwell_time(sc, c); + + /* Make sure they're valid. */ + if (dwell_active > dwell_passive) + dwell_active = dwell_passive; + + chan->active = htole16(dwell_active); + chan->passive = htole16(dwell_passive); + + chan->dsp_gain = 0x6e; /* Default level */ + + if (IEEE80211_IS_CHAN_5GHZ(c)) + chan->rf_gain = 0x3b; + else + chan->rf_gain = 0x28; + + DPRINTF(sc, WPI_DEBUG_SCAN, "Scanning %u Passive: %d\n", + chan->chan, IEEE80211_IS_CHAN_PASSIVE(c)); + + hdr->nchan++; + + if (hdr->nchan == 1 && sc->rxon.chan == chan->chan) { + /* XXX Force probe request transmission. */ + memcpy(chan + 1, chan, sizeof (struct wpi_scan_chan)); + + chan++; + + /* Reduce unnecessary delay. */ + chan->flags = 0; + chan->passive = chan->active = hdr->quiet_time; + + hdr->nchan++; + } + + chan++; + + buflen = (uint8_t *)chan - buf; + hdr->len = htole16(buflen); + + DPRINTF(sc, WPI_DEBUG_CMD, "sending scan command nchan=%d\n", + hdr->nchan); + error = wpi_cmd(sc, WPI_CMD_SCAN, buf, buflen, 1); + kfree(buf, M_DEVBUF); + + if (error != 0) + goto fail; + + callout_reset(&sc->scan_timeout, 5*hz, wpi_scan_timeout, sc); + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); + + return 0; + +fail: DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__); + + return error; +} + +static int +wpi_auth(struct wpi_softc *sc, struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_node *ni = vap->iv_bss; + struct ieee80211_channel *c = ni->ni_chan; + int error; + + WPI_RXON_LOCK(sc); + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); + + /* Update adapter configuration. */ + sc->rxon.associd = 0; + sc->rxon.filter &= ~htole32(WPI_FILTER_BSS); + IEEE80211_ADDR_COPY(sc->rxon.bssid, ni->ni_bssid); + sc->rxon.chan = ieee80211_chan2ieee(ic, c); + sc->rxon.flags = htole32(WPI_RXON_TSF | WPI_RXON_CTS_TO_SELF); + if (IEEE80211_IS_CHAN_2GHZ(c)) + sc->rxon.flags |= htole32(WPI_RXON_AUTO | WPI_RXON_24GHZ); + if (ic->ic_flags & IEEE80211_F_SHSLOT) + sc->rxon.flags |= htole32(WPI_RXON_SHSLOT); + if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) + sc->rxon.flags |= htole32(WPI_RXON_SHPREAMBLE); + if (IEEE80211_IS_CHAN_A(c)) { + sc->rxon.cck_mask = 0; + sc->rxon.ofdm_mask = 0x15; + } else if (IEEE80211_IS_CHAN_B(c)) { + sc->rxon.cck_mask = 0x03; + sc->rxon.ofdm_mask = 0; + } else { + /* Assume 802.11b/g. */ + sc->rxon.cck_mask = 0x0f; + sc->rxon.ofdm_mask = 0x15; + } + + DPRINTF(sc, WPI_DEBUG_STATE, "rxon chan %d flags %x cck %x ofdm %x\n", + sc->rxon.chan, sc->rxon.flags, sc->rxon.cck_mask, + sc->rxon.ofdm_mask); + + if ((error = wpi_send_rxon(sc, 0, 1)) != 0) { + device_printf(sc->sc_dev, "%s: could not send RXON\n", + __func__); + } + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); + + WPI_RXON_UNLOCK(sc); + + return error; +} + +static int +wpi_config_beacon(struct wpi_vap *wvp) +{ + struct ieee80211vap *vap = &wvp->wv_vap; + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_beacon_offsets *bo = &vap->iv_bcn_off; + struct wpi_buf *bcn = &wvp->wv_bcbuf; + struct wpi_softc *sc = ic->ic_softc; + struct wpi_cmd_beacon *cmd = (struct wpi_cmd_beacon *)&bcn->data; + struct ieee80211_tim_ie *tie; + struct mbuf *m; + uint8_t *ptr; + int error; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + + WPI_VAP_LOCK_ASSERT(wvp); + + cmd->len = htole16(bcn->m->m_pkthdr.len); + cmd->plcp = (ic->ic_curmode == IEEE80211_MODE_11A) ? + wpi_ridx_to_plcp[WPI_RIDX_OFDM6] : wpi_ridx_to_plcp[WPI_RIDX_CCK1]; + + /* XXX seems to be unused */ + if (*(bo->bo_tim) == IEEE80211_ELEMID_TIM) { + tie = (struct ieee80211_tim_ie *) bo->bo_tim; + ptr = mtod(bcn->m, uint8_t *); + + cmd->tim = htole16(bo->bo_tim - ptr); + cmd->timsz = tie->tim_len; + } + + /* Necessary for recursion in ieee80211_beacon_update(). */ + m = bcn->m; + bcn->m = m_dup(m, M_NOWAIT); + if (bcn->m == NULL) { + device_printf(sc->sc_dev, + "%s: could not copy beacon frame\n", __func__); + error = ENOMEM; + goto end; + } + + if ((error = wpi_cmd2(sc, bcn)) != 0) { + device_printf(sc->sc_dev, + "%s: could not update beacon frame, error %d", __func__, + error); + m_freem(bcn->m); + } + + /* Restore mbuf. */ +end: bcn->m = m; + + return error; +} + +static int +wpi_setup_beacon(struct wpi_softc *sc, struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct wpi_vap *wvp = WPI_VAP(vap); + struct wpi_buf *bcn = &wvp->wv_bcbuf; + struct mbuf *m; + int error; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + + if (ni->ni_chan == IEEE80211_CHAN_ANYC) + return EINVAL; + + m = ieee80211_beacon_alloc(ni); + if (m == NULL) { + device_printf(sc->sc_dev, + "%s: could not allocate beacon frame\n", __func__); + return ENOMEM; + } + + WPI_VAP_LOCK(wvp); + if (bcn->m != NULL) + m_freem(bcn->m); + + bcn->m = m; + + error = wpi_config_beacon(wvp); + WPI_VAP_UNLOCK(wvp); + + return error; +} + +static void +wpi_update_beacon(struct ieee80211vap *vap, int item) +{ + struct wpi_softc *sc = vap->iv_ic->ic_softc; + struct wpi_vap *wvp = WPI_VAP(vap); + struct wpi_buf *bcn = &wvp->wv_bcbuf; + struct ieee80211_beacon_offsets *bo = &vap->iv_bcn_off; + struct ieee80211_node *ni = vap->iv_bss; + int mcast = 0; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); + + WPI_VAP_LOCK(wvp); + if (bcn->m == NULL) { + bcn->m = ieee80211_beacon_alloc(ni); + if (bcn->m == NULL) { + device_printf(sc->sc_dev, + "%s: could not allocate beacon frame\n", __func__); + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, + __func__); + + WPI_VAP_UNLOCK(wvp); + return; + } + } + WPI_VAP_UNLOCK(wvp); + + if (item == IEEE80211_BEACON_TIM) + mcast = 1; /* TODO */ + + setbit(bo->bo_flags, item); + ieee80211_beacon_update(ni, bcn->m, mcast); + + WPI_VAP_LOCK(wvp); + wpi_config_beacon(wvp); + WPI_VAP_UNLOCK(wvp); + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); +} + +static void +wpi_newassoc(struct ieee80211_node *ni, int isnew) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct wpi_softc *sc = ni->ni_ic->ic_softc; + struct wpi_node *wn = WPI_NODE(ni); + int error; + + WPI_NT_LOCK(sc); + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + + if (vap->iv_opmode != IEEE80211_M_STA && wn->id == WPI_ID_UNDEFINED) { + if ((error = wpi_add_ibss_node(sc, ni)) != 0) { + device_printf(sc->sc_dev, + "%s: could not add IBSS node, error %d\n", + __func__, error); + } + } + WPI_NT_UNLOCK(sc); +} + +static int +wpi_run(struct wpi_softc *sc, struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_node *ni = vap->iv_bss; + struct ieee80211_channel *c = ni->ni_chan; + int error; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); + + if (vap->iv_opmode == IEEE80211_M_MONITOR) { + /* Link LED blinks while monitoring. */ + wpi_set_led(sc, WPI_LED_LINK, 5, 5); + return 0; + } + + /* XXX kernel panic workaround */ + if (c == IEEE80211_CHAN_ANYC) { + device_printf(sc->sc_dev, "%s: incomplete configuration\n", + __func__); + return EINVAL; + } + + if ((error = wpi_set_timing(sc, ni)) != 0) { + device_printf(sc->sc_dev, + "%s: could not set timing, error %d\n", __func__, error); + return error; + } + + /* Update adapter configuration. */ + WPI_RXON_LOCK(sc); + IEEE80211_ADDR_COPY(sc->rxon.bssid, ni->ni_bssid); + sc->rxon.associd = htole16(IEEE80211_NODE_AID(ni)); + sc->rxon.chan = ieee80211_chan2ieee(ic, c); + sc->rxon.flags = htole32(WPI_RXON_TSF | WPI_RXON_CTS_TO_SELF); + if (IEEE80211_IS_CHAN_2GHZ(c)) + sc->rxon.flags |= htole32(WPI_RXON_AUTO | WPI_RXON_24GHZ); + if (ic->ic_flags & IEEE80211_F_SHSLOT) + sc->rxon.flags |= htole32(WPI_RXON_SHSLOT); + if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) + sc->rxon.flags |= htole32(WPI_RXON_SHPREAMBLE); + if (IEEE80211_IS_CHAN_A(c)) { + sc->rxon.cck_mask = 0; + sc->rxon.ofdm_mask = 0x15; + } else if (IEEE80211_IS_CHAN_B(c)) { + sc->rxon.cck_mask = 0x03; + sc->rxon.ofdm_mask = 0; + } else { + /* Assume 802.11b/g. */ + sc->rxon.cck_mask = 0x0f; + sc->rxon.ofdm_mask = 0x15; + } + sc->rxon.filter |= htole32(WPI_FILTER_BSS); + + DPRINTF(sc, WPI_DEBUG_STATE, "rxon chan %d flags %x\n", + sc->rxon.chan, sc->rxon.flags); + + if ((error = wpi_send_rxon(sc, 0, 1)) != 0) { + device_printf(sc->sc_dev, "%s: could not send RXON\n", + __func__); + return error; + } + + /* Start periodic calibration timer. */ + callout_reset(&sc->calib_to, 60*hz, wpi_calib_timeout, sc); + + WPI_RXON_UNLOCK(sc); + + if (vap->iv_opmode == IEEE80211_M_IBSS || + vap->iv_opmode == IEEE80211_M_HOSTAP) { + if ((error = wpi_setup_beacon(sc, ni)) != 0) { + device_printf(sc->sc_dev, + "%s: could not setup beacon, error %d\n", __func__, + error); + return error; + } + } + + if (vap->iv_opmode == IEEE80211_M_STA) { + /* Add BSS node. */ + WPI_NT_LOCK(sc); + error = wpi_add_sta_node(sc, ni); + WPI_NT_UNLOCK(sc); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: could not add BSS node, error %d\n", __func__, + error); + return error; + } + } + + /* Link LED always on while associated. */ + wpi_set_led(sc, WPI_LED_LINK, 0, 1); + + /* Enable power-saving mode if requested by user. */ + if ((vap->iv_flags & IEEE80211_F_PMGTON) && + vap->iv_opmode != IEEE80211_M_IBSS) + (void)wpi_set_pslevel(sc, 0, 3, 1); + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); + + return 0; +} + +static int +wpi_load_key(struct ieee80211_node *ni, const struct ieee80211_key *k) +{ + const struct ieee80211_cipher *cip = k->wk_cipher; + struct ieee80211vap *vap = ni->ni_vap; + struct wpi_softc *sc = ni->ni_ic->ic_softc; + struct wpi_node *wn = WPI_NODE(ni); + struct wpi_node_info node; + uint16_t kflags; + int error; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + + if (wpi_check_node_entry(sc, wn->id) == 0) { + device_printf(sc->sc_dev, "%s: node does not exist\n", + __func__); + return 0; + } + + switch (cip->ic_cipher) { + case IEEE80211_CIPHER_AES_CCM: + kflags = WPI_KFLAG_CCMP; + break; + + default: + device_printf(sc->sc_dev, "%s: unknown cipher %d\n", __func__, + cip->ic_cipher); + return 0; + } + + kflags |= WPI_KFLAG_KID(k->wk_keyix); + if (k->wk_flags & IEEE80211_KEY_GROUP) + kflags |= WPI_KFLAG_MULTICAST; + + memset(&node, 0, sizeof node); + node.id = wn->id; + node.control = WPI_NODE_UPDATE; + node.flags = WPI_FLAG_KEY_SET; + node.kflags = htole16(kflags); + memcpy(node.key, k->wk_key, k->wk_keylen); +again: + DPRINTF(sc, WPI_DEBUG_KEY, + "%s: setting %s key id %d for node %d (%s)\n", __func__, + (kflags & WPI_KFLAG_MULTICAST) ? "group" : "ucast", k->wk_keyix, + node.id, ether_sprintf(ni->ni_macaddr)); + + error = wpi_cmd(sc, WPI_CMD_ADD_NODE, &node, sizeof node, 1); + if (error != 0) { + device_printf(sc->sc_dev, "can't update node info, error %d\n", + error); + return !error; + } + + if (!(kflags & WPI_KFLAG_MULTICAST) && &vap->iv_nw_keys[0] <= k && + k < &vap->iv_nw_keys[IEEE80211_WEP_NKID]) { + kflags |= WPI_KFLAG_MULTICAST; + node.kflags = htole16(kflags); + + goto again; + } + + return 1; +} + +static void +wpi_load_key_cb(void *arg, struct ieee80211_node *ni) +{ + const struct ieee80211_key *k = arg; + struct ieee80211vap *vap = ni->ni_vap; + struct wpi_softc *sc = ni->ni_ic->ic_softc; + struct wpi_node *wn = WPI_NODE(ni); + int error; + + if (vap->iv_bss == ni && wn->id == WPI_ID_UNDEFINED) + return; + + WPI_NT_LOCK(sc); + error = wpi_load_key(ni, k); + WPI_NT_UNLOCK(sc); + + if (error == 0) { + device_printf(sc->sc_dev, "%s: error while setting key\n", + __func__); + } +} + +static int +wpi_set_global_keys(struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211_key *wk = &vap->iv_nw_keys[0]; + int error = 1; + + for (; wk < &vap->iv_nw_keys[IEEE80211_WEP_NKID] && error; wk++) + if (wk->wk_keyix != IEEE80211_KEYIX_NONE) + error = wpi_load_key(ni, wk); + + return !error; +} + +static int +wpi_del_key(struct ieee80211_node *ni, const struct ieee80211_key *k) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct wpi_softc *sc = ni->ni_ic->ic_softc; + struct wpi_node *wn = WPI_NODE(ni); + struct wpi_node_info node; + uint16_t kflags; + int error; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + + if (wpi_check_node_entry(sc, wn->id) == 0) { + DPRINTF(sc, WPI_DEBUG_KEY, "%s: node was removed\n", __func__); + return 1; /* Nothing to do. */ + } + + kflags = WPI_KFLAG_KID(k->wk_keyix); + if (k->wk_flags & IEEE80211_KEY_GROUP) + kflags |= WPI_KFLAG_MULTICAST; + + memset(&node, 0, sizeof node); + node.id = wn->id; + node.control = WPI_NODE_UPDATE; + node.flags = WPI_FLAG_KEY_SET; + node.kflags = htole16(kflags); +again: + DPRINTF(sc, WPI_DEBUG_KEY, "%s: deleting %s key %d for node %d (%s)\n", + __func__, (kflags & WPI_KFLAG_MULTICAST) ? "group" : "ucast", + k->wk_keyix, node.id, ether_sprintf(ni->ni_macaddr)); + + error = wpi_cmd(sc, WPI_CMD_ADD_NODE, &node, sizeof node, 1); + if (error != 0) { + device_printf(sc->sc_dev, "can't update node info, error %d\n", + error); + return !error; + } + + if (!(kflags & WPI_KFLAG_MULTICAST) && &vap->iv_nw_keys[0] <= k && + k < &vap->iv_nw_keys[IEEE80211_WEP_NKID]) { + kflags |= WPI_KFLAG_MULTICAST; + node.kflags = htole16(kflags); + + goto again; + } + + return 1; +} + +static void +wpi_del_key_cb(void *arg, struct ieee80211_node *ni) +{ + const struct ieee80211_key *k = arg; + struct ieee80211vap *vap = ni->ni_vap; + struct wpi_softc *sc = ni->ni_ic->ic_softc; + struct wpi_node *wn = WPI_NODE(ni); + int error; + + if (vap->iv_bss == ni && wn->id == WPI_ID_UNDEFINED) + return; + + WPI_NT_LOCK(sc); + error = wpi_del_key(ni, k); + WPI_NT_UNLOCK(sc); + + if (error == 0) { + device_printf(sc->sc_dev, "%s: error while deleting key\n", + __func__); + } +} + +static int +wpi_process_key(struct ieee80211vap *vap, const struct ieee80211_key *k, + int set) +{ + struct ieee80211com *ic = vap->iv_ic; + struct wpi_softc *sc = ic->ic_softc; + struct wpi_vap *wvp = WPI_VAP(vap); + struct ieee80211_node *ni; + int error, ni_ref = 0; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + + if (k->wk_flags & IEEE80211_KEY_SWCRYPT) { + /* Not for us. */ + return 1; + } + + if (!(k->wk_flags & IEEE80211_KEY_RECV)) { + /* XMIT keys are handled in wpi_tx_data(). */ + return 1; + } + + /* Handle group keys. */ + if (&vap->iv_nw_keys[0] <= k && + k < &vap->iv_nw_keys[IEEE80211_WEP_NKID]) { + WPI_NT_LOCK(sc); + if (set) + wvp->wv_gtk |= WPI_VAP_KEY(k->wk_keyix); + else + wvp->wv_gtk &= ~WPI_VAP_KEY(k->wk_keyix); + WPI_NT_UNLOCK(sc); + + if (vap->iv_state == IEEE80211_S_RUN) { + ieee80211_iterate_nodes(&ic->ic_sta, + set ? wpi_load_key_cb : wpi_del_key_cb, + __DECONST(void *, k)); + } + + return 1; + } + + switch (vap->iv_opmode) { + case IEEE80211_M_STA: + ni = vap->iv_bss; + break; + + case IEEE80211_M_IBSS: + case IEEE80211_M_AHDEMO: + case IEEE80211_M_HOSTAP: + ni = ieee80211_find_vap_node(&ic->ic_sta, vap, k->wk_macaddr); + if (ni == NULL) + return 0; /* should not happen */ + + ni_ref = 1; + break; + + default: + device_printf(sc->sc_dev, "%s: unknown opmode %d\n", __func__, + vap->iv_opmode); + return 0; + } + + WPI_NT_LOCK(sc); + if (set) + error = wpi_load_key(ni, k); + else + error = wpi_del_key(ni, k); + WPI_NT_UNLOCK(sc); + + if (ni_ref) + ieee80211_node_decref(ni); + + return error; +} + +static int +wpi_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k) +{ + return wpi_process_key(vap, k, 1); +} + +static int +wpi_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k) +{ + return wpi_process_key(vap, k, 0); +} + +/* + * This function is called after the runtime firmware notifies us of its + * readiness (called in a process context). + */ +static int +wpi_post_alive(struct wpi_softc *sc) +{ + int ntries, error; + + /* Check (again) that the radio is not disabled. */ + if ((error = wpi_nic_lock(sc)) != 0) + return error; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + + /* NB: Runtime firmware must be up and running. */ + if (!(wpi_prph_read(sc, WPI_APMG_RFKILL) & 1)) { + device_printf(sc->sc_dev, + "RF switch: radio disabled (%s)\n", __func__); + wpi_nic_unlock(sc); + return EPERM; /* :-) */ + } + wpi_nic_unlock(sc); + + /* Wait for thermal sensor to calibrate. */ + for (ntries = 0; ntries < 1000; ntries++) { + if ((sc->temp = (int)WPI_READ(sc, WPI_UCODE_GP2)) != 0) + break; + DELAY(10); + } + + if (ntries == 1000) { + device_printf(sc->sc_dev, + "timeout waiting for thermal sensor calibration\n"); + return ETIMEDOUT; + } + + DPRINTF(sc, WPI_DEBUG_TEMP, "temperature %d\n", sc->temp); + return 0; +} + +/* + * The firmware boot code is small and is intended to be copied directly into + * the NIC internal memory (no DMA transfer). + */ +static int +wpi_load_bootcode(struct wpi_softc *sc, const uint8_t *ucode, uint32_t size) +{ + int error, ntries; + + DPRINTF(sc, WPI_DEBUG_HW, "Loading microcode size 0x%x\n", size); + + size /= sizeof (uint32_t); + + if ((error = wpi_nic_lock(sc)) != 0) + return error; + + /* Copy microcode image into NIC memory. */ + wpi_prph_write_region_4(sc, WPI_BSM_SRAM_BASE, + (const uint32_t *)ucode, size); + + wpi_prph_write(sc, WPI_BSM_WR_MEM_SRC, 0); + wpi_prph_write(sc, WPI_BSM_WR_MEM_DST, WPI_FW_TEXT_BASE); + wpi_prph_write(sc, WPI_BSM_WR_DWCOUNT, size); + + /* Start boot load now. */ + wpi_prph_write(sc, WPI_BSM_WR_CTRL, WPI_BSM_WR_CTRL_START); + + /* Wait for transfer to complete. */ + for (ntries = 0; ntries < 1000; ntries++) { + uint32_t status = WPI_READ(sc, WPI_FH_TX_STATUS); + DPRINTF(sc, WPI_DEBUG_HW, + "firmware status=0x%x, val=0x%x, result=0x%x\n", status, + WPI_FH_TX_STATUS_IDLE(6), + status & WPI_FH_TX_STATUS_IDLE(6)); + if (status & WPI_FH_TX_STATUS_IDLE(6)) { + DPRINTF(sc, WPI_DEBUG_HW, + "Status Match! - ntries = %d\n", ntries); + break; + } + DELAY(10); + } + if (ntries == 1000) { + device_printf(sc->sc_dev, "%s: could not load boot firmware\n", + __func__); + wpi_nic_unlock(sc); + return ETIMEDOUT; + } + + /* Enable boot after power up. */ + wpi_prph_write(sc, WPI_BSM_WR_CTRL, WPI_BSM_WR_CTRL_START_EN); + + wpi_nic_unlock(sc); + return 0; +} + +static int +wpi_load_firmware(struct wpi_softc *sc) +{ + struct wpi_fw_info *fw = &sc->fw; + struct wpi_dma_info *dma = &sc->fw_dma; + int error; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + + /* Copy initialization sections into pre-allocated DMA-safe memory. */ + memcpy(dma->vaddr, fw->init.data, fw->init.datasz); + bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); + memcpy(dma->vaddr + WPI_FW_DATA_MAXSZ, fw->init.text, fw->init.textsz); + bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); + + /* Tell adapter where to find initialization sections. */ + if ((error = wpi_nic_lock(sc)) != 0) + return error; + wpi_prph_write(sc, WPI_BSM_DRAM_DATA_ADDR, dma->paddr); + wpi_prph_write(sc, WPI_BSM_DRAM_DATA_SIZE, fw->init.datasz); + wpi_prph_write(sc, WPI_BSM_DRAM_TEXT_ADDR, + dma->paddr + WPI_FW_DATA_MAXSZ); + wpi_prph_write(sc, WPI_BSM_DRAM_TEXT_SIZE, fw->init.textsz); + wpi_nic_unlock(sc); + + /* Load firmware boot code. */ + error = wpi_load_bootcode(sc, fw->boot.text, fw->boot.textsz); + if (error != 0) { + device_printf(sc->sc_dev, "%s: could not load boot firmware\n", + __func__); + return error; + } + + /* Now press "execute". */ + WPI_WRITE(sc, WPI_RESET, 0); + + /* Wait at most one second for first alive notification. */ +#if defined(__DragonFly__) + if ((error = lksleep(sc, &sc->sc_mtx, PCATCH, "wpiinit", hz)) != 0) { +#else + if ((error = mtx_sleep(sc, &sc->sc_mtx, PCATCH, "wpiinit", hz)) != 0) { +#endif + device_printf(sc->sc_dev, + "%s: timeout waiting for adapter to initialize, error %d\n", + __func__, error); + return error; + } + + /* Copy runtime sections into pre-allocated DMA-safe memory. */ + memcpy(dma->vaddr, fw->main.data, fw->main.datasz); + bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); + memcpy(dma->vaddr + WPI_FW_DATA_MAXSZ, fw->main.text, fw->main.textsz); + bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); + + /* Tell adapter where to find runtime sections. */ + if ((error = wpi_nic_lock(sc)) != 0) + return error; + wpi_prph_write(sc, WPI_BSM_DRAM_DATA_ADDR, dma->paddr); + wpi_prph_write(sc, WPI_BSM_DRAM_DATA_SIZE, fw->main.datasz); + wpi_prph_write(sc, WPI_BSM_DRAM_TEXT_ADDR, + dma->paddr + WPI_FW_DATA_MAXSZ); + wpi_prph_write(sc, WPI_BSM_DRAM_TEXT_SIZE, + WPI_FW_UPDATED | fw->main.textsz); + wpi_nic_unlock(sc); + + return 0; +} + +static int +wpi_read_firmware(struct wpi_softc *sc) +{ + const struct firmware *fp; + struct wpi_fw_info *fw = &sc->fw; + const struct wpi_firmware_hdr *hdr; + int error; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + + DPRINTF(sc, WPI_DEBUG_FIRMWARE, + "Attempting Loading Firmware from %s module\n", WPI_FW_NAME); + + WPI_UNLOCK(sc); + fp = firmware_get(WPI_FW_NAME); + WPI_LOCK(sc); + + if (fp == NULL) { + device_printf(sc->sc_dev, + "could not load firmware image '%s'\n", WPI_FW_NAME); + return EINVAL; + } + + sc->fw_fp = fp; + + if (fp->datasize < sizeof (struct wpi_firmware_hdr)) { + device_printf(sc->sc_dev, + "firmware file too short: %zu bytes\n", fp->datasize); + error = EINVAL; + goto fail; + } + + fw->size = fp->datasize; + fw->data = (const uint8_t *)fp->data; + + /* Extract firmware header information. */ + hdr = (const struct wpi_firmware_hdr *)fw->data; + + /* | RUNTIME FIRMWARE | INIT FIRMWARE | BOOT FW | + |HDR|<--TEXT-->|<--DATA-->|<--TEXT-->|<--DATA-->|<--TEXT-->| */ + + fw->main.textsz = le32toh(hdr->rtextsz); + fw->main.datasz = le32toh(hdr->rdatasz); + fw->init.textsz = le32toh(hdr->itextsz); + fw->init.datasz = le32toh(hdr->idatasz); + fw->boot.textsz = le32toh(hdr->btextsz); + fw->boot.datasz = 0; + + /* Sanity-check firmware header. */ + if (fw->main.textsz > WPI_FW_TEXT_MAXSZ || + fw->main.datasz > WPI_FW_DATA_MAXSZ || + fw->init.textsz > WPI_FW_TEXT_MAXSZ || + fw->init.datasz > WPI_FW_DATA_MAXSZ || + fw->boot.textsz > WPI_FW_BOOT_TEXT_MAXSZ || + (fw->boot.textsz & 3) != 0) { + device_printf(sc->sc_dev, "invalid firmware header\n"); + error = EINVAL; + goto fail; + } + + /* Check that all firmware sections fit. */ + if (fw->size < sizeof (*hdr) + fw->main.textsz + fw->main.datasz + + fw->init.textsz + fw->init.datasz + fw->boot.textsz) { + device_printf(sc->sc_dev, + "firmware file too short: %zu bytes\n", fw->size); + error = EINVAL; + goto fail; + } + + /* Get pointers to firmware sections. */ + fw->main.text = (const uint8_t *)(hdr + 1); + fw->main.data = fw->main.text + fw->main.textsz; + fw->init.text = fw->main.data + fw->main.datasz; + fw->init.data = fw->init.text + fw->init.textsz; + fw->boot.text = fw->init.data + fw->init.datasz; + + DPRINTF(sc, WPI_DEBUG_FIRMWARE, + "Firmware Version: Major %d, Minor %d, Driver %d, \n" + "runtime (text: %u, data: %u) init (text: %u, data %u) " + "boot (text %u)\n", hdr->major, hdr->minor, le32toh(hdr->driver), + fw->main.textsz, fw->main.datasz, + fw->init.textsz, fw->init.datasz, fw->boot.textsz); + + DPRINTF(sc, WPI_DEBUG_FIRMWARE, "fw->main.text %p\n", fw->main.text); + DPRINTF(sc, WPI_DEBUG_FIRMWARE, "fw->main.data %p\n", fw->main.data); + DPRINTF(sc, WPI_DEBUG_FIRMWARE, "fw->init.text %p\n", fw->init.text); + DPRINTF(sc, WPI_DEBUG_FIRMWARE, "fw->init.data %p\n", fw->init.data); + DPRINTF(sc, WPI_DEBUG_FIRMWARE, "fw->boot.text %p\n", fw->boot.text); + + return 0; + +fail: wpi_unload_firmware(sc); + return error; +} + +/** + * Free the referenced firmware image + */ +static void +wpi_unload_firmware(struct wpi_softc *sc) +{ + if (sc->fw_fp != NULL) { + firmware_put(sc->fw_fp, FIRMWARE_UNLOAD); + sc->fw_fp = NULL; + } +} + +static int +wpi_clock_wait(struct wpi_softc *sc) +{ + int ntries; + + /* Set "initialization complete" bit. */ + WPI_SETBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_INIT_DONE); + + /* Wait for clock stabilization. */ + for (ntries = 0; ntries < 2500; ntries++) { + if (WPI_READ(sc, WPI_GP_CNTRL) & WPI_GP_CNTRL_MAC_CLOCK_READY) + return 0; + DELAY(100); + } + device_printf(sc->sc_dev, + "%s: timeout waiting for clock stabilization\n", __func__); + + return ETIMEDOUT; +} + +static int +wpi_apm_init(struct wpi_softc *sc) +{ + uint32_t reg; + int error; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + + /* Disable L0s exit timer (NMI bug workaround). */ + WPI_SETBITS(sc, WPI_GIO_CHICKEN, WPI_GIO_CHICKEN_DIS_L0S_TIMER); + /* Don't wait for ICH L0s (ICH bug workaround). */ + WPI_SETBITS(sc, WPI_GIO_CHICKEN, WPI_GIO_CHICKEN_L1A_NO_L0S_RX); + + /* Set FH wait threshold to max (HW bug under stress workaround). */ + WPI_SETBITS(sc, WPI_DBG_HPET_MEM, 0xffff0000); + + /* Retrieve PCIe Active State Power Management (ASPM). */ +#if defined(__DragonFly__) + reg = pci_read_config(sc->sc_dev, sc->sc_cap_off + PCIER_LINKCTRL, 1); + /* Workaround for HW instability in PCIe L0->L0s->L1 transition. */ + if (reg & PCIEM_LNKCTL_ASPM_L1) /* L1 Entry enabled. */ +#else + reg = pci_read_config(sc->sc_dev, sc->sc_cap_off + PCIER_LINK_CTL, 1); + /* Workaround for HW instability in PCIe L0->L0s->L1 transition. */ + if (reg & PCIEM_LINK_CTL_ASPMC_L1) /* L1 Entry enabled. */ +#endif + WPI_SETBITS(sc, WPI_GIO, WPI_GIO_L0S_ENA); + else + WPI_CLRBITS(sc, WPI_GIO, WPI_GIO_L0S_ENA); + + WPI_SETBITS(sc, WPI_ANA_PLL, WPI_ANA_PLL_INIT); + + /* Wait for clock stabilization before accessing prph. */ + if ((error = wpi_clock_wait(sc)) != 0) + return error; + + if ((error = wpi_nic_lock(sc)) != 0) + return error; + /* Cleanup. */ + wpi_prph_write(sc, WPI_APMG_CLK_DIS, 0x00000400); + wpi_prph_clrbits(sc, WPI_APMG_PS, 0x00000200); + + /* Enable DMA and BSM (Bootstrap State Machine). */ + wpi_prph_write(sc, WPI_APMG_CLK_EN, + WPI_APMG_CLK_CTRL_DMA_CLK_RQT | WPI_APMG_CLK_CTRL_BSM_CLK_RQT); + DELAY(20); + /* Disable L1-Active. */ + wpi_prph_setbits(sc, WPI_APMG_PCI_STT, WPI_APMG_PCI_STT_L1A_DIS); + wpi_nic_unlock(sc); + + return 0; +} + +static void +wpi_apm_stop_master(struct wpi_softc *sc) +{ + int ntries; + + /* Stop busmaster DMA activity. */ + WPI_SETBITS(sc, WPI_RESET, WPI_RESET_STOP_MASTER); + + if ((WPI_READ(sc, WPI_GP_CNTRL) & WPI_GP_CNTRL_PS_MASK) == + WPI_GP_CNTRL_MAC_PS) + return; /* Already asleep. */ + + for (ntries = 0; ntries < 100; ntries++) { + if (WPI_READ(sc, WPI_RESET) & WPI_RESET_MASTER_DISABLED) + return; + DELAY(10); + } + device_printf(sc->sc_dev, "%s: timeout waiting for master\n", + __func__); +} + +static void +wpi_apm_stop(struct wpi_softc *sc) +{ + wpi_apm_stop_master(sc); + + /* Reset the entire device. */ + WPI_SETBITS(sc, WPI_RESET, WPI_RESET_SW); + DELAY(10); + /* Clear "initialization complete" bit. */ + WPI_CLRBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_INIT_DONE); +} + +static void +wpi_nic_config(struct wpi_softc *sc) +{ + uint32_t rev; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + + /* voodoo from the Linux "driver".. */ + rev = pci_read_config(sc->sc_dev, PCIR_REVID, 1); + if ((rev & 0xc0) == 0x40) + WPI_SETBITS(sc, WPI_HW_IF_CONFIG, WPI_HW_IF_CONFIG_ALM_MB); + else if (!(rev & 0x80)) + WPI_SETBITS(sc, WPI_HW_IF_CONFIG, WPI_HW_IF_CONFIG_ALM_MM); + + if (sc->cap == 0x80) + WPI_SETBITS(sc, WPI_HW_IF_CONFIG, WPI_HW_IF_CONFIG_SKU_MRC); + + if ((sc->rev & 0xf0) == 0xd0) + WPI_SETBITS(sc, WPI_HW_IF_CONFIG, WPI_HW_IF_CONFIG_REV_D); + else + WPI_CLRBITS(sc, WPI_HW_IF_CONFIG, WPI_HW_IF_CONFIG_REV_D); + + if (sc->type > 1) + WPI_SETBITS(sc, WPI_HW_IF_CONFIG, WPI_HW_IF_CONFIG_TYPE_B); +} + +static int +wpi_hw_init(struct wpi_softc *sc) +{ + uint8_t chnl; + int ntries, error; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); + + /* Clear pending interrupts. */ + WPI_WRITE(sc, WPI_INT, 0xffffffff); + + if ((error = wpi_apm_init(sc)) != 0) { + device_printf(sc->sc_dev, + "%s: could not power ON adapter, error %d\n", __func__, + error); + return error; + } + + /* Select VMAIN power source. */ + if ((error = wpi_nic_lock(sc)) != 0) + return error; + wpi_prph_clrbits(sc, WPI_APMG_PS, WPI_APMG_PS_PWR_SRC_MASK); + wpi_nic_unlock(sc); + /* Spin until VMAIN gets selected. */ + for (ntries = 0; ntries < 5000; ntries++) { + if (WPI_READ(sc, WPI_GPIO_IN) & WPI_GPIO_IN_VMAIN) + break; + DELAY(10); + } + if (ntries == 5000) { + device_printf(sc->sc_dev, "timeout selecting power source\n"); + return ETIMEDOUT; + } + + /* Perform adapter initialization. */ + wpi_nic_config(sc); + + /* Initialize RX ring. */ + if ((error = wpi_nic_lock(sc)) != 0) + return error; + /* Set physical address of RX ring. */ + WPI_WRITE(sc, WPI_FH_RX_BASE, sc->rxq.desc_dma.paddr); + /* Set physical address of RX read pointer. */ + WPI_WRITE(sc, WPI_FH_RX_RPTR_ADDR, sc->shared_dma.paddr + + offsetof(struct wpi_shared, next)); + WPI_WRITE(sc, WPI_FH_RX_WPTR, 0); + /* Enable RX. */ + WPI_WRITE(sc, WPI_FH_RX_CONFIG, + WPI_FH_RX_CONFIG_DMA_ENA | + WPI_FH_RX_CONFIG_RDRBD_ENA | + WPI_FH_RX_CONFIG_WRSTATUS_ENA | + WPI_FH_RX_CONFIG_MAXFRAG | + WPI_FH_RX_CONFIG_NRBD(WPI_RX_RING_COUNT_LOG) | + WPI_FH_RX_CONFIG_IRQ_DST_HOST | + WPI_FH_RX_CONFIG_IRQ_TIMEOUT(1)); + (void)WPI_READ(sc, WPI_FH_RSSR_TBL); /* barrier */ + wpi_nic_unlock(sc); + WPI_WRITE(sc, WPI_FH_RX_WPTR, (WPI_RX_RING_COUNT - 1) & ~7); + + /* Initialize TX rings. */ + if ((error = wpi_nic_lock(sc)) != 0) + return error; + wpi_prph_write(sc, WPI_ALM_SCHED_MODE, 2); /* bypass mode */ + wpi_prph_write(sc, WPI_ALM_SCHED_ARASTAT, 1); /* enable RA0 */ + /* Enable all 6 TX rings. */ + wpi_prph_write(sc, WPI_ALM_SCHED_TXFACT, 0x3f); + wpi_prph_write(sc, WPI_ALM_SCHED_SBYPASS_MODE1, 0x10000); + wpi_prph_write(sc, WPI_ALM_SCHED_SBYPASS_MODE2, 0x30002); + wpi_prph_write(sc, WPI_ALM_SCHED_TXF4MF, 4); + wpi_prph_write(sc, WPI_ALM_SCHED_TXF5MF, 5); + /* Set physical address of TX rings. */ + WPI_WRITE(sc, WPI_FH_TX_BASE, sc->shared_dma.paddr); + WPI_WRITE(sc, WPI_FH_MSG_CONFIG, 0xffff05a5); + + /* Enable all DMA channels. */ + for (chnl = 0; chnl < WPI_NDMACHNLS; chnl++) { + WPI_WRITE(sc, WPI_FH_CBBC_CTRL(chnl), 0); + WPI_WRITE(sc, WPI_FH_CBBC_BASE(chnl), 0); + WPI_WRITE(sc, WPI_FH_TX_CONFIG(chnl), 0x80200008); + } + wpi_nic_unlock(sc); + (void)WPI_READ(sc, WPI_FH_TX_BASE); /* barrier */ + + /* Clear "radio off" and "commands blocked" bits. */ + WPI_WRITE(sc, WPI_UCODE_GP1_CLR, WPI_UCODE_GP1_RFKILL); + WPI_WRITE(sc, WPI_UCODE_GP1_CLR, WPI_UCODE_GP1_CMD_BLOCKED); + + /* Clear pending interrupts. */ + WPI_WRITE(sc, WPI_INT, 0xffffffff); + /* Enable interrupts. */ + WPI_WRITE(sc, WPI_INT_MASK, WPI_INT_MASK_DEF); + + /* _Really_ make sure "radio off" bit is cleared! */ + WPI_WRITE(sc, WPI_UCODE_GP1_CLR, WPI_UCODE_GP1_RFKILL); + WPI_WRITE(sc, WPI_UCODE_GP1_CLR, WPI_UCODE_GP1_RFKILL); + + if ((error = wpi_load_firmware(sc)) != 0) { + device_printf(sc->sc_dev, + "%s: could not load firmware, error %d\n", __func__, + error); + return error; + } + /* Wait at most one second for firmware alive notification. */ +#if defined(__DragonFly__) + if ((error = lksleep(sc, &sc->sc_mtx, PCATCH, "wpiinit", hz)) != 0) { +#else + if ((error = mtx_sleep(sc, &sc->sc_mtx, PCATCH, "wpiinit", hz)) != 0) { +#endif + device_printf(sc->sc_dev, + "%s: timeout waiting for adapter to initialize, error %d\n", + __func__, error); + return error; + } + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); + + /* Do post-firmware initialization. */ + return wpi_post_alive(sc); +} + +static void +wpi_hw_stop(struct wpi_softc *sc) +{ + uint8_t chnl, qid; + int ntries; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + + if (WPI_READ(sc, WPI_UCODE_GP1) & WPI_UCODE_GP1_MAC_SLEEP) + wpi_nic_lock(sc); + + WPI_WRITE(sc, WPI_RESET, WPI_RESET_NEVO); + + /* Disable interrupts. */ + WPI_WRITE(sc, WPI_INT_MASK, 0); + WPI_WRITE(sc, WPI_INT, 0xffffffff); + WPI_WRITE(sc, WPI_FH_INT, 0xffffffff); + + /* Make sure we no longer hold the NIC lock. */ + wpi_nic_unlock(sc); + + if (wpi_nic_lock(sc) == 0) { + /* Stop TX scheduler. */ + wpi_prph_write(sc, WPI_ALM_SCHED_MODE, 0); + wpi_prph_write(sc, WPI_ALM_SCHED_TXFACT, 0); + + /* Stop all DMA channels. */ + for (chnl = 0; chnl < WPI_NDMACHNLS; chnl++) { + WPI_WRITE(sc, WPI_FH_TX_CONFIG(chnl), 0); + for (ntries = 0; ntries < 200; ntries++) { + if (WPI_READ(sc, WPI_FH_TX_STATUS) & + WPI_FH_TX_STATUS_IDLE(chnl)) + break; + DELAY(10); + } + } + wpi_nic_unlock(sc); + } + + /* Stop RX ring. */ + wpi_reset_rx_ring(sc); + + /* Reset all TX rings. */ + for (qid = 0; qid < WPI_DRV_NTXQUEUES; qid++) + wpi_reset_tx_ring(sc, &sc->txq[qid]); + + if (wpi_nic_lock(sc) == 0) { + wpi_prph_write(sc, WPI_APMG_CLK_DIS, + WPI_APMG_CLK_CTRL_DMA_CLK_RQT); + wpi_nic_unlock(sc); + } + DELAY(5); + /* Power OFF adapter. */ + wpi_apm_stop(sc); +} + +static void +wpi_radio_on(void *arg0, int pending) +{ + struct wpi_softc *sc = arg0; + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + + device_printf(sc->sc_dev, "RF switch: radio enabled\n"); + + WPI_LOCK(sc); + callout_stop(&sc->watchdog_rfkill); + WPI_UNLOCK(sc); + + if (vap != NULL) + ieee80211_init(vap); +} + +static void +wpi_radio_off(void *arg0, int pending) +{ + struct wpi_softc *sc = arg0; + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + + device_printf(sc->sc_dev, "RF switch: radio disabled\n"); + + ieee80211_notify_radio(ic, 0); + wpi_stop(sc); + if (vap != NULL) + ieee80211_stop(vap); + + WPI_LOCK(sc); + callout_reset(&sc->watchdog_rfkill, hz, wpi_watchdog_rfkill, sc); + WPI_UNLOCK(sc); +} + +static int +wpi_init(struct wpi_softc *sc) +{ + int error = 0; + + WPI_LOCK(sc); + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); + + if (sc->sc_running != 0) + goto end; + + /* Check that the radio is not disabled by hardware switch. */ + if (!(WPI_READ(sc, WPI_GP_CNTRL) & WPI_GP_CNTRL_RFKILL)) { + device_printf(sc->sc_dev, + "RF switch: radio disabled (%s)\n", __func__); + callout_reset(&sc->watchdog_rfkill, hz, wpi_watchdog_rfkill, + sc); + error = EINPROGRESS; + goto end; + } + + /* Read firmware images from the filesystem. */ + if ((error = wpi_read_firmware(sc)) != 0) { + device_printf(sc->sc_dev, + "%s: could not read firmware, error %d\n", __func__, + error); + goto end; + } + + sc->sc_running = 1; + + /* Initialize hardware and upload firmware. */ + error = wpi_hw_init(sc); + wpi_unload_firmware(sc); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: could not initialize hardware, error %d\n", __func__, + error); + goto fail; + } + + /* Configure adapter now that it is ready. */ + if ((error = wpi_config(sc)) != 0) { + device_printf(sc->sc_dev, + "%s: could not configure device, error %d\n", __func__, + error); + goto fail; + } + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); + + WPI_UNLOCK(sc); + + return 0; + +fail: wpi_stop_locked(sc); + +end: DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__); + WPI_UNLOCK(sc); + + return error; +} + +static void +wpi_stop_locked(struct wpi_softc *sc) +{ + + WPI_LOCK_ASSERT(sc); + + if (sc->sc_running == 0) + return; + + WPI_TX_LOCK(sc); + WPI_TXQ_LOCK(sc); + sc->sc_running = 0; + WPI_TXQ_UNLOCK(sc); + WPI_TX_UNLOCK(sc); + + WPI_TXQ_STATE_LOCK(sc); + callout_stop(&sc->tx_timeout); + WPI_TXQ_STATE_UNLOCK(sc); + + WPI_RXON_LOCK(sc); + callout_stop(&sc->scan_timeout); + callout_stop(&sc->calib_to); + WPI_RXON_UNLOCK(sc); + + /* Power OFF hardware. */ + wpi_hw_stop(sc); +} + +static void +wpi_stop(struct wpi_softc *sc) +{ + WPI_LOCK(sc); + wpi_stop_locked(sc); + WPI_UNLOCK(sc); +} + +/* + * Callback from net80211 to start a scan. + */ +static void +wpi_scan_start(struct ieee80211com *ic) +{ + struct wpi_softc *sc = ic->ic_softc; + + wpi_set_led(sc, WPI_LED_LINK, 20, 2); +} + +/* + * Callback from net80211 to terminate a scan. + */ +static void +wpi_scan_end(struct ieee80211com *ic) +{ + struct wpi_softc *sc = ic->ic_softc; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + + if (vap->iv_state == IEEE80211_S_RUN) + wpi_set_led(sc, WPI_LED_LINK, 0, 1); +} + +/** + * Called by the net80211 framework to indicate to the driver + * that the channel should be changed + */ +static void +wpi_set_channel(struct ieee80211com *ic) +{ + const struct ieee80211_channel *c = ic->ic_curchan; + struct wpi_softc *sc = ic->ic_softc; + int error; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + + WPI_LOCK(sc); + sc->sc_rxtap.wr_chan_freq = htole16(c->ic_freq); + sc->sc_rxtap.wr_chan_flags = htole16(c->ic_flags); + WPI_UNLOCK(sc); + WPI_TX_LOCK(sc); + sc->sc_txtap.wt_chan_freq = htole16(c->ic_freq); + sc->sc_txtap.wt_chan_flags = htole16(c->ic_flags); + WPI_TX_UNLOCK(sc); + + /* + * Only need to set the channel in Monitor mode. AP scanning and auth + * are already taken care of by their respective firmware commands. + */ + if (ic->ic_opmode == IEEE80211_M_MONITOR) { + WPI_RXON_LOCK(sc); + sc->rxon.chan = ieee80211_chan2ieee(ic, c); + if (IEEE80211_IS_CHAN_2GHZ(c)) { + sc->rxon.flags |= htole32(WPI_RXON_AUTO | + WPI_RXON_24GHZ); + } else { + sc->rxon.flags &= ~htole32(WPI_RXON_AUTO | + WPI_RXON_24GHZ); + } + if ((error = wpi_send_rxon(sc, 0, 1)) != 0) + device_printf(sc->sc_dev, + "%s: error %d setting channel\n", __func__, + error); + WPI_RXON_UNLOCK(sc); + } +} + +/** + * Called by net80211 to indicate that we need to scan the current + * channel. The channel is previously be set via the wpi_set_channel + * callback. + */ +static void +wpi_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell) +{ + struct ieee80211vap *vap = ss->ss_vap; + struct ieee80211com *ic = vap->iv_ic; + struct wpi_softc *sc = ic->ic_softc; + int error; + + WPI_RXON_LOCK(sc); + error = wpi_scan(sc, ic->ic_curchan); + WPI_RXON_UNLOCK(sc); + if (error != 0) + ieee80211_cancel_scan(vap); +} + +/** + * Called by the net80211 framework to indicate + * the minimum dwell time has been met, terminate the scan. + * We don't actually terminate the scan as the firmware will notify + * us when it's finished and we have no way to interrupt it. + */ +static void +wpi_scan_mindwell(struct ieee80211_scan_state *ss) +{ + /* NB: don't try to abort scan; wait for firmware to finish */ +} diff --git a/sys/dev/netif/wpi/if_wpi_debug.h b/sys/dev/netif/wpi/if_wpi_debug.h new file mode 100644 index 0000000000..da4b71f16d --- /dev/null +++ b/sys/dev/netif/wpi/if_wpi_debug.h @@ -0,0 +1,144 @@ +/*- + * Copyright (c) 2006,2007 + * Damien Bergamini + * Benjamin Close + * Copyright (c) 2015 Andriy Voskoboinyk + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * $FreeBSD$ + */ + +#ifndef __IF_WPI_DEBUG_H__ +#define __IF_WPI_DEBUG_H__ + +#ifdef WPI_DEBUG +enum { + WPI_DEBUG_XMIT = 0x00000001, /* basic xmit operation */ + WPI_DEBUG_RECV = 0x00000002, /* basic recv operation */ + WPI_DEBUG_STATE = 0x00000004, /* 802.11 state transitions */ + WPI_DEBUG_HW = 0x00000008, /* Stage 1 (eeprom) debugging */ + WPI_DEBUG_RESET = 0x00000010, /* reset processing */ + WPI_DEBUG_FIRMWARE = 0x00000020, /* firmware(9) loading debug */ + WPI_DEBUG_BEACON = 0x00000040, /* beacon handling */ + WPI_DEBUG_WATCHDOG = 0x00000080, /* watchdog timeout */ + WPI_DEBUG_INTR = 0x00000100, /* ISR */ + WPI_DEBUG_SCAN = 0x00000200, /* Scan related operations */ + WPI_DEBUG_NOTIFY = 0x00000400, /* State 2 Notif intr debug */ + WPI_DEBUG_TEMP = 0x00000800, /* TXPower/Temp Calibration */ + WPI_DEBUG_CMD = 0x00001000, /* cmd submission */ + WPI_DEBUG_TRACE = 0x00002000, /* Print begin and start driver function */ + WPI_DEBUG_PWRSAVE = 0x00004000, /* Power save operations */ + WPI_DEBUG_EEPROM = 0x00008000, /* EEPROM info */ + WPI_DEBUG_NODE = 0x00010000, /* node addition/removal */ + WPI_DEBUG_KEY = 0x00020000, /* node key management */ + WPI_DEBUG_EDCA = 0x00040000, /* WME info */ + WPI_DEBUG_REGISTER = 0x00080000, /* print chipset register */ + WPI_DEBUG_BMISS = 0x00100000, /* print number of missed beacons */ + WPI_DEBUG_ANY = 0xffffffff +}; + +#define DPRINTF(sc, m, ...) do { \ + if (sc->sc_debug & (m)) \ + printf(__VA_ARGS__); \ +} while (0) + +#define TRACE_STR_BEGIN "->%s: begin\n" +#define TRACE_STR_DOING "->Doing %s\n" +#define TRACE_STR_END "->%s: end\n" +#define TRACE_STR_END_ERR "->%s: end in error\n" + +#define WPI_DESC(x) case x: return #x + +static const char *wpi_cmd_str(int cmd) +{ + switch (cmd) { + /* Notifications. */ + WPI_DESC(WPI_UC_READY); + WPI_DESC(WPI_RX_DONE); + WPI_DESC(WPI_START_SCAN); + WPI_DESC(WPI_SCAN_RESULTS); + WPI_DESC(WPI_STOP_SCAN); + WPI_DESC(WPI_BEACON_SENT); + WPI_DESC(WPI_RX_STATISTICS); + WPI_DESC(WPI_BEACON_STATISTICS); + WPI_DESC(WPI_STATE_CHANGED); + WPI_DESC(WPI_BEACON_MISSED); + + /* Command notifications. */ + WPI_DESC(WPI_CMD_RXON); + WPI_DESC(WPI_CMD_RXON_ASSOC); + WPI_DESC(WPI_CMD_EDCA_PARAMS); + WPI_DESC(WPI_CMD_TIMING); + WPI_DESC(WPI_CMD_ADD_NODE); + WPI_DESC(WPI_CMD_DEL_NODE); + WPI_DESC(WPI_CMD_TX_DATA); + WPI_DESC(WPI_CMD_MRR_SETUP); + WPI_DESC(WPI_CMD_SET_LED); + WPI_DESC(WPI_CMD_SET_POWER_MODE); + WPI_DESC(WPI_CMD_SCAN); + WPI_DESC(WPI_CMD_SCAN_ABORT); + WPI_DESC(WPI_CMD_SET_BEACON); + WPI_DESC(WPI_CMD_TXPOWER); + WPI_DESC(WPI_CMD_BT_COEX); + + default: + return "UNKNOWN CMD"; + } +} + +/* + * Translate CSR code to string + */ +static const char *wpi_get_csr_string(size_t csr) +{ + switch (csr) { + WPI_DESC(WPI_HW_IF_CONFIG); + WPI_DESC(WPI_INT); + WPI_DESC(WPI_INT_MASK); + WPI_DESC(WPI_FH_INT); + WPI_DESC(WPI_GPIO_IN); + WPI_DESC(WPI_RESET); + WPI_DESC(WPI_GP_CNTRL); + WPI_DESC(WPI_EEPROM); + WPI_DESC(WPI_EEPROM_GP); + WPI_DESC(WPI_GIO); + WPI_DESC(WPI_UCODE_GP1); + WPI_DESC(WPI_UCODE_GP2); + WPI_DESC(WPI_GIO_CHICKEN); + WPI_DESC(WPI_ANA_PLL); + WPI_DESC(WPI_DBG_HPET_MEM); + default: + KASSERT(0, ("Unknown CSR: %d\n", csr)); + return "UNKNOWN CSR"; + } +} + +static const char *wpi_get_prph_string(size_t prph) +{ + switch (prph) { + WPI_DESC(WPI_APMG_CLK_CTRL); + WPI_DESC(WPI_APMG_PS); + WPI_DESC(WPI_APMG_PCI_STT); + WPI_DESC(WPI_APMG_RFKILL); + default: + KASSERT(0, ("Unknown register: %d\n", prph)); + return "UNKNOWN PRPH"; + } +} + +#else +#define DPRINTF(sc, m, ...) do { (void) sc; } while (0) +#endif + +#endif /* __IF_WPI_DEBUG_H__ */ diff --git a/sys/dev/netif/wpi/if_wpireg.h b/sys/dev/netif/wpi/if_wpireg.h index 60d183a673..b0154f22bc 100644 --- a/sys/dev/netif/wpi/if_wpireg.h +++ b/sys/dev/netif/wpi/if_wpireg.h @@ -18,181 +18,264 @@ */ #define WPI_TX_RING_COUNT 256 -#define WPI_CMD_RING_COUNT 256 -#define WPI_RX_RING_COUNT 64 +#define WPI_TX_RING_LOMARK 192 +#define WPI_TX_RING_HIMARK 224 + +#ifdef DIAGNOSTIC +#define WPI_RX_RING_COUNT_LOG 8 +#else +#define WPI_RX_RING_COUNT_LOG 6 +#endif + +#define WPI_RX_RING_COUNT (1 << WPI_RX_RING_COUNT_LOG) + +#define WPI_NTXQUEUES 8 +#define WPI_DRV_NTXQUEUES 5 +#define WPI_CMD_QUEUE_NUM 4 + +#define WPI_NDMACHNLS 6 + +/* Maximum scatter/gather. */ +#define WPI_MAX_SCATTER 4 /* * Rings must be aligned on a 16K boundary. */ #define WPI_RING_DMA_ALIGN 0x4000 -/* maximum scatter/gather */ -#define WPI_MAX_SCATTER 4 - -/* maximum Rx buffer size */ +/* Maximum Rx buffer size. */ #define WPI_RBUF_SIZE ( 3 * 1024 ) /* XXX 3000 but must be aligned */ /* * Control and status registers. */ -#define WPI_HWCONFIG 0x000 -#define WPI_INTR 0x008 -#define WPI_MASK 0x00c -#define WPI_INTR_STATUS 0x010 -#define WPI_GPIO_STATUS 0x018 +#define WPI_HW_IF_CONFIG 0x000 +#define WPI_INT 0x008 +#define WPI_INT_MASK 0x00c +#define WPI_FH_INT 0x010 +#define WPI_GPIO_IN 0x018 #define WPI_RESET 0x020 -#define WPI_GPIO_CTL 0x024 -#define WPI_EEPROM_CTL 0x02c -#define WPI_EEPROM_STATUS 0x030 -#define WPI_UCODE_SET 0x058 -#define WPI_UCODE_CLR 0x05c -#define WPI_TEMPERATURE 0x060 -#define WPI_CHICKEN 0x100 -#define WPI_PLL_CTL 0x20c -#define WPI_WRITE_MEM_ADDR 0x444 -#define WPI_READ_MEM_ADDR 0x448 -#define WPI_WRITE_MEM_DATA 0x44c -#define WPI_READ_MEM_DATA 0x450 -#define WPI_TX_WIDX 0x460 -#define WPI_TX_CTL(qid) (0x940 + (qid) * 8) -#define WPI_TX_BASE(qid) (0x944 + (qid) * 8) -#define WPI_TX_DESC(qid) (0x980 + (qid) * 80) -#define WPI_RX_CONFIG 0xc00 -#define WPI_RX_BASE 0xc04 -#define WPI_RX_WIDX 0xc20 -#define WPI_RX_RIDX_PTR 0xc24 -#define WPI_RX_CTL 0xcc0 -#define WPI_RX_STATUS 0xcc4 -#define WPI_TX_CONFIG(qid) (0xd00 + (qid) * 32) -#define WPI_TX_CREDIT(qid) (0xd04 + (qid) * 32) -#define WPI_TX_STATE(qid) (0xd08 + (qid) * 32) -#define WPI_TX_BASE_PTR 0xe80 -#define WPI_MSG_CONFIG 0xe88 -#define WPI_TX_STATUS 0xe90 +#define WPI_GP_CNTRL 0x024 +#define WPI_EEPROM 0x02c +#define WPI_EEPROM_GP 0x030 +#define WPI_GIO 0x03c +#define WPI_UCODE_GP1 0x054 +#define WPI_UCODE_GP1_SET 0x058 +#define WPI_UCODE_GP1_CLR 0x05c +#define WPI_UCODE_GP2 0x060 +#define WPI_GIO_CHICKEN 0x100 +#define WPI_ANA_PLL 0x20c +#define WPI_DBG_HPET_MEM 0x240 +#define WPI_MEM_RADDR 0x40c +#define WPI_MEM_WADDR 0x410 +#define WPI_MEM_WDATA 0x418 +#define WPI_MEM_RDATA 0x41c +#define WPI_PRPH_WADDR 0x444 +#define WPI_PRPH_RADDR 0x448 +#define WPI_PRPH_WDATA 0x44c +#define WPI_PRPH_RDATA 0x450 +#define WPI_HBUS_TARG_WRPTR 0x460 + +/* + * Flow-Handler registers. + */ +#define WPI_FH_CBBC_CTRL(qid) (0x940 + (qid) * 8) +#define WPI_FH_CBBC_BASE(qid) (0x944 + (qid) * 8) +#define WPI_FH_RX_CONFIG 0xc00 +#define WPI_FH_RX_BASE 0xc04 +#define WPI_FH_RX_WPTR 0xc20 +#define WPI_FH_RX_RPTR_ADDR 0xc24 +#define WPI_FH_RSSR_TBL 0xcc0 +#define WPI_FH_RX_STATUS 0xcc4 +#define WPI_FH_TX_CONFIG(qid) (0xd00 + (qid) * 32) +#define WPI_FH_TX_BASE 0xe80 +#define WPI_FH_MSG_CONFIG 0xe88 +#define WPI_FH_TX_STATUS 0xe90 /* * NIC internal memory offsets. */ -#define WPI_MEM_MODE 0x2e00 -#define WPI_MEM_RA 0x2e04 -#define WPI_MEM_TXCFG 0x2e10 -#define WPI_MEM_MAGIC4 0x2e14 -#define WPI_MEM_MAGIC5 0x2e20 -#define WPI_MEM_BYPASS1 0x2e2c -#define WPI_MEM_BYPASS2 0x2e30 -#define WPI_MEM_CLOCK1 0x3004 -#define WPI_MEM_CLOCK2 0x3008 -#define WPI_MEM_POWER 0x300c -#define WPI_MEM_PCIDEV 0x3010 -#define WPI_MEM_HW_RADIO_OFF 0x3014 -#define WPI_MEM_UCODE_CTL 0x3400 -#define WPI_MEM_UCODE_SRC 0x3404 -#define WPI_MEM_UCODE_DST 0x3408 -#define WPI_MEM_UCODE_SIZE 0x340c -#define WPI_MEM_UCODE_BASE 0x3800 - -#define WPI_MEM_TEXT_BASE 0x3490 -#define WPI_MEM_TEXT_SIZE 0x3494 -#define WPI_MEM_DATA_BASE 0x3498 -#define WPI_MEM_DATA_SIZE 0x349c - - -/* possible flags for register WPI_HWCONFIG */ -#define WPI_HW_ALM_MB (1 << 8) -#define WPI_HW_ALM_MM (1 << 9) -#define WPI_HW_SKU_MRC (1 << 10) -#define WPI_HW_REV_D (1 << 11) -#define WPI_HW_TYPE_B (1 << 12) - -/* possible flags for registers WPI_READ_MEM_ADDR/WPI_WRITE_MEM_ADDR */ -#define WPI_MEM_4 ((sizeof (uint32_t) - 1) << 24) - -/* possible values for WPI_MEM_UCODE_DST */ -#define WPI_FW_TEXT 0x00000000 - -/* possible flags for WPI_GPIO_STATUS */ -#define WPI_POWERED (1 << 9) - -/* possible flags for register WPI_RESET */ -#define WPI_NEVO_RESET (1 << 0) -#define WPI_SW_RESET (1 << 7) -#define WPI_MASTER_DISABLED (1 << 8) -#define WPI_STOP_MASTER (1 << 9) - -/* possible flags for register WPI_GPIO_CTL */ -#define WPI_GPIO_CLOCK (1 << 0) -#define WPI_GPIO_INIT (1 << 2) -#define WPI_GPIO_MAC (1 << 3) -#define WPI_GPIO_SLEEP (1 << 4) -#define WPI_GPIO_PWR_STATUS 0x07000000 -#define WPI_GPIO_PWR_SLEEP (4 << 24) - -/* possible flags for register WPI_CHICKEN */ -#define WPI_CHICKEN_RXNOLOS (1 << 23) - -/* possible flags for register WPI_PLL_CTL */ -#define WPI_PLL_INIT (1 << 24) - -/* possible flags for register WPI_UCODE_CLR */ -#define WPI_RADIO_OFF (1 << 1) -#define WPI_DISABLE_CMD (1 << 2) - -/* possible flags for WPI_RX_STATUS */ -#define WPI_RX_IDLE (1 << 24) - -/* possible flags for register WPI_UC_CTL */ -#define WPI_UC_ENABLE (1 << 30) -#define WPI_UC_RUN (1U << 31) - -/* possible flags for register WPI_INTR_CSR */ -#define WPI_ALIVE_INTR (1 << 0) -#define WPI_WAKEUP_INTR (1 << 1) -#define WPI_SW_ERROR (1 << 25) -#define WPI_TX_INTR (1 << 27) -#define WPI_HW_ERROR (1 << 29) -#define WPI_RX_INTR (1U << 31) - -#define WPI_INTR_MASK \ - (WPI_SW_ERROR | WPI_HW_ERROR | WPI_TX_INTR | WPI_RX_INTR | \ - WPI_ALIVE_INTR | WPI_WAKEUP_INTR) - -/* possible flags for register WPI_TX_STATUS */ -#define WPI_TX_IDLE(qid) (1 << ((qid) + 24) | 1 << ((qid) + 16)) - -/* possible flags for register WPI_EEPROM_CTL */ -#define WPI_EEPROM_READY (1 << 0) - -/* possible flags for register WPI_EEPROM_STATUS */ +#define WPI_ALM_SCHED_MODE 0x2e00 +#define WPI_ALM_SCHED_ARASTAT 0x2e04 +#define WPI_ALM_SCHED_TXFACT 0x2e10 +#define WPI_ALM_SCHED_TXF4MF 0x2e14 +#define WPI_ALM_SCHED_TXF5MF 0x2e20 +#define WPI_ALM_SCHED_SBYPASS_MODE1 0x2e2c +#define WPI_ALM_SCHED_SBYPASS_MODE2 0x2e30 +#define WPI_APMG_CLK_CTRL 0x3000 +#define WPI_APMG_CLK_EN 0x3004 +#define WPI_APMG_CLK_DIS 0x3008 +#define WPI_APMG_PS 0x300c +#define WPI_APMG_PCI_STT 0x3010 +#define WPI_APMG_RFKILL 0x3014 +#define WPI_BSM_WR_CTRL 0x3400 +#define WPI_BSM_WR_MEM_SRC 0x3404 +#define WPI_BSM_WR_MEM_DST 0x3408 +#define WPI_BSM_WR_DWCOUNT 0x340c +#define WPI_BSM_DRAM_TEXT_ADDR 0x3490 +#define WPI_BSM_DRAM_TEXT_SIZE 0x3494 +#define WPI_BSM_DRAM_DATA_ADDR 0x3498 +#define WPI_BSM_DRAM_DATA_SIZE 0x349c +#define WPI_BSM_SRAM_BASE 0x3800 + + +/* Possible flags for register WPI_HW_IF_CONFIG. */ +#define WPI_HW_IF_CONFIG_ALM_MB (1 << 8) +#define WPI_HW_IF_CONFIG_ALM_MM (1 << 9) +#define WPI_HW_IF_CONFIG_SKU_MRC (1 << 10) +#define WPI_HW_IF_CONFIG_REV_D (1 << 11) +#define WPI_HW_IF_CONFIG_TYPE_B (1 << 12) + +/* Possible flags for registers WPI_PRPH_RADDR/WPI_PRPH_WADDR. */ +#define WPI_PRPH_DWORD ((sizeof (uint32_t) - 1) << 24) + +/* Possible values for WPI_BSM_WR_MEM_DST. */ +#define WPI_FW_TEXT_BASE 0x00000000 +#define WPI_FW_DATA_BASE 0x00800000 + +/* Possible flags for WPI_GPIO_IN. */ +#define WPI_GPIO_IN_VMAIN (1 << 9) + +/* Possible flags for register WPI_RESET. */ +#define WPI_RESET_NEVO (1 << 0) +#define WPI_RESET_SW (1 << 7) +#define WPI_RESET_MASTER_DISABLED (1 << 8) +#define WPI_RESET_STOP_MASTER (1 << 9) + +/* Possible flags for register WPI_GP_CNTRL. */ +#define WPI_GP_CNTRL_MAC_ACCESS_ENA (1 << 0) +#define WPI_GP_CNTRL_MAC_CLOCK_READY (1 << 0) +#define WPI_GP_CNTRL_INIT_DONE (1 << 2) +#define WPI_GP_CNTRL_MAC_ACCESS_REQ (1 << 3) +#define WPI_GP_CNTRL_SLEEP (1 << 4) +#define WPI_GP_CNTRL_PS_MASK (7 << 24) +#define WPI_GP_CNTRL_MAC_PS (4 << 24) +#define WPI_GP_CNTRL_RFKILL (1 << 27) + +/* Possible flags for register WPI_GIO_CHICKEN. */ +#define WPI_GIO_CHICKEN_L1A_NO_L0S_RX (1 << 23) +#define WPI_GIO_CHICKEN_DIS_L0S_TIMER (1 << 29) + +/* Possible flags for register WPI_GIO. */ +#define WPI_GIO_L0S_ENA (1 << 1) + +/* Possible flags for register WPI_FH_RX_CONFIG. */ +#define WPI_FH_RX_CONFIG_DMA_ENA (1U << 31) +#define WPI_FH_RX_CONFIG_RDRBD_ENA (1 << 29) +#define WPI_FH_RX_CONFIG_WRSTATUS_ENA (1 << 27) +#define WPI_FH_RX_CONFIG_MAXFRAG (1 << 24) +#define WPI_FH_RX_CONFIG_NRBD(x) ((x) << 20) +#define WPI_FH_RX_CONFIG_IRQ_DST_HOST (1 << 12) +#define WPI_FH_RX_CONFIG_IRQ_TIMEOUT(x) ((x) << 4) + +/* Possible flags for register WPI_ANA_PLL. */ +#define WPI_ANA_PLL_INIT (1 << 24) + +/* Possible flags for register WPI_UCODE_GP1*. */ +#define WPI_UCODE_GP1_MAC_SLEEP (1 << 0) +#define WPI_UCODE_GP1_RFKILL (1 << 1) +#define WPI_UCODE_GP1_CMD_BLOCKED (1 << 2) + +/* Possible flags for register WPI_FH_RX_STATUS. */ +#define WPI_FH_RX_STATUS_IDLE (1 << 24) + +/* Possible flags for register WPI_BSM_WR_CTRL. */ +#define WPI_BSM_WR_CTRL_START_EN (1 << 30) +#define WPI_BSM_WR_CTRL_START (1U << 31) + +/* Possible flags for register WPI_INT. */ +#define WPI_INT_ALIVE (1 << 0) +#define WPI_INT_WAKEUP (1 << 1) +#define WPI_INT_SW_RX (1 << 3) +#define WPI_INT_SW_ERR (1 << 25) +#define WPI_INT_FH_TX (1 << 27) +#define WPI_INT_HW_ERR (1 << 29) +#define WPI_INT_FH_RX (1U << 31) + +/* Shortcut. */ +#define WPI_INT_MASK_DEF \ + (WPI_INT_SW_ERR | WPI_INT_HW_ERR | WPI_INT_FH_TX | \ + WPI_INT_FH_RX | WPI_INT_ALIVE | WPI_INT_WAKEUP | \ + WPI_INT_SW_RX) + +/* Possible flags for register WPI_FH_INT. */ +#define WPI_FH_INT_RX_CHNL(x) (1 << ((x) + 16)) +#define WPI_FH_INT_HI_PRIOR (1 << 30) +/* Shortcuts for the above. */ +#define WPI_FH_INT_RX \ + (WPI_FH_INT_RX_CHNL(0) | \ + WPI_FH_INT_RX_CHNL(1) | \ + WPI_FH_INT_RX_CHNL(2) | \ + WPI_FH_INT_HI_PRIOR) + +/* Possible flags for register WPI_FH_TX_STATUS. */ +#define WPI_FH_TX_STATUS_IDLE(qid) \ + (1 << ((qid) + 24) | 1 << ((qid) + 16)) + +/* Possible flags for register WPI_EEPROM. */ +#define WPI_EEPROM_READ_VALID (1 << 0) + +/* Possible flags for register WPI_EEPROM_GP. */ #define WPI_EEPROM_VERSION 0x00000007 -#define WPI_EEPROM_LOCKED 0x00000180 +#define WPI_EEPROM_GP_IF_OWNER 0x00000180 + +/* Possible flags for register WPI_APMG_PS. */ +#define WPI_APMG_PS_PWR_SRC_MASK (3 << 24) +/* Possible flags for registers WPI_APMG_CLK_*. */ +#define WPI_APMG_CLK_CTRL_DMA_CLK_RQT (1 << 9) +#define WPI_APMG_CLK_CTRL_BSM_CLK_RQT (1 << 11) + +/* Possible flags for register WPI_APMG_PCI_STT. */ +#define WPI_APMG_PCI_STT_L1A_DIS (1 << 11) struct wpi_shared { - uint32_t txbase[8]; + uint32_t txbase[WPI_NTXQUEUES]; uint32_t next; uint32_t reserved[2]; } __packed; #define WPI_MAX_SEG_LEN 65520 struct wpi_tx_desc { - uint32_t flags; + uint8_t reserved1[3]; + uint8_t nsegs; #define WPI_PAD32(x) (roundup2(x, 4) - (x)) struct { uint32_t addr; uint32_t len; - } __attribute__((__packed__)) segs[WPI_MAX_SCATTER]; - uint8_t reserved[28]; + } __packed segs[WPI_MAX_SCATTER]; + uint8_t reserved2[28]; } __packed; struct wpi_tx_stat { - uint8_t nrts; - uint8_t ntries; - uint8_t nkill; + uint8_t rtsfailcnt; + uint8_t ackfailcnt; + uint8_t btkillcnt; uint8_t rate; uint32_t duration; uint32_t status; +#define WPI_TX_STATUS_SUCCESS 0x01 +#define WPI_TX_STATUS_DIRECT_DONE 0x02 +#define WPI_TX_STATUS_FAIL 0x80 +#define WPI_TX_STATUS_FAIL_SHORT_LIMIT 0x82 +#define WPI_TX_STATUS_FAIL_LONG_LIMIT 0x83 +#define WPI_TX_STATUS_FAIL_FIFO_UNDERRUN 0x84 +#define WPI_TX_STATUS_FAIL_MGMNT_ABORT 0x85 +#define WPI_TX_STATUS_FAIL_NEXT_FRAG 0x86 +#define WPI_TX_STATUS_FAIL_LIFE_EXPIRE 0x87 +#define WPI_TX_STATUS_FAIL_NODE_PS 0x88 +#define WPI_TX_STATUS_FAIL_ABORTED 0x89 +#define WPI_TX_STATUS_FAIL_BT_RETRY 0x8a +#define WPI_TX_STATUS_FAIL_NODE_INVALID 0x8b +#define WPI_TX_STATUS_FAIL_FRAG_DROPPED 0x8c +#define WPI_TX_STATUS_FAIL_TID_DISABLE 0x8d +#define WPI_TX_STATUS_FAIL_FRAME_FLUSHED 0x8e +#define WPI_TX_STATUS_FAIL_INSUFFICIENT_CF_POLL 0x8f +#define WPI_TX_STATUS_FAIL_TX_LOCKED 0x90 +#define WPI_TX_STATUS_FAIL_NO_BEACON_ON_RADAR 0x91 + } __packed; struct wpi_rx_desc { @@ -204,21 +287,27 @@ struct wpi_rx_desc { #define WPI_START_SCAN 130 #define WPI_SCAN_RESULTS 131 #define WPI_STOP_SCAN 132 +#define WPI_BEACON_SENT 144 +#define WPI_RX_STATISTICS 156 +#define WPI_BEACON_STATISTICS 157 #define WPI_STATE_CHANGED 161 -#define WPI_MISSED_BEACON 162 +#define WPI_BEACON_MISSED 162 uint8_t flags; uint8_t idx; uint8_t qid; } __packed; +#define WPI_RX_DESC_QID_MSK 0x07 +#define WPI_UNSOLICITED_RX_NOTIF 0x80 + struct wpi_rx_stat { uint8_t len; #define WPI_STAT_MAXLEN 20 uint8_t id; uint8_t rssi; /* received signal strength */ -#define WPI_RSSI_OFFSET 95 +#define WPI_RSSI_OFFSET -95 uint8_t agc; /* access gain control */ uint16_t signal; @@ -228,8 +317,10 @@ struct wpi_rx_stat { struct wpi_rx_head { uint16_t chan; uint16_t flags; +#define WPI_STAT_FLAG_SHPREAMBLE (1 << 2) + uint8_t reserved; - uint8_t rate; + uint8_t plcp; uint16_t len; } __packed; @@ -239,39 +330,47 @@ struct wpi_rx_tail { #define WPI_RX_NO_OVFL_ERR (1 << 1) /* shortcut for the above */ #define WPI_RX_NOERROR (WPI_RX_NO_CRC_ERR | WPI_RX_NO_OVFL_ERR) +#define WPI_RX_CIPHER_MASK (7 << 8) +#define WPI_RX_CIPHER_CCMP (2 << 8) +#define WPI_RX_DECRYPT_MASK (3 << 11) +#define WPI_RX_DECRYPT_OK (3 << 11) + uint64_t tstamp; uint32_t tbeacon; } __packed; struct wpi_tx_cmd { uint8_t code; -#define WPI_CMD_CONFIGURE 16 -#define WPI_CMD_ASSOCIATE 17 -#define WPI_CMD_SET_WME 19 -#define WPI_CMD_TSF 20 +#define WPI_CMD_RXON 16 +#define WPI_CMD_RXON_ASSOC 17 +#define WPI_CMD_EDCA_PARAMS 19 +#define WPI_CMD_TIMING 20 #define WPI_CMD_ADD_NODE 24 +#define WPI_CMD_DEL_NODE 25 #define WPI_CMD_TX_DATA 28 #define WPI_CMD_MRR_SETUP 71 #define WPI_CMD_SET_LED 72 #define WPI_CMD_SET_POWER_MODE 119 #define WPI_CMD_SCAN 128 +#define WPI_CMD_SCAN_ABORT 129 #define WPI_CMD_SET_BEACON 145 #define WPI_CMD_TXPOWER 151 -#define WPI_CMD_BLUETOOTH 155 +#define WPI_CMD_BT_COEX 155 +#define WPI_CMD_GET_STATISTICS 156 uint8_t flags; uint8_t idx; uint8_t qid; - uint8_t data[360]; + uint8_t data[124]; } __packed; -/* structure for WPI_CMD_CONFIGURE */ -struct wpi_config { +/* Structure for command WPI_CMD_RXON. */ +struct wpi_rxon { uint8_t myaddr[IEEE80211_ADDR_LEN]; uint16_t reserved1; uint8_t bssid[IEEE80211_ADDR_LEN]; uint16_t reserved2; - uint8_t wlap_bssid_addr[6]; + uint8_t wlap[IEEE80211_ADDR_LEN]; uint16_t reserved3; uint8_t mode; #define WPI_MODE_HOSTAP 1 @@ -279,21 +378,22 @@ struct wpi_config { #define WPI_MODE_IBSS 4 #define WPI_MODE_MONITOR 6 - uint8_t air_propogation; + uint8_t air; uint16_t reserved4; uint8_t ofdm_mask; uint8_t cck_mask; uint16_t associd; uint32_t flags; -#define WPI_CONFIG_24GHZ (1 << 0) -#define WPI_CONFIG_CCK (1 << 1) -#define WPI_CONFIG_AUTO (1 << 2) -#define WPI_CONFIG_SHSLOT (1 << 4) -#define WPI_CONFIG_SHPREAMBLE (1 << 5) -#define WPI_CONFIG_NODIVERSITY (1 << 7) -#define WPI_CONFIG_ANTENNA_A (1 << 8) -#define WPI_CONFIG_ANTENNA_B (1 << 9) -#define WPI_CONFIG_TSF (1 << 15) +#define WPI_RXON_24GHZ (1 << 0) +#define WPI_RXON_CCK (1 << 1) +#define WPI_RXON_AUTO (1 << 2) +#define WPI_RXON_SHSLOT (1 << 4) +#define WPI_RXON_SHPREAMBLE (1 << 5) +#define WPI_RXON_NODIVERSITY (1 << 7) +#define WPI_RXON_ANTENNA_A (1 << 8) +#define WPI_RXON_ANTENNA_B (1 << 9) +#define WPI_RXON_TSF (1 << 15) +#define WPI_RXON_CTS_TO_SELF (1 << 30) uint32_t filter; #define WPI_FILTER_PROMISC (1 << 0) @@ -302,12 +402,13 @@ struct wpi_config { #define WPI_FILTER_NODECRYPT (1 << 3) #define WPI_FILTER_BSS (1 << 5) #define WPI_FILTER_BEACON (1 << 6) +#define WPI_FILTER_ASSOC (1 << 7) /* Accept associaton requests. */ uint8_t chan; - uint16_t reserved6; + uint16_t reserved5; } __packed; -/* structure for command WPI_CMD_ASSOCIATE */ +/* Structure for command WPI_CMD_RXON_ASSOC. */ struct wpi_assoc { uint32_t flags; uint32_t filter; @@ -316,20 +417,22 @@ struct wpi_assoc { uint16_t reserved; } __packed; -/* structure for command WPI_CMD_SET_WME */ -struct wpi_wme_setup { +/* Structure for command WPI_CMD_EDCA_PARAMS. */ +struct wpi_edca_params { uint32_t flags; +#define WPI_EDCA_UPDATE (1 << 0) + struct { uint16_t cwmin; uint16_t cwmax; uint8_t aifsn; uint8_t reserved; - uint16_t txop; + uint16_t txoplimit; } __packed ac[WME_NUM_AC]; } __packed; -/* structure for command WPI_CMD_TSF */ -struct wpi_cmd_tsf { +/* Structure for command WPI_CMD_TIMING. */ +struct wpi_cmd_timing { uint64_t tstamp; uint16_t bintval; uint16_t atim; @@ -338,77 +441,104 @@ struct wpi_cmd_tsf { uint16_t reserved; } __packed; -/* structure for WPI_CMD_ADD_NODE */ +/* Structure for command WPI_CMD_ADD_NODE. */ struct wpi_node_info { uint8_t control; -#define WPI_NODE_UPDATE (1 << 0) +#define WPI_NODE_UPDATE (1 << 0) uint8_t reserved1[3]; - uint8_t bssid[IEEE80211_ADDR_LEN]; + uint8_t macaddr[IEEE80211_ADDR_LEN]; uint16_t reserved2; uint8_t id; #define WPI_ID_BSS 0 +#define WPI_ID_IBSS_MIN 2 +#define WPI_ID_IBSS_MAX 23 #define WPI_ID_BROADCAST 24 +#define WPI_ID_UNDEFINED (uint8_t)-1 uint8_t flags; +#define WPI_FLAG_KEY_SET (1 << 0) + uint16_t reserved3; - uint16_t key_flags; - uint8_t tkip; + uint16_t kflags; +#define WPI_KFLAG_CCMP (1 << 1) +#define WPI_KFLAG_KID(kid) ((kid) << 8) +#define WPI_KFLAG_MULTICAST (1 << 14) + + uint8_t tsc2; uint8_t reserved4; uint16_t ttak[5]; uint16_t reserved5; uint8_t key[IEEE80211_KEYBUF_SIZE]; uint32_t action; -#define WPI_ACTION_SET_RATE 4 +#define WPI_ACTION_SET_RATE (1 << 2) + uint32_t mask; uint16_t tid; - uint8_t rate; + uint8_t plcp; uint8_t antenna; -#define WPI_ANTENNA_A (1<<6) -#define WPI_ANTENNA_B (1<<7) -#define WPI_ANTENNA_BOTH (WPI_ANTENNA_A|WPI_ANTENNA_B) +#define WPI_ANTENNA_A (1 << 6) +#define WPI_ANTENNA_B (1 << 7) +#define WPI_ANTENNA_BOTH (WPI_ANTENNA_A | WPI_ANTENNA_B) + uint8_t add_imm; uint8_t del_imm; uint16_t add_imm_start; } __packed; -/* structure for command WPI_CMD_TX_DATA */ +/* Structure for command WPI_CMD_DEL_NODE. */ +struct wpi_cmd_del_node { + uint8_t count; + uint8_t reserved1[3]; + uint8_t macaddr[IEEE80211_ADDR_LEN]; + uint16_t reserved2; +} __packed; + +/* Structure for command WPI_CMD_TX_DATA. */ struct wpi_cmd_data { uint16_t len; uint16_t lnext; uint32_t flags; #define WPI_TX_NEED_RTS (1 << 1) -#define WPI_TX_NEED_CTS (1 << 2) +#define WPI_TX_NEED_CTS (1 << 2) #define WPI_TX_NEED_ACK (1 << 3) #define WPI_TX_FULL_TXOP (1 << 7) -#define WPI_TX_BT_DISABLE (1 << 12) /* bluetooth coexistence */ +#define WPI_TX_BT_DISABLE (1 << 12) /* bluetooth coexistence */ #define WPI_TX_AUTO_SEQ (1 << 13) +#define WPI_TX_MORE_FRAG (1 << 14) #define WPI_TX_INSERT_TSTAMP (1 << 16) - uint8_t rate; + uint8_t plcp; uint8_t id; uint8_t tid; uint8_t security; +#define WPI_CIPHER_WEP 1 +#define WPI_CIPHER_CCMP 2 +#define WPI_CIPHER_TKIP 3 +#define WPI_CIPHER_WEP104 9 + uint8_t key[IEEE80211_KEYBUF_SIZE]; uint8_t tkip[IEEE80211_WEP_MICLEN]; uint32_t fnext; +#define WPI_NEXT_STA_ID(id) ((id) << 8) + uint32_t lifetime; #define WPI_LIFETIME_INFINITE 0xffffffff + uint8_t ofdm_mask; uint8_t cck_mask; uint8_t rts_ntries; uint8_t data_ntries; uint16_t timeout; uint16_t txop; - struct ieee80211_frame wh; } __packed; -/* structure for command WPI_CMD_SET_BEACON */ +/* Structure for command WPI_CMD_SET_BEACON. */ struct wpi_cmd_beacon { uint16_t len; uint16_t reserved1; uint32_t flags; /* same as wpi_cmd_data */ - uint8_t rate; + uint8_t plcp; uint8_t id; uint8_t reserved2[30]; uint32_t lifetime; @@ -418,41 +548,35 @@ struct wpi_cmd_beacon { uint16_t tim; uint8_t timsz; uint8_t reserved4; - struct ieee80211_frame wh; } __packed; -/* structure for notification WPI_MISSED_BEACON */ -struct wpi_missed_beacon { - uint32_t consecutive; - uint32_t total; - uint32_t expected; - uint32_t received; +/* Structure for notification WPI_BEACON_MISSED. */ +struct wpi_beacon_missed { + uint32_t consecutive; + uint32_t total; + uint32_t expected; + uint32_t received; } __packed; -/* structure for WPI_CMD_MRR_SETUP */ +/* Structure for command WPI_CMD_MRR_SETUP. */ +#define WPI_RIDX_MAX 11 struct wpi_mrr_setup { - uint8_t which; + uint32_t which; #define WPI_MRR_CTL 0 #define WPI_MRR_DATA 1 - uint8_t reserved[3]; - struct { - uint8_t signal; + uint8_t plcp; uint8_t flags; uint8_t ntries; - uint8_t next; -#define WPI_OFDM6 0 -#define WPI_OFDM54 7 -#define WPI_CCK1 8 -#define WPI_CCK2 9 -#define WPI_CCK11 11 +#define WPI_NTRIES_DEFAULT 2 - } __attribute__((__packed__)) rates[WPI_CCK11 + 1]; + uint8_t next; + } __packed rates[WPI_RIDX_MAX + 1]; } __packed; -/* structure for WPI_CMD_SET_LED */ +/* Structure for command WPI_CMD_SET_LED. */ struct wpi_cmd_led { uint32_t unit; /* multiplier (in usecs) */ uint8_t which; @@ -464,136 +588,133 @@ struct wpi_cmd_led { uint8_t reserved; } __packed; -/* structure for WPI_CMD_SET_POWER_MODE */ -struct wpi_power { - uint32_t flags; -#define WPI_POWER_CAM 0 /* constantly awake mode */ - uint32_t rx_timeout; - uint32_t tx_timeout; - uint32_t sleep[5]; +/* Structure for command WPI_CMD_SET_POWER_MODE. */ +struct wpi_pmgt_cmd { + uint16_t flags; +#define WPI_PS_ALLOW_SLEEP (1 << 0) +#define WPI_PS_NOTIFY (1 << 1) +#define WPI_PS_SLEEP_OVER_DTIM (1 << 2) +#define WPI_PS_PCI_PMGT (1 << 3) + + uint8_t reserved[2]; + uint32_t rxtimeout; + uint32_t txtimeout; + uint32_t intval[5]; +} __packed; + +/* Structures for command WPI_CMD_SCAN. */ +#define WPI_SCAN_MAX_ESSIDS 4 +struct wpi_scan_essid { + uint8_t id; + uint8_t len; + uint8_t data[IEEE80211_NWID_LEN]; } __packed; -/* structure for command WPI_CMD_SCAN */ struct wpi_scan_hdr { uint16_t len; uint8_t reserved1; uint8_t nchan; - uint16_t quiet; - uint16_t threshold; - uint16_t promotion; + uint16_t quiet_time; /* timeout in milliseconds */ +#define WPI_QUIET_TIME_DEFAULT 10 + + uint16_t quiet_threshold; /* min # of packets */ + uint16_t crc_threshold; uint16_t reserved2; - uint32_t maxtimeout; - uint32_t suspend; - uint32_t flags; - uint32_t filter; + uint32_t max_svc; /* background scans */ + uint32_t pause_svc; /* background scans */ +#define WPI_PAUSE_MAX_TIME ((1 << 20) - 1) +#define WPI_PAUSE_SCAN(nbeacons, time) ((nbeacons << 24) | time) -struct { - uint16_t len; - uint16_t lnext; uint32_t flags; - uint8_t rate; - uint8_t id; - uint8_t tid; - uint8_t security; - uint8_t key[IEEE80211_KEYBUF_SIZE]; - uint8_t tkip[IEEE80211_WEP_MICLEN]; - uint32_t fnext; - uint32_t lifetime; - uint8_t ofdm_mask; - uint8_t cck_mask; - uint8_t rts_ntries; - uint8_t data_ntries; - uint16_t timeout; - uint16_t txop; -} tx __attribute__((__packed__)); + uint32_t filter; -#define WPI_SCAN_MAX_ESSIDS 4 - struct { - uint8_t id; - uint8_t esslen; - uint8_t essid[IEEE80211_NWID_LEN]; - }scan_essids[WPI_SCAN_MAX_ESSIDS]; - /* followed by probe request body */ - /* followed by nchan x wpi_scan_chan */ + /* Followed by a struct wpi_cmd_data. */ + /* Followed by an array of 4 structs wpi_scan_essid. */ + /* Followed by probe request body. */ + /* Followed by an array of ``nchan'' structs wpi_scan_chan. */ } __packed; struct wpi_scan_chan { uint8_t flags; +#define WPI_CHAN_ACTIVE (1 << 0) +#define WPI_CHAN_NPBREQS(x) (((1 << (x)) - 1) << 1) + uint8_t chan; -#define WPI_CHAN_ACTIVE (1 << 0) -#define WPI_CHAN_DIRECT (1 << 1) - uint8_t gain_radio; - uint8_t gain_dsp; + uint8_t rf_gain; + uint8_t dsp_gain; uint16_t active; /* msecs */ uint16_t passive; /* msecs */ } __packed; -/* structure for WPI_CMD_BLUETOOTH */ -struct wpi_bluetooth { - uint8_t flags; - uint8_t lead; - uint8_t kill; - uint8_t reserved; - uint32_t ack; - uint32_t cts; -} __packed; +#define WPI_SCAN_CRC_TH_DEFAULT htole16(1) +#define WPI_SCAN_CRC_TH_NEVER htole16(0xffff) -/* structure for command WPI_CMD_TXPOWER */ -struct wpi_cmd_txpower { +/* Maximum size of a scan command. */ +#define WPI_SCAN_MAXSZ (MCLBYTES - 4) +#define WPI_ACTIVE_DWELL_TIME_2GHZ (30) /* all times in msec */ +#define WPI_ACTIVE_DWELL_TIME_5GHZ (20) +#define WPI_ACTIVE_DWELL_FACTOR_2GHZ ( 3) +#define WPI_ACTIVE_DWELL_FACTOR_5GHZ ( 2) + +#define WPI_PASSIVE_DWELL_TIME_2GHZ ( 20) +#define WPI_PASSIVE_DWELL_TIME_5GHZ ( 10) +#define WPI_PASSIVE_DWELL_BASE (100) +#define WPI_CHANNEL_TUNE_TIME ( 6) + +/* Structure for command WPI_CMD_TXPOWER. */ +struct wpi_cmd_txpower { uint8_t band; -#define WPI_RATE_5GHZ 0 -#define WPI_RATE_2GHZ 1 +#define WPI_BAND_5GHZ 0 +#define WPI_BAND_2GHZ 1 + uint8_t reserved; - uint16_t channel; + uint16_t chan; -#define WPI_RATE_MAPPING_COUNT 12 struct { - uint8_t rate; - uint8_t gain_radio; - uint8_t gain_dsp; - uint8_t reserved; - } __packed rates [WPI_RATE_MAPPING_COUNT]; + uint8_t plcp; + uint8_t rf_gain; + uint8_t dsp_gain; + uint8_t reserved; + } __packed rates[WPI_RIDX_MAX + 1]; } __packed; +/* Structure for command WPI_CMD_BT_COEX. */ +struct wpi_bluetooth { + uint8_t flags; +#define WPI_BT_COEX_DISABLE 0 +#define WPI_BT_COEX_MODE_2WIRE 1 +#define WPI_BT_COEX_MODE_3WIRE 2 +#define WPI_BT_COEX_MODE_4WIRE 3 + uint8_t lead_time; +#define WPI_BT_LEAD_TIME_DEF 30 -#define WPI_FW_MAIN_TEXT_MAXSZ (80 * 1024 ) -#define WPI_FW_MAIN_DATA_MAXSZ (32 * 1024 ) -#define WPI_FW_INIT_TEXT_MAXSZ (80 * 1024 ) -#define WPI_FW_INIT_DATA_MAXSZ (32 * 1024 ) -#define WPI_FW_BOOT_TEXT_MAXSZ 1024 - -#define WPI_FW_UPDATED (1 << 31 ) - -/* firmware image header */ -struct wpi_firmware_hdr { + uint8_t max_kill; +#define WPI_BT_MAX_KILL_DEF 5 -#define WPI_FW_MINVERSION 2144 - - uint32_t version; - uint32_t rtextsz; - uint32_t rdatasz; - uint32_t itextsz; - uint32_t idatasz; - uint32_t btextsz; + uint8_t reserved; + uint32_t kill_ack; + uint32_t kill_cts; } __packed; -/* structure for WPI_UC_READY notification */ +/* Structure for WPI_UC_READY notification. */ struct wpi_ucode_info { - uint32_t version; + uint8_t minor; + uint8_t major; + uint16_t reserved1; uint8_t revision[8]; uint8_t type; uint8_t subtype; - uint16_t reserved; + uint16_t reserved2; uint32_t logptr; - uint32_t errorptr; - uint32_t timestamp; + uint32_t errptr; + uint32_t tstamp; uint32_t valid; } __packed; -/* structure for WPI_START_SCAN notification */ +/* Structure for WPI_START_SCAN notification. */ struct wpi_start_scan { uint64_t tstamp; uint32_t tbeacon; @@ -603,18 +724,126 @@ struct wpi_start_scan { uint32_t status; } __packed; -/* structure for WPI_STOP_SCAN notification */ +/* Structure for WPI_STOP_SCAN notification. */ struct wpi_stop_scan { uint8_t nchan; uint8_t status; +#define WPI_SCAN_COMPLETED 1 +#define WPI_SCAN_ABORTED 2 + uint8_t reserved; uint8_t chan; uint64_t tsf; } __packed; +/* Structures for WPI_{RX,BEACON}_STATISTICS notification. */ +struct wpi_rx_phy_stats { + uint32_t ina; + uint32_t fina; + uint32_t bad_plcp; + uint32_t bad_crc32; + uint32_t overrun; + uint32_t eoverrun; + uint32_t good_crc32; + uint32_t fa; + uint32_t bad_fina_sync; + uint32_t sfd_timeout; + uint32_t fina_timeout; + uint32_t no_rts_ack; + uint32_t rxe_limit; + uint32_t ack; + uint32_t cts; +} __packed; + +struct wpi_rx_general_stats { + uint32_t bad_cts; + uint32_t bad_ack; + uint32_t not_bss; + uint32_t filtered; + uint32_t bad_chan; +} __packed; + +struct wpi_rx_stats { + struct wpi_rx_phy_stats ofdm; + struct wpi_rx_phy_stats cck; + struct wpi_rx_general_stats general; +} __packed; + +struct wpi_tx_stats { + uint32_t preamble; + uint32_t rx_detected; + uint32_t bt_defer; + uint32_t bt_kill; + uint32_t short_len; + uint32_t cts_timeout; + uint32_t ack_timeout; + uint32_t exp_ack; + uint32_t ack; +} __packed; + +struct wpi_general_stats { + uint32_t temp; + uint32_t burst_check; + uint32_t burst; + uint32_t reserved[4]; + uint32_t sleep; + uint32_t slot_out; + uint32_t slot_idle; + uint32_t ttl_tstamp; + uint32_t tx_ant_a; + uint32_t tx_ant_b; + uint32_t exec; + uint32_t probe; +} __packed; + +struct wpi_stats { + uint32_t flags; + struct wpi_rx_stats rx; + struct wpi_tx_stats tx; + struct wpi_general_stats general; +} __packed; + +/* Possible flags for command WPI_CMD_GET_STATISTICS. */ +#define WPI_STATISTICS_BEACON_DISABLE (1 << 1) + + +/* Firmware error dump entry. */ +struct wpi_fw_dump { + uint32_t desc; + uint32_t time; + uint32_t blink[2]; + uint32_t ilink[2]; + uint32_t data; +} __packed; + +/* Firmware image file header. */ +struct wpi_firmware_hdr { + +#define WPI_FW_MINVERSION 2144 +#define WPI_FW_NAME "wpifw" + + uint16_t driver; + uint8_t minor; + uint8_t major; + uint32_t rtextsz; + uint32_t rdatasz; + uint32_t itextsz; + uint32_t idatasz; + uint32_t btextsz; +} __packed; + +#define WPI_FW_TEXT_MAXSZ ( 80 * 1024 ) +#define WPI_FW_DATA_MAXSZ ( 32 * 1024 ) +#define WPI_FW_BOOT_TEXT_MAXSZ 1024 + +#define WPI_FW_UPDATED (1U << 31 ) + +/* + * Offsets into EEPROM. + */ #define WPI_EEPROM_MAC 0x015 #define WPI_EEPROM_REVISION 0x035 -#define WPI_EEPROM_CAPABILITIES 0x045 +#define WPI_EEPROM_SKU_CAP 0x045 #define WPI_EEPROM_TYPE 0x04a #define WPI_EEPROM_DOMAIN 0x060 #define WPI_EEPROM_BAND1 0x063 @@ -626,49 +855,66 @@ struct wpi_stop_scan { struct wpi_eeprom_chan { uint8_t flags; -#define WPI_EEPROM_CHAN_VALID (1<<0) -#define WPI_EEPROM_CHAN_IBSS (1<<1) -#define WPI_EEPROM_CHAN_ACTIVE (1<<3) -#define WPI_EEPROM_CHAN_RADAR (1<<4) +#define WPI_EEPROM_CHAN_VALID (1 << 0) +#define WPI_EEPROM_CHAN_IBSS (1 << 1) +#define WPI_EEPROM_CHAN_ACTIVE (1 << 3) +#define WPI_EEPROM_CHAN_RADAR (1 << 4) int8_t maxpwr; } __packed; struct wpi_eeprom_sample { - uint8_t index; - int8_t power; - uint16_t volt; -}; + uint8_t index; + int8_t power; + uint16_t volt; +} __packed; #define WPI_POWER_GROUPS_COUNT 5 - struct wpi_eeprom_group { - struct wpi_eeprom_sample samples[5]; - int32_t coef[5]; - int32_t corr[5]; - int8_t maxpwr; - uint8_t chan; - int16_t temp; + struct wpi_eeprom_sample samples[5]; + int32_t coef[5]; + int32_t corr[5]; + int8_t maxpwr; + uint8_t chan; + int16_t temp; } __packed; -#define WPI_CHAN_BANDS_COUNT 5 +#define WPI_CHAN_BANDS_COUNT 5 #define WPI_MAX_CHAN_PER_BAND 14 - static const struct wpi_chan_band { - uint32_t addr; /* offset in EEPROM */ - uint8_t nchan; - uint8_t chan[WPI_MAX_CHAN_PER_BAND]; -} wpi_bands[5] = { - { WPI_EEPROM_BAND1, 14, - { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }}, - { WPI_EEPROM_BAND2, 13, - { 183, 184, 185, 187, 188, 189, 192, 196, 7, 8, 11, 12, 16 }}, - { WPI_EEPROM_BAND3, 12, - { 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64 }}, - { WPI_EEPROM_BAND4, 11, - { 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140 }}, - { WPI_EEPROM_BAND5, 6, - { 145, 149, 153, 157, 161, 165 }} + uint32_t addr; /* offset in EEPROM */ + uint8_t nchan; + uint8_t chan[WPI_MAX_CHAN_PER_BAND]; +} wpi_bands[] = { + /* 20MHz channels, 2GHz band. */ + { WPI_EEPROM_BAND1, 14, + { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 } }, + /* 20MHz channels, 5GHz band. */ + { WPI_EEPROM_BAND2, 13, + { 183, 184, 185, 187, 188, 189, 192, 196, 7, 8, 11, 12, 16 } }, + { WPI_EEPROM_BAND3, 12, + { 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64 } }, + { WPI_EEPROM_BAND4, 11, + { 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140 } }, + { WPI_EEPROM_BAND5, 6, + { 145, 149, 153, 157, 161, 165 } } +}; + +/* HW rate indices. */ +#define WPI_RIDX_OFDM6 0 +#define WPI_RIDX_OFDM36 5 +#define WPI_RIDX_OFDM48 6 +#define WPI_RIDX_OFDM54 7 +#define WPI_RIDX_CCK1 8 +#define WPI_RIDX_CCK2 9 +#define WPI_RIDX_CCK11 11 + +static const uint8_t wpi_ridx_to_plcp[] = { + /* OFDM: IEEE Std 802.11a-1999, pp. 14 Table 80 */ + /* R1-R4 (ral/ural is R4-R1) */ + 0xd, 0xf, 0x5, 0x7, 0x9, 0xb, 0x1, 0x3, + /* CCK: device-dependent */ + 10, 20, 55, 110 }; #define WPI_MAX_PWR_INDEX 77 @@ -678,25 +924,25 @@ static const struct wpi_chan_band { * the reference driver.) */ static const uint8_t wpi_rf_gain_2ghz[WPI_MAX_PWR_INDEX + 1] = { - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xbb, 0xbb, 0xbb, - 0xbb, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xd3, 0xd3, 0xb3, 0xb3, 0xb3, - 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x73, 0xeb, 0xeb, 0xeb, - 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xab, 0xab, 0xab, 0x8b, - 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xc3, 0xc3, 0xc3, 0xc3, 0xa3, - 0xa3, 0xa3, 0xa3, 0x83, 0x83, 0x83, 0x83, 0x63, 0x63, 0x63, 0x63, - 0x43, 0x43, 0x43, 0x43, 0x23, 0x23, 0x23, 0x23, 0x03, 0x03, 0x03, - 0x03 + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xbb, 0xbb, 0xbb, + 0xbb, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xd3, 0xd3, 0xb3, 0xb3, 0xb3, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x73, 0xeb, 0xeb, 0xeb, + 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xab, 0xab, 0xab, 0x8b, + 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xc3, 0xc3, 0xc3, 0xc3, 0xa3, + 0xa3, 0xa3, 0xa3, 0x83, 0x83, 0x83, 0x83, 0x63, 0x63, 0x63, 0x63, + 0x43, 0x43, 0x43, 0x43, 0x23, 0x23, 0x23, 0x23, 0x03, 0x03, 0x03, + 0x03 }; static const uint8_t wpi_rf_gain_5ghz[WPI_MAX_PWR_INDEX + 1] = { - 0xfb, 0xfb, 0xfb, 0xdb, 0xdb, 0xbb, 0xbb, 0x9b, 0x9b, 0x7b, 0x7b, - 0x7b, 0x7b, 0x5b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x1b, 0x1b, - 0x1b, 0x73, 0x73, 0x73, 0x53, 0x53, 0x53, 0x53, 0x53, 0x33, 0x33, - 0x33, 0x33, 0x13, 0x13, 0x13, 0x13, 0x13, 0xab, 0xab, 0xab, 0x8b, - 0x8b, 0x8b, 0x8b, 0x6b, 0x6b, 0x6b, 0x6b, 0x4b, 0x4b, 0x4b, 0x4b, - 0x2b, 0x2b, 0x2b, 0x2b, 0x0b, 0x0b, 0x0b, 0x0b, 0x83, 0x83, 0x63, - 0x63, 0x63, 0x63, 0x43, 0x43, 0x43, 0x43, 0x23, 0x23, 0x23, 0x23, - 0x03 + 0xfb, 0xfb, 0xfb, 0xdb, 0xdb, 0xbb, 0xbb, 0x9b, 0x9b, 0x7b, 0x7b, + 0x7b, 0x7b, 0x5b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x1b, 0x1b, + 0x1b, 0x73, 0x73, 0x73, 0x53, 0x53, 0x53, 0x53, 0x53, 0x33, 0x33, + 0x33, 0x33, 0x13, 0x13, 0x13, 0x13, 0x13, 0xab, 0xab, 0xab, 0x8b, + 0x8b, 0x8b, 0x8b, 0x6b, 0x6b, 0x6b, 0x6b, 0x4b, 0x4b, 0x4b, 0x4b, + 0x2b, 0x2b, 0x2b, 0x2b, 0x0b, 0x0b, 0x0b, 0x0b, 0x83, 0x83, 0x63, + 0x63, 0x63, 0x63, 0x43, 0x43, 0x43, 0x43, 0x23, 0x23, 0x23, 0x23, + 0x03 }; /* @@ -704,34 +950,89 @@ static const uint8_t wpi_rf_gain_5ghz[WPI_MAX_PWR_INDEX + 1] = { * from the reference driver.) */ static const uint8_t wpi_dsp_gain_2ghz[WPI_MAX_PWR_INDEX + 1] = { - 0x7f, 0x7f, 0x7f, 0x7f, 0x7d, 0x6e, 0x69, 0x62, 0x7d, 0x73, 0x6c, - 0x63, 0x77, 0x6f, 0x69, 0x61, 0x5c, 0x6a, 0x64, 0x78, 0x71, 0x6b, - 0x7d, 0x77, 0x70, 0x6a, 0x65, 0x61, 0x5b, 0x6b, 0x79, 0x73, 0x6d, - 0x7f, 0x79, 0x73, 0x6c, 0x66, 0x60, 0x5c, 0x6e, 0x68, 0x62, 0x74, - 0x7d, 0x77, 0x71, 0x6b, 0x65, 0x60, 0x71, 0x6a, 0x66, 0x5f, 0x71, - 0x6a, 0x66, 0x5f, 0x71, 0x6a, 0x66, 0x5f, 0x71, 0x6a, 0x66, 0x5f, - 0x71, 0x6a, 0x66, 0x5f, 0x71, 0x6a, 0x66, 0x5f, 0x71, 0x6a, 0x66, - 0x5f + 0x7f, 0x7f, 0x7f, 0x7f, 0x7d, 0x6e, 0x69, 0x62, 0x7d, 0x73, 0x6c, + 0x63, 0x77, 0x6f, 0x69, 0x61, 0x5c, 0x6a, 0x64, 0x78, 0x71, 0x6b, + 0x7d, 0x77, 0x70, 0x6a, 0x65, 0x61, 0x5b, 0x6b, 0x79, 0x73, 0x6d, + 0x7f, 0x79, 0x73, 0x6c, 0x66, 0x60, 0x5c, 0x6e, 0x68, 0x62, 0x74, + 0x7d, 0x77, 0x71, 0x6b, 0x65, 0x60, 0x71, 0x6a, 0x66, 0x5f, 0x71, + 0x6a, 0x66, 0x5f, 0x71, 0x6a, 0x66, 0x5f, 0x71, 0x6a, 0x66, 0x5f, + 0x71, 0x6a, 0x66, 0x5f, 0x71, 0x6a, 0x66, 0x5f, 0x71, 0x6a, 0x66, + 0x5f }; static const uint8_t wpi_dsp_gain_5ghz[WPI_MAX_PWR_INDEX + 1] = { - 0x7f, 0x78, 0x72, 0x77, 0x65, 0x71, 0x66, 0x72, 0x67, 0x75, 0x6b, - 0x63, 0x5c, 0x6c, 0x7d, 0x76, 0x6d, 0x66, 0x60, 0x5a, 0x68, 0x62, - 0x5c, 0x76, 0x6f, 0x68, 0x7e, 0x79, 0x71, 0x69, 0x63, 0x76, 0x6f, - 0x68, 0x62, 0x74, 0x6d, 0x66, 0x62, 0x5d, 0x71, 0x6b, 0x63, 0x78, - 0x71, 0x6b, 0x63, 0x78, 0x71, 0x6b, 0x63, 0x78, 0x71, 0x6b, 0x63, - 0x78, 0x71, 0x6b, 0x63, 0x78, 0x71, 0x6b, 0x63, 0x6b, 0x63, 0x78, - 0x71, 0x6b, 0x63, 0x78, 0x71, 0x6b, 0x63, 0x78, 0x71, 0x6b, 0x63, - 0x78 + 0x7f, 0x78, 0x72, 0x77, 0x65, 0x71, 0x66, 0x72, 0x67, 0x75, 0x6b, + 0x63, 0x5c, 0x6c, 0x7d, 0x76, 0x6d, 0x66, 0x60, 0x5a, 0x68, 0x62, + 0x5c, 0x76, 0x6f, 0x68, 0x7e, 0x79, 0x71, 0x69, 0x63, 0x76, 0x6f, + 0x68, 0x62, 0x74, 0x6d, 0x66, 0x62, 0x5d, 0x71, 0x6b, 0x63, 0x78, + 0x71, 0x6b, 0x63, 0x78, 0x71, 0x6b, 0x63, 0x78, 0x71, 0x6b, 0x63, + 0x78, 0x71, 0x6b, 0x63, 0x78, 0x71, 0x6b, 0x63, 0x6b, 0x63, 0x78, + 0x71, 0x6b, 0x63, 0x78, 0x71, 0x6b, 0x63, 0x78, 0x71, 0x6b, 0x63, + 0x78 }; +/* + * Power saving settings (values obtained from the reference driver.) + */ +#define WPI_NDTIMRANGES 2 +#define WPI_NPOWERLEVELS 6 +static const struct wpi_pmgt { + uint32_t rxtimeout; + uint32_t txtimeout; + uint32_t intval[5]; + uint8_t skip_dtim; +} wpi_pmgt[WPI_NDTIMRANGES][WPI_NPOWERLEVELS] = { + /* DTIM <= 10 */ + { + { 0, 0, { 0, 0, 0, 0, 0 }, 0 }, /* CAM */ + { 200, 500, { 1, 2, 3, 4, 4 }, 0 }, /* PS level 1 */ + { 200, 300, { 2, 4, 6, 7, 7 }, 0 }, /* PS level 2 */ + { 50, 100, { 2, 6, 9, 9, 10 }, 0 }, /* PS level 3 */ + { 50, 25, { 2, 7, 9, 9, 10 }, 1 }, /* PS level 4 */ + { 25, 25, { 4, 7, 10, 10, 10 }, 1 } /* PS level 5 */ + }, + /* DTIM >= 11 */ + { + { 0, 0, { 0, 0, 0, 0, 0 }, 0 }, /* CAM */ + { 200, 500, { 1, 2, 3, 4, -1 }, 0 }, /* PS level 1 */ + { 200, 300, { 2, 4, 6, 7, -1 }, 0 }, /* PS level 2 */ + { 50, 100, { 2, 6, 9, 9, -1 }, 0 }, /* PS level 3 */ + { 50, 25, { 2, 7, 9, 9, -1 }, 0 }, /* PS level 4 */ + { 25, 25, { 4, 7, 10, 10, -1 }, 0 } /* PS level 5 */ + } +}; + +/* Firmware errors. */ +static const char * const wpi_fw_errmsg[] = { + "OK", + "FAIL", + "BAD_PARAM", + "BAD_CHECKSUM", + "NMI_INTERRUPT", + "SYSASSERT", + "FATAL_ERROR" +}; #define WPI_READ(sc, reg) \ - bus_space_read_4((sc)->sc_st, (sc)->sc_sh, (reg)) + bus_space_read_4((sc)->sc_st, (sc)->sc_sh, (reg)) #define WPI_WRITE(sc, reg, val) \ - bus_space_write_4((sc)->sc_st, (sc)->sc_sh, (reg), (val)) + bus_space_write_4((sc)->sc_st, (sc)->sc_sh, (reg), (val)) #define WPI_WRITE_REGION_4(sc, offset, datap, count) \ - bus_space_write_region_4((sc)->sc_st, (sc)->sc_sh, (offset), \ - (datap), (count)) + bus_space_write_region_4((sc)->sc_st, (sc)->sc_sh, (offset), \ + (datap), (count)) + +#define WPI_SETBITS(sc, reg, mask) \ + WPI_WRITE(sc, reg, WPI_READ(sc, reg) | (mask)) + +#define WPI_CLRBITS(sc, reg, mask) \ + WPI_WRITE(sc, reg, WPI_READ(sc, reg) & ~(mask)) + +#define WPI_BARRIER_WRITE(sc) \ + bus_space_barrier((sc)->sc_st, (sc)->sc_sh, 0, (sc)->sc_sz, \ + BUS_SPACE_BARRIER_WRITE) + +#define WPI_BARRIER_READ_WRITE(sc) \ + bus_space_barrier((sc)->sc_st, (sc)->sc_sh, 0, (sc)->sc_sz, \ + BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE) diff --git a/sys/dev/netif/wpi/if_wpivar.h b/sys/dev/netif/wpi/if_wpivar.h index ac22ec212f..8de1c03f80 100644 --- a/sys/dev/netif/wpi/if_wpivar.h +++ b/sys/dev/netif/wpi/if_wpivar.h @@ -16,8 +16,6 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include - struct wpi_rx_radiotap_header { struct ieee80211_radiotap_header wr_ihdr; uint64_t wr_tsft; @@ -28,7 +26,7 @@ struct wpi_rx_radiotap_header { int8_t wr_dbm_antsignal; int8_t wr_dbm_antnoise; uint8_t wr_antenna; -}; +} __packed; #define WPI_RX_RADIOTAP_PRESENT \ ((1 << IEEE80211_RADIOTAP_TSFT) | \ @@ -45,8 +43,7 @@ struct wpi_tx_radiotap_header { uint8_t wt_rate; uint16_t wt_chan_freq; uint16_t wt_chan_flags; - uint8_t wt_hwqueue; -}; +} __packed; #define WPI_TX_RADIOTAP_PRESENT \ ((1 << IEEE80211_RADIOTAP_FLAGS) | \ @@ -55,16 +52,15 @@ struct wpi_tx_radiotap_header { struct wpi_dma_info { bus_dma_tag_t tag; - bus_dmamap_t map; - bus_addr_t paddr; /* aligned p address */ - bus_addr_t paddr_start; /* possibly unaligned p start*/ - caddr_t vaddr; /* aligned v address */ - caddr_t vaddr_start; /* possibly unaligned v start */ + bus_dmamap_t map; + bus_addr_t paddr; + caddr_t vaddr; bus_size_t size; }; struct wpi_tx_data { bus_dmamap_t map; + bus_addr_t cmd_paddr; struct mbuf *m; struct ieee80211_node *ni; }; @@ -74,19 +70,18 @@ struct wpi_tx_ring { struct wpi_dma_info cmd_dma; struct wpi_tx_desc *desc; struct wpi_tx_cmd *cmd; - struct wpi_tx_data *data; + struct wpi_tx_data data[WPI_TX_RING_COUNT]; bus_dma_tag_t data_dmat; - int qid; - int count; - int queued; - int cur; + uint8_t qid; + uint8_t cur; + uint8_t pending; + int16_t queued; + int update:1; }; -#define WPI_RBUF_COUNT ( WPI_RX_RING_COUNT + 16 ) - struct wpi_rx_data { - bus_dmamap_t map; - struct mbuf *m; + struct mbuf *m; + bus_dmamap_t map; }; struct wpi_rx_ring { @@ -94,17 +89,15 @@ struct wpi_rx_ring { uint32_t *desc; struct wpi_rx_data data[WPI_RX_RING_COUNT]; bus_dma_tag_t data_dmat; - int cur; + uint16_t cur; + int update; }; -struct wpi_amrr { - struct ieee80211_node ni; /* must be the first */ - int txcnt; - int retrycnt; - int success; - int success_threshold; - int recovery; +struct wpi_node { + struct ieee80211_node ni; /* must be the first */ + uint8_t id; }; +#define WPI_NODE(ni) ((struct wpi_node *)(ni)) struct wpi_power_sample { uint8_t index; @@ -119,85 +112,249 @@ struct wpi_power_group { int16_t temp; }; +struct wpi_buf { + uint8_t data[56]; /* sizeof(struct wpi_cmd_beacon) */ + struct ieee80211_node *ni; + struct mbuf *m; + size_t size; + uint8_t code; + uint16_t ac; +}; + struct wpi_vap { - struct ieee80211vap vap; + struct ieee80211vap wv_vap; - int (*newstate)(struct ieee80211vap *, + struct wpi_buf wv_bcbuf; +#if defined(__DragonFly__) + struct lock wv_mtx; +#else + struct mtx wv_mtx; +#endif + + uint8_t wv_gtk; +#define WPI_VAP_KEY(kid) (1 << kid) + + int (*wv_newstate)(struct ieee80211vap *, enum ieee80211_state, int); + void (*wv_recv_mgmt)(struct ieee80211_node *, + struct mbuf *, int, + const struct ieee80211_rx_stats *, + int, int); }; #define WPI_VAP(vap) ((struct wpi_vap *)(vap)) +#define WPI_VAP_LOCK_INIT(_wvp) \ + lockinit(&(_wvp)->wv_mtx, "lock for wv_bcbuf/wv_boff structures", 0, 0) +#define WPI_VAP_LOCK(_wvp) lockmgr(&(_wvp)->wv_mtx, LK_EXCLUSIVE) +#define WPI_VAP_UNLOCK(_wvp) lockmgr(&(_wvp)->wv_mtx, LK_RELEASE) +#define WPI_VAP_LOCK_ASSERT(_wvp) KKASSERT(lockstatus(&(_wvp)->wv_mtx) == LK_EXCLUSIVE) +#define WPI_VAP_LOCK_DESTROY(_wvp) lockuninit(&(_wvp)->wv_mtx) + +struct wpi_fw_part { + const uint8_t *text; + uint32_t textsz; + const uint8_t *data; + uint32_t datasz; +}; + +struct wpi_fw_info { + const uint8_t *data; + size_t size; + struct wpi_fw_part init; + struct wpi_fw_part main; + struct wpi_fw_part boot; +}; + struct wpi_softc { device_t sc_dev; - struct ifnet *sc_ifp; - struct lock sc_mtx; + int sc_debug; - /* Flags indicating the current state the driver - * expects the hardware to be in - */ - uint32_t flags; -#define WPI_FLAG_HW_RADIO_OFF (1 << 0) -#define WPI_FLAG_BUSY (1 << 1) -#define WPI_FLAG_AUTH (1 << 2) + int sc_running; - /* shared area */ +#if defined(__DragonFly__) + struct lock sc_mtx; +#else + struct mtx sc_mtx; +#endif + struct ieee80211com sc_ic; + +#if defined(__DragonFly__) + struct lock tx_mtx; +#else + struct mtx tx_mtx; +#endif + + /* Shared area. */ struct wpi_dma_info shared_dma; struct wpi_shared *shared; - struct wpi_tx_ring txq[WME_NUM_AC]; - struct wpi_tx_ring cmdq; + struct wpi_tx_ring txq[WPI_DRV_NTXQUEUES]; +#if defined(__DragonFly__) + struct lock txq_mtx; + struct lock txq_state_mtx; +#else + struct mtx txq_mtx; + struct mtx txq_state_mtx; +#endif + struct wpi_rx_ring rxq; + uint64_t rx_tstamp; - /* TX Thermal Callibration */ + /* TX Thermal Callibration. */ struct callout calib_to; - int calib_cnt; - /* Watch dog timer */ - struct callout watchdog_to; - /* Hardware switch polling timer */ - struct callout hwswitch_to; + struct callout scan_timeout; + struct callout tx_timeout; + + /* Watch dog timer. */ + struct callout watchdog_rfkill; + + /* Firmware image. */ + struct wpi_fw_info fw; + uint32_t errptr; struct resource *irq; struct resource *mem; bus_space_tag_t sc_st; bus_space_handle_t sc_sh; void *sc_ih; + bus_size_t sc_sz; + int sc_cap_off; /* PCIe Capabilities. */ - struct wpi_config config; - int temp; + struct wpi_rxon rxon; +#if defined(__DragonFly__) + struct lock rxon_mtx; +#else + struct mtx rxon_mtx; +#endif + int temp; - int sc_tx_timer; - int sc_scan_timer; + uint32_t nodesmsk; +#if defined(__DragonFly__) + struct lock nt_mtx; +#else + struct mtx nt_mtx; +#endif - struct bpf_if *sc_drvbpf; + void (*sc_node_free)(struct ieee80211_node *); + void (*sc_update_rx_ring)(struct wpi_softc *); + void (*sc_update_tx_ring)(struct wpi_softc *, + struct wpi_tx_ring *); - struct wpi_rx_radiotap_header sc_rxtap; - struct wpi_tx_radiotap_header sc_txtap; + struct wpi_rx_radiotap_header sc_rxtap; + struct wpi_tx_radiotap_header sc_txtap; - /* firmware image */ + /* Firmware image. */ const struct firmware *fw_fp; - /* firmware DMA transfer */ + /* Firmware DMA transfer. */ struct wpi_dma_info fw_dma; - /* Tasks used by the driver */ - struct task sc_restarttask; /* reset firmware task */ - struct task sc_radiotask; /* reset rf task */ + /* Tasks used by the driver. */ + struct task sc_radiooff_task; + struct task sc_radioon_task; - /* Eeprom info */ + /* Eeprom info. */ uint8_t cap; uint16_t rev; uint8_t type; + struct wpi_eeprom_chan + eeprom_channels[WPI_CHAN_BANDS_COUNT][WPI_MAX_CHAN_PER_BAND]; struct wpi_power_group groups[WPI_POWER_GROUPS_COUNT]; int8_t maxpwr[IEEE80211_CHAN_MAX]; - char domain[4]; /*reglatory domain XXX */ + char domain[4]; /* Regulatory domain. */ }; +/* + * Locking order: + * 1. WPI_LOCK; + * 2. WPI_RXON_LOCK; + * 3. WPI_TX_LOCK; + * 4. WPI_NT_LOCK / WPI_VAP_LOCK; + * 5. WPI_TXQ_LOCK; + * 6. WPI_TXQ_STATE_LOCK; + */ + +#if defined(__DragonFly__) + #define WPI_LOCK_INIT(_sc) \ - lockinit(&(_sc)->sc_mtx, device_get_nameunit((_sc)->sc_dev), 0, 0) -#define WPI_LOCK(_sc) lockmgr(&(_sc)->sc_mtx, LK_EXCLUSIVE) -#define WPI_UNLOCK(_sc) lockmgr(&(_sc)->sc_mtx, LK_RELEASE) -#define WPI_LOCK_ASSERT(_sc) KKASSERT(lockstatus(&(_sc)->sc_mtx, curthread) == LK_EXCLUSIVE) -#define WPI_LOCK_DESTROY(_sc) lockuninit(&(_sc)->sc_mtx) + lockinit(&(_sc)->sc_mtx, device_get_nameunit((_sc)->sc_dev), 0, 0); +#define WPI_LOCK(_sc) lockmgr(&(_sc)->sc_mtx, LK_EXCLUSIVE) +#define WPI_UNLOCK(_sc) lockmgr(&(_sc)->sc_mtx, LK_RELEASE) +#define WPI_LOCK_ASSERT(_sc) KKASSERT(lockstatus(&(_sc)->sc_mtx) == LK_EXCLUSIVE) +#define WPI_LOCK_DESTROY(_sc) lockuninit(&(_sc)->sc_mtx) + +#define WPI_RXON_LOCK_INIT(_sc) \ + lockinit(&(_sc)->rxon_mtx, "lock for wpi_rxon structure", 0, 0) +#define WPI_RXON_LOCK(_sc) lockmgr(&(_sc)->rxon_mtx, LK_EXCLUSIVE) +#define WPI_RXON_UNLOCK(_sc) lockmgr(&(_sc)->rxon_mtx, LK_RELEASE) +#define WPI_RXON_LOCK_ASSERT(_sc) KKASSERT(lockstatus(&(_sc)->rxon_mtx) == LK_EXCLUSIVE) +#define WPI_RXON_LOCK_DESTROY(_sc) lockuninit(&(_sc)->rxon_mtx) + +#define WPI_TX_LOCK_INIT(_sc) \ + lockinit(&(_sc)->tx_mtx, "tx path lock", 0, 0) +#define WPI_TX_LOCK(_sc) lockmgr(&(_sc)->tx_mtx, LK_EXCLUSIVE) +#define WPI_TX_UNLOCK(_sc) lockmgr(&(_sc)->tx_mtx, LK_RELEASE) +#define WPI_TX_LOCK_DESTROY(_sc) lockuninit(&(_sc)->tx_mtx) + +#define WPI_NT_LOCK_INIT(_sc) \ + lockinit(&(_sc)->nt_mtx, "node table lock", 0, 0) +#define WPI_NT_LOCK(_sc) lockmgr(&(_sc)->nt_mtx, LK_EXCLUSIVE) +#define WPI_NT_UNLOCK(_sc) lockmgr(&(_sc)->nt_mtx, LK_RELEASE) +#define WPI_NT_LOCK_DESTROY(_sc) lockuninit(&(_sc)->nt_mtx) + +#define WPI_TXQ_LOCK_INIT(_sc) \ + lockinit(&(_sc)->txq_mtx, "txq/cmdq lock", 0, 0) +#define WPI_TXQ_LOCK(_sc) lockmgr(&(_sc)->txq_mtx, LK_EXCLUSIVE) +#define WPI_TXQ_UNLOCK(_sc) lockmgr(&(_sc)->txq_mtx, LK_RELEASE) +#define WPI_TXQ_LOCK_DESTROY(_sc) lockuninit(&(_sc)->txq_mtx) + +#define WPI_TXQ_STATE_LOCK_INIT(_sc) \ + lockinit(&(_sc)->txq_state_mtx, "txq state lock", 0, 0) +#define WPI_TXQ_STATE_LOCK(_sc) lockmgr(&(_sc)->txq_state_mtx, LK_EXCLUSIVE) +#define WPI_TXQ_STATE_UNLOCK(_sc) lockmgr(&(_sc)->txq_state_mtx, LK_RELEASE) +#define WPI_TXQ_STATE_LOCK_DESTROY(_sc) lockuninit(&(_sc)->txq_state_mtx) + +#else +#define WPI_LOCK_INIT(_sc) \ + mtx_init(&(_sc)->sc_mtx, device_get_nameunit((_sc)->sc_dev), \ + MTX_NETWORK_LOCK, MTX_DEF) +#define WPI_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define WPI_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define WPI_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED) +#define WPI_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx) + +#define WPI_RXON_LOCK_INIT(_sc) \ + mtx_init(&(_sc)->rxon_mtx, "lock for wpi_rxon structure", NULL, MTX_DEF) +#define WPI_RXON_LOCK(_sc) mtx_lock(&(_sc)->rxon_mtx) +#define WPI_RXON_UNLOCK(_sc) mtx_unlock(&(_sc)->rxon_mtx) +#define WPI_RXON_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->rxon_mtx, MA_OWNED) +#define WPI_RXON_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->rxon_mtx) + +#define WPI_TX_LOCK_INIT(_sc) \ + mtx_init(&(_sc)->tx_mtx, "tx path lock", NULL, MTX_DEF) +#define WPI_TX_LOCK(_sc) mtx_lock(&(_sc)->tx_mtx) +#define WPI_TX_UNLOCK(_sc) mtx_unlock(&(_sc)->tx_mtx) +#define WPI_TX_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->tx_mtx) + +#define WPI_NT_LOCK_INIT(_sc) \ + mtx_init(&(_sc)->nt_mtx, "node table lock", NULL, MTX_DEF) +#define WPI_NT_LOCK(_sc) mtx_lock(&(_sc)->nt_mtx) +#define WPI_NT_UNLOCK(_sc) mtx_unlock(&(_sc)->nt_mtx) +#define WPI_NT_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->nt_mtx) + +#define WPI_TXQ_LOCK_INIT(_sc) \ + mtx_init(&(_sc)->txq_mtx, "txq/cmdq lock", NULL, MTX_DEF) +#define WPI_TXQ_LOCK(_sc) mtx_lock(&(_sc)->txq_mtx) +#define WPI_TXQ_UNLOCK(_sc) mtx_unlock(&(_sc)->txq_mtx) +#define WPI_TXQ_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->txq_mtx) + +#define WPI_TXQ_STATE_LOCK_INIT(_sc) \ + mtx_init(&(_sc)->txq_state_mtx, "txq state lock", NULL, MTX_DEF) +#define WPI_TXQ_STATE_LOCK(_sc) mtx_lock(&(_sc)->txq_state_mtx) +#define WPI_TXQ_STATE_UNLOCK(_sc) mtx_unlock(&(_sc)->txq_state_mtx) +#define WPI_TXQ_STATE_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->txq_state_mtx) + +#endif -- 2.11.4.GIT