diff --git a/src/add-ons/kernel/drivers/network/wlan/iprowifi3945/Jamfile b/src/add-ons/kernel/drivers/network/wlan/iprowifi3945/Jamfile index 58cf156675..d3261c8db2 100644 --- a/src/add-ons/kernel/drivers/network/wlan/iprowifi3945/Jamfile +++ b/src/add-ons/kernel/drivers/network/wlan/iprowifi3945/Jamfile @@ -1,24 +1,22 @@ SubDir HAIKU_TOP src add-ons kernel drivers network wlan iprowifi3945 ; -UseHeaders [ FDirName $(HAIKU_TOP) src libs compat freebsd_network compat ] - : true ; -UseHeaders [ FDirName $(HAIKU_TOP) src libs compat freebsd_wlan ] : true ; +UseHeaders [ FDirName $(HAIKU_TOP) src libs compat openbsd_network compat ] : true ; +UseHeaders [ FDirName $(HAIKU_TOP) src libs compat openbsd_wlan ] : true ; +UseHeaders [ FDirName $(HAIKU_TOP) src libs compat freebsd_network compat ] : true ; UsePrivateHeaders net system ; UsePrivateKernelHeaders ; -SubDirCcFlags [ FDefines _KERNEL=1 FBSD_DRIVER=1 _XOPEN_SOURCE ] - -Wno-format - -Wno-unused - -Wno-uninitialized ; +SubDirCcFlags [ FDefines _KERNEL=1 _XOPEN_SOURCE ] + -Wno-unused -Wno-sign-compare ; UseHeaders [ FDirName $(SUBDIR) ] : true ; -SEARCH_SOURCE += [ FDirName $(SUBDIR) dev wpi ] ; +SEARCH_SOURCE += [ FDirName $(SUBDIR) dev pci ] ; KernelAddon iprowifi3945 : if_wpi.c glue.c : - freebsd_wlan.a + openbsd_wlan.a libfreebsd_network.a ; diff --git a/src/add-ons/kernel/drivers/network/wlan/iprowifi3945/dev/pci/if_wpi.c b/src/add-ons/kernel/drivers/network/wlan/iprowifi3945/dev/pci/if_wpi.c new file mode 100644 index 0000000000..7a6d5be958 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/iprowifi3945/dev/pci/if_wpi.c @@ -0,0 +1,3408 @@ +/* $OpenBSD: if_wpi.c,v 1.158 2024/05/24 06:02:57 jsg Exp $ */ + +/*- + * Copyright (c) 2006-2008 + * Damien Bergamini + * + * 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. + */ + +/* + * Driver for Intel PRO/Wireless 3945ABG 802.11 network adapters. + */ + +//#include "bpfilter.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#if NBPFILTER > 0 +#include +#endif +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + +#ifdef __FreeBSD_version +#include +#include +#define DEVNAME(_s) gDriverName +#define SC_DEV_FOR_PCI sc->sc_dev +#else +#define DEVNAME(_s) ((_s)->sc_dev.dv_xname) +#endif + +static const struct pci_matchid wpi_devices[] = { +#ifdef __FreeBSD_version +#define PCI_VENDOR_INTEL 0x8086 +#define PCI_PRODUCT_INTEL_PRO_WL_3945ABG_1 0x4222 +#define PCI_PRODUCT_INTEL_PRO_WL_3945ABG_2 0x4227 +#endif + { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_PRO_WL_3945ABG_1 }, + { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_PRO_WL_3945ABG_2 } +}; + +int wpi_match(struct device *, void *, void *); +#ifdef __FreeBSD_version +int wpi_attach(struct device *); +#else +void wpi_attach(struct device *, struct device *, void *); +#endif +#if NBPFILTER > 0 +void wpi_radiotap_attach(struct wpi_softc *); +#endif +int wpi_detach(struct device *, int); +int wpi_activate(struct device *, int); +void wpi_wakeup(struct wpi_softc *); +void wpi_init_task(void *); +int wpi_nic_lock(struct wpi_softc *); +int wpi_read_prom_data(struct wpi_softc *, uint32_t, void *, int); +int wpi_dma_contig_alloc(bus_dma_tag_t, struct wpi_dma_info *, + void **, bus_size_t, bus_size_t); +void wpi_dma_contig_free(struct wpi_dma_info *); +int wpi_alloc_shared(struct wpi_softc *); +void wpi_free_shared(struct wpi_softc *); +int wpi_alloc_fwmem(struct wpi_softc *); +void wpi_free_fwmem(struct wpi_softc *); +int wpi_alloc_rx_ring(struct wpi_softc *, struct wpi_rx_ring *); +void wpi_reset_rx_ring(struct wpi_softc *, struct wpi_rx_ring *); +void wpi_free_rx_ring(struct wpi_softc *, struct wpi_rx_ring *); +int wpi_alloc_tx_ring(struct wpi_softc *, struct wpi_tx_ring *, + int); +void wpi_reset_tx_ring(struct wpi_softc *, struct wpi_tx_ring *); +void wpi_free_tx_ring(struct wpi_softc *, struct wpi_tx_ring *); +int wpi_read_eeprom(struct wpi_softc *); +void wpi_read_eeprom_channels(struct wpi_softc *, int); +void wpi_read_eeprom_group(struct wpi_softc *, int); +struct ieee80211_node *wpi_node_alloc(struct ieee80211com *); +void wpi_newassoc(struct ieee80211com *, struct ieee80211_node *, + int); +int wpi_media_change(struct ifnet *); +int wpi_newstate(struct ieee80211com *, enum ieee80211_state, int); +void wpi_iter_func(void *, struct ieee80211_node *); +void wpi_calib_timeout(void *); +int wpi_ccmp_decap(struct wpi_softc *, struct mbuf *, + struct ieee80211_key *); +void wpi_rx_done(struct wpi_softc *, struct wpi_rx_desc *, + struct wpi_rx_data *, struct mbuf_list *); +void wpi_tx_done(struct wpi_softc *, struct wpi_rx_desc *); +void wpi_cmd_done(struct wpi_softc *, struct wpi_rx_desc *); +void wpi_notif_intr(struct wpi_softc *); +void wpi_fatal_intr(struct wpi_softc *); +int wpi_intr(void *); +int wpi_tx(struct wpi_softc *, struct mbuf *, + struct ieee80211_node *); +void wpi_start(struct ifnet *); +void wpi_watchdog(struct ifnet *); +int wpi_ioctl(struct ifnet *, u_long, caddr_t); +int wpi_cmd(struct wpi_softc *, int, const void *, int, int); +int wpi_mrr_setup(struct wpi_softc *); +void wpi_updateedca(struct ieee80211com *); +void wpi_set_led(struct wpi_softc *, uint8_t, uint8_t, uint8_t); +int wpi_set_timing(struct wpi_softc *, struct ieee80211_node *); +void wpi_power_calibration(struct wpi_softc *); +int wpi_set_txpower(struct wpi_softc *, int); +int wpi_get_power_index(struct wpi_softc *, + struct wpi_power_group *, struct ieee80211_channel *, int); +int wpi_set_pslevel(struct wpi_softc *, int, int, int); +int wpi_config(struct wpi_softc *); +int wpi_scan(struct wpi_softc *, uint16_t); +int wpi_auth(struct wpi_softc *); +int wpi_run(struct wpi_softc *); +int wpi_set_key(struct ieee80211com *, struct ieee80211_node *, + struct ieee80211_key *); +void wpi_delete_key(struct ieee80211com *, struct ieee80211_node *, + struct ieee80211_key *); +int wpi_post_alive(struct wpi_softc *); +int wpi_load_bootcode(struct wpi_softc *, const uint8_t *, int); +int wpi_load_firmware(struct wpi_softc *); +int wpi_read_firmware(struct wpi_softc *); +int wpi_clock_wait(struct wpi_softc *); +int wpi_apm_init(struct wpi_softc *); +void wpi_apm_stop_master(struct wpi_softc *); +void wpi_apm_stop(struct wpi_softc *); +void wpi_nic_config(struct wpi_softc *); +int wpi_hw_init(struct wpi_softc *); +void wpi_hw_stop(struct wpi_softc *); +int wpi_init(struct ifnet *); +void wpi_stop(struct ifnet *, int); + +#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) +int wpi_debug = 0; +#else +#define DPRINTF(x) +#define DPRINTFN(n, x) +#endif + +#ifdef __FreeBSD_version +static int +wpi_probe(device_t dev) +{ + int i; + + for (i = 0; i < nitems(wpi_devices); i++) { + if (pci_get_vendor(dev) == wpi_devices[i].pm_vid && + pci_get_device(dev) == wpi_devices[i].pm_pid) { + return (BUS_PROBE_DEFAULT); + } + } + + return (ENXIO); +} + +static device_method_t wpi_pci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, wpi_probe), + DEVMETHOD(device_attach, wpi_attach), +#if 0 + DEVMETHOD(device_detach, wpi_detach), + DEVMETHOD(device_suspend, wpi_suspend), + DEVMETHOD(device_resume, wpi_resume), +#endif + + DEVMETHOD_END +}; + +static driver_t wpi_pci_driver = { + "wpi", + wpi_pci_methods, + sizeof (struct wpi_softc) +}; + +DRIVER_MODULE(wpi, pci, wpi_pci_driver, NULL, NULL); +#else +struct cfdriver wpi_cd = { + NULL, "wpi", DV_IFNET +}; + +const struct cfattach wpi_ca = { + sizeof (struct wpi_softc), wpi_match, wpi_attach, wpi_detach, + wpi_activate +}; + +int +wpi_match(struct device *parent, void *match, void *aux) +{ + return pci_matchbyid((struct pci_attach_args *)aux, wpi_devices, + nitems(wpi_devices)); +} +#endif + +#ifdef __FreeBSD_version +int +wpi_attach(device_t dev) +#else +void +wpi_attach(struct device *parent, struct device *self, void *aux) +#endif +{ +#ifdef __FreeBSD_version +#define pa dev + struct wpi_softc *sc = device_get_softc(dev); +#else + struct wpi_softc *sc = (struct wpi_softc *)self; +#endif + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; +#ifndef __FreeBSD_version + struct pci_attach_args *pa = aux; +#endif + const char *intrstr; + pci_intr_handle_t ih; + pcireg_t memtype, reg; + int i, error; + +#ifdef __FreeBSD_version + sc->sc_dev = dev; + sc->sc_dmat = bus_get_dma_tag(sc->sc_dev); + bus_dma_tag_create(sc->sc_dmat, 1, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, + BUS_SPACE_MAXSIZE_32BIT, BUS_SPACE_UNRESTRICTED, BUS_SPACE_MAXSIZE_32BIT, 0, NULL, NULL, + &sc->sc_dmat); + pci_enable_busmaster(sc->sc_dev); + + if_alloc_inplace(ifp, IFT_ETHER); +#else + sc->sc_pct = pa->pa_pc; + sc->sc_pcitag = pa->pa_tag; + sc->sc_dmat = pa->pa_dmat; +#endif + + /* + * Get the offset of the PCI Express Capability Structure in PCI + * Configuration Space (the vendor driver hard-codes it as E0h.) + */ + error = pci_get_capability(sc->sc_pct, sc->sc_pcitag, + PCI_CAP_PCIEXPRESS, &sc->sc_cap_off, NULL); + if (error == 0) { + printf(": PCIe capability structure not found!\n"); + return -1; + } + + /* Clear device-specific "PCI retry timeout" register (41h). */ + reg = pci_conf_read(sc->sc_pct, sc->sc_pcitag, 0x40); + reg &= ~0xff00; + pci_conf_write(sc->sc_pct, sc->sc_pcitag, 0x40, reg); + + memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, WPI_PCI_BAR0); + error = pci_mapreg_map(pa, WPI_PCI_BAR0, memtype, 0, &sc->sc_st, + &sc->sc_sh, NULL, &sc->sc_sz, 0); + if (error != 0) { + printf(": can't map mem space\n"); + return -1; + } + + /* Install interrupt handler. */ + if (pci_intr_map_msi(pa, &ih) != 0 && pci_intr_map(pa, &ih) != 0) { + printf(": can't map interrupt\n"); + return -1; + } + intrstr = pci_intr_string(sc->sc_pct, ih); + sc->sc_ih = pci_intr_establish(sc->sc_pct, ih, IPL_NET, wpi_intr, sc, + DEVNAME(sc)); + if (sc->sc_ih == NULL) { + printf(": can't establish interrupt"); + if (intrstr != NULL) + printf(" at %s", intrstr); + printf("\n"); + return -1; + } + printf(": %s", intrstr); + + /* Power ON adapter. */ + if ((error = wpi_apm_init(sc)) != 0) { + printf(": could not power ON adapter\n"); + return -1; + } + + /* Read MAC address, channels, etc from EEPROM. */ + if ((error = wpi_read_eeprom(sc)) != 0) { + printf(": could not read EEPROM\n"); + return -1; + } + + /* Allocate DMA memory for firmware transfers. */ + if ((error = wpi_alloc_fwmem(sc)) != 0) { + printf(": could not allocate memory for firmware\n"); + return -1; + } + + /* Allocate shared area. */ + if ((error = wpi_alloc_shared(sc)) != 0) { + printf(": could not allocate shared area\n"); + goto fail1; + } + + /* Allocate TX rings. */ + for (i = 0; i < WPI_NTXQUEUES; i++) { + if ((error = wpi_alloc_tx_ring(sc, &sc->txq[i], i)) != 0) { + printf(": could not allocate TX ring %d\n", i); + goto fail2; + } + } + + /* Allocate RX ring. */ + if ((error = wpi_alloc_rx_ring(sc, &sc->rxq)) != 0) { + printf(": could not allocate Rx ring\n"); + goto fail2; + } + + /* Power OFF adapter. */ + wpi_apm_stop(sc); + /* Clear pending interrupts. */ + WPI_WRITE(sc, WPI_INT, 0xffffffff); + + ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ + ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ + ic->ic_state = IEEE80211_S_INIT; + + /* Set device capabilities. */ + ic->ic_caps = + IEEE80211_C_WEP | /* WEP */ + IEEE80211_C_RSN | /* WPA/RSN */ + IEEE80211_C_SCANALL | /* device scans all channels at once */ + IEEE80211_C_SCANALLBAND | /* driver scans all bands at once */ + IEEE80211_C_MONITOR | /* monitor mode supported */ + IEEE80211_C_SHSLOT | /* short slot time supported */ + IEEE80211_C_SHPREAMBLE | /* short preamble supported */ + IEEE80211_C_PMGT; /* power saving supported */ + + /* Set supported rates. */ + ic->ic_sup_rates[IEEE80211_MODE_11B] = ieee80211_std_rateset_11b; + ic->ic_sup_rates[IEEE80211_MODE_11G] = ieee80211_std_rateset_11g; + if (sc->sc_flags & WPI_FLAG_HAS_5GHZ) { + ic->ic_sup_rates[IEEE80211_MODE_11A] = + ieee80211_std_rateset_11a; + } + + /* IBSS channel undefined for now. */ + ic->ic_ibss_chan = &ic->ic_channels[0]; + + ifp->if_softc = sc; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = wpi_ioctl; + ifp->if_start = wpi_start; + ifp->if_watchdog = wpi_watchdog; + memcpy(ifp->if_xname, DEVNAME(sc), IFNAMSIZ); + + if_attach(ifp); + ieee80211_ifattach(ifp); + ic->ic_node_alloc = wpi_node_alloc; + ic->ic_newassoc = wpi_newassoc; + ic->ic_updateedca = wpi_updateedca; + ic->ic_set_key = wpi_set_key; + ic->ic_delete_key = wpi_delete_key; + + /* Override 802.11 state transition machine. */ + sc->sc_newstate = ic->ic_newstate; + ic->ic_newstate = wpi_newstate; + ieee80211_media_init(ifp, wpi_media_change, ieee80211_media_status); + + sc->amrr.amrr_min_success_threshold = 1; + sc->amrr.amrr_max_success_threshold = 15; + +#if NBPFILTER > 0 + wpi_radiotap_attach(sc); +#endif + timeout_set(&sc->calib_to, wpi_calib_timeout, sc); + rw_init(&sc->sc_rwlock, "wpilock"); + task_set(&sc->init_task, wpi_init_task, sc); + return 0; + + /* Free allocated memory if something failed during attachment. */ +fail2: while (--i >= 0) + wpi_free_tx_ring(sc, &sc->txq[i]); + wpi_free_shared(sc); +fail1: wpi_free_fwmem(sc); + return -1; +} + +#if NBPFILTER > 0 +/* + * Attach the interface to 802.11 radiotap. + */ +void +wpi_radiotap_attach(struct wpi_softc *sc) +{ + bpfattach(&sc->sc_drvbpf, &sc->sc_ic.ic_if, DLT_IEEE802_11_RADIO, + sizeof (struct ieee80211_frame) + IEEE80211_RADIOTAP_HDRLEN); + + sc->sc_rxtap_len = sizeof sc->sc_rxtapu; + sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len); + sc->sc_rxtap.wr_ihdr.it_present = htole32(WPI_RX_RADIOTAP_PRESENT); + + sc->sc_txtap_len = sizeof sc->sc_txtapu; + sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len); + sc->sc_txtap.wt_ihdr.it_present = htole32(WPI_TX_RADIOTAP_PRESENT); +} +#endif + +#ifndef __FreeBSD_version +int +wpi_detach(struct device *self, int flags) +{ + struct wpi_softc *sc = (struct wpi_softc *)self; + struct ifnet *ifp = &sc->sc_ic.ic_if; + int qid; + + timeout_del(&sc->calib_to); + task_del(systq, &sc->init_task); + + /* Uninstall interrupt handler. */ + if (sc->sc_ih != NULL) + pci_intr_disestablish(sc->sc_pct, sc->sc_ih); + + /* Free DMA resources. */ + wpi_free_rx_ring(sc, &sc->rxq); + for (qid = 0; qid < WPI_NTXQUEUES; qid++) + wpi_free_tx_ring(sc, &sc->txq[qid]); + wpi_free_shared(sc); + wpi_free_fwmem(sc); + + bus_space_unmap(sc->sc_st, sc->sc_sh, sc->sc_sz); + + ieee80211_ifdetach(ifp); + if_detach(ifp); + + return 0; +} + +int +wpi_activate(struct device *self, int act) +{ + struct wpi_softc *sc = (struct wpi_softc *)self; + struct ifnet *ifp = &sc->sc_ic.ic_if; + + switch (act) { + case DVACT_SUSPEND: + if (ifp->if_flags & IFF_RUNNING) + wpi_stop(ifp, 0); + break; + case DVACT_WAKEUP: + wpi_wakeup(sc); + break; + } + + return 0; +} +#endif + +void +wpi_wakeup(struct wpi_softc *sc) +{ + pcireg_t reg; + + /* Clear device-specific "PCI retry timeout" register (41h). */ + reg = pci_conf_read(sc->sc_pct, sc->sc_pcitag, 0x40); + reg &= ~0xff00; + pci_conf_write(sc->sc_pct, sc->sc_pcitag, 0x40, reg); + + wpi_init_task(sc); +} + +void +wpi_init_task(void *arg1) +{ + struct wpi_softc *sc = arg1; + struct ifnet *ifp = &sc->sc_ic.ic_if; + int s; + + rw_enter_write(&sc->sc_rwlock); + s = splnet(); + + if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == IFF_UP) + wpi_init(ifp); + + splx(s); + rw_exit_write(&sc->sc_rwlock); +} + +int +wpi_nic_lock(struct wpi_softc *sc) +{ + int ntries; + + /* Request exclusive access to NIC. */ + WPI_SETBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_MAC_ACCESS_REQ); + + /* Spin until we actually get the lock. */ + for (ntries = 0; ntries < 1000; ntries++) { + if ((WPI_READ(sc, WPI_GP_CNTRL) & + (WPI_GP_CNTRL_MAC_ACCESS_ENA | WPI_GP_CNTRL_SLEEP)) == + WPI_GP_CNTRL_MAC_ACCESS_ENA) + return 0; + DELAY(10); + } + return ETIMEDOUT; +} + +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); +} + +#ifdef WPI_DEBUG + +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_write(struct wpi_softc *sc, uint32_t addr, uint32_t data) +{ + WPI_WRITE(sc, WPI_MEM_WADDR, addr); + WPI_BARRIER_WRITE(sc); + WPI_WRITE(sc, WPI_MEM_WDATA, data); +} + +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); +} + +#endif + +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; + + if ((error = wpi_nic_lock(sc)) != 0) + return error; + + for (; count > 0; count -= 2, addr++) { + WPI_WRITE(sc, WPI_EEPROM, addr << 2); + WPI_CLRBITS(sc, WPI_EEPROM, WPI_EEPROM_CMD); + + for (ntries = 0; ntries < 10; ntries++) { + val = WPI_READ(sc, WPI_EEPROM); + if (val & WPI_EEPROM_READ_VALID) + break; + DELAY(5); + } + if (ntries == 10) { + printf("%s: could not read EEPROM\n", + DEVNAME(sc)); + return ETIMEDOUT; + } + *out++ = val >> 16; + if (count > 1) + *out++ = val >> 24; + } + + wpi_nic_unlock(sc); + return 0; +} + +int +wpi_dma_contig_alloc(bus_dma_tag_t tag, struct wpi_dma_info *dma, void **kvap, + bus_size_t size, bus_size_t alignment) +{ + int nsegs, error; + + dma->tag = tag; + dma->size = size; + + error = bus_dmamap_create(tag, size, 1, size, 0, BUS_DMA_NOWAIT, + &dma->map); + if (error != 0) + goto fail; + + error = bus_dmamem_alloc(tag, size, alignment, 0, &dma->seg, 1, &nsegs, + BUS_DMA_NOWAIT | BUS_DMA_ZERO); + if (error != 0) + goto fail; + + error = bus_dmamem_map(tag, &dma->seg, 1, size, &dma->vaddr, + BUS_DMA_NOWAIT | BUS_DMA_COHERENT); + if (error != 0) + goto fail; + +#ifdef __FreeBSD_version + error = bus_dmamap_load(tag, dma->map, dma->vaddr, size, NULL, + BUS_DMA_NOWAIT); +#else + error = bus_dmamap_load_raw(tag, dma->map, &dma->seg, 1, size, + BUS_DMA_NOWAIT); +#endif + if (error != 0) + goto fail; + + bus_dmamap_sync(tag, dma->map, 0, size, BUS_DMASYNC_PREWRITE); + + dma->paddr = dma->map->dm_segs[0].ds_addr; + if (kvap != NULL) + *kvap = dma->vaddr; + + return 0; + +fail: wpi_dma_contig_free(dma); + return error; +} + +void +wpi_dma_contig_free(struct wpi_dma_info *dma) +{ + if (dma->map != NULL) { + if (dma->vaddr != NULL) { + bus_dmamap_sync(dma->tag, dma->map, 0, dma->size, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(dma->tag, dma->map); + bus_dmamem_unmap(dma->tag, dma->vaddr, dma->size); + bus_dmamem_free(dma->tag, &dma->seg, 1); + dma->vaddr = NULL; + } + bus_dmamap_destroy(dma->tag, dma->map); + dma->map = NULL; + } +} + +int +wpi_alloc_shared(struct wpi_softc *sc) +{ + /* Shared buffer must be aligned on a 4KB boundary. */ + return wpi_dma_contig_alloc(sc->sc_dmat, &sc->shared_dma, + (void **)&sc->shared, sizeof (struct wpi_shared), 4096); +} + +void +wpi_free_shared(struct wpi_softc *sc) +{ + wpi_dma_contig_free(&sc->shared_dma); +} + +int +wpi_alloc_fwmem(struct wpi_softc *sc) +{ + /* Allocate enough contiguous space to store text and data. */ + return wpi_dma_contig_alloc(sc->sc_dmat, &sc->fw_dma, NULL, + WPI_FW_TEXT_MAXSZ + WPI_FW_DATA_MAXSZ, 16); +} + +void +wpi_free_fwmem(struct wpi_softc *sc) +{ + wpi_dma_contig_free(&sc->fw_dma); +} + +int +wpi_alloc_rx_ring(struct wpi_softc *sc, struct wpi_rx_ring *ring) +{ + bus_size_t size; + int i, error; + + ring->cur = 0; + + /* Allocate RX descriptors (16KB aligned.) */ + size = WPI_RX_RING_COUNT * sizeof (uint32_t); + error = wpi_dma_contig_alloc(sc->sc_dmat, &ring->desc_dma, + (void **)&ring->desc, size, 16 * 1024); + if (error != 0) { + printf("%s: could not allocate RX ring DMA memory\n", + DEVNAME(sc)); + goto fail; + } + + /* + * Allocate and map RX buffers. + */ + for (i = 0; i < WPI_RX_RING_COUNT; i++) { + struct wpi_rx_data *data = &ring->data[i]; + + error = bus_dmamap_create(sc->sc_dmat, WPI_RBUF_SIZE, 1, + WPI_RBUF_SIZE, 0, BUS_DMA_NOWAIT, &data->map); + if (error != 0) { + printf("%s: could not create RX buf DMA map\n", + DEVNAME(sc)); + goto fail; + } + + data->m = MCLGETL(NULL, M_DONTWAIT, WPI_RBUF_SIZE); + if (data->m == NULL) { + printf("%s: could not allocate RX mbuf\n", + DEVNAME(sc)); + error = ENOBUFS; + goto fail; + } + + error = bus_dmamap_load(sc->sc_dmat, data->map, + mtod(data->m, void *), WPI_RBUF_SIZE, NULL, + BUS_DMA_NOWAIT | BUS_DMA_READ); + if (error != 0) { + printf("%s: can't map mbuf (error %d)\n", + DEVNAME(sc), error); + goto fail; + } + + /* Set physical address of RX buffer. */ + ring->desc[i] = htole32(data->map->dm_segs[0].ds_addr); + } + + bus_dmamap_sync(sc->sc_dmat, ring->desc_dma.map, 0, size, + BUS_DMASYNC_PREWRITE); + + return 0; + +fail: wpi_free_rx_ring(sc, ring); + return error; +} + +void +wpi_reset_rx_ring(struct wpi_softc *sc, struct wpi_rx_ring *ring) +{ + int ntries; + + if (wpi_nic_lock(sc) == 0) { + WPI_WRITE(sc, WPI_FH_RX_CONFIG, 0); + for (ntries = 0; ntries < 100; ntries++) { + if (WPI_READ(sc, WPI_FH_RX_STATUS) & + WPI_FH_RX_STATUS_IDLE) + break; + DELAY(10); + } + wpi_nic_unlock(sc); + } + ring->cur = 0; +} + +void +wpi_free_rx_ring(struct wpi_softc *sc, struct wpi_rx_ring *ring) +{ + int i; + + wpi_dma_contig_free(&ring->desc_dma); + + for (i = 0; i < WPI_RX_RING_COUNT; i++) { + struct wpi_rx_data *data = &ring->data[i]; + + if (data->m != NULL) { + bus_dmamap_sync(sc->sc_dmat, data->map, 0, + data->map->dm_mapsize, BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(sc->sc_dmat, data->map); + m_freem(data->m); + } + if (data->map != NULL) + bus_dmamap_destroy(sc->sc_dmat, data->map); + } +} + +int +wpi_alloc_tx_ring(struct wpi_softc *sc, struct wpi_tx_ring *ring, int qid) +{ + bus_addr_t paddr; + bus_size_t size; + int i, error; + + ring->qid = qid; + ring->queued = 0; + ring->cur = 0; + + /* Allocate TX descriptors (16KB aligned.) */ + size = WPI_TX_RING_COUNT * sizeof (struct wpi_tx_desc); + error = wpi_dma_contig_alloc(sc->sc_dmat, &ring->desc_dma, + (void **)&ring->desc, size, 16 * 1024); + if (error != 0) { + printf("%s: could not allocate TX ring DMA memory\n", + DEVNAME(sc)); + goto fail; + } + + /* Update shared area with ring physical address. */ + sc->shared->txbase[qid] = htole32(ring->desc_dma.paddr); + bus_dmamap_sync(sc->sc_dmat, sc->shared_dma.map, 0, + sizeof (struct wpi_shared), BUS_DMASYNC_PREWRITE); + + /* + * 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->sc_dmat, &ring->cmd_dma, + (void **)&ring->cmd, size, 4); + if (error != 0) { + printf("%s: could not allocate TX cmd DMA memory\n", + DEVNAME(sc)); + goto fail; + } + + paddr = ring->cmd_dma.paddr; + for (i = 0; i < WPI_TX_RING_COUNT; i++) { + struct wpi_tx_data *data = &ring->data[i]; + + data->cmd_paddr = paddr; + paddr += sizeof (struct wpi_tx_cmd); + + error = bus_dmamap_create(sc->sc_dmat, MCLBYTES, + WPI_MAX_SCATTER - 1, MCLBYTES, 0, BUS_DMA_NOWAIT, + &data->map); + if (error != 0) { + printf("%s: could not create TX buf DMA map\n", + DEVNAME(sc)); + goto fail; + } + } + return 0; + +fail: wpi_free_tx_ring(sc, ring); + return error; +} + +void +wpi_reset_tx_ring(struct wpi_softc *sc, struct wpi_tx_ring *ring) +{ + int 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(sc->sc_dmat, data->map, 0, + data->map->dm_mapsize, BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_dmat, data->map); + m_freem(data->m); + data->m = NULL; + } + } + /* Clear TX descriptors. */ + memset(ring->desc, 0, ring->desc_dma.size); + sc->qfullmsk &= ~(1 << ring->qid); + ring->queued = 0; + ring->cur = 0; +} + +void +wpi_free_tx_ring(struct wpi_softc *sc, struct wpi_tx_ring *ring) +{ + int i; + + wpi_dma_contig_free(&ring->desc_dma); + wpi_dma_contig_free(&ring->cmd_dma); + + for (i = 0; i < WPI_TX_RING_COUNT; i++) { + struct wpi_tx_data *data = &ring->data[i]; + + if (data->m != NULL) { + bus_dmamap_sync(sc->sc_dmat, data->map, 0, + data->map->dm_mapsize, BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_dmat, data->map); + m_freem(data->m); + } + if (data->map != NULL) + bus_dmamap_destroy(sc->sc_dmat, data->map); + } +} + +int +wpi_read_eeprom(struct wpi_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + char domain[4]; + int i; + + if ((WPI_READ(sc, WPI_EEPROM_GP) & 0x6) == 0) { + printf("%s: bad EEPROM signature\n", DEVNAME(sc)); + return EIO; + } + /* Clear HW ownership of EEPROM. */ + WPI_CLRBITS(sc, WPI_EEPROM_GP, WPI_EEPROM_GP_IF_OWNER); + + 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); + + DPRINTF(("cap=%x rev=%x type=%x\n", sc->cap, letoh16(sc->rev), + sc->type)); + + /* Read and print regulatory domain (4 ASCII characters.) */ + wpi_read_prom_data(sc, WPI_EEPROM_DOMAIN, domain, 4); + printf(", %.4s", domain); + + /* Read and print MAC address. */ + wpi_read_prom_data(sc, WPI_EEPROM_MAC, ic->ic_myaddr, 6); + printf(", address %s\n", ether_sprintf(ic->ic_myaddr)); + + /* Read the list of authorized channels. */ + for (i = 0; i < WPI_CHAN_BANDS_COUNT; i++) + wpi_read_eeprom_channels(sc, i); + + /* Read the list of TX power groups. */ + for (i = 0; i < WPI_POWER_GROUPS_COUNT; i++) + wpi_read_eeprom_group(sc, i); + + return 0; +} + +void +wpi_read_eeprom_channels(struct wpi_softc *sc, int n) +{ + struct ieee80211com *ic = &sc->sc_ic; + const struct wpi_chan_band *band = &wpi_bands[n]; + struct wpi_eeprom_chan channels[WPI_MAX_CHAN_PER_BAND]; + int chan, i; + + wpi_read_prom_data(sc, band->addr, channels, + band->nchan * sizeof (struct wpi_eeprom_chan)); + + for (i = 0; i < band->nchan; i++) { + if (!(channels[i].flags & WPI_EEPROM_CHAN_VALID)) + continue; + + chan = band->chan[i]; + + if (n == 0) { /* 2GHz band */ + ic->ic_channels[chan].ic_freq = + ieee80211_ieee2mhz(chan, IEEE80211_CHAN_2GHZ); + ic->ic_channels[chan].ic_flags = + IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM | + IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ; + + } else { /* 5GHz band */ + /* + * Some adapters support channels 7, 8, 11 and 12 + * both in the 2GHz and 4.9GHz bands. + * Because of limitations in our net80211 layer, + * we don't support them in the 4.9GHz band. + */ + if (chan <= 14) + continue; + + ic->ic_channels[chan].ic_freq = + ieee80211_ieee2mhz(chan, IEEE80211_CHAN_5GHZ); + ic->ic_channels[chan].ic_flags = IEEE80211_CHAN_A; + /* We have at least one valid 5GHz channel. */ + sc->sc_flags |= WPI_FLAG_HAS_5GHZ; + } + + /* Is active scan allowed on this channel? */ + if (!(channels[i].flags & WPI_EEPROM_CHAN_ACTIVE)) { + ic->ic_channels[chan].ic_flags |= + IEEE80211_CHAN_PASSIVE; + } + + /* Save maximum allowed TX power for this channel. */ + sc->maxpwr[chan] = channels[i].maxpwr; + + DPRINTF(("adding chan %d flags=0x%x maxpwr=%d\n", + chan, channels[i].flags, sc->maxpwr[chan])); + } +} + +void +wpi_read_eeprom_group(struct wpi_softc *sc, int n) +{ + struct wpi_power_group *group = &sc->groups[n]; + struct wpi_eeprom_group rgroup; + int i; + + wpi_read_prom_data(sc, WPI_EEPROM_POWER_GRP + n * 32, &rgroup, + sizeof rgroup); + + /* Save TX power group information. */ + group->chan = rgroup.chan; + group->maxpwr = rgroup.maxpwr; + /* Retrieve temperature at which the samples were taken. */ + group->temp = (int16_t)letoh16(rgroup.temp); + + DPRINTF(("power group %d: chan=%d maxpwr=%d temp=%d\n", n, + group->chan, group->maxpwr, group->temp)); + + for (i = 0; i < WPI_SAMPLES_COUNT; i++) { + group->samples[i].index = rgroup.samples[i].index; + group->samples[i].power = rgroup.samples[i].power; + + DPRINTF(("\tsample %d: index=%d power=%d\n", i, + group->samples[i].index, group->samples[i].power)); + } +} + +struct ieee80211_node * +wpi_node_alloc(struct ieee80211com *ic) +{ + return malloc(sizeof (struct wpi_node), M_DEVBUF, M_NOWAIT | M_ZERO); +} + +void +wpi_newassoc(struct ieee80211com *ic, struct ieee80211_node *ni, int isnew) +{ + struct wpi_softc *sc = ic->ic_if.if_softc; + struct wpi_node *wn = (void *)ni; + uint8_t rate; + int ridx, i; + + ieee80211_amrr_node_init(&sc->amrr, &wn->amn); + /* Start at lowest available bit-rate, AMRR will raise. */ + ni->ni_txrate = 0; + + for (i = 0; i < ni->ni_rates.rs_nrates; i++) { + rate = ni->ni_rates.rs_rates[i] & IEEE80211_RATE_VAL; + /* Map 802.11 rate to HW rate index. */ + for (ridx = 0; ridx <= WPI_RIDX_MAX; ridx++) + if (wpi_rates[ridx].rate == rate) + break; + wn->ridx[i] = ridx; + } +} + +int +wpi_media_change(struct ifnet *ifp) +{ + struct wpi_softc *sc = ifp->if_softc; + struct ieee80211com *ic = &sc->sc_ic; + uint8_t rate, ridx; + int error; + + error = ieee80211_media_change(ifp); + if (error != ENETRESET) + return error; + + if (ic->ic_fixed_rate != -1) { + rate = ic->ic_sup_rates[ic->ic_curmode]. + rs_rates[ic->ic_fixed_rate] & IEEE80211_RATE_VAL; + /* Map 802.11 rate to HW rate index. */ + for (ridx = 0; ridx <= WPI_RIDX_MAX; ridx++) + if (wpi_rates[ridx].rate == rate) + break; + sc->fixed_ridx = ridx; + } + + if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == + (IFF_UP | IFF_RUNNING)) { + wpi_stop(ifp, 0); + error = wpi_init(ifp); + } + return error; +} + +int +wpi_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) +{ + struct ifnet *ifp = &ic->ic_if; + struct wpi_softc *sc = ifp->if_softc; + int error; + + timeout_del(&sc->calib_to); + + switch (nstate) { + case IEEE80211_S_SCAN: + /* Make the link LED blink while we're scanning. */ + wpi_set_led(sc, WPI_LED_LINK, 20, 2); + + if ((error = wpi_scan(sc, IEEE80211_CHAN_2GHZ)) != 0) { + printf("%s: could not initiate scan\n", + DEVNAME(sc)); + return error; + } + if (ifp->if_flags & IFF_DEBUG) + printf("%s: %s -> %s\n", ifp->if_xname, + ieee80211_state_name[ic->ic_state], + ieee80211_state_name[nstate]); + ieee80211_set_link_state(ic, LINK_STATE_DOWN); + ieee80211_node_cleanup(ic, ic->ic_bss); + ic->ic_state = nstate; + return 0; + + case IEEE80211_S_ASSOC: + if (ic->ic_state != IEEE80211_S_RUN) + break; + /* FALLTHROUGH */ + case IEEE80211_S_AUTH: + /* Reset state to handle reassociations correctly. */ + sc->rxon.associd = 0; + sc->rxon.filter &= ~htole32(WPI_FILTER_BSS); + + if ((error = wpi_auth(sc)) != 0) { + printf("%s: could not move to auth state\n", + DEVNAME(sc)); + return error; + } + break; + + case IEEE80211_S_RUN: + if ((error = wpi_run(sc)) != 0) { + printf("%s: could not move to run state\n", + DEVNAME(sc)); + return error; + } + break; + + case IEEE80211_S_INIT: + break; + } + + return sc->sc_newstate(ic, nstate, arg); +} + +void +wpi_iter_func(void *arg, struct ieee80211_node *ni) +{ + struct wpi_softc *sc = arg; + struct wpi_node *wn = (struct wpi_node *)ni; + + ieee80211_amrr_choose(&sc->amrr, ni, &wn->amn); +} + +void +wpi_calib_timeout(void *arg) +{ + struct wpi_softc *sc = arg; + struct ieee80211com *ic = &sc->sc_ic; + int s; + + s = splnet(); + /* Automatic rate control triggered every 500ms. */ + if (ic->ic_fixed_rate == -1) { + if (ic->ic_opmode == IEEE80211_M_STA) + wpi_iter_func(sc, ic->ic_bss); + else + ieee80211_iterate_nodes(ic, wpi_iter_func, sc); + } + + /* Force automatic TX power calibration every 60 secs. */ + if (++sc->calib_cnt >= 120) { + wpi_power_calibration(sc); + sc->calib_cnt = 0; + } + splx(s); + + /* Automatic rate control triggered every 500ms. */ + timeout_add_msec(&sc->calib_to, 500); +} + +int +wpi_ccmp_decap(struct wpi_softc *sc, struct mbuf *m, struct ieee80211_key *k) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_frame *wh; + uint64_t pn, *prsc; + uint8_t *ivp; + uint8_t tid; + int hdrlen; + + wh = mtod(m, struct ieee80211_frame *); + hdrlen = ieee80211_get_hdrlen(wh); + ivp = (uint8_t *)wh + hdrlen; + + /* Check that ExtIV bit is set. */ + if (!(ivp[3] & IEEE80211_WEP_EXTIV)) { + DPRINTF(("CCMP decap ExtIV not set\n")); + return 1; + } + tid = ieee80211_has_qos(wh) ? + ieee80211_get_qos(wh) & IEEE80211_QOS_TID : 0; + prsc = &k->k_rsc[tid]; + + /* Extract the 48-bit PN from the CCMP header. */ + pn = (uint64_t)ivp[0] | + (uint64_t)ivp[1] << 8 | + (uint64_t)ivp[4] << 16 | + (uint64_t)ivp[5] << 24 | + (uint64_t)ivp[6] << 32 | + (uint64_t)ivp[7] << 40; + if (pn <= *prsc) { + DPRINTF(("CCMP replayed\n")); + ic->ic_stats.is_ccmp_replays++; + return 1; + } + /* Last seen packet number is updated in ieee80211_inputm(). */ + + /* Strip MIC. IV will be stripped by ieee80211_inputm(). */ + m_adj(m, -IEEE80211_CCMP_MICLEN); + return 0; +} + +void +wpi_rx_done(struct wpi_softc *sc, struct wpi_rx_desc *desc, + struct wpi_rx_data *data, struct mbuf_list *ml) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; + 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_rxinfo rxi; + struct ieee80211_node *ni; + struct mbuf *m, *m1; + uint32_t flags; + int error; + + bus_dmamap_sync(sc->sc_dmat, data->map, 0, WPI_RBUF_SIZE, + BUS_DMASYNC_POSTREAD); + stat = (struct wpi_rx_stat *)(desc + 1); + + if (stat->len > WPI_STAT_MAXLEN) { + printf("%s: invalid RX statistic header\n", + DEVNAME(sc)); + ifp->if_ierrors++; + return; + } + head = (struct wpi_rx_head *)((caddr_t)(stat + 1) + stat->len); + tail = (struct wpi_rx_tail *)((caddr_t)(head + 1) + letoh16(head->len)); + flags = letoh32(tail->flags); + + /* Discard frames with a bad FCS early. */ + if ((flags & WPI_RX_NOERROR) != WPI_RX_NOERROR) { + DPRINTFN(2, ("rx tail flags error %x\n", flags)); + ifp->if_ierrors++; + return; + } + /* Discard frames that are too short. */ + if (letoh16(head->len) < sizeof (*wh)) { + DPRINTF(("frame too short: %d\n", letoh16(head->len))); + ic->ic_stats.is_rx_tooshort++; + ifp->if_ierrors++; + return; + } + + m1 = MCLGETL(NULL, M_DONTWAIT, WPI_RBUF_SIZE); + if (m1 == NULL) { + ic->ic_stats.is_rx_nombuf++; + ifp->if_ierrors++; + return; + } + bus_dmamap_unload(sc->sc_dmat, data->map); + + error = bus_dmamap_load(sc->sc_dmat, data->map, mtod(m1, void *), + WPI_RBUF_SIZE, NULL, BUS_DMA_NOWAIT | BUS_DMA_READ); + if (error != 0) { + m_freem(m1); + + /* Try to reload the old mbuf. */ + error = bus_dmamap_load(sc->sc_dmat, data->map, + mtod(data->m, void *), WPI_RBUF_SIZE, NULL, + BUS_DMA_NOWAIT | BUS_DMA_READ); + if (error != 0) { + panic("%s: could not load old RX mbuf", + DEVNAME(sc)); + } + /* Physical address may have changed. */ + ring->desc[ring->cur] = htole32(data->map->dm_segs[0].ds_addr); + bus_dmamap_sync(sc->sc_dmat, ring->desc_dma.map, + ring->cur * sizeof (uint32_t), sizeof (uint32_t), + BUS_DMASYNC_PREWRITE); + ifp->if_ierrors++; + return; + } + + m = data->m; + data->m = m1; + /* Update RX descriptor. */ + ring->desc[ring->cur] = htole32(data->map->dm_segs[0].ds_addr); + bus_dmamap_sync(sc->sc_dmat, ring->desc_dma.map, + ring->cur * sizeof (uint32_t), sizeof (uint32_t), + BUS_DMASYNC_PREWRITE); + + /* Finalize mbuf. */ + m->m_data = (caddr_t)(head + 1); + m->m_pkthdr.len = m->m_len = letoh16(head->len); + + /* Grab a reference to the source node. */ + wh = mtod(m, struct ieee80211_frame *); + ni = ieee80211_find_rxnode(ic, wh); + + memset(&rxi, 0, sizeof(rxi)); + if ((wh->i_fc[1] & IEEE80211_FC1_PROTECTED) && + !IEEE80211_IS_MULTICAST(wh->i_addr1) && + (ni->ni_flags & IEEE80211_NODE_RXPROT) && + ni->ni_pairwise_key.k_cipher == IEEE80211_CIPHER_CCMP) { + if ((flags & WPI_RX_CIPHER_MASK) != WPI_RX_CIPHER_CCMP) { + ic->ic_stats.is_ccmp_dec_errs++; + ifp->if_ierrors++; + m_freem(m); + ieee80211_release_node(ic, ni); + return; + } + /* Check whether decryption was successful or not. */ + if ((flags & WPI_RX_DECRYPT_MASK) != WPI_RX_DECRYPT_OK) { + DPRINTF(("CCMP decryption failed 0x%x\n", flags)); + ic->ic_stats.is_ccmp_dec_errs++; + ifp->if_ierrors++; + m_freem(m); + ieee80211_release_node(ic, ni); + return; + } + if (wpi_ccmp_decap(sc, m, &ni->ni_pairwise_key) != 0) { + ifp->if_ierrors++; + m_freem(m); + ieee80211_release_node(ic, ni); + return; + } + rxi.rxi_flags |= IEEE80211_RXI_HWDEC; + } + +#if NBPFILTER > 0 + if (sc->sc_drvbpf != NULL) { + struct wpi_rx_radiotap_header *tap = &sc->sc_rxtap; + + tap->wr_flags = 0; + if (letoh16(head->flags) & 0x4) + tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; + tap->wr_chan_freq = + htole16(ic->ic_channels[head->chan].ic_freq); + tap->wr_chan_flags = + htole16(ic->ic_channels[head->chan].ic_flags); + tap->wr_dbm_antsignal = (int8_t)(stat->rssi - WPI_RSSI_OFFSET); + tap->wr_dbm_antnoise = (int8_t)letoh16(stat->noise); + tap->wr_tsft = tail->tstamp; + tap->wr_antenna = (letoh16(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; + } + + bpf_mtap_hdr(sc->sc_drvbpf, tap, sc->sc_rxtap_len, + m, BPF_DIRECTION_IN); + } +#endif + + /* Send the frame to the 802.11 layer. */ + rxi.rxi_rssi = stat->rssi; + ieee80211_inputm(ifp, m, ni, &rxi, ml); + + /* Node is no longer needed. */ + ieee80211_release_node(ic, ni); +} + +void +wpi_tx_done(struct wpi_softc *sc, struct wpi_rx_desc *desc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; + struct wpi_tx_ring *ring = &sc->txq[desc->qid & 0x3]; + struct wpi_tx_data *data = &ring->data[desc->idx]; + struct wpi_tx_stat *stat = (struct wpi_tx_stat *)(desc + 1); + struct wpi_node *wn = (struct wpi_node *)data->ni; + + /* Update rate control statistics. */ + wn->amn.amn_txcnt++; + if (stat->retrycnt > 0) + wn->amn.amn_retrycnt++; + + if ((letoh32(stat->status) & 0xff) != 1) + ifp->if_oerrors++; + + /* Unmap and free mbuf. */ + bus_dmamap_sync(sc->sc_dmat, data->map, 0, data->map->dm_mapsize, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_dmat, data->map); + m_freem(data->m); + data->m = NULL; + ieee80211_release_node(ic, data->ni); + data->ni = NULL; + + sc->sc_tx_timer = 0; + if (--ring->queued < WPI_TX_RING_LOMARK) { + sc->qfullmsk &= ~(1 << ring->qid); + if (sc->qfullmsk == 0 && ifq_is_oactive(&ifp->if_snd)) { + ifq_clr_oactive(&ifp->if_snd); + (*ifp->if_start)(ifp); + } + } +} + +void +wpi_cmd_done(struct wpi_softc *sc, struct wpi_rx_desc *desc) +{ + struct wpi_tx_ring *ring = &sc->txq[4]; + struct wpi_tx_data *data; + + if ((desc->qid & 7) != 4) + return; /* Not a command ack. */ + + data = &ring->data[desc->idx]; + + /* If the command was mapped in an mbuf, free it. */ + if (data->m != NULL) { + bus_dmamap_sync(sc->sc_dmat, data->map, 0, + data->map->dm_mapsize, BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_dmat, data->map); + m_freem(data->m); + data->m = NULL; + } + wakeup(&ring->cmd[desc->idx]); +} + +void +wpi_notif_intr(struct wpi_softc *sc) +{ + struct mbuf_list ml = MBUF_LIST_INITIALIZER(); + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; + uint32_t hw; + + bus_dmamap_sync(sc->sc_dmat, sc->shared_dma.map, 0, + sizeof (struct wpi_shared), BUS_DMASYNC_POSTREAD); + + hw = letoh32(sc->shared->next); + while (sc->rxq.cur != hw) { + struct wpi_rx_data *data = &sc->rxq.data[sc->rxq.cur]; + struct wpi_rx_desc *desc; + + bus_dmamap_sync(sc->sc_dmat, data->map, 0, sizeof (*desc), + BUS_DMASYNC_POSTREAD); + desc = mtod(data->m, struct wpi_rx_desc *); + + DPRINTFN(4, ("rx notification qid=%x idx=%d flags=%x type=%d " + "len=%d\n", desc->qid, desc->idx, desc->flags, desc->type, + letoh32(desc->len))); + + if (!(desc->qid & 0x80)) /* Reply to a command. */ + wpi_cmd_done(sc, desc); + + switch (desc->type) { + case WPI_RX_DONE: + /* An 802.11 frame has been received. */ + wpi_rx_done(sc, desc, data, &ml); + break; + + case WPI_TX_DONE: + /* An 802.11 frame has been transmitted. */ + wpi_tx_done(sc, desc); + break; + + case WPI_UC_READY: + { + struct wpi_ucode_info *uc = + (struct wpi_ucode_info *)(desc + 1); + + /* The microcontroller is ready. */ + bus_dmamap_sync(sc->sc_dmat, data->map, sizeof (*desc), + sizeof (*uc), BUS_DMASYNC_POSTREAD); + DPRINTF(("microcode alive notification version %x " + "alive %x\n", letoh32(uc->version), + letoh32(uc->valid))); + + if (letoh32(uc->valid) != 1) { + printf("%s: microcontroller initialization " + "failed\n", DEVNAME(sc)); + } + if (uc->subtype != WPI_UCODE_INIT) { + /* Save the address of the error log. */ + sc->errptr = letoh32(uc->errptr); + } + break; + } + case WPI_STATE_CHANGED: + { + uint32_t *status = (uint32_t *)(desc + 1); + + /* Enabled/disabled notification. */ + bus_dmamap_sync(sc->sc_dmat, data->map, sizeof (*desc), + sizeof (*status), BUS_DMASYNC_POSTREAD); + DPRINTF(("state changed to %x\n", letoh32(*status))); + + if (letoh32(*status) & 1) { + /* The radio button has to be pushed. */ + printf("%s: Radio transmitter is off\n", + DEVNAME(sc)); + /* Turn the interface down. */ + wpi_stop(ifp, 1); + return; /* No further processing. */ + } + break; + } + case WPI_START_SCAN: + { + struct wpi_start_scan *scan = + (struct wpi_start_scan *)(desc + 1); + + bus_dmamap_sync(sc->sc_dmat, data->map, sizeof (*desc), + sizeof (*scan), BUS_DMASYNC_POSTREAD); + DPRINTFN(2, ("scanning channel %d status %x\n", + scan->chan, letoh32(scan->status))); + + /* Fix current channel. */ + ic->ic_bss->ni_chan = &ic->ic_channels[scan->chan]; + break; + } + case WPI_STOP_SCAN: + { + struct wpi_stop_scan *scan = + (struct wpi_stop_scan *)(desc + 1); + + bus_dmamap_sync(sc->sc_dmat, data->map, sizeof (*desc), + sizeof (*scan), BUS_DMASYNC_POSTREAD); + DPRINTF(("scan finished nchan=%d status=%d chan=%d\n", + scan->nchan, scan->status, scan->chan)); + + if (scan->status == 1 && scan->chan <= 14 && + (sc->sc_flags & WPI_FLAG_HAS_5GHZ)) { + /* + * We just finished scanning 2GHz channels, + * start scanning 5GHz ones. + */ + if (wpi_scan(sc, IEEE80211_CHAN_5GHZ) == 0) + break; + } + ieee80211_end_scan(ifp); + break; + } + } + + sc->rxq.cur = (sc->rxq.cur + 1) % WPI_RX_RING_COUNT; + } + if_input(&ic->ic_if, &ml); + + /* Tell the firmware what we have processed. */ + hw = (hw == 0) ? WPI_RX_RING_COUNT - 1 : hw - 1; + WPI_WRITE(sc, WPI_FH_RX_WPTR, hw & ~7); +} + +#ifdef WPI_DEBUG +/* + * 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. + */ +void +wpi_fatal_intr(struct wpi_softc *sc) +{ +#define N(a) (sizeof (a) / sizeof ((a)[0])) + struct wpi_fwdump dump; + uint32_t i, offset, count; + + /* Check that the error log address is valid. */ + if (sc->errptr < WPI_FW_DATA_BASE || + sc->errptr + sizeof (dump) > + WPI_FW_DATA_BASE + WPI_FW_DATA_MAXSZ) { + printf("%s: bad firmware error log address 0x%08x\n", + DEVNAME(sc), sc->errptr); + return; + } + + if (wpi_nic_lock(sc) != 0) { + printf("%s: could not read firmware error log\n", + DEVNAME(sc)); + 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", + DEVNAME(sc), 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 < N(wpi_fw_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 < 6; 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); + printf(" 802.11 state %d\n", sc->sc_ic.ic_state); +#undef N +} +#endif + +int +wpi_intr(void *arg) +{ + struct wpi_softc *sc = arg; + struct ifnet *ifp = &sc->sc_ic.ic_if; + uint32_t r1, r2; + + /* Disable interrupts. */ + WPI_WRITE(sc, WPI_MASK, 0); + + r1 = WPI_READ(sc, WPI_INT); + r2 = WPI_READ(sc, WPI_FH_INT); + + if (r1 == 0 && r2 == 0) { + if (ifp->if_flags & IFF_UP) + WPI_WRITE(sc, WPI_MASK, WPI_INT_MASK); + return 0; /* Interrupt not for us. */ + } + if (r1 == 0xffffffff || (r1 & 0xfffffff0) == 0xa5a5a5a0) + return 0; /* Hardware gone! */ + + /* Acknowledge interrupts. */ + WPI_WRITE(sc, WPI_INT, r1); + WPI_WRITE(sc, WPI_FH_INT, r2); + + if (r1 & (WPI_INT_SW_ERR | WPI_INT_HW_ERR)) { + printf("%s: fatal firmware error\n", DEVNAME(sc)); + /* Dump firmware error log and stop. */ +#ifdef WPI_DEBUG + wpi_fatal_intr(sc); +#endif + wpi_stop(ifp, 1); + task_add(systq, &sc->init_task); + return 1; + } + if ((r1 & (WPI_INT_FH_RX | WPI_INT_SW_RX)) || + (r2 & WPI_FH_INT_RX)) + wpi_notif_intr(sc); + + if (r1 & WPI_INT_ALIVE) + wakeup(sc); /* Firmware is alive. */ + + /* Re-enable interrupts. */ + if (ifp->if_flags & IFF_UP) + WPI_WRITE(sc, WPI_MASK, WPI_INT_MASK); + + return 1; +} + +int +wpi_tx(struct wpi_softc *sc, struct mbuf *m, struct ieee80211_node *ni) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct wpi_node *wn = (void *)ni; + struct wpi_tx_ring *ring; + struct wpi_tx_desc *desc; + struct wpi_tx_data *data; + struct wpi_tx_cmd *cmd; + struct wpi_cmd_data *tx; + const struct wpi_rate *rinfo; + struct ieee80211_frame *wh; + struct ieee80211_key *k = NULL; + enum ieee80211_edca_ac ac; + uint32_t flags; + uint16_t qos; + u_int hdrlen; + uint8_t *ivp, tid, ridx, type; + int i, totlen, hasqos, error; + + wh = mtod(m, struct ieee80211_frame *); + hdrlen = ieee80211_get_hdrlen(wh); + type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + + /* Select EDCA Access Category and TX ring for this frame. */ + if ((hasqos = ieee80211_has_qos(wh))) { + qos = ieee80211_get_qos(wh); + tid = qos & IEEE80211_QOS_TID; + ac = ieee80211_up_to_ac(ic, tid); + } else { + tid = 0; + ac = EDCA_AC_BE; + } + + ring = &sc->txq[ac]; + desc = &ring->desc[ring->cur]; + data = &ring->data[ring->cur]; + + /* Choose a TX rate index. */ + if (IEEE80211_IS_MULTICAST(wh->i_addr1) || + type != IEEE80211_FC0_TYPE_DATA) { + ridx = (ic->ic_curmode == IEEE80211_MODE_11A) ? + WPI_RIDX_OFDM6 : WPI_RIDX_CCK1; + } else if (ic->ic_fixed_rate != -1) { + ridx = sc->fixed_ridx; + } else + ridx = wn->ridx[ni->ni_txrate]; + rinfo = &wpi_rates[ridx]; + +#if NBPFILTER > 0 + if (sc->sc_drvbpf != NULL) { + struct wpi_tx_radiotap_header *tap = &sc->sc_txtap; + + tap->wt_flags = 0; + tap->wt_chan_freq = htole16(ni->ni_chan->ic_freq); + tap->wt_chan_flags = htole16(ni->ni_chan->ic_flags); + tap->wt_rate = rinfo->rate; + if ((ic->ic_flags & IEEE80211_F_WEPON) && + (wh->i_fc[1] & IEEE80211_FC1_PROTECTED)) + tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP; + + bpf_mtap_hdr(sc->sc_drvbpf, tap, sc->sc_txtap_len, + m, BPF_DIRECTION_OUT); + } +#endif + + totlen = m->m_pkthdr.len; + + /* Encrypt the frame if need be. */ + if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { + /* Retrieve key for TX. */ + k = ieee80211_get_txkey(ic, wh, ni); + if (k->k_cipher != IEEE80211_CIPHER_CCMP) { + /* Do software encryption. */ + if ((m = ieee80211_encrypt(ic, m, k)) == NULL) + return ENOBUFS; + /* 802.11 header may have moved. */ + wh = mtod(m, struct ieee80211_frame *); + totlen = m->m_pkthdr.len; + + } else /* HW appends CCMP MIC. */ + totlen += IEEE80211_CCMP_HDRLEN; + } + + /* Prepare TX firmware command. */ + cmd = &ring->cmd[ring->cur]; + cmd->code = WPI_CMD_TX_DATA; + cmd->flags = 0; + cmd->qid = ring->qid; + cmd->idx = ring->cur; + + tx = (struct wpi_cmd_data *)cmd->data; + /* NB: No need to clear tx, all fields are reinitialized here. */ + + flags = 0; + if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { + /* Unicast frame, check if an ACK is expected. */ + if (!hasqos || (qos & IEEE80211_QOS_ACK_POLICY_MASK) != + IEEE80211_QOS_ACK_POLICY_NOACK) + flags |= WPI_TX_NEED_ACK; + } + + /* Check if frame must be protected using RTS/CTS or CTS-to-self. */ + if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { + /* NB: Group frames are sent using CCK in 802.11b/g. */ + if (totlen + IEEE80211_CRC_LEN > ic->ic_rtsthreshold) { + flags |= WPI_TX_NEED_RTS | WPI_TX_FULL_TXOP; + } else if ((ic->ic_flags & IEEE80211_F_USEPROT) && + ridx <= WPI_RIDX_OFDM54) { + if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) + flags |= WPI_TX_NEED_CTS | WPI_TX_FULL_TXOP; + else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) + flags |= WPI_TX_NEED_RTS | WPI_TX_FULL_TXOP; + } + } + + if (IEEE80211_IS_MULTICAST(wh->i_addr1) || + type != IEEE80211_FC0_TYPE_DATA) + tx->id = WPI_ID_BROADCAST; + else + tx->id = wn->id; + + if (type == IEEE80211_FC0_TYPE_MGT) { + uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; + +#ifndef IEEE80211_STA_ONLY + /* Tell HW to set timestamp in probe responses. */ + if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP) + flags |= WPI_TX_INSERT_TSTAMP; +#endif + if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ || + subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) + tx->timeout = htole16(3); + else + tx->timeout = htole16(2); + } else + tx->timeout = htole16(0); + + tx->len = htole16(totlen); + tx->tid = tid; + tx->rts_ntries = 7; + tx->data_ntries = 15; + tx->ofdm_mask = 0xff; + tx->cck_mask = 0x0f; + tx->lifetime = htole32(WPI_LIFETIME_INFINITE); + tx->plcp = rinfo->plcp; + + /* Copy 802.11 header in TX command. */ + memcpy((uint8_t *)(tx + 1), wh, hdrlen); + + if (k != NULL && k->k_cipher == IEEE80211_CIPHER_CCMP) { + /* Trim 802.11 header and prepend CCMP IV. */ + m_adj(m, hdrlen - IEEE80211_CCMP_HDRLEN); + ivp = mtod(m, uint8_t *); + k->k_tsc++; + ivp[0] = k->k_tsc; + ivp[1] = k->k_tsc >> 8; + ivp[2] = 0; + ivp[3] = k->k_id << 6 | IEEE80211_WEP_EXTIV; + ivp[4] = k->k_tsc >> 16; + ivp[5] = k->k_tsc >> 24; + ivp[6] = k->k_tsc >> 32; + ivp[7] = k->k_tsc >> 40; + + tx->security = WPI_CIPHER_CCMP; + memcpy(tx->key, k->k_key, k->k_len); + } else { + /* Trim 802.11 header. */ + m_adj(m, hdrlen); + tx->security = 0; + } + tx->flags = htole32(flags); + + error = bus_dmamap_load_mbuf(sc->sc_dmat, data->map, m, + BUS_DMA_NOWAIT | BUS_DMA_WRITE); + if (error != 0 && error != EFBIG) { + printf("%s: can't map mbuf (error %d)\n", + DEVNAME(sc), error); + m_freem(m); + return error; + } + if (error != 0) { +#ifdef __FreeBSD_version + /* Too many DMA segments, linearize mbuf. */ + struct mbuf* m1 = m_collapse(m, M_NOWAIT, WPI_MAX_SCATTER); + if (m1 == NULL) { + device_printf(sc->sc_dev, + "%s: could not defrag mbuf\n", __func__); + m_freem(m); + return (ENOBUFS); + } + m = m1; +#else + /* Too many DMA segments, linearize mbuf. */ + if (m_defrag(m, M_DONTWAIT)) { + m_freem(m); + return ENOBUFS; + } +#endif + error = bus_dmamap_load_mbuf(sc->sc_dmat, data->map, m, + BUS_DMA_NOWAIT | BUS_DMA_WRITE); + if (error != 0) { + printf("%s: can't map mbuf (error %d)\n", + DEVNAME(sc), error); + m_freem(m); + return error; + } + } + + data->m = m; + data->ni = ni; + + DPRINTFN(4, ("sending data: qid=%d idx=%d len=%d nsegs=%d\n", + ring->qid, ring->cur, m->m_pkthdr.len, data->map->dm_nsegs)); + + /* Fill TX descriptor. */ + desc->flags = htole32(WPI_PAD32(m->m_pkthdr.len) << 28 | + (1 + data->map->dm_nsegs) << 24); + /* First DMA segment is used by the TX command. */ + 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) + + ((hdrlen + 3) & ~3)); + /* Other DMA segments are for data payload. */ + for (i = 1; i <= data->map->dm_nsegs; i++) { + desc->segs[i].addr = + htole32(data->map->dm_segs[i - 1].ds_addr); + desc->segs[i].len = + htole32(data->map->dm_segs[i - 1].ds_len); + } + + bus_dmamap_sync(sc->sc_dmat, data->map, 0, data->map->dm_mapsize, + BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(sc->sc_dmat, ring->cmd_dma.map, + (caddr_t)cmd - ring->cmd_dma.vaddr, sizeof (*cmd), + BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(sc->sc_dmat, ring->desc_dma.map, + (caddr_t)desc - ring->desc_dma.vaddr, sizeof (*desc), + BUS_DMASYNC_PREWRITE); + + /* Kick TX ring. */ + ring->cur = (ring->cur + 1) % WPI_TX_RING_COUNT; + WPI_WRITE(sc, WPI_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur); + + /* Mark TX ring as full if we reach a certain threshold. */ + if (++ring->queued > WPI_TX_RING_HIMARK) + sc->qfullmsk |= 1 << ring->qid; + + return 0; +} + +void +wpi_start(struct ifnet *ifp) +{ + struct wpi_softc *sc = ifp->if_softc; + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_node *ni; + struct mbuf *m; + + if (!(ifp->if_flags & IFF_RUNNING) || ifq_is_oactive(&ifp->if_snd)) + return; + + for (;;) { + if (sc->qfullmsk != 0) { + ifq_set_oactive(&ifp->if_snd); + break; + } + /* Send pending management frames first. */ + m = mq_dequeue(&ic->ic_mgtq); + if (m != NULL) { + ni = m->m_pkthdr.ph_cookie; + goto sendit; + } + if (ic->ic_state != IEEE80211_S_RUN) + break; + + /* Encapsulate and send data frames. */ + m = ifq_dequeue(&ifp->if_snd); + if (m == NULL) + break; +#if NBPFILTER > 0 + if (ifp->if_bpf != NULL) + bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT); +#endif + if ((m = ieee80211_encap(ifp, m, &ni)) == NULL) + continue; +sendit: +#if NBPFILTER > 0 + if (ic->ic_rawbpf != NULL) + bpf_mtap(ic->ic_rawbpf, m, BPF_DIRECTION_OUT); +#endif + if (wpi_tx(sc, m, ni) != 0) { + ieee80211_release_node(ic, ni); + ifp->if_oerrors++; + continue; + } + + sc->sc_tx_timer = 5; + ifp->if_timer = 1; + } +} + +void +wpi_watchdog(struct ifnet *ifp) +{ + struct wpi_softc *sc = ifp->if_softc; + + ifp->if_timer = 0; + + if (sc->sc_tx_timer > 0) { + if (--sc->sc_tx_timer == 0) { + printf("%s: device timeout\n", DEVNAME(sc)); + wpi_stop(ifp, 1); + ifp->if_oerrors++; + return; + } + ifp->if_timer = 1; + } + + ieee80211_watchdog(ifp); +} + +int +wpi_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct wpi_softc *sc = ifp->if_softc; + struct ieee80211com *ic = &sc->sc_ic; + int s, error = 0; + + error = rw_enter(&sc->sc_rwlock, RW_WRITE | RW_INTR); + if (error) + return error; + s = splnet(); + + switch (cmd) { + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + /* FALLTHROUGH */ + case SIOCSIFFLAGS: + if (ifp->if_flags & IFF_UP) { + if (!(ifp->if_flags & IFF_RUNNING)) + error = wpi_init(ifp); + } else { + if (ifp->if_flags & IFF_RUNNING) + wpi_stop(ifp, 1); + } + break; + + case SIOCS80211POWER: + error = ieee80211_ioctl(ifp, cmd, data); + if (error != ENETRESET) + break; + if (ic->ic_state == IEEE80211_S_RUN) { + if (ic->ic_flags & IEEE80211_F_PMGTON) + error = wpi_set_pslevel(sc, 0, 3, 0); + else /* back to CAM */ + error = wpi_set_pslevel(sc, 0, 0, 0); + } else { + /* Defer until transition to IEEE80211_S_RUN. */ + error = 0; + } + break; + + default: + error = ieee80211_ioctl(ifp, cmd, data); + } + + if (error == ENETRESET) { + error = 0; + if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == + (IFF_UP | IFF_RUNNING)) { + wpi_stop(ifp, 0); + error = wpi_init(ifp); + } + } + + splx(s); + rw_exit_write(&sc->sc_rwlock); + return error; +} + +/* + * Send a command to the firmware. + */ +int +wpi_cmd(struct wpi_softc *sc, int code, const void *buf, int size, int async) +{ + 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; + + desc = &ring->desc[ring->cur]; + data = &ring->data[ring->cur]; + totlen = 4 + size; + + if (size > sizeof cmd->data) { + /* Command is too large to fit in a descriptor. */ + if (totlen > MCLBYTES) + return EINVAL; + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) + return ENOMEM; + if (totlen > MHLEN) { + MCLGET(m, M_DONTWAIT); + if (!(m->m_flags & M_EXT)) { + m_freem(m); + return ENOMEM; + } + } + cmd = mtod(m, struct wpi_tx_cmd *); + error = bus_dmamap_load(sc->sc_dmat, data->map, cmd, totlen, + NULL, BUS_DMA_NOWAIT | BUS_DMA_WRITE); + if (error != 0) { + m_freem(m); + return error; + } + data->m = m; + paddr = data->map->dm_segs[0].ds_addr; + } else { + cmd = &ring->cmd[ring->cur]; + paddr = data->cmd_paddr; + } + + cmd->code = code; + cmd->flags = 0; + cmd->qid = ring->qid; + cmd->idx = ring->cur; + memcpy(cmd->data, buf, size); + + desc->flags = htole32(WPI_PAD32(size) << 28 | 1 << 24); + desc->segs[0].addr = htole32(paddr); + desc->segs[0].len = htole32(totlen); + + if (size > sizeof cmd->data) { + bus_dmamap_sync(sc->sc_dmat, data->map, 0, totlen, + BUS_DMASYNC_PREWRITE); + } else { + bus_dmamap_sync(sc->sc_dmat, ring->cmd_dma.map, + (caddr_t)cmd - ring->cmd_dma.vaddr, totlen, + BUS_DMASYNC_PREWRITE); + } + bus_dmamap_sync(sc->sc_dmat, ring->desc_dma.map, + (caddr_t)desc - ring->desc_dma.vaddr, sizeof (*desc), + BUS_DMASYNC_PREWRITE); + + /* Kick command ring. */ + ring->cur = (ring->cur + 1) % WPI_TX_RING_COUNT; + WPI_WRITE(sc, WPI_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur); + + return async ? 0 : tsleep_nsec(cmd, PCATCH, "wpicmd", SEC_TO_NSEC(1)); +} + +/* + * Configure HW multi-rate retries. + */ +int +wpi_mrr_setup(struct wpi_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct wpi_mrr_setup mrr; + int i, error; + + /* CCK rates (not used with 802.11a). */ + for (i = WPI_RIDX_CCK1; i <= WPI_RIDX_CCK11; i++) { + mrr.rates[i].flags = 0; + mrr.rates[i].plcp = wpi_rates[i].plcp; + /* 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_RIDX_OFDM6; i <= WPI_RIDX_OFDM54; i++) { + mrr.rates[i].flags = 0; + mrr.rates[i].plcp = wpi_rates[i].plcp; + /* Fallback to the immediate lower rate (if any.) */ + /* We allow fallback from OFDM/6 to CCK/2 in 11b/g mode. */ + mrr.rates[i].next = (i == WPI_RIDX_OFDM6) ? + ((ic->ic_curmode == IEEE80211_MODE_11A) ? + WPI_RIDX_OFDM6 : WPI_RIDX_CCK2) : + i - 1; + /* Try one time at this rate before falling back to "next". */ + mrr.rates[i].ntries = 1; + } + /* 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) { + printf("%s: could not setup MRR for control frames\n", + DEVNAME(sc)); + return error; + } + /* Setup MRR for data frames. */ + mrr.which = htole32(WPI_MRR_DATA); + error = wpi_cmd(sc, WPI_CMD_MRR_SETUP, &mrr, sizeof mrr, 0); + if (error != 0) { + printf("%s: could not setup MRR for data frames\n", + DEVNAME(sc)); + return error; + } + return 0; +} + +void +wpi_updateedca(struct ieee80211com *ic) +{ +#define WPI_EXP2(x) ((1 << (x)) - 1) /* CWmin = 2^ECWmin - 1 */ + struct wpi_softc *sc = ic->ic_softc; + struct wpi_edca_params cmd; + int aci; + + memset(&cmd, 0, sizeof cmd); + cmd.flags = htole32(WPI_EDCA_UPDATE); + for (aci = 0; aci < EDCA_NUM_AC; aci++) { + const struct ieee80211_edca_ac_params *ac = + &ic->ic_edca_ac[aci]; + cmd.ac[aci].aifsn = ac->ac_aifsn; + cmd.ac[aci].cwmin = htole16(WPI_EXP2(ac->ac_ecwmin)); + cmd.ac[aci].cwmax = htole16(WPI_EXP2(ac->ac_ecwmax)); + cmd.ac[aci].txoplimit = + htole16(IEEE80211_TXOP_TO_US(ac->ac_txoplimit)); + } + (void)wpi_cmd(sc, WPI_CMD_EDCA_PARAMS, &cmd, sizeof cmd, 1); +#undef WPI_EXP2 +} + +void +wpi_set_led(struct wpi_softc *sc, uint8_t which, uint8_t off, uint8_t on) +{ + struct wpi_cmd_led led; + + led.which = which; + led.unit = htole32(100000); /* on/off in unit of 100ms */ + led.off = off; + led.on = on; + (void)wpi_cmd(sc, WPI_CMD_SET_LED, &led, sizeof led, 1); +} + +int +wpi_set_timing(struct wpi_softc *sc, struct ieee80211_node *ni) +{ + struct wpi_cmd_timing cmd; + uint64_t val, mod; + + memset(&cmd, 0, sizeof cmd); + memcpy(&cmd.tstamp, ni->ni_tstamp, 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; /* msecs -> usecs */ + mod = letoh64(cmd.tstamp) % val; + cmd.binitval = htole32((uint32_t)(val - mod)); + + DPRINTF(("timing bintval=%u, tstamp=%llu, init=%u\n", + ni->ni_intval, letoh64(cmd.tstamp), (uint32_t)(val - mod))); + + return wpi_cmd(sc, WPI_CMD_TIMING, &cmd, sizeof cmd, 1); +} + +/* + * This function is called periodically (every minute) to adjust TX power + * based on temperature variation. + */ +void +wpi_power_calibration(struct wpi_softc *sc) +{ + int temp; + + temp = (int)WPI_READ(sc, WPI_UCODE_GP2); + /* Sanity-check temperature. */ + if (temp < -260 || temp > 25) { + /* This can't be correct, ignore. */ + DPRINTF(("out-of-range temperature reported: %d\n", temp)); + return; + } + DPRINTF(("temperature %d->%d\n", sc->temp, temp)); + /* Adjust TX power if need be (delta > 6). */ + if (abs(temp - sc->temp) > 6) { + /* Record temperature of last calibration. */ + sc->temp = temp; + (void)wpi_set_txpower(sc, 1); + } +} + +/* + * Set TX power for current channel (each rate has its own power settings). + */ +int +wpi_set_txpower(struct wpi_softc *sc, int async) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_channel *ch; + struct wpi_power_group *group; + struct wpi_cmd_txpower cmd; + u_int chan; + int idx, i; + + /* Retrieve current channel from last RXON. */ + chan = sc->rxon.chan; + DPRINTF(("setting TX power for channel %d\n", chan)); + ch = &ic->ic_channels[chan]; + + /* 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_rates[i].plcp; + + 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(("chan %d/rate %d: power index %d\n", chan, + wpi_rates[i].rate, idx)); + } + return wpi_cmd(sc, WPI_CMD_TXPOWER, &cmd, sizeof cmd, async); +} + +/* + * Determine TX power index for a given channel/rate combination. + * This takes into account the regulatory information from EEPROM and the + * current temperature. + */ +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 ieee80211com *ic = &sc->sc_ic; + 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; + } + + /* Never exceed the channel maximum allowed TX power. */ + pwr = MIN(pwr, sc->maxpwr[chan]); + + /* Retrieve TX power index into gain tables from samples. */ + for (sample = group->samples; sample < &group->samples[3]; sample++) + if (pwr > sample[1].power) + break; + /* Fixed-point linear interpolation using a 19-bit fractional part. */ + idx = interpolate(pwr, sample[0].power, sample[0].index, + sample[1].power, sample[1].index, 19); + + /*- + * Adjust power index based on current temperature: + * - if cooler than factory-calibrated: decrease output power + * - if warmer than factory-calibrated: increase output power + */ + idx -= (sc->temp - group->temp) * 11 / 100; + + /* Decrease TX power for CCK rates (-5dB). */ + if (ridx >= WPI_RIDX_CCK1) + idx += 10; + + /* Make sure idx stays in a valid range. */ + if (idx < 0) + idx = 0; + else if (idx > WPI_MAX_PWR_INDEX) + idx = WPI_MAX_PWR_INDEX; + return idx; + +#undef interpolate +#undef fdivround +} + +/* + * Set STA mode power saving level (between 0 and 5). + * Level 0 is CAM (Continuously Aware Mode), 5 is for maximum power saving. + */ +int +wpi_set_pslevel(struct wpi_softc *sc, int dtim, int level, int async) +{ + struct wpi_pmgt_cmd cmd; + const struct wpi_pmgt *pmgt; + uint32_t max, skip_dtim; + pcireg_t reg; + int i; + + /* 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_conf_read(sc->sc_pct, sc->sc_pcitag, + sc->sc_cap_off + PCI_PCIE_LCSR); + if (!(reg & PCI_PCIE_LCSR_ASPM_L0S)) /* L0s Entry disabled. */ + cmd.flags |= htole16(WPI_PS_PCI_PMGT); + cmd.rxtimeout = htole32(pmgt->rxtimeout * 1024); + cmd.txtimeout = htole32(pmgt->txtimeout * 1024); + + 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])); + + DPRINTF(("setting power saving level to %d\n", level)); + return wpi_cmd(sc, WPI_CMD_SET_POWER_MODE, &cmd, sizeof cmd, async); +} + +int +wpi_config(struct wpi_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; + struct wpi_bluetooth bluetooth; + struct wpi_node_info node; + int error; + + /* Set power saving level to CAM during initialization. */ + if ((error = wpi_set_pslevel(sc, 0, 0, 0)) != 0) { + printf("%s: could not set power saving level\n", + DEVNAME(sc)); + return error; + } + + /* Configure bluetooth coexistence. */ + memset(&bluetooth, 0, sizeof bluetooth); + bluetooth.flags = WPI_BT_COEX_MODE_4WIRE; + bluetooth.lead_time = WPI_BT_LEAD_TIME_DEF; + bluetooth.max_kill = WPI_BT_MAX_KILL_DEF; + error = wpi_cmd(sc, WPI_CMD_BT_COEX, &bluetooth, sizeof bluetooth, 0); + if (error != 0) { + printf("%s: could not configure bluetooth coexistence\n", + DEVNAME(sc)); + return error; + } + + /* Configure adapter. */ + memset(&sc->rxon, 0, sizeof (struct wpi_rxon)); + IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp)); + IEEE80211_ADDR_COPY(sc->rxon.myaddr, ic->ic_myaddr); + /* Set default channel. */ + sc->rxon.chan = ieee80211_chan2ieee(ic, ic->ic_ibss_chan); + sc->rxon.flags = htole32(WPI_RXON_TSF); + if (IEEE80211_IS_CHAN_2GHZ(ic->ic_ibss_chan)) + sc->rxon.flags |= htole32(WPI_RXON_AUTO | WPI_RXON_24GHZ); + 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_MONITOR: + sc->rxon.mode = WPI_MODE_MONITOR; + sc->rxon.filter = htole32(WPI_FILTER_MULTICAST | + WPI_FILTER_CTL | WPI_FILTER_PROMISC); + break; + default: + /* Should not get there. */ + break; + } + sc->rxon.cck_mask = 0x0f; /* not yet negotiated */ + sc->rxon.ofdm_mask = 0xff; /* not yet negotiated */ + DPRINTF(("setting configuration\n")); + error = wpi_cmd(sc, WPI_CMD_RXON, &sc->rxon, sizeof (struct wpi_rxon), + 0); + if (error != 0) { + printf("%s: RXON command failed\n", DEVNAME(sc)); + return error; + } + + /* Configuration has changed, set TX power accordingly. */ + if ((error = wpi_set_txpower(sc, 0)) != 0) { + printf("%s: could not set TX power\n", DEVNAME(sc)); + return error; + } + + /* Add broadcast node. */ + memset(&node, 0, sizeof node); + IEEE80211_ADDR_COPY(node.macaddr, etherbroadcastaddr); + node.id = WPI_ID_BROADCAST; + node.plcp = wpi_rates[WPI_RIDX_CCK1].plcp; + node.action = htole32(WPI_ACTION_SET_RATE); + node.antenna = WPI_ANTENNA_BOTH; + error = wpi_cmd(sc, WPI_CMD_ADD_NODE, &node, sizeof node, 0); + if (error != 0) { + printf("%s: could not add broadcast node\n", + DEVNAME(sc)); + return error; + } + + if ((error = wpi_mrr_setup(sc)) != 0) { + printf("%s: could not setup MRR\n", DEVNAME(sc)); + return error; + } + return 0; +} + +int +wpi_scan(struct wpi_softc *sc, uint16_t flags) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct wpi_scan_hdr *hdr; + struct wpi_cmd_data *tx; + struct wpi_scan_essid *essid; + struct wpi_scan_chan *chan; + struct ieee80211_frame *wh; + struct ieee80211_rateset *rs; + struct ieee80211_channel *c; + uint8_t *buf, *frm; + int buflen, error; + + buf = malloc(WPI_SCAN_MAXSZ, M_DEVBUF, M_NOWAIT | M_ZERO); + if (buf == NULL) { + printf("%s: could not allocate buffer for scan command\n", + DEVNAME(sc)); + return ENOMEM; + } + hdr = (struct wpi_scan_hdr *)buf; + /* + * Move to the next channel if no frames are received within 10ms + * after sending the probe request. + */ + hdr->quiet_time = htole16(10); /* timeout in milliseconds */ + hdr->quiet_threshold = htole16(1); /* min # of packets */ + + tx = (struct wpi_cmd_data *)(hdr + 1); + tx->flags = htole32(WPI_TX_AUTO_SEQ); + tx->id = WPI_ID_BROADCAST; + tx->lifetime = htole32(WPI_LIFETIME_INFINITE); + + if (flags & IEEE80211_CHAN_5GHZ) { + hdr->crc_threshold = htole16(1); + /* Send probe requests at 6Mbps. */ + tx->plcp = wpi_rates[WPI_RIDX_OFDM6].plcp; + rs = &ic->ic_sup_rates[IEEE80211_MODE_11A]; + } else { + hdr->flags = htole32(WPI_RXON_24GHZ | WPI_RXON_AUTO); + /* Send probe requests at 1Mbps. */ + tx->plcp = wpi_rates[WPI_RIDX_CCK1].plcp; + rs = &ic->ic_sup_rates[IEEE80211_MODE_11G]; + } + + essid = (struct wpi_scan_essid *)(tx + 1); + if (ic->ic_des_esslen != 0) { + essid[0].id = IEEE80211_ELEMID_SSID; + essid[0].len = ic->ic_des_esslen; + memcpy(essid[0].data, ic->ic_des_essid, ic->ic_des_esslen); + } + /* + * Build a probe request frame. Most of the following code is a + * copy & paste of what is done in net80211. + */ + wh = (struct ieee80211_frame *)(essid + 4); + 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, etherbroadcastaddr); + IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr); + IEEE80211_ADDR_COPY(wh->i_addr3, etherbroadcastaddr); + *(uint16_t *)&wh->i_dur[0] = 0; /* filled by HW */ + *(uint16_t *)&wh->i_seq[0] = 0; /* filled by HW */ + + frm = (uint8_t *)(wh + 1); + frm = ieee80211_add_ssid(frm, NULL, 0); + frm = ieee80211_add_rates(frm, rs); + if (rs->rs_nrates > IEEE80211_RATE_SIZE) + frm = ieee80211_add_xrates(frm, rs); + + /* Set length of probe request. */ + tx->len = htole16(frm - (uint8_t *)wh); + + chan = (struct wpi_scan_chan *)frm; + for (c = &ic->ic_channels[1]; + c <= &ic->ic_channels[IEEE80211_CHAN_MAX]; c++) { + if ((c->ic_flags & flags) != flags) + continue; + + chan->chan = ieee80211_chan2ieee(ic, c); + DPRINTFN(2, ("adding channel %d\n", chan->chan)); + chan->flags = 0; + if (!(c->ic_flags & IEEE80211_CHAN_PASSIVE)) + chan->flags |= WPI_CHAN_ACTIVE; + if (ic->ic_des_esslen != 0) + chan->flags |= WPI_CHAN_NPBREQS(1); + chan->dsp_gain = 0x6e; + if (IEEE80211_IS_CHAN_5GHZ(c)) { + chan->rf_gain = 0x3b; + chan->active = htole16(24); + chan->passive = htole16(110); + } else { + chan->rf_gain = 0x28; + chan->active = htole16(36); + chan->passive = htole16(120); + } + hdr->nchan++; + chan++; + } + + buflen = (uint8_t *)chan - buf; + hdr->len = htole16(buflen); + + DPRINTF(("sending scan command nchan=%d\n", hdr->nchan)); + error = wpi_cmd(sc, WPI_CMD_SCAN, buf, buflen, 1); + free(buf, M_DEVBUF, WPI_SCAN_MAXSZ); + return error; +} + +int +wpi_auth(struct wpi_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_node *ni = ic->ic_bss; + struct wpi_node_info node; + int error; + + /* Update adapter configuration. */ + 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); + 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); + switch (ic->ic_curmode) { + case IEEE80211_MODE_11A: + sc->rxon.cck_mask = 0; + sc->rxon.ofdm_mask = 0x15; + break; + case IEEE80211_MODE_11B: + sc->rxon.cck_mask = 0x03; + sc->rxon.ofdm_mask = 0; + break; + default: /* Assume 802.11b/g. */ + sc->rxon.cck_mask = 0x0f; + sc->rxon.ofdm_mask = 0x15; + } + DPRINTF(("rxon chan %d flags %x cck %x ofdm %x\n", sc->rxon.chan, + sc->rxon.flags, sc->rxon.cck_mask, sc->rxon.ofdm_mask)); + error = wpi_cmd(sc, WPI_CMD_RXON, &sc->rxon, sizeof (struct wpi_rxon), + 1); + if (error != 0) { + printf("%s: RXON command failed\n", DEVNAME(sc)); + return error; + } + + /* Configuration has changed, set TX power accordingly. */ + if ((error = wpi_set_txpower(sc, 1)) != 0) { + printf("%s: could not set TX power\n", DEVNAME(sc)); + return error; + } + /* + * Reconfiguring RXON clears the firmware nodes table so we must + * add the broadcast node again. + */ + memset(&node, 0, sizeof node); + IEEE80211_ADDR_COPY(node.macaddr, etherbroadcastaddr); + node.id = WPI_ID_BROADCAST; + node.plcp = (ic->ic_curmode == IEEE80211_MODE_11A) ? + wpi_rates[WPI_RIDX_OFDM6].plcp : wpi_rates[WPI_RIDX_CCK1].plcp; + 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) { + printf("%s: could not add broadcast node\n", + DEVNAME(sc)); + return error; + } + return 0; +} + +int +wpi_run(struct wpi_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_node *ni = ic->ic_bss; + struct wpi_node_info node; + int error; + + if (ic->ic_opmode == IEEE80211_M_MONITOR) { + /* Link LED blinks while monitoring. */ + wpi_set_led(sc, WPI_LED_LINK, 5, 5); + return 0; + } + if ((error = wpi_set_timing(sc, ni)) != 0) { + printf("%s: could not set timing\n", DEVNAME(sc)); + return error; + } + + /* Update adapter configuration. */ + sc->rxon.associd = htole16(IEEE80211_AID(ni->ni_associd)); + /* 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); + sc->rxon.filter |= htole32(WPI_FILTER_BSS); + DPRINTF(("rxon chan %d flags %x\n", sc->rxon.chan, sc->rxon.flags)); + error = wpi_cmd(sc, WPI_CMD_RXON, &sc->rxon, sizeof (struct wpi_rxon), + 1); + if (error != 0) { + printf("%s: RXON command failed\n", DEVNAME(sc)); + return error; + } + + /* Configuration has changed, set TX power accordingly. */ + if ((error = wpi_set_txpower(sc, 1)) != 0) { + printf("%s: could not set TX power\n", DEVNAME(sc)); + return error; + } + + /* Fake a join to init the TX rate. */ + ((struct wpi_node *)ni)->id = WPI_ID_BSS; + wpi_newassoc(ic, ni, 1); + + /* Add BSS node. */ + memset(&node, 0, sizeof node); + IEEE80211_ADDR_COPY(node.macaddr, ni->ni_bssid); + node.id = WPI_ID_BSS; + node.plcp = (ic->ic_curmode == IEEE80211_MODE_11A) ? + wpi_rates[WPI_RIDX_OFDM6].plcp : wpi_rates[WPI_RIDX_CCK1].plcp; + node.action = htole32(WPI_ACTION_SET_RATE); + node.antenna = WPI_ANTENNA_BOTH; + DPRINTF(("adding BSS node\n")); + error = wpi_cmd(sc, WPI_CMD_ADD_NODE, &node, sizeof node, 1); + if (error != 0) { + printf("%s: could not add BSS node\n", DEVNAME(sc)); + return error; + } + + /* Start periodic calibration timer. */ + sc->calib_cnt = 0; + timeout_add_msec(&sc->calib_to, 500); + + /* Link LED always on while associated. */ + wpi_set_led(sc, WPI_LED_LINK, 0, 1); + + /* Enable power-saving mode if requested by user. */ + if (sc->sc_ic.ic_flags & IEEE80211_F_PMGTON) + (void)wpi_set_pslevel(sc, 0, 3, 1); + + return 0; +} + +/* + * We support CCMP hardware encryption/decryption of unicast frames only. + * HW support for TKIP really sucks. We should let TKIP die anyway. + */ +int +wpi_set_key(struct ieee80211com *ic, struct ieee80211_node *ni, + struct ieee80211_key *k) +{ + struct wpi_softc *sc = ic->ic_softc; + struct wpi_node *wn = (void *)ni; + struct wpi_node_info node; + uint16_t kflags; + + if ((k->k_flags & IEEE80211_KEY_GROUP) || + k->k_cipher != IEEE80211_CIPHER_CCMP) + return ieee80211_set_key(ic, ni, k); + + kflags = WPI_KFLAG_CCMP | WPI_KFLAG_KID(k->k_id); + memset(&node, 0, sizeof node); + node.id = wn->id; + node.control = WPI_NODE_UPDATE; + node.flags = WPI_FLAG_SET_KEY; + node.kflags = htole16(kflags); + memcpy(node.key, k->k_key, k->k_len); + DPRINTF(("set key id=%d for node %d\n", k->k_id, node.id)); + return wpi_cmd(sc, WPI_CMD_ADD_NODE, &node, sizeof node, 1); +} + +void +wpi_delete_key(struct ieee80211com *ic, struct ieee80211_node *ni, + struct ieee80211_key *k) +{ + struct wpi_softc *sc = ic->ic_softc; + struct wpi_node *wn = (void *)ni; + struct wpi_node_info node; + + if ((k->k_flags & IEEE80211_KEY_GROUP) || + k->k_cipher != IEEE80211_CIPHER_CCMP) { + /* See comment about other ciphers above. */ + ieee80211_delete_key(ic, ni, k); + return; + } + if (ic->ic_state != IEEE80211_S_RUN) + return; /* Nothing to do. */ + memset(&node, 0, sizeof node); + node.id = wn->id; + node.control = WPI_NODE_UPDATE; + node.flags = WPI_FLAG_SET_KEY; + node.kflags = 0; + DPRINTF(("delete keys for node %d\n", node.id)); + (void)wpi_cmd(sc, WPI_CMD_ADD_NODE, &node, sizeof node, 1); +} + +int +wpi_post_alive(struct wpi_softc *sc) +{ + int ntries, error; + + /* Check (again) that the radio is not disabled. */ + if ((error = wpi_nic_lock(sc)) != 0) + return error; + /* NB: Runtime firmware must be up and running. */ + if (!(wpi_prph_read(sc, WPI_APMG_RFKILL) & 1)) { + printf("%s: radio is disabled by hardware switch\n", + DEVNAME(sc)); + wpi_nic_unlock(sc); + return EPERM; /* :-) */ + } + wpi_nic_unlock(sc); + + /* Wait for thermal sensor to calibrate. */ + for (ntries = 0; ntries < 1000; ntries++) { + if ((sc->temp = (int)WPI_READ(sc, WPI_UCODE_GP2)) != 0) + break; + DELAY(10); + } + if (ntries == 1000) { + printf("%s: timeout waiting for thermal sensor calibration\n", + DEVNAME(sc)); + return ETIMEDOUT; + } + DPRINTF(("temperature %d\n", sc->temp)); + return 0; +} + +/* + * The firmware boot code is small and is intended to be copied directly into + * the NIC internal memory (no DMA transfer.) + */ +int +wpi_load_bootcode(struct wpi_softc *sc, const uint8_t *ucode, int size) +{ + int error, ntries; + + 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++) { + if (!(wpi_prph_read(sc, WPI_BSM_WR_CTRL) & + WPI_BSM_WR_CTRL_START)) + break; + DELAY(10); + } + if (ntries == 1000) { + printf("%s: could not load boot firmware\n", + DEVNAME(sc)); + wpi_nic_unlock(sc); + return ETIMEDOUT; + } + + /* Enable boot after power up. */ + wpi_prph_write(sc, WPI_BSM_WR_CTRL, WPI_BSM_WR_CTRL_START_EN); + + wpi_nic_unlock(sc); + return 0; +} + +int +wpi_load_firmware(struct wpi_softc *sc) +{ + struct wpi_fw_info *fw = &sc->fw; + struct wpi_dma_info *dma = &sc->fw_dma; + int error; + + /* Copy initialization sections into pre-allocated DMA-safe memory. */ + memcpy(dma->vaddr, fw->init.data, fw->init.datasz); + bus_dmamap_sync(sc->sc_dmat, dma->map, 0, fw->init.datasz, + BUS_DMASYNC_PREWRITE); + memcpy(dma->vaddr + WPI_FW_DATA_MAXSZ, + fw->init.text, fw->init.textsz); + bus_dmamap_sync(sc->sc_dmat, dma->map, WPI_FW_DATA_MAXSZ, + fw->init.textsz, BUS_DMASYNC_PREWRITE); + + /* Tell adapter where to find initialization sections. */ + if ((error = wpi_nic_lock(sc)) != 0) + return error; + wpi_prph_write(sc, WPI_BSM_DRAM_DATA_ADDR, dma->paddr); + wpi_prph_write(sc, WPI_BSM_DRAM_DATA_SIZE, fw->init.datasz); + wpi_prph_write(sc, WPI_BSM_DRAM_TEXT_ADDR, + dma->paddr + WPI_FW_DATA_MAXSZ); + wpi_prph_write(sc, WPI_BSM_DRAM_TEXT_SIZE, fw->init.textsz); + wpi_nic_unlock(sc); + + /* Load firmware boot code. */ + error = wpi_load_bootcode(sc, fw->boot.text, fw->boot.textsz); + if (error != 0) { + printf("%s: could not load boot firmware\n", + DEVNAME(sc)); + return error; + } + /* Now press "execute". */ + WPI_WRITE(sc, WPI_RESET, 0); + + /* Wait at most one second for first alive notification. */ + if ((error = tsleep_nsec(sc, PCATCH, "wpiinit", SEC_TO_NSEC(1))) != 0) { + printf("%s: timeout waiting for adapter to initialize\n", + DEVNAME(sc)); + return error; + } + + /* Copy runtime sections into pre-allocated DMA-safe memory. */ + memcpy(dma->vaddr, fw->main.data, fw->main.datasz); + bus_dmamap_sync(sc->sc_dmat, dma->map, 0, fw->main.datasz, + BUS_DMASYNC_PREWRITE); + memcpy(dma->vaddr + WPI_FW_DATA_MAXSZ, + fw->main.text, fw->main.textsz); + bus_dmamap_sync(sc->sc_dmat, dma->map, WPI_FW_DATA_MAXSZ, + fw->main.textsz, 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; +} + +int +wpi_read_firmware(struct wpi_softc *sc) +{ + struct wpi_fw_info *fw = &sc->fw; + const struct wpi_firmware_hdr *hdr; + int error; + + /* Read firmware image from filesystem. */ + if ((error = loadfirmware("wpi-3945abg", &fw->data, &fw->datalen)) != 0) { + printf("%s: error, %d, could not read firmware %s\n", + DEVNAME(sc), error, "wpi-3945abg"); + return error; + } + if (fw->datalen < sizeof (*hdr)) { + printf("%s: truncated firmware header: %zu bytes\n", + DEVNAME(sc), fw->datalen); + free(fw->data, M_DEVBUF, fw->datalen); + return EINVAL; + } + /* Extract firmware header information. */ + hdr = (struct wpi_firmware_hdr *)fw->data; + fw->main.textsz = letoh32(hdr->main_textsz); + fw->main.datasz = letoh32(hdr->main_datasz); + fw->init.textsz = letoh32(hdr->init_textsz); + fw->init.datasz = letoh32(hdr->init_datasz); + fw->boot.textsz = letoh32(hdr->boot_textsz); + 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) { + printf("%s: invalid firmware header\n", DEVNAME(sc)); + free(fw->data, M_DEVBUF, fw->datalen); + return EINVAL; + } + + /* Check that all firmware sections fit. */ + if (fw->datalen < sizeof (*hdr) + fw->main.textsz + fw->main.datasz + + fw->init.textsz + fw->init.datasz + fw->boot.textsz) { + printf("%s: firmware file too short: %zu bytes\n", + DEVNAME(sc), fw->datalen); + free(fw->data, M_DEVBUF, fw->datalen); + return EINVAL; + } + + /* 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; + + return 0; +} + +int +wpi_clock_wait(struct wpi_softc *sc) +{ + int ntries; + + /* Set "initialization complete" bit. */ + WPI_SETBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_INIT_DONE); + + /* Wait for clock stabilization. */ + for (ntries = 0; ntries < 25000; ntries++) { + if (WPI_READ(sc, WPI_GP_CNTRL) & WPI_GP_CNTRL_MAC_CLOCK_READY) + return 0; + DELAY(100); + } + printf("%s: timeout waiting for clock stabilization\n", + DEVNAME(sc)); + return ETIMEDOUT; +} + +int +wpi_apm_init(struct wpi_softc *sc) +{ + int error; + + WPI_SETBITS(sc, WPI_ANA_PLL, WPI_ANA_PLL_INIT); + /* Disable L0s. */ + WPI_SETBITS(sc, WPI_GIO_CHICKEN, WPI_GIO_CHICKEN_L1A_NO_L0S_RX); + + if ((error = wpi_clock_wait(sc)) != 0) + return error; + + if ((error = wpi_nic_lock(sc)) != 0) + return error; + /* Enable DMA. */ + wpi_prph_write(sc, WPI_APMG_CLK_ENA, + WPI_APMG_CLK_DMA_CLK_RQT | WPI_APMG_CLK_BSM_CLK_RQT); + DELAY(20); + /* Disable L1. */ + wpi_prph_setbits(sc, WPI_APMG_PCI_STT, WPI_APMG_PCI_STT_L1A_DIS); + wpi_nic_unlock(sc); + + return 0; +} + +void +wpi_apm_stop_master(struct wpi_softc *sc) +{ + int ntries; + + 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); + } + printf("%s: timeout waiting for master\n", DEVNAME(sc)); +} + +void +wpi_apm_stop(struct wpi_softc *sc) +{ + wpi_apm_stop_master(sc); + WPI_SETBITS(sc, WPI_RESET, WPI_RESET_SW); +} + +void +wpi_nic_config(struct wpi_softc *sc) +{ + pcireg_t reg; + uint8_t rev; + + /* Voodoo from the reference driver. */ + reg = pci_conf_read(sc->sc_pct, sc->sc_pcitag, PCI_CLASS_REG); + rev = PCI_REVISION(reg); + if ((rev & 0xc0) == 0x40) + WPI_SETBITS(sc, WPI_HW_IF_CONFIG, WPI_HW_IF_CONFIG_ALM_MB); + else if (!(rev & 0x80)) + WPI_SETBITS(sc, WPI_HW_IF_CONFIG, WPI_HW_IF_CONFIG_ALM_MM); + + if (sc->cap == 0x80) + WPI_SETBITS(sc, WPI_HW_IF_CONFIG, WPI_HW_IF_CONFIG_SKU_MRC); + + if ((letoh16(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); +} + +int +wpi_hw_init(struct wpi_softc *sc) +{ + int chnl, ntries, error; + + /* Clear pending interrupts. */ + WPI_WRITE(sc, WPI_INT, 0xffffffff); + + if ((error = wpi_apm_init(sc)) != 0) { + printf("%s: could not power ON adapter\n", + DEVNAME(sc)); + return error; + } + + /* Select VMAIN power source. */ + if ((error = wpi_nic_lock(sc)) != 0) + return error; + wpi_prph_clrbits(sc, WPI_APMG_PS, WPI_APMG_PS_PWR_SRC_MASK); + wpi_nic_unlock(sc); + /* Spin until VMAIN gets selected. */ + for (ntries = 0; ntries < 5000; ntries++) { + if (WPI_READ(sc, WPI_GPIO_IN) & WPI_GPIO_IN_VMAIN) + break; + DELAY(10); + } + if (ntries == 5000) { + printf("%s: timeout selecting power source\n", + DEVNAME(sc)); + return ETIMEDOUT; + } + + /* Perform adapter initialization. */ + (void)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_RBTH(1)); + (void)WPI_READ(sc, WPI_FH_RSSR_TBL); /* barrier */ + WPI_WRITE(sc, WPI_FH_RX_WPTR, (WPI_RX_RING_COUNT - 1) & ~7); + wpi_nic_unlock(sc); + + /* 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_MASK, WPI_INT_MASK); + + /* _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) { + printf("%s: could not load firmware\n", DEVNAME(sc)); + return error; + } + /* Wait at most one second for firmware alive notification. */ + if ((error = tsleep_nsec(sc, PCATCH, "wpiinit", SEC_TO_NSEC(1))) != 0) { + printf("%s: timeout waiting for adapter to initialize\n", + DEVNAME(sc)); + return error; + } + /* Do post-firmware initialization. */ + return wpi_post_alive(sc); +} + +void +wpi_hw_stop(struct wpi_softc *sc) +{ + int chnl, qid, ntries; + uint32_t tmp; + + WPI_WRITE(sc, WPI_RESET, WPI_RESET_NEVO); + + /* Disable interrupts. */ + WPI_WRITE(sc, WPI_MASK, 0); + WPI_WRITE(sc, WPI_INT, 0xffffffff); + WPI_WRITE(sc, WPI_FH_INT, 0xffffffff); + + /* Make sure we no longer hold the NIC lock. */ + wpi_nic_unlock(sc); + + if (wpi_nic_lock(sc) == 0) { + /* Stop TX scheduler. */ + wpi_prph_write(sc, WPI_ALM_SCHED_MODE, 0); + wpi_prph_write(sc, WPI_ALM_SCHED_TXFACT, 0); + + /* Stop all DMA channels. */ + for (chnl = 0; chnl < WPI_NDMACHNLS; chnl++) { + WPI_WRITE(sc, WPI_FH_TX_CONFIG(chnl), 0); + for (ntries = 0; ntries < 100; ntries++) { + tmp = WPI_READ(sc, WPI_FH_TX_STATUS); + if ((tmp & WPI_FH_TX_STATUS_IDLE(chnl)) == + WPI_FH_TX_STATUS_IDLE(chnl)) + break; + DELAY(10); + } + } + wpi_nic_unlock(sc); + } + + /* Stop RX ring. */ + wpi_reset_rx_ring(sc, &sc->rxq); + + /* Reset all TX rings. */ + for (qid = 0; qid < WPI_NTXQUEUES; qid++) + wpi_reset_tx_ring(sc, &sc->txq[qid]); + + if (wpi_nic_lock(sc) == 0) { + wpi_prph_write(sc, WPI_APMG_CLK_DIS, WPI_APMG_CLK_DMA_CLK_RQT); + wpi_nic_unlock(sc); + } + DELAY(5); + /* Power OFF adapter. */ + wpi_apm_stop(sc); +} + +int +wpi_init(struct ifnet *ifp) +{ + struct wpi_softc *sc = ifp->if_softc; + struct ieee80211com *ic = &sc->sc_ic; + int error; + +#ifdef notyet + /* Check that the radio is not disabled by hardware switch. */ + if (!(WPI_READ(sc, WPI_GP_CNTRL) & WPI_GP_CNTRL_RFKILL)) { + printf("%s: radio is disabled by hardware switch\n", + DEVNAME(sc)); + error = EPERM; /* :-) */ + goto fail; + } +#endif + /* Read firmware images from the filesystem. */ + if ((error = wpi_read_firmware(sc)) != 0) { + printf("%s: could not read firmware\n", DEVNAME(sc)); + goto fail; + } + + /* Initialize hardware and upload firmware. */ + error = wpi_hw_init(sc); + free(sc->fw.data, M_DEVBUF, sc->fw.datalen); + if (error != 0) { + printf("%s: could not initialize hardware\n", + DEVNAME(sc)); + goto fail; + } + + /* Configure adapter now that it is ready. */ + if ((error = wpi_config(sc)) != 0) { + printf("%s: could not configure device\n", + DEVNAME(sc)); + goto fail; + } + + ifq_clr_oactive(&ifp->if_snd); + ifp->if_flags |= IFF_RUNNING; + + if (ic->ic_opmode != IEEE80211_M_MONITOR) + ieee80211_begin_scan(ifp); + else + ieee80211_new_state(ic, IEEE80211_S_RUN, -1); + + return 0; + +fail: wpi_stop(ifp, 1); + return error; +} + +void +wpi_stop(struct ifnet *ifp, int disable) +{ + struct wpi_softc *sc = ifp->if_softc; + struct ieee80211com *ic = &sc->sc_ic; + + ifp->if_timer = sc->sc_tx_timer = 0; + ifp->if_flags &= ~IFF_RUNNING; + ifq_clr_oactive(&ifp->if_snd); + + ieee80211_new_state(ic, IEEE80211_S_INIT, -1); + + /* Power OFF hardware. */ + wpi_hw_stop(sc); +} diff --git a/src/add-ons/kernel/drivers/network/wlan/iprowifi3945/dev/wpi/if_wpireg.h b/src/add-ons/kernel/drivers/network/wlan/iprowifi3945/dev/pci/if_wpireg.h similarity index 70% rename from src/add-ons/kernel/drivers/network/wlan/iprowifi3945/dev/wpi/if_wpireg.h rename to src/add-ons/kernel/drivers/network/wlan/iprowifi3945/dev/pci/if_wpireg.h index 84d25bbeb9..26fa72b88b 100644 --- a/src/add-ons/kernel/drivers/network/wlan/iprowifi3945/dev/wpi/if_wpireg.h +++ b/src/add-ons/kernel/drivers/network/wlan/iprowifi3945/dev/pci/if_wpireg.h @@ -1,6 +1,7 @@ +/* $OpenBSD: if_wpireg.h,v 1.28 2013/11/26 20:33:17 deraadt Exp $ */ /*- - * Copyright (c) 2006,2007 + * Copyright (c) 2006-2008 * Damien Bergamini * * Permission to use, copy, modify, and distribute this software for any @@ -19,52 +20,37 @@ #define WPI_TX_RING_COUNT 256 #define WPI_TX_RING_LOMARK 192 #define WPI_TX_RING_HIMARK 224 - -#ifdef DIAGNOSTIC -#define WPI_RX_RING_COUNT_LOG 8 -#else #define WPI_RX_RING_COUNT_LOG 6 -#endif - #define WPI_RX_RING_COUNT (1 << WPI_RX_RING_COUNT_LOG) #define WPI_NTXQUEUES 8 -#define WPI_DRV_NTXQUEUES 5 -#define WPI_CMD_QUEUE_NUM 4 - #define WPI_NDMACHNLS 6 /* Maximum scatter/gather. */ -#define WPI_MAX_SCATTER 4 +#define WPI_MAX_SCATTER 4 -/* - * Rings must be aligned on a 16K boundary. - */ -#define WPI_RING_DMA_ALIGN 0x4000 +/* Maximum RX buffer size (larger than MCLBYTES.) */ +#define WPI_RBUF_SIZE (3 * 1024) -/* Maximum Rx buffer size. */ -#define WPI_RBUF_SIZE ( 3 * 1024 ) /* XXX 3000 but must be aligned */ +/* Base Address Register. */ +#define WPI_PCI_BAR0 PCI_MAPREG_START /* * Control and status registers. */ #define WPI_HW_IF_CONFIG 0x000 #define WPI_INT 0x008 -#define WPI_INT_MASK 0x00c +#define WPI_MASK 0x00c #define WPI_FH_INT 0x010 #define WPI_GPIO_IN 0x018 #define WPI_RESET 0x020 #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 @@ -74,10 +60,6 @@ #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 @@ -91,6 +73,7 @@ #define WPI_FH_MSG_CONFIG 0xe88 #define WPI_FH_TX_STATUS 0xe90 + /* * NIC internal memory offsets. */ @@ -101,21 +84,21 @@ #define WPI_ALM_SCHED_TXF5MF 0x2e20 #define WPI_ALM_SCHED_SBYPASS_MODE1 0x2e2c #define WPI_ALM_SCHED_SBYPASS_MODE2 0x2e30 -#define WPI_APMG_CLK_CTRL 0x3000 -#define WPI_APMG_CLK_EN 0x3004 +#define WPI_APMG_CLK_ENA 0x3004 #define WPI_APMG_CLK_DIS 0x3008 +#define WPI_APMG_RFKILL 0x3014 #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_SRAM_BASE 0x3800 #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) @@ -141,8 +124,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_MAC_ACCESS_ENA (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) @@ -152,49 +135,45 @@ /* 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) +#define WPI_FH_RX_CONFIG_DMA_ENA (1U << 31) +#define WPI_FH_RX_CONFIG_RDRBD_ENA (1U << 29) +#define WPI_FH_RX_CONFIG_WRSTATUS_ENA (1U << 27) +#define WPI_FH_RX_CONFIG_MAXFRAG (1U << 24) +#define WPI_FH_RX_CONFIG_NRBD(x) ((x) << 20) +#define WPI_FH_RX_CONFIG_IRQ_DST_HOST (1U << 12) +#define WPI_FH_RX_CONFIG_IRQ_RBTH(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) +/* Possible flags for register WPI_UCODE_GP1_CLR. */ #define WPI_UCODE_GP1_RFKILL (1 << 1) #define WPI_UCODE_GP1_CMD_BLOCKED (1 << 2) -/* Possible flags for register WPI_FH_RX_STATUS. */ +/* Possible flags for 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_EN (1U << 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_ALIVE (1U << 0) +#define WPI_INT_WAKEUP (1U << 1) +#define WPI_INT_SW_RX (1U << 3) +#define WPI_INT_RF_TOGGLED (1U << 7) +#define WPI_INT_SW_ERR (1U << 25) +#define WPI_INT_FH_TX (1U << 27) +#define WPI_INT_HW_ERR (1U << 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) +#define WPI_INT_MASK \ + (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 | WPI_INT_RF_TOGGLED) /* Possible flags for register WPI_FH_INT. */ #define WPI_FH_INT_RX_CHNL(x) (1 << ((x) + 16)) @@ -212,148 +191,78 @@ /* Possible flags for register WPI_EEPROM. */ #define WPI_EEPROM_READ_VALID (1 << 0) +#define WPI_EEPROM_CMD (1 << 1) /* Possible flags for register WPI_EEPROM_GP. */ -#define WPI_EEPROM_VERSION 0x00000007 #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_CLK_ENA/WPI_APMG_CLK_DIS. */ +#define WPI_APMG_CLK_DMA_CLK_RQT (1 << 9) +#define WPI_APMG_CLK_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[WPI_NTXQUEUES]; + uint32_t txbase[8]; uint32_t next; uint32_t reserved[2]; } __packed; #define WPI_MAX_SEG_LEN 65520 struct wpi_tx_desc { - uint8_t reserved1[3]; - uint8_t nsegs; -#define WPI_PAD32(x) (roundup2(x, 4) - (x)) + uint32_t flags; +#define WPI_PAD32(x) ((((x) + 3) & ~3) - (x)) struct { uint32_t addr; uint32_t len; } __packed segs[WPI_MAX_SCATTER]; - uint8_t reserved2[28]; + uint8_t reserved[28]; } __packed; struct wpi_tx_stat { - uint8_t rtsfailcnt; - uint8_t ackfailcnt; - uint8_t btkillcnt; + uint8_t nrts; + uint8_t retrycnt; + uint8_t nkill; uint8_t rate; uint32_t duration; uint32_t status; -#define WPI_TX_STATUS_SUCCESS 0x01 -#define WPI_TX_STATUS_DIRECT_DONE 0x02 -#define WPI_TX_STATUS_FAIL 0x80 -#define WPI_TX_STATUS_FAIL_SHORT_LIMIT 0x82 -#define WPI_TX_STATUS_FAIL_LONG_LIMIT 0x83 -#define WPI_TX_STATUS_FAIL_FIFO_UNDERRUN 0x84 -#define WPI_TX_STATUS_FAIL_MGMNT_ABORT 0x85 -#define WPI_TX_STATUS_FAIL_NEXT_FRAG 0x86 -#define WPI_TX_STATUS_FAIL_LIFE_EXPIRE 0x87 -#define WPI_TX_STATUS_FAIL_NODE_PS 0x88 -#define WPI_TX_STATUS_FAIL_ABORTED 0x89 -#define WPI_TX_STATUS_FAIL_BT_RETRY 0x8a -#define WPI_TX_STATUS_FAIL_NODE_INVALID 0x8b -#define WPI_TX_STATUS_FAIL_FRAG_DROPPED 0x8c -#define WPI_TX_STATUS_FAIL_TID_DISABLE 0x8d -#define WPI_TX_STATUS_FAIL_FRAME_FLUSHED 0x8e -#define WPI_TX_STATUS_FAIL_INSUFFICIENT_CF_POLL 0x8f -#define WPI_TX_STATUS_FAIL_TX_LOCKED 0x90 -#define WPI_TX_STATUS_FAIL_NO_BEACON_ON_RADAR 0x91 - } __packed; struct wpi_rx_desc { uint32_t len; uint8_t type; #define WPI_UC_READY 1 +#define WPI_ADD_NODE_DONE 24 #define WPI_RX_DONE 27 #define WPI_TX_DONE 28 #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_BEACON_MISSED 162 uint8_t flags; uint8_t idx; uint8_t qid; } __packed; -#define WPI_RX_DESC_QID_MSK 0x07 -#define WPI_UNSOLICITED_RX_NOTIF 0x80 - -struct wpi_rx_stat { - uint8_t len; -#define WPI_STAT_MAXLEN 20 - - uint8_t id; - uint8_t rssi; /* received signal strength */ -#define WPI_RSSI_OFFSET -95 - - uint8_t agc; /* access gain control */ - uint16_t signal; - uint16_t noise; -} __packed; - -struct wpi_rx_head { - uint16_t chan; - uint16_t flags; -#define WPI_STAT_FLAG_SHPREAMBLE (1 << 2) - - uint8_t reserved; - uint8_t plcp; - uint16_t len; -} __packed; - -struct wpi_rx_tail { - uint32_t flags; -#define WPI_RX_NO_CRC_ERR (1 << 0) -#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_RXON 16 -#define WPI_CMD_RXON_ASSOC 17 +#define WPI_CMD_ASSOCIATE 17 #define WPI_CMD_EDCA_PARAMS 19 #define WPI_CMD_TIMING 20 #define WPI_CMD_ADD_NODE 24 -#define WPI_CMD_DEL_NODE 25 #define WPI_CMD_TX_DATA 28 #define WPI_CMD_MRR_SETUP 71 #define WPI_CMD_SET_LED 72 #define WPI_CMD_SET_POWER_MODE 119 #define WPI_CMD_SCAN 128 -#define WPI_CMD_SCAN_ABORT 129 -#define WPI_CMD_SET_BEACON 145 #define WPI_CMD_TXPOWER 151 #define WPI_CMD_BT_COEX 155 -#define WPI_CMD_GET_STATISTICS 156 uint8_t flags; uint8_t idx; @@ -367,16 +276,14 @@ struct wpi_rxon { uint16_t reserved1; uint8_t bssid[IEEE80211_ADDR_LEN]; uint16_t reserved2; - uint8_t wlap[IEEE80211_ADDR_LEN]; - uint16_t reserved3; + uint32_t reserved3[2]; uint8_t mode; #define WPI_MODE_HOSTAP 1 #define WPI_MODE_STA 3 #define WPI_MODE_IBSS 4 #define WPI_MODE_MONITOR 6 - uint8_t air; - uint16_t reserved4; + uint8_t reserved4[3]; uint8_t ofdm_mask; uint8_t cck_mask; uint16_t associd; @@ -387,10 +294,9 @@ struct wpi_rxon { #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_ANT_A (1 << 8) +#define WPI_RXON_ANT_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) @@ -398,14 +304,12 @@ struct wpi_rxon { #define WPI_FILTER_MULTICAST (1 << 2) #define WPI_FILTER_NODECRYPT (1 << 3) #define WPI_FILTER_BSS (1 << 5) -#define WPI_FILTER_BEACON (1 << 6) -#define WPI_FILTER_ASSOC (1 << 7) /* Accept associaton requests. */ uint8_t chan; - uint16_t reserved5; + uint8_t reserved6[3]; } __packed; -/* Structure for command WPI_CMD_RXON_ASSOC. */ +/* Structure for command WPI_CMD_ASSOCIATE. */ struct wpi_assoc { uint32_t flags; uint32_t filter; @@ -418,6 +322,7 @@ struct wpi_assoc { struct wpi_edca_params { uint32_t flags; #define WPI_EDCA_UPDATE (1 << 0) +#define WPI_EDCA_TXOP (1 << 4) struct { uint16_t cwmin; @@ -425,7 +330,7 @@ struct wpi_edca_params { uint8_t aifsn; uint8_t reserved; uint16_t txoplimit; - } __packed ac[WME_NUM_AC]; + } __packed ac[EDCA_NUM_AC]; } __packed; /* Structure for command WPI_CMD_TIMING. */ @@ -448,25 +353,22 @@ struct wpi_node_info { 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) +#define WPI_FLAG_SET_KEY (1 << 0) uint16_t reserved3; 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]; + uint8_t key[16]; + uint32_t action; #define WPI_ACTION_SET_RATE (1 << 2) @@ -483,14 +385,6 @@ struct wpi_node_info { uint16_t add_imm_start; } __packed; -/* 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; @@ -500,25 +394,22 @@ 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_MORE_FRAG (1 << 14) #define WPI_TX_INSERT_TSTAMP (1 << 16) uint8_t plcp; uint8_t id; uint8_t tid; uint8_t security; -#define WPI_CIPHER_WEP 1 +#define WPI_CIPHER_WEP40 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]; + uint8_t tkip[IEEE80211_TKIP_MICLEN]; uint32_t fnext; -#define WPI_NEXT_STA_ID(id) ((id) << 8) - uint32_t lifetime; #define WPI_LIFETIME_INFINITE 0xffffffff @@ -530,31 +421,6 @@ struct wpi_cmd_data { uint16_t txop; } __packed; -/* 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 plcp; - uint8_t id; - uint8_t reserved2[30]; - uint32_t lifetime; - uint8_t ofdm_mask; - uint8_t cck_mask; - uint16_t reserved3[3]; - uint16_t tim; - uint8_t timsz; - uint8_t reserved4; -} __packed; - -/* Structure for notification WPI_BEACON_MISSED. */ -struct wpi_beacon_missed { - uint32_t consecutive; - uint32_t total; - uint32_t expected; - uint32_t received; -} __packed; - /* Structure for command WPI_CMD_MRR_SETUP. */ #define WPI_RIDX_MAX 11 struct wpi_mrr_setup { @@ -566,8 +432,6 @@ struct wpi_mrr_setup { uint8_t plcp; uint8_t flags; uint8_t ntries; -#define WPI_NTRIES_DEFAULT 2 - uint8_t next; } __packed rates[WPI_RIDX_MAX + 1]; } __packed; @@ -586,20 +450,17 @@ struct wpi_cmd_led { /* Structure for command WPI_CMD_SET_POWER_MODE. */ struct wpi_pmgt_cmd { - uint16_t flags; + uint32_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; @@ -610,22 +471,17 @@ struct wpi_scan_hdr { uint16_t len; uint8_t reserved1; uint8_t nchan; - uint16_t quiet_time; /* timeout in milliseconds */ -#define WPI_QUIET_TIME_DEFAULT 10 - - uint16_t quiet_threshold; /* min # of packets */ + uint16_t quiet_time; + uint16_t quiet_threshold; uint16_t crc_threshold; uint16_t reserved2; uint32_t max_svc; /* background scans */ uint32_t pause_svc; /* background scans */ -#define WPI_PAUSE_MAX_TIME ((1 << 20) - 1) -#define WPI_PAUSE_SCAN(nbeacons, time) ((nbeacons << 24) | time) - uint32_t flags; uint32_t filter; /* Followed by a struct wpi_cmd_data. */ - /* Followed by an array of 4 structs wpi_scan_essid. */ + /* Followed by an array of 4 structq wpi_scan_essid. */ /* Followed by probe request body. */ /* Followed by an array of ``nchan'' structs wpi_scan_chan. */ } __packed; @@ -642,22 +498,9 @@ struct wpi_scan_chan { uint16_t passive; /* msecs */ } __packed; -#define WPI_SCAN_CRC_TH_DEFAULT htole16(1) -#define WPI_SCAN_CRC_TH_NEVER htole16(0xffff) - /* Maximum size of a scan command. */ #define WPI_SCAN_MAXSZ (MCLBYTES - 4) -#define WPI_ACTIVE_DWELL_TIME_2GHZ (30) /* all times in msec */ -#define WPI_ACTIVE_DWELL_TIME_5GHZ (20) -#define WPI_ACTIVE_DWELL_FACTOR_2GHZ ( 3) -#define WPI_ACTIVE_DWELL_FACTOR_5GHZ ( 2) - -#define WPI_PASSIVE_DWELL_TIME_2GHZ ( 20) -#define WPI_PASSIVE_DWELL_TIME_5GHZ ( 10) -#define WPI_PASSIVE_DWELL_BASE (100) -#define WPI_CHANNEL_TUNE_TIME ( 6) - /* Structure for command WPI_CMD_TXPOWER. */ struct wpi_cmd_txpower { uint8_t band; @@ -666,14 +509,12 @@ struct wpi_cmd_txpower { uint8_t reserved; uint16_t chan; - struct { 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. */ @@ -695,18 +536,55 @@ struct wpi_bluetooth { uint32_t kill_cts; } __packed; +/* Structures for WPI_RX_DONE notification. */ +struct wpi_rx_stat { + uint8_t len; +#define WPI_STAT_MAXLEN 20 + + uint8_t id; + uint8_t rssi; /* received signal strength */ +#define WPI_RSSI_OFFSET 95 + + uint8_t agc; /* access gain control */ + uint16_t signal; + uint16_t noise; +} __packed; + +struct wpi_rx_head { + uint16_t chan; + uint16_t flags; + uint8_t reserved; + uint8_t rate; + uint16_t len; +} __packed; + +struct wpi_rx_tail { + uint32_t flags; +#define WPI_RX_NO_CRC_ERR (1 << 0) +#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; + /* Structure for WPI_UC_READY notification. */ struct wpi_ucode_info { - uint8_t minor; - uint8_t major; - uint16_t reserved1; + uint32_t version; uint8_t revision[8]; uint8_t type; uint8_t subtype; - uint16_t reserved2; +#define WPI_UCODE_INIT 9 + + uint16_t reserved; uint32_t logptr; uint32_t errptr; - uint32_t tstamp; + uint32_t timestamp; uint32_t valid; } __packed; @@ -724,86 +602,14 @@ struct wpi_start_scan { struct wpi_stop_scan { uint8_t nchan; uint8_t status; -#define WPI_SCAN_COMPLETED 1 -#define WPI_SCAN_ABORTED 2 - uint8_t reserved; uint8_t chan; uint64_t tsf; } __packed; -/* Structures for WPI_{RX,BEACON}_STATISTICS notification. */ -struct wpi_rx_phy_stats { - uint32_t ina; - uint32_t fina; - uint32_t bad_plcp; - uint32_t bad_crc32; - uint32_t overrun; - uint32_t eoverrun; - uint32_t good_crc32; - uint32_t fa; - uint32_t bad_fina_sync; - uint32_t sfd_timeout; - uint32_t fina_timeout; - uint32_t no_rts_ack; - uint32_t rxe_limit; - uint32_t ack; - uint32_t cts; -} __packed; - -struct wpi_rx_general_stats { - uint32_t bad_cts; - uint32_t bad_ack; - uint32_t not_bss; - uint32_t filtered; - uint32_t bad_chan; -} __packed; - -struct wpi_rx_stats { - struct wpi_rx_phy_stats ofdm; - struct wpi_rx_phy_stats cck; - struct wpi_rx_general_stats general; -} __packed; - -struct wpi_tx_stats { - uint32_t preamble; - uint32_t rx_detected; - uint32_t bt_defer; - uint32_t bt_kill; - uint32_t short_len; - uint32_t cts_timeout; - uint32_t ack_timeout; - uint32_t exp_ack; - uint32_t ack; -} __packed; - -struct wpi_general_stats { - uint32_t temp; - uint32_t burst_check; - uint32_t burst; - uint32_t reserved[4]; - uint32_t sleep; - uint32_t slot_out; - uint32_t slot_idle; - uint32_t ttl_tstamp; - uint32_t tx_ant_a; - uint32_t tx_ant_b; - uint32_t exec; - uint32_t probe; -} __packed; - -struct wpi_stats { - uint32_t flags; - struct wpi_rx_stats rx; - struct wpi_tx_stats tx; - struct wpi_general_stats general; -} __packed; - -/* Possible flags for command WPI_CMD_GET_STATISTICS. */ -#define WPI_STATISTICS_BEACON_DISABLE (1 << 1) /* Firmware error dump entry. */ -struct wpi_fw_dump { +struct wpi_fwdump { uint32_t desc; uint32_t time; uint32_t blink[2]; @@ -813,31 +619,26 @@ struct wpi_fw_dump { /* 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; + uint32_t version; + uint32_t main_textsz; + uint32_t main_datasz; + uint32_t init_textsz; + uint32_t init_datasz; + uint32_t boot_textsz; } __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_TEXT_MAXSZ (80 * 1024) +#define WPI_FW_DATA_MAXSZ (32 * 1024) +#define WPI_FW_BOOT_TEXT_MAXSZ 1024 -#define WPI_FW_UPDATED (1U << 31 ) +#define WPI_FW_UPDATED (1U << 31) /* * Offsets into EEPROM. */ #define WPI_EEPROM_MAC 0x015 #define WPI_EEPROM_REVISION 0x035 -#define WPI_EEPROM_SKU_CAP 0x045 +#define WPI_EEPROM_CAPABILITIES 0x045 #define WPI_EEPROM_TYPE 0x04a #define WPI_EEPROM_DOMAIN 0x060 #define WPI_EEPROM_BAND1 0x063 @@ -850,7 +651,7 @@ struct wpi_firmware_hdr { 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_IBSS (1 << 1) #define WPI_EEPROM_CHAN_ACTIVE (1 << 3) #define WPI_EEPROM_CHAN_RADAR (1 << 4) @@ -873,17 +674,15 @@ struct wpi_eeprom_group { 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[] = { - /* 20MHz channels, 2GHz band. */ +} wpi_bands[5] = { { 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, @@ -903,16 +702,25 @@ static const struct wpi_chan_band { #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 +static const struct wpi_rate { + uint8_t rate; + uint8_t plcp; +} wpi_rates[WPI_RIDX_MAX + 1] = { + { 12, 0xd }, + { 18, 0xf }, + { 24, 0x5 }, + { 36, 0x7 }, + { 48, 0x9 }, + { 72, 0xb }, + { 96, 0x1 }, + { 108, 0x3 }, + { 2, 10 }, + { 4, 20 }, + { 11, 55 }, + { 22, 110 } }; #define WPI_MAX_PWR_INDEX 77 - /* * RF Tx gain values from highest to lowest power (values obtained from * the reference driver.) @@ -974,7 +782,7 @@ static const struct wpi_pmgt { uint32_t rxtimeout; uint32_t txtimeout; uint32_t intval[5]; - uint8_t skip_dtim; + int skip_dtim; } wpi_pmgt[WPI_NDTIMRANGES][WPI_NPOWERLEVELS] = { /* DTIM <= 10 */ { @@ -1019,7 +827,7 @@ static const char * const wpi_fw_errmsg[] = { #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)) diff --git a/src/add-ons/kernel/drivers/network/wlan/iprowifi3945/dev/pci/if_wpivar.h b/src/add-ons/kernel/drivers/network/wlan/iprowifi3945/dev/pci/if_wpivar.h new file mode 100644 index 0000000000..765e436168 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/iprowifi3945/dev/pci/if_wpivar.h @@ -0,0 +1,208 @@ +/* $OpenBSD: if_wpivar.h,v 1.29 2020/10/11 07:05:29 mpi Exp $ */ + +/*- + * Copyright (c) 2006-2008 + * Damien Bergamini + * + * 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. + */ + +struct wpi_rx_radiotap_header { + struct ieee80211_radiotap_header wr_ihdr; + uint64_t wr_tsft; + uint8_t wr_flags; + uint8_t wr_rate; + uint16_t wr_chan_freq; + uint16_t wr_chan_flags; + int8_t wr_dbm_antsignal; + int8_t wr_dbm_antnoise; + uint8_t wr_antenna; +} __packed; + +#define WPI_RX_RADIOTAP_PRESENT \ + ((1 << IEEE80211_RADIOTAP_TSFT) | \ + (1 << IEEE80211_RADIOTAP_FLAGS) | \ + (1 << IEEE80211_RADIOTAP_RATE) | \ + (1 << IEEE80211_RADIOTAP_CHANNEL) | \ + (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | \ + (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) | \ + (1 << IEEE80211_RADIOTAP_ANTENNA)) + +struct wpi_tx_radiotap_header { + struct ieee80211_radiotap_header wt_ihdr; + uint8_t wt_flags; + uint8_t wt_rate; + uint16_t wt_chan_freq; + uint16_t wt_chan_flags; +} __packed; + +#define WPI_TX_RADIOTAP_PRESENT \ + ((1 << IEEE80211_RADIOTAP_FLAGS) | \ + (1 << IEEE80211_RADIOTAP_RATE) | \ + (1 << IEEE80211_RADIOTAP_CHANNEL)) + +struct wpi_dma_info { + bus_dma_tag_t tag; + bus_dmamap_t map; + bus_dma_segment_t seg; + 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; +}; + +struct wpi_tx_ring { + struct wpi_dma_info desc_dma; + struct wpi_dma_info cmd_dma; + struct wpi_tx_desc *desc; + struct wpi_tx_cmd *cmd; + struct wpi_tx_data data[WPI_TX_RING_COUNT]; + int qid; + int queued; + int cur; +}; + +struct wpi_softc; + +struct wpi_rx_data { + struct mbuf *m; + bus_dmamap_t map; +}; + +struct wpi_rx_ring { + struct wpi_dma_info desc_dma; + uint32_t *desc; + struct wpi_rx_data data[WPI_RX_RING_COUNT]; + int cur; +}; + +struct wpi_node { + struct ieee80211_node ni; /* must be the first */ + struct ieee80211_amrr_node amn; + uint8_t id; + uint8_t ridx[IEEE80211_RATE_MAXSIZE]; +}; + +struct wpi_power_sample { + uint8_t index; + int8_t power; +}; + +struct wpi_power_group { +#define WPI_SAMPLES_COUNT 5 + struct wpi_power_sample samples[WPI_SAMPLES_COUNT]; + uint8_t chan; + int8_t maxpwr; + int16_t temp; +}; + +struct wpi_fw_part { + const uint8_t *text; + uint32_t textsz; + const uint8_t *data; + uint32_t datasz; +}; + +struct wpi_fw_info { + u_char *data; + size_t datalen; + struct wpi_fw_part init; + struct wpi_fw_part main; + struct wpi_fw_part boot; +}; + +struct wpi_softc { +#ifdef __FreeBSD_version + struct device* sc_dev; +#else + struct device sc_dev; +#endif + + struct ieee80211com sc_ic; + int (*sc_newstate)(struct ieee80211com *, + enum ieee80211_state, int); + + struct ieee80211_amrr amrr; + uint8_t fixed_ridx; + + bus_dma_tag_t sc_dmat; + + struct rwlock sc_rwlock; + u_int sc_flags; +#define WPI_FLAG_HAS_5GHZ (1 << 0) + + /* Shared area. */ + struct wpi_dma_info shared_dma; + struct wpi_shared *shared; + + /* Firmware DMA transfer. */ + struct wpi_dma_info fw_dma; + + /* TX/RX rings. */ + struct wpi_tx_ring txq[WPI_NTXQUEUES]; + struct wpi_rx_ring rxq; + + bus_space_tag_t sc_st; + bus_space_handle_t sc_sh; + void *sc_ih; +#ifndef __FreeBSD_version + pci_chipset_tag_t sc_pct; + pcitag_t sc_pcitag; +#endif + bus_size_t sc_sz; + int sc_cap_off; /* PCIe Capabilities. */ + + struct timeout calib_to; + int calib_cnt; + + struct task init_task; + + struct wpi_fw_info fw; + uint32_t errptr; + + struct wpi_rxon rxon; + int temp; + uint32_t qfullmsk; + + uint8_t cap; + uint16_t rev; + uint8_t type; + struct wpi_power_group groups[WPI_POWER_GROUPS_COUNT]; + int8_t maxpwr[IEEE80211_CHAN_MAX]; + + int sc_tx_timer; + +#if NBPFILTER > 0 + caddr_t sc_drvbpf; + + union { + struct wpi_rx_radiotap_header th; + uint8_t pad[IEEE80211_RADIOTAP_HDRLEN]; + } sc_rxtapu; +#define sc_rxtap sc_rxtapu.th + int sc_rxtap_len; + + union { + struct wpi_tx_radiotap_header th; + uint8_t pad[IEEE80211_RADIOTAP_HDRLEN]; + } sc_txtapu; +#define sc_txtap sc_txtapu.th + int sc_txtap_len; +#endif +}; diff --git a/src/add-ons/kernel/drivers/network/wlan/iprowifi3945/dev/wpi/if_wpi.c b/src/add-ons/kernel/drivers/network/wlan/iprowifi3945/dev/wpi/if_wpi.c deleted file mode 100644 index 3fffe982b9..0000000000 --- a/src/add-ons/kernel/drivers/network/wlan/iprowifi3945/dev/wpi/if_wpi.c +++ /dev/null @@ -1,5650 +0,0 @@ -/*- - * Copyright (c) 2006,2007 - * Damien Bergamini - * Benjamin Close - * Copyright (c) 2015 Andriy Voskoboinyk - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include -/* - * Driver for Intel PRO/Wireless 3945ABG 802.11 network adapters. - * - * The 3945ABG network adapter doesn't use traditional hardware as - * many other adaptors do. Instead at run time the eeprom is set into a known - * state and told to load boot firmware. The boot firmware loads an init and a - * main binary firmware image into SRAM on the card via DMA. - * Once the firmware is loaded, the driver/hw then - * communicate by way of circular dma rings via the SRAM to the firmware. - * - * There is 6 memory rings. 1 command ring, 1 rx data ring & 4 tx data rings. - * The 4 tx data rings allow for prioritization QoS. - * - * The rx data ring consists of 32 dma buffers. Two registers are used to - * indicate where in the ring the driver and the firmware are up to. The - * driver sets the initial read index (reg1) and the initial write index (reg2), - * the firmware updates the read index (reg1) on rx of a packet and fires an - * interrupt. The driver then processes the buffers starting at reg1 indicating - * to the firmware which buffers have been accessed by updating reg2. At the - * same time allocating new memory for the processed buffer. - * - * A similar thing happens with the tx rings. The difference is the firmware - * stop processing buffers once the queue is full and until confirmation - * of a successful transmition (tx_done) has occurred. - * - * The command ring operates in the same manner as the tx queues. - * - * All communication direct to the card (ie eeprom) is classed as Stage1 - * communication - * - * All communication via the firmware to the card is classed as State2. - * The firmware consists of 2 parts. A bootstrap firmware and a runtime - * firmware. The bootstrap firmware and runtime firmware are loaded - * from host memory via dma to the card then told to execute. From this point - * on the majority of communications between the driver and the card goes - * via the firmware. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include - -struct wpi_ident { - uint16_t vendor; - uint16_t device; - uint16_t subdevice; - const char *name; -}; - -static const struct wpi_ident wpi_ident_table[] = { - /* The below entries support ABG regardless of the subid */ - { 0x8086, 0x4222, 0x0, "Intel(R) PRO/Wireless 3945ABG" }, - { 0x8086, 0x4227, 0x0, "Intel(R) PRO/Wireless 3945ABG" }, - /* The below entries only support BG */ - { 0x8086, 0x4222, 0x1005, "Intel(R) PRO/Wireless 3945BG" }, - { 0x8086, 0x4222, 0x1034, "Intel(R) PRO/Wireless 3945BG" }, - { 0x8086, 0x4227, 0x1014, "Intel(R) PRO/Wireless 3945BG" }, - { 0x8086, 0x4222, 0x1044, "Intel(R) PRO/Wireless 3945BG" }, - { 0, 0, 0, NULL } -}; - -static int wpi_probe(device_t); -static int wpi_attach(device_t); -static void wpi_radiotap_attach(struct wpi_softc *); -static void wpi_sysctlattach(struct wpi_softc *); -static void wpi_init_beacon(struct wpi_vap *); -static struct ieee80211vap *wpi_vap_create(struct ieee80211com *, - const char [IFNAMSIZ], int, enum ieee80211_opmode, int, - const uint8_t [IEEE80211_ADDR_LEN], - const uint8_t [IEEE80211_ADDR_LEN]); -static void wpi_vap_delete(struct ieee80211vap *); -static int wpi_detach(device_t); -static int wpi_shutdown(device_t); -static int wpi_suspend(device_t); -static int wpi_resume(device_t); -static int wpi_nic_lock(struct wpi_softc *); -static int wpi_read_prom_data(struct wpi_softc *, uint32_t, void *, int); -static void wpi_dma_map_addr(void *, bus_dma_segment_t *, int, int); -static int wpi_dma_contig_alloc(struct wpi_softc *, struct wpi_dma_info *, - void **, bus_size_t, bus_size_t); -static void wpi_dma_contig_free(struct wpi_dma_info *); -static int wpi_alloc_shared(struct wpi_softc *); -static void wpi_free_shared(struct wpi_softc *); -static int wpi_alloc_fwmem(struct wpi_softc *); -static void wpi_free_fwmem(struct wpi_softc *); -static int wpi_alloc_rx_ring(struct wpi_softc *); -static void wpi_update_rx_ring(struct wpi_softc *); -static void wpi_update_rx_ring_ps(struct wpi_softc *); -static void wpi_reset_rx_ring(struct wpi_softc *); -static void wpi_free_rx_ring(struct wpi_softc *); -static int wpi_alloc_tx_ring(struct wpi_softc *, struct wpi_tx_ring *, - uint8_t); -static void wpi_update_tx_ring(struct wpi_softc *, struct wpi_tx_ring *); -static void wpi_update_tx_ring_ps(struct wpi_softc *, - struct wpi_tx_ring *); -static void wpi_reset_tx_ring(struct wpi_softc *, struct wpi_tx_ring *); -static void wpi_free_tx_ring(struct wpi_softc *, struct wpi_tx_ring *); -static int wpi_read_eeprom(struct wpi_softc *, - uint8_t macaddr[IEEE80211_ADDR_LEN]); -static uint32_t wpi_eeprom_channel_flags(struct wpi_eeprom_chan *); -static void wpi_read_eeprom_band(struct wpi_softc *, uint8_t, int, int *, - struct ieee80211_channel[]); -static int wpi_read_eeprom_channels(struct wpi_softc *, uint8_t); -static struct wpi_eeprom_chan *wpi_find_eeprom_channel(struct wpi_softc *, - struct ieee80211_channel *); -static void wpi_getradiocaps(struct ieee80211com *, int, int *, - struct ieee80211_channel[]); -static int wpi_setregdomain(struct ieee80211com *, - struct ieee80211_regdomain *, int, - struct ieee80211_channel[]); -static int wpi_read_eeprom_group(struct wpi_softc *, uint8_t); -static struct ieee80211_node *wpi_node_alloc(struct ieee80211vap *, - const uint8_t mac[IEEE80211_ADDR_LEN]); -static void wpi_node_free(struct ieee80211_node *); -static void wpi_ibss_recv_mgmt(struct ieee80211_node *, struct mbuf *, int, - const struct ieee80211_rx_stats *, - int, int); -static void wpi_restore_node(void *, struct ieee80211_node *); -static void wpi_restore_node_table(struct wpi_softc *, struct wpi_vap *); -static int wpi_newstate(struct ieee80211vap *, enum ieee80211_state, int); -static void wpi_calib_timeout(void *); -static void wpi_rx_done(struct wpi_softc *, struct wpi_rx_desc *, - struct wpi_rx_data *); -static void wpi_rx_statistics(struct wpi_softc *, struct wpi_rx_desc *, - struct wpi_rx_data *); -static void wpi_tx_done(struct wpi_softc *, struct wpi_rx_desc *); -static void wpi_cmd_done(struct wpi_softc *, struct wpi_rx_desc *); -static void wpi_notif_intr(struct wpi_softc *); -static void wpi_wakeup_intr(struct wpi_softc *); -#ifdef WPI_DEBUG -static void wpi_debug_registers(struct wpi_softc *); -#endif -static void wpi_fatal_intr(struct wpi_softc *); -static void wpi_intr(void *); -static void wpi_free_txfrags(struct wpi_softc *, uint16_t); -static int wpi_cmd2(struct wpi_softc *, struct wpi_buf *); -static int wpi_tx_data(struct wpi_softc *, struct mbuf *, - struct ieee80211_node *); -static int wpi_tx_data_raw(struct wpi_softc *, struct mbuf *, - struct ieee80211_node *, - const struct ieee80211_bpf_params *); -static int wpi_raw_xmit(struct ieee80211_node *, struct mbuf *, - const struct ieee80211_bpf_params *); -static int wpi_transmit(struct ieee80211com *, struct mbuf *); -static void wpi_watchdog_rfkill(void *); -static void wpi_scan_timeout(void *); -static void wpi_tx_timeout(void *); -static void wpi_parent(struct ieee80211com *); -static int wpi_cmd(struct wpi_softc *, uint8_t, const void *, uint16_t, - int); -static int wpi_mrr_setup(struct wpi_softc *); -static int wpi_add_node(struct wpi_softc *, struct ieee80211_node *); -static int wpi_add_broadcast_node(struct wpi_softc *, int); -static int wpi_add_ibss_node(struct wpi_softc *, struct ieee80211_node *); -static void wpi_del_node(struct wpi_softc *, struct ieee80211_node *); -static int wpi_updateedca(struct ieee80211com *); -static void wpi_set_promisc(struct wpi_softc *); -static void wpi_update_promisc(struct ieee80211com *); -static void wpi_update_mcast(struct ieee80211com *); -static void wpi_set_led(struct wpi_softc *, uint8_t, uint8_t, uint8_t); -static int wpi_set_timing(struct wpi_softc *, struct ieee80211_node *); -static void wpi_power_calibration(struct wpi_softc *); -static int wpi_set_txpower(struct wpi_softc *, int); -static int wpi_get_power_index(struct wpi_softc *, - struct wpi_power_group *, uint8_t, int, int); -static int wpi_set_pslevel(struct wpi_softc *, uint8_t, int, int); -static int wpi_send_btcoex(struct wpi_softc *); -static int wpi_send_rxon(struct wpi_softc *, int, int); -static int wpi_config(struct wpi_softc *); -static uint16_t wpi_get_active_dwell_time(struct wpi_softc *, - struct ieee80211_channel *, uint8_t); -static uint16_t wpi_limit_dwell(struct wpi_softc *, uint16_t); -static uint16_t wpi_get_passive_dwell_time(struct wpi_softc *, - struct ieee80211_channel *); -static uint32_t wpi_get_scan_pause_time(uint32_t, uint16_t); -static int wpi_scan(struct wpi_softc *, struct ieee80211_channel *); -static int wpi_auth(struct wpi_softc *, struct ieee80211vap *); -static int wpi_config_beacon(struct wpi_vap *); -static int wpi_setup_beacon(struct wpi_softc *, struct ieee80211_node *); -static void wpi_update_beacon(struct ieee80211vap *, int); -static void wpi_newassoc(struct ieee80211_node *, int); -static int wpi_run(struct wpi_softc *, struct ieee80211vap *); -static int wpi_load_key(struct ieee80211_node *, - const struct ieee80211_key *); -static void wpi_load_key_cb(void *, struct ieee80211_node *); -static int wpi_set_global_keys(struct ieee80211_node *); -static int wpi_del_key(struct ieee80211_node *, - const struct ieee80211_key *); -static void wpi_del_key_cb(void *, struct ieee80211_node *); -static int wpi_process_key(struct ieee80211vap *, - const struct ieee80211_key *, int); -static int wpi_key_set(struct ieee80211vap *, - const struct ieee80211_key *); -static int wpi_key_delete(struct ieee80211vap *, - const struct ieee80211_key *); -static int wpi_post_alive(struct wpi_softc *); -static int wpi_load_bootcode(struct wpi_softc *, const uint8_t *, - uint32_t); -static int wpi_load_firmware(struct wpi_softc *); -static int wpi_read_firmware(struct wpi_softc *); -static void wpi_unload_firmware(struct wpi_softc *); -static int wpi_clock_wait(struct wpi_softc *); -static int wpi_apm_init(struct wpi_softc *); -static void wpi_apm_stop_master(struct wpi_softc *); -static void wpi_apm_stop(struct wpi_softc *); -static void wpi_nic_config(struct wpi_softc *); -static int wpi_hw_init(struct wpi_softc *); -static void wpi_hw_stop(struct wpi_softc *); -static void wpi_radio_on(void *, int); -static void wpi_radio_off(void *, int); -static int wpi_init(struct wpi_softc *); -static void wpi_stop_locked(struct wpi_softc *); -static void wpi_stop(struct wpi_softc *); -static void wpi_scan_start(struct ieee80211com *); -static void wpi_scan_end(struct ieee80211com *); -static void wpi_set_channel(struct ieee80211com *); -static void wpi_scan_curchan(struct ieee80211_scan_state *, unsigned long); -static void wpi_scan_mindwell(struct ieee80211_scan_state *); - -static device_method_t wpi_methods[] = { - /* Device interface */ - DEVMETHOD(device_probe, wpi_probe), - DEVMETHOD(device_attach, wpi_attach), - DEVMETHOD(device_detach, wpi_detach), - DEVMETHOD(device_shutdown, wpi_shutdown), - DEVMETHOD(device_suspend, wpi_suspend), - DEVMETHOD(device_resume, wpi_resume), - - DEVMETHOD_END -}; - -static driver_t wpi_driver = { - "wpi", - wpi_methods, - sizeof (struct wpi_softc) -}; - -DRIVER_MODULE(wpi, pci, wpi_driver, NULL, NULL); - -MODULE_VERSION(wpi, 1); - -MODULE_DEPEND(wpi, pci, 1, 1, 1); -MODULE_DEPEND(wpi, wlan, 1, 1, 1); -MODULE_DEPEND(wpi, firmware, 1, 1, 1); - -static int -wpi_probe(device_t dev) -{ - const struct wpi_ident *ident; - - for (ident = wpi_ident_table; ident->name != NULL; ident++) { - if (pci_get_vendor(dev) == ident->vendor && - pci_get_device(dev) == ident->device) { - device_set_desc(dev, ident->name); - return (BUS_PROBE_DEFAULT); - } - } - return ENXIO; -} - -static int -wpi_attach(device_t dev) -{ - struct wpi_softc *sc = (struct wpi_softc *)device_get_softc(dev); - struct ieee80211com *ic; - uint8_t i; - int error, rid; -#ifdef WPI_DEBUG - int supportsa = 1; - const struct wpi_ident *ident; -#endif - - sc->sc_dev = dev; - -#ifdef WPI_DEBUG - error = resource_int_value(device_get_name(sc->sc_dev), - device_get_unit(sc->sc_dev), "debug", &(sc->sc_debug)); - if (error != 0) - sc->sc_debug = 0; -#else - sc->sc_debug = 0; -#endif - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); - - /* - * Get the offset of the PCI Express Capability Structure in PCI - * Configuration Space. - */ - 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 - * this is one such card. A 0x0 in the subdevice table indicates - * the entire subdevice range is to be ignored. - */ -#ifdef WPI_DEBUG - for (ident = wpi_ident_table; ident->name != NULL; ident++) { - if (ident->subdevice && - pci_get_subdevice(dev) == ident->subdevice) { - supportsa = 0; - break; - } - } -#endif - - /* Clear device-specific "PCI retry timeout" register (41h). */ - pci_write_config(dev, 0x41, 0, 1); - - /* Enable bus-mastering. */ - pci_enable_busmaster(dev); - - rid = PCIR_BAR(0); - sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, - RF_ACTIVE); - if (sc->mem == NULL) { - device_printf(dev, "can't map mem space\n"); - return ENOMEM; - } - sc->sc_st = rman_get_bustag(sc->mem); - sc->sc_sh = rman_get_bushandle(sc->mem); - - rid = 1; - if (pci_alloc_msi(dev, &rid) == 0) - rid = 1; - else - rid = 0; - /* Install interrupt handler. */ - sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE | - (rid != 0 ? 0 : RF_SHAREABLE)); - if (sc->irq == NULL) { - device_printf(dev, "can't map interrupt\n"); - error = ENOMEM; - goto fail; - } - - WPI_LOCK_INIT(sc); - WPI_TX_LOCK_INIT(sc); - WPI_RXON_LOCK_INIT(sc); - WPI_NT_LOCK_INIT(sc); - WPI_TXQ_LOCK_INIT(sc); - WPI_TXQ_STATE_LOCK_INIT(sc); - - /* Allocate DMA memory for firmware transfers. */ - if ((error = wpi_alloc_fwmem(sc)) != 0) { - device_printf(dev, - "could not allocate memory for firmware, error %d\n", - error); - goto fail; - } - - /* Allocate shared page. */ - if ((error = wpi_alloc_shared(sc)) != 0) { - device_printf(dev, "could not allocate shared page\n"); - goto fail; - } - - /* Allocate TX rings - 4 for QoS purposes, 1 for commands. */ - for (i = 0; i < WPI_DRV_NTXQUEUES; i++) { - if ((error = wpi_alloc_tx_ring(sc, &sc->txq[i], i)) != 0) { - device_printf(dev, - "could not allocate TX ring %d, error %d\n", i, - error); - goto fail; - } - } - - /* Allocate RX ring. */ - if ((error = wpi_alloc_rx_ring(sc)) != 0) { - device_printf(dev, "could not allocate RX ring, error %d\n", - error); - goto fail; - } - - /* Clear pending interrupts. */ - WPI_WRITE(sc, WPI_INT, 0xffffffff); - - ic = &sc->sc_ic; - ic->ic_softc = sc; - ic->ic_name = device_get_nameunit(dev); - ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ - ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ - - /* Set device capabilities. */ - ic->ic_caps = - IEEE80211_C_STA /* station mode supported */ - | IEEE80211_C_IBSS /* IBSS mode supported */ - | IEEE80211_C_HOSTAP /* Host access point mode */ - | IEEE80211_C_MONITOR /* monitor mode supported */ - | IEEE80211_C_AHDEMO /* adhoc demo mode */ - | IEEE80211_C_BGSCAN /* capable of bg scanning */ - | IEEE80211_C_TXFRAG /* handle tx frags */ - | IEEE80211_C_TXPMGT /* tx power management */ - | IEEE80211_C_SHSLOT /* short slot time supported */ - | IEEE80211_C_WPA /* 802.11i */ - | IEEE80211_C_SHPREAMBLE /* short preamble supported */ - | IEEE80211_C_WME /* 802.11e */ - | IEEE80211_C_PMGT /* Station-side power mgmt */ - ; - - ic->ic_cryptocaps = - IEEE80211_CRYPTO_AES_CCM; - - /* - * Read in the eeprom and also setup the channels for - * net80211. We don't set the rates as net80211 does this for us - */ - if ((error = wpi_read_eeprom(sc, ic->ic_macaddr)) != 0) { - device_printf(dev, "could not read EEPROM, error %d\n", - error); - goto fail; - } - -#ifdef WPI_DEBUG - if (bootverbose) { - device_printf(sc->sc_dev, "Regulatory Domain: %.4s\n", - sc->domain); - device_printf(sc->sc_dev, "Hardware Type: %c\n", - sc->type > 1 ? 'B': '?'); - device_printf(sc->sc_dev, "Hardware Revision: %c\n", - ((sc->rev & 0xf0) == 0xd0) ? 'D': '?'); - device_printf(sc->sc_dev, "SKU %s support 802.11a\n", - supportsa ? "does" : "does not"); - - /* XXX hw_config uses the PCIDEV for the Hardware rev. Must - check what sc->rev really represents - benjsc 20070615 */ - } -#endif - - ieee80211_ifattach(ic); - ic->ic_vap_create = wpi_vap_create; - ic->ic_vap_delete = wpi_vap_delete; - ic->ic_parent = wpi_parent; - ic->ic_raw_xmit = wpi_raw_xmit; - ic->ic_transmit = wpi_transmit; - ic->ic_node_alloc = wpi_node_alloc; - sc->sc_node_free = ic->ic_node_free; - ic->ic_node_free = wpi_node_free; - ic->ic_wme.wme_update = wpi_updateedca; - ic->ic_update_promisc = wpi_update_promisc; - ic->ic_update_mcast = wpi_update_mcast; - ic->ic_newassoc = wpi_newassoc; - ic->ic_scan_start = wpi_scan_start; - ic->ic_scan_end = wpi_scan_end; - ic->ic_set_channel = wpi_set_channel; - ic->ic_scan_curchan = wpi_scan_curchan; - ic->ic_scan_mindwell = wpi_scan_mindwell; - ic->ic_getradiocaps = wpi_getradiocaps; - ic->ic_setregdomain = wpi_setregdomain; - - sc->sc_update_rx_ring = wpi_update_rx_ring; - sc->sc_update_tx_ring = wpi_update_tx_ring; - - wpi_radiotap_attach(sc); - - /* Setup Tx status flags (constant). */ - sc->sc_txs.flags = IEEE80211_RATECTL_STATUS_PKTLEN | - IEEE80211_RATECTL_STATUS_SHORT_RETRY | - IEEE80211_RATECTL_STATUS_LONG_RETRY; - - callout_init_mtx(&sc->calib_to, &sc->rxon_mtx, 0); - callout_init_mtx(&sc->scan_timeout, &sc->rxon_mtx, 0); - callout_init_mtx(&sc->tx_timeout, &sc->txq_state_mtx, 0); - callout_init_mtx(&sc->watchdog_rfkill, &sc->sc_mtx, 0); - TASK_INIT(&sc->sc_radiooff_task, 0, wpi_radio_off, sc); - TASK_INIT(&sc->sc_radioon_task, 0, wpi_radio_on, sc); - - wpi_sysctlattach(sc); - - /* - * Hook our interrupt after all initialization is complete. - */ - 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, "can't establish interrupt, error %d\n", - error); - goto fail; - } - - if (bootverbose) - ieee80211_announce(ic); - -#ifdef WPI_DEBUG - if (sc->sc_debug & WPI_DEBUG_HW) - ieee80211_announce_channels(ic); -#endif - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); - return 0; - -fail: wpi_detach(dev); - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__); - return error; -} - -/* - * Attach the interface to 802.11 radiotap. - */ -static void -wpi_radiotap_attach(struct wpi_softc *sc) -{ - struct wpi_rx_radiotap_header *rxtap = &sc->sc_rxtap; - struct wpi_tx_radiotap_header *txtap = &sc->sc_txtap; - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); - ieee80211_radiotap_attach(&sc->sc_ic, - &txtap->wt_ihdr, sizeof(*txtap), WPI_TX_RADIOTAP_PRESENT, - &rxtap->wr_ihdr, sizeof(*rxtap), WPI_RX_RADIOTAP_PRESENT); - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); -} - -static void -wpi_sysctlattach(struct wpi_softc *sc) -{ -#ifdef WPI_DEBUG - struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); - struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); - - SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, - "debug", CTLFLAG_RW, &sc->sc_debug, sc->sc_debug, - "control debugging printfs"); -#endif -} - -static void -wpi_init_beacon(struct wpi_vap *wvp) -{ - struct wpi_buf *bcn = &wvp->wv_bcbuf; - struct wpi_cmd_beacon *cmd = (struct wpi_cmd_beacon *)&bcn->data; - - cmd->id = WPI_ID_BROADCAST; - cmd->ofdm_mask = 0xff; - cmd->cck_mask = 0x0f; - cmd->lifetime = htole32(WPI_LIFETIME_INFINITE); - - /* - * XXX WPI_TX_AUTO_SEQ seems to be ignored - workaround this issue - * XXX by using WPI_TX_NEED_ACK instead (with some side effects). - */ - cmd->flags = htole32(WPI_TX_NEED_ACK | WPI_TX_INSERT_TSTAMP); - - bcn->code = WPI_CMD_SET_BEACON; - bcn->ac = WPI_CMD_QUEUE_NUM; - bcn->size = sizeof(struct wpi_cmd_beacon); -} - -static struct ieee80211vap * -wpi_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, - enum ieee80211_opmode opmode, int flags, - const uint8_t bssid[IEEE80211_ADDR_LEN], - const uint8_t mac[IEEE80211_ADDR_LEN]) -{ - struct wpi_vap *wvp; - struct ieee80211vap *vap; - - if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ - return NULL; - - wvp = malloc(sizeof(struct wpi_vap), M_80211_VAP, M_WAITOK | M_ZERO); - vap = &wvp->wv_vap; - ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid); - - if (opmode == IEEE80211_M_IBSS || opmode == IEEE80211_M_HOSTAP) { - WPI_VAP_LOCK_INIT(wvp); - wpi_init_beacon(wvp); - } - - /* Override with driver methods. */ - vap->iv_key_set = wpi_key_set; - vap->iv_key_delete = wpi_key_delete; - if (opmode == IEEE80211_M_IBSS) { - wvp->wv_recv_mgmt = vap->iv_recv_mgmt; - vap->iv_recv_mgmt = wpi_ibss_recv_mgmt; - } - wvp->wv_newstate = vap->iv_newstate; - vap->iv_newstate = wpi_newstate; - vap->iv_update_beacon = wpi_update_beacon; - vap->iv_max_aid = WPI_ID_IBSS_MAX - WPI_ID_IBSS_MIN + 1; - - ieee80211_ratectl_init(vap); - /* Complete setup. */ - ieee80211_vap_attach(vap, ieee80211_media_change, - ieee80211_media_status, mac); - ic->ic_opmode = opmode; - return vap; -} - -static void -wpi_vap_delete(struct ieee80211vap *vap) -{ - struct wpi_vap *wvp = WPI_VAP(vap); - struct wpi_buf *bcn = &wvp->wv_bcbuf; - enum ieee80211_opmode opmode = vap->iv_opmode; - - ieee80211_ratectl_deinit(vap); - ieee80211_vap_detach(vap); - - if (opmode == IEEE80211_M_IBSS || opmode == IEEE80211_M_HOSTAP) { - if (bcn->m != NULL) - m_freem(bcn->m); - - WPI_VAP_LOCK_DESTROY(wvp); - } - - free(wvp, M_80211_VAP); -} - -static int -wpi_detach(device_t dev) -{ - struct wpi_softc *sc = device_get_softc(dev); - struct ieee80211com *ic = &sc->sc_ic; - uint8_t qid; - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); - - if (ic->ic_vap_create == wpi_vap_create) { - ieee80211_draintask(ic, &sc->sc_radioon_task); - ieee80211_draintask(ic, &sc->sc_radiooff_task); - - wpi_stop(sc); - - callout_drain(&sc->watchdog_rfkill); - callout_drain(&sc->tx_timeout); - callout_drain(&sc->scan_timeout); - callout_drain(&sc->calib_to); - ieee80211_ifdetach(ic); - } - - /* Uninstall interrupt handler. */ - if (sc->irq != NULL) { - bus_teardown_intr(dev, sc->irq, sc->sc_ih); - bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(sc->irq), - sc->irq); - pci_release_msi(dev); - } - - if (sc->txq[0].data_dmat) { - /* Free DMA resources. */ - for (qid = 0; qid < WPI_DRV_NTXQUEUES; qid++) - wpi_free_tx_ring(sc, &sc->txq[qid]); - - wpi_free_rx_ring(sc); - wpi_free_shared(sc); - } - - if (sc->fw_dma.tag) - wpi_free_fwmem(sc); - - if (sc->mem != NULL) - bus_release_resource(dev, SYS_RES_MEMORY, - rman_get_rid(sc->mem), sc->mem); - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); - WPI_TXQ_STATE_LOCK_DESTROY(sc); - WPI_TXQ_LOCK_DESTROY(sc); - WPI_NT_LOCK_DESTROY(sc); - WPI_RXON_LOCK_DESTROY(sc); - WPI_TX_LOCK_DESTROY(sc); - WPI_LOCK_DESTROY(sc); - return 0; -} - -static int -wpi_shutdown(device_t dev) -{ - struct wpi_softc *sc = device_get_softc(dev); - - wpi_stop(sc); - return 0; -} - -static int -wpi_suspend(device_t dev) -{ - struct wpi_softc *sc = device_get_softc(dev); - struct ieee80211com *ic = &sc->sc_ic; - - ieee80211_suspend_all(ic); - return 0; -} - -static int -wpi_resume(device_t dev) -{ - struct wpi_softc *sc = device_get_softc(dev); - struct ieee80211com *ic = &sc->sc_ic; - - /* Clear device-specific "PCI retry timeout" register (41h). */ - pci_write_config(dev, 0x41, 0, 1); - - ieee80211_resume_all(ic); - return 0; -} - -/* - * Grab exclusive access to NIC memory. - */ -static int -wpi_nic_lock(struct wpi_softc *sc) -{ - int ntries; - - /* Request exclusive access to NIC. */ - WPI_SETBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_MAC_ACCESS_REQ); - - /* Spin until we actually get the lock. */ - for (ntries = 0; ntries < 1000; ntries++) { - if ((WPI_READ(sc, WPI_GP_CNTRL) & - (WPI_GP_CNTRL_MAC_ACCESS_ENA | WPI_GP_CNTRL_SLEEP)) == - WPI_GP_CNTRL_MAC_ACCESS_ENA) - return 0; - DELAY(10); - } - - device_printf(sc->sc_dev, "could not lock memory\n"); - - return ETIMEDOUT; -} - -/* - * Release lock on NIC memory. - */ -static __inline void -wpi_nic_unlock(struct wpi_softc *sc) -{ - WPI_CLRBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_MAC_ACCESS_REQ); -} - -static __inline uint32_t -wpi_prph_read(struct wpi_softc *sc, uint32_t addr) -{ - WPI_WRITE(sc, WPI_PRPH_RADDR, WPI_PRPH_DWORD | addr); - WPI_BARRIER_READ_WRITE(sc); - return WPI_READ(sc, WPI_PRPH_RDATA); -} - -static __inline void -wpi_prph_write(struct wpi_softc *sc, uint32_t addr, uint32_t data) -{ - WPI_WRITE(sc, WPI_PRPH_WADDR, WPI_PRPH_DWORD | addr); - WPI_BARRIER_WRITE(sc); - WPI_WRITE(sc, WPI_PRPH_WDATA, data); -} - -static __inline void -wpi_prph_setbits(struct wpi_softc *sc, uint32_t addr, uint32_t mask) -{ - wpi_prph_write(sc, addr, wpi_prph_read(sc, addr) | mask); -} - -static __inline void -wpi_prph_clrbits(struct wpi_softc *sc, uint32_t addr, uint32_t mask) -{ - wpi_prph_write(sc, addr, wpi_prph_read(sc, addr) & ~mask); -} - -static __inline void -wpi_prph_write_region_4(struct wpi_softc *sc, uint32_t addr, - const uint32_t *data, uint32_t count) -{ - for (; count != 0; count--, data++, addr += 4) - wpi_prph_write(sc, addr, *data); -} - -static __inline uint32_t -wpi_mem_read(struct wpi_softc *sc, uint32_t addr) -{ - WPI_WRITE(sc, WPI_MEM_RADDR, addr); - WPI_BARRIER_READ_WRITE(sc); - return WPI_READ(sc, WPI_MEM_RDATA); -} - -static __inline void -wpi_mem_read_region_4(struct wpi_softc *sc, uint32_t addr, uint32_t *data, - int count) -{ - for (; count > 0; count--, addr += 4) - *data++ = wpi_mem_read(sc, addr); -} - -static int -wpi_read_prom_data(struct wpi_softc *sc, uint32_t addr, void *data, int count) -{ - uint8_t *out = data; - uint32_t val; - int error, ntries; - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); - - if ((error = wpi_nic_lock(sc)) != 0) - return error; - - for (; count > 0; count -= 2, addr++) { - WPI_WRITE(sc, WPI_EEPROM, addr << 2); - for (ntries = 0; ntries < 10; ntries++) { - val = WPI_READ(sc, WPI_EEPROM); - if (val & WPI_EEPROM_READ_VALID) - break; - DELAY(5); - } - if (ntries == 10) { - device_printf(sc->sc_dev, - "timeout reading ROM at 0x%x\n", addr); - return ETIMEDOUT; - } - *out++= val >> 16; - if (count > 1) - *out ++= val >> 24; - } - - wpi_nic_unlock(sc); - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); - - return 0; -} - -static void -wpi_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) -{ - if (error != 0) - return; - KASSERT(nsegs == 1, ("too many DMA segments, %d should be 1", nsegs)); - *(bus_addr_t *)arg = segs[0].ds_addr; -} - -/* - * Allocates a contiguous block of dma memory of the requested size and - * alignment. - */ -static int -wpi_dma_contig_alloc(struct wpi_softc *sc, struct wpi_dma_info *dma, - void **kvap, bus_size_t size, bus_size_t alignment) -{ - int error; - - dma->tag = NULL; - dma->size = size; - - error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), alignment, - 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, size, - 1, size, 0, NULL, NULL, &dma->tag); - if (error != 0) - goto fail; - - error = bus_dmamem_alloc(dma->tag, (void **)&dma->vaddr, - BUS_DMA_NOWAIT | BUS_DMA_ZERO | BUS_DMA_COHERENT, &dma->map); - if (error != 0) - goto fail; - - error = bus_dmamap_load(dma->tag, dma->map, dma->vaddr, size, - wpi_dma_map_addr, &dma->paddr, BUS_DMA_NOWAIT); - if (error != 0) - goto fail; - - bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); - - if (kvap != NULL) - *kvap = dma->vaddr; - - return 0; - -fail: wpi_dma_contig_free(dma); - return error; -} - -static void -wpi_dma_contig_free(struct wpi_dma_info *dma) -{ - if (dma->vaddr != NULL) { - bus_dmamap_sync(dma->tag, dma->map, - BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); - bus_dmamap_unload(dma->tag, dma->map); - bus_dmamem_free(dma->tag, dma->vaddr, dma->map); - dma->vaddr = NULL; - } - if (dma->tag != NULL) { - bus_dma_tag_destroy(dma->tag); - dma->tag = NULL; - } -} - -/* - * Allocate a shared page between host and NIC. - */ -static int -wpi_alloc_shared(struct wpi_softc *sc) -{ - /* Shared buffer must be aligned on a 4KB boundary. */ - return wpi_dma_contig_alloc(sc, &sc->shared_dma, - (void **)&sc->shared, sizeof (struct wpi_shared), 4096); -} - -static void -wpi_free_shared(struct wpi_softc *sc) -{ - wpi_dma_contig_free(&sc->shared_dma); -} - -/* - * Allocate DMA-safe memory for firmware transfer. - */ -static int -wpi_alloc_fwmem(struct wpi_softc *sc) -{ - /* Must be aligned on a 16-byte boundary. */ - return wpi_dma_contig_alloc(sc, &sc->fw_dma, NULL, - WPI_FW_TEXT_MAXSZ + WPI_FW_DATA_MAXSZ, 16); -} - -static void -wpi_free_fwmem(struct wpi_softc *sc) -{ - wpi_dma_contig_free(&sc->fw_dma); -} - -static int -wpi_alloc_rx_ring(struct wpi_softc *sc) -{ - struct wpi_rx_ring *ring = &sc->rxq; - bus_size_t size; - int i, error; - - ring->cur = 0; - ring->update = 0; - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); - - /* Allocate RX descriptors (16KB aligned.) */ - size = WPI_RX_RING_COUNT * sizeof (uint32_t); - error = wpi_dma_contig_alloc(sc, &ring->desc_dma, - (void **)&ring->desc, size, WPI_RING_DMA_ALIGN); - if (error != 0) { - device_printf(sc->sc_dev, - "%s: could not allocate RX ring DMA memory, error %d\n", - __func__, error); - goto fail; - } - - /* Create RX buffer DMA tag. */ - error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, - BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, - MJUMPAGESIZE, 1, MJUMPAGESIZE, 0, NULL, NULL, &ring->data_dmat); - if (error != 0) { - device_printf(sc->sc_dev, - "%s: could not create RX buf DMA tag, error %d\n", - __func__, error); - goto fail; - } - - /* - * Allocate and map RX buffers. - */ - for (i = 0; i < WPI_RX_RING_COUNT; i++) { - struct wpi_rx_data *data = &ring->data[i]; - bus_addr_t paddr; - - error = bus_dmamap_create(ring->data_dmat, 0, &data->map); - if (error != 0) { - device_printf(sc->sc_dev, - "%s: could not create RX buf DMA map, error %d\n", - __func__, error); - goto fail; - } - - data->m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE); - if (data->m == NULL) { - device_printf(sc->sc_dev, - "%s: could not allocate RX mbuf\n", __func__); - error = ENOBUFS; - goto fail; - } - - error = bus_dmamap_load(ring->data_dmat, data->map, - mtod(data->m, void *), MJUMPAGESIZE, wpi_dma_map_addr, - &paddr, BUS_DMA_NOWAIT); - if (error != 0 && error != EFBIG) { - device_printf(sc->sc_dev, - "%s: can't map mbuf (error %d)\n", __func__, - error); - goto fail; - } - - /* Set physical address of RX buffer. */ - ring->desc[i] = htole32(paddr); - } - - bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, - BUS_DMASYNC_PREWRITE); - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); - - return 0; - -fail: wpi_free_rx_ring(sc); - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__); - - return error; -} - -static void -wpi_update_rx_ring(struct wpi_softc *sc) -{ - WPI_WRITE(sc, WPI_FH_RX_WPTR, sc->rxq.cur & ~7); -} - -static void -wpi_update_rx_ring_ps(struct wpi_softc *sc) -{ - struct wpi_rx_ring *ring = &sc->rxq; - - if (ring->update != 0) { - /* Wait for INT_WAKEUP event. */ - return; - } - - WPI_TXQ_LOCK(sc); - WPI_SETBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_MAC_ACCESS_REQ); - if (WPI_READ(sc, WPI_GP_CNTRL) & WPI_GP_CNTRL_SLEEP) { - DPRINTF(sc, WPI_DEBUG_PWRSAVE, "%s: wakeup request\n", - __func__); - ring->update = 1; - } else { - wpi_update_rx_ring(sc); - WPI_CLRBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_MAC_ACCESS_REQ); - } - WPI_TXQ_UNLOCK(sc); -} - -static void -wpi_reset_rx_ring(struct wpi_softc *sc) -{ - struct wpi_rx_ring *ring = &sc->rxq; - int ntries; - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); - - if (wpi_nic_lock(sc) == 0) { - WPI_WRITE(sc, WPI_FH_RX_CONFIG, 0); - for (ntries = 0; ntries < 1000; ntries++) { - if (WPI_READ(sc, WPI_FH_RX_STATUS) & - WPI_FH_RX_STATUS_IDLE) - break; - DELAY(10); - } - wpi_nic_unlock(sc); - } - - ring->cur = 0; - ring->update = 0; -} - -static void -wpi_free_rx_ring(struct wpi_softc *sc) -{ - struct wpi_rx_ring *ring = &sc->rxq; - int i; - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); - - wpi_dma_contig_free(&ring->desc_dma); - - for (i = 0; i < WPI_RX_RING_COUNT; i++) { - struct wpi_rx_data *data = &ring->data[i]; - - if (data->m != NULL) { - bus_dmamap_sync(ring->data_dmat, data->map, - BUS_DMASYNC_POSTREAD); - bus_dmamap_unload(ring->data_dmat, data->map); - m_freem(data->m); - data->m = NULL; - } - if (data->map != NULL) - bus_dmamap_destroy(ring->data_dmat, data->map); - } - if (ring->data_dmat != NULL) { - bus_dma_tag_destroy(ring->data_dmat); - ring->data_dmat = NULL; - } -} - -static int -wpi_alloc_tx_ring(struct wpi_softc *sc, struct wpi_tx_ring *ring, uint8_t qid) -{ - bus_addr_t paddr; - bus_size_t size; - int i, error; - - ring->qid = qid; - ring->queued = 0; - ring->cur = 0; - ring->pending = 0; - ring->update = 0; - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); - - /* Allocate TX descriptors (16KB aligned.) */ - size = WPI_TX_RING_COUNT * sizeof (struct wpi_tx_desc); - error = wpi_dma_contig_alloc(sc, &ring->desc_dma, (void **)&ring->desc, - size, WPI_RING_DMA_ALIGN); - if (error != 0) { - device_printf(sc->sc_dev, - "%s: could not allocate TX ring DMA memory, error %d\n", - __func__, error); - goto fail; - } - - /* Update shared area with ring physical address. */ - sc->shared->txbase[qid] = htole32(ring->desc_dma.paddr); - bus_dmamap_sync(sc->shared_dma.tag, sc->shared_dma.map, - BUS_DMASYNC_PREWRITE); - - size = WPI_TX_RING_COUNT * sizeof (struct wpi_tx_cmd); - error = wpi_dma_contig_alloc(sc, &ring->cmd_dma, (void **)&ring->cmd, - size, 4); - if (error != 0) { - device_printf(sc->sc_dev, - "%s: could not allocate TX cmd DMA memory, error %d\n", - __func__, error); - goto fail; - } - - error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, - BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, - WPI_MAX_SCATTER - 1, MCLBYTES, 0, NULL, NULL, &ring->data_dmat); - if (error != 0) { - device_printf(sc->sc_dev, - "%s: could not create TX buf DMA tag, error %d\n", - __func__, error); - goto fail; - } - - paddr = ring->cmd_dma.paddr; - for (i = 0; i < WPI_TX_RING_COUNT; i++) { - struct wpi_tx_data *data = &ring->data[i]; - - data->cmd_paddr = paddr; - paddr += sizeof (struct wpi_tx_cmd); - - error = bus_dmamap_create(ring->data_dmat, 0, &data->map); - if (error != 0) { - device_printf(sc->sc_dev, - "%s: could not create TX buf DMA map, error %d\n", - __func__, error); - goto fail; - } - } - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); - - return 0; - -fail: wpi_free_tx_ring(sc, ring); - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__); - return error; -} - -static void -wpi_update_tx_ring(struct wpi_softc *sc, struct wpi_tx_ring *ring) -{ - WPI_WRITE(sc, WPI_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur); -} - -static void -wpi_update_tx_ring_ps(struct wpi_softc *sc, struct wpi_tx_ring *ring) -{ - - if (ring->update != 0) { - /* Wait for INT_WAKEUP event. */ - return; - } - - WPI_SETBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_MAC_ACCESS_REQ); - if (WPI_READ(sc, WPI_GP_CNTRL) & WPI_GP_CNTRL_SLEEP) { - DPRINTF(sc, WPI_DEBUG_PWRSAVE, "%s (%d): requesting wakeup\n", - __func__, ring->qid); - ring->update = 1; - } else { - wpi_update_tx_ring(sc, ring); - WPI_CLRBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_MAC_ACCESS_REQ); - } -} - -static void -wpi_reset_tx_ring(struct wpi_softc *sc, struct wpi_tx_ring *ring) -{ - int i; - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); - - for (i = 0; i < WPI_TX_RING_COUNT; i++) { - struct wpi_tx_data *data = &ring->data[i]; - - if (data->m != NULL) { - bus_dmamap_sync(ring->data_dmat, data->map, - BUS_DMASYNC_POSTWRITE); - bus_dmamap_unload(ring->data_dmat, data->map); - m_freem(data->m); - data->m = NULL; - } - if (data->ni != NULL) { - ieee80211_free_node(data->ni); - data->ni = NULL; - } - } - /* Clear TX descriptors. */ - memset(ring->desc, 0, ring->desc_dma.size); - bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, - BUS_DMASYNC_PREWRITE); - ring->queued = 0; - ring->cur = 0; - ring->pending = 0; - ring->update = 0; -} - -static void -wpi_free_tx_ring(struct wpi_softc *sc, struct wpi_tx_ring *ring) -{ - int i; - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); - - wpi_dma_contig_free(&ring->desc_dma); - wpi_dma_contig_free(&ring->cmd_dma); - - for (i = 0; i < WPI_TX_RING_COUNT; i++) { - struct wpi_tx_data *data = &ring->data[i]; - - if (data->m != NULL) { - bus_dmamap_sync(ring->data_dmat, data->map, - BUS_DMASYNC_POSTWRITE); - bus_dmamap_unload(ring->data_dmat, data->map); - m_freem(data->m); - } - if (data->map != NULL) - bus_dmamap_destroy(ring->data_dmat, data->map); - } - if (ring->data_dmat != NULL) { - bus_dma_tag_destroy(ring->data_dmat); - ring->data_dmat = NULL; - } -} - -/* - * Extract various information from EEPROM. - */ -static int -wpi_read_eeprom(struct wpi_softc *sc, uint8_t macaddr[IEEE80211_ADDR_LEN]) -{ -#define WPI_CHK(res) do { \ - if ((error = res) != 0) \ - goto fail; \ -} while (0) - uint8_t i; - int error; - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); - - /* Adapter has to be powered on for EEPROM access to work. */ - if ((error = wpi_apm_init(sc)) != 0) { - device_printf(sc->sc_dev, - "%s: could not power ON adapter, error %d\n", __func__, - error); - return error; - } - - if ((WPI_READ(sc, WPI_EEPROM_GP) & 0x6) == 0) { - device_printf(sc->sc_dev, "bad EEPROM signature\n"); - error = EIO; - goto fail; - } - /* Clear HW ownership of EEPROM. */ - WPI_CLRBITS(sc, WPI_EEPROM_GP, WPI_EEPROM_GP_IF_OWNER); - - /* Read the hardware capabilities, revision and SKU type. */ - WPI_CHK(wpi_read_prom_data(sc, WPI_EEPROM_SKU_CAP, &sc->cap, - sizeof(sc->cap))); - WPI_CHK(wpi_read_prom_data(sc, WPI_EEPROM_REVISION, &sc->rev, - sizeof(sc->rev))); - WPI_CHK(wpi_read_prom_data(sc, WPI_EEPROM_TYPE, &sc->type, - sizeof(sc->type))); - - sc->rev = le16toh(sc->rev); - DPRINTF(sc, WPI_DEBUG_EEPROM, "cap=%x rev=%x type=%x\n", sc->cap, - sc->rev, sc->type); - - /* Read the regulatory domain (4 ASCII characters.) */ - WPI_CHK(wpi_read_prom_data(sc, WPI_EEPROM_DOMAIN, sc->domain, - sizeof(sc->domain))); - - /* Read MAC address. */ - WPI_CHK(wpi_read_prom_data(sc, WPI_EEPROM_MAC, macaddr, - IEEE80211_ADDR_LEN)); - - /* Read the list of authorized channels. */ - for (i = 0; i < WPI_CHAN_BANDS_COUNT; i++) - WPI_CHK(wpi_read_eeprom_channels(sc, i)); - - /* Read the list of TX power groups. */ - for (i = 0; i < WPI_POWER_GROUPS_COUNT; i++) - WPI_CHK(wpi_read_eeprom_group(sc, i)); - -fail: wpi_apm_stop(sc); /* Power OFF adapter. */ - - DPRINTF(sc, WPI_DEBUG_TRACE, error ? TRACE_STR_END_ERR : TRACE_STR_END, - __func__); - - return error; -#undef WPI_CHK -} - -/* - * Translate EEPROM flags to net80211. - */ -static uint32_t -wpi_eeprom_channel_flags(struct wpi_eeprom_chan *channel) -{ - uint32_t nflags; - - nflags = 0; - if ((channel->flags & WPI_EEPROM_CHAN_ACTIVE) == 0) - nflags |= IEEE80211_CHAN_PASSIVE; - if ((channel->flags & WPI_EEPROM_CHAN_IBSS) == 0) - nflags |= IEEE80211_CHAN_NOADHOC; - if (channel->flags & WPI_EEPROM_CHAN_RADAR) { - nflags |= IEEE80211_CHAN_DFS; - /* XXX apparently IBSS may still be marked */ - nflags |= IEEE80211_CHAN_NOADHOC; - } - - /* XXX HOSTAP uses WPI_MODE_IBSS */ - if (nflags & IEEE80211_CHAN_NOADHOC) - nflags |= IEEE80211_CHAN_NOHOSTAP; - - return nflags; -} - -static void -wpi_read_eeprom_band(struct wpi_softc *sc, uint8_t n, int maxchans, - int *nchans, struct ieee80211_channel chans[]) -{ - struct wpi_eeprom_chan *channels = sc->eeprom_channels[n]; - const struct wpi_chan_band *band = &wpi_bands[n]; - uint32_t nflags; - uint8_t bands[IEEE80211_MODE_BYTES]; - uint8_t chan, i; - int error; - - memset(bands, 0, sizeof(bands)); - - if (n == 0) { - setbit(bands, IEEE80211_MODE_11B); - setbit(bands, IEEE80211_MODE_11G); - } else - setbit(bands, IEEE80211_MODE_11A); - - for (i = 0; i < band->nchan; i++) { - if (!(channels[i].flags & WPI_EEPROM_CHAN_VALID)) { - DPRINTF(sc, WPI_DEBUG_EEPROM, - "Channel Not Valid: %d, band %d\n", - band->chan[i],n); - continue; - } - - chan = band->chan[i]; - nflags = wpi_eeprom_channel_flags(&channels[i]); - error = ieee80211_add_channel(chans, maxchans, nchans, - chan, 0, channels[i].maxpwr, nflags, bands); - if (error != 0) - break; - - /* Save maximum allowed TX power for this channel. */ - sc->maxpwr[chan] = channels[i].maxpwr; - - DPRINTF(sc, WPI_DEBUG_EEPROM, - "adding chan %d flags=0x%x maxpwr=%d, offset %d\n", - chan, channels[i].flags, sc->maxpwr[chan], *nchans); - } -} - -/** - * Read the eeprom to find out what channels are valid for the given - * band and update net80211 with what we find. - */ -static int -wpi_read_eeprom_channels(struct wpi_softc *sc, uint8_t n) -{ - struct ieee80211com *ic = &sc->sc_ic; - const struct wpi_chan_band *band = &wpi_bands[n]; - int error; - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); - - error = wpi_read_prom_data(sc, band->addr, &sc->eeprom_channels[n], - band->nchan * sizeof (struct wpi_eeprom_chan)); - if (error != 0) { - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__); - return error; - } - - wpi_read_eeprom_band(sc, n, IEEE80211_CHAN_MAX, &ic->ic_nchans, - ic->ic_channels); - - ieee80211_sort_channels(ic->ic_channels, ic->ic_nchans); - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); - - return 0; -} - -static struct wpi_eeprom_chan * -wpi_find_eeprom_channel(struct wpi_softc *sc, struct ieee80211_channel *c) -{ - int i, j; - - for (j = 0; j < WPI_CHAN_BANDS_COUNT; j++) - for (i = 0; i < wpi_bands[j].nchan; i++) - if (wpi_bands[j].chan[i] == c->ic_ieee && - ((j == 0) ^ IEEE80211_IS_CHAN_A(c)) == 1) - return &sc->eeprom_channels[j][i]; - - return NULL; -} - -static void -wpi_getradiocaps(struct ieee80211com *ic, - int maxchans, int *nchans, struct ieee80211_channel chans[]) -{ - struct wpi_softc *sc = ic->ic_softc; - int i; - - /* Parse the list of authorized channels. */ - for (i = 0; i < WPI_CHAN_BANDS_COUNT && *nchans < maxchans; i++) - wpi_read_eeprom_band(sc, i, maxchans, nchans, chans); -} - -/* - * Enforce flags read from EEPROM. - */ -static int -wpi_setregdomain(struct ieee80211com *ic, struct ieee80211_regdomain *rd, - int nchan, struct ieee80211_channel chans[]) -{ - struct wpi_softc *sc = ic->ic_softc; - int i; - - for (i = 0; i < nchan; i++) { - struct ieee80211_channel *c = &chans[i]; - struct wpi_eeprom_chan *channel; - - channel = wpi_find_eeprom_channel(sc, c); - if (channel == NULL) { - ic_printf(ic, "%s: invalid channel %u freq %u/0x%x\n", - __func__, c->ic_ieee, c->ic_freq, c->ic_flags); - return EINVAL; - } - c->ic_flags |= wpi_eeprom_channel_flags(channel); - } - - return 0; -} - -static int -wpi_read_eeprom_group(struct wpi_softc *sc, uint8_t n) -{ - struct wpi_power_group *group = &sc->groups[n]; - struct wpi_eeprom_group rgroup; - int i, error; - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); - - if ((error = wpi_read_prom_data(sc, WPI_EEPROM_POWER_GRP + n * 32, - &rgroup, sizeof rgroup)) != 0) { - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__); - return error; - } - - /* Save TX power group information. */ - group->chan = rgroup.chan; - group->maxpwr = rgroup.maxpwr; - /* Retrieve temperature at which the samples were taken. */ - group->temp = (int16_t)le16toh(rgroup.temp); - - DPRINTF(sc, WPI_DEBUG_EEPROM, - "power group %d: chan=%d maxpwr=%d temp=%d\n", n, group->chan, - group->maxpwr, group->temp); - - for (i = 0; i < WPI_SAMPLES_COUNT; i++) { - group->samples[i].index = rgroup.samples[i].index; - group->samples[i].power = rgroup.samples[i].power; - - DPRINTF(sc, WPI_DEBUG_EEPROM, - "\tsample %d: index=%d power=%d\n", i, - group->samples[i].index, group->samples[i].power); - } - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); - - return 0; -} - -static __inline uint8_t -wpi_add_node_entry_adhoc(struct wpi_softc *sc) -{ - uint8_t newid = WPI_ID_IBSS_MIN; - - for (; newid <= WPI_ID_IBSS_MAX; newid++) { - if ((sc->nodesmsk & (1 << newid)) == 0) { - sc->nodesmsk |= 1 << newid; - return newid; - } - } - - return WPI_ID_UNDEFINED; -} - -static __inline uint8_t -wpi_add_node_entry_sta(struct wpi_softc *sc) -{ - sc->nodesmsk |= 1 << WPI_ID_BSS; - - return WPI_ID_BSS; -} - -static __inline int -wpi_check_node_entry(struct wpi_softc *sc, uint8_t id) -{ - if (id == WPI_ID_UNDEFINED) - return 0; - - return (sc->nodesmsk >> id) & 1; -} - -static __inline void -wpi_clear_node_table(struct wpi_softc *sc) -{ - sc->nodesmsk = 0; -} - -static __inline void -wpi_del_node_entry(struct wpi_softc *sc, uint8_t id) -{ - sc->nodesmsk &= ~(1 << id); -} - -static struct ieee80211_node * -wpi_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) -{ - struct wpi_node *wn; - - wn = 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 wpi_softc *sc = ni->ni_ic->ic_softc; - struct wpi_node *wn = WPI_NODE(ni); - - if (wn->id != WPI_ID_UNDEFINED) { - WPI_NT_LOCK(sc); - if (wpi_check_node_entry(sc, wn->id)) { - wpi_del_node_entry(sc, wn->id); - wpi_del_node(sc, ni); - } - WPI_NT_UNLOCK(sc); - } - - sc->sc_node_free(ni); -} - -static __inline int -wpi_check_bss_filter(struct wpi_softc *sc) -{ - return (sc->rxon.filter & htole32(WPI_FILTER_BSS)) != 0; -} - -static void -wpi_ibss_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, int subtype, - const struct ieee80211_rx_stats *rxs, - int rssi, int nf) -{ - struct ieee80211vap *vap = ni->ni_vap; - struct wpi_softc *sc = vap->iv_ic->ic_softc; - struct wpi_vap *wvp = WPI_VAP(vap); - uint64_t ni_tstamp, rx_tstamp; - - wvp->wv_recv_mgmt(ni, m, subtype, rxs, rssi, nf); - - if (vap->iv_state == IEEE80211_S_RUN && - (subtype == IEEE80211_FC0_SUBTYPE_BEACON || - subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)) { - ni_tstamp = le64toh(ni->ni_tstamp.tsf); - rx_tstamp = le64toh(sc->rx_tstamp); - - if (ni_tstamp >= rx_tstamp) { - DPRINTF(sc, WPI_DEBUG_STATE, - "ibss merge, tsf %ju tstamp %ju\n", - (uintmax_t)rx_tstamp, (uintmax_t)ni_tstamp); - (void) ieee80211_ibss_merge(ni); - } - } -} - -static void -wpi_restore_node(void *arg, struct ieee80211_node *ni) -{ - struct wpi_softc *sc = arg; - struct wpi_node *wn = WPI_NODE(ni); - int error; - - WPI_NT_LOCK(sc); - if (wn->id != WPI_ID_UNDEFINED) { - wn->id = WPI_ID_UNDEFINED; - if ((error = wpi_add_ibss_node(sc, ni)) != 0) { - device_printf(sc->sc_dev, - "%s: could not add IBSS node, error %d\n", - __func__, error); - } - } - WPI_NT_UNLOCK(sc); -} - -static void -wpi_restore_node_table(struct wpi_softc *sc, struct wpi_vap *wvp) -{ - struct ieee80211com *ic = &sc->sc_ic; - - /* Set group keys once. */ - WPI_NT_LOCK(sc); - wvp->wv_gtk = 0; - WPI_NT_UNLOCK(sc); - - ieee80211_iterate_nodes(&ic->ic_sta, wpi_restore_node, sc); - ieee80211_crypto_reload_keys(ic); -} - -/** - * Called by net80211 when ever there is a change to 80211 state machine - */ -static int -wpi_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) -{ - struct wpi_vap *wvp = WPI_VAP(vap); - struct ieee80211com *ic = vap->iv_ic; - struct wpi_softc *sc = ic->ic_softc; - int error = 0; - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); - - WPI_TXQ_LOCK(sc); - if (nstate > IEEE80211_S_INIT && sc->sc_running == 0) { - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__); - WPI_TXQ_UNLOCK(sc); - - return ENXIO; - } - WPI_TXQ_UNLOCK(sc); - - DPRINTF(sc, WPI_DEBUG_STATE, "%s: %s -> %s\n", __func__, - ieee80211_state_name[vap->iv_state], - ieee80211_state_name[nstate]); - - if (vap->iv_state == IEEE80211_S_RUN && nstate < IEEE80211_S_RUN) { - if ((error = wpi_set_pslevel(sc, 0, 0, 1)) != 0) { - device_printf(sc->sc_dev, - "%s: could not set power saving level\n", - __func__); - return error; - } - - wpi_set_led(sc, WPI_LED_LINK, 1, 0); - } - - switch (nstate) { - case IEEE80211_S_SCAN: - WPI_RXON_LOCK(sc); - if (wpi_check_bss_filter(sc) != 0) { - sc->rxon.filter &= ~htole32(WPI_FILTER_BSS); - if ((error = wpi_send_rxon(sc, 0, 1)) != 0) { - device_printf(sc->sc_dev, - "%s: could not send RXON\n", __func__); - } - } - WPI_RXON_UNLOCK(sc); - break; - - case IEEE80211_S_ASSOC: - if (vap->iv_state != IEEE80211_S_RUN) - break; - /* FALLTHROUGH */ - case IEEE80211_S_AUTH: - /* - * NB: do not optimize AUTH -> AUTH state transmission - - * this will break powersave with non-QoS AP! - */ - - /* - * The node must be registered in the firmware before auth. - * Also the associd must be cleared on RUN -> ASSOC - * transitions. - */ - if ((error = wpi_auth(sc, vap)) != 0) { - device_printf(sc->sc_dev, - "%s: could not move to AUTH state, error %d\n", - __func__, error); - } - break; - - case IEEE80211_S_RUN: - /* - * RUN -> RUN transition: - * STA mode: Just restart the timers. - * IBSS mode: Process IBSS merge. - */ - if (vap->iv_state == IEEE80211_S_RUN) { - if (vap->iv_opmode != IEEE80211_M_IBSS) { - WPI_RXON_LOCK(sc); - wpi_calib_timeout(sc); - WPI_RXON_UNLOCK(sc); - break; - } else { - /* - * Drop the BSS_FILTER bit - * (there is no another way to change bssid). - */ - WPI_RXON_LOCK(sc); - sc->rxon.filter &= ~htole32(WPI_FILTER_BSS); - if ((error = wpi_send_rxon(sc, 0, 1)) != 0) { - device_printf(sc->sc_dev, - "%s: could not send RXON\n", - __func__); - } - WPI_RXON_UNLOCK(sc); - - /* Restore all what was lost. */ - wpi_restore_node_table(sc, wvp); - - /* XXX set conditionally? */ - wpi_updateedca(ic); - } - } - - /* - * !RUN -> RUN requires setting the association id - * which is done with a firmware cmd. We also defer - * starting the timers until that work is done. - */ - if ((error = wpi_run(sc, vap)) != 0) { - device_printf(sc->sc_dev, - "%s: could not move to RUN state\n", __func__); - } - break; - - default: - break; - } - if (error != 0) { - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__); - return error; - } - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); - - return wvp->wv_newstate(vap, nstate, arg); -} - -static void -wpi_calib_timeout(void *arg) -{ - struct wpi_softc *sc = arg; - - if (wpi_check_bss_filter(sc) == 0) - return; - - wpi_power_calibration(sc); - - callout_reset(&sc->calib_to, 60*hz, wpi_calib_timeout, sc); -} - -static __inline uint8_t -rate2plcp(const uint8_t rate) -{ - switch (rate) { - case 12: return 0xd; - case 18: return 0xf; - case 24: return 0x5; - case 36: return 0x7; - case 48: return 0x9; - case 72: return 0xb; - case 96: return 0x1; - case 108: return 0x3; - case 2: return 10; - case 4: return 20; - case 11: return 55; - case 22: return 110; - default: return 0; - } -} - -static __inline uint8_t -plcp2rate(const uint8_t plcp) -{ - switch (plcp) { - case 0xd: return 12; - case 0xf: return 18; - case 0x5: return 24; - case 0x7: return 36; - case 0x9: return 48; - case 0xb: return 72; - case 0x1: return 96; - case 0x3: return 108; - case 10: return 2; - case 20: return 4; - case 55: return 11; - case 110: return 22; - default: return 0; - } -} - -/* Quickly determine if a given rate is CCK or OFDM. */ -#define WPI_RATE_IS_OFDM(rate) ((rate) >= 12 && (rate) != 22) - -static void -wpi_rx_done(struct wpi_softc *sc, struct wpi_rx_desc *desc, - struct wpi_rx_data *data) -{ - struct ieee80211com *ic = &sc->sc_ic; - struct wpi_rx_ring *ring = &sc->rxq; - struct wpi_rx_stat *stat; - struct wpi_rx_head *head; - struct wpi_rx_tail *tail; - struct ieee80211_frame *wh; - struct ieee80211_node *ni; - struct mbuf *m, *m1; - bus_addr_t paddr; - uint32_t flags; - uint16_t len; - int error; - - stat = (struct wpi_rx_stat *)(desc + 1); - - if (__predict_false(stat->len > WPI_STAT_MAXLEN)) { - device_printf(sc->sc_dev, "invalid RX statistic header\n"); - goto fail1; - } - - bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD); - head = (struct wpi_rx_head *)((caddr_t)(stat + 1) + stat->len); - len = le16toh(head->len); - tail = (struct wpi_rx_tail *)((caddr_t)(head + 1) + len); - flags = le32toh(tail->flags); - - DPRINTF(sc, WPI_DEBUG_RECV, "%s: idx %d len %d stat len %u rssi %d" - " rate %x chan %d tstamp %ju\n", __func__, ring->cur, - le32toh(desc->len), len, (int8_t)stat->rssi, - head->plcp, head->chan, (uintmax_t)le64toh(tail->tstamp)); - - /* Discard frames with a bad FCS early. */ - if ((flags & WPI_RX_NOERROR) != WPI_RX_NOERROR) { - DPRINTF(sc, WPI_DEBUG_RECV, "%s: RX flags error %x\n", - __func__, flags); - goto fail1; - } - /* Discard frames that are too short. */ - if (len < sizeof (struct ieee80211_frame_ack)) { - DPRINTF(sc, WPI_DEBUG_RECV, "%s: frame too short: %d\n", - __func__, len); - goto fail1; - } - - m1 = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE); - if (__predict_false(m1 == NULL)) { - DPRINTF(sc, WPI_DEBUG_ANY, "%s: no mbuf to restock ring\n", - __func__); - goto fail1; - } - bus_dmamap_unload(ring->data_dmat, data->map); - - error = bus_dmamap_load(ring->data_dmat, data->map, mtod(m1, void *), - MJUMPAGESIZE, wpi_dma_map_addr, &paddr, BUS_DMA_NOWAIT); - if (__predict_false(error != 0 && error != EFBIG)) { - device_printf(sc->sc_dev, - "%s: bus_dmamap_load failed, error %d\n", __func__, error); - m_freem(m1); - - /* Try to reload the old mbuf. */ - error = bus_dmamap_load(ring->data_dmat, data->map, - mtod(data->m, void *), MJUMPAGESIZE, wpi_dma_map_addr, - &paddr, BUS_DMA_NOWAIT); - if (error != 0 && error != EFBIG) { - panic("%s: could not load old RX mbuf", __func__); - } - /* Physical address may have changed. */ - ring->desc[ring->cur] = htole32(paddr); - bus_dmamap_sync(ring->data_dmat, ring->desc_dma.map, - BUS_DMASYNC_PREWRITE); - goto fail1; - } - - m = data->m; - data->m = m1; - /* Update RX descriptor. */ - ring->desc[ring->cur] = htole32(paddr); - bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, - BUS_DMASYNC_PREWRITE); - - /* Finalize mbuf. */ - m->m_data = (caddr_t)(head + 1); - m->m_pkthdr.len = m->m_len = len; - - /* Grab a reference to the source node. */ - wh = mtod(m, struct ieee80211_frame *); - - if ((wh->i_fc[1] & IEEE80211_FC1_PROTECTED) && - (flags & WPI_RX_CIPHER_MASK) == WPI_RX_CIPHER_CCMP) { - /* Check whether decryption was successful or not. */ - if ((flags & WPI_RX_DECRYPT_MASK) != WPI_RX_DECRYPT_OK) { - DPRINTF(sc, WPI_DEBUG_RECV, - "CCMP decryption failed 0x%x\n", flags); - goto fail2; - } - m->m_flags |= M_WEP; - } - - if (len >= sizeof(struct ieee80211_frame_min)) - ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh); - else - ni = NULL; - - sc->rx_tstamp = tail->tstamp; - - if (ieee80211_radiotap_active(ic)) { - struct wpi_rx_radiotap_header *tap = &sc->sc_rxtap; - - tap->wr_flags = 0; - if (head->flags & htole16(WPI_STAT_FLAG_SHPREAMBLE)) - tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; - tap->wr_dbm_antsignal = (int8_t)(stat->rssi + WPI_RSSI_OFFSET); - tap->wr_dbm_antnoise = WPI_RSSI_OFFSET; - tap->wr_tsft = tail->tstamp; - tap->wr_antenna = (le16toh(head->flags) >> 4) & 0xf; - tap->wr_rate = plcp2rate(head->plcp); - } - - WPI_UNLOCK(sc); - - /* Send the frame to the 802.11 layer. */ - if (ni != NULL) { - (void)ieee80211_input(ni, m, stat->rssi, WPI_RSSI_OFFSET); - /* Node is no longer needed. */ - ieee80211_free_node(ni); - } else - (void)ieee80211_input_all(ic, m, stat->rssi, WPI_RSSI_OFFSET); - - WPI_LOCK(sc); - - return; - -fail2: m_freem(m); - -fail1: counter_u64_add(ic->ic_ierrors, 1); -} - -static void -wpi_rx_statistics(struct wpi_softc *sc, struct wpi_rx_desc *desc, - struct wpi_rx_data *data) -{ - /* Ignore */ -} - -static void -wpi_tx_done(struct wpi_softc *sc, struct wpi_rx_desc *desc) -{ - struct ieee80211_ratectl_tx_status *txs = &sc->sc_txs; - struct wpi_tx_ring *ring = &sc->txq[desc->qid & 0x3]; - struct wpi_tx_data *data = &ring->data[desc->idx]; - struct wpi_tx_stat *stat = (struct wpi_tx_stat *)(desc + 1); - struct mbuf *m; - struct ieee80211_node *ni; - uint32_t status = le32toh(stat->status); - - KASSERT(data->ni != NULL, ("no node")); - KASSERT(data->m != NULL, ("no mbuf")); - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); - - DPRINTF(sc, WPI_DEBUG_XMIT, "%s: " - "qid %d idx %d retries %d btkillcnt %d rate %x duration %d " - "status %x\n", __func__, desc->qid, desc->idx, stat->ackfailcnt, - stat->btkillcnt, stat->rate, le32toh(stat->duration), status); - - /* Unmap and free mbuf. */ - bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTWRITE); - bus_dmamap_unload(ring->data_dmat, data->map); - m = data->m, data->m = NULL; - ni = data->ni, data->ni = NULL; - - /* Restore frame header. */ - KASSERT(M_LEADINGSPACE(m) >= data->hdrlen, ("no frame header!")); - M_PREPEND(m, data->hdrlen, M_NOWAIT); - KASSERT(m != NULL, ("%s: m is NULL\n", __func__)); - - /* - * Update rate control statistics for the node. - */ - txs->pktlen = m->m_pkthdr.len; - txs->short_retries = stat->rtsfailcnt; - txs->long_retries = stat->ackfailcnt / WPI_NTRIES_DEFAULT; - if (!(status & WPI_TX_STATUS_FAIL)) - txs->status = IEEE80211_RATECTL_TX_SUCCESS; - else { - switch (status & 0xff) { - case WPI_TX_STATUS_FAIL_SHORT_LIMIT: - txs->status = IEEE80211_RATECTL_TX_FAIL_SHORT; - break; - case WPI_TX_STATUS_FAIL_LONG_LIMIT: - txs->status = IEEE80211_RATECTL_TX_FAIL_LONG; - break; - case WPI_TX_STATUS_FAIL_LIFE_EXPIRE: - txs->status = IEEE80211_RATECTL_TX_FAIL_EXPIRED; - break; - default: - txs->status = IEEE80211_RATECTL_TX_FAIL_UNSPECIFIED; - break; - } - } - - ieee80211_ratectl_tx_complete(ni, txs); - ieee80211_tx_complete(ni, m, (status & WPI_TX_STATUS_FAIL) != 0); - - WPI_TXQ_STATE_LOCK(sc); - if (--ring->queued > 0) - callout_reset(&sc->tx_timeout, 5*hz, wpi_tx_timeout, sc); - else - callout_stop(&sc->tx_timeout); - WPI_TXQ_STATE_UNLOCK(sc); - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); -} - -/* - * Process a "command done" firmware notification. This is where we wakeup - * processes waiting for a synchronous command completion. - */ -static void -wpi_cmd_done(struct wpi_softc *sc, struct wpi_rx_desc *desc) -{ - struct wpi_tx_ring *ring = &sc->txq[WPI_CMD_QUEUE_NUM]; - struct wpi_tx_data *data; - struct wpi_tx_cmd *cmd; - - DPRINTF(sc, WPI_DEBUG_CMD, "cmd notification qid %x idx %d flags %x " - "type %s len %d\n", desc->qid, desc->idx, - desc->flags, wpi_cmd_str(desc->type), - le32toh(desc->len)); - - if ((desc->qid & WPI_RX_DESC_QID_MSK) != WPI_CMD_QUEUE_NUM) - return; /* Not a command ack. */ - - KASSERT(ring->queued == 0, ("ring->queued must be 0")); - - data = &ring->data[desc->idx]; - cmd = &ring->cmd[desc->idx]; - - /* If the command was mapped in an mbuf, free it. */ - if (data->m != NULL) { - bus_dmamap_sync(ring->data_dmat, data->map, - BUS_DMASYNC_POSTWRITE); - bus_dmamap_unload(ring->data_dmat, data->map); - m_freem(data->m); - data->m = NULL; - } - - wakeup(cmd); - - if (desc->type == WPI_CMD_SET_POWER_MODE) { - struct wpi_pmgt_cmd *pcmd = (struct wpi_pmgt_cmd *)cmd->data; - - bus_dmamap_sync(ring->data_dmat, ring->cmd_dma.map, - BUS_DMASYNC_POSTREAD); - - WPI_TXQ_LOCK(sc); - if (le16toh(pcmd->flags) & WPI_PS_ALLOW_SLEEP) { - sc->sc_update_rx_ring = wpi_update_rx_ring_ps; - sc->sc_update_tx_ring = wpi_update_tx_ring_ps; - } else { - sc->sc_update_rx_ring = wpi_update_rx_ring; - sc->sc_update_tx_ring = wpi_update_tx_ring; - } - WPI_TXQ_UNLOCK(sc); - } -} - -static void -wpi_notif_intr(struct wpi_softc *sc) -{ - struct ieee80211com *ic = &sc->sc_ic; - struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); - uint32_t hw; - - bus_dmamap_sync(sc->shared_dma.tag, sc->shared_dma.map, - BUS_DMASYNC_POSTREAD); - - hw = le32toh(sc->shared->next) & 0xfff; - hw = (hw == 0) ? WPI_RX_RING_COUNT - 1 : hw - 1; - - while (sc->rxq.cur != hw) { - sc->rxq.cur = (sc->rxq.cur + 1) % WPI_RX_RING_COUNT; - - struct wpi_rx_data *data = &sc->rxq.data[sc->rxq.cur]; - struct wpi_rx_desc *desc; - - bus_dmamap_sync(sc->rxq.data_dmat, data->map, - BUS_DMASYNC_POSTREAD); - desc = mtod(data->m, struct wpi_rx_desc *); - - DPRINTF(sc, WPI_DEBUG_NOTIFY, - "%s: cur=%d; qid %x idx %d flags %x type %d(%s) len %d\n", - __func__, sc->rxq.cur, desc->qid, desc->idx, desc->flags, - desc->type, wpi_cmd_str(desc->type), le32toh(desc->len)); - - if (!(desc->qid & WPI_UNSOLICITED_RX_NOTIF)) { - /* Reply to a command. */ - wpi_cmd_done(sc, desc); - } - - switch (desc->type) { - case WPI_RX_DONE: - /* An 802.11 frame has been received. */ - wpi_rx_done(sc, desc, data); - - if (__predict_false(sc->sc_running == 0)) { - /* wpi_stop() was called. */ - return; - } - - break; - - case WPI_TX_DONE: - /* An 802.11 frame has been transmitted. */ - wpi_tx_done(sc, desc); - break; - - case WPI_RX_STATISTICS: - case WPI_BEACON_STATISTICS: - wpi_rx_statistics(sc, desc, data); - break; - - case WPI_BEACON_MISSED: - { - struct wpi_beacon_missed *miss = - (struct wpi_beacon_missed *)(desc + 1); - uint32_t expected, misses, received, threshold; - - bus_dmamap_sync(sc->rxq.data_dmat, data->map, - BUS_DMASYNC_POSTREAD); - - misses = le32toh(miss->consecutive); - expected = le32toh(miss->expected); - received = le32toh(miss->received); - threshold = MAX(2, vap->iv_bmissthreshold); - - DPRINTF(sc, WPI_DEBUG_BMISS, - "%s: beacons missed %u(%u) (received %u/%u)\n", - __func__, misses, le32toh(miss->total), received, - expected); - - if (misses >= threshold || - (received == 0 && expected >= threshold)) { - WPI_RXON_LOCK(sc); - if (callout_pending(&sc->scan_timeout)) { - wpi_cmd(sc, WPI_CMD_SCAN_ABORT, NULL, - 0, 1); - } - WPI_RXON_UNLOCK(sc); - if (vap->iv_state == IEEE80211_S_RUN && - (ic->ic_flags & IEEE80211_F_SCAN) == 0) - ieee80211_beacon_miss(ic); - } - - break; - } -#ifdef WPI_DEBUG - case WPI_BEACON_SENT: - { - struct wpi_tx_stat *stat = - (struct wpi_tx_stat *)(desc + 1); - uint64_t *tsf = (uint64_t *)(stat + 1); - uint32_t *mode = (uint32_t *)(tsf + 1); - - bus_dmamap_sync(sc->rxq.data_dmat, data->map, - BUS_DMASYNC_POSTREAD); - - DPRINTF(sc, WPI_DEBUG_BEACON, - "beacon sent: rts %u, ack %u, btkill %u, rate %u, " - "duration %u, status %x, tsf %ju, mode %x\n", - stat->rtsfailcnt, stat->ackfailcnt, - stat->btkillcnt, stat->rate, le32toh(stat->duration), - le32toh(stat->status), le64toh(*tsf), - le32toh(*mode)); - - break; - } -#endif - case WPI_UC_READY: - { - struct wpi_ucode_info *uc = - (struct wpi_ucode_info *)(desc + 1); - - /* The microcontroller is ready. */ - bus_dmamap_sync(sc->rxq.data_dmat, data->map, - BUS_DMASYNC_POSTREAD); - DPRINTF(sc, WPI_DEBUG_RESET, - "microcode alive notification version=%d.%d " - "subtype=%x alive=%x\n", uc->major, uc->minor, - uc->subtype, le32toh(uc->valid)); - - if (le32toh(uc->valid) != 1) { - device_printf(sc->sc_dev, - "microcontroller initialization failed\n"); - wpi_stop_locked(sc); - return; - } - /* Save the address of the error log in SRAM. */ - sc->errptr = le32toh(uc->errptr); - break; - } - case WPI_STATE_CHANGED: - { - bus_dmamap_sync(sc->rxq.data_dmat, data->map, - BUS_DMASYNC_POSTREAD); - - uint32_t *status = (uint32_t *)(desc + 1); - - DPRINTF(sc, WPI_DEBUG_STATE, "state changed to %x\n", - le32toh(*status)); - - if (le32toh(*status) & 1) { - WPI_NT_LOCK(sc); - wpi_clear_node_table(sc); - WPI_NT_UNLOCK(sc); - ieee80211_runtask(ic, - &sc->sc_radiooff_task); - return; - } - break; - } -#ifdef WPI_DEBUG - case WPI_START_SCAN: - { - bus_dmamap_sync(sc->rxq.data_dmat, data->map, - BUS_DMASYNC_POSTREAD); - - struct wpi_start_scan *scan = - (struct wpi_start_scan *)(desc + 1); - DPRINTF(sc, WPI_DEBUG_SCAN, - "%s: scanning channel %d status %x\n", - __func__, scan->chan, le32toh(scan->status)); - - break; - } -#endif - case WPI_STOP_SCAN: - { - bus_dmamap_sync(sc->rxq.data_dmat, data->map, - BUS_DMASYNC_POSTREAD); - - struct wpi_stop_scan *scan = - (struct wpi_stop_scan *)(desc + 1); - - DPRINTF(sc, WPI_DEBUG_SCAN, - "scan finished nchan=%d status=%d chan=%d\n", - scan->nchan, scan->status, scan->chan); - - WPI_RXON_LOCK(sc); - callout_stop(&sc->scan_timeout); - WPI_RXON_UNLOCK(sc); - if (scan->status == WPI_SCAN_ABORTED) - ieee80211_cancel_scan(vap); - else - ieee80211_scan_next(vap); - break; - } - } - - if (sc->rxq.cur % 8 == 0) { - /* Tell the firmware what we have processed. */ - sc->sc_update_rx_ring(sc); - } - } -} - -/* - * Process an INT_WAKEUP interrupt raised when the microcontroller wakes up - * from power-down sleep mode. - */ -static void -wpi_wakeup_intr(struct wpi_softc *sc) -{ - int qid; - - DPRINTF(sc, WPI_DEBUG_PWRSAVE, - "%s: ucode wakeup from power-down sleep\n", __func__); - - /* Wakeup RX and TX rings. */ - if (sc->rxq.update) { - sc->rxq.update = 0; - wpi_update_rx_ring(sc); - } - WPI_TXQ_LOCK(sc); - for (qid = 0; qid < WPI_DRV_NTXQUEUES; qid++) { - struct wpi_tx_ring *ring = &sc->txq[qid]; - - if (ring->update) { - ring->update = 0; - wpi_update_tx_ring(sc, ring); - } - } - WPI_CLRBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_MAC_ACCESS_REQ); - WPI_TXQ_UNLOCK(sc); -} - -/* - * This function prints firmware registers - */ -#ifdef WPI_DEBUG -static void -wpi_debug_registers(struct wpi_softc *sc) -{ - size_t i; - static const uint32_t csr_tbl[] = { - WPI_HW_IF_CONFIG, - WPI_INT, - WPI_INT_MASK, - WPI_FH_INT, - WPI_GPIO_IN, - WPI_RESET, - WPI_GP_CNTRL, - WPI_EEPROM, - WPI_EEPROM_GP, - WPI_GIO, - WPI_UCODE_GP1, - WPI_UCODE_GP2, - WPI_GIO_CHICKEN, - WPI_ANA_PLL, - WPI_DBG_HPET_MEM, - }; - static const uint32_t prph_tbl[] = { - WPI_APMG_CLK_CTRL, - WPI_APMG_PS, - WPI_APMG_PCI_STT, - WPI_APMG_RFKILL, - }; - - DPRINTF(sc, WPI_DEBUG_REGISTER,"%s","\n"); - - for (i = 0; i < nitems(csr_tbl); i++) { - DPRINTF(sc, WPI_DEBUG_REGISTER, " %-18s: 0x%08x ", - wpi_get_csr_string(csr_tbl[i]), WPI_READ(sc, csr_tbl[i])); - - if ((i + 1) % 2 == 0) - DPRINTF(sc, WPI_DEBUG_REGISTER, "\n"); - } - DPRINTF(sc, WPI_DEBUG_REGISTER, "\n\n"); - - if (wpi_nic_lock(sc) == 0) { - for (i = 0; i < nitems(prph_tbl); i++) { - DPRINTF(sc, WPI_DEBUG_REGISTER, " %-18s: 0x%08x ", - wpi_get_prph_string(prph_tbl[i]), - wpi_prph_read(sc, prph_tbl[i])); - - if ((i + 1) % 2 == 0) - DPRINTF(sc, WPI_DEBUG_REGISTER, "\n"); - } - DPRINTF(sc, WPI_DEBUG_REGISTER, "\n"); - wpi_nic_unlock(sc); - } else { - DPRINTF(sc, WPI_DEBUG_REGISTER, - "Cannot access internal registers.\n"); - } -} -#endif - -/* - * Dump the error log of the firmware when a firmware panic occurs. Although - * we can't debug the firmware because it is neither open source nor free, it - * can help us to identify certain classes of problems. - */ -static void -wpi_fatal_intr(struct wpi_softc *sc) -{ - struct wpi_fw_dump dump; - uint32_t i, offset, count; - - /* Check that the error log address is valid. */ - if (sc->errptr < WPI_FW_DATA_BASE || - sc->errptr + sizeof (dump) > - WPI_FW_DATA_BASE + WPI_FW_DATA_MAXSZ) { - 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 < nitems(wpi_fw_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"); - WPI_TXQ_LOCK(sc); - for (i = 0; i < WPI_DRV_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); - } - WPI_TXQ_UNLOCK(sc); - printf(" rx ring: cur=%d\n", sc->rxq.cur); -} - -static void -wpi_intr(void *arg) -{ - struct wpi_softc *sc = arg; - uint32_t r1, r2; - - WPI_LOCK(sc); - -#if !defined(__HAIKU__) - /* Disable interrupts. */ - WPI_WRITE(sc, WPI_INT_MASK, 0); - - r1 = WPI_READ(sc, WPI_INT); - - if (__predict_false(r1 == 0xffffffff || - (r1 & 0xfffffff0) == 0xa5a5a5a0)) - goto end; /* Hardware gone! */ - - r2 = WPI_READ(sc, WPI_FH_INT); - - DPRINTF(sc, WPI_DEBUG_INTR, "%s: reg1=0x%08x reg2=0x%08x\n", __func__, - r1, r2); - - if (r1 == 0 && r2 == 0) - goto done; /* Interrupt not for us. */ -#else - r1 = atomic_get((int32 *)&sc->sc_intr_status_1); - r2 = atomic_get((int32 *)&sc->sc_intr_status_2); -#endif - - /* Acknowledge interrupts. */ - WPI_WRITE(sc, WPI_INT, r1); - WPI_WRITE(sc, WPI_FH_INT, r2); - - if (__predict_false(r1 & (WPI_INT_SW_ERR | WPI_INT_HW_ERR))) { - struct ieee80211com *ic = &sc->sc_ic; - - device_printf(sc->sc_dev, "fatal firmware error\n"); -#ifdef WPI_DEBUG - wpi_debug_registers(sc); -#endif - wpi_fatal_intr(sc); - DPRINTF(sc, WPI_DEBUG_HW, - "(%s)\n", (r1 & WPI_INT_SW_ERR) ? "(Software Error)" : - "(Hardware Error)"); - ieee80211_restart_all(ic); - goto end; - } - - if ((r1 & (WPI_INT_FH_RX | WPI_INT_SW_RX)) || - (r2 & WPI_FH_INT_RX)) - wpi_notif_intr(sc); - - if (r1 & WPI_INT_ALIVE) - wakeup(sc); /* Firmware is alive. */ - - if (r1 & WPI_INT_WAKEUP) - wpi_wakeup_intr(sc); - -done: - /* Re-enable interrupts. */ - if (__predict_true(sc->sc_running)) - WPI_WRITE(sc, WPI_INT_MASK, WPI_INT_MASK_DEF); - -end: WPI_UNLOCK(sc); -} - -static void -wpi_free_txfrags(struct wpi_softc *sc, uint16_t ac) -{ - struct wpi_tx_ring *ring; - struct wpi_tx_data *data; - uint8_t cur; - - WPI_TXQ_LOCK(sc); - ring = &sc->txq[ac]; - - while (ring->pending != 0) { - ring->pending--; - cur = (ring->cur + ring->pending) % WPI_TX_RING_COUNT; - data = &ring->data[cur]; - - bus_dmamap_sync(ring->data_dmat, data->map, - BUS_DMASYNC_POSTWRITE); - bus_dmamap_unload(ring->data_dmat, data->map); - m_freem(data->m); - data->m = NULL; - - ieee80211_node_decref(data->ni); - data->ni = NULL; - } - - WPI_TXQ_UNLOCK(sc); -} - -static int -wpi_cmd2(struct wpi_softc *sc, struct wpi_buf *buf) -{ - struct ieee80211_frame *wh; - struct wpi_tx_cmd *cmd; - struct wpi_tx_data *data; - struct wpi_tx_desc *desc; - struct wpi_tx_ring *ring; - struct mbuf *m1; - bus_dma_segment_t *seg, segs[WPI_MAX_SCATTER]; - uint8_t cur, pad; - uint16_t hdrlen; - int error, i, nsegs, totlen, frag; - - WPI_TXQ_LOCK(sc); - - KASSERT(buf->size <= sizeof(buf->data), ("buffer overflow")); - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); - - if (__predict_false(sc->sc_running == 0)) { - /* wpi_stop() was called */ - error = ENETDOWN; - goto end; - } - - wh = mtod(buf->m, struct ieee80211_frame *); - hdrlen = ieee80211_anyhdrsize(wh); - totlen = buf->m->m_pkthdr.len; - frag = ((buf->m->m_flags & (M_FRAG | M_LASTFRAG)) == M_FRAG); - - if (__predict_false(totlen < sizeof(struct ieee80211_frame_min))) { - error = EINVAL; - goto end; - } - - if (hdrlen & 3) { - /* First segment length must be a multiple of 4. */ - pad = 4 - (hdrlen & 3); - } else - pad = 0; - - ring = &sc->txq[buf->ac]; - cur = (ring->cur + ring->pending) % WPI_TX_RING_COUNT; - desc = &ring->desc[cur]; - data = &ring->data[cur]; - - /* Prepare TX firmware command. */ - cmd = &ring->cmd[cur]; - cmd->code = buf->code; - cmd->flags = 0; - cmd->qid = ring->qid; - cmd->idx = cur; - - memcpy(cmd->data, buf->data, buf->size); - - /* Save and trim IEEE802.11 header. */ - memcpy((uint8_t *)(cmd->data + buf->size), wh, hdrlen); - m_adj(buf->m, hdrlen); - - 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); - goto end; - } - if (error != 0) { - /* Too many DMA segments, linearize mbuf. */ - m1 = m_collapse(buf->m, M_NOWAIT, WPI_MAX_SCATTER - 1); - if (m1 == NULL) { - device_printf(sc->sc_dev, - "%s: could not defrag mbuf\n", __func__); - error = ENOBUFS; - goto end; - } - buf->m = m1; - - error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, - buf->m, segs, &nsegs, BUS_DMA_NOWAIT); - if (__predict_false(error != 0)) { - /* XXX fix this (applicable to the iwn(4) too) */ - /* - * NB: Do not return error; - * original mbuf does not exist anymore. - */ - device_printf(sc->sc_dev, - "%s: can't map mbuf (error %d)\n", __func__, - error); - if (ring->qid < WPI_CMD_QUEUE_NUM) { - if_inc_counter(buf->ni->ni_vap->iv_ifp, - IFCOUNTER_OERRORS, 1); - if (!frag) - ieee80211_free_node(buf->ni); - } - m_freem(buf->m); - error = 0; - goto end; - } - } - - KASSERT(nsegs < WPI_MAX_SCATTER, - ("too many DMA segments, nsegs (%d) should be less than %d", - nsegs, WPI_MAX_SCATTER)); - - data->m = buf->m; - data->ni = buf->ni; - data->hdrlen = hdrlen; - - DPRINTF(sc, WPI_DEBUG_XMIT, "%s: qid %d idx %d len %d nsegs %d\n", - __func__, ring->qid, cur, totlen, nsegs); - - /* Fill TX descriptor. */ - desc->nsegs = WPI_PAD32(totlen + pad) << 4 | (1 + nsegs); - /* First DMA segment is used by the TX command. */ - desc->segs[0].addr = htole32(data->cmd_paddr); - desc->segs[0].len = htole32(4 + buf->size + hdrlen + pad); - /* Other DMA segments are for data payload. */ - seg = &segs[0]; - for (i = 1; i <= nsegs; i++) { - desc->segs[i].addr = htole32(seg->ds_addr); - desc->segs[i].len = htole32(seg->ds_len); - seg++; - } - - bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE); - bus_dmamap_sync(ring->data_dmat, ring->cmd_dma.map, - BUS_DMASYNC_PREWRITE); - bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, - BUS_DMASYNC_PREWRITE); - - ring->pending += 1; - - if (!frag) { - if (ring->qid < WPI_CMD_QUEUE_NUM) { - WPI_TXQ_STATE_LOCK(sc); - ring->queued += ring->pending; - callout_reset(&sc->tx_timeout, 5*hz, wpi_tx_timeout, - sc); - WPI_TXQ_STATE_UNLOCK(sc); - } - - /* Kick TX ring. */ - ring->cur = (ring->cur + ring->pending) % WPI_TX_RING_COUNT; - ring->pending = 0; - sc->sc_update_tx_ring(sc, ring); - } else - (void) ieee80211_ref_node(data->ni); - -end: DPRINTF(sc, WPI_DEBUG_TRACE, error ? TRACE_STR_END_ERR : TRACE_STR_END, - __func__); - - WPI_TXQ_UNLOCK(sc); - - return (error); -} - -/* - * Construct the data packet for a transmit buffer. - */ -static int -wpi_tx_data(struct wpi_softc *sc, struct mbuf *m, struct ieee80211_node *ni) -{ - const struct ieee80211_txparam *tp = ni->ni_txparms; - struct ieee80211vap *vap = ni->ni_vap; - struct ieee80211com *ic = ni->ni_ic; - struct wpi_node *wn = WPI_NODE(ni); - struct ieee80211_frame *wh; - struct ieee80211_key *k = NULL; - struct wpi_buf tx_data; - struct wpi_cmd_data *tx = (struct wpi_cmd_data *)&tx_data.data; - uint32_t flags; - uint16_t ac, qos; - uint8_t tid, type, rate; - int swcrypt, ismcast, totlen; - - wh = mtod(m, struct ieee80211_frame *); - type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; - ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); - swcrypt = 1; - - /* Select EDCA Access Category and TX ring for this frame. */ - if (IEEE80211_QOS_HAS_SEQ(wh)) { - qos = ((const struct ieee80211_qosframe *)wh)->i_qos[0]; - tid = qos & IEEE80211_QOS_TID; - } else { - qos = 0; - tid = 0; - } - ac = M_WME_GETAC(m); - - /* Choose a TX rate index. */ - if (type == IEEE80211_FC0_TYPE_MGT || - type == IEEE80211_FC0_TYPE_CTL || - (m->m_flags & M_EAPOL) != 0) - rate = tp->mgmtrate; - else if (ismcast) - rate = tp->mcastrate; - else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) - rate = tp->ucastrate; - else { - /* XXX pass pktlen */ - (void) ieee80211_ratectl_rate(ni, NULL, 0); - rate = ni->ni_txrate; - } - - /* Encrypt the frame if need be. */ - if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { - /* Retrieve key for TX. */ - k = ieee80211_crypto_encap(ni, m); - if (k == NULL) - return (ENOBUFS); - - swcrypt = k->wk_flags & IEEE80211_KEY_SWCRYPT; - - /* 802.11 header may have moved. */ - wh = mtod(m, struct ieee80211_frame *); - } - totlen = m->m_pkthdr.len; - - if (ieee80211_radiotap_active_vap(vap)) { - struct wpi_tx_radiotap_header *tap = &sc->sc_txtap; - - tap->wt_flags = 0; - tap->wt_rate = rate; - if (k != NULL) - tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP; - if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG) - tap->wt_flags |= IEEE80211_RADIOTAP_F_FRAG; - - ieee80211_radiotap_tx(vap, m); - } - - flags = 0; - if (!ismcast) { - /* Unicast frame, check if an ACK is expected. */ - if (!qos || (qos & IEEE80211_QOS_ACKPOLICY) != - IEEE80211_QOS_ACKPOLICY_NOACK) - flags |= WPI_TX_NEED_ACK; - } - - if (!IEEE80211_QOS_HAS_SEQ(wh)) - flags |= WPI_TX_AUTO_SEQ; - if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG) - flags |= WPI_TX_MORE_FRAG; - - /* Check if frame must be protected using RTS/CTS or CTS-to-self. */ - if (!ismcast) { - /* NB: Group frames are sent using CCK in 802.11b/g. */ - if (totlen + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) { - flags |= WPI_TX_NEED_RTS; - } else if ((ic->ic_flags & IEEE80211_F_USEPROT) && - WPI_RATE_IS_OFDM(rate)) { - if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) - flags |= WPI_TX_NEED_CTS; - else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) - flags |= WPI_TX_NEED_RTS; - } - - if (flags & (WPI_TX_NEED_RTS | WPI_TX_NEED_CTS)) - flags |= WPI_TX_FULL_TXOP; - } - - memset(tx, 0, sizeof (struct wpi_cmd_data)); - if (type == IEEE80211_FC0_TYPE_MGT) { - uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; - - /* Tell HW to set timestamp in probe responses. */ - if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP) - flags |= WPI_TX_INSERT_TSTAMP; - if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ || - subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) - tx->timeout = htole16(3); - else - tx->timeout = htole16(2); - } - - if (ismcast || type != IEEE80211_FC0_TYPE_DATA) - tx->id = WPI_ID_BROADCAST; - else { - if (wn->id == WPI_ID_UNDEFINED) { - device_printf(sc->sc_dev, - "%s: undefined node id\n", __func__); - return (EINVAL); - } - - tx->id = wn->id; - } - - if (!swcrypt) { - switch (k->wk_cipher->ic_cipher) { - case IEEE80211_CIPHER_AES_CCM: - tx->security = WPI_CIPHER_CCMP; - break; - - default: - break; - } - - memcpy(tx->key, k->wk_key, k->wk_keylen); - } - - if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG) { - struct mbuf *next = m->m_nextpkt; - - tx->lnext = htole16(next->m_pkthdr.len); - tx->fnext = htole32(tx->security | - (flags & WPI_TX_NEED_ACK) | - WPI_NEXT_STA_ID(tx->id)); - } - - tx->len = htole16(totlen); - tx->flags = htole32(flags); - tx->plcp = rate2plcp(rate); - tx->tid = tid; - tx->lifetime = htole32(WPI_LIFETIME_INFINITE); - tx->ofdm_mask = 0xff; - tx->cck_mask = 0x0f; - tx->rts_ntries = 7; - tx->data_ntries = tp->maxretry; - - tx_data.ni = ni; - tx_data.m = m; - tx_data.size = sizeof(struct wpi_cmd_data); - tx_data.code = WPI_CMD_TX_DATA; - tx_data.ac = ac; - - return wpi_cmd2(sc, &tx_data); -} - -static int -wpi_tx_data_raw(struct wpi_softc *sc, struct mbuf *m, - struct ieee80211_node *ni, const struct ieee80211_bpf_params *params) -{ - struct ieee80211vap *vap = ni->ni_vap; - struct ieee80211_key *k = NULL; - struct ieee80211_frame *wh; - struct wpi_buf tx_data; - struct wpi_cmd_data *tx = (struct wpi_cmd_data *)&tx_data.data; - uint32_t flags; - uint8_t ac, type, rate; - int swcrypt, totlen; - - wh = mtod(m, struct ieee80211_frame *); - type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; - swcrypt = 1; - - ac = params->ibp_pri & 3; - - /* Choose a TX rate index. */ - rate = params->ibp_rate0; - - flags = 0; - if (!IEEE80211_QOS_HAS_SEQ(wh)) - flags |= WPI_TX_AUTO_SEQ; - if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0) - flags |= WPI_TX_NEED_ACK; - if (params->ibp_flags & IEEE80211_BPF_RTS) - flags |= WPI_TX_NEED_RTS; - if (params->ibp_flags & IEEE80211_BPF_CTS) - flags |= WPI_TX_NEED_CTS; - if (flags & (WPI_TX_NEED_RTS | WPI_TX_NEED_CTS)) - flags |= WPI_TX_FULL_TXOP; - - /* Encrypt the frame if need be. */ - if (params->ibp_flags & IEEE80211_BPF_CRYPTO) { - /* Retrieve key for TX. */ - k = ieee80211_crypto_encap(ni, m); - if (k == NULL) - return (ENOBUFS); - - swcrypt = k->wk_flags & IEEE80211_KEY_SWCRYPT; - - /* 802.11 header may have moved. */ - wh = mtod(m, struct ieee80211_frame *); - } - totlen = m->m_pkthdr.len; - - if (ieee80211_radiotap_active_vap(vap)) { - struct wpi_tx_radiotap_header *tap = &sc->sc_txtap; - - tap->wt_flags = 0; - tap->wt_rate = rate; - if (params->ibp_flags & IEEE80211_BPF_CRYPTO) - tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP; - - ieee80211_radiotap_tx(vap, m); - } - - memset(tx, 0, sizeof (struct wpi_cmd_data)); - if (type == IEEE80211_FC0_TYPE_MGT) { - uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; - - /* Tell HW to set timestamp in probe responses. */ - if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP) - flags |= WPI_TX_INSERT_TSTAMP; - if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ || - subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) - tx->timeout = htole16(3); - else - tx->timeout = htole16(2); - } - - if (!swcrypt) { - switch (k->wk_cipher->ic_cipher) { - case IEEE80211_CIPHER_AES_CCM: - tx->security = WPI_CIPHER_CCMP; - break; - - default: - break; - } - - memcpy(tx->key, k->wk_key, k->wk_keylen); - } - - tx->len = htole16(totlen); - tx->flags = htole32(flags); - tx->plcp = rate2plcp(rate); - tx->id = WPI_ID_BROADCAST; - tx->lifetime = htole32(WPI_LIFETIME_INFINITE); - tx->rts_ntries = params->ibp_try1; - tx->data_ntries = params->ibp_try0; - - tx_data.ni = ni; - tx_data.m = m; - tx_data.size = sizeof(struct wpi_cmd_data); - tx_data.code = WPI_CMD_TX_DATA; - tx_data.ac = ac; - - return wpi_cmd2(sc, &tx_data); -} - -static __inline int -wpi_tx_ring_free_space(struct wpi_softc *sc, uint16_t ac) -{ - struct wpi_tx_ring *ring = &sc->txq[ac]; - int retval; - - WPI_TXQ_STATE_LOCK(sc); - retval = WPI_TX_RING_HIMARK - ring->queued; - WPI_TXQ_STATE_UNLOCK(sc); - - return retval; -} - -static int -wpi_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, - const struct ieee80211_bpf_params *params) -{ - struct ieee80211com *ic = ni->ni_ic; - struct wpi_softc *sc = ic->ic_softc; - uint16_t ac; - int error = 0; - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); - - ac = M_WME_GETAC(m); - - WPI_TX_LOCK(sc); - - /* NB: no fragments here */ - if (sc->sc_running == 0 || wpi_tx_ring_free_space(sc, ac) < 1) { - error = sc->sc_running ? ENOBUFS : ENETDOWN; - goto unlock; - } - - if (params == NULL) { - /* - * Legacy path; interpret frame contents to decide - * precisely how to send the frame. - */ - error = wpi_tx_data(sc, m, ni); - } else { - /* - * Caller supplied explicit parameters to use in - * sending the frame. - */ - error = wpi_tx_data_raw(sc, m, ni, params); - } - -unlock: WPI_TX_UNLOCK(sc); - - if (error != 0) { - m_freem(m); - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__); - - return error; - } - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); - - return 0; -} - -static int -wpi_transmit(struct ieee80211com *ic, struct mbuf *m) -{ - struct wpi_softc *sc = ic->ic_softc; - struct ieee80211_node *ni; - struct mbuf *mnext; - uint16_t ac; - int error, nmbufs; - - WPI_TX_LOCK(sc); - DPRINTF(sc, WPI_DEBUG_XMIT, "%s: called\n", __func__); - - /* Check if interface is up & running. */ - if (__predict_false(sc->sc_running == 0)) { - error = ENXIO; - goto unlock; - } - - nmbufs = 1; - for (mnext = m->m_nextpkt; mnext != NULL; mnext = mnext->m_nextpkt) - nmbufs++; - - /* Check for available space. */ - ac = M_WME_GETAC(m); - if (wpi_tx_ring_free_space(sc, ac) < nmbufs) { - error = ENOBUFS; - goto unlock; - } - - error = 0; - ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; - do { - mnext = m->m_nextpkt; - if (wpi_tx_data(sc, m, ni) != 0) { - if_inc_counter(ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, - nmbufs); - wpi_free_txfrags(sc, ac); - ieee80211_free_mbuf(m); - ieee80211_free_node(ni); - break; - } - } while((m = mnext) != NULL); - - DPRINTF(sc, WPI_DEBUG_XMIT, "%s: done\n", __func__); - -unlock: WPI_TX_UNLOCK(sc); - - return (error); -} - -static void -wpi_watchdog_rfkill(void *arg) -{ - struct wpi_softc *sc = arg; - struct ieee80211com *ic = &sc->sc_ic; - - DPRINTF(sc, WPI_DEBUG_WATCHDOG, "RFkill Watchdog: tick\n"); - - /* No need to lock firmware memory. */ - if ((wpi_prph_read(sc, WPI_APMG_RFKILL) & 0x1) == 0) { - /* Radio kill switch is still off. */ - callout_reset(&sc->watchdog_rfkill, hz, wpi_watchdog_rfkill, - sc); - } else - ieee80211_runtask(ic, &sc->sc_radioon_task); -} - -static void -wpi_scan_timeout(void *arg) -{ - struct wpi_softc *sc = arg; - struct ieee80211com *ic = &sc->sc_ic; - - ic_printf(ic, "scan timeout\n"); - ieee80211_restart_all(ic); -} - -static void -wpi_tx_timeout(void *arg) -{ - struct wpi_softc *sc = arg; - struct ieee80211com *ic = &sc->sc_ic; - - ic_printf(ic, "device timeout\n"); - ieee80211_restart_all(ic); -} - -static void -wpi_parent(struct ieee80211com *ic) -{ - struct wpi_softc *sc = ic->ic_softc; - struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); - - if (ic->ic_nrunning > 0) { - if (wpi_init(sc) == 0) { - ieee80211_notify_radio(ic, 1); - ieee80211_start_all(ic); - } else { - ieee80211_notify_radio(ic, 0); - ieee80211_stop(vap); - } - } else { - ieee80211_notify_radio(ic, 0); - wpi_stop(sc); - } -} - -/* - * Send a command to the firmware. - */ -static int -wpi_cmd(struct wpi_softc *sc, uint8_t code, const void *buf, uint16_t size, - int async) -{ - struct wpi_tx_ring *ring = &sc->txq[WPI_CMD_QUEUE_NUM]; - struct wpi_tx_desc *desc; - struct wpi_tx_data *data; - struct wpi_tx_cmd *cmd; - struct mbuf *m; - bus_addr_t paddr; - uint16_t totlen; - int error; - - WPI_TXQ_LOCK(sc); - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); - - if (__predict_false(sc->sc_running == 0)) { - /* wpi_stop() was called */ - if (code == WPI_CMD_SCAN) - error = ENETDOWN; - else - error = 0; - - goto fail; - } - - if (async == 0) - WPI_LOCK_ASSERT(sc); - - DPRINTF(sc, WPI_DEBUG_CMD, "%s: cmd %s size %u async %d\n", - __func__, wpi_cmd_str(code), size, async); - - desc = &ring->desc[ring->cur]; - data = &ring->data[ring->cur]; - totlen = 4 + size; - - if (size > sizeof cmd->data) { - /* Command is too large to fit in a descriptor. */ - if (totlen > MCLBYTES) { - error = EINVAL; - goto fail; - } - m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE); - if (m == NULL) { - error = ENOMEM; - goto fail; - } - cmd = mtod(m, struct wpi_tx_cmd *); - error = bus_dmamap_load(ring->data_dmat, data->map, cmd, - totlen, wpi_dma_map_addr, &paddr, BUS_DMA_NOWAIT); - if (error != 0) { - m_freem(m); - goto fail; - } - data->m = m; - } else { - cmd = &ring->cmd[ring->cur]; - paddr = data->cmd_paddr; - } - - cmd->code = code; - cmd->flags = 0; - cmd->qid = ring->qid; - cmd->idx = ring->cur; - memcpy(cmd->data, buf, size); - - desc->nsegs = 1 + (WPI_PAD32(size) << 4); - desc->segs[0].addr = htole32(paddr); - desc->segs[0].len = htole32(totlen); - - if (size > sizeof cmd->data) { - bus_dmamap_sync(ring->data_dmat, data->map, - BUS_DMASYNC_PREWRITE); - } else { - bus_dmamap_sync(ring->data_dmat, ring->cmd_dma.map, - BUS_DMASYNC_PREWRITE); - } - bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, - BUS_DMASYNC_PREWRITE); - - /* Kick command ring. */ - ring->cur = (ring->cur + 1) % WPI_TX_RING_COUNT; - sc->sc_update_tx_ring(sc, ring); - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); - - WPI_TXQ_UNLOCK(sc); - - return async ? 0 : mtx_sleep(cmd, &sc->sc_mtx, PCATCH, "wpicmd", hz); - -fail: DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__); - - WPI_TXQ_UNLOCK(sc); - - return error; -} - -/* - * Configure HW multi-rate retries. - */ -static int -wpi_mrr_setup(struct wpi_softc *sc) -{ - struct ieee80211com *ic = &sc->sc_ic; - struct wpi_mrr_setup mrr; - uint8_t i; - int error; - - /* CCK rates (not used with 802.11a). */ - for (i = WPI_RIDX_CCK1; i <= WPI_RIDX_CCK11; i++) { - mrr.rates[i].flags = 0; - mrr.rates[i].plcp = wpi_ridx_to_plcp[i]; - /* Fallback to the immediate lower CCK rate (if any.) */ - mrr.rates[i].next = - (i == WPI_RIDX_CCK1) ? WPI_RIDX_CCK1 : i - 1; - /* Try twice at this rate before falling back to "next". */ - mrr.rates[i].ntries = WPI_NTRIES_DEFAULT; - } - /* OFDM rates (not used with 802.11b). */ - for (i = WPI_RIDX_OFDM6; i <= WPI_RIDX_OFDM54; i++) { - mrr.rates[i].flags = 0; - mrr.rates[i].plcp = wpi_ridx_to_plcp[i]; - /* Fallback to the immediate lower rate (if any.) */ - /* We allow fallback from OFDM/6 to CCK/2 in 11b/g mode. */ - mrr.rates[i].next = (i == WPI_RIDX_OFDM6) ? - ((ic->ic_curmode == IEEE80211_MODE_11A) ? - WPI_RIDX_OFDM6 : WPI_RIDX_CCK2) : - i - 1; - /* Try twice at this rate before falling back to "next". */ - mrr.rates[i].ntries = WPI_NTRIES_DEFAULT; - } - /* Setup MRR for control frames. */ - mrr.which = htole32(WPI_MRR_CTL); - error = wpi_cmd(sc, WPI_CMD_MRR_SETUP, &mrr, sizeof mrr, 0); - if (error != 0) { - device_printf(sc->sc_dev, - "could not setup MRR for control frames\n"); - return error; - } - /* Setup MRR for data frames. */ - mrr.which = htole32(WPI_MRR_DATA); - error = wpi_cmd(sc, WPI_CMD_MRR_SETUP, &mrr, sizeof mrr, 0); - if (error != 0) { - device_printf(sc->sc_dev, - "could not setup MRR for data frames\n"); - return error; - } - return 0; -} - -static int -wpi_add_node(struct wpi_softc *sc, struct ieee80211_node *ni) -{ - struct ieee80211com *ic = ni->ni_ic; - struct wpi_vap *wvp = WPI_VAP(ni->ni_vap); - struct wpi_node *wn = WPI_NODE(ni); - struct wpi_node_info node; - int error; - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); - - if (wn->id == WPI_ID_UNDEFINED) - return EINVAL; - - memset(&node, 0, sizeof node); - IEEE80211_ADDR_COPY(node.macaddr, ni->ni_macaddr); - node.id = wn->id; - node.plcp = (ic->ic_curmode == IEEE80211_MODE_11A) ? - wpi_ridx_to_plcp[WPI_RIDX_OFDM6] : wpi_ridx_to_plcp[WPI_RIDX_CCK1]; - node.action = htole32(WPI_ACTION_SET_RATE); - node.antenna = WPI_ANTENNA_BOTH; - - DPRINTF(sc, WPI_DEBUG_NODE, "%s: adding node %d (%s)\n", __func__, - wn->id, ether_sprintf(ni->ni_macaddr)); - - error = wpi_cmd(sc, WPI_CMD_ADD_NODE, &node, sizeof node, 1); - if (error != 0) { - device_printf(sc->sc_dev, - "%s: wpi_cmd() call failed with error code %d\n", __func__, - error); - return error; - } - - if (wvp->wv_gtk != 0) { - error = wpi_set_global_keys(ni); - if (error != 0) { - device_printf(sc->sc_dev, - "%s: error while setting global keys\n", __func__); - return ENXIO; - } - } - - return 0; -} - -/* - * Broadcast node is used to send group-addressed and management frames. - */ -static int -wpi_add_broadcast_node(struct wpi_softc *sc, int async) -{ - struct ieee80211com *ic = &sc->sc_ic; - struct wpi_node_info node; - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); - - memset(&node, 0, sizeof node); - IEEE80211_ADDR_COPY(node.macaddr, ieee80211broadcastaddr); - node.id = WPI_ID_BROADCAST; - node.plcp = (ic->ic_curmode == IEEE80211_MODE_11A) ? - wpi_ridx_to_plcp[WPI_RIDX_OFDM6] : wpi_ridx_to_plcp[WPI_RIDX_CCK1]; - node.action = htole32(WPI_ACTION_SET_RATE); - node.antenna = WPI_ANTENNA_BOTH; - - DPRINTF(sc, WPI_DEBUG_NODE, "%s: adding broadcast node\n", __func__); - - return wpi_cmd(sc, WPI_CMD_ADD_NODE, &node, sizeof node, async); -} - -static int -wpi_add_sta_node(struct wpi_softc *sc, struct ieee80211_node *ni) -{ - struct wpi_node *wn = WPI_NODE(ni); - int error; - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); - - wn->id = wpi_add_node_entry_sta(sc); - - if ((error = wpi_add_node(sc, ni)) != 0) { - wpi_del_node_entry(sc, wn->id); - wn->id = WPI_ID_UNDEFINED; - return error; - } - - return 0; -} - -static int -wpi_add_ibss_node(struct wpi_softc *sc, struct ieee80211_node *ni) -{ - struct wpi_node *wn = WPI_NODE(ni); - int error; - - KASSERT(wn->id == WPI_ID_UNDEFINED, - ("the node %d was added before", wn->id)); - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); - - if ((wn->id = wpi_add_node_entry_adhoc(sc)) == WPI_ID_UNDEFINED) { - device_printf(sc->sc_dev, "%s: h/w table is full\n", __func__); - return ENOMEM; - } - - if ((error = wpi_add_node(sc, ni)) != 0) { - wpi_del_node_entry(sc, wn->id); - wn->id = WPI_ID_UNDEFINED; - return error; - } - - return 0; -} - -static void -wpi_del_node(struct wpi_softc *sc, struct ieee80211_node *ni) -{ - struct wpi_node *wn = WPI_NODE(ni); - struct wpi_cmd_del_node node; - int error; - - KASSERT(wn->id != WPI_ID_UNDEFINED, ("undefined node id passed")); - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); - - memset(&node, 0, sizeof node); - IEEE80211_ADDR_COPY(node.macaddr, ni->ni_macaddr); - node.count = 1; - - DPRINTF(sc, WPI_DEBUG_NODE, "%s: deleting node %d (%s)\n", __func__, - wn->id, ether_sprintf(ni->ni_macaddr)); - - error = wpi_cmd(sc, WPI_CMD_DEL_NODE, &node, sizeof node, 1); - if (error != 0) { - device_printf(sc->sc_dev, - "%s: could not delete node %u, error %d\n", __func__, - wn->id, error); - } -} - -static int -wpi_updateedca(struct ieee80211com *ic) -{ -#define WPI_EXP2(x) ((1 << (x)) - 1) /* CWmin = 2^ECWmin - 1 */ - struct wpi_softc *sc = ic->ic_softc; - struct chanAccParams chp; - struct wpi_edca_params cmd; - int aci, error; - - ieee80211_wme_ic_getparams(ic, &chp); - - 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 = &chp.cap_wmeParams[aci]; - cmd.ac[aci].aifsn = ac->wmep_aifsn; - cmd.ac[aci].cwmin = htole16(WPI_EXP2(ac->wmep_logcwmin)); - cmd.ac[aci].cwmax = htole16(WPI_EXP2(ac->wmep_logcwmax)); - cmd.ac[aci].txoplimit = - htole16(IEEE80211_TXOP_TO_US(ac->wmep_txopLimit)); - - DPRINTF(sc, WPI_DEBUG_EDCA, - "setting WME for queue %d aifsn=%d cwmin=%d cwmax=%d " - "txoplimit=%d\n", aci, cmd.ac[aci].aifsn, - cmd.ac[aci].cwmin, cmd.ac[aci].cwmax, - cmd.ac[aci].txoplimit); - } - error = wpi_cmd(sc, WPI_CMD_EDCA_PARAMS, &cmd, sizeof cmd, 1); - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); - - return error; -#undef WPI_EXP2 -} - -static void -wpi_set_promisc(struct wpi_softc *sc) -{ - struct ieee80211com *ic = &sc->sc_ic; - struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); - uint32_t promisc_filter; - - promisc_filter = WPI_FILTER_CTL; - if (vap != NULL && vap->iv_opmode != IEEE80211_M_HOSTAP) - promisc_filter |= WPI_FILTER_PROMISC; - - if (ic->ic_promisc > 0) - sc->rxon.filter |= htole32(promisc_filter); - else - sc->rxon.filter &= ~htole32(promisc_filter); -} - -static void -wpi_update_promisc(struct ieee80211com *ic) -{ - struct wpi_softc *sc = ic->ic_softc; - - WPI_LOCK(sc); - if (sc->sc_running == 0) { - WPI_UNLOCK(sc); - return; - } - WPI_UNLOCK(sc); - - WPI_RXON_LOCK(sc); - wpi_set_promisc(sc); - - if (wpi_send_rxon(sc, 1, 1) != 0) { - device_printf(sc->sc_dev, "%s: could not send RXON\n", - __func__); - } - WPI_RXON_UNLOCK(sc); -} - -static void -wpi_update_mcast(struct ieee80211com *ic) -{ - /* Ignore */ -} - -static void -wpi_set_led(struct wpi_softc *sc, uint8_t which, uint8_t off, uint8_t on) -{ - struct wpi_cmd_led led; - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); - - led.which = which; - led.unit = htole32(100000); /* on/off in unit of 100ms */ - led.off = off; - led.on = on; - (void)wpi_cmd(sc, WPI_CMD_SET_LED, &led, sizeof led, 1); -} - -static int -wpi_set_timing(struct wpi_softc *sc, struct ieee80211_node *ni) -{ - struct wpi_cmd_timing cmd; - uint64_t val, mod; - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); - - memset(&cmd, 0, sizeof cmd); - memcpy(&cmd.tstamp, ni->ni_tstamp.data, sizeof (uint64_t)); - cmd.bintval = htole16(ni->ni_intval); - cmd.lintval = htole16(10); - - /* Compute remaining time until next beacon. */ - val = (uint64_t)ni->ni_intval * IEEE80211_DUR_TU; - mod = le64toh(cmd.tstamp) % val; - cmd.binitval = htole32((uint32_t)(val - mod)); - - DPRINTF(sc, WPI_DEBUG_RESET, "timing bintval=%u tstamp=%ju, init=%u\n", - ni->ni_intval, le64toh(cmd.tstamp), (uint32_t)(val - mod)); - - return wpi_cmd(sc, WPI_CMD_TIMING, &cmd, sizeof cmd, 1); -} - -/* - * This function is called periodically (every 60 seconds) to adjust output - * power to temperature changes. - */ -static void -wpi_power_calibration(struct wpi_softc *sc) -{ - int temp; - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); - - /* Update sensor data. */ - temp = (int)WPI_READ(sc, WPI_UCODE_GP2); - DPRINTF(sc, WPI_DEBUG_TEMP, "Temp in calibration is: %d\n", temp); - - /* Sanity-check read value. */ - if (temp < -260 || temp > 25) { - /* This can't be correct, ignore. */ - DPRINTF(sc, WPI_DEBUG_TEMP, - "out-of-range temperature reported: %d\n", temp); - return; - } - - DPRINTF(sc, WPI_DEBUG_TEMP, "temperature %d->%d\n", sc->temp, temp); - - /* Adjust Tx power if need be. */ - if (abs(temp - sc->temp) <= 6) - return; - - sc->temp = temp; - - if (wpi_set_txpower(sc, 1) != 0) { - /* just warn, too bad for the automatic calibration... */ - device_printf(sc->sc_dev,"could not adjust Tx power\n"); - } -} - -/* - * Set TX power for current channel. - */ -static int -wpi_set_txpower(struct wpi_softc *sc, int async) -{ - struct wpi_power_group *group; - struct wpi_cmd_txpower cmd; - uint8_t chan; - int idx, is_chan_5ghz, i; - - /* Retrieve current channel from last RXON. */ - chan = sc->rxon.chan; - is_chan_5ghz = (sc->rxon.flags & htole32(WPI_RXON_24GHZ)) == 0; - - /* Find the TX power group to which this channel belongs. */ - if (is_chan_5ghz) { - for (group = &sc->groups[1]; group < &sc->groups[4]; group++) - if (chan <= group->chan) - break; - } else - group = &sc->groups[0]; - - memset(&cmd, 0, sizeof cmd); - cmd.band = is_chan_5ghz ? WPI_BAND_5GHZ : WPI_BAND_2GHZ; - cmd.chan = htole16(chan); - - /* Set TX power for all OFDM and CCK rates. */ - for (i = 0; i <= WPI_RIDX_MAX ; i++) { - /* Retrieve TX power for this channel/rate. */ - idx = wpi_get_power_index(sc, group, chan, is_chan_5ghz, i); - - cmd.rates[i].plcp = wpi_ridx_to_plcp[i]; - - if (is_chan_5ghz) { - cmd.rates[i].rf_gain = wpi_rf_gain_5ghz[idx]; - cmd.rates[i].dsp_gain = wpi_dsp_gain_5ghz[idx]; - } else { - cmd.rates[i].rf_gain = wpi_rf_gain_2ghz[idx]; - cmd.rates[i].dsp_gain = wpi_dsp_gain_2ghz[idx]; - } - DPRINTF(sc, WPI_DEBUG_TEMP, - "chan %d/ridx %d: power index %d\n", chan, i, idx); - } - - return wpi_cmd(sc, WPI_CMD_TXPOWER, &cmd, sizeof cmd, async); -} - -/* - * Determine Tx power index for a given channel/rate combination. - * This takes into account the regulatory information from EEPROM and the - * current temperature. - */ -static int -wpi_get_power_index(struct wpi_softc *sc, struct wpi_power_group *group, - uint8_t chan, int is_chan_5ghz, int ridx) -{ -/* Fixed-point arithmetic division using a n-bit fractional part. */ -#define fdivround(a, b, n) \ - ((((1 << n) * (a)) / (b) + (1 << n) / 2) / (1 << n)) - -/* Linear interpolation. */ -#define interpolate(x, x1, y1, x2, y2, n) \ - ((y1) + fdivround(((x) - (x1)) * ((y2) - (y1)), (x2) - (x1), n)) - - struct wpi_power_sample *sample; - int pwr, idx; - - /* Default TX power is group maximum TX power minus 3dB. */ - pwr = group->maxpwr / 2; - - /* Decrease TX power for highest OFDM rates to reduce distortion. */ - switch (ridx) { - case WPI_RIDX_OFDM36: - pwr -= is_chan_5ghz ? 5 : 0; - break; - case WPI_RIDX_OFDM48: - pwr -= is_chan_5ghz ? 10 : 7; - break; - case WPI_RIDX_OFDM54: - pwr -= is_chan_5ghz ? 12 : 9; - break; - } - - /* Never exceed the channel maximum allowed TX power. */ - pwr = min(pwr, sc->maxpwr[chan]); - - /* Retrieve TX power index into gain tables from samples. */ - for (sample = group->samples; sample < &group->samples[3]; sample++) - if (pwr > sample[1].power) - break; - /* Fixed-point linear interpolation using a 19-bit fractional part. */ - idx = interpolate(pwr, sample[0].power, sample[0].index, - sample[1].power, sample[1].index, 19); - - /*- - * Adjust power index based on current temperature: - * - if cooler than factory-calibrated: decrease output power - * - if warmer than factory-calibrated: increase output power - */ - idx -= (sc->temp - group->temp) * 11 / 100; - - /* Decrease TX power for CCK rates (-5dB). */ - if (ridx >= WPI_RIDX_CCK1) - idx += 10; - - /* Make sure idx stays in a valid range. */ - if (idx < 0) - return 0; - if (idx > WPI_MAX_PWR_INDEX) - return WPI_MAX_PWR_INDEX; - return idx; - -#undef interpolate -#undef fdivround -} - -/* - * Set STA mode power saving level (between 0 and 5). - * Level 0 is CAM (Continuously Aware Mode), 5 is for maximum power saving. - */ -static int -wpi_set_pslevel(struct wpi_softc *sc, uint8_t dtim, int level, int async) -{ - struct wpi_pmgt_cmd cmd; - const struct wpi_pmgt *pmgt; - uint32_t max, reg; - uint8_t skip_dtim; - int i; - - DPRINTF(sc, WPI_DEBUG_PWRSAVE, - "%s: dtim=%d, level=%d, async=%d\n", - __func__, dtim, level, async); - - /* Select which PS parameters to use. */ - if (dtim <= 10) - pmgt = &wpi_pmgt[0][level]; - else - pmgt = &wpi_pmgt[1][level]; - - memset(&cmd, 0, sizeof cmd); - if (level != 0) /* not CAM */ - cmd.flags |= htole16(WPI_PS_ALLOW_SLEEP); - /* Retrieve PCIe Active State Power Management (ASPM). */ - reg = pci_read_config(sc->sc_dev, sc->sc_cap_off + PCIER_LINK_CTL, 1); - if (!(reg & PCIEM_LINK_CTL_ASPMC_L0S)) /* L0s Entry disabled. */ - cmd.flags |= htole16(WPI_PS_PCI_PMGT); - - cmd.rxtimeout = htole32(pmgt->rxtimeout * IEEE80211_DUR_TU); - cmd.txtimeout = htole32(pmgt->txtimeout * IEEE80211_DUR_TU); - - if (dtim == 0) { - dtim = 1; - skip_dtim = 0; - } else - skip_dtim = pmgt->skip_dtim; - - if (skip_dtim != 0) { - cmd.flags |= htole16(WPI_PS_SLEEP_OVER_DTIM); - max = pmgt->intval[4]; - if (max == (uint32_t)-1) - max = dtim * (skip_dtim + 1); - else if (max > dtim) - max = rounddown(max, dtim); - } else - max = dtim; - - for (i = 0; i < 5; i++) - cmd.intval[i] = htole32(MIN(max, pmgt->intval[i])); - - return wpi_cmd(sc, WPI_CMD_SET_POWER_MODE, &cmd, sizeof cmd, async); -} - -static int -wpi_send_btcoex(struct wpi_softc *sc) -{ - struct wpi_bluetooth cmd; - - memset(&cmd, 0, sizeof cmd); - cmd.flags = WPI_BT_COEX_MODE_4WIRE; - cmd.lead_time = WPI_BT_LEAD_TIME_DEF; - cmd.max_kill = WPI_BT_MAX_KILL_DEF; - DPRINTF(sc, WPI_DEBUG_RESET, "%s: configuring bluetooth coexistence\n", - __func__); - return wpi_cmd(sc, WPI_CMD_BT_COEX, &cmd, sizeof(cmd), 0); -} - -static int -wpi_send_rxon(struct wpi_softc *sc, int assoc, int async) -{ - int error; - - if (async) - WPI_RXON_LOCK_ASSERT(sc); - - if (assoc && wpi_check_bss_filter(sc) != 0) { - struct wpi_assoc rxon_assoc; - - rxon_assoc.flags = sc->rxon.flags; - rxon_assoc.filter = sc->rxon.filter; - rxon_assoc.ofdm_mask = sc->rxon.ofdm_mask; - rxon_assoc.cck_mask = sc->rxon.cck_mask; - rxon_assoc.reserved = 0; - - error = wpi_cmd(sc, WPI_CMD_RXON_ASSOC, &rxon_assoc, - sizeof (struct wpi_assoc), async); - if (error != 0) { - device_printf(sc->sc_dev, - "RXON_ASSOC command failed, error %d\n", error); - return error; - } - } else { - if (async) { - WPI_NT_LOCK(sc); - error = wpi_cmd(sc, WPI_CMD_RXON, &sc->rxon, - sizeof (struct wpi_rxon), async); - if (error == 0) - wpi_clear_node_table(sc); - WPI_NT_UNLOCK(sc); - } else { - error = wpi_cmd(sc, WPI_CMD_RXON, &sc->rxon, - sizeof (struct wpi_rxon), async); - if (error == 0) - wpi_clear_node_table(sc); - } - - if (error != 0) { - device_printf(sc->sc_dev, - "RXON command failed, error %d\n", error); - return error; - } - - /* Add broadcast node. */ - error = wpi_add_broadcast_node(sc, async); - if (error != 0) { - device_printf(sc->sc_dev, - "could not add broadcast node, error %d\n", error); - return error; - } - } - - /* Configuration has changed, set Tx power accordingly. */ - if ((error = wpi_set_txpower(sc, async)) != 0) { - device_printf(sc->sc_dev, - "%s: could not set TX power, error %d\n", __func__, error); - return error; - } - - return 0; -} - -/** - * Configure the card to listen to a particular channel, this transisions the - * card in to being able to receive frames from remote devices. - */ -static int -wpi_config(struct wpi_softc *sc) -{ - struct ieee80211com *ic = &sc->sc_ic; - struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); - struct ieee80211_channel *c = ic->ic_curchan; - int error; - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); - - /* Set power saving level to CAM during initialization. */ - if ((error = wpi_set_pslevel(sc, 0, 0, 0)) != 0) { - device_printf(sc->sc_dev, - "%s: could not set power saving level\n", __func__); - return error; - } - - /* Configure bluetooth coexistence. */ - if ((error = wpi_send_btcoex(sc)) != 0) { - device_printf(sc->sc_dev, - "could not configure bluetooth coexistence\n"); - return error; - } - - /* Configure adapter. */ - memset(&sc->rxon, 0, sizeof (struct wpi_rxon)); - IEEE80211_ADDR_COPY(sc->rxon.myaddr, vap->iv_myaddr); - - /* Set default channel. */ - sc->rxon.chan = ieee80211_chan2ieee(ic, c); - sc->rxon.flags = htole32(WPI_RXON_TSF | WPI_RXON_CTS_TO_SELF); - if (IEEE80211_IS_CHAN_2GHZ(c)) - sc->rxon.flags |= htole32(WPI_RXON_AUTO | WPI_RXON_24GHZ); - - sc->rxon.filter = WPI_FILTER_MULTICAST; - switch (ic->ic_opmode) { - case IEEE80211_M_STA: - sc->rxon.mode = WPI_MODE_STA; - break; - case IEEE80211_M_IBSS: - sc->rxon.mode = WPI_MODE_IBSS; - sc->rxon.filter |= WPI_FILTER_BEACON; - break; - case IEEE80211_M_HOSTAP: - /* XXX workaround for beaconing */ - sc->rxon.mode = WPI_MODE_IBSS; - sc->rxon.filter |= WPI_FILTER_ASSOC | WPI_FILTER_PROMISC; - break; - case IEEE80211_M_AHDEMO: - sc->rxon.mode = WPI_MODE_HOSTAP; - break; - case IEEE80211_M_MONITOR: - sc->rxon.mode = WPI_MODE_MONITOR; - break; - default: - device_printf(sc->sc_dev, "unknown opmode %d\n", - ic->ic_opmode); - return EINVAL; - } - sc->rxon.filter = htole32(sc->rxon.filter); - wpi_set_promisc(sc); - sc->rxon.cck_mask = 0x0f; /* not yet negotiated */ - sc->rxon.ofdm_mask = 0xff; /* not yet negotiated */ - - if ((error = wpi_send_rxon(sc, 0, 0)) != 0) { - device_printf(sc->sc_dev, "%s: could not send RXON\n", - __func__); - return error; - } - - /* Setup rate scalling. */ - if ((error = wpi_mrr_setup(sc)) != 0) { - device_printf(sc->sc_dev, "could not setup MRR, error %d\n", - error); - return error; - } - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); - - return 0; -} - -static uint16_t -wpi_get_active_dwell_time(struct wpi_softc *sc, - struct ieee80211_channel *c, uint8_t n_probes) -{ - /* No channel? Default to 2GHz settings. */ - if (c == NULL || IEEE80211_IS_CHAN_2GHZ(c)) { - return (WPI_ACTIVE_DWELL_TIME_2GHZ + - WPI_ACTIVE_DWELL_FACTOR_2GHZ * (n_probes + 1)); - } - - /* 5GHz dwell time. */ - return (WPI_ACTIVE_DWELL_TIME_5GHZ + - WPI_ACTIVE_DWELL_FACTOR_5GHZ * (n_probes + 1)); -} - -/* - * Limit the total dwell time. - * - * Returns the dwell time in milliseconds. - */ -static uint16_t -wpi_limit_dwell(struct wpi_softc *sc, uint16_t dwell_time) -{ - struct ieee80211com *ic = &sc->sc_ic; - struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); - uint16_t bintval = 0; - - /* bintval is in TU (1.024mS) */ - if (vap != NULL) - bintval = vap->iv_bss->ni_intval; - - /* - * If it's non-zero, we should calculate the minimum of - * it and the DWELL_BASE. - * - * XXX Yes, the math should take into account that bintval - * is 1.024mS, not 1mS.. - */ - if (bintval > 0) { - DPRINTF(sc, WPI_DEBUG_SCAN, "%s: bintval=%d\n", __func__, - bintval); - return (MIN(dwell_time, bintval - WPI_CHANNEL_TUNE_TIME * 2)); - } - - /* No association context? Default. */ - return dwell_time; -} - -static uint16_t -wpi_get_passive_dwell_time(struct wpi_softc *sc, struct ieee80211_channel *c) -{ - uint16_t passive; - - if (c == NULL || IEEE80211_IS_CHAN_2GHZ(c)) - passive = WPI_PASSIVE_DWELL_BASE + WPI_PASSIVE_DWELL_TIME_2GHZ; - else - passive = WPI_PASSIVE_DWELL_BASE + WPI_PASSIVE_DWELL_TIME_5GHZ; - - /* Clamp to the beacon interval if we're associated. */ - return (wpi_limit_dwell(sc, passive)); -} - -static uint32_t -wpi_get_scan_pause_time(uint32_t time, uint16_t bintval) -{ - uint32_t mod = (time % bintval) * IEEE80211_DUR_TU; - uint32_t nbeacons = time / bintval; - - if (mod > WPI_PAUSE_MAX_TIME) - mod = WPI_PAUSE_MAX_TIME; - - return WPI_PAUSE_SCAN(nbeacons, mod); -} - -/* - * Send a scan request to the firmware. - */ -static int -wpi_scan(struct wpi_softc *sc, struct ieee80211_channel *c) -{ - struct ieee80211com *ic = &sc->sc_ic; - struct ieee80211_scan_state *ss = ic->ic_scan; - struct ieee80211vap *vap = ss->ss_vap; - struct wpi_scan_hdr *hdr; - struct wpi_cmd_data *tx; - struct wpi_scan_essid *essids; - struct wpi_scan_chan *chan; - struct ieee80211_frame *wh; - struct ieee80211_rateset *rs; - uint16_t bintval, buflen, dwell_active, dwell_passive; - uint8_t *buf, *frm, i, nssid; - int bgscan, error; - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); - - /* - * We are absolutely not allowed to send a scan command when another - * scan command is pending. - */ - if (callout_pending(&sc->scan_timeout)) { - device_printf(sc->sc_dev, "%s: called whilst scanning!\n", - __func__); - error = EAGAIN; - goto fail; - } - - bgscan = wpi_check_bss_filter(sc); - bintval = vap->iv_bss->ni_intval; - if (bgscan != 0 && - bintval < WPI_QUIET_TIME_DEFAULT + WPI_CHANNEL_TUNE_TIME * 2) { - error = EOPNOTSUPP; - goto fail; - } - - buf = malloc(WPI_SCAN_MAXSZ, M_DEVBUF, M_NOWAIT | M_ZERO); - if (buf == NULL) { - device_printf(sc->sc_dev, - "%s: could not allocate buffer for scan command\n", - __func__); - error = ENOMEM; - goto fail; - } - hdr = (struct wpi_scan_hdr *)buf; - - /* - * Move to the next channel if no packets are received within 10 msecs - * after sending the probe request. - */ - hdr->quiet_time = htole16(WPI_QUIET_TIME_DEFAULT); - hdr->quiet_threshold = htole16(1); - - if (bgscan != 0) { - /* - * Max needs to be greater than active and passive and quiet! - * It's also in microseconds! - */ - hdr->max_svc = htole32(250 * IEEE80211_DUR_TU); - hdr->pause_svc = htole32(wpi_get_scan_pause_time(100, - bintval)); - } - - hdr->filter = htole32(WPI_FILTER_MULTICAST | WPI_FILTER_BEACON); - - tx = (struct wpi_cmd_data *)(hdr + 1); - tx->flags = htole32(WPI_TX_AUTO_SEQ); - tx->id = WPI_ID_BROADCAST; - tx->lifetime = htole32(WPI_LIFETIME_INFINITE); - - if (IEEE80211_IS_CHAN_5GHZ(c)) { - /* Send probe requests at 6Mbps. */ - tx->plcp = wpi_ridx_to_plcp[WPI_RIDX_OFDM6]; - rs = &ic->ic_sup_rates[IEEE80211_MODE_11A]; - } else { - hdr->flags = htole32(WPI_RXON_24GHZ | WPI_RXON_AUTO); - /* Send probe requests at 1Mbps. */ - tx->plcp = wpi_ridx_to_plcp[WPI_RIDX_CCK1]; - rs = &ic->ic_sup_rates[IEEE80211_MODE_11G]; - } - - essids = (struct wpi_scan_essid *)(tx + 1); - nssid = MIN(ss->ss_nssid, WPI_SCAN_MAX_ESSIDS); - for (i = 0; i < nssid; i++) { - essids[i].id = IEEE80211_ELEMID_SSID; - essids[i].len = MIN(ss->ss_ssid[i].len, IEEE80211_NWID_LEN); - memcpy(essids[i].data, ss->ss_ssid[i].ssid, essids[i].len); -#ifdef WPI_DEBUG - if (sc->sc_debug & WPI_DEBUG_SCAN) { - printf("Scanning Essid: "); - ieee80211_print_essid(essids[i].data, essids[i].len); - printf("\n"); - } -#endif - } - - /* - * Build a probe request frame. Most of the following code is a - * copy & paste of what is done in net80211. - */ - wh = (struct ieee80211_frame *)(essids + WPI_SCAN_MAX_ESSIDS); - wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | - IEEE80211_FC0_SUBTYPE_PROBE_REQ; - wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; - IEEE80211_ADDR_COPY(wh->i_addr1, ieee80211broadcastaddr); - IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); - IEEE80211_ADDR_COPY(wh->i_addr3, ieee80211broadcastaddr); - - frm = (uint8_t *)(wh + 1); - frm = ieee80211_add_ssid(frm, NULL, 0); - frm = ieee80211_add_rates(frm, rs); - if (rs->rs_nrates > IEEE80211_RATE_SIZE) - frm = ieee80211_add_xrates(frm, rs); - - /* Set length of probe request. */ - tx->len = htole16(frm - (uint8_t *)wh); - - /* - * Construct information about the channel that we - * want to scan. The firmware expects this to be directly - * after the scan probe request - */ - chan = (struct wpi_scan_chan *)frm; - chan->chan = ieee80211_chan2ieee(ic, c); - chan->flags = 0; - if (nssid) { - hdr->crc_threshold = WPI_SCAN_CRC_TH_DEFAULT; - chan->flags |= WPI_CHAN_NPBREQS(nssid); - } else - hdr->crc_threshold = WPI_SCAN_CRC_TH_NEVER; - - if (!IEEE80211_IS_CHAN_PASSIVE(c)) - chan->flags |= WPI_CHAN_ACTIVE; - - /* - * Calculate the active/passive dwell times. - */ - dwell_active = wpi_get_active_dwell_time(sc, c, nssid); - dwell_passive = wpi_get_passive_dwell_time(sc, c); - - /* Make sure they're valid. */ - if (dwell_active > dwell_passive) - dwell_active = dwell_passive; - - chan->active = htole16(dwell_active); - chan->passive = htole16(dwell_passive); - - chan->dsp_gain = 0x6e; /* Default level */ - - if (IEEE80211_IS_CHAN_5GHZ(c)) - chan->rf_gain = 0x3b; - else - chan->rf_gain = 0x28; - - DPRINTF(sc, WPI_DEBUG_SCAN, "Scanning %u Passive: %d\n", - chan->chan, IEEE80211_IS_CHAN_PASSIVE(c)); - - hdr->nchan++; - - if (hdr->nchan == 1 && sc->rxon.chan == chan->chan) { - /* XXX Force probe request transmission. */ - memcpy(chan + 1, chan, sizeof (struct wpi_scan_chan)); - - chan++; - - /* Reduce unnecessary delay. */ - chan->flags = 0; - chan->passive = chan->active = hdr->quiet_time; - - hdr->nchan++; - } - - chan++; - - buflen = (uint8_t *)chan - buf; - hdr->len = htole16(buflen); - - DPRINTF(sc, WPI_DEBUG_CMD, "sending scan command nchan=%d\n", - hdr->nchan); - error = wpi_cmd(sc, WPI_CMD_SCAN, buf, buflen, 1); - free(buf, M_DEVBUF); - - if (error != 0) - goto fail; - - callout_reset(&sc->scan_timeout, 5*hz, wpi_scan_timeout, sc); - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); - - return 0; - -fail: DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__); - - return error; -} - -static int -wpi_auth(struct wpi_softc *sc, struct ieee80211vap *vap) -{ - struct ieee80211com *ic = vap->iv_ic; - struct ieee80211_node *ni = vap->iv_bss; - struct ieee80211_channel *c = ni->ni_chan; - int error; - - WPI_RXON_LOCK(sc); - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); - - /* Update adapter configuration. */ - sc->rxon.associd = 0; - sc->rxon.filter &= ~htole32(WPI_FILTER_BSS); - IEEE80211_ADDR_COPY(sc->rxon.bssid, ni->ni_bssid); - sc->rxon.chan = ieee80211_chan2ieee(ic, c); - sc->rxon.flags = htole32(WPI_RXON_TSF | WPI_RXON_CTS_TO_SELF); - if (IEEE80211_IS_CHAN_2GHZ(c)) - sc->rxon.flags |= htole32(WPI_RXON_AUTO | WPI_RXON_24GHZ); - if (ic->ic_flags & IEEE80211_F_SHSLOT) - sc->rxon.flags |= htole32(WPI_RXON_SHSLOT); - if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) - sc->rxon.flags |= htole32(WPI_RXON_SHPREAMBLE); - if (IEEE80211_IS_CHAN_A(c)) { - sc->rxon.cck_mask = 0; - sc->rxon.ofdm_mask = 0x15; - } else if (IEEE80211_IS_CHAN_B(c)) { - sc->rxon.cck_mask = 0x03; - sc->rxon.ofdm_mask = 0; - } else { - /* Assume 802.11b/g. */ - sc->rxon.cck_mask = 0x0f; - sc->rxon.ofdm_mask = 0x15; - } - - DPRINTF(sc, WPI_DEBUG_STATE, "rxon chan %d flags %x cck %x ofdm %x\n", - sc->rxon.chan, sc->rxon.flags, sc->rxon.cck_mask, - sc->rxon.ofdm_mask); - - if ((error = wpi_send_rxon(sc, 0, 1)) != 0) { - device_printf(sc->sc_dev, "%s: could not send RXON\n", - __func__); - } - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); - - WPI_RXON_UNLOCK(sc); - - return error; -} - -static int -wpi_config_beacon(struct wpi_vap *wvp) -{ - struct ieee80211vap *vap = &wvp->wv_vap; - struct ieee80211com *ic = vap->iv_ic; - struct ieee80211_beacon_offsets *bo = &vap->iv_bcn_off; - struct wpi_buf *bcn = &wvp->wv_bcbuf; - struct wpi_softc *sc = ic->ic_softc; - struct wpi_cmd_beacon *cmd = (struct wpi_cmd_beacon *)&bcn->data; - struct ieee80211_tim_ie *tie; - struct mbuf *m; - uint8_t *ptr; - int error; - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); - - WPI_VAP_LOCK_ASSERT(wvp); - - cmd->len = htole16(bcn->m->m_pkthdr.len); - cmd->plcp = (ic->ic_curmode == IEEE80211_MODE_11A) ? - wpi_ridx_to_plcp[WPI_RIDX_OFDM6] : wpi_ridx_to_plcp[WPI_RIDX_CCK1]; - - /* XXX seems to be unused */ - if (*(bo->bo_tim) == IEEE80211_ELEMID_TIM) { - tie = (struct ieee80211_tim_ie *) bo->bo_tim; - ptr = mtod(bcn->m, uint8_t *); - - cmd->tim = htole16(bo->bo_tim - ptr); - cmd->timsz = tie->tim_len; - } - - /* Necessary for recursion in ieee80211_beacon_update(). */ - m = bcn->m; - bcn->m = m_dup(m, M_NOWAIT); - if (bcn->m == NULL) { - device_printf(sc->sc_dev, - "%s: could not copy beacon frame\n", __func__); - error = ENOMEM; - goto end; - } - - if ((error = wpi_cmd2(sc, bcn)) != 0) { - device_printf(sc->sc_dev, - "%s: could not update beacon frame, error %d", __func__, - error); - m_freem(bcn->m); - } - - /* Restore mbuf. */ -end: bcn->m = m; - - return error; -} - -static int -wpi_setup_beacon(struct wpi_softc *sc, struct ieee80211_node *ni) -{ - struct ieee80211vap *vap = ni->ni_vap; - struct wpi_vap *wvp = WPI_VAP(vap); - struct wpi_buf *bcn = &wvp->wv_bcbuf; - struct mbuf *m; - int error; - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); - - if (ni->ni_chan == IEEE80211_CHAN_ANYC) - return EINVAL; - - m = ieee80211_beacon_alloc(ni); - if (m == NULL) { - device_printf(sc->sc_dev, - "%s: could not allocate beacon frame\n", __func__); - return ENOMEM; - } - - WPI_VAP_LOCK(wvp); - if (bcn->m != NULL) - m_freem(bcn->m); - - bcn->m = m; - - error = wpi_config_beacon(wvp); - WPI_VAP_UNLOCK(wvp); - - return error; -} - -static void -wpi_update_beacon(struct ieee80211vap *vap, int item) -{ - struct wpi_softc *sc = vap->iv_ic->ic_softc; - struct wpi_vap *wvp = WPI_VAP(vap); - struct wpi_buf *bcn = &wvp->wv_bcbuf; - struct ieee80211_beacon_offsets *bo = &vap->iv_bcn_off; - struct ieee80211_node *ni = vap->iv_bss; - int mcast = 0; - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); - - WPI_VAP_LOCK(wvp); - if (bcn->m == NULL) { - bcn->m = ieee80211_beacon_alloc(ni); - if (bcn->m == NULL) { - device_printf(sc->sc_dev, - "%s: could not allocate beacon frame\n", __func__); - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, - __func__); - - WPI_VAP_UNLOCK(wvp); - return; - } - } - WPI_VAP_UNLOCK(wvp); - - if (item == IEEE80211_BEACON_TIM) - mcast = 1; /* TODO */ - - setbit(bo->bo_flags, item); - ieee80211_beacon_update(ni, bcn->m, mcast); - - WPI_VAP_LOCK(wvp); - wpi_config_beacon(wvp); - WPI_VAP_UNLOCK(wvp); - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); -} - -static void -wpi_newassoc(struct ieee80211_node *ni, int isnew) -{ - struct ieee80211vap *vap = ni->ni_vap; - struct wpi_softc *sc = ni->ni_ic->ic_softc; - struct wpi_node *wn = WPI_NODE(ni); - int error; - - WPI_NT_LOCK(sc); - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); - - if (vap->iv_opmode != IEEE80211_M_STA && wn->id == WPI_ID_UNDEFINED) { - if ((error = wpi_add_ibss_node(sc, ni)) != 0) { - device_printf(sc->sc_dev, - "%s: could not add IBSS node, error %d\n", - __func__, error); - } - } - WPI_NT_UNLOCK(sc); -} - -static int -wpi_run(struct wpi_softc *sc, struct ieee80211vap *vap) -{ - struct ieee80211com *ic = vap->iv_ic; - struct ieee80211_node *ni = vap->iv_bss; - struct ieee80211_channel *c = ni->ni_chan; - int error; - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); - - if (vap->iv_opmode == IEEE80211_M_MONITOR) { - /* Link LED blinks while monitoring. */ - wpi_set_led(sc, WPI_LED_LINK, 5, 5); - return 0; - } - - /* XXX kernel panic workaround */ - if (c == IEEE80211_CHAN_ANYC) { - device_printf(sc->sc_dev, "%s: incomplete configuration\n", - __func__); - return EINVAL; - } - - if ((error = wpi_set_timing(sc, ni)) != 0) { - device_printf(sc->sc_dev, - "%s: could not set timing, error %d\n", __func__, error); - return error; - } - - /* Update adapter configuration. */ - WPI_RXON_LOCK(sc); - IEEE80211_ADDR_COPY(sc->rxon.bssid, ni->ni_bssid); - sc->rxon.associd = htole16(IEEE80211_NODE_AID(ni)); - sc->rxon.chan = ieee80211_chan2ieee(ic, c); - sc->rxon.flags = htole32(WPI_RXON_TSF | WPI_RXON_CTS_TO_SELF); - if (IEEE80211_IS_CHAN_2GHZ(c)) - sc->rxon.flags |= htole32(WPI_RXON_AUTO | WPI_RXON_24GHZ); - if (ic->ic_flags & IEEE80211_F_SHSLOT) - sc->rxon.flags |= htole32(WPI_RXON_SHSLOT); - if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) - sc->rxon.flags |= htole32(WPI_RXON_SHPREAMBLE); - if (IEEE80211_IS_CHAN_A(c)) { - sc->rxon.cck_mask = 0; - sc->rxon.ofdm_mask = 0x15; - } else if (IEEE80211_IS_CHAN_B(c)) { - sc->rxon.cck_mask = 0x03; - sc->rxon.ofdm_mask = 0; - } else { - /* Assume 802.11b/g. */ - sc->rxon.cck_mask = 0x0f; - sc->rxon.ofdm_mask = 0x15; - } - sc->rxon.filter |= htole32(WPI_FILTER_BSS); - - DPRINTF(sc, WPI_DEBUG_STATE, "rxon chan %d flags %x\n", - sc->rxon.chan, sc->rxon.flags); - - if ((error = wpi_send_rxon(sc, 0, 1)) != 0) { - WPI_RXON_UNLOCK(sc); - device_printf(sc->sc_dev, "%s: could not send RXON\n", - __func__); - return error; - } - - /* Start periodic calibration timer. */ - callout_reset(&sc->calib_to, 60*hz, wpi_calib_timeout, sc); - - WPI_RXON_UNLOCK(sc); - - if (vap->iv_opmode == IEEE80211_M_IBSS || - vap->iv_opmode == IEEE80211_M_HOSTAP) { - if ((error = wpi_setup_beacon(sc, ni)) != 0) { - device_printf(sc->sc_dev, - "%s: could not setup beacon, error %d\n", __func__, - error); - return error; - } - } - - if (vap->iv_opmode == IEEE80211_M_STA) { - /* Add BSS node. */ - WPI_NT_LOCK(sc); - error = wpi_add_sta_node(sc, ni); - WPI_NT_UNLOCK(sc); - if (error != 0) { - device_printf(sc->sc_dev, - "%s: could not add BSS node, error %d\n", __func__, - error); - return error; - } - } - - /* Link LED always on while associated. */ - wpi_set_led(sc, WPI_LED_LINK, 0, 1); - - /* Enable power-saving mode if requested by user. */ - if ((vap->iv_flags & IEEE80211_F_PMGTON) && - vap->iv_opmode != IEEE80211_M_IBSS) - (void)wpi_set_pslevel(sc, 0, 3, 1); - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); - - return 0; -} - -static int -wpi_load_key(struct ieee80211_node *ni, const struct ieee80211_key *k) -{ - const struct ieee80211_cipher *cip = k->wk_cipher; - struct ieee80211vap *vap = ni->ni_vap; - struct wpi_softc *sc = ni->ni_ic->ic_softc; - struct wpi_node *wn = WPI_NODE(ni); - struct wpi_node_info node; - uint16_t kflags; - int error; - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); - - if (wpi_check_node_entry(sc, wn->id) == 0) { - device_printf(sc->sc_dev, "%s: node does not exist\n", - __func__); - return 0; - } - - switch (cip->ic_cipher) { - case IEEE80211_CIPHER_AES_CCM: - kflags = WPI_KFLAG_CCMP; - break; - - default: - device_printf(sc->sc_dev, "%s: unknown cipher %d\n", __func__, - cip->ic_cipher); - return 0; - } - - kflags |= WPI_KFLAG_KID(k->wk_keyix); - if (k->wk_flags & IEEE80211_KEY_GROUP) - kflags |= WPI_KFLAG_MULTICAST; - - memset(&node, 0, sizeof node); - node.id = wn->id; - node.control = WPI_NODE_UPDATE; - node.flags = WPI_FLAG_KEY_SET; - node.kflags = htole16(kflags); - memcpy(node.key, k->wk_key, k->wk_keylen); -again: - DPRINTF(sc, WPI_DEBUG_KEY, - "%s: setting %s key id %d for node %d (%s)\n", __func__, - (kflags & WPI_KFLAG_MULTICAST) ? "group" : "ucast", k->wk_keyix, - node.id, ether_sprintf(ni->ni_macaddr)); - - error = wpi_cmd(sc, WPI_CMD_ADD_NODE, &node, sizeof node, 1); - if (error != 0) { - device_printf(sc->sc_dev, "can't update node info, error %d\n", - error); - return !error; - } - - if (!(kflags & WPI_KFLAG_MULTICAST) && &vap->iv_nw_keys[0] <= k && - k < &vap->iv_nw_keys[IEEE80211_WEP_NKID]) { - kflags |= WPI_KFLAG_MULTICAST; - node.kflags = htole16(kflags); - - goto again; - } - - return 1; -} - -static void -wpi_load_key_cb(void *arg, struct ieee80211_node *ni) -{ - const struct ieee80211_key *k = arg; - struct ieee80211vap *vap = ni->ni_vap; - struct wpi_softc *sc = ni->ni_ic->ic_softc; - struct wpi_node *wn = WPI_NODE(ni); - int error; - - if (vap->iv_bss == ni && wn->id == WPI_ID_UNDEFINED) - return; - - WPI_NT_LOCK(sc); - error = wpi_load_key(ni, k); - WPI_NT_UNLOCK(sc); - - if (error == 0) { - device_printf(sc->sc_dev, "%s: error while setting key\n", - __func__); - } -} - -static int -wpi_set_global_keys(struct ieee80211_node *ni) -{ - struct ieee80211vap *vap = ni->ni_vap; - struct ieee80211_key *wk = &vap->iv_nw_keys[0]; - int error = 1; - - for (; wk < &vap->iv_nw_keys[IEEE80211_WEP_NKID] && error; wk++) - if (wk->wk_keyix != IEEE80211_KEYIX_NONE) - error = wpi_load_key(ni, wk); - - return !error; -} - -static int -wpi_del_key(struct ieee80211_node *ni, const struct ieee80211_key *k) -{ - struct ieee80211vap *vap = ni->ni_vap; - struct wpi_softc *sc = ni->ni_ic->ic_softc; - struct wpi_node *wn = WPI_NODE(ni); - struct wpi_node_info node; - uint16_t kflags; - int error; - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); - - if (wpi_check_node_entry(sc, wn->id) == 0) { - DPRINTF(sc, WPI_DEBUG_KEY, "%s: node was removed\n", __func__); - return 1; /* Nothing to do. */ - } - - kflags = WPI_KFLAG_KID(k->wk_keyix); - if (k->wk_flags & IEEE80211_KEY_GROUP) - kflags |= WPI_KFLAG_MULTICAST; - - memset(&node, 0, sizeof node); - node.id = wn->id; - node.control = WPI_NODE_UPDATE; - node.flags = WPI_FLAG_KEY_SET; - node.kflags = htole16(kflags); -again: - DPRINTF(sc, WPI_DEBUG_KEY, "%s: deleting %s key %d for node %d (%s)\n", - __func__, (kflags & WPI_KFLAG_MULTICAST) ? "group" : "ucast", - k->wk_keyix, node.id, ether_sprintf(ni->ni_macaddr)); - - error = wpi_cmd(sc, WPI_CMD_ADD_NODE, &node, sizeof node, 1); - if (error != 0) { - device_printf(sc->sc_dev, "can't update node info, error %d\n", - error); - return !error; - } - - if (!(kflags & WPI_KFLAG_MULTICAST) && &vap->iv_nw_keys[0] <= k && - k < &vap->iv_nw_keys[IEEE80211_WEP_NKID]) { - kflags |= WPI_KFLAG_MULTICAST; - node.kflags = htole16(kflags); - - goto again; - } - - return 1; -} - -static void -wpi_del_key_cb(void *arg, struct ieee80211_node *ni) -{ - const struct ieee80211_key *k = arg; - struct ieee80211vap *vap = ni->ni_vap; - struct wpi_softc *sc = ni->ni_ic->ic_softc; - struct wpi_node *wn = WPI_NODE(ni); - int error; - - if (vap->iv_bss == ni && wn->id == WPI_ID_UNDEFINED) - return; - - WPI_NT_LOCK(sc); - error = wpi_del_key(ni, k); - WPI_NT_UNLOCK(sc); - - if (error == 0) { - device_printf(sc->sc_dev, "%s: error while deleting key\n", - __func__); - } -} - -static int -wpi_process_key(struct ieee80211vap *vap, const struct ieee80211_key *k, - int set) -{ - struct ieee80211com *ic = vap->iv_ic; - struct wpi_softc *sc = ic->ic_softc; - struct wpi_vap *wvp = WPI_VAP(vap); - struct ieee80211_node *ni; - int error, ni_ref = 0; - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); - - if (k->wk_flags & IEEE80211_KEY_SWCRYPT) { - /* Not for us. */ - return 1; - } - - if (!(k->wk_flags & IEEE80211_KEY_RECV)) { - /* XMIT keys are handled in wpi_tx_data(). */ - return 1; - } - - /* Handle group keys. */ - if (&vap->iv_nw_keys[0] <= k && - k < &vap->iv_nw_keys[IEEE80211_WEP_NKID]) { - WPI_NT_LOCK(sc); - if (set) - wvp->wv_gtk |= WPI_VAP_KEY(k->wk_keyix); - else - wvp->wv_gtk &= ~WPI_VAP_KEY(k->wk_keyix); - WPI_NT_UNLOCK(sc); - - if (vap->iv_state == IEEE80211_S_RUN) { - ieee80211_iterate_nodes(&ic->ic_sta, - set ? wpi_load_key_cb : wpi_del_key_cb, - __DECONST(void *, k)); - } - - return 1; - } - - switch (vap->iv_opmode) { - case IEEE80211_M_STA: - ni = vap->iv_bss; - break; - - case IEEE80211_M_IBSS: - case IEEE80211_M_AHDEMO: - case IEEE80211_M_HOSTAP: - ni = ieee80211_find_vap_node(&ic->ic_sta, vap, k->wk_macaddr); - if (ni == NULL) - return 0; /* should not happen */ - - ni_ref = 1; - break; - - default: - device_printf(sc->sc_dev, "%s: unknown opmode %d\n", __func__, - vap->iv_opmode); - return 0; - } - - WPI_NT_LOCK(sc); - if (set) - error = wpi_load_key(ni, k); - else - error = wpi_del_key(ni, k); - WPI_NT_UNLOCK(sc); - - if (ni_ref) - ieee80211_node_decref(ni); - - return error; -} - -static int -wpi_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k) -{ - return wpi_process_key(vap, k, 1); -} - -static int -wpi_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k) -{ - return wpi_process_key(vap, k, 0); -} - -/* - * This function is called after the runtime firmware notifies us of its - * readiness (called in a process context). - */ -static int -wpi_post_alive(struct wpi_softc *sc) -{ - int ntries, error; - - /* Check (again) that the radio is not disabled. */ - if ((error = wpi_nic_lock(sc)) != 0) - return error; - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); - - /* NB: Runtime firmware must be up and running. */ - if (!(wpi_prph_read(sc, WPI_APMG_RFKILL) & 1)) { - device_printf(sc->sc_dev, - "RF switch: radio disabled (%s)\n", __func__); - wpi_nic_unlock(sc); - return EPERM; /* :-) */ - } - wpi_nic_unlock(sc); - - /* Wait for thermal sensor to calibrate. */ - for (ntries = 0; ntries < 1000; ntries++) { - if ((sc->temp = (int)WPI_READ(sc, WPI_UCODE_GP2)) != 0) - break; - DELAY(10); - } - - if (ntries == 1000) { - device_printf(sc->sc_dev, - "timeout waiting for thermal sensor calibration\n"); - return ETIMEDOUT; - } - - DPRINTF(sc, WPI_DEBUG_TEMP, "temperature %d\n", sc->temp); - return 0; -} - -/* - * The firmware boot code is small and is intended to be copied directly into - * the NIC internal memory (no DMA transfer). - */ -static int -wpi_load_bootcode(struct wpi_softc *sc, const uint8_t *ucode, uint32_t size) -{ - int error, ntries; - - DPRINTF(sc, WPI_DEBUG_HW, "Loading microcode size 0x%x\n", size); - - size /= sizeof (uint32_t); - - if ((error = wpi_nic_lock(sc)) != 0) - return error; - - /* Copy microcode image into NIC memory. */ - wpi_prph_write_region_4(sc, WPI_BSM_SRAM_BASE, - (const uint32_t *)ucode, size); - - wpi_prph_write(sc, WPI_BSM_WR_MEM_SRC, 0); - wpi_prph_write(sc, WPI_BSM_WR_MEM_DST, WPI_FW_TEXT_BASE); - wpi_prph_write(sc, WPI_BSM_WR_DWCOUNT, size); - - /* Start boot load now. */ - wpi_prph_write(sc, WPI_BSM_WR_CTRL, WPI_BSM_WR_CTRL_START); - - /* Wait for transfer to complete. */ - for (ntries = 0; ntries < 1000; ntries++) { - uint32_t status = WPI_READ(sc, WPI_FH_TX_STATUS); - DPRINTF(sc, WPI_DEBUG_HW, - "firmware status=0x%x, val=0x%x, result=0x%x\n", status, - WPI_FH_TX_STATUS_IDLE(6), - status & WPI_FH_TX_STATUS_IDLE(6)); - if (status & WPI_FH_TX_STATUS_IDLE(6)) { - DPRINTF(sc, WPI_DEBUG_HW, - "Status Match! - ntries = %d\n", ntries); - break; - } - DELAY(10); - } - if (ntries == 1000) { - device_printf(sc->sc_dev, "%s: could not load boot firmware\n", - __func__); - wpi_nic_unlock(sc); - return ETIMEDOUT; - } - - /* Enable boot after power up. */ - wpi_prph_write(sc, WPI_BSM_WR_CTRL, WPI_BSM_WR_CTRL_START_EN); - - wpi_nic_unlock(sc); - return 0; -} - -static int -wpi_load_firmware(struct wpi_softc *sc) -{ - struct wpi_fw_info *fw = &sc->fw; - struct wpi_dma_info *dma = &sc->fw_dma; - int error; - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); - - /* Copy initialization sections into pre-allocated DMA-safe memory. */ - memcpy(dma->vaddr, fw->init.data, fw->init.datasz); - bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); - memcpy(dma->vaddr + WPI_FW_DATA_MAXSZ, fw->init.text, fw->init.textsz); - bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); - - /* Tell adapter where to find initialization sections. */ - if ((error = wpi_nic_lock(sc)) != 0) - return error; - wpi_prph_write(sc, WPI_BSM_DRAM_DATA_ADDR, dma->paddr); - wpi_prph_write(sc, WPI_BSM_DRAM_DATA_SIZE, fw->init.datasz); - wpi_prph_write(sc, WPI_BSM_DRAM_TEXT_ADDR, - dma->paddr + WPI_FW_DATA_MAXSZ); - wpi_prph_write(sc, WPI_BSM_DRAM_TEXT_SIZE, fw->init.textsz); - wpi_nic_unlock(sc); - - /* Load firmware boot code. */ - error = wpi_load_bootcode(sc, fw->boot.text, fw->boot.textsz); - if (error != 0) { - device_printf(sc->sc_dev, "%s: could not load boot firmware\n", - __func__); - return error; - } - - /* Now press "execute". */ - WPI_WRITE(sc, WPI_RESET, 0); - - /* Wait at most one second for first alive notification. */ - if ((error = mtx_sleep(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; - } - - /* Copy runtime sections into pre-allocated DMA-safe memory. */ - memcpy(dma->vaddr, fw->main.data, fw->main.datasz); - bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); - memcpy(dma->vaddr + WPI_FW_DATA_MAXSZ, fw->main.text, fw->main.textsz); - bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); - - /* Tell adapter where to find runtime sections. */ - if ((error = wpi_nic_lock(sc)) != 0) - return error; - wpi_prph_write(sc, WPI_BSM_DRAM_DATA_ADDR, dma->paddr); - wpi_prph_write(sc, WPI_BSM_DRAM_DATA_SIZE, fw->main.datasz); - wpi_prph_write(sc, WPI_BSM_DRAM_TEXT_ADDR, - dma->paddr + WPI_FW_DATA_MAXSZ); - wpi_prph_write(sc, WPI_BSM_DRAM_TEXT_SIZE, - WPI_FW_UPDATED | fw->main.textsz); - wpi_nic_unlock(sc); - - return 0; -} - -static int -wpi_read_firmware(struct wpi_softc *sc) -{ - const struct firmware *fp; - struct wpi_fw_info *fw = &sc->fw; - const struct wpi_firmware_hdr *hdr; - int error; - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); - - DPRINTF(sc, WPI_DEBUG_FIRMWARE, - "Attempting Loading Firmware from %s module\n", WPI_FW_NAME); - - WPI_UNLOCK(sc); - fp = firmware_get(WPI_FW_NAME); - WPI_LOCK(sc); - - if (fp == NULL) { - device_printf(sc->sc_dev, - "could not load firmware image '%s'\n", WPI_FW_NAME); - return EINVAL; - } - - sc->fw_fp = fp; - - if (fp->datasize < sizeof (struct wpi_firmware_hdr)) { - device_printf(sc->sc_dev, - "firmware file too short: %zu bytes\n", fp->datasize); - error = EINVAL; - goto fail; - } - - fw->size = fp->datasize; - fw->data = (const uint8_t *)fp->data; - - /* Extract firmware header information. */ - hdr = (const struct wpi_firmware_hdr *)fw->data; - - /* | RUNTIME FIRMWARE | INIT FIRMWARE | BOOT FW | - |HDR|<--TEXT-->|<--DATA-->|<--TEXT-->|<--DATA-->|<--TEXT-->| */ - - fw->main.textsz = le32toh(hdr->rtextsz); - fw->main.datasz = le32toh(hdr->rdatasz); - fw->init.textsz = le32toh(hdr->itextsz); - fw->init.datasz = le32toh(hdr->idatasz); - fw->boot.textsz = le32toh(hdr->btextsz); - fw->boot.datasz = 0; - - /* Sanity-check firmware header. */ - if (fw->main.textsz > WPI_FW_TEXT_MAXSZ || - fw->main.datasz > WPI_FW_DATA_MAXSZ || - fw->init.textsz > WPI_FW_TEXT_MAXSZ || - fw->init.datasz > WPI_FW_DATA_MAXSZ || - fw->boot.textsz > WPI_FW_BOOT_TEXT_MAXSZ || - (fw->boot.textsz & 3) != 0) { - device_printf(sc->sc_dev, "invalid firmware header\n"); - error = EINVAL; - goto fail; - } - - /* Check that all firmware sections fit. */ - if (fw->size < sizeof (*hdr) + fw->main.textsz + fw->main.datasz + - fw->init.textsz + fw->init.datasz + fw->boot.textsz) { - device_printf(sc->sc_dev, - "firmware file too short: %zu bytes\n", fw->size); - error = EINVAL; - goto fail; - } - - /* Get pointers to firmware sections. */ - fw->main.text = (const uint8_t *)(hdr + 1); - fw->main.data = fw->main.text + fw->main.textsz; - fw->init.text = fw->main.data + fw->main.datasz; - fw->init.data = fw->init.text + fw->init.textsz; - fw->boot.text = fw->init.data + fw->init.datasz; - - DPRINTF(sc, WPI_DEBUG_FIRMWARE, - "Firmware Version: Major %d, Minor %d, Driver %d, \n" - "runtime (text: %u, data: %u) init (text: %u, data %u) " - "boot (text %u)\n", hdr->major, hdr->minor, le32toh(hdr->driver), - fw->main.textsz, fw->main.datasz, - fw->init.textsz, fw->init.datasz, fw->boot.textsz); - - DPRINTF(sc, WPI_DEBUG_FIRMWARE, "fw->main.text %p\n", fw->main.text); - DPRINTF(sc, WPI_DEBUG_FIRMWARE, "fw->main.data %p\n", fw->main.data); - DPRINTF(sc, WPI_DEBUG_FIRMWARE, "fw->init.text %p\n", fw->init.text); - DPRINTF(sc, WPI_DEBUG_FIRMWARE, "fw->init.data %p\n", fw->init.data); - DPRINTF(sc, WPI_DEBUG_FIRMWARE, "fw->boot.text %p\n", fw->boot.text); - - return 0; - -fail: wpi_unload_firmware(sc); - return error; -} - -/** - * Free the referenced firmware image - */ -static void -wpi_unload_firmware(struct wpi_softc *sc) -{ - if (sc->fw_fp != NULL) { - firmware_put(sc->fw_fp, FIRMWARE_UNLOAD); - sc->fw_fp = NULL; - } -} - -static int -wpi_clock_wait(struct wpi_softc *sc) -{ - int ntries; - - /* Set "initialization complete" bit. */ - WPI_SETBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_INIT_DONE); - - /* Wait for clock stabilization. */ - for (ntries = 0; ntries < 2500; ntries++) { - if (WPI_READ(sc, WPI_GP_CNTRL) & WPI_GP_CNTRL_MAC_CLOCK_READY) - return 0; - DELAY(100); - } - device_printf(sc->sc_dev, - "%s: timeout waiting for clock stabilization\n", __func__); - - return ETIMEDOUT; -} - -static int -wpi_apm_init(struct wpi_softc *sc) -{ - uint32_t reg; - int error; - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); - - /* Disable L0s exit timer (NMI bug workaround). */ - WPI_SETBITS(sc, WPI_GIO_CHICKEN, WPI_GIO_CHICKEN_DIS_L0S_TIMER); - /* Don't wait for ICH L0s (ICH bug workaround). */ - WPI_SETBITS(sc, WPI_GIO_CHICKEN, WPI_GIO_CHICKEN_L1A_NO_L0S_RX); - - /* Set FH wait threshold to max (HW bug under stress workaround). */ - WPI_SETBITS(sc, WPI_DBG_HPET_MEM, 0xffff0000); - - /* Retrieve PCIe Active State Power Management (ASPM). */ - reg = pci_read_config(sc->sc_dev, sc->sc_cap_off + PCIER_LINK_CTL, 1); - /* Workaround for HW instability in PCIe L0->L0s->L1 transition. */ - if (reg & PCIEM_LINK_CTL_ASPMC_L1) /* L1 Entry enabled. */ - WPI_SETBITS(sc, WPI_GIO, WPI_GIO_L0S_ENA); - else - WPI_CLRBITS(sc, WPI_GIO, WPI_GIO_L0S_ENA); - - WPI_SETBITS(sc, WPI_ANA_PLL, WPI_ANA_PLL_INIT); - - /* Wait for clock stabilization before accessing prph. */ - if ((error = wpi_clock_wait(sc)) != 0) - return error; - - if ((error = wpi_nic_lock(sc)) != 0) - return error; - /* Cleanup. */ - wpi_prph_write(sc, WPI_APMG_CLK_DIS, 0x00000400); - wpi_prph_clrbits(sc, WPI_APMG_PS, 0x00000200); - - /* Enable DMA and BSM (Bootstrap State Machine). */ - wpi_prph_write(sc, WPI_APMG_CLK_EN, - WPI_APMG_CLK_CTRL_DMA_CLK_RQT | WPI_APMG_CLK_CTRL_BSM_CLK_RQT); - DELAY(20); - /* Disable L1-Active. */ - wpi_prph_setbits(sc, WPI_APMG_PCI_STT, WPI_APMG_PCI_STT_L1A_DIS); - wpi_nic_unlock(sc); - - return 0; -} - -static void -wpi_apm_stop_master(struct wpi_softc *sc) -{ - int ntries; - - /* Stop busmaster DMA activity. */ - WPI_SETBITS(sc, WPI_RESET, WPI_RESET_STOP_MASTER); - - if ((WPI_READ(sc, WPI_GP_CNTRL) & WPI_GP_CNTRL_PS_MASK) == - WPI_GP_CNTRL_MAC_PS) - return; /* Already asleep. */ - - for (ntries = 0; ntries < 100; ntries++) { - if (WPI_READ(sc, WPI_RESET) & WPI_RESET_MASTER_DISABLED) - return; - DELAY(10); - } - device_printf(sc->sc_dev, "%s: timeout waiting for master\n", - __func__); -} - -static void -wpi_apm_stop(struct wpi_softc *sc) -{ - wpi_apm_stop_master(sc); - - /* Reset the entire device. */ - WPI_SETBITS(sc, WPI_RESET, WPI_RESET_SW); - DELAY(10); - /* Clear "initialization complete" bit. */ - WPI_CLRBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_INIT_DONE); -} - -static void -wpi_nic_config(struct wpi_softc *sc) -{ - uint32_t rev; - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); - - /* voodoo from the Linux "driver".. */ - rev = pci_read_config(sc->sc_dev, PCIR_REVID, 1); - if ((rev & 0xc0) == 0x40) - WPI_SETBITS(sc, WPI_HW_IF_CONFIG, WPI_HW_IF_CONFIG_ALM_MB); - else if (!(rev & 0x80)) - WPI_SETBITS(sc, WPI_HW_IF_CONFIG, WPI_HW_IF_CONFIG_ALM_MM); - - if (sc->cap == 0x80) - WPI_SETBITS(sc, WPI_HW_IF_CONFIG, WPI_HW_IF_CONFIG_SKU_MRC); - - if ((sc->rev & 0xf0) == 0xd0) - WPI_SETBITS(sc, WPI_HW_IF_CONFIG, WPI_HW_IF_CONFIG_REV_D); - else - WPI_CLRBITS(sc, WPI_HW_IF_CONFIG, WPI_HW_IF_CONFIG_REV_D); - - if (sc->type > 1) - WPI_SETBITS(sc, WPI_HW_IF_CONFIG, WPI_HW_IF_CONFIG_TYPE_B); -} - -static int -wpi_hw_init(struct wpi_softc *sc) -{ - uint8_t chnl; - int ntries, error; - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); - - /* Clear pending interrupts. */ - WPI_WRITE(sc, WPI_INT, 0xffffffff); - - if ((error = wpi_apm_init(sc)) != 0) { - device_printf(sc->sc_dev, - "%s: could not power ON adapter, error %d\n", __func__, - error); - return error; - } - - /* Select VMAIN power source. */ - if ((error = wpi_nic_lock(sc)) != 0) - return error; - wpi_prph_clrbits(sc, WPI_APMG_PS, WPI_APMG_PS_PWR_SRC_MASK); - wpi_nic_unlock(sc); - /* Spin until VMAIN gets selected. */ - for (ntries = 0; ntries < 5000; ntries++) { - if (WPI_READ(sc, WPI_GPIO_IN) & WPI_GPIO_IN_VMAIN) - break; - DELAY(10); - } - if (ntries == 5000) { - device_printf(sc->sc_dev, "timeout selecting power source\n"); - return ETIMEDOUT; - } - - /* Perform adapter initialization. */ - wpi_nic_config(sc); - - /* Initialize RX ring. */ - if ((error = wpi_nic_lock(sc)) != 0) - return error; - /* Set physical address of RX ring. */ - WPI_WRITE(sc, WPI_FH_RX_BASE, sc->rxq.desc_dma.paddr); - /* Set physical address of RX read pointer. */ - WPI_WRITE(sc, WPI_FH_RX_RPTR_ADDR, sc->shared_dma.paddr + - offsetof(struct wpi_shared, next)); - WPI_WRITE(sc, WPI_FH_RX_WPTR, 0); - /* Enable RX. */ - WPI_WRITE(sc, WPI_FH_RX_CONFIG, - WPI_FH_RX_CONFIG_DMA_ENA | - WPI_FH_RX_CONFIG_RDRBD_ENA | - WPI_FH_RX_CONFIG_WRSTATUS_ENA | - WPI_FH_RX_CONFIG_MAXFRAG | - WPI_FH_RX_CONFIG_NRBD(WPI_RX_RING_COUNT_LOG) | - WPI_FH_RX_CONFIG_IRQ_DST_HOST | - WPI_FH_RX_CONFIG_IRQ_TIMEOUT(1)); - (void)WPI_READ(sc, WPI_FH_RSSR_TBL); /* barrier */ - wpi_nic_unlock(sc); - WPI_WRITE(sc, WPI_FH_RX_WPTR, (WPI_RX_RING_COUNT - 1) & ~7); - - /* Initialize TX rings. */ - if ((error = wpi_nic_lock(sc)) != 0) - return error; - wpi_prph_write(sc, WPI_ALM_SCHED_MODE, 2); /* bypass mode */ - wpi_prph_write(sc, WPI_ALM_SCHED_ARASTAT, 1); /* enable RA0 */ - /* Enable all 6 TX rings. */ - wpi_prph_write(sc, WPI_ALM_SCHED_TXFACT, 0x3f); - wpi_prph_write(sc, WPI_ALM_SCHED_SBYPASS_MODE1, 0x10000); - wpi_prph_write(sc, WPI_ALM_SCHED_SBYPASS_MODE2, 0x30002); - wpi_prph_write(sc, WPI_ALM_SCHED_TXF4MF, 4); - wpi_prph_write(sc, WPI_ALM_SCHED_TXF5MF, 5); - /* Set physical address of TX rings. */ - WPI_WRITE(sc, WPI_FH_TX_BASE, sc->shared_dma.paddr); - WPI_WRITE(sc, WPI_FH_MSG_CONFIG, 0xffff05a5); - - /* Enable all DMA channels. */ - for (chnl = 0; chnl < WPI_NDMACHNLS; chnl++) { - WPI_WRITE(sc, WPI_FH_CBBC_CTRL(chnl), 0); - WPI_WRITE(sc, WPI_FH_CBBC_BASE(chnl), 0); - WPI_WRITE(sc, WPI_FH_TX_CONFIG(chnl), 0x80200008); - } - wpi_nic_unlock(sc); - (void)WPI_READ(sc, WPI_FH_TX_BASE); /* barrier */ - - /* Clear "radio off" and "commands blocked" bits. */ - WPI_WRITE(sc, WPI_UCODE_GP1_CLR, WPI_UCODE_GP1_RFKILL); - WPI_WRITE(sc, WPI_UCODE_GP1_CLR, WPI_UCODE_GP1_CMD_BLOCKED); - - /* Clear pending interrupts. */ - WPI_WRITE(sc, WPI_INT, 0xffffffff); - /* Enable interrupts. */ - WPI_WRITE(sc, WPI_INT_MASK, WPI_INT_MASK_DEF); - - /* _Really_ make sure "radio off" bit is cleared! */ - WPI_WRITE(sc, WPI_UCODE_GP1_CLR, WPI_UCODE_GP1_RFKILL); - WPI_WRITE(sc, WPI_UCODE_GP1_CLR, WPI_UCODE_GP1_RFKILL); - - if ((error = wpi_load_firmware(sc)) != 0) { - device_printf(sc->sc_dev, - "%s: could not load firmware, error %d\n", __func__, - error); - return error; - } - /* Wait at most one second for firmware alive notification. */ - if ((error = mtx_sleep(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); -} - -static void -wpi_hw_stop(struct wpi_softc *sc) -{ - uint8_t chnl, qid; - int ntries; - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); - - if (WPI_READ(sc, WPI_UCODE_GP1) & WPI_UCODE_GP1_MAC_SLEEP) - wpi_nic_lock(sc); - - WPI_WRITE(sc, WPI_RESET, WPI_RESET_NEVO); - - /* Disable interrupts. */ - WPI_WRITE(sc, WPI_INT_MASK, 0); - WPI_WRITE(sc, WPI_INT, 0xffffffff); - WPI_WRITE(sc, WPI_FH_INT, 0xffffffff); - - /* Make sure we no longer hold the NIC lock. */ - wpi_nic_unlock(sc); - - if (wpi_nic_lock(sc) == 0) { - /* Stop TX scheduler. */ - wpi_prph_write(sc, WPI_ALM_SCHED_MODE, 0); - wpi_prph_write(sc, WPI_ALM_SCHED_TXFACT, 0); - - /* Stop all DMA channels. */ - for (chnl = 0; chnl < WPI_NDMACHNLS; chnl++) { - WPI_WRITE(sc, WPI_FH_TX_CONFIG(chnl), 0); - for (ntries = 0; ntries < 200; ntries++) { - if (WPI_READ(sc, WPI_FH_TX_STATUS) & - WPI_FH_TX_STATUS_IDLE(chnl)) - break; - DELAY(10); - } - } - wpi_nic_unlock(sc); - } - - /* Stop RX ring. */ - wpi_reset_rx_ring(sc); - - /* Reset all TX rings. */ - for (qid = 0; qid < WPI_DRV_NTXQUEUES; qid++) - wpi_reset_tx_ring(sc, &sc->txq[qid]); - - if (wpi_nic_lock(sc) == 0) { - wpi_prph_write(sc, WPI_APMG_CLK_DIS, - WPI_APMG_CLK_CTRL_DMA_CLK_RQT); - wpi_nic_unlock(sc); - } - DELAY(5); - /* Power OFF adapter. */ - wpi_apm_stop(sc); -} - -static void -wpi_radio_on(void *arg0, int pending) -{ - struct wpi_softc *sc = arg0; - struct ieee80211com *ic = &sc->sc_ic; - struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); - - device_printf(sc->sc_dev, "RF switch: radio enabled\n"); - - WPI_LOCK(sc); - callout_stop(&sc->watchdog_rfkill); - WPI_UNLOCK(sc); - - if (vap != NULL) - ieee80211_init(vap); -} - -static void -wpi_radio_off(void *arg0, int pending) -{ - struct wpi_softc *sc = arg0; - struct ieee80211com *ic = &sc->sc_ic; - struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); - - device_printf(sc->sc_dev, "RF switch: radio disabled\n"); - - ieee80211_notify_radio(ic, 0); - wpi_stop(sc); - if (vap != NULL) - ieee80211_stop(vap); - - WPI_LOCK(sc); - callout_reset(&sc->watchdog_rfkill, hz, wpi_watchdog_rfkill, sc); - WPI_UNLOCK(sc); -} - -static int -wpi_init(struct wpi_softc *sc) -{ - int error = 0; - - WPI_LOCK(sc); - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); - - if (sc->sc_running != 0) - goto end; - - /* Check that the radio is not disabled by hardware switch. */ - if (!(WPI_READ(sc, WPI_GP_CNTRL) & WPI_GP_CNTRL_RFKILL)) { - device_printf(sc->sc_dev, - "RF switch: radio disabled (%s)\n", __func__); - callout_reset(&sc->watchdog_rfkill, hz, wpi_watchdog_rfkill, - sc); - error = EINPROGRESS; - goto end; - } - - /* Read firmware images from the filesystem. */ - if ((error = wpi_read_firmware(sc)) != 0) { - device_printf(sc->sc_dev, - "%s: could not read firmware, error %d\n", __func__, - error); - goto end; - } - - sc->sc_running = 1; - - /* Initialize hardware and upload firmware. */ - error = wpi_hw_init(sc); - wpi_unload_firmware(sc); - if (error != 0) { - device_printf(sc->sc_dev, - "%s: could not initialize hardware, error %d\n", __func__, - error); - goto fail; - } - - /* Configure adapter now that it is ready. */ - if ((error = wpi_config(sc)) != 0) { - device_printf(sc->sc_dev, - "%s: could not configure device, error %d\n", __func__, - error); - goto fail; - } - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); - - WPI_UNLOCK(sc); - - return 0; - -fail: wpi_stop_locked(sc); - -end: DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__); - WPI_UNLOCK(sc); - - return error; -} - -static void -wpi_stop_locked(struct wpi_softc *sc) -{ - - WPI_LOCK_ASSERT(sc); - - if (sc->sc_running == 0) - return; - - WPI_TX_LOCK(sc); - WPI_TXQ_LOCK(sc); - sc->sc_running = 0; - WPI_TXQ_UNLOCK(sc); - WPI_TX_UNLOCK(sc); - - WPI_TXQ_STATE_LOCK(sc); - callout_stop(&sc->tx_timeout); - WPI_TXQ_STATE_UNLOCK(sc); - - WPI_RXON_LOCK(sc); - callout_stop(&sc->scan_timeout); - callout_stop(&sc->calib_to); - WPI_RXON_UNLOCK(sc); - - /* Power OFF hardware. */ - wpi_hw_stop(sc); -} - -static void -wpi_stop(struct wpi_softc *sc) -{ - WPI_LOCK(sc); - wpi_stop_locked(sc); - WPI_UNLOCK(sc); -} - -/* - * Callback from net80211 to start a scan. - */ -static void -wpi_scan_start(struct ieee80211com *ic) -{ - struct wpi_softc *sc = ic->ic_softc; - - wpi_set_led(sc, WPI_LED_LINK, 20, 2); -} - -/* - * Callback from net80211 to terminate a scan. - */ -static void -wpi_scan_end(struct ieee80211com *ic) -{ - struct wpi_softc *sc = ic->ic_softc; - struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); - - if (vap->iv_state == IEEE80211_S_RUN) - wpi_set_led(sc, WPI_LED_LINK, 0, 1); -} - -/** - * Called by the net80211 framework to indicate to the driver - * that the channel should be changed - */ -static void -wpi_set_channel(struct ieee80211com *ic) -{ - const struct ieee80211_channel *c = ic->ic_curchan; - struct wpi_softc *sc = ic->ic_softc; - int error; - - DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); - - WPI_LOCK(sc); - sc->sc_rxtap.wr_chan_freq = htole16(c->ic_freq); - sc->sc_rxtap.wr_chan_flags = htole16(c->ic_flags); - WPI_UNLOCK(sc); - WPI_TX_LOCK(sc); - sc->sc_txtap.wt_chan_freq = htole16(c->ic_freq); - sc->sc_txtap.wt_chan_flags = htole16(c->ic_flags); - WPI_TX_UNLOCK(sc); - - /* - * Only need to set the channel in Monitor mode. AP scanning and auth - * are already taken care of by their respective firmware commands. - */ - if (ic->ic_opmode == IEEE80211_M_MONITOR) { - WPI_RXON_LOCK(sc); - sc->rxon.chan = ieee80211_chan2ieee(ic, c); - if (IEEE80211_IS_CHAN_2GHZ(c)) { - sc->rxon.flags |= htole32(WPI_RXON_AUTO | - WPI_RXON_24GHZ); - } else { - sc->rxon.flags &= ~htole32(WPI_RXON_AUTO | - WPI_RXON_24GHZ); - } - if ((error = wpi_send_rxon(sc, 0, 1)) != 0) - device_printf(sc->sc_dev, - "%s: error %d setting channel\n", __func__, - error); - WPI_RXON_UNLOCK(sc); - } -} - -/** - * Called by net80211 to indicate that we need to scan the current - * channel. The channel is previously be set via the wpi_set_channel - * callback. - */ -static void -wpi_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell) -{ - struct ieee80211vap *vap = ss->ss_vap; - struct ieee80211com *ic = vap->iv_ic; - struct wpi_softc *sc = ic->ic_softc; - int error; - - WPI_RXON_LOCK(sc); - error = wpi_scan(sc, ic->ic_curchan); - WPI_RXON_UNLOCK(sc); - if (error != 0) - ieee80211_cancel_scan(vap); -} - -/** - * Called by the net80211 framework to indicate - * the minimum dwell time has been met, terminate the scan. - * We don't actually terminate the scan as the firmware will notify - * us when it's finished and we have no way to interrupt it. - */ -static void -wpi_scan_mindwell(struct ieee80211_scan_state *ss) -{ - /* NB: don't try to abort scan; wait for firmware to finish */ -} diff --git a/src/add-ons/kernel/drivers/network/wlan/iprowifi3945/dev/wpi/if_wpi_debug.h b/src/add-ons/kernel/drivers/network/wlan/iprowifi3945/dev/wpi/if_wpi_debug.h deleted file mode 100644 index 044bc9164b..0000000000 --- a/src/add-ons/kernel/drivers/network/wlan/iprowifi3945/dev/wpi/if_wpi_debug.h +++ /dev/null @@ -1,142 +0,0 @@ -/*- - * Copyright (c) 2006,2007 - * Damien Bergamini - * Benjamin Close - * Copyright (c) 2015 Andriy Voskoboinyk - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef __IF_WPI_DEBUG_H__ -#define __IF_WPI_DEBUG_H__ - -#ifdef WPI_DEBUG -enum { - WPI_DEBUG_XMIT = 0x00000001, /* basic xmit operation */ - WPI_DEBUG_RECV = 0x00000002, /* basic recv operation */ - WPI_DEBUG_STATE = 0x00000004, /* 802.11 state transitions */ - WPI_DEBUG_HW = 0x00000008, /* Stage 1 (eeprom) debugging */ - WPI_DEBUG_RESET = 0x00000010, /* reset processing */ - WPI_DEBUG_FIRMWARE = 0x00000020, /* firmware(9) loading debug */ - WPI_DEBUG_BEACON = 0x00000040, /* beacon handling */ - WPI_DEBUG_WATCHDOG = 0x00000080, /* watchdog timeout */ - WPI_DEBUG_INTR = 0x00000100, /* ISR */ - WPI_DEBUG_SCAN = 0x00000200, /* Scan related operations */ - WPI_DEBUG_NOTIFY = 0x00000400, /* State 2 Notif intr debug */ - WPI_DEBUG_TEMP = 0x00000800, /* TXPower/Temp Calibration */ - WPI_DEBUG_CMD = 0x00001000, /* cmd submission */ - WPI_DEBUG_TRACE = 0x00002000, /* Print begin and start driver function */ - WPI_DEBUG_PWRSAVE = 0x00004000, /* Power save operations */ - WPI_DEBUG_EEPROM = 0x00008000, /* EEPROM info */ - WPI_DEBUG_NODE = 0x00010000, /* node addition/removal */ - WPI_DEBUG_KEY = 0x00020000, /* node key management */ - WPI_DEBUG_EDCA = 0x00040000, /* WME info */ - WPI_DEBUG_REGISTER = 0x00080000, /* print chipset register */ - WPI_DEBUG_BMISS = 0x00100000, /* print number of missed beacons */ - WPI_DEBUG_ANY = 0xffffffff -}; - -#define DPRINTF(sc, m, ...) do { \ - if (sc->sc_debug & (m)) \ - printf(__VA_ARGS__); \ -} while (0) - -#define TRACE_STR_BEGIN "->%s: begin\n" -#define TRACE_STR_DOING "->Doing %s\n" -#define TRACE_STR_END "->%s: end\n" -#define TRACE_STR_END_ERR "->%s: end in error\n" - -#define WPI_DESC(x) case x: return #x - -static const char *wpi_cmd_str(int cmd) -{ - switch (cmd) { - /* Notifications. */ - WPI_DESC(WPI_UC_READY); - WPI_DESC(WPI_RX_DONE); - WPI_DESC(WPI_START_SCAN); - WPI_DESC(WPI_SCAN_RESULTS); - WPI_DESC(WPI_STOP_SCAN); - WPI_DESC(WPI_BEACON_SENT); - WPI_DESC(WPI_RX_STATISTICS); - WPI_DESC(WPI_BEACON_STATISTICS); - WPI_DESC(WPI_STATE_CHANGED); - WPI_DESC(WPI_BEACON_MISSED); - - /* Command notifications. */ - WPI_DESC(WPI_CMD_RXON); - WPI_DESC(WPI_CMD_RXON_ASSOC); - WPI_DESC(WPI_CMD_EDCA_PARAMS); - WPI_DESC(WPI_CMD_TIMING); - WPI_DESC(WPI_CMD_ADD_NODE); - WPI_DESC(WPI_CMD_DEL_NODE); - WPI_DESC(WPI_CMD_TX_DATA); - WPI_DESC(WPI_CMD_MRR_SETUP); - WPI_DESC(WPI_CMD_SET_LED); - WPI_DESC(WPI_CMD_SET_POWER_MODE); - WPI_DESC(WPI_CMD_SCAN); - WPI_DESC(WPI_CMD_SCAN_ABORT); - WPI_DESC(WPI_CMD_SET_BEACON); - WPI_DESC(WPI_CMD_TXPOWER); - WPI_DESC(WPI_CMD_BT_COEX); - - default: - return "UNKNOWN CMD"; - } -} - -/* - * Translate CSR code to string - */ -static const char *wpi_get_csr_string(size_t csr) -{ - switch (csr) { - WPI_DESC(WPI_HW_IF_CONFIG); - WPI_DESC(WPI_INT); - WPI_DESC(WPI_INT_MASK); - WPI_DESC(WPI_FH_INT); - WPI_DESC(WPI_GPIO_IN); - WPI_DESC(WPI_RESET); - WPI_DESC(WPI_GP_CNTRL); - WPI_DESC(WPI_EEPROM); - WPI_DESC(WPI_EEPROM_GP); - WPI_DESC(WPI_GIO); - WPI_DESC(WPI_UCODE_GP1); - WPI_DESC(WPI_UCODE_GP2); - WPI_DESC(WPI_GIO_CHICKEN); - WPI_DESC(WPI_ANA_PLL); - WPI_DESC(WPI_DBG_HPET_MEM); - default: - KASSERT(0, ("Unknown CSR: %d\n", csr)); - return "UNKNOWN CSR"; - } -} - -static const char *wpi_get_prph_string(size_t prph) -{ - switch (prph) { - WPI_DESC(WPI_APMG_CLK_CTRL); - WPI_DESC(WPI_APMG_PS); - WPI_DESC(WPI_APMG_PCI_STT); - WPI_DESC(WPI_APMG_RFKILL); - default: - KASSERT(0, ("Unknown register: %d\n", prph)); - return "UNKNOWN PRPH"; - } -} - -#else -#define DPRINTF(sc, m, ...) do { (void) sc; } while (0) -#endif - -#endif /* __IF_WPI_DEBUG_H__ */ diff --git a/src/add-ons/kernel/drivers/network/wlan/iprowifi3945/dev/wpi/if_wpivar.h b/src/add-ons/kernel/drivers/network/wlan/iprowifi3945/dev/wpi/if_wpivar.h deleted file mode 100644 index 6e3ab1476c..0000000000 --- a/src/add-ons/kernel/drivers/network/wlan/iprowifi3945/dev/wpi/if_wpivar.h +++ /dev/null @@ -1,298 +0,0 @@ - -/*- - * Copyright (c) 2006,2007 - * Damien Bergamini - * - * 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. - */ -struct wpi_rx_radiotap_header { - struct ieee80211_radiotap_header wr_ihdr; - uint64_t wr_tsft; - uint8_t wr_flags; - uint8_t wr_rate; - uint16_t wr_chan_freq; - uint16_t wr_chan_flags; - int8_t wr_dbm_antsignal; - int8_t wr_dbm_antnoise; - uint8_t wr_antenna; -} __packed __aligned(8); - -#define WPI_RX_RADIOTAP_PRESENT \ - ((1 << IEEE80211_RADIOTAP_TSFT) | \ - (1 << IEEE80211_RADIOTAP_FLAGS) | \ - (1 << IEEE80211_RADIOTAP_RATE) | \ - (1 << IEEE80211_RADIOTAP_CHANNEL) | \ - (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | \ - (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) | \ - (1 << IEEE80211_RADIOTAP_ANTENNA)) - -struct wpi_tx_radiotap_header { - struct ieee80211_radiotap_header wt_ihdr; - uint8_t wt_flags; - uint8_t wt_rate; - uint16_t wt_chan_freq; - uint16_t wt_chan_flags; -} __packed; - -#define WPI_TX_RADIOTAP_PRESENT \ - ((1 << IEEE80211_RADIOTAP_FLAGS) | \ - (1 << IEEE80211_RADIOTAP_RATE) | \ - (1 << IEEE80211_RADIOTAP_CHANNEL)) - -struct wpi_dma_info { - bus_dma_tag_t tag; - bus_dmamap_t map; - bus_addr_t paddr; - caddr_t vaddr; - bus_size_t size; -}; - -struct wpi_tx_data { - bus_dmamap_t map; - bus_addr_t cmd_paddr; - struct mbuf *m; - struct ieee80211_node *ni; - int hdrlen; -}; - -struct wpi_tx_ring { - struct wpi_dma_info desc_dma; - struct wpi_dma_info cmd_dma; - struct wpi_tx_desc *desc; - struct wpi_tx_cmd *cmd; - struct wpi_tx_data data[WPI_TX_RING_COUNT]; - bus_dma_tag_t data_dmat; - uint8_t qid; - uint8_t cur; - uint8_t pending; - int16_t queued; - bool update:1; -}; - -struct wpi_rx_data { - struct mbuf *m; - bus_dmamap_t map; -}; - -struct wpi_rx_ring { - struct wpi_dma_info desc_dma; - uint32_t *desc; - struct wpi_rx_data data[WPI_RX_RING_COUNT]; - bus_dma_tag_t data_dmat; - uint16_t cur; - int update; -}; - -struct wpi_node { - struct ieee80211_node ni; /* must be the first */ - uint8_t id; -}; -#define WPI_NODE(ni) ((struct wpi_node *)(ni)) - -struct wpi_power_sample { - uint8_t index; - int8_t power; -}; - -struct wpi_power_group { -#define WPI_SAMPLES_COUNT 5 - struct wpi_power_sample samples[WPI_SAMPLES_COUNT]; - uint8_t chan; - int8_t maxpwr; - int16_t temp; -}; - -struct wpi_buf { - uint8_t data[56]; /* sizeof(struct wpi_cmd_beacon) */ - struct ieee80211_node *ni; - struct mbuf *m; - size_t size; - uint8_t code; - uint16_t ac; -}; - -struct wpi_vap { - struct ieee80211vap wv_vap; - - struct wpi_buf wv_bcbuf; - struct mtx wv_mtx; - - uint8_t wv_gtk; -#define WPI_VAP_KEY(kid) (1 << kid) - - int (*wv_newstate)(struct ieee80211vap *, - enum ieee80211_state, int); - void (*wv_recv_mgmt)(struct ieee80211_node *, - struct mbuf *, int, - const struct ieee80211_rx_stats *, - int, int); -}; -#define WPI_VAP(vap) ((struct wpi_vap *)(vap)) - -#define WPI_VAP_LOCK_INIT(_wvp) \ - mtx_init(&(_wvp)->wv_mtx, "lock for wv_bcbuf/wv_boff structures", \ - NULL, MTX_DEF) -#define WPI_VAP_LOCK(_wvp) mtx_lock(&(_wvp)->wv_mtx) -#define WPI_VAP_UNLOCK(_wvp) mtx_unlock(&(_wvp)->wv_mtx) -#define WPI_VAP_LOCK_ASSERT(_wvp) mtx_assert(&(_wvp)->wv_mtx, MA_OWNED) -#define WPI_VAP_LOCK_DESTROY(_wvp) mtx_destroy(&(_wvp)->wv_mtx) - -struct wpi_fw_part { - const uint8_t *text; - uint32_t textsz; - const uint8_t *data; - uint32_t datasz; -}; - -struct wpi_fw_info { - const uint8_t *data; - size_t size; - struct wpi_fw_part init; - struct wpi_fw_part main; - struct wpi_fw_part boot; -}; - -struct wpi_softc { - device_t sc_dev; - int sc_debug; - - int sc_running; - - struct mtx sc_mtx; - struct ieee80211com sc_ic; - struct ieee80211_ratectl_tx_status sc_txs; - - struct mtx tx_mtx; - - /* Shared area. */ - struct wpi_dma_info shared_dma; - struct wpi_shared *shared; - - struct wpi_tx_ring txq[WPI_DRV_NTXQUEUES]; - struct mtx txq_mtx; - struct mtx txq_state_mtx; - - struct wpi_rx_ring rxq; - uint64_t rx_tstamp; - - /* TX Thermal Callibration. */ - struct callout calib_to; - - struct callout scan_timeout; - struct callout tx_timeout; - - /* Watch dog timer. */ - struct callout watchdog_rfkill; - - /* Firmware image. */ - struct wpi_fw_info fw; - uint32_t errptr; - - struct resource *irq; - struct resource *mem; - bus_space_tag_t sc_st; - bus_space_handle_t sc_sh; - void *sc_ih; - bus_size_t sc_sz; - int sc_cap_off; /* PCIe Capabilities. */ - - struct wpi_rxon rxon; - struct mtx rxon_mtx; - - int temp; - - uint32_t nodesmsk; - struct mtx nt_mtx; - - void (*sc_node_free)(struct ieee80211_node *); - void (*sc_update_rx_ring)(struct wpi_softc *); - void (*sc_update_tx_ring)(struct wpi_softc *, - struct wpi_tx_ring *); - - struct wpi_rx_radiotap_header sc_rxtap; - struct wpi_tx_radiotap_header sc_txtap; - - /* Firmware image. */ - const struct firmware *fw_fp; - - /* Firmware DMA transfer. */ - struct wpi_dma_info fw_dma; - - /* Tasks used by the driver. */ - struct task sc_radiooff_task; - struct task sc_radioon_task; - - /* 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]; /* Regulatory domain. */ - -#if defined(__HAIKU__) - uint32_t sc_intr_status_1; - uint32_t sc_intr_status_2; -#endif -}; - -/* - * Locking order: - * 1. WPI_LOCK; - * 2. WPI_RXON_LOCK; - * 3. WPI_TX_LOCK; - * 4. WPI_NT_LOCK / WPI_VAP_LOCK; - * 5. WPI_TXQ_LOCK; - * 6. WPI_TXQ_STATE_LOCK; - */ - -#define WPI_LOCK_INIT(_sc) \ - mtx_init(&(_sc)->sc_mtx, device_get_nameunit((_sc)->sc_dev), \ - MTX_NETWORK_LOCK, MTX_DEF) -#define WPI_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) -#define WPI_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) -#define WPI_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED) -#define WPI_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx) - -#define WPI_RXON_LOCK_INIT(_sc) \ - mtx_init(&(_sc)->rxon_mtx, "lock for wpi_rxon structure", NULL, MTX_DEF) -#define WPI_RXON_LOCK(_sc) mtx_lock(&(_sc)->rxon_mtx) -#define WPI_RXON_UNLOCK(_sc) mtx_unlock(&(_sc)->rxon_mtx) -#define WPI_RXON_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->rxon_mtx, MA_OWNED) -#define WPI_RXON_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->rxon_mtx) - -#define WPI_TX_LOCK_INIT(_sc) \ - mtx_init(&(_sc)->tx_mtx, "tx path lock", NULL, MTX_DEF) -#define WPI_TX_LOCK(_sc) mtx_lock(&(_sc)->tx_mtx) -#define WPI_TX_UNLOCK(_sc) mtx_unlock(&(_sc)->tx_mtx) -#define WPI_TX_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->tx_mtx) - -#define WPI_NT_LOCK_INIT(_sc) \ - mtx_init(&(_sc)->nt_mtx, "node table lock", NULL, MTX_DEF) -#define WPI_NT_LOCK(_sc) mtx_lock(&(_sc)->nt_mtx) -#define WPI_NT_UNLOCK(_sc) mtx_unlock(&(_sc)->nt_mtx) -#define WPI_NT_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->nt_mtx) - -#define WPI_TXQ_LOCK_INIT(_sc) \ - mtx_init(&(_sc)->txq_mtx, "txq/cmdq lock", NULL, MTX_DEF) -#define WPI_TXQ_LOCK(_sc) mtx_lock(&(_sc)->txq_mtx) -#define WPI_TXQ_UNLOCK(_sc) mtx_unlock(&(_sc)->txq_mtx) -#define WPI_TXQ_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->txq_mtx) - -#define WPI_TXQ_STATE_LOCK_INIT(_sc) \ - mtx_init(&(_sc)->txq_state_mtx, "txq state lock", NULL, MTX_DEF) -#define WPI_TXQ_STATE_LOCK(_sc) mtx_lock(&(_sc)->txq_state_mtx) -#define WPI_TXQ_STATE_UNLOCK(_sc) mtx_unlock(&(_sc)->txq_state_mtx) -#define WPI_TXQ_STATE_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->txq_state_mtx) diff --git a/src/add-ons/kernel/drivers/network/wlan/iprowifi3945/glue.c b/src/add-ons/kernel/drivers/network/wlan/iprowifi3945/glue.c index 8941f0aa4c..22feb0eb94 100644 --- a/src/add-ons/kernel/drivers/network/wlan/iprowifi3945/glue.c +++ b/src/add-ons/kernel/drivers/network/wlan/iprowifi3945/glue.c @@ -1,57 +1,24 @@ /* - * Copyright 2009, Colin Günther, coling@gmx.de. - * Copyright 2018, Haiku, Inc. - * All rights reserved. Distributed under the terms of the MIT license. + * Copyright 2025, Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. */ - -#include -#include - -#include - -#include -#include - -#include -#include - -#include -#include +#include HAIKU_FBSD_WLAN_DRIVER_GLUE(iprowifi3945, wpi, pci) NO_HAIKU_FBSD_MII_DRIVER(); NO_HAIKU_REENABLE_INTERRUPTS(); -HAIKU_DRIVER_REQUIREMENTS(FBSD_WLAN); -HAIKU_FIRMWARE_VERSION(2144); +HAIKU_DRIVER_REQUIREMENTS(OBSD_WLAN); +HAIKU_FIRMWARE_VERSION(1); HAIKU_FIRMWARE_NAME_MAP({ - {"wpifw", "iwlwifi-3945-15.ucode"} + {"wpi-3945abg", "iwlwifi-3945-15.ucode"} }); int HAIKU_CHECK_DISABLE_INTERRUPTS(device_t dev) { - struct wpi_softc* sc = (struct wpi_softc*)device_get_softc(dev); - uint32 r1, r2; - - r1 = WPI_READ(sc, WPI_INT); - - if (__predict_false(r1 == 0xffffffff || - (r1 & 0xfffffff0) == 0xa5a5a5a0)) - return 0; /* Hardware gone! */ - - r2 = WPI_READ(sc, WPI_FH_INT); - - if (r1 == 0 && r2 == 0) - return 0; /* Interrupt not for us. */ - - /* Disable interrupts. */ - WPI_WRITE(sc, WPI_INT_MASK, 0); - - atomic_set((int32*)&sc->sc_intr_status_1, r1); - atomic_set((int32*)&sc->sc_intr_status_2, r2); - + // We only support MSI, so we handle all interrupts. return 1; }