diff options
author | adrian <adrian@FreeBSD.org> | 2015-02-07 23:11:38 +0000 |
---|---|---|
committer | adrian <adrian@FreeBSD.org> | 2015-02-07 23:11:38 +0000 |
commit | 3b47fe6ce11b8bb574e28ab0003a0bda729e9bb9 (patch) | |
tree | 6f7ea6dff298819344ddb5feb590714ca50c902d | |
parent | 37b5ea9947a071c6b5c2e859041c3fb372576b42 (diff) | |
download | FreeBSD-src-3b47fe6ce11b8bb574e28ab0003a0bda729e9bb9.zip FreeBSD-src-3b47fe6ce11b8bb574e28ab0003a0bda729e9bb9.tar.gz |
Big wpi(4) overhaul! Not by me!
This is a sync against iwn(4) and openbsd.
- Add power management support;
- Add background scanning support;
- Fix few LORs;
- Handle rfkill switch state changes properly;
- Fix recovering after firmware failure;
- Add more error checking;
- Cleanup & disable by default debug output;
- Update macroses names;
- Other various fixes;
- Add IBSS support:
- don't set data_ntries field for management frames;
- Add AHDEMO support:
- fix padding;
- Sync eeprom functions;
- Use CMD_RXON_ASSOC where possible;
- Enable HW CCMP encryption/decryption for pairwise keys;
- Fix filter flags for CMD_RXON.
Tested (by submitter) - iwn 3945 NIC. I have one somewhere; I'll
validate this later on and revert it if it's a problem.
Thanks!
PR: 197143
Submitted by: Andriy Voskoboinyk <s3erios@gmail.com>
-rw-r--r-- | sys/dev/wpi/if_wpi.c | 5742 | ||||
-rw-r--r-- | sys/dev/wpi/if_wpi_debug.h | 98 | ||||
-rw-r--r-- | sys/dev/wpi/if_wpireg.h | 965 | ||||
-rw-r--r-- | sys/dev/wpi/if_wpivar.h | 112 |
4 files changed, 4243 insertions, 2674 deletions
diff --git a/sys/dev/wpi/if_wpi.c b/sys/dev/wpi/if_wpi.c index accb46c..52846dd 100644 --- a/sys/dev/wpi/if_wpi.c +++ b/sys/dev/wpi/if_wpi.c @@ -16,8 +16,6 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#define VERSION "20071127" - #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); @@ -60,6 +58,7 @@ __FBSDID("$FreeBSD$"); */ #include "opt_wlan.h" +#include "opt_wpi.h" #include <sys/param.h> #include <sys/sysctl.h> @@ -93,51 +92,20 @@ __FBSDID("$FreeBSD$"); #include <net/if_media.h> #include <net/if_types.h> -#include <net80211/ieee80211_var.h> -#include <net80211/ieee80211_radiotap.h> -#include <net80211/ieee80211_regdomain.h> -#include <net80211/ieee80211_ratectl.h> - #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/in_var.h> -#include <netinet/ip.h> #include <netinet/if_ether.h> +#include <netinet/ip.h> + +#include <net80211/ieee80211_var.h> +#include <net80211/ieee80211_radiotap.h> +#include <net80211/ieee80211_regdomain.h> +#include <net80211/ieee80211_ratectl.h> #include <dev/wpi/if_wpireg.h> #include <dev/wpi/if_wpivar.h> - -#define WPI_DEBUG - -#ifdef WPI_DEBUG -#define DPRINTF(x) do { if (wpi_debug != 0) printf x; } while (0) -#define DPRINTFN(n, x) do { if (wpi_debug & n) printf 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_RWTUN, &wpi_debug, 0, "wpi debug level"); - -#else -#define DPRINTF(x) -#define DPRINTFN(n, x) -#define WPI_DEBUG_SET 0 -#endif +#include <dev/wpi/if_wpi_debug.h> struct wpi_ident { uint16_t vendor; @@ -158,99 +126,138 @@ static const struct wpi_ident wpi_ident_table[] = { { 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 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, int); + void **, bus_size_t, bus_size_t); 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_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_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 *, - int, int); + int); +static void wpi_update_tx_ring(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 *, int); +static int wpi_read_eeprom_channels(struct wpi_softc *, int); +static struct wpi_eeprom_chan *wpi_find_eeprom_channel(struct wpi_softc *, + 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 *, int); +static void wpi_node_free(struct ieee80211_node *); +static struct ieee80211_node *wpi_node_alloc(struct ieee80211vap *, + const uint8_t mac[IEEE80211_ADDR_LEN]); 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 *, +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_intr(struct wpi_softc *, struct wpi_rx_desc *); -static void wpi_cmd_intr(struct wpi_softc *, struct wpi_rx_desc *); +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 *); +static void wpi_fatal_intr(struct wpi_softc *); static void wpi_intr(void *); -static uint8_t wpi_plcp_signal(int); -static void wpi_watchdog(void *); +static int wpi_cmd2(struct wpi_softc *, struct wpi_buf *); static int wpi_tx_data(struct wpi_softc *, struct mbuf *, - struct ieee80211_node *, int); -static void wpi_start(struct ifnet *); -static void wpi_start_locked(struct ifnet *); + 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 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 void wpi_start(struct ifnet *); +static void wpi_start_locked(struct ifnet *); +static void wpi_watchdog_rfkill(void *); +static void wpi_watchdog(void *); static int wpi_ioctl(struct ifnet *, u_long, caddr_t); -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_cmd(struct wpi_softc *, int, const void *, size_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 ifnet *); +static void wpi_update_mcast(struct ifnet *); 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_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 *, struct ieee80211_channel *, 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 int wpi_scan(struct wpi_softc *, struct ieee80211_channel *); static int wpi_auth(struct wpi_softc *, struct ieee80211vap *); +static void wpi_update_beacon(struct ieee80211vap *, int); +static int wpi_setup_beacon(struct wpi_softc *, struct ieee80211_node *); 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 int wpi_key_alloc(struct ieee80211vap *, struct ieee80211_key *, + ieee80211_keyix *, ieee80211_keyix *); +static int wpi_key_set(struct ieee80211vap *, + const struct ieee80211_key *, + const uint8_t mac[IEEE80211_ADDR_LEN]); +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 *, int); +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 void wpi_init_locked(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); +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 void wpi_hw_reset(void *, int); static device_method_t wpi_methods[] = { /* Device interface */ @@ -269,25 +276,15 @@ static driver_t wpi_driver = { 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 */ -}; +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) @@ -304,202 +301,38 @@ wpi_probe(device_t dev) 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 ((error = msleep(sc, &sc->sc_mtx, PCATCH, "wpiinit", hz)) != 0) { - device_printf(sc->sc_dev, - "timeout waiting for adapter to initialize\n"); - goto fail; - } - - /* 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 ((error = msleep(sc, &sc->sc_mtx, PCATCH, "wpiinit", hz)) != 0) { - device_printf(sc->sc_dev, - "timeout waiting for adapter to initialize2\n"); - goto fail; - } - - 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 wpi_softc *sc = (struct wpi_softc *)device_get_softc(dev); struct ieee80211com *ic; - int ac, error, rid, supportsa = 1; - uint32_t tmp; + struct ifnet *ifp; + int i, error, rid, supportsa = 1; 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); +#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. + */ + error = pci_find_cap(dev, PCIY_EXPRESS, &sc->sc_cap_off); + 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 @@ -514,131 +347,118 @@ wpi_attach(device_t dev) } } - /* 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 */ + /* Clear device-specific "PCI retry timeout" register (41h). */ pci_write_config(dev, 0x41, 0, 1); - /* enable bus-mastering */ + /* 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"); + device_printf(dev, "can't map mem space\n"); error = ENOMEM; - goto fail; + return error; } - sc->sc_st = rman_get_bustag(sc->mem); sc->sc_sh = rman_get_bushandle(sc->mem); + i = 1; rid = 0; - sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, - RF_ACTIVE | RF_SHAREABLE); + if (pci_alloc_msi(dev, &i) == 0) + rid = 1; + /* Install interrupt handler. */ + sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE | + (rid != 0 ? 0 : RF_SHAREABLE)); if (sc->irq == NULL) { - device_printf(dev, "could not allocate interrupt resource\n"); + device_printf(dev, "can't map interrupt\n"); error = ENOMEM; goto fail; } - /* - * Allocate DMA memory for firmware transfers. - */ - if ((error = wpi_alloc_fwmem(sc)) != 0) { - printf(": could not allocate firmware memory\n"); - error = ENOMEM; - goto fail; - } + WPI_LOCK_INIT(sc); - /* - * Put adapter into a known state. - */ - if ((error = wpi_reset(sc)) != 0) { - device_printf(dev, "could not reset adapter\n"); + sc->sc_unr = new_unrhdr(WPI_ID_IBSS_MIN, WPI_ID_IBSS_MAX, &sc->sc_mtx); + + /* 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; } - 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 */ + /* 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; + /* Allocate TX rings - 4 for QoS purposes, 1 for commands. */ + for (i = 0; i < WPI_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; } } - /* 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"); + /* 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; } - /* 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; - } + /* Clear pending interrupts. */ + WPI_WRITE(sc, WPI_INT, 0xffffffff); ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); if (ifp == NULL) { - device_printf(dev, "can not if_alloc()\n"); - error = ENOMEM; + device_printf(dev, "can not allocate ifnet structure\n"); goto fail; } - ic = ifp->if_l2com; + ic = ifp->if_l2com; ic->ic_ifp = ifp; ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ - /* set device capabilities */ + /* Set device capabilities. */ ic->ic_caps = IEEE80211_C_STA /* station mode supported */ + | IEEE80211_C_IBSS /* IBSS mode supported */ | IEEE80211_C_MONITOR /* monitor mode supported */ + | IEEE80211_C_AHDEMO /* adhoc demo mode */ + | IEEE80211_C_BGSCAN /* capable of bg scanning */ | 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? */ + | IEEE80211_C_SHPREAMBLE /* short preamble 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 + | 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 */ - wpi_read_eeprom(sc, macaddr); + if ((error = wpi_read_eeprom(sc, macaddr)) != 0) { + device_printf(dev, "could not read EEPROM, error %d\n", + error); + goto fail; + } - if (bootverbose || WPI_DEBUG_SET) { +#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': '?'); @@ -650,6 +470,7 @@ wpi_attach(device_t dev) /* XXX hw_config uses the PCIDEV for the Hardware rev. Must check what sc->rev really represents - benjsc 20070615 */ } +#endif if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_softc = sc; @@ -662,43 +483,141 @@ wpi_attach(device_t dev) IFQ_SET_READY(&ifp->if_snd); ieee80211_ifattach(ic, macaddr); - /* override default methods */ + ic->ic_vap_create = wpi_vap_create; + ic->ic_vap_delete = wpi_vap_delete; ic->ic_raw_xmit = wpi_raw_xmit; - ic->ic_wme.wme_update = wpi_wme_update; + 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_scan_start = wpi_scan_start; ic->ic_scan_end = wpi_scan_end; ic->ic_set_channel = wpi_set_channel; + sc->sc_scan_curchan = ic->ic_scan_curchan; ic->ic_scan_curchan = wpi_scan_curchan; ic->ic_scan_mindwell = wpi_scan_mindwell; + ic->ic_setregdomain = wpi_setregdomain; - ic->ic_vap_create = wpi_vap_create; - ic->ic_vap_delete = wpi_vap_delete; + wpi_radiotap_attach(sc); - 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); + callout_init_mtx(&sc->calib_to, &sc->sc_mtx, 0); + callout_init_mtx(&sc->watchdog_to, &sc->sc_mtx, 0); + callout_init_mtx(&sc->watchdog_rfkill, &sc->sc_mtx, 0); + TASK_INIT(&sc->sc_reinittask, 0, wpi_hw_reset, sc); + 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. */ - error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET |INTR_MPSAFE, + error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET | INTR_MPSAFE, NULL, wpi_intr, sc, &sc->sc_ih); if (error != 0) { - device_printf(dev, "could not set up interrupt\n"); + device_printf(dev, "can't establish interrupt, error %d\n", + error); goto fail; } if (bootverbose) ieee80211_announce(ic); -#ifdef XXX_DEBUG - ieee80211_announce_channels(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); - return ENXIO; + 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 ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); + 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); + 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 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 wpi_buf *bcn; + struct ieee80211vap *vap; + + if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ + return NULL; + + wvp = (struct wpi_vap *) malloc(sizeof(struct wpi_vap), + M_80211_VAP, M_NOWAIT | M_ZERO); + if (wvp == NULL) + return NULL; + vap = &wvp->vap; + ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac); + + bcn = &wvp->wv_bcbuf; + bcn->data = NULL; + + /* Override with driver methods. */ + wvp->newstate = vap->iv_newstate; + vap->iv_key_alloc = wpi_key_alloc; + vap->iv_key_set = wpi_key_set; + vap->iv_key_delete = wpi_key_delete; + vap->iv_newstate = wpi_newstate; + vap->iv_update_beacon = wpi_update_beacon; + + 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); + struct wpi_buf *bcn = &wvp->wv_bcbuf; + + ieee80211_ratectl_deinit(vap); + ieee80211_vap_detach(vap); + + if (bcn->data != NULL) + free(bcn->data, M_DEVBUF); + free(wvp, M_80211_VAP); } static int @@ -707,43 +626,44 @@ wpi_detach(device_t dev) struct wpi_softc *sc = device_get_softc(dev); struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic; - int ac; + int qid; - if (sc->irq != NULL) - bus_teardown_intr(dev, sc->irq, sc->sc_ih); + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); if (ifp != NULL) { ic = ifp->if_l2com; - ieee80211_draintask(ic, &sc->sc_restarttask); - ieee80211_draintask(ic, &sc->sc_radiotask); + ieee80211_draintask(ic, &sc->sc_reinittask); + ieee80211_draintask(ic, &sc->sc_radiooff_task); + wpi_stop(sc); + callout_drain(&sc->watchdog_to); + callout_drain(&sc->watchdog_rfkill); callout_drain(&sc->calib_to); ieee80211_ifdetach(ic); } - WPI_LOCK(sc); + /* 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) { - for (ac = 0; ac < WME_NUM_AC; ac++) - wpi_free_tx_ring(sc, &sc->txq[ac]); + /* Free DMA resources. */ + for (qid = 0; qid < WPI_NTXQUEUES; qid++) + wpi_free_tx_ring(sc, &sc->txq[qid]); - wpi_free_tx_ring(sc, &sc->cmdq); - wpi_free_rx_ring(sc, &sc->rxq); + wpi_free_rx_ring(sc); 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); @@ -751,47 +671,166 @@ wpi_detach(device_t dev) if (ifp != NULL) if_free(ifp); + delete_unrhdr(sc->sc_unr); + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); 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 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]) +static int +wpi_suspend(device_t dev) { - struct wpi_vap *wvp; - struct ieee80211vap *vap; + struct wpi_softc *sc = device_get_softc(dev); + struct ieee80211com *ic = sc->sc_ifp->if_l2com; - if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ - return NULL; - wvp = (struct wpi_vap *) malloc(sizeof(struct wpi_vap), - M_80211_VAP, M_NOWAIT | 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_suspend_all(ic); + return 0; +} - ieee80211_ratectl_init(vap); - /* complete setup */ - ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status); - ic->ic_opmode = opmode; - return vap; +static int +wpi_resume(device_t dev) +{ + struct wpi_softc *sc = device_get_softc(dev); + struct ieee80211com *ic = sc->sc_ifp->if_l2com; + + /* Clear device-specific "PCI retry timeout" register (41h). */ + pci_write_config(dev, 0x41, 0, 1); + + ieee80211_resume_all(ic); + return 0; } -static void -wpi_vap_delete(struct ieee80211vap *vap) +/* + * Grab exclusive access to NIC memory. + */ +static int +wpi_nic_lock(struct wpi_softc *sc) { - struct wpi_vap *wvp = WPI_VAP(vap); + int ntries; - ieee80211_ratectl_deinit(vap); - ieee80211_vap_detach(vap); - free(wvp, M_80211_VAP); + /* 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, int 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 @@ -799,116 +838,63 @@ 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. + * 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 flags) + void **kvap, bus_size_t size, bus_size_t alignment) { 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; + dma->size = size; - if (alignment > 4096) { - align = PAGE_SIZE; - reqsize = size + alignment; - } else { - align = alignment; - reqsize = size; - } - 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); - 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"); + 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, BUS_DMA_NOWAIT, NULL, NULL, &dma->tag); + if (error != 0) 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; - } - } + 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; - if (error != 0) { - device_printf(sc->sc_dev, - "could not load shared page DMA map\n"); + 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); +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); - } + 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; } } @@ -918,19 +904,9 @@ wpi_dma_contig_free(struct wpi_dma_info *dma) 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; + /* 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 @@ -939,114 +915,161 @@ 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_rx_ring(struct wpi_softc *sc, struct wpi_rx_ring *ring) +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; - 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); + 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", + "%s: could not allocate RX ring DMA memory, error %d\n", __func__, error); goto fail; } + /* Create RX buffer DMA tag. */ 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); + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, + MJUMPAGESIZE, 1, MJUMPAGESIZE, BUS_DMA_NOWAIT, NULL, NULL, + &ring->data_dmat); if (error != 0) { device_printf(sc->sc_dev, - "%s: bus_dma_tag_create_failed, error %d\n", + "%s: could not create RX buf DMA tag, error %d\n", __func__, error); goto fail; } /* - * Setup Rx buffers. + * Allocate and map 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", + "%s: could not create RX buf DMA map, error %d\n", __func__, error); goto fail; } - m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE); - if (m == NULL) { + + 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 = ENOMEM; + "%s: could not allocate RX mbuf\n", __func__); + error = ENOBUFS; 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); + mtod(data->m, void *), 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 */ + "%s: can't map mbuf (error %d)\n", __func__, + error); goto fail; } - bus_dmamap_sync(ring->data_dmat, data->map, - BUS_DMASYNC_PREWRITE); - data->m = m; + /* 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, ring); + +fail: wpi_free_rx_ring(sc); + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__); + return error; } static void -wpi_reset_rx_ring(struct wpi_softc *sc, struct wpi_rx_ring *ring) +wpi_update_rx_ring(struct wpi_softc *sc) { - int ntries; + struct wpi_rx_ring *ring = &sc->rxq; - wpi_mem_lock(sc); + if (WPI_READ(sc, WPI_UCODE_GP1) & WPI_UCODE_GP1_MAC_SLEEP) { + DPRINTF(sc, WPI_DEBUG_PWRSAVE, "%s: wakeup request\n", + __func__); - WPI_WRITE(sc, WPI_RX_CONFIG, 0); + WPI_SETBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_MAC_ACCESS_REQ); + ring->update = 1; + } else + WPI_WRITE(sc, WPI_FH_RX_WPTR, ring->cur & ~7); +} - for (ntries = 0; ntries < 100; ntries++) { - if (WPI_READ(sc, WPI_RX_STATUS) & WPI_RX_IDLE) - break; - DELAY(10); - } +static void +wpi_reset_rx_ring(struct wpi_softc *sc) +{ + struct wpi_rx_ring *ring = &sc->rxq; + int ntries; - wpi_mem_unlock(sc); + 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); + } #ifdef WPI_DEBUG - if (ntries == 100 && wpi_debug > 0) - device_printf(sc->sc_dev, "timeout resetting Rx ring\n"); + if (ntries == 1000) { + device_printf(sc->sc_dev, + "timeout resetting Rx ring\n"); + } #endif + 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) +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++) { @@ -1057,52 +1080,62 @@ wpi_free_rx_ring(struct wpi_softc *sc, struct wpi_rx_ring *ring) 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, int count, - int qid) +wpi_alloc_tx_ring(struct wpi_softc *sc, struct wpi_tx_ring *ring, int qid) { - struct wpi_tx_data *data; + bus_addr_t paddr; + bus_size_t size; int i, error; ring->qid = qid; - ring->count = count; ring->queued = 0; ring->cur = 0; - ring->data = NULL; + ring->update = 0; - 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); + 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, "could not allocate tx dma memory\n"); - goto fail; + device_printf(sc->sc_dev, + "%s: could not allocate TX ring DMA memory, error %d\n", + __func__, error); + goto fail; } - /* update shared page with ring's base address */ + /* 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); - 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); + /* + * We only use rings 0 through 4 (4 EDCA + cmd) so there is no need + * to allocate commands space for other rings. + * XXX Do we really need to allocate descriptors for other rings? + */ + if (qid > 4) + return 0; + 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, - "could not allocate tx command DMA memory\n"); - goto fail; - } - - ring->data = malloc(count * sizeof (struct wpi_tx_data), M_DEVBUF, - M_NOWAIT | M_ZERO); - if (ring->data == NULL) { - device_printf(sc->sc_dev, - "could not allocate tx data slots\n"); + "%s: could not allocate TX cmd DMA memory, error %d\n", + __func__, error); goto fail; } @@ -1111,128 +1144,390 @@ wpi_alloc_tx_ring(struct wpi_softc *sc, struct wpi_tx_ring *ring, int count, WPI_MAX_SCATTER - 1, MCLBYTES, BUS_DMA_NOWAIT, NULL, NULL, &ring->data_dmat); if (error != 0) { - device_printf(sc->sc_dev, "could not create data DMA tag\n"); + device_printf(sc->sc_dev, + "%s: could not create TX buf DMA tag, error %d\n", + __func__, error); goto fail; } - for (i = 0; i < count; i++) { - data = &ring->data[i]; + 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, - "could not create tx buf DMA map\n"); + "%s: could not create TX buf DMA map, error %d\n", + __func__, error); goto fail; } - bus_dmamap_sync(ring->data_dmat, data->map, - BUS_DMASYNC_PREWRITE); } + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); + return 0; -fail: - wpi_free_tx_ring(sc, ring); +fail: wpi_free_tx_ring(sc, ring); + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__); return error; } static void -wpi_reset_tx_ring(struct wpi_softc *sc, struct wpi_tx_ring *ring) +wpi_update_tx_ring(struct wpi_softc *sc, struct wpi_tx_ring *ring) { - struct wpi_tx_data *data; - int i, ntries; + if (WPI_READ(sc, WPI_UCODE_GP1) & WPI_UCODE_GP1_MAC_SLEEP) { + DPRINTF(sc, WPI_DEBUG_PWRSAVE, "%s (%d): requesting wakeup\n", + __func__, ring->qid); - wpi_mem_lock(sc); + WPI_SETBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_MAC_ACCESS_REQ); + ring->update = 1; + } else + WPI_WRITE(sc, WPI_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur); +} - 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); +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 < ring->count; i++) { - data = &ring->data[i]; + 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; } } - + /* 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); + sc->qfullmsk &= ~(1 << ring->qid); ring->queued = 0; ring->cur = 0; + ring->update = 0; } static void wpi_free_tx_ring(struct wpi_softc *sc, struct wpi_tx_ring *ring) { - struct wpi_tx_data *data; 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); - if (ring->data != NULL) { - for (i = 0; i < ring->count; i++) { - data = &ring->data[i]; + 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->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); } - free(ring->data, M_DEVBUF); + if (data->map != NULL) + bus_dmamap_destroy(ring->data_dmat, data->map); } - - if (ring->data_dmat != NULL) + 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) + int error, i; + + 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))); + + DPRINTF(sc, WPI_DEBUG_EEPROM, "cap=%x rev=%x type=%x\n", sc->cap, le16toh(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; + } + + return nflags; } +static void +wpi_read_eeprom_band(struct wpi_softc *sc, int n) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct wpi_eeprom_chan *channels = sc->eeprom_channels[n]; + const struct wpi_chan_band *band = &wpi_bands[n]; + struct ieee80211_channel *c; + uint8_t chan; + int i, nflags; + + for (i = 0; i < band->nchan; i++) { + if (!(channels[i].flags & WPI_EEPROM_CHAN_VALID)) { + DPRINTF(sc, WPI_DEBUG_HW, + "Channel Not Valid: %d, band %d\n", + band->chan[i],n); + continue; + } + + chan = band->chan[i]; + nflags = wpi_eeprom_channel_flags(&channels[i]); + + c = &ic->ic_channels[ic->ic_nchans++]; + c->ic_ieee = chan; + c->ic_maxregpower = channels[i].maxpwr; + c->ic_maxpower = 2*c->ic_maxregpower; + + if (n == 0) { /* 2GHz band */ + c->ic_freq = ieee80211_ieee2mhz(chan, IEEE80211_CHAN_G); + /* G =>'s B is supported */ + c->ic_flags = IEEE80211_CHAN_B | nflags; + c = &ic->ic_channels[ic->ic_nchans++]; + c[0] = c[-1]; + c->ic_flags = IEEE80211_CHAN_G | nflags; + } else { /* 5GHz band */ + c->ic_freq = ieee80211_ieee2mhz(chan, IEEE80211_CHAN_A); + c->ic_flags = IEEE80211_CHAN_A | nflags; + } + + /* Save maximum allowed TX power for this channel. */ + sc->maxpwr[chan] = channels[i].maxpwr; + + DPRINTF(sc, WPI_DEBUG_EEPROM, + "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); + } +} + +/** + * Read the eeprom to find out what channels are valid for the given + * band and update net80211 with what we find. + */ static int -wpi_shutdown(device_t dev) +wpi_read_eeprom_channels(struct wpi_softc *sc, int n) { - struct wpi_softc *sc = device_get_softc(dev); + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + const struct wpi_chan_band *band = &wpi_bands[n]; + int error; - WPI_LOCK(sc); - wpi_stop_locked(sc); - wpi_unload_firmware(sc); - WPI_UNLOCK(sc); + 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_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) + return &sc->eeprom_channels[j][i]; + + return NULL; +} + +/* + * Enforce flags read from EEPROM. + */ static int -wpi_suspend(device_t dev) +wpi_setregdomain(struct ieee80211com *ic, struct ieee80211_regdomain *rd, + int nchan, struct ieee80211_channel chans[]) { - struct wpi_softc *sc = device_get_softc(dev); - struct ieee80211com *ic = sc->sc_ifp->if_l2com; + struct ifnet *ifp = ic->ic_ifp; + struct wpi_softc *sc = ifp->if_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) { + if_printf(ic->ic_ifp, + "%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); + } - ieee80211_suspend_all(ic); return 0; } static int -wpi_resume(device_t dev) +wpi_read_eeprom_group(struct wpi_softc *sc, int n) { - struct wpi_softc *sc = device_get_softc(dev); - struct ieee80211com *ic = sc->sc_ifp->if_l2com; + struct wpi_power_group *group = &sc->groups[n]; + struct wpi_eeprom_group rgroup; + int i, error; - pci_write_config(dev, 0x41, 0, 1); + 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__); - ieee80211_resume_all(ic); return 0; } +static struct ieee80211_node * +wpi_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) +{ + struct wpi_node *wn; + + wn = malloc(sizeof (struct wpi_node), M_80211_NODE, + M_NOWAIT | 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 ieee80211com *ic = ni->ni_ic; + struct wpi_softc *sc = ic->ic_ifp->if_softc; + struct wpi_node *wn = (struct wpi_node *)ni; + + if (wn->id >= WPI_ID_IBSS_MIN && wn->id <= WPI_ID_IBSS_MAX) { + free_unr(sc->sc_unr, wn->id); + + WPI_LOCK(sc); + if (sc->rxon.filter & htole32(WPI_FILTER_BSS)) + wpi_del_node(sc, ni); + WPI_UNLOCK(sc); + } + + sc->sc_node_free(ni); +} + /** * Called by net80211 when ever there is a change to 80211 state machine */ @@ -1243,396 +1538,376 @@ wpi_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) struct ieee80211com *ic = vap->iv_ic; struct ifnet *ifp = ic->ic_ifp; struct wpi_softc *sc = ifp->if_softc; - int error; + int error = 0; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); - DPRINTF(("%s: %s -> %s flags 0x%x\n", __func__, + DPRINTF(sc, WPI_DEBUG_STATE, "%s: %s -> %s\n", __func__, ieee80211_state_name[vap->iv_state], - ieee80211_state_name[nstate], sc->flags)); + ieee80211_state_name[nstate]); 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); + switch (nstate) { + case IEEE80211_S_SCAN: + if ((vap->iv_opmode == IEEE80211_M_IBSS || + vap->iv_opmode == IEEE80211_M_AHDEMO) && + (sc->rxon.filter & htole32(WPI_FILTER_BSS))) { + 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__); + } } - } - if (nstate == IEEE80211_S_AUTH || - (nstate == IEEE80211_S_ASSOC && vap->iv_state == IEEE80211_S_RUN)) { + break; + + case IEEE80211_S_ASSOC: + if (vap->iv_state != IEEE80211_S_RUN) + break; + /* FALLTHROUGH */ + case IEEE80211_S_AUTH: /* * 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) { + if ((error = wpi_auth(sc, vap)) != 0) { device_printf(sc->sc_dev, - "%s: could not move to auth state, error %d\n", + "%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) { + break; + + case IEEE80211_S_RUN: + /* + * RUN -> RUN transition; Just restart the timers. + */ + if (vap->iv_state == IEEE80211_S_RUN) { + wpi_calib_timeout(sc); + break; + } + + /* + * !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, error %d\n", - __func__, error); + "%s: could not move to RUN state\n", __func__); } - } - if (nstate == IEEE80211_S_RUN) { - /* RUN -> RUN transition; just restart the timers */ - wpi_calib_timeout(sc); - /* XXX split out rate control timer */ + break; + + default: + break; } 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; + if (error != 0) { + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__); + return error; + } - tmp = WPI_READ(sc, WPI_GPIO_CTL); - WPI_WRITE(sc, WPI_GPIO_CTL, tmp | WPI_GPIO_MAC); + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); - /* 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"); + return wvp->newstate(vap, nstate, arg); } -/* - * Release lock on NIC memory. - */ static void -wpi_mem_unlock(struct wpi_softc *sc) +wpi_calib_timeout(void *arg) { - uint32_t tmp = WPI_READ(sc, WPI_GPIO_CTL); - WPI_WRITE(sc, WPI_GPIO_CTL, tmp & ~WPI_GPIO_MAC); -} + 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); -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); -} + if (vap->iv_state != IEEE80211_S_RUN) + return; -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); -} + wpi_power_calibration(sc); -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); + callout_reset(&sc->calib_to, 60*hz, wpi_calib_timeout, sc); } -/* - * 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) +static __inline uint8_t +rate2plcp(const uint8_t rate) { - 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; + 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; } - - 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) +static __inline uint8_t +plcp2rate(const uint8_t plcp) { - 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; + 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; } - - /* start the microcode executing */ - wpi_mem_write(sc, WPI_MEM_UCODE_CTL, WPI_UC_ENABLE); - - wpi_mem_unlock(sc); - - return (error); } +/* Quickly determine if a given rate is CCK or OFDM. */ +#define WPI_RATE_IS_OFDM(rate) ((rate) >= 12 && (rate) != 22) + static void -wpi_rx_intr(struct wpi_softc *sc, struct wpi_rx_desc *desc, - struct wpi_rx_data *data) +wpi_rx_done(struct wpi_softc *sc, struct wpi_rx_desc *desc, + struct wpi_rx_data *data) { struct ifnet *ifp = sc->sc_ifp; + const struct ieee80211_cipher *cip = NULL; 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_frame *wh; struct ieee80211_node *ni; - struct mbuf *m, *mnew; + struct mbuf *m, *m1; bus_addr_t paddr; + uint32_t flags; + uint16_t len; 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"); + device_printf(sc->sc_dev, "invalid RX statistic header\n"); if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 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))); + 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); if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); return; } - if (le16toh(head->len) < sizeof (struct ieee80211_frame)) { - DPRINTFN(WPI_DEBUG_RX, ("%s: frame too short: %d\n", __func__, - le16toh(head->len))); + /* Discard frames that are too short. */ + if (len < sizeof (*wh)) { + DPRINTF(sc, WPI_DEBUG_RECV, "%s: frame too short: %d\n", + __func__, len); if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 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__)); + m1 = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE); + if (m1 == NULL) { + DPRINTF(sc, WPI_DEBUG_ANY, "%s: no mbuf to restock ring\n", + __func__); if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 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); + error = bus_dmamap_load(ring->data_dmat, data->map, mtod(m1, void *), + 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); + 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); if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); return; } - bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE); - /* finalize mbuf and swap in new one */ 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_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); + m->m_pkthdr.len = m->m_len = len; + + /* Grab a reference to the source node. */ + wh = mtod(m, struct ieee80211_frame *); + ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh); + + if (ni != NULL) + cip = ni->ni_ucastkey.wk_cipher; + if ((wh->i_fc[1] & IEEE80211_FC1_PROTECTED) && + !IEEE80211_IS_MULTICAST(wh->i_addr1) && + cip != NULL && cip->ic_cipher == IEEE80211_CIPHER_AES_CCM) { + if ((flags & WPI_RX_CIPHER_MASK) != WPI_RX_CIPHER_CCMP) { + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); + m_freem(m); + return; + } + /* 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); + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); + m_freem(m); + return; + } + m->m_flags |= M_WEP; + } 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); + 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 = (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; + tap->wr_rate = plcp2rate(head->plcp); } WPI_UNLOCK(sc); - ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *)); + /* Send the frame to the 802.11 layer. */ if (ni != NULL) { - (void) ieee80211_input(ni, m, stat->rssi, 0); + (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, 0); + (void)ieee80211_input_all(ic, m, stat->rssi, -WPI_RSSI_OFFSET); WPI_LOCK(sc); } static void -wpi_tx_intr(struct wpi_softc *sc, struct wpi_rx_desc *desc) +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 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_data *data = &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; + struct mbuf *m; + struct ieee80211_node *ni; + struct ieee80211vap *vap; + int status = le32toh(stat->status); + + KASSERT(data->ni != NULL, ("no node")); - 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))); + 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. - * 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 ((le32toh(stat->status) & 0xff) != 1) + WPI_UNLOCK(sc); + if ((status & 0xff) != 1) { if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); - else + ieee80211_ratectl_tx_complete(vap, ni, + IEEE80211_RATECTL_TX_FAILURE, &stat->ackfailcnt, NULL); + } else { if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); + ieee80211_ratectl_tx_complete(vap, ni, + IEEE80211_RATECTL_TX_SUCCESS, &stat->ackfailcnt, NULL); + } - 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--; + ieee80211_tx_complete(ni, m, (status & 0xff) != 1); + WPI_LOCK(sc); sc->sc_tx_timer = 0; - ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; - wpi_start_locked(ifp); + if (--ring->queued < WPI_TX_RING_LOMARK) { + sc->qfullmsk &= ~(1 << ring->qid); + if (sc->qfullmsk == 0 && + (ifp->if_drv_flags & IFF_DRV_OACTIVE)) { + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + wpi_start_locked(ifp); + } + } + + 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_intr(struct wpi_softc *sc, struct wpi_rx_desc *desc) +wpi_cmd_done(struct wpi_softc *sc, struct wpi_rx_desc *desc) { - struct wpi_tx_ring *ring = &sc->cmdq; + struct wpi_tx_ring *ring = &sc->txq[4]; 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))); + 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 & 7) != 4) - return; /* not a command ack */ + return; /* Not a command ack. */ data = &ring->data[desc->idx]; - /* if the command was mapped in a mbuf, free it */ + /* 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; @@ -1647,374 +1922,710 @@ 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; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + int 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]; + hw = (hw == 0) ? WPI_RX_RING_COUNT - 1 : hw - 1; + + if (sc->rxq.cur == hw) + return; + + do { + 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 = (void *)data->m->m_ext.ext_buf; + desc = mtod(data->m, struct wpi_rx_desc *); - 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))); + 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 & 0x80)) /* reply to a command */ - wpi_cmd_intr(sc, desc); + if (!(desc->qid & 0x80)) /* Reply to a command. */ + wpi_cmd_done(sc, desc); switch (desc->type) { case WPI_RX_DONE: - /* a 802.11 frame was received */ - wpi_rx_intr(sc, desc, data); + /* An 802.11 frame has been received. */ + wpi_rx_done(sc, desc, data); break; case WPI_TX_DONE: - /* a 802.11 frame has been transmitted */ - wpi_tx_intr(sc, desc); + /* 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); + int misses; + + bus_dmamap_sync(sc->rxq.data_dmat, data->map, + BUS_DMASYNC_POSTREAD); + misses = le32toh(miss->consecutive); + + DPRINTF(sc, WPI_DEBUG_STATE, + "%s: beacons missed %d/%d\n", __func__, misses, + le32toh(miss->total)); + + if (vap->iv_state == IEEE80211_S_RUN && + (ic->ic_flags & IEEE80211_S_SCAN) == 0) { + if (misses >= vap->iv_bmissthreshold) { + WPI_UNLOCK(sc); + ieee80211_beacon_miss(ic); + WPI_LOCK(sc); + } + } + break; + } case WPI_UC_READY: { struct wpi_ucode_info *uc = - (struct wpi_ucode_info *)(desc + 1); + (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))); + /* 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); } + /* Save the address of the error log in SRAM. */ + sc->errptr = le32toh(uc->errptr); break; } case WPI_STATE_CHANGED: { - uint32_t *status = (uint32_t *)(desc + 1); - - /* enabled/disabled notification */ - DPRINTF(("state changed to %x\n", le32toh(*status))); + bus_dmamap_sync(sc->rxq.data_dmat, data->map, + BUS_DMASYNC_POSTREAD); + uint32_t *status = (uint32_t *)(desc + 1); +#ifdef WPI_DEBUG + DPRINTF(sc, WPI_DEBUG_STATE, "state changed to %x\n", + le32toh(*status)); +#endif if (le32toh(*status) & 1) { - device_printf(sc->sc_dev, - "Radio transmitter is switched off\n"); - sc->flags |= WPI_FLAG_HW_RADIO_OFF; - ifp->if_drv_flags &= ~IFF_DRV_RUNNING; - /* Disable firmware commands */ - WPI_WRITE(sc, WPI_UCODE_SET, WPI_DISABLE_CMD); + ieee80211_runtask(ic, &sc->sc_radiooff_task); + return; } break; } case WPI_START_SCAN: { + bus_dmamap_sync(sc->rxq.data_dmat, data->map, + BUS_DMASYNC_POSTREAD); #ifdef WPI_DEBUG struct wpi_start_scan *scan = - (struct wpi_start_scan *)(desc + 1); + (struct wpi_start_scan *)(desc + 1); + DPRINTF(sc, WPI_DEBUG_SCAN, + "%s: scanning channel %d status %x\n", + __func__, scan->chan, le32toh(scan->status)); #endif - - DPRINTFN(WPI_DEBUG_SCANNING, - ("scanning channel %d status %x\n", - scan->chan, le32toh(scan->status))); break; } case WPI_STOP_SCAN: { + bus_dmamap_sync(sc->rxq.data_dmat, data->map, + BUS_DMASYNC_POSTREAD); #ifdef WPI_DEBUG struct wpi_stop_scan *scan = - (struct wpi_stop_scan *)(desc + 1); + (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); #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; + WPI_UNLOCK(sc); 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); - } + WPI_LOCK(sc); break; } } + } while (sc->rxq.cur != hw); - sc->rxq.cur = (sc->rxq.cur + 1) % WPI_RX_RING_COUNT; + /* Tell the firmware what we have processed. */ + wpi_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) { + wpi_update_rx_ring(sc); + sc->rxq.update = 0; } + for (qid = 0; qid < WPI_NTXQUEUES; qid++) { + struct wpi_tx_ring *ring = &sc->txq[qid]; - /* 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); + if (ring->update) { + wpi_update_tx_ring(sc, ring); + ring->update = 0; + } + } + + WPI_CLRBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_MAC_ACCESS_REQ); +} + +/* + * 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; + const uint32_t size_errmsg = + (sizeof (wpi_fw_errmsg) / sizeof ((wpi_fw_errmsg)[0])); + + /* 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) { + printf("%s: bad firmware error log address 0x%08x\n", __func__, + sc->errptr); + return; + } + if (wpi_nic_lock(sc) != 0) { + printf("%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) { + printf("%s: invalid count field (count = %u)\n", __func__, + count); + wpi_nic_unlock(sc); + return; + } + /* Skip "count" field. */ + offset = sc->errptr + sizeof (uint32_t); + printf("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)); + + printf(" error type = \"%s\" (0x%08X)\n", + (dump.desc < size_errmsg) ? + wpi_fw_errmsg[dump.desc] : "UNKNOWN", + dump.desc); + printf(" error data = 0x%08X\n", + dump.data); + printf(" branch link = 0x%08X%08X\n", + dump.blink[0], dump.blink[1]); + printf(" interrupt link = 0x%08X%08X\n", + dump.ilink[0], dump.ilink[1]); + printf(" time = %u\n", dump.time); + + offset += sizeof (dump); + } + wpi_nic_unlock(sc); + /* Dump driver status (TX and RX rings) while we're here. */ + printf("driver status:\n"); + for (i = 0; i < WPI_NTXQUEUES; i++) { + struct wpi_tx_ring *ring = &sc->txq[i]; + printf(" tx ring %2d: qid=%-2d cur=%-3d queued=%-3d\n", + i, ring->qid, ring->cur, ring->queued); + } + printf(" rx ring: cur=%d\n", sc->rxq.cur); } static void wpi_intr(void *arg) { struct wpi_softc *sc = arg; - uint32_t r; + struct ifnet *ifp = sc->sc_ifp; + uint32_t r1, r2; WPI_LOCK(sc); - r = WPI_READ(sc, WPI_INTR); - if (r == 0 || r == 0xffffffff) { + /* Disable interrupts. */ + WPI_WRITE(sc, WPI_INT_MASK, 0); + + r1 = WPI_READ(sc, WPI_INT); + + if (r1 == 0xffffffff || (r1 & 0xfffffff0) == 0xa5a5a5a0) { WPI_UNLOCK(sc); - return; + return; /* Hardware gone! */ } - /* disable interrupts */ - WPI_WRITE(sc, WPI_MASK, 0); - /* ack interrupts */ - WPI_WRITE(sc, WPI_INTR, r); + 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 (r & (WPI_SW_ERROR | WPI_HW_ERROR)) { - struct ifnet *ifp = sc->sc_ifp; + if (r1 & (WPI_INT_SW_ERR | WPI_INT_HW_ERR)) { 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); + wpi_fatal_intr(sc); + DPRINTF(sc, WPI_DEBUG_HW, + "(%s)\n", (r1 & WPI_INT_SW_ERR) ? "(Software Error)" : + "(Hardware Error)"); + ieee80211_runtask(ic, &sc->sc_reinittask); sc->flags &= ~WPI_FLAG_BUSY; WPI_UNLOCK(sc); return; } - if (r & WPI_RX_INTR) + if ((r1 & (WPI_INT_FH_RX | WPI_INT_SW_RX)) || + (r2 & WPI_FH_INT_RX)) wpi_notif_intr(sc); - if (r & WPI_ALIVE_INTR) /* firmware initialized */ - wakeup(sc); + if (r1 & WPI_INT_ALIVE) + wakeup(sc); /* Firmware is alive. */ - /* re-enable interrupts */ - if (sc->sc_ifp->if_flags & IFF_UP) - WPI_WRITE(sc, WPI_MASK, WPI_INTR_MASK); + if (r1 & WPI_INT_WAKEUP) + wpi_wakeup_intr(sc); + +done: + /* Re-enable interrupts. */ + if (ifp->if_flags & IFF_UP) + WPI_WRITE(sc, WPI_INT_MASK, WPI_INT_MASK_DEF); WPI_UNLOCK(sc); } -static uint8_t -wpi_plcp_signal(int rate) +static int +wpi_cmd2(struct wpi_softc *sc, struct wpi_buf *buf) { - 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; + 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]; + u_int hdrlen; + int error, i, nsegs, pad, totlen; - /* 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; + WPI_LOCK_ASSERT(sc); - /* unsupported rates (should not get there) */ - default: return 0; + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); + + wh = mtod(buf->m, struct ieee80211_frame *); + hdrlen = ieee80211_anyhdrsize(wh); + totlen = buf->m->m_pkthdr.len; + + if (hdrlen & 3) { + /* First segment length must be a multiple of 4. */ + pad = 4 - (hdrlen & 3); + } else + pad = 0; + + ring = &sc->txq[buf->ac]; + desc = &ring->desc[ring->cur]; + data = &ring->data[ring->cur]; + + /* Prepare TX firmware command. */ + cmd = &ring->cmd[ring->cur]; + cmd->code = buf->code; + cmd->flags = 0; + cmd->qid = ring->qid; + cmd->idx = ring->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); + + error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, buf->m, + segs, &nsegs, BUS_DMA_NOWAIT); + if (error != 0 && error != EFBIG) { + device_printf(sc->sc_dev, + "%s: can't map mbuf (error %d)\n", __func__, error); + m_freem(buf->m); + return error; } -} + if (error != 0) { + /* Too many DMA segments, linearize mbuf. */ + m1 = m_collapse(buf->m, M_NOWAIT, WPI_MAX_SCATTER); + if (m1 == NULL) { + device_printf(sc->sc_dev, + "%s: could not defrag mbuf\n", __func__); + m_freem(buf->m); + return ENOBUFS; + } + buf->m = m1; -/* quickly determine if a given rate is CCK or OFDM */ -#define WPI_RATE_IS_OFDM(rate) ((rate) >= 12 && (rate) != 22) + error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, + buf->m, segs, &nsegs, BUS_DMA_NOWAIT); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: can't map mbuf (error %d)\n", __func__, error); + m_freem(buf->m); + return error; + } + } + + 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, ring->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); + + /* Kick TX ring. */ + ring->cur = (ring->cur + 1) % WPI_TX_RING_COUNT; + wpi_update_tx_ring(sc, ring); + + /* Mark TX ring as full if we reach a certain threshold. */ + if (++ring->queued > WPI_TX_RING_HIMARK) + sc->qfullmsk |= 1 << ring->qid; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); + + return 0; +} /* - * 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. + * Construct the data packet for a transmit buffer. */ static int -wpi_tx_data(struct wpi_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, - int ac) +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 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 ieee80211com *ic = ni->ni_ic; + struct wpi_node *wn = (void *)ni; + struct ieee80211_channel *chan; 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]; + struct ieee80211_key *k = NULL; + struct wpi_cmd_data tx; + struct wpi_buf tx_data; + uint32_t flags; + uint16_t qos; + uint8_t tid, type; + int ac, error, rate, ismcast, totlen; + + wh = mtod(m, struct ieee80211_frame *); + type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); - desc = &ring->desc[ring->cur]; - data = &ring->data[ring->cur]; + /* 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); - wh = mtod(m0, struct ieee80211_frame *); + chan = (ni->ni_chan != IEEE80211_CHAN_ANYC) ? + ni->ni_chan : ic->ic_curchan; + tp = &vap->iv_txparms[ieee80211_chan2mode(chan)]; - hdrlen = ieee80211_hdrsize(wh); - ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); + /* 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) { - k = ieee80211_crypto_encap(ni, m0); + /* Retrieve key for TX. */ + k = ieee80211_crypto_encap(ni, m); if (k == NULL) { - m_freem(m0); - return ENOBUFS; + error = ENOBUFS; + goto fail; } - /* packet header may have moved, reset our local pointer */ - wh = mtod(m0, struct ieee80211_frame *); + /* 802.11 header may have moved. */ + wh = mtod(m, struct ieee80211_frame *); } + totlen = m->m_pkthdr.len; - cmd = &ring->cmd[ring->cur]; - cmd->code = WPI_CMD_TX_DATA; - cmd->flags = 0; - cmd->qid = ring->qid; - cmd->idx = ring->cur; + if (ieee80211_radiotap_active_vap(vap)) { + struct wpi_tx_radiotap_header *tap = &sc->sc_txtap; - 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); + tap->wt_flags = 0; + tap->wt_rate = rate; + if (k != NULL) + tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP; + + ieee80211_radiotap_tx(vap, m); + } + flags = 0; 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; + /* Unicast frame, check if an ACK is expected. */ + if (!qos || (qos & IEEE80211_QOS_ACKPOLICY) != + IEEE80211_QOS_ACKPOLICY_NOACK) + flags |= WPI_TX_NEED_ACK; + } + + /* 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; } - /* 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) { + + 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 h/w to set timestamp in probe responses */ + + /* Tell HW to set timestamp in probe responses. */ if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP) - tx->flags |= htole32(WPI_TX_INSERT_TSTAMP); + flags |= WPI_TX_INSERT_TSTAMP; if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ || subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) - tx->timeout = htole16(3); + 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.timeout = htole16(2); } - 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 (ismcast || type != IEEE80211_FC0_TYPE_DATA) + tx.id = WPI_ID_BROADCAST; + else { + if (wn->id == WPI_ID_UNDEFINED && + (vap->iv_opmode == IEEE80211_M_IBSS || + vap->iv_opmode == IEEE80211_M_AHDEMO)) { + error = wpi_add_ibss_node(sc, ni); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: could not add IBSS node, error %d\n", + __func__, error); + goto fail; + } + } + + if (wn->id == WPI_ID_UNDEFINED) { + device_printf(sc->sc_dev, + "%s: undefined node id\n", __func__); + error = EINVAL; + goto fail; + } + + tx.id = wn->id; + } + + if (type != IEEE80211_FC0_TYPE_MGT) + tx.data_ntries = tp->maxretry; + + 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; + + if (k != NULL && k->wk_cipher->ic_cipher == IEEE80211_CIPHER_AES_CCM) { + if (!(k->wk_flags & IEEE80211_KEY_SWCRYPT)) { + tx.security = WPI_CIPHER_CCMP; + memcpy(tx.key, k->wk_key, k->wk_keylen); + } + } + + tx_data.data = &tx; + tx_data.ni = ni; + tx_data.m = m; + tx_data.size = sizeof(tx); + tx_data.code = WPI_CMD_TX_DATA; + tx_data.ac = ac; + + return wpi_cmd2(sc, &tx_data); + +fail: m_freem(m); + return error; +} + +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_frame *wh; + struct wpi_cmd_data tx; + struct wpi_buf tx_data; + uint32_t flags; + uint8_t type; + int ac, rate, totlen; + + wh = mtod(m, struct ieee80211_frame *); + type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + totlen = m->m_pkthdr.len; + + ac = params->ibp_pri & 3; + + /* Choose a TX rate index. */ + rate = params->ibp_rate0; + + flags = 0; + 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; 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); + ieee80211_radiotap_tx(vap, m); } - /* save and trim IEEE802.11 header */ - m_copydata(m0, 0, hdrlen, (caddr_t)&tx->wh); - m_adj(m0, hdrlen); + 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; - error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m0, segs, - &nsegs, BUS_DMA_NOWAIT); - if (error != 0 && error != EFBIG) { - device_printf(sc->sc_dev, "could not map mbuf (error %d)\n", - error); - m_freem(m0); - return error; + /* 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 (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; - error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, - m0, segs, &nsegs, BUS_DMA_NOWAIT); - if (error != 0) { - device_printf(sc->sc_dev, - "could not map mbuf (error %d)\n", error); - m_freem(m0); - return error; - } - } + 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; - data->m = m0; - data->ni = ni; + tx_data.data = &tx; + tx_data.ni = ni; + tx_data.m = m; + tx_data.size = sizeof(tx); + tx_data.code = WPI_CMD_TX_DATA; + tx_data.ac = ac; - DPRINTFN(WPI_DEBUG_TX, ("sending data: qid=%d idx=%d len=%d nsegs=%d\n", - ring->qid, ring->cur, m0->m_pkthdr.len, nsegs)); + return wpi_cmd2(sc, &tx_data); +} - /* 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); +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 = ifp->if_softc; + int error = 0; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { + ieee80211_free_node(ni); + m_freem(m); + return ENETDOWN; } - 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); + WPI_LOCK(sc); + 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); + } + WPI_UNLOCK(sc); - ring->queued++; + if (error != 0) { + /* NB: m is reclaimed on tx failure */ + ieee80211_free_node(ni); + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); - /* kick ring */ - ring->cur = (ring->cur + 1) % WPI_TX_RING_COUNT; - WPI_WRITE(sc, WPI_TX_WIDX, ring->qid << 8 | ring->cur); + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__); + + return error; + } + + sc->sc_tx_timer = 5; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); return 0; } @@ -2038,72 +2649,86 @@ 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 ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) + DPRINTF(sc, WPI_DEBUG_XMIT, "%s: called\n", __func__); + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || + (ifp->if_drv_flags & IFF_DRV_OACTIVE)) return; for (;;) { - IFQ_DRV_DEQUEUE(&ifp->if_snd, m); - 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 */ - IFQ_DRV_PREPEND(&ifp->if_snd, m); + if (sc->qfullmsk != 0) { ifp->if_drv_flags |= IFF_DRV_OACTIVE; break; } - ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; - if (wpi_tx_data(sc, m, ni, ac) != 0) { + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + if (m == NULL) + break; + ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; + if (wpi_tx_data(sc, m, ni) != 0) { + WPI_UNLOCK(sc); ieee80211_free_node(ni); + WPI_LOCK(sc); if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); - break; - } - sc->sc_tx_timer = 5; + } else + sc->sc_tx_timer = 5; } + + DPRINTF(sc, WPI_DEBUG_XMIT, "%s: done\n", __func__); } -static int -wpi_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, - const struct ieee80211_bpf_params *params) +static void +wpi_watchdog_rfkill(void *arg) { - struct ieee80211com *ic = ni->ni_ic; - struct ifnet *ifp = ic->ic_ifp; - struct wpi_softc *sc = ifp->if_softc; + struct wpi_softc *sc = arg; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; - /* prevent management frames from being sent if we're not ready */ - if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { - m_freem(m); - ieee80211_free_node(ni); - return ENETDOWN; - } - WPI_LOCK(sc); + DPRINTF(sc, WPI_DEBUG_WATCHDOG, "RFkill Watchdog: tick\n"); - /* management frames go into ring 0 */ - if (sc->txq[0].queued > sc->txq[0].count - 8) { - ifp->if_drv_flags |= IFF_DRV_OACTIVE; - m_freem(m); - WPI_UNLOCK(sc); - ieee80211_free_node(ni); - return ENOBUFS; /* XXX */ + /* 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); +} + +/** + * 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; + + DPRINTF(sc, WPI_DEBUG_WATCHDOG, "Watchdog: tick\n"); + + if (sc->sc_tx_timer > 0) { + if (--sc->sc_tx_timer == 0) { + if_printf(ifp, "device timeout\n"); + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + ieee80211_runtask(ic, &sc->sc_reinittask); + } } - if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); - 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); + if (sc->sc_scan_timer > 0) { + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + if (--sc->sc_scan_timer == 0 && vap != NULL) { + if_printf(ifp, "scan timeout\n"); + ieee80211_cancel_scan(vap); + ieee80211_runtask(ic, &sc->sc_reinittask); + } + } - WPI_UNLOCK(sc); - return 0; -bad: - if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); - WPI_UNLOCK(sc); - ieee80211_free_node(ni); - return EIO; /* XXX */ + if (ifp->if_drv_flags & IFF_DRV_RUNNING) + callout_reset(&sc->watchdog_to, hz, wpi_watchdog, sc); } static int @@ -2111,30 +2736,36 @@ wpi_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct wpi_softc *sc = ifp->if_softc; struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); struct ifreq *ifr = (struct ifreq *) data; - int error = 0, startall = 0; + int error = 0, startall = 0, stop = 0; switch (cmd) { + case SIOCGIFADDR: + error = ether_ioctl(ifp, cmd, data); + break; case SIOCSIFFLAGS: WPI_LOCK(sc); - if ((ifp->if_flags & IFF_UP)) { + if (ifp->if_flags & IFF_UP) { if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { - wpi_init_locked(sc, 0); - startall = 1; + wpi_init_locked(sc); + if (WPI_READ(sc, WPI_GP_CNTRL) & + WPI_GP_CNTRL_RFKILL) + startall = 1; + else + stop = 1; } - } else if ((ifp->if_drv_flags & IFF_DRV_RUNNING) || - (sc->flags & WPI_FLAG_HW_RADIO_OFF)) + } else if (ifp->if_drv_flags & IFF_DRV_RUNNING) wpi_stop_locked(sc); WPI_UNLOCK(sc); if (startall) ieee80211_start_all(ic); + else if (vap != NULL && stop) + ieee80211_stop(vap); 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; @@ -2143,64 +2774,58 @@ wpi_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) } /* - * 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) +wpi_cmd(struct wpi_softc *sc, int code, const void *buf, size_t size, + int async) { - struct wpi_tx_ring *ring = &sc->cmdq; + struct wpi_tx_ring *ring = &sc->txq[4]; struct wpi_tx_desc *desc; + struct wpi_tx_data *data; struct wpi_tx_cmd *cmd; + struct mbuf *m; + bus_addr_t paddr; + int totlen, error; -#ifdef WPI_DEBUG - if (!async) { + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); + + if (async == 0) WPI_LOCK_ASSERT(sc); - } -#endif - DPRINTFN(WPI_DEBUG_CMD,("wpi_cmd %d size %d async %d\n", code, size, - async)); + DPRINTF(sc, WPI_DEBUG_CMD, "wpi_cmd %s size %zu async %d\n", + wpi_cmd_str(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)); + sc->flags |= WPI_FLAG_BUSY; desc = &ring->desc[ring->cur]; - cmd = &ring->cmd[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) + return EINVAL; + m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE); + if (m == NULL) + return ENOMEM; + 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); + return error; + } + data->m = m; + } else { + cmd = &ring->cmd[ring->cur]; + paddr = data->cmd_paddr; + } cmd->code = code; cmd->flags = 0; @@ -2208,56 +2833,36 @@ wpi_cmd(struct wpi_softc *sc, int code, const void *buf, int size, int async) 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); + desc->nsegs = 1 + (WPI_PAD32(size) << 4); + desc->segs[0].addr = htole32(paddr); + desc->segs[0].len = htole32(totlen); - /* 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 (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); - return msleep(cmd, &sc->sc_mtx, PCATCH, "wpicmd", hz); -} + /* Kick command ring. */ + ring->cur = (ring->cur + 1) % WPI_TX_RING_COUNT; + wpi_update_tx_ring(sc, ring); -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_ifp->if_softc; - const struct wmeParams *wmep; - struct wpi_wme_setup wme; - int ac; + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); - /* don't override default WME values if WME is not actually enabled */ - if (!(ic->ic_flags & IEEE80211_F_WME)) + if (async) { + sc->flags &= ~WPI_FLAG_BUSY; 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 + + return msleep(cmd, &sc->sc_mtx, PCATCH, "wpicmd", hz); } /* - * Configure h/w multi-rate retries. + * Configure HW multi-rate retries. */ static int wpi_mrr_setup(struct wpi_softc *sc) @@ -2267,360 +2872,769 @@ wpi_mrr_setup(struct wpi_softc *sc) 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++) { + /* 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].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].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 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++) { + /* 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].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) ? + 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_OFDM6 : WPI_CCK2) : + WPI_RIDX_OFDM6 : WPI_RIDX_CCK2) : i - 1; - /* try one time at this rate before falling back to "next" */ + /* 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; + /* 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 = WPI_MRR_DATA; + /* 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_node *wn = (void *)ni; + struct wpi_node_info node; + + 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_bssid); + 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; + + return wpi_cmd(sc, WPI_CMD_ADD_NODE, &node, sizeof node, 1); +} + +/* + * Broadcast node is used to send group-addressed and management frames. + */ +static int +wpi_add_broadcast_node(struct wpi_softc *sc, int async) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct wpi_node_info node; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + + memset(&node, 0, sizeof node); + IEEE80211_ADDR_COPY(node.macaddr, ifp->if_broadcastaddr); + 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; + + return wpi_cmd(sc, WPI_CMD_ADD_NODE, &node, sizeof node, async); +} + +static int +wpi_add_ibss_node(struct wpi_softc *sc, struct ieee80211_node *ni) +{ + struct wpi_node *wn = (void *)ni; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + + if (wn->id != WPI_ID_UNDEFINED) + return EINVAL; + + wn->id = alloc_unrl(sc->sc_unr); + + if (wn->id == (uint8_t)-1) + return ENOBUFS; + + return wpi_add_node(sc, ni); +} + +static void +wpi_del_node(struct wpi_softc *sc, struct ieee80211_node *ni) +{ + struct wpi_node *wn = (void *)ni; + struct wpi_cmd_del_node node; + int error; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + + if (wn->id == WPI_ID_UNDEFINED) { + device_printf(sc->sc_dev, "%s: undefined node id passed\n", + __func__); + return; + } + + memset(&node, 0, sizeof node); + IEEE80211_ADDR_COPY(node.macaddr, ni->ni_bssid); + node.count = 1; + + 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_ifp->if_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); + } + IEEE80211_UNLOCK(ic); + WPI_LOCK(sc); + error = wpi_cmd(sc, WPI_CMD_EDCA_PARAMS, &cmd, sizeof cmd, 1); + WPI_UNLOCK(sc); + IEEE80211_LOCK(ic); + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); + + return error; +#undef WPI_EXP2 +} + +static void +wpi_set_promisc(struct wpi_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; + uint32_t promisc_filter; + + promisc_filter = WPI_FILTER_PROMISC | WPI_FILTER_CTL; + + if (ifp->if_flags & IFF_PROMISC) + sc->rxon.filter |= htole32(promisc_filter); + else + sc->rxon.filter &= ~htole32(promisc_filter); +} + +static void +wpi_update_promisc(struct ifnet *ifp) +{ + struct wpi_softc *sc = ifp->if_softc; + + wpi_set_promisc(sc); + + WPI_LOCK(sc); + if (wpi_send_rxon(sc, 1, 1) != 0) { + device_printf(sc->sc_dev, "%s: could not send RXON\n", + __func__); + } + WPI_UNLOCK(sc); +} + +static void +wpi_update_mcast(struct ifnet *ifp) +{ + /* 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 void -wpi_enable_tsf(struct wpi_softc *sc, struct ieee80211_node *ni) +static int +wpi_set_timing(struct wpi_softc *sc, struct ieee80211_node *ni) { - struct wpi_cmd_tsf tsf; + struct wpi_cmd_timing cmd; 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); + 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 * 1024; /* msec -> usec */ - mod = le64toh(tsf.tstamp) % val; - tsf.binitval = htole32((uint32_t)(val - mod)); + /* 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)); - if (wpi_cmd(sc, WPI_CMD_TSF, &tsf, sizeof tsf, 1) != 0) - device_printf(sc->sc_dev, "could not enable TSF\n"); + 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"); + } } -#if 0 /* - * Build a beacon frame that the firmware will broadcast periodically in - * IBSS or HostAP modes. + * Set TX power for current channel. */ static int -wpi_setup_beacon(struct wpi_softc *sc, struct ieee80211_node *ni) +wpi_set_txpower(struct wpi_softc *sc, int async) { 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; + struct ieee80211_channel *ch; + struct wpi_power_group *group; + struct wpi_cmd_txpower cmd; + uint8_t chan; + int idx, i; - desc = &ring->desc[ring->cur]; - data = &ring->data[ring->cur]; + /* Retrieve current channel from last RXON. */ + chan = sc->rxon.chan; + ch = &ic->ic_channels[chan]; - m0 = ieee80211_beacon_alloc(ic, ni, &bo); - if (m0 == NULL) { - device_printf(sc->sc_dev, "could not allocate beacon frame\n"); - return ENOMEM; + /* Find the TX power group to which this channel belongs. */ + if (IEEE80211_IS_CHAN_5GHZ(ch)) { + 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 = IEEE80211_IS_CHAN_5GHZ(ch) ? 0 : 1; + 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, ch, i); + + cmd.rates[i].plcp = wpi_ridx_to_plcp[i]; + + if (IEEE80211_IS_CHAN_5GHZ(ch)) { + 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); } - cmd = &ring->cmd[ring->cur]; - cmd->code = WPI_CMD_SET_BEACON; - cmd->flags = 0; - cmd->qid = ring->qid; - cmd->idx = ring->cur; + return wpi_cmd(sc, WPI_CMD_TXPOWER, &cmd, sizeof cmd, async); +} - 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; +/* + * 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 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 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 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 -= IEEE80211_IS_CHAN_2GHZ(c) ? 0 : 5; + break; + case WPI_RIDX_OFDM48: + pwr -= IEEE80211_IS_CHAN_2GHZ(c) ? 7 : 10; + break; + case WPI_RIDX_OFDM54: + pwr -= IEEE80211_IS_CHAN_2GHZ(c) ? 9 : 12; + break; } - data->m = m0; + /* Never exceed the channel maximum allowed TX power. */ + pwr = min(pwr, sc->maxpwr[chan]); - /* 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); + /* 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); - /* kick cmd ring */ - ring->cur = (ring->cur + 1) % WPI_CMD_RING_COUNT; - WPI_WRITE(sc, WPI_TX_WIDX, ring->qid << 8 | ring->cur); + /*- + * 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; - return 0; + /* 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 } -#endif +/* + * 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_auth(struct wpi_softc *sc, struct ieee80211vap *vap) +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, skip_dtim; + uint32_t reg; + 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). */ + reg = pci_read_config(sc->sc_dev, sc->sc_cap_off + 0x10, 1); + if (!(reg & 0x1)) /* L0s Entry disabled. */ + cmd.flags |= htole16(WPI_PS_PCI_PMGT); + + 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 = (max / dtim) * 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) { - struct ieee80211com *ic = vap->iv_ic; - struct ieee80211_node *ni = vap->iv_bss; - struct wpi_node_info node; int error; + if (assoc && (sc->rxon.filter & htole32(WPI_FILTER_BSS))) { + struct wpi_assoc rxon_assoc; - /* 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; + 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); } else { - /* XXX assume 802.11b/g */ - sc->config.cck_mask = 0x0f; - sc->config.ofdm_mask = 0x15; + error = wpi_cmd(sc, WPI_CMD_RXON, &sc->rxon, + sizeof (struct wpi_rxon), async); } - - 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"); + device_printf(sc->sc_dev, "RXON command failed, error %d\n", + error); 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"); + /* 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; } - /* 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"); + if (!(sc->rxon.filter & htole32(WPI_FILTER_BSS))) { + /* 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; + } + } - 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_run(struct wpi_softc *sc, struct ieee80211vap *vap) +wpi_config(struct wpi_softc *sc) { - struct ieee80211com *ic = vap->iv_ic; - struct ieee80211_node *ni = vap->iv_bss; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + uint32_t flags; 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; + 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; } - wpi_enable_tsf(sc, ni); + /* Configure bluetooth coexistence. */ + if ((error = wpi_send_btcoex(sc)) != 0) { + device_printf(sc->sc_dev, + "could not configure bluetooth coexistence\n"); + return error; + } - /* 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); + /* Configure adapter. */ + memset(&sc->rxon, 0, sizeof (struct wpi_rxon)); + IEEE80211_ADDR_COPY(sc->rxon.myaddr, IF_LLADDR(ifp)); - /* XXX put somewhere HC_QOS_SUPPORT_ASSOC + HC_IBSS_START */ + /* Set default channel. */ + sc->rxon.chan = ieee80211_chan2ieee(ic, ic->ic_curchan); + sc->rxon.flags = htole32(WPI_RXON_TSF | WPI_RXON_CTS_TO_SELF); + if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) + sc->rxon.flags |= htole32(WPI_RXON_AUTO | WPI_RXON_24GHZ); - 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"); + switch (ic->ic_opmode) { + case IEEE80211_M_STA: + sc->rxon.mode = WPI_MODE_STA; + sc->rxon.filter = htole32(WPI_FILTER_MULTICAST); + break; + case IEEE80211_M_IBSS: + sc->rxon.mode = WPI_MODE_IBSS; + sc->rxon.filter = htole32(WPI_FILTER_BEACON | + WPI_FILTER_MULTICAST); + break; + /* XXX workaround for passive channels selection */ + case IEEE80211_M_AHDEMO: + sc->rxon.filter = htole32(WPI_FILTER_MULTICAST); + /* FALLTHROUGH */ + case IEEE80211_M_HOSTAP: + sc->rxon.mode = WPI_MODE_HOSTAP; + break; + case IEEE80211_M_MONITOR: + sc->rxon.mode = WPI_MODE_MONITOR; + sc->rxon.filter = htole32(WPI_FILTER_MULTICAST); + break; + default: + device_printf(sc->sc_dev, "unknown opmode %d\n", ic->ic_opmode); + return EINVAL; + } + 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; } - error = wpi_set_txpower(sc, ni->ni_chan, 1); + /* Disable beacon notifications (unused). */ + flags = WPI_STATISTICS_BEACON_DISABLE; + error = wpi_cmd(sc, WPI_CMD_GET_STATISTICS, &flags, sizeof flags, 1); if (error != 0) { - device_printf(sc->sc_dev, "could set txpower\n"); + device_printf(sc->sc_dev, + "could not disable beacon statistics, error %d\n", error); return error; } - /* link LED always on while associated */ - wpi_set_led(sc, WPI_LED_LINK, 0, 1); + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); - /* start automatic rate control timer */ - callout_reset(&sc->calib_to, 60*hz, wpi_calib_timeout, sc); + 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)); + } - return (error); + /* 5GHz dwell time. */ + return (WPI_ACTIVE_DWELL_TIME_5GHZ + + WPI_ACTIVE_DWELL_FACTOR_5GHZ * (n_probes + 1)); } /* - * 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. + * Limit the total dwell time to 85% of the beacon interval. + * + * 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_ifp->if_l2com; + struct ieee80211vap *vap = NULL; + int bintval = 0; + + /* bintval is in TU (1.024mS) */ + if (! TAILQ_EMPTY(&ic->ic_vaps)) { + vap = TAILQ_FIRST(&ic->ic_vaps); + 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(WPI_PASSIVE_DWELL_BASE, ((bintval * 85) / 100))); + } + + /* No association context? Default. */ + return (WPI_PASSIVE_DWELL_BASE); +} + +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)); +} + +/* + * Send a scan request to the firmware. */ static int -wpi_scan(struct wpi_softc *sc) +wpi_scan(struct wpi_softc *sc, struct ieee80211_channel *c) { 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_cmd_data *tx; + struct wpi_scan_essid *essids; 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; + uint16_t dwell_active, dwell_passive; + uint8_t *buf, *frm; + int buflen, error, i, nssid; - desc = &ring->desc[ring->cur]; - data = &ring->data[ring->cur]; + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); - data->m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); - if (data->m == NULL) { + /* + * We are absolutely not allowed to send a scan command when another + * scan command is pending. + */ + if (sc->sc_scan_timer) { + device_printf(sc->sc_dev, "%s: called whilst scanning!\n", + __func__); + return (EAGAIN); + } + + buf = malloc(WPI_SCAN_MAXSZ, M_DEVBUF, M_NOWAIT | M_ZERO); + if (buf == NULL) { device_printf(sc->sc_dev, - "could not allocate mbuf for scan command\n"); + "%s: could not allocate buffer for scan command\n", + __func__); 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)); + hdr = (struct wpi_scan_hdr *)buf; /* - * 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). + * Move to the next channel if no packets are received within 10 msecs + * after sending the probe request. */ - hdr->quiet = htole16(5); - hdr->threshold = htole16(1); + hdr->quiet_time = htole16(10); /* timeout in milliseconds */ + hdr->quiet_threshold = htole16(1); /* min # of packets */ + /* + * 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((4 << 24) | + (100 * IEEE80211_DUR_TU)); /* Hardcode for now */ + hdr->filter = htole32(WPI_FILTER_MULTICAST | WPI_FILTER_BEACON); - if (IEEE80211_IS_CHAN_A(ic->ic_curchan)) { - /* send probe requests at 6Mbps */ - hdr->tx.rate = wpi_ridx_to_plcp[WPI_OFDM6]; + 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); - /* Enable crc checking */ - hdr->promotion = htole16(1); + 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_CONFIG_24GHZ | WPI_CONFIG_AUTO); - /* send probe requests at 1Mbps */ - hdr->tx.rate = wpi_ridx_to_plcp[WPI_CCK1]; + 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]; } - 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)); + essids = (struct wpi_scan_essid *)(tx + 1); 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); + 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 (wpi_debug & WPI_DEBUG_SCANNING) { + if (sc->sc_debug & WPI_DEBUG_SCAN) { printf("Scanning Essid: "); - ieee80211_print_essid(hdr->scan_essids[i].essid, - hdr->scan_essids[i].esslen); + ieee80211_print_essid(essids[i].data, essids[i].len); printf("\n"); } #endif @@ -2630,877 +3644,1118 @@ wpi_scan(struct wpi_softc *sc) * 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 = (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, 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 */ + *(uint16_t *)&wh->i_dur[0] = 0; /* filled by h/w */ + *(uint16_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); + if (rs->rs_nrates > IEEE80211_RATE_SIZE) + frm = ieee80211_add_xrates(frm, rs); - /* setup length of probe request */ - hdr->tx.len = htole16(frm - (uint8_t *)wh); + /* 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 */ - c = ic->ic_curchan; chan = (struct wpi_scan_chan *)frm; - chan->chan = ieee80211_chan2ieee(ic, c); + chan->chan = htole16(ieee80211_chan2ieee(ic, c)); chan->flags = 0; - if (!(c->ic_flags & IEEE80211_CHAN_PASSIVE)) { + 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 (!(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)); + /* + * 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_passive <= dwell_active) + dwell_passive = dwell_active + 1; + + 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, (c->ic_flags & IEEE80211_CHAN_PASSIVE) ? 1 : 0); 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; + buflen = (uint8_t *)chan - buf; + hdr->len = htole16(buflen); - 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; - } + DPRINTF(sc, WPI_DEBUG_CMD, "sending scan command nchan=%d\n", + hdr->nchan); + error = wpi_cmd(sc, WPI_CMD_SCAN, buf, buflen, 1); + free(buf, M_DEVBUF); - DPRINTFN(WPI_DEBUG_SCANNING, - ("Scanning %u Passive: %d\n", - chan->chan, - c->ic_flags & IEEE80211_CHAN_PASSIVE)); + sc->sc_scan_timer = 5; - hdr->nchan++; - chan++; + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); - frm += sizeof (struct wpi_scan_chan); - } -#endif + return error; +} - hdr->len = htole16(frm - (uint8_t *)hdr); - pktlen = frm - (uint8_t *)cmd; +static int +wpi_auth(struct wpi_softc *sc, struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_node *ni = vap->iv_bss; + int error; - 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; + 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, ni->ni_chan); + sc->rxon.flags = htole32(WPI_RXON_TSF | WPI_RXON_CTS_TO_SELF); + if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) + 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(ni->ni_chan)) { + sc->rxon.cck_mask = 0; + sc->rxon.ofdm_mask = 0x15; + } else if (IEEE80211_IS_CHAN_B(ni->ni_chan)) { + 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; } - desc->flags = htole32(WPI_PAD32(pktlen) << 28 | 1 << 24); - desc->segs[0].addr = htole32(physaddr); - desc->segs[0].len = htole32(pktlen); + 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); - 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); + if ((error = wpi_send_rxon(sc, 0, 1)) != 0) { + device_printf(sc->sc_dev, "%s: could not send RXON\n", + __func__); + } - /* kick cmd ring */ - ring->cur = (ring->cur + 1) % WPI_CMD_RING_COUNT; - WPI_WRITE(sc, WPI_TX_WIDX, ring->qid << 8 | ring->cur); + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); - sc->sc_scan_timer = 5; - return 0; /* will be notified async. of failure/success */ + return error; } -/** - * 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) +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_power power; - struct wpi_bluetooth bluetooth; - struct wpi_node_info node; - int error; + struct ieee80211vap *vap = ni->ni_vap; + struct wpi_vap *wvp = WPI_VAP(vap); + struct wpi_buf *bcn = &wvp->wv_bcbuf; + struct ieee80211_beacon_offsets bo; + struct wpi_cmd_beacon *cmd; + struct mbuf *m; + int totlen; - /* 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; + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + + if (ni->ni_chan == IEEE80211_CHAN_ANYC) + return EINVAL; + + m = ieee80211_beacon_alloc(ni, &bo); + if (m == NULL) { + device_printf(sc->sc_dev, + "%s: could not allocate beacon frame\n", __func__); + return ENOMEM; } + totlen = m->m_pkthdr.len; - /* 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) { + if (bcn->data == NULL) { + cmd = malloc(sizeof(struct wpi_cmd_beacon), M_DEVBUF, + M_NOWAIT | M_ZERO); + + if (cmd == NULL) { + device_printf(sc->sc_dev, + "could not allocate buffer for beacon command\n"); + m_freem(m); + return ENOMEM; + } + + cmd->id = WPI_ID_BROADCAST; + cmd->ofdm_mask = 0xff; + cmd->cck_mask = 0x0f; + cmd->lifetime = htole32(WPI_LIFETIME_INFINITE); + cmd->flags = htole32(WPI_TX_AUTO_SEQ | WPI_TX_INSERT_TSTAMP); + + bcn->data = cmd; + bcn->ni = NULL; + bcn->code = WPI_CMD_SET_BEACON; + bcn->ac = 4; + bcn->size = sizeof(struct wpi_cmd_beacon); + } else + cmd = bcn->data; + + cmd->len = htole16(totlen); + cmd->plcp = (ic->ic_curmode == IEEE80211_MODE_11A) ? + wpi_ridx_to_plcp[WPI_RIDX_OFDM6] : wpi_ridx_to_plcp[WPI_RIDX_CCK1]; + + /* NB: m will be freed in wpi_cmd_done() */ + bcn->m = m; + + return wpi_cmd2(sc, bcn); +} + +static void +wpi_update_beacon(struct ieee80211vap *vap, int item) +{ + struct ieee80211_node *ni = vap->iv_bss; + struct ifnet *ifp = vap->iv_ifp; + struct wpi_softc *sc = ifp->if_softc; + int error; + + if ((error = wpi_setup_beacon(sc, ni)) != 0) { device_printf(sc->sc_dev, - "could not configure bluetooth coexistence\n"); - return error; + "%s: could not update beacon frame, error %d", __func__, + 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); +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; + + 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; } - 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); + + /* XXX kernel panic workaround */ + if (ni->ni_chan == IEEE80211_CHAN_ANYC) { + device_printf(sc->sc_dev, "%s: incomplete configuration\n", + __func__); 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"); + + 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; } - /* 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; + /* Update adapter configuration. */ + IEEE80211_ADDR_COPY(sc->rxon.bssid, ni->ni_bssid); + sc->rxon.associd = htole16(IEEE80211_NODE_AID(ni)); + sc->rxon.chan = ieee80211_chan2ieee(ic, ni->ni_chan); + sc->rxon.flags = htole32(WPI_RXON_TSF | WPI_RXON_CTS_TO_SELF); + if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) + sc->rxon.flags |= htole32(WPI_RXON_AUTO | WPI_RXON_24GHZ); + /* Short preamble and slot time are negotiated when associating. */ + sc->rxon.flags &= ~htole32(WPI_RXON_SHPREAMBLE | WPI_RXON_SHSLOT); + 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(ni->ni_chan)) { + sc->rxon.cck_mask = 0; + sc->rxon.ofdm_mask = 0x15; + } else if (IEEE80211_IS_CHAN_B(ni->ni_chan)) { + 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); - /* 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"); + /* XXX put somewhere HC_QOS_SUPPORT_ASSOC + HC_IBSS_START */ + + 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; } - /* Setup rate scalling */ - error = wpi_mrr_setup(sc); - if (error != 0) { - device_printf(sc->sc_dev, "could not setup MRR\n"); - return error; + if (vap->iv_opmode == IEEE80211_M_IBSS) { + 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; + } } - return 0; -} + if (vap->iv_opmode == IEEE80211_M_STA) { + /* Add BSS node. */ + ((struct wpi_node *)ni)->id = WPI_ID_BSS; + if ((error = wpi_add_node(sc, ni)) != 0) { + device_printf(sc->sc_dev, + "%s: could not add BSS node, error %d\n", __func__, + error); + return error; + } + } -static void -wpi_stop_master(struct wpi_softc *sc) -{ - uint32_t tmp; - int ntries; + /* Link LED always on while associated. */ + wpi_set_led(sc, WPI_LED_LINK, 0, 1); - DPRINTFN(WPI_DEBUG_HW,("Disabling Firmware execution\n")); + /* Start periodic calibration timer. */ + callout_reset(&sc->calib_to, 60*hz, wpi_calib_timeout, sc); - tmp = WPI_READ(sc, WPI_RESET); - WPI_WRITE(sc, WPI_RESET, tmp | WPI_STOP_MASTER | WPI_NEVO_RESET); + /* Enable power-saving mode if requested by user. */ + if (vap->iv_flags & IEEE80211_F_PMGTON) + (void)wpi_set_pslevel(sc, 0, 3, 1); - tmp = WPI_READ(sc, WPI_GPIO_CTL); - if ((tmp & WPI_GPIO_PWR_STATUS) == WPI_GPIO_PWR_SLEEP) - return; /* already asleep */ + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); - 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"); - } + return 0; } static int -wpi_power_up(struct wpi_softc *sc) +wpi_key_alloc(struct ieee80211vap *vap, struct ieee80211_key *k, + ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix) { - uint32_t tmp; - int ntries; + struct ifnet *ifp = vap->iv_ifp; + struct wpi_softc *sc = ifp->if_softc; - 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); + if (!(&vap->iv_nw_keys[0] <= k && + k < &vap->iv_nw_keys[IEEE80211_WEP_NKID])) { + if (k->wk_flags & IEEE80211_KEY_GROUP) { + /* should not happen */ + DPRINTF(sc, WPI_DEBUG_KEY, "%s: bogus group key\n", + __func__); + return 0; + } + *keyix = 0; /* NB: use key index 0 for ucast key */ + } else { + *keyix = *rxkeyix = k - vap->iv_nw_keys; - for (ntries = 0; ntries < 5000; ntries++) { - if (WPI_READ(sc, WPI_GPIO_STATUS) & WPI_POWERED) - break; - DELAY(10); + if (k->wk_cipher->ic_cipher == IEEE80211_CIPHER_AES_CCM) + k->wk_flags |= IEEE80211_KEY_SWCRYPT; } - if (ntries == 5000) { - device_printf(sc->sc_dev, - "timeout waiting for NIC to power up\n"); - return ETIMEDOUT; - } - return 0; + return 1; } static int -wpi_reset(struct wpi_softc *sc) +wpi_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k, + const uint8_t mac[IEEE80211_ADDR_LEN]) { - uint32_t tmp; - int ntries; + const struct ieee80211_cipher *cip = k->wk_cipher; + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_node *ni = vap->iv_bss; + struct wpi_softc *sc = ic->ic_ifp->if_softc; + struct wpi_node *wn = (void *)ni; + struct wpi_node_info node; + uint16_t kflags; + int error; - DPRINTFN(WPI_DEBUG_HW, - ("Resetting the card - clearing any uploaded firmware\n")); + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); - /* clear any pending interrupts */ - WPI_WRITE(sc, WPI_INTR, 0xffffffff); + switch (cip->ic_cipher) { + case IEEE80211_CIPHER_AES_CCM: + if (k->wk_flags & IEEE80211_KEY_GROUP) + return 1; - tmp = WPI_READ(sc, WPI_PLL_CTL); - WPI_WRITE(sc, WPI_PLL_CTL, tmp | WPI_PLL_INIT); + kflags = WPI_KFLAG_CCMP; + break; + default: + /* null_key_set() */ + return 1; + } - tmp = WPI_READ(sc, WPI_CHICKEN); - WPI_WRITE(sc, WPI_CHICKEN, tmp | WPI_CHICKEN_RXNOLOS); + if (wn->id == WPI_ID_UNDEFINED) + return 0; - tmp = WPI_READ(sc, WPI_GPIO_CTL); - WPI_WRITE(sc, WPI_GPIO_CTL, tmp | WPI_GPIO_INIT); + kflags |= WPI_KFLAG_KID(k->wk_keyix); + if (k->wk_flags & IEEE80211_KEY_GROUP) + kflags |= WPI_KFLAG_MULTICAST; - /* 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; - } + 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); - /* initialize EEPROM */ - tmp = WPI_READ(sc, WPI_EEPROM_STATUS); + DPRINTF(sc, WPI_DEBUG_KEY, "set key id=%d for node %d\n", k->wk_keyix, + node.id); - if ((tmp & WPI_EEPROM_VERSION) == 0) { - device_printf(sc->sc_dev, "EEPROM not found\n"); - return EIO; + 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 0; } - WPI_WRITE(sc, WPI_EEPROM_STATUS, tmp & ~WPI_EEPROM_LOCKED); - return 0; + return 1; } -static void -wpi_hw_config(struct wpi_softc *sc) +static int +wpi_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k) { - uint32_t rev, hw; + const struct ieee80211_cipher *cip = k->wk_cipher; + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_node *ni = vap->iv_bss; + struct wpi_softc *sc = ic->ic_ifp->if_softc; + struct wpi_node *wn = (void *)ni; + struct wpi_node_info node; - /* voodoo from the Linux "driver".. */ - hw = WPI_READ(sc, WPI_HWCONFIG); + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); - 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; + switch (cip->ic_cipher) { + case IEEE80211_CIPHER_AES_CCM: + break; + default: + /* null_key_delete() */ + return 1; + } - if (sc->cap == 0x80) - hw |= WPI_HW_SKU_MRC; + if (vap->iv_state != IEEE80211_S_RUN || + (k->wk_flags & IEEE80211_KEY_GROUP)) + return 1; /* Nothing to do. */ - hw &= ~WPI_HW_REV_D; - if ((le16toh(sc->rev) & 0xf0) == 0xd0) - hw |= WPI_HW_REV_D; + memset(&node, 0, sizeof node); + node.id = wn->id; + node.control = WPI_NODE_UPDATE; + node.flags = WPI_FLAG_KEY_SET; - if (sc->type > 1) - hw |= WPI_HW_TYPE_B; + DPRINTF(sc, WPI_DEBUG_KEY, "delete keys for node %d\n", node.id); + (void)wpi_cmd(sc, WPI_CMD_ADD_NODE, &node, sizeof node, 1); - WPI_WRITE(sc, WPI_HWCONFIG, hw); + return 1; } -static void -wpi_rfkill_resume(struct wpi_softc *sc) +/* + * 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) { - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); - int ntries; + int ntries, error; + + /* Check (again) that the radio is not disabled. */ + if ((error = wpi_nic_lock(sc)) != 0) + return error; - /* enable firmware again */ - WPI_WRITE(sc, WPI_UCODE_CLR, WPI_RADIO_OFF); - WPI_WRITE(sc, WPI_UCODE_CLR, WPI_DISABLE_CMD); + 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 sensors to calibrate */ + /* Wait for thermal sensor to calibrate. */ for (ntries = 0; ntries < 1000; ntries++) { - if ((sc->temp = (int)WPI_READ(sc, WPI_TEMPERATURE)) != 0) + 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 calibration\n"); - return; - } - DPRINTFN(WPI_DEBUG_TEMP,("temperature %d\n", sc->temp)); + "timeout waiting for thermal sensor calibration\n"); + return ETIMEDOUT; + } - if (wpi_config(sc) != 0) { - device_printf(sc->sc_dev, "device config failed\n"); - return; - } + DPRINTF(sc, WPI_DEBUG_TEMP, "temperature %d\n", sc->temp); + return 0; +} - ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; - ifp->if_drv_flags |= IFF_DRV_RUNNING; - sc->flags &= ~WPI_FLAG_HW_RADIO_OFF; +/* + * 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, int size) +{ + int error, ntries; - 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); + 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; } - callout_reset(&sc->watchdog_to, hz, wpi_watchdog, sc); + /* 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 void -wpi_init_locked(struct wpi_softc *sc, int force) +static int +wpi_load_firmware(struct wpi_softc *sc) { - struct ifnet *ifp = sc->sc_ifp; - uint32_t tmp; - int ntries, qid; + struct wpi_fw_info *fw = &sc->fw; + struct wpi_dma_info *dma = &sc->fw_dma; + int error; - wpi_stop_locked(sc); - (void)wpi_reset(sc); + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); - 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); + /* 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); - (void)wpi_power_up(sc); - wpi_hw_config(sc); + /* 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; + } - /* 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); + /* Now press "execute". */ + WPI_WRITE(sc, WPI_RESET, 0); - if (!(tmp & 0x1)) { - sc->flags |= WPI_FLAG_HW_RADIO_OFF; - device_printf(sc->sc_dev,"Radio Transmitter is switched off\n"); - goto out; + /* Wait at most one second for first alive notification. */ + if ((error = msleep(sc, &sc->sc_mtx, PCATCH, "wpiinit", hz)) != 0) { + device_printf(sc->sc_dev, + "%s: timeout waiting for adapter to initialize, error %d\n", + __func__, error); + return error; } - /* 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); + /* 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; } - if (ntries == 1000) { + sc->fw_fp = fp; + + if (fp->datasize < sizeof (struct wpi_firmware_hdr)) { device_printf(sc->sc_dev, - "timeout waiting for thermal sensors calibration\n"); - return; + "firmware file too short: %zu bytes\n", fp->datasize); + error = EINVAL; + goto fail; } - DPRINTFN(WPI_DEBUG_TEMP,("temperature %d\n", sc->temp)); - if (wpi_config(sc) != 0) { - device_printf(sc->sc_dev, "device config failed\n"); - return; + 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; } - ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; - ifp->if_drv_flags |= IFF_DRV_RUNNING; -out: - callout_reset(&sc->watchdog_to, hz, wpi_watchdog, sc); + /* 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_init(void *arg) +wpi_unload_firmware(struct wpi_softc *sc) { - struct wpi_softc *sc = arg; - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; + if (sc->fw_fp != NULL) { + firmware_put(sc->fw_fp, FIRMWARE_UNLOAD); + sc->fw_fp = NULL; + } +} - WPI_LOCK(sc); - wpi_init_locked(sc, 0); - WPI_UNLOCK(sc); +static int +wpi_clock_wait(struct wpi_softc *sc) +{ + int ntries; - if (ifp->if_drv_flags & IFF_DRV_RUNNING) - ieee80211_start_all(ic); /* start all vaps */ + /* 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 void -wpi_stop_locked(struct wpi_softc *sc) +static int +wpi_apm_init(struct wpi_softc *sc) { - struct ifnet *ifp = sc->sc_ifp; - uint32_t tmp; - int ac; + uint32_t reg; + int error; - sc->sc_tx_timer = 0; - sc->sc_scan_timer = 0; - ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); - sc->flags &= ~WPI_FLAG_HW_RADIO_OFF; - callout_stop(&sc->watchdog_to); - callout_stop(&sc->calib_to); + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); - /* 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); + /* 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); - wpi_mem_lock(sc); - wpi_mem_write(sc, WPI_MEM_MODE, 0); - wpi_mem_unlock(sc); + /* Set FH wait threshold to max (HW bug under stress workaround). */ + WPI_SETBITS(sc, WPI_DBG_HPET_MEM, 0xffff0000); - /* 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); + /* Retrieve PCIe Active State Power Management (ASPM). */ + reg = pci_read_config(sc->sc_dev, sc->sc_cap_off + 0x10, 1); + /* Workaround for HW instability in PCIe L0->L0s->L1 transition. */ + if (reg & 0x02) /* L1 Entry enabled. */ + WPI_SETBITS(sc, WPI_GIO, WPI_GIO_L0S_ENA); + else + WPI_CLRBITS(sc, WPI_GIO, WPI_GIO_L0S_ENA); - /* reset Rx ring */ - wpi_reset_rx_ring(sc, &sc->rxq); + WPI_SETBITS(sc, WPI_ANA_PLL, WPI_ANA_PLL_INIT); - wpi_mem_lock(sc); - wpi_mem_write(sc, WPI_MEM_CLOCK2, 0x200); - wpi_mem_unlock(sc); + /* Wait for clock stabilization before accessing prph. */ + if ((error = wpi_clock_wait(sc)) != 0) + return error; - DELAY(5); + if ((error = wpi_nic_lock(sc)) != 0) + return error; + /* 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); - wpi_stop_master(sc); + return 0; +} - tmp = WPI_READ(sc, WPI_RESET); - WPI_WRITE(sc, WPI_RESET, tmp | WPI_SW_RESET); - sc->flags &= ~WPI_FLAG_BUSY; +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_stop(struct wpi_softc *sc) +wpi_apm_stop(struct wpi_softc *sc) { - WPI_LOCK(sc); - wpi_stop_locked(sc); - WPI_UNLOCK(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_calib_timeout(void *arg) +wpi_nic_config(struct wpi_softc *sc) { - 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; + uint32_t rev; - if (vap->iv_state != IEEE80211_S_RUN) - return; + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); - /* update sensor data */ - temp = (int)WPI_READ(sc, WPI_TEMPERATURE); - DPRINTFN(WPI_DEBUG_TEMP,("Temp in calibration is: %d\n", temp)); + /* 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); - wpi_power_calibration(sc, temp); + if (sc->cap == 0x80) + WPI_SETBITS(sc, WPI_HW_IF_CONFIG, WPI_HW_IF_CONFIG_SKU_MRC); - callout_reset(&sc->calib_to, 60*hz, wpi_calib_timeout, sc); + if ((le16toh(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); } -/* - * 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) +static int +wpi_hw_init(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 chnl, ntries, error; - /* 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; - } + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); - DPRINTFN(WPI_DEBUG_TEMP,("temperature %d->%d\n", sc->temp, temp)); + /* Clear pending interrupts. */ + WPI_WRITE(sc, WPI_INT, 0xffffffff); - /* adjust Tx power if need be */ - if (abs(temp - sc->temp) <= 6) - return; + 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; + } - sc->temp = temp; + /* 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; + } - 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"); + /* 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 ((error = msleep(sc, &sc->sc_mtx, PCATCH, "wpiinit", hz)) != 0) { + 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); } -/** - * 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) +wpi_hw_stop(struct wpi_softc *sc) { - 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; + int chnl, qid, ntries; - wpi_read_prom_data(sc, band->addr, channels, - band->nchan * sizeof (struct wpi_eeprom_chan)); + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); - 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; - } + if (WPI_READ(sc, WPI_UCODE_GP1) & WPI_UCODE_GP1_MAC_SLEEP) + wpi_nic_lock(sc); - passive = 0; - chan = band->chan[i]; - c = &ic->ic_channels[ic->ic_nchans++]; + WPI_WRITE(sc, WPI_RESET, WPI_RESET_NEVO); - /* is active scan allowed on this channel? */ - if (!(channels[i].flags & WPI_EEPROM_CHAN_ACTIVE)) { - passive = IEEE80211_CHAN_PASSIVE; - } + /* Disable interrupts. */ + WPI_WRITE(sc, WPI_INT_MASK, 0); + WPI_WRITE(sc, WPI_INT, 0xffffffff); + WPI_WRITE(sc, WPI_FH_INT, 0xffffffff); - 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; + /* Make sure we no longer hold the NIC lock. */ + wpi_nic_unlock(sc); - 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; + 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); - } 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; + /* 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); + } - /* save maximum allowed power for this channel */ - sc->maxpwr[chan] = channels[i].maxpwr; + /* Stop RX ring. */ + wpi_reset_rx_ring(sc); -#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 + /* Reset all TX rings. */ + for (qid = 0; qid < WPI_NTXQUEUES; qid++) + wpi_reset_tx_ring(sc, &sc->txq[qid]); - 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)); + 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_read_eeprom_group(struct wpi_softc *sc, int n) +wpi_radio_on(void *arg0, int pending) { - 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); + struct wpi_softc *sc = arg0; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); - DPRINTF(("power group %d: chan=%d maxpwr=%d temp=%d\n", n, - group->chan, group->maxpwr, group->temp)); + device_printf(sc->sc_dev, "RF switch: radio enabled\n"); - for (i = 0; i < WPI_SAMPLES_COUNT; i++) { - group->samples[i].index = rgroup.samples[i].index; - group->samples[i].power = rgroup.samples[i].power; + if (vap != NULL) { + wpi_init(sc); + ieee80211_init(vap); + } - DPRINTF(("\tsample %d: index=%d power=%d\n", i, - group->samples[i].index, group->samples[i].power)); + if (WPI_READ(sc, WPI_GP_CNTRL) & WPI_GP_CNTRL_RFKILL) { + WPI_LOCK(sc); + callout_stop(&sc->watchdog_rfkill); + WPI_UNLOCK(sc); } } -/* - * 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) +static void +wpi_radio_off(void *arg0, int pending) { + struct wpi_softc *sc = arg0; 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; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); - /* get channel number */ - chan = ieee80211_chan2ieee(ic, c); + device_printf(sc->sc_dev, "RF switch: radio disabled\n"); - /* 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]; + wpi_stop(sc); + if (vap != NULL) + ieee80211_stop(vap); - memset(&txpower, 0, sizeof txpower); - txpower.band = IEEE80211_IS_CHAN_5GHZ(c) ? 0 : 1; - txpower.channel = htole16(chan); + callout_reset(&sc->watchdog_rfkill, hz, wpi_watchdog_rfkill, sc); +} - /* 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]); +static void +wpi_init_locked(struct wpi_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; + int error; - txpower.rates[i].rate = wpi_ridx_to_plcp[i]; + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); - 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)); + WPI_LOCK_ASSERT(sc); + + /* 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); + return; } - return wpi_cmd(sc, WPI_CMD_TXPOWER, &txpower, sizeof txpower, async); -} + /* 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 fail; + } -/* - * 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)) + /* 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; + } -/* linear interpolation */ -#define interpolate(x, x1, y1, x2, y2, n) \ - ((y1) + fdivround(((x) - (x1)) * ((y2) - (y1)), (x2) - (x1), n)) + /* 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; + } - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - struct wpi_power_sample *sample; - int pwr, idx; - u_int chan; + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + ifp->if_drv_flags |= IFF_DRV_RUNNING; - /* get channel number */ - chan = ieee80211_chan2ieee(ic, c); + callout_reset(&sc->watchdog_to, hz, wpi_watchdog, sc); - /* default power is group's maximum power - 3dB */ - pwr = group->maxpwr / 2; + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); - /* 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; - } + return; - /* never exceed channel's maximum allowed Tx power */ - pwr = min(pwr, sc->maxpwr[chan]); +fail: wpi_stop_locked(sc); + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__); +} - /* 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); +static void +wpi_init(void *arg) +{ + struct wpi_softc *sc = arg; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; - /* - * 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; + WPI_LOCK(sc); + wpi_init_locked(sc); + WPI_UNLOCK(sc); - /* decrease power for CCK rates (-5dB) */ - if (!WPI_RATE_IS_OFDM(rate)) - idx += 10; + if (ifp->if_drv_flags & IFF_DRV_RUNNING) + ieee80211_start_all(ic); +} - /* 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; +static void +wpi_stop_locked(struct wpi_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; -#undef interpolate -#undef fdivround + WPI_LOCK_ASSERT(sc); + + sc->sc_scan_timer = 0; + sc->sc_tx_timer = 0; + callout_stop(&sc->watchdog_to); + callout_stop(&sc->calib_to); + ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); + + /* Power OFF hardware. */ + wpi_hw_stop(sc); } -/** - * 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_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) @@ -3513,15 +4768,21 @@ wpi_scan_start(struct ieee80211com *ic) 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. +/* + * Callback from net80211 to terminate a scan. */ static void wpi_scan_end(struct ieee80211com *ic) { - /* XXX ignore */ + struct ifnet *ifp = ic->ic_ifp; + struct wpi_softc *sc = ifp->if_softc; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + + if (vap->iv_state == IEEE80211_S_RUN) { + WPI_LOCK(sc); + wpi_set_led(sc, WPI_LED_LINK, 0, 1); + WPI_UNLOCK(sc); + } } /** @@ -3531,22 +4792,38 @@ wpi_scan_end(struct ieee80211com *ic) static void wpi_set_channel(struct ieee80211com *ic) { + const struct ieee80211_channel *c = ic->ic_curchan; struct ifnet *ifp = ic->ic_ifp; struct wpi_softc *sc = ifp->if_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); + sc->sc_txtap.wt_chan_freq = htole16(c->ic_freq); + sc->sc_txtap.wt_chan_flags = htole16(c->ic_flags); + /* * 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) + 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, 0)) != 0) device_printf(sc->sc_dev, - "error %d settting channel\n", error); + "%s: error %d settting channel\n", __func__, + error); } + WPI_UNLOCK(sc); } /** @@ -3558,13 +4835,21 @@ static void wpi_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell) { struct ieee80211vap *vap = ss->ss_vap; - struct ifnet *ifp = vap->iv_ic->ic_ifp; + struct ieee80211com *ic = vap->iv_ic; + struct ifnet *ifp = ic->ic_ifp; struct wpi_softc *sc = ifp->if_softc; + int error; - WPI_LOCK(sc); - if (wpi_scan(sc)) - ieee80211_cancel_scan(vap); - WPI_UNLOCK(sc); + if (sc->rxon.chan != ieee80211_chan2ieee(ic, ic->ic_curchan)) { + WPI_LOCK(sc); + error = wpi_scan(sc, ic->ic_curchan); + WPI_UNLOCK(sc); + if (error != 0) + ieee80211_cancel_scan(vap); + } else { + /* Send probe request when associated. */ + sc->sc_scan_curchan(ss, maxdwell); + } } /** @@ -3580,118 +4865,19 @@ wpi_scan_mindwell(struct ieee80211_scan_state *ss) } 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) +wpi_hw_reset(void *arg, int pending) { 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_inc_counter(ifp, IFCOUNTER_OERRORS, 1); - 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 (ifp->if_drv_flags & IFF_DRV_RUNNING) - callout_reset(&sc->watchdog_to, hz, wpi_watchdog, sc); -} + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); -#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"; + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); - default: - KASSERT(1, ("Unknown Command: %d\n", cmd)); - return "UNKNOWN CMD"; /* Make the compiler happy */ - } + wpi_stop(sc); + if (vap != NULL) + ieee80211_stop(vap); + wpi_init(sc); + if (vap != NULL) + ieee80211_init(vap); } -#endif - -MODULE_DEPEND(wpi, pci, 1, 1, 1); -MODULE_DEPEND(wpi, wlan, 1, 1, 1); -MODULE_DEPEND(wpi, firmware, 1, 1, 1); diff --git a/sys/dev/wpi/if_wpi_debug.h b/sys/dev/wpi/if_wpi_debug.h new file mode 100644 index 0000000..91a7383 --- /dev/null +++ b/sys/dev/wpi/if_wpi_debug.h @@ -0,0 +1,98 @@ +/*- + * Copyright (c) 2006,2007 + * Damien Bergamini <damien.bergamini@free.fr> + * Benjamin Close <Benjamin.Close@clearchain.com> + * + * 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_KEY = 0x00010000, /* node key management */ + WPI_DEBUG_EDCA = 0x00020000, /* WME info */ + 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" + +static const char *wpi_cmd_str(int cmd) +{ + switch (cmd) { + /* Notifications */ + case WPI_UC_READY: return "UC_READY"; + case WPI_RX_DONE: return "RX_DONE"; + case WPI_START_SCAN: return "START_SCAN"; + case WPI_SCAN_RESULTS: return "SCAN_RESULTS"; + case WPI_STOP_SCAN: return "STOP_SCAN"; + case WPI_BEACON_SENT: return "BEACON_SENT"; + case WPI_RX_STATISTICS: return "RX_STATS"; + case WPI_BEACON_STATISTICS: return "BEACON_STATS"; + case WPI_STATE_CHANGED: return "STATE_CHANGED"; + case WPI_BEACON_MISSED: return "BEACON_MISSED"; + + /* Command notifications */ + case WPI_CMD_RXON: return "WPI_CMD_RXON"; + case WPI_CMD_RXON_ASSOC: return "WPI_CMD_RXON_ASSOC"; + case WPI_CMD_EDCA_PARAMS: return "WPI_CMD_EDCA_PARAMS"; + case WPI_CMD_TIMING: return "WPI_CMD_TIMING"; + case WPI_CMD_ADD_NODE: return "WPI_CMD_ADD_NODE"; + case WPI_CMD_DEL_NODE: return "WPI_CMD_DEL_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_BT_COEX: return "WPI_CMD_BT_COEX"; + + default: + KASSERT(1, ("Unknown Command: %d\n", cmd)); + return "UNKNOWN CMD"; + } +} + +#else +#define DPRINTF(sc, m, ...) do { (void) sc; } while (0) +#endif + +#endif /* __IF_WPI_DEBUG_H__ */ diff --git a/sys/dev/wpi/if_wpireg.h b/sys/dev/wpi/if_wpireg.h index 60d183a..cfd8a09 100644 --- a/sys/dev/wpi/if_wpireg.h +++ b/sys/dev/wpi/if_wpireg.h @@ -18,155 +18,207 @@ */ #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 +#define WPI_RX_RING_COUNT_LOG 6 +#define WPI_RX_RING_COUNT (1 << WPI_RX_RING_COUNT_LOG) + +#define WPI_NTXQUEUES 8 +#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_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]; @@ -176,20 +228,21 @@ struct wpi_shared { #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; @@ -204,8 +257,11 @@ 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; @@ -228,8 +284,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,17 +297,23 @@ 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 @@ -257,21 +321,22 @@ struct wpi_tx_cmd { #define WPI_CMD_SCAN 128 #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 +344,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) @@ -304,10 +370,10 @@ struct wpi_config { #define WPI_FILTER_BEACON (1 << 6) 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 +382,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,41 +406,60 @@ 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; @@ -381,34 +468,39 @@ struct wpi_cmd_data { #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_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; 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,11 +510,10 @@ 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 { +/* Structure for notification WPI_BEACON_MISSED. */ +struct wpi_beacon_missed { uint32_t consecutive; uint32_t total; uint32_t expected; @@ -430,29 +521,22 @@ struct wpi_missed_beacon { } __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 - - } __attribute__((__packed__)) rates[WPI_CCK11 + 1]; + } __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 +548,127 @@ 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; + uint16_t quiet_threshold; + uint16_t crc_threshold; uint16_t reserved2; - uint32_t maxtimeout; - uint32_t suspend; + uint32_t max_svc; /* background scans */ + uint32_t pause_svc; /* background scans */ uint32_t flags; uint32_t filter; -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__)); - -#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) + +/* 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 { - -#define WPI_FW_MINVERSION 2144 + uint8_t max_kill; +#define WPI_BT_MAX_KILL_DEF 5 - 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,7 +678,7 @@ 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; @@ -612,9 +687,114 @@ struct wpi_stop_scan { 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 +806,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 +875,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 +901,94 @@ 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]; + int 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" +}; + +/* XXX description for some error codes (error data). */ +/* 0x00000074 - wrong totlen field */ +/* 0x000003B3 - powersave error */ +/* 0x00000447 - wrong channel selected */ #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/wpi/if_wpivar.h b/sys/dev/wpi/if_wpivar.h index e579264..5362727 100644 --- a/sys/dev/wpi/if_wpivar.h +++ b/sys/dev/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 <net80211/ieee80211_amrr.h> - 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) | \ @@ -56,15 +53,14 @@ 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_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,17 @@ 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; + int update; }; -#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 { @@ -95,15 +89,12 @@ struct wpi_rx_ring { struct wpi_rx_data data[WPI_RX_RING_COUNT]; bus_dma_tag_t data_dmat; int 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; }; struct wpi_power_sample { @@ -119,80 +110,117 @@ struct wpi_power_group { int16_t temp; }; +struct wpi_buf { + void *data; + struct ieee80211_node *ni; + struct mbuf *m; + size_t size; + int code; + int ac; +}; + struct wpi_vap { struct ieee80211vap vap; + struct wpi_buf wv_bcbuf; int (*newstate)(struct ieee80211vap *, enum ieee80211_state, int); }; #define WPI_VAP(vap) ((struct wpi_vap *)(vap)) +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; + int sc_debug; + struct mtx sc_mtx; + struct unrhdr *sc_unr; /* 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) +#define WPI_FLAG_BUSY (1 << 0) - /* shared area */ + /* 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_NTXQUEUES]; struct wpi_rx_ring rxq; - /* TX Thermal Callibration */ + /* TX Thermal Callibration. */ struct callout calib_to; int calib_cnt; - /* Watch dog timer */ + /* Watch dog timers. */ struct callout watchdog_to; - /* Hardware switch polling timer */ - struct callout hwswitch_to; + 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; + struct wpi_rxon rxon; int temp; - + uint32_t qfullmsk; int sc_tx_timer; int sc_scan_timer; - struct bpf_if *sc_drvbpf; + void (*sc_node_free)(struct ieee80211_node *); + void (*sc_scan_curchan)(struct ieee80211_scan_state *, + unsigned long); 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_reinittask; + 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. */ }; + #define WPI_LOCK_INIT(_sc) \ mtx_init(&(_sc)->sc_mtx, device_get_nameunit((_sc)->sc_dev), \ MTX_NETWORK_LOCK, MTX_DEF) |