xref: /openbsd-src/sys/dev/ic/acx.c (revision cf96265bb25b08cea5a0b5654925448396fd9df0)
1*cf96265bSbluhm /*	$OpenBSD: acx.c,v 1.128 2023/11/10 15:51:20 bluhm Exp $ */
295339239Smglocker 
395339239Smglocker /*
495339239Smglocker  * Copyright (c) 2006 Jonathan Gray <jsg@openbsd.org>
595339239Smglocker  *
695339239Smglocker  * Permission to use, copy, modify, and distribute this software for any
795339239Smglocker  * purpose with or without fee is hereby granted, provided that the above
895339239Smglocker  * copyright notice and this permission notice appear in all copies.
995339239Smglocker  *
1095339239Smglocker  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1195339239Smglocker  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1295339239Smglocker  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1395339239Smglocker  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1495339239Smglocker  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1595339239Smglocker  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1695339239Smglocker  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1795339239Smglocker  */
180f91cf57Smglocker 
190f91cf57Smglocker /*
200f91cf57Smglocker  * Copyright (c) 2006 The DragonFly Project.  All rights reserved.
210f91cf57Smglocker  *
220f91cf57Smglocker  * This code is derived from software contributed to The DragonFly Project
230f91cf57Smglocker  * by Sepherosa Ziehau <sepherosa@gmail.com>
240f91cf57Smglocker  *
250f91cf57Smglocker  * Redistribution and use in source and binary forms, with or without
260f91cf57Smglocker  * modification, are permitted provided that the following conditions
270f91cf57Smglocker  * are met:
280f91cf57Smglocker  *
290f91cf57Smglocker  * 1. Redistributions of source code must retain the above copyright
300f91cf57Smglocker  *    notice, this list of conditions and the following disclaimer.
310f91cf57Smglocker  * 2. Redistributions in binary form must reproduce the above copyright
320f91cf57Smglocker  *    notice, this list of conditions and the following disclaimer in
330f91cf57Smglocker  *    the documentation and/or other materials provided with the
340f91cf57Smglocker  *    distribution.
350f91cf57Smglocker  * 3. Neither the name of The DragonFly Project nor the names of its
360f91cf57Smglocker  *    contributors may be used to endorse or promote products derived
370f91cf57Smglocker  *    from this software without specific, prior written permission.
380f91cf57Smglocker  *
390f91cf57Smglocker  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
400f91cf57Smglocker  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
410f91cf57Smglocker  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
420f91cf57Smglocker  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
430f91cf57Smglocker  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
440f91cf57Smglocker  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
450f91cf57Smglocker  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
460f91cf57Smglocker  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
470f91cf57Smglocker  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
480f91cf57Smglocker  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
490f91cf57Smglocker  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
500f91cf57Smglocker  * SUCH DAMAGE.
510f91cf57Smglocker  */
520f91cf57Smglocker 
530f91cf57Smglocker /*
540f91cf57Smglocker  * Copyright (c) 2003-2004 wlan.kewl.org Project
550f91cf57Smglocker  * All rights reserved.
560f91cf57Smglocker  *
570f91cf57Smglocker  * Redistribution and use in source and binary forms, with or without
580f91cf57Smglocker  * modification, are permitted provided that the following conditions
590f91cf57Smglocker  * are met:
600f91cf57Smglocker  *
610f91cf57Smglocker  * 1. Redistributions of source code must retain the above copyright
620f91cf57Smglocker  *    notice, this list of conditions and the following disclaimer.
630f91cf57Smglocker  *
640f91cf57Smglocker  * 2. Redistributions in binary form must reproduce the above copyright
650f91cf57Smglocker  *    notice, this list of conditions and the following disclaimer in the
660f91cf57Smglocker  *    documentation and/or other materials provided with the distribution.
670f91cf57Smglocker  *
680f91cf57Smglocker  * 3. All advertising materials mentioning features or use of this software
690f91cf57Smglocker  *    must display the following acknowledgement:
700f91cf57Smglocker  *
710f91cf57Smglocker  *    This product includes software developed by the wlan.kewl.org Project.
720f91cf57Smglocker  *
730f91cf57Smglocker  * 4. Neither the name of the wlan.kewl.org Project nor the names of its
740f91cf57Smglocker  *    contributors may be used to endorse or promote products derived from
750f91cf57Smglocker  *    this software without specific prior written permission.
760f91cf57Smglocker  *
770f91cf57Smglocker  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
780f91cf57Smglocker  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
790f91cf57Smglocker  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
800f91cf57Smglocker  * THE wlan.kewl.org Project BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
810f91cf57Smglocker  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
820f91cf57Smglocker  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
830f91cf57Smglocker  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
840f91cf57Smglocker  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
850f91cf57Smglocker  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
860f91cf57Smglocker  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
870f91cf57Smglocker  */
880f91cf57Smglocker 
890f91cf57Smglocker #include "bpfilter.h"
900f91cf57Smglocker 
910f91cf57Smglocker #include <sys/param.h>
920f91cf57Smglocker #include <sys/systm.h>
930f91cf57Smglocker #include <sys/kernel.h>
940f91cf57Smglocker #include <sys/malloc.h>
950f91cf57Smglocker #include <sys/mbuf.h>
960f91cf57Smglocker #include <sys/socket.h>
970f91cf57Smglocker #include <sys/sockio.h>
980f91cf57Smglocker #include <sys/ioctl.h>
99b302470dSkettenis #include <sys/errno.h>
100b302470dSkettenis #include <sys/device.h>
1019b18ffb8Sguenther #include <sys/endian.h>
1020f91cf57Smglocker 
1030f91cf57Smglocker #include <machine/bus.h>
1040f91cf57Smglocker #include <machine/intr.h>
1050f91cf57Smglocker 
1060f91cf57Smglocker #include <net/if.h>
1070f91cf57Smglocker #include <net/if_media.h>
1080f91cf57Smglocker 
1090f91cf57Smglocker #if NBPFILTER > 0
1100f91cf57Smglocker #include <net/bpf.h>
1110f91cf57Smglocker #endif
1120f91cf57Smglocker 
1130f91cf57Smglocker #include <netinet/in.h>
1140f91cf57Smglocker #include <netinet/if_ether.h>
1150f91cf57Smglocker 
1160f91cf57Smglocker #include <net80211/ieee80211_var.h>
117960b9804Smglocker #include <net80211/ieee80211_amrr.h>
1180f91cf57Smglocker #include <net80211/ieee80211_radiotap.h>
1190f91cf57Smglocker 
1200f91cf57Smglocker #include <dev/ic/acxvar.h>
1210f91cf57Smglocker #include <dev/ic/acxreg.h>
1220f91cf57Smglocker 
1230f91cf57Smglocker #ifdef ACX_DEBUG
12432934a82Sjsg int acxdebug = 0;
1250f91cf57Smglocker #endif
1260f91cf57Smglocker 
1270f91cf57Smglocker int	 acx_attach(struct acx_softc *);
1280f91cf57Smglocker int	 acx_detach(void *);
1290f91cf57Smglocker 
1300f91cf57Smglocker int	 acx_init(struct ifnet *);
1310f91cf57Smglocker int	 acx_stop(struct acx_softc *);
1320f91cf57Smglocker void	 acx_init_info_reg(struct acx_softc *);
1330f91cf57Smglocker int	 acx_config(struct acx_softc *);
1340f91cf57Smglocker int	 acx_read_config(struct acx_softc *, struct acx_config *);
1350f91cf57Smglocker int	 acx_write_config(struct acx_softc *, struct acx_config *);
136611f5384Smglocker int	 acx_rx_config(struct acx_softc *);
1370f91cf57Smglocker int	 acx_set_crypt_keys(struct acx_softc *);
1380f91cf57Smglocker void	 acx_next_scan(void *);
1390f91cf57Smglocker 
1400f91cf57Smglocker void	 acx_start(struct ifnet *);
1410f91cf57Smglocker void	 acx_watchdog(struct ifnet *);
1420f91cf57Smglocker 
1430f91cf57Smglocker int	 acx_ioctl(struct ifnet *, u_long, caddr_t);
1440f91cf57Smglocker 
1450f91cf57Smglocker int	 acx_intr(void *);
1460f91cf57Smglocker void	 acx_disable_intr(struct acx_softc *);
1470f91cf57Smglocker void	 acx_enable_intr(struct acx_softc *);
1480f91cf57Smglocker void	 acx_txeof(struct acx_softc *);
1490f91cf57Smglocker void	 acx_txerr(struct acx_softc *, uint8_t);
1500f91cf57Smglocker void	 acx_rxeof(struct acx_softc *);
1510f91cf57Smglocker 
1520f91cf57Smglocker int	 acx_dma_alloc(struct acx_softc *);
1530f91cf57Smglocker void	 acx_dma_free(struct acx_softc *);
1548eaebac4Sstsp void	 acx_init_tx_ring(struct acx_softc *);
1550f91cf57Smglocker int	 acx_init_rx_ring(struct acx_softc *);
1560f91cf57Smglocker int	 acx_newbuf(struct acx_softc *, struct acx_rxbuf *, int);
1570f91cf57Smglocker int	 acx_encap(struct acx_softc *, struct acx_txbuf *,
1580f91cf57Smglocker 	     struct mbuf *, struct ieee80211_node *, int);
1590f91cf57Smglocker 
1600f91cf57Smglocker int	 acx_reset(struct acx_softc *);
1610f91cf57Smglocker 
1620f91cf57Smglocker int	 acx_set_null_tmplt(struct acx_softc *);
1630f91cf57Smglocker int	 acx_set_probe_req_tmplt(struct acx_softc *, const char *, int);
164171ac09aSdamien #ifndef IEEE80211_STA_ONLY
165864278e7Smglocker int	 acx_set_probe_resp_tmplt(struct acx_softc *, struct ieee80211_node *);
166791621e9Sclaudio int	 acx_beacon_locate(struct mbuf *, u_int8_t);
167864278e7Smglocker int	 acx_set_beacon_tmplt(struct acx_softc *, struct ieee80211_node *);
168171ac09aSdamien #endif
1690f91cf57Smglocker 
1700f91cf57Smglocker int	 acx_read_eeprom(struct acx_softc *, uint32_t, uint8_t *);
1710f91cf57Smglocker int	 acx_read_phyreg(struct acx_softc *, uint32_t, uint8_t *);
172fb4deeb8Smglocker const char *	acx_get_rf(int);
1738dc7d017Smglocker int	 acx_get_maxrssi(int);
1740f91cf57Smglocker 
1750f91cf57Smglocker int	 acx_load_firmware(struct acx_softc *, uint32_t,
1760f91cf57Smglocker 	     const uint8_t *, int);
1778a86a891Sjsg int	 acx_load_radio_firmware(struct acx_softc *, const char *);
1785f0637dbSjsg int	 acx_load_base_firmware(struct acx_softc *, const char *);
1790f91cf57Smglocker 
18098e37aaaSmglocker struct ieee80211_node
18198e37aaaSmglocker 	*acx_node_alloc(struct ieee80211com *);
1820f91cf57Smglocker int	 acx_newstate(struct ieee80211com *, enum ieee80211_state, int);
1830f91cf57Smglocker 
1840f91cf57Smglocker void	 acx_init_cmd_reg(struct acx_softc *);
1850f91cf57Smglocker int	 acx_join_bss(struct acx_softc *, uint8_t, struct ieee80211_node *);
186611f5384Smglocker int	 acx_set_channel(struct acx_softc *, uint8_t);
1870f91cf57Smglocker int	 acx_init_radio(struct acx_softc *, uint32_t, uint32_t);
1880f91cf57Smglocker 
189960b9804Smglocker void	 acx_iter_func(void *, struct ieee80211_node *);
190960b9804Smglocker void	 acx_amrr_timeout(void *);
191960b9804Smglocker void	 acx_newassoc(struct ieee80211com *, struct ieee80211_node *, int);
192a0de3126Sclaudio #ifndef IEEE80211_STA_ONLY
193a0de3126Sclaudio void	 acx_set_tim(struct ieee80211com *, int, int);
194a0de3126Sclaudio #endif
195960b9804Smglocker 
1960f91cf57Smglocker int		acx_beacon_intvl = 100;	/* 100 TU */
1970f91cf57Smglocker 
1980f91cf57Smglocker /*
1990f91cf57Smglocker  * Possible values for the second parameter of acx_join_bss()
2000f91cf57Smglocker  */
2010f91cf57Smglocker #define ACX_MODE_ADHOC	0
2020f91cf57Smglocker #define ACX_MODE_UNUSED	1
2030f91cf57Smglocker #define ACX_MODE_STA	2
2040f91cf57Smglocker #define ACX_MODE_AP	3
2050f91cf57Smglocker 
2060f91cf57Smglocker struct cfdriver acx_cd = {
2070f91cf57Smglocker 	NULL, "acx", DV_IFNET
2080f91cf57Smglocker };
2090f91cf57Smglocker 
2100f91cf57Smglocker int
acx_attach(struct acx_softc * sc)2110f91cf57Smglocker acx_attach(struct acx_softc *sc)
2120f91cf57Smglocker {
2130f91cf57Smglocker 	struct ieee80211com *ic = &sc->sc_ic;
2140f91cf57Smglocker 	struct ifnet *ifp = &sc->sc_ic.ic_if;
2150f91cf57Smglocker 	int i, error;
2160f91cf57Smglocker 
2179dceea96Smglocker 	/* Initialize channel scanning timer */
2180f91cf57Smglocker 	timeout_set(&sc->sc_chanscan_timer, acx_next_scan, sc);
2190f91cf57Smglocker 
2200f91cf57Smglocker 	/* Allocate busdma stuffs */
2210f91cf57Smglocker 	error = acx_dma_alloc(sc);
222a4da89f3Sstsp 	if (error) {
223a4da89f3Sstsp 		printf("%s: attach failed, could not allocate DMA!\n",
224a4da89f3Sstsp 		    sc->sc_dev.dv_xname);
22520be36bbSclaudio 		return (error);
226a4da89f3Sstsp 	}
2270f91cf57Smglocker 
2280f91cf57Smglocker 	/* Reset Hardware */
2290f91cf57Smglocker 	error = acx_reset(sc);
230a4da89f3Sstsp 	if (error) {
231a4da89f3Sstsp 		printf("%s: attach failed, could not reset device!\n",
232a4da89f3Sstsp 		    sc->sc_dev.dv_xname);
23320be36bbSclaudio 		return (error);
234a4da89f3Sstsp 	}
2350f91cf57Smglocker 
2360f91cf57Smglocker 	/* Disable interrupts before firmware is loaded */
2370f91cf57Smglocker 	acx_disable_intr(sc);
2380f91cf57Smglocker 
2390f91cf57Smglocker 	/* Get radio type and form factor */
2400f91cf57Smglocker #define EEINFO_RETRY_MAX	50
2410f91cf57Smglocker 	for (i = 0; i < EEINFO_RETRY_MAX; ++i) {
2420f91cf57Smglocker 		uint16_t ee_info;
2430f91cf57Smglocker 
2440f91cf57Smglocker 		ee_info = CSR_READ_2(sc, ACXREG_EEPROM_INFO);
2450f91cf57Smglocker 		if (ACX_EEINFO_HAS_RADIO_TYPE(ee_info)) {
2460f91cf57Smglocker 			sc->sc_form_factor = ACX_EEINFO_FORM_FACTOR(ee_info);
2470f91cf57Smglocker 			sc->sc_radio_type = ACX_EEINFO_RADIO_TYPE(ee_info);
2480f91cf57Smglocker 			break;
2490f91cf57Smglocker 		}
2500f91cf57Smglocker 		DELAY(10000);
2510f91cf57Smglocker 	}
252a4da89f3Sstsp 	if (i == EEINFO_RETRY_MAX) {
253a4da89f3Sstsp 		printf("%s: attach failed, could not get radio type!\n",
254a4da89f3Sstsp 		    sc->sc_dev.dv_xname);
25520be36bbSclaudio 		return (ENXIO);
256a4da89f3Sstsp 	}
2570f91cf57Smglocker #undef EEINFO_RETRY_MAX
2580f91cf57Smglocker 
2590f91cf57Smglocker #ifdef DUMP_EEPROM
2600f91cf57Smglocker 	for (i = 0; i < 0x40; ++i) {
2610f91cf57Smglocker 		uint8_t val;
2620f91cf57Smglocker 
2630f91cf57Smglocker 		error = acx_read_eeprom(sc, i, &val);
2648eaebac4Sstsp 		if (error)
2658eaebac4Sstsp 			return (error);
2660f91cf57Smglocker 		if (i % 10 == 0)
2670f91cf57Smglocker 			printf("\n");
2680f91cf57Smglocker 		printf("%02x ", val);
2690f91cf57Smglocker 	}
2700f91cf57Smglocker 	printf("\n");
2710f91cf57Smglocker #endif	/* DUMP_EEPROM */
2720f91cf57Smglocker 
2730f91cf57Smglocker 	/* Get EEPROM version */
2740f91cf57Smglocker 	error = acx_read_eeprom(sc, ACX_EE_VERSION_OFS, &sc->sc_eeprom_ver);
275a4da89f3Sstsp 	if (error) {
276992476d5Sstsp 		printf("%s: attach failed, could not get EEPROM version!\n",
277a4da89f3Sstsp 		    sc->sc_dev.dv_xname);
27820be36bbSclaudio 		return (error);
279a4da89f3Sstsp 	}
280f7437aceSderaadt 
2810f91cf57Smglocker 	ifp->if_softc = sc;
2820f91cf57Smglocker 	ifp->if_ioctl = acx_ioctl;
2830f91cf57Smglocker 	ifp->if_start = acx_start;
2840f91cf57Smglocker 	ifp->if_watchdog = acx_watchdog;
2850f91cf57Smglocker 	ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST;
2860f91cf57Smglocker 	strlcpy(ifp->if_xname, sc->sc_dev.dv_xname, IFNAMSIZ);
287*cf96265bSbluhm 	ifq_init_maxlen(&ifp->if_snd, IFQ_MAXLEN);
2880f91cf57Smglocker 
2890f91cf57Smglocker 	/* Set channels */
2900f91cf57Smglocker 	for (i = 1; i <= 14; ++i) {
2910f91cf57Smglocker 		ic->ic_channels[i].ic_freq =
2920f91cf57Smglocker 		    ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ);
2930f91cf57Smglocker 		ic->ic_channels[i].ic_flags = sc->chip_chan_flags;
2940f91cf57Smglocker 	}
2950f91cf57Smglocker 
2960f91cf57Smglocker 	ic->ic_opmode = IEEE80211_M_STA;
2970f91cf57Smglocker 	ic->ic_state = IEEE80211_S_INIT;
2980f91cf57Smglocker 
2990f91cf57Smglocker 	/*
3000f91cf57Smglocker 	 * NOTE: Don't overwrite ic_caps set by chip specific code
3010f91cf57Smglocker 	 */
302611f5384Smglocker 	ic->ic_caps =
303611f5384Smglocker 	    IEEE80211_C_WEP |			/* WEP */
304611f5384Smglocker 	    IEEE80211_C_MONITOR |		/* Monitor mode */
305171ac09aSdamien #ifndef IEEE80211_STA_ONLY
306171ac09aSdamien 	    IEEE80211_C_IBSS |			/* IBSS mode */
307b206d01bSmglocker 	    IEEE80211_C_HOSTAP |		/* Access Point */
308a0de3126Sclaudio 	    IEEE80211_C_APPMGT |		/* AP Power Mgmt */
309171ac09aSdamien #endif
3100f91cf57Smglocker 	    IEEE80211_C_SHPREAMBLE;		/* Short preamble */
3110f91cf57Smglocker 
3120f91cf57Smglocker 	/* Get station id */
3130f91cf57Smglocker 	for (i = 0; i < IEEE80211_ADDR_LEN; ++i) {
3140f91cf57Smglocker 		error = acx_read_eeprom(sc, sc->chip_ee_eaddr_ofs - i,
3150f91cf57Smglocker 		    &ic->ic_myaddr[i]);
3168eaebac4Sstsp 		if (error) {
3178eaebac4Sstsp 			printf("%s: attach failed, could not get station id\n",
3188eaebac4Sstsp 			    sc->sc_dev.dv_xname);
3198eaebac4Sstsp 			return error;
3208eaebac4Sstsp 		}
3210f91cf57Smglocker 	}
3220f91cf57Smglocker 
323992476d5Sstsp 	printf("%s: %s, radio %s (0x%02x), EEPROM ver %u, address %s\n",
324992476d5Sstsp 	    sc->sc_dev.dv_xname,
325992476d5Sstsp 	    (sc->sc_flags & ACX_FLAG_ACX111) ? "ACX111" : "ACX100",
326992476d5Sstsp 	    acx_get_rf(sc->sc_radio_type), sc->sc_radio_type,
327992476d5Sstsp 	    sc->sc_eeprom_ver, ether_sprintf(ic->ic_myaddr));
3280f91cf57Smglocker 
3290f91cf57Smglocker 	if_attach(ifp);
3300f91cf57Smglocker 	ieee80211_ifattach(ifp);
3310f91cf57Smglocker 
3320f91cf57Smglocker 	/* Override node alloc */
3330f91cf57Smglocker 	ic->ic_node_alloc = acx_node_alloc;
334960b9804Smglocker 	ic->ic_newassoc = acx_newassoc;
3350f91cf57Smglocker 
336a0de3126Sclaudio #ifndef IEEE80211_STA_ONLY
337a0de3126Sclaudio 	/* Override set TIM */
338a0de3126Sclaudio 	ic->ic_set_tim = acx_set_tim;
339a0de3126Sclaudio #endif
340a0de3126Sclaudio 
3410f91cf57Smglocker 	/* Override newstate */
3420f91cf57Smglocker 	sc->sc_newstate = ic->ic_newstate;
3430f91cf57Smglocker 	ic->ic_newstate = acx_newstate;
3440f91cf57Smglocker 
3458dc7d017Smglocker 	/* Set maximal rssi */
3468dc7d017Smglocker 	ic->ic_max_rssi = acx_get_maxrssi(sc->sc_radio_type);
3478dc7d017Smglocker 
3480f91cf57Smglocker 	ieee80211_media_init(ifp, ieee80211_media_change,
3490f91cf57Smglocker 	    ieee80211_media_status);
3500f91cf57Smglocker 
351960b9804Smglocker 	/* AMRR rate control */
352960b9804Smglocker 	sc->amrr.amrr_min_success_threshold = 1;
353960b9804Smglocker 	sc->amrr.amrr_max_success_threshold = 15;
354960b9804Smglocker 	timeout_set(&sc->amrr_ch, acx_amrr_timeout, sc);
355960b9804Smglocker 
3560f91cf57Smglocker 	sc->sc_long_retry_limit = 4;
3570f91cf57Smglocker 	sc->sc_short_retry_limit = 7;
3580f91cf57Smglocker 	sc->sc_msdu_lifetime = 4096;
3590f91cf57Smglocker 
360172cbfceSmglocker #if NBPFILTER > 0
361172cbfceSmglocker 	bpfattach(&sc->sc_drvbpf, ifp, DLT_IEEE802_11_RADIO,
362172cbfceSmglocker 	    sizeof(struct ieee80211_frame) + 64);
363172cbfceSmglocker 
364172cbfceSmglocker 	sc->sc_rxtap_len = sizeof(sc->sc_rxtapu);
365172cbfceSmglocker 	sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len);
366172cbfceSmglocker 	sc->sc_rxtap.wr_ihdr.it_present = htole32(ACX_RX_RADIOTAP_PRESENT);
367172cbfceSmglocker 
368172cbfceSmglocker 	sc->sc_txtap_len = sizeof(sc->sc_txtapu);
369172cbfceSmglocker 	sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len);
370172cbfceSmglocker 	sc->sc_txtap.wt_ihdr.it_present = htole32(ACX_TX_RADIOTAP_PRESENT);
371172cbfceSmglocker #endif
372172cbfceSmglocker 
37320be36bbSclaudio 	return (0);
3740f91cf57Smglocker }
3750f91cf57Smglocker 
3760f91cf57Smglocker int
acx_detach(void * xsc)3770f91cf57Smglocker acx_detach(void *xsc)
3780f91cf57Smglocker {
3790f91cf57Smglocker 	struct acx_softc *sc = xsc;
3800f91cf57Smglocker 	struct ieee80211com *ic = &sc->sc_ic;
3810f91cf57Smglocker 	struct ifnet *ifp = &ic->ic_if;
3820f91cf57Smglocker 
3830f91cf57Smglocker 	acx_stop(sc);
3840f91cf57Smglocker 	ieee80211_ifdetach(ifp);
3852bab479aSjsg 	if_detach(ifp);
3860f91cf57Smglocker 
3870f91cf57Smglocker 	acx_dma_free(sc);
38898e37aaaSmglocker 
38920be36bbSclaudio 	return (0);
3900f91cf57Smglocker }
3910f91cf57Smglocker 
3920f91cf57Smglocker int
acx_init(struct ifnet * ifp)3930f91cf57Smglocker acx_init(struct ifnet *ifp)
3940f91cf57Smglocker {
3950f91cf57Smglocker 	struct acx_softc *sc = ifp->if_softc;
396611f5384Smglocker 	struct ieee80211com *ic = &sc->sc_ic;
39705c6c77cSjsg 	char fname[] = "tiacx111c16";
39805c6c77cSjsg 	int error, combined = 0;
3990f91cf57Smglocker 
4000f91cf57Smglocker 	error = acx_stop(sc);
4010f91cf57Smglocker 	if (error)
40298e37aaaSmglocker 		return (EIO);
4030f91cf57Smglocker 
4040f91cf57Smglocker 	/* enable card if possible */
4058eaebac4Sstsp 	if (sc->sc_enable != NULL) {
4068eaebac4Sstsp 		error = (*sc->sc_enable)(sc);
4078eaebac4Sstsp 		if (error)
4088eaebac4Sstsp 			return (EIO);
4090f91cf57Smglocker 	}
4100f91cf57Smglocker 
4118eaebac4Sstsp 	acx_init_tx_ring(sc);
4128eaebac4Sstsp 
4130f91cf57Smglocker 	error = acx_init_rx_ring(sc);
4140f91cf57Smglocker 	if (error) {
4150f91cf57Smglocker 		printf("%s: can't initialize RX ring\n",
4160f91cf57Smglocker 		    sc->sc_dev.dv_xname);
4170f91cf57Smglocker 		goto back;
4180f91cf57Smglocker 	}
4190f91cf57Smglocker 
42005c6c77cSjsg 	if (sc->sc_flags & ACX_FLAG_ACX111) {
42105c6c77cSjsg 		snprintf(fname, sizeof(fname), "tiacx111c%02X",
42205c6c77cSjsg 		    sc->sc_radio_type);
42305c6c77cSjsg 		error = acx_load_base_firmware(sc, fname);
42405c6c77cSjsg 
42505c6c77cSjsg 		if (!error)
42605c6c77cSjsg 			combined = 1;
42705c6c77cSjsg 	}
42805c6c77cSjsg 
42905c6c77cSjsg 	if (!combined) {
43005c6c77cSjsg 		snprintf(fname, sizeof(fname), "tiacx%s",
43105c6c77cSjsg 		    (sc->sc_flags & ACX_FLAG_ACX111) ? "111" : "100");
43205c6c77cSjsg 		error = acx_load_base_firmware(sc, fname);
43305c6c77cSjsg 	}
4345f0637dbSjsg 
4350f91cf57Smglocker 	if (error)
4360f91cf57Smglocker 		goto back;
4370f91cf57Smglocker 
4380f91cf57Smglocker 	/*
4390f91cf57Smglocker 	 * Initialize command and information registers
4400f91cf57Smglocker 	 * NOTE: This should be done after base firmware is loaded
4410f91cf57Smglocker 	 */
4420f91cf57Smglocker 	acx_init_cmd_reg(sc);
4430f91cf57Smglocker 	acx_init_info_reg(sc);
4440f91cf57Smglocker 
4450f91cf57Smglocker 	sc->sc_flags |= ACX_FLAG_FW_LOADED;
4460f91cf57Smglocker 
44705c6c77cSjsg 	if (!combined) {
44805c6c77cSjsg 		snprintf(fname, sizeof(fname), "tiacx%sr%02X",
44905c6c77cSjsg 		    (sc->sc_flags & ACX_FLAG_ACX111) ? "111" : "100",
45005c6c77cSjsg 		    sc->sc_radio_type);
45105c6c77cSjsg 		error = acx_load_radio_firmware(sc, fname);
45205c6c77cSjsg 
4530f91cf57Smglocker 		if (error)
4540f91cf57Smglocker 			goto back;
4555f0637dbSjsg 	}
4560f91cf57Smglocker 
4570f91cf57Smglocker 	error = sc->chip_init(sc);
4580f91cf57Smglocker 	if (error)
4590f91cf57Smglocker 		goto back;
4600f91cf57Smglocker 
4610f91cf57Smglocker 	/* Get and set device various configuration */
4620f91cf57Smglocker 	error = acx_config(sc);
4630f91cf57Smglocker 	if (error)
4640f91cf57Smglocker 		goto back;
4650f91cf57Smglocker 
4660f91cf57Smglocker 	/* Setup crypto stuffs */
4670f91cf57Smglocker 	if (sc->sc_ic.ic_flags & IEEE80211_F_WEPON) {
4680f91cf57Smglocker 		error = acx_set_crypt_keys(sc);
4690f91cf57Smglocker 		if (error)
4700f91cf57Smglocker 			goto back;
4710f91cf57Smglocker 	}
4720f91cf57Smglocker 
4730f91cf57Smglocker 	/* Turn on power led */
4740f91cf57Smglocker 	CSR_CLRB_2(sc, ACXREG_GPIO_OUT, sc->chip_gpio_pled);
4750f91cf57Smglocker 
4760f91cf57Smglocker 	acx_enable_intr(sc);
4770f91cf57Smglocker 
4780f91cf57Smglocker 	ifp->if_flags |= IFF_RUNNING;
479de6cd8fbSdlg 	ifq_clr_oactive(&ifp->if_snd);
4800f91cf57Smglocker 
481611f5384Smglocker 	if (ic->ic_opmode != IEEE80211_M_MONITOR)
482611f5384Smglocker 		/* start background scanning */
483611f5384Smglocker 		ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
484611f5384Smglocker 	else
485611f5384Smglocker 		/* in monitor mode change directly into run state */
486611f5384Smglocker 		ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
4870f91cf57Smglocker 
4880f91cf57Smglocker 	return (0);
4898eaebac4Sstsp back:
4908eaebac4Sstsp 	acx_stop(sc);
4918eaebac4Sstsp 	return (error);
4920f91cf57Smglocker }
4930f91cf57Smglocker 
4940f91cf57Smglocker void
acx_init_info_reg(struct acx_softc * sc)4950f91cf57Smglocker acx_init_info_reg(struct acx_softc *sc)
4960f91cf57Smglocker {
4970f91cf57Smglocker 	sc->sc_info = CSR_READ_4(sc, ACXREG_INFO_REG_OFFSET);
4980f91cf57Smglocker 	sc->sc_info_param = sc->sc_info + ACX_INFO_REG_SIZE;
4990f91cf57Smglocker }
5000f91cf57Smglocker 
5010f91cf57Smglocker int
acx_set_crypt_keys(struct acx_softc * sc)5020f91cf57Smglocker acx_set_crypt_keys(struct acx_softc *sc)
5030f91cf57Smglocker {
5040f91cf57Smglocker 	struct ieee80211com *ic = &sc->sc_ic;
5050f91cf57Smglocker 	struct acx_conf_wep_txkey wep_txkey;
5060f91cf57Smglocker 	int i, error, got_wk = 0;
5070f91cf57Smglocker 
5080f91cf57Smglocker 	for (i = 0; i < IEEE80211_WEP_NKID; ++i) {
50983da4af0Sdamien 		struct ieee80211_key *k = &ic->ic_nw_keys[i];
5100f91cf57Smglocker 
51183da4af0Sdamien 		if (k->k_len == 0)
5120f91cf57Smglocker 			continue;
5130f91cf57Smglocker 
5140f91cf57Smglocker 		if (sc->chip_hw_crypt) {
51583da4af0Sdamien 			error = sc->chip_set_wepkey(sc, k, i);
5160f91cf57Smglocker 			if (error)
517cd0c2b94Smglocker 				return (error);
5180f91cf57Smglocker 			got_wk = 1;
5190f91cf57Smglocker 		}
5200f91cf57Smglocker 	}
5210f91cf57Smglocker 
522cd0c2b94Smglocker 	if (!got_wk)
523cd0c2b94Smglocker 		return (0);
5240f91cf57Smglocker 
5250f91cf57Smglocker 	/* Set current WEP key index */
526cd0c2b94Smglocker 	wep_txkey.wep_txkey = ic->ic_wep_txkey;
527bb966e7aSclaudio 	if (acx_set_conf(sc, ACX_CONF_WEP_TXKEY, &wep_txkey,
528bb966e7aSclaudio 	    sizeof(wep_txkey)) != 0) {
529cd0c2b94Smglocker 		printf("%s: set WEP txkey failed\n", sc->sc_dev.dv_xname);
53098e37aaaSmglocker 		return (ENXIO);
5310f91cf57Smglocker 	}
5320f91cf57Smglocker 
533cd0c2b94Smglocker 	return (0);
534cd0c2b94Smglocker }
535cd0c2b94Smglocker 
5360f91cf57Smglocker void
acx_next_scan(void * arg)5370f91cf57Smglocker acx_next_scan(void *arg)
5380f91cf57Smglocker {
5390f91cf57Smglocker 	struct acx_softc *sc = arg;
5400f91cf57Smglocker 	struct ieee80211com *ic = &sc->sc_ic;
5410f91cf57Smglocker 	struct ifnet *ifp = &ic->ic_if;
5420f91cf57Smglocker 
54320be36bbSclaudio 	if (ic->ic_state == IEEE80211_S_SCAN)
5440f91cf57Smglocker 		ieee80211_next_scan(ifp);
5450f91cf57Smglocker }
5460f91cf57Smglocker 
5470f91cf57Smglocker int
acx_stop(struct acx_softc * sc)5480f91cf57Smglocker acx_stop(struct acx_softc *sc)
5490f91cf57Smglocker {
5500f91cf57Smglocker 	struct ieee80211com *ic = &sc->sc_ic;
5510f91cf57Smglocker 	struct ifnet *ifp = &ic->ic_if;
5520f91cf57Smglocker 	struct acx_buf_data *bd = &sc->sc_buf_data;
5530f91cf57Smglocker 	struct acx_ring_data *rd = &sc->sc_ring_data;
5540f91cf57Smglocker 	int i, error;
5550f91cf57Smglocker 
5560f91cf57Smglocker 	sc->sc_firmware_ver = 0;
5570f91cf57Smglocker 	sc->sc_hardware_id = 0;
5580f91cf57Smglocker 
5590f91cf57Smglocker 	/* Reset hardware */
5600f91cf57Smglocker 	error = acx_reset(sc);
5610f91cf57Smglocker 	if (error)
56298e37aaaSmglocker 		return (error);
5630f91cf57Smglocker 
5640f91cf57Smglocker 	/* Firmware no longer functions after hardware reset */
5650f91cf57Smglocker 	sc->sc_flags &= ~ACX_FLAG_FW_LOADED;
5660f91cf57Smglocker 
5670f91cf57Smglocker 	acx_disable_intr(sc);
5680f91cf57Smglocker 
5694b1a56afSjsg 	/* Stop background scanning */
5700f91cf57Smglocker 	timeout_del(&sc->sc_chanscan_timer);
5710f91cf57Smglocker 
5720f91cf57Smglocker 	/* Turn off power led */
5730f91cf57Smglocker 	CSR_SETB_2(sc, ACXREG_GPIO_OUT, sc->chip_gpio_pled);
5740f91cf57Smglocker 
5750f91cf57Smglocker 	/* Free TX mbuf */
5760f91cf57Smglocker 	for (i = 0; i < ACX_TX_DESC_CNT; ++i) {
5770f91cf57Smglocker 		struct acx_txbuf *buf;
5780f91cf57Smglocker 		struct ieee80211_node *ni;
5790f91cf57Smglocker 
5800f91cf57Smglocker 		buf = &bd->tx_buf[i];
5810f91cf57Smglocker 
5820f91cf57Smglocker 		if (buf->tb_mbuf != NULL) {
58398e37aaaSmglocker 			bus_dmamap_unload(sc->sc_dmat, buf->tb_mbuf_dmamap);
5840f91cf57Smglocker 			m_freem(buf->tb_mbuf);
5850f91cf57Smglocker 			buf->tb_mbuf = NULL;
5860f91cf57Smglocker 		}
5870f91cf57Smglocker 
5880f91cf57Smglocker 		ni = (struct ieee80211_node *)buf->tb_node;
5890f91cf57Smglocker 		if (ni != NULL)
5900f91cf57Smglocker 			ieee80211_release_node(ic, ni);
5910f91cf57Smglocker 		buf->tb_node = NULL;
5920f91cf57Smglocker 	}
5930f91cf57Smglocker 
5940f91cf57Smglocker 	/* Clear TX host descriptors */
5950f91cf57Smglocker 	bzero(rd->tx_ring, ACX_TX_RING_SIZE);
5960f91cf57Smglocker 
5970f91cf57Smglocker 	/* Free RX mbuf */
5980f91cf57Smglocker 	for (i = 0; i < ACX_RX_DESC_CNT; ++i) {
5990f91cf57Smglocker 		if (bd->rx_buf[i].rb_mbuf != NULL) {
6000f91cf57Smglocker 			bus_dmamap_unload(sc->sc_dmat,
6010f91cf57Smglocker 			    bd->rx_buf[i].rb_mbuf_dmamap);
6020f91cf57Smglocker 			m_freem(bd->rx_buf[i].rb_mbuf);
6030f91cf57Smglocker 			bd->rx_buf[i].rb_mbuf = NULL;
6040f91cf57Smglocker 		}
6050f91cf57Smglocker 	}
6060f91cf57Smglocker 
6070f91cf57Smglocker 	/* Clear RX host descriptors */
6080f91cf57Smglocker 	bzero(rd->rx_ring, ACX_RX_RING_SIZE);
6090f91cf57Smglocker 
61054fb4829Sbrad 	sc->sc_txtimer = 0;
6110f91cf57Smglocker 	ifp->if_timer = 0;
612de6cd8fbSdlg 	ifp->if_flags &= ~IFF_RUNNING;
613de6cd8fbSdlg 	ifq_clr_oactive(&ifp->if_snd);
6140f91cf57Smglocker 	ieee80211_new_state(&sc->sc_ic, IEEE80211_S_INIT, -1);
6150f91cf57Smglocker 
6160f91cf57Smglocker 	/* disable card if possible */
6170f91cf57Smglocker 	if (sc->sc_disable != NULL)
6180f91cf57Smglocker 		(*sc->sc_disable)(sc);
6190f91cf57Smglocker 
62098e37aaaSmglocker 	return (0);
6210f91cf57Smglocker }
6220f91cf57Smglocker 
6230f91cf57Smglocker int
acx_config(struct acx_softc * sc)6240f91cf57Smglocker acx_config(struct acx_softc *sc)
6250f91cf57Smglocker {
6260f91cf57Smglocker 	struct acx_config conf;
6270f91cf57Smglocker 	int error;
6280f91cf57Smglocker 
6290f91cf57Smglocker 	error = acx_read_config(sc, &conf);
6300f91cf57Smglocker 	if (error)
63198e37aaaSmglocker 		return (error);
6320f91cf57Smglocker 
6330f91cf57Smglocker 	error = acx_write_config(sc, &conf);
6340f91cf57Smglocker 	if (error)
63598e37aaaSmglocker 		return (error);
6360f91cf57Smglocker 
637611f5384Smglocker 	error = acx_rx_config(sc);
638611f5384Smglocker 	if (error)
639611f5384Smglocker 		return (error);
640611f5384Smglocker 
6410f91cf57Smglocker 	if (acx_set_probe_req_tmplt(sc, "", 0) != 0) {
6420f91cf57Smglocker 		printf("%s: can't set probe req template "
6430f91cf57Smglocker 		    "(empty ssid)\n", sc->sc_dev.dv_xname);
64498e37aaaSmglocker 		return (ENXIO);
6450f91cf57Smglocker 	}
6460f91cf57Smglocker 
6470f91cf57Smglocker 	/* XXX for PM?? */
6480f91cf57Smglocker 	if (acx_set_null_tmplt(sc) != 0) {
6490f91cf57Smglocker 		printf("%s: can't set null data template\n",
6500f91cf57Smglocker 		    sc->sc_dev.dv_xname);
65198e37aaaSmglocker 		return (ENXIO);
6520f91cf57Smglocker 	}
65398e37aaaSmglocker 
65498e37aaaSmglocker 	return (0);
6550f91cf57Smglocker }
6560f91cf57Smglocker 
6570f91cf57Smglocker int
acx_read_config(struct acx_softc * sc,struct acx_config * conf)6580f91cf57Smglocker acx_read_config(struct acx_softc *sc, struct acx_config *conf)
6590f91cf57Smglocker {
6600f91cf57Smglocker 	struct acx_conf_regdom reg_dom;
6610f91cf57Smglocker 	struct acx_conf_antenna ant;
6620f91cf57Smglocker 	struct acx_conf_fwrev fw_rev;
6630f91cf57Smglocker 	uint32_t fw_rev_no;
6640f91cf57Smglocker 	uint8_t sen;
66533f06d6dSjsg 	int error;
6660f91cf57Smglocker 
6670f91cf57Smglocker 	/* Get region domain */
668bb966e7aSclaudio 	if (acx_get_conf(sc, ACX_CONF_REGDOM, &reg_dom, sizeof(reg_dom)) != 0) {
6690f91cf57Smglocker 		printf("%s: can't get region domain\n", sc->sc_dev.dv_xname);
67098e37aaaSmglocker 		return (ENXIO);
6710f91cf57Smglocker 	}
6720f91cf57Smglocker 	conf->regdom = reg_dom.regdom;
6730f91cf57Smglocker 	DPRINTF(("%s: regdom %02x\n", sc->sc_dev.dv_xname, reg_dom.regdom));
6740f91cf57Smglocker 
6750f91cf57Smglocker 	/* Get antenna */
676bb966e7aSclaudio 	if (acx_get_conf(sc, ACX_CONF_ANTENNA, &ant, sizeof(ant)) != 0) {
6770f91cf57Smglocker 		printf("%s: can't get antenna\n", sc->sc_dev.dv_xname);
67898e37aaaSmglocker 		return (ENXIO);
6790f91cf57Smglocker 	}
6800f91cf57Smglocker 	conf->antenna = ant.antenna;
6810f91cf57Smglocker 	DPRINTF(("%s: antenna %02x\n", sc->sc_dev.dv_xname, ant.antenna));
6820f91cf57Smglocker 
6830f91cf57Smglocker 	/* Get sensitivity XXX not used */
6840f91cf57Smglocker 	if (sc->sc_radio_type == ACX_RADIO_TYPE_MAXIM ||
6850f91cf57Smglocker 	    sc->sc_radio_type == ACX_RADIO_TYPE_RFMD ||
6860f91cf57Smglocker 	    sc->sc_radio_type == ACX_RADIO_TYPE_RALINK) {
6870f91cf57Smglocker 		error = acx_read_phyreg(sc, ACXRV_PHYREG_SENSITIVITY, &sen);
6880f91cf57Smglocker 		if (error) {
6890f91cf57Smglocker 			printf("%s: can't get sensitivity\n",
6900f91cf57Smglocker 			    sc->sc_dev.dv_xname);
69198e37aaaSmglocker 			return (error);
6920f91cf57Smglocker 		}
69398e37aaaSmglocker 	} else
6940f91cf57Smglocker 		sen = 0;
6950f91cf57Smglocker 	DPRINTF(("%s: sensitivity %02x\n", sc->sc_dev.dv_xname, sen));
6960f91cf57Smglocker 
6970f91cf57Smglocker 	/* Get firmware revision */
698bb966e7aSclaudio 	if (acx_get_conf(sc, ACX_CONF_FWREV, &fw_rev, sizeof(fw_rev)) != 0) {
6990f91cf57Smglocker 		printf("%s: can't get firmware revision\n",
7000f91cf57Smglocker 		    sc->sc_dev.dv_xname);
70198e37aaaSmglocker 		return (ENXIO);
7020f91cf57Smglocker 	}
7030f91cf57Smglocker 
7040f91cf57Smglocker 	if (strncmp(fw_rev.fw_rev, "Rev ", 4) != 0) {
7050f91cf57Smglocker 		printf("%s: strange revision string -- %s\n",
7060f91cf57Smglocker 		    sc->sc_dev.dv_xname, fw_rev.fw_rev);
7070f91cf57Smglocker 		fw_rev_no = 0x01090407;
7080f91cf57Smglocker 	} else {
7090f91cf57Smglocker 		/*
7100f91cf57Smglocker 		 *  01234
7110f91cf57Smglocker 		 * "Rev xx.xx.xx.xx"
7120f91cf57Smglocker 		 *      ^ Start from here
7130f91cf57Smglocker 		 */
7140f91cf57Smglocker 		fw_rev_no  = fw_rev.fw_rev[0] << 24;
7150f91cf57Smglocker 		fw_rev_no |= fw_rev.fw_rev[1] << 16;
7160f91cf57Smglocker 		fw_rev_no |= fw_rev.fw_rev[2] <<  8;
7170f91cf57Smglocker 		fw_rev_no |= fw_rev.fw_rev[3];
7180f91cf57Smglocker 	}
7190f91cf57Smglocker 	sc->sc_firmware_ver = fw_rev_no;
7200f91cf57Smglocker 	sc->sc_hardware_id = letoh32(fw_rev.hw_id);
7210f91cf57Smglocker 	DPRINTF(("%s: fw rev %08x, hw id %08x\n",
7220f91cf57Smglocker 	    sc->sc_dev.dv_xname, sc->sc_firmware_ver, sc->sc_hardware_id));
7230f91cf57Smglocker 
7240f91cf57Smglocker 	if (sc->chip_read_config != NULL) {
7250f91cf57Smglocker 		error = sc->chip_read_config(sc, conf);
7260f91cf57Smglocker 		if (error)
72798e37aaaSmglocker 			return (error);
7280f91cf57Smglocker 	}
72998e37aaaSmglocker 
73098e37aaaSmglocker 	return (0);
7310f91cf57Smglocker }
7320f91cf57Smglocker 
7330f91cf57Smglocker int
acx_write_config(struct acx_softc * sc,struct acx_config * conf)7340f91cf57Smglocker acx_write_config(struct acx_softc *sc, struct acx_config *conf)
7350f91cf57Smglocker {
7360f91cf57Smglocker 	struct acx_conf_nretry_short sretry;
7370f91cf57Smglocker 	struct acx_conf_nretry_long lretry;
7380f91cf57Smglocker 	struct acx_conf_msdu_lifetime msdu_lifetime;
7390f91cf57Smglocker 	struct acx_conf_rate_fallback rate_fb;
7400f91cf57Smglocker 	struct acx_conf_antenna ant;
7410f91cf57Smglocker 	struct acx_conf_regdom reg_dom;
7420f91cf57Smglocker 	struct ifnet *ifp = &sc->sc_ic.ic_if;
7430f91cf57Smglocker 	int error;
7440f91cf57Smglocker 
7450f91cf57Smglocker 	/* Set number of long/short retry */
7460f91cf57Smglocker 	sretry.nretry = sc->sc_short_retry_limit;
747bb966e7aSclaudio 	if (acx_set_conf(sc, ACX_CONF_NRETRY_SHORT, &sretry,
748bb966e7aSclaudio 	    sizeof(sretry)) != 0) {
7490f91cf57Smglocker 		printf("%s: can't set short retry limit\n", ifp->if_xname);
75098e37aaaSmglocker 		return (ENXIO);
7510f91cf57Smglocker 	}
7520f91cf57Smglocker 
7530f91cf57Smglocker 	lretry.nretry = sc->sc_long_retry_limit;
754bb966e7aSclaudio 	if (acx_set_conf(sc, ACX_CONF_NRETRY_LONG, &lretry,
755bb966e7aSclaudio 	    sizeof(lretry)) != 0) {
7560f91cf57Smglocker 		printf("%s: can't set long retry limit\n", ifp->if_xname);
75798e37aaaSmglocker 		return (ENXIO);
7580f91cf57Smglocker 	}
7590f91cf57Smglocker 
7600f91cf57Smglocker 	/* Set MSDU lifetime */
7610f91cf57Smglocker 	msdu_lifetime.lifetime = htole32(sc->sc_msdu_lifetime);
762bb966e7aSclaudio 	if (acx_set_conf(sc, ACX_CONF_MSDU_LIFETIME, &msdu_lifetime,
763bb966e7aSclaudio 	    sizeof(msdu_lifetime)) != 0) {
7640f91cf57Smglocker 		printf("%s: can't set MSDU lifetime\n", ifp->if_xname);
76598e37aaaSmglocker 		return (ENXIO);
7660f91cf57Smglocker 	}
7670f91cf57Smglocker 
7680f91cf57Smglocker 	/* Enable rate fallback */
7690f91cf57Smglocker 	rate_fb.ratefb_enable = 1;
770bb966e7aSclaudio 	if (acx_set_conf(sc, ACX_CONF_RATE_FALLBACK, &rate_fb,
771bb966e7aSclaudio 	    sizeof(rate_fb)) != 0) {
7720f91cf57Smglocker 		printf("%s: can't enable rate fallback\n", ifp->if_xname);
77398e37aaaSmglocker 		return (ENXIO);
7740f91cf57Smglocker 	}
7750f91cf57Smglocker 
7760f91cf57Smglocker 	/* Set antenna */
7770f91cf57Smglocker 	ant.antenna = conf->antenna;
778bb966e7aSclaudio 	if (acx_set_conf(sc, ACX_CONF_ANTENNA, &ant, sizeof(ant)) != 0) {
7790f91cf57Smglocker 		printf("%s: can't set antenna\n", ifp->if_xname);
78098e37aaaSmglocker 		return (ENXIO);
7810f91cf57Smglocker 	}
7820f91cf57Smglocker 
7830f91cf57Smglocker 	/* Set region domain */
7840f91cf57Smglocker 	reg_dom.regdom = conf->regdom;
785bb966e7aSclaudio 	if (acx_set_conf(sc, ACX_CONF_REGDOM, &reg_dom, sizeof(reg_dom)) != 0) {
7860f91cf57Smglocker 		printf("%s: can't set region domain\n", ifp->if_xname);
78798e37aaaSmglocker 		return (ENXIO);
7880f91cf57Smglocker 	}
7890f91cf57Smglocker 
7900f91cf57Smglocker 	if (sc->chip_write_config != NULL) {
7910f91cf57Smglocker 		error = sc->chip_write_config(sc, conf);
7920f91cf57Smglocker 		if (error)
79398e37aaaSmglocker 			return (error);
7940f91cf57Smglocker 	}
7950f91cf57Smglocker 
796611f5384Smglocker 	return (0);
797611f5384Smglocker }
798611f5384Smglocker 
799611f5384Smglocker int
acx_rx_config(struct acx_softc * sc)800611f5384Smglocker acx_rx_config(struct acx_softc *sc)
801611f5384Smglocker {
802611f5384Smglocker 	struct ieee80211com *ic = &sc->sc_ic;
803611f5384Smglocker 	struct acx_conf_rxopt rx_opt;
804611f5384Smglocker 
805611f5384Smglocker 	/* tell the RX receiver what frames we want to have */
806611f5384Smglocker 	rx_opt.opt1 = htole16(RXOPT1_INCL_RXBUF_HDR);
807611f5384Smglocker 	rx_opt.opt2 = htole16(
808611f5384Smglocker 	    RXOPT2_RECV_ASSOC_REQ |
809611f5384Smglocker 	    RXOPT2_RECV_AUTH |
810611f5384Smglocker 	    RXOPT2_RECV_BEACON |
811611f5384Smglocker 	    RXOPT2_RECV_CF |
812611f5384Smglocker 	    RXOPT2_RECV_CTRL |
813611f5384Smglocker 	    RXOPT2_RECV_DATA |
814611f5384Smglocker 	    RXOPT2_RECV_MGMT |
815611f5384Smglocker 	    RXOPT2_RECV_PROBE_REQ |
816611f5384Smglocker 	    RXOPT2_RECV_PROBE_RESP |
817611f5384Smglocker 	    RXOPT2_RECV_OTHER);
818611f5384Smglocker 
819611f5384Smglocker 	/* in monitor mode go promiscuous */
820611f5384Smglocker 	if (ic->ic_opmode == IEEE80211_M_MONITOR) {
821611f5384Smglocker 		rx_opt.opt1 |= RXOPT1_PROMISC;
822611f5384Smglocker 		rx_opt.opt2 |= RXOPT2_RECV_BROKEN | RXOPT2_RECV_ACK;
823611f5384Smglocker 	} else
824611f5384Smglocker 		rx_opt.opt1 |= RXOPT1_FILT_FDEST;
825611f5384Smglocker 
826611f5384Smglocker 	/* finally set the RX options */
827bb966e7aSclaudio 	if (acx_set_conf(sc, ACX_CONF_RXOPT, &rx_opt, sizeof(rx_opt)) != 0) {
828611f5384Smglocker 		printf("%s: can not set RX options!\n", sc->sc_dev.dv_xname);
82998e37aaaSmglocker 		return (ENXIO);
8300f91cf57Smglocker 	}
83198e37aaaSmglocker 
83298e37aaaSmglocker 	return (0);
8330f91cf57Smglocker }
8340f91cf57Smglocker 
8350f91cf57Smglocker int
acx_ioctl(struct ifnet * ifp,u_long cmd,caddr_t data)8360f91cf57Smglocker acx_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
8370f91cf57Smglocker {
8380f91cf57Smglocker 	struct acx_softc *sc = ifp->if_softc;
8390f91cf57Smglocker 	struct ieee80211com *ic = &sc->sc_ic;
8400f91cf57Smglocker 	int s, error = 0;
841611f5384Smglocker 	uint8_t chan;
8420f91cf57Smglocker 
8430f91cf57Smglocker 	s = splnet();
8440f91cf57Smglocker 
8450f91cf57Smglocker 	switch (cmd) {
8460f91cf57Smglocker 	case SIOCSIFADDR:
8470f91cf57Smglocker 		ifp->if_flags |= IFF_UP;
8480f91cf57Smglocker 		/* FALLTHROUGH */
8490f91cf57Smglocker 	case SIOCSIFFLAGS:
8500f91cf57Smglocker 		if (ifp->if_flags & IFF_UP) {
8510f91cf57Smglocker 			if ((ifp->if_flags & IFF_RUNNING) == 0)
8528eaebac4Sstsp 				error = acx_init(ifp);
8530f91cf57Smglocker 		} else {
8540f91cf57Smglocker 			if (ifp->if_flags & IFF_RUNNING)
8558eaebac4Sstsp 				error = acx_stop(sc);
8560f91cf57Smglocker 		}
8570f91cf57Smglocker 		break;
858611f5384Smglocker 	case SIOCS80211CHANNEL:
859611f5384Smglocker 		/* allow fast channel switching in monitor mode */
860611f5384Smglocker 		error = ieee80211_ioctl(ifp, cmd, data);
861611f5384Smglocker 		if (error == ENETRESET &&
862611f5384Smglocker 		    ic->ic_opmode == IEEE80211_M_MONITOR) {
863611f5384Smglocker 			if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) ==
864611f5384Smglocker 			    (IFF_UP | IFF_RUNNING)) {
865611f5384Smglocker 				ic->ic_bss->ni_chan = ic->ic_ibss_chan;
866611f5384Smglocker 				chan = ieee80211_chan2ieee(ic,
867611f5384Smglocker 				    ic->ic_bss->ni_chan);
868611f5384Smglocker 				(void)acx_set_channel(sc, chan);
869611f5384Smglocker 			}
870611f5384Smglocker 			error = 0;
871611f5384Smglocker 		}
872611f5384Smglocker 		break;
8730f91cf57Smglocker 	default:
8740f91cf57Smglocker 		error = ieee80211_ioctl(ifp, cmd, data);
8750f91cf57Smglocker 		break;
8760f91cf57Smglocker 	}
8770f91cf57Smglocker 
8780f91cf57Smglocker 	if (error == ENETRESET) {
8790f91cf57Smglocker 		if ((ifp->if_flags & (IFF_RUNNING | IFF_UP)) ==
8800f91cf57Smglocker 		    (IFF_RUNNING | IFF_UP))
8818eaebac4Sstsp 			error = acx_init(ifp);
8828eaebac4Sstsp 		else
8830f91cf57Smglocker 			error = 0;
8840f91cf57Smglocker 	}
8850f91cf57Smglocker 
8860f91cf57Smglocker 	splx(s);
8870f91cf57Smglocker 
88898e37aaaSmglocker 	return (error);
8890f91cf57Smglocker }
8900f91cf57Smglocker 
8910f91cf57Smglocker void
acx_start(struct ifnet * ifp)8920f91cf57Smglocker acx_start(struct ifnet *ifp)
8930f91cf57Smglocker {
8940f91cf57Smglocker 	struct acx_softc *sc = ifp->if_softc;
8950f91cf57Smglocker 	struct ieee80211com *ic = &sc->sc_ic;
8960f91cf57Smglocker 	struct acx_buf_data *bd = &sc->sc_buf_data;
8970f91cf57Smglocker 	struct acx_txbuf *buf;
8980f91cf57Smglocker 	int trans, idx;
8990f91cf57Smglocker 
9000f91cf57Smglocker 	if ((sc->sc_flags & ACX_FLAG_FW_LOADED) == 0 ||
9010f91cf57Smglocker 	    (ifp->if_flags & IFF_RUNNING) == 0 ||
902de6cd8fbSdlg 	    ifq_is_oactive(&ifp->if_snd))
9030f91cf57Smglocker 		return;
9040f91cf57Smglocker 
9050f91cf57Smglocker 	/*
9060f91cf57Smglocker 	 * NOTE:
9070f91cf57Smglocker 	 * We can't start from a random position that TX descriptor
9080f91cf57Smglocker 	 * is free, since hardware will be confused by that.
9090f91cf57Smglocker 	 * We have to follow the order of the TX ring.
9100f91cf57Smglocker 	 */
9110f91cf57Smglocker 	idx = bd->tx_free_start;
9120f91cf57Smglocker 	trans = 0;
9130f91cf57Smglocker 	for (buf = &bd->tx_buf[idx]; buf->tb_mbuf == NULL;
9140f91cf57Smglocker 	     buf = &bd->tx_buf[idx]) {
91558684cc2Sdamien 		struct ieee80211_frame *wh;
9160f91cf57Smglocker 		struct ieee80211_node *ni = NULL;
9170f91cf57Smglocker 		struct mbuf *m;
9180f91cf57Smglocker 		int rate;
9190f91cf57Smglocker 
920351e1934Sdlg 		m = mq_dequeue(&ic->ic_mgtq);
921a0de3126Sclaudio 		/* first dequeue management frames */
9220f91cf57Smglocker 		if (m != NULL) {
9236da4b19dSmpi 			ni = m->m_pkthdr.ph_cookie;
9240f91cf57Smglocker 
9250f91cf57Smglocker 			/*
92698bb54dcSmglocker 			 * probe response mgmt frames are handled by the
92798bb54dcSmglocker 			 * firmware already.  So, don't send them twice.
92898bb54dcSmglocker 			 */
92998bb54dcSmglocker 			wh = mtod(m, struct ieee80211_frame *);
93098bb54dcSmglocker 			if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
93198bb54dcSmglocker 			    IEEE80211_FC0_SUBTYPE_PROBE_RESP) {
93298bb54dcSmglocker 				if (ni != NULL)
93398bb54dcSmglocker 					ieee80211_release_node(ic, ni);
93498bb54dcSmglocker                                 m_freem(m);
93598bb54dcSmglocker                                 continue;
93698bb54dcSmglocker 			}
93798bb54dcSmglocker 
93898bb54dcSmglocker 			/*
939960b9804Smglocker 			 * mgmt frames are sent at the lowest available
940960b9804Smglocker 			 * bit-rate.
9410f91cf57Smglocker 			 */
942960b9804Smglocker 			rate = ni->ni_rates.rs_rates[0];
94352d3181bSmglocker 			rate &= IEEE80211_RATE_VAL;
944a0de3126Sclaudio 		} else {
9450f91cf57Smglocker 			struct ether_header *eh;
9460f91cf57Smglocker 
947a0de3126Sclaudio 			/* then dequeue packets on the powersave queue */
948351e1934Sdlg 			m = mq_dequeue(&ic->ic_pwrsaveq);
949a0de3126Sclaudio 			if (m != NULL) {
9506da4b19dSmpi 				ni = m->m_pkthdr.ph_cookie;
951a0de3126Sclaudio 				goto encapped;
952a0de3126Sclaudio 			} else {
95363bcfa73Spatrick 				m = ifq_dequeue(&ifp->if_snd);
954b687bd55Sclaudio 				if (m == NULL)
955b687bd55Sclaudio 					break;
956a0de3126Sclaudio 			}
9570f91cf57Smglocker 			if (ic->ic_state != IEEE80211_S_RUN) {
95872f2a522Smglocker 				DPRINTF(("%s: data packet dropped due to "
9590f91cf57Smglocker 				    "not RUN.  Current state %d\n",
96072f2a522Smglocker 				    ifp->if_xname, ic->ic_state));
961b687bd55Sclaudio 				m_freem(m);
9620f91cf57Smglocker 				break;
9630f91cf57Smglocker 			}
9640f91cf57Smglocker 
9650f91cf57Smglocker 			if (m->m_len < sizeof(struct ether_header)) {
9660f91cf57Smglocker 				m = m_pullup(m, sizeof(struct ether_header));
9670f91cf57Smglocker 				if (m == NULL) {
9680f91cf57Smglocker 					ifp->if_oerrors++;
9690f91cf57Smglocker 					continue;
9700f91cf57Smglocker 				}
9710f91cf57Smglocker 			}
9720f91cf57Smglocker 			eh = mtod(m, struct ether_header *);
9730f91cf57Smglocker 
9740f91cf57Smglocker 			/* TODO power save */
9750f91cf57Smglocker 
976172cbfceSmglocker #if NBPFILTER > 0
977172cbfceSmglocker 			if (ifp->if_bpf != NULL)
978172cbfceSmglocker 				bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
979172cbfceSmglocker #endif
980172cbfceSmglocker 
9811d1e9414Sjsg 			if ((m = ieee80211_encap(ifp, m, &ni)) == NULL) {
9820f91cf57Smglocker 				ifp->if_oerrors++;
9830f91cf57Smglocker 				continue;
9840f91cf57Smglocker 			}
985a0de3126Sclaudio encapped:
986960b9804Smglocker 			if (ic->ic_fixed_rate != -1) {
987960b9804Smglocker 				rate = ic->ic_sup_rates[ic->ic_curmode].
988960b9804Smglocker 				    rs_rates[ic->ic_fixed_rate];
989960b9804Smglocker 			} else
990960b9804Smglocker 				rate = ni->ni_rates.rs_rates[ni->ni_txrate];
991960b9804Smglocker 			rate &= IEEE80211_RATE_VAL;
992a0de3126Sclaudio 		}
993a0de3126Sclaudio 
994a0de3126Sclaudio #if NBPFILTER > 0
995a0de3126Sclaudio 		if (ic->ic_rawbpf != NULL)
996a0de3126Sclaudio 			bpf_mtap(ic->ic_rawbpf, m, BPF_DIRECTION_OUT);
997a0de3126Sclaudio #endif
9980f91cf57Smglocker 
99958684cc2Sdamien 		wh = mtod(m, struct ieee80211_frame *);
100058684cc2Sdamien 		if ((wh->i_fc[1] & IEEE80211_FC1_WEP) && !sc->chip_hw_crypt) {
1001e03e709cSdamien 			struct ieee80211_key *k;
1002e03e709cSdamien 
1003e03e709cSdamien 			k = ieee80211_get_txkey(ic, wh, ni);
1004e03e709cSdamien 			if ((m = ieee80211_encrypt(ic, m, k)) == NULL) {
10050f91cf57Smglocker 				ieee80211_release_node(ic, ni);
10060f91cf57Smglocker 				ifp->if_oerrors++;
10070f91cf57Smglocker 				continue;
10080f91cf57Smglocker 			}
10090f91cf57Smglocker 		}
10100f91cf57Smglocker 
1011172cbfceSmglocker #if NBPFILTER > 0
1012172cbfceSmglocker 		if (sc->sc_drvbpf != NULL) {
1013172cbfceSmglocker 			struct mbuf mb;
1014172cbfceSmglocker 			struct acx_tx_radiotap_hdr *tap = &sc->sc_txtap;
1015172cbfceSmglocker 
1016172cbfceSmglocker 			tap->wt_flags = 0;
1017172cbfceSmglocker 			tap->wt_rate = rate;
1018172cbfceSmglocker 			tap->wt_chan_freq =
1019172cbfceSmglocker 			    htole16(ic->ic_bss->ni_chan->ic_freq);
1020172cbfceSmglocker 			tap->wt_chan_flags =
1021172cbfceSmglocker 			    htole16(ic->ic_bss->ni_chan->ic_flags);
1022172cbfceSmglocker 
1023172cbfceSmglocker 			mb.m_data = (caddr_t)tap;
1024172cbfceSmglocker 			mb.m_len = sc->sc_txtap_len;
1025172cbfceSmglocker 			mb.m_next = m;
102602677ac7Sclaudio 			mb.m_nextpkt = NULL;
102702677ac7Sclaudio 			mb.m_type = 0;
102802677ac7Sclaudio 			mb.m_flags = 0;
1029172cbfceSmglocker 			bpf_mtap(sc->sc_drvbpf, &mb, BPF_DIRECTION_OUT);
1030172cbfceSmglocker 		}
1031172cbfceSmglocker #endif
1032172cbfceSmglocker 
10330f91cf57Smglocker 		if (acx_encap(sc, buf, m, ni, rate) != 0) {
10340f91cf57Smglocker 			/*
10350f91cf57Smglocker 			 * NOTE: `m' will be freed in acx_encap()
10360f91cf57Smglocker 			 * if we reach here.
10370f91cf57Smglocker 			 */
10380f91cf57Smglocker 			if (ni != NULL)
10390f91cf57Smglocker 				ieee80211_release_node(ic, ni);
10400f91cf57Smglocker 			ifp->if_oerrors++;
10410f91cf57Smglocker 			continue;
10420f91cf57Smglocker 		}
10430f91cf57Smglocker 
10440f91cf57Smglocker 		/*
10450f91cf57Smglocker 		 * NOTE:
10460f91cf57Smglocker 		 * 1) `m' should not be touched after acx_encap()
10470f91cf57Smglocker 		 * 2) `node' will be used to do TX rate control during
10480f91cf57Smglocker 		 *    acx_txeof(), so it is not freed here.  acx_txeof()
10490f91cf57Smglocker 		 *    will free it for us
10500f91cf57Smglocker 		 */
1051a0de3126Sclaudio 		trans++;
10520f91cf57Smglocker 		bd->tx_used_count++;
10530f91cf57Smglocker 		idx = (idx + 1) % ACX_TX_DESC_CNT;
10540f91cf57Smglocker 	}
10550f91cf57Smglocker 	bd->tx_free_start = idx;
10560f91cf57Smglocker 
10570f91cf57Smglocker 	if (bd->tx_used_count == ACX_TX_DESC_CNT)
1058de6cd8fbSdlg 		ifq_set_oactive(&ifp->if_snd);
10590f91cf57Smglocker 
106054fb4829Sbrad 	if (trans && sc->sc_txtimer == 0)
106152d2b7e2Smglocker 		sc->sc_txtimer = 5;
106254fb4829Sbrad 	ifp->if_timer = 1;
10630f91cf57Smglocker }
10640f91cf57Smglocker 
10650f91cf57Smglocker void
acx_watchdog(struct ifnet * ifp)10660f91cf57Smglocker acx_watchdog(struct ifnet *ifp)
10670f91cf57Smglocker {
106852d2b7e2Smglocker 	struct acx_softc *sc = ifp->if_softc;
106998e37aaaSmglocker 
107052d2b7e2Smglocker 	ifp->if_timer = 0;
107152d2b7e2Smglocker 
107252d2b7e2Smglocker 	if ((ifp->if_flags & IFF_RUNNING) == 0)
107352d2b7e2Smglocker 		return;
107452d2b7e2Smglocker 
107552d2b7e2Smglocker 	if (sc->sc_txtimer) {
107652d2b7e2Smglocker 		if (--sc->sc_txtimer == 0) {
107752d2b7e2Smglocker 			printf("%s: watchdog timeout\n", ifp->if_xname);
10781f0b1a60Sclaudio 			acx_init(ifp);
107952d2b7e2Smglocker 			ifp->if_oerrors++;
108052d2b7e2Smglocker 			return;
108154fb4829Sbrad 		} else
108254fb4829Sbrad 			ifp->if_timer = 1;
108352d2b7e2Smglocker 	}
108452d2b7e2Smglocker 
108552d2b7e2Smglocker 	ieee80211_watchdog(ifp);
10860f91cf57Smglocker }
10870f91cf57Smglocker 
10880f91cf57Smglocker int
acx_intr(void * arg)10890f91cf57Smglocker acx_intr(void *arg)
10900f91cf57Smglocker {
10910f91cf57Smglocker 	struct acx_softc *sc = arg;
10920f91cf57Smglocker 	uint16_t intr_status;
10930f91cf57Smglocker 
10940f91cf57Smglocker 	if ((sc->sc_flags & ACX_FLAG_FW_LOADED) == 0)
109598e37aaaSmglocker 		return (0);
10960f91cf57Smglocker 
10970f91cf57Smglocker 	intr_status = CSR_READ_2(sc, ACXREG_INTR_STATUS_CLR);
10980f91cf57Smglocker 	if (intr_status == ACXRV_INTR_ALL) {
10990f91cf57Smglocker 		/* not our interrupt */
110098e37aaaSmglocker 		return (0);
11010f91cf57Smglocker 	}
11020f91cf57Smglocker 
1103a0de3126Sclaudio 	/* Acknowledge all interrupts */
1104a0de3126Sclaudio 	CSR_WRITE_2(sc, ACXREG_INTR_ACK, intr_status);
1105a0de3126Sclaudio 
11060f91cf57Smglocker 	intr_status &= sc->chip_intr_enable;
11070f91cf57Smglocker 	if (intr_status == 0) {
11080f91cf57Smglocker 		/* not interrupts we care about */
110998e37aaaSmglocker 		return (1);
11100f91cf57Smglocker 	}
11110f91cf57Smglocker 
1112a0de3126Sclaudio #ifndef IEEE80211_STA_ONLY
1113a0de3126Sclaudio 	if (intr_status & ACXRV_INTR_DTIM)
1114a0de3126Sclaudio 		ieee80211_notify_dtim(&sc->sc_ic);
1115a0de3126Sclaudio #endif
11160f91cf57Smglocker 
11170f91cf57Smglocker 	if (intr_status & ACXRV_INTR_TX_FINI)
11180f91cf57Smglocker 		acx_txeof(sc);
11190f91cf57Smglocker 
11200f91cf57Smglocker 	if (intr_status & ACXRV_INTR_RX_FINI)
11210f91cf57Smglocker 		acx_rxeof(sc);
11220f91cf57Smglocker 
112398e37aaaSmglocker 	return (1);
11240f91cf57Smglocker }
11250f91cf57Smglocker 
11260f91cf57Smglocker void
acx_disable_intr(struct acx_softc * sc)11270f91cf57Smglocker acx_disable_intr(struct acx_softc *sc)
11280f91cf57Smglocker {
11290f91cf57Smglocker 	CSR_WRITE_2(sc, ACXREG_INTR_MASK, sc->chip_intr_disable);
11300f91cf57Smglocker 	CSR_WRITE_2(sc, ACXREG_EVENT_MASK, 0);
11310f91cf57Smglocker }
11320f91cf57Smglocker 
11330f91cf57Smglocker void
acx_enable_intr(struct acx_softc * sc)11340f91cf57Smglocker acx_enable_intr(struct acx_softc *sc)
11350f91cf57Smglocker {
11360f91cf57Smglocker 	/* Mask out interrupts that are not in the enable set */
11370f91cf57Smglocker 	CSR_WRITE_2(sc, ACXREG_INTR_MASK, ~sc->chip_intr_enable);
11380f91cf57Smglocker 	CSR_WRITE_2(sc, ACXREG_EVENT_MASK, ACXRV_EVENT_DISABLE);
11390f91cf57Smglocker }
11400f91cf57Smglocker 
11410f91cf57Smglocker void
acx_txeof(struct acx_softc * sc)11420f91cf57Smglocker acx_txeof(struct acx_softc *sc)
11430f91cf57Smglocker {
11440f91cf57Smglocker 	struct acx_buf_data *bd;
11450f91cf57Smglocker 	struct acx_txbuf *buf;
11460f91cf57Smglocker 	struct ifnet *ifp;
11470f91cf57Smglocker 	int idx;
11480f91cf57Smglocker 
11490f91cf57Smglocker 	ifp = &sc->sc_ic.ic_if;
11500f91cf57Smglocker 
11510f91cf57Smglocker 	bd = &sc->sc_buf_data;
11520f91cf57Smglocker 	idx = bd->tx_used_start;
11530f91cf57Smglocker 	for (buf = &bd->tx_buf[idx]; buf->tb_mbuf != NULL;
11540f91cf57Smglocker 	     buf = &bd->tx_buf[idx]) {
11550f91cf57Smglocker 		uint8_t ctrl, error;
11560f91cf57Smglocker 
11570f91cf57Smglocker 		ctrl = FW_TXDESC_GETFIELD_1(sc, buf, f_tx_ctrl);
11580f91cf57Smglocker 		if ((ctrl & (DESC_CTRL_HOSTOWN | DESC_CTRL_ACXDONE)) !=
11590f91cf57Smglocker 		    (DESC_CTRL_HOSTOWN | DESC_CTRL_ACXDONE))
11600f91cf57Smglocker 			break;
11610f91cf57Smglocker 
11620f91cf57Smglocker 		bus_dmamap_unload(sc->sc_dmat, buf->tb_mbuf_dmamap);
11630f91cf57Smglocker 		m_freem(buf->tb_mbuf);
11640f91cf57Smglocker 		buf->tb_mbuf = NULL;
11650f91cf57Smglocker 
11660f91cf57Smglocker 		error = FW_TXDESC_GETFIELD_1(sc, buf, f_tx_error);
11670f91cf57Smglocker 		if (error) {
11680f91cf57Smglocker 			acx_txerr(sc, error);
11690f91cf57Smglocker 			ifp->if_oerrors++;
117088a08f2aSdlg 		}
11710f91cf57Smglocker 
117252d3181bSmglocker 		/* Update rate control statistics for the node */
11730f91cf57Smglocker 		if (buf->tb_node != NULL) {
11740f91cf57Smglocker 			struct ieee80211com *ic;
11750f91cf57Smglocker 			struct ieee80211_node *ni;
117652d3181bSmglocker 			struct acx_node *wn;
117752d3181bSmglocker 			int ntries;
11780f91cf57Smglocker 
11790f91cf57Smglocker 			ic = &sc->sc_ic;
11800f91cf57Smglocker 			ni = (struct ieee80211_node *)buf->tb_node;
118152d3181bSmglocker 			wn = (struct acx_node *)ni;
118252d3181bSmglocker 			ntries = FW_TXDESC_GETFIELD_1(sc, buf, f_tx_rts_fail) +
118352d3181bSmglocker 			    FW_TXDESC_GETFIELD_1(sc, buf, f_tx_ack_fail);
118452d3181bSmglocker 
118552d3181bSmglocker 			wn->amn.amn_txcnt++;
118652d3181bSmglocker 			if (ntries > 0) {
11873186c1a6Sclaudio 				DPRINTFN(2, ("%s: tx intr ntries %d\n",
118852d3181bSmglocker 				    sc->sc_dev.dv_xname, ntries));
118952d3181bSmglocker 				wn->amn.amn_retrycnt++;
119052d3181bSmglocker 			}
11910f91cf57Smglocker 
11920f91cf57Smglocker 			ieee80211_release_node(ic, ni);
11930f91cf57Smglocker 			buf->tb_node = NULL;
11940f91cf57Smglocker 		}
11950f91cf57Smglocker 
11960f91cf57Smglocker 		FW_TXDESC_SETFIELD_1(sc, buf, f_tx_ctrl, DESC_CTRL_HOSTOWN);
11970f91cf57Smglocker 
11980f91cf57Smglocker 		bd->tx_used_count--;
11990f91cf57Smglocker 
12000f91cf57Smglocker 		idx = (idx + 1) % ACX_TX_DESC_CNT;
12010f91cf57Smglocker 	}
12020f91cf57Smglocker 	bd->tx_used_start = idx;
12030f91cf57Smglocker 
120454fb4829Sbrad 	sc->sc_txtimer = bd->tx_used_count == 0 ? 0 : 5;
12050f91cf57Smglocker 
12060f91cf57Smglocker 	if (bd->tx_used_count != ACX_TX_DESC_CNT) {
1207de6cd8fbSdlg 		ifq_clr_oactive(&ifp->if_snd);
12080f91cf57Smglocker 		acx_start(ifp);
12090f91cf57Smglocker 	}
12100f91cf57Smglocker }
12110f91cf57Smglocker 
12120f91cf57Smglocker void
acx_txerr(struct acx_softc * sc,uint8_t err)12130f91cf57Smglocker acx_txerr(struct acx_softc *sc, uint8_t err)
12140f91cf57Smglocker {
12150f91cf57Smglocker 	struct ifnet *ifp = &sc->sc_ic.ic_if;
12160f91cf57Smglocker 	struct acx_stats *stats = &sc->sc_stats;
12170f91cf57Smglocker 
12180f91cf57Smglocker 	if (err == DESC_ERR_EXCESSIVE_RETRY) {
12190f91cf57Smglocker 		/*
12200f91cf57Smglocker 		 * This a common error (see comment below),
1221e50d87e7Smglocker 		 * so print it using DPRINTF().
12220f91cf57Smglocker 		 */
12230f91cf57Smglocker 		DPRINTF(("%s: TX failed -- excessive retry\n",
12240f91cf57Smglocker 		    sc->sc_dev.dv_xname));
122598e37aaaSmglocker 	} else
12260f91cf57Smglocker 		printf("%s: TX failed -- ", ifp->if_xname);
12270f91cf57Smglocker 
12280f91cf57Smglocker 	/*
12290f91cf57Smglocker 	 * Although `err' looks like bitmask, it never
12300f91cf57Smglocker 	 * has multiple bits set.
12310f91cf57Smglocker 	 */
12320f91cf57Smglocker 	switch (err) {
12330f91cf57Smglocker #if 0
12340f91cf57Smglocker 	case DESC_ERR_OTHER_FRAG:
12350f91cf57Smglocker 		/* XXX what's this */
12360f91cf57Smglocker 		printf("error in other fragment\n");
12370f91cf57Smglocker 		stats->err_oth_frag++;
12380f91cf57Smglocker 		break;
12390f91cf57Smglocker #endif
12400f91cf57Smglocker 	case DESC_ERR_ABORT:
12410f91cf57Smglocker 		printf("aborted\n");
12420f91cf57Smglocker 		stats->err_abort++;
12430f91cf57Smglocker 		break;
12440f91cf57Smglocker 	case DESC_ERR_PARAM:
1245af52927aSmartynas 		printf("wrong parameters in descriptor\n");
12460f91cf57Smglocker 		stats->err_param++;
12470f91cf57Smglocker 		break;
12480f91cf57Smglocker 	case DESC_ERR_NO_WEPKEY:
12490f91cf57Smglocker 		printf("WEP key missing\n");
12500f91cf57Smglocker 		stats->err_no_wepkey++;
12510f91cf57Smglocker 		break;
12520f91cf57Smglocker 	case DESC_ERR_MSDU_TIMEOUT:
12530f91cf57Smglocker 		printf("MSDU life timeout\n");
12540f91cf57Smglocker 		stats->err_msdu_timeout++;
12550f91cf57Smglocker 		break;
12560f91cf57Smglocker 	case DESC_ERR_EXCESSIVE_RETRY:
12570f91cf57Smglocker 		/*
12580f91cf57Smglocker 		 * Possible causes:
12590f91cf57Smglocker 		 * 1) Distance is too long
12600f91cf57Smglocker 		 * 2) Transmit failed (e.g. no MAC level ACK)
12610f91cf57Smglocker 		 * 3) Chip overheated (this should be rare)
12620f91cf57Smglocker 		 */
12630f91cf57Smglocker 		stats->err_ex_retry++;
12640f91cf57Smglocker 		break;
12650f91cf57Smglocker 	case DESC_ERR_BUF_OVERFLOW:
12660f91cf57Smglocker 		printf("buffer overflow\n");
12670f91cf57Smglocker 		stats->err_buf_oflow++;
12680f91cf57Smglocker 		break;
12690f91cf57Smglocker 	case DESC_ERR_DMA:
12700f91cf57Smglocker 		printf("DMA error\n");
12710f91cf57Smglocker 		stats->err_dma++;
12720f91cf57Smglocker 		break;
12730f91cf57Smglocker 	default:
12740f91cf57Smglocker 		printf("unknown error %d\n", err);
12750f91cf57Smglocker 		stats->err_unkn++;
12760f91cf57Smglocker 		break;
12770f91cf57Smglocker 	}
12780f91cf57Smglocker }
12790f91cf57Smglocker 
12800f91cf57Smglocker void
acx_rxeof(struct acx_softc * sc)12810f91cf57Smglocker acx_rxeof(struct acx_softc *sc)
12820f91cf57Smglocker {
12830f91cf57Smglocker 	struct ieee80211com *ic = &sc->sc_ic;
12840f91cf57Smglocker 	struct acx_ring_data *rd = &sc->sc_ring_data;
12850f91cf57Smglocker 	struct acx_buf_data *bd = &sc->sc_buf_data;
12860f91cf57Smglocker 	struct ifnet *ifp = &ic->ic_if;
12870f91cf57Smglocker 	int idx, ready;
12880f91cf57Smglocker 
12890f91cf57Smglocker 	bus_dmamap_sync(sc->sc_dmat, rd->rx_ring_dmamap, 0,
12900f91cf57Smglocker 	    rd->rx_ring_dmamap->dm_mapsize, BUS_DMASYNC_POSTREAD);
12910f91cf57Smglocker 
12920f91cf57Smglocker 	/*
12930f91cf57Smglocker 	 * Locate first "ready" rx buffer,
1294e50d87e7Smglocker 	 * start from last stopped position.
12950f91cf57Smglocker 	 */
12960f91cf57Smglocker 	idx = bd->rx_scan_start;
12970f91cf57Smglocker 	ready = 0;
12980f91cf57Smglocker 	do {
12990f91cf57Smglocker 		struct acx_rxbuf *buf;
13000f91cf57Smglocker 
13010f91cf57Smglocker 		buf = &bd->rx_buf[idx];
13020f91cf57Smglocker 		if ((buf->rb_desc->h_ctrl & htole16(DESC_CTRL_HOSTOWN)) &&
13030f91cf57Smglocker 		    (buf->rb_desc->h_status & htole32(DESC_STATUS_FULL))) {
13040f91cf57Smglocker 			ready = 1;
13050f91cf57Smglocker 			break;
13060f91cf57Smglocker 		}
13070f91cf57Smglocker 		idx = (idx + 1) % ACX_RX_DESC_CNT;
13080f91cf57Smglocker 	} while (idx != bd->rx_scan_start);
13090f91cf57Smglocker 
13100f91cf57Smglocker 	if (!ready)
13110f91cf57Smglocker 		return;
13120f91cf57Smglocker 
13130f91cf57Smglocker 	/*
13140f91cf57Smglocker 	 * NOTE: don't mess up `idx' here, it will
1315e50d87e7Smglocker 	 * be used in the following code.
13160f91cf57Smglocker 	 */
13170f91cf57Smglocker 	do {
13180f91cf57Smglocker 		struct acx_rxbuf_hdr *head;
13190f91cf57Smglocker 		struct acx_rxbuf *buf;
13200f91cf57Smglocker 		struct mbuf *m;
132130d05aacSdamien 		struct ieee80211_rxinfo rxi;
13220f91cf57Smglocker 		uint32_t desc_status;
13230f91cf57Smglocker 		uint16_t desc_ctrl;
13240f91cf57Smglocker 		int len, error;
13250f91cf57Smglocker 
13260f91cf57Smglocker 		buf = &bd->rx_buf[idx];
13270f91cf57Smglocker 
13280f91cf57Smglocker 		desc_ctrl = letoh16(buf->rb_desc->h_ctrl);
13290f91cf57Smglocker 		desc_status = letoh32(buf->rb_desc->h_status);
13300f91cf57Smglocker 		if (!(desc_ctrl & DESC_CTRL_HOSTOWN) ||
13310f91cf57Smglocker 		    !(desc_status & DESC_STATUS_FULL))
13320f91cf57Smglocker 			break;
13330f91cf57Smglocker 
13340f91cf57Smglocker 		bus_dmamap_sync(sc->sc_dmat, buf->rb_mbuf_dmamap, 0,
13350f91cf57Smglocker 		    buf->rb_mbuf_dmamap->dm_mapsize, BUS_DMASYNC_POSTREAD);
13360f91cf57Smglocker 
13370f91cf57Smglocker 		m = buf->rb_mbuf;
13380f91cf57Smglocker 
13390f91cf57Smglocker 		error = acx_newbuf(sc, buf, 0);
13400f91cf57Smglocker 		if (error) {
13410f91cf57Smglocker 			ifp->if_ierrors++;
13420f91cf57Smglocker 			goto next;
13430f91cf57Smglocker 		}
13440f91cf57Smglocker 
13450f91cf57Smglocker 		head = mtod(m, struct acx_rxbuf_hdr *);
13460f91cf57Smglocker 
13470f91cf57Smglocker 		len = letoh16(head->rbh_len) & ACX_RXBUF_LEN_MASK;
13480f91cf57Smglocker 		if (len >= sizeof(struct ieee80211_frame_min) &&
13490f91cf57Smglocker 		    len < MCLBYTES) {
135058684cc2Sdamien 			struct ieee80211_frame *wh;
13510f91cf57Smglocker 			struct ieee80211_node *ni;
13520f91cf57Smglocker 
13530f91cf57Smglocker 			m_adj(m, sizeof(struct acx_rxbuf_hdr) +
13540f91cf57Smglocker 			    sc->chip_rxbuf_exhdr);
135558684cc2Sdamien 			wh = mtod(m, struct ieee80211_frame *);
13560f91cf57Smglocker 
135752a13037Sstsp 			memset(&rxi, 0, sizeof(rxi));
135858684cc2Sdamien 			if ((wh->i_fc[1] & IEEE80211_FC1_WEP) &&
13590f91cf57Smglocker 			    sc->chip_hw_crypt) {
13600f91cf57Smglocker 				/* Short circuit software WEP */
136158684cc2Sdamien 				wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
13620f91cf57Smglocker 
13630f91cf57Smglocker 				/* Do chip specific RX buffer processing */
13640f91cf57Smglocker 				if (sc->chip_proc_wep_rxbuf != NULL) {
13650f91cf57Smglocker 					sc->chip_proc_wep_rxbuf(sc, m, &len);
136658684cc2Sdamien 					wh = mtod(m, struct ieee80211_frame *);
13670f91cf57Smglocker 				}
136830d05aacSdamien 				rxi.rxi_flags |= IEEE80211_RXI_HWDEC;
13690f91cf57Smglocker 			}
13700f91cf57Smglocker 
13710f91cf57Smglocker 			m->m_len = m->m_pkthdr.len = len;
13720f91cf57Smglocker 
1373172cbfceSmglocker #if NBPFILTER > 0
1374172cbfceSmglocker 			if (sc->sc_drvbpf != NULL) {
1375172cbfceSmglocker 				struct mbuf mb;
1376172cbfceSmglocker 				struct acx_rx_radiotap_hdr *tap = &sc->sc_rxtap;
1377172cbfceSmglocker 
1378172cbfceSmglocker 				tap->wr_flags = 0;
1379172cbfceSmglocker 				tap->wr_chan_freq =
1380172cbfceSmglocker 				    htole16(ic->ic_bss->ni_chan->ic_freq);
1381172cbfceSmglocker 				tap->wr_chan_flags =
1382172cbfceSmglocker 				    htole16(ic->ic_bss->ni_chan->ic_flags);
1383172cbfceSmglocker 				tap->wr_rssi = head->rbh_level;
13848dc7d017Smglocker 				tap->wr_max_rssi = ic->ic_max_rssi;
1385172cbfceSmglocker 
1386172cbfceSmglocker 				mb.m_data = (caddr_t)tap;
1387172cbfceSmglocker 				mb.m_len = sc->sc_rxtap_len;
1388172cbfceSmglocker 				mb.m_next = m;
138902677ac7Sclaudio 				mb.m_nextpkt = NULL;
139002677ac7Sclaudio 				mb.m_type = 0;
139102677ac7Sclaudio 				mb.m_flags = 0;
1392172cbfceSmglocker 				bpf_mtap(sc->sc_drvbpf, &mb, BPF_DIRECTION_IN);
1393172cbfceSmglocker 			}
1394172cbfceSmglocker #endif
1395172cbfceSmglocker 
139658684cc2Sdamien 			ni = ieee80211_find_rxnode(ic, wh);
13970f91cf57Smglocker 
139830d05aacSdamien 			rxi.rxi_rssi = head->rbh_level;
139930d05aacSdamien 			rxi.rxi_tstamp = letoh32(head->rbh_time);
140030d05aacSdamien 			ieee80211_input(ifp, m, ni, &rxi);
14010f91cf57Smglocker 
14020f91cf57Smglocker 			ieee80211_release_node(ic, ni);
14030f91cf57Smglocker 		} else {
14040f91cf57Smglocker 			m_freem(m);
14050f91cf57Smglocker 			ifp->if_ierrors++;
14060f91cf57Smglocker 		}
14070f91cf57Smglocker 
14080f91cf57Smglocker next:
14090f91cf57Smglocker 		buf->rb_desc->h_ctrl = htole16(desc_ctrl & ~DESC_CTRL_HOSTOWN);
14100f91cf57Smglocker 		buf->rb_desc->h_status = 0;
14110f91cf57Smglocker 		bus_dmamap_sync(sc->sc_dmat, rd->rx_ring_dmamap, 0,
14120f91cf57Smglocker 		    rd->rx_ring_dmamap->dm_mapsize, BUS_DMASYNC_PREWRITE);
14130f91cf57Smglocker 
14140f91cf57Smglocker 		idx = (idx + 1) % ACX_RX_DESC_CNT;
14150f91cf57Smglocker 	} while (idx != bd->rx_scan_start);
14160f91cf57Smglocker 
14170f91cf57Smglocker 	/*
14180f91cf57Smglocker 	 * Record the position so that next
1419e50d87e7Smglocker 	 * time we can start from it.
14200f91cf57Smglocker 	 */
14210f91cf57Smglocker 	bd->rx_scan_start = idx;
14220f91cf57Smglocker }
14230f91cf57Smglocker 
14240f91cf57Smglocker int
acx_reset(struct acx_softc * sc)14250f91cf57Smglocker acx_reset(struct acx_softc *sc)
14260f91cf57Smglocker {
14270f91cf57Smglocker 	uint16_t reg;
14280f91cf57Smglocker 
14290f91cf57Smglocker 	/* Halt ECPU */
14300f91cf57Smglocker 	CSR_SETB_2(sc, ACXREG_ECPU_CTRL, ACXRV_ECPU_HALT);
14310f91cf57Smglocker 
14320f91cf57Smglocker 	/* Software reset */
14330f91cf57Smglocker 	reg = CSR_READ_2(sc, ACXREG_SOFT_RESET);
14340f91cf57Smglocker 	CSR_WRITE_2(sc, ACXREG_SOFT_RESET, reg | ACXRV_SOFT_RESET);
14350f91cf57Smglocker 	DELAY(100);
14360f91cf57Smglocker 	CSR_WRITE_2(sc, ACXREG_SOFT_RESET, reg);
14370f91cf57Smglocker 
14380f91cf57Smglocker 	/* Initialize EEPROM */
14390f91cf57Smglocker 	CSR_SETB_2(sc, ACXREG_EEPROM_INIT, ACXRV_EEPROM_INIT);
14400f91cf57Smglocker 	DELAY(50000);
14410f91cf57Smglocker 
14420f91cf57Smglocker 	/* Test whether ECPU is stopped */
14430f91cf57Smglocker 	reg = CSR_READ_2(sc, ACXREG_ECPU_CTRL);
14440f91cf57Smglocker 	if (!(reg & ACXRV_ECPU_HALT)) {
14450f91cf57Smglocker 		printf("%s: can't halt ECPU\n", sc->sc_dev.dv_xname);
144698e37aaaSmglocker 		return (ENXIO);
14470f91cf57Smglocker 	}
144898e37aaaSmglocker 
144998e37aaaSmglocker 	return (0);
14500f91cf57Smglocker }
14510f91cf57Smglocker 
14520f91cf57Smglocker int
acx_read_eeprom(struct acx_softc * sc,uint32_t offset,uint8_t * val)14530f91cf57Smglocker acx_read_eeprom(struct acx_softc *sc, uint32_t offset, uint8_t *val)
14540f91cf57Smglocker {
14550f91cf57Smglocker 	int i;
14560f91cf57Smglocker 
14570f91cf57Smglocker 	CSR_WRITE_4(sc, ACXREG_EEPROM_CONF, 0);
14580f91cf57Smglocker 	CSR_WRITE_4(sc, ACXREG_EEPROM_ADDR, offset);
14590f91cf57Smglocker 	CSR_WRITE_4(sc, ACXREG_EEPROM_CTRL, ACXRV_EEPROM_READ);
14600f91cf57Smglocker 
14610f91cf57Smglocker #define EE_READ_RETRY_MAX	100
14620f91cf57Smglocker 	for (i = 0; i < EE_READ_RETRY_MAX; ++i) {
14630f91cf57Smglocker 		if (CSR_READ_2(sc, ACXREG_EEPROM_CTRL) == 0)
14640f91cf57Smglocker 			break;
14650f91cf57Smglocker 		DELAY(10000);
14660f91cf57Smglocker 	}
14670f91cf57Smglocker 	if (i == EE_READ_RETRY_MAX) {
14680f91cf57Smglocker 		printf("%s: can't read EEPROM offset %x (timeout)\n",
1469fda8df90Sclaudio 		    sc->sc_dev.dv_xname, offset);
14700f91cf57Smglocker 		return (ETIMEDOUT);
14710f91cf57Smglocker 	}
14720f91cf57Smglocker #undef EE_READ_RETRY_MAX
14730f91cf57Smglocker 
14740f91cf57Smglocker 	*val = CSR_READ_1(sc, ACXREG_EEPROM_DATA);
14750f91cf57Smglocker 
14760f91cf57Smglocker 	return (0);
14770f91cf57Smglocker }
14780f91cf57Smglocker 
14790f91cf57Smglocker int
acx_read_phyreg(struct acx_softc * sc,uint32_t reg,uint8_t * val)14800f91cf57Smglocker acx_read_phyreg(struct acx_softc *sc, uint32_t reg, uint8_t *val)
14810f91cf57Smglocker {
14820f91cf57Smglocker 	struct ifnet *ifp = &sc->sc_ic.ic_if;
14830f91cf57Smglocker 	int i;
14840f91cf57Smglocker 
14850f91cf57Smglocker 	CSR_WRITE_4(sc, ACXREG_PHY_ADDR, reg);
14860f91cf57Smglocker 	CSR_WRITE_4(sc, ACXREG_PHY_CTRL, ACXRV_PHY_READ);
14870f91cf57Smglocker 
14880f91cf57Smglocker #define PHY_READ_RETRY_MAX	100
14890f91cf57Smglocker 	for (i = 0; i < PHY_READ_RETRY_MAX; ++i) {
14900f91cf57Smglocker 		if (CSR_READ_4(sc, ACXREG_PHY_CTRL) == 0)
14910f91cf57Smglocker 			break;
14920f91cf57Smglocker 		DELAY(10000);
14930f91cf57Smglocker 	}
14940f91cf57Smglocker 	if (i == PHY_READ_RETRY_MAX) {
14950f91cf57Smglocker 		printf("%s: can't read phy reg %x (timeout)\n",
14960f91cf57Smglocker 		    ifp->if_xname, reg);
149798e37aaaSmglocker 		return (ETIMEDOUT);
14980f91cf57Smglocker 	}
14990f91cf57Smglocker #undef PHY_READ_RETRY_MAX
15000f91cf57Smglocker 
15010f91cf57Smglocker 	*val = CSR_READ_1(sc, ACXREG_PHY_DATA);
150298e37aaaSmglocker 
150398e37aaaSmglocker 	return (0);
15040f91cf57Smglocker }
15050f91cf57Smglocker 
15060f91cf57Smglocker void
acx_write_phyreg(struct acx_softc * sc,uint32_t reg,uint8_t val)15070f91cf57Smglocker acx_write_phyreg(struct acx_softc *sc, uint32_t reg, uint8_t val)
15080f91cf57Smglocker {
15090f91cf57Smglocker 	CSR_WRITE_4(sc, ACXREG_PHY_DATA, val);
15100f91cf57Smglocker 	CSR_WRITE_4(sc, ACXREG_PHY_ADDR, reg);
15110f91cf57Smglocker 	CSR_WRITE_4(sc, ACXREG_PHY_CTRL, ACXRV_PHY_WRITE);
15120f91cf57Smglocker }
15130f91cf57Smglocker 
15140f91cf57Smglocker int
acx_load_base_firmware(struct acx_softc * sc,const char * name)15155f0637dbSjsg acx_load_base_firmware(struct acx_softc *sc, const char *name)
15160f91cf57Smglocker {
15170f91cf57Smglocker 	struct ifnet *ifp = &sc->sc_ic.ic_if;
15180f91cf57Smglocker 	int i, error;
15190f91cf57Smglocker 	uint8_t *ucode;
15200f91cf57Smglocker 	size_t size;
15210f91cf57Smglocker 
15220f91cf57Smglocker 	error = loadfirmware(name, &ucode, &size);
15230f91cf57Smglocker 
15240f91cf57Smglocker 	if (error != 0) {
1525b2f1b584Sderaadt 		printf("%s: error %d, could not read firmware %s\n",
15260f91cf57Smglocker 		    ifp->if_xname, error, name);
152798e37aaaSmglocker 		return (EIO);
15280f91cf57Smglocker 	}
15290f91cf57Smglocker 
15300f91cf57Smglocker 	/* Load base firmware */
15310f91cf57Smglocker 	error = acx_load_firmware(sc, 0, ucode, size);
15320f91cf57Smglocker 
153395cabb2eSderaadt 	free(ucode, M_DEVBUF, size);
15340f91cf57Smglocker 
15350f91cf57Smglocker 	if (error) {
15360f91cf57Smglocker 		printf("%s: can't load base firmware\n", ifp->if_xname);
15370f91cf57Smglocker 		return error;
15380f91cf57Smglocker 	}
15390f91cf57Smglocker 	DPRINTF(("%s: base firmware loaded\n", sc->sc_dev.dv_xname));
15400f91cf57Smglocker 
15410f91cf57Smglocker 	/* Start ECPU */
15420f91cf57Smglocker 	CSR_WRITE_2(sc, ACXREG_ECPU_CTRL, ACXRV_ECPU_START);
15430f91cf57Smglocker 
15440f91cf57Smglocker 	/* Wait for ECPU to be up */
15450f91cf57Smglocker 	for (i = 0; i < 500; ++i) {
15460f91cf57Smglocker 		uint16_t reg;
15470f91cf57Smglocker 
15480f91cf57Smglocker 		reg = CSR_READ_2(sc, ACXREG_INTR_STATUS);
15490f91cf57Smglocker 		if (reg & ACXRV_INTR_FCS_THRESH) {
15500f91cf57Smglocker 			CSR_WRITE_2(sc, ACXREG_INTR_ACK, ACXRV_INTR_FCS_THRESH);
155198e37aaaSmglocker 			return (0);
15520f91cf57Smglocker 		}
15530f91cf57Smglocker 		DELAY(10000);
15540f91cf57Smglocker 	}
15550f91cf57Smglocker 
15560f91cf57Smglocker 	printf("%s: can't initialize ECPU (timeout)\n", ifp->if_xname);
155798e37aaaSmglocker 
155898e37aaaSmglocker 	return (ENXIO);
15590f91cf57Smglocker }
15600f91cf57Smglocker 
15610f91cf57Smglocker int
acx_load_radio_firmware(struct acx_softc * sc,const char * name)15628a86a891Sjsg acx_load_radio_firmware(struct acx_softc *sc, const char *name)
15630f91cf57Smglocker {
15640f91cf57Smglocker 	struct ifnet *ifp = &sc->sc_ic.ic_if;
15650f91cf57Smglocker 	struct acx_conf_mmap mem_map;
15660f91cf57Smglocker 	uint32_t radio_fw_ofs;
15670f91cf57Smglocker 	int error;
15680f91cf57Smglocker 	uint8_t *ucode;
15690f91cf57Smglocker 	size_t size;
15700f91cf57Smglocker 
15710f91cf57Smglocker 	error = loadfirmware(name, &ucode, &size);
15720f91cf57Smglocker 
15730f91cf57Smglocker 	if (error != 0) {
1574b2f1b584Sderaadt 		printf("%s: error %d, could not read firmware %s\n",
15750f91cf57Smglocker 		    ifp->if_xname, error, name);
15760f91cf57Smglocker 		return (EIO);
15770f91cf57Smglocker 	}
15780f91cf57Smglocker 
15790f91cf57Smglocker 	/*
15800f91cf57Smglocker 	 * Get the position, where base firmware is loaded, so that
15810f91cf57Smglocker 	 * radio firmware can be loaded after it.
15820f91cf57Smglocker 	 */
1583bb966e7aSclaudio 	if (acx_get_conf(sc, ACX_CONF_MMAP, &mem_map, sizeof(mem_map)) != 0) {
158495cabb2eSderaadt 		free(ucode, M_DEVBUF, size);
15850f91cf57Smglocker 		return (ENXIO);
1586c23a3d11Sclaudio 	}
15870f91cf57Smglocker 	radio_fw_ofs = letoh32(mem_map.code_end);
15880f91cf57Smglocker 
15890f91cf57Smglocker 	/* Put ECPU into sleeping state, before loading radio firmware */
1590c23a3d11Sclaudio 	if (acx_exec_command(sc, ACXCMD_SLEEP, NULL, 0, NULL, 0) != 0) {
159195cabb2eSderaadt 		free(ucode, M_DEVBUF, size);
15920f91cf57Smglocker 		return (ENXIO);
1593c23a3d11Sclaudio 	}
15940f91cf57Smglocker 
15950f91cf57Smglocker 	/* Load radio firmware */
15960f91cf57Smglocker 	error = acx_load_firmware(sc, radio_fw_ofs, ucode, size);
1597c23a3d11Sclaudio 
159895cabb2eSderaadt 	free(ucode, M_DEVBUF, size);
1599c23a3d11Sclaudio 
16000f91cf57Smglocker 	if (error) {
16010f91cf57Smglocker 		printf("%s: can't load radio firmware\n", ifp->if_xname);
16020f91cf57Smglocker 		return (ENXIO);
16030f91cf57Smglocker 	}
16040f91cf57Smglocker 	DPRINTF(("%s: radio firmware loaded\n", sc->sc_dev.dv_xname));
16050f91cf57Smglocker 
16060f91cf57Smglocker 	/* Wake up sleeping ECPU, after radio firmware is loaded */
1607f7407174Sclaudio 	if (acx_exec_command(sc, ACXCMD_WAKEUP, NULL, 0, NULL, 0) != 0)
16080f91cf57Smglocker 		return (ENXIO);
16090f91cf57Smglocker 
16100f91cf57Smglocker 	/* Initialize radio */
16110f91cf57Smglocker 	if (acx_init_radio(sc, radio_fw_ofs, size) != 0)
16120f91cf57Smglocker 		return (ENXIO);
16130f91cf57Smglocker 
16140f91cf57Smglocker 	/* Verify radio firmware's loading position */
1615bb966e7aSclaudio 	if (acx_get_conf(sc, ACX_CONF_MMAP, &mem_map, sizeof(mem_map)) != 0)
16160f91cf57Smglocker 		return (ENXIO);
16170f91cf57Smglocker 
16180f91cf57Smglocker 	if (letoh32(mem_map.code_end) != radio_fw_ofs + size) {
16190f91cf57Smglocker 		printf("%s: loaded radio firmware position mismatch\n",
16200f91cf57Smglocker 		    ifp->if_xname);
162198e37aaaSmglocker 		return (ENXIO);
16220f91cf57Smglocker 	}
16230f91cf57Smglocker 
16240f91cf57Smglocker 	DPRINTF(("%s: radio firmware initialized\n", sc->sc_dev.dv_xname));
16250f91cf57Smglocker 
16260f91cf57Smglocker 	return (0);
16270f91cf57Smglocker }
16280f91cf57Smglocker 
16290f91cf57Smglocker int
acx_load_firmware(struct acx_softc * sc,uint32_t offset,const uint8_t * data,int data_len)16300f91cf57Smglocker acx_load_firmware(struct acx_softc *sc, uint32_t offset, const uint8_t *data,
16310f91cf57Smglocker     int data_len)
16320f91cf57Smglocker {
16330f91cf57Smglocker 	struct ifnet *ifp = &sc->sc_ic.ic_if;
16340f91cf57Smglocker 	const uint32_t *fw;
16350f91cf57Smglocker 	u_int32_t csum = 0;
16360f91cf57Smglocker 	int i, fw_len;
16370f91cf57Smglocker 
163898e37aaaSmglocker 	for (i = 4; i < data_len; i++)
16390f91cf57Smglocker 		csum += data[i];
16400f91cf57Smglocker 
16410f91cf57Smglocker 	fw = (const uint32_t *)data;
16420f91cf57Smglocker 
1643586e9409Sclaudio 	if (*fw != htole32(csum)) {
16440f91cf57Smglocker 		printf("%s: firmware checksum 0x%x does not match 0x%x!\n",
1645586e9409Sclaudio 		    ifp->if_xname, *fw, htole32(csum));
164698e37aaaSmglocker 		return (ENXIO);
16470f91cf57Smglocker 	}
16480f91cf57Smglocker 
16490f91cf57Smglocker 	/* skip csum + length */
16500f91cf57Smglocker 	data += 8;
16510f91cf57Smglocker 	data_len -= 8;
16520f91cf57Smglocker 
16530f91cf57Smglocker 	fw = (const uint32_t *)data;
16540f91cf57Smglocker 	fw_len = data_len / sizeof(uint32_t);
16550f91cf57Smglocker 
16560f91cf57Smglocker 	/*
16570f91cf57Smglocker 	 * LOADFW_AUTO_INC only works with some older firmware:
16580f91cf57Smglocker 	 * 1) acx100's firmware
16590f91cf57Smglocker 	 * 2) acx111's firmware whose rev is 0x00010011
16600f91cf57Smglocker 	 */
16610f91cf57Smglocker 
16620f91cf57Smglocker 	/* Load firmware */
16630f91cf57Smglocker 	CSR_WRITE_4(sc, ACXREG_FWMEM_START, ACXRV_FWMEM_START_OP);
16640f91cf57Smglocker #ifndef LOADFW_AUTO_INC
16650f91cf57Smglocker 	CSR_WRITE_4(sc, ACXREG_FWMEM_CTRL, 0);
16660f91cf57Smglocker #else
16670f91cf57Smglocker 	CSR_WRITE_4(sc, ACXREG_FWMEM_CTRL, ACXRV_FWMEM_ADDR_AUTOINC);
16680f91cf57Smglocker 	CSR_WRITE_4(sc, ACXREG_FWMEM_ADDR, offset);
16690f91cf57Smglocker #endif
16700f91cf57Smglocker 
16710f91cf57Smglocker 	for (i = 0; i < fw_len; ++i) {
16720f91cf57Smglocker #ifndef LOADFW_AUTO_INC
16730f91cf57Smglocker 		CSR_WRITE_4(sc, ACXREG_FWMEM_ADDR, offset + (i * 4));
16740f91cf57Smglocker #endif
16750f91cf57Smglocker 		CSR_WRITE_4(sc, ACXREG_FWMEM_DATA, betoh32(fw[i]));
16760f91cf57Smglocker 	}
16770f91cf57Smglocker 
16780f91cf57Smglocker 	/* Verify firmware */
16790f91cf57Smglocker 	CSR_WRITE_4(sc, ACXREG_FWMEM_START, ACXRV_FWMEM_START_OP);
16800f91cf57Smglocker #ifndef LOADFW_AUTO_INC
16810f91cf57Smglocker 	CSR_WRITE_4(sc, ACXREG_FWMEM_CTRL, 0);
16820f91cf57Smglocker #else
16830f91cf57Smglocker 	CSR_WRITE_4(sc, ACXREG_FWMEM_CTRL, ACXRV_FWMEM_ADDR_AUTOINC);
16840f91cf57Smglocker 	CSR_WRITE_4(sc, ACXREG_FWMEM_ADDR, offset);
16850f91cf57Smglocker #endif
16860f91cf57Smglocker 
16870f91cf57Smglocker 	for (i = 0; i < fw_len; ++i) {
16880f91cf57Smglocker 		uint32_t val;
16890f91cf57Smglocker 
16900f91cf57Smglocker #ifndef LOADFW_AUTO_INC
16910f91cf57Smglocker 		CSR_WRITE_4(sc, ACXREG_FWMEM_ADDR, offset + (i * 4));
16920f91cf57Smglocker #endif
16930f91cf57Smglocker 		val = CSR_READ_4(sc, ACXREG_FWMEM_DATA);
16940f91cf57Smglocker 		if (betoh32(fw[i]) != val) {
16950f91cf57Smglocker 			printf("%s: firmware mismatch fw %08x  loaded %08x\n",
16960f91cf57Smglocker 			    ifp->if_xname, fw[i], val);
169798e37aaaSmglocker 			return (ENXIO);
16980f91cf57Smglocker 		}
16990f91cf57Smglocker 	}
170098e37aaaSmglocker 
170198e37aaaSmglocker 	return (0);
17020f91cf57Smglocker }
17030f91cf57Smglocker 
17040f91cf57Smglocker struct ieee80211_node *
acx_node_alloc(struct ieee80211com * ic)17050f91cf57Smglocker acx_node_alloc(struct ieee80211com *ic)
17060f91cf57Smglocker {
1707960b9804Smglocker 	struct acx_node *wn;
17080f91cf57Smglocker 
17097a2a96ecSkrw 	wn = malloc(sizeof(*wn), M_DEVBUF, M_NOWAIT | M_ZERO);
1710960b9804Smglocker 	if (wn == NULL)
171198e37aaaSmglocker 		return (NULL);
17120f91cf57Smglocker 
1713960b9804Smglocker 	return ((struct ieee80211_node *)wn);
17140f91cf57Smglocker }
17150f91cf57Smglocker 
17160f91cf57Smglocker int
acx_newstate(struct ieee80211com * ic,enum ieee80211_state nstate,int arg)17170f91cf57Smglocker acx_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
17180f91cf57Smglocker {
17190f91cf57Smglocker 	struct acx_softc *sc = ic->ic_if.if_softc;
17200f91cf57Smglocker 	struct ifnet *ifp = &ic->ic_if;
17210f91cf57Smglocker 	int error = 0;
17220f91cf57Smglocker 
1723960b9804Smglocker 	timeout_del(&sc->amrr_ch);
1724960b9804Smglocker 
17250f91cf57Smglocker 	switch (nstate) {
172601490a9fSmglocker 	case IEEE80211_S_INIT:
172701490a9fSmglocker 		break;
172801490a9fSmglocker 	case IEEE80211_S_SCAN: {
17290f91cf57Smglocker 			uint8_t chan;
17300f91cf57Smglocker 
17310f91cf57Smglocker 			chan = ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan);
17328eaebac4Sstsp 			if (acx_set_channel(sc, chan) != 0) {
17338eaebac4Sstsp 				error = 1;
17348eaebac4Sstsp 				goto back;
17358eaebac4Sstsp 			}
17360f91cf57Smglocker 
1737ed934a0cSblambert 			/* 200ms => 5 channels per second */
1738ed934a0cSblambert 			timeout_add_msec(&sc->sc_chanscan_timer, 200);
17390f91cf57Smglocker 		}
17400f91cf57Smglocker 		break;
17410f91cf57Smglocker 	case IEEE80211_S_AUTH:
17420f91cf57Smglocker 		if (ic->ic_opmode == IEEE80211_M_STA) {
17430f91cf57Smglocker 			struct ieee80211_node *ni;
17440f91cf57Smglocker #ifdef ACX_DEBUG
17450f91cf57Smglocker 			int i;
17460f91cf57Smglocker #endif
17470f91cf57Smglocker 
17480f91cf57Smglocker 			ni = ic->ic_bss;
17490f91cf57Smglocker 
17500f91cf57Smglocker 			if (acx_join_bss(sc, ACX_MODE_STA, ni) != 0) {
17510f91cf57Smglocker 				printf("%s: join BSS failed\n", ifp->if_xname);
17520f91cf57Smglocker 				error = 1;
17530f91cf57Smglocker 				goto back;
17540f91cf57Smglocker 			}
17550f91cf57Smglocker 
17560f91cf57Smglocker 			DPRINTF(("%s: join BSS\n", sc->sc_dev.dv_xname));
17570f91cf57Smglocker 			if (ic->ic_state == IEEE80211_S_ASSOC) {
17580f91cf57Smglocker 				DPRINTF(("%s: change from assoc to run\n",
17590f91cf57Smglocker 				    sc->sc_dev.dv_xname));
17600f91cf57Smglocker 				ic->ic_state = IEEE80211_S_RUN;
17610f91cf57Smglocker 			}
17620f91cf57Smglocker 
17630f91cf57Smglocker #ifdef ACX_DEBUG
17640f91cf57Smglocker 			printf("%s: AP rates: ", sc->sc_dev.dv_xname);
17650f91cf57Smglocker 			for (i = 0; i < ni->ni_rates.rs_nrates; ++i)
17660f91cf57Smglocker 				printf("%d ", ni->ni_rates.rs_rates[i]);
17670f91cf57Smglocker 			ieee80211_print_essid(ni->ni_essid, ni->ni_esslen);
1768aa5b6d39Smglocker 			printf(" %s\n", ether_sprintf(ni->ni_bssid));
17690f91cf57Smglocker #endif
17700f91cf57Smglocker 		}
17710f91cf57Smglocker 		break;
17720f91cf57Smglocker 	case IEEE80211_S_RUN:
1773171ac09aSdamien #ifndef IEEE80211_STA_ONLY
1774b206d01bSmglocker 		if (ic->ic_opmode == IEEE80211_M_IBSS ||
1775b206d01bSmglocker 		    ic->ic_opmode == IEEE80211_M_HOSTAP) {
17760f91cf57Smglocker 			struct ieee80211_node *ni;
17770f91cf57Smglocker 			uint8_t chan;
17780f91cf57Smglocker 
17790f91cf57Smglocker 			ni = ic->ic_bss;
17800f91cf57Smglocker 			chan = ieee80211_chan2ieee(ic, ni->ni_chan);
17810f91cf57Smglocker 
17820f91cf57Smglocker 			error = 1;
17830f91cf57Smglocker 
1784611f5384Smglocker 			if (acx_set_channel(sc, chan) != 0)
17850f91cf57Smglocker 				goto back;
17860f91cf57Smglocker 
1787864278e7Smglocker 			if (acx_set_beacon_tmplt(sc, ni) != 0) {
178864688e70Smbalmer 				printf("%s: set beacon template failed\n",
17890f91cf57Smglocker 				    ifp->if_xname);
17900f91cf57Smglocker 				goto back;
17910f91cf57Smglocker 			}
17920f91cf57Smglocker 
1793864278e7Smglocker 			if (acx_set_probe_resp_tmplt(sc, ni) != 0) {
17940f91cf57Smglocker 				printf("%s: set probe response template "
17950f91cf57Smglocker 				    "failed\n", ifp->if_xname);
17960f91cf57Smglocker 				goto back;
17970f91cf57Smglocker 			}
17980f91cf57Smglocker 
1799b206d01bSmglocker 			if (ic->ic_opmode == IEEE80211_M_IBSS) {
18000f91cf57Smglocker 				if (acx_join_bss(sc, ACX_MODE_ADHOC, ni) != 0) {
1801b206d01bSmglocker 					printf("%s: join IBSS failed\n",
1802b206d01bSmglocker 					    ifp->if_xname);
18030f91cf57Smglocker 					goto back;
18040f91cf57Smglocker 				}
1805b206d01bSmglocker 			} else {
1806b206d01bSmglocker 				if (acx_join_bss(sc, ACX_MODE_AP, ni) != 0) {
1807b206d01bSmglocker 					printf("%s: join HOSTAP failed\n",
1808b206d01bSmglocker 					    ifp->if_xname);
1809b206d01bSmglocker 					goto back;
1810b206d01bSmglocker 				}
1811b206d01bSmglocker 			}
18120f91cf57Smglocker 
18130f91cf57Smglocker 			DPRINTF(("%s: join IBSS\n", sc->sc_dev.dv_xname));
18140f91cf57Smglocker 			error = 0;
18150f91cf57Smglocker 		}
1816171ac09aSdamien #endif
1817960b9804Smglocker 		/* fake a join to init the tx rate */
1818960b9804Smglocker 		if (ic->ic_opmode == IEEE80211_M_STA)
1819960b9804Smglocker 			acx_newassoc(ic, ic->ic_bss, 1);
1820960b9804Smglocker 
1821960b9804Smglocker 		/* start automatic rate control timer */
1822960b9804Smglocker 		if (ic->ic_fixed_rate == -1)
1823ed934a0cSblambert 			timeout_add_msec(&sc->amrr_ch, 500);
18240f91cf57Smglocker 		break;
18250f91cf57Smglocker 	default:
18260f91cf57Smglocker 		break;
18270f91cf57Smglocker 	}
18280f91cf57Smglocker 
18290f91cf57Smglocker back:
18300f91cf57Smglocker 	if (error) {
18310f91cf57Smglocker 		/* XXX */
18320f91cf57Smglocker 		nstate = IEEE80211_S_INIT;
18330f91cf57Smglocker 		arg = -1;
18340f91cf57Smglocker 	}
183598e37aaaSmglocker 
183698e37aaaSmglocker 	return (sc->sc_newstate(ic, nstate, arg));
18370f91cf57Smglocker }
18380f91cf57Smglocker 
18390f91cf57Smglocker int
acx_init_tmplt_ordered(struct acx_softc * sc)18400f91cf57Smglocker acx_init_tmplt_ordered(struct acx_softc *sc)
18410f91cf57Smglocker {
1842f7407174Sclaudio 	union {
1843f7407174Sclaudio 		struct acx_tmplt_beacon		beacon;
1844f7407174Sclaudio 		struct acx_tmplt_null_data	null;
1845f7407174Sclaudio 		struct acx_tmplt_probe_req	preq;
1846f7407174Sclaudio 		struct acx_tmplt_probe_resp	presp;
1847ae689328Smglocker 		struct acx_tmplt_tim		tim;
1848f7407174Sclaudio 	} data;
1849ae689328Smglocker 
1850f7407174Sclaudio 	bzero(&data, sizeof(data));
18510f91cf57Smglocker 	/*
18520f91cf57Smglocker 	 * NOTE:
18530f91cf57Smglocker 	 * Order of templates initialization:
18540f91cf57Smglocker 	 * 1) Probe request
18550f91cf57Smglocker 	 * 2) NULL data
18560f91cf57Smglocker 	 * 3) Beacon
18570f91cf57Smglocker 	 * 4) TIM
18580f91cf57Smglocker 	 * 5) Probe response
18590f91cf57Smglocker 	 * Above order is critical to get a correct memory map.
18600f91cf57Smglocker 	 */
1861f7407174Sclaudio 	if (acx_set_tmplt(sc, ACXCMD_TMPLT_PROBE_REQ, &data.preq,
1862f7407174Sclaudio 	    sizeof(data.preq)) != 0)
186398e37aaaSmglocker 		return (1);
18640f91cf57Smglocker 
1865f7407174Sclaudio 	if (acx_set_tmplt(sc, ACXCMD_TMPLT_NULL_DATA, &data.null,
1866f7407174Sclaudio 	    sizeof(data.null)) != 0)
186798e37aaaSmglocker 		return (1);
18680f91cf57Smglocker 
1869f7407174Sclaudio 	if (acx_set_tmplt(sc, ACXCMD_TMPLT_BEACON, &data.beacon,
1870f7407174Sclaudio 	    sizeof(data.beacon)) != 0)
187198e37aaaSmglocker 		return (1);
18720f91cf57Smglocker 
1873f7407174Sclaudio 	if (acx_set_tmplt(sc, ACXCMD_TMPLT_TIM, &data.tim,
1874f7407174Sclaudio 	    sizeof(data.tim)) != 0)
187598e37aaaSmglocker 		return (1);
18760f91cf57Smglocker 
1877f7407174Sclaudio 	if (acx_set_tmplt(sc, ACXCMD_TMPLT_PROBE_RESP, &data.presp,
1878f7407174Sclaudio 	    sizeof(data.presp)) != 0)
187998e37aaaSmglocker 		return (1);
18800f91cf57Smglocker 
188198e37aaaSmglocker 	return (0);
18820f91cf57Smglocker }
18830f91cf57Smglocker 
18840f91cf57Smglocker int
acx_dma_alloc(struct acx_softc * sc)18850f91cf57Smglocker acx_dma_alloc(struct acx_softc *sc)
18860f91cf57Smglocker {
18870f91cf57Smglocker 	struct acx_ring_data *rd = &sc->sc_ring_data;
18880f91cf57Smglocker 	struct acx_buf_data *bd = &sc->sc_buf_data;
18890f91cf57Smglocker 	int i, error, nsegs;
18900f91cf57Smglocker 
18910f91cf57Smglocker 	/* Allocate DMA stuffs for RX descriptors  */
18920f91cf57Smglocker 	error = bus_dmamap_create(sc->sc_dmat, ACX_RX_RING_SIZE, 1,
18930f91cf57Smglocker 	    ACX_RX_RING_SIZE, 0, BUS_DMA_NOWAIT, &rd->rx_ring_dmamap);
18940f91cf57Smglocker 
18950f91cf57Smglocker 	if (error) {
18960f91cf57Smglocker 		printf("%s: can't create rx ring dma tag\n",
18970f91cf57Smglocker 		    sc->sc_dev.dv_xname);
189898e37aaaSmglocker 		return (error);
18990f91cf57Smglocker 	}
19000f91cf57Smglocker 
19010f91cf57Smglocker 	error = bus_dmamem_alloc(sc->sc_dmat, ACX_RX_RING_SIZE, PAGE_SIZE,
19020f91cf57Smglocker 	    0, &rd->rx_ring_seg, 1, &nsegs, BUS_DMA_NOWAIT);
19030f91cf57Smglocker 
19040f91cf57Smglocker 	if (error != 0) {
19050f91cf57Smglocker 		printf("%s: can't allocate rx ring dma memory\n",
19060f91cf57Smglocker 		    sc->sc_dev.dv_xname);
190798e37aaaSmglocker 		return (error);
19080f91cf57Smglocker 	}
19090f91cf57Smglocker 
19100f91cf57Smglocker 	error = bus_dmamem_map(sc->sc_dmat, &rd->rx_ring_seg, nsegs,
19110f91cf57Smglocker 	    ACX_RX_RING_SIZE, (caddr_t *)&rd->rx_ring,
19120f91cf57Smglocker 	    BUS_DMA_NOWAIT);
19130f91cf57Smglocker 
19140f91cf57Smglocker 	if (error != 0) {
1915e10c952fSsthen 		printf("%s: can't map rx desc DMA memory\n",
19160f91cf57Smglocker 		    sc->sc_dev.dv_xname);
191798e37aaaSmglocker 		return (error);
19180f91cf57Smglocker 	}
19190f91cf57Smglocker 
19200f91cf57Smglocker 	error = bus_dmamap_load(sc->sc_dmat, rd->rx_ring_dmamap,
192198e37aaaSmglocker 	    rd->rx_ring, ACX_RX_RING_SIZE, NULL, BUS_DMA_WAITOK);
1922e50d87e7Smglocker 
19230f91cf57Smglocker 	if (error) {
19240f91cf57Smglocker 		printf("%s: can't get rx ring dma address\n",
19250f91cf57Smglocker 		    sc->sc_dev.dv_xname);
19260f91cf57Smglocker 		bus_dmamem_free(sc->sc_dmat, &rd->rx_ring_seg, 1);
192798e37aaaSmglocker 		return (error);
19280f91cf57Smglocker 	}
19290f91cf57Smglocker 
19300e7cec0cSclaudio 	rd->rx_ring_paddr = rd->rx_ring_dmamap->dm_segs[0].ds_addr;
19310e7cec0cSclaudio 
19320f91cf57Smglocker 	/* Allocate DMA stuffs for TX descriptors */
19330f91cf57Smglocker 	error = bus_dmamap_create(sc->sc_dmat, ACX_TX_RING_SIZE, 1,
19340f91cf57Smglocker 	    ACX_TX_RING_SIZE, 0, BUS_DMA_NOWAIT, &rd->tx_ring_dmamap);
19350f91cf57Smglocker 
19360f91cf57Smglocker 	if (error) {
1937fda8df90Sclaudio 		printf("%s: can't create tx ring dma tag\n",
1938fda8df90Sclaudio 		    sc->sc_dev.dv_xname);
193998e37aaaSmglocker 		return (error);
19400f91cf57Smglocker 	}
19410f91cf57Smglocker 
19420f91cf57Smglocker 	error = bus_dmamem_alloc(sc->sc_dmat, ACX_TX_RING_SIZE, PAGE_SIZE,
19430f91cf57Smglocker 	    0, &rd->tx_ring_seg, 1, &nsegs, BUS_DMA_NOWAIT);
19440f91cf57Smglocker 
19450f91cf57Smglocker 	if (error) {
194698e37aaaSmglocker 		printf("%s: can't allocate tx ring dma memory\n",
1947fda8df90Sclaudio 		    sc->sc_dev.dv_xname);
194898e37aaaSmglocker 		return (error);
19490f91cf57Smglocker 	}
19500f91cf57Smglocker 
19510f91cf57Smglocker 	error = bus_dmamem_map(sc->sc_dmat, &rd->tx_ring_seg, nsegs,
195298e37aaaSmglocker 	    ACX_TX_RING_SIZE, (caddr_t *)&rd->tx_ring, BUS_DMA_NOWAIT);
19530f91cf57Smglocker 
19540f91cf57Smglocker 	if (error != 0) {
1955e10c952fSsthen 		printf("%s: can't map tx desc DMA memory\n",
19560f91cf57Smglocker 		    sc->sc_dev.dv_xname);
195798e37aaaSmglocker 		return (error);
19580f91cf57Smglocker 	}
19590f91cf57Smglocker 
19600f91cf57Smglocker 	error = bus_dmamap_load(sc->sc_dmat, rd->tx_ring_dmamap,
196198e37aaaSmglocker 	    rd->tx_ring, ACX_TX_RING_SIZE, NULL, BUS_DMA_WAITOK);
1962e50d87e7Smglocker 
19630f91cf57Smglocker 	if (error) {
1964fda8df90Sclaudio 		printf("%s: can't get tx ring dma address\n",
1965fda8df90Sclaudio 		    sc->sc_dev.dv_xname);
19660f91cf57Smglocker 		bus_dmamem_free(sc->sc_dmat, &rd->tx_ring_seg, 1);
196798e37aaaSmglocker 		return (error);
19680f91cf57Smglocker 	}
19690f91cf57Smglocker 
19700e7cec0cSclaudio 	rd->tx_ring_paddr = rd->tx_ring_dmamap->dm_segs[0].ds_addr;
19710e7cec0cSclaudio 
19720f91cf57Smglocker 	/* Create a spare RX DMA map */
19730f91cf57Smglocker 	error = bus_dmamap_create(sc->sc_dmat, MCLBYTES, 1, MCLBYTES,
19740f91cf57Smglocker 	    0, 0, &bd->mbuf_tmp_dmamap);
1975e50d87e7Smglocker 
19760f91cf57Smglocker 	if (error) {
1977fda8df90Sclaudio 		printf("%s: can't create tmp mbuf dma map\n",
1978fda8df90Sclaudio 		    sc->sc_dev.dv_xname);
197998e37aaaSmglocker 		return (error);
19800f91cf57Smglocker 	}
19810f91cf57Smglocker 
19820f91cf57Smglocker 	/* Create DMA map for RX mbufs */
19830f91cf57Smglocker 	for (i = 0; i < ACX_RX_DESC_CNT; ++i) {
19840f91cf57Smglocker 		error = bus_dmamap_create(sc->sc_dmat, MCLBYTES, 1,
19850f91cf57Smglocker 		    MCLBYTES, 0, 0, &bd->rx_buf[i].rb_mbuf_dmamap);
19860f91cf57Smglocker 		if (error) {
19870f91cf57Smglocker 			printf("%s: can't create rx mbuf dma map (%d)\n",
1988fda8df90Sclaudio 			    sc->sc_dev.dv_xname, i);
198998e37aaaSmglocker 			return (error);
19900f91cf57Smglocker 		}
19910f91cf57Smglocker 		bd->rx_buf[i].rb_desc = &rd->rx_ring[i];
19920f91cf57Smglocker 	}
19930f91cf57Smglocker 
19940f91cf57Smglocker 	/* Create DMA map for TX mbufs */
19950f91cf57Smglocker 	for (i = 0; i < ACX_TX_DESC_CNT; ++i) {
19960f91cf57Smglocker 		error = bus_dmamap_create(sc->sc_dmat, MCLBYTES, 1,
19970f91cf57Smglocker 		    MCLBYTES, 0, 0, &bd->tx_buf[i].tb_mbuf_dmamap);
19980f91cf57Smglocker 		if (error) {
19990f91cf57Smglocker 			printf("%s: can't create tx mbuf dma map (%d)\n",
2000fda8df90Sclaudio 			    sc->sc_dev.dv_xname, i);
200198e37aaaSmglocker 			return (error);
20020f91cf57Smglocker 		}
20030f91cf57Smglocker 		bd->tx_buf[i].tb_desc1 = &rd->tx_ring[i * 2];
20040f91cf57Smglocker 		bd->tx_buf[i].tb_desc2 = &rd->tx_ring[(i * 2) + 1];
20050f91cf57Smglocker 	}
20060f91cf57Smglocker 
200798e37aaaSmglocker 	return (0);
20080f91cf57Smglocker }
20090f91cf57Smglocker 
20100f91cf57Smglocker void
acx_dma_free(struct acx_softc * sc)20110f91cf57Smglocker acx_dma_free(struct acx_softc *sc)
20120f91cf57Smglocker {
20130f91cf57Smglocker 	struct acx_ring_data *rd = &sc->sc_ring_data;
20140f91cf57Smglocker 	struct acx_buf_data *bd = &sc->sc_buf_data;
20150f91cf57Smglocker 	int i;
20160f91cf57Smglocker 
20170f91cf57Smglocker 	if (rd->rx_ring != NULL) {
20180f91cf57Smglocker 		bus_dmamap_unload(sc->sc_dmat, rd->rx_ring_dmamap);
20190f91cf57Smglocker 		bus_dmamem_free(sc->sc_dmat, &rd->rx_ring_seg, 1);
20200f91cf57Smglocker 	}
20210f91cf57Smglocker 
20220f91cf57Smglocker 	if (rd->tx_ring != NULL) {
20230f91cf57Smglocker 		bus_dmamap_unload(sc->sc_dmat, rd->tx_ring_dmamap);
20240f91cf57Smglocker 		bus_dmamem_free(sc->sc_dmat, &rd->tx_ring_seg, 1);
20250f91cf57Smglocker 	}
20260f91cf57Smglocker 
20270f91cf57Smglocker 	for (i = 0; i < ACX_RX_DESC_CNT; ++i) {
20280f91cf57Smglocker 		if (bd->rx_buf[i].rb_desc != NULL) {
20290f91cf57Smglocker 			if (bd->rx_buf[i].rb_mbuf != NULL) {
20300f91cf57Smglocker 				bus_dmamap_unload(sc->sc_dmat,
20310f91cf57Smglocker 				    bd->rx_buf[i].rb_mbuf_dmamap);
20320f91cf57Smglocker 				m_freem(bd->rx_buf[i].rb_mbuf);
20330f91cf57Smglocker 			}
20340f91cf57Smglocker 			bus_dmamap_destroy(sc->sc_dmat,
20350f91cf57Smglocker 			    bd->rx_buf[i].rb_mbuf_dmamap);
20360f91cf57Smglocker 		}
20370f91cf57Smglocker 	}
20380f91cf57Smglocker 
20390f91cf57Smglocker 	for (i = 0; i < ACX_TX_DESC_CNT; ++i) {
20400f91cf57Smglocker 		if (bd->tx_buf[i].tb_desc1 != NULL) {
20410f91cf57Smglocker 			if (bd->tx_buf[i].tb_mbuf != NULL) {
20420f91cf57Smglocker 				bus_dmamap_unload(sc->sc_dmat,
20430f91cf57Smglocker 				    bd->tx_buf[i].tb_mbuf_dmamap);
20440f91cf57Smglocker 				m_freem(bd->tx_buf[i].tb_mbuf);
20450f91cf57Smglocker 			}
20460f91cf57Smglocker 			bus_dmamap_destroy(sc->sc_dmat,
20470f91cf57Smglocker 			    bd->tx_buf[i].tb_mbuf_dmamap);
20480f91cf57Smglocker 		}
20490f91cf57Smglocker 	}
20500f91cf57Smglocker 
205198e37aaaSmglocker 	if (bd->mbuf_tmp_dmamap != NULL)
20520f91cf57Smglocker 		bus_dmamap_destroy(sc->sc_dmat, bd->mbuf_tmp_dmamap);
20530f91cf57Smglocker }
20540f91cf57Smglocker 
20558eaebac4Sstsp void
acx_init_tx_ring(struct acx_softc * sc)20560f91cf57Smglocker acx_init_tx_ring(struct acx_softc *sc)
20570f91cf57Smglocker {
20580f91cf57Smglocker 	struct acx_ring_data *rd;
20590f91cf57Smglocker 	struct acx_buf_data *bd;
20600f91cf57Smglocker 	uint32_t paddr;
20610f91cf57Smglocker 	int i;
20620f91cf57Smglocker 
20630f91cf57Smglocker 	rd = &sc->sc_ring_data;
20640f91cf57Smglocker 	paddr = rd->tx_ring_paddr;
20650f91cf57Smglocker 	for (i = 0; i < (ACX_TX_DESC_CNT * 2) - 1; ++i) {
20660f91cf57Smglocker 		paddr += sizeof(struct acx_host_desc);
20670f91cf57Smglocker 
20680e7cec0cSclaudio 		bzero(&rd->tx_ring[i], sizeof(struct acx_host_desc));
20690f91cf57Smglocker 		rd->tx_ring[i].h_ctrl = htole16(DESC_CTRL_HOSTOWN);
20700f91cf57Smglocker 
20710f91cf57Smglocker 		if (i == (ACX_TX_DESC_CNT * 2) - 1)
20720f91cf57Smglocker 			rd->tx_ring[i].h_next_desc = htole32(rd->tx_ring_paddr);
20730f91cf57Smglocker 		else
20740f91cf57Smglocker 			rd->tx_ring[i].h_next_desc = htole32(paddr);
20750f91cf57Smglocker 	}
20760f91cf57Smglocker 
20770f91cf57Smglocker 	bus_dmamap_sync(sc->sc_dmat, rd->tx_ring_dmamap, 0,
20780f91cf57Smglocker 	    rd->tx_ring_dmamap->dm_mapsize, BUS_DMASYNC_PREWRITE);
20790f91cf57Smglocker 
20800f91cf57Smglocker 	bd = &sc->sc_buf_data;
20810f91cf57Smglocker 	bd->tx_free_start = 0;
20820f91cf57Smglocker 	bd->tx_used_start = 0;
20830f91cf57Smglocker 	bd->tx_used_count = 0;
20840f91cf57Smglocker }
20850f91cf57Smglocker 
20860f91cf57Smglocker int
acx_init_rx_ring(struct acx_softc * sc)20870f91cf57Smglocker acx_init_rx_ring(struct acx_softc *sc)
20880f91cf57Smglocker {
20890f91cf57Smglocker 	struct acx_ring_data *rd;
20900f91cf57Smglocker 	struct acx_buf_data *bd;
20910f91cf57Smglocker 	uint32_t paddr;
20920f91cf57Smglocker 	int i;
20930f91cf57Smglocker 
20940f91cf57Smglocker 	bd = &sc->sc_buf_data;
20950f91cf57Smglocker 	rd = &sc->sc_ring_data;
20960f91cf57Smglocker 	paddr = rd->rx_ring_paddr;
20970f91cf57Smglocker 
20980f91cf57Smglocker 	for (i = 0; i < ACX_RX_DESC_CNT; ++i) {
20990f91cf57Smglocker 		int error;
21000f91cf57Smglocker 
21010f91cf57Smglocker 		paddr += sizeof(struct acx_host_desc);
21020e7cec0cSclaudio 		bzero(&rd->rx_ring[i], sizeof(struct acx_host_desc));
21030f91cf57Smglocker 
21040f91cf57Smglocker 		error = acx_newbuf(sc, &bd->rx_buf[i], 1);
21050f91cf57Smglocker 		if (error)
210698e37aaaSmglocker 			return (error);
21070f91cf57Smglocker 
21080f91cf57Smglocker 		if (i == ACX_RX_DESC_CNT - 1)
21090f91cf57Smglocker 			rd->rx_ring[i].h_next_desc = htole32(rd->rx_ring_paddr);
21100f91cf57Smglocker 		else
21110f91cf57Smglocker 			rd->rx_ring[i].h_next_desc = htole32(paddr);
21120f91cf57Smglocker 	}
21130f91cf57Smglocker 
21140f91cf57Smglocker 	bus_dmamap_sync(sc->sc_dmat, rd->rx_ring_dmamap, 0,
21150f91cf57Smglocker 	    rd->rx_ring_dmamap->dm_mapsize, BUS_DMASYNC_PREWRITE);
21160f91cf57Smglocker 
21170f91cf57Smglocker 	bd->rx_scan_start = 0;
211898e37aaaSmglocker 
211998e37aaaSmglocker 	return (0);
21200f91cf57Smglocker }
21210f91cf57Smglocker 
21220f91cf57Smglocker int
acx_newbuf(struct acx_softc * sc,struct acx_rxbuf * rb,int wait)21230f91cf57Smglocker acx_newbuf(struct acx_softc *sc, struct acx_rxbuf *rb, int wait)
21240f91cf57Smglocker {
21250f91cf57Smglocker 	struct acx_buf_data *bd;
21260f91cf57Smglocker 	struct mbuf *m;
21270f91cf57Smglocker 	bus_dmamap_t map;
21280f91cf57Smglocker 	uint32_t paddr;
21290f91cf57Smglocker 	int error;
21300f91cf57Smglocker 
21310f91cf57Smglocker 	bd = &sc->sc_buf_data;
21320f91cf57Smglocker 
21330f91cf57Smglocker 	MGETHDR(m, wait ? M_WAITOK : M_DONTWAIT, MT_DATA);
21340f91cf57Smglocker 	if (m == NULL)
21350f91cf57Smglocker 		return (ENOBUFS);
21360f91cf57Smglocker 
21370f91cf57Smglocker 	MCLGET(m, wait ? M_WAITOK : M_DONTWAIT);
21380f91cf57Smglocker 	if (!(m->m_flags & M_EXT)) {
21390f91cf57Smglocker 		m_freem(m);
21400f91cf57Smglocker 		return (ENOBUFS);
21410f91cf57Smglocker 	}
21420f91cf57Smglocker 
21430f91cf57Smglocker 	m->m_len = m->m_pkthdr.len = MCLBYTES;
21440f91cf57Smglocker 
214598e37aaaSmglocker 	error = bus_dmamap_load_mbuf(sc->sc_dmat, bd->mbuf_tmp_dmamap, m,
21460f91cf57Smglocker 	    wait ? BUS_DMA_WAITOK : BUS_DMA_NOWAIT);
21470f91cf57Smglocker 	if (error) {
21480f91cf57Smglocker 		m_freem(m);
214998e37aaaSmglocker 		printf("%s: can't map rx mbuf %d\n",
215098e37aaaSmglocker 		    sc->sc_dev.dv_xname, error);
215198e37aaaSmglocker 		return (error);
21520f91cf57Smglocker 	}
21530f91cf57Smglocker 
21540f91cf57Smglocker 	/* Unload originally mapped mbuf */
2155c4c60a01Sclaudio 	if (rb->rb_mbuf != NULL)
21560f91cf57Smglocker 		bus_dmamap_unload(sc->sc_dmat, rb->rb_mbuf_dmamap);
21570f91cf57Smglocker 
21580f91cf57Smglocker 	/* Swap this dmamap with tmp dmamap */
21590f91cf57Smglocker 	map = rb->rb_mbuf_dmamap;
21600f91cf57Smglocker 	rb->rb_mbuf_dmamap = bd->mbuf_tmp_dmamap;
21610f91cf57Smglocker 	bd->mbuf_tmp_dmamap = map;
21620e7cec0cSclaudio 	paddr = rb->rb_mbuf_dmamap->dm_segs[0].ds_addr;
21630f91cf57Smglocker 
21640f91cf57Smglocker 	rb->rb_mbuf = m;
21650f91cf57Smglocker 	rb->rb_desc->h_data_paddr = htole32(paddr);
21660f91cf57Smglocker 	rb->rb_desc->h_data_len = htole16(m->m_len);
21670f91cf57Smglocker 
21680f91cf57Smglocker 	bus_dmamap_sync(sc->sc_dmat, rb->rb_mbuf_dmamap, 0,
21690f91cf57Smglocker 	    rb->rb_mbuf_dmamap->dm_mapsize, BUS_DMASYNC_PREREAD);
21700f91cf57Smglocker 
217198e37aaaSmglocker 	return (0);
21720f91cf57Smglocker }
21730f91cf57Smglocker 
21740f91cf57Smglocker int
acx_encap(struct acx_softc * sc,struct acx_txbuf * txbuf,struct mbuf * m,struct ieee80211_node * ni,int rate)21750f91cf57Smglocker acx_encap(struct acx_softc *sc, struct acx_txbuf *txbuf, struct mbuf *m,
21760f91cf57Smglocker     struct ieee80211_node *ni, int rate)
21770f91cf57Smglocker {
21780f91cf57Smglocker 	struct acx_ring_data *rd = &sc->sc_ring_data;
21790f91cf57Smglocker 	struct acx_node *node = (struct acx_node *)ni;
21800f91cf57Smglocker 	struct ifnet *ifp = &sc->sc_ic.ic_if;
21810f91cf57Smglocker 	uint32_t paddr;
21820f91cf57Smglocker 	uint8_t ctrl;
21830f91cf57Smglocker 	int error;
21840f91cf57Smglocker 
21850f91cf57Smglocker 	if (txbuf->tb_mbuf != NULL)
2186859d5ed4Skrw 		panic("free TX buf has mbuf installed");
218798e37aaaSmglocker 
21880f91cf57Smglocker 	if (m->m_pkthdr.len > MCLBYTES) {
21890f91cf57Smglocker 		printf("%s: mbuf too big\n", ifp->if_xname);
21900f91cf57Smglocker 		error = E2BIG;
21910f91cf57Smglocker 		goto back;
21920f91cf57Smglocker 	} else if (m->m_pkthdr.len < ACX_FRAME_HDRLEN) {
21930f91cf57Smglocker 		printf("%s: mbuf too small\n", ifp->if_xname);
21940f91cf57Smglocker 		error = EINVAL;
21950f91cf57Smglocker 		goto back;
21960f91cf57Smglocker 	}
21970f91cf57Smglocker 
219898e37aaaSmglocker 	error = bus_dmamap_load_mbuf(sc->sc_dmat, txbuf->tb_mbuf_dmamap, m,
219998e37aaaSmglocker 	    BUS_DMA_NOWAIT);
22000f91cf57Smglocker 
22010f91cf57Smglocker 	if (error && error != EFBIG) {
220298e37aaaSmglocker 		printf("%s: can't map tx mbuf1 %d\n",
220398e37aaaSmglocker 		    sc->sc_dev.dv_xname, error);
22040f91cf57Smglocker 		goto back;
22050f91cf57Smglocker 	}
22060f91cf57Smglocker 
22070f91cf57Smglocker 	if (error) {	/* error == EFBIG */
22080f91cf57Smglocker 		/* too many fragments, linearize */
2209da3be2eeSkrw 		if (m_defrag(m, M_DONTWAIT)) {
22100e7cec0cSclaudio 			printf("%s: can't defrag tx mbuf\n", ifp->if_xname);
22110e7cec0cSclaudio 			goto back;
22120f91cf57Smglocker 		}
22130f91cf57Smglocker 		error = bus_dmamap_load_mbuf(sc->sc_dmat,
221498e37aaaSmglocker 		    txbuf->tb_mbuf_dmamap, m, BUS_DMA_NOWAIT);
22150f91cf57Smglocker 		if (error) {
22160f91cf57Smglocker 			printf("%s: can't map tx mbuf2 %d\n",
22170f91cf57Smglocker 			    sc->sc_dev.dv_xname, error);
22180f91cf57Smglocker 			goto back;
22190f91cf57Smglocker 		}
22200f91cf57Smglocker 	}
22210f91cf57Smglocker 
22220f91cf57Smglocker 	error = 0;
22230f91cf57Smglocker 
22240f91cf57Smglocker 	bus_dmamap_sync(sc->sc_dmat, txbuf->tb_mbuf_dmamap, 0,
22250f91cf57Smglocker 	    txbuf->tb_mbuf_dmamap->dm_mapsize, BUS_DMASYNC_PREWRITE);
22260f91cf57Smglocker 
22270f91cf57Smglocker 	txbuf->tb_mbuf = m;
22280f91cf57Smglocker 	txbuf->tb_node = node;
22290f91cf57Smglocker 	txbuf->tb_rate = rate;
22300f91cf57Smglocker 
22310f91cf57Smglocker 	/*
22320f91cf57Smglocker 	 * TX buffers are accessed in following way:
22330f91cf57Smglocker 	 * acx_fw_txdesc -> acx_host_desc -> buffer
22340f91cf57Smglocker 	 *
223558684cc2Sdamien 	 * It is quite strange that acx also queries acx_host_desc next to
22360f91cf57Smglocker 	 * the one we have assigned to acx_fw_txdesc even if first one's
22370f91cf57Smglocker 	 * acx_host_desc.h_data_len == acx_fw_txdesc.f_tx_len
22380f91cf57Smglocker 	 *
22390f91cf57Smglocker 	 * So we allocate two acx_host_desc for one acx_fw_txdesc and
22400f91cf57Smglocker 	 * assign the first acx_host_desc to acx_fw_txdesc
22410f91cf57Smglocker 	 *
22420f91cf57Smglocker 	 * For acx111
22430f91cf57Smglocker 	 * host_desc1.h_data_len = buffer_len
22440f91cf57Smglocker 	 * host_desc2.h_data_len = buffer_len - mac_header_len
22450f91cf57Smglocker 	 *
22460f91cf57Smglocker 	 * For acx100
22470f91cf57Smglocker 	 * host_desc1.h_data_len = mac_header_len
22480f91cf57Smglocker 	 * host_desc2.h_data_len = buffer_len - mac_header_len
22490f91cf57Smglocker 	 */
22500e7cec0cSclaudio 	paddr = txbuf->tb_mbuf_dmamap->dm_segs[0].ds_addr;
22510f91cf57Smglocker 	txbuf->tb_desc1->h_data_paddr = htole32(paddr);
22520f91cf57Smglocker 	txbuf->tb_desc2->h_data_paddr = htole32(paddr + ACX_FRAME_HDRLEN);
22530f91cf57Smglocker 
22540f91cf57Smglocker 	txbuf->tb_desc1->h_data_len =
22550f91cf57Smglocker 	    htole16(sc->chip_txdesc1_len ? sc->chip_txdesc1_len
22560f91cf57Smglocker 	    : m->m_pkthdr.len);
22570f91cf57Smglocker 	txbuf->tb_desc2->h_data_len =
22580f91cf57Smglocker 	    htole16(m->m_pkthdr.len - ACX_FRAME_HDRLEN);
22590f91cf57Smglocker 
22600f91cf57Smglocker 	/*
22610f91cf57Smglocker 	 * NOTE:
22620f91cf57Smglocker 	 * We can't simply assign f_tx_ctrl, we will first read it back
22630f91cf57Smglocker 	 * and change it bit by bit
22640f91cf57Smglocker 	 */
22650f91cf57Smglocker 	ctrl = FW_TXDESC_GETFIELD_1(sc, txbuf, f_tx_ctrl);
22660f91cf57Smglocker 	ctrl |= sc->chip_fw_txdesc_ctrl; /* extra chip specific flags */
22670f91cf57Smglocker 	ctrl &= ~(DESC_CTRL_HOSTOWN | DESC_CTRL_ACXDONE);
22680f91cf57Smglocker 
22698587bc98Sclaudio 	FW_TXDESC_SETFIELD_2(sc, txbuf, f_tx_len, m->m_pkthdr.len);
22700f91cf57Smglocker 	FW_TXDESC_SETFIELD_1(sc, txbuf, f_tx_error, 0);
22710f91cf57Smglocker 	FW_TXDESC_SETFIELD_1(sc, txbuf, f_tx_ack_fail, 0);
22720f91cf57Smglocker 	FW_TXDESC_SETFIELD_1(sc, txbuf, f_tx_rts_fail, 0);
22730f91cf57Smglocker 	FW_TXDESC_SETFIELD_1(sc, txbuf, f_tx_rts_ok, 0);
22740f91cf57Smglocker 	sc->chip_set_fw_txdesc_rate(sc, txbuf, rate);
22750f91cf57Smglocker 
22760f91cf57Smglocker 	txbuf->tb_desc1->h_ctrl = 0;
22770f91cf57Smglocker 	txbuf->tb_desc2->h_ctrl = 0;
22780f91cf57Smglocker 	bus_dmamap_sync(sc->sc_dmat, rd->tx_ring_dmamap, 0,
22790f91cf57Smglocker 	    rd->tx_ring_dmamap->dm_mapsize, BUS_DMASYNC_PREWRITE);
22800f91cf57Smglocker 
22810f91cf57Smglocker 	FW_TXDESC_SETFIELD_1(sc, txbuf, f_tx_ctrl2, 0);
22820f91cf57Smglocker 	FW_TXDESC_SETFIELD_1(sc, txbuf, f_tx_ctrl, ctrl);
22830f91cf57Smglocker 
22840f91cf57Smglocker 	/* Tell chip to inform us about TX completion */
22850f91cf57Smglocker 	CSR_WRITE_2(sc, ACXREG_INTR_TRIG, ACXRV_TRIG_TX_FINI);
22860f91cf57Smglocker back:
22870f91cf57Smglocker 	if (error)
22880f91cf57Smglocker 		m_freem(m);
228998e37aaaSmglocker 
229098e37aaaSmglocker 	return (error);
22910f91cf57Smglocker }
22920f91cf57Smglocker 
22930f91cf57Smglocker int
acx_set_null_tmplt(struct acx_softc * sc)22940f91cf57Smglocker acx_set_null_tmplt(struct acx_softc *sc)
22950f91cf57Smglocker {
2296864278e7Smglocker 	struct ieee80211com *ic = &sc->sc_ic;
22970f91cf57Smglocker 	struct acx_tmplt_null_data n;
2298202033cdSdamien 	struct ieee80211_frame *wh;
22990f91cf57Smglocker 
23000f91cf57Smglocker 	bzero(&n, sizeof(n));
23010f91cf57Smglocker 
2302202033cdSdamien 	wh = &n.data;
2303202033cdSdamien 	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA |
2304202033cdSdamien 	    IEEE80211_FC0_SUBTYPE_NODATA;
2305202033cdSdamien 	wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
2306202033cdSdamien 	IEEE80211_ADDR_COPY(wh->i_addr1, etherbroadcastaddr);
2307202033cdSdamien 	IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr);
2308202033cdSdamien 	IEEE80211_ADDR_COPY(wh->i_addr3, etherbroadcastaddr);
23090f91cf57Smglocker 
231085c13c25Sclaudio 	return (acx_set_tmplt(sc, ACXCMD_TMPLT_NULL_DATA, &n, sizeof(n)));
23110f91cf57Smglocker }
23120f91cf57Smglocker 
23130f91cf57Smglocker int
acx_set_probe_req_tmplt(struct acx_softc * sc,const char * ssid,int ssid_len)23140f91cf57Smglocker acx_set_probe_req_tmplt(struct acx_softc *sc, const char *ssid, int ssid_len)
23150f91cf57Smglocker {
2316864278e7Smglocker 	struct ieee80211com *ic = &sc->sc_ic;
23170f91cf57Smglocker 	struct acx_tmplt_probe_req req;
2318202033cdSdamien 	struct ieee80211_frame *wh;
23191a8e1ebbSmglocker 	struct ieee80211_rateset *rs;
2320202033cdSdamien 	uint8_t *frm;
2321202033cdSdamien 	int len;
23220f91cf57Smglocker 
23230f91cf57Smglocker 	bzero(&req, sizeof(req));
23240f91cf57Smglocker 
2325202033cdSdamien 	wh = &req.data.u_data.f;
2326202033cdSdamien 	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
2327202033cdSdamien 	    IEEE80211_FC0_SUBTYPE_PROBE_REQ;
2328202033cdSdamien 	wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
2329202033cdSdamien 	IEEE80211_ADDR_COPY(wh->i_addr1, etherbroadcastaddr);
2330202033cdSdamien 	IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr);
2331202033cdSdamien 	IEEE80211_ADDR_COPY(wh->i_addr3, etherbroadcastaddr);
23320f91cf57Smglocker 
2333202033cdSdamien 	frm = req.data.u_data.var;
2334202033cdSdamien 	frm = ieee80211_add_ssid(frm, ssid, ssid_len);
23351a8e1ebbSmglocker 	rs = &ic->ic_sup_rates[sc->chip_phymode];
23361a8e1ebbSmglocker 	frm = ieee80211_add_rates(frm, rs);
23371a8e1ebbSmglocker 	if (rs->rs_nrates > IEEE80211_RATE_SIZE)
23381a8e1ebbSmglocker 		frm = ieee80211_add_xrates(frm, rs);
2339202033cdSdamien 	len = frm - req.data.u_data.var;
23400f91cf57Smglocker 
234185c13c25Sclaudio 	return (acx_set_tmplt(sc, ACXCMD_TMPLT_PROBE_REQ, &req,
2342202033cdSdamien 	    ACX_TMPLT_PROBE_REQ_SIZ(len)));
23430f91cf57Smglocker }
23440f91cf57Smglocker 
2345171ac09aSdamien #ifndef IEEE80211_STA_ONLY
23463186c1a6Sclaudio struct mbuf *ieee80211_get_probe_resp(struct ieee80211com *,
23473186c1a6Sclaudio     struct ieee80211_node *);
23483186c1a6Sclaudio 
23490f91cf57Smglocker int
acx_set_probe_resp_tmplt(struct acx_softc * sc,struct ieee80211_node * ni)2350864278e7Smglocker acx_set_probe_resp_tmplt(struct acx_softc *sc, struct ieee80211_node *ni)
23510f91cf57Smglocker {
23520f91cf57Smglocker 	struct ieee80211com *ic = &sc->sc_ic;
2353864278e7Smglocker 	struct acx_tmplt_probe_resp resp;
23543186c1a6Sclaudio 	struct ieee80211_frame *wh;
2355864278e7Smglocker 	struct mbuf *m;
2356202033cdSdamien 	int len;
23570f91cf57Smglocker 
23580f91cf57Smglocker 	bzero(&resp, sizeof(resp));
23590f91cf57Smglocker 
23603186c1a6Sclaudio 	m = ieee80211_get_probe_resp(ic, ni);
23612581ba88Sclaudio 	if (m == NULL)
23622581ba88Sclaudio 		return (1);
23633186c1a6Sclaudio 	M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT);
23643186c1a6Sclaudio 	if (m == NULL)
23653186c1a6Sclaudio 		return (1);
23663186c1a6Sclaudio 	wh = mtod(m, struct ieee80211_frame *);
23673186c1a6Sclaudio 	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
23683186c1a6Sclaudio 	    IEEE80211_FC0_SUBTYPE_PROBE_RESP;
23693186c1a6Sclaudio 	wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
23703186c1a6Sclaudio 	*(u_int16_t *)&wh->i_dur[0] = 0;
23713186c1a6Sclaudio 	IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr);
23723186c1a6Sclaudio 	IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr);
23733186c1a6Sclaudio 	IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid);
23743186c1a6Sclaudio 	*(u_int16_t *)wh->i_seq = 0;
23753186c1a6Sclaudio 
23765c7fed39Sdlg 	m_copydata(m, 0, m->m_pkthdr.len, &resp.data);
2377864278e7Smglocker 	len = m->m_pkthdr.len + sizeof(resp.size);
2378864278e7Smglocker 	m_freem(m);
23790f91cf57Smglocker 
238085c13c25Sclaudio 	return (acx_set_tmplt(sc, ACXCMD_TMPLT_PROBE_RESP, &resp, len));
23810f91cf57Smglocker }
23820f91cf57Smglocker 
23830f91cf57Smglocker int
acx_beacon_locate(struct mbuf * m,u_int8_t type)2384791621e9Sclaudio acx_beacon_locate(struct mbuf *m, u_int8_t type)
2385791621e9Sclaudio {
2386791621e9Sclaudio 	int off;
2387791621e9Sclaudio 	u_int8_t *frm;
2388791621e9Sclaudio 	/*
2389791621e9Sclaudio 	 * beacon frame format
2390791621e9Sclaudio 	 *	[8] time stamp
2391791621e9Sclaudio 	 *	[2] beacon interval
23924b1a56afSjsg 	 *	[2] capability information
2393791621e9Sclaudio 	 *	from here on [tlv] values
2394791621e9Sclaudio 	 */
2395791621e9Sclaudio 
2396791621e9Sclaudio 	if (m->m_len != m->m_pkthdr.len)
2397791621e9Sclaudio 		panic("beacon not in contiguous mbuf");
2398791621e9Sclaudio 
2399791621e9Sclaudio 	off = sizeof(struct ieee80211_frame) + 8 + 2 + 2;
2400791621e9Sclaudio 	frm = mtod(m, u_int8_t *);
2401791621e9Sclaudio 	for (; off + 1 < m->m_len; off += frm[off + 1] + 2) {
2402791621e9Sclaudio 		if (frm[off] == type)
2403791621e9Sclaudio 			return (off);
2404791621e9Sclaudio 	}
24058eaebac4Sstsp 	return (-1);
2406791621e9Sclaudio }
2407791621e9Sclaudio 
2408791621e9Sclaudio int
acx_set_beacon_tmplt(struct acx_softc * sc,struct ieee80211_node * ni)2409864278e7Smglocker acx_set_beacon_tmplt(struct acx_softc *sc, struct ieee80211_node *ni)
24100f91cf57Smglocker {
24110f91cf57Smglocker 	struct ieee80211com *ic = &sc->sc_ic;
2412864278e7Smglocker 	struct acx_tmplt_beacon beacon;
2413791621e9Sclaudio 	struct acx_tmplt_tim tim;
2414864278e7Smglocker 	struct mbuf *m;
2415791621e9Sclaudio 	int len, off;
2416791621e9Sclaudio 
2417791621e9Sclaudio 	bzero(&beacon, sizeof(beacon));
2418791621e9Sclaudio 	bzero(&tim, sizeof(tim));
24190f91cf57Smglocker 
2420864278e7Smglocker 	m = ieee80211_beacon_alloc(ic, ni);
2421ae689328Smglocker 	if (m == NULL)
2422ae689328Smglocker 		return (1);
2423791621e9Sclaudio 
2424791621e9Sclaudio 	off = acx_beacon_locate(m, IEEE80211_ELEMID_TIM);
24258eaebac4Sstsp 	if (off < 0) {
24268eaebac4Sstsp 		m_free(m);
24278eaebac4Sstsp 		return (1);
24288eaebac4Sstsp 	}
2429791621e9Sclaudio 
24305c7fed39Sdlg 	m_copydata(m, 0, off, &beacon.data);
2431791621e9Sclaudio 	len = off + sizeof(beacon.size);
2432791621e9Sclaudio 
2433791621e9Sclaudio 	if (acx_set_tmplt(sc, ACXCMD_TMPLT_BEACON, &beacon, len) != 0) {
2434791621e9Sclaudio 		m_freem(m);
2435791621e9Sclaudio 		return (1);
2436791621e9Sclaudio 	}
2437791621e9Sclaudio 
2438791621e9Sclaudio 	len = m->m_pkthdr.len - off;
2439791621e9Sclaudio 	if (len == 0) {
2440791621e9Sclaudio 		/* no TIM field */
2441791621e9Sclaudio 		m_freem(m);
2442791621e9Sclaudio 		return (0);
2443791621e9Sclaudio 	}
2444791621e9Sclaudio 
24455c7fed39Sdlg 	m_copydata(m, off, len, &tim.data);
2446791621e9Sclaudio 	len += sizeof(beacon.size);
2447864278e7Smglocker 	m_freem(m);
24480f91cf57Smglocker 
2449791621e9Sclaudio 	return (acx_set_tmplt(sc, ACXCMD_TMPLT_TIM, &tim, len));
24500f91cf57Smglocker }
2451171ac09aSdamien #endif	/* IEEE80211_STA_ONLY */
24520f91cf57Smglocker 
24530f91cf57Smglocker void
acx_init_cmd_reg(struct acx_softc * sc)24540f91cf57Smglocker acx_init_cmd_reg(struct acx_softc *sc)
24550f91cf57Smglocker {
24560f91cf57Smglocker 	sc->sc_cmd = CSR_READ_4(sc, ACXREG_CMD_REG_OFFSET);
24570f91cf57Smglocker 	sc->sc_cmd_param = sc->sc_cmd + ACX_CMD_REG_SIZE;
24580f91cf57Smglocker 
24590f91cf57Smglocker 	/* Clear command & status */
24600f91cf57Smglocker 	CMD_WRITE_4(sc, 0);
24610f91cf57Smglocker }
24620f91cf57Smglocker 
24630f91cf57Smglocker int
acx_join_bss(struct acx_softc * sc,uint8_t mode,struct ieee80211_node * node)24640f91cf57Smglocker acx_join_bss(struct acx_softc *sc, uint8_t mode, struct ieee80211_node *node)
24650f91cf57Smglocker {
24660f91cf57Smglocker 	uint8_t bj_buf[BSS_JOIN_BUFLEN];
24670f91cf57Smglocker 	struct bss_join_hdr *bj;
24680f91cf57Smglocker 	int i, dtim_intvl;
24690f91cf57Smglocker 
24700f91cf57Smglocker 	bzero(bj_buf, sizeof(bj_buf));
24710f91cf57Smglocker 	bj = (struct bss_join_hdr *)bj_buf;
24720f91cf57Smglocker 
24730f91cf57Smglocker 	for (i = 0; i < IEEE80211_ADDR_LEN; ++i)
24740f91cf57Smglocker 		bj->bssid[i] = node->ni_bssid[IEEE80211_ADDR_LEN - i - 1];
24750f91cf57Smglocker 
24760f91cf57Smglocker 	bj->beacon_intvl = htole16(acx_beacon_intvl);
24770f91cf57Smglocker 
24780f91cf57Smglocker 	/* TODO tunable */
2479171ac09aSdamien #ifndef IEEE80211_STA_ONLY
2480171ac09aSdamien 	if (sc->sc_ic.ic_opmode == IEEE80211_M_IBSS)
2481171ac09aSdamien 		dtim_intvl = 1;
2482171ac09aSdamien 	else
2483171ac09aSdamien #endif
2484171ac09aSdamien 		dtim_intvl = 10;
24850f91cf57Smglocker 	sc->chip_set_bss_join_param(sc, bj->chip_spec, dtim_intvl);
24860f91cf57Smglocker 
248769d24b9aSbrad 	bj->ndata_txrate = ACX_NDATA_TXRATE_1;
24880f91cf57Smglocker 	bj->ndata_txopt = 0;
24890f91cf57Smglocker 	bj->mode = mode;
24900f91cf57Smglocker 	bj->channel = ieee80211_chan2ieee(&sc->sc_ic, node->ni_chan);
24910f91cf57Smglocker 	bj->esslen = node->ni_esslen;
24920f91cf57Smglocker 	bcopy(node->ni_essid, bj->essid, node->ni_esslen);
24930f91cf57Smglocker 
24940f91cf57Smglocker 	DPRINTF(("%s: join BSS/IBSS on channel %d\n", sc->sc_dev.dv_xname,
24950f91cf57Smglocker 	    bj->channel));
249698e37aaaSmglocker 	return (acx_exec_command(sc, ACXCMD_JOIN_BSS,
249798e37aaaSmglocker 	    bj, BSS_JOIN_PARAM_SIZE(bj), NULL, 0));
24980f91cf57Smglocker }
24990f91cf57Smglocker 
25000f91cf57Smglocker int
acx_set_channel(struct acx_softc * sc,uint8_t chan)2501611f5384Smglocker acx_set_channel(struct acx_softc *sc, uint8_t chan)
25020f91cf57Smglocker {
2503611f5384Smglocker 	if (acx_exec_command(sc, ACXCMD_ENABLE_TXCHAN, &chan, sizeof(chan),
2504611f5384Smglocker 	    NULL, 0) != 0) {
2505611f5384Smglocker 		DPRINTF(("%s: setting TX channel %d failed\n",
2506611f5384Smglocker 		    sc->sc_dev.dv_xname, chan));
2507611f5384Smglocker 		return (ENXIO);
25080f91cf57Smglocker 	}
25090f91cf57Smglocker 
2510611f5384Smglocker 	if (acx_exec_command(sc, ACXCMD_ENABLE_RXCHAN, &chan, sizeof(chan),
2511611f5384Smglocker 	    NULL, 0) != 0) {
2512611f5384Smglocker 		DPRINTF(("%s: setting RX channel %d failed\n",
2513611f5384Smglocker 		    sc->sc_dev.dv_xname, chan));
2514611f5384Smglocker 		return (ENXIO);
2515611f5384Smglocker 	}
2516611f5384Smglocker 
2517611f5384Smglocker 	return (0);
25180f91cf57Smglocker }
25190f91cf57Smglocker 
25200f91cf57Smglocker int
acx_get_conf(struct acx_softc * sc,uint16_t conf_id,void * conf,uint16_t conf_len)25210f91cf57Smglocker acx_get_conf(struct acx_softc *sc, uint16_t conf_id, void *conf,
25220f91cf57Smglocker     uint16_t conf_len)
25230f91cf57Smglocker {
25240f91cf57Smglocker 	struct acx_conf *confcom;
25250f91cf57Smglocker 
25260f91cf57Smglocker 	if (conf_len < sizeof(*confcom)) {
25270f91cf57Smglocker 		printf("%s: %s configure data is too short\n",
25280f91cf57Smglocker 		    sc->sc_dev.dv_xname, __func__);
252998e37aaaSmglocker 		return (1);
25300f91cf57Smglocker 	}
25310f91cf57Smglocker 
25320f91cf57Smglocker 	confcom = conf;
25330f91cf57Smglocker 	confcom->conf_id = htole16(conf_id);
25340f91cf57Smglocker 	confcom->conf_data_len = htole16(conf_len - sizeof(*confcom));
25350f91cf57Smglocker 
253698e37aaaSmglocker 	return (acx_exec_command(sc, ACXCMD_GET_CONF, confcom, sizeof(*confcom),
253798e37aaaSmglocker 	    conf, conf_len));
25380f91cf57Smglocker }
25390f91cf57Smglocker 
25400f91cf57Smglocker int
acx_set_conf(struct acx_softc * sc,uint16_t conf_id,void * conf,uint16_t conf_len)25410f91cf57Smglocker acx_set_conf(struct acx_softc *sc, uint16_t conf_id, void *conf,
25420f91cf57Smglocker     uint16_t conf_len)
25430f91cf57Smglocker {
25440f91cf57Smglocker 	struct acx_conf *confcom;
25450f91cf57Smglocker 
25460f91cf57Smglocker 	if (conf_len < sizeof(*confcom)) {
25470f91cf57Smglocker 		printf("%s: %s configure data is too short\n",
25480f91cf57Smglocker 		    sc->sc_dev.dv_xname, __func__);
254998e37aaaSmglocker 		return (1);
25500f91cf57Smglocker 	}
25510f91cf57Smglocker 
25520f91cf57Smglocker 	confcom = conf;
25530f91cf57Smglocker 	confcom->conf_id = htole16(conf_id);
25540f91cf57Smglocker 	confcom->conf_data_len = htole16(conf_len - sizeof(*confcom));
25550f91cf57Smglocker 
255698e37aaaSmglocker 	return (acx_exec_command(sc, ACXCMD_SET_CONF, conf, conf_len, NULL, 0));
25570f91cf57Smglocker }
25580f91cf57Smglocker 
25590f91cf57Smglocker int
acx_set_tmplt(struct acx_softc * sc,uint16_t cmd,void * tmplt,uint16_t tmplt_len)25600f91cf57Smglocker acx_set_tmplt(struct acx_softc *sc, uint16_t cmd, void *tmplt,
25610f91cf57Smglocker     uint16_t tmplt_len)
25620f91cf57Smglocker {
25630f91cf57Smglocker 	uint16_t *size;
25640f91cf57Smglocker 
25650f91cf57Smglocker 	if (tmplt_len < sizeof(*size)) {
25660f91cf57Smglocker 		printf("%s: %s template is too short\n",
25670f91cf57Smglocker 		    sc->sc_dev.dv_xname, __func__);
256898e37aaaSmglocker 		return (1);
25690f91cf57Smglocker 	}
25700f91cf57Smglocker 
25710f91cf57Smglocker 	size = tmplt;
25720f91cf57Smglocker 	*size = htole16(tmplt_len - sizeof(*size));
25730f91cf57Smglocker 
257498e37aaaSmglocker 	return (acx_exec_command(sc, cmd, tmplt, tmplt_len, NULL, 0));
25750f91cf57Smglocker }
25760f91cf57Smglocker 
25770f91cf57Smglocker int
acx_init_radio(struct acx_softc * sc,uint32_t radio_ofs,uint32_t radio_len)25780f91cf57Smglocker acx_init_radio(struct acx_softc *sc, uint32_t radio_ofs, uint32_t radio_len)
25790f91cf57Smglocker {
25800f91cf57Smglocker 	struct radio_init r;
25810f91cf57Smglocker 
25820f91cf57Smglocker 	r.radio_ofs = htole32(radio_ofs);
25830f91cf57Smglocker 	r.radio_len = htole32(radio_len);
258498e37aaaSmglocker 
258598e37aaaSmglocker 	return (acx_exec_command(sc, ACXCMD_INIT_RADIO, &r, sizeof(r), NULL,
258698e37aaaSmglocker 	    0));
25870f91cf57Smglocker }
25880f91cf57Smglocker 
25890f91cf57Smglocker int
acx_exec_command(struct acx_softc * sc,uint16_t cmd,void * param,uint16_t param_len,void * result,uint16_t result_len)25900f91cf57Smglocker acx_exec_command(struct acx_softc *sc, uint16_t cmd, void *param,
25910f91cf57Smglocker     uint16_t param_len, void *result, uint16_t result_len)
25920f91cf57Smglocker {
25930f91cf57Smglocker 	uint16_t status;
25940f91cf57Smglocker 	int i, ret;
25950f91cf57Smglocker 
25960f91cf57Smglocker 	if ((sc->sc_flags & ACX_FLAG_FW_LOADED) == 0) {
259798e37aaaSmglocker 		printf("%s: cmd 0x%04x failed (base firmware not loaded)\n",
259898e37aaaSmglocker 		    sc->sc_dev.dv_xname, cmd);
259998e37aaaSmglocker 		return (1);
26000f91cf57Smglocker 	}
26010f91cf57Smglocker 
26020f91cf57Smglocker 	ret = 0;
26030f91cf57Smglocker 
26040f91cf57Smglocker 	if (param != NULL && param_len != 0) {
26050f91cf57Smglocker 		/* Set command param */
26060f91cf57Smglocker 		CMDPRM_WRITE_REGION_1(sc, param, param_len);
26070f91cf57Smglocker 	}
26080f91cf57Smglocker 
26090f91cf57Smglocker 	/* Set command */
26100f91cf57Smglocker 	CMD_WRITE_4(sc, cmd);
26110f91cf57Smglocker 
26120f91cf57Smglocker 	/* Exec command */
26130f91cf57Smglocker 	CSR_WRITE_2(sc, ACXREG_INTR_TRIG, ACXRV_TRIG_CMD_FINI);
261493b9f4a4Smglocker 	DELAY(50);
26150f91cf57Smglocker 
26160f91cf57Smglocker 	/* Wait for command to complete */
26170f91cf57Smglocker 	if (cmd == ACXCMD_INIT_RADIO) {
2618e50d87e7Smglocker 		/* radio initialization is extremely long */
261953d49253Scheloha 		tsleep_nsec(&cmd, 0, "rdinit", MSEC_TO_NSEC(300));
26200f91cf57Smglocker 	}
26210f91cf57Smglocker 
26220f91cf57Smglocker #define CMDWAIT_RETRY_MAX	1000
26230f91cf57Smglocker 	for (i = 0; i < CMDWAIT_RETRY_MAX; ++i) {
26240f91cf57Smglocker 		uint16_t reg;
26250f91cf57Smglocker 
26260f91cf57Smglocker 		reg = CSR_READ_2(sc, ACXREG_INTR_STATUS);
26270f91cf57Smglocker 		if (reg & ACXRV_INTR_CMD_FINI) {
26280f91cf57Smglocker 			CSR_WRITE_2(sc, ACXREG_INTR_ACK, ACXRV_INTR_CMD_FINI);
26290f91cf57Smglocker 			break;
26300f91cf57Smglocker 		}
26310f91cf57Smglocker 		DELAY(50);
26320f91cf57Smglocker 	}
26330f91cf57Smglocker 	if (i == CMDWAIT_RETRY_MAX) {
26340f91cf57Smglocker 		printf("%s: cmd %04x failed (timeout)\n",
26350f91cf57Smglocker 		    sc->sc_dev.dv_xname, cmd);
26360f91cf57Smglocker 		ret = 1;
26370f91cf57Smglocker 		goto back;
26380f91cf57Smglocker 	}
26390f91cf57Smglocker #undef CMDWAIT_RETRY_MAX
26400f91cf57Smglocker 
26410f91cf57Smglocker 	/* Get command exec status */
26420f91cf57Smglocker 	status = (CMD_READ_4(sc) >> ACX_CMD_STATUS_SHIFT);
26430f91cf57Smglocker 	if (status != ACX_CMD_STATUS_OK) {
2644ad4f15e5Smglocker 		DPRINTF(("%s: cmd %04x failed\n", sc->sc_dev.dv_xname, cmd));
26450f91cf57Smglocker 		ret = 1;
26460f91cf57Smglocker 		goto back;
26470f91cf57Smglocker 	}
26480f91cf57Smglocker 
26490f91cf57Smglocker 	if (result != NULL && result_len != 0) {
26500f91cf57Smglocker 		/* Get command result */
26510f91cf57Smglocker 		CMDPRM_READ_REGION_1(sc, result, result_len);
26520f91cf57Smglocker 	}
26530f91cf57Smglocker 
26540f91cf57Smglocker back:
26550f91cf57Smglocker 	CMD_WRITE_4(sc, 0);
265698e37aaaSmglocker 
265798e37aaaSmglocker 	return (ret);
26580f91cf57Smglocker }
26592226c610Sjsg 
26602226c610Sjsg const char *
acx_get_rf(int rev)26612226c610Sjsg acx_get_rf(int rev)
26622226c610Sjsg {
26632226c610Sjsg 	switch (rev) {
26642226c610Sjsg 	case ACX_RADIO_TYPE_MAXIM:	return "MAX2820";
26652226c610Sjsg 	case ACX_RADIO_TYPE_RFMD:	return "RFMD";
26662226c610Sjsg 	case ACX_RADIO_TYPE_RALINK:	return "Ralink";
26672226c610Sjsg 	case ACX_RADIO_TYPE_RADIA:	return "Radia";
26682226c610Sjsg 	default:			return "unknown";
26692226c610Sjsg 	}
26702226c610Sjsg }
26718dc7d017Smglocker 
26728dc7d017Smglocker int
acx_get_maxrssi(int radio)26738dc7d017Smglocker acx_get_maxrssi(int radio)
26748dc7d017Smglocker {
26758dc7d017Smglocker 	switch (radio) {
26768dc7d017Smglocker 	case ACX_RADIO_TYPE_MAXIM:	return ACX_RADIO_RSSI_MAXIM;
26778dc7d017Smglocker 	case ACX_RADIO_TYPE_RFMD:	return ACX_RADIO_RSSI_RFMD;
26788dc7d017Smglocker 	case ACX_RADIO_TYPE_RALINK:	return ACX_RADIO_RSSI_RALINK;
26798dc7d017Smglocker 	case ACX_RADIO_TYPE_RADIA:	return ACX_RADIO_RSSI_RADIA;
26808dc7d017Smglocker 	default:			return ACX_RADIO_RSSI_UNKN;
26818dc7d017Smglocker 	}
26828dc7d017Smglocker }
2683960b9804Smglocker 
2684960b9804Smglocker void
acx_iter_func(void * arg,struct ieee80211_node * ni)2685960b9804Smglocker acx_iter_func(void *arg, struct ieee80211_node *ni)
2686960b9804Smglocker {
2687960b9804Smglocker 	struct acx_softc *sc = arg;
2688960b9804Smglocker 	struct acx_node *wn = (struct acx_node *)ni;
2689960b9804Smglocker 
2690960b9804Smglocker 	ieee80211_amrr_choose(&sc->amrr, ni, &wn->amn);
2691960b9804Smglocker }
2692960b9804Smglocker 
2693960b9804Smglocker void
acx_amrr_timeout(void * arg)2694960b9804Smglocker acx_amrr_timeout(void *arg)
2695960b9804Smglocker {
2696960b9804Smglocker 	struct acx_softc *sc = arg;
2697960b9804Smglocker 	struct ieee80211com *ic = &sc->sc_ic;
2698960b9804Smglocker 
2699960b9804Smglocker 	if (ic->ic_opmode == IEEE80211_M_STA)
2700960b9804Smglocker 		acx_iter_func(sc, ic->ic_bss);
2701960b9804Smglocker 	else
2702960b9804Smglocker 		ieee80211_iterate_nodes(ic, acx_iter_func, sc);
2703960b9804Smglocker 
2704ed934a0cSblambert 	timeout_add_msec(&sc->amrr_ch, 500);
2705960b9804Smglocker }
2706960b9804Smglocker 
2707960b9804Smglocker void
acx_newassoc(struct ieee80211com * ic,struct ieee80211_node * ni,int isnew)2708960b9804Smglocker acx_newassoc(struct ieee80211com *ic, struct ieee80211_node *ni, int isnew)
2709960b9804Smglocker {
2710960b9804Smglocker 	struct acx_softc *sc = ic->ic_if.if_softc;
2711960b9804Smglocker 	int i;
2712960b9804Smglocker 
2713960b9804Smglocker 	ieee80211_amrr_node_init(&sc->amrr, &((struct acx_node *)ni)->amn);
2714960b9804Smglocker 
2715960b9804Smglocker 	/* set rate to some reasonable initial value */
2716960b9804Smglocker 	for (i = ni->ni_rates.rs_nrates - 1;
2717960b9804Smglocker 	    i > 0 && (ni->ni_rates.rs_rates[i] & IEEE80211_RATE_VAL) > 72;
2718960b9804Smglocker 	    i--);
2719960b9804Smglocker 	ni->ni_txrate = i;
2720960b9804Smglocker }
2721a0de3126Sclaudio 
2722a0de3126Sclaudio #ifndef IEEE80211_STA_ONLY
2723a0de3126Sclaudio void
acx_set_tim(struct ieee80211com * ic,int aid,int set)2724a0de3126Sclaudio acx_set_tim(struct ieee80211com *ic, int aid, int set)
2725a0de3126Sclaudio {
2726a0de3126Sclaudio 	struct acx_softc *sc = ic->ic_if.if_softc;
2727a0de3126Sclaudio 	struct acx_tmplt_tim tim;
2728a0de3126Sclaudio 	u_int8_t *ep;
2729a0de3126Sclaudio 
2730f24d97dfSclaudio 	ieee80211_set_tim(ic, aid, set);
2731a0de3126Sclaudio 
2732a0de3126Sclaudio 	bzero(&tim, sizeof(tim));
2733a0de3126Sclaudio 	ep = ieee80211_add_tim(tim.data.u_mem, ic);
2734a0de3126Sclaudio 
2735a0de3126Sclaudio 	acx_set_tmplt(sc, ACXCMD_TMPLT_TIM, &tim, ep - (u_int8_t *)&tim);
2736a0de3126Sclaudio }
2737a0de3126Sclaudio #endif
2738