summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorDamien Bergamini <damien@cvs.openbsd.org>2006-05-14 19:00:52 +0000
committerDamien Bergamini <damien@cvs.openbsd.org>2006-05-14 19:00:52 +0000
commitc51f93a9394c6317e7705dbd069d4d6ad8e8fa55 (patch)
tree751d7ea4ee33214d964f78058808510fc7631750 /sys/dev
parentfd608b090a49be0989830bbc32c271ba453194f6 (diff)
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@
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/pci/files.pci7
-rw-r--r--sys/dev/pci/if_wpi.c2404
-rw-r--r--sys/dev/pci/if_wpireg.h509
-rw-r--r--sys/dev/pci/if_wpivar.h131
4 files changed, 3050 insertions, 1 deletions
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 <damien.bergamini@free.fr>
+ *
+ * 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 <sys/param.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/conf.h>
+#include <sys/device.h>
+
+#include <machine/bus.h>
+#include <machine/endian.h>
+#include <machine/intr.h>
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcidevs.h>
+
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#endif
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/if_ether.h>
+#include <netinet/ip.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_radiotap.h>
+
+#include <dev/pci/if_wpireg.h>
+#include <dev/pci/if_wpivar.h>
+
+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 <damien.bergamini@free.fr>
+ *
+ * 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 <damien.bergamini@free.fr>
+ *
+ * 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
+};