summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorMarcus Glocker <mglocker@cvs.openbsd.org>2006-08-03 08:45:02 +0000
committerMarcus Glocker <mglocker@cvs.openbsd.org>2006-08-03 08:45:02 +0000
commited942f3438d54a63c8fe9da9e311b76e6c74a5a6 (patch)
tree2bd0320804b875f93133a1519ffbdd54676d639c /sys/dev
parentdb650eb0221a47878ca601bd66b9675845174f08 (diff)
Inital import for the acx(4) driver.
ok deraadt@ jsg@
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/cardbus/files.cardbus8
-rw-r--r--sys/dev/cardbus/if_acx_cardbus.c303
-rw-r--r--sys/dev/ic/acx.c3017
-rw-r--r--sys/dev/ic/acx100.c768
-rw-r--r--sys/dev/ic/acx111.c458
-rw-r--r--sys/dev/ic/acxreg.h574
-rw-r--r--sys/dev/ic/acxvar.h469
7 files changed, 5596 insertions, 1 deletions
diff --git a/sys/dev/cardbus/files.cardbus b/sys/dev/cardbus/files.cardbus
index 61a7aa8f8fa..938de3c82ea 100644
--- a/sys/dev/cardbus/files.cardbus
+++ b/sys/dev/cardbus/files.cardbus
@@ -1,4 +1,4 @@
-# $OpenBSD: files.cardbus,v 1.19 2006/07/31 11:06:27 mickey Exp $
+# $OpenBSD: files.cardbus,v 1.20 2006/08/03 08:45:01 mglocker Exp $
# $NetBSD: files.cardbus,v 1.8 2000/01/26 06:37:24 thorpej Exp $
#
# files.cardbus
@@ -77,6 +77,12 @@ attach ral at cardbus with ral_cardbus
file dev/cardbus/if_ral_cardbus.c ral_cardbus
#
+# Texas Instruments ACX
+#
+attach acx at cardbus with acx_cardbus
+file dev/cardbus/if_acx_cardbus.c
+
+#
# EHCI USB controller
#
attach ehci at cardbus with ehci_cardbus
diff --git a/sys/dev/cardbus/if_acx_cardbus.c b/sys/dev/cardbus/if_acx_cardbus.c
new file mode 100644
index 00000000000..45f52a426c3
--- /dev/null
+++ b/sys/dev/cardbus/if_acx_cardbus.c
@@ -0,0 +1,303 @@
+/* $Id: if_acx_cardbus.c,v 1.1 2006/08/03 08:45:01 mglocker Exp $ */
+
+/* $OpenBSD: if_acx_cardbus.c,v 1.1 2006/08/03 08:45:01 mglocker Exp $ */
+
+/*-
+ * Copyright (c) 2005, 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.
+ */
+
+/*
+ * CardBus front-end for the Texas Instruments ACX driver
+ */
+
+#include "bpfilter.h"
+
+#include <sys/param.h>
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/timeout.h>
+#include <sys/device.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#endif
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_rssadapt.h>
+#include <net80211/ieee80211_radiotap.h>
+
+#include <dev/ic/acxvar.h>
+#include <dev/ic/acxreg.h>
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcidevs.h>
+
+#include <dev/cardbus/cardbusvar.h>
+
+struct acx_cardbus_softc {
+ struct acx_softc sc_acx;
+
+ /* cardbus specific goo */
+ cardbus_devfunc_t sc_ct;
+ cardbustag_t sc_tag;
+ void *sc_ih;
+ bus_size_t sc_mapsize1;
+ bus_size_t sc_mapsize2;
+ pcireg_t sc_iobar_val; /* acx100 only */
+ pcireg_t sc_bar1_val;
+ pcireg_t sc_bar2_val;
+ int sc_intrline;
+
+ /* hack for ACX100A */
+ bus_space_tag_t sc_io_bt;
+ bus_space_handle_t sc_io_bh;
+ bus_size_t sc_iomapsize;
+};
+
+int acx_cardbus_match(struct device *, void *, void *);
+void acx_cardbus_attach(struct device *, struct device *, void *);
+int acx_cardbus_detach(struct device *, int);
+
+struct cfattach acx_cardbus_ca = {
+ sizeof (struct acx_cardbus_softc), acx_cardbus_match,
+ acx_cardbus_attach, acx_cardbus_detach
+};
+
+static const struct cardbus_matchid acx_cardbus_devices[] = {
+ { PCI_VENDOR_TI, PCI_PRODUCT_TI_ACX100A },
+ { PCI_VENDOR_TI, PCI_PRODUCT_TI_ACX100B },
+ { PCI_VENDOR_TI, PCI_PRODUCT_TI_ACX111 },
+};
+
+int acx_cardbus_enable(struct acx_softc *);
+void acx_cardbus_disable(struct acx_softc *);
+void acx_cardbus_power(struct acx_softc *, int);
+void acx_cardbus_setup(struct acx_cardbus_softc *);
+
+int
+acx_cardbus_match(struct device *parent, void *match, void *aux)
+{
+ return (cardbus_matchbyid((struct cardbus_attach_args *)aux,
+ acx_cardbus_devices,
+ sizeof (acx_cardbus_devices) / sizeof (acx_cardbus_devices[0])));
+}
+
+void
+acx_cardbus_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct acx_cardbus_softc *csc = (struct acx_cardbus_softc *)self;
+ struct acx_softc *sc = &csc->sc_acx;
+ struct cardbus_attach_args *ca = aux;
+ cardbus_devfunc_t ct = ca->ca_ct;
+ bus_addr_t base;
+ int error, b1 = CARDBUS_BASE0_REG, b2 = CARDBUS_BASE1_REG;
+
+ sc->sc_dmat = ca->ca_dmat;
+ csc->sc_ct = ct;
+ csc->sc_tag = ca->ca_tag;
+ csc->sc_intrline = ca->ca_intrline;
+
+ /* power management hooks */
+ sc->sc_enable = acx_cardbus_enable;
+ sc->sc_disable = acx_cardbus_disable;
+ sc->sc_power = acx_cardbus_power;
+
+ if (CARDBUS_PRODUCT(ca->ca_id) == PCI_PRODUCT_TI_ACX100A) {
+ /* first map I/O space as seen in the dragonfly code */
+ error = Cardbus_mapreg_map(ct, CARDBUS_BASE0_REG,
+ CARDBUS_MAPREG_TYPE_IO, 0, &csc->sc_io_bt, &csc->sc_io_bh,
+ &base, &csc->sc_iomapsize);
+ if (error != 0) {
+ printf(": could not map i/o space\n");
+ return;
+ }
+ csc->sc_iobar_val = base | CARDBUS_MAPREG_TYPE_IO;
+ b1 = CARDBUS_BASE1_REG;
+ b2 = CARDBUS_BASE2_REG;
+
+ }
+
+ /* map control/status registers */
+ error = Cardbus_mapreg_map(ct, b1, CARDBUS_MAPREG_TYPE_MEM, 0,
+ &sc->sc_mem1_bt, &sc->sc_mem1_bh, &base, &csc->sc_mapsize1);
+ if (error != 0) {
+ printf(": could not map memory space\n");
+ return;
+ }
+
+ csc->sc_bar1_val = base | CARDBUS_MAPREG_TYPE_MEM;
+
+ /* map the other memory region */
+ error = Cardbus_mapreg_map(ct, b2, CARDBUS_MAPREG_TYPE_MEM, 0,
+ &sc->sc_mem2_bt, &sc->sc_mem2_bh, &base, &csc->sc_mapsize2);
+ if (error != 0) {
+ printf(": could not map memory space\n");
+ return;
+ }
+
+ csc->sc_bar2_val = base | CARDBUS_MAPREG_TYPE_MEM;
+
+ /* set up the PCI configuration registers */
+ acx_cardbus_setup(csc);
+
+ printf(": irq %d\n", csc->sc_intrline);
+
+
+ if (CARDBUS_PRODUCT(ca->ca_id) == PCI_PRODUCT_TI_ACX111)
+ acx111_set_param(sc);
+ else
+ acx100_set_param(sc);
+
+ acx_attach(sc);
+
+ Cardbus_function_disable(ct);
+}
+
+int
+acx_cardbus_detach(struct device *self, int flags)
+{
+ struct acx_cardbus_softc *csc = (struct acx_cardbus_softc *)self;
+ struct acx_softc *sc = &csc->sc_acx;
+ cardbus_devfunc_t ct = csc->sc_ct;
+ cardbus_chipset_tag_t cc = ct->ct_cc;
+ cardbus_function_tag_t cf = ct->ct_cf;
+ int error;
+
+ error = acx_detach(sc);
+ if (error != 0)
+ return error;
+
+ /* unhook the interrupt handler */
+ if (csc->sc_ih != NULL) {
+ cardbus_intr_disestablish(cc, cf, csc->sc_ih);
+ csc->sc_ih = NULL;
+ }
+
+ /* release bus space and close window */
+ Cardbus_mapreg_unmap(ct, CARDBUS_BASE0_REG, sc->sc_mem1_bt,
+ sc->sc_mem1_bh, csc->sc_mapsize1);
+ Cardbus_mapreg_unmap(ct, CARDBUS_BASE1_REG, sc->sc_mem2_bt,
+ sc->sc_mem2_bh, csc->sc_mapsize2);
+ if (csc->sc_io_bt)
+ Cardbus_mapreg_unmap(ct, CARDBUS_BASE0_REG, csc->sc_io_bt,
+ csc->sc_io_bh, csc->sc_iomapsize);
+
+ return 0;
+}
+
+int
+acx_cardbus_enable(struct acx_softc *sc)
+{
+ struct acx_cardbus_softc *csc = (struct acx_cardbus_softc *)sc;
+ cardbus_devfunc_t ct = csc->sc_ct;
+ cardbus_chipset_tag_t cc = ct->ct_cc;
+ cardbus_function_tag_t cf = ct->ct_cf;
+
+ /* power on the socket */
+ Cardbus_function_enable(ct);
+
+ /* setup the PCI configuration registers */
+ acx_cardbus_setup(csc);
+
+ /* map and establish the interrupt handler */
+ csc->sc_ih = cardbus_intr_establish(cc, cf, csc->sc_intrline, IPL_NET,
+ acx_intr, sc);
+ if (csc->sc_ih == NULL) {
+ printf("%s: could not establish interrupt at %d\n",
+ sc->sc_dev.dv_xname, csc->sc_intrline);
+ Cardbus_function_disable(ct);
+ return 1;
+ }
+
+ return 0;
+}
+
+void
+acx_cardbus_disable(struct acx_softc *sc)
+{
+ struct acx_cardbus_softc *csc = (struct acx_cardbus_softc *)sc;
+ cardbus_devfunc_t ct = csc->sc_ct;
+ cardbus_chipset_tag_t cc = ct->ct_cc;
+ cardbus_function_tag_t cf = ct->ct_cf;
+
+ /* unhook the interrupt handler */
+ cardbus_intr_disestablish(cc, cf, csc->sc_ih);
+ csc->sc_ih = NULL;
+
+ /* power down the socket */
+ Cardbus_function_disable(ct);
+}
+
+void
+acx_cardbus_power(struct acx_softc *sc, int why)
+{
+ struct acx_cardbus_softc *csc = (struct acx_cardbus_softc *)sc;
+
+ if (why == PWR_RESUME) {
+ /* kick the PCI configuration registers */
+ acx_cardbus_setup(csc);
+ }
+}
+
+void
+acx_cardbus_setup(struct acx_cardbus_softc *csc)
+{
+ cardbus_devfunc_t ct = csc->sc_ct;
+ cardbus_chipset_tag_t cc = ct->ct_cc;
+ cardbus_function_tag_t cf = ct->ct_cf;
+ pcireg_t reg;
+ int b1 = CARDBUS_BASE0_REG, b2 = CARDBUS_BASE1_REG;
+
+ if (csc->sc_iobar_val) {
+ cardbus_conf_write(cc, cf, csc->sc_tag, CARDBUS_BASE0_REG,
+ csc->sc_bar1_val);
+ b1 = CARDBUS_BASE1_REG;
+ b2 = CARDBUS_BASE2_REG;
+ /* (*cf->cardbus_ctrl)(cc, CARDBUS_IO_ENABLE); */
+ }
+
+ /* program the BAR */
+ cardbus_conf_write(cc, cf, csc->sc_tag, b1, csc->sc_bar1_val);
+ cardbus_conf_write(cc, cf, csc->sc_tag, b2, csc->sc_bar2_val);
+
+ /* make sure the right access type is on the cardbus bridge */
+ (*cf->cardbus_ctrl)(cc, CARDBUS_MEM_ENABLE);
+ (*cf->cardbus_ctrl)(cc, CARDBUS_BM_ENABLE);
+
+ /* enable the appropriate bits in the PCI CSR */
+ reg = cardbus_conf_read(cc, cf, csc->sc_tag,
+ CARDBUS_COMMAND_STATUS_REG);
+ reg |= CARDBUS_COMMAND_MASTER_ENABLE | CARDBUS_COMMAND_MEM_ENABLE;
+#if 0
+ if (csc->sc_iobar_val)
+ reg |= CARDBUS_COMMAND_IO_ENABLE;
+#endif
+ cardbus_conf_write(cc, cf, csc->sc_tag, CARDBUS_COMMAND_STATUS_REG,
+ reg);
+}
diff --git a/sys/dev/ic/acx.c b/sys/dev/ic/acx.c
new file mode 100644
index 00000000000..bed32a7a571
--- /dev/null
+++ b/sys/dev/ic/acx.c
@@ -0,0 +1,3017 @@
+/* $Id: acx.c,v 1.1 2006/08/03 08:45:01 mglocker Exp $ */
+
+/*
+ * Copyright (c) 2006 The DragonFly Project. All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Sepherosa Ziehau <sepherosa@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $DragonFly$
+ */
+
+/*
+ * Copyright (c) 2003-2004 wlan.kewl.org Project
+ * All rights reserved.
+ *
+ * $Id: acx.c,v 1.1 2006/08/03 08:45:01 mglocker Exp $
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ *
+ * This product includes software developed by the wlan.kewl.org Project.
+ *
+ * 4. Neither the name of the wlan.kewl.org Project nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE wlan.kewl.org Project BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#include "bpfilter.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/proc.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+
+#include <machine/bus.h>
+#include <machine/endian.h>
+#include <machine/intr.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#endif
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/if_ether.h>
+#include <netinet/ip.h>
+#endif
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_radiotap.h>
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcidevs.h>
+
+#define ACX_DEBUG
+
+#include <dev/ic/acxvar.h>
+#include <dev/ic/acxreg.h>
+
+#ifdef ACX_DEBUG
+int acxdebug = 1;
+#endif
+
+
+#define ACX_ENABLE_TXCHAN(sc, chan) \
+do { \
+ if (acx_enable_txchan((sc), (chan)) != 0) { \
+ printf("enable TX on channel %d failed\n", (chan)); \
+ } \
+} while (0)
+
+#define ACX_ENABLE_RXCHAN(sc, chan) \
+do { \
+ if (acx_enable_rxchan((sc), (chan)) != 0) { \
+ printf("enable RX on channel %d failed\n", (chan)); \
+ } \
+} while (0)
+
+#if 0
+int acx_probe(device_t);
+#endif
+int acx_attach(struct acx_softc *);
+int acx_detach(void *);
+void acx_shutdown(void *);
+
+int acx_init(struct ifnet *);
+int acx_stop(struct acx_softc *);
+void acx_init_info_reg(struct acx_softc *);
+int acx_config(struct acx_softc *);
+int acx_read_config(struct acx_softc *, struct acx_config *);
+int acx_write_config(struct acx_softc *, struct acx_config *);
+int acx_set_crypt_keys(struct acx_softc *);
+#ifdef foo
+void acx_begin_scan(struct acx_softc *);
+#endif
+void acx_next_scan(void *);
+
+void acx_start(struct ifnet *);
+void acx_watchdog(struct ifnet *);
+
+int acx_ioctl(struct ifnet *, u_long, caddr_t);
+
+int acx_intr(void *);
+void acx_disable_intr(struct acx_softc *);
+void acx_enable_intr(struct acx_softc *);
+void acx_txeof(struct acx_softc *);
+void acx_txerr(struct acx_softc *, uint8_t);
+void acx_rxeof(struct acx_softc *);
+
+int acx_dma_alloc(struct acx_softc *);
+void acx_dma_free(struct acx_softc *);
+int acx_init_tx_ring(struct acx_softc *);
+int acx_init_rx_ring(struct acx_softc *);
+int acx_newbuf(struct acx_softc *, struct acx_rxbuf *, int);
+int acx_encap(struct acx_softc *, struct acx_txbuf *,
+ struct mbuf *, struct ieee80211_node *, int);
+
+int acx_reset(struct acx_softc *);
+
+int acx_set_null_tmplt(struct acx_softc *);
+int acx_set_probe_req_tmplt(struct acx_softc *, const char *, int);
+int acx_set_probe_resp_tmplt(struct acx_softc *, const char *, int,
+ int);
+int acx_set_beacon_tmplt(struct acx_softc *, const char *, int,
+ int);
+
+int acx_read_eeprom(struct acx_softc *, uint32_t, uint8_t *);
+int acx_read_phyreg(struct acx_softc *, uint32_t, uint8_t *);
+
+#if 0
+int acx_copyin_firmware(struct acx_softc *, struct ifreq *);
+#endif
+void acx_free_firmware(struct acx_softc *);
+int acx_load_firmware(struct acx_softc *, uint32_t,
+ const uint8_t *, int);
+int acx_load_radio_firmware(struct acx_softc *);
+int acx_load_base_firmware(struct acx_softc *);
+
+struct ieee80211_node *acx_node_alloc(struct ieee80211com *);
+void acx_node_init(struct acx_softc *, struct acx_node *);
+void acx_node_update(struct acx_softc *, struct acx_node *,
+ uint8_t, uint8_t);
+int acx_newstate(struct ieee80211com *, enum ieee80211_state, int);
+
+#if 0
+int acx_sysctl_txrate_upd_intvl_min(SYSCTL_HANDLER_ARGS);
+int acx_sysctl_txrate_upd_intvl_max(SYSCTL_HANDLER_ARGS);
+int acx_sysctl_txrate_sample_thresh(SYSCTL_HANDLER_ARGS);
+int acx_sysctl_long_retry_limit(SYSCTL_HANDLER_ARGS);
+int acx_sysctl_short_retry_limit(SYSCTL_HANDLER_ARGS);
+int acx_sysctl_msdu_lifetime(SYSCTL_HANDLER_ARGS);
+#endif
+
+void acx_init_cmd_reg(struct acx_softc *);
+int acx_join_bss(struct acx_softc *, uint8_t, struct ieee80211_node *);
+int acx_enable_txchan(struct acx_softc *, uint8_t);
+int acx_enable_rxchan(struct acx_softc *, uint8_t);
+int acx_init_radio(struct acx_softc *, uint32_t, uint32_t);
+
+const struct ieee80211_rateset acx_rates_11b =
+ { 4, { 2, 4, 11, 22 } };
+const struct ieee80211_rateset acx_rates_11g =
+ { 12, { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 } };
+
+static int acx_chanscan_rate = 5; /* 5/second */
+int acx_beacon_intvl = 100; /* 100 TU */
+
+/*
+ * Possible values for the second parameter of acx_join_bss()
+ */
+#define ACX_MODE_ADHOC 0
+#define ACX_MODE_UNUSED 1
+#define ACX_MODE_STA 2
+#define ACX_MODE_AP 3
+
+#if 0
+static const struct acx_device {
+ uint16_t vid;
+ uint16_t did;
+ void (*set_param)(struct acx_softc *);
+ const char *desc;
+} acx_devices[] = {
+ { PCI_VENDOR_TI, PCI_PRODUCT_TI_ACX100A, acx100_set_param,
+ "Texas Instruments TNETW1100A Wireless Adapter" },
+ { PCI_VENDOR_TI, PCI_PRODUCT_TI_ACX100B, acx100_set_param,
+ "Texas Instruments TNETW1100B Wireless Adapter" },
+ { PCI_VENDOR_TI, PCI_PRODUCT_TI_ACX111, acx111_set_param,
+ "Texas Instruments TNETW1130 Wireless Adapter" },
+ { 0, 0, NULL, NULL }
+};
+
+static device_method_t acx_methods[] = {
+ DEVMETHOD(device_probe, acx_probe),
+ DEVMETHOD(device_attach, acx_attach),
+ DEVMETHOD(device_detach, acx_detach),
+ DEVMETHOD(device_shutdown, acx_shutdown),
+ DEVMETHOD(device_suspend, acx_suspend),
+ DEVMETHOD(device_resume, acx_resume),
+ { 0, 0 }
+};
+#endif
+
+struct cfdriver acx_cd = {
+ NULL, "acx", DV_IFNET
+};
+
+#if 0
+static driver_t acx_driver = {
+ "acx",
+ acx_methods,
+ sizeof(struct acx_softc)
+};
+
+static devclass_t acx_devclass;
+#endif
+
+#if 0
+int
+acx_probe(device_t dev)
+{
+ const struct acx_device *a;
+ uint16_t did, vid;
+
+ vid = pci_get_vendor(dev);
+ did = pci_get_device(dev);
+ for (a = acx_devices; a->desc != NULL; ++a) {
+ if (vid == a->vid && did == a->did) {
+ a->set_param(dev);
+ device_set_desc(dev, a->desc);
+ return 0;
+ }
+ }
+ return ENXIO;
+}
+#endif
+
+int
+acx_attach(struct acx_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = &sc->sc_ic.ic_if;
+ int i, error;
+
+ //acx111_set_param(sc);
+ //acx100_set_param(sc);
+
+ bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ);
+ ifp->if_softc = sc;
+
+#if 0
+#ifndef BURN_BRIDGES
+ if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) {
+ uint32_t mem1, mem2, irq;
+
+ mem1 = pci_read_config(dev, sc->chip_mem1_rid, 4);
+ mem2 = pci_read_config(dev, sc->chip_mem2_rid, 4);
+ irq = pci_read_config(dev, PCIR_INTLINE, 4);
+
+ device_printf(dev, "chip is in D%d power mode "
+ "-- setting to D0\n", pci_get_powerstate(dev));
+
+ pci_set_powerstate(dev, PCI_POWERSTATE_D0);
+
+ pci_write_config(dev, sc->chip_mem1_rid, mem1, 4);
+ pci_write_config(dev, sc->chip_mem2_rid, mem2, 4);
+ pci_write_config(dev, PCIR_INTLINE, irq, 4);
+ }
+#endif /* !BURN_BRIDGE */
+#endif
+
+#if 0
+ /* Enable bus mastering */
+ pci_enable_busmaster(dev);
+
+ /* Allocate IO memory 1 */
+ sc->sc_mem1_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &sc->chip_mem1_rid,
+ RF_ACTIVE);
+ if (sc->sc_mem1_res == NULL) {
+ error = ENXIO;
+ device_printf(dev, "can't allocate IO mem1\n");
+ goto fail;
+ }
+ sc->sc_mem1_bt = rman_get_bustag(sc->sc_mem1_res);
+ sc->sc_mem1_bh = rman_get_bushandle(sc->sc_mem1_res);
+
+ /* Allocate IO memory 2 */
+ sc->sc_mem2_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &sc->chip_mem2_rid,
+ RF_ACTIVE);
+ if (sc->sc_mem2_res == NULL) {
+ error = ENXIO;
+ device_printf(dev, "can't allocate IO mem2\n");
+ goto fail;
+ }
+ sc->sc_mem2_bt = rman_get_bustag(sc->sc_mem2_res);
+ sc->sc_mem2_bh = rman_get_bushandle(sc->sc_mem2_res);
+
+ /* Allocate irq */
+ sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
+ &sc->sc_irq_rid,
+ RF_SHAREABLE | RF_ACTIVE);
+ if (sc->sc_irq_res == NULL) {
+ error = ENXIO;
+ device_printf(dev, "can't allocate intr\n");
+ goto fail;
+ }
+#endif
+
+ /* Initilize channel scanning timer */
+ timeout_set(&sc->sc_chanscan_timer, acx_next_scan, sc);
+
+ /* Allocate busdma stuffs */
+ error = acx_dma_alloc(sc);
+ if (error)
+ goto fail;
+
+ /* Reset Hardware */
+ error = acx_reset(sc);
+ if (error)
+ goto fail;
+
+ /* Disable interrupts before firmware is loaded */
+ acx_disable_intr(sc);
+
+ /* Get radio type and form factor */
+#define EEINFO_RETRY_MAX 50
+ for (i = 0; i < EEINFO_RETRY_MAX; ++i) {
+ uint16_t ee_info;
+
+ ee_info = CSR_READ_2(sc, ACXREG_EEPROM_INFO);
+ if (ACX_EEINFO_HAS_RADIO_TYPE(ee_info)) {
+ sc->sc_form_factor = ACX_EEINFO_FORM_FACTOR(ee_info);
+ sc->sc_radio_type = ACX_EEINFO_RADIO_TYPE(ee_info);
+ break;
+ }
+ DELAY(10000);
+ }
+ if (i == EEINFO_RETRY_MAX) {
+ error = ENXIO;
+ goto fail;
+ }
+#undef EEINFO_RETRY_MAX
+
+ DPRINTF(("%s: radio type %02x\n", sc->sc_dev.dv_xname,
+ sc->sc_radio_type));
+
+#ifdef DUMP_EEPROM
+ for (i = 0; i < 0x40; ++i) {
+ uint8_t val;
+
+ error = acx_read_eeprom(sc, i, &val);
+ if (i % 10 == 0)
+ printf("\n");
+ printf("%02x ", val);
+ }
+ printf("\n");
+#endif /* DUMP_EEPROM */
+
+ /* Get EEPROM version */
+ error = acx_read_eeprom(sc, ACX_EE_VERSION_OFS, &sc->sc_eeprom_ver);
+ if (error)
+ goto fail;
+ DPRINTF(("%s: EEPROM version %u\n", sc->sc_dev.dv_xname,
+ sc->sc_eeprom_ver));
+
+ ifp->if_softc = sc;
+ ifp->if_init = acx_init;
+ ifp->if_ioctl = acx_ioctl;
+ ifp->if_start = acx_start;
+ ifp->if_watchdog = acx_watchdog;
+ ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST;
+ strlcpy(ifp->if_xname, sc->sc_dev.dv_xname, IFNAMSIZ);
+
+ /* Set channels */
+ for (i = 1; i <= 14; ++i) {
+ ic->ic_channels[i].ic_freq =
+ ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ);
+ ic->ic_channels[i].ic_flags = sc->chip_chan_flags;
+ }
+
+ ic->ic_opmode = IEEE80211_M_STA;
+ ic->ic_state = IEEE80211_S_INIT;
+
+ /*
+ * NOTE: Don't overwrite ic_caps set by chip specific code
+ */
+ ic->ic_caps |= IEEE80211_C_WEP | /* WEP */
+ IEEE80211_C_IBSS | /* IBSS modes */
+ IEEE80211_C_SHPREAMBLE; /* Short preamble */
+
+ /* Get station id */
+ for (i = 0; i < IEEE80211_ADDR_LEN; ++i) {
+ error = acx_read_eeprom(sc, sc->chip_ee_eaddr_ofs - i,
+ &ic->ic_myaddr[i]);
+ }
+
+ printf("%s: address %s\n", sc->sc_dev.dv_xname,
+ ether_sprintf(ic->ic_myaddr));
+
+ if_attach(ifp);
+ ieee80211_ifattach(ifp);
+
+ /* Override node alloc */
+ ic->ic_node_alloc = acx_node_alloc;
+
+ /* Override newstate */
+ sc->sc_newstate = ic->ic_newstate;
+ ic->ic_newstate = acx_newstate;
+
+ ieee80211_media_init(ifp, ieee80211_media_change,
+ ieee80211_media_status);
+
+ sc->sc_txrate_upd_intvl_min = 10; /* 10 seconds */
+ sc->sc_txrate_upd_intvl_max = 300; /* 5 minutes */
+ sc->sc_txrate_sample_thresh = 30; /* 30 packets */
+ sc->sc_long_retry_limit = 4;
+ sc->sc_short_retry_limit = 7;
+ sc->sc_msdu_lifetime = 4096;
+
+#if 0
+ sysctl_ctx_init(&sc->sc_sysctl_ctx);
+ sc->sc_sysctl_tree = SYSCTL_ADD_NODE(&sc->sc_sysctl_ctx,
+ SYSCTL_STATIC_CHILDREN(_hw),
+ OID_AUTO,
+ device_get_nameunit(dev),
+ CTLFLAG_RD, 0, "");
+ if (sc->sc_sysctl_tree == NULL) {
+ device_printf(dev, "can't add sysctl node\n");
+ error = ENXIO;
+ goto fail1;
+ }
+
+ SYSCTL_ADD_PROC(&sc->sc_sysctl_ctx,
+ SYSCTL_CHILDREN(sc->sc_sysctl_tree),
+ OID_AUTO, "txrate_upd_intvl_min",
+ CTLTYPE_INT | CTLFLAG_RW,
+ sc, 0, acx_sysctl_txrate_upd_intvl_min, "I",
+ "min seconds to wait before raising TX rate");
+ SYSCTL_ADD_PROC(&sc->sc_sysctl_ctx,
+ SYSCTL_CHILDREN(sc->sc_sysctl_tree),
+ OID_AUTO, "txrate_upd_intvl_max",
+ CTLTYPE_INT | CTLFLAG_RW,
+ sc, 0, acx_sysctl_txrate_upd_intvl_max, "I",
+ "max seconds to wait before raising TX rate");
+ SYSCTL_ADD_PROC(&sc->sc_sysctl_ctx,
+ SYSCTL_CHILDREN(sc->sc_sysctl_tree),
+ OID_AUTO, "txrate_sample_threshold",
+ CTLTYPE_INT | CTLFLAG_RW,
+ sc, 0, acx_sysctl_txrate_sample_thresh, "I",
+ "number of packets to be sampled "
+ "before raising TX rate");
+
+ SYSCTL_ADD_PROC(&sc->sc_sysctl_ctx,
+ SYSCTL_CHILDREN(sc->sc_sysctl_tree),
+ OID_AUTO, "long_retry_limit",
+ CTLTYPE_INT | CTLFLAG_RW,
+ sc, 0, acx_sysctl_long_retry_limit, "I",
+ "max number of retries for RTS packets");
+ SYSCTL_ADD_PROC(&sc->sc_sysctl_ctx,
+ SYSCTL_CHILDREN(sc->sc_sysctl_tree),
+ OID_AUTO, "short_retry_limit",
+ CTLTYPE_INT | CTLFLAG_RW,
+ sc, 0, acx_sysctl_short_retry_limit, "I",
+ "max number of retries for non-RTS packets");
+
+ SYSCTL_ADD_PROC(&sc->sc_sysctl_ctx,
+ SYSCTL_CHILDREN(sc->sc_sysctl_tree),
+ OID_AUTO, "msdu_lifetime",
+ CTLTYPE_INT | CTLFLAG_RW,
+ sc, 0, acx_sysctl_msdu_lifetime, "I",
+ "MSDU life time");
+#endif
+
+
+#if 0
+ error = bus_setup_intr(dev, sc->sc_irq_res, INTR_MPSAFE, acx_intr, sc,
+ &sc->sc_irq_handle, ifp->if_serializer);
+ if (error) {
+ device_printf(dev, "can't set up interrupt\n");
+ goto fail1;
+ }
+
+ if (bootverbose)
+ ieee80211_announce(ic);
+#endif
+
+ return 0;
+//fail1:
+ ieee80211_ifdetach(ifp);
+fail:
+ return error;
+}
+
+int
+acx_detach(void *xsc)
+{
+ struct acx_softc *sc = xsc;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = &ic->ic_if;
+
+ acx_stop(sc);
+ acx_free_firmware(sc);
+
+ ieee80211_ifdetach(ifp);
+
+ acx_dma_free(sc);
+ return 0;
+}
+
+void
+acx_shutdown(void *arg)
+{
+ struct acx_softc *sc = arg;
+
+ acx_stop(sc);
+}
+
+int
+acx_init(struct ifnet *ifp)
+{
+ struct acx_softc *sc = ifp->if_softc;
+ int error;
+
+ printf("A\n");
+
+ error = acx_stop(sc);
+ if (error)
+ return EIO;
+
+ /* enable card if possible */
+ if (sc->sc_enable != NULL)
+ (*sc->sc_enable)(sc);
+
+ printf("B");
+
+ error = acx_init_tx_ring(sc);
+ if (error) {
+ printf("%s: can't initialize TX ring\n",
+ sc->sc_dev.dv_xname);
+ goto back;
+ }
+
+ printf("C");
+
+ error = acx_init_rx_ring(sc);
+ if (error) {
+ printf("%s: can't initialize RX ring\n",
+ sc->sc_dev.dv_xname);
+ goto back;
+ }
+
+ printf("D");
+
+ error = acx_load_base_firmware(sc);
+ if (error)
+ goto back;
+
+ printf("E\n");
+
+ /*
+ * Initialize command and information registers
+ * NOTE: This should be done after base firmware is loaded
+ */
+ acx_init_cmd_reg(sc);
+ acx_init_info_reg(sc);
+
+ printf("F");
+
+ sc->sc_flags |= ACX_FLAG_FW_LOADED;
+
+#if 0
+ if (sc->chip_post_basefw != NULL) {
+ error = sc->chip_post_basefw(sc);
+ if (error)
+ goto back;
+ }
+#endif
+
+ printf("G");
+
+ /* XXX decide whether firmware is combined */
+ error = acx_load_radio_firmware(sc);
+ if (error)
+ goto back;
+
+ error = sc->chip_init(sc);
+ if (error)
+ goto back;
+
+ printf("H");
+
+ /* Get and set device various configuration */
+ error = acx_config(sc);
+ if (error)
+ goto back;
+
+ printf("I\n");
+
+ /* Setup crypto stuffs */
+ if (sc->sc_ic.ic_flags & IEEE80211_F_WEPON) {
+ error = acx_set_crypt_keys(sc);
+ if (error)
+ goto back;
+// sc->sc_ic.ic_flags &= ~IEEE80211_F_DROPUNENC;
+ }
+
+ printf("J");
+
+ /* Turn on power led */
+ CSR_CLRB_2(sc, ACXREG_GPIO_OUT, sc->chip_gpio_pled);
+
+ acx_enable_intr(sc);
+
+ ifp->if_flags |= IFF_RUNNING;
+ ifp->if_flags &= ~IFF_OACTIVE;
+
+ /* Begin background scanning */
+#ifdef foo
+ acx_begin_scan(sc);
+#else
+ ieee80211_new_state(&sc->sc_ic, IEEE80211_S_SCAN, -1);
+#endif
+
+ printf("L\n");
+
+back:
+ if (error)
+ acx_stop(sc);
+ return (0);
+}
+
+void
+acx_init_info_reg(struct acx_softc *sc)
+{
+ sc->sc_info = CSR_READ_4(sc, ACXREG_INFO_REG_OFFSET);
+ sc->sc_info_param = sc->sc_info + ACX_INFO_REG_SIZE;
+}
+
+int
+acx_set_crypt_keys(struct acx_softc *sc)
+{
+#if 0
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct acx_conf_wep_txkey wep_txkey;
+ int i, error, got_wk = 0;
+
+ for (i = 0; i < IEEE80211_WEP_NKID; ++i) {
+ struct ieee80211_key *wk = &ic->ic_nw_keys[i];
+
+ if (wk->wk_keylen == 0)
+ continue;
+
+ if (sc->chip_hw_crypt) {
+ error = sc->chip_set_wepkey(sc, wk, i);
+ if (error)
+ return error;
+ got_wk = 1;
+ } else if (wk->wk_flags & IEEE80211_KEY_XMIT) {
+ wk->wk_flags |= IEEE80211_KEY_SWCRYPT;
+ }
+ }
+
+ if (!got_wk || sc->chip_hw_crypt ||
+ ic->ic_def_txkey == IEEE80211_KEYIX_NONE)
+ return 0;
+
+ /* Set current WEP key index */
+ wep_txkey.wep_txkey = ic->ic_def_txkey;
+ if (acx_set_wep_txkey_conf(sc, &wep_txkey) != 0) {
+ printf("%s: set WEP txkey failed\n",
+ sc->sc_dev.dv_xname);
+ return ENXIO;
+ }
+ return 0;
+#endif
+ return ENXIO;
+}
+
+#ifdef foo
+void
+acx_begin_scan(struct acx_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ uint8_t chan;
+
+ ieee80211_begin_scan(ic, 1);
+
+ chan = ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan);
+
+ ACX_ENABLE_TXCHAN(sc, chan);
+ ACX_ENABLE_RXCHAN(sc, chan);
+
+ /* Start background scanning */
+ timeout_add(&sc->sc_chanscan_timer, hz / acx_chanscan_rate);
+}
+#endif
+
+void
+acx_next_scan(void *arg)
+{
+ struct acx_softc *sc = arg;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = &ic->ic_if;
+
+ if (ic->ic_state == IEEE80211_S_SCAN) {
+#if 0
+ uint8_t chan;
+#endif
+
+ ieee80211_next_scan(ifp);
+
+#if 0
+ chan = ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan);
+
+ ACX_ENABLE_TXCHAN(sc, chan);
+ ACX_ENABLE_RXCHAN(sc, chan);
+
+ callout_reset(&sc->sc_chanscan_timer, hz / acx_chanscan_rate,
+ acx_next_scan, sc);
+#endif
+ }
+}
+
+int
+acx_stop(struct acx_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = &ic->ic_if;
+ struct acx_buf_data *bd = &sc->sc_buf_data;
+ struct acx_ring_data *rd = &sc->sc_ring_data;
+ int i, error;
+
+ sc->sc_firmware_ver = 0;
+ sc->sc_hardware_id = 0;
+
+ /* Reset hardware */
+ error = acx_reset(sc);
+ if (error)
+ return error;
+
+ /* Firmware no longer functions after hardware reset */
+ sc->sc_flags &= ~ACX_FLAG_FW_LOADED;
+
+ acx_disable_intr(sc);
+
+ /* Stop backgroud scanning */
+ timeout_del(&sc->sc_chanscan_timer);
+
+ /* Turn off power led */
+ CSR_SETB_2(sc, ACXREG_GPIO_OUT, sc->chip_gpio_pled);
+
+ /* Free TX mbuf */
+ for (i = 0; i < ACX_TX_DESC_CNT; ++i) {
+ struct acx_txbuf *buf;
+ struct ieee80211_node *ni;
+
+ buf = &bd->tx_buf[i];
+
+ if (buf->tb_mbuf != NULL) {
+ bus_dmamap_unload(sc->sc_dmat,
+ buf->tb_mbuf_dmamap);
+ m_freem(buf->tb_mbuf);
+ buf->tb_mbuf = NULL;
+ }
+
+ ni = (struct ieee80211_node *)buf->tb_node;
+ if (ni != NULL)
+ ieee80211_release_node(ic, ni);
+ buf->tb_node = NULL;
+ }
+
+ /* Clear TX host descriptors */
+ bzero(rd->tx_ring, ACX_TX_RING_SIZE);
+
+ /* Free RX mbuf */
+ for (i = 0; i < ACX_RX_DESC_CNT; ++i) {
+ if (bd->rx_buf[i].rb_mbuf != NULL) {
+ bus_dmamap_unload(sc->sc_dmat,
+ bd->rx_buf[i].rb_mbuf_dmamap);
+ m_freem(bd->rx_buf[i].rb_mbuf);
+ bd->rx_buf[i].rb_mbuf = NULL;
+ }
+ }
+
+ /* Clear RX host descriptors */
+ bzero(rd->rx_ring, ACX_RX_RING_SIZE);
+
+ ifp->if_timer = 0;
+ ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
+ ieee80211_new_state(&sc->sc_ic, IEEE80211_S_INIT, -1);
+
+ /* disable card if possible */
+ if (sc->sc_disable != NULL)
+ (*sc->sc_disable)(sc);
+
+ return 0;
+}
+
+int
+acx_config(struct acx_softc *sc)
+{
+ struct acx_config conf;
+ int error;
+
+ error = acx_read_config(sc, &conf);
+ if (error)
+ return error;
+
+ error = acx_write_config(sc, &conf);
+ if (error)
+ return error;
+
+ if (acx_set_probe_req_tmplt(sc, "", 0) != 0) {
+ printf("%s: can't set probe req template "
+ "(empty ssid)\n", sc->sc_dev.dv_xname);
+ return ENXIO;
+ }
+
+ /* XXX for PM?? */
+ if (acx_set_null_tmplt(sc) != 0) {
+ printf("%s: can't set null data template\n",
+ sc->sc_dev.dv_xname);
+ return ENXIO;
+ }
+ return 0;
+}
+
+int
+acx_read_config(struct acx_softc *sc, struct acx_config *conf)
+{
+ struct acx_conf_eaddr addr;
+ struct acx_conf_regdom reg_dom;
+ struct acx_conf_antenna ant;
+ struct acx_conf_fwrev fw_rev;
+ uint32_t fw_rev_no;
+ uint8_t sen;
+ int i, error;
+
+ /* Get station id */
+ if (acx_get_eaddr_conf(sc, &addr) != 0) {
+ printf("%s: can't get station id\n", sc->sc_dev.dv_xname);
+ return ENXIO;
+ }
+
+ /*
+ * Get and print station id in case that EEPROM station id's
+ * offset is not correct
+ */
+ for (i = 0; i < IEEE80211_ADDR_LEN; ++i)
+ conf->eaddr[IEEE80211_ADDR_LEN - 1 - i] = addr.eaddr[i];
+ printf("%s: MAC address (from firmware): %s\n",
+ sc->sc_dev.dv_xname, ether_sprintf(conf->eaddr));
+
+ /* Get region domain */
+ if (acx_get_regdom_conf(sc, &reg_dom) != 0) {
+ printf("%s: can't get region domain\n", sc->sc_dev.dv_xname);
+ return ENXIO;
+ }
+ conf->regdom = reg_dom.regdom;
+ DPRINTF(("%s: regdom %02x\n", sc->sc_dev.dv_xname, reg_dom.regdom));
+
+ /* Get antenna */
+ if (acx_get_antenna_conf(sc, &ant) != 0) {
+ printf("%s: can't get antenna\n", sc->sc_dev.dv_xname);
+ return ENXIO;
+ }
+ conf->antenna = ant.antenna;
+ DPRINTF(("%s: antenna %02x\n", sc->sc_dev.dv_xname, ant.antenna));
+
+ /* Get sensitivity XXX not used */
+ if (sc->sc_radio_type == ACX_RADIO_TYPE_MAXIM ||
+ sc->sc_radio_type == ACX_RADIO_TYPE_RFMD ||
+ sc->sc_radio_type == ACX_RADIO_TYPE_RALINK) {
+ error = acx_read_phyreg(sc, ACXRV_PHYREG_SENSITIVITY, &sen);
+ if (error) {
+ printf("%s: can't get sensitivity\n",
+ sc->sc_dev.dv_xname);
+ return error;
+ }
+ } else {
+ sen = 0;
+ }
+ DPRINTF(("%s: sensitivity %02x\n", sc->sc_dev.dv_xname, sen));
+
+ /* Get firmware revision */
+ if (acx_get_fwrev_conf(sc, &fw_rev) != 0) {
+ printf("%s: can't get firmware revision\n",
+ sc->sc_dev.dv_xname);
+ return ENXIO;
+ }
+
+ if (strncmp(fw_rev.fw_rev, "Rev ", 4) != 0) {
+ printf("%s: strange revision string -- %s\n",
+ sc->sc_dev.dv_xname, fw_rev.fw_rev);
+ fw_rev_no = 0x01090407;
+ } else {
+ /*
+ * 01234
+ * "Rev xx.xx.xx.xx"
+ * ^ Start from here
+ */
+ fw_rev_no = fw_rev.fw_rev[0] << 24;
+ fw_rev_no |= fw_rev.fw_rev[1] << 16;
+ fw_rev_no |= fw_rev.fw_rev[2] << 8;
+ fw_rev_no |= fw_rev.fw_rev[3];
+ }
+ sc->sc_firmware_ver = fw_rev_no;
+ sc->sc_hardware_id = letoh32(fw_rev.hw_id);
+ DPRINTF(("%s: fw rev %08x, hw id %08x\n",
+ sc->sc_dev.dv_xname, sc->sc_firmware_ver, sc->sc_hardware_id));
+
+ if (sc->chip_read_config != NULL) {
+ error = sc->chip_read_config(sc, conf);
+ if (error)
+ return error;
+ }
+ return 0;
+}
+
+int
+acx_write_config(struct acx_softc *sc, struct acx_config *conf)
+{
+ struct acx_conf_nretry_short sretry;
+ struct acx_conf_nretry_long lretry;
+ struct acx_conf_msdu_lifetime msdu_lifetime;
+ struct acx_conf_rate_fallback rate_fb;
+ struct acx_conf_antenna ant;
+ struct acx_conf_regdom reg_dom;
+ struct acx_conf_rxopt rx_opt;
+ struct ifnet *ifp = &sc->sc_ic.ic_if;
+ int error;
+
+ /* Set number of long/short retry */
+ sretry.nretry = sc->sc_short_retry_limit;
+ if (acx_set_nretry_short_conf(sc, &sretry) != 0) {
+ printf("%s: can't set short retry limit\n", ifp->if_xname);
+ return ENXIO;
+ }
+
+ lretry.nretry = sc->sc_long_retry_limit;
+ if (acx_set_nretry_long_conf(sc, &lretry) != 0) {
+ printf("%s: can't set long retry limit\n", ifp->if_xname);
+ return ENXIO;
+ }
+
+ /* Set MSDU lifetime */
+ msdu_lifetime.lifetime = htole32(sc->sc_msdu_lifetime);
+ if (acx_set_msdu_lifetime_conf(sc, &msdu_lifetime) != 0) {
+ printf("%s: can't set MSDU lifetime\n", ifp->if_xname);
+ return ENXIO;
+ }
+
+ /* Enable rate fallback */
+ rate_fb.ratefb_enable = 1;
+ if (acx_set_rate_fallback_conf(sc, &rate_fb) != 0) {
+ printf("%s: can't enable rate fallback\n", ifp->if_xname);
+ return ENXIO;
+ }
+
+ /* Set antenna */
+ ant.antenna = conf->antenna;
+ if (acx_set_antenna_conf(sc, &ant) != 0) {
+ printf("%s: can't set antenna\n", ifp->if_xname);
+ return ENXIO;
+ }
+
+ /* Set region domain */
+ reg_dom.regdom = conf->regdom;
+ if (acx_set_regdom_conf(sc, &reg_dom) != 0) {
+ printf("%s: can't set region domain\n", ifp->if_xname);
+ return ENXIO;
+ }
+
+ if (sc->chip_write_config != NULL) {
+ error = sc->chip_write_config(sc, conf);
+ if (error)
+ return error;
+ }
+
+ /* What we want to receive and how to receive */
+ /* XXX may not belong here, acx_init() */
+ rx_opt.opt1 = RXOPT1_FILT_FDEST | RXOPT1_INCL_RXBUF_HDR;
+ rx_opt.opt2 = RXOPT2_RECV_ASSOC_REQ |
+ RXOPT2_RECV_AUTH |
+ RXOPT2_RECV_BEACON |
+ RXOPT2_RECV_CF |
+ RXOPT2_RECV_CTRL |
+ RXOPT2_RECV_DATA |
+ RXOPT2_RECV_MGMT |
+ RXOPT2_RECV_PROBE_REQ |
+ RXOPT2_RECV_PROBE_RESP |
+ RXOPT2_RECV_OTHER;
+ if (acx_set_rxopt_conf(sc, &rx_opt) != 0) {
+ printf("%s: can't set RX option\n", ifp->if_xname);
+ return ENXIO;
+ }
+ return 0;
+}
+
+int
+acx_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ struct acx_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) == 0)
+ acx_init(ifp);
+ } else {
+ if (ifp->if_flags & IFF_RUNNING)
+ acx_stop(sc);
+ }
+ 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;
+ default:
+ error = ieee80211_ioctl(ifp, cmd, data);
+ break;
+ }
+
+ if (error == ENETRESET) {
+ if ((ifp->if_flags & (IFF_RUNNING | IFF_UP)) ==
+ (IFF_RUNNING | IFF_UP))
+ acx_init(ifp);
+ error = 0;
+ }
+
+ splx(s);
+
+ return error;
+}
+
+void
+acx_start(struct ifnet *ifp)
+{
+ struct acx_softc *sc = ifp->if_softc;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct acx_buf_data *bd = &sc->sc_buf_data;
+ struct acx_txbuf *buf;
+ int trans, idx;
+
+ if ((sc->sc_flags & ACX_FLAG_FW_LOADED) == 0 ||
+ (ifp->if_flags & IFF_RUNNING) == 0 ||
+ (ifp->if_flags & IFF_OACTIVE))
+ return;
+
+ /*
+ * NOTE:
+ * We can't start from a random position that TX descriptor
+ * is free, since hardware will be confused by that.
+ * We have to follow the order of the TX ring.
+ */
+ idx = bd->tx_free_start;
+ trans = 0;
+ for (buf = &bd->tx_buf[idx]; buf->tb_mbuf == NULL;
+ buf = &bd->tx_buf[idx]) {
+ struct ieee80211_frame *f;
+ struct ieee80211_node *ni = NULL;
+ struct mbuf *m;
+ int rate;
+
+ IF_DEQUEUE(&ic->ic_mgtq, m);
+ if (m != NULL) {
+
+ ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
+ m->m_pkthdr.rcvif = NULL;
+
+#if 0
+ /*
+ * Since mgmt data are transmitted at fixed rate
+ * they will not be used to do rate control.
+ */
+ if (ni != NULL)
+ ieee80211_free_node(ni);
+#endif
+ rate = 4; /* XXX 2Mb/s for mgmt packet */
+ } else if (!IFQ_IS_EMPTY(&ifp->if_snd)) {
+ struct ether_header *eh;
+ struct acx_node *node;
+
+ if (ic->ic_state != IEEE80211_S_RUN) {
+ printf("%s: data packet dropped due to "
+ "not RUN. Current state %d\n",
+ ifp->if_xname, ic->ic_state);
+ break;
+ }
+
+ IFQ_DEQUEUE(&ifp->if_snd, m);
+ if (m == NULL)
+ break;
+
+ if (m->m_len < sizeof(struct ether_header)) {
+ m = m_pullup(m, sizeof(struct ether_header));
+ if (m == NULL) {
+ ifp->if_oerrors++;
+ continue;
+ }
+ }
+ eh = mtod(m, struct ether_header *);
+
+ ni = ieee80211_find_txnode(ic, eh->ether_dhost);
+ if (ni == NULL) {
+ m_freem(m);
+ ifp->if_oerrors++;
+ continue;
+ }
+
+ /* TODO power save */
+
+ m = ieee80211_encap(ifp, m, &ni);
+ if (m == NULL) {
+ ieee80211_release_node(ic, ni);
+ ifp->if_oerrors++;
+ continue;
+ }
+
+ node = (struct acx_node *)ni;
+ if (node->nd_txrate < 0) {
+ acx_node_init(sc, node);
+#if 0
+ if (ic->ic_opmode == IEEE80211_M_IBSS) {
+ /* XXX
+ * Add extra reference here,
+ * so that some node (bss_dup)
+ * will not be freed just after
+ * they are allocated, which
+ * make TX rate control impossible
+ */
+ ieee80211_ref_node(ni);
+ }
+#endif
+ }
+
+ rate = node->nd_rates.rs_rates[node->nd_txrate];
+
+ bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
+ } else {
+ break;
+ }
+
+ f = mtod(m, struct ieee80211_frame *);
+ if ((f->i_fc[1] & IEEE80211_FC1_WEP) && !sc->chip_hw_crypt) {
+ m = ieee80211_wep_crypt(ifp, m, 1);
+ if (m == NULL) {
+ ieee80211_release_node(ic, ni);
+ m_freem(m);
+ ifp->if_oerrors++;
+ continue;
+ }
+ }
+
+#if NBPFILTER > 0
+ if (ic->ic_rawbpf != NULL)
+ bpf_mtap(ic->ic_rawbpf, m, BPF_DIRECTION_OUT);
+#endif
+
+ if (acx_encap(sc, buf, m, ni, rate) != 0) {
+ /*
+ * NOTE: `m' will be freed in acx_encap()
+ * if we reach here.
+ */
+ if (ni != NULL)
+ ieee80211_release_node(ic, ni);
+ ifp->if_oerrors++;
+ continue;
+ }
+
+ /*
+ * NOTE:
+ * 1) `m' should not be touched after acx_encap()
+ * 2) `node' will be used to do TX rate control during
+ * acx_txeof(), so it is not freed here. acx_txeof()
+ * will free it for us
+ */
+
+ trans = 1;
+ bd->tx_used_count++;
+ idx = (idx + 1) % ACX_TX_DESC_CNT;
+ }
+ bd->tx_free_start = idx;
+
+ if (bd->tx_used_count == ACX_TX_DESC_CNT)
+ ifp->if_flags |= IFF_OACTIVE;
+
+ if (trans && ifp->if_timer == 0)
+ ifp->if_timer = 5;
+}
+
+void
+acx_watchdog(struct ifnet *ifp)
+{
+ printf("%s: watchdog timeout\n", ifp->if_xname);
+ acx_txeof(ifp->if_softc);
+ /* TODO */
+}
+
+int
+acx_intr(void *arg)
+{
+ struct acx_softc *sc = arg;
+ uint16_t intr_status;
+
+ if ((sc->sc_flags & ACX_FLAG_FW_LOADED) == 0)
+ return 0;
+
+ intr_status = CSR_READ_2(sc, ACXREG_INTR_STATUS_CLR);
+ if (intr_status == ACXRV_INTR_ALL) {
+ /* not our interrupt */
+ return 0;
+ }
+
+ intr_status &= sc->chip_intr_enable;
+ if (intr_status == 0) {
+ /* not interrupts we care about */
+ return 1;
+ }
+
+ /* Acknowledge all interrupts */
+ CSR_WRITE_2(sc, ACXREG_INTR_ACK, ACXRV_INTR_ALL);
+
+ if (intr_status & ACXRV_INTR_TX_FINI)
+ acx_txeof(sc);
+
+ if (intr_status & ACXRV_INTR_RX_FINI)
+ acx_rxeof(sc);
+
+ return 1;
+}
+
+void
+acx_disable_intr(struct acx_softc *sc)
+{
+ CSR_WRITE_2(sc, ACXREG_INTR_MASK, sc->chip_intr_disable);
+ CSR_WRITE_2(sc, ACXREG_EVENT_MASK, 0);
+}
+
+void
+acx_enable_intr(struct acx_softc *sc)
+{
+ /* Mask out interrupts that are not in the enable set */
+ CSR_WRITE_2(sc, ACXREG_INTR_MASK, ~sc->chip_intr_enable);
+ CSR_WRITE_2(sc, ACXREG_EVENT_MASK, ACXRV_EVENT_DISABLE);
+}
+
+void
+acx_txeof(struct acx_softc *sc)
+{
+ struct acx_buf_data *bd;
+ struct acx_txbuf *buf;
+ struct ifnet *ifp;
+ int idx;
+
+ ifp = &sc->sc_ic.ic_if;
+
+ bd = &sc->sc_buf_data;
+ idx = bd->tx_used_start;
+ for (buf = &bd->tx_buf[idx]; buf->tb_mbuf != NULL;
+ buf = &bd->tx_buf[idx]) {
+ uint8_t ctrl, error;
+
+ ctrl = FW_TXDESC_GETFIELD_1(sc, buf, f_tx_ctrl);
+ if ((ctrl & (DESC_CTRL_HOSTOWN | DESC_CTRL_ACXDONE)) !=
+ (DESC_CTRL_HOSTOWN | DESC_CTRL_ACXDONE))
+ break;
+
+ bus_dmamap_unload(sc->sc_dmat, buf->tb_mbuf_dmamap);
+ m_freem(buf->tb_mbuf);
+ buf->tb_mbuf = NULL;
+
+ error = FW_TXDESC_GETFIELD_1(sc, buf, f_tx_error);
+ if (error) {
+ acx_txerr(sc, error);
+ ifp->if_oerrors++;
+ } else {
+ ifp->if_opackets++;
+ }
+
+ if (buf->tb_node != NULL) {
+ struct ieee80211com *ic;
+ struct ieee80211_node *ni;
+
+ ic = &sc->sc_ic;
+ ni = (struct ieee80211_node *)buf->tb_node;
+
+ acx_node_update(sc, buf->tb_node, buf->tb_rate, error);
+ ieee80211_release_node(ic, ni);
+ buf->tb_node = NULL;
+ }
+
+ FW_TXDESC_SETFIELD_1(sc, buf, f_tx_ctrl, DESC_CTRL_HOSTOWN);
+
+ bd->tx_used_count--;
+
+ idx = (idx + 1) % ACX_TX_DESC_CNT;
+ }
+ bd->tx_used_start = idx;
+
+ ifp->if_timer = bd->tx_used_count == 0 ? 0 : 5;
+
+ if (bd->tx_used_count != ACX_TX_DESC_CNT) {
+ ifp->if_flags &= ~IFF_OACTIVE;
+ acx_start(ifp);
+ }
+}
+
+void
+acx_txerr(struct acx_softc *sc, uint8_t err)
+{
+ struct ifnet *ifp = &sc->sc_ic.ic_if;
+ struct acx_stats *stats = &sc->sc_stats;
+
+ if (err == DESC_ERR_EXCESSIVE_RETRY) {
+ /*
+ * This a common error (see comment below),
+ * so print it using DPRINTF()
+ */
+ DPRINTF(("%s: TX failed -- excessive retry\n",
+ sc->sc_dev.dv_xname));
+ } else {
+ printf("%s: TX failed -- ", ifp->if_xname);
+ }
+
+ /*
+ * Although `err' looks like bitmask, it never
+ * has multiple bits set.
+ */
+ switch (err) {
+#if 0
+ case DESC_ERR_OTHER_FRAG:
+ /* XXX what's this */
+ printf("error in other fragment\n");
+ stats->err_oth_frag++;
+ break;
+#endif
+ case DESC_ERR_ABORT:
+ printf("aborted\n");
+ stats->err_abort++;
+ break;
+ case DESC_ERR_PARAM:
+ printf("wrong paramters in descriptor\n");
+ stats->err_param++;
+ break;
+ case DESC_ERR_NO_WEPKEY:
+ printf("WEP key missing\n");
+ stats->err_no_wepkey++;
+ break;
+ case DESC_ERR_MSDU_TIMEOUT:
+ printf("MSDU life timeout\n");
+ stats->err_msdu_timeout++;
+ break;
+ case DESC_ERR_EXCESSIVE_RETRY:
+ /*
+ * Possible causes:
+ * 1) Distance is too long
+ * 2) Transmit failed (e.g. no MAC level ACK)
+ * 3) Chip overheated (this should be rare)
+ */
+ stats->err_ex_retry++;
+ break;
+ case DESC_ERR_BUF_OVERFLOW:
+ printf("buffer overflow\n");
+ stats->err_buf_oflow++;
+ break;
+ case DESC_ERR_DMA:
+ printf("DMA error\n");
+ stats->err_dma++;
+ break;
+ default:
+ printf("unknown error %d\n", err);
+ stats->err_unkn++;
+ break;
+ }
+}
+
+void
+acx_rxeof(struct acx_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct acx_ring_data *rd = &sc->sc_ring_data;
+ struct acx_buf_data *bd = &sc->sc_buf_data;
+ struct ifnet *ifp = &ic->ic_if;
+ int idx, ready;
+
+ bus_dmamap_sync(sc->sc_dmat, rd->rx_ring_dmamap, 0,
+ rd->rx_ring_dmamap->dm_mapsize, BUS_DMASYNC_POSTREAD);
+
+ /*
+ * Locate first "ready" rx buffer,
+ * start from last stopped position
+ */
+ idx = bd->rx_scan_start;
+ ready = 0;
+ do {
+ struct acx_rxbuf *buf;
+
+ buf = &bd->rx_buf[idx];
+ if ((buf->rb_desc->h_ctrl & htole16(DESC_CTRL_HOSTOWN)) &&
+ (buf->rb_desc->h_status & htole32(DESC_STATUS_FULL))) {
+ ready = 1;
+ break;
+ }
+ idx = (idx + 1) % ACX_RX_DESC_CNT;
+ } while (idx != bd->rx_scan_start);
+
+ if (!ready)
+ return;
+
+ /*
+ * NOTE: don't mess up `idx' here, it will
+ * be used in the following code
+ */
+
+ do {
+ struct acx_rxbuf_hdr *head;
+ struct acx_rxbuf *buf;
+ struct mbuf *m;
+ uint32_t desc_status;
+ uint16_t desc_ctrl;
+ int len, error;
+
+ buf = &bd->rx_buf[idx];
+
+ desc_ctrl = letoh16(buf->rb_desc->h_ctrl);
+ desc_status = letoh32(buf->rb_desc->h_status);
+ if (!(desc_ctrl & DESC_CTRL_HOSTOWN) ||
+ !(desc_status & DESC_STATUS_FULL))
+ break;
+
+ bus_dmamap_sync(sc->sc_dmat, buf->rb_mbuf_dmamap, 0,
+ buf->rb_mbuf_dmamap->dm_mapsize, BUS_DMASYNC_POSTREAD);
+
+ m = buf->rb_mbuf;
+
+ error = acx_newbuf(sc, buf, 0);
+ if (error) {
+ ifp->if_ierrors++;
+ goto next;
+ }
+
+ head = mtod(m, struct acx_rxbuf_hdr *);
+
+ len = letoh16(head->rbh_len) & ACX_RXBUF_LEN_MASK;
+ if (len >= sizeof(struct ieee80211_frame_min) &&
+ len < MCLBYTES) {
+ struct ieee80211_frame *f;
+ struct ieee80211_node *ni;
+
+ m_adj(m, sizeof(struct acx_rxbuf_hdr) +
+ sc->chip_rxbuf_exhdr);
+ f = mtod(m, struct ieee80211_frame *);
+
+ if ((f->i_fc[1] & IEEE80211_FC1_WEP) &&
+ sc->chip_hw_crypt) {
+ /* Short circuit software WEP */
+ f->i_fc[1] &= ~IEEE80211_FC1_WEP;
+
+ /* Do chip specific RX buffer processing */
+ if (sc->chip_proc_wep_rxbuf != NULL) {
+ sc->chip_proc_wep_rxbuf(sc, m, &len);
+ f = mtod(m, struct ieee80211_frame *);
+ }
+ }
+
+ m->m_len = m->m_pkthdr.len = len;
+ m->m_pkthdr.rcvif = &ic->ic_if;
+
+ ni = ieee80211_find_rxnode(ic, f);
+
+ ieee80211_input(ifp, m, ni, head->rbh_level,
+ letoh32(head->rbh_time));
+
+ ieee80211_release_node(ic, ni);
+ ifp->if_ipackets++;
+ } else {
+ m_freem(m);
+ ifp->if_ierrors++;
+ }
+
+next:
+ buf->rb_desc->h_ctrl = htole16(desc_ctrl & ~DESC_CTRL_HOSTOWN);
+ buf->rb_desc->h_status = 0;
+ bus_dmamap_sync(sc->sc_dmat, rd->rx_ring_dmamap, 0,
+ rd->rx_ring_dmamap->dm_mapsize, BUS_DMASYNC_PREWRITE);
+
+ idx = (idx + 1) % ACX_RX_DESC_CNT;
+ } while (idx != bd->rx_scan_start);
+
+ /*
+ * Record the position so that next
+ * time we can start from it
+ */
+ bd->rx_scan_start = idx;
+}
+
+int
+acx_reset(struct acx_softc *sc)
+{
+ uint16_t reg;
+
+ /* Halt ECPU */
+ CSR_SETB_2(sc, ACXREG_ECPU_CTRL, ACXRV_ECPU_HALT);
+
+ /* Software reset */
+ reg = CSR_READ_2(sc, ACXREG_SOFT_RESET);
+ CSR_WRITE_2(sc, ACXREG_SOFT_RESET, reg | ACXRV_SOFT_RESET);
+ DELAY(100);
+ CSR_WRITE_2(sc, ACXREG_SOFT_RESET, reg);
+
+ /* Initialize EEPROM */
+ CSR_SETB_2(sc, ACXREG_EEPROM_INIT, ACXRV_EEPROM_INIT);
+ DELAY(50000);
+
+ /* Test whether ECPU is stopped */
+ reg = CSR_READ_2(sc, ACXREG_ECPU_CTRL);
+ if (!(reg & ACXRV_ECPU_HALT)) {
+ printf("%s: can't halt ECPU\n", sc->sc_dev.dv_xname);
+ return ENXIO;
+ }
+ return 0;
+}
+
+int
+acx_read_eeprom(struct acx_softc *sc, uint32_t offset, uint8_t *val)
+{
+ int i;
+ struct ifnet *ifp = &sc->sc_ic.ic_if;
+
+ CSR_WRITE_4(sc, ACXREG_EEPROM_CONF, 0);
+ CSR_WRITE_4(sc, ACXREG_EEPROM_ADDR, offset);
+ CSR_WRITE_4(sc, ACXREG_EEPROM_CTRL, ACXRV_EEPROM_READ);
+
+#define EE_READ_RETRY_MAX 100
+ for (i = 0; i < EE_READ_RETRY_MAX; ++i) {
+ if (CSR_READ_2(sc, ACXREG_EEPROM_CTRL) == 0)
+ break;
+ DELAY(10000);
+ }
+ if (i == EE_READ_RETRY_MAX) {
+ printf("%s: can't read EEPROM offset %x (timeout)\n",
+ ifp->if_xname, offset);
+ return (ETIMEDOUT);
+ }
+#undef EE_READ_RETRY_MAX
+
+ *val = CSR_READ_1(sc, ACXREG_EEPROM_DATA);
+
+ return (0);
+}
+
+int
+acx_read_phyreg(struct acx_softc *sc, uint32_t reg, uint8_t *val)
+{
+ struct ifnet *ifp = &sc->sc_ic.ic_if;
+ int i;
+
+ CSR_WRITE_4(sc, ACXREG_PHY_ADDR, reg);
+ CSR_WRITE_4(sc, ACXREG_PHY_CTRL, ACXRV_PHY_READ);
+
+#define PHY_READ_RETRY_MAX 100
+ for (i = 0; i < PHY_READ_RETRY_MAX; ++i) {
+ if (CSR_READ_4(sc, ACXREG_PHY_CTRL) == 0)
+ break;
+ DELAY(10000);
+ }
+ if (i == PHY_READ_RETRY_MAX) {
+ printf("%s: can't read phy reg %x (timeout)\n",
+ ifp->if_xname, reg);
+ return ETIMEDOUT;
+ }
+#undef PHY_READ_RETRY_MAX
+
+ *val = CSR_READ_1(sc, ACXREG_PHY_DATA);
+ return 0;
+}
+
+void
+acx_write_phyreg(struct acx_softc *sc, uint32_t reg, uint8_t val)
+{
+ CSR_WRITE_4(sc, ACXREG_PHY_DATA, val);
+ CSR_WRITE_4(sc, ACXREG_PHY_ADDR, reg);
+ CSR_WRITE_4(sc, ACXREG_PHY_CTRL, ACXRV_PHY_WRITE);
+}
+
+#if 0
+int
+acx_copyin_firmware(struct acx_softc *sc, struct ifreq *req)
+{
+ struct acx_firmware ufw, *kfw;
+ uint8_t *base_fw, *radio_fw;
+ int error;
+
+ kfw = &sc->sc_firmware;
+ base_fw = NULL;
+ radio_fw = NULL;
+
+ error = copyin(req->ifr_data, &ufw, sizeof(ufw));
+ if (error)
+ return error;
+
+ /*
+ * For combined base firmware, there is no radio firmware.
+ * But base firmware must exist.
+ */
+ if (ufw.base_fw_len <= 0 || ufw.radio_fw_len < 0)
+ return EINVAL;
+
+ base_fw = malloc(ufw.base_fw_len, M_DEVBUF, M_INTWAIT);
+ error = copyin(ufw.base_fw, base_fw, ufw.base_fw_len);
+ if (error)
+ goto fail;
+
+ if (ufw.radio_fw_len > 0) {
+ radio_fw = malloc(ufw.radio_fw_len, M_DEVBUF, M_INTWAIT);
+ error = copyin(ufw.radio_fw, radio_fw, ufw.radio_fw_len);
+ if (error)
+ goto fail;
+ }
+
+ kfw->base_fw_len = ufw.base_fw_len;
+ if (kfw->base_fw != NULL)
+ free(kfw->base_fw, M_DEVBUF);
+ kfw->base_fw = base_fw;
+
+ kfw->radio_fw_len = ufw.radio_fw_len;
+ if (kfw->radio_fw != NULL)
+ free(kfw->radio_fw, M_DEVBUF);
+ kfw->radio_fw = radio_fw;
+
+ return 0;
+fail:
+ if (base_fw != NULL)
+ free(base_fw, M_DEVBUF);
+ if (radio_fw != NULL)
+ free(radio_fw, M_DEVBUF);
+ return error;
+}
+#endif
+
+void
+acx_free_firmware(struct acx_softc *sc)
+{
+ struct acx_firmware *fw = &sc->sc_firmware;
+
+ if (fw->base_fw != NULL) {
+ free(fw->base_fw, M_DEVBUF);
+ fw->base_fw = NULL;
+ fw->base_fw_len = 0;
+ }
+ if (fw->radio_fw != NULL) {
+ free(fw->radio_fw, M_DEVBUF);
+ fw->radio_fw = NULL;
+ fw->radio_fw_len = 0;
+ }
+}
+
+int
+acx_load_base_firmware(struct acx_softc *sc)
+{
+ struct ifnet *ifp = &sc->sc_ic.ic_if;
+ int i, error;
+ uint8_t *ucode;
+ const char name[]= "acx-100_base";
+ size_t size;
+
+ error = loadfirmware(name, &ucode, &size);
+
+ if (error != 0) {
+ printf("%s: error %d, could not read microcode %s!\n",
+ ifp->if_xname, error, name);
+ return EIO;
+ }
+
+ /* Load base firmware */
+ error = acx_load_firmware(sc, 0, ucode, size);
+
+ free(ucode, M_DEVBUF);
+
+ if (error) {
+ printf("%s: can't load base firmware\n", ifp->if_xname);
+ return error;
+ }
+ DPRINTF(("%s: base firmware loaded\n", sc->sc_dev.dv_xname));
+
+ /* Start ECPU */
+ CSR_WRITE_2(sc, ACXREG_ECPU_CTRL, ACXRV_ECPU_START);
+
+ /* Wait for ECPU to be up */
+ for (i = 0; i < 500; ++i) {
+ uint16_t reg;
+
+ reg = CSR_READ_2(sc, ACXREG_INTR_STATUS);
+ if (reg & ACXRV_INTR_FCS_THRESH) {
+ CSR_WRITE_2(sc, ACXREG_INTR_ACK, ACXRV_INTR_FCS_THRESH);
+ return 0;
+ }
+ DELAY(10000);
+ }
+
+ printf("%s: can't initialize ECPU (timeout)\n", ifp->if_xname);
+ return ENXIO;
+}
+
+int
+acx_load_radio_firmware(struct acx_softc *sc)
+{
+ struct ifnet *ifp = &sc->sc_ic.ic_if;
+ struct acx_conf_mmap mem_map;
+ uint32_t radio_fw_ofs;
+ int error;
+ uint8_t *ucode;
+ const char name[] = "acx-100_radio_0d";
+ size_t size;
+
+ error = loadfirmware(name, &ucode, &size);
+
+ if (error != 0) {
+ printf("%s: error %d, could not read microcode %s!\n",
+ ifp->if_xname, error, name);
+ return (EIO);
+ }
+
+ /*
+ * Get the position, where base firmware is loaded, so that
+ * radio firmware can be loaded after it.
+ */
+ if (acx_get_mmap_conf(sc, &mem_map) != 0)
+ return (ENXIO);
+ radio_fw_ofs = letoh32(mem_map.code_end);
+
+ /* Put ECPU into sleeping state, before loading radio firmware */
+ if (acx_sleep(sc) != 0)
+ return (ENXIO);
+
+ /* Load radio firmware */
+ error = acx_load_firmware(sc, radio_fw_ofs, ucode, size);
+ if (error) {
+ printf("%s: can't load radio firmware\n", ifp->if_xname);
+ return (ENXIO);
+ }
+ DPRINTF(("%s: radio firmware loaded\n", sc->sc_dev.dv_xname));
+
+ /* Wake up sleeping ECPU, after radio firmware is loaded */
+ if (acx_wakeup(sc) != 0)
+ return (ENXIO);
+
+ /* Initialize radio */
+ if (acx_init_radio(sc, radio_fw_ofs, size) != 0)
+ return (ENXIO);
+
+ /* Verify radio firmware's loading position */
+ if (acx_get_mmap_conf(sc, &mem_map) != 0)
+ return (ENXIO);
+
+ if (letoh32(mem_map.code_end) != radio_fw_ofs + size) {
+ printf("%s: loaded radio firmware position mismatch\n",
+ ifp->if_xname);
+ return ENXIO;
+ }
+
+ DPRINTF(("%s: radio firmware initialized\n", sc->sc_dev.dv_xname));
+
+ return (0);
+}
+
+int
+acx_load_firmware(struct acx_softc *sc, uint32_t offset, const uint8_t *data,
+ int data_len)
+{
+ struct ifnet *ifp = &sc->sc_ic.ic_if;
+ const uint32_t *fw;
+ u_int32_t csum = 0;
+ int i, fw_len;
+
+ for (i = 4; i < data_len; i++) {
+ csum += data[i];
+ }
+
+ fw = (const uint32_t *)data;
+
+ if (*fw != csum) {
+ printf("%s: firmware checksum 0x%x does not match 0x%x!\n",
+ ifp->if_xname, fw, csum);
+ return ENXIO;
+ }
+
+ /* skip csum + length */
+ data += 8;
+ data_len -= 8;
+
+ fw = (const uint32_t *)data;
+ fw_len = data_len / sizeof(uint32_t);
+
+ /*
+ * LOADFW_AUTO_INC only works with some older firmware:
+ * 1) acx100's firmware
+ * 2) acx111's firmware whose rev is 0x00010011
+ */
+
+ /* Load firmware */
+ CSR_WRITE_4(sc, ACXREG_FWMEM_START, ACXRV_FWMEM_START_OP);
+#ifndef LOADFW_AUTO_INC
+ CSR_WRITE_4(sc, ACXREG_FWMEM_CTRL, 0);
+#else
+ CSR_WRITE_4(sc, ACXREG_FWMEM_CTRL, ACXRV_FWMEM_ADDR_AUTOINC);
+ CSR_WRITE_4(sc, ACXREG_FWMEM_ADDR, offset);
+#endif
+
+ for (i = 0; i < fw_len; ++i) {
+#ifndef LOADFW_AUTO_INC
+ CSR_WRITE_4(sc, ACXREG_FWMEM_ADDR, offset + (i * 4));
+#endif
+ CSR_WRITE_4(sc, ACXREG_FWMEM_DATA, betoh32(fw[i]));
+ }
+
+ /* Verify firmware */
+ CSR_WRITE_4(sc, ACXREG_FWMEM_START, ACXRV_FWMEM_START_OP);
+#ifndef LOADFW_AUTO_INC
+ CSR_WRITE_4(sc, ACXREG_FWMEM_CTRL, 0);
+#else
+ CSR_WRITE_4(sc, ACXREG_FWMEM_CTRL, ACXRV_FWMEM_ADDR_AUTOINC);
+ CSR_WRITE_4(sc, ACXREG_FWMEM_ADDR, offset);
+#endif
+
+ for (i = 0; i < fw_len; ++i) {
+ uint32_t val;
+
+#ifndef LOADFW_AUTO_INC
+ CSR_WRITE_4(sc, ACXREG_FWMEM_ADDR, offset + (i * 4));
+#endif
+ val = CSR_READ_4(sc, ACXREG_FWMEM_DATA);
+ if (betoh32(fw[i]) != val) {
+ printf("%s: firmware mismatch fw %08x loaded %08x\n",
+ ifp->if_xname, fw[i], val);
+ return ENXIO;
+ }
+ }
+ return 0;
+}
+
+struct ieee80211_node *
+acx_node_alloc(struct ieee80211com *ic)
+{
+ struct acx_node *node;
+
+ node = malloc(sizeof(struct acx_node), M_DEVBUF, M_NOWAIT);
+ if (node == NULL)
+ return NULL;
+
+ bzero(node, (sizeof(struct acx_node)));
+ node->nd_txrate = -1;
+ return (struct ieee80211_node *)node;
+}
+
+void
+acx_node_init(struct acx_softc *sc, struct acx_node *node)
+{
+ struct ieee80211_rateset *nd_rset, *ic_rset, *cp_rset;
+ struct ieee80211com *ic;
+ int i, j, c;
+
+ ic = &sc->sc_ic;
+
+ nd_rset = &node->nd_node.ni_rates;
+ ic_rset = &ic->ic_sup_rates[sc->chip_phymode];
+ cp_rset = &node->nd_rates;
+ c = 0;
+
+#define IEEERATE(rate) ((rate) & IEEE80211_RATE_VAL)
+ for (i = 0; i < nd_rset->rs_nrates; ++i) {
+ uint8_t nd_rate = IEEERATE(nd_rset->rs_rates[i]);
+
+ for (j = 0; j < ic_rset->rs_nrates; ++j) {
+ if (nd_rate == IEEERATE(ic_rset->rs_rates[j])) {
+ cp_rset->rs_rates[c++] = nd_rate;
+ if (node->nd_txrate < 0) {
+ /* XXX slow start?? */
+ node->nd_txrate = 0;
+ node->nd_node.ni_txrate = i;
+ }
+ break;
+ }
+ }
+ }
+ if (node->nd_node.ni_txrate < 0)
+ panic("no compat rates");
+ DPRINTF(("%s: node rate %d\n",
+ sc->sc_dev.dv_xname,
+ IEEERATE(nd_rset->rs_rates[node->nd_node.ni_txrate])));
+#undef IEEERATE
+
+ cp_rset->rs_nrates = c;
+
+ node->nd_txrate_upd_intvl = sc->sc_txrate_upd_intvl_min;
+ node->nd_txrate_upd_time = time_second;
+ node->nd_txrate_sample = 0;
+}
+
+void
+acx_node_update(struct acx_softc *sc, struct acx_node *node, uint8_t rate,
+ uint8_t error)
+{
+ struct ieee80211_rateset *nd_rset, *cp_rset;
+ int i, time_diff;
+
+ nd_rset = &node->nd_node.ni_rates;
+ cp_rset = &node->nd_rates;
+
+ time_diff = time_second - node->nd_txrate_upd_time;
+
+ if (error == DESC_ERR_MSDU_TIMEOUT ||
+ error == DESC_ERR_EXCESSIVE_RETRY) {
+ uint8_t cur_rate;
+
+ /* Reset packet sample counter */
+ node->nd_txrate_sample = 0;
+
+ if (rate > cp_rset->rs_rates[node->nd_txrate]) {
+ /*
+ * This rate has already caused toubles,
+ * so don't count it in here
+ */
+ return;
+ }
+
+ /* Double TX rate updating interval */
+ node->nd_txrate_upd_intvl *= 2;
+ if (node->nd_txrate_upd_intvl <=
+ sc->sc_txrate_upd_intvl_min) {
+ node->nd_txrate_upd_intvl =
+ sc->sc_txrate_upd_intvl_min;
+ } else if (node->nd_txrate_upd_intvl >
+ sc->sc_txrate_upd_intvl_max) {
+ node->nd_txrate_upd_intvl =
+ sc->sc_txrate_upd_intvl_max;
+ }
+
+ if (node->nd_txrate == 0)
+ return;
+
+ node->nd_txrate_upd_time += time_diff;
+
+ /* TX rate down */
+ node->nd_txrate--;
+ cur_rate = cp_rset->rs_rates[node->nd_txrate + 1];
+ while (cp_rset->rs_rates[node->nd_txrate] > cur_rate) {
+ if (node->nd_txrate - 1 > 0)
+ node->nd_txrate--;
+ else
+ break;
+ }
+ DPRINTF(("%s: rate down %6D %d -> %d\n",
+ sc->sc_dev.dv_xname,
+ node->nd_node.ni_macaddr, ":",
+ cp_rset->rs_rates[node->nd_txrate + 1],
+ cp_rset->rs_rates[node->nd_txrate]));
+ } else if (node->nd_txrate + 1 < node->nd_rates.rs_nrates) {
+ uint8_t cur_rate;
+
+ node->nd_txrate_sample++;
+
+ if (node->nd_txrate_sample <= sc->sc_txrate_sample_thresh ||
+ time_diff <= node->nd_txrate_upd_intvl)
+ return;
+
+ /* Reset packet sample counter */
+ node->nd_txrate_sample = 0;
+
+ /* Half TX rate updating interval */
+ node->nd_txrate_upd_intvl /= 2;
+ if (node->nd_txrate_upd_intvl <
+ sc->sc_txrate_upd_intvl_min) {
+ node->nd_txrate_upd_intvl =
+ sc->sc_txrate_upd_intvl_min;
+ } else if (node->nd_txrate_upd_intvl >
+ sc->sc_txrate_upd_intvl_max) {
+ node->nd_txrate_upd_intvl =
+ sc->sc_txrate_upd_intvl_max;
+ }
+
+ node->nd_txrate_upd_time += time_diff;
+
+ /* TX Rate up */
+ node->nd_txrate++;
+ cur_rate = cp_rset->rs_rates[node->nd_txrate - 1];
+ while (cp_rset->rs_rates[node->nd_txrate] < cur_rate) {
+ if (node->nd_txrate + 1 < cp_rset->rs_nrates)
+ node->nd_txrate++;
+ else
+ break;
+ }
+ DPRINTF(("%s: rate up %6D %d -> %d\n",
+ sc->sc_dev.dv_xname,
+ node->nd_node.ni_macaddr, ":",
+ cur_rate, cp_rset->rs_rates[node->nd_txrate]));
+ } else {
+ return;
+ }
+
+#define IEEERATE(rate) ((rate) & IEEE80211_RATE_VAL)
+ /* XXX Update ieee80211_node's TX rate index */
+ for (i = 0; i < nd_rset->rs_nrates; ++i) {
+ if (IEEERATE(nd_rset->rs_rates[i]) ==
+ cp_rset->rs_rates[node->nd_txrate]) {
+ node->nd_node.ni_txrate = i;
+ break;
+ }
+ }
+#undef IEEERATE
+}
+
+int
+acx_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
+{
+ struct acx_softc *sc = ic->ic_if.if_softc;
+ struct ifnet *ifp = &ic->ic_if;
+ int error = 0;
+
+ switch (nstate) {
+ case IEEE80211_S_SCAN:
+ if (ic->ic_state != IEEE80211_S_INIT) {
+ uint8_t chan;
+
+ chan = ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan);
+ ACX_ENABLE_TXCHAN(sc, chan);
+ ACX_ENABLE_RXCHAN(sc, chan);
+
+ timeout_add(&sc->sc_chanscan_timer,
+ hz / acx_chanscan_rate);
+ }
+ break;
+ case IEEE80211_S_AUTH:
+ if (ic->ic_opmode == IEEE80211_M_STA) {
+ struct ieee80211_node *ni;
+#ifdef ACX_DEBUG
+ int i;
+#endif
+
+ ni = ic->ic_bss;
+
+ if (acx_join_bss(sc, ACX_MODE_STA, ni) != 0) {
+ printf("%s: join BSS failed\n", ifp->if_xname);
+ error = 1;
+ goto back;
+ }
+
+ DPRINTF(("%s: join BSS\n", sc->sc_dev.dv_xname));
+ if (ic->ic_state == IEEE80211_S_ASSOC) {
+ DPRINTF(("%s: change from assoc to run\n",
+ sc->sc_dev.dv_xname));
+ ic->ic_state = IEEE80211_S_RUN;
+ }
+
+#ifdef ACX_DEBUG
+ printf("%s: AP rates: ", sc->sc_dev.dv_xname);
+ for (i = 0; i < ni->ni_rates.rs_nrates; ++i)
+ printf("%d ", ni->ni_rates.rs_rates[i]);
+ ieee80211_print_essid(ni->ni_essid, ni->ni_esslen);
+ printf(" %6D\n", ni->ni_bssid, ":");
+#endif
+ }
+ break;
+ case IEEE80211_S_RUN:
+ if (ic->ic_opmode == IEEE80211_M_IBSS) {
+ struct ieee80211_node *ni;
+ uint8_t chan;
+
+ ni = ic->ic_bss;
+ chan = ieee80211_chan2ieee(ic, ni->ni_chan);
+
+ error = 1;
+
+ if (acx_enable_txchan(sc, chan) != 0) {
+ printf("%s: enable TX on channel %d failed\n",
+ ifp->if_xname, chan);
+ goto back;
+ }
+
+ if (acx_enable_rxchan(sc, chan) != 0) {
+ printf("%s: enable RX on channel %d failed\n",
+ ifp->if_xname, chan);
+ goto back;
+ }
+
+ if (acx_set_beacon_tmplt(sc, ni->ni_essid,
+ ni->ni_esslen, chan) != 0) {
+ printf("%s: set bescon template failed\n",
+ ifp->if_xname);
+ goto back;
+ }
+
+ if (acx_set_probe_resp_tmplt(sc, ni->ni_essid,
+ ni->ni_esslen,
+ chan) != 0) {
+ printf("%s: set probe response template "
+ "failed\n", ifp->if_xname);
+ goto back;
+ }
+
+ if (acx_join_bss(sc, ACX_MODE_ADHOC, ni) != 0) {
+ printf("%s: join IBSS failed\n", ifp->if_xname);
+ goto back;
+ }
+
+ DPRINTF(("%s: join IBSS\n", sc->sc_dev.dv_xname));
+ error = 0;
+ }
+ break;
+ default:
+ break;
+ }
+
+back:
+ if (error) {
+ /* XXX */
+ nstate = IEEE80211_S_INIT;
+ arg = -1;
+ }
+ return sc->sc_newstate(ic, nstate, arg);
+}
+
+int
+acx_init_tmplt_ordered(struct acx_softc *sc)
+{
+ /*
+ * NOTE:
+ * Order of templates initialization:
+ * 1) Probe request
+ * 2) NULL data
+ * 3) Beacon
+ * 4) TIM
+ * 5) Probe response
+ * Above order is critical to get a correct memory map.
+ */
+ if (acx_init_probe_req_tmplt(sc) != 0)
+ return 1;
+
+ if (acx_init_null_data_tmplt(sc) != 0)
+ return 1;
+
+ if (acx_init_beacon_tmplt(sc) != 0)
+ return 1;
+
+ if (acx_init_tim_tmplt(sc) != 0)
+ return 1;
+
+ if (acx_init_probe_resp_tmplt(sc) != 0)
+ return 1;
+
+
+#undef CALL_SET_TMPLT
+ return 0;
+}
+
+#if 0
+void
+acx_ring_dma_addr(void *arg, bus_dma_segment_t *seg, int nseg, int error)
+{
+ *((uint32_t *)arg) = seg->ds_addr;
+}
+#endif
+
+int
+acx_dma_alloc(struct acx_softc *sc)
+{
+ struct acx_ring_data *rd = &sc->sc_ring_data;
+ struct acx_buf_data *bd = &sc->sc_buf_data;
+ struct ifnet *ifp = &sc->sc_ic.ic_if;
+ int i, error, nsegs;
+
+ /* Allocate DMA stuffs for RX descriptors */
+ error = bus_dmamap_create(sc->sc_dmat, ACX_RX_RING_SIZE, 1,
+ ACX_RX_RING_SIZE, 0, BUS_DMA_NOWAIT, &rd->rx_ring_dmamap);
+
+ if (error) {
+ printf("%s: can't create rx ring dma tag\n",
+ sc->sc_dev.dv_xname);
+ return error;
+ }
+
+ error = bus_dmamem_alloc(sc->sc_dmat, ACX_RX_RING_SIZE, PAGE_SIZE,
+ 0, &rd->rx_ring_seg, 1, &nsegs, BUS_DMA_NOWAIT);
+
+ if (error != 0) {
+ printf("%s: can't allocate rx ring dma memory\n",
+ sc->sc_dev.dv_xname);
+ return error;
+ }
+
+ error = bus_dmamem_map(sc->sc_dmat, &rd->rx_ring_seg, nsegs,
+ ACX_RX_RING_SIZE, (caddr_t *)&rd->rx_ring,
+ BUS_DMA_NOWAIT);
+
+ if (error != 0) {
+ printf("%s: could not map rx desc DMA memory\n",
+ sc->sc_dev.dv_xname);
+ return error;
+ }
+
+ error = bus_dmamap_load(sc->sc_dmat, rd->rx_ring_dmamap,
+ rd->rx_ring, ACX_RX_RING_SIZE,
+ NULL, BUS_DMA_WAITOK);
+ if (error) {
+ printf("%s: can't get rx ring dma address\n",
+ sc->sc_dev.dv_xname);
+ bus_dmamem_free(sc->sc_dmat, &rd->rx_ring_seg, 1);
+ return error;
+ }
+
+ /* Allocate DMA stuffs for TX descriptors */
+ error = bus_dmamap_create(sc->sc_dmat, ACX_TX_RING_SIZE, 1,
+ ACX_TX_RING_SIZE, 0, BUS_DMA_NOWAIT, &rd->tx_ring_dmamap);
+
+ if (error) {
+ printf("%s: can't create tx ring dma tag\n", ifp->if_xname);
+ return error;
+ }
+
+
+ error = bus_dmamem_alloc(sc->sc_dmat, ACX_TX_RING_SIZE, PAGE_SIZE,
+ 0, &rd->tx_ring_seg, 1, &nsegs, BUS_DMA_NOWAIT);
+
+ if (error) {
+ printf("%s: can't allocate tx ring dma memory\n", ifp->if_xname);
+ return error;
+ }
+
+ error = bus_dmamem_map(sc->sc_dmat, &rd->tx_ring_seg, nsegs,
+ ACX_TX_RING_SIZE, (caddr_t *)&rd->tx_ring,
+ BUS_DMA_NOWAIT);
+
+ if (error != 0) {
+ printf("%s: could not map tx desc DMA memory\n",
+ sc->sc_dev.dv_xname);
+ return error;
+ }
+
+ error = bus_dmamap_load(sc->sc_dmat, rd->tx_ring_dmamap,
+ rd->tx_ring, ACX_TX_RING_SIZE,
+ NULL, BUS_DMA_WAITOK);
+ if (error) {
+ printf("%s: can't get tx ring dma address\n", ifp->if_xname);
+ bus_dmamem_free(sc->sc_dmat, &rd->tx_ring_seg, 1);
+ return error;
+ }
+
+ /* Create a spare RX DMA map */
+ error = bus_dmamap_create(sc->sc_dmat, MCLBYTES, 1, MCLBYTES,
+ 0, 0, &bd->mbuf_tmp_dmamap);
+ if (error) {
+ printf("%s: can't create tmp mbuf dma map\n", ifp->if_xname);
+ return error;
+ }
+
+ /* Create DMA map for RX mbufs */
+ for (i = 0; i < ACX_RX_DESC_CNT; ++i) {
+ error = bus_dmamap_create(sc->sc_dmat, MCLBYTES, 1,
+ MCLBYTES, 0, 0, &bd->rx_buf[i].rb_mbuf_dmamap);
+ if (error) {
+ printf("%s: can't create rx mbuf dma map (%d)\n",
+ ifp->if_xname, i);
+ return error;
+ }
+ bd->rx_buf[i].rb_desc = &rd->rx_ring[i];
+ }
+
+ /* Create DMA map for TX mbufs */
+ for (i = 0; i < ACX_TX_DESC_CNT; ++i) {
+ error = bus_dmamap_create(sc->sc_dmat, MCLBYTES, 1,
+ MCLBYTES, 0, 0, &bd->tx_buf[i].tb_mbuf_dmamap);
+ if (error) {
+ printf("%s: can't create tx mbuf dma map (%d)\n",
+ ifp->if_xname, i);
+ return error;
+ }
+ bd->tx_buf[i].tb_desc1 = &rd->tx_ring[i * 2];
+ bd->tx_buf[i].tb_desc2 = &rd->tx_ring[(i * 2) + 1];
+ }
+
+ return 0;
+}
+
+void
+acx_dma_free(struct acx_softc *sc)
+{
+ struct acx_ring_data *rd = &sc->sc_ring_data;
+ struct acx_buf_data *bd = &sc->sc_buf_data;
+ int i;
+
+ if (rd->rx_ring != NULL) {
+ bus_dmamap_unload(sc->sc_dmat, rd->rx_ring_dmamap);
+ bus_dmamem_free(sc->sc_dmat, &rd->rx_ring_seg, 1);
+ }
+
+ if (rd->tx_ring != NULL) {
+ bus_dmamap_unload(sc->sc_dmat, rd->tx_ring_dmamap);
+ bus_dmamem_free(sc->sc_dmat, &rd->tx_ring_seg, 1);
+ }
+
+ for (i = 0; i < ACX_RX_DESC_CNT; ++i) {
+ if (bd->rx_buf[i].rb_desc != NULL) {
+ if (bd->rx_buf[i].rb_mbuf != NULL) {
+ bus_dmamap_unload(sc->sc_dmat,
+ bd->rx_buf[i].rb_mbuf_dmamap);
+ m_freem(bd->rx_buf[i].rb_mbuf);
+ }
+ bus_dmamap_destroy(sc->sc_dmat,
+ bd->rx_buf[i].rb_mbuf_dmamap);
+ }
+ }
+
+ for (i = 0; i < ACX_TX_DESC_CNT; ++i) {
+ if (bd->tx_buf[i].tb_desc1 != NULL) {
+ if (bd->tx_buf[i].tb_mbuf != NULL) {
+ bus_dmamap_unload(sc->sc_dmat,
+ bd->tx_buf[i].tb_mbuf_dmamap);
+ m_freem(bd->tx_buf[i].tb_mbuf);
+ }
+ bus_dmamap_destroy(sc->sc_dmat,
+ bd->tx_buf[i].tb_mbuf_dmamap);
+ }
+ }
+
+ if (bd->mbuf_tmp_dmamap != NULL) {
+ bus_dmamap_destroy(sc->sc_dmat, bd->mbuf_tmp_dmamap);
+ }
+}
+
+int
+acx_init_tx_ring(struct acx_softc *sc)
+{
+ struct acx_ring_data *rd;
+ struct acx_buf_data *bd;
+ uint32_t paddr;
+ int i;
+
+ rd = &sc->sc_ring_data;
+ paddr = rd->tx_ring_paddr;
+ for (i = 0; i < (ACX_TX_DESC_CNT * 2) - 1; ++i) {
+ paddr += sizeof(struct acx_host_desc);
+
+ rd->tx_ring[i].h_ctrl = htole16(DESC_CTRL_HOSTOWN);
+
+ if (i == (ACX_TX_DESC_CNT * 2) - 1)
+ rd->tx_ring[i].h_next_desc = htole32(rd->tx_ring_paddr);
+ else
+ rd->tx_ring[i].h_next_desc = htole32(paddr);
+ }
+
+ bus_dmamap_sync(sc->sc_dmat, rd->tx_ring_dmamap, 0,
+ rd->tx_ring_dmamap->dm_mapsize, BUS_DMASYNC_PREWRITE);
+
+ bd = &sc->sc_buf_data;
+ bd->tx_free_start = 0;
+ bd->tx_used_start = 0;
+ bd->tx_used_count = 0;
+
+ return 0;
+}
+
+int
+acx_init_rx_ring(struct acx_softc *sc)
+{
+ struct acx_ring_data *rd;
+ struct acx_buf_data *bd;
+ uint32_t paddr;
+ int i;
+
+ bd = &sc->sc_buf_data;
+ rd = &sc->sc_ring_data;
+ paddr = rd->rx_ring_paddr;
+
+ for (i = 0; i < ACX_RX_DESC_CNT; ++i) {
+ int error;
+
+ paddr += sizeof(struct acx_host_desc);
+
+ error = acx_newbuf(sc, &bd->rx_buf[i], 1);
+ if (error)
+ return error;
+
+ if (i == ACX_RX_DESC_CNT - 1)
+ rd->rx_ring[i].h_next_desc = htole32(rd->rx_ring_paddr);
+ else
+ rd->rx_ring[i].h_next_desc = htole32(paddr);
+ }
+
+ bus_dmamap_sync(sc->sc_dmat, rd->rx_ring_dmamap, 0,
+ rd->rx_ring_dmamap->dm_mapsize, BUS_DMASYNC_PREWRITE);
+
+ bd->rx_scan_start = 0;
+ return 0;
+}
+
+#if 0
+void
+acx_buf_dma_addr(void *arg, bus_dma_segment_t *seg, int nseg,
+ bus_size_t mapsz, int error)
+{
+ if (error)
+ return;
+
+ /* XXX */
+ if (nseg != 1)
+ panic("too many RX DMA segments\n");
+ *((uint32_t *)arg) = seg->ds_addr;
+}
+#endif
+
+int
+acx_newbuf(struct acx_softc *sc, struct acx_rxbuf *rb, int wait)
+{
+ struct acx_buf_data *bd;
+ struct mbuf *m;
+ bus_dmamap_t map;
+ uint32_t paddr;
+ int error;
+
+ bd = &sc->sc_buf_data;
+
+ MGETHDR(m, wait ? M_WAITOK : M_DONTWAIT, MT_DATA);
+ if (m == NULL)
+ return (ENOBUFS);
+
+ MCLGET(m, wait ? M_WAITOK : M_DONTWAIT);
+ if (!(m->m_flags & M_EXT)) {
+ m_freem(m);
+ return (ENOBUFS);
+ }
+
+ m->m_len = m->m_pkthdr.len = MCLBYTES;
+
+ error = bus_dmamap_load_mbuf(sc->sc_dmat, bd->mbuf_tmp_dmamap,
+ m,
+ wait ? BUS_DMA_WAITOK : BUS_DMA_NOWAIT);
+ if (error) {
+ m_freem(m);
+ printf("%s: can't map rx mbuf %d\n", sc->sc_dev.dv_xname,
+ error);
+ return error;
+ }
+
+ /* Unload originally mapped mbuf */
+ bus_dmamap_unload(sc->sc_dmat, rb->rb_mbuf_dmamap);
+
+ /* Swap this dmamap with tmp dmamap */
+ map = rb->rb_mbuf_dmamap;
+ rb->rb_mbuf_dmamap = bd->mbuf_tmp_dmamap;
+ bd->mbuf_tmp_dmamap = map;
+
+ rb->rb_mbuf = m;
+ rb->rb_desc->h_data_paddr = htole32(paddr);
+ rb->rb_desc->h_data_len = htole16(m->m_len);
+
+ bus_dmamap_sync(sc->sc_dmat, rb->rb_mbuf_dmamap, 0,
+ rb->rb_mbuf_dmamap->dm_mapsize, BUS_DMASYNC_PREREAD);
+
+ return 0;
+}
+
+int
+acx_encap(struct acx_softc *sc, struct acx_txbuf *txbuf, struct mbuf *m,
+ struct ieee80211_node *ni, int rate)
+{
+ struct acx_ring_data *rd = &sc->sc_ring_data;
+ struct acx_node *node = (struct acx_node *)ni;
+ struct ifnet *ifp = &sc->sc_ic.ic_if;
+ uint32_t paddr;
+ uint8_t ctrl;
+ int error;
+
+ if (txbuf->tb_mbuf != NULL)
+ panic("free TX buf has mbuf installed\n");
+ error = 0;
+
+ if (m->m_pkthdr.len > MCLBYTES) {
+ printf("%s: mbuf too big\n", ifp->if_xname);
+ error = E2BIG;
+ goto back;
+ } else if (m->m_pkthdr.len < ACX_FRAME_HDRLEN) {
+ printf("%s: mbuf too small\n", ifp->if_xname);
+ error = EINVAL;
+ goto back;
+ }
+
+ error = bus_dmamap_load_mbuf(sc->sc_dmat, txbuf->tb_mbuf_dmamap,
+ m, BUS_DMA_NOWAIT);
+
+ if (error && error != EFBIG) {
+ printf("%s: can't map tx mbuf1 %d\n", sc->sc_dev.dv_xname,
+ error);
+ goto back;
+ }
+
+ if (error) { /* error == EFBIG */
+ /* too many fragments, linearize */
+ struct mbuf *mnew;
+
+ MGETHDR(mnew, M_DONTWAIT, MT_DATA);
+ if (mnew == NULL) {
+ m_freem(m);
+ error = ENOBUFS;
+ }
+
+ M_DUP_PKTHDR(mnew, m);
+ if (m->m_pkthdr.len > MHLEN) {
+ MCLGET(mnew, M_DONTWAIT);
+ if (!(mnew->m_flags & M_EXT)) {
+ m_freem(m);
+ m_freem(mnew);
+ error = ENOBUFS;
+ }
+ }
+
+ if (error) {
+ printf("%s: can't defrag tx mbuf\n", ifp->if_xname);
+ goto back;
+ }
+
+ m_copydata(m, 0, m->m_pkthdr.len, mtod(mnew, caddr_t));
+ m_freem(m);
+ mnew->m_len = mnew->m_pkthdr.len;
+ m = mnew;
+
+ error = bus_dmamap_load_mbuf(sc->sc_dmat,
+ txbuf->tb_mbuf_dmamap, m,
+ BUS_DMA_NOWAIT);
+ if (error) {
+ printf("%s: can't map tx mbuf2 %d\n",
+ sc->sc_dev.dv_xname, error);
+ goto back;
+ }
+ }
+
+ error = 0;
+
+ bus_dmamap_sync(sc->sc_dmat, txbuf->tb_mbuf_dmamap, 0,
+ txbuf->tb_mbuf_dmamap->dm_mapsize, BUS_DMASYNC_PREWRITE);
+
+ txbuf->tb_mbuf = m;
+ txbuf->tb_node = node;
+ txbuf->tb_rate = rate;
+
+ /*
+ * TX buffers are accessed in following way:
+ * acx_fw_txdesc -> acx_host_desc -> buffer
+ *
+ * It is quite strange that acx also querys acx_host_desc next to
+ * the one we have assigned to acx_fw_txdesc even if first one's
+ * acx_host_desc.h_data_len == acx_fw_txdesc.f_tx_len
+ *
+ * So we allocate two acx_host_desc for one acx_fw_txdesc and
+ * assign the first acx_host_desc to acx_fw_txdesc
+ *
+ * For acx111
+ * host_desc1.h_data_len = buffer_len
+ * host_desc2.h_data_len = buffer_len - mac_header_len
+ *
+ * For acx100
+ * host_desc1.h_data_len = mac_header_len
+ * host_desc2.h_data_len = buffer_len - mac_header_len
+ */
+
+ txbuf->tb_desc1->h_data_paddr = htole32(paddr);
+ txbuf->tb_desc2->h_data_paddr = htole32(paddr + ACX_FRAME_HDRLEN);
+
+ txbuf->tb_desc1->h_data_len =
+ htole16(sc->chip_txdesc1_len ? sc->chip_txdesc1_len
+ : m->m_pkthdr.len);
+ txbuf->tb_desc2->h_data_len =
+ htole16(m->m_pkthdr.len - ACX_FRAME_HDRLEN);
+
+ /*
+ * NOTE:
+ * We can't simply assign f_tx_ctrl, we will first read it back
+ * and change it bit by bit
+ */
+ ctrl = FW_TXDESC_GETFIELD_1(sc, txbuf, f_tx_ctrl);
+ ctrl |= sc->chip_fw_txdesc_ctrl; /* extra chip specific flags */
+ ctrl &= ~(DESC_CTRL_HOSTOWN | DESC_CTRL_ACXDONE);
+
+ FW_TXDESC_SETFIELD_4(sc, txbuf, f_tx_len, m->m_pkthdr.len);
+ FW_TXDESC_SETFIELD_1(sc, txbuf, f_tx_error, 0);
+ FW_TXDESC_SETFIELD_1(sc, txbuf, f_tx_ack_fail, 0);
+ FW_TXDESC_SETFIELD_1(sc, txbuf, f_tx_rts_fail, 0);
+ FW_TXDESC_SETFIELD_1(sc, txbuf, f_tx_rts_ok, 0);
+ sc->chip_set_fw_txdesc_rate(sc, txbuf, rate);
+
+ txbuf->tb_desc1->h_ctrl = 0;
+ txbuf->tb_desc2->h_ctrl = 0;
+ bus_dmamap_sync(sc->sc_dmat, rd->tx_ring_dmamap, 0,
+ rd->tx_ring_dmamap->dm_mapsize, BUS_DMASYNC_PREWRITE);
+
+ FW_TXDESC_SETFIELD_1(sc, txbuf, f_tx_ctrl2, 0);
+ FW_TXDESC_SETFIELD_1(sc, txbuf, f_tx_ctrl, ctrl);
+
+ /* Tell chip to inform us about TX completion */
+ CSR_WRITE_2(sc, ACXREG_INTR_TRIG, ACXRV_TRIG_TX_FINI);
+back:
+ if (error)
+ m_freem(m);
+ return error;
+}
+
+int
+acx_set_null_tmplt(struct acx_softc *sc)
+{
+ struct acx_tmplt_null_data n;
+ struct ieee80211_frame *f;
+ struct ifnet *ifp = &sc->sc_ic.ic_if;
+
+ bzero(&n, sizeof(n));
+
+ f = &n.data;
+ f->i_fc[0] = IEEE80211_FC0_SUBTYPE_NODATA | IEEE80211_FC0_TYPE_DATA;
+ IEEE80211_ADDR_COPY(f->i_addr1, etherbroadcastaddr);
+ IEEE80211_ADDR_COPY(f->i_addr2, LLADDR(ifp->if_sadl));
+ IEEE80211_ADDR_COPY(f->i_addr3, etherbroadcastaddr);
+
+ return _acx_set_null_data_tmplt(sc, &n, sizeof(n));
+}
+
+int
+acx_set_probe_req_tmplt(struct acx_softc *sc, const char *ssid, int ssid_len)
+{
+ struct acx_tmplt_probe_req req;
+ struct ieee80211_frame *f;
+ struct ifnet *ifp = &sc->sc_ic.ic_if;
+ uint8_t *v;
+ int vlen;
+
+ bzero(&req, sizeof(req));
+
+ f = &req.data.u_data.f;
+ f->i_fc[0] = IEEE80211_FC0_SUBTYPE_PROBE_REQ | IEEE80211_FC0_TYPE_MGT;
+ IEEE80211_ADDR_COPY(f->i_addr1, etherbroadcastaddr);
+ IEEE80211_ADDR_COPY(f->i_addr2, LLADDR(ifp->if_sadl));
+ IEEE80211_ADDR_COPY(f->i_addr3, etherbroadcastaddr);
+
+ v = req.data.u_data.var;
+ v = ieee80211_add_ssid(v, ssid, ssid_len);
+ v = ieee80211_add_rates(v, &sc->sc_ic.ic_sup_rates[sc->chip_phymode]);
+ v = ieee80211_add_xrates(v, &sc->sc_ic.ic_sup_rates[sc->chip_phymode]);
+ vlen = v - req.data.u_data.var;
+
+ return _acx_set_probe_req_tmplt(sc, &req,
+ ACX_TMPLT_PROBE_REQ_SIZ(vlen));
+}
+
+int
+acx_set_probe_resp_tmplt(struct acx_softc *sc, const char *ssid, int ssid_len,
+ int chan)
+{
+ struct acx_tmplt_probe_resp resp;
+ struct ieee80211_frame *f;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = &ic->ic_if;
+ uint8_t *v;
+ int vlen;
+
+ bzero(&resp, sizeof(resp));
+
+ f = &resp.data.u_data.f;
+ f->i_fc[0] = IEEE80211_FC0_SUBTYPE_PROBE_RESP | IEEE80211_FC0_TYPE_MGT;
+ IEEE80211_ADDR_COPY(f->i_addr1, etherbroadcastaddr);
+ IEEE80211_ADDR_COPY(f->i_addr2, LLADDR(ifp->if_sadl));
+ IEEE80211_ADDR_COPY(f->i_addr3, LLADDR(ifp->if_sadl));
+
+ resp.data.u_data.beacon_intvl = htole16(acx_beacon_intvl);
+ resp.data.u_data.cap = htole16(IEEE80211_CAPINFO_IBSS);
+
+ v = resp.data.u_data.var;
+ v = ieee80211_add_ssid(v, ssid, ssid_len);
+ v = ieee80211_add_rates(v, &ic->ic_sup_rates[sc->chip_phymode]);
+
+ *v++ = IEEE80211_ELEMID_DSPARMS;
+ *v++ = 1;
+ *v++ = chan;
+
+ /* This should after IBSS or TIM, but acx always keeps them last */
+ v = ieee80211_add_xrates(v, &ic->ic_sup_rates[sc->chip_phymode]);
+
+ if (ic->ic_opmode == IEEE80211_M_IBSS) {
+ *v++ = IEEE80211_ELEMID_IBSSPARMS;
+ *v++ = 2;
+ }
+
+ vlen = v - resp.data.u_data.var;
+
+ return _acx_set_probe_resp_tmplt(sc, &resp,
+ ACX_TMPLT_PROBE_RESP_SIZ(vlen));
+}
+
+/* XXX C&P of acx_set_probe_resp_tmplt() */
+int
+acx_set_beacon_tmplt(struct acx_softc *sc, const char *ssid, int ssid_len,
+ int chan)
+{
+ struct acx_tmplt_beacon beacon;
+ struct ieee80211_frame *f;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = &ic->ic_if;
+ uint8_t *v;
+ int vlen;
+
+ bzero(&beacon, sizeof(beacon));
+
+ f = &beacon.data.u_data.f;
+ f->i_fc[0] = IEEE80211_FC0_SUBTYPE_BEACON | IEEE80211_FC0_TYPE_MGT;
+ IEEE80211_ADDR_COPY(f->i_addr1, etherbroadcastaddr);
+ IEEE80211_ADDR_COPY(f->i_addr2, LLADDR(ifp->if_sadl));
+ IEEE80211_ADDR_COPY(f->i_addr3, LLADDR(ifp->if_sadl));
+
+ beacon.data.u_data.beacon_intvl = htole16(acx_beacon_intvl);
+ beacon.data.u_data.cap = htole16(IEEE80211_CAPINFO_IBSS);
+
+ v = beacon.data.u_data.var;
+ v = ieee80211_add_ssid(v, ssid, ssid_len);
+ v = ieee80211_add_rates(v, &ic->ic_sup_rates[sc->chip_phymode]);
+
+ *v++ = IEEE80211_ELEMID_DSPARMS;
+ *v++ = 1;
+ *v++ = chan;
+
+ /* This should after IBSS or TIM, but acx always keeps them last */
+ v = ieee80211_add_xrates(v, &ic->ic_sup_rates[sc->chip_phymode]);
+
+ if (ic->ic_opmode == IEEE80211_M_IBSS) {
+ *v++ = IEEE80211_ELEMID_IBSSPARMS;
+ *v++ = 2;
+ }
+
+ vlen = v - beacon.data.u_data.var;
+
+ return _acx_set_beacon_tmplt(sc, &beacon, ACX_TMPLT_BEACON_SIZ(vlen));
+}
+
+void
+acx_init_cmd_reg(struct acx_softc *sc)
+{
+ sc->sc_cmd = CSR_READ_4(sc, ACXREG_CMD_REG_OFFSET);
+ sc->sc_cmd_param = sc->sc_cmd + ACX_CMD_REG_SIZE;
+
+ /* Clear command & status */
+ CMD_WRITE_4(sc, 0);
+}
+
+int
+acx_join_bss(struct acx_softc *sc, uint8_t mode, struct ieee80211_node *node)
+{
+ uint8_t bj_buf[BSS_JOIN_BUFLEN];
+ struct bss_join_hdr *bj;
+ int i, dtim_intvl;
+
+ bzero(bj_buf, sizeof(bj_buf));
+ bj = (struct bss_join_hdr *)bj_buf;
+
+ for (i = 0; i < IEEE80211_ADDR_LEN; ++i)
+ bj->bssid[i] = node->ni_bssid[IEEE80211_ADDR_LEN - i - 1];
+
+ bj->beacon_intvl = htole16(acx_beacon_intvl);
+
+ /* TODO tunable */
+ dtim_intvl = sc->sc_ic.ic_opmode == IEEE80211_M_IBSS ? 1 : 10;
+ sc->chip_set_bss_join_param(sc, bj->chip_spec, dtim_intvl);
+
+ bj->ndata_txrate = ACX_NDATA_TXRATE_2;
+ bj->ndata_txopt = 0;
+ bj->mode = mode;
+ bj->channel = ieee80211_chan2ieee(&sc->sc_ic, node->ni_chan);
+ bj->esslen = node->ni_esslen;
+ bcopy(node->ni_essid, bj->essid, node->ni_esslen);
+
+ DPRINTF(("%s: join BSS/IBSS on channel %d\n", sc->sc_dev.dv_xname,
+ bj->channel));
+ return acx_exec_command(sc, ACXCMD_JOIN_BSS,
+ bj, BSS_JOIN_PARAM_SIZE(bj), NULL, 0);
+}
+
+int
+acx_enable_txchan(struct acx_softc *sc, uint8_t chan)
+{
+ return acx_exec_command(sc, ACXCMD_ENABLE_TXCHAN, &chan, sizeof(chan),
+ NULL, 0);
+}
+
+int
+acx_enable_rxchan(struct acx_softc *sc, uint8_t chan)
+{
+ return acx_exec_command(sc, ACXCMD_ENABLE_RXCHAN, &chan, sizeof(chan),
+ NULL, 0);
+}
+
+int
+acx_get_conf(struct acx_softc *sc, uint16_t conf_id, void *conf,
+ uint16_t conf_len)
+{
+ struct acx_conf *confcom;
+
+ if (conf_len < sizeof(*confcom)) {
+ printf("%s: %s configure data is too short\n",
+ sc->sc_dev.dv_xname, __func__);
+ return 1;
+ }
+
+ confcom = conf;
+ confcom->conf_id = htole16(conf_id);
+ confcom->conf_data_len = htole16(conf_len - sizeof(*confcom));
+
+ return acx_exec_command(sc, ACXCMD_GET_CONF, confcom, sizeof(*confcom),
+ conf, conf_len);
+}
+
+int
+acx_set_conf(struct acx_softc *sc, uint16_t conf_id, void *conf,
+ uint16_t conf_len)
+{
+ struct acx_conf *confcom;
+
+ if (conf_len < sizeof(*confcom)) {
+ printf("%s: %s configure data is too short\n",
+ sc->sc_dev.dv_xname, __func__);
+ return 1;
+ }
+
+ confcom = conf;
+ confcom->conf_id = htole16(conf_id);
+ confcom->conf_data_len = htole16(conf_len - sizeof(*confcom));
+
+ return acx_exec_command(sc, ACXCMD_SET_CONF, conf, conf_len, NULL, 0);
+}
+
+int
+acx_set_tmplt(struct acx_softc *sc, uint16_t cmd, void *tmplt,
+ uint16_t tmplt_len)
+{
+ uint16_t *size;
+
+ if (tmplt_len < sizeof(*size)) {
+ printf("%s: %s template is too short\n",
+ sc->sc_dev.dv_xname, __func__);
+ return 1;
+ }
+
+ size = tmplt;
+ *size = htole16(tmplt_len - sizeof(*size));
+
+ return acx_exec_command(sc, cmd, tmplt, tmplt_len, NULL, 0);
+}
+
+int
+acx_init_radio(struct acx_softc *sc, uint32_t radio_ofs, uint32_t radio_len)
+{
+ struct radio_init r;
+
+ r.radio_ofs = htole32(radio_ofs);
+ r.radio_len = htole32(radio_len);
+ return acx_exec_command(sc, ACXCMD_INIT_RADIO, &r, sizeof(r), NULL, 0);
+}
+
+int
+acx_exec_command(struct acx_softc *sc, uint16_t cmd, void *param,
+ uint16_t param_len, void *result, uint16_t result_len)
+{
+ uint16_t status;
+ int i, ret;
+
+ if ((sc->sc_flags & ACX_FLAG_FW_LOADED) == 0) {
+ printf("%s: cmd 0x%04x failed (base firmware "
+ "not loaded)", sc->sc_dev.dv_xname, cmd);
+ return 1;
+ }
+
+ ret = 0;
+
+ if (param != NULL && param_len != 0) {
+ /* Set command param */
+ CMDPRM_WRITE_REGION_1(sc, param, param_len);
+ }
+
+ /* Set command */
+ CMD_WRITE_4(sc, cmd);
+
+ /* Exec command */
+ CSR_WRITE_2(sc, ACXREG_INTR_TRIG, ACXRV_TRIG_CMD_FINI);
+ DELAY(50); /* XXX maybe 100 */
+
+ /* Wait for command to complete */
+ if (cmd == ACXCMD_INIT_RADIO) {
+ /* XXX radio initialization is extremely long */
+ tsleep(&cmd, 0, "rdinit", (150 * hz) / 1000); /* 150ms */
+ }
+
+#define CMDWAIT_RETRY_MAX 1000
+ for (i = 0; i < CMDWAIT_RETRY_MAX; ++i) {
+ uint16_t reg;
+
+ reg = CSR_READ_2(sc, ACXREG_INTR_STATUS);
+ if (reg & ACXRV_INTR_CMD_FINI) {
+ CSR_WRITE_2(sc, ACXREG_INTR_ACK, ACXRV_INTR_CMD_FINI);
+ break;
+ }
+ DELAY(50);
+ }
+ if (i == CMDWAIT_RETRY_MAX) {
+ printf("%s: cmd %04x failed (timeout)\n",
+ sc->sc_dev.dv_xname, cmd);
+ ret = 1;
+ goto back;
+ }
+#undef CMDWAIT_RETRY_MAX
+
+ /* Get command exec status */
+ status = (CMD_READ_4(sc) >> ACX_CMD_STATUS_SHIFT);
+ if (status != ACX_CMD_STATUS_OK) {
+ printf("%s: cmd %04x failed\n", sc->sc_dev.dv_xname, cmd);
+ ret = 1;
+ goto back;
+ }
+
+ if (result != NULL && result_len != 0) {
+ /* Get command result */
+ CMDPRM_READ_REGION_1(sc, result, result_len);
+ }
+
+back:
+ CMD_WRITE_4(sc, 0);
+ return ret;
+}
diff --git a/sys/dev/ic/acx100.c b/sys/dev/ic/acx100.c
new file mode 100644
index 00000000000..e4da18930e0
--- /dev/null
+++ b/sys/dev/ic/acx100.c
@@ -0,0 +1,768 @@
+/* $Id: acx100.c,v 1.1 2006/08/03 08:45:01 mglocker Exp $ */
+
+/*
+ * Copyright (c) 2006 The DragonFly Project. All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Sepherosa Ziehau <sepherosa@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $DragonFly$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/endian.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/if_media.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#endif
+
+#include <net80211/ieee80211.h>
+#include <net80211/ieee80211_var.h>
+
+#include <dev/pci/pcireg.h>
+
+#define ACX_DEBUG
+
+#include <dev/ic/acxvar.h>
+#include <dev/ic/acxreg.h>
+
+#define ACX100_CONF_FW_RING 0x0003
+#define ACX100_CONF_MEMOPT 0x0005
+
+#define ACX100_INTR_ENABLE (ACXRV_INTR_TX_FINI | ACXRV_INTR_RX_FINI)
+/*
+ * XXX do we really care about following interrupts?
+ *
+ * ACXRV_INTR_INFO | ACXRV_INTR_SCAN_FINI
+ */
+
+#define ACX100_INTR_DISABLE (uint16_t)~(ACXRV_INTR_UNKN)
+
+#define ACX100_RATE(rate) ((rate) * 5)
+
+#define ACX100_TXPOWER 18
+#define ACX100_GPIO_POWER_LED 0x0800
+#define ACX100_EE_EADDR_OFS 0x1a
+
+#define ACX100_FW_TXRING_SIZE (ACX_TX_DESC_CNT * sizeof(struct acx_fw_txdesc))
+#define ACX100_FW_RXRING_SIZE (ACX_RX_DESC_CNT * sizeof(struct acx_fw_rxdesc))
+
+/*
+ * NOTE:
+ * Following structs' fields are little endian
+ */
+
+struct acx100_bss_join {
+ uint8_t dtim_intvl;
+ uint8_t basic_rates;
+ uint8_t all_rates;
+} __packed;
+
+struct acx100_conf_fw_ring {
+ struct acx_conf confcom;
+ uint32_t fw_ring_size; /* total size of fw (tx + rx) ring */
+ uint32_t fw_rxring_addr; /* start phyaddr of fw rx desc */
+ uint8_t opt; /* see ACX100_RINGOPT_ */
+ uint8_t fw_txring_num; /* num of TX ring */
+ uint8_t fw_rxdesc_num; /* num of fw rx desc */
+ uint8_t reserved0;
+ uint32_t fw_ring_end[2]; /* see ACX100_SET_RING_END() */
+ uint32_t fw_txring_addr; /* start phyaddr of fw tx desc */
+ uint8_t fw_txring_prio; /* see ACX100_TXRING_PRIO_ */
+ uint8_t fw_txdesc_num; /* num of fw tx desc */
+ uint16_t reserved1;
+} __packed;
+
+#define ACX100_RINGOPT_AUTO_RESET 0x1
+#define ACX100_TXRING_PRIO_DEFAULT 0
+#define ACX100_SET_RING_END(conf, end) \
+do { \
+ (conf)->fw_ring_end[0] = htole32(end); \
+ (conf)->fw_ring_end[1] = htole32(end + 8); \
+} while (0)
+
+struct acx100_conf_memblk_size {
+ struct acx_conf confcom;
+ uint16_t memblk_size; /* size of each mem block */
+} __packed;
+
+struct acx100_conf_mem {
+ struct acx_conf confcom;
+ uint32_t opt; /* see ACX100_MEMOPT_ */
+ uint32_t h_rxring_paddr; /* host rx desc start phyaddr */
+
+ /*
+ * Memory blocks are controled by hardware
+ * once after they are initialized
+ */
+ uint32_t rx_memblk_addr; /* start addr of rx mem blocks */
+ uint32_t tx_memblk_addr; /* start addr of tx mem blocks */
+ uint16_t rx_memblk_num; /* num of RX mem block */
+ uint16_t tx_memblk_num; /* num of TX mem block */
+} __packed;
+
+#define ACX100_MEMOPT_MEM_INSTR 0x00000000 /* memory access instruct */
+#define ACX100_MEMOPT_HOSTDESC 0x00010000 /* host indirect desc */
+#define ACX100_MEMOPT_MEMBLOCK 0x00020000 /* local mem block list */
+#define ACX100_MEMOPT_IO_INSTR 0x00040000 /* IO instruct */
+#define ACX100_MEMOPT_PCICONF 0x00080000 /* PCI conf space */
+
+#define ACX100_MEMBLK_ALIGN 0x20
+
+struct acx100_conf_cca_mode {
+ struct acx_conf confcom;
+ uint8_t cca_mode;
+ uint8_t unknown;
+} __packed;
+
+struct acx100_conf_ed_thresh {
+ struct acx_conf confcom;
+ uint8_t ed_thresh;
+ uint8_t unknown[3];
+} __packed;
+
+struct acx100_conf_wepkey {
+ struct acx_conf confcom;
+ uint8_t action; /* see ACX100_WEPKEY_ACT_ */
+ uint8_t key_len;
+ uint8_t key_idx;
+#define ACX100_WEPKEY_LEN 29
+ uint8_t key[ACX100_WEPKEY_LEN];
+} __packed;
+
+#define ACX100_WEPKEY_ACT_ADD 1
+
+#define ACX100_CONF_FUNC(sg, name) _ACX_CONF_FUNC(sg, name, 100)
+#define ACX_CONF_fw_ring ACX100_CONF_FW_RING
+#define ACX_CONF_memblk_size ACX_CONF_MEMBLK_SIZE
+#define ACX_CONF_mem ACX100_CONF_MEMOPT
+#define ACX_CONF_cca_mode ACX_CONF_CCA_MODE
+#define ACX_CONF_ed_thresh ACX_CONF_ED_THRESH
+#define ACX_CONF_wepkey ACX_CONF_WEPKEY
+ACX100_CONF_FUNC(set, fw_ring);
+ACX100_CONF_FUNC(set, memblk_size);
+ACX100_CONF_FUNC(set, mem);
+ACX100_CONF_FUNC(get, cca_mode);
+ACX100_CONF_FUNC(set, cca_mode);
+ACX100_CONF_FUNC(get, ed_thresh);
+ACX100_CONF_FUNC(set, ed_thresh);
+ACX100_CONF_FUNC(set, wepkey);
+
+#define ACXCMD_init_mem ACXCMD_INIT_MEM
+ACX_NOARG_FUNC(init_mem);
+
+static const uint16_t acx100_reg[ACXREG_MAX] = {
+ ACXREG(SOFT_RESET, 0x0000),
+
+ ACXREG(FWMEM_ADDR, 0x0014),
+ ACXREG(FWMEM_DATA, 0x0018),
+ ACXREG(FWMEM_CTRL, 0x001c),
+ ACXREG(FWMEM_START, 0x0020),
+
+ ACXREG(EVENT_MASK, 0x0034),
+
+ ACXREG(INTR_TRIG, 0x007c),
+ ACXREG(INTR_MASK, 0x0098),
+ ACXREG(INTR_STATUS, 0x00a4),
+ ACXREG(INTR_STATUS_CLR, 0x00a8),
+ ACXREG(INTR_ACK, 0x00ac),
+
+ ACXREG(HINTR_TRIG, 0x00b0),
+ ACXREG(RADIO_ENABLE, 0x0104),
+
+ ACXREG(EEPROM_INIT, 0x02d0),
+ ACXREG(EEPROM_CTRL, 0x0250),
+ ACXREG(EEPROM_ADDR, 0x0254),
+ ACXREG(EEPROM_DATA, 0x0258),
+ ACXREG(EEPROM_CONF, 0x025c),
+ ACXREG(EEPROM_INFO, 0x02ac),
+
+ ACXREG(PHY_ADDR, 0x0268),
+ ACXREG(PHY_DATA, 0x026c),
+ ACXREG(PHY_CTRL, 0x0270),
+
+ ACXREG(GPIO_OUT_ENABLE, 0x0290),
+ ACXREG(GPIO_OUT, 0x0298),
+
+ ACXREG(CMD_REG_OFFSET, 0x02a4),
+ ACXREG(INFO_REG_OFFSET, 0x02a8),
+
+ ACXREG(RESET_SENSE, 0x02d4),
+ ACXREG(ECPU_CTRL, 0x02d8)
+};
+
+static const uint8_t acx100_txpower_maxim[21] = {
+ 63, 63, 63, 62,
+ 61, 61, 60, 60,
+ 59, 58, 57, 55,
+ 53, 50, 47, 43,
+ 38, 31, 23, 13,
+ 0
+};
+
+static const uint8_t acx100_txpower_rfmd[21] = {
+ 0, 0, 0, 1,
+ 2, 2, 3, 3,
+ 4, 5, 6, 8,
+ 10, 13, 16, 20,
+ 25, 32, 41, 50,
+ 63
+};
+
+int acx100_init(struct acx_softc *);
+int acx100_init_wep(struct acx_softc *);
+int acx100_init_tmplt(struct acx_softc *);
+int acx100_init_fw_ring(struct acx_softc *);
+int acx100_init_memory(struct acx_softc *);
+
+void acx100_init_fw_txring(struct acx_softc *, uint32_t);
+void acx100_init_fw_rxring(struct acx_softc *, uint32_t);
+int acx100_read_config(struct acx_softc *, struct acx_config *);
+int acx100_write_config(struct acx_softc *, struct acx_config *);
+
+int acx100_set_txpower(struct acx_softc *);
+
+void acx100_set_fw_txdesc_rate(struct acx_softc *,
+ struct acx_txbuf *, int);
+void acx100_set_bss_join_param(struct acx_softc *, void *, int);
+
+#if 0
+int acx100_set_wepkey(struct acx_softc *, struct ieee80211_key *,
+ int);
+#endif
+
+void acx100_proc_wep_rxbuf(struct acx_softc *, struct mbuf *, int *);
+
+void
+acx100_set_param(struct acx_softc *sc)
+{
+ sc->chip_mem1_rid = PCIR_BAR(1);
+ sc->chip_mem2_rid = PCIR_BAR(2);
+ sc->chip_ioreg = acx100_reg;
+ sc->chip_hw_crypt = 1;
+ sc->chip_intr_enable = ACX100_INTR_ENABLE;
+ sc->chip_intr_disable = ACX100_INTR_DISABLE;
+ sc->chip_gpio_pled = ACX100_GPIO_POWER_LED;
+ sc->chip_ee_eaddr_ofs = ACX100_EE_EADDR_OFS;
+ sc->chip_txdesc1_len = ACX_FRAME_HDRLEN;
+ sc->chip_fw_txdesc_ctrl = DESC_CTRL_AUTODMA |
+ DESC_CTRL_RECLAIM |
+ DESC_CTRL_FIRST_FRAG;
+
+ sc->chip_phymode = IEEE80211_MODE_11B;
+ sc->chip_chan_flags = IEEE80211_CHAN_B;
+ sc->sc_ic.ic_phytype = IEEE80211_T_DS;
+ sc->sc_ic.ic_sup_rates[IEEE80211_MODE_11B] = acx_rates_11b;
+
+ sc->chip_init = acx100_init;
+#if 0
+ sc->chip_set_wepkey = acx100_set_wepkey;
+#endif
+ sc->chip_read_config = acx100_read_config;
+ sc->chip_write_config = acx100_write_config;
+ sc->chip_set_fw_txdesc_rate = acx100_set_fw_txdesc_rate;
+ sc->chip_set_bss_join_param = acx100_set_bss_join_param;
+ sc->chip_proc_wep_rxbuf = acx100_proc_wep_rxbuf;
+}
+
+int
+acx100_init(struct acx_softc *sc)
+{
+ struct ifnet *ifp = &sc->sc_ic.ic_if;
+
+ /*
+ * NOTE:
+ * Order of initialization:
+ * 1) WEP
+ * 2) Templates
+ * 3) Firmware TX/RX ring
+ * 4) Hardware memory
+ * Above order is critical to get a correct memory map
+ */
+
+ if (acx100_init_wep(sc) != 0) {
+ printf("%s: %s can't initialize wep\n",
+ ifp->if_xname, __func__);
+ return ENXIO;
+ }
+
+ if (acx100_init_tmplt(sc) != 0) {
+ printf("%s: %s can't initialize templates\n",
+ ifp->if_xname, __func__);
+ return ENXIO;
+ }
+
+ if (acx100_init_fw_ring(sc) != 0) {
+ printf("%s: %s can't initialize fw ring\n",
+ ifp->if_xname, __func__);
+ return ENXIO;
+ }
+
+ if (acx100_init_memory(sc) != 0) {
+ printf("%s: %s can't initialize hw memory\n",
+ ifp->if_xname, __func__);
+ return ENXIO;
+ }
+ return 0;
+}
+
+int
+acx100_init_wep(struct acx_softc *sc)
+{
+ struct acx_conf_wepopt wep_opt;
+ struct acx_conf_mmap mem_map;
+ struct ifnet *ifp = &sc->sc_ic.ic_if;
+
+ /* Set WEP cache start/end address */
+ if (acx_get_mmap_conf(sc, &mem_map) != 0) {
+ printf("%s: can't get mmap\n", ifp->if_xname);
+ return 1;
+ }
+
+ mem_map.wep_cache_start = htole32(letoh32(mem_map.code_end) + 4);
+ mem_map.wep_cache_end = htole32(letoh32(mem_map.code_end) + 4);
+ if (acx_set_mmap_conf(sc, &mem_map) != 0) {
+ printf("%s: can't set mmap\n", ifp->if_xname);
+ return 1;
+ }
+
+ /* Set WEP options */
+ wep_opt.nkey = htole16(IEEE80211_WEP_NKID + 10);
+ wep_opt.opt = WEPOPT_HDWEP;
+ if (acx_set_wepopt_conf(sc, &wep_opt) != 0) {
+ printf("%s: can't set wep opt\n", ifp->if_xname);
+ return 1;
+ }
+ return 0;
+}
+
+int
+acx100_init_tmplt(struct acx_softc *sc)
+{
+ struct acx_conf_mmap mem_map;
+ struct acx_tmplt_tim tim;
+ struct ifnet *ifp = &sc->sc_ic.ic_if;
+
+ /* Set templates start address */
+ if (acx_get_mmap_conf(sc, &mem_map) != 0) {
+ printf("%s: can't get mmap\n", ifp->if_xname);
+ return 1;
+ }
+
+ mem_map.pkt_tmplt_start = mem_map.wep_cache_end;
+ if (acx_set_mmap_conf(sc, &mem_map) != 0) {
+ printf("%s: can't set mmap\n", ifp->if_xname);
+ return 1;
+ }
+
+ /* Initialize various packet templates */
+ if (acx_init_tmplt_ordered(sc) != 0) {
+ printf("%s: can't init tmplt\n", ifp->if_xname);
+ return 1;
+ }
+
+ /* Setup TIM template */
+ bzero(&tim, sizeof(tim));
+ tim.tim_eid = IEEE80211_ELEMID_TIM;
+ tim.tim_len = ACX_TIM_LEN(ACX_TIM_BITMAP_LEN);
+ if (_acx_set_tim_tmplt(sc, &tim,
+ ACX_TMPLT_TIM_SIZ(ACX_TIM_BITMAP_LEN)) != 0) {
+ printf("%s: can't set tim tmplt\n", ifp->if_xname);
+ return 1;
+ }
+ return 0;
+}
+
+int
+acx100_init_fw_ring(struct acx_softc *sc)
+{
+ struct acx100_conf_fw_ring ring;
+ struct acx_conf_mmap mem_map;
+ struct ifnet *ifp = &sc->sc_ic.ic_if;
+ uint32_t txring_start, rxring_start, ring_end;
+
+ /* Set firmware descriptor ring start address */
+ if (acx_get_mmap_conf(sc, &mem_map) != 0) {
+ printf("%s: can't get mmap\n", ifp->if_xname);
+ return 1;
+ }
+
+ txring_start = letoh32(mem_map.pkt_tmplt_end) + 4;
+ rxring_start = txring_start + ACX100_FW_TXRING_SIZE;
+ ring_end = rxring_start + ACX100_FW_RXRING_SIZE;
+
+ mem_map.fw_desc_start = htole32(txring_start);
+ if (acx_set_mmap_conf(sc, &mem_map) != 0) {
+ printf("%s: can't set mmap\n", ifp->if_xname);
+ return 1;
+ }
+
+ /* Set firmware descriptor ring configure */
+ bzero(&ring, sizeof(ring));
+ ring.fw_ring_size = htole32(ACX100_FW_TXRING_SIZE +
+ ACX100_FW_RXRING_SIZE + 8);
+
+ ring.fw_txring_num = 1;
+ ring.fw_txring_addr = htole32(txring_start);
+ ring.fw_txring_prio = ACX100_TXRING_PRIO_DEFAULT;
+ ring.fw_txdesc_num = 0; /* XXX ignored?? */
+
+ ring.fw_rxring_addr = htole32(rxring_start);
+ ring.fw_rxdesc_num = 0; /* XXX ignored?? */
+
+ ring.opt = ACX100_RINGOPT_AUTO_RESET;
+ ACX100_SET_RING_END(&ring, ring_end);
+ if (acx100_set_fw_ring_conf(sc, &ring) != 0) {
+ printf("%s: can't set fw ring configure\n", ifp->if_xname);
+ return 1;
+ }
+
+ /* Setup firmware TX/RX descriptor ring */
+ acx100_init_fw_txring(sc, txring_start);
+ acx100_init_fw_rxring(sc, rxring_start);
+
+ return 0;
+}
+
+#define MEMBLK_ALIGN(addr) \
+ (((addr) + (ACX100_MEMBLK_ALIGN - 1)) & ~(ACX100_MEMBLK_ALIGN - 1))
+
+int
+acx100_init_memory(struct acx_softc *sc)
+{
+ struct acx100_conf_memblk_size memblk_sz;
+ struct acx100_conf_mem mem;
+ struct acx_conf_mmap mem_map;
+ struct ifnet *ifp = &sc->sc_ic.ic_if;
+ uint32_t memblk_start, memblk_end;
+ int total_memblk, txblk_num, rxblk_num;
+
+ /* Set memory block start address */
+ if (acx_get_mmap_conf(sc, &mem_map) != 0) {
+ printf("%s: can't get mmap\n", ifp->if_xname);
+ return 1;
+ }
+
+ mem_map.memblk_start =
+ htole32(MEMBLK_ALIGN(letoh32(mem_map.fw_desc_end) + 4));
+
+ if (acx_set_mmap_conf(sc, &mem_map) != 0) {
+ printf("%s: can't set mmap\n", ifp->if_xname);
+ return 1;
+ }
+
+ /* Set memory block size */
+ memblk_sz.memblk_size = htole16(ACX_MEMBLOCK_SIZE);
+ if (acx100_set_memblk_size_conf(sc, &memblk_sz) != 0) {
+ printf("%s: can't set mem block size\n", ifp->if_xname);
+ return 1;
+ }
+
+ /* Get memory map after setting it */
+ if (acx_get_mmap_conf(sc, &mem_map) != 0) {
+ printf("%s: can't get mmap again\n", ifp->if_xname);
+ return 1;
+ }
+ memblk_start = letoh32(mem_map.memblk_start);
+ memblk_end = letoh32(mem_map.memblk_end);
+
+ /* Set memory options */
+ mem.opt = htole32(ACX100_MEMOPT_MEMBLOCK | ACX100_MEMOPT_HOSTDESC);
+ mem.h_rxring_paddr = htole32(sc->sc_ring_data.rx_ring_paddr);
+
+ total_memblk = (memblk_end - memblk_start) / ACX_MEMBLOCK_SIZE;
+
+ rxblk_num = total_memblk / 2; /* 50% */
+ txblk_num = total_memblk - rxblk_num; /* 50% */
+
+ DPRINTF(("%s: \ttotal memory blocks\t%d\n"
+ "\trx memory blocks\t%d\n"
+ "\ttx memory blocks\t%d\n",
+ ifp->if_xname, total_memblk, rxblk_num, txblk_num));
+
+ mem.rx_memblk_num = htole16(rxblk_num);
+ mem.tx_memblk_num = htole16(txblk_num);
+
+ mem.rx_memblk_addr = htole32(MEMBLK_ALIGN(memblk_start));
+ mem.tx_memblk_addr =
+ htole32(MEMBLK_ALIGN(memblk_start +
+ (ACX_MEMBLOCK_SIZE * rxblk_num)));
+
+ if (acx100_set_mem_conf(sc, &mem) != 0) {
+ printf("%s: can't set mem options\n", ifp->if_xname);
+ return 1;
+ }
+
+ /* Initialize memory */
+ if (acx_init_mem(sc) != 0) {
+ printf("%s: can't init mem\n", ifp->if_xname);
+ return 1;
+ }
+ return 0;
+}
+
+#undef MEMBLK_ALIGN
+
+void
+acx100_init_fw_txring(struct acx_softc *sc, uint32_t fw_txdesc_start)
+{
+ struct acx_fw_txdesc fw_desc;
+ struct acx_txbuf *tx_buf;
+ uint32_t desc_paddr, fw_desc_offset;
+ int i;
+
+ bzero(&fw_desc, sizeof(fw_desc));
+ fw_desc.f_tx_ctrl = DESC_CTRL_HOSTOWN |
+ DESC_CTRL_RECLAIM |
+ DESC_CTRL_AUTODMA |
+ DESC_CTRL_FIRST_FRAG;
+
+ tx_buf = sc->sc_buf_data.tx_buf;
+ fw_desc_offset = fw_txdesc_start;
+ desc_paddr = sc->sc_ring_data.tx_ring_paddr;
+
+ for (i = 0; i < ACX_TX_DESC_CNT; ++i) {
+ fw_desc.f_tx_host_desc = htole32(desc_paddr);
+
+ if (i == ACX_TX_DESC_CNT - 1) {
+ fw_desc.f_tx_next_desc = htole32(fw_txdesc_start);
+ } else {
+ fw_desc.f_tx_next_desc =
+ htole32(fw_desc_offset +
+ sizeof(struct acx_fw_txdesc));
+ }
+
+ tx_buf[i].tb_fwdesc_ofs = fw_desc_offset;
+ DESC_WRITE_REGION_1(sc, fw_desc_offset, &fw_desc,
+ sizeof(fw_desc));
+
+ desc_paddr += (2 * sizeof(struct acx_host_desc));
+ fw_desc_offset += sizeof(fw_desc);
+ }
+}
+
+void
+acx100_init_fw_rxring(struct acx_softc *sc, uint32_t fw_rxdesc_start)
+{
+ struct acx_fw_rxdesc fw_desc;
+ uint32_t fw_desc_offset;
+ int i;
+
+ bzero(&fw_desc, sizeof(fw_desc));
+ fw_desc.f_rx_ctrl = DESC_CTRL_RECLAIM | DESC_CTRL_AUTODMA;
+
+ fw_desc_offset = fw_rxdesc_start;
+
+ for (i = 0; i < ACX_RX_DESC_CNT; ++i) {
+ if (i == ACX_RX_DESC_CNT - 1) {
+ fw_desc.f_rx_next_desc = htole32(fw_rxdesc_start);
+ } else {
+ fw_desc.f_rx_next_desc =
+ htole32(fw_desc_offset +
+ sizeof(struct acx_fw_rxdesc));
+ }
+
+ DESC_WRITE_REGION_1(sc, fw_desc_offset, &fw_desc,
+ sizeof(fw_desc));
+
+ fw_desc_offset += sizeof(fw_desc);
+ }
+}
+
+int
+acx100_read_config(struct acx_softc *sc, struct acx_config *conf)
+{
+ struct acx100_conf_cca_mode cca;
+ struct acx100_conf_ed_thresh ed;
+ struct ifnet *ifp = &sc->sc_ic.ic_if;
+
+ /*
+ * NOTE:
+ * CCA mode and ED threshold MUST be read during initialization
+ * or the acx100 card won't work as expected
+ */
+
+ /* Get CCA mode */
+ if (acx100_get_cca_mode_conf(sc, &cca) != 0) {
+ printf("%s: %s can't get cca mode\n",
+ ifp->if_xname, __func__);
+ return ENXIO;
+ }
+ conf->cca_mode = cca.cca_mode;
+ DPRINTF(("%s: cca mode %02x\n", ifp->if_xname, cca.cca_mode));
+
+ /* Get ED threshold */
+ if (acx100_get_ed_thresh_conf(sc, &ed) != 0) {
+ printf("%s: %s can't get ed threshold\n",
+ ifp->if_xname, __func__);
+ return ENXIO;
+ }
+ conf->ed_thresh = ed.ed_thresh;
+ DPRINTF(("%s: ed threshold %02x\n", ifp->if_xname, ed.ed_thresh));
+
+ return 0;
+}
+
+int
+acx100_write_config(struct acx_softc *sc, struct acx_config *conf)
+{
+ struct acx100_conf_cca_mode cca;
+ struct acx100_conf_ed_thresh ed;
+ struct ifnet *ifp = &sc->sc_ic.ic_if;
+
+ /* Set CCA mode */
+ cca.cca_mode = conf->cca_mode;
+ if (acx100_set_cca_mode_conf(sc, &cca) != 0) {
+ printf("%s: %s can't set cca mode\n",
+ ifp->if_xname, __func__);
+ return ENXIO;
+ }
+
+ /* Set ED threshold */
+ ed.ed_thresh = conf->ed_thresh;
+ if (acx100_set_ed_thresh_conf(sc, &ed) != 0) {
+ printf("%s: %s can't set ed threshold\n",
+ ifp->if_xname, __func__);
+ return ENXIO;
+ }
+
+ /* Set TX power */
+ acx100_set_txpower(sc); /* ignore return value */
+
+ return 0;
+}
+
+int
+acx100_set_txpower(struct acx_softc *sc)
+{
+ struct ifnet *ifp = &sc->sc_ic.ic_if;
+ const uint8_t *map;
+
+ switch (sc->sc_radio_type) {
+ case ACX_RADIO_TYPE_MAXIM:
+ map = acx100_txpower_maxim;
+ break;
+ case ACX_RADIO_TYPE_RFMD:
+ case ACX_RADIO_TYPE_RALINK:
+ map = acx100_txpower_rfmd;
+ break;
+ default:
+ printf("%s: TX power for radio type 0x%02x "
+ "can't be set yet\n", ifp->if_xname, sc->sc_radio_type);
+ return 1;
+ }
+
+ acx_write_phyreg(sc, ACXRV_PHYREG_TXPOWER, map[ACX100_TXPOWER]);
+ return 0;
+}
+
+void
+acx100_set_fw_txdesc_rate(struct acx_softc *sc, struct acx_txbuf *tx_buf,
+ int rate)
+{
+ FW_TXDESC_SETFIELD_1(sc, tx_buf, f_tx_rate100, ACX100_RATE(rate));
+}
+
+void
+acx100_set_bss_join_param(struct acx_softc *sc, void *param, int dtim_intvl)
+{
+ struct acx100_bss_join *bj = param;
+
+ bj->dtim_intvl = dtim_intvl;
+ bj->basic_rates = 15; /* XXX */
+ bj->all_rates = 31; /* XXX */
+}
+
+#if 0
+int
+acx100_set_wepkey(struct acx_softc *sc, struct ieee80211_key *wk, int wk_idx)
+{
+ struct acx100_conf_wepkey conf_wk;
+ struct ifnet *ifp = &sc->sc_ic.ic_if;
+
+ if (wk->wk_keylen > ACX100_WEPKEY_LEN) {
+ printf("%s: %dth WEP key size beyond %d\n",
+ ifp->if_xname, wk_idx, ACX100_WEPKEY_LEN);
+ return EINVAL;
+ }
+
+ conf_wk.action = ACX100_WEPKEY_ACT_ADD;
+ conf_wk.key_len = wk->wk_keylen;
+ conf_wk.key_idx = wk_idx;
+ bcopy(wk->wk_key, conf_wk.key, wk->wk_keylen);
+ if (acx100_set_wepkey_conf(sc, &conf_wk) != 0) {
+ printf("%s: %s set %dth WEP key failed\n",
+ ifp->if_xname, __func__, wk_idx);
+ return ENXIO;
+ }
+ return 0;
+}
+#endif
+
+void
+acx100_proc_wep_rxbuf(struct acx_softc *sc, struct mbuf *m, int *len)
+{
+ int mac_hdrlen;
+ struct ieee80211_frame *f;
+
+ /*
+ * Strip leading IV and KID, and trailing CRC
+ */
+
+ f = mtod(m, struct ieee80211_frame *);
+
+ if ((f->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS)
+ mac_hdrlen = sizeof(struct ieee80211_frame_addr4);
+ else
+ mac_hdrlen = sizeof(struct ieee80211_frame);
+
+#define IEEEWEP_IVLEN (IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN)
+#define IEEEWEP_EXLEN (IEEEWEP_IVLEN + IEEE80211_WEP_CRCLEN)
+
+ *len = *len - IEEEWEP_EXLEN;
+
+ /* Move MAC header toward frame body */
+ ovbcopy(f, (uint8_t *)f + IEEEWEP_IVLEN, mac_hdrlen);
+ m_adj(m, IEEEWEP_IVLEN);
+
+#undef IEEEWEP_EXLEN
+#undef IEEEWEP_IVLEN
+}
diff --git a/sys/dev/ic/acx111.c b/sys/dev/ic/acx111.c
new file mode 100644
index 00000000000..15453d5b7fd
--- /dev/null
+++ b/sys/dev/ic/acx111.c
@@ -0,0 +1,458 @@
+/* $Id: acx111.c,v 1.1 2006/08/03 08:45:01 mglocker Exp $ */
+
+/*
+ * Copyright (c) 2006 The DragonFly Project. All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Sepherosa Ziehau <sepherosa@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $DragonFly$
+ */
+
+#include <sys/param.h>
+#include <sys/endian.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/if_media.h>
+
+#include <net/if.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#endif
+
+#include <net80211/ieee80211_var.h>
+
+#include <dev/pci/pcireg.h>
+
+#define ACX_DEBUG
+
+#include <dev/ic/acxvar.h>
+#include <dev/ic/acxreg.h>
+
+#define ACX111_CONF_MEM 0x0003
+#define ACX111_CONF_MEMINFO 0x0005
+
+#define ACX111_INTR_ENABLE (ACXRV_INTR_TX_FINI | ACXRV_INTR_RX_FINI)
+/*
+ * XXX do we really care about fowlling interrupts?
+ *
+ * ACXRV_INTR_IV_ICV_FAILURE | ACXRV_INTR_INFO |
+ * ACXRV_INTR_SCAN_FINI | ACXRV_INTR_FCS_THRESHOLD
+ */
+
+#define ACX111_INTR_DISABLE (uint16_t)~(ACXRV_INTR_CMD_FINI)
+
+#define ACX111_RATE_2 0x0001
+#define ACX111_RATE_4 0x0002
+#define ACX111_RATE_11 0x0004
+#define ACX111_RATE_12 0x0008
+#define ACX111_RATE_18 0x0010
+#define ACX111_RATE_22 0x0020
+#define ACX111_RATE_24 0x0040
+#define ACX111_RATE_36 0x0080
+#define ACX111_RATE_44 0x0100
+#define ACX111_RATE_48 0x0200
+#define ACX111_RATE_72 0x0400
+#define ACX111_RATE_96 0x0800
+#define ACX111_RATE_108 0x1000
+#define ACX111_RATE(rate) [rate] = ACX111_RATE_##rate
+
+/* XXX skip ACX111_RATE_44 */
+#define ACX111_RATE_ALL 0x1eff
+
+#define ACX111_TXPOWER 15
+#define ACX111_GPIO_POWER_LED 0x0040
+#define ACX111_EE_EADDR_OFS 0x21
+
+#define ACX111_FW_TXDESC_SIZE (sizeof(struct acx_fw_txdesc) + 4)
+
+#if ACX111_TXPOWER <= 12
+#define ACX111_TXPOWER_VAL 1
+#else
+#define ACX111_TXPOWER_VAL 2
+#endif
+
+/*
+ * NOTE:
+ * Following structs' fields are little endian
+ */
+
+struct acx111_bss_join {
+ uint16_t basic_rates;
+ uint8_t dtim_intvl;
+} __packed;
+
+struct acx111_conf_mem {
+ struct acx_conf confcom;
+
+ uint16_t sta_max; /* max num of sta, ACX111_STA_MAX */
+ uint16_t memblk_size; /* mem block size */
+ uint8_t rx_memblk_perc; /* percent of RX mem block, unit: 5% */
+ uint8_t fw_rxring_num; /* num of RX ring */
+ uint8_t fw_txring_num; /* num of TX ring */
+ uint8_t opt; /* see ACX111_MEMOPT_ */
+ uint8_t xfer_perc; /* frag/xfer proportion, unit: 5% */
+ uint16_t reserved0;
+ uint8_t reserved1;
+
+ uint8_t fw_rxdesc_num; /* num of fw rx desc */
+ uint8_t fw_rxring_reserved1;
+ uint8_t fw_rxring_type; /* see ACX111_RXRING_TYPE_ */
+ uint8_t fw_rxring_prio; /* see ACX111_RXRING_PRIO_ */
+
+ uint32_t h_rxring_paddr; /* host rx desc start phyaddr */
+
+ uint8_t fw_txdesc_num; /* num of fw tx desc */
+ uint8_t fw_txring_reserved1;
+ uint8_t fw_txring_reserved2;
+ uint8_t fw_txring_attr; /* see ACX111_TXRING_ATTR_ */
+} __packed;
+
+#define ACX111_STA_MAX 32
+#define ACX111_RX_MEMBLK_PERCENT 10 /* 50% */
+#define ACX111_XFER_PERCENT 15 /* 75% */
+#define ACX111_RXRING_TYPE_DEFAULT 7
+#define ACX111_RXRING_PRIO_DEFAULT 0
+#define ACX111_TXRING_ATTR_DEFAULT 0
+#define ACX111_MEMOPT_DEFAULT 0
+
+struct acx111_conf_meminfo {
+ struct acx_conf confcom;
+ uint32_t tx_memblk_addr; /* start addr of tx mem blocks */
+ uint32_t rx_memblk_addr; /* start addr of rx mem blocks */
+ uint32_t fw_rxring_start; /* start phyaddr of fw rx ring */
+ uint32_t reserved0;
+ uint32_t fw_txring_start; /* start phyaddr of fw tx ring */
+ uint8_t fw_txring_attr; /* XXX see ACX111_TXRING_ATTR_ */
+ uint16_t reserved1;
+ uint8_t reserved2;
+} __packed;
+
+struct acx111_conf_txpower {
+ struct acx_conf confcom;
+ uint8_t txpower;
+} __packed;
+
+struct acx111_conf_option {
+ struct acx_conf confcom;
+ uint32_t feature;
+ uint32_t dataflow; /* see ACX111_DF_ */
+} __packed;
+
+#define ACX111_DF_NO_RXDECRYPT 0x00000080
+#define ACX111_DF_NO_TXENCRYPT 0x00000001
+
+struct acx111_wepkey {
+ uint8_t mac_addr[IEEE80211_ADDR_LEN];
+ uint16_t action; /* see ACX111_WEPKEY_ACT_ */
+ uint16_t reserved;
+ uint8_t key_len;
+ uint8_t key_type; /* see ACX111_WEPKEY_TYPE_ */
+ uint8_t index; /* XXX ?? */
+ uint8_t key_idx;
+ uint8_t counter[6];
+#define ACX111_WEPKEY_LEN 32
+ uint8_t key[ACX111_WEPKEY_LEN];
+} __packed;
+
+#define ACX111_WEPKEY_ACT_ADD 1
+#define ACX111_WEPKEY_TYPE_DEFAULT 0
+
+#define ACX111_CONF_FUNC(sg, name) _ACX_CONF_FUNC(sg, name, 111)
+#define ACX_CONF_mem ACX111_CONF_MEM
+#define ACX_CONF_meminfo ACX111_CONF_MEMINFO
+#define ACX_CONF_txpower ACX_CONF_TXPOWER
+#define ACX_CONF_option ACX_CONF_OPTION
+ACX111_CONF_FUNC(set, mem);
+ACX111_CONF_FUNC(get, meminfo);
+ACX111_CONF_FUNC(set, txpower);
+ACX111_CONF_FUNC(get, option);
+ACX111_CONF_FUNC(set, option);
+
+static const uint16_t acx111_reg[ACXREG_MAX] = {
+ ACXREG(SOFT_RESET, 0x0000),
+
+ ACXREG(FWMEM_ADDR, 0x0014),
+ ACXREG(FWMEM_DATA, 0x0018),
+ ACXREG(FWMEM_CTRL, 0x001c),
+ ACXREG(FWMEM_START, 0x0020),
+
+ ACXREG(EVENT_MASK, 0x0034),
+
+ ACXREG(INTR_TRIG, 0x00b4),
+ ACXREG(INTR_MASK, 0x00d4),
+ ACXREG(INTR_STATUS, 0x00f0),
+ ACXREG(INTR_STATUS_CLR, 0x00e4),
+ ACXREG(INTR_ACK, 0x00e8),
+
+ ACXREG(HINTR_TRIG, 0x00ec),
+ ACXREG(RADIO_ENABLE, 0x01d0),
+
+ ACXREG(EEPROM_INIT, 0x0100),
+ ACXREG(EEPROM_CTRL, 0x0338),
+ ACXREG(EEPROM_ADDR, 0x033c),
+ ACXREG(EEPROM_DATA, 0x0340),
+ ACXREG(EEPROM_CONF, 0x0344),
+ ACXREG(EEPROM_INFO, 0x0390),
+
+ ACXREG(PHY_ADDR, 0x0350),
+ ACXREG(PHY_DATA, 0x0354),
+ ACXREG(PHY_CTRL, 0x0358),
+
+ ACXREG(GPIO_OUT_ENABLE, 0x0374),
+ ACXREG(GPIO_OUT, 0x037c),
+
+ ACXREG(CMD_REG_OFFSET, 0x0388),
+ ACXREG(INFO_REG_OFFSET, 0x038c),
+
+ ACXREG(RESET_SENSE, 0x0104),
+ ACXREG(ECPU_CTRL, 0x0108)
+};
+
+/* XXX */
+static uint16_t acx111_rate_map[109] = {
+ ACX111_RATE(2),
+ ACX111_RATE(4),
+ ACX111_RATE(11),
+ ACX111_RATE(22),
+ ACX111_RATE(12),
+ ACX111_RATE(18),
+ ACX111_RATE(24),
+ ACX111_RATE(36),
+ ACX111_RATE(48),
+ ACX111_RATE(72),
+ ACX111_RATE(96),
+ ACX111_RATE(108)
+};
+
+int acx111_init(struct acx_softc *);
+int acx111_init_memory(struct acx_softc *);
+void acx111_init_fw_txring(struct acx_softc *, uint32_t);
+
+int acx111_write_config(struct acx_softc *, struct acx_config *);
+
+void acx111_set_fw_txdesc_rate(struct acx_softc *,
+ struct acx_txbuf *, int);
+void acx111_set_bss_join_param(struct acx_softc *, void *, int);
+
+
+void
+acx111_set_param(struct acx_softc *sc)
+{
+ sc->chip_mem1_rid = PCIR_BAR(0);
+ sc->chip_mem2_rid = PCIR_BAR(1);
+ sc->chip_ioreg = acx111_reg;
+ sc->chip_intr_enable = ACX111_INTR_ENABLE;
+ sc->chip_intr_disable = ACX111_INTR_DISABLE;
+ sc->chip_gpio_pled = ACX111_GPIO_POWER_LED;
+ sc->chip_ee_eaddr_ofs = ACX111_EE_EADDR_OFS;
+
+ sc->chip_phymode = IEEE80211_MODE_11G;
+ sc->chip_chan_flags = IEEE80211_CHAN_CCK |
+ IEEE80211_CHAN_OFDM |
+ IEEE80211_CHAN_DYN |
+ IEEE80211_CHAN_2GHZ;
+ sc->sc_ic.ic_caps = IEEE80211_C_WEP;
+ sc->sc_ic.ic_phytype = IEEE80211_T_OFDM;
+ sc->sc_ic.ic_sup_rates[IEEE80211_MODE_11B] = acx_rates_11b;
+ sc->sc_ic.ic_sup_rates[IEEE80211_MODE_11G] = acx_rates_11g;
+
+ sc->chip_init = acx111_init;
+ sc->chip_write_config = acx111_write_config;
+ sc->chip_set_fw_txdesc_rate = acx111_set_fw_txdesc_rate;
+ sc->chip_set_bss_join_param = acx111_set_bss_join_param;
+}
+
+int
+acx111_init(struct acx_softc *sc)
+{
+ struct ifnet *ifp = &sc->sc_ic.ic_if;
+
+ /*
+ * NOTE:
+ * Order of initialization:
+ * 1) Templates
+ * 2) Hardware memory
+ * Above order is critical to get a correct memory map
+ */
+
+ if (acx_init_tmplt_ordered(sc) != 0) {
+ printf("%s: %s can't initialize templates\n",
+ ifp->if_xname, __func__);
+ return ENXIO;
+ }
+
+ if (acx111_init_memory(sc) != 0) {
+ printf("%s: %s can't initialize hw memory\n",
+ ifp->if_xname, __func__);
+ return ENXIO;
+ }
+ return 0;
+}
+
+int
+acx111_init_memory(struct acx_softc *sc)
+{
+ struct acx111_conf_mem mem;
+ struct acx111_conf_meminfo mem_info;
+ struct ifnet *ifp = &sc->sc_ic.ic_if;
+
+ /* Set memory configuration */
+ bzero(&mem, sizeof(mem));
+
+ mem.sta_max = htole16(ACX111_STA_MAX);
+ mem.memblk_size = htole16(ACX_MEMBLOCK_SIZE);
+ mem.rx_memblk_perc = ACX111_RX_MEMBLK_PERCENT;
+ mem.opt = ACX111_MEMOPT_DEFAULT;
+ mem.xfer_perc = ACX111_XFER_PERCENT;
+
+ mem.fw_rxring_num = 1;
+ mem.fw_rxring_type = ACX111_RXRING_TYPE_DEFAULT;
+ mem.fw_rxring_prio = ACX111_RXRING_PRIO_DEFAULT;
+ mem.fw_rxdesc_num = ACX_RX_DESC_CNT;
+ mem.h_rxring_paddr = htole32(sc->sc_ring_data.rx_ring_paddr);
+
+ mem.fw_txring_num = 1;
+ mem.fw_txring_attr = ACX111_TXRING_ATTR_DEFAULT;
+ mem.fw_txdesc_num = ACX_TX_DESC_CNT;
+
+ if (acx111_set_mem_conf(sc, &mem) != 0) {
+ printf("%s: can't set mem\n", ifp->if_xname);
+ return 1;
+ }
+
+ /* Get memory configuration */
+ if (acx111_get_meminfo_conf(sc, &mem_info) != 0) {
+ printf("%s: can't get meminfo\n", ifp->if_xname);
+ return 1;
+ }
+
+ /* Setup firmware TX descriptor ring */
+ acx111_init_fw_txring(sc, letoh32(mem_info.fw_txring_start));
+
+ /*
+ * There is no need to setup firmware RX descriptor ring,
+ * it is automaticly setup by hardware.
+ */
+
+ return 0;
+}
+
+void
+acx111_init_fw_txring(struct acx_softc *sc, uint32_t fw_txdesc_start)
+{
+ struct acx_txbuf *tx_buf;
+ uint32_t desc_paddr;
+ int i;
+
+ tx_buf = sc->sc_buf_data.tx_buf;
+ desc_paddr = sc->sc_ring_data.tx_ring_paddr;
+
+ for (i = 0; i < ACX_TX_DESC_CNT; ++i) {
+ tx_buf[i].tb_fwdesc_ofs = fw_txdesc_start +
+ (i * ACX111_FW_TXDESC_SIZE);
+
+ /*
+ * Except for the following fields, rest of the fields
+ * are setup by hardware.
+ */
+ FW_TXDESC_SETFIELD_4(sc, &tx_buf[i], f_tx_host_desc,
+ desc_paddr);
+ FW_TXDESC_SETFIELD_1(sc, &tx_buf[i], f_tx_ctrl,
+ DESC_CTRL_HOSTOWN);
+
+ desc_paddr += (2 * sizeof(struct acx_host_desc));
+ }
+}
+
+int
+acx111_write_config(struct acx_softc *sc, struct acx_config *conf)
+{
+ struct acx111_conf_txpower tx_power;
+ struct acx111_conf_option opt;
+ struct ifnet *ifp = &sc->sc_ic.ic_if;
+ uint32_t dataflow;
+
+ /* Set TX power */
+ tx_power.txpower = ACX111_TXPOWER_VAL;
+ if (acx111_set_txpower_conf(sc, &tx_power) != 0) {
+ printf("%s: %s can't set TX power\n",
+ ifp->if_xname, __func__);
+ return ENXIO;
+ }
+
+ /*
+ * Turn off hardware WEP
+ */
+ if (acx111_get_option_conf(sc, &opt) != 0) {
+ printf("%s: %s can't get option\n", ifp->if_xname, __func__);
+ return ENXIO;
+ }
+
+ dataflow = letoh32(opt.dataflow) |
+ ACX111_DF_NO_TXENCRYPT |
+ ACX111_DF_NO_RXDECRYPT;
+ opt.dataflow = htole32(dataflow);
+
+ if (acx111_set_option_conf(sc, &opt) != 0) {
+ printf("%s: %s can't set option\n", ifp->if_xname, __func__);
+ return ENXIO;
+ }
+ return 0;
+}
+
+void
+acx111_set_fw_txdesc_rate(struct acx_softc *sc, struct acx_txbuf *tx_buf,
+ int rate0)
+{
+ uint16_t rate;
+
+ rate = acx111_rate_map[rate0];
+ if (rate == 0)
+ panic("no rate map for %d\n", rate0);
+
+ FW_TXDESC_SETFIELD_2(sc, tx_buf, u.r2.rate111, rate);
+}
+
+void
+acx111_set_bss_join_param(struct acx_softc *sc, void *param, int dtim_intvl)
+{
+ struct acx111_bss_join *bj = param;
+
+ bj->basic_rates = htole16(ACX111_RATE_ALL);
+ bj->dtim_intvl = dtim_intvl;
+}
diff --git a/sys/dev/ic/acxreg.h b/sys/dev/ic/acxreg.h
new file mode 100644
index 00000000000..2a55c6048dd
--- /dev/null
+++ b/sys/dev/ic/acxreg.h
@@ -0,0 +1,574 @@
+/* $Id: acxreg.h,v 1.1 2006/08/03 08:45:01 mglocker Exp $ */
+
+/*
+ * Copyright (c) 2006 The DragonFly Project. All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Sepherosa Ziehau <sepherosa@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $DragonFly$
+ */
+
+#ifndef _ACXREG_H
+#define _ACXREG_H
+
+/*
+ * IO register index
+ */
+#define ACXREG_SOFT_RESET 0
+#define ACXREG_FWMEM_ADDR 1
+#define ACXREG_FWMEM_DATA 2
+#define ACXREG_FWMEM_CTRL 3
+#define ACXREG_FWMEM_START 4
+#define ACXREG_EVENT_MASK 5
+#define ACXREG_INTR_TRIG 6
+#define ACXREG_INTR_MASK 7
+#define ACXREG_INTR_STATUS 8
+#define ACXREG_INTR_STATUS_CLR 9 /* cleared after being read */
+#define ACXREG_INTR_ACK 10
+#define ACXREG_HINTR_TRIG 11 /* XXX what's this? */
+#define ACXREG_RADIO_ENABLE 12
+#define ACXREG_EEPROM_INIT 13
+#define ACXREG_EEPROM_CTRL 14
+#define ACXREG_EEPROM_ADDR 15
+#define ACXREG_EEPROM_DATA 16
+#define ACXREG_EEPROM_CONF 17
+#define ACXREG_EEPROM_INFO 18
+#define ACXREG_PHY_ADDR 19
+#define ACXREG_PHY_DATA 20
+#define ACXREG_PHY_CTRL 21
+#define ACXREG_GPIO_OUT_ENABLE 22
+#define ACXREG_GPIO_OUT 23
+#define ACXREG_CMD_REG_OFFSET 24
+#define ACXREG_INFO_REG_OFFSET 25
+#define ACXREG_RESET_SENSE 26
+#define ACXREG_ECPU_CTRL 27
+#define ACXREG_MAX 28
+#define ACXREG(reg, val) [ACXREG_##reg] = val
+
+/*
+ * Value read from ACXREG_EEPROM_INFO
+ * upper 8bits are radio type
+ * lower 8bits are form factor
+ */
+#define ACX_EEINFO_RADIO_TYPE_SHIFT 8
+#define ACX_EEINFO_RADIO_TYPE_MASK (0xff << ACX_EEINFO_RADIO_TYPE_SHIFT)
+#define ACX_EEINFO_FORM_FACTOR_MASK 0xff
+
+#define ACX_EEINFO_HAS_RADIO_TYPE(info) ((info) & ACX_EEINFO_RADIO_TYPE_MASK)
+#define ACX_EEINFO_RADIO_TYPE(info) ((info) >> ACX_EEINFO_RADIO_TYPE_SHIFT)
+#define ACX_EEINFO_FORM_FACTOR(info) ((info) & ACX_EEINFO_FORM_FACTOR_MASK)
+
+/*
+ * Size of command register whose location is obtained
+ * from ACXREG_CMD_REG_OFFSET IO register
+ */
+#define ACX_CMD_REG_SIZE 4 /* 4 bytes */
+
+/*
+ * Size of infomation register whose location is obtained
+ * from ACXREG_INFO_REG_OFFSET IO register
+ */
+#define ACX_INFO_REG_SIZE 4 /* 4 bytes */
+
+/*
+ * Offset of EEPROM variables
+ */
+#define ACX_EE_VERSION_OFS 0x05
+
+/*
+ * Possible values for various IO registers
+ */
+
+/* ACXREG_SOFT_RESET */
+#define ACXRV_SOFT_RESET 0x1
+
+/* ACXREG_FWMEM_START */
+#define ACXRV_FWMEM_START_OP 0x0
+
+/* ACXREG_FWMEM_CTRL */
+#define ACXRV_FWMEM_ADDR_AUTOINC 0x10000
+
+/* ACXREG_EVENT_MASK */
+#define ACXRV_EVENT_DISABLE 0x8000 /* XXX What's this?? */
+
+/* ACXREG_INTR_TRIG */
+#define ACXRV_TRIG_CMD_FINI 0x0001
+#define ACXRV_TRIG_TX_FINI 0x0004
+
+/* ACXREG_INTR_MASK */
+#define ACXRV_INTR_RX_DATA 0x0001
+#define ACXRV_INTR_TX_FINI 0x0002
+#define ACXRV_INTR_TX_XFER 0x0004
+#define ACXRV_INTR_RX_FINI 0x0008
+#define ACXRV_INTR_DTIM 0x0010
+#define ACXRV_INTR_BEACON 0x0020
+#define ACXRV_INTR_TIMER 0x0040
+#define ACXRV_INTR_KEY_MISS 0x0080
+#define ACXRV_INTR_WEP_FAIL 0x0100
+#define ACXRV_INTR_CMD_FINI 0x0200
+#define ACXRV_INTR_INFO 0x0400
+#define ACXRV_INTR_OVERFLOW 0x0800 /* XXX */
+#define ACXRV_INTR_PROC_ERR 0x1000 /* XXX */
+#define ACXRV_INTR_SCAN_FINI 0x2000
+#define ACXRV_INTR_FCS_THRESH 0x4000 /* XXX */
+#define ACXRV_INTR_UNKN 0x8000
+#define ACXRV_INTR_ALL 0xffff
+
+/* ACXREG_EEPROM_INIT */
+#define ACXRV_EEPROM_INIT 0x1
+
+/* ACXREG_EEPROM_CTRL */
+#define ACXRV_EEPROM_READ 0x2
+
+/* ACXREG_PHY_CTRL */
+#define ACXRV_PHY_WRITE 0x1
+#define ACXRV_PHY_READ 0x2
+
+/* ACXREG_PHY_ADDR */
+#define ACXRV_PHYREG_TXPOWER 0x11 /* axc100 */
+#define ACXRV_PHYREG_SENSITIVITY 0x30
+
+/* ACXREG_ECPU_CTRL */
+#define ACXRV_ECPU_HALT 0x1
+#define ACXRV_ECPU_START 0x0
+
+/* Commands */
+#define ACXCMD_GET_CONF 0x01
+#define ACXCMD_SET_CONF 0x02
+#define ACXCMD_ENABLE_RXCHAN 0x03
+#define ACXCMD_ENABLE_TXCHAN 0x04
+#define ACXCMD_TMPLT_TIM 0x0a
+#define ACXCMD_JOIN_BSS 0x0b
+#define ACXCMD_WEP_MGMT 0x0c /* acx111 */
+#define ACXCMD_SLEEP 0x0f
+#define ACXCMD_WAKEUP 0x10
+#define ACXCMD_INIT_MEM 0x12 /* acx100 */
+#define ACXCMD_TMPLT_BEACON 0x13
+#define ACXCMD_TMPLT_PROBE_RESP 0x14
+#define ACXCMD_TMPLT_NULL_DATA 0x15
+#define ACXCMD_TMPLT_PROBE_REQ 0x16
+#define ACXCMD_INIT_RADIO 0x18
+
+#if 0
+/*
+ * acx111 does not agree with acx100 about
+ * the meaning of following values. So they
+ * are put into chip specific files.
+ */
+#define ACX_CONF_FW_RING 0x0003
+#define ACX_CONF_MEMOPT 0x0005
+#endif
+#define ACX_CONF_MEMBLK_SIZE 0x0004 /* acx100 */
+#define ACX_CONF_RATE_FALLBACK 0x0006
+#define ACX_CONF_WEPOPT 0x0007 /* acx100 */
+#define ACX_CONF_MMAP 0x0008
+#define ACX_CONF_FWREV 0x000d
+#define ACX_CONF_RXOPT 0x0010
+#define ACX_CONF_OPTION 0x0015 /* acx111 */
+#define ACX_CONF_EADDR 0x1001
+#define ACX_CONF_NRETRY_SHORT 0x1005
+#define ACX_CONF_NRETRY_LONG 0x1006
+#define ACX_CONF_WEPKEY 0x1007 /* acx100 */
+#define ACX_CONF_MSDU_LIFETIME 0x1008
+#define ACX_CONF_REGDOM 0x100a
+#define ACX_CONF_ANTENNA 0x100b
+#define ACX_CONF_TXPOWER 0x100d /* acx111 */
+#define ACX_CONF_CCA_MODE 0x100e
+#define ACX_CONF_ED_THRESH 0x100f
+#define ACX_CONF_WEP_TXKEY 0x1010
+
+/*
+ * NOTE:
+ * Following structs' fields are little endian
+ */
+
+struct acx_conf {
+ uint16_t conf_id; /* see ACXCONF_ (_acxcmd.h) */
+ uint16_t conf_data_len;
+} __packed;
+
+struct acx_conf_mmap {
+ struct acx_conf confcom;
+ uint32_t code_start;
+ uint32_t code_end;
+ uint32_t wep_cache_start;
+ uint32_t wep_cache_end;
+ uint32_t pkt_tmplt_start;
+ uint32_t pkt_tmplt_end;
+ uint32_t fw_desc_start;
+ uint32_t fw_desc_end;
+ uint32_t memblk_start;
+ uint32_t memblk_end;
+} __packed;
+
+struct acx_conf_wepopt {
+ struct acx_conf confcom;
+ uint16_t nkey;
+ uint8_t opt; /* see WEPOPT_ */
+} __packed;
+
+#define WEPOPT_HDWEP 0 /* hardware WEP */
+
+struct acx_conf_eaddr {
+ struct acx_conf confcom;
+ uint8_t eaddr[IEEE80211_ADDR_LEN];
+} __packed;
+
+struct acx_conf_regdom {
+ struct acx_conf confcom;
+ uint8_t regdom;
+ uint8_t unknown;
+} __packed;
+
+struct acx_conf_antenna {
+ struct acx_conf confcom;
+ uint8_t antenna;
+} __packed;
+
+struct acx_conf_fwrev {
+ struct acx_conf confcom;
+#define ACX_FWREV_LEN 20
+ /*
+ * "Rev xx.xx.xx.xx"
+ * '\0' terminated
+ */
+ char fw_rev[ACX_FWREV_LEN];
+ uint32_t hw_id;
+} __packed;
+
+struct acx_conf_nretry_long {
+ struct acx_conf confcom;
+ uint8_t nretry;
+} __packed;
+
+struct acx_conf_nretry_short {
+ struct acx_conf confcom;
+ uint8_t nretry;
+} __packed;
+
+struct acx_conf_msdu_lifetime {
+ struct acx_conf confcom;
+ uint32_t lifetime;
+} __packed;
+
+struct acx_conf_rate_fallback {
+ struct acx_conf confcom;
+ uint8_t ratefb_enable; /* 0/1 */
+} __packed;
+
+struct acx_conf_rxopt {
+ struct acx_conf confcom;
+ uint16_t opt1; /* see RXOPT1_ */
+ uint16_t opt2; /* see RXOPT2_ */
+} __packed;
+
+#define RXOPT1_INCL_RXBUF_HDR 0x2000 /* rxbuf with acx_rxbuf_hdr */
+#define RXOPT1_RECV_SSID 0x0400 /* recv frame for joined SSID */
+#define RXOPT1_FILT_BCAST 0x0200 /* filt broadcast pkt */
+#define RXOPT1_RECV_MCAST1 0x0100 /* recv pkt for multicast addr1 */
+#define RXOPT1_RECV_MCAST0 0x0080 /* recv pkt for multicast addr0 */
+#define RXOPT1_FILT_ALLMULTI 0x0040 /* filt allmulti pkt */
+#define RXOPT1_FILT_FSSID 0x0020 /* filt frame for foreign SSID */
+#define RXOPT1_FILT_FDEST 0x0010 /* filt frame for foreign dest addr */
+#define RXOPT1_PROMISC 0x0008 /* promisc mode */
+#define RXOPT1_INCL_FCS 0x0004
+#define RXOPT1_INCL_PHYHDR 0x0000 /* XXX 0x0002 */
+
+#define RXOPT2_RECV_ASSOC_REQ 0x0800
+#define RXOPT2_RECV_AUTH 0x0400
+#define RXOPT2_RECV_BEACON 0x0200
+#define RXOPT2_RECV_CF 0x0100
+#define RXOPT2_RECV_CTRL 0x0080
+#define RXOPT2_RECV_DATA 0x0040
+#define RXOPT2_RECV_BROKEN 0x0020 /* broken frame */
+#define RXOPT2_RECV_MGMT 0x0010
+#define RXOPT2_RECV_PROBE_REQ 0x0008
+#define RXOPT2_RECV_PROBE_RESP 0x0004
+#define RXOPT2_RECV_ACK 0x0002 /* RTS/CTS/ACK */
+#define RXOPT2_RECV_OTHER 0x0001
+
+struct acx_conf_wep_txkey {
+ struct acx_conf confcom;
+ uint8_t wep_txkey;
+} __packed;
+
+
+struct acx_tmplt_null_data {
+ uint16_t size;
+ struct ieee80211_frame data;
+} __packed;
+
+struct acx_tmplt_probe_req {
+ uint16_t size;
+ union {
+ struct {
+ struct ieee80211_frame f;
+ uint8_t var[1];
+ } __packed u_data;
+ uint8_t u_mem[0x44];
+ } data;
+} __packed;
+
+#define ACX_TMPLT_PROBE_REQ_SIZ(var_len) \
+ (sizeof(uint16_t) + sizeof(struct ieee80211_frame) + (var_len))
+
+struct acx_tmplt_probe_resp {
+ uint16_t size;
+ union {
+ struct {
+ struct ieee80211_frame f;
+ uint8_t time_stamp[8];
+ uint16_t beacon_intvl;
+ uint16_t cap;
+ uint8_t var[1];
+ } __packed u_data;
+ uint8_t u_mem[0x54];
+ } data;
+} __packed;
+
+#define ACX_TMPLT_PROBE_RESP_SIZ(var_len) \
+ (sizeof(uint16_t) + sizeof(struct ieee80211_frame) + \
+ 8 * sizeof(uint8_t) + sizeof(uint16_t) + sizeof(uint16_t) + (var_len))
+
+/* XXX same as acx_tmplt_probe_resp */
+struct acx_tmplt_beacon {
+ uint16_t size;
+ union {
+ struct {
+ struct ieee80211_frame f;
+ uint8_t time_stamp[8];
+ uint16_t beacon_intvl;
+ uint16_t cap;
+ uint8_t var[1];
+ } __packed u_data;
+ uint8_t u_mem[0x54];
+ } data;
+} __packed;
+
+/* XXX C&P of ACX_TMPLT_PROVE_RESP_SIZ() */
+#define ACX_TMPLT_BEACON_SIZ(var_len) \
+ (sizeof(uint16_t) + sizeof(struct ieee80211_frame) + \
+ 8 * sizeof(uint8_t) + sizeof(uint16_t) + sizeof(uint16_t) + (var_len))
+
+/* XXX do NOT belong here */
+struct tim_head {
+ uint8_t eid;
+ uint8_t len;
+ uint8_t dtim_count;
+ uint8_t dtim_period;
+ uint8_t bitmap_ctrl;
+} __packed;
+
+/* For tim_head.len (tim_head - eid - len + bitmap) */
+#define ACX_TIM_LEN(bitmap_len) \
+ (sizeof(struct tim_head) - (2 * sizeof(uint8_t)) + (bitmap_len))
+#define ACX_TIM_BITMAP_LEN 5
+
+struct acx_tmplt_tim {
+ uint16_t size;
+ union {
+ struct {
+ struct tim_head th;
+ uint8_t bitmap[1];
+ } __packed u_data;
+ uint8_t u_mem[0x100];
+ } data;
+#define tim_eid data.u_data.th.eid
+#define tim_len data.u_data.th.len
+#define tim_dtim_count data.u_data.th.dtim_count
+#define tim_dtim_period data.u_data.th.dtim_period
+#define tim_bitmap_ctrl data.u_data.th.bitmap_ctrl
+#define tim_bitmap data.u_data.bitmap
+} __packed;
+
+#define ACX_TMPLT_TIM_SIZ(bitmap_len) \
+ (sizeof(uint16_t) + sizeof(struct tim_head) + (bitmap_len))
+
+
+#define ACX_INIT_TMPLT_FUNC(name) \
+static __inline int \
+acx_init_##name##_tmplt(struct acx_softc *_sc) \
+{ \
+ struct acx_tmplt_##name _tmplt; \
+ \
+ bzero(&_tmplt, sizeof(_tmplt)); \
+ return acx_set_tmplt(_sc, ACXCMD_TMPLT_##name, \
+ &_tmplt, sizeof(_tmplt)); \
+} \
+struct __hack
+
+#define ACX_SET_TMPLT_FUNC(name) \
+static __inline int \
+_acx_set_##name##_tmplt(struct acx_softc *_sc, \
+ struct acx_tmplt_##name *_tmplt, \
+ uint16_t _tmplt_len) \
+{ \
+ return acx_set_tmplt(_sc, ACXCMD_TMPLT_##name, \
+ _tmplt, _tmplt_len); \
+} \
+struct __hack
+
+#define _ACX_CONF_FUNC(sg, name, chip) \
+static __inline int \
+acx##chip##_##sg##_##name##_conf(struct acx_softc *_sc, \
+ struct acx##chip##_conf_##name *_conf) \
+{ \
+ return acx_##sg##_conf(_sc, ACX_CONF_##name, \
+ _conf, sizeof(*_conf)); \
+} \
+struct __hack
+
+#define ACX_NOARG_FUNC(name) \
+static __inline int \
+acx_##name(struct acx_softc *_sc) \
+{ \
+ return acx_exec_command(_sc, ACXCMD_##name, \
+ NULL, 0, NULL, 0); \
+} \
+struct __hack
+
+
+#define ACXCMD_TMPLT_tim ACXCMD_TMPLT_TIM
+#define ACXCMD_TMPLT_beacon ACXCMD_TMPLT_BEACON
+#define ACXCMD_TMPLT_probe_resp ACXCMD_TMPLT_PROBE_RESP
+#define ACXCMD_TMPLT_null_data ACXCMD_TMPLT_NULL_DATA
+#define ACXCMD_TMPLT_probe_req ACXCMD_TMPLT_PROBE_REQ
+ACX_INIT_TMPLT_FUNC(tim);
+ACX_INIT_TMPLT_FUNC(null_data);
+ACX_INIT_TMPLT_FUNC(beacon);
+ACX_INIT_TMPLT_FUNC(probe_req);
+ACX_INIT_TMPLT_FUNC(probe_resp);
+ACX_SET_TMPLT_FUNC(tim);
+ACX_SET_TMPLT_FUNC(null_data);
+ACX_SET_TMPLT_FUNC(beacon);
+ACX_SET_TMPLT_FUNC(probe_req);
+ACX_SET_TMPLT_FUNC(probe_resp);
+
+#define ACX_CONF_FUNC(sg, name) _ACX_CONF_FUNC(sg, name,)
+#define ACX_CONF_wepopt ACX_CONF_WEPOPT
+#define ACX_CONF_mmap ACX_CONF_MMAP
+#define ACX_CONF_eaddr ACX_CONF_EADDR
+#define ACX_CONF_regdom ACX_CONF_REGDOM
+#define ACX_CONF_antenna ACX_CONF_ANTENNA
+#define ACX_CONF_fwrev ACX_CONF_FWREV
+#define ACX_CONF_nretry_long ACX_CONF_NRETRY_LONG
+#define ACX_CONF_nretry_short ACX_CONF_NRETRY_SHORT
+#define ACX_CONF_msdu_lifetime ACX_CONF_MSDU_LIFETIME
+#define ACX_CONF_rate_fallback ACX_CONF_RATE_FALLBACK
+#define ACX_CONF_rxopt ACX_CONF_RXOPT
+#define ACX_CONF_wep_txkey ACX_CONF_WEP_TXKEY
+ACX_CONF_FUNC(get, mmap);
+ACX_CONF_FUNC(set, mmap);
+ACX_CONF_FUNC(set, wepopt);
+ACX_CONF_FUNC(get, eaddr);
+ACX_CONF_FUNC(get, regdom);
+ACX_CONF_FUNC(set, regdom);
+ACX_CONF_FUNC(get, antenna);
+ACX_CONF_FUNC(set, antenna);
+ACX_CONF_FUNC(get, fwrev);
+ACX_CONF_FUNC(set, nretry_long);
+ACX_CONF_FUNC(set, nretry_short);
+ACX_CONF_FUNC(set, msdu_lifetime);
+ACX_CONF_FUNC(set, rate_fallback);
+ACX_CONF_FUNC(set, rxopt);
+ACX_CONF_FUNC(set, wep_txkey);
+
+#define ACXCMD_sleep ACXCMD_SLEEP
+#define ACXCMD_wakeup ACXCMD_WAKEUP
+ACX_NOARG_FUNC(sleep);
+ACX_NOARG_FUNC(wakeup);
+
+#define CMDPRM_WRITE_REGION_1(sc, r, rlen) \
+ bus_space_write_region_1((sc)->sc_mem2_bt, \
+ (sc)->sc_mem2_bh, \
+ (sc)->sc_cmd_param, \
+ (const uint8_t *)(r), (rlen))
+
+#define CMDPRM_READ_REGION_1(sc, r, rlen) \
+ bus_space_read_region_1((sc)->sc_mem2_bt, (sc)->sc_mem2_bh, \
+ (sc)->sc_cmd_param, (uint8_t *)(r), (rlen))
+
+/*
+ * This will clear previous command's
+ * execution status too
+ */
+#define CMD_WRITE_4(sc, val) \
+ bus_space_write_4((sc)->sc_mem2_bt, (sc)->sc_mem2_bh, \
+ (sc)->sc_cmd, (val))
+#define CMD_READ_4(sc) \
+ bus_space_read_4((sc)->sc_mem2_bt, (sc)->sc_mem2_bh, (sc)->sc_cmd)
+
+/*
+ * acx command register layerout:
+ * upper 16bits are command execution status
+ * lower 16bits are command to be executed
+ */
+#define ACX_CMD_STATUS_SHIFT 16
+#define ACX_CMD_STATUS_OK 1
+
+struct radio_init {
+ uint32_t radio_ofs; /* radio firmware offset */
+ uint32_t radio_len; /* radio firmware length */
+} __packed;
+
+struct bss_join_hdr {
+ uint8_t bssid[IEEE80211_ADDR_LEN];
+ uint16_t beacon_intvl;
+ uint8_t chip_spec[3];
+ uint8_t ndata_txrate; /* see ACX_NDATA_TXRATE_ */
+ uint8_t ndata_txopt; /* see ACX_NDATA_TXOPT_ */
+ uint8_t mode; /* see ACX_MODE_ */
+ uint8_t channel;
+ uint8_t esslen;
+ char essid[1];
+} __packed;
+
+/*
+ * non-data frame tx rate
+ */
+#define ACX_NDATA_TXRATE_2 20 /* 2Mbits/s */
+
+/*
+ * non-data frame tx options
+ */
+#define ACX_NDATA_TXOPT_PBCC 0x40
+#define ACX_NDATA_TXOPT_OFDM 0x20
+#define ACX_NDATA_TXOPT_SHORT_PREAMBLE 0x10
+
+#define BSS_JOIN_BUFLEN \
+ (sizeof(struct bss_join_hdr) + IEEE80211_NWID_LEN - 1)
+#define BSS_JOIN_PARAM_SIZE(bj) \
+ (sizeof(struct bss_join_hdr) + (bj)->esslen)
+
+
+#define PCIR_BAR(x) (PCI_MAPS + (x) * 4)
+
+#endif /* !_ACXREG_H */
diff --git a/sys/dev/ic/acxvar.h b/sys/dev/ic/acxvar.h
new file mode 100644
index 00000000000..c439c3a3246
--- /dev/null
+++ b/sys/dev/ic/acxvar.h
@@ -0,0 +1,469 @@
+/* $Id: acxvar.h,v 1.1 2006/08/03 08:45:01 mglocker Exp $ */
+
+/*
+ * Copyright (c) 2006 The DragonFly Project. All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Sepherosa Ziehau <sepherosa@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $DragonFly$
+ */
+
+#ifndef _IF_ACXVAR_H
+#define _IF_ACXVAR_H
+
+#ifdef ACX_DEBUG
+extern int acxdebug;
+#define DPRINTF(x) do { if (acxdebug) printf x; } while (0)
+#define DPRINTFN(n,x) do { if (acxdebug >= (n)) printf x; } while (0)
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+#ifndef __offsetof
+#define __offsetof(type, field) ((size_t)(&((type *)0)->field))
+#endif
+
+#define ACX_FRAME_HDRLEN sizeof(struct ieee80211_frame)
+#define ACX_MEMBLOCK_SIZE 256
+
+#define ACX_TX_DESC_CNT 16
+#define ACX_RX_DESC_CNT 16
+
+#define ACX_TX_RING_SIZE \
+ (2 * ACX_TX_DESC_CNT * sizeof(struct acx_host_desc))
+#define ACX_RX_RING_SIZE \
+ (ACX_RX_DESC_CNT * sizeof(struct acx_host_desc))
+
+#define CSR_READ_1(sc, reg) \
+ bus_space_read_1((sc)->sc_mem1_bt, (sc)->sc_mem1_bh, \
+ (sc)->chip_ioreg[(reg)])
+#define CSR_READ_2(sc, reg) \
+ bus_space_read_2((sc)->sc_mem1_bt, (sc)->sc_mem1_bh, \
+ (sc)->chip_ioreg[(reg)])
+#define CSR_READ_4(sc, reg) \
+ bus_space_read_4((sc)->sc_mem1_bt, (sc)->sc_mem1_bh, \
+ (sc)->chip_ioreg[(reg)])
+
+#define CSR_WRITE_2(sc, reg, val) \
+ bus_space_write_2((sc)->sc_mem1_bt, (sc)->sc_mem1_bh, \
+ (sc)->chip_ioreg[(reg)], val)
+#define CSR_WRITE_4(sc, reg, val) \
+ bus_space_write_4((sc)->sc_mem1_bt, (sc)->sc_mem1_bh, \
+ (sc)->chip_ioreg[(reg)], val)
+
+#define CSR_SETB_2(sc, reg, b) \
+ CSR_WRITE_2((sc), (reg), CSR_READ_2((sc), (reg)) | (b))
+#define CSR_CLRB_2(sc, reg, b) \
+ CSR_WRITE_2((sc), (reg), CSR_READ_2((sc), (reg)) & (~(b)))
+
+#define DESC_READ_1(sc, off) \
+ bus_space_read_1((sc)->sc_mem2_bt, (sc)->sc_mem2_bh, (off))
+#define DESC_READ_4(sc, off) \
+ bus_space_read_4((sc)->sc_mem2_bt, (sc)->sc_mem2_bh, (off))
+
+#define DESC_WRITE_1(sc, off, val) \
+ bus_space_write_1((sc)->sc_mem2_bt, (sc)->sc_mem2_bh, (off), (val))
+#define DESC_WRITE_2(sc, off, val) \
+ bus_space_write_2((sc)->sc_mem2_bt, (sc)->sc_mem2_bh, (off), (val))
+#define DESC_WRITE_4(sc, off, val) \
+ bus_space_write_4((sc)->sc_mem2_bt, (sc)->sc_mem2_bh, (off), (val))
+#define DESC_WRITE_REGION_1(sc, off, d, dlen) \
+ bus_space_write_region_1((sc)->sc_mem2_bt, (sc)->sc_mem2_bh, \
+ (off), (const uint8_t *)(d), (dlen))
+
+#define FW_TXDESC_SETFIELD(sc, mb, field, val, sz) \
+ DESC_WRITE_##sz((sc), (mb)->tb_fwdesc_ofs + \
+ __offsetof(struct acx_fw_txdesc, field), (val))
+
+#define FW_TXDESC_GETFIELD(sc, mb, field, sz) \
+ DESC_READ_##sz((sc), (mb)->tb_fwdesc_ofs + \
+ __offsetof(struct acx_fw_txdesc, field))
+
+#define FW_TXDESC_SETFIELD_1(sc, mb, field, val) \
+ FW_TXDESC_SETFIELD(sc, mb, field, val, 1)
+#define FW_TXDESC_SETFIELD_2(sc, mb, field, val) \
+ FW_TXDESC_SETFIELD(sc, mb, field, htole16(val), 2)
+#define FW_TXDESC_SETFIELD_4(sc, mb, field, val) \
+ FW_TXDESC_SETFIELD(sc, mb, field, htole32(val), 4)
+
+#define FW_TXDESC_GETFIELD_1(sc, mb, field) \
+ FW_TXDESC_GETFIELD(sc, mb, field, 1)
+#define FW_TXDESC_GETFIELD_4(sc, mb, field) \
+ le32toh(FW_TXDESC_GETFIELD(sc, mb, field, 4))
+
+/*
+ * Firmware TX descriptor
+ * Fields are little endian
+ */
+struct acx_fw_txdesc {
+ uint32_t f_tx_next_desc; /* next acx_fw_txdesc phyaddr */
+ uint32_t f_tx_host_desc; /* acx_host_desc phyaddr */
+ uint32_t f_tx_acx_ptr;
+ uint32_t f_tx_time;
+ uint16_t f_tx_len;
+ uint16_t f_tx_reserved;
+
+ uint32_t f_tx_dev_spec[4];
+
+ uint8_t f_tx_ctrl; /* see DESC_CTRL_ */
+ uint8_t f_tx_ctrl2;
+ uint8_t f_tx_error; /* see DESC_ERR_ */
+ uint8_t f_tx_ack_fail;
+ uint8_t f_tx_rts_fail;
+ uint8_t f_tx_rts_ok;
+
+ /* XXX should be moved to chip specific file */
+ union {
+ struct {
+ uint8_t rate100; /* acx100 tx rate */
+ uint8_t queue_ctrl;
+ } __packed r1;
+ struct {
+ uint16_t rate111; /* acx111 tx rate */
+ } __packed r2;
+ } u;
+#define f_tx_rate100 u.r1.rate100
+#define f_tx_queue_ctrl u.r1.queue_ctrl
+#define f_tx_rate111 u.r2.rate111
+ uint32_t f_tx_queue_info;
+} __packed;
+
+/*
+ * Firmware RX descriptor
+ * Fields are little endian
+ */
+struct acx_fw_rxdesc {
+ uint32_t f_rx_next_desc; /* next acx_fw_rxdesc phyaddr */
+ uint32_t f_rx_host_desc; /* acx_host_desc phyaddr */
+ uint32_t f_rx_acx_ptr;
+ uint32_t f_rx_time;
+ uint16_t f_rx_len;
+ uint16_t f_rx_wep_len;
+ uint32_t f_rx_wep_ofs;
+
+ uint8_t f_rx_dev_spec[16];
+
+ uint8_t f_rx_ctrl; /* see DESC_CTRL_ */
+ uint8_t f_rx_rate;
+ uint8_t f_rx_error;
+ uint8_t f_rx_snr; /* signal noise ratio */
+ uint8_t f_rx_level;
+ uint8_t f_rx_queue_ctrl;
+ uint16_t f_rx_unknown0;
+ uint32_t f_rx_unknown1;
+} __packed;
+
+/*
+ * Host TX/RX descriptor
+ * Fields are little endian
+ */
+struct acx_host_desc {
+ uint32_t h_data_paddr; /* data phyaddr */
+ uint16_t h_data_ofs;
+ uint16_t h_reserved;
+ uint16_t h_ctrl; /* see DESC_CTRL_ */
+ uint16_t h_data_len; /* data length */
+ uint32_t h_next_desc; /* next acx_host_desc phyaddr */
+ uint32_t h_pnext;
+ uint32_t h_status; /* see DESC_STATUS_ */
+} __packed;
+
+#define DESC_STATUS_FULL 0x80000000
+
+#define DESC_CTRL_SHORT_PREAMBLE 0x01
+#define DESC_CTRL_FIRST_FRAG 0x02
+#define DESC_CTRL_AUTODMA 0x04
+#define DESC_CTRL_RECLAIM 0x08
+#define DESC_CTRL_HOSTDONE 0x20 /* host finished buf proc */
+#define DESC_CTRL_ACXDONE 0x40 /* chip finished buf proc */
+#define DESC_CTRL_HOSTOWN 0x80 /* host controls desc */
+
+#define DESC_ERR_OTHER_FRAG 0x01
+#define DESC_ERR_ABORT 0x02
+#define DESC_ERR_PARAM 0x04
+#define DESC_ERR_NO_WEPKEY 0x08
+#define DESC_ERR_MSDU_TIMEOUT 0x10
+#define DESC_ERR_EXCESSIVE_RETRY 0x20
+#define DESC_ERR_BUF_OVERFLOW 0x40
+#define DESC_ERR_DMA 0x80
+
+/*
+ * Extra header in receiving buffer
+ * Fields are little endian
+ */
+struct acx_rxbuf_hdr {
+ uint16_t rbh_len; /* ACX_RXBUG_LEN_MASK part is len */
+ uint8_t rbh_memblk_cnt;
+ uint8_t rbh_status;
+ uint8_t rbh_stat_baseband; /* see ACX_RXBUF_STAT_ */
+ uint8_t rbh_plcp;
+ uint8_t rbh_level; /* signal level */
+ uint8_t rbh_snr; /* signal noise ratio */
+ uint32_t rbh_time; /* recv timestamp */
+
+ /*
+ * XXX may have 4~8 byte here which
+ * depends on firmware version
+ */
+} __packed;
+
+#define ACX_RXBUF_LEN_MASK 0xfff
+#define ACX_RXBUF_STAT_LNA 0x80 /* low noise amplifier */
+
+struct acx_ring_data {
+ struct acx_host_desc *rx_ring;
+ bus_dma_segment_t rx_ring_seg;
+ bus_dmamap_t rx_ring_dmamap;
+ uint32_t rx_ring_paddr;
+
+ struct acx_host_desc *tx_ring;
+ bus_dma_segment_t tx_ring_seg;
+ bus_dmamap_t tx_ring_dmamap;
+ uint32_t tx_ring_paddr;
+};
+
+struct acx_txbuf {
+ struct mbuf *tb_mbuf;
+ bus_dmamap_t tb_mbuf_dmamap;
+
+ struct acx_host_desc *tb_desc1;
+ struct acx_host_desc *tb_desc2;
+
+ uint32_t tb_fwdesc_ofs;
+
+ /*
+ * Used by tx rate updating
+ */
+ struct acx_node *tb_node; /* remote node */
+ int tb_rate; /* current tx rate */
+};
+
+struct acx_rxbuf {
+ struct mbuf *rb_mbuf;
+ bus_dmamap_t rb_mbuf_dmamap;
+
+ struct acx_host_desc *rb_desc;
+};
+
+struct acx_buf_data {
+ struct acx_rxbuf rx_buf[ACX_RX_DESC_CNT];
+ struct acx_txbuf tx_buf[ACX_TX_DESC_CNT];
+ bus_dmamap_t mbuf_tmp_dmamap;
+
+ int rx_scan_start;
+
+ int tx_free_start;
+ int tx_used_start;
+ int tx_used_count;
+};
+
+struct acx_node {
+ struct ieee80211_node nd_node; /* MUST be first */
+
+ struct ieee80211_rateset nd_rates; /* shared rates */
+ int nd_txrate; /* index into nd_rates[] */
+
+ int nd_txrate_upd_intvl; /* tx rate upd interval */
+ int nd_txrate_upd_time; /* tx rate upd timestamp */
+ int nd_txrate_sample; /* num of samples for specific rate */
+};
+
+struct acx_firmware {
+ uint8_t *base_fw;
+ int base_fw_len;
+
+ uint8_t *radio_fw;
+ int radio_fw_len;
+};
+
+struct acx_config {
+ uint8_t eaddr[IEEE80211_ADDR_LEN];
+ uint8_t antenna;
+ uint8_t regdom;
+ uint8_t cca_mode; /* acx100 */
+ uint8_t ed_thresh; /* acx100 */
+};
+
+struct acx_stats {
+ uint64_t err_oth_frag; /* XXX error in other frag?? */
+ uint64_t err_abort; /* tx abortion */
+ uint64_t err_param; /* tx desc contains invalid param */
+ uint64_t err_no_wepkey; /* no WEP key exists */
+ uint64_t err_msdu_timeout; /* MSDU timed out */
+ uint64_t err_ex_retry; /* excessive tx retry */
+ uint64_t err_buf_oflow; /* buffer overflow */
+ uint64_t err_dma; /* DMA error */
+ uint64_t err_unkn; /* XXX unknown error */
+};
+
+struct acx_softc {
+ /*
+ * sc_xxx are filled in by common code
+ * chip_xxx are filled in by chip specific code
+ */
+ struct device sc_dev;
+ struct ieee80211com sc_ic;
+
+ struct timeout sc_chanscan_timer;
+ uint32_t sc_flags; /* see ACX_FLAG_ */
+
+ struct acx_firmware sc_firmware;
+ uint32_t sc_firmware_ver;
+ uint32_t sc_hardware_id;
+
+ bus_dma_tag_t sc_dmat;
+
+ /*
+ * MMIO 1
+ */
+ struct resource *sc_mem1_res;
+ bus_space_tag_t sc_mem1_bt;
+ bus_space_handle_t sc_mem1_bh;
+ int chip_mem1_rid;
+
+ /*
+ * MMIO 2
+ */
+ struct resource *sc_mem2_res;
+ bus_space_tag_t sc_mem2_bt;
+ bus_space_handle_t sc_mem2_bh;
+ int chip_mem2_rid;
+
+ struct resource *sc_irq_res;
+ void *sc_irq_handle;
+ int sc_irq_rid;
+
+ int (*sc_enable)(struct acx_softc *);
+ void (*sc_disable)(struct acx_softc *);
+ void (*sc_power)(struct acx_softc *, int);
+
+ uint32_t sc_cmd; /* cmd reg (MMIO 2) */
+ uint32_t sc_cmd_param; /* cmd param reg (MMIO 2) */
+ uint32_t sc_info; /* unused */
+ uint32_t sc_info_param; /* unused */
+
+ const uint16_t *chip_ioreg; /* reg map (MMIO 1) */
+
+ /*
+ * NOTE:
+ * chip_intr_enable is not necessarily same as
+ * ~chip_intr_disable
+ */
+ uint16_t chip_intr_enable;
+ uint16_t chip_intr_disable;
+
+ int chip_hw_crypt;
+ uint16_t chip_gpio_pled; /* power led */
+ uint16_t chip_chan_flags; /* see IEEE80211_CHAN_ */
+ uint16_t chip_txdesc1_len;
+ int chip_rxbuf_exhdr; /* based on fw ver */
+ uint32_t chip_ee_eaddr_ofs;
+ enum ieee80211_phymode chip_phymode; /* see IEEE80211_MODE_ */
+ uint8_t chip_fw_txdesc_ctrl;
+
+ uint8_t sc_eeprom_ver; /* unused */
+ uint8_t sc_form_factor; /* unused */
+ uint8_t sc_radio_type; /* see ACX_RADIO_TYPE_ */
+
+ struct acx_ring_data sc_ring_data;
+ struct acx_buf_data sc_buf_data;
+
+ struct acx_stats sc_stats; /* statistics */
+
+ /*
+ * Per interface sysctl variables
+ */
+ int sc_txrate_upd_intvl_min;
+ int sc_txrate_upd_intvl_max;
+ int sc_txrate_sample_thresh;
+ int sc_long_retry_limit;
+ int sc_short_retry_limit;
+ int sc_msdu_lifetime;
+
+ int (*sc_newstate)
+ (struct ieee80211com *,
+ enum ieee80211_state, int);
+
+ int (*chip_init) /* non-NULL */
+ (struct acx_softc *);
+
+#if 0
+ int (*chip_set_wepkey)
+ (struct acx_softc *,
+ struct ieee80211_key *, int);
+#endif
+
+ int (*chip_read_config)
+ (struct acx_softc *, struct acx_config *);
+
+ int (*chip_write_config)
+ (struct acx_softc *, struct acx_config *);
+
+ void (*chip_set_fw_txdesc_rate) /* non-NULL */
+ (struct acx_softc *, struct acx_txbuf *, int);
+
+ void (*chip_set_bss_join_param) /* non-NULL */
+ (struct acx_softc *, void *, int);
+
+ void (*chip_proc_wep_rxbuf)
+ (struct acx_softc *, struct mbuf *, int *);
+};
+
+#define ACX_FLAG_FW_LOADED 0x1
+
+#define ACX_RADIO_TYPE_MAXIM 0x0d
+#define ACX_RADIO_TYPE_RFMD 0x11
+#define ACX_RADIO_TYPE_RALINK 0x15
+#define ACX_RADIO_TYPE_RADIA 0x16
+#define ACX_RADIO_TYPE_UNKN17 0x17
+#define ACX_RADIO_TYPE_UNKN19 0x19
+
+extern const struct ieee80211_rateset acx_rates_11b;
+extern const struct ieee80211_rateset acx_rates_11g;
+extern int acx_beacon_intvl;
+
+void acx100_set_param(struct acx_softc *);
+void acx111_set_param(struct acx_softc *);
+
+int acx_init_tmplt_ordered(struct acx_softc *);
+void acx_write_phyreg(struct acx_softc *, uint32_t, uint8_t);
+
+int acx_set_tmplt(struct acx_softc *, uint16_t, void *, uint16_t);
+int acx_get_conf(struct acx_softc *, uint16_t, void *, uint16_t);
+int acx_set_conf(struct acx_softc *, uint16_t, void *, uint16_t);
+int acx_exec_command(struct acx_softc *, uint16_t, void *, uint16_t,
+ void *, uint16_t);
+int acx_attach(struct acx_softc *);
+int acx_detach(void *);
+int acx_intr(void *);
+
+#endif /* !_IF_ACXVAR_H */