From c51f93a9394c6317e7705dbd069d4d6ad8e8fa55 Mon Sep 17 00:00:00 2001 From: Damien Bergamini Date: Sun, 14 May 2006 19:00:52 +0000 Subject: Initial import of wpi(4), a blob-free driver for Intel PRO/Wireless 3945ABG 802.11 adapters as found in Centrino Duo laptops. More to come but works well enough for me to commit this over a wpi(4). go for it deraadt@ --- sys/dev/pci/files.pci | 7 +- sys/dev/pci/if_wpi.c | 2404 +++++++++++++++++++++++++++++++++++++++++++++++ sys/dev/pci/if_wpireg.h | 509 ++++++++++ sys/dev/pci/if_wpivar.h | 131 +++ 4 files changed, 3050 insertions(+), 1 deletion(-) create mode 100644 sys/dev/pci/if_wpi.c create mode 100644 sys/dev/pci/if_wpireg.h create mode 100644 sys/dev/pci/if_wpivar.h (limited to 'sys/dev') diff --git a/sys/dev/pci/files.pci b/sys/dev/pci/files.pci index 14949acd316..e8ced38bbfc 100644 --- a/sys/dev/pci/files.pci +++ b/sys/dev/pci/files.pci @@ -1,4 +1,4 @@ -# $OpenBSD: files.pci,v 1.204 2006/05/01 00:34:12 brad Exp $ +# $OpenBSD: files.pci,v 1.205 2006/05/14 19:00:48 damien Exp $ # $NetBSD: files.pci,v 1.20 1996/09/24 17:47:15 christos Exp $ # # Config file and device description for machine-independent PCI code. @@ -459,6 +459,11 @@ device iwi: ifnet, wlan, firmload attach iwi at pci file dev/pci/if_iwi.c iwi +# Intel PRO/Wireless 3945ABG +device wpi: ifnet, wlan, firmload +attach wpi at pci +file dev/pci/if_wpi.c wpi + # C-Media CMI8x38 Audio Chip device cmpci: audio, auconv, mulaw attach cmpci at pci diff --git a/sys/dev/pci/if_wpi.c b/sys/dev/pci/if_wpi.c new file mode 100644 index 00000000000..ba77f77a1be --- /dev/null +++ b/sys/dev/pci/if_wpi.c @@ -0,0 +1,2404 @@ +/* $OpenBSD: if_wpi.c,v 1.1 2006/05/14 19:00:48 damien Exp $ */ + +/*- + * Copyright (c) 2006 + * 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. + */ + +/*- + * Intel(R) PRO/Wireless 3945ABG driver + * http://www.intel.com/network/connectivity/products/wireless/prowireless_mobile.htm + */ + +#include "bpfilter.h" + +#include +#include +#include +#include +#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 +#include + +#include +#include + +const struct pci_matchid wpi_devices[] = { + { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_PRO_WL_3945ABG_1 }, + { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_PRO_WL_3945ABG_2 } +}; + +/* + * Supported rates for 802.11a/b/g modes (in 500Kbps unit). + */ +#ifdef notyet +static const struct ieee80211_rateset wpi_rateset_11a = + { 8, { 12, 18, 24, 36, 48, 72, 96, 108 } }; +#endif +static const struct ieee80211_rateset wpi_rateset_11b = + { 4, { 2, 4, 11, 22 } }; + +static const struct ieee80211_rateset wpi_rateset_11g = + { 12, { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 } }; + +static const uint8_t wpi_ridx_to_rate[] = { + 0xd, 0xf, 0x5, 0x7, 0x9, 0xb, 0x1, 0x3, /* OFDM */ + 10, 20, 55, 110 /* CCK */ +}; + +int wpi_match(struct device *, void *, void *); +void wpi_attach(struct device *, struct device *, void *); +int wpi_detach(struct device *, int); +void wpi_power(int, void *); +int wpi_alloc_shared(struct wpi_softc *); +void wpi_free_shared(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, 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_media_change(struct ifnet *); +int wpi_newstate(struct ieee80211com *, enum ieee80211_state, int); +void wpi_mem_lock(struct wpi_softc *); +void wpi_mem_unlock(struct wpi_softc *); +uint32_t wpi_mem_read(struct wpi_softc *, uint16_t); +void wpi_mem_write(struct wpi_softc *, uint16_t, uint32_t); +void wpi_mem_write_region_4(struct wpi_softc *, uint16_t, + const uint32_t *, int); +uint16_t wpi_read_prom_word(struct wpi_softc *, uint32_t); +int wpi_load_microcode(struct wpi_softc *, const char *, int); +int wpi_load_firmware(struct wpi_softc *, uint32_t, const char *, + int); +void wpi_rx_intr(struct wpi_softc *, struct wpi_rx_desc *, + struct wpi_rx_data *); +void wpi_tx_intr(struct wpi_softc *, struct wpi_rx_desc *, + struct wpi_rx_data *); +void wpi_cmd_intr(struct wpi_softc *, struct wpi_rx_desc *); +void wpi_notif_intr(struct wpi_softc *); +int wpi_intr(void *); +int wpi_ioctl(struct ifnet *, u_long, caddr_t); +int wpi_tx_data(struct wpi_softc *, struct mbuf *, + struct ieee80211_node *, int); +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_set_led(struct wpi_softc *, uint8_t, uint8_t, uint8_t); +void wpi_enable_tsf(struct wpi_softc *, struct ieee80211_node *); +int wpi_auth(struct wpi_softc *); +int wpi_scan(struct wpi_softc *); +int wpi_config(struct wpi_softc *); +void wpi_stop_master(struct wpi_softc *); +int wpi_power_up(struct wpi_softc *); +int wpi_reset(struct wpi_softc *); +void wpi_hw_config(struct wpi_softc *); +int wpi_init(struct ifnet *); +void wpi_stop(struct ifnet *, int); + +#define WPI_DEBUG + +#ifdef WPI_DEBUG +#define DPRINTF(x) do { if (wpi_debug > 0) printf x; } while (0) +#define DPRINTFN(n, x) do { if (wpi_debug >= (n)) printf x; } while (0) +int wpi_debug = 1; +#else +#define DPRINTF(x) +#define DPRINTFN(n, x) +#endif + +struct cfattach wpi_ca = { + sizeof (struct wpi_softc), wpi_match, wpi_attach, wpi_detach +}; + +int +wpi_match(struct device *parent, void *match, void *aux) +{ + return pci_matchbyid((struct pci_attach_args *)aux, wpi_devices, + sizeof (wpi_devices) / sizeof (wpi_devices[0])); +} + +/* Base Address Register */ +#define WPI_PCI_BAR0 0x10 + +void +wpi_attach(struct device *parent, struct device *self, void *aux) +{ + struct wpi_softc *sc = (struct wpi_softc *)self; + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; + struct pci_attach_args *pa = aux; + const char *intrstr; + bus_space_tag_t memt; + bus_space_handle_t memh; + pci_intr_handle_t ih; + pcireg_t data; + uint16_t val; + int i, ac, error; + + sc->sc_pct = pa->pa_pc; + sc->sc_pcitag = pa->pa_tag; + + /* clear device specific PCI configuration register 0x41 */ + data = pci_conf_read(sc->sc_pct, sc->sc_pcitag, 0x40); + data &= ~0x0000ff00; + pci_conf_write(sc->sc_pct, sc->sc_pcitag, 0x40, data); + + /* map the register window */ + error = pci_mapreg_map(pa, WPI_PCI_BAR0, PCI_MAPREG_TYPE_MEM | + PCI_MAPREG_MEM_TYPE_32BIT, 0, &memt, &memh, NULL, &sc->sc_sz, 0); + if (error != 0) { + printf(": could not map memory space\n"); + return; + } + + sc->sc_st = memt; + sc->sc_sh = memh; + sc->sc_dmat = pa->pa_dmat; + + if (pci_intr_map(pa, &ih) != 0) { + printf(": could not map interrupt\n"); + return; + } + + intrstr = pci_intr_string(sc->sc_pct, ih); + sc->sc_ih = pci_intr_establish(sc->sc_pct, ih, IPL_NET, wpi_intr, sc, + sc->sc_dev.dv_xname); + if (sc->sc_ih == NULL) { + printf(": could not establish interrupt"); + if (intrstr != NULL) + printf(" at %s", intrstr); + printf("\n"); + return; + } + printf(": %s", intrstr); + + + /* + * Put adapter into a known state. + */ + if ((error = wpi_reset(sc)) != 0) { + printf(": could not reset adapter\n"); + return; + } + + /* + * Allocate shared page and Tx/Rx rings. + */ + if ((error = wpi_alloc_shared(sc)) != 0) { + printf(": could not allocate shared area\n"); + return; + } + + for (ac = 0; ac < 4; ac++) { + error = wpi_alloc_tx_ring(sc, &sc->txq[ac], WPI_TX_RING_COUNT, + ac); + if (error != 0) { + printf(": could not allocate Tx ring %d\n", ac); + goto fail1; + } + } + + error = wpi_alloc_tx_ring(sc, &sc->cmdq, WPI_CMD_RING_COUNT, 4); + if (error != 0) { + printf(": could not allocate command ring\n"); + goto fail1; + } + + error = wpi_alloc_tx_ring(sc, &sc->svcq, WPI_SVC_RING_COUNT, 5); + if (error != 0) { + printf(": could not allocate service ring\n"); + goto fail2; + } + + error = wpi_alloc_rx_ring(sc, &sc->rxq); + if (error != 0) { + printf(": could not allocate Rx ring\n"); + goto fail3; + } + + 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_SCANALL | /* h/w scan */ + IEEE80211_C_WEP | /* s/w WEP */ + IEEE80211_C_MONITOR | /* monitor mode supported */ + IEEE80211_C_TXPMGT | /* tx power management */ + IEEE80211_C_SHPREAMBLE; /* short preamble supported */ + + /* read MAC address from EEPROM */ + val = wpi_read_prom_word(sc, WPI_EEPROM_MAC + 0); + ic->ic_myaddr[0] = val & 0xff; + ic->ic_myaddr[1] = val >> 8; + val = wpi_read_prom_word(sc, WPI_EEPROM_MAC + 1); + ic->ic_myaddr[2] = val & 0xff; + ic->ic_myaddr[3] = val >> 8; + val = wpi_read_prom_word(sc, WPI_EEPROM_MAC + 2); + ic->ic_myaddr[4] = val & 0xff; + ic->ic_myaddr[5] = val >> 8; + + printf(", address %s\n", ether_sprintf(ic->ic_myaddr)); + +#ifdef notyet + /* set supported .11a rates */ + ic->ic_sup_rates[IEEE80211_MODE_11A] = wpi_rateset_11a; + + /* set supported .11a channels */ + for (i = 36; i <= 64; i += 4) { + ic->ic_channels[i].ic_freq = + ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ); + ic->ic_channels[i].ic_flags = IEEE80211_CHAN_A; + } + for (i = 149; i <= 165; i += 4) { + ic->ic_channels[i].ic_freq = + ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ); + ic->ic_channels[i].ic_flags = IEEE80211_CHAN_A; + } +#endif + + /* set supported .11b and .11g rates */ + ic->ic_sup_rates[IEEE80211_MODE_11B] = wpi_rateset_11b; + ic->ic_sup_rates[IEEE80211_MODE_11G] = wpi_rateset_11g; + + /* set supported .11b and .11g channels (1 through 14) */ + for (i = 1; i <= 14; i++) { + ic->ic_channels[i].ic_freq = + ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ); + ic->ic_channels[i].ic_flags = + IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM | + IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ; + } + + /* 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_init = wpi_init; + ifp->if_ioctl = wpi_ioctl; + ifp->if_start = wpi_start; + ifp->if_watchdog = wpi_watchdog; + IFQ_SET_READY(&ifp->if_snd); + bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ); + + if_attach(ifp); + ieee80211_ifattach(ifp); + /* override 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->powerhook = powerhook_establish(wpi_power, sc); + +#if NBPFILTER > 0 + bpfattach(&sc->sc_drvbpf, ifp, DLT_IEEE802_11_RADIO, + sizeof (struct ieee80211_frame) + 64); + + 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 + + return; + +fail3: wpi_free_tx_ring(sc, &sc->svcq); +fail2: wpi_free_tx_ring(sc, &sc->cmdq); +fail1: while (--ac >= 0) + wpi_free_tx_ring(sc, &sc->txq[ac]); + wpi_free_shared(sc); +} + +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 ac; + + wpi_stop(ifp, 1); + + ieee80211_ifdetach(ifp); + if_detach(ifp); + + for (ac = 0; ac < 4; ac++) + wpi_free_tx_ring(sc, &sc->txq[ac]); + wpi_free_tx_ring(sc, &sc->cmdq); + wpi_free_tx_ring(sc, &sc->svcq); + wpi_free_rx_ring(sc, &sc->rxq); + wpi_free_shared(sc); + + if (sc->sc_ih != NULL) { + pci_intr_disestablish(sc->sc_pct, sc->sc_ih); + sc->sc_ih = NULL; + } + + bus_space_unmap(sc->sc_st, sc->sc_sh, sc->sc_sz); + + return 0; +} + +void +wpi_power(int why, void *arg) +{ + struct wpi_softc *sc = arg; + struct ifnet *ifp; + pcireg_t data; + + if (why != PWR_RESUME) + return; + + /* clear device specific PCI configuration register 0x41 */ + data = pci_conf_read(sc->sc_pct, sc->sc_pcitag, 0x40); + data &= ~0x0000ff00; + pci_conf_write(sc->sc_pct, sc->sc_pcitag, 0x40, data); + + ifp = &sc->sc_ic.ic_if; + if (ifp->if_flags & IFF_UP) { + ifp->if_init(ifp); + if (ifp->if_flags & IFF_RUNNING) + ifp->if_start(ifp); + } +} + +/* + * Allocate a shared page between host and NIC. + */ +int +wpi_alloc_shared(struct wpi_softc *sc) +{ + int nsegs, error; + + error = bus_dmamap_create(sc->sc_dmat, sizeof (struct wpi_shared), 1, + sizeof (struct wpi_shared), 0, BUS_DMA_NOWAIT, &sc->shmap); + if (error != 0) { + printf("%s: could not create shared area DMA map\n", + sc->sc_dev.dv_xname); + goto fail; + } + + /* must be aligned on a 4K-page boundary */ + error = bus_dmamem_alloc(sc->sc_dmat, sizeof (struct wpi_shared), + PAGE_SIZE, 0, &sc->shseg, 1, &nsegs, BUS_DMA_NOWAIT); + if (error != 0) { + printf("%s: could not allocate shared area DMA memory\n", + sc->sc_dev.dv_xname); + goto fail; + } + + error = bus_dmamem_map(sc->sc_dmat, &sc->shseg, nsegs, + sizeof (struct wpi_shared), (caddr_t *)&sc->shared, + BUS_DMA_NOWAIT); + if (error != 0) { + printf("%s: could not map shared area DMA memory\n", + sc->sc_dev.dv_xname); + goto fail; + } + + error = bus_dmamap_load(sc->sc_dmat, sc->shmap, sc->shared, + sizeof (struct wpi_shared), NULL, BUS_DMA_NOWAIT); + if (error != 0) { + printf("%s: could not load shared area DMA map\n", + sc->sc_dev.dv_xname); + goto fail; + } + + bzero(sc->shared, sizeof (struct wpi_shared)); + + return 0; + +fail: wpi_free_shared(sc); + return error; +} + +void +wpi_free_shared(struct wpi_softc *sc) +{ + if (sc->shared != NULL) { + bus_dmamap_unload(sc->sc_dmat, sc->shmap); + bus_dmamem_unmap(sc->sc_dmat, (caddr_t)sc->shared, + sizeof (struct wpi_shared)); + bus_dmamem_free(sc->sc_dmat, &sc->shseg, 1); + } + if (sc->shmap != NULL) + bus_dmamap_destroy(sc->sc_dmat, sc->shmap); +} + +int +wpi_alloc_rx_ring(struct wpi_softc *sc, struct wpi_rx_ring *ring) +{ + struct wpi_rx_data *data; + int i, nsegs, error; + + ring->cur = 0; + + error = bus_dmamap_create(sc->sc_dmat, + WPI_RX_RING_COUNT * sizeof (struct wpi_rx_desc), 1, + WPI_RX_RING_COUNT * sizeof (struct wpi_rx_desc), 0, BUS_DMA_NOWAIT, + &ring->map); + if (error != 0) { + printf("%s: could not create rx ring DMA map\n", + sc->sc_dev.dv_xname); + goto fail; + } + + error = bus_dmamem_alloc(sc->sc_dmat, + WPI_RX_RING_COUNT * sizeof (struct wpi_rx_desc), + WPI_RING_DMA_ALIGN, 0, &ring->seg, 1, &nsegs, BUS_DMA_NOWAIT); + if (error != 0) { + printf("%s: could not allocate rx ring DMA memory\n", + sc->sc_dev.dv_xname); + goto fail; + } + + error = bus_dmamem_map(sc->sc_dmat, &ring->seg, nsegs, + WPI_RX_RING_COUNT * sizeof (struct wpi_rx_desc), + (caddr_t *)&ring->desc, BUS_DMA_NOWAIT); + if (error != 0) { + printf("%s: could not map rx ring DMA memory\n", + sc->sc_dev.dv_xname); + goto fail; + } + + error = bus_dmamap_load(sc->sc_dmat, ring->map, ring->desc, + WPI_RX_RING_COUNT * sizeof (struct wpi_rx_desc), NULL, + BUS_DMA_NOWAIT); + if (error != 0) { + printf("%s: could not load rx ring DMA map\n", + sc->sc_dev.dv_xname); + goto fail; + } + + bzero(ring->desc, WPI_RX_RING_COUNT * sizeof (struct wpi_rx_desc)); + + /* + * Allocate Rx buffers. + */ + for (i = 0; i < WPI_RX_RING_COUNT; i++) { + data = &ring->data[i]; + + error = bus_dmamap_create(sc->sc_dmat, MCLBYTES, 1, MCLBYTES, + 0, BUS_DMA_NOWAIT, &data->map); + if (error != 0) { + printf("%s: could not create rx buf DMA map\n", + sc->sc_dev.dv_xname); + goto fail; + } + + MGETHDR(data->m, M_DONTWAIT, MT_DATA); + if (data->m == NULL) { + printf("%s: could not allocate rx mbuf\n", + sc->sc_dev.dv_xname); + error = ENOMEM; + goto fail; + } + + MCLGET(data->m, M_DONTWAIT); + if (!(data->m->m_flags & M_EXT)) { + m_freem(data->m); + data->m = NULL; + printf("%s: could not allocate rx mbuf cluster\n", + sc->sc_dev.dv_xname); + error = ENOMEM; + goto fail; + } + + error = bus_dmamap_load(sc->sc_dmat, data->map, + mtod(data->m, void *), MCLBYTES, NULL, BUS_DMA_NOWAIT | + BUS_DMA_READ); + if (error != 0) { + printf("%s: could not load rx buf DMA map\n", + sc->sc_dev.dv_xname); + goto fail; + } + + ring->desc[i] = htole32(data->map->dm_segs[0].ds_addr); + } + + 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; + + wpi_mem_lock(sc); + + WPI_WRITE(sc, WPI_RX_CONFIG, 0); + for (ntries = 0; ntries < 100; ntries++) { + if (WPI_READ(sc, WPI_RX_STATUS) & WPI_RX_IDLE) + break; + DELAY(10); + } +#ifdef WPI_DEBUG + if (ntries == 100 && wpi_debug > 0) + printf("%s: timeout resetting Rx ring\n", sc->sc_dev.dv_xname); +#endif + wpi_mem_unlock(sc); + + ring->cur = 0; +} + +void +wpi_free_rx_ring(struct wpi_softc *sc, struct wpi_rx_ring *ring) +{ + struct wpi_rx_data *data; + int i; + + if (ring->desc != NULL) { + bus_dmamap_unload(sc->sc_dmat, ring->map); + bus_dmamem_unmap(sc->sc_dmat, (caddr_t)ring->desc, + WPI_RX_RING_COUNT * sizeof (struct wpi_rx_desc)); + bus_dmamem_free(sc->sc_dmat, &ring->seg, 1); + } + if (ring->map != NULL) + bus_dmamap_destroy(sc->sc_dmat, ring->map); + + for (i = 0; i < WPI_RX_RING_COUNT; i++) { + data = &ring->data[i]; + + if (data->m != NULL) { + bus_dmamap_unload(sc->sc_dmat, data->map); + m_freem(data->m); + } + bus_dmamap_destroy(sc->sc_dmat, data->map); + } +} + +int +wpi_alloc_tx_ring(struct wpi_softc *sc, struct wpi_tx_ring *ring, int count, + int qid) +{ + struct wpi_tx_data *data; + int i, nsegs, error; + + ring->qid = qid; + ring->count = count; + ring->queued = 0; + ring->cur = 0; + + error = bus_dmamap_create(sc->sc_dmat, + count * sizeof (struct wpi_tx_desc), 1, + count * sizeof (struct wpi_tx_desc), 0, BUS_DMA_NOWAIT, + &ring->map); + if (error != 0) { + printf("%s: could not create tx ring DMA map\n", + sc->sc_dev.dv_xname); + goto fail; + } + + error = bus_dmamem_alloc(sc->sc_dmat, + count * sizeof (struct wpi_tx_desc), WPI_RING_DMA_ALIGN, 0, + &ring->seg, 1, &nsegs, BUS_DMA_NOWAIT); + if (error != 0) { + printf("%s: could not allocate tx ring DMA memory\n", + sc->sc_dev.dv_xname); + goto fail; + } + + error = bus_dmamem_map(sc->sc_dmat, &ring->seg, nsegs, + count * sizeof (struct wpi_tx_desc), (caddr_t *)&ring->desc, + BUS_DMA_NOWAIT); + if (error != 0) { + printf("%s: could not map tx ring DMA memory\n", + sc->sc_dev.dv_xname); + goto fail; + } + + error = bus_dmamap_load(sc->sc_dmat, ring->map, ring->desc, + count * sizeof (struct wpi_tx_desc), NULL, BUS_DMA_NOWAIT); + if (error != 0) { + printf("%s: could not load tx ring DMA map\n", + sc->sc_dev.dv_xname); + goto fail; + } + + bzero(ring->desc, count * sizeof (struct wpi_tx_desc)); + + /* update shared page with ring's base address */ + sc->shared->txbase[qid] = htole32(ring->map->dm_segs[0].ds_addr); + + error = bus_dmamap_create(sc->sc_dmat, + count * sizeof (struct wpi_tx_cmd), 1, + count * sizeof (struct wpi_tx_cmd), 0, BUS_DMA_NOWAIT, + &ring->cmd_map); + if (error != 0) { + printf("%s: could not create tx cmd DMA map\n", + sc->sc_dev.dv_xname); + goto fail; + } + + error = bus_dmamem_alloc(sc->sc_dmat, + count * sizeof (struct wpi_tx_cmd), PAGE_SIZE, 0, &ring->cmd_seg, + 1, &nsegs, BUS_DMA_NOWAIT); + if (error != 0) { + printf("%s: could not allocate tx cmd DMA memory\n", + sc->sc_dev.dv_xname); + goto fail; + } + + error = bus_dmamem_map(sc->sc_dmat, &ring->cmd_seg, nsegs, + count * sizeof (struct wpi_tx_cmd), (caddr_t *)&ring->cmd, + BUS_DMA_NOWAIT); + if (error != 0) { + printf("%s: could not map tx cmd DMA memory\n", + sc->sc_dev.dv_xname); + goto fail; + } + + error = bus_dmamap_load(sc->sc_dmat, ring->cmd_map, ring->cmd, + count * sizeof (struct wpi_tx_cmd), NULL, BUS_DMA_NOWAIT); + if (error != 0) { + printf("%s: could not load tx ring DMA map\n", + sc->sc_dev.dv_xname); + goto fail; + } + + bzero(ring->cmd, count * sizeof (struct wpi_tx_cmd)); + + ring->data = malloc(count * sizeof (struct wpi_tx_data), M_DEVBUF, + M_NOWAIT); + if (ring->data == NULL) { + printf("%s: could not allocate tx data slots\n", + sc->sc_dev.dv_xname); + goto fail; + } + + bzero(ring->data, count * sizeof (struct wpi_tx_data)); + + for (i = 0; i < count; i++) { + data = &ring->data[i]; + + 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", + sc->sc_dev.dv_xname); + 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) +{ + struct wpi_tx_data *data; + int i, ntries; + + wpi_mem_lock(sc); + + WPI_WRITE(sc, WPI_TX_CONFIG(ring->qid), 0); + for (ntries = 0; ntries < 100; ntries++) { + if (WPI_READ(sc, WPI_TX_STATUS) & WPI_TX_IDLE(ring->qid)) + break; + DELAY(10); + } +#ifdef WPI_DEBUG + if (ntries == 100 && wpi_debug > 0) { + printf("%s: timeout resetting Tx ring %d\n", + sc->sc_dev.dv_xname, ring->qid); + } +#endif + wpi_mem_unlock(sc); + + for (i = 0; i < ring->count; i++) { + data = &ring->data[i]; + + if (data->m != NULL) { + bus_dmamap_unload(sc->sc_dmat, data->map); + m_freem(data->m); + data->m = NULL; + } + } + + ring->queued = 0; + ring->cur = 0; +} + +void +wpi_free_tx_ring(struct wpi_softc *sc, struct wpi_tx_ring *ring) +{ + struct wpi_tx_data *data; + int i; + + if (ring->desc != NULL) { + bus_dmamap_unload(sc->sc_dmat, ring->map); + bus_dmamem_unmap(sc->sc_dmat, (caddr_t)ring->desc, + WPI_RX_RING_COUNT * sizeof (struct wpi_rx_desc)); + bus_dmamem_free(sc->sc_dmat, &ring->seg, 1); + } + if (ring->map != NULL) + bus_dmamap_destroy(sc->sc_dmat, ring->map); + + if (ring->cmd != NULL) { + bus_dmamap_unload(sc->sc_dmat, ring->cmd_map); + bus_dmamem_unmap(sc->sc_dmat, (caddr_t)ring->cmd, + ring->count * sizeof (struct wpi_tx_cmd)); + bus_dmamem_free(sc->sc_dmat, &ring->cmd_seg, 1); + } + if (ring->cmd_map != NULL) + bus_dmamap_destroy(sc->sc_dmat, ring->cmd_map); + + if (ring->data != NULL) { + for (i = 0; i < ring->count; i++) { + data = &ring->data[i]; + + if (data->m != NULL) { + bus_dmamap_unload(sc->sc_dmat, data->map); + m_freem(data->m); + } + } + free(ring->data, M_DEVBUF); + } +} + +int +wpi_media_change(struct ifnet *ifp) +{ + int error; + + error = ieee80211_media_change(ifp); + if (error != ENETRESET) + return error; + + if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == (IFF_UP | IFF_RUNNING)) + wpi_init(ifp); + + return 0; +} + +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; + + 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)) != 0) { + printf("%s: could not initiate scan\n", + sc->sc_dev.dv_xname); + return error; + } + ic->ic_state = nstate; + return 0; + + case IEEE80211_S_AUTH: + if ((error = wpi_auth(sc)) != 0) { + printf("%s: could not send authentication request\n", + sc->sc_dev.dv_xname); + return error; + } + break; + + case IEEE80211_S_RUN: + wpi_enable_tsf(sc, ic->ic_bss); + + /* update adapter's configuration */ + sc->config.state = htole16(WPI_CONFIG_ASSOCIATED); + sc->config.filter |= htole32(WPI_FILTER_BSSID); + + error = wpi_cmd(sc, WPI_CMD_CONFIGURE, &sc->config, + sizeof (struct wpi_config), 1); + if (error != 0) { + printf("%s: could not update configuration\n", + sc->sc_dev.dv_xname); + return error; + } + + /* link LED always on while associated */ + wpi_set_led(sc, WPI_LED_LINK, 0, 1); + break; + + case IEEE80211_S_ASSOC: + case IEEE80211_S_INIT: + break; + } + + return sc->sc_newstate(ic, nstate, arg); +} + +/* + * Grab exclusive access to NIC memory. + */ +void +wpi_mem_lock(struct wpi_softc *sc) +{ + uint32_t tmp; + int ntries; + + tmp = WPI_READ(sc, WPI_GPIO_CTL); + WPI_WRITE(sc, WPI_GPIO_CTL, tmp | WPI_GPIO_MAC); + + /* spin until we actually get the lock */ + for (ntries = 0; ntries < 1000; ntries++) { + if ((WPI_READ(sc, WPI_GPIO_CTL) & + (WPI_GPIO_CLOCK | WPI_GPIO_SLEEP)) == WPI_GPIO_CLOCK) + break; + DELAY(10); + } + if (ntries == 1000) + printf("%s: could not lock memory\n", sc->sc_dev.dv_xname); +} + +/* + * Release lock on NIC memory. + */ +void +wpi_mem_unlock(struct wpi_softc *sc) +{ + uint32_t tmp = WPI_READ(sc, WPI_GPIO_CTL); + WPI_WRITE(sc, WPI_GPIO_CTL, tmp & ~WPI_GPIO_MAC); +} + +uint32_t +wpi_mem_read(struct wpi_softc *sc, uint16_t addr) +{ + WPI_WRITE(sc, WPI_READ_MEM_ADDR, WPI_MEM_4 | addr); + return WPI_READ(sc, WPI_READ_MEM_DATA); +} + +void +wpi_mem_write(struct wpi_softc *sc, uint16_t addr, uint32_t data) +{ + WPI_WRITE(sc, WPI_WRITE_MEM_ADDR, WPI_MEM_4 | addr); + WPI_WRITE(sc, WPI_WRITE_MEM_DATA, data); +} + +void +wpi_mem_write_region_4(struct wpi_softc *sc, uint16_t addr, + const uint32_t *data, int wlen) +{ + for (; wlen > 0; wlen--, data++, addr += 4) + wpi_mem_write(sc, addr, *data); +} + +uint16_t +wpi_read_prom_word(struct wpi_softc *sc, uint32_t addr) +{ + int ntries; + uint32_t val; + + WPI_WRITE(sc, WPI_EEPROM_CTL, addr << 2); + + wpi_mem_lock(sc); + for (ntries = 0; ntries < 10; ntries++) { + if ((val = WPI_READ(sc, WPI_EEPROM_CTL)) & WPI_EEPROM_READY) + break; + DELAY(10); + } + wpi_mem_unlock(sc); + + if (ntries == 10) { + printf("%s: could not read EEPROM\n", sc->sc_dev.dv_xname); + return 0xdead; + } + return val >> 16; +} + +/* + * The firmware boot code is small and is intended to be copied directly into + * the NIC internal memory. + */ +int +wpi_load_microcode(struct wpi_softc *sc, const char *ucode, int size) +{ + /* check that microcode size is a multiple of 4 */ + if (size & 3) + return EINVAL; + + size /= sizeof (uint32_t); + + wpi_mem_lock(sc); + + /* copy microcode image into NIC memory */ + wpi_mem_write_region_4(sc, WPI_MEM_UCODE_BASE, (const uint32_t *)ucode, + size); + + wpi_mem_write(sc, WPI_MEM_UCODE_SRC, 0); + wpi_mem_write(sc, WPI_MEM_UCODE_DST, WPI_FW_TEXT); + wpi_mem_write(sc, WPI_MEM_UCODE_SIZE, size); + + /* run microcode */ + wpi_mem_write(sc, WPI_MEM_UCODE_CTL, WPI_UC_RUN); + + wpi_mem_unlock(sc); + + return 0; +} + +/* + * The firmware text and data segments are transferred to the NIC using DMA. + * The driver just copies the firmware into DMA'able memory and tells the NIC + * where to find it. Once the NIC has copied the firmware into its internal + * memory, we can free our local copy in the driver. + */ +int +wpi_load_firmware(struct wpi_softc *sc, uint32_t target, const char *fw, + int size) +{ + bus_dmamap_t map; + bus_dma_segment_t seg; + caddr_t virtaddr; + struct wpi_tx_desc desc; + int i, ntries, nsegs, error; + + /* + * Allocate DMA'able memory to store the firmware. + */ + error = bus_dmamap_create(sc->sc_dmat, size, WPI_MAX_SCATTER, + WPI_MAX_SEG_LEN, 0, BUS_DMA_NOWAIT, &map); + if (error != 0) { + printf("%s: could not create firmware DMA map\n", + sc->sc_dev.dv_xname); + goto fail1; + } + + error = bus_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE, 0, &seg, 1, + &nsegs, BUS_DMA_NOWAIT); + if (error != 0) { + printf("%s: could not allocate firmware DMA memory\n", + sc->sc_dev.dv_xname); + goto fail2; + } + + error = bus_dmamem_map(sc->sc_dmat, &seg, nsegs, size, &virtaddr, + BUS_DMA_NOWAIT); + if (error != 0) { + printf("%s: could not map firmware DMA memory\n", + sc->sc_dev.dv_xname); + goto fail3; + } + + error = bus_dmamap_load(sc->sc_dmat, map, virtaddr, size, NULL, + BUS_DMA_NOWAIT | BUS_DMA_WRITE); + if (error != 0) { + printf("%s: could not load firmware DMA map\n", + sc->sc_dev.dv_xname); + goto fail4; + } + + /* copy firmware image to DMA'able memory */ + bcopy(fw, virtaddr, size); + + /* make sure the adapter will get up-to-date values */ + bus_dmamap_sync(sc->sc_dmat, map, 0, size, BUS_DMASYNC_PREWRITE); + + bzero(&desc, sizeof desc); + desc.flags = htole32(WPI_PAD32(size) << 28 | map->dm_nsegs << 24); + for (i = 0; i < map->dm_nsegs; i++) { + desc.segs[i].physaddr = htole32(map->dm_segs[i].ds_addr); + desc.segs[i].len = htole32(map->dm_segs[i].ds_len); + } + + wpi_mem_lock(sc); + + /* tell adapter where to copy image in its internal memory */ + WPI_WRITE(sc, WPI_FW_TARGET, target); + + WPI_WRITE(sc, WPI_TX_CONFIG(6), 0); + + /* copy firmware descriptor into NIC memory */ + WPI_WRITE_REGION_4(sc, WPI_TX_DESC(6), (uint32_t *)&desc, + sizeof desc / sizeof (uint32_t)); + + WPI_WRITE(sc, WPI_TX_CREDIT(6), 0xfffff); + WPI_WRITE(sc, WPI_TX_STATE(6), 0x4001); + WPI_WRITE(sc, WPI_TX_CONFIG(6), 0x80000001); + + /* wait while the adapter is busy copying the firmware */ + for (ntries = 0; ntries < 100; ntries++) { + if (WPI_READ(sc, WPI_TX_STATUS) & WPI_TX_IDLE(6)) + break; + DELAY(1000); + } + if (ntries == 100) { + printf("%s: timeout transferring firmware\n", + sc->sc_dev.dv_xname); + error = EIO; + } + + WPI_WRITE(sc, WPI_TX_CREDIT(6), 0); + + wpi_mem_unlock(sc); + + bus_dmamap_sync(sc->sc_dmat, map, 0, size, BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_dmat, map); +fail4: bus_dmamem_unmap(sc->sc_dmat, virtaddr, size); +fail3: bus_dmamem_free(sc->sc_dmat, &seg, 1); +fail2: bus_dmamap_destroy(sc->sc_dmat, map); +fail1: return error; +} + +void +wpi_rx_intr(struct wpi_softc *sc, struct wpi_rx_desc *desc, + struct wpi_rx_data *data) +{ + 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_node *ni; + struct mbuf *m, *mnew; + int error; + + stat = (struct wpi_rx_stat *)(desc + 1); + + if (stat->len > WPI_STAT_MAXLEN) { + printf("%s: invalid rx statistic header\n", + sc->sc_dev.dv_xname); + 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)); + + DPRINTFN(4, ("rx intr: idx=%d len=%d stat len=%d rssi=%d rate=%x " + "chan=%d tstamp=%lld\n", ring->cur, letoh32(desc->len), + letoh16(head->len), (int8_t)stat->rssi, head->rate, head->chan, + letoh64(tail->tstamp))); + + MGETHDR(mnew, M_DONTWAIT, MT_DATA); + if (mnew == NULL) { + ifp->if_ierrors++; + return; + } + + MCLGET(mnew, M_DONTWAIT); + if (!(mnew->m_flags & M_EXT)) { + m_freem(mnew); + ifp->if_ierrors++; + return; + } + + bus_dmamap_unload(sc->sc_dmat, data->map); + + error = bus_dmamap_load(sc->sc_dmat, data->map, mtod(mnew, void *), + MCLBYTES, NULL, BUS_DMA_NOWAIT); + if (error != 0) { + m_freem(mnew); + + /* try to reload the old mbuf */ + error = bus_dmamap_load(sc->sc_dmat, data->map, + mtod(data->m, void *), MCLBYTES, NULL, BUS_DMA_NOWAIT); + if (error != 0) { + /* very unlikely that it will fail... */ + panic("%s: could not load old rx mbuf", + sc->sc_dev.dv_xname); + } + ifp->if_ierrors++; + return; + } + + m = data->m; + data->m = mnew; + + /* update Rx descriptor */ + ring->desc[ring->cur] = htole32(data->map->dm_segs[0].ds_addr); + + /* finalize mbuf */ + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = m->m_len = + sizeof (struct wpi_rx_desc) + + sizeof (struct wpi_rx_stat) + stat->len + + sizeof (struct wpi_rx_head) + letoh16(head->len) + + sizeof (struct wpi_rx_tail); + + /* remove head + tail */ + m_adj(m, sizeof (struct wpi_rx_desc) + sizeof (struct wpi_rx_stat) + + stat->len + sizeof (struct wpi_rx_head)); + m_adj(m, -sizeof (struct wpi_rx_tail)); + +#if NBPFILTER > 0 + if (sc->sc_drvbpf != NULL) { + struct mbuf mb; + struct wpi_rx_radiotap_header *tap = &sc->sc_rxtap; + + tap->wr_flags = 0; + tap->wr_chan_freq = + htole16(ic->ic_channels[head->chan].ic_freq); + tap->wr_chan_flags = + htole16(ic->ic_channels[head->chan].ic_flags); + tap->wr_antsignal = stat->signal; + + M_DUP_PKTHDR(&mb, m); + mb.m_data = (caddr_t)tap; + mb.m_len = sc->sc_rxtap_len; + mb.m_next = m; + mb.m_pkthdr.len += mb.m_len; + bpf_mtap(sc->sc_drvbpf, &mb, BPF_DIRECTION_IN); + } +#endif + + /* grab a reference to the source node */ + wh = mtod(m, struct ieee80211_frame *); + ni = ieee80211_find_rxnode(ic, wh); + + /* send the frame to the 802.11 layer */ + ieee80211_input(ifp, m, ni, stat->rssi, 0); + + /* node is no longer needed */ + ieee80211_release_node(ic, ni); +} + +void +wpi_tx_intr(struct wpi_softc *sc, struct wpi_rx_desc *desc, + struct wpi_rx_data *data) +{ + 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 *txdata = &ring->data[desc->idx]; +#ifdef WPI_DEBUG + struct wpi_tx_stat *stat = (struct wpi_tx_stat *)(desc + 1); +#endif + DPRINTFN(4, ("tx done: qid=%d idx=%d retries=%d nkill=%d rate=%x " + "duration=%d status=%x\n", desc->qid, desc->idx, stat->ntries, + stat->nkill, stat->rate, letoh32(stat->duration), + letoh32(stat->status))); + + bus_dmamap_unload(sc->sc_dmat, data->map); + + m_freem(txdata->m); + txdata->m = NULL; + ieee80211_release_node(ic, txdata->ni); + txdata->ni = NULL; + + ifp->if_opackets++; + ring->queued--; + + sc->sc_tx_timer = 0; + ifp->if_flags &= ~IFF_OACTIVE; + (*ifp->if_start)(ifp); +} + +void +wpi_cmd_intr(struct wpi_softc *sc, struct wpi_rx_desc *desc) +{ + if ((desc->qid & 7) != 4) + return; /* not a command ack */ + + wakeup(&sc->cmdq.cmd[desc->idx]); +} + +void +wpi_notif_intr(struct wpi_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; + struct wpi_rx_desc *desc; + struct wpi_rx_data *data; + uint32_t hw; + + hw = letoh32(sc->shared->next); + while (sc->rxq.cur != hw) { + data = &sc->rxq.data[sc->rxq.cur]; + + 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_intr(sc, desc); + + switch (desc->type) { + case WPI_RX_DONE: + /* a 802.11 frame was received */ + wpi_rx_intr(sc, desc, data); + break; + + case WPI_TX_DONE: + /* a 802.11 frame has been transmitted */ + wpi_tx_intr(sc, desc, data); + break; + + case WPI_UC_READY: + { + struct wpi_ucode_info *uc = + (struct wpi_ucode_info *)(desc + 1); + + /* the microcontroller is ready */ + DPRINTF(("microcode alive notification version %x " + "alive %x\n", letoh32(uc->version), + letoh32(uc->valid))); + + if (letoh32(uc->valid) != 1) { + printf("%s microcontroller initialization " + "failed\n", sc->sc_dev.dv_xname); + } + break; + } + case WPI_STATE_CHANGED: + { + uint32_t *status = (uint32_t *)(desc + 1); + + /* enabled/disabled notification */ + DPRINTF(("state changed to %x\n", letoh32(*status))); + + if (letoh32(*status) & 1) { + printf("%s: Radio transmitter is off\n", + sc->sc_dev.dv_xname); + } + break; + } + case WPI_START_SCAN: + { + struct wpi_start_scan *scan = + (struct wpi_start_scan *)(desc + 1); + + DPRINTFN(2, ("scanning channel %d status %x\n", + scan->chan, letoh32(scan->status))); + break; + } + case WPI_STOP_SCAN: + DPRINTF(("scan finished\n")); + ieee80211_end_scan(ifp); + break; + } + + sc->rxq.cur = (sc->rxq.cur + 1) % WPI_RX_RING_COUNT; + } + + /* tell the firmware what we have processed */ + hw = (hw == 0) ? WPI_RX_RING_COUNT - 1 : hw - 1; + WPI_WRITE(sc, WPI_RX_WIDX, hw & ~0x7); +} + +int +wpi_intr(void *arg) +{ + struct wpi_softc *sc = arg; + struct ifnet *ifp = &sc->sc_ic.ic_if; + uint32_t r; + + r = WPI_READ(sc, WPI_INTR); + if (r == 0 || r == 0xffffffff) + return 0; /* not for us */ + + DPRINTFN(6, ("interrupt reg %x\n", r)); + + /* disable interrupts */ + WPI_WRITE(sc, WPI_MASK, 0); + /* ack interrupts */ + WPI_WRITE(sc, WPI_INTR, r); + + if (r & (WPI_SW_ERROR | WPI_HW_ERROR)) { + printf("%s: fatal firmware error\n", sc->sc_dev.dv_xname); + ifp->if_flags &= ~IFF_UP; + wpi_stop(ifp, 1); + r = 0; /* don't process any other interrupt */ + } + + if (r & WPI_RX_INTR) + wpi_notif_intr(sc); + + if (r & WPI_ALIVE_INTR) /* firmware initialized */ + wakeup(sc); + + /* re-enable interrupts */ + WPI_WRITE(sc, WPI_MASK, WPI_INTR_MASK); + + return 1; +} + +int +wpi_tx_data(struct wpi_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, + int ac) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; + struct wpi_tx_ring *ring = &sc->txq[ac]; + struct wpi_tx_desc *desc; + struct wpi_tx_data *data; + struct wpi_tx_cmd *cmd; + struct wpi_cmd_data *tx; + struct ieee80211_frame *wh; + struct mbuf *mnew; + int i, error; + + desc = &ring->desc[ring->cur]; + data = &ring->data[ring->cur]; + cmd = &ring->cmd[ring->cur]; + + wh = mtod(m0, struct ieee80211_frame *); + + if ((ic->ic_flags & IEEE80211_F_WEPON) && + (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == + IEEE80211_FC0_TYPE_DATA) { + m0 = ieee80211_wep_crypt(ifp, m0, 1); + if (m0 == NULL) + return ENOBUFS; + + /* packet header may have moved, reset our local pointer */ + wh = mtod(m0, struct ieee80211_frame *); + } + +#if NBPFILTER > 0 + if (sc->sc_drvbpf != NULL) { + struct mbuf mb; + struct wpi_tx_radiotap_header *tap = &sc->sc_txtap; + + tap->wt_flags = 0; + tap->wt_chan_freq = htole16(ic->ic_ibss_chan->ic_freq); + tap->wt_chan_flags = htole16(ic->ic_ibss_chan->ic_flags); + + M_DUP_PKTHDR(&mb, m0); + mb.m_data = (caddr_t)tap; + mb.m_len = sc->sc_txtap_len; + mb.m_next = m0; + mb.m_pkthdr.len += mb.m_len; + bpf_mtap(sc->sc_drvbpf, &mb, BPF_DIRECTION_OUT); + } +#endif + + cmd->code = WPI_CMD_TX_DATA; + cmd->flags = 0; + cmd->qid = ring->qid; + cmd->idx = ring->cur; + + tx = (struct wpi_cmd_data *)cmd->data; + tx->flags = 0; + + if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) + tx->flags |= htole32(WPI_TX_NEED_ACK); + + tx->flags |= htole32(WPI_TX_AUTO_SEQ); + + /* tell h/w to add timestamp in probe responses */ + if ((wh->i_fc[0] & + (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) == + (IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_RESP)) + tx->flags |= htole32(WPI_TX_INSERT_TSTAMP); + +#ifdef notyet + tx->flags |= htole32(WPI_TX_NEED_RTS); +#endif + + /* retrieve destination node's id */ + tx->id = IEEE80211_IS_MULTICAST(wh->i_addr1) ? WPI_ID_BROADCAST : + WPI_ID_BSSID; + + if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_MGT) { + if (((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == + IEEE80211_FC0_SUBTYPE_ASSOC_REQ) || + ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == + IEEE80211_FC0_SUBTYPE_REASSOC_REQ)) + tx->duration = 3; + else + tx->duration = 2; + } else + tx->duration = 0; + + tx->rate = 10; /* 1Mb/s */ + + /* be very persistant at sending frames out */ + tx->rts_ntries = 7; + tx->data_ntries = 15; + + tx->ofdm_mask = sc->config.ofdm_mask; + tx->cck_mask = sc->config.cck_mask; + tx->lifetime = htole32(0xffffffff); + + tx->len = htole16(m0->m_pkthdr.len); + + /* save and trim IEEE802.11 header */ + m_copydata(m0, 0, sizeof (struct ieee80211_frame), (caddr_t)&tx->wh); + m_adj(m0, sizeof (struct ieee80211_frame)); + + error = bus_dmamap_load_mbuf(sc->sc_dmat, data->map, m0, + BUS_DMA_NOWAIT); + if (error != 0 && error != EFBIG) { + printf("%s: could not map mbuf (error %d)\n", + sc->sc_dev.dv_xname, error); + m_freem(m0); + return error; + } + if (error != 0) { + /* too many fragments, linearize */ + + MGETHDR(mnew, M_DONTWAIT, MT_DATA); + if (mnew == NULL) { + m_freem(m0); + return ENOMEM; + } + + M_DUP_PKTHDR(mnew, m0); + if (m0->m_pkthdr.len > MHLEN) { + MCLGET(mnew, M_DONTWAIT); + if (!(mnew->m_flags & M_EXT)) { + m_freem(m0); + m_freem(mnew); + return ENOMEM; + } + } + + m_copydata(m0, 0, m0->m_pkthdr.len, mtod(mnew, caddr_t)); + m_freem(m0); + mnew->m_len = mnew->m_pkthdr.len; + m0 = mnew; + + error = bus_dmamap_load_mbuf(sc->sc_dmat, data->map, m0, + BUS_DMA_NOWAIT); + if (error != 0) { + printf("%s: could not map mbuf (error %d)\n", + sc->sc_dev.dv_xname, error); + m_freem(m0); + return error; + } + } + + data->m = m0; + data->ni = ni; + + DPRINTFN(4, ("sending data: qid=%d idx=%d len=%d nsegs=%d\n", + ring->qid, ring->cur, m0->m_pkthdr.len, data->map->dm_nsegs)); + + /* first scatter/gather segment is used by the tx data command */ + memset(desc, 0, sizeof (struct wpi_tx_desc)); + desc->segs[0].physaddr = htole32(ring->cmd_map->dm_segs[0].ds_addr + + ring->cur * sizeof (struct wpi_tx_cmd)); + desc->segs[0].len = htole32( + (4 + sizeof (struct wpi_cmd_data) + 3) & ~3); + for (i = 1; i <= data->map->dm_nsegs; i++) { + desc->segs[i].physaddr = + htole32(data->map->dm_segs[i - 1].ds_addr); + desc->segs[i].len = + htole32(data->map->dm_segs[i - 1].ds_len); + } + desc->flags = htole32(WPI_PAD32(m0->m_pkthdr.len) << 28 | + (1 + data->map->dm_nsegs) << 24); + + ring->queued++; + + /* kick ring */ + ring->cur = (ring->cur + 1) % WPI_TX_RING_COUNT; + WPI_WRITE(sc, WPI_TX_WIDX, ring->qid << 8 | ring->cur); + + return 0; +} + +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 *m0; + + /* + * net80211 may still try to send management frames even if the + * IFF_RUNNING flag is not set... + */ + if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING) + return; + + for (;;) { + IF_POLL(&ic->ic_mgtq, m0); + if (m0 != NULL) { + /* management frames go into ring 0 */ + if (sc->txq[0].queued >= sc->txq[0].count - 8) { + ifp->if_flags |= IFF_OACTIVE; + break; + } + IF_DEQUEUE(&ic->ic_mgtq, m0); + + ni = (struct ieee80211_node *)m0->m_pkthdr.rcvif; + m0->m_pkthdr.rcvif = NULL; +#if NBPFILTER > 0 + if (ic->ic_rawbpf != NULL) + bpf_mtap(ic->ic_rawbpf, m0, BPF_DIRECTION_OUT); +#endif + if (wpi_tx_data(sc, m0, ni, 0) != 0) + break; + + } else { + if (ic->ic_state != IEEE80211_S_RUN) + break; + IFQ_DEQUEUE(&ifp->if_snd, m0); + if (m0 == NULL) + break; + if (sc->txq[0].queued >= sc->txq[0].count - 8) { + /* there is no place left in this ring */ + IF_PREPEND(&ifp->if_snd, m0); + ifp->if_flags |= IFF_OACTIVE; + break; + } +#if NBPFILTER > 0 + if (ifp->if_bpf != NULL) + bpf_mtap(ifp->if_bpf, m0, BPF_DIRECTION_OUT); +#endif + m0 = ieee80211_encap(ifp, m0, &ni); + if (m0 == NULL) + continue; +#if NBPFILTER > 0 + if (ic->ic_rawbpf != NULL) + bpf_mtap(ic->ic_rawbpf, m0, BPF_DIRECTION_OUT); +#endif + if (wpi_tx_data(sc, m0, ni, 0) != 0) { + if (ni != NULL) + ieee80211_release_node(ic, ni); + ifp->if_oerrors++; + break; + } + } + + 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", sc->sc_dev.dv_xname); + 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; + struct ifaddr *ifa; + struct ifreq *ifr; + int s, error = 0; + + s = splnet(); + + switch (cmd) { + case SIOCSIFADDR: + ifa = (struct ifaddr *)data; + ifp->if_flags |= IFF_UP; +#ifdef INET + if (ifa->ifa_addr->sa_family == AF_INET) + arp_ifinit(&ic->ic_ac, ifa); +#endif + /* FALLTHROUGH */ + case SIOCSIFFLAGS: + if (ifp->if_flags & IFF_UP) { + if (!(ifp->if_flags & IFF_RUNNING)) + wpi_init(ifp); + } else { + if (ifp->if_flags & IFF_RUNNING) + wpi_stop(ifp, 1); + } + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: + ifr = (struct ifreq *)data; + error = (cmd == SIOCADDMULTI) ? + ether_addmulti(ifr, &ic->ic_ac) : + ether_delmulti(ifr, &ic->ic_ac); + + if (error == ENETRESET) + error = 0; + break; + + case SIOCG80211AUTH: + ((struct ieee80211_auth *)data)->i_authtype = sc->authmode; + break; + + case SIOCS80211AUTH: + /* only super-user can do that! */ + if ((error = suser(curproc, 0)) != 0) + break; + + sc->authmode = ((struct ieee80211_auth *)data)->i_authtype; + break; + + default: + error = ieee80211_ioctl(ifp, cmd, data); + } + + if (error == ENETRESET) { + if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == + (IFF_UP | IFF_RUNNING)) + wpi_init(ifp); + error = 0; + } + + splx(s); + 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->cmdq; + struct wpi_tx_desc *desc; + struct wpi_tx_cmd *cmd; + + desc = &ring->desc[ring->cur]; + cmd = &ring->cmd[ring->cur]; + + cmd->code = code; + cmd->flags = 0; + cmd->qid = ring->qid; + cmd->idx = ring->cur; + bcopy(buf, cmd->data, size); + + bzero(desc, sizeof (struct wpi_tx_desc)); + desc->flags = htole32(WPI_PAD32(size) << 28 | 1 << 24); + desc->segs[0].physaddr = htole32(ring->cmd_map->dm_segs[0].ds_addr + + ring->cur * sizeof (struct wpi_tx_cmd)); + desc->segs[0].len = htole32(4 + size); + + /* kick cmd ring */ + ring->cur = (ring->cur + 1) % WPI_CMD_RING_COUNT; + WPI_WRITE(sc, WPI_TX_WIDX, ring->qid << 8 | ring->cur); + + return async ? 0 : tsleep(cmd, PCATCH, "iwicmd", hz); +} + +/* + * Configure h/w multi-rate retries. + */ +int +wpi_mrr_setup(struct wpi_softc *sc) +{ + struct wpi_mrr_setup mrr; + int i, error; + + /* CCK rates (not used with 802.11a) */ + for (i = WPI_CCK1; i <= WPI_CCK11; i++) { + mrr.rates[i].flags = 0; + mrr.rates[i].plcp = wpi_ridx_to_rate[i]; + /* fallback to the immediate lower CCK rate (if any) */ + mrr.rates[i].next = (i == WPI_CCK1) ? WPI_CCK1 : i - 1; + /* try one time at this rate before falling back to "next" */ + mrr.rates[i].ntries = 1; + } + + /* OFDM rates (not used with 802.11b) */ + for (i = WPI_OFDM6; i <= WPI_OFDM54; i++) { + mrr.rates[i].flags = 0; + mrr.rates[i].plcp = wpi_ridx_to_rate[i]; + /* fallback to the immediate lower OFDM rate (if any) */ + mrr.rates[i].next = (i == WPI_OFDM6) ? WPI_OFDM6 : 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, 1); + if (error != 0) { + printf("%s: could not setup MRR for control frames\n", + sc->sc_dev.dv_xname); + return error; + } + + /* setup MRR for data frames */ + mrr.which = htole32(WPI_MRR_DATA); + error = wpi_cmd(sc, WPI_CMD_MRR_SETUP, &mrr, sizeof mrr, 1); + if (error != 0) { + printf("%s: could not setup MRR for data frames\n", + sc->sc_dev.dv_xname); + return error; + } + + return 0; +} + +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); +} + +void +wpi_enable_tsf(struct wpi_softc *sc, struct ieee80211_node *ni) +{ + struct wpi_cmd_tsf tsf; + + memset(&tsf, 0, sizeof tsf); + tsf.tstamp = 0; + tsf.bintval = htole16(ni->ni_intval); + tsf.binitval = htole32(102400); /* XXX */ + tsf.lintval = htole16(10); + + if (wpi_cmd(sc, WPI_CMD_TSF, &tsf, sizeof tsf, 1) != 0) + printf("%s: could not enable TSF\n", sc->sc_dev.dv_xname); +} + +int +wpi_auth(struct wpi_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_node *ni = ic->ic_bss; + struct wpi_node node; + int error; + + /* update adapter's configuration */ + IEEE80211_ADDR_COPY(sc->config.bssid, ni->ni_bssid); + sc->config.cck_mask = 0x0f; /* XXX */ + sc->config.ofdm_mask = 0x15; /* XXX */ + sc->config.chan = ieee80211_chan2ieee(ic, ni->ni_chan); + sc->config.flags |= htole32(WPI_CONFIG_SHORT_SLOT); + + error = wpi_cmd(sc, WPI_CMD_CONFIGURE, &sc->config, + sizeof (struct wpi_config), 1); + if (error != 0) { + printf("%s: could not configure\n", sc->sc_dev.dv_xname); + return error; + } + + /* add default node */ + memset(&node, 0, sizeof node); + IEEE80211_ADDR_COPY(node.bssid, ni->ni_bssid); + node.id = WPI_ID_BSSID; + node.rate = 10; /* 1Mb/s */ + error = wpi_cmd(sc, WPI_CMD_ADD_NODE, &node, sizeof node, 1); + if (error != 0) { + printf("%s: could not add BSS node\n", sc->sc_dev.dv_xname); + return error; + } + + error = wpi_mrr_setup(sc); + if (error != 0) { + printf("%s: could not setup MRR\n", sc->sc_dev.dv_xname); + return error; + } + + return 0; +} + +/* + * Send a scan request to the firmware. Since this command is huge, we map it + * into a mbuf instead of using the pre-allocated set of commands. + */ +int +wpi_scan(struct wpi_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct wpi_tx_ring *ring = &sc->cmdq; + struct wpi_tx_desc *desc; + struct wpi_tx_data *data; + struct wpi_tx_cmd *cmd; + struct wpi_scan_hdr *hdr; + struct wpi_scan_chan *chan; + struct ieee80211_frame *wh; + struct ieee80211_node *ni = ic->ic_bss; + struct ieee80211_rateset *rs; + enum ieee80211_phymode mode; + uint8_t *frm; + int i, pktlen, error; + + desc = &ring->desc[ring->cur]; + data = &ring->data[ring->cur]; + + MGETHDR(data->m, M_DONTWAIT, MT_DATA); + if (data->m == NULL) { + printf("%s: could not allocate mbuf for scan command\n", + sc->sc_dev.dv_xname); + return ENOMEM; + } + + MCLGET(data->m, M_DONTWAIT); + if (!(data->m->m_flags & M_EXT)) { + m_freem(data->m); + data->m = NULL; + printf("%s: could not allocate mbuf for scan command\n", + sc->sc_dev.dv_xname); + return ENOMEM; + } + + cmd = mtod(data->m, struct wpi_tx_cmd *); + cmd->code = WPI_CMD_SCAN; + cmd->flags = 0; + cmd->qid = ring->qid; + cmd->idx = ring->cur; + + hdr = (struct wpi_scan_hdr *)cmd->data; + memset(hdr, 0, sizeof (struct wpi_scan_hdr)); + hdr->first = 1; + hdr->nchan = 13; + hdr->len = hdr->nchan * sizeof (struct wpi_scan_chan); + hdr->quiet = htole16(5); + hdr->threshold = htole16(1); + hdr->filter = htole32(5); /* XXX */ + hdr->rate = 10; /* 1Mb/s */ + hdr->id = WPI_ID_BROADCAST; + hdr->mask = htole32(0xffffffff); + hdr->esslen = ni->ni_esslen; + bcopy(ni->ni_essid, hdr->essid, ni->ni_esslen); + + /* + * Build a probe request frame. Most of the following code is a + * copy & paste of what is done in net80211. Unfortunately, the + * functions to add IEs are static and thus can't be reused here. + */ + wh = (struct ieee80211_frame *)(hdr + 1); + 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); + *(u_int16_t *)&wh->i_dur[0] = 0; /* filled by h/w */ + *(u_int16_t *)&wh->i_seq[0] = 0; /* filled by h/w */ + + frm = (uint8_t *)(wh + 1); + + /* add essid IE */ + /* frm = ieee80211_add_ssid(frm, ni->ni_essid, ni->ni_esslen);*/ + *frm++ = IEEE80211_ELEMID_SSID; + *frm++ = ni->ni_esslen; + bcopy(ni->ni_essid, frm, ni->ni_esslen); + frm += ni->ni_esslen; + + mode = ieee80211_chan2mode(ic, ic->ic_ibss_chan); + rs = &ic->ic_sup_rates[mode]; + + /* add supported rates IE */ + frm = ieee80211_add_rates(frm, rs); + + /* add supported xrates IE */ + frm = ieee80211_add_xrates(frm, rs); + + /* setup length of probe request */ + hdr->length = htole16(frm - (uint8_t *)wh); + + /* XXX: align on a 4-byte boundary? */ + chan = (struct wpi_scan_chan *)frm; + for (i = 1; i <= hdr->nchan; i++, chan++) { + chan->flags = 3; + chan->chan = i; + chan->magic = htole16(0x62ab); + chan->active = htole16(20); + chan->passive = htole16(120); + + frm += sizeof (struct wpi_scan_chan); + } + + pktlen = frm - mtod(data->m, uint8_t *); + + error = bus_dmamap_load(sc->sc_dmat, data->map, cmd, pktlen, + NULL, BUS_DMA_NOWAIT); + if (error != 0) { + printf("%s: could not map scan command\n", + sc->sc_dev.dv_xname); + m_freem(data->m); + data->m = NULL; + return error; + } + + desc->flags = htole32(WPI_PAD32(pktlen) << 28 | 1 << 24); + desc->segs[0].physaddr = htole32(data->map->dm_segs[0].ds_addr); + desc->segs[0].len = htole32(data->map->dm_segs[0].ds_len); + + /* kick cmd ring */ + ring->cur = (ring->cur + 1) % WPI_CMD_RING_COUNT; + WPI_WRITE(sc, WPI_TX_WIDX, ring->qid << 8 | ring->cur); + + return 0; /* will be notified async. of failure/success */ +} + +int +wpi_config(struct wpi_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct wpi_txpower txpower; + struct wpi_power power; + struct wpi_bluetooth bluetooth; + struct wpi_node node; + int error; + + /* Intel's binary only daemon is a joke.. */ + + /* set Tx power for 2.4GHz channels (values read from EEPROM) */ + bzero(&txpower, sizeof txpower); + bcopy(sc->calib1, txpower.calib1, 14 * sizeof (uint16_t)); + bcopy(sc->calib2, txpower.calib2, 14 * sizeof (uint16_t)); + error = wpi_cmd(sc, WPI_CMD_TXPOWER, &txpower, sizeof txpower, 0); + if (error != 0) { + printf("%s: could not set txpower\n", sc->sc_dev.dv_xname); + return error; + } + + /* set power mode */ + bzero(&power, sizeof power); + power.flags = htole32(0x8); /* XXX */ + error = wpi_cmd(sc, WPI_CMD_SET_POWER_MODE, &power, sizeof power, 0); + if (error != 0) { + printf("%s: could not set power mode\n", sc->sc_dev.dv_xname); + return error; + } + + /* configure bluetooth coexistence */ + bzero(&bluetooth, sizeof bluetooth); + bluetooth.flags = 3; + bluetooth.lead = 0xaa; + bluetooth.kill = 1; + error = wpi_cmd(sc, WPI_CMD_BLUETOOTH, &bluetooth, sizeof bluetooth, + 0); + if (error != 0) { + printf("%s: could not configure bluetooth coexistence\n", + sc->sc_dev.dv_xname); + return error; + } + + /* configure adapter */ + bzero(&sc->config, sizeof (struct wpi_config)); + IEEE80211_ADDR_COPY(sc->config.myaddr, ic->ic_myaddr); + sc->config.chan = ieee80211_chan2ieee(ic, ic->ic_ibss_chan); + sc->config.mode = WPI_MODE_STA; + sc->config.flags = + htole32(WPI_CONFIG_TSF | WPI_CONFIG_AUTO | WPI_CONFIG_24GHZ); + sc->config.filter = htole32(WPI_FILTER_MULTICAST); + sc->config.cck_mask = 0x0f; /* not yet negotiated */ + sc->config.ofdm_mask = 0xff; /* not yet negotiated */ + error = wpi_cmd(sc, WPI_CMD_CONFIGURE, &sc->config, + sizeof (struct wpi_config), 0); + if (error != 0) { + printf("%s: configure command failed\n", sc->sc_dev.dv_xname); + return error; + } + + /* add broadcast node */ + bzero(&node, sizeof node); + IEEE80211_ADDR_COPY(node.bssid, etherbroadcastaddr); + node.id = WPI_ID_BROADCAST; + node.rate = 10; /* 1Mb/s */ + error = wpi_cmd(sc, WPI_CMD_ADD_NODE, &node, sizeof node, 0); + if (error != 0) { + printf("%s: could not add broadcast node\n", + sc->sc_dev.dv_xname); + return error; + } + + return 0; +} + +void +wpi_stop_master(struct wpi_softc *sc) +{ + uint32_t tmp; + int ntries; + + tmp = WPI_READ(sc, WPI_RESET); + WPI_WRITE(sc, WPI_RESET, tmp | WPI_STOP_MASTER); + + tmp = WPI_READ(sc, WPI_GPIO_CTL); + if ((tmp & WPI_GPIO_PWR_STATUS) == WPI_GPIO_PWR_SLEEP) + return; /* already asleep */ + + for (ntries = 0; ntries < 100; ntries++) { + if (WPI_READ(sc, WPI_RESET) & WPI_MASTER_DISABLED) + break; + DELAY(10); + } + if (ntries == 100) { + printf("%s: timeout waiting for master\n", + sc->sc_dev.dv_xname); + } +} + +int +wpi_power_up(struct wpi_softc *sc) +{ + uint32_t tmp; + int ntries; + + wpi_mem_lock(sc); + tmp = wpi_mem_read(sc, WPI_MEM_POWER); + wpi_mem_write(sc, WPI_MEM_POWER, tmp & ~0x03000000); + wpi_mem_unlock(sc); + + for (ntries = 0; ntries < 5000; ntries++) { + if (WPI_READ(sc, WPI_GPIO_STATUS) & WPI_POWERED) + break; + DELAY(10); + } + if (ntries == 5000) { + printf("%s: timeout waiting for NIC to power up\n", + sc->sc_dev.dv_xname); + return ETIMEDOUT; + } + return 0; +} + +int +wpi_reset(struct wpi_softc *sc) +{ + uint32_t tmp; + int ntries; + + /* clear any pending interrupts */ + WPI_WRITE(sc, WPI_INTR, 0xffffffff); + + tmp = WPI_READ(sc, WPI_PLL_CTL); + WPI_WRITE(sc, WPI_PLL_CTL, tmp | WPI_PLL_INIT); + + tmp = WPI_READ(sc, WPI_CHICKEN); + WPI_WRITE(sc, WPI_CHICKEN, tmp | WPI_CHICKEN_RXNOLOS); + + tmp = WPI_READ(sc, WPI_GPIO_CTL); + WPI_WRITE(sc, WPI_GPIO_CTL, tmp | WPI_GPIO_INIT); + + /* wait for clock stabilization */ + for (ntries = 0; ntries < 1000; ntries++) { + if (WPI_READ(sc, WPI_GPIO_CTL) & WPI_GPIO_CLOCK) + break; + DELAY(10); + } + if (ntries == 1000) { + printf("%s: timeout waiting for clock stabilization\n", + sc->sc_dev.dv_xname); + return ETIMEDOUT; + } + + /* initialize EEPROM */ + tmp = WPI_READ(sc, WPI_EEPROM_STATUS); + if ((tmp & WPI_EEPROM_VERSION) == 0) { + printf("%s: EEPROM not found\n", sc->sc_dev.dv_xname); + return EIO; + } + WPI_WRITE(sc, WPI_EEPROM_STATUS, tmp & ~WPI_EEPROM_LOCKED); + + return 0; +} + +void +wpi_hw_config(struct wpi_softc *sc) +{ + uint16_t val; + uint32_t rev, hw; + + /* voodoo from the Linux "driver".. */ + hw = WPI_READ(sc, WPI_HWCONFIG); + + rev = pci_conf_read(sc->sc_pct, sc->sc_pcitag, PCI_CLASS_REG); + rev = PCI_REVISION(rev); + if ((rev & 0xc0) == 0x40) + hw |= WPI_HW_ALM_MB; + else if (!(rev & 0x80)) + hw |= WPI_HW_ALM_MM; + + val = wpi_read_prom_word(sc, WPI_EEPROM_CAPABILITIES); + if ((val & 0xff) == 0x80) + hw |= WPI_HW_SKU_MRC; + + val = wpi_read_prom_word(sc, WPI_EEPROM_REVISION); + hw &= ~WPI_HW_REV_D; + if ((val & 0xf0) == 0xd0) + hw |= WPI_HW_REV_D; + + val = wpi_read_prom_word(sc, WPI_EEPROM_TYPE); + if ((val & 0xff) > 1) + hw |= WPI_HW_TYPE_B; + + DPRINTF(("setting h/w config %x\n", hw)); + WPI_WRITE(sc, WPI_HWCONFIG, hw); +} + +int +wpi_init(struct ifnet *ifp) +{ + struct wpi_softc *sc = ifp->if_softc; + struct ieee80211com *ic = &sc->sc_ic; + const struct wpi_firmware_hdr *hdr; + const char *boot, *text, *data; + u_char *fw; + size_t size; + uint32_t tmp; + bus_addr_t pshared; + int i, qid, ntries, error; + + wpi_reset(sc); + + wpi_mem_lock(sc); + wpi_mem_write(sc, WPI_MEM_CLOCK1, 0xa00); + DELAY(20); + tmp = wpi_mem_read(sc, WPI_MEM_PCIDEV); + wpi_mem_write(sc, WPI_MEM_PCIDEV, tmp | 0x800); + wpi_mem_unlock(sc); + + wpi_power_up(sc); + wpi_hw_config(sc); + + pshared = sc->shmap->dm_segs[0].ds_addr; + + /* init Rx ring */ + wpi_mem_lock(sc); + WPI_WRITE(sc, WPI_RX_BASE, sc->rxq.map->dm_segs[0].ds_addr); + WPI_WRITE(sc, WPI_RX_RIDX_PTR, + (uint32_t)&((struct wpi_shared *)pshared)->next); + WPI_WRITE(sc, WPI_RX_WIDX, (WPI_RX_RING_COUNT - 1) & ~7); + WPI_WRITE(sc, WPI_RX_CONFIG, 0xa9601010); + wpi_mem_unlock(sc); + + /* init Tx rings */ + wpi_mem_lock(sc); + wpi_mem_write(sc, WPI_MEM_MODE, 2); /* bypass mode */ + wpi_mem_write(sc, WPI_MEM_RA, 1); /* enable RA0 */ + wpi_mem_write(sc, WPI_MEM_TXCFG, 0x3f); /* enable all 6 Tx rings */ + wpi_mem_write(sc, WPI_MEM_BYPASS1, 0x10000); + wpi_mem_write(sc, WPI_MEM_BYPASS2, 0x30002); + wpi_mem_write(sc, WPI_MEM_MAGIC4, 4); + wpi_mem_write(sc, WPI_MEM_MAGIC5, 5); + + WPI_WRITE(sc, WPI_TX_BASE_PTR, pshared); + WPI_WRITE(sc, WPI_MSG_CONFIG, 0xffff05a5); + + for (qid = 0; qid < 6; qid++) { + WPI_WRITE(sc, WPI_TX_CTL(qid), 0); + WPI_WRITE(sc, WPI_TX_BASE(qid), 0); + WPI_WRITE(sc, WPI_TX_CONFIG(qid), 0x80200008); + } + wpi_mem_unlock(sc); + + /* clear radio off and disable command bits (reversed logic) */ + WPI_WRITE(sc, WPI_UCODE_CLR, WPI_RADIO_OFF); + WPI_WRITE(sc, WPI_UCODE_CLR, WPI_DISABLE_CMD); + + /* clear any pending interrupts */ + WPI_WRITE(sc, WPI_INTR, 0xffffffff); + /* enable interrupts */ + WPI_WRITE(sc, WPI_MASK, WPI_INTR_MASK); + + /* read channels power settings before firmware locks EEPROM */ + for (i = 0; i < 14; i++) { + sc->calib1[i] = wpi_read_prom_word(sc, WPI_EEPROM_CALIB1 + i); + sc->calib2[i] = wpi_read_prom_word(sc, WPI_EEPROM_CALIB2 + i); + DPRINTF(("channel %d calib1 0x%04x calib2 0x%04x\n", i + 1, + sc->calib1[i], sc->calib2[i])); + } + + if ((error = loadfirmware("wpi-ucode", &fw, &size)) != 0) { + printf("%s: could not read firmware file\n", + sc->sc_dev.dv_xname); + goto fail1; + } + + if (size < sizeof (struct wpi_firmware_hdr)) { + printf("%s: firmware file too short: %d bytes\n", + sc->sc_dev.dv_xname, size); + error = EINVAL; + goto fail2; + } + + hdr = (const struct wpi_firmware_hdr *)fw; + if (size < sizeof (struct wpi_firmware_hdr) + letoh32(hdr->textsz) + + letoh32(hdr->datasz) + letoh32(hdr->bootsz)) { + printf("%s: firmware file too short: %d bytes\n", + sc->sc_dev.dv_xname, size); + error = EINVAL; + goto fail2; + } + + /* firmware image layout: |HDR|<--TEXT-->|<--DATA-->|<--BOOT-->| */ + text = (const char *)(hdr + 1); + data = text + letoh32(hdr->textsz); + boot = data + letoh32(hdr->datasz); + + /* load firmware boot code into NIC */ + error = wpi_load_microcode(sc, boot, letoh32(hdr->bootsz)); + if (error != 0) { + printf("%s: could not load microcode\n", sc->sc_dev.dv_xname); + goto fail2; + } + + /* load firmware .text segment into NIC */ + error = wpi_load_firmware(sc, WPI_FW_TEXT, text, letoh32(hdr->textsz)); + if (error != 0) { + printf("%s: could not load firmware\n", sc->sc_dev.dv_xname); + goto fail2; + } + + /* load firmware .data segment into NIC */ + error = wpi_load_firmware(sc, WPI_FW_DATA, data, letoh32(hdr->datasz)); + if (error != 0) { + printf("%s: could not load firmware\n", sc->sc_dev.dv_xname); + goto fail2; + } + + free(fw, M_DEVBUF); + + /* now press "execute" ;-) */ + tmp = WPI_READ(sc, WPI_RESET); + tmp &= ~(WPI_MASTER_DISABLED | WPI_STOP_MASTER | WPI_NEVO_RESET); + WPI_WRITE(sc, WPI_RESET, tmp); + + /* ..and wait at most one second for adapter to initialize */ + if ((error = tsleep(sc, PCATCH, "wpiinit", hz)) != 0) { + printf("%s: timeout waiting for adapter to initialize\n", + sc->sc_dev.dv_xname); + goto fail1; + } + + /* wait for thermal sensors to calibrate */ + for (ntries = 0; ntries < 1000; ntries++) { + if (WPI_READ(sc, WPI_TEMPERATURE) != 0) + break; + DELAY(10); + } + if (ntries == 1000) { + printf("%s: timeout waiting for thermal sensors calibration\n", + sc->sc_dev.dv_xname); + error = EIO; + goto fail1; + } + DPRINTF(("temperature %d\n", (int)WPI_READ(sc, WPI_TEMPERATURE))); + + if ((error = wpi_config(sc)) != 0) { + printf("%s: could not configure device\n", + sc->sc_dev.dv_xname); + goto fail1; + } + + ifp->if_flags &= ~IFF_OACTIVE; + 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; + +fail2: free(fw, M_DEVBUF); +fail1: 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; + uint32_t tmp; + int ac; + + ifp->if_timer = sc->sc_tx_timer = 0; + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + + ieee80211_new_state(ic, IEEE80211_S_INIT, -1); + + /* disable interrupts */ + WPI_WRITE(sc, WPI_MASK, 0); + WPI_WRITE(sc, WPI_INTR, WPI_INTR_MASK); + WPI_WRITE(sc, WPI_INTR_STATUS, 0xff); + WPI_WRITE(sc, WPI_INTR_STATUS, 0x00070000); + + wpi_mem_lock(sc); + wpi_mem_write(sc, WPI_MEM_MODE, 0); + wpi_mem_unlock(sc); + + /* reset all Tx rings */ + for (ac = 0; ac < 4; ac++) + wpi_reset_tx_ring(sc, &sc->txq[ac]); + wpi_reset_tx_ring(sc, &sc->cmdq); + wpi_reset_tx_ring(sc, &sc->svcq); + + /* reset Rx ring */ + wpi_reset_rx_ring(sc, &sc->rxq); + + wpi_mem_lock(sc); + wpi_mem_write(sc, WPI_MEM_CLOCK2, 0x200); + wpi_mem_unlock(sc); + + DELAY(5); + + wpi_stop_master(sc); + + tmp = WPI_READ(sc, WPI_RESET); + WPI_WRITE(sc, WPI_RESET, tmp | WPI_SW_RESET); +} + +struct cfdriver wpi_cd = { + NULL, "wpi", DV_IFNET +}; diff --git a/sys/dev/pci/if_wpireg.h b/sys/dev/pci/if_wpireg.h new file mode 100644 index 00000000000..34fbafbb1a5 --- /dev/null +++ b/sys/dev/pci/if_wpireg.h @@ -0,0 +1,509 @@ +/* $OpenBSD: if_wpireg.h,v 1.1 2006/05/14 19:00:48 damien Exp $ */ + + +/*- + * Copyright (c) 2006 + * 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. + */ + +#define WPI_TX_RING_COUNT 256 +#define WPI_SVC_RING_COUNT 256 +#define WPI_CMD_RING_COUNT 256 +#define WPI_RX_RING_COUNT 64 + +/* + * Rings must be aligned on a four 4K-pages boundary. + * I had a hard time figuring this out. + */ +#define WPI_RING_DMA_ALIGN 0x4000 + +/* maximum scatter/gather */ +#define WPI_MAX_SCATTER 4 + +/* + * Control and status registers. + */ +#define WPI_HWCONFIG 0x000 +#define WPI_INTR 0x008 +#define WPI_MASK 0x00c +#define WPI_INTR_STATUS 0x010 +#define WPI_GPIO_STATUS 0x018 +#define WPI_RESET 0x020 +#define WPI_GPIO_CTL 0x024 +#define WPI_EEPROM_CTL 0x02c +#define WPI_EEPROM_STATUS 0x030 +#define WPI_UCODE_CLR 0x05c +#define WPI_TEMPERATURE 0x060 +#define WPI_CHICKEN 0x100 +#define WPI_PLL_CTL 0x20c +#define WPI_FW_TARGET 0x410 +#define WPI_WRITE_MEM_ADDR 0x444 +#define WPI_READ_MEM_ADDR 0x448 +#define WPI_WRITE_MEM_DATA 0x44c +#define WPI_READ_MEM_DATA 0x450 +#define WPI_TX_WIDX 0x460 +#define WPI_TX_CTL(qid) (0x940 + (qid) * 8) +#define WPI_TX_BASE(qid) (0x944 + (qid) * 8) +#define WPI_TX_DESC(qid) (0x980 + (qid) * 80) +#define WPI_RX_CONFIG 0xc00 +#define WPI_RX_BASE 0xc04 +#define WPI_RX_WIDX 0xc20 +#define WPI_RX_RIDX_PTR 0xc24 +#define WPI_RX_CTL 0xcc0 +#define WPI_RX_STATUS 0xcc4 +#define WPI_TX_CONFIG(qid) (0xd00 + (qid) * 32) +#define WPI_TX_CREDIT(qid) (0xd04 + (qid) * 32) +#define WPI_TX_STATE(qid) (0xd08 + (qid) * 32) +#define WPI_TX_BASE_PTR 0xe80 +#define WPI_MSG_CONFIG 0xe88 +#define WPI_TX_STATUS 0xe90 + + +/* + * NIC internal memory offsets. + */ +#define WPI_MEM_MODE 0x2e00 +#define WPI_MEM_RA 0x2e04 +#define WPI_MEM_TXCFG 0x2e10 +#define WPI_MEM_MAGIC4 0x2e14 +#define WPI_MEM_MAGIC5 0x2e20 +#define WPI_MEM_BYPASS1 0x2e2c +#define WPI_MEM_BYPASS2 0x2e30 +#define WPI_MEM_CLOCK1 0x3004 +#define WPI_MEM_CLOCK2 0x3008 +#define WPI_MEM_POWER 0x300c +#define WPI_MEM_PCIDEV 0x3010 +#define WPI_MEM_UCODE_CTL 0x3400 +#define WPI_MEM_UCODE_SRC 0x3404 +#define WPI_MEM_UCODE_DST 0x3408 +#define WPI_MEM_UCODE_SIZE 0x340c +#define WPI_MEM_UCODE_BASE 0x3800 + + +/* possible flags for register WPI_HWCONFIG */ +#define WPI_HW_ALM_MB (1 << 8) +#define WPI_HW_ALM_MM (1 << 9) +#define WPI_HW_SKU_MRC (1 << 10) +#define WPI_HW_REV_D (1 << 11) +#define WPI_HW_TYPE_B (1 << 12) + +/* possible flags for registers WPI_READ_MEM_ADDR/WPI_WRITE_MEM_ADDR */ +#define WPI_MEM_4 ((sizeof (uint32_t) - 1) << 24) + +/* possible values for WPI_FW_TARGET */ +#define WPI_FW_TEXT 0x00000000 +#define WPI_FW_DATA 0x00800000 + +/* possible flags for WPI_GPIO_STATUS */ +#define WPI_POWERED (1 << 9) + +/* possible flags for register WPI_RESET */ +#define WPI_NEVO_RESET (1 << 0) +#define WPI_SW_RESET (1 << 7) +#define WPI_MASTER_DISABLED (1 << 8) +#define WPI_STOP_MASTER (1 << 9) + +/* possible flags for register WPI_GPIO_CTL */ +#define WPI_GPIO_CLOCK (1 << 0) +#define WPI_GPIO_INIT (1 << 2) +#define WPI_GPIO_MAC (1 << 3) +#define WPI_GPIO_SLEEP (1 << 4) +#define WPI_GPIO_PWR_STATUS 0x07000000 +#define WPI_GPIO_PWR_SLEEP (4 << 24) + +/* possible flags for register WPI_CHICKEN */ +#define WPI_CHICKEN_RXNOLOS (1 << 23) + +/* possible flags for register WPI_PLL_CTL */ +#define WPI_PLL_INIT (1 << 24) + +/* possible flags for register WPI_UCODE_CLR */ +#define WPI_RADIO_OFF (1 << 1) +#define WPI_DISABLE_CMD (1 << 2) + +/* possible flags for WPI_RX_STATUS */ +#define WPI_RX_IDLE (1 << 24) + +/* possible flags for register WPI_UC_CTL */ +#define WPI_UC_RUN (1 << 30) + +/* possible flags for register WPI_INTR_CSR */ +#define WPI_ALIVE_INTR (1 << 0) +#define WPI_WAKEUP_INTR (1 << 1) +#define WPI_SW_ERROR (1 << 25) +#define WPI_TX_INTR (1 << 27) +#define WPI_HW_ERROR (1 << 29) +#define WPI_RX_INTR (1 << 31) + +#define WPI_INTR_MASK \ + (WPI_SW_ERROR | WPI_HW_ERROR | WPI_TX_INTR | WPI_RX_INTR | \ + WPI_ALIVE_INTR | WPI_WAKEUP_INTR) + +/* possible flags for register WPI_TX_STATUS */ +#define WPI_TX_IDLE(qid) (1 << ((qid) + 24) | 1 << ((qid) + 16)) + +/* possible flags for register WPI_EEPROM_CTL */ +#define WPI_EEPROM_READY (1 << 0) + +/* possible flags for register WPI_EEPROM_STATUS */ +#define WPI_EEPROM_VERSION 0x00000007 +#define WPI_EEPROM_LOCKED 0x00000180 + + +struct wpi_shared { + uint32_t txbase[8]; + uint32_t next; + uint32_t reserved[2]; +} __packed; + +#define WPI_MAX_SEG_LEN 65520 +struct wpi_tx_desc { + uint32_t flags; +#define WPI_PAD32(x) ((((x) + 3) & ~3) - (x)) + + struct { + uint32_t physaddr; + uint32_t len; + } __packed segs[WPI_MAX_SCATTER]; + uint8_t reserved[28]; +} __packed; + +struct wpi_tx_stat { + uint8_t nrts; + uint8_t ntries; + uint8_t nkill; + uint8_t rate; + uint32_t duration; + uint32_t status; +} __packed; + +struct wpi_rx_desc { + uint32_t len; + uint8_t type; +#define WPI_UC_READY 1 +#define WPI_RX_DONE 27 +#define WPI_TX_DONE 28 +#define WPI_START_SCAN 130 +#define WPI_STOP_SCAN 132 +#define WPI_STATE_CHANGED 161 + + uint8_t flags; + uint8_t idx; + uint8_t qid; +} __packed; + +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; + uint64_t tstamp; + uint32_t tbeacon; +} __packed; + +struct wpi_tx_cmd { + uint8_t code; +#define WPI_CMD_CONFIGURE 16 +#define WPI_CMD_ASSOCIATE 17 +#define WPI_CMD_TSF 20 +#define WPI_CMD_ADD_NODE 24 +#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_BLUETOOTH 155 +#define WPI_CMD_TXPOWER 176 + + uint8_t flags; + uint8_t idx; + uint8_t qid; + uint8_t data[124]; +} __packed; + +/* structure for WPI_CMD_CONFIGURE */ +struct wpi_config { + uint8_t myaddr[IEEE80211_ADDR_LEN]; + uint16_t reserved1; + uint8_t bssid[IEEE80211_ADDR_LEN]; + uint16_t reserved2; + uint32_t reserved3[2]; + uint8_t mode; +#define WPI_MODE_STA 3 + + uint8_t reserved4[3]; + uint8_t ofdm_mask; + uint8_t cck_mask; + uint16_t state; +#define WPI_CONFIG_ASSOCIATED 4 + + uint32_t flags; +#define WPI_CONFIG_24GHZ (1 << 0) +#define WPI_CONFIG_CCK (1 << 1) +#define WPI_CONFIG_AUTO (1 << 2) +#define WPI_CONFIG_SHORT_SLOT (1 << 4) +#define WPI_CONFIG_SHPREAMBLE (1 << 5) +#define WPI_CONFIG_NODIVERSITY (1 << 7) +#define WPI_CONFIG_ANTENNA_A (1 << 8) +#define WPI_CONFIG_ANTENNA_B (1 << 9) +#define WPI_CONFIG_TSF (1 << 15) + + uint32_t filter; +#define WPI_FILTER_PROMISC (1 << 0) +#define WPI_FILTER_CTL (1 << 1) +#define WPI_FILTER_MULTICAST (1 << 2) +#define WPI_FILTER_NODECRYPT (1 << 3) +#define WPI_FILTER_BSSID (1 << 5) + + uint8_t chan; + uint8_t reserved6[3]; +} __packed; + +/* structure for command WPI_CMD_ASSOCIATE */ +struct wpi_assoc { + uint32_t flags; + uint32_t filter; + uint8_t ofdm_mask; + uint8_t cck_mask; + uint16_t reserved; +} __packed; + +/* structure for command WPI_CMD_TSF */ +struct wpi_cmd_tsf { + uint64_t tstamp; + uint16_t bintval; + uint16_t atim; + uint32_t binitval; + uint16_t lintval; + uint16_t reserved; +} __packed; + +/* structure for WPI_CMD_ADD_NODE */ +struct wpi_node { + uint8_t control; +#define WPI_NODE_UPDATE (1 << 0) + + uint8_t reserved1[3]; + uint8_t bssid[IEEE80211_ADDR_LEN]; + uint16_t reserved2; + uint8_t id; +#define WPI_ID_BSSID 0 +#define WPI_ID_BROADCAST 24 + + uint8_t sta_mask; + uint16_t reserved3; + uint16_t key_flags; + uint8_t tkip; + uint8_t reserved4; + uint16_t ttak[5]; + uint16_t reserved5; + uint8_t key[IEEE80211_KEYBUF_SIZE]; + uint32_t flags; + uint32_t mask; + uint16_t tid; + uint8_t rate; + uint8_t reserved6; + uint8_t add_imm; + uint8_t del_imm; + uint16_t add_imm_start; +} __packed; + +/* structure for command WPI_CMD_TX_DATA */ +struct wpi_cmd_data { + uint16_t len; + uint16_t lnext; + uint32_t flags; +#define WPI_TX_NEED_ACK (1 << 3) +#define WPI_TX_AUTO_SEQ (1 << 13) +#define WPI_TX_INSERT_TSTAMP (1 << 16) + + uint8_t rate; + uint8_t id; + uint8_t tid; + uint8_t security; + uint8_t key[IEEE80211_KEYBUF_SIZE]; + uint8_t tkip[IEEE80211_WEP_MICLEN]; + uint32_t fnext; + uint32_t lifetime; + uint8_t ofdm_mask; + uint8_t cck_mask; + uint8_t rts_ntries; + uint8_t data_ntries; + uint16_t duration; + uint16_t txop; + struct ieee80211_frame wh; +} __packed; + +/* structure for WPI_CMD_MRR_SETUP */ +struct wpi_mrr_setup { + uint32_t which; +#define WPI_MRR_CTL 0 +#define WPI_MRR_DATA 1 + + struct { + uint8_t plcp; + uint8_t flags; + uint8_t ntries; + uint8_t next; +#define WPI_OFDM6 0 +#define WPI_OFDM54 7 +#define WPI_CCK1 8 +#define WPI_CCK11 11 + + } __packed rates[WPI_CCK11 + 1]; +} __packed; + +/* structure for WPI_CMD_SET_LED */ +struct wpi_cmd_led { + uint32_t unit; /* multiplier (in usecs) */ + uint8_t which; +#define WPI_LED_ACTIVITY 1 +#define WPI_LED_LINK 2 + + uint8_t off; + uint8_t on; + uint8_t reserved; +} __packed; + +/* structure for WPI_CMD_SET_POWER_MODE */ +struct wpi_power { + uint32_t flags; + uint32_t rx_timeout; + uint32_t tx_timeout; + uint32_t sleep[5]; +} __packed; + +/* structure for command WPI_CMD_SCAN */ +struct wpi_scan_hdr { + uint8_t len; + uint8_t first; + uint8_t reserved1; + uint8_t nchan; + uint16_t quiet; + uint16_t threshold; + uint32_t reserved2[3]; + uint32_t filter; + uint32_t reserved3; + uint16_t length; + uint16_t reserved4; + uint32_t magic1; + uint8_t rate; + uint8_t id; + uint16_t reserved5; + uint32_t reserved6[7]; + uint32_t mask; + uint32_t reserved7[2]; + uint8_t reserved8; + uint8_t esslen; + uint8_t essid[134]; + + /* followed by probe request body */ + /* followed by nchan x wpi_scan_chan */ +} __packed; + +struct wpi_scan_chan { + uint8_t flags; + uint8_t chan; + uint16_t magic; /* XXX */ + uint16_t active; /* dwell time */ + uint16_t passive; /* dwell time */ +} __packed; + +/* structure for WPI_CMD_BLUETOOTH */ +struct wpi_bluetooth { + uint8_t flags; + uint8_t lead; + uint8_t kill; + uint8_t reserved; + uint32_t ack; + uint32_t cts; +} __packed; + +/* structure for command WPI_CMD_TXPOWER */ +struct wpi_txpower { + uint32_t reserved1; + uint16_t calib1[14]; + uint32_t reserved2[2]; + uint16_t calib2[14]; + uint32_t reserved3[2]; +} __packed; + + +/* firmware image header */ +struct wpi_firmware_hdr { + uint32_t version; + uint32_t textsz; + uint32_t datasz; + uint32_t bootsz; +} __packed; + +/* structure for WPI_UC_READY notification */ +struct wpi_ucode_info { + uint32_t version; + uint8_t revision[8]; + uint8_t type; + uint8_t subtype; + uint16_t reserved; + uint32_t logptr; + uint32_t errorptr; + uint32_t timestamp; + uint32_t valid; +} __packed; + +/* structure for WPI_START_SCAN notification */ +struct wpi_start_scan { + uint64_t tstamp; + uint32_t tbeacon; + uint8_t chan; + uint8_t band; + uint16_t reserved; + uint32_t status; +} __packed; + + +#define WPI_EEPROM_MAC 0x015 +#define WPI_EEPROM_REVISION 0x035 +#define WPI_EEPROM_CAPABILITIES 0x045 +#define WPI_EEPROM_TYPE 0x04a +#define WPI_EEPROM_CALIB1 0x1ae +#define WPI_EEPROM_CALIB2 0x1bc + +#define WPI_READ(sc, reg) \ + bus_space_read_4((sc)->sc_st, (sc)->sc_sh, (reg)) + +#define WPI_WRITE(sc, reg, val) \ + bus_space_write_4((sc)->sc_st, (sc)->sc_sh, (reg), (val)) + +#define WPI_WRITE_REGION_4(sc, offset, datap, count) \ + bus_space_write_region_4((sc)->sc_st, (sc)->sc_sh, (offset), \ + (datap), (count)) diff --git a/sys/dev/pci/if_wpivar.h b/sys/dev/pci/if_wpivar.h new file mode 100644 index 00000000000..29dd6ee84df --- /dev/null +++ b/sys/dev/pci/if_wpivar.h @@ -0,0 +1,131 @@ +/* $OpenBSD: if_wpivar.h,v 1.1 2006/05/14 19:00:48 damien Exp $ */ + +/*- + * Copyright (c) 2006 + * 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; + uint8_t wr_flags; + uint16_t wr_chan_freq; + uint16_t wr_chan_flags; + uint8_t wr_antsignal; +} __packed; + +#define WPI_RX_RADIOTAP_PRESENT \ + ((1 << IEEE80211_RADIOTAP_FLAGS) | \ + (1 << IEEE80211_RADIOTAP_CHANNEL) | \ + (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL)) + +struct wpi_tx_radiotap_header { + struct ieee80211_radiotap_header wt_ihdr; + uint8_t wt_flags; + uint16_t wt_chan_freq; + uint16_t wt_chan_flags; +} __packed; + +#define WPI_TX_RADIOTAP_PRESENT \ + ((1 << IEEE80211_RADIOTAP_FLAGS) | \ + (1 << IEEE80211_RADIOTAP_CHANNEL)) + +struct wpi_tx_data { + bus_dmamap_t map; + struct mbuf *m; + struct ieee80211_node *ni; +}; + +struct wpi_tx_ring { + bus_dmamap_t map; + bus_dma_segment_t seg; + bus_dmamap_t cmd_map; + bus_dma_segment_t cmd_seg; + struct wpi_tx_desc *desc; + struct wpi_tx_cmd *cmd; + struct wpi_tx_data *data; + int qid; + int count; + int queued; + int cur; +}; + +struct wpi_rx_data { + bus_dmamap_t map; + struct mbuf *m; +}; + +struct wpi_rx_ring { + bus_dmamap_t map; + bus_dma_segment_t seg; + uint32_t *desc; + struct wpi_rx_data data[WPI_RX_RING_COUNT]; + int cur; +}; + +struct wpi_softc { + struct device sc_dev; + + struct ieee80211com sc_ic; + int (*sc_newstate)(struct ieee80211com *, + enum ieee80211_state, int); + + uint32_t flags; +#define WPI_FLAG_FW_INITED (1 << 0) + + bus_dma_tag_t sc_dmat; + + /* shared area */ + bus_dmamap_t shmap; + bus_dma_segment_t shseg; + struct wpi_shared *shared; + + struct wpi_tx_ring txq[4]; + struct wpi_tx_ring cmdq; + struct wpi_tx_ring svcq; + struct wpi_rx_ring rxq; + + bus_space_tag_t sc_st; + bus_space_handle_t sc_sh; + void *sc_ih; + pci_chipset_tag_t sc_pct; + pcitag_t sc_pcitag; + bus_size_t sc_sz; + + struct wpi_config config; + uint16_t calib1[14]; + uint16_t calib2[14]; + + int authmode; + int sc_tx_timer; + void *powerhook; + +#if NBPFILTER > 0 + caddr_t sc_drvbpf; + + union { + struct wpi_rx_radiotap_header th; + uint8_t pad[64]; + } sc_rxtapu; +#define sc_rxtap sc_rxtapu.th + int sc_rxtap_len; + + union { + struct wpi_tx_radiotap_header th; + uint8_t pad[64]; + } sc_txtapu; +#define sc_txtap sc_txtapu.th + int sc_txtap_len; +#endif +}; -- cgit v1.2.3