- Import driver[acx(4)] for TI acx100/acx111 based WiFi NIC.
authorSepherosa Ziehau <sephe@dragonflybsd.org>
Sat, 1 Apr 2006 02:55:36 +0000 (02:55 +0000)
committerSepherosa Ziehau <sephe@dragonflybsd.org>
Sat, 1 Apr 2006 02:55:36 +0000 (02:55 +0000)
- Import user space utility[acxcontrol(8)] to load firmware and show driver
  statistics.
- Add acx(4) and acxcontrol(8) man pages.
- Build acx(4) as module only, since it needs firmware to work.
- Add an entry for acx(4) in LINT.

This driver is known to work with following hardware:
D-Link DWL-520+
D-Link DWL-650+
D-Link DWL-G520+
D-Link DWL-G650+

Although both infrastructured mode and adhoc mode are supported, it may not
work well in adhoc mode.  PBCC based rate, 22Mbits/s, is not supported yet.

acxcontrl(8) and man pages are written by Sascha Wildner.  He also kindly
helped debugging and testing the driver.  Thank you, Sascha!

The meaning and layout of hardware registers are based on the reverse
engineering work done by people at acx100.sourceforge.net
Thank them for their great work!

This driver is initially based on acx100 developed by people at wlan.kewl.org
Thank them for their nice work.

19 files changed:
share/man/man4/Makefile
share/man/man4/acx.4 [new file with mode: 0644]
sys/conf/files
sys/config/LINT
sys/dev/netif/Makefile
sys/dev/netif/acx/Makefile [new file with mode: 0644]
sys/dev/netif/acx/_acxcmd.h [new file with mode: 0644]
sys/dev/netif/acx/acx100.c [new file with mode: 0644]
sys/dev/netif/acx/acx111.c [new file with mode: 0644]
sys/dev/netif/acx/acxcmd.c [new file with mode: 0644]
sys/dev/netif/acx/acxcmd.h [new file with mode: 0644]
sys/dev/netif/acx/if_acx.c [new file with mode: 0644]
sys/dev/netif/acx/if_acxreg.h [new file with mode: 0644]
sys/dev/netif/acx/if_acxvar.h [new file with mode: 0644]
sys/i386/conf/LINT
usr.sbin/Makefile
usr.sbin/acxcontrol/Makefile [new file with mode: 0644]
usr.sbin/acxcontrol/acxcontrol.8 [new file with mode: 0644]
usr.sbin/acxcontrol/acxcontrol.c [new file with mode: 0644]

index 16bf629..3fd58eb 100644 (file)
@@ -1,10 +1,11 @@
 #      @(#)Makefile    8.1 (Berkeley) 6/18/93
 # $FreeBSD: src/share/man/man4/Makefile,v 1.83.2.66 2003/06/04 17:10:30 sam Exp $
-# $DragonFly: src/share/man/man4/Makefile,v 1.31 2006/03/19 02:57:38 swildner Exp $
+# $DragonFly: src/share/man/man4/Makefile,v 1.32 2006/04/01 02:55:36 sephe Exp $
 
 MAN=   aac.4 \
        acpi.4 \
        acpi_thermal.4 \
+       acx.4 \
        adv.4 \
        adw.4 \
        agp.4 \
diff --git a/share/man/man4/acx.4 b/share/man/man4/acx.4
new file mode 100644 (file)
index 0000000..0a7bc6f
--- /dev/null
@@ -0,0 +1,137 @@
+.\"
+.\" Copyright (c) 2006 The DragonFly Project.  All rights reserved.
+.\" 
+.\" 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: src/share/man/man4/acx.4,v 1.1 2006/04/01 02:55:36 sephe Exp $
+.\"
+.Dd March 24, 2006
+.Dt ACX 4
+.Os
+.Sh NAME
+.Nm acx
+.Nd Texas Instruments ACX100/TNETW1130(ACX111) IEEE 802.11 driver
+.Sh SYNOPSIS
+To compile this driver into the kernel, place the following lines in
+your kernel configuration file:
+.Bd -ragged -offset indent
+.Cd "device acx"
+.Cd "device wlan"
+.Ed
+.Pp
+Alternatively, to load the driver as a module at boot time, place the
+following line in
+.Pa /boot/loader.conf :
+.Bd -literal -offset indent
+if_acx_load="YES"
+.Ed
+.Sh DESCRIPTION
+The
+.Nm
+driver provides support for Texas Instruments ACX100 or
+TNETW1130 (aka ACX111) based PCI and Cardbus network adapters.
+.Pp
+By default, the
+.Nm
+driver configures the adapter for BSS operation (infrastructure mode).
+This mode requires the use of an access point. In addition, IBSS
+operation (adhoc mode) is also supported.
+For more
+information on configuring this device, see
+.Xr ifconfig 8 .
+.Ss MIB Variables
+A number of per-interface variables are implemented in the
+.Va hw.acx Ns Em X
+branch of the
+.Xr sysctl 3
+MIB.
+.Bl -tag -width ".Va txrate_sample_threshold"
+.It Va txrate_upd_intvl_min
+Minumum number of seconds to wait before raising the TX rate.
+.It Va txrate_upd_intvl_max
+Maximum number of seconds to wait before raising the TX rate.
+.It Va txrate_sample_threshold
+Number of packets to be sampled before raising the TX rate.
+.It Va long_retry_limit
+Maximum number of retries for RTS packets.
+.It Va short_retry_limit
+Maximum number of retries for non-RTS packets.
+.It Va msdu_lifetime
+MSDU life time.
+.El
+.Sh HARDWARE
+The following cards are known to work with the
+.Nm
+driver:
+.Pp
+.Bl -bullet -compact
+.It
+D-Link DWL-520+
+.It
+D-Link DWL-650+
+.It
+D-Link DWL-G520+
+.It
+D-Link DWL-G650+
+.El
+.Sh FILES
+The firmware for the adapter is not shipped with
+.Dx
+and must be obtained seperately.
+Check the disk/CD shipped with the adapter or
+.Pp
+.Pa ftp://ftp.dlink.co.uk/wireless
+.Pp
+See
+.Xr acxcontrol 8
+for more information on loading firmware.
+.\" XXX .Sh DIAGNOSTICS
+.Sh SEE ALSO
+.Xr acxcontrol 8 ,
+.Xr ifconfig 8 ,
+.Xr sysctl 8
+.Sh HISTORY
+The
+.Nm
+driver first appeared in
+.Dx 1.5 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was written by
+.An Sepherosa Ziehau .
+The manual page was written by
+.An Sascha Wildner .
+Both are based on the
+.Pa http://wlan.kewl.org
+project team's original code.
+.Pp
+The hardware specification was reverse engineered by the good folks at
+.Pa http://acx100.sourceforge.net .
+Without them this driver would not have been possible.
index 4d84dfb..25d9d5e 100644 (file)
@@ -1,5 +1,5 @@
 # $FreeBSD: src/sys/conf/files,v 1.340.2.137 2003/06/04 17:10:30 sam Exp $
-# $DragonFly: src/sys/conf/files,v 1.115 2006/03/27 16:18:30 dillon Exp $
+# $DragonFly: src/sys/conf/files,v 1.116 2006/04/01 02:55:36 sephe Exp $
 #
 # The long compile-with and dependency lines are required because of
 # limitations in config: backslash-newline doesn't work in strings, and
@@ -247,6 +247,10 @@ dev/raid/iir/iir.c         optional iir
 dev/raid/iir/iir_ctrl.c        optional iir
 dev/raid/iir/iir_pci.c optional iir pci
 dev/netif/iwi/if_iwi.c optional iwi
+dev/netif/acx/if_acx.c optional acx
+dev/netif/acx/acxcmd.c optional acx
+dev/netif/acx/acx100.c optional acx
+dev/netif/acx/acx111.c optional acx
 dev/netif/lge/if_lge.c optional lge
 dev/disk/md/md.c               optional md
 dev/netif/mii_layer/mii.c              optional miibus
index 43c4a2e..09800d4 100644 (file)
@@ -3,7 +3,7 @@
 #      as much of the source tree as it can.
 #
 # $FreeBSD: src/sys/i386/conf/LINT,v 1.749.2.144 2003/06/04 17:56:59 sam Exp $
-# $DragonFly: src/sys/config/LINT,v 1.70 2006/03/23 13:45:12 sephe Exp $
+# $DragonFly: src/sys/config/LINT,v 1.71 2006/04/01 02:55:36 sephe Exp $
 #
 # NB: You probably don't want to try running a kernel built from this
 # file.  Instead, you should start from GENERIC, and add options from
@@ -1433,6 +1433,7 @@ device            an              # Aironet Communications 4500/4800
 device         ipw             # Intel PRO/Wireless 2100
 device         iwi             # Intel PRO/Wireless 2200BG/2915ABG
 device         wi              # WaveLAN/IEEE, PRISM-II, Spectrum24 802.11DS
+device         acx             # TI ACX100/ACX111
 device wl0 at isa? port 0x300  # T1 speed ISA/radio lan
 device         xe              # Xircom PCMCIA
 device         ray             # Raytheon Raylink/Webgear Aviator
index dfe7716..cb20def 100644 (file)
@@ -1,8 +1,8 @@
-# $DragonFly: src/sys/dev/netif/Makefile,v 1.15 2005/09/19 02:53:27 sephe Exp $
+# $DragonFly: src/sys/dev/netif/Makefile,v 1.16 2006/04/01 02:55:36 sephe Exp $
 #
 
-SUBDIR= an ar aue axe bfe bge cue dc ed em ep fwe fxp gx ipw iwi kue lge lnc \
-       mii_layer my nge nv owi pcn ray re rl rue sbni sbsh sf sis sk \
+SUBDIR= an acx ar aue axe bfe bge cue dc ed em ep fwe fxp gx ipw iwi kue lge \
+       lnc mii_layer my nge nv owi pcn ray re rl rue sbni sbsh sf sis sk \
        sr ste ti tl tx txp vr vx wb wi xe xl
 
 .include <bsd.subdir.mk>
diff --git a/sys/dev/netif/acx/Makefile b/sys/dev/netif/acx/Makefile
new file mode 100644 (file)
index 0000000..4b4455b
--- /dev/null
@@ -0,0 +1,7 @@
+# $DragonFly: src/sys/dev/netif/acx/Makefile,v 1.1 2006/04/01 02:55:36 sephe Exp $
+KMOD   = if_acx
+
+SRCS   = if_acx.c acxcmd.c acx100.c acx111.c
+SRCS   += device_if.h bus_if.h pci_if.h
+
+.include <bsd.kmod.mk>
diff --git a/sys/dev/netif/acx/_acxcmd.h b/sys/dev/netif/acx/_acxcmd.h
new file mode 100644 (file)
index 0000000..87c060f
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * 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: src/sys/dev/netif/acx/_acxcmd.h,v 1.1 2006/04/01 02:55:36 sephe Exp $
+ */
+
+#ifndef _X_ACXCMD_H
+#define _X_ACXCMD_H
+
+#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
+
+#endif /* !_X_ACXCMD_H */
diff --git a/sys/dev/netif/acx/acx100.c b/sys/dev/netif/acx/acx100.c
new file mode 100644 (file)
index 0000000..bd72442
--- /dev/null
@@ -0,0 +1,749 @@
+/*
+ * 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: src/sys/dev/netif/acx/acx100.c,v 1.1 2006/04/01 02:55:36 sephe Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/rman.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/if_media.h>
+
+#include <netproto/802_11/ieee80211.h>
+#include <netproto/802_11/ieee80211_var.h>
+
+#include <bus/pci/pcireg.h>
+
+#define ACX_DEBUG
+
+#include "if_acxreg.h"
+#include "if_acxvar.h"
+#include "acxcmd.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
+};
+
+static int     acx100_init(struct acx_softc *);
+static int     acx100_init_wep(struct acx_softc *);
+static int     acx100_init_tmplt(struct acx_softc *);
+static int     acx100_init_fw_ring(struct acx_softc *);
+static int     acx100_init_memory(struct acx_softc *);
+
+static void    acx100_init_fw_txring(struct acx_softc *, uint32_t);
+static void    acx100_init_fw_rxring(struct acx_softc *, uint32_t);
+
+static int     acx100_read_config(struct acx_softc *, struct acx_config *);
+static int     acx100_write_config(struct acx_softc *, struct acx_config *);
+
+static int     acx100_set_txpower(struct acx_softc *);
+
+static void    acx100_set_fw_txdesc_rate(struct acx_softc *,
+                                         struct acx_txbuf *, int);
+static void    acx100_set_bss_join_param(struct acx_softc *, void *, int);
+
+static int     acx100_set_wepkey(struct acx_softc *,
+                                 struct ieee80211_wepkey *, int);
+
+static void    acx100_proc_wep_rxbuf(struct acx_softc *, struct mbuf *, int *);
+
+void
+acx100_set_param(device_t dev)
+{
+       struct acx_softc *sc = device_get_softc(dev);
+
+       sc->chip_mem1_rid = PCIR_BAR(1);
+       sc->chip_mem2_rid = PCIR_BAR(2);
+       sc->chip_ioreg = acx100_reg;
+       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;
+       sc->chip_set_wepkey = acx100_set_wepkey;
+       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;
+}
+
+static int
+acx100_init(struct acx_softc *sc)
+{
+       /*
+        * 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) {
+               if_printf(&sc->sc_ic.ic_if, "%s can't initialize wep\n",
+                         __func__);
+               return ENXIO;
+       }
+
+       if (acx100_init_tmplt(sc) != 0) {
+               if_printf(&sc->sc_ic.ic_if, "%s can't initialize templates\n",
+                         __func__);
+               return ENXIO;
+       }
+
+       if (acx100_init_fw_ring(sc) != 0) {
+               if_printf(&sc->sc_ic.ic_if, "%s can't initialize fw ring\n",
+                         __func__);
+               return ENXIO;
+       }
+
+       if (acx100_init_memory(sc) != 0) {
+               if_printf(&sc->sc_ic.ic_if, "%s can't initialize hw memory\n",
+                         __func__);
+               return ENXIO;
+       }
+       return 0;
+}
+
+static int
+acx100_init_wep(struct acx_softc *sc)
+{
+       struct acx_conf_wepopt wep_opt;
+       struct acx_conf_mmap mem_map;
+
+       /* Set WEP cache start/end address */
+       if (acx_get_mmap_conf(sc, &mem_map) != 0) {
+               if_printf(&sc->sc_ic.ic_if, "can't get mmap\n");
+               return 1;
+       }
+
+       mem_map.wep_cache_start = htole32(le32toh(mem_map.code_end) + 4);
+       mem_map.wep_cache_end = htole32(le32toh(mem_map.code_end) + 4);
+       if (acx_set_mmap_conf(sc, &mem_map) != 0) {
+               if_printf(&sc->sc_ic.ic_if, "can't set mmap\n");
+               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) {
+               if_printf(&sc->sc_ic.ic_if, "can't set wep opt\n");
+               return 1;
+       }
+       return 0;
+}
+
+static int
+acx100_init_tmplt(struct acx_softc *sc)
+{
+       struct acx_conf_mmap mem_map;
+       struct acx_tmplt_tim tim;
+
+       /* Set templates start address */
+       if (acx_get_mmap_conf(sc, &mem_map) != 0) {
+               if_printf(&sc->sc_ic.ic_if, "can't get mmap\n");
+               return 1;
+       }
+
+       mem_map.pkt_tmplt_start = mem_map.wep_cache_end;
+       if (acx_set_mmap_conf(sc, &mem_map) != 0) {
+               if_printf(&sc->sc_ic.ic_if, "can't set mmap\n");
+               return 1;
+       }
+
+       /* Initialize various packet templates */
+       if (acx_init_tmplt_ordered(sc) != 0) {
+               if_printf(&sc->sc_ic.ic_if, "can't init tmplt\n");
+               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) {
+               if_printf(&sc->sc_ic.ic_if, "can't set tim tmplt\n");
+               return 1;
+       }
+       return 0;
+}
+
+static int
+acx100_init_fw_ring(struct acx_softc *sc)
+{
+       struct acx100_conf_fw_ring ring;
+       struct acx_conf_mmap mem_map;
+       uint32_t txring_start, rxring_start, ring_end;
+
+       /* Set firmware descriptor ring start address */
+       if (acx_get_mmap_conf(sc, &mem_map) != 0) {
+               if_printf(&sc->sc_ic.ic_if, "can't get mmap\n");
+               return 1;
+       }
+
+       txring_start = le32toh(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) {
+               if_printf(&sc->sc_ic.ic_if, "can't set mmap\n");
+               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) {
+               if_printf(&sc->sc_ic.ic_if, "can't set fw ring configure\n");
+               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))
+
+static 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;
+       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) {
+               if_printf(&sc->sc_ic.ic_if, "can't get mmap\n");
+               return 1;
+       }
+
+       mem_map.memblk_start =
+               htole32(MEMBLK_ALIGN(le32toh(mem_map.fw_desc_end) + 4));
+
+       if (acx_set_mmap_conf(sc, &mem_map) != 0) {
+               if_printf(&sc->sc_ic.ic_if, "can't set mmap\n");
+               return 1;
+       }
+
+       /* Set memory block size */
+       memblk_sz.memblk_size = htole16(ACX_MEMBLOCK_SIZE);
+       if (acx100_set_memblk_size_conf(sc, &memblk_sz) != 0) {
+               if_printf(&sc->sc_ic.ic_if, "can't set mem block size\n");
+               return 1;
+       }
+
+       /* Get memory map after setting it */
+       if (acx_get_mmap_conf(sc, &mem_map) != 0) {
+               if_printf(&sc->sc_ic.ic_if, "can't get mmap again\n");
+               return 1;
+       }
+       memblk_start = le32toh(mem_map.memblk_start);
+       memblk_end = le32toh(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((&sc->sc_ic.ic_if, "\ttotal memory blocks\t%d\n"
+                                  "\trx memory blocks\t%d\n"
+                                  "\ttx memory blocks\t%d\n",
+                                  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) {
+               if_printf(&sc->sc_ic.ic_if, "can't set mem options\n");
+               return 1;
+       }
+
+       /* Initialize memory */
+       if (acx_init_mem(sc) != 0) {
+               if_printf(&sc->sc_ic.ic_if, "can't init mem\n");
+               return 1;
+       }
+       return 0;
+}
+
+#undef MEMBLK_ALIGN
+
+static 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);
+       }
+}
+
+static 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);
+       }
+}
+
+static int
+acx100_read_config(struct acx_softc *sc, struct acx_config *conf)
+{
+       struct acx100_conf_cca_mode cca;
+       struct acx100_conf_ed_thresh ed;
+
+       /*
+        * 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) {
+               if_printf(&sc->sc_ic.ic_if, "%s can't get cca mode\n",
+                         __func__);
+               return ENXIO;
+       }
+       conf->cca_mode = cca.cca_mode;
+       DPRINTF((&sc->sc_ic.ic_if, "cca mode %02x\n", cca.cca_mode));
+
+       /* Get ED threshold */
+       if (acx100_get_ed_thresh_conf(sc, &ed) != 0) {
+               if_printf(&sc->sc_ic.ic_if, "%s can't get ed threshold\n",
+                         __func__);
+               return ENXIO;
+       }
+       conf->ed_thresh = ed.ed_thresh;
+       DPRINTF((&sc->sc_ic.ic_if, "ed threshold %02x\n", ed.ed_thresh));
+
+       return 0;
+}
+
+static int
+acx100_write_config(struct acx_softc *sc, struct acx_config *conf)
+{
+       struct acx100_conf_cca_mode cca;
+       struct acx100_conf_ed_thresh ed;
+
+       /* Set CCA mode */
+       cca.cca_mode = conf->cca_mode;
+       if (acx100_set_cca_mode_conf(sc, &cca) != 0) {
+               if_printf(&sc->sc_ic.ic_if, "%s can't set cca mode\n",
+                         __func__);
+               return ENXIO;
+       }
+
+       /* Set ED threshold */
+       ed.ed_thresh = conf->ed_thresh;
+       if (acx100_set_ed_thresh_conf(sc, &ed) != 0) {
+               if_printf(&sc->sc_ic.ic_if, "%s can't set ed threshold\n",
+                         __func__);
+               return ENXIO;
+       }
+
+       /* Set TX power */
+       acx100_set_txpower(sc); /* ignore return value */
+
+       return 0;
+}
+
+static int
+acx100_set_txpower(struct acx_softc *sc)
+{
+       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:
+               if_printf(&sc->sc_ic.ic_if, "TX power for radio type 0x%02x "
+                         "can't be set yet\n", sc->sc_radio_type);
+               return 1;
+       }
+
+       acx_write_phyreg(sc, ACXRV_PHYREG_TXPOWER, map[ACX100_TXPOWER]);
+       return 0;
+}
+
+static 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));
+}
+
+static 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 */
+}
+
+static int
+acx100_set_wepkey(struct acx_softc *sc, struct ieee80211_wepkey *wk, int wk_idx)
+{
+       struct acx100_conf_wepkey conf_wk;
+
+       if (wk->wk_len > ACX100_WEPKEY_LEN) {
+               if_printf(&sc->sc_ic.ic_if, "%dth WEP key size beyond %d\n",
+                         wk_idx, ACX100_WEPKEY_LEN);
+               return EINVAL;
+       }
+
+       conf_wk.action = ACX100_WEPKEY_ACT_ADD;
+       conf_wk.key_len = wk->wk_len;
+       conf_wk.key_idx = wk_idx;
+       bcopy(wk->wk_key, conf_wk.key, wk->wk_len);
+       if (acx100_set_wepkey_conf(sc, &conf_wk) != 0) {
+               if_printf(&sc->sc_ic.ic_if, "%s set %dth WEP key failed\n",
+                         __func__, wk_idx);
+               return ENXIO;
+       }
+       return 0;
+}
+
+static 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/netif/acx/acx111.c b/sys/dev/netif/acx/acx111.c
new file mode 100644 (file)
index 0000000..e381c2d
--- /dev/null
@@ -0,0 +1,505 @@
+/*
+ * 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: src/sys/dev/netif/acx/acx111.c,v 1.1 2006/04/01 02:55:36 sephe Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/rman.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/if_media.h>
+
+#include <netproto/802_11/ieee80211_var.h>
+
+#include <bus/pci/pcireg.h>
+
+#define ACX_DEBUG
+
+#include "if_acxreg.h"
+#include "if_acxvar.h"
+#include "acxcmd.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_DATAFLOW_ */
+} __packed;
+
+#define ACX111_DATAFLOW_SNIFFER                0x00000080
+#define ACX111_DATAFLOW_NO_TXCRYPT     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)
+};
+
+/*
+ * For firmware whose version is listed in the following array,
+ * software WEP should be used, since hardware WEP's performance
+ * is too poor
+ */
+static const uint32_t  acx111_softwep_fwver[] = {
+       0x01020030,
+       0       /* KEEP THIS */
+};
+
+static int     acx111_init(struct acx_softc *);
+static int     acx111_init_memory(struct acx_softc *);
+static void    acx111_init_fw_txring(struct acx_softc *, uint32_t);
+
+static int     acx111_write_config(struct acx_softc *, struct acx_config *);
+
+static void    acx111_set_fw_txdesc_rate(struct acx_softc *,
+                                         struct acx_txbuf *, int);
+static void    acx111_set_bss_join_param(struct acx_softc *, void *, int);
+
+static int     acx111_set_wepkey(struct acx_softc *,
+                                 struct ieee80211_wepkey *, int);
+
+void
+acx111_set_param(device_t dev)
+{
+       struct acx_softc *sc = device_get_softc(dev);
+
+       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_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_set_wepkey = acx111_set_wepkey;
+       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;
+}
+
+static int
+acx111_init(struct acx_softc *sc)
+{
+       /*
+        * 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) {
+               if_printf(&sc->sc_ic.ic_if, "%s can't initialize templates\n",
+                         __func__);
+               return ENXIO;
+       }
+
+       if (acx111_init_memory(sc) != 0) {
+               if_printf(&sc->sc_ic.ic_if, "%s can't initialize hw memory\n",
+                         __func__);
+               return ENXIO;
+       }
+       return 0;
+}
+
+static int
+acx111_init_memory(struct acx_softc *sc)
+{
+       struct acx111_conf_mem mem;
+       struct acx111_conf_meminfo mem_info;
+
+       /* 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) {
+               if_printf(&sc->sc_ic.ic_if, "can't set mem\n");
+               return 1;
+       }
+
+       /* Get memory configuration */
+       if (acx111_get_meminfo_conf(sc, &mem_info) != 0) {
+               if_printf(&sc->sc_ic.ic_if, "can't get meminfo\n");
+               return 1;
+       }
+
+       /* Setup firmware TX descriptor ring */
+       acx111_init_fw_txring(sc, le32toh(mem_info.fw_txring_start));
+
+       /*
+        * There is no need to setup firmware RX descriptor ring,
+        * it is automaticly setup by hardware.
+        */
+
+       return 0;
+}
+
+static 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));
+       }
+}
+
+static int
+acx111_write_config(struct acx_softc *sc, struct acx_config *conf)
+{
+       struct acx111_conf_txpower tx_power;
+       struct acx111_conf_option opt;
+       uint32_t dataflow;
+       const uint32_t *fwver;
+
+       /* Set TX power */
+       tx_power.txpower = ACX111_TXPOWER_VAL;
+       if (acx111_set_txpower_conf(sc, &tx_power) != 0) {
+               if_printf(&sc->sc_ic.ic_if, "%s can't set TX power\n",
+                         __func__);
+               return ENXIO;
+       }
+
+       /*
+        * Turn off sniffering
+        * Turn on hardware/software WEP
+        */
+       if (acx111_get_option_conf(sc, &opt) != 0) {
+               if_printf(&sc->sc_ic.ic_if, "%s can't get option\n", __func__);
+               return ENXIO;
+       }
+
+       dataflow = le32toh(opt.dataflow) & ~ACX111_DATAFLOW_SNIFFER;
+
+       for (fwver = acx111_softwep_fwver; *fwver != 0; ++fwver) {
+               if (sc->sc_firmware_ver == *fwver)
+                       break;
+       }
+
+       if (*fwver != 0) {
+               DPRINTF((&sc->sc_ic.ic_if, "disable hardware WEP\n"));
+               dataflow |= ACX111_DATAFLOW_NO_TXCRYPT;
+               sc->sc_softwep = 1;
+       } else {
+               DPRINTF((&sc->sc_ic.ic_if, "enable hardware WEP\n"));
+               dataflow &= ~ACX111_DATAFLOW_NO_TXCRYPT;
+               sc->sc_softwep = 0;
+       }
+
+       opt.dataflow = htole32(dataflow);
+
+       if (acx111_set_option_conf(sc, &opt) != 0) {
+               if_printf(&sc->sc_ic.ic_if, "%s can't set option\n", __func__);
+               return ENXIO;
+       }
+       return 0;
+}
+
+static 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];
+       KASSERT(rate != 0, ("no rate map for %d\n", rate0));
+
+       FW_TXDESC_SETFIELD_2(sc, tx_buf, u.r2.rate111, rate);
+}
+
+static 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;
+}
+
+static int
+acx111_set_wepkey(struct acx_softc *sc, struct ieee80211_wepkey *wk, int wk_idx)
+{
+       struct acx111_wepkey wepkey;
+
+       if (wk->wk_len > ACX111_WEPKEY_LEN) {
+               if_printf(&sc->sc_ic.ic_if, "%dth WEP key size beyond %d\n",
+                         wk_idx, ACX111_WEPKEY_LEN);
+               return EINVAL;
+       }
+
+       bzero(&wepkey, sizeof(wepkey));
+       wepkey.action = htole16(ACX111_WEPKEY_ACT_ADD);
+       wepkey.key_type = ACX111_WEPKEY_TYPE_DEFAULT;
+       wepkey.index = 0; /* XXX ignored? */
+       wepkey.key_len = wk->wk_len;
+       wepkey.key_idx = wk_idx;
+       bcopy(wk->wk_key, wepkey.key, wk->wk_len);
+       if (acx_exec_command(sc, ACXCMD_WEP_MGMT, &wepkey, sizeof(wepkey),
+                            NULL, 0) != 0) {
+               if_printf(&sc->sc_ic.ic_if, "%s set %dth WEP key failed\n",
+                         __func__, wk_idx);
+               return ENXIO;
+       }
+       return 0;
+}
diff --git a/sys/dev/netif/acx/acxcmd.c b/sys/dev/netif/acx/acxcmd.c
new file mode 100644 (file)
index 0000000..62824bb
--- /dev/null
@@ -0,0 +1,312 @@
+/*
+ * 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: src/sys/dev/netif/acx/acxcmd.c,v 1.1 2006/04/01 02:55:36 sephe Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/rman.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/if_media.h>
+
+#include <netproto/802_11/ieee80211_var.h>
+
+#define ACX_DEBUG
+
+#include "if_acxreg.h"
+#include "if_acxvar.h"
+#include "acxcmd.h"
+
+#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)
+
+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);
+
+       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)) {
+               if_printf(&sc->sc_ic.ic_if, "%s configure data is too short\n",
+                         __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)) {
+               if_printf(&sc->sc_ic.ic_if, "%s configure data is too short\n",
+                         __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)) {
+               if_printf(&sc->sc_ic.ic_if, "%s template is too short\n",
+                         __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;
+
+       ASSERT_SERIALIZED(sc->sc_ic.ic_if.if_serializer);
+
+       if ((sc->sc_flags & ACX_FLAG_FW_LOADED) == 0) {
+               if_printf(&sc->sc_ic.ic_if, "cmd 0x%04x failed (base firmware "
+                         "not loaded)", 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) {
+               if_printf(&sc->sc_ic.ic_if, "cmd %04x failed (timeout)\n", 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) {
+               if_printf(&sc->sc_ic.ic_if, "cmd %04x failed\n", 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/netif/acx/acxcmd.h b/sys/dev/netif/acx/acxcmd.h
new file mode 100644 (file)
index 0000000..7b29e65
--- /dev/null
@@ -0,0 +1,368 @@
+/*
+ * 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: src/sys/dev/netif/acx/acxcmd.h,v 1.1 2006/04/01 02:55:36 sephe Exp $
+ */
+
+#ifndef _ACXCMD_H
+#define _ACXCMD_H
+
+#include "_acxcmd.h"
+
+void   acx_init_cmd_reg(struct acx_softc *);
+
+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);
+int    acx_join_bss(struct acx_softc *, uint8_t, struct ieee80211_node *);
+
+/*
+ * 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
+
+/*
+ * Do not use following functions directly
+ */
+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_set_tmplt(struct acx_softc *, uint16_t, void *, uint16_t);
+int    acx_exec_command(struct acx_softc *, uint16_t, void *, uint16_t,
+                        void *, uint16_t);
+
+
+/*
+ * 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);
+
+#endif /* !_ACXCMD_H */
diff --git a/sys/dev/netif/acx/if_acx.c b/sys/dev/netif/acx/if_acx.c
new file mode 100644 (file)
index 0000000..2b14dd0
--- /dev/null
@@ -0,0 +1,2948 @@
+/*
+ * 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: src/sys/dev/netif/acx/if_acx.c,v 1.1 2006/04/01 02:55:36 sephe Exp $
+ */
+
+/*
+ * Copyright (c) 2003-2004 wlan.kewl.org Project
+ * All rights reserved.
+ * 
+ * $Id: LICENSE,v 1.1.1.1 2004/07/01 12:20:39 darron 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/param.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+#include <sys/rman.h>
+#include <sys/serialize.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/bpf.h>
+#include <net/if_arp.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/ifq_var.h>
+
+#include <netproto/802_11/ieee80211_var.h>
+
+#include <bus/pci/pcireg.h>
+#include <bus/pci/pcivar.h>
+#include <bus/pci/pcidevs.h>
+
+#define ACX_DEBUG
+
+#include "if_acxreg.h"
+#include "if_acxvar.h"
+#include "acxcmd.h"
+
+#define ACX_ENABLE_TXCHAN(sc, chan)                                    \
+do {                                                                   \
+       if (acx_enable_txchan((sc), (chan)) != 0) {                     \
+               if_printf(&(sc)->sc_ic.ic_if,                           \
+                         "enable TX on channel %d failed\n", (chan));  \
+       }                                                               \
+} while (0)
+
+#define ACX_ENABLE_RXCHAN(sc, chan)                                    \
+do {                                                                   \
+       if (acx_enable_rxchan((sc), (chan)) != 0) {                     \
+               if_printf(&(sc)->sc_ic.ic_if,                           \
+                         "enable RX on channel %d failed\n", (chan));  \
+       }                                                               \
+} while (0)
+
+#define SIOCSLOADFW    _IOW('i', 137, struct ifreq)    /* load firmware */
+#define SIOCGRADIO     _IOW('i', 138, struct ifreq)    /* get radio type */
+#define SIOCGSTATS     _IOW('i', 139, struct ifreq)    /* get acx stats */
+#define SIOCSKILLFW    _IOW('i', 140, struct ifreq)    /* free firmware */
+#define SIOCGFWVER     _IOW('i', 141, struct ifreq)    /* get firmware ver */
+#define SIOCGHWID      _IOW('i', 142, struct ifreq)    /* get hardware id */
+
+static int     acx_probe(device_t);
+static int     acx_attach(device_t);
+static int     acx_detach(device_t);
+static int     acx_shutdown(device_t);
+
+static void    acx_init(void *);
+static int     acx_stop(struct acx_softc *);
+static void    acx_init_info_reg(struct acx_softc *);
+static int     acx_config(struct acx_softc *);
+static int     acx_read_config(struct acx_softc *, struct acx_config *);
+static int     acx_write_config(struct acx_softc *, struct acx_config *);
+static int     acx_set_wepkeys(struct acx_softc *);
+static void    acx_begin_scan(struct acx_softc *);
+static void    acx_next_scan(void *);
+
+static void    acx_start(struct ifnet *);
+static void    acx_watchdog(struct ifnet *);
+
+static int     acx_ioctl(struct ifnet *, u_long, caddr_t, struct ucred *);
+
+static void    acx_intr(void *);
+static void    acx_disable_intr(struct acx_softc *);
+static void    acx_enable_intr(struct acx_softc *);
+static void    acx_txeof(struct acx_softc *);
+static void    acx_txerr(struct acx_softc *, uint8_t);
+static void    acx_rxeof(struct acx_softc *);
+
+static int     acx_dma_alloc(struct acx_softc *);
+static void    acx_dma_free(struct acx_softc *);
+static int     acx_init_tx_ring(struct acx_softc *);
+static int     acx_init_rx_ring(struct acx_softc *);
+static int     acx_newbuf(struct acx_softc *, struct acx_rxbuf *, int);
+static int     acx_encap(struct acx_softc *, struct acx_txbuf *,
+                         struct mbuf *, struct acx_node *, int);
+
+static int     acx_reset(struct acx_softc *);
+
+static int     acx_set_null_tmplt(struct acx_softc *);
+static int     acx_set_probe_req_tmplt(struct acx_softc *, const char *, int);
+static int     acx_set_probe_resp_tmplt(struct acx_softc *, const char *, int,
+                                        int);
+static int     acx_set_beacon_tmplt(struct acx_softc *, const char *, int,
+                                    int);
+
+static int     acx_read_eeprom(struct acx_softc *, uint32_t, uint8_t *);
+static int     acx_read_phyreg(struct acx_softc *, uint32_t, uint8_t *);
+
+static int     acx_copyin_firmware(struct acx_softc *, struct ifreq *);
+static void    acx_free_firmware(struct acx_softc *);
+static int     acx_load_firmware(struct acx_softc *, uint32_t,
+                                 const uint8_t *, int);
+static int     acx_load_radio_firmware(struct acx_softc *, const uint8_t *,
+                                       uint32_t);
+static int     acx_load_base_firmware(struct acx_softc *, const uint8_t *,
+                                      uint32_t);
+
+static struct ieee80211_node *acx_node_alloc(struct ieee80211com *);
+static void    acx_node_free(struct ieee80211com *, struct ieee80211_node *);
+static void    acx_node_init(struct acx_softc *, struct acx_node *);
+static void    acx_node_update(struct acx_softc *, struct acx_node *,
+                               uint8_t, uint8_t);
+static int     acx_newstate(struct ieee80211com *, enum ieee80211_state, int);
+
+/* XXX */
+static void    acx_media_status(struct ifnet *, struct ifmediareq *);
+
+static int     acx_sysctl_txrate_upd_intvl_min(SYSCTL_HANDLER_ARGS);
+static int     acx_sysctl_txrate_upd_intvl_max(SYSCTL_HANDLER_ARGS);
+static int     acx_sysctl_txrate_sample_thresh(SYSCTL_HANDLER_ARGS);
+static int     acx_sysctl_long_retry_limit(SYSCTL_HANDLER_ARGS);
+static int     acx_sysctl_short_retry_limit(SYSCTL_HANDLER_ARGS);
+static int     acx_sysctl_msdu_lifetime(SYSCTL_HANDLER_ARGS);
+
+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 */
+
+static const struct acx_device {
+       uint16_t        vid;
+       uint16_t        did;
+       void            (*set_param)(device_t);
+       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),
+#if 0
+       DEVMETHOD(device_suspend,       acx_suspend),
+       DEVMETHOD(device_resume,        acx_resume),
+#endif
+       { 0, 0 }
+};
+
+static driver_t acx_driver = {
+       "acx",
+       acx_methods,
+       sizeof(struct acx_softc)
+};
+
+static devclass_t acx_devclass;
+
+DRIVER_MODULE(acx, pci, acx_driver, acx_devclass, 0, 0);
+DRIVER_MODULE(acx, cardbus, acx_driver, acx_devclass, 0, 0);
+
+MODULE_DEPEND(acx, wlan, 1, 1, 1);
+MODULE_DEPEND(acx, pci, 1, 1, 1);
+MODULE_DEPEND(acx, cardbus, 1, 1, 1);
+
+static 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;
+}
+
+static int
+acx_attach(device_t dev)
+{
+       struct acx_softc *sc;
+       struct ifnet *ifp;
+       struct ieee80211com *ic;
+       int i, error;
+
+       sc = device_get_softc(dev);
+       ic = &sc->sc_ic;
+       ifp = &ic->ic_if;
+
+       if_initname(ifp, device_get_name(dev), device_get_unit(dev));
+
+#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 */
+
+       /* 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;
+       }
+
+       /* Initilize channel scanning timer */
+       callout_init(&sc->sc_chanscan_timer);
+
+       /* 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((&sc->sc_ic.ic_if, "radio type %02x\n", 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((&sc->sc_ic.ic_if, "EEPROM version %u\n", 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;
+       ifq_set_maxlen(&ifp->if_snd, IFQ_MAXLEN);
+       ifq_set_ready(&ifp->if_snd);
+
+       /* 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;
+
+       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]);
+       }
+
+       ieee80211_ifattach(ifp);
+
+       /* Override alloc/free */
+       ic->ic_node_alloc = acx_node_alloc;
+       ic->ic_node_free = acx_node_free;
+
+       /* Override newstate */
+       sc->sc_newstate = ic->ic_newstate;
+       ic->ic_newstate = acx_newstate;
+
+       ieee80211_media_init(ifp, ieee80211_media_change, acx_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;
+
+       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");
+
+       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;
+       }
+
+       return 0;
+fail1:
+       ieee80211_ifdetach(ifp);
+fail:
+       acx_detach(dev);
+       return error;
+}
+
+static int
+acx_detach(device_t dev)
+{
+       struct acx_softc *sc = device_get_softc(dev);
+
+       if (device_is_attached(dev)) {
+               struct ifnet *ifp = &sc->sc_ic.ic_if;
+
+               lwkt_serialize_enter(ifp->if_serializer);
+
+               acx_stop(sc);
+               acx_free_firmware(sc);
+               bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_irq_handle);
+
+               lwkt_serialize_exit(ifp->if_serializer);
+
+               ieee80211_ifdetach(ifp);
+       }
+
+       if (sc->sc_sysctl_tree != NULL)
+               sysctl_ctx_free(&sc->sc_sysctl_ctx);
+
+       if (sc->sc_irq_res != NULL) {
+               bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_rid,
+                                    sc->sc_irq_res);
+       }
+       if (sc->sc_mem1_res != NULL) {
+               bus_release_resource(dev, SYS_RES_MEMORY, sc->chip_mem1_rid,
+                                    sc->sc_mem1_res);
+       }
+       if (sc->sc_mem2_res != NULL) {
+               bus_release_resource(dev, SYS_RES_MEMORY, sc->chip_mem2_rid,
+                                    sc->sc_mem2_res);
+       }
+
+       acx_dma_free(sc);
+       return 0;
+}
+
+static int
+acx_shutdown(device_t dev)
+{
+       struct acx_softc *sc = device_get_softc(dev);
+
+       lwkt_serialize_enter(sc->sc_ic.ic_if.if_serializer);
+       acx_stop(sc);
+       lwkt_serialize_exit(sc->sc_ic.ic_if.if_serializer);
+       return 0;
+}
+
+static void
+acx_init(void *arg)
+{
+       struct acx_softc *sc = arg;
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+       struct acx_firmware *fw = &sc->sc_firmware;
+       int error;
+
+       error = acx_stop(sc);
+       if (error)
+               return;
+
+       if (fw->base_fw == NULL) {
+               error = EINVAL;
+               if_printf(ifp, "base firmware is not loaded yet\n");
+               return;
+       }
+
+       error = acx_init_tx_ring(sc);
+       if (error) {
+               if_printf(ifp, "can't initialize TX ring\n");
+               goto back;
+       }
+
+       error = acx_init_rx_ring(sc);
+       if (error) {
+               if_printf(ifp, "can't initialize RX ring\n");
+               goto back;
+       }
+
+       error = acx_load_base_firmware(sc, fw->base_fw, fw->base_fw_len);
+       if (error)
+               goto back;
+
+       /*
+        * 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);
+
+       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
+
+       if (fw->radio_fw != NULL) {
+               error = acx_load_radio_firmware(sc, fw->radio_fw,
+                                               fw->radio_fw_len);
+               if (error)
+                       goto back;
+       }
+
+       error = sc->chip_init(sc);
+       if (error)
+               goto back;
+
+       /* Get and set device various configuration */
+       error = acx_config(sc);
+       if (error)
+               goto back;
+
+       /* Setup WEP */
+       if (sc->sc_ic.ic_flags & IEEE80211_WEP_ON) {
+               error = acx_set_wepkeys(sc);
+               if (error)
+                       goto back;
+       }
+
+       /* 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 */
+       acx_begin_scan(sc);
+back:
+       if (error)
+               acx_stop(sc);
+}
+
+static 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;
+}
+
+static int
+acx_set_wepkeys(struct acx_softc *sc)
+{
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct acx_conf_wep_txkey wep_txkey;
+       int i, error;
+
+       for (i = 0; i < IEEE80211_WEP_NKID; ++i) {
+               struct ieee80211_wepkey *wk = &ic->ic_nw_keys[i];
+
+               if (wk->wk_len == 0)
+                       continue;
+
+               error = sc->chip_set_wepkey(sc, wk, i);
+               if (error)
+                       return error;
+       }
+
+       /* Set current WEP key index */
+       wep_txkey.wep_txkey = ic->ic_wep_txkey;
+       if (acx_set_wep_txkey_conf(sc, &wep_txkey) != 0) {
+               if_printf(&ic->ic_if, "set WEP txkey failed\n");
+               return ENXIO;
+       }
+       return 0;
+}
+
+static void
+acx_begin_scan(struct acx_softc *sc)
+{
+       struct ieee80211com *ic = &sc->sc_ic;
+       uint8_t chan;
+
+       ieee80211_begin_scan(&ic->ic_if);
+
+       chan = ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan);
+
+       ACX_ENABLE_TXCHAN(sc, chan);
+       ACX_ENABLE_RXCHAN(sc, chan);
+
+       /* Start background scanning */
+       callout_reset(&sc->sc_chanscan_timer, hz / acx_chanscan_rate,
+                     acx_next_scan, sc);
+}
+
+static void
+acx_next_scan(void *arg)
+{
+       struct acx_softc *sc = arg;
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ifnet *ifp = &ic->ic_if;
+
+       lwkt_serialize_enter(ifp->if_serializer);
+
+       if (ic->ic_state == IEEE80211_S_SCAN) {
+               uint8_t chan;
+
+               ieee80211_next_scan(ifp);
+
+               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);
+       }
+
+       lwkt_serialize_exit(ifp->if_serializer);
+}
+
+static 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;
+
+       ASSERT_SERIALIZED(ifp->if_serializer);
+
+       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 */
+       callout_stop(&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(bd->mbuf_dma_tag,
+                                         buf->tb_mbuf_dmamap);
+                       m_free(buf->tb_mbuf);
+                       buf->tb_mbuf = NULL;
+               }
+
+               ni = (struct ieee80211_node *)buf->tb_node;
+               if (ni != NULL && ni != ic->ic_bss)
+                       ieee80211_free_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(bd->mbuf_dma_tag,
+                                         bd->rx_buf[i].rb_mbuf_dmamap);
+                       m_free(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);
+
+       return 0;
+}
+
+static 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) {
+               if_printf(&sc->sc_ic.ic_if, "can't set probe req template "
+                         "(empty ssid)\n");
+               return ENXIO;
+       }
+
+       /* XXX for PM?? */
+       if (acx_set_null_tmplt(sc) != 0) {
+               if_printf(&sc->sc_ic.ic_if, "can't set null data template\n");
+               return ENXIO;
+       }
+       return 0;
+}
+
+static 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) {
+               if_printf(&sc->sc_ic.ic_if, "can't get station id\n");
+               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];
+       if_printf(&sc->sc_ic.ic_if, "MAC address (from firmware): %6D\n",
+                 conf->eaddr, ":");
+
+       /* Get region domain */
+       if (acx_get_regdom_conf(sc, &reg_dom) != 0) {
+               if_printf(&sc->sc_ic.ic_if, "can't get region domain\n");
+               return ENXIO;
+       }
+       conf->regdom = reg_dom.regdom;
+       DPRINTF((&sc->sc_ic.ic_if, "regdom %02x\n", reg_dom.regdom));
+
+       /* Get antenna */
+       if (acx_get_antenna_conf(sc, &ant) != 0) {
+               if_printf(&sc->sc_ic.ic_if, "can't get antenna\n");
+               return ENXIO;
+       }
+       conf->antenna = ant.antenna;
+       DPRINTF((&sc->sc_ic.ic_if, "antenna %02x\n", 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) {
+                       if_printf(&sc->sc_ic.ic_if, "can't get sensitivity\n");
+                       return error;
+               }
+       } else {
+               sen = 0;
+       }
+       DPRINTF((&sc->sc_ic.ic_if, "sensitivity %02x\n", sen));
+
+       /* Get firmware revision */
+       if (acx_get_fwrev_conf(sc, &fw_rev) != 0) {
+               if_printf(&sc->sc_ic.ic_if, "can't get firmware revision\n");
+               return ENXIO;
+       }
+
+       if (strncmp(fw_rev.fw_rev, "Rev ", 4) != 0) {
+               if_printf(&sc->sc_ic.ic_if, "strange revision string -- %s\n",
+                         fw_rev.fw_rev);
+               fw_rev_no = 0x01090407;
+       } else {
+               char *s, *endp;
+
+               /*
+                *  01234
+                * "Rev xx.xx.xx.xx"
+                *      ^ Start from here
+                */
+               s = &fw_rev.fw_rev[4];
+               fw_rev_no = 0;
+               for (i = 0; i < 4; ++i) {
+                       uint8_t val;
+
+                       val = strtoul(s, &endp, 16);
+                       fw_rev_no |= val << ((3 - i) * 8);
+
+                       if (*endp == '\0')
+                               break;
+                       else
+                               s = ++endp;
+               }
+       }
+       sc->sc_firmware_ver = fw_rev_no;
+       sc->sc_hardware_id = le32toh(fw_rev.hw_id);
+       DPRINTF((&sc->sc_ic.ic_if, "fw rev %08x, hw id %08x\n",
+                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;
+}
+
+static 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;
+       int error;
+
+       /* Set number of long/short retry */
+       sretry.nretry = sc->sc_short_retry_limit;
+       if (acx_set_nretry_short_conf(sc, &sretry) != 0) {
+               if_printf(&sc->sc_ic.ic_if, "can't set short retry limit\n");
+               return ENXIO;
+       }
+
+       lretry.nretry = sc->sc_long_retry_limit;
+       if (acx_set_nretry_long_conf(sc, &lretry) != 0) {
+               if_printf(&sc->sc_ic.ic_if, "can't set long retry limit\n");
+               return ENXIO;
+       }
+
+       /* Set MSDU lifetime */
+       msdu_lifetime.lifetime = htole32(sc->sc_msdu_lifetime);
+       if (acx_set_msdu_lifetime_conf(sc, &msdu_lifetime) != 0) {
+               if_printf(&sc->sc_ic.ic_if, "can't set MSDU lifetime\n");
+               return ENXIO;
+       }
+
+       /* Enable rate fallback */
+       rate_fb.ratefb_enable = 1;
+       if (acx_set_rate_fallback_conf(sc, &rate_fb) != 0) {
+               if_printf(&sc->sc_ic.ic_if, "can't enable rate fallback\n");
+               return ENXIO;
+       }
+
+       /* Set antenna */
+       ant.antenna = conf->antenna;
+       if (acx_set_antenna_conf(sc, &ant) != 0) {
+               if_printf(&sc->sc_ic.ic_if, "can't set antenna\n");
+               return ENXIO;
+       }
+
+       /* Set region domain */
+       reg_dom.regdom = conf->regdom;
+       if (acx_set_regdom_conf(sc, &reg_dom) != 0) {
+               if_printf(&sc->sc_ic.ic_if, "can't set region domain\n");
+               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) {
+               if_printf(&sc->sc_ic.ic_if, "can't set RX option\n");
+               return ENXIO;
+       }
+       return 0;
+}
+
+static int
+acx_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr)
+{
+       struct acx_softc *sc = ifp->if_softc;
+       struct ifreq *req;
+       int error;
+
+       error = 0;
+       req = (struct ifreq *)data;
+
+       switch (cmd) {
+       case SIOCSLOADFW:
+               error = suser(curthread);
+               if (error)
+                       break;
+
+               error = acx_copyin_firmware(sc, req);
+               break;
+       case SIOCSKILLFW:
+               error = suser(curthread);
+               if (error)
+                       break;
+               acx_free_firmware(sc);
+               break;
+       case SIOCGRADIO:
+               error = copyout(&sc->sc_radio_type, req->ifr_data,
+                               sizeof(sc->sc_radio_type));
+               break;
+       case SIOCGFWVER:
+               error = copyout(&sc->sc_firmware_ver, req->ifr_data,
+                               sizeof(sc->sc_firmware_ver));
+               break;
+       case SIOCGHWID:
+               error = copyout(&sc->sc_hardware_id, req->ifr_data,
+                               sizeof(sc->sc_hardware_id));
+               break;
+       case SIOCGSTATS:
+               error = copyout(&sc->sc_stats, req->ifr_data,
+                               sizeof(sc->sc_stats));
+               break;
+       case SIOCSIFFLAGS:
+               if (ifp->if_flags & IFF_UP) {
+                       if ((ifp->if_flags & IFF_RUNNING) == 0)
+                               acx_init(sc);
+               } else {
+                       if (ifp->if_flags & IFF_RUNNING)
+                               acx_stop(sc);
+               }
+               break;
+       case SIOCADDMULTI:
+       case SIOCDELMULTI:
+               /* TODO */
+               break;
+       default:
+               error = ieee80211_ioctl(ifp, cmd, data, cr);
+               break;
+       }
+
+       if (error == ENETRESET) {
+               if ((ifp->if_flags & (IFF_RUNNING | IFF_UP)) ==
+                   (IFF_RUNNING | IFF_UP))
+                       acx_init(sc);
+               error = 0;
+       }
+       return error;
+}
+
+static __inline struct mbuf *
+acx_softwep(struct ieee80211com *ic, struct mbuf *m, struct acx_node *node)
+{
+       m = ieee80211_wep_crypt(&ic->ic_if, m, 1);
+       if (m != NULL)
+               return m;
+
+       if (node != NULL && (struct ieee80211_node *)node != ic->ic_bss)
+               ieee80211_free_node(ic, (struct ieee80211_node *)node);
+       return NULL;
+}
+
+static 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;
+
+       ASSERT_SERIALIZED(ifp->if_serializer);
+
+       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 acx_node *node;
+               struct mbuf *m;
+               int rate;
+
+               node = NULL;
+               if (!IF_QEMPTY(&ic->ic_mgtq)) {
+                       struct ieee80211_node *ni;
+
+                       IF_DEQUEUE(&ic->ic_mgtq, m);
+
+                       ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
+                       m->m_pkthdr.rcvif = NULL;
+
+                       /*
+                        * Since mgmt data are transmitted at fixed rate
+                        * they will not be used to do rate control.
+                        */
+                       if (ni && ni != ic->ic_bss)
+                               ieee80211_free_node(ic, ni);
+
+                       rate = 4;       /* XXX 2Mb/s for mgmt packet */
+               } else if (!ifq_is_empty(&ifp->if_snd)) {
+                       struct ieee80211_frame *f;
+
+                       /* XXX */
+#if 0
+                       if (ic->ic_state != IEEE80211_S_RUN) {
+                               if_printf(ifp, "data packet dropped due to "
+                                         "not RUN.  Current state %d\n",
+                                         ic->ic_state);
+                               break;
+                       }
+#endif
+
+                       m = ifq_dequeue(&ifp->if_snd, NULL);
+                       if (m == NULL)
+                               break;
+
+                       m = ieee80211_encap(ifp, m,
+                                           (struct ieee80211_node **)&node);
+                       if (m == NULL) {
+                               ifp->if_oerrors++;
+                               continue;
+                       }
+                       f = mtod(m, struct ieee80211_frame *);
+
+                       if (ic->ic_flags & IEEE80211_F_WEPON) {
+                               f->i_fc[1] |= IEEE80211_FC1_WEP;
+                               if (sc->sc_softwep) {
+                                       m = acx_softwep(ic, m, node);
+                                       if (m == NULL) {
+                                               /*
+                                                * axc_softwep() will free
+                                                * `node' for us if it fails
+                                                */
+                                               ifp->if_oerrors++;
+                                               node = NULL;
+                                               continue;
+                                       }
+                               }
+                       }
+
+                       if (node->nd_txrate < 0) {
+                               acx_node_init(sc, node);
+                               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(
+                                               (struct ieee80211_node *)node);
+                               }
+                       }
+
+                       rate = node->nd_rates.rs_rates[node->nd_txrate];
+
+                       BPF_MTAP(ifp, m);
+               } else {
+                       break;
+               }
+
+               if (ic->ic_rawbpf != NULL)
+                       bpf_mtap(ic->ic_rawbpf, m);
+
+               if (acx_encap(sc, buf, m, node, rate) != 0) {
+                       struct ieee80211_node *ni;
+
+                       ni = (struct ieee80211_node *)node;
+                       if (ni != NULL && ni != ic->ic_bss)
+                               ieee80211_free_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;
+}
+
+static void
+acx_watchdog(struct ifnet *ifp)
+{
+       if_printf(ifp, "watchdog timeout\n");
+       acx_txeof(ifp->if_softc);
+       /* TODO */
+}
+
+static void
+acx_intr(void *arg)
+{
+       struct acx_softc *sc = arg;
+       uint16_t intr_status;
+
+       if ((sc->sc_flags & ACX_FLAG_FW_LOADED) == 0)
+               return;
+
+       intr_status = CSR_READ_2(sc, ACXREG_INTR_STATUS_CLR);
+       if (intr_status == ACXRV_INTR_ALL) {
+               /* not our interrupt */
+               return;
+       }
+
+       intr_status &= sc->chip_intr_enable;
+       if (intr_status == 0) {
+               /* not interrupts we care about */
+               return;
+       }
+
+       /* 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);
+}
+
+static 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);
+}
+
+static 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);
+}
+
+static 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;
+       ASSERT_SERIALIZED(ifp->if_serializer);
+
+       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(bd->mbuf_dma_tag, buf->tb_mbuf_dmamap);
+               m_free(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);
+                       if (ni != ic->ic_bss)
+                               ieee80211_free_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);
+       }
+}
+
+static 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((ifp, "TX failed -- excessive retry\n"));
+       } else {
+               if_printf(ifp, "TX failed -- ");
+       }
+
+       /*
+        * 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;
+       }
+}
+
+static 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;
+
+       ASSERT_SERIALIZED(ic->ic_if.if_serializer);
+
+       bus_dmamap_sync(rd->rx_ring_dma_tag, rd->rx_ring_dmamap,
+                       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 = le16toh(buf->rb_desc->h_ctrl);
+               desc_status = le32toh(buf->rb_desc->h_status);
+               if (!(desc_ctrl & DESC_CTRL_HOSTOWN) ||
+                   !(desc_status & DESC_STATUS_FULL))
+                       break;
+
+               bus_dmamap_sync(bd->mbuf_dma_tag, buf->rb_mbuf_dmamap,
+                               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 = le16toh(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 (ic->ic_opmode == IEEE80211_M_STA) {
+                               ni = ieee80211_ref_node(ic->ic_bss);
+                       } else {
+                               ni = ieee80211_find_node(ic, f->i_addr2);
+                               if (ni == NULL)
+                                       ni = ieee80211_ref_node(ic->ic_bss);
+                       }
+
+                       if (f->i_fc[1] & IEEE80211_FC1_WEP) {
+                               /* 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);
+                       }
+
+                       m->m_len = m->m_pkthdr.len = len;
+                       m->m_pkthdr.rcvif = &ic->ic_if;
+
+                       ieee80211_input(&ic->ic_if, m, ni, head->rbh_level,
+                                       le32toh(head->rbh_time));
+
+                       if (ni == ic->ic_bss)
+                               ieee80211_unref_node(&ni);
+                       else
+                               ieee80211_free_node(ic, ni);
+
+                       ifp->if_ipackets++;
+               } else {
+                       m_free(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(rd->rx_ring_dma_tag, rd->rx_ring_dmamap,
+                               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;
+}
+
+static 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)) {
+               if_printf(&sc->sc_ic.ic_if, "can't halt ECPU\n");
+               return ENXIO;
+       }
+       return 0;
+}
+
+static int
+acx_read_eeprom(struct acx_softc *sc, uint32_t offset, uint8_t *val)
+{
+       int i;
+
+       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) {
+               if_printf(&sc->sc_ic.ic_if, "can't read EEPROM offset %x "
+                         "(timeout)\n", offset);
+               return ETIMEDOUT;
+       }
+#undef EE_READ_RETRY_MAX
+
+       *val = CSR_READ_1(sc, ACXREG_EEPROM_DATA);
+       return 0;
+}
+
+static int
+acx_read_phyreg(struct acx_softc *sc, uint32_t reg, uint8_t *val)
+{
+       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) {
+               if_printf(&sc->sc_ic.ic_if, "can't read phy reg %x (timeout)\n",
+                         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);
+}
+
+static 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;
+}
+
+static 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;
+       }
+}
+
+static int
+acx_load_base_firmware(struct acx_softc *sc, const uint8_t *base_fw,
+                      uint32_t base_fw_len)
+{
+       int i, error;
+
+       /* Load base firmware */
+       error = acx_load_firmware(sc, 0, base_fw, base_fw_len);
+       if (error) {
+               if_printf(&sc->sc_ic.ic_if, "can't load base firmware\n");
+               return error;
+       }
+       DPRINTF((&sc->sc_ic.ic_if, "base firmware loaded\n"));
+
+       /* 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);
+       }
+
+       if_printf(&sc->sc_ic.ic_if, "can't initialize ECPU (timeout)\n");
+       return ENXIO;
+}
+
+static int
+acx_load_radio_firmware(struct acx_softc *sc, const uint8_t *radio_fw,
+                       uint32_t radio_fw_len)
+{
+       struct acx_conf_mmap mem_map;
+       uint32_t radio_fw_ofs;
+       int error;
+
+       /*
+        * 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 = le32toh(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, radio_fw, radio_fw_len);
+       if (error) {
+               if_printf(&sc->sc_ic.ic_if, "can't load radio firmware\n");
+               return ENXIO;
+       }
+       DPRINTF((&sc->sc_ic.ic_if, "radio firmware loaded\n"));
+
+       /* 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, radio_fw_len) != 0)
+               return ENXIO;
+
+       /* Verify radio firmware's loading position */
+       if (acx_get_mmap_conf(sc, &mem_map) != 0)
+               return ENXIO;
+       if (le32toh(mem_map.code_end) != radio_fw_ofs + radio_fw_len) {
+               if_printf(&sc->sc_ic.ic_if, "loaded radio firmware position "
+                         "mismatch\n");
+               return ENXIO;
+       }
+
+       DPRINTF((&sc->sc_ic.ic_if, "radio firmware initialized\n"));
+       return 0;
+}
+
+static int
+acx_load_firmware(struct acx_softc *sc, uint32_t offset, const uint8_t *data,
+                 int data_len)
+{
+       const uint32_t *fw;
+       int i, fw_len;
+
+       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, be32toh(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 (be32toh(fw[i]) != val) {
+                       if_printf(&sc->sc_ic.ic_if, "fireware mismatch "
+                                 "fw %08x  loaded %08x\n", fw[i], val);
+                       return ENXIO;
+               }
+       }
+       return 0;
+}
+
+MALLOC_DECLARE(ACX_NODE);
+MALLOC_DEFINE(ACX_NODE, "acx_node", "acx(4) wrapper for ieee80211_node");
+
+static struct ieee80211_node *
+acx_node_alloc(struct ieee80211com *ic)
+{
+       struct acx_node *node;
+
+       node = malloc(sizeof(struct acx_node), ACX_NODE, M_NOWAIT | M_ZERO);
+       node->nd_txrate = -1;
+       return (struct ieee80211_node *)node;
+}
+
+static 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;
+                       }
+               }
+       }
+       KASSERT(node->nd_node.ni_txrate >= 0, ("no compat rates"));
+       DPRINTF((&ic->ic_if, "node rate %d\n",
+                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;
+}
+
+static 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((&sc->sc_ic.ic_if, "rate down %6D %d -> %d\n",
+                        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((&sc->sc_ic.ic_if, "rate up %6D %d -> %d\n",
+                        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
+}
+
+static void
+acx_node_free(struct ieee80211com *ic, struct ieee80211_node *n)
+{
+       free(n, ACX_NODE);
+}
+
+static int
+acx_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
+{
+       struct acx_softc *sc = ic->ic_if.if_softc;
+       int error = 0;
+
+       ASSERT_SERIALIZED(ic->ic_if.if_serializer);
+
+       switch (nstate) {
+       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) {
+                               if_printf(&ic->ic_if, "join BSS failed\n");
+                               error = 1;
+                               goto back;
+                       }
+
+                       DPRINTF((&ic->ic_if, "join BSS\n"));
+                       if (ic->ic_state == IEEE80211_S_ASSOC) {
+                               DPRINTF((&ic->ic_if,
+                                        "change from assoc to run\n"));
+                               ic->ic_state = IEEE80211_S_RUN;
+                       }
+
+#ifdef ACX_DEBUG
+                       if_printf(&ic->ic_if, "AP rates: ");
+                       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) {
+                               if_printf(&ic->ic_if,
+                                         "enable TX on channel %d failed\n",
+                                         chan);
+                               goto back;
+                       }
+
+                       if (acx_enable_rxchan(sc, chan) != 0) {
+                               if_printf(&ic->ic_if,
+                                         "enable RX on channel %d failed\n",
+                                         chan);
+                               goto back;
+                       }
+
+                       if (acx_set_beacon_tmplt(sc, ni->ni_essid,
+                                                ni->ni_esslen, chan) != 0) {
+                               if_printf(&ic->ic_if,
+                                         "set bescon template failed\n");
+                               goto back;
+                       }
+
+                       if (acx_set_probe_resp_tmplt(sc, ni->ni_essid,
+                                                    ni->ni_esslen,
+                                                    chan) != 0) {
+                               if_printf(&ic->ic_if, "set probe response "
+                                         "template failed\n");
+                               goto back;
+                       }
+
+                       if (acx_join_bss(sc, ACX_MODE_ADHOC, ni) != 0) {
+                               if_printf(&ic->ic_if, "join IBSS failed\n");
+                               goto back;
+                       }
+
+                       DPRINTF((&ic->ic_if, "join IBSS\n"));
+                       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)
+{
+#define INIT_TMPLT(name)                       \
+do {                                           \
+       if (acx_init_##name##_tmplt(sc) != 0)   \
+               return 1;                       \
+} while (0)
+
+       /*
+        * 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.
+        */
+       INIT_TMPLT(probe_req);
+       INIT_TMPLT(null_data);
+       INIT_TMPLT(beacon);
+       INIT_TMPLT(tim);
+       INIT_TMPLT(probe_resp);
+
+#undef CALL_SET_TMPLT
+       return 0;
+}
+
+static void
+acx_ring_dma_addr(void *arg, bus_dma_segment_t *seg, int nseg, int error)
+{
+       *((uint32_t *)arg) = seg->ds_addr;
+}
+
+static 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;
+       int i, error;
+
+       /* Allocate DMA stuffs for RX descriptors  */
+       error = bus_dma_tag_create(NULL, PAGE_SIZE, 0,
+                                  BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR,
+                                  NULL, NULL,
+                                  ACX_RX_RING_SIZE, 1, ACX_RX_RING_SIZE,
+                                  0, &rd->rx_ring_dma_tag);
+       if (error) {
+               if_printf(&sc->sc_ic.ic_if, "can't create rx ring dma tag\n");
+               return error;
+       }
+
+       error = bus_dmamem_alloc(rd->rx_ring_dma_tag, (void **)&rd->rx_ring,
+                                BUS_DMA_WAITOK | BUS_DMA_ZERO,
+                                &rd->rx_ring_dmamap);
+       if (error) {
+               if_printf(&sc->sc_ic.ic_if,
+                         "can't allocate rx ring dma memory\n");
+               bus_dma_tag_destroy(rd->rx_ring_dma_tag);
+               rd->rx_ring_dma_tag = NULL;
+               return error;
+       }
+
+       error = bus_dmamap_load(rd->rx_ring_dma_tag, rd->rx_ring_dmamap,
+                               rd->rx_ring, ACX_RX_RING_SIZE,
+                               acx_ring_dma_addr, &rd->rx_ring_paddr,
+                               BUS_DMA_WAITOK);
+       if (error) {
+               if_printf(&sc->sc_ic.ic_if, "can't get rx ring dma address\n");
+               bus_dmamem_free(rd->rx_ring_dma_tag, rd->rx_ring,
+                               rd->rx_ring_dmamap);
+               bus_dma_tag_destroy(rd->rx_ring_dma_tag);
+               rd->rx_ring_dma_tag = NULL;
+               return error;
+       }
+
+       /* Allocate DMA stuffs for TX descriptors */
+       error = bus_dma_tag_create(NULL, PAGE_SIZE, 0,
+                                  BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR,
+                                  NULL, NULL,
+                                  ACX_TX_RING_SIZE, 1, ACX_TX_RING_SIZE,
+                                  0, &rd->tx_ring_dma_tag);
+       if (error) {
+               if_printf(&sc->sc_ic.ic_if, "can't create tx ring dma tag\n");
+               return error;
+       }
+
+       error = bus_dmamem_alloc(rd->tx_ring_dma_tag, (void **)&rd->tx_ring,
+                                BUS_DMA_WAITOK | BUS_DMA_ZERO,
+                                &rd->tx_ring_dmamap);
+       if (error) {
+               if_printf(&sc->sc_ic.ic_if,
+                         "can't allocate tx ring dma memory\n");
+               bus_dma_tag_destroy(rd->tx_ring_dma_tag);
+               rd->tx_ring_dma_tag = NULL;
+               return error;
+       }
+
+       error = bus_dmamap_load(rd->tx_ring_dma_tag, rd->tx_ring_dmamap,
+                               rd->tx_ring, ACX_TX_RING_SIZE,
+                               acx_ring_dma_addr, &rd->tx_ring_paddr,
+                               BUS_DMA_WAITOK);
+       if (error) {
+               if_printf(&sc->sc_ic.ic_if, "can't get tx ring dma address\n");
+               bus_dmamem_free(rd->tx_ring_dma_tag, rd->tx_ring,
+                               rd->tx_ring_dmamap);
+               bus_dma_tag_destroy(rd->tx_ring_dma_tag);
+               rd->tx_ring_dma_tag = NULL;
+               return error;
+       }
+
+       /* Create DMA tag for RX/TX mbuf map */
+       error = bus_dma_tag_create(NULL, 1, 0,
+                                  BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR,
+                                  NULL, NULL,
+                                  MCLBYTES, 1, MCLBYTES,
+                                  0, &bd->mbuf_dma_tag);
+       if (error) {
+               if_printf(&sc->sc_ic.ic_if, "can't create mbuf dma tag\n");
+               return error;
+       }
+
+       /* Create a spare RX DMA map */
+       error = bus_dmamap_create(bd->mbuf_dma_tag, 0, &bd->mbuf_tmp_dmamap);
+       if (error) {
+               if_printf(&sc->sc_ic.ic_if, "can't create tmp mbuf dma map\n");
+               bus_dma_tag_destroy(bd->mbuf_dma_tag);
+               bd->mbuf_dma_tag = NULL;
+               return error;
+       }
+
+       /* Create DMA map for RX mbufs */
+       for (i = 0; i < ACX_RX_DESC_CNT; ++i) {
+               error = bus_dmamap_create(bd->mbuf_dma_tag, 0,
+                                         &bd->rx_buf[i].rb_mbuf_dmamap);
+               if (error) {
+                       if_printf(&sc->sc_ic.ic_if, "can't create rx mbuf "
+                                 "dma map (%d)\n", 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(bd->mbuf_dma_tag, 0,
+                                         &bd->tx_buf[i].tb_mbuf_dmamap);
+               if (error) {
+                       if_printf(&sc->sc_ic.ic_if, "can't create tx mbuf "
+                                 "dma map (%d)\n", 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;
+}
+
+static 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_dma_tag != NULL) {
+               bus_dmamap_unload(rd->rx_ring_dma_tag, rd->rx_ring_dmamap);
+               bus_dmamem_free(rd->rx_ring_dma_tag, rd->rx_ring,
+                               rd->rx_ring_dmamap);
+               bus_dma_tag_destroy(rd->rx_ring_dma_tag);
+       }
+
+       if (rd->tx_ring_dma_tag != NULL) {
+               bus_dmamap_unload(rd->tx_ring_dma_tag, rd->tx_ring_dmamap);
+               bus_dmamem_free(rd->tx_ring_dma_tag, rd->tx_ring,
+                               rd->tx_ring_dmamap);
+               bus_dma_tag_destroy(rd->tx_ring_dma_tag);
+       }
+
+       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(bd->mbuf_dma_tag,
+                                                 bd->rx_buf[i].rb_mbuf_dmamap);
+                               m_free(bd->rx_buf[i].rb_mbuf);
+                       }
+                       bus_dmamap_destroy(bd->mbuf_dma_tag,
+                                          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(bd->mbuf_dma_tag,
+                                                 bd->tx_buf[i].tb_mbuf_dmamap);
+                               m_free(bd->tx_buf[i].tb_mbuf);
+                       }
+                       bus_dmamap_destroy(bd->mbuf_dma_tag,
+                                          bd->tx_buf[i].tb_mbuf_dmamap);
+               }
+       }
+
+       if (bd->mbuf_dma_tag != NULL) {
+               bus_dmamap_destroy(bd->mbuf_dma_tag, bd->mbuf_tmp_dmamap);
+               bus_dma_tag_destroy(bd->mbuf_dma_tag);
+       }
+}
+
+static 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(rd->tx_ring_dma_tag, rd->tx_ring_dmamap,
+                       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;
+}
+
+static 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(rd->rx_ring_dma_tag, rd->rx_ring_dmamap,
+                       BUS_DMASYNC_PREWRITE);
+
+       bd->rx_scan_start = 0;
+       return 0;
+}
+
+static void
+acx_buf_dma_addr(void *arg, bus_dma_segment_t *seg, int nseg,
+                bus_size_t mapsz, int error)
+{
+       if (error)
+               return;
+
+       /* XXX */
+       KASSERT(nseg == 1, ("too many RX dma segments\n"));
+       *((uint32_t *)arg) = seg->ds_addr;
+}
+
+static 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;
+
+       m = m_getcl(wait ? MB_WAIT : MB_DONTWAIT, MT_DATA, M_PKTHDR);
+       if (m == NULL)
+               return ENOBUFS;
+
+       m->m_len = m->m_pkthdr.len = MCLBYTES;
+
+       error = bus_dmamap_load_mbuf(bd->mbuf_dma_tag, bd->mbuf_tmp_dmamap,
+                                    m, acx_buf_dma_addr, &paddr,
+                                    wait ? BUS_DMA_WAITOK : BUS_DMA_NOWAIT);
+       if (error) {
+               m_free(m);
+               if_printf(&sc->sc_ic.ic_if, "can't map rx mbuf %d\n", error);
+               return error;
+       }
+
+       /* Unload originally mapped mbuf */
+       bus_dmamap_unload(bd->mbuf_dma_tag, 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(bd->mbuf_dma_tag, rb->rb_mbuf_dmamap,
+                       BUS_DMASYNC_PREREAD);
+       return 0;
+}
+
+static int
+acx_encap(struct acx_softc *sc, struct acx_txbuf *txbuf, struct mbuf *m,
+         struct acx_node *node, int rate)
+{
+       struct acx_buf_data *bd = &sc->sc_buf_data;
+       struct acx_ring_data *rd = &sc->sc_ring_data;
+       uint32_t paddr;
+       uint8_t ctrl;
+       int error;
+
+       KASSERT(txbuf->tb_mbuf == NULL, ("free TX buf has mbuf installed\n"));
+       error = 0;
+
+       if (m->m_pkthdr.len > MCLBYTES) {
+               if_printf(&sc->sc_ic.ic_if, "mbuf too big\n");
+               error = E2BIG;
+               goto back;
+       } else if (m->m_pkthdr.len < ACX_FRAME_HDRLEN) {
+               if_printf(&sc->sc_ic.ic_if, "mbuf too small\n");
+               error = EINVAL;
+               goto back;
+       }
+
+       error = bus_dmamap_load_mbuf(bd->mbuf_dma_tag, txbuf->tb_mbuf_dmamap,
+                                    m, acx_buf_dma_addr, &paddr,
+                                    BUS_DMA_NOWAIT);
+       if (error && error != EFBIG) {
+               if_printf(&sc->sc_ic.ic_if, "can't map tx mbuf1 %d\n", error);
+               goto back;
+       }
+
+       if (error) {    /* error == EFBIG */
+               struct mbuf *m_new;
+
+               m_new = m_defrag(m, MB_DONTWAIT);
+               if (m_new == NULL) {
+                       if_printf(&sc->sc_ic.ic_if, "can't defrag tx mbuf\n");
+                       error = ENOBUFS;
+                       goto back;
+               } else {
+                       m = m_new;
+               }
+
+               error = bus_dmamap_load_mbuf(bd->mbuf_dma_tag,
+                                            txbuf->tb_mbuf_dmamap, m,
+                                            acx_buf_dma_addr, &paddr,
+                                            BUS_DMA_NOWAIT);
+               if (error) {
+                       if_printf(&sc->sc_ic.ic_if, "can't map tx mbuf2 %d\n",
+                                 error);
+                       goto back;
+               }
+       }
+
+       error = 0;
+
+       bus_dmamap_sync(bd->mbuf_dma_tag, txbuf->tb_mbuf_dmamap,
+                       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(rd->tx_ring_dma_tag, rd->tx_ring_dmamap,
+                       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_free(m);
+       return error;
+}
+
+/* XXX C&P of ieee80211_add_ssid() */
+static uint8_t *
+my_ieee80211_add_ssid(uint8_t *frm, const uint8_t *ssid, u_int len)
+{
+       *frm++ = IEEE80211_ELEMID_SSID;
+       *frm++ = len;
+       memcpy(frm, ssid, len);
+       return frm + len;
+}
+
+static int
+acx_set_null_tmplt(struct acx_softc *sc)
+{
+       struct acx_tmplt_null_data n;
+       struct ieee80211_frame *f;
+
+       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, IF_LLADDR(&sc->sc_ic.ic_if));
+       IEEE80211_ADDR_COPY(f->i_addr3, etherbroadcastaddr);
+
+       return _acx_set_null_data_tmplt(sc, &n, sizeof(n));
+}
+
+static 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;
+       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, IF_LLADDR(&sc->sc_ic.ic_if));
+       IEEE80211_ADDR_COPY(f->i_addr3, etherbroadcastaddr);
+
+       v = req.data.u_data.var;
+       v = my_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));
+}
+
+static 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;
+       uint8_t *v;
+       int vlen;
+
+       ic = &sc->sc_ic;
+
+       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, IF_LLADDR(&ic->ic_if));
+       IEEE80211_ADDR_COPY(f->i_addr3, IF_LLADDR(&ic->ic_if));
+
+       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 = my_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() */
+static 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;
+       uint8_t *v;
+       int vlen;
+
+       ic = &sc->sc_ic;
+
+       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, IF_LLADDR(&ic->ic_if));
+       IEEE80211_ADDR_COPY(f->i_addr3, IF_LLADDR(&ic->ic_if));
+
+       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 = my_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));
+}
+
+/*
+ * XXX
+ * C&P of ieee80211_media_status(), only
+ * imr->ifm_status |= IFM_ACTIVE; is added
+ */
+static void
+acx_media_status(struct ifnet *ifp, struct ifmediareq *imr)
+{
+       struct ieee80211com *ic = (void *)ifp;
+       struct ieee80211_node *ni = NULL;
+
+       imr->ifm_status = IFM_AVALID;
+       imr->ifm_active = IFM_IEEE80211;
+
+       if (ic->ic_state == IEEE80211_S_RUN)
+               imr->ifm_status |= IFM_ACTIVE;
+
+       imr->ifm_active |= IFM_AUTO;
+       switch (ic->ic_opmode) {
+       case IEEE80211_M_STA:
+               ni = ic->ic_bss;
+               /* calculate rate subtype */
+               imr->ifm_active |= ieee80211_rate2media(ic,
+                       ni->ni_rates.rs_rates[ni->ni_txrate], ic->ic_curmode);
+               break;
+       case IEEE80211_M_IBSS:
+               imr->ifm_active |= IFM_IEEE80211_ADHOC;
+               break;
+       case IEEE80211_M_AHDEMO:
+               /* should not come here */
+               break;
+       case IEEE80211_M_HOSTAP:
+               imr->ifm_active |= IFM_IEEE80211_HOSTAP;
+               break;
+       case IEEE80211_M_MONITOR:
+               imr->ifm_active |= IFM_IEEE80211_MONITOR;
+               break;
+       }
+       switch (ic->ic_curmode) {
+       case IEEE80211_MODE_11A:
+               imr->ifm_active |= IFM_IEEE80211_11A;
+               break;
+       case IEEE80211_MODE_11B:
+               imr->ifm_active |= IFM_IEEE80211_11B;
+               break;
+       case IEEE80211_MODE_11G:
+               imr->ifm_active |= IFM_IEEE80211_11G;
+               break;
+       case IEEE80211_MODE_FH:
+               imr->ifm_active |= IFM_IEEE80211_FH;
+               break;
+       case IEEE80211_MODE_TURBO:
+               imr->ifm_active |= IFM_IEEE80211_11A
+                               |  IFM_IEEE80211_TURBO;
+               break;
+       }
+}
+
+static int
+acx_sysctl_txrate_upd_intvl_min(SYSCTL_HANDLER_ARGS)
+{
+       struct acx_softc *sc = arg1;
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+       int error = 0, v;
+
+       lwkt_serialize_enter(ifp->if_serializer);
+
+       v = sc->sc_txrate_upd_intvl_min;
+       error = sysctl_handle_int(oidp, &v, 0, req);
+       if (error || req->newptr == NULL)
+               goto back;
+       if (v <= 0 || v > sc->sc_txrate_upd_intvl_max) {
+               error = EINVAL;
+               goto back;
+       }
+
+       sc->sc_txrate_upd_intvl_min = v;
+back:
+       lwkt_serialize_exit(ifp->if_serializer);
+       return error;
+}
+
+static int
+acx_sysctl_txrate_upd_intvl_max(SYSCTL_HANDLER_ARGS)
+{
+       struct acx_softc *sc = arg1;
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+       int error = 0, v;
+
+       lwkt_serialize_enter(ifp->if_serializer);
+
+       v = sc->sc_txrate_upd_intvl_max;
+       error = sysctl_handle_int(oidp, &v, 0, req);
+       if (error || req->newptr == NULL)
+               goto back;
+       if (v <= 0 || v < sc->sc_txrate_upd_intvl_min) {
+               error = EINVAL;
+               goto back;
+       }
+
+       sc->sc_txrate_upd_intvl_max = v;
+back:
+       lwkt_serialize_exit(ifp->if_serializer);
+       return error;
+}
+
+static int
+acx_sysctl_txrate_sample_thresh(SYSCTL_HANDLER_ARGS)
+{
+       struct acx_softc *sc = arg1;
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+       int error = 0, v;
+
+       lwkt_serialize_enter(ifp->if_serializer);
+
+       v = sc->sc_txrate_sample_thresh;
+       error = sysctl_handle_int(oidp, &v, 0, req);
+       if (error || req->newptr == NULL)
+               goto back;
+       if (v <= 0) {
+               error = EINVAL;
+               goto back;
+       }
+
+       sc->sc_txrate_sample_thresh = v;
+back:
+       lwkt_serialize_exit(ifp->if_serializer);
+       return error;
+}
+
+static int
+acx_sysctl_long_retry_limit(SYSCTL_HANDLER_ARGS)
+{
+       struct acx_softc *sc = arg1;
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+       int error = 0, v;
+
+       lwkt_serialize_enter(ifp->if_serializer);
+
+       v = sc->sc_long_retry_limit;
+       error = sysctl_handle_int(oidp, &v, 0, req);
+       if (error || req->newptr == NULL)
+               goto back;
+       if (v <= 0) {
+               error = EINVAL;
+               goto back;
+       }
+
+       if (sc->sc_flags & ACX_FLAG_FW_LOADED) {
+               struct acx_conf_nretry_long lretry;
+
+               lretry.nretry = v;
+               if (acx_set_nretry_long_conf(sc, &lretry) != 0) {
+                       if_printf(ifp, "can't set long retry limit\n");
+                       error = ENXIO;
+                       goto back;
+               }
+       }
+       sc->sc_long_retry_limit = v;
+back:
+       lwkt_serialize_exit(ifp->if_serializer);
+       return error;
+}
+
+static int
+acx_sysctl_short_retry_limit(SYSCTL_HANDLER_ARGS)
+{
+       struct acx_softc *sc = arg1;
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+       int error = 0, v;
+
+       lwkt_serialize_enter(ifp->if_serializer);
+
+       v = sc->sc_short_retry_limit;
+       error = sysctl_handle_int(oidp, &v, 0, req);
+       if (error || req->newptr == NULL)
+               goto back;
+       if (v <= 0) {
+               error = EINVAL;
+               goto back;
+       }
+
+       if (sc->sc_flags & ACX_FLAG_FW_LOADED) {
+               struct acx_conf_nretry_short sretry;
+
+               sretry.nretry = v;
+               if (acx_set_nretry_short_conf(sc, &sretry) != 0) {
+                       if_printf(ifp, "can't set short retry limit\n");
+                       error = ENXIO;
+                       goto back;
+               }
+       }
+       sc->sc_short_retry_limit = v;
+back:
+       lwkt_serialize_exit(ifp->if_serializer);
+       return error;
+}
+
+static int
+acx_sysctl_msdu_lifetime(SYSCTL_HANDLER_ARGS)
+{
+       struct acx_softc *sc = arg1;
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+       int error = 0, v;
+
+       lwkt_serialize_enter(ifp->if_serializer);
+
+       v = sc->sc_msdu_lifetime;
+       error = sysctl_handle_int(oidp, &v, 0, req);
+       if (error || req->newptr == NULL)
+               goto back;
+       if (v <= 0) {
+               error = EINVAL;
+               goto back;
+       }
+
+       if (sc->sc_flags & ACX_FLAG_FW_LOADED) {
+               struct acx_conf_msdu_lifetime msdu_lifetime;
+
+               msdu_lifetime.lifetime = htole32(v);
+               if (acx_set_msdu_lifetime_conf(sc, &msdu_lifetime) != 0) {
+                       if_printf(&sc->sc_ic.ic_if,
+                                 "can't set MSDU lifetime\n");
+                       error = ENXIO;
+                       goto back;
+               }
+       }
+       sc->sc_msdu_lifetime = v;
+back:
+       lwkt_serialize_exit(ifp->if_serializer);
+       return error;
+}
diff --git a/sys/dev/netif/acx/if_acxreg.h b/sys/dev/netif/acx/if_acxreg.h
new file mode 100644 (file)
index 0000000..425b8ab
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * 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: src/sys/dev/netif/acx/if_acxreg.h,v 1.1 2006/04/01 02:55:36 sephe Exp $
+ */
+
+#ifndef _IF_ACXREG_H
+#define _IF_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
+
+#endif /* !_IF_ACXREG_H */
diff --git a/sys/dev/netif/acx/if_acxvar.h b/sys/dev/netif/acx/if_acxvar.h
new file mode 100644 (file)
index 0000000..f1a1110
--- /dev/null
@@ -0,0 +1,446 @@
+/*
+ * 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: src/sys/dev/netif/acx/if_acxvar.h,v 1.1 2006/04/01 02:55:36 sephe Exp $
+ */
+
+#ifndef _IF_ACXVAR_H
+#define _IF_ACXVAR_H
+
+#ifdef ACX_DEBUG
+#define DPRINTF(x)             if_printf x
+#else
+#define DPRINTF(x)             ((void)0)
+#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_tag_t           rx_ring_dma_tag;
+       bus_dmamap_t            rx_ring_dmamap;
+       uint32_t                rx_ring_paddr;
+
+       struct acx_host_desc    *tx_ring;
+       bus_dma_tag_t           tx_ring_dma_tag;
+       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_dma_tag_t           mbuf_dma_tag;
+       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 ieee80211com     sc_ic;
+
+       struct callout          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;
+
+       /*
+        * 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;
+
+       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;
+
+       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_ */
+       int                     sc_softwep;
+
+       struct acx_ring_data    sc_ring_data;
+       struct acx_buf_data     sc_buf_data;
+
+       struct acx_stats        sc_stats;       /* statistics */
+
+       struct sysctl_ctx_list  sc_sysctl_ctx;
+       struct sysctl_oid       *sc_sysctl_tree;
+
+       /*
+        * 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 *);
+
+       int                     (*chip_set_wepkey)      /* non-NULL */
+                               (struct acx_softc *,
+                                struct ieee80211_wepkey *, int);
+
+       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(device_t);
+void   acx111_set_param(device_t);
+
+int    acx_init_tmplt_ordered(struct acx_softc *);
+void   acx_write_phyreg(struct acx_softc *, uint32_t, uint8_t);
+
+#endif /* !_IF_ACXVAR_H */
index 06e7e50..7c76487 100644 (file)
@@ -3,7 +3,7 @@
 #      as much of the source tree as it can.
 #
 # $FreeBSD: src/sys/i386/conf/LINT,v 1.749.2.144 2003/06/04 17:56:59 sam Exp $
-# $DragonFly: src/sys/i386/conf/Attic/LINT,v 1.70 2006/03/23 13:45:12 sephe Exp $
+# $DragonFly: src/sys/i386/conf/Attic/LINT,v 1.71 2006/04/01 02:55:36 sephe Exp $
 #
 # NB: You probably don't want to try running a kernel built from this
 # file.  Instead, you should start from GENERIC, and add options from
@@ -1433,6 +1433,7 @@ device            an              # Aironet Communications 4500/4800
 device         ipw             # Intel PRO/Wireless 2100
 device         iwi             # Intel PRO/Wireless 2200BG/2915ABG
 device         wi              # WaveLAN/IEEE, PRISM-II, Spectrum24 802.11DS
+device         acx             # TI ACX100/ACX111
 device wl0 at isa? port 0x300  # T1 speed ISA/radio lan
 device         xe              # Xircom PCMCIA
 device         ray             # Raytheon Raylink/Webgear Aviator
index 482d70a..2fc3b99 100644 (file)
@@ -1,12 +1,13 @@
 #      From: @(#)Makefile      5.20 (Berkeley) 6/12/93
 # $FreeBSD: src/usr.sbin/Makefile,v 1.183.2.14 2003/04/16 11:01:51 ru Exp $
-# $DragonFly: src/usr.sbin/Makefile,v 1.27 2005/04/30 15:39:17 joerg Exp $
+# $DragonFly: src/usr.sbin/Makefile,v 1.28 2006/04/01 02:55:36 sephe Exp $
 
 # XXX MISSING:         mkproto
 SUBDIR=        IPXrouted \
        ac \
        accton \
        acpi \
+       acxcontrol \
        adduser \
        amd \
        ancontrol \
diff --git a/usr.sbin/acxcontrol/Makefile b/usr.sbin/acxcontrol/Makefile
new file mode 100644 (file)
index 0000000..51217ac
--- /dev/null
@@ -0,0 +1,7 @@
+# $DragonFly: src/usr.sbin/acxcontrol/Attic/Makefile,v 1.1 2006/04/01 02:55:36 sephe Exp $
+#
+PROG   = acxcontrol
+MAN    = acxcontrol.8
+WARNS  ?= 6
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/acxcontrol/acxcontrol.8 b/usr.sbin/acxcontrol/acxcontrol.8
new file mode 100644 (file)
index 0000000..fe8b166
--- /dev/null
@@ -0,0 +1,82 @@
+.\"
+.\" Copyright (c) 2006 The DragonFly Project.  All rights reserved.
+.\"
+.\" 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: src/usr.sbin/acxcontrol/Attic/acxcontrol.8,v 1.1 2006/04/01 02:55:36 sephe Exp $
+.\"
+.Dd March 24, 2006
+.Os
+.Dt ACXCONTROL 8
+.Sh NAME
+.Nm acxcontrol
+.Nd configure Texas Instruments ACX100/TNETW1130(ACX111) network adapters
+.Sh SYNOPSIS
+.Nm
+.Op Fl i
+.Ar iface
+.Nm
+.Op Fl i
+.Ar iface Fl f Ar file Op Fl r
+.Nm
+.Op Fl i
+.Ar iface Fl k
+.Sh DESCRIPTION
+The
+.Nm
+utility controls the operation of Texas Instruments ACX100/TNETW1130(ACX111)
+networking devices via the
+.Xr acx 4
+driver.
+.Pp
+In order to configure IEEE 802.11 parameters use
+.Xr ifconfig 8 .
+.Sh OPTIONS
+The options are as follows:
+.Bl -tag -width indent
+.It Oo Fl i Oc Ar iface
+Displays adapter's internal statistics.
+.It Oo Fl i Oc Ar iface Fl f Ar file Op Fl r
+Download firmware binary image to the adapter.
+The
+.Fl r
+option must be specified if the adapter needs an additional radio firmware.
+.It Oo Fl i Oc Ar iface Fl k
+Kill the firmware and reset the adapter.
+.El
+.Sh SEE ALSO
+.Xr acx 4 ,
+.Xr ifconfig 8
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+utility and its man page were written by
+.An Sepherosa Ziehau Aq sepherosa@gmail.com
+and
+.An Sascha Wildner Aq swildner@gmail.com .
diff --git a/usr.sbin/acxcontrol/acxcontrol.c b/usr.sbin/acxcontrol/acxcontrol.c
new file mode 100644 (file)
index 0000000..1e29e39
--- /dev/null
@@ -0,0 +1,272 @@
+/*
+ * 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> and
+ * Sascha Wildner <swildner@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: src/usr.sbin/acxcontrol/Attic/acxcontrol.c,v 1.1 2006/04/01 02:55:36 sephe Exp $
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#define SIOCSLOADFW     _IOW('i', 137, struct ifreq)    /* load firmware */
+#define SIOCGRADIO      _IOW('i', 138, struct ifreq)    /* get radio type */
+#define SIOCGSTATS      _IOW('i', 139, struct ifreq)    /* get acx stats */
+#define SIOCSKILLFW     _IOW('i', 140, struct ifreq)    /* free firmware */
+#define SIOCGFWVER      _IOW('i', 141, struct ifreq)    /* get firmware ver */
+#define SIOCGHWID       _IOW('i', 142, struct ifreq)    /* get hardware id */
+
+#define RADIO_FW_FMT   "radio%02x"
+
+static int     do_req(const char *, unsigned long, void *);
+static void    get_statistics(const char *);
+static void    kill_firmware(const char *);
+static void    load_firmware(const char *, const char *, int);
+static void    mmap_file(const char *, uint8_t **, int *);
+static void    usage(void);
+
+struct firmware {
+       uint8_t *base_fw;
+       int     base_fw_len;
+       uint8_t *radio_fw;
+       int     radio_fw_len;
+};
+
+struct firmware_head {
+       uint32_t        fwh_cksum;
+       uint32_t        fwh_len;
+};
+
+struct statistic {
+       int             index;
+       const char      *desc;
+};
+
+static const struct statistic tbl[] = {
+       {  1, "Invalid param in TX description" },
+       {  2, "No WEP key exists" },
+       {  3, "MSDU timeouts" },
+       {  4, "Excessive TX retries" },
+       {  5, "Buffer overflows" },
+       {  6, "DMA errors" },
+       {  7, "Unknown errors" },
+       { -1, NULL }
+};
+
+static int
+do_req(const char *iface, unsigned long req, void *data)
+{
+       int s;
+       struct ifreq ifr;
+       int error;
+
+       if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+               err(EX_OSERR, "Can't create socket");
+
+       memset(&ifr, 0, sizeof(ifr));
+       strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name));
+       ifr.ifr_data = data;
+       error = ioctl(s, req, &ifr);
+
+       close(s);
+
+       return error;
+}
+
+static void
+get_statistics(const char *iface)
+{
+       uint32_t i;
+       uint64_t stats[16];
+       const struct statistic *stt;
+
+       if (do_req(iface, SIOCGHWID, &i) == -1)
+               err(EX_OSERR, "Can't get hardware ID");
+       printf("Hardware ID                    0x%x\n", i);
+
+       if (do_req(iface, SIOCGFWVER, &i) == -1)
+               err(EX_OSERR, "Can't get firmware version");
+       printf("Firmware Version               0x%x\n", i);
+
+       if (do_req(iface, SIOCGSTATS, &stats) == -1)
+               err(EX_OSERR, "Can't get statistics");
+
+       for (stt = tbl; stt->index != -1; stt++)
+               printf("%-30s %qd\n", stt->desc, stats[stt->index]);
+}
+
+static void
+kill_firmware(const char *iface)
+{
+       if (do_req(iface, SIOCSKILLFW, NULL) == -1)
+               err(EX_OSERR, "Can't kill firmware");
+}
+
+static void
+load_firmware(const char *iface, const char *filename, int uncombined)
+{
+       char radio_name[FILENAME_MAX];
+       struct firmware fw;
+
+       memset(&fw, 0, sizeof(fw));
+       mmap_file(filename, &fw.base_fw, &fw.base_fw_len);
+
+       if (uncombined) {
+               uint8_t radio_type;
+
+               if (do_req(iface, SIOCGRADIO, &radio_type) == -1)
+                       err(EX_OSERR, "Can't get radio type");
+               snprintf(radio_name, FILENAME_MAX, "%s/" RADIO_FW_FMT ".bin",
+                        dirname(filename), radio_type);
+               mmap_file(radio_name, &fw.radio_fw, &fw.radio_fw_len);
+       }
+
+       do_req(iface, SIOCSLOADFW, &fw);
+}
+
+static void
+mmap_file(const char *filename, uint8_t **addr, int *len)
+{
+       struct stat st;
+       struct firmware_head *fwh;
+       uint32_t cksum;
+       uint8_t *p;
+       int i, fd;
+
+       fd = open(filename, O_RDONLY);
+       if (fd < 0)
+               err(EX_OSERR, "Can't open %s", filename);
+
+       if (fstat(fd, &st) < 0)
+               err(EX_OSERR, "Can't stat %s", filename);
+
+       if (st.st_size <= sizeof(struct firmware_head))
+               err(EX_SOFTWARE, "%s is too short", filename);
+
+       fwh = mmap(NULL, st.st_size, PROT_READ, 0, fd, 0);
+       if (fwh == NULL)
+               err(EX_OSERR, "Can't map %s into memory", filename);
+
+       if (fwh->fwh_len != st.st_size - sizeof(struct firmware_head))
+               err(EX_SOFTWARE, "%s length mismatch", filename);
+
+       cksum = 0;
+       for (i = 0, p = (uint8_t *)&fwh->fwh_len;
+            i < st.st_size - sizeof(fwh->fwh_cksum);
+            ++i, ++p)
+               cksum += *p;
+       if (cksum != fwh->fwh_cksum)
+               err(EX_SOFTWARE, "%s checksum mismatch", filename);
+
+       *addr = (uint8_t *)(fwh + 1);
+       *len = st.st_size - sizeof(struct firmware_head);
+
+       close(fd);
+}
+
+static void
+usage(void)
+{
+       fprintf(stderr, "usage: acxcontrol iface\n"
+           "       acxcontrol iface -f file [-r]\n"
+           "       acxcontrol iface -k\n");
+       exit(EX_USAGE);
+}
+
+int
+main(int argc, char *argv[])
+{
+       int c;
+       int noflag = 1, kflag = 0, rflag = 0;
+       const char *iface = NULL, *path = NULL;
+
+       if (argc > 1 && argv[1][0] != '-') {
+               iface = argv[1];
+               optind++;
+       }
+
+       while ((c = getopt(argc, argv, "f:i:kr")) != -1) {
+               if (c != 'i')
+                       noflag = 0;
+
+               switch (c) {
+               case 'f':
+                       path = optarg;
+                       break;
+               case 'i':
+                       iface = optarg;
+                       break;
+               case 'k':
+                       kflag = 1;
+                       break;
+               case 'r':
+                       rflag = 1;
+                       break;
+               default:
+                       usage();
+               }
+       }
+
+       if (iface == NULL)
+               usage();
+
+       if (kflag && ((path != NULL) || rflag))
+               usage();
+
+       if (kflag)
+               kill_firmware(iface);
+
+       if (path != NULL)
+               load_firmware(iface, path, rflag);
+
+       if (noflag)
+               get_statistics(iface);
+
+       return EX_OK;
+}