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, ®_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, ®_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