diff options
author | Marcus Glocker <mglocker@cvs.openbsd.org> | 2006-08-03 08:45:02 +0000 |
---|---|---|
committer | Marcus Glocker <mglocker@cvs.openbsd.org> | 2006-08-03 08:45:02 +0000 |
commit | ed942f3438d54a63c8fe9da9e311b76e6c74a5a6 (patch) | |
tree | 2bd0320804b875f93133a1519ffbdd54676d639c /sys/dev | |
parent | db650eb0221a47878ca601bd66b9675845174f08 (diff) |
Inital import for the acx(4) driver.
ok deraadt@ jsg@
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/cardbus/files.cardbus | 8 | ||||
-rw-r--r-- | sys/dev/cardbus/if_acx_cardbus.c | 303 | ||||
-rw-r--r-- | sys/dev/ic/acx.c | 3017 | ||||
-rw-r--r-- | sys/dev/ic/acx100.c | 768 | ||||
-rw-r--r-- | sys/dev/ic/acx111.c | 458 | ||||
-rw-r--r-- | sys/dev/ic/acxreg.h | 574 | ||||
-rw-r--r-- | sys/dev/ic/acxvar.h | 469 |
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, ®_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, ®_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 */ |