mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-16 10:20:30 +00:00
0d5c223da4
From the README: Any IEEE 802.11 cards use AMD Am79C930 and Harris (Intersil) Chipset with PCnetMobile firmware by AMD. BayStack 650 1Mbps Frequency Hopping PCCARD adapter BayStack 660 2Mbps Direct Sequence PCCARD adapter Icom SL-200 2Mbps Direct Sequence PCCARD adapter Melco WLI-PCM 2Mbps Direct Sequence PCCARD adapter NEL SSMagic 2Mbps Direct Sequence PCCARD adapter Netwave AirSurfer Plus 1Mbps Frequency Hopping PCCARD adapter Netwave AirSurfer Pro 2Mbps Direct Sequence PCCARD adapter Known Problems: WEP is not supported. Does not create IBSS itself. Cannot configure the following on FreeBSD: selection of infrastructure/adhoc mode ESSID ... Submitted by: Atsushi Onoe <onoe@sm.sony.co.jp>
2770 lines
66 KiB
C
2770 lines
66 KiB
C
/* $NetBSD: awi.c,v 1.12 2000/03/23 05:26:00 mycroft Exp $ */
|
|
/* $FreeBSD$ */
|
|
|
|
/*
|
|
* Copyright (c) 2000 The NetBSD Foundation, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to The NetBSD Foundation
|
|
* by Atsushi Onoe.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by the NetBSD
|
|
* Foundation, Inc. and its contributors.
|
|
* 4. Neither the name of The NetBSD Foundation nor the names of its
|
|
* contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/*
|
|
* Driver for AMD 802.11 PCnetMobile firmware.
|
|
* Uses am79c930 chip driver to talk to firmware running on the am79c930.
|
|
*
|
|
* The awi device driver first appeared in NetBSD 1.5.
|
|
*
|
|
* The initial version of the driver was written by
|
|
* Bill Sommerfeld <sommerfeld@netbsd.org>.
|
|
* Then the driver module completely rewritten to support cards with DS phy
|
|
* and to support adhoc mode by Atsushi Onoe <onoe@netbsd.org>
|
|
*/
|
|
|
|
#ifdef __NetBSD__
|
|
#include "opt_inet.h"
|
|
#include "opt_ns.h"
|
|
#include "bpfilter.h"
|
|
#include "rnd.h"
|
|
#endif
|
|
#ifdef __FreeBSD__
|
|
#if __FreeBSD__ >= 3
|
|
#include "opt_inet.h"
|
|
#endif
|
|
#if __FreeBSD__ >= 4
|
|
#include "bpf.h"
|
|
#define NBPFILTER NBPF
|
|
#else
|
|
#include "bpfilter.h"
|
|
#endif
|
|
#endif
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/mbuf.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/socket.h>
|
|
#ifdef __FreeBSD__
|
|
#include <sys/sockio.h>
|
|
#else
|
|
#include <sys/ioctl.h>
|
|
#endif
|
|
#include <sys/errno.h>
|
|
#include <sys/syslog.h>
|
|
#include <sys/select.h>
|
|
#if defined(__FreeBSD__) && __FreeBSD__ >= 4
|
|
#include <sys/bus.h>
|
|
#else
|
|
#include <sys/device.h>
|
|
#endif
|
|
#if NRND > 0
|
|
#include <sys/rnd.h>
|
|
#endif
|
|
|
|
#include <net/if.h>
|
|
#include <net/if_dl.h>
|
|
#ifdef __FreeBSD__
|
|
#include <net/ethernet.h>
|
|
#else
|
|
#include <net/if_ether.h>
|
|
#endif
|
|
#include <net/if_media.h>
|
|
#include <net/if_llc.h>
|
|
#include <net/if_ieee80211.h>
|
|
|
|
#ifdef INET
|
|
#include <netinet/in.h>
|
|
#include <netinet/in_systm.h>
|
|
#include <netinet/in_var.h>
|
|
#include <netinet/ip.h>
|
|
#ifdef __NetBSD__
|
|
#include <netinet/if_inarp.h>
|
|
#else
|
|
#include <netinet/if_ether.h>
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef NS
|
|
#include <netns/ns.h>
|
|
#include <netns/ns_if.h>
|
|
#endif
|
|
|
|
#if NBPFILTER > 0
|
|
#include <net/bpf.h>
|
|
#include <net/bpfdesc.h>
|
|
#endif
|
|
|
|
#include <machine/cpu.h>
|
|
#include <machine/bus.h>
|
|
#ifdef __NetBSD__
|
|
#include <machine/intr.h>
|
|
#endif
|
|
#ifdef __FreeBSD__
|
|
#include <machine/clock.h>
|
|
#endif
|
|
|
|
#ifdef __NetBSD__
|
|
#include <dev/ic/am79c930reg.h>
|
|
#include <dev/ic/am79c930var.h>
|
|
#include <dev/ic/awireg.h>
|
|
#include <dev/ic/awivar.h>
|
|
#include <dev/ic/awictl.h>
|
|
#endif
|
|
#ifdef __FreeBSD__
|
|
#include <dev/awi/am79c930reg.h>
|
|
#include <dev/awi/am79c930var.h>
|
|
#include <dev/awi/awireg.h>
|
|
#include <dev/awi/awivar.h>
|
|
#include <dev/awi/awictl.h>
|
|
#endif
|
|
|
|
static int awi_ioctl __P((struct ifnet *ifp, u_long cmd, caddr_t data));
|
|
#ifdef IFM_IEEE80211
|
|
static int awi_media_rate2opt __P((struct awi_softc *sc, int rate));
|
|
static int awi_media_opt2rate __P((struct awi_softc *sc, int opt));
|
|
static int awi_media_change __P((struct ifnet *ifp));
|
|
static void awi_media_status __P((struct ifnet *ifp, struct ifmediareq *imr));
|
|
#endif
|
|
static int awi_drvget __P((struct ifnet *ifp, u_long cmd, caddr_t data));
|
|
static int awi_drvset __P((struct ifnet *ifp, u_long cmd, caddr_t data));
|
|
static int awi_init __P((struct awi_softc *sc));
|
|
static void awi_stop __P((struct awi_softc *sc));
|
|
static void awi_watchdog __P((struct ifnet *ifp));
|
|
static void awi_start __P((struct ifnet *ifp));
|
|
static void awi_txint __P((struct awi_softc *sc));
|
|
static struct mbuf * awi_fix_txhdr __P((struct awi_softc *sc, struct mbuf *m0));
|
|
static struct mbuf * awi_fix_rxhdr __P((struct awi_softc *sc, struct mbuf *m0));
|
|
static void awi_input __P((struct awi_softc *sc, struct mbuf *m, u_int32_t rxts, u_int8_t rssi));
|
|
static void awi_rxint __P((struct awi_softc *sc));
|
|
struct mbuf * awi_devget __P((struct awi_softc *sc, u_int32_t off, u_int16_t len));
|
|
static int awi_init_hw __P((struct awi_softc *sc));
|
|
static int awi_init_mibs __P((struct awi_softc *sc));
|
|
static int awi_init_txrx __P((struct awi_softc *sc));
|
|
static void awi_stop_txrx __P((struct awi_softc *sc));
|
|
static int awi_init_region __P((struct awi_softc *sc));
|
|
static int awi_start_scan __P((struct awi_softc *sc));
|
|
static int awi_next_scan __P((struct awi_softc *sc));
|
|
static void awi_stop_scan __P((struct awi_softc *sc));
|
|
static void awi_recv_beacon __P((struct awi_softc *sc, struct mbuf *m0, u_int32_t rxts, u_int8_t rssi));
|
|
static int awi_set_ss __P((struct awi_softc *sc));
|
|
static void awi_try_sync __P((struct awi_softc *sc));
|
|
static void awi_sync_done __P((struct awi_softc *sc));
|
|
static void awi_send_deauth __P((struct awi_softc *sc));
|
|
static void awi_send_auth __P((struct awi_softc *sc));
|
|
static void awi_recv_auth __P((struct awi_softc *sc, struct mbuf *m0));
|
|
static void awi_send_asreq __P((struct awi_softc *sc, int reassoc));
|
|
static void awi_recv_asresp __P((struct awi_softc *sc, struct mbuf *m0));
|
|
static int awi_mib __P((struct awi_softc *sc, u_int8_t cmd, u_int8_t mib));
|
|
static int awi_cmd_scan __P((struct awi_softc *sc));
|
|
static int awi_cmd __P((struct awi_softc *sc, u_int8_t cmd));
|
|
static void awi_cmd_done __P((struct awi_softc *sc));
|
|
static int awi_next_txd __P((struct awi_softc *sc, int len, u_int32_t *framep, u_int32_t*ntxdp));
|
|
static int awi_lock __P((struct awi_softc *sc));
|
|
static void awi_unlock __P((struct awi_softc *sc));
|
|
static int awi_intr_lock __P((struct awi_softc *sc));
|
|
static void awi_intr_unlock __P((struct awi_softc *sc));
|
|
static int awi_cmd_wait __P((struct awi_softc *sc));
|
|
|
|
#ifdef AWI_DEBUG
|
|
static void awi_dump_pkt __P((struct awi_softc *sc, struct mbuf *m, u_int8_t rssi));
|
|
int awi_verbose = 0;
|
|
int awi_dump = 0;
|
|
#define AWI_DUMP_MASK(fc0) (1 << (((fc0) & IEEE80211_FC0_SUBTYPE_MASK) >> 4))
|
|
int awi_dump_mask = AWI_DUMP_MASK(IEEE80211_FC0_SUBTYPE_BEACON);
|
|
int awi_dump_hdr = 0;
|
|
int awi_dump_len = 28;
|
|
#endif
|
|
|
|
#if NBPFILTER > 0
|
|
#define AWI_BPF_NORM 0
|
|
#define AWI_BPF_RAW 1
|
|
#ifdef __FreeBSD__
|
|
#define AWI_BPF_MTAP(sc, m, raw) do { \
|
|
if ((sc)->sc_ifp->if_bpf && (sc)->sc_rawbpf == (raw)) \
|
|
bpf_mtap((sc)->sc_ifp, (m)); \
|
|
} while (0);
|
|
#else
|
|
#define AWI_BPF_MTAP(sc, m, raw) do { \
|
|
if ((sc)->sc_ifp->if_bpf && (sc)->sc_rawbpf == (raw)) \
|
|
bpf_mtap((sc)->sc_ifp->if_bpf, (m)); \
|
|
} while (0);
|
|
#endif
|
|
#else
|
|
#define AWI_BPF_MTAP(sc, m, raw)
|
|
#endif
|
|
|
|
#ifndef llc_snap
|
|
#define llc_snap llc_un.type_snap
|
|
#endif
|
|
|
|
#ifdef __FreeBSD__
|
|
#if __FreeBSD__ < 4
|
|
#define memset(p, v, n) bzero(p, n) /*XXX*/
|
|
#endif
|
|
|
|
#if __FreeBSD__ >= 4
|
|
devclass_t awi_devclass;
|
|
#endif
|
|
|
|
/* NetBSD compatible functions */
|
|
static char * ether_sprintf __P((u_int8_t *));
|
|
|
|
static char *
|
|
ether_sprintf(enaddr)
|
|
u_int8_t *enaddr;
|
|
{
|
|
static char strbuf[18];
|
|
|
|
sprintf(strbuf, "%6D", enaddr, ":");
|
|
return strbuf;
|
|
}
|
|
#endif
|
|
|
|
int
|
|
awi_attach(sc)
|
|
struct awi_softc *sc;
|
|
{
|
|
struct ifnet *ifp = sc->sc_ifp;
|
|
#ifdef IFM_IEEE80211
|
|
int i;
|
|
u_int8_t *phy_rates;
|
|
int mword;
|
|
struct ifmediareq imr;
|
|
#endif
|
|
int s;
|
|
int error;
|
|
|
|
s = splnet();
|
|
|
|
/*
|
|
* Even if we can sleep in initialization state,
|
|
* all other processes (e.g. ifconfig) have to wait for
|
|
* completion of attaching interface.
|
|
*/
|
|
sc->sc_busy = 1;
|
|
sc->sc_status = AWI_ST_INIT;
|
|
TAILQ_INIT(&sc->sc_scan);
|
|
error = awi_init_hw(sc);
|
|
if (error) {
|
|
sc->sc_invalid = 1;
|
|
splx(s);
|
|
return error;
|
|
}
|
|
error = awi_init_mibs(sc);
|
|
splx(s);
|
|
if (error) {
|
|
sc->sc_invalid = 1;
|
|
return error;
|
|
}
|
|
|
|
ifp->if_softc = sc;
|
|
ifp->if_start = awi_start;
|
|
ifp->if_ioctl = awi_ioctl;
|
|
ifp->if_watchdog = awi_watchdog;
|
|
ifp->if_mtu = ETHERMTU;
|
|
ifp->if_hdrlen = sizeof(struct ieee80211_frame) +
|
|
sizeof(struct ether_header);
|
|
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
|
|
#ifdef IFF_NOTRAILERS
|
|
ifp->if_flags |= IFF_NOTRAILERS;
|
|
#endif
|
|
#ifdef __NetBSD__
|
|
memcpy(ifp->if_xname, sc->sc_dev.dv_xname, IFNAMSIZ);
|
|
#endif
|
|
#ifdef __FreeBSD__
|
|
ifp->if_output = ether_output;
|
|
ifp->if_snd.ifq_maxlen = ifqmaxlen;
|
|
memcpy(sc->sc_ec.ac_enaddr, sc->sc_mib_addr.aMAC_Address,
|
|
ETHER_ADDR_LEN);
|
|
#endif
|
|
|
|
printf("%s: IEEE802.11 (%s %dMbps) address %s\n",
|
|
sc->sc_dev.dv_xname,
|
|
sc->sc_mib_phy.IEEE_PHY_Type == AWI_PHY_TYPE_FH ? "FH" : "DS",
|
|
sc->sc_tx_rate / 10, ether_sprintf(sc->sc_mib_addr.aMAC_Address));
|
|
if_attach(ifp);
|
|
#ifdef __FreeBSD__
|
|
ether_ifattach(ifp);
|
|
#if NBPFILTER > 0
|
|
bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header));
|
|
#endif
|
|
#else
|
|
ether_ifattach(ifp, sc->sc_mib_addr.aMAC_Address);
|
|
#if NBPFILTER > 0
|
|
bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB, sizeof(struct ether_header));
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef IFM_IEEE80211
|
|
ifmedia_init(&sc->sc_media, 0, awi_media_change, awi_media_status);
|
|
phy_rates = sc->sc_mib_phy.aSuprt_Data_Rates;
|
|
for (i = 0; i < phy_rates[1]; i++) {
|
|
mword = awi_media_rate2opt(sc, AWI_80211_RATE(phy_rates[2 + i]));
|
|
if (mword == 0)
|
|
continue;
|
|
mword |= IFM_IEEE80211;
|
|
ifmedia_add(&sc->sc_media, mword, 0, NULL);
|
|
ifmedia_add(&sc->sc_media,
|
|
mword | IFM_IEEE80211_ADHOC, 0, NULL);
|
|
ifmedia_add(&sc->sc_media,
|
|
mword | IFM_IEEE80211_ADHOC | IFM_FLAG0, 0, NULL);
|
|
}
|
|
awi_media_status(ifp, &imr);
|
|
ifmedia_set(&sc->sc_media, imr.ifm_active);
|
|
#endif
|
|
|
|
/* ready to accept ioctl */
|
|
awi_unlock(sc);
|
|
return 0;
|
|
}
|
|
|
|
#ifdef __NetBSD__
|
|
int
|
|
awi_detach(sc)
|
|
struct awi_softc *sc;
|
|
{
|
|
struct ifnet *ifp = sc->sc_ifp;
|
|
int s;
|
|
|
|
s = splnet();
|
|
sc->sc_invalid = 1;
|
|
awi_stop(sc);
|
|
while (sc->sc_sleep_cnt > 0) {
|
|
wakeup(sc);
|
|
(void)tsleep(sc, PWAIT, "awidet", 1);
|
|
}
|
|
#if NBPFILTER > 0
|
|
bpfdetach(ifp);
|
|
#endif
|
|
#ifdef IFM_IEEE80211
|
|
ifmedia_delete_instance(&sc->sc_media, IFM_INST_ANY);
|
|
#endif
|
|
ether_ifdetach(ifp);
|
|
if_detach(ifp);
|
|
if (sc->sc_enabled) {
|
|
if (sc->sc_disable)
|
|
(*sc->sc_disable)(sc);
|
|
sc->sc_enabled = 0;
|
|
}
|
|
splx(s);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
awi_activate(self, act)
|
|
struct device *self;
|
|
enum devact act;
|
|
{
|
|
struct awi_softc *sc = (struct awi_softc *)self;
|
|
int s, error = 0;
|
|
|
|
s = splnet();
|
|
switch (act) {
|
|
case DVACT_ACTIVATE:
|
|
error = EOPNOTSUPP;
|
|
break;
|
|
|
|
case DVACT_DEACTIVATE:
|
|
sc->sc_invalid = 1;
|
|
if_deactivate(sc->sc_ifp);
|
|
break;
|
|
}
|
|
splx(s);
|
|
|
|
return error;
|
|
}
|
|
#endif /* __NetBSD__ */
|
|
|
|
void
|
|
awi_reset(sc)
|
|
struct awi_softc *sc;
|
|
{
|
|
int s;
|
|
|
|
if (!sc->sc_enabled)
|
|
return;
|
|
s = splnet();
|
|
sc->sc_invalid = 1;
|
|
awi_stop(sc);
|
|
if (sc->sc_disable)
|
|
(*sc->sc_disable)(sc);
|
|
sc->sc_enabled = 0;
|
|
DELAY(1000);
|
|
sc->sc_invalid = 0;
|
|
(void)awi_init(sc);
|
|
splx(s);
|
|
}
|
|
|
|
static int
|
|
awi_ioctl(ifp, cmd, data)
|
|
struct ifnet *ifp;
|
|
u_long cmd;
|
|
caddr_t data;
|
|
{
|
|
struct awi_softc *sc = ifp->if_softc;
|
|
struct ifreq *ifr = (struct ifreq *)data;
|
|
struct ifaddr *ifa = (struct ifaddr *)data;
|
|
int s, error;
|
|
size_t nwidlen;
|
|
u_int8_t nwid[IEEE80211_NWID_LEN + 1];
|
|
u_int8_t *p;
|
|
|
|
s = splnet();
|
|
|
|
/* serialize ioctl */
|
|
error = awi_lock(sc);
|
|
if (error)
|
|
goto cantlock;
|
|
switch (cmd) {
|
|
case SIOCSIFADDR:
|
|
ifp->if_flags |= IFF_UP;
|
|
switch (ifa->ifa_addr->sa_family) {
|
|
#ifdef INET
|
|
case AF_INET:
|
|
arp_ifinit((void *)ifp, ifa);
|
|
break;
|
|
#endif
|
|
}
|
|
/* FALLTHROUGH */
|
|
case SIOCSIFFLAGS:
|
|
sc->sc_format_llc = !(ifp->if_flags & IFF_LINK0);
|
|
if (!(ifp->if_flags & IFF_UP)) {
|
|
if (sc->sc_enabled) {
|
|
awi_stop(sc);
|
|
if (sc->sc_disable)
|
|
(*sc->sc_disable)(sc);
|
|
sc->sc_enabled = 0;
|
|
}
|
|
break;
|
|
}
|
|
error = awi_init(sc);
|
|
break;
|
|
|
|
case SIOCADDMULTI:
|
|
case SIOCDELMULTI:
|
|
#ifdef __FreeBSD__
|
|
error = ENETRESET; /*XXX*/
|
|
#else
|
|
error = (cmd == SIOCADDMULTI) ?
|
|
ether_addmulti(ifr, &sc->sc_ec) :
|
|
ether_delmulti(ifr, &sc->sc_ec);
|
|
#endif
|
|
if (error == ENETRESET) {
|
|
if (sc->sc_enabled)
|
|
error = awi_init(sc);
|
|
else
|
|
error = 0;
|
|
}
|
|
break;
|
|
case SIOCSIFMTU:
|
|
if (ifr->ifr_mtu > ETHERMTU)
|
|
error = EINVAL;
|
|
else
|
|
ifp->if_mtu = ifr->ifr_mtu;
|
|
break;
|
|
case SIOCS80211NWID:
|
|
error = copyinstr(ifr->ifr_data, nwid, sizeof(nwid), &nwidlen);
|
|
if (error)
|
|
break;
|
|
nwidlen--; /* eliminate trailing '\0' */
|
|
if (nwidlen > IEEE80211_NWID_LEN) {
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
if (sc->sc_mib_mac.aDesired_ESS_ID[1] == nwidlen &&
|
|
memcmp(&sc->sc_mib_mac.aDesired_ESS_ID[2], nwid,
|
|
nwidlen) == 0)
|
|
break;
|
|
memset(sc->sc_mib_mac.aDesired_ESS_ID, 0, AWI_ESS_ID_SIZE);
|
|
sc->sc_mib_mac.aDesired_ESS_ID[0] = IEEE80211_ELEMID_SSID;
|
|
sc->sc_mib_mac.aDesired_ESS_ID[1] = nwidlen;
|
|
memcpy(&sc->sc_mib_mac.aDesired_ESS_ID[2], nwid, nwidlen);
|
|
if (sc->sc_enabled) {
|
|
awi_stop(sc);
|
|
error = awi_init(sc);
|
|
}
|
|
break;
|
|
case SIOCG80211NWID:
|
|
if (ifp->if_flags & IFF_RUNNING)
|
|
p = sc->sc_bss.essid;
|
|
else
|
|
p = sc->sc_mib_mac.aDesired_ESS_ID;
|
|
error = copyout(p + 2, ifr->ifr_data, IEEE80211_NWID_LEN);
|
|
break;
|
|
#ifdef IFM_IEEE80211
|
|
case SIOCSIFMEDIA:
|
|
case SIOCGIFMEDIA:
|
|
error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd);
|
|
break;
|
|
#endif
|
|
case SIOCGDRVSPEC:
|
|
error = awi_drvget(ifp, cmd, data);
|
|
break;
|
|
case SIOCSDRVSPEC:
|
|
error = awi_drvset(ifp, cmd, data);
|
|
break;
|
|
default:
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
awi_unlock(sc);
|
|
cantlock:
|
|
splx(s);
|
|
return error;
|
|
}
|
|
|
|
#ifdef IFM_IEEE80211
|
|
static int
|
|
awi_media_rate2opt(sc, rate)
|
|
struct awi_softc *sc;
|
|
int rate;
|
|
{
|
|
int mword;
|
|
|
|
mword = 0;
|
|
switch (rate) {
|
|
case 10:
|
|
if (sc->sc_mib_phy.IEEE_PHY_Type == AWI_PHY_TYPE_FH)
|
|
mword = IFM_IEEE80211_FH1;
|
|
else
|
|
mword = IFM_IEEE80211_DS1;
|
|
break;
|
|
case 20:
|
|
if (sc->sc_mib_phy.IEEE_PHY_Type == AWI_PHY_TYPE_FH)
|
|
mword = IFM_IEEE80211_FH2;
|
|
else
|
|
mword = IFM_IEEE80211_DS2;
|
|
break;
|
|
case 55:
|
|
if (sc->sc_mib_phy.IEEE_PHY_Type == AWI_PHY_TYPE_DS)
|
|
mword = IFM_IEEE80211_DS5;
|
|
break;
|
|
case 110:
|
|
if (sc->sc_mib_phy.IEEE_PHY_Type == AWI_PHY_TYPE_DS)
|
|
mword = IFM_IEEE80211_DS11;
|
|
break;
|
|
}
|
|
return mword;
|
|
}
|
|
|
|
static int
|
|
awi_media_opt2rate(sc, opt)
|
|
struct awi_softc *sc;
|
|
int opt;
|
|
{
|
|
int rate;
|
|
|
|
rate = 0;
|
|
switch (IFM_SUBTYPE(opt)) {
|
|
case IFM_IEEE80211_FH1:
|
|
case IFM_IEEE80211_FH2:
|
|
if (sc->sc_mib_phy.IEEE_PHY_Type != AWI_PHY_TYPE_FH)
|
|
return 0;
|
|
break;
|
|
case IFM_IEEE80211_DS1:
|
|
case IFM_IEEE80211_DS2:
|
|
case IFM_IEEE80211_DS5:
|
|
case IFM_IEEE80211_DS11:
|
|
if (sc->sc_mib_phy.IEEE_PHY_Type != AWI_PHY_TYPE_DS)
|
|
return 0;
|
|
break;
|
|
}
|
|
|
|
switch (IFM_SUBTYPE(opt)) {
|
|
case IFM_IEEE80211_FH1:
|
|
case IFM_IEEE80211_DS1:
|
|
rate = 10;
|
|
break;
|
|
case IFM_IEEE80211_FH2:
|
|
case IFM_IEEE80211_DS2:
|
|
rate = 20;
|
|
break;
|
|
case IFM_IEEE80211_DS5:
|
|
rate = 55;
|
|
break;
|
|
case IFM_IEEE80211_DS11:
|
|
rate = 110;
|
|
break;
|
|
}
|
|
return rate;
|
|
}
|
|
|
|
/*
|
|
* Called from ifmedia_ioctl via awi_ioctl with lock obtained.
|
|
*/
|
|
static int
|
|
awi_media_change(ifp)
|
|
struct ifnet *ifp;
|
|
{
|
|
struct awi_softc *sc = ifp->if_softc;
|
|
struct ifmedia_entry *ime;
|
|
u_int8_t *phy_rates;
|
|
int i, rate, error;
|
|
|
|
error = 0;
|
|
ime = sc->sc_media.ifm_cur;
|
|
rate = awi_media_opt2rate(sc, ime->ifm_media);
|
|
if (rate == 0)
|
|
return EINVAL;
|
|
if (rate != sc->sc_tx_rate) {
|
|
phy_rates = sc->sc_mib_phy.aSuprt_Data_Rates;
|
|
for (i = 0; i < phy_rates[1]; i++) {
|
|
if (rate == AWI_80211_RATE(phy_rates[2 + i]))
|
|
break;
|
|
}
|
|
if (i == phy_rates[1])
|
|
return EINVAL;
|
|
}
|
|
if (ime->ifm_media & IFM_IEEE80211_ADHOC) {
|
|
sc->sc_mib_local.Network_Mode = 0;
|
|
sc->sc_no_bssid = (ime->ifm_media & IFM_FLAG0) ? 1 : 0;
|
|
} else {
|
|
sc->sc_mib_local.Network_Mode = 1;
|
|
}
|
|
if (sc->sc_enabled) {
|
|
awi_stop(sc);
|
|
error = awi_init(sc);
|
|
}
|
|
return error;
|
|
}
|
|
|
|
static void
|
|
awi_media_status(ifp, imr)
|
|
struct ifnet *ifp;
|
|
struct ifmediareq *imr;
|
|
{
|
|
struct awi_softc *sc = ifp->if_softc;
|
|
|
|
imr->ifm_status = IFM_AVALID;
|
|
if (ifp->if_flags & IFF_RUNNING)
|
|
imr->ifm_status |= IFM_ACTIVE;
|
|
imr->ifm_active = IFM_IEEE80211;
|
|
imr->ifm_active |= awi_media_rate2opt(sc, sc->sc_tx_rate);
|
|
if (sc->sc_mib_local.Network_Mode == 0) {
|
|
imr->ifm_active |= IFM_IEEE80211_ADHOC;
|
|
if (sc->sc_no_bssid)
|
|
imr->ifm_active |= IFM_FLAG0;
|
|
}
|
|
}
|
|
#endif /* IFM_IEEE80211 */
|
|
|
|
static int
|
|
awi_drvget(ifp, cmd, data)
|
|
struct ifnet *ifp;
|
|
u_long cmd;
|
|
caddr_t data;
|
|
{
|
|
struct awi_softc *sc = ifp->if_softc;
|
|
struct ifdrv *ifd = (struct ifdrv *)data;
|
|
u_int8_t buf[AWICTL_BUFSIZE];
|
|
u_int8_t *essid;
|
|
int error = 0;
|
|
|
|
switch (ifd->ifd_cmd) {
|
|
case AWICTL_REGION:
|
|
if (ifd->ifd_len < 1)
|
|
return ENOSPC;
|
|
ifd->ifd_len = 1;
|
|
buf[0] = sc->sc_mib_phy.aCurrent_Reg_Domain;
|
|
break;
|
|
case AWICTL_CHANSET:
|
|
if (ifd->ifd_len < 3)
|
|
return ENOSPC;
|
|
ifd->ifd_len = 3;
|
|
buf[0] = sc->sc_bss.chanset;
|
|
buf[1] = sc->sc_scan_min;
|
|
buf[2] = sc->sc_scan_max;
|
|
break;
|
|
case AWICTL_RAWBPF:
|
|
if (ifd->ifd_len < 1)
|
|
return ENOSPC;
|
|
ifd->ifd_len = 1;
|
|
buf[0] = sc->sc_rawbpf;
|
|
break;
|
|
case AWICTL_DESSID:
|
|
case AWICTL_CESSID:
|
|
if (ifd->ifd_cmd == AWICTL_DESSID)
|
|
essid = sc->sc_mib_mac.aDesired_ESS_ID;
|
|
else
|
|
essid = sc->sc_bss.essid;
|
|
if (ifd->ifd_len < essid[1])
|
|
return ENOSPC;
|
|
ifd->ifd_len = essid[1];
|
|
if (ifd->ifd_len > 0)
|
|
memcpy(buf, essid, ifd->ifd_len);
|
|
break;
|
|
case AWICTL_MODE:
|
|
if (ifd->ifd_len < 1)
|
|
return ENOSPC;
|
|
ifd->ifd_len = 1;
|
|
if (sc->sc_mib_local.Network_Mode == 0) {
|
|
if (sc->sc_no_bssid)
|
|
buf[0] = AWICTL_MODE_NOBSSID;
|
|
else
|
|
buf[0] = AWICTL_MODE_ADHOC;
|
|
} else
|
|
buf[0] = AWICTL_MODE_INFRA;
|
|
break;
|
|
default:
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
if (error == 0 && ifd->ifd_len > 0)
|
|
error = copyout(ifd->ifd_data, buf, ifd->ifd_len);
|
|
return error;
|
|
}
|
|
|
|
static int
|
|
awi_drvset(ifp, cmd, data)
|
|
struct ifnet *ifp;
|
|
u_long cmd;
|
|
caddr_t data;
|
|
{
|
|
struct awi_softc *sc = ifp->if_softc;
|
|
struct ifdrv *ifd = (struct ifdrv *)data;
|
|
u_int8_t buf[AWICTL_BUFSIZE];
|
|
u_int8_t oregion;
|
|
int error = 0;
|
|
|
|
if (ifd->ifd_len > sizeof(buf))
|
|
return EINVAL;
|
|
error = copyin(ifd->ifd_data, buf, ifd->ifd_len);
|
|
if (error)
|
|
return error;
|
|
|
|
switch (ifd->ifd_cmd) {
|
|
case AWICTL_REGION:
|
|
if (ifd->ifd_len != 1)
|
|
return EINVAL;
|
|
oregion = sc->sc_mib_phy.aCurrent_Reg_Domain;
|
|
if (buf[0] == oregion)
|
|
break;
|
|
sc->sc_mib_phy.aCurrent_Reg_Domain = buf[0];
|
|
error = awi_init_region(sc);
|
|
if (error) {
|
|
sc->sc_mib_phy.aCurrent_Reg_Domain = oregion;
|
|
break;
|
|
}
|
|
if (sc->sc_enabled) {
|
|
awi_stop(sc);
|
|
error = awi_init(sc);
|
|
}
|
|
break;
|
|
case AWICTL_CHANSET:
|
|
if (ifd->ifd_len != 3)
|
|
return EINVAL;
|
|
/* reset scan min/max */
|
|
awi_init_region(sc);
|
|
if (buf[0] < sc->sc_scan_min || buf[0] > sc->sc_scan_max ||
|
|
buf[1] < sc->sc_scan_min || buf[1] > sc->sc_scan_max ||
|
|
buf[2] < sc->sc_scan_min || buf[2] > sc->sc_scan_max)
|
|
return EINVAL;
|
|
sc->sc_scan_cur = buf[0];
|
|
sc->sc_scan_min = buf[1];
|
|
sc->sc_scan_max = buf[2];
|
|
if (sc->sc_mib_phy.IEEE_PHY_Type == AWI_PHY_TYPE_FH)
|
|
sc->sc_scan_set = sc->sc_scan_cur % 3 + 1;
|
|
if (sc->sc_enabled) {
|
|
awi_stop(sc);
|
|
error = awi_init(sc);
|
|
}
|
|
break;
|
|
case AWICTL_RAWBPF:
|
|
if (ifd->ifd_len != 1)
|
|
return EINVAL;
|
|
sc->sc_rawbpf = buf[0];
|
|
break;
|
|
case AWICTL_DESSID:
|
|
if (ifd->ifd_len > IEEE80211_NWID_LEN)
|
|
return EINVAL;
|
|
if (sc->sc_mib_mac.aDesired_ESS_ID[1] == ifd->ifd_len &&
|
|
memcmp(&sc->sc_mib_mac.aDesired_ESS_ID[2], buf,
|
|
ifd->ifd_len) == 0)
|
|
break;
|
|
memset(sc->sc_mib_mac.aDesired_ESS_ID, 0, AWI_ESS_ID_SIZE);
|
|
sc->sc_mib_mac.aDesired_ESS_ID[0] = IEEE80211_ELEMID_SSID;
|
|
sc->sc_mib_mac.aDesired_ESS_ID[1] = ifd->ifd_len;
|
|
memcpy(&sc->sc_mib_mac.aDesired_ESS_ID[2], buf, ifd->ifd_len);
|
|
if (sc->sc_enabled) {
|
|
awi_stop(sc);
|
|
error = awi_init(sc);
|
|
}
|
|
break;
|
|
case AWICTL_CESSID:
|
|
error = EINVAL;
|
|
break;
|
|
case AWICTL_MODE:
|
|
switch (buf[0]) {
|
|
case AWICTL_MODE_INFRA:
|
|
sc->sc_mib_local.Network_Mode = 1;
|
|
sc->sc_no_bssid = 0;
|
|
break;
|
|
case AWICTL_MODE_ADHOC:
|
|
sc->sc_mib_local.Network_Mode = 0;
|
|
sc->sc_no_bssid = 0;
|
|
break;
|
|
case AWICTL_MODE_NOBSSID:
|
|
sc->sc_mib_local.Network_Mode = 0;
|
|
sc->sc_no_bssid = 1;
|
|
break;
|
|
default:
|
|
return EINVAL;
|
|
}
|
|
if (sc->sc_enabled) {
|
|
awi_stop(sc);
|
|
error = awi_init(sc);
|
|
}
|
|
break;
|
|
default:
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
return error;
|
|
}
|
|
|
|
int
|
|
awi_intr(arg)
|
|
void *arg;
|
|
{
|
|
struct awi_softc *sc = arg;
|
|
u_int16_t status;
|
|
int error, handled = 0, ocansleep;
|
|
|
|
if (sc->sc_invalid)
|
|
return 0;
|
|
|
|
am79c930_gcr_setbits(&sc->sc_chip,
|
|
AM79C930_GCR_DISPWDN | AM79C930_GCR_ECINT);
|
|
awi_write_1(sc, AWI_DIS_PWRDN, 1);
|
|
ocansleep = sc->sc_cansleep;
|
|
sc->sc_cansleep = 0;
|
|
|
|
for (;;) {
|
|
error = awi_intr_lock(sc);
|
|
if (error)
|
|
break;
|
|
status = awi_read_1(sc, AWI_INTSTAT);
|
|
awi_write_1(sc, AWI_INTSTAT, 0);
|
|
awi_write_1(sc, AWI_INTSTAT, 0);
|
|
status |= awi_read_1(sc, AWI_INTSTAT2) << 8;
|
|
awi_write_1(sc, AWI_INTSTAT2, 0);
|
|
DELAY(10);
|
|
awi_intr_unlock(sc);
|
|
if (!sc->sc_cmd_inprog)
|
|
status &= ~AWI_INT_CMD; /* make sure */
|
|
if (status == 0)
|
|
break;
|
|
handled = 1;
|
|
if (status & AWI_INT_RX)
|
|
awi_rxint(sc);
|
|
if (status & AWI_INT_TX)
|
|
awi_txint(sc);
|
|
if (status & AWI_INT_CMD)
|
|
awi_cmd_done(sc);
|
|
if (status & AWI_INT_SCAN_CMPLT) {
|
|
if (sc->sc_status == AWI_ST_SCAN &&
|
|
sc->sc_mgt_timer > 0)
|
|
(void)awi_next_scan(sc);
|
|
}
|
|
}
|
|
sc->sc_cansleep = ocansleep;
|
|
am79c930_gcr_clearbits(&sc->sc_chip, AM79C930_GCR_DISPWDN);
|
|
awi_write_1(sc, AWI_DIS_PWRDN, 0);
|
|
return handled;
|
|
}
|
|
|
|
static int
|
|
awi_init(sc)
|
|
struct awi_softc *sc;
|
|
{
|
|
int error, ostatus;
|
|
int n;
|
|
struct ifnet *ifp = sc->sc_ifp;
|
|
#ifdef __FreeBSD__
|
|
struct ifmultiaddr *ifma;
|
|
#else
|
|
struct ether_multi *enm;
|
|
struct ether_multistep step;
|
|
#endif
|
|
|
|
n = 0;
|
|
ifp->if_flags |= IFF_ALLMULTI;
|
|
sc->sc_mib_local.Accept_All_Multicast_Dis = 0;
|
|
if (ifp->if_flags & IFF_PROMISC) {
|
|
sc->sc_mib_mac.aPromiscuous_Enable = 1;
|
|
goto set_mib;
|
|
}
|
|
sc->sc_mib_mac.aPromiscuous_Enable = 0;
|
|
ifp->if_flags &= ~IFF_ALLMULTI;
|
|
#ifdef __FreeBSD__
|
|
if (ifp->if_amcount != 0)
|
|
goto set_mib;
|
|
for (ifma = LIST_FIRST(&ifp->if_multiaddrs); ifma != NULL;
|
|
ifma = LIST_NEXT(ifma, ifma_link)) {
|
|
if (ifma->ifma_addr->sa_family != AF_LINK)
|
|
continue;
|
|
if (n == AWI_GROUP_ADDR_SIZE)
|
|
goto set_mib;
|
|
memcpy(sc->sc_mib_addr.aGroup_Addresses[n],
|
|
LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
|
|
ETHER_ADDR_LEN);
|
|
n++;
|
|
}
|
|
#else
|
|
ETHER_FIRST_MULTI(step, &sc->sc_ec, enm);
|
|
while (enm != NULL) {
|
|
if (n == AWI_GROUP_ADDR_SIZE ||
|
|
memcmp(enm->enm_addrlo, enm->enm_addrhi, ETHER_ADDR_LEN)
|
|
!= 0)
|
|
goto set_mib;
|
|
memcpy(sc->sc_mib_addr.aGroup_Addresses[n], enm->enm_addrlo,
|
|
ETHER_ADDR_LEN);
|
|
n++;
|
|
ETHER_NEXT_MULTI(step, enm);
|
|
}
|
|
#endif
|
|
ifp->if_flags &= ~IFF_ALLMULTI;
|
|
sc->sc_mib_local.Accept_All_Multicast_Dis = 1;
|
|
|
|
set_mib:
|
|
if (!sc->sc_enabled) {
|
|
sc->sc_enabled = 1;
|
|
if (sc->sc_enable)
|
|
(*sc->sc_enable)(sc);
|
|
sc->sc_status = AWI_ST_INIT;
|
|
error = awi_init_hw(sc);
|
|
if (error)
|
|
return error;
|
|
}
|
|
ostatus = sc->sc_status;
|
|
sc->sc_status = AWI_ST_INIT;
|
|
if ((error = awi_mib(sc, AWI_CMD_SET_MIB, AWI_MIB_LOCAL)) != 0 ||
|
|
(error = awi_mib(sc, AWI_CMD_SET_MIB, AWI_MIB_ADDR)) != 0 ||
|
|
(error = awi_mib(sc, AWI_CMD_SET_MIB, AWI_MIB_MAC)) != 0 ||
|
|
(error = awi_mib(sc, AWI_CMD_SET_MIB, AWI_MIB_MGT)) != 0 ||
|
|
(error = awi_mib(sc, AWI_CMD_SET_MIB, AWI_MIB_PHY)) != 0) {
|
|
awi_stop(sc);
|
|
return error;
|
|
}
|
|
if (ifp->if_flags & IFF_RUNNING)
|
|
sc->sc_status = AWI_ST_RUNNING;
|
|
else {
|
|
if (ostatus == AWI_ST_INIT) {
|
|
error = awi_init_txrx(sc);
|
|
if (error)
|
|
return error;
|
|
}
|
|
error = awi_start_scan(sc);
|
|
}
|
|
return error;
|
|
}
|
|
|
|
static void
|
|
awi_stop(sc)
|
|
struct awi_softc *sc;
|
|
{
|
|
struct ifnet *ifp = sc->sc_ifp;
|
|
struct awi_bss *bp;
|
|
struct mbuf *m;
|
|
|
|
sc->sc_status = AWI_ST_INIT;
|
|
if (!sc->sc_invalid) {
|
|
(void)awi_cmd_wait(sc);
|
|
if (sc->sc_mib_local.Network_Mode &&
|
|
sc->sc_status > AWI_ST_AUTH)
|
|
awi_send_deauth(sc);
|
|
awi_stop_txrx(sc);
|
|
}
|
|
ifp->if_flags &= ~(IFF_RUNNING|IFF_OACTIVE);
|
|
ifp->if_timer = 0;
|
|
sc->sc_tx_timer = sc->sc_rx_timer = sc->sc_mgt_timer = 0;
|
|
for (;;) {
|
|
IF_DEQUEUE(&sc->sc_mgtq, m);
|
|
if (m == NULL)
|
|
break;
|
|
m_freem(m);
|
|
}
|
|
for (;;) {
|
|
IF_DEQUEUE(&ifp->if_snd, m);
|
|
if (m == NULL)
|
|
break;
|
|
m_freem(m);
|
|
}
|
|
while ((bp = TAILQ_FIRST(&sc->sc_scan)) != NULL)
|
|
TAILQ_REMOVE(&sc->sc_scan, bp, list);
|
|
}
|
|
|
|
static void
|
|
awi_watchdog(ifp)
|
|
struct ifnet *ifp;
|
|
{
|
|
struct awi_softc *sc = ifp->if_softc;
|
|
int ocansleep;
|
|
|
|
if (sc->sc_invalid) {
|
|
ifp->if_timer = 0;
|
|
return;
|
|
}
|
|
|
|
ocansleep = sc->sc_cansleep;
|
|
sc->sc_cansleep = 0;
|
|
if (sc->sc_tx_timer && --sc->sc_tx_timer == 0) {
|
|
printf("%s: transmit timeout\n", sc->sc_dev.dv_xname);
|
|
awi_txint(sc);
|
|
}
|
|
if (sc->sc_rx_timer && --sc->sc_rx_timer == 0) {
|
|
printf("%s: no recent beacons from %s; rescanning\n",
|
|
sc->sc_dev.dv_xname, ether_sprintf(sc->sc_bss.bssid));
|
|
awi_start_scan(sc);
|
|
}
|
|
if (sc->sc_mgt_timer && --sc->sc_mgt_timer == 0) {
|
|
switch (sc->sc_status) {
|
|
case AWI_ST_SCAN:
|
|
awi_stop_scan(sc);
|
|
break;
|
|
case AWI_ST_AUTH:
|
|
case AWI_ST_ASSOC:
|
|
/* restart scan */
|
|
awi_start_scan(sc);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (sc->sc_tx_timer == 0 && sc->sc_rx_timer == 0 &&
|
|
sc->sc_mgt_timer == 0)
|
|
ifp->if_timer = 0;
|
|
else
|
|
ifp->if_timer = 1;
|
|
sc->sc_cansleep = ocansleep;
|
|
}
|
|
|
|
static void
|
|
awi_start(ifp)
|
|
struct ifnet *ifp;
|
|
{
|
|
struct awi_softc *sc = ifp->if_softc;
|
|
struct mbuf *m0, *m;
|
|
u_int32_t txd, frame, ntxd;
|
|
u_int8_t rate;
|
|
int len, sent = 0;
|
|
|
|
for (;;) {
|
|
txd = sc->sc_txnext;
|
|
IF_DEQUEUE(&sc->sc_mgtq, m0);
|
|
if (m0 != NULL) {
|
|
if (awi_next_txd(sc, m0->m_pkthdr.len, &frame, &ntxd)) {
|
|
IF_PREPEND(&sc->sc_mgtq, m0);
|
|
ifp->if_flags |= IFF_OACTIVE;
|
|
break;
|
|
}
|
|
} else {
|
|
if (!(ifp->if_flags & IFF_RUNNING))
|
|
break;
|
|
IF_DEQUEUE(&ifp->if_snd, m0);
|
|
if (m0 == NULL)
|
|
break;
|
|
if (awi_next_txd(sc, m0->m_pkthdr.len +
|
|
sizeof(struct ieee80211_frame), &frame, &ntxd)) {
|
|
IF_PREPEND(&ifp->if_snd, m0);
|
|
ifp->if_flags |= IFF_OACTIVE;
|
|
break;
|
|
}
|
|
AWI_BPF_MTAP(sc, m0, AWI_BPF_NORM);
|
|
m0 = awi_fix_txhdr(sc, m0);
|
|
if (m0 == NULL) {
|
|
ifp->if_oerrors++;
|
|
continue;
|
|
}
|
|
ifp->if_opackets++;
|
|
}
|
|
AWI_BPF_MTAP(sc, m0, AWI_BPF_RAW);
|
|
len = 0;
|
|
for (m = m0; m != NULL; m = m->m_next) {
|
|
awi_write_bytes(sc, frame + len, mtod(m, u_int8_t *),
|
|
m->m_len);
|
|
len += m->m_len;
|
|
}
|
|
m_freem(m0);
|
|
rate = sc->sc_tx_rate; /*XXX*/
|
|
awi_write_1(sc, ntxd + AWI_TXD_STATE, 0);
|
|
awi_write_4(sc, txd + AWI_TXD_START, frame);
|
|
awi_write_4(sc, txd + AWI_TXD_NEXT, ntxd);
|
|
awi_write_4(sc, txd + AWI_TXD_LENGTH, len);
|
|
awi_write_1(sc, txd + AWI_TXD_RATE, rate);
|
|
awi_write_4(sc, txd + AWI_TXD_NDA, 0);
|
|
awi_write_4(sc, txd + AWI_TXD_NRA, 0);
|
|
awi_write_1(sc, txd + AWI_TXD_STATE, AWI_TXD_ST_OWN);
|
|
sc->sc_txnext = ntxd;
|
|
sent++;
|
|
}
|
|
if (sent) {
|
|
if (sc->sc_tx_timer == 0)
|
|
sc->sc_tx_timer = 5;
|
|
ifp->if_timer = 1;
|
|
#ifdef AWI_DEBUG
|
|
if (awi_verbose)
|
|
printf("awi_start: sent %d txdone %d txnext %d txbase %d txend %d\n", sent, sc->sc_txdone, sc->sc_txnext, sc->sc_txbase, sc->sc_txend);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static void
|
|
awi_txint(sc)
|
|
struct awi_softc *sc;
|
|
{
|
|
struct ifnet *ifp = sc->sc_ifp;
|
|
u_int8_t flags;
|
|
|
|
while (sc->sc_txdone != sc->sc_txnext) {
|
|
flags = awi_read_1(sc, sc->sc_txdone + AWI_TXD_STATE);
|
|
if ((flags & AWI_TXD_ST_OWN) || !(flags & AWI_TXD_ST_DONE))
|
|
break;
|
|
if (flags & AWI_TXD_ST_ERROR)
|
|
ifp->if_oerrors++;
|
|
sc->sc_txdone = awi_read_4(sc, sc->sc_txdone + AWI_TXD_NEXT) &
|
|
0x7fff;
|
|
}
|
|
sc->sc_tx_timer = 0;
|
|
ifp->if_flags &= ~IFF_OACTIVE;
|
|
#ifdef AWI_DEBUG
|
|
if (awi_verbose)
|
|
printf("awi_txint: txdone %d txnext %d txbase %d txend %d\n",
|
|
sc->sc_txdone, sc->sc_txnext, sc->sc_txbase, sc->sc_txend);
|
|
#endif
|
|
awi_start(ifp);
|
|
}
|
|
|
|
static struct mbuf *
|
|
awi_fix_txhdr(sc, m0)
|
|
struct awi_softc *sc;
|
|
struct mbuf *m0;
|
|
{
|
|
struct ether_header eh;
|
|
struct ieee80211_frame *wh;
|
|
struct llc *llc;
|
|
|
|
if (m0->m_len < sizeof(eh)) {
|
|
m0 = m_pullup(m0, sizeof(eh));
|
|
if (m0 == NULL)
|
|
return NULL;
|
|
}
|
|
memcpy(&eh, mtod(m0, caddr_t), sizeof(eh));
|
|
if (sc->sc_format_llc) {
|
|
m_adj(m0, sizeof(struct ether_header) - sizeof(struct llc));
|
|
llc = mtod(m0, struct llc *);
|
|
llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP;
|
|
llc->llc_control = LLC_UI;
|
|
llc->llc_snap.org_code[0] = llc->llc_snap.org_code[1] =
|
|
llc->llc_snap.org_code[2] = 0;
|
|
llc->llc_snap.ether_type = eh.ether_type;
|
|
}
|
|
M_PREPEND(m0, sizeof(struct ieee80211_frame), M_DONTWAIT);
|
|
if (m0 == NULL)
|
|
return NULL;
|
|
wh = mtod(m0, struct ieee80211_frame *);
|
|
|
|
wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA;
|
|
LE_WRITE_2(wh->i_dur, 0);
|
|
LE_WRITE_2(wh->i_seq, 0);
|
|
if (sc->sc_mib_local.Network_Mode) {
|
|
wh->i_fc[1] = IEEE80211_FC1_DIR_TODS;
|
|
memcpy(wh->i_addr1, sc->sc_bss.bssid, ETHER_ADDR_LEN);
|
|
memcpy(wh->i_addr2, eh.ether_shost, ETHER_ADDR_LEN);
|
|
memcpy(wh->i_addr3, eh.ether_dhost, ETHER_ADDR_LEN);
|
|
} else {
|
|
wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
|
|
memcpy(wh->i_addr1, eh.ether_dhost, ETHER_ADDR_LEN);
|
|
memcpy(wh->i_addr2, eh.ether_shost, ETHER_ADDR_LEN);
|
|
memcpy(wh->i_addr3, sc->sc_bss.bssid, ETHER_ADDR_LEN);
|
|
}
|
|
return m0;
|
|
}
|
|
|
|
static struct mbuf *
|
|
awi_fix_rxhdr(sc, m0)
|
|
struct awi_softc *sc;
|
|
struct mbuf *m0;
|
|
{
|
|
struct ieee80211_frame wh;
|
|
struct ether_header *eh;
|
|
struct llc *llc;
|
|
|
|
if (m0->m_len < sizeof(wh)) {
|
|
m_freem(m0);
|
|
return NULL;
|
|
}
|
|
llc = (struct llc *)(mtod(m0, caddr_t) + sizeof(wh));
|
|
if (llc->llc_dsap == LLC_SNAP_LSAP &&
|
|
llc->llc_ssap == LLC_SNAP_LSAP &&
|
|
llc->llc_control == LLC_UI &&
|
|
llc->llc_snap.org_code[0] == 0 &&
|
|
llc->llc_snap.org_code[1] == 0 &&
|
|
llc->llc_snap.org_code[2] == 0) {
|
|
memcpy(&wh, mtod(m0, caddr_t), sizeof(wh));
|
|
m_adj(m0, sizeof(wh) + sizeof(*llc) - sizeof(*eh));
|
|
eh = mtod(m0, struct ether_header *);
|
|
switch (wh.i_fc[1] & IEEE80211_FC1_DIR_MASK) {
|
|
case IEEE80211_FC1_DIR_NODS:
|
|
memcpy(eh->ether_dhost, wh.i_addr1, ETHER_ADDR_LEN);
|
|
memcpy(eh->ether_shost, wh.i_addr2, ETHER_ADDR_LEN);
|
|
break;
|
|
case IEEE80211_FC1_DIR_TODS:
|
|
memcpy(eh->ether_dhost, wh.i_addr3, ETHER_ADDR_LEN);
|
|
memcpy(eh->ether_shost, wh.i_addr2, ETHER_ADDR_LEN);
|
|
break;
|
|
case IEEE80211_FC1_DIR_FROMDS:
|
|
memcpy(eh->ether_dhost, wh.i_addr1, ETHER_ADDR_LEN);
|
|
memcpy(eh->ether_shost, wh.i_addr3, ETHER_ADDR_LEN);
|
|
break;
|
|
case IEEE80211_FC1_DIR_DSTODS:
|
|
m_freem(m0);
|
|
return NULL;
|
|
}
|
|
} else {
|
|
/* assuming ethernet encapsulation, just strip 802.11 header */
|
|
m_adj(m0, sizeof(wh));
|
|
}
|
|
return m0;
|
|
}
|
|
|
|
static void
|
|
awi_input(sc, m, rxts, rssi)
|
|
struct awi_softc *sc;
|
|
struct mbuf *m;
|
|
u_int32_t rxts;
|
|
u_int8_t rssi;
|
|
{
|
|
struct ifnet *ifp = sc->sc_ifp;
|
|
struct ieee80211_frame *wh;
|
|
#ifndef __NetBSD__
|
|
struct ether_header *eh;
|
|
#endif
|
|
|
|
AWI_BPF_MTAP(sc, m, AWI_BPF_RAW);
|
|
wh = mtod(m, struct ieee80211_frame *);
|
|
if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
|
|
IEEE80211_FC0_VERSION_0) {
|
|
printf("%s; receive packet with wrong version: %x\n",
|
|
sc->sc_dev.dv_xname, wh->i_fc[0]);
|
|
m_freem(m);
|
|
return;
|
|
}
|
|
#ifdef AWI_DEBUG
|
|
if (awi_dump)
|
|
awi_dump_pkt(sc, m, rssi);
|
|
#endif
|
|
|
|
if ((sc->sc_mib_local.Network_Mode || !sc->sc_no_bssid) &&
|
|
sc->sc_status == AWI_ST_RUNNING) {
|
|
if (memcmp(wh->i_addr2, sc->sc_bss.bssid, ETHER_ADDR_LEN) == 0) {
|
|
sc->sc_rx_timer = 10;
|
|
sc->sc_bss.rssi = rssi;
|
|
}
|
|
}
|
|
switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) {
|
|
case IEEE80211_FC0_TYPE_DATA:
|
|
if (sc->sc_mib_local.Network_Mode) {
|
|
if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) !=
|
|
IEEE80211_FC1_DIR_FROMDS) {
|
|
m_freem(m);
|
|
return;
|
|
}
|
|
} else {
|
|
if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) !=
|
|
IEEE80211_FC1_DIR_NODS) {
|
|
m_freem(m);
|
|
return;
|
|
}
|
|
}
|
|
m = awi_fix_rxhdr(sc, m);
|
|
if (m == NULL) {
|
|
ifp->if_ierrors++;
|
|
break;
|
|
}
|
|
ifp->if_ipackets++;
|
|
AWI_BPF_MTAP(sc, m, AWI_BPF_NORM);
|
|
#ifdef __NetBSD__
|
|
m->m_flags |= M_HASFCS;
|
|
(*ifp->if_input)(ifp, m);
|
|
#else
|
|
eh = mtod(m, struct ether_header *);
|
|
m_adj(m, sizeof(*eh));
|
|
m_adj(m, -ETHER_CRC_LEN);
|
|
ether_input(ifp, eh, m);
|
|
#endif
|
|
break;
|
|
case IEEE80211_FC0_TYPE_MGT:
|
|
if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) !=
|
|
IEEE80211_FC1_DIR_NODS) {
|
|
m_freem(m);
|
|
return;
|
|
}
|
|
switch (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) {
|
|
case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
|
|
case IEEE80211_FC0_SUBTYPE_BEACON:
|
|
awi_recv_beacon(sc, m, rxts, rssi);
|
|
break;
|
|
case IEEE80211_FC0_SUBTYPE_AUTH:
|
|
awi_recv_auth(sc, m);
|
|
break;
|
|
case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
|
|
case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
|
|
awi_recv_asresp(sc, m);
|
|
break;
|
|
case IEEE80211_FC0_SUBTYPE_DEAUTH:
|
|
if (sc->sc_mib_local.Network_Mode)
|
|
awi_send_auth(sc);
|
|
break;
|
|
case IEEE80211_FC0_SUBTYPE_DISASSOC:
|
|
if (sc->sc_mib_local.Network_Mode)
|
|
awi_send_asreq(sc, 1);
|
|
break;
|
|
}
|
|
m_freem(m);
|
|
break;
|
|
case IEEE80211_FC0_TYPE_CTL:
|
|
default:
|
|
/* should not come here */
|
|
m_freem(m);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
awi_rxint(sc)
|
|
struct awi_softc *sc;
|
|
{
|
|
u_int8_t state, rate, rssi;
|
|
u_int16_t len;
|
|
u_int32_t frame, next, rxts, rxoff;
|
|
struct mbuf *m;
|
|
|
|
rxoff = sc->sc_rxdoff;
|
|
for (;;) {
|
|
state = awi_read_1(sc, rxoff + AWI_RXD_HOST_DESC_STATE);
|
|
if (state & AWI_RXD_ST_OWN)
|
|
break;
|
|
if (!(state & AWI_RXD_ST_CONSUMED)) {
|
|
if (state & AWI_RXD_ST_RXERROR)
|
|
sc->sc_ifp->if_ierrors++;
|
|
else {
|
|
len = awi_read_2(sc, rxoff + AWI_RXD_LEN);
|
|
rate = awi_read_1(sc, rxoff + AWI_RXD_RATE);
|
|
rssi = awi_read_1(sc, rxoff + AWI_RXD_RSSI);
|
|
frame = awi_read_4(sc, rxoff + AWI_RXD_START_FRAME) & 0x7fff;
|
|
rxts = awi_read_4(sc, rxoff + AWI_RXD_LOCALTIME);
|
|
m = awi_devget(sc, frame, len);
|
|
if (state & AWI_RXD_ST_LF)
|
|
awi_input(sc, m, rxts, rssi);
|
|
else
|
|
sc->sc_rxpend = m;
|
|
}
|
|
state |= AWI_RXD_ST_CONSUMED;
|
|
awi_write_1(sc, rxoff + AWI_RXD_HOST_DESC_STATE, state);
|
|
}
|
|
next = awi_read_4(sc, rxoff + AWI_RXD_NEXT);
|
|
if (next & AWI_RXD_NEXT_LAST)
|
|
break;
|
|
/* make sure the next pointer is correct */
|
|
if (next != awi_read_4(sc, rxoff + AWI_RXD_NEXT))
|
|
break;
|
|
state |= AWI_RXD_ST_OWN;
|
|
awi_write_1(sc, rxoff + AWI_RXD_HOST_DESC_STATE, state);
|
|
rxoff = next & 0x7fff;
|
|
}
|
|
sc->sc_rxdoff = rxoff;
|
|
}
|
|
|
|
struct mbuf *
|
|
awi_devget(sc, off, len)
|
|
struct awi_softc *sc;
|
|
u_int32_t off;
|
|
u_int16_t len;
|
|
{
|
|
struct mbuf *m;
|
|
struct mbuf *top, **mp = ⊤
|
|
u_int tlen;
|
|
|
|
top = sc->sc_rxpend;
|
|
if (top != NULL) {
|
|
sc->sc_rxpend = NULL;
|
|
top->m_pkthdr.len += len;
|
|
while ((m = *mp) != NULL)
|
|
mp = &m->m_next;
|
|
if (m->m_flags & M_EXT)
|
|
tlen = m->m_ext.ext_size;
|
|
else if (m->m_flags & M_PKTHDR)
|
|
tlen = MHLEN;
|
|
else
|
|
tlen = MLEN;
|
|
tlen -= m->m_len;
|
|
if (tlen > len)
|
|
tlen = len;
|
|
awi_read_bytes(sc, off, mtod(m, u_int8_t *) + m->m_len, tlen);
|
|
off += tlen;
|
|
len -= tlen;
|
|
}
|
|
|
|
while (len > 0) {
|
|
if (top == NULL) {
|
|
MGETHDR(m, M_DONTWAIT, MT_DATA);
|
|
if (m == NULL)
|
|
return NULL;
|
|
m->m_pkthdr.rcvif = sc->sc_ifp;
|
|
m->m_pkthdr.len = len;
|
|
m->m_len = MHLEN;
|
|
} else {
|
|
MGET(m, M_DONTWAIT, MT_DATA);
|
|
if (m == NULL) {
|
|
m_freem(top);
|
|
return NULL;
|
|
}
|
|
m->m_len = MLEN;
|
|
}
|
|
if (len >= MINCLSIZE) {
|
|
MCLGET(m, M_DONTWAIT);
|
|
if (m->m_flags & M_EXT)
|
|
m->m_len = m->m_ext.ext_size;
|
|
}
|
|
if (m->m_len > len)
|
|
m->m_len = len;
|
|
awi_read_bytes(sc, off, mtod(m, u_int8_t *), m->m_len);
|
|
off += m->m_len;
|
|
len -= m->m_len;
|
|
*mp = m;
|
|
mp = &m->m_next;
|
|
}
|
|
return top;
|
|
}
|
|
|
|
/*
|
|
* Initialize hardware and start firmware to accept commands.
|
|
* Called everytime after power on firmware.
|
|
*/
|
|
|
|
static int
|
|
awi_init_hw(sc)
|
|
struct awi_softc *sc;
|
|
{
|
|
u_int8_t status;
|
|
u_int16_t intmask;
|
|
int i, error;
|
|
u_int8_t banner[AWI_BANNER_LEN];
|
|
|
|
awi_drvstate(sc, AWI_DRV_RESET);
|
|
|
|
/* reset firmware */
|
|
am79c930_gcr_setbits(&sc->sc_chip, AM79C930_GCR_CORESET);
|
|
DELAY(100);
|
|
awi_write_1(sc, AWI_SELFTEST, 0);
|
|
am79c930_gcr_clearbits(&sc->sc_chip, AM79C930_GCR_CORESET);
|
|
DELAY(100);
|
|
|
|
/* wait for selftest completion */
|
|
for (i = 0; ; i++) {
|
|
if (i >= AWI_SELFTEST_TIMEOUT*hz/1000) {
|
|
printf("%s: failed to complete selftest (timeout)\n",
|
|
sc->sc_dev.dv_xname);
|
|
return ENXIO;
|
|
}
|
|
status = awi_read_1(sc, AWI_SELFTEST);
|
|
if ((status & 0xf0) == 0xf0)
|
|
break;
|
|
if (sc->sc_cansleep) {
|
|
sc->sc_sleep_cnt++;
|
|
(void)tsleep(sc, PWAIT, "awitst", 1);
|
|
sc->sc_sleep_cnt--;
|
|
} else {
|
|
DELAY(1000*1000/hz);
|
|
}
|
|
}
|
|
if (status != AWI_SELFTEST_PASSED) {
|
|
printf("%s: failed to complete selftest (code %x)\n",
|
|
sc->sc_dev.dv_xname, status);
|
|
return ENXIO;
|
|
}
|
|
|
|
/* check banner to confirm firmware write it */
|
|
awi_read_bytes(sc, AWI_BANNER, banner, AWI_BANNER_LEN);
|
|
if (memcmp(banner, "PCnetMobile:", 12) != 0) {
|
|
printf("%s: failed to complete selftest (bad banner)\n",
|
|
sc->sc_dev.dv_xname);
|
|
for (i = 0; i < AWI_BANNER_LEN; i++)
|
|
printf("%s%02x", i ? ":" : "\t", banner[i]);
|
|
printf("\n");
|
|
return ENXIO;
|
|
}
|
|
|
|
/* initializing interrupt */
|
|
error = awi_intr_lock(sc);
|
|
if (error)
|
|
return error;
|
|
intmask = AWI_INT_GROGGY | AWI_INT_SCAN_CMPLT |
|
|
AWI_INT_TX | AWI_INT_RX | AWI_INT_CMD;
|
|
awi_write_1(sc, AWI_INTMASK, ~intmask & 0xff);
|
|
awi_write_1(sc, AWI_INTMASK2, 0);
|
|
awi_write_1(sc, AWI_INTSTAT, 0);
|
|
awi_write_1(sc, AWI_INTSTAT2, 0);
|
|
awi_intr_unlock(sc);
|
|
am79c930_gcr_setbits(&sc->sc_chip, AM79C930_GCR_ENECINT);
|
|
|
|
/* issueing interface test command */
|
|
error = awi_cmd(sc, AWI_CMD_NOP);
|
|
if (error) {
|
|
printf("%s: failed to complete selftest", sc->sc_dev.dv_xname);
|
|
if (error != EWOULDBLOCK)
|
|
printf(" (error %d)\n", error);
|
|
else if (sc->sc_cansleep)
|
|
printf(" (lost interrupt)\n");
|
|
else
|
|
printf(" (command timeout)\n");
|
|
}
|
|
return error;
|
|
}
|
|
|
|
/*
|
|
* Extract the factory default MIB value from firmware and assign the driver
|
|
* default value.
|
|
* Called once at attaching the interface.
|
|
*/
|
|
|
|
static int
|
|
awi_init_mibs(sc)
|
|
struct awi_softc *sc;
|
|
{
|
|
int i, error;
|
|
u_int8_t *rate;
|
|
|
|
if ((error = awi_mib(sc, AWI_CMD_GET_MIB, AWI_MIB_LOCAL)) != 0 ||
|
|
(error = awi_mib(sc, AWI_CMD_GET_MIB, AWI_MIB_ADDR)) != 0 ||
|
|
(error = awi_mib(sc, AWI_CMD_GET_MIB, AWI_MIB_MAC)) != 0 ||
|
|
(error = awi_mib(sc, AWI_CMD_GET_MIB, AWI_MIB_MGT)) != 0 ||
|
|
(error = awi_mib(sc, AWI_CMD_GET_MIB, AWI_MIB_PHY)) != 0) {
|
|
printf("%s: failed to get default mib value (error %d)\n",
|
|
sc->sc_dev.dv_xname, error);
|
|
return error;
|
|
}
|
|
|
|
rate = sc->sc_mib_phy.aSuprt_Data_Rates;
|
|
sc->sc_tx_rate = AWI_RATE_1MBIT;
|
|
for (i = 0; i < rate[1]; i++) {
|
|
if (AWI_80211_RATE(rate[2 + i]) > sc->sc_tx_rate)
|
|
sc->sc_tx_rate = AWI_80211_RATE(rate[2 + i]);
|
|
}
|
|
awi_init_region(sc);
|
|
memset(&sc->sc_mib_mac.aDesired_ESS_ID, 0, AWI_ESS_ID_SIZE);
|
|
sc->sc_mib_mac.aDesired_ESS_ID[0] = IEEE80211_ELEMID_SSID;
|
|
sc->sc_mib_local.Fragmentation_Dis = 1;
|
|
sc->sc_mib_local.Accept_All_Multicast_Dis = 1;
|
|
|
|
/* allocate buffers */
|
|
sc->sc_txbase = AWI_BUFFERS;
|
|
sc->sc_txend = sc->sc_txbase +
|
|
(AWI_TXD_SIZE + sizeof(struct ieee80211_frame) +
|
|
sizeof(struct ether_header) + ETHERMTU) * AWI_NTXBUFS;
|
|
LE_WRITE_4(&sc->sc_mib_local.Tx_Buffer_Offset, sc->sc_txbase);
|
|
LE_WRITE_4(&sc->sc_mib_local.Tx_Buffer_Size,
|
|
sc->sc_txend - sc->sc_txbase);
|
|
LE_WRITE_4(&sc->sc_mib_local.Rx_Buffer_Offset, sc->sc_txend);
|
|
LE_WRITE_4(&sc->sc_mib_local.Rx_Buffer_Size,
|
|
AWI_BUFFERS_END - sc->sc_txend);
|
|
sc->sc_mib_local.Network_Mode = 1;
|
|
sc->sc_mib_local.Acting_as_AP = 0;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Start transmitter and receiver of firmware
|
|
* Called after awi_init_hw() to start operation.
|
|
*/
|
|
|
|
static int
|
|
awi_init_txrx(sc)
|
|
struct awi_softc *sc;
|
|
{
|
|
int error;
|
|
|
|
/* start transmitter */
|
|
sc->sc_txdone = sc->sc_txnext = sc->sc_txbase;
|
|
awi_write_4(sc, sc->sc_txbase + AWI_TXD_START, 0);
|
|
awi_write_4(sc, sc->sc_txbase + AWI_TXD_NEXT, 0);
|
|
awi_write_4(sc, sc->sc_txbase + AWI_TXD_LENGTH, 0);
|
|
awi_write_1(sc, sc->sc_txbase + AWI_TXD_RATE, 0);
|
|
awi_write_4(sc, sc->sc_txbase + AWI_TXD_NDA, 0);
|
|
awi_write_4(sc, sc->sc_txbase + AWI_TXD_NRA, 0);
|
|
awi_write_1(sc, sc->sc_txbase + AWI_TXD_STATE, 0);
|
|
awi_write_4(sc, AWI_CMD_PARAMS+AWI_CA_TX_DATA, sc->sc_txbase);
|
|
awi_write_4(sc, AWI_CMD_PARAMS+AWI_CA_TX_MGT, 0);
|
|
awi_write_4(sc, AWI_CMD_PARAMS+AWI_CA_TX_BCAST, 0);
|
|
awi_write_4(sc, AWI_CMD_PARAMS+AWI_CA_TX_PS, 0);
|
|
awi_write_4(sc, AWI_CMD_PARAMS+AWI_CA_TX_CF, 0);
|
|
error = awi_cmd(sc, AWI_CMD_INIT_TX);
|
|
if (error)
|
|
return error;
|
|
|
|
/* start receiver */
|
|
if (sc->sc_rxpend) {
|
|
m_freem(sc->sc_rxpend);
|
|
sc->sc_rxpend = NULL;
|
|
}
|
|
error = awi_cmd(sc, AWI_CMD_INIT_RX);
|
|
if (error)
|
|
return error;
|
|
sc->sc_rxdoff = awi_read_4(sc, AWI_CMD_PARAMS+AWI_CA_IRX_DATA_DESC);
|
|
sc->sc_rxmoff = awi_read_4(sc, AWI_CMD_PARAMS+AWI_CA_IRX_PS_DESC);
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
awi_stop_txrx(sc)
|
|
struct awi_softc *sc;
|
|
{
|
|
|
|
(void)awi_cmd(sc, AWI_CMD_KILL_RX);
|
|
awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_FTX_DATA, 1);
|
|
awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_FTX_MGT, 0);
|
|
awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_FTX_BCAST, 0);
|
|
awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_FTX_PS, 0);
|
|
awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_FTX_CF, 0);
|
|
(void)awi_cmd(sc, AWI_CMD_FLUSH_TX);
|
|
}
|
|
|
|
static int
|
|
awi_init_region(sc)
|
|
struct awi_softc *sc;
|
|
{
|
|
|
|
if (sc->sc_mib_phy.IEEE_PHY_Type == AWI_PHY_TYPE_FH) {
|
|
switch (sc->sc_mib_phy.aCurrent_Reg_Domain) {
|
|
case AWI_REG_DOMAIN_US:
|
|
case AWI_REG_DOMAIN_CA:
|
|
case AWI_REG_DOMAIN_EU:
|
|
sc->sc_scan_min = 0;
|
|
sc->sc_scan_max = 77;
|
|
break;
|
|
case AWI_REG_DOMAIN_ES:
|
|
sc->sc_scan_min = 0;
|
|
sc->sc_scan_max = 26;
|
|
break;
|
|
case AWI_REG_DOMAIN_FR:
|
|
sc->sc_scan_min = 0;
|
|
sc->sc_scan_max = 32;
|
|
break;
|
|
case AWI_REG_DOMAIN_JP:
|
|
sc->sc_scan_min = 6;
|
|
sc->sc_scan_max = 17;
|
|
break;
|
|
default:
|
|
return EINVAL;
|
|
}
|
|
sc->sc_scan_cur = sc->sc_scan_min;
|
|
sc->sc_scan_set = sc->sc_scan_cur % 3 + 1;
|
|
} else {
|
|
switch (sc->sc_mib_phy.aCurrent_Reg_Domain) {
|
|
case AWI_REG_DOMAIN_US:
|
|
case AWI_REG_DOMAIN_CA:
|
|
sc->sc_scan_min = 1;
|
|
sc->sc_scan_max = 11;
|
|
sc->sc_scan_cur = 3;
|
|
break;
|
|
case AWI_REG_DOMAIN_EU:
|
|
sc->sc_scan_min = 1;
|
|
sc->sc_scan_max = 13;
|
|
sc->sc_scan_cur = 3;
|
|
break;
|
|
case AWI_REG_DOMAIN_ES:
|
|
sc->sc_scan_min = 10;
|
|
sc->sc_scan_max = 11;
|
|
sc->sc_scan_cur = 10;
|
|
break;
|
|
case AWI_REG_DOMAIN_FR:
|
|
sc->sc_scan_min = 10;
|
|
sc->sc_scan_max = 13;
|
|
sc->sc_scan_cur = 10;
|
|
break;
|
|
case AWI_REG_DOMAIN_JP:
|
|
sc->sc_scan_min = 14;
|
|
sc->sc_scan_max = 14;
|
|
sc->sc_scan_cur = 14;
|
|
break;
|
|
default:
|
|
return EINVAL;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
awi_start_scan(sc)
|
|
struct awi_softc *sc;
|
|
{
|
|
int error = 0;
|
|
|
|
if (!sc->sc_mib_local.Network_Mode && sc->sc_no_bssid) {
|
|
memset(&sc->sc_bss, 0, sizeof(sc->sc_bss));
|
|
sc->sc_bss.rxtime = 0;
|
|
memcpy(sc->sc_bss.essid, &sc->sc_mib_mac.aDesired_ESS_ID,
|
|
sizeof(sc->sc_bss.essid));
|
|
if (sc->sc_mib_phy.IEEE_PHY_Type == AWI_PHY_TYPE_FH) {
|
|
sc->sc_bss.chanset = sc->sc_scan_set;
|
|
sc->sc_bss.pattern = sc->sc_scan_cur;
|
|
sc->sc_bss.index = 1;
|
|
sc->sc_bss.dwell_time = 19; /*XXX*/
|
|
} else
|
|
sc->sc_bss.chanset = sc->sc_scan_cur;
|
|
sc->sc_status = AWI_ST_SETSS;
|
|
error = awi_set_ss(sc);
|
|
} else {
|
|
if (sc->sc_mib_local.Network_Mode)
|
|
awi_drvstate(sc, AWI_DRV_INFSC);
|
|
else
|
|
awi_drvstate(sc, AWI_DRV_ADHSC);
|
|
sc->sc_start_bss = 0;
|
|
sc->sc_active_scan = 1;
|
|
sc->sc_mgt_timer = AWI_ASCAN_WAIT / 1000;
|
|
sc->sc_ifp->if_timer = 1;
|
|
sc->sc_status = AWI_ST_SCAN;
|
|
error = awi_cmd_scan(sc);
|
|
}
|
|
return error;
|
|
}
|
|
|
|
static int
|
|
awi_next_scan(sc)
|
|
struct awi_softc *sc;
|
|
{
|
|
int error;
|
|
|
|
for (;;) {
|
|
/*
|
|
* The pattern parameter for FH phy should be incremented
|
|
* by 3. But BayStack 650 Access Points apparently always
|
|
* assign hop pattern set parameter to 1 for any pattern.
|
|
* So we try all combinations of pattern/set parameters.
|
|
* Since this causes no error, it may be a bug of
|
|
* PCnetMobile firmware.
|
|
*/
|
|
sc->sc_scan_cur++;
|
|
if (sc->sc_scan_cur > sc->sc_scan_max) {
|
|
sc->sc_scan_cur = sc->sc_scan_min;
|
|
if (sc->sc_mib_phy.IEEE_PHY_Type == AWI_PHY_TYPE_FH)
|
|
sc->sc_scan_set = (sc->sc_scan_set + 1) % 3;
|
|
}
|
|
error = awi_cmd_scan(sc);
|
|
if (error != EINVAL)
|
|
break;
|
|
}
|
|
return error;
|
|
}
|
|
|
|
static void
|
|
awi_stop_scan(sc)
|
|
struct awi_softc *sc;
|
|
{
|
|
struct ifnet *ifp = sc->sc_ifp;
|
|
struct awi_bss *bp, *sbp;
|
|
|
|
bp = TAILQ_FIRST(&sc->sc_scan);
|
|
if (bp == NULL) {
|
|
notfound:
|
|
if (sc->sc_active_scan) {
|
|
if (ifp->if_flags & IFF_DEBUG)
|
|
printf("%s: entering passive scan mode\n",
|
|
sc->sc_dev.dv_xname);
|
|
sc->sc_active_scan = 0;
|
|
}
|
|
sc->sc_mgt_timer = AWI_PSCAN_WAIT / 1000;
|
|
ifp->if_timer = 1;
|
|
(void)awi_next_scan(sc);
|
|
return;
|
|
}
|
|
sbp = NULL;
|
|
for (; bp != NULL; bp = TAILQ_NEXT(bp, list)) {
|
|
if (bp->fails) {
|
|
/*
|
|
* The configuration of the access points may change
|
|
* during my scan. So we retries to associate with
|
|
* it unless there are any suitable AP.
|
|
*/
|
|
if (bp->fails < 3)
|
|
continue;
|
|
bp->fails = 0;
|
|
}
|
|
if (sc->sc_mib_mac.aDesired_ESS_ID[1] != 0 &&
|
|
memcmp(&sc->sc_mib_mac.aDesired_ESS_ID, bp->essid,
|
|
sizeof(bp->essid) != 0))
|
|
continue;
|
|
/*
|
|
* Since the firmware apparently scans not only the specified
|
|
* channel of SCAN command but all available channel within
|
|
* the region, we should filter out unnecessary responses here.
|
|
*/
|
|
if (sc->sc_mib_phy.IEEE_PHY_Type == AWI_PHY_TYPE_FH) {
|
|
if (bp->pattern < sc->sc_scan_min ||
|
|
bp->pattern > sc->sc_scan_max)
|
|
continue;
|
|
} else {
|
|
if (bp->chanset < sc->sc_scan_min ||
|
|
bp->chanset > sc->sc_scan_max)
|
|
continue;
|
|
}
|
|
if (sbp == NULL || bp->rssi > sbp->rssi)
|
|
sbp = bp;
|
|
}
|
|
if (sbp == NULL)
|
|
goto notfound;
|
|
sc->sc_bss = *sbp;
|
|
(void)awi_set_ss(sc);
|
|
}
|
|
|
|
static void
|
|
awi_recv_beacon(sc, m0, rxts, rssi)
|
|
struct awi_softc *sc;
|
|
struct mbuf *m0;
|
|
u_int32_t rxts;
|
|
u_int8_t rssi;
|
|
{
|
|
struct ieee80211_frame *wh;
|
|
struct awi_bss *bp;
|
|
u_int8_t *frame, *eframe;
|
|
u_int8_t *tstamp, *capinfo, *ssid, *rates, *parms;
|
|
u_int16_t bintval;
|
|
|
|
if (sc->sc_status != AWI_ST_SCAN)
|
|
return;
|
|
wh = mtod(m0, struct ieee80211_frame *);
|
|
|
|
frame = (u_int8_t *)&wh[1];
|
|
eframe = mtod(m0, u_int8_t *) + m0->m_len;
|
|
/*
|
|
* XXX:
|
|
* timestamp [8]
|
|
* beacon interval [2]
|
|
* capability information [2]
|
|
* ssid [tlv]
|
|
* supported rates [tlv]
|
|
* parameter set [tlv]
|
|
* ...
|
|
*/
|
|
if (frame + 12 > eframe) {
|
|
#ifdef AWI_DEBUG
|
|
if (awi_verbose)
|
|
printf("awi_recv_beacon: frame too short \n");
|
|
#endif
|
|
return;
|
|
}
|
|
tstamp = frame;
|
|
frame += 8;
|
|
bintval = LE_READ_2(frame);
|
|
frame += 2;
|
|
capinfo = frame;
|
|
frame += 2;
|
|
|
|
if (sc->sc_mib_local.Network_Mode) {
|
|
if (!(capinfo[0] & IEEE80211_CAPINFO_ESS) ||
|
|
(capinfo[0] & IEEE80211_CAPINFO_IBSS)) {
|
|
#ifdef AWI_DEBUG
|
|
if (awi_verbose)
|
|
printf("awi_recv_beacon: non ESS \n");
|
|
#endif
|
|
return;
|
|
}
|
|
} else {
|
|
if ((capinfo[0] & IEEE80211_CAPINFO_ESS) ||
|
|
!(capinfo[0] & IEEE80211_CAPINFO_IBSS)) {
|
|
#ifdef AWI_DEBUG
|
|
if (awi_verbose)
|
|
printf("awi_recv_beacon: non IBSS \n");
|
|
#endif
|
|
return;
|
|
}
|
|
}
|
|
|
|
ssid = rates = parms = NULL;
|
|
while (frame < eframe) {
|
|
switch (*frame) {
|
|
case IEEE80211_ELEMID_SSID:
|
|
ssid = frame;
|
|
break;
|
|
case IEEE80211_ELEMID_RATES:
|
|
rates = frame;
|
|
break;
|
|
case IEEE80211_ELEMID_FHPARMS:
|
|
case IEEE80211_ELEMID_DSPARMS:
|
|
parms = frame;
|
|
break;
|
|
}
|
|
frame += frame[1] + 2;
|
|
}
|
|
if (ssid == NULL || rates == NULL || parms == NULL) {
|
|
#ifdef AWI_DEBUG
|
|
if (awi_verbose)
|
|
printf("awi_recv_beacon: ssid=%p, rates=%p, parms=%p\n",
|
|
ssid, rates, parms);
|
|
#endif
|
|
return;
|
|
}
|
|
if (ssid[1] > IEEE80211_NWID_LEN) {
|
|
#ifdef AWI_DEBUG
|
|
if (awi_verbose)
|
|
printf("awi_recv_beacon: bad ssid len: %d from %s\n",
|
|
ssid[1], ether_sprintf(wh->i_addr2));
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
for (bp = TAILQ_FIRST(&sc->sc_scan); bp != NULL;
|
|
bp = TAILQ_NEXT(bp, list)) {
|
|
if (memcmp(bp->esrc, wh->i_addr2, ETHER_ADDR_LEN) == 0 &&
|
|
memcmp(bp->bssid, wh->i_addr3, ETHER_ADDR_LEN) == 0)
|
|
break;
|
|
}
|
|
if (bp == NULL) {
|
|
bp = malloc(sizeof(struct awi_bss), M_DEVBUF, M_NOWAIT);
|
|
if (bp == NULL)
|
|
return;
|
|
TAILQ_INSERT_TAIL(&sc->sc_scan, bp, list);
|
|
memcpy(bp->esrc, wh->i_addr2, ETHER_ADDR_LEN);
|
|
memcpy(bp->bssid, wh->i_addr3, ETHER_ADDR_LEN);
|
|
memset(bp->essid, 0, sizeof(bp->essid));
|
|
memcpy(bp->essid, ssid, 2 + ssid[1]);
|
|
}
|
|
bp->rssi = rssi;
|
|
bp->rxtime = rxts;
|
|
memcpy(bp->timestamp, tstamp, sizeof(bp->timestamp));
|
|
bp->interval = bintval;
|
|
if (sc->sc_mib_phy.IEEE_PHY_Type == AWI_PHY_TYPE_FH) {
|
|
bp->chanset = parms[4];
|
|
bp->pattern = parms[5];
|
|
bp->index = parms[6];
|
|
bp->dwell_time = LE_READ_2(parms + 2);
|
|
} else {
|
|
bp->chanset = parms[2];
|
|
bp->pattern = 0;
|
|
bp->index = 0;
|
|
bp->dwell_time = 0;
|
|
}
|
|
if (sc->sc_mgt_timer == 0)
|
|
awi_stop_scan(sc);
|
|
}
|
|
|
|
static int
|
|
awi_set_ss(sc)
|
|
struct awi_softc *sc;
|
|
{
|
|
struct ifnet *ifp = sc->sc_ifp;
|
|
struct awi_bss *bp;
|
|
int error;
|
|
|
|
sc->sc_status = AWI_ST_SETSS;
|
|
bp = &sc->sc_bss;
|
|
if (ifp->if_flags & IFF_DEBUG) {
|
|
printf("%s: ch %d pat %d id %d dw %d iv %d bss %s ssid \"%s\"\n",
|
|
sc->sc_dev.dv_xname, bp->chanset,
|
|
bp->pattern, bp->index, bp->dwell_time, bp->interval,
|
|
ether_sprintf(bp->bssid), bp->essid + 2);
|
|
}
|
|
memcpy(&sc->sc_mib_mgt.aCurrent_BSS_ID, bp->bssid, ETHER_ADDR_LEN);
|
|
memcpy(&sc->sc_mib_mgt.aCurrent_ESS_ID, bp->essid,
|
|
AWI_ESS_ID_SIZE);
|
|
LE_WRITE_2(&sc->sc_mib_mgt.aBeacon_Period, bp->interval);
|
|
error = awi_mib(sc, AWI_CMD_SET_MIB, AWI_MIB_MGT);
|
|
return error;
|
|
}
|
|
|
|
static void
|
|
awi_try_sync(sc)
|
|
struct awi_softc *sc;
|
|
{
|
|
struct awi_bss *bp;
|
|
|
|
sc->sc_status = AWI_ST_SYNC;
|
|
bp = &sc->sc_bss;
|
|
|
|
if (sc->sc_cmd_inprog) {
|
|
if (awi_cmd_wait(sc))
|
|
return;
|
|
}
|
|
sc->sc_cmd_inprog = 1;
|
|
awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_SYNC_SET, bp->chanset);
|
|
awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_SYNC_PATTERN, bp->pattern);
|
|
awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_SYNC_IDX, bp->index);
|
|
awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_SYNC_STARTBSS,
|
|
sc->sc_start_bss ? 1 : 0);
|
|
awi_write_2(sc, AWI_CMD_PARAMS+AWI_CA_SYNC_DWELL, bp->dwell_time);
|
|
awi_write_2(sc, AWI_CMD_PARAMS+AWI_CA_SYNC_MBZ, 0);
|
|
awi_write_bytes(sc, AWI_CMD_PARAMS+AWI_CA_SYNC_TIMESTAMP,
|
|
bp->timestamp, 8);
|
|
awi_write_4(sc, AWI_CMD_PARAMS+AWI_CA_SYNC_REFTIME, bp->rxtime);
|
|
(void)awi_cmd(sc, AWI_CMD_SYNC);
|
|
}
|
|
|
|
static void
|
|
awi_sync_done(sc)
|
|
struct awi_softc *sc;
|
|
{
|
|
struct ifnet *ifp = sc->sc_ifp;
|
|
|
|
if (sc->sc_mib_local.Network_Mode) {
|
|
awi_drvstate(sc, AWI_DRV_INFSY);
|
|
awi_send_auth(sc);
|
|
} else {
|
|
printf("%s: synced with %s ssid \"%s\" at chanset %d\n",
|
|
sc->sc_dev.dv_xname, ether_sprintf(sc->sc_bss.bssid),
|
|
sc->sc_bss.essid + 2, sc->sc_bss.chanset);
|
|
awi_drvstate(sc, AWI_DRV_ADHSY);
|
|
sc->sc_status = AWI_ST_RUNNING;
|
|
ifp->if_flags |= IFF_RUNNING;
|
|
awi_start(ifp);
|
|
}
|
|
}
|
|
|
|
static void
|
|
awi_send_deauth(sc)
|
|
struct awi_softc *sc;
|
|
{
|
|
struct ifnet *ifp = sc->sc_ifp;
|
|
struct mbuf *m;
|
|
struct ieee80211_frame *wh;
|
|
u_int8_t *deauth;
|
|
|
|
MGETHDR(m, M_DONTWAIT, MT_DATA);
|
|
if (m == NULL)
|
|
return;
|
|
if (ifp->if_flags & IFF_DEBUG)
|
|
printf("%s: sending deauth to %s\n", sc->sc_dev.dv_xname,
|
|
ether_sprintf(sc->sc_bss.bssid));
|
|
|
|
wh = mtod(m, struct ieee80211_frame *);
|
|
wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
|
|
IEEE80211_FC0_SUBTYPE_AUTH;
|
|
wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
|
|
LE_WRITE_2(wh->i_dur, 0);
|
|
LE_WRITE_2(wh->i_seq, 0);
|
|
memcpy(wh->i_addr1, sc->sc_bss.bssid, ETHER_ADDR_LEN);
|
|
memcpy(wh->i_addr2, sc->sc_mib_addr.aMAC_Address, ETHER_ADDR_LEN);
|
|
memcpy(wh->i_addr3, sc->sc_bss.bssid, ETHER_ADDR_LEN);
|
|
|
|
deauth = (u_int8_t *)&wh[1];
|
|
LE_WRITE_2(deauth, IEEE80211_REASON_AUTH_LEAVE);
|
|
deauth += 2;
|
|
|
|
m->m_pkthdr.len = m->m_len = deauth - mtod(m, u_int8_t *);
|
|
IF_ENQUEUE(&sc->sc_mgtq, m);
|
|
awi_start(ifp);
|
|
awi_drvstate(sc, AWI_DRV_INFTOSS);
|
|
}
|
|
|
|
static void
|
|
awi_send_auth(sc)
|
|
struct awi_softc *sc;
|
|
{
|
|
struct ifnet *ifp = sc->sc_ifp;
|
|
struct mbuf *m;
|
|
struct ieee80211_frame *wh;
|
|
u_int8_t *auth;
|
|
|
|
MGETHDR(m, M_DONTWAIT, MT_DATA);
|
|
if (m == NULL)
|
|
return;
|
|
sc->sc_status = AWI_ST_AUTH;
|
|
if (ifp->if_flags & IFF_DEBUG)
|
|
printf("%s: sending auth to %s\n", sc->sc_dev.dv_xname,
|
|
ether_sprintf(sc->sc_bss.bssid));
|
|
|
|
wh = mtod(m, struct ieee80211_frame *);
|
|
wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
|
|
IEEE80211_FC0_SUBTYPE_AUTH;
|
|
wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
|
|
LE_WRITE_2(wh->i_dur, 0);
|
|
LE_WRITE_2(wh->i_seq, 0);
|
|
memcpy(wh->i_addr1, sc->sc_bss.bssid, ETHER_ADDR_LEN);
|
|
memcpy(wh->i_addr2, sc->sc_mib_addr.aMAC_Address, ETHER_ADDR_LEN);
|
|
memcpy(wh->i_addr3, sc->sc_bss.bssid, ETHER_ADDR_LEN);
|
|
|
|
auth = (u_int8_t *)&wh[1];
|
|
/* algorithm number */
|
|
LE_WRITE_2(auth, IEEE80211_AUTH_ALG_OPEN);
|
|
auth += 2;
|
|
/* sequence number */
|
|
LE_WRITE_2(auth, 1);
|
|
auth += 2;
|
|
/* status */
|
|
LE_WRITE_2(auth, 0);
|
|
auth += 2;
|
|
|
|
m->m_pkthdr.len = m->m_len = auth - mtod(m, u_int8_t *);
|
|
IF_ENQUEUE(&sc->sc_mgtq, m);
|
|
awi_start(ifp);
|
|
|
|
sc->sc_mgt_timer = AWI_TRANS_TIMEOUT / 1000;
|
|
ifp->if_timer = 1;
|
|
}
|
|
|
|
static void
|
|
awi_recv_auth(sc, m0)
|
|
struct awi_softc *sc;
|
|
struct mbuf *m0;
|
|
{
|
|
struct ieee80211_frame *wh;
|
|
u_int8_t *auth, *eframe;
|
|
struct awi_bss *bp;
|
|
u_int16_t status;
|
|
|
|
wh = mtod(m0, struct ieee80211_frame *);
|
|
auth = (u_int8_t *)&wh[1];
|
|
eframe = mtod(m0, u_int8_t *) + m0->m_len;
|
|
if (sc->sc_ifp->if_flags & IFF_DEBUG)
|
|
printf("%s: receive auth from %s\n", sc->sc_dev.dv_xname,
|
|
ether_sprintf(wh->i_addr2));
|
|
|
|
if (!sc->sc_mib_local.Network_Mode) {
|
|
/* XXX: 802.11 allow auth for IBSS */
|
|
return;
|
|
}
|
|
if (sc->sc_status != AWI_ST_AUTH)
|
|
return;
|
|
/* algorithm number */
|
|
if (LE_READ_2(auth) != IEEE80211_AUTH_ALG_OPEN)
|
|
return;
|
|
auth += 2;
|
|
/* sequence number */
|
|
if (LE_READ_2(auth) != 2)
|
|
return;
|
|
auth += 2;
|
|
/* status */
|
|
status = LE_READ_2(auth);
|
|
if (status != 0) {
|
|
printf("%s: authentication failed (reason %d)\n",
|
|
sc->sc_dev.dv_xname, status);
|
|
for (bp = TAILQ_FIRST(&sc->sc_scan); bp != NULL;
|
|
bp = TAILQ_NEXT(bp, list)) {
|
|
if (memcmp(bp->esrc, sc->sc_bss.esrc, ETHER_ADDR_LEN)
|
|
== 0) {
|
|
bp->fails++;
|
|
break;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
sc->sc_mgt_timer = 0;
|
|
awi_drvstate(sc, AWI_DRV_INFAUTH);
|
|
awi_send_asreq(sc, 0);
|
|
}
|
|
|
|
static void
|
|
awi_send_asreq(sc, reassoc)
|
|
struct awi_softc *sc;
|
|
int reassoc;
|
|
{
|
|
struct ifnet *ifp = sc->sc_ifp;
|
|
struct mbuf *m;
|
|
struct ieee80211_frame *wh;
|
|
u_int16_t lintval;
|
|
u_int8_t *asreq;
|
|
|
|
MGETHDR(m, M_DONTWAIT, MT_DATA);
|
|
if (m == NULL)
|
|
return;
|
|
sc->sc_status = AWI_ST_ASSOC;
|
|
if (ifp->if_flags & IFF_DEBUG)
|
|
printf("%s: sending %sassoc req to %s\n", sc->sc_dev.dv_xname,
|
|
reassoc ? "re" : "",
|
|
ether_sprintf(sc->sc_bss.bssid));
|
|
|
|
wh = mtod(m, struct ieee80211_frame *);
|
|
wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT;
|
|
if (reassoc)
|
|
wh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_REASSOC_REQ;
|
|
else
|
|
wh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_ASSOC_REQ;
|
|
wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
|
|
LE_WRITE_2(wh->i_dur, 0);
|
|
LE_WRITE_2(wh->i_seq, 0);
|
|
memcpy(wh->i_addr1, sc->sc_bss.bssid, ETHER_ADDR_LEN);
|
|
memcpy(wh->i_addr2, sc->sc_mib_addr.aMAC_Address, ETHER_ADDR_LEN);
|
|
memcpy(wh->i_addr3, sc->sc_bss.bssid, ETHER_ADDR_LEN);
|
|
|
|
asreq = (u_int8_t *)&wh[1];
|
|
|
|
/* capability info */
|
|
LE_WRITE_2(asreq, IEEE80211_CAPINFO_CF_POLLABLE);
|
|
asreq += 2;
|
|
/* listen interval */
|
|
lintval = LE_READ_2(&sc->sc_mib_mgt.aListen_Interval);
|
|
LE_WRITE_2(asreq, lintval);
|
|
asreq += 2;
|
|
if (reassoc) {
|
|
/* current AP address */
|
|
memcpy(asreq, sc->sc_bss.bssid, ETHER_ADDR_LEN);
|
|
asreq += ETHER_ADDR_LEN;
|
|
}
|
|
/* ssid */
|
|
memcpy(asreq, sc->sc_bss.essid, 2 + sc->sc_bss.essid[1]);
|
|
asreq += 2 + asreq[1];
|
|
/* supported rates */
|
|
memcpy(asreq, &sc->sc_mib_phy.aSuprt_Data_Rates, 4);
|
|
asreq += 2 + asreq[1];
|
|
|
|
m->m_pkthdr.len = m->m_len = asreq - mtod(m, u_int8_t *);
|
|
IF_ENQUEUE(&sc->sc_mgtq, m);
|
|
awi_start(ifp);
|
|
|
|
sc->sc_mgt_timer = AWI_TRANS_TIMEOUT / 1000;
|
|
ifp->if_timer = 1;
|
|
}
|
|
|
|
static void
|
|
awi_recv_asresp(sc, m0)
|
|
struct awi_softc *sc;
|
|
struct mbuf *m0;
|
|
{
|
|
struct ieee80211_frame *wh;
|
|
u_int8_t *asresp, *eframe;
|
|
u_int16_t status;
|
|
u_int8_t rate, *phy_rates;
|
|
struct awi_bss *bp;
|
|
int i, j;
|
|
|
|
wh = mtod(m0, struct ieee80211_frame *);
|
|
asresp = (u_int8_t *)&wh[1];
|
|
eframe = mtod(m0, u_int8_t *) + m0->m_len;
|
|
if (sc->sc_ifp->if_flags & IFF_DEBUG)
|
|
printf("%s: receive assoc resp from %s\n", sc->sc_dev.dv_xname,
|
|
ether_sprintf(wh->i_addr2));
|
|
|
|
if (!sc->sc_mib_local.Network_Mode)
|
|
return;
|
|
|
|
if (sc->sc_status != AWI_ST_ASSOC)
|
|
return;
|
|
/* capability info */
|
|
asresp += 2;
|
|
/* status */
|
|
status = LE_READ_2(asresp);
|
|
if (status != 0) {
|
|
printf("%s: association failed (reason %d)\n",
|
|
sc->sc_dev.dv_xname, status);
|
|
for (bp = TAILQ_FIRST(&sc->sc_scan); bp != NULL;
|
|
bp = TAILQ_NEXT(bp, list)) {
|
|
if (memcmp(bp->esrc, sc->sc_bss.esrc, ETHER_ADDR_LEN)
|
|
== 0) {
|
|
bp->fails++;
|
|
break;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
asresp += 2;
|
|
/* association id */
|
|
asresp += 2;
|
|
/* supported rates */
|
|
rate = AWI_RATE_1MBIT;
|
|
for (i = 0; i < asresp[1]; i++) {
|
|
if (AWI_80211_RATE(asresp[2 + i]) <= rate)
|
|
continue;
|
|
phy_rates = sc->sc_mib_phy.aSuprt_Data_Rates;
|
|
for (j = 0; j < phy_rates[1]; j++) {
|
|
if (AWI_80211_RATE(asresp[2 + i]) ==
|
|
AWI_80211_RATE(phy_rates[2 + j]))
|
|
rate = AWI_80211_RATE(asresp[2 + i]);
|
|
}
|
|
}
|
|
printf("%s: associated with %s ssid \"%s\"",
|
|
sc->sc_dev.dv_xname, ether_sprintf(sc->sc_bss.bssid),
|
|
sc->sc_bss.essid + 2);
|
|
if (sc->sc_mib_phy.IEEE_PHY_Type == AWI_PHY_TYPE_FH)
|
|
printf(" chanset %d pattern %d",
|
|
sc->sc_bss.chanset, sc->sc_bss.pattern);
|
|
else
|
|
printf(" channel %d", sc->sc_bss.chanset);
|
|
printf(" signal %d\n", sc->sc_bss.rssi);
|
|
sc->sc_tx_rate = rate;
|
|
sc->sc_mgt_timer = 0;
|
|
sc->sc_rx_timer = 10;
|
|
sc->sc_ifp->if_timer = 1;
|
|
sc->sc_status = AWI_ST_RUNNING;
|
|
sc->sc_ifp->if_flags |= IFF_RUNNING;
|
|
awi_drvstate(sc, AWI_DRV_INFASSOC);
|
|
while ((bp = TAILQ_FIRST(&sc->sc_scan)) != NULL)
|
|
TAILQ_REMOVE(&sc->sc_scan, bp, list);
|
|
awi_start(sc->sc_ifp);
|
|
}
|
|
|
|
static int
|
|
awi_mib(sc, cmd, mib)
|
|
struct awi_softc *sc;
|
|
u_int8_t cmd;
|
|
u_int8_t mib;
|
|
{
|
|
int error;
|
|
u_int8_t size, *ptr;
|
|
|
|
switch (mib) {
|
|
case AWI_MIB_LOCAL:
|
|
ptr = (u_int8_t *)&sc->sc_mib_local;
|
|
size = sizeof(sc->sc_mib_local);
|
|
break;
|
|
case AWI_MIB_ADDR:
|
|
ptr = (u_int8_t *)&sc->sc_mib_addr;
|
|
size = sizeof(sc->sc_mib_addr);
|
|
break;
|
|
case AWI_MIB_MAC:
|
|
ptr = (u_int8_t *)&sc->sc_mib_mac;
|
|
size = sizeof(sc->sc_mib_mac);
|
|
break;
|
|
case AWI_MIB_STAT:
|
|
ptr = (u_int8_t *)&sc->sc_mib_stat;
|
|
size = sizeof(sc->sc_mib_stat);
|
|
break;
|
|
case AWI_MIB_MGT:
|
|
ptr = (u_int8_t *)&sc->sc_mib_mgt;
|
|
size = sizeof(sc->sc_mib_mgt);
|
|
break;
|
|
case AWI_MIB_PHY:
|
|
ptr = (u_int8_t *)&sc->sc_mib_phy;
|
|
size = sizeof(sc->sc_mib_phy);
|
|
break;
|
|
default:
|
|
return EINVAL;
|
|
}
|
|
if (sc->sc_cmd_inprog) {
|
|
error = awi_cmd_wait(sc);
|
|
if (error) {
|
|
printf("awi_mib: cmd %d inprog\n",
|
|
awi_read_1(sc, AWI_CMD));
|
|
return error;
|
|
}
|
|
}
|
|
sc->sc_cmd_inprog = 1;
|
|
if (cmd == AWI_CMD_SET_MIB)
|
|
awi_write_bytes(sc, AWI_CMD_PARAMS+AWI_CA_MIB_DATA, ptr, size);
|
|
awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_TYPE, mib);
|
|
awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_SIZE, size);
|
|
awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_INDEX, 0);
|
|
error = awi_cmd(sc, cmd);
|
|
if (error)
|
|
return error;
|
|
if (cmd == AWI_CMD_GET_MIB) {
|
|
awi_read_bytes(sc, AWI_CMD_PARAMS+AWI_CA_MIB_DATA, ptr, size);
|
|
#ifdef AWI_DEBUG
|
|
if (awi_verbose) {
|
|
int i;
|
|
|
|
printf("awi_mib: #%d:", mib);
|
|
for (i = 0; i < size; i++)
|
|
printf(" %02x", ptr[i]);
|
|
printf("\n");
|
|
}
|
|
#endif
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
awi_cmd_scan(sc)
|
|
struct awi_softc *sc;
|
|
{
|
|
int error;
|
|
u_int8_t scan_mode;
|
|
|
|
if (sc->sc_active_scan)
|
|
scan_mode = AWI_SCAN_ACTIVE;
|
|
else
|
|
scan_mode = AWI_SCAN_PASSIVE;
|
|
if (sc->sc_mib_mgt.aScan_Mode != scan_mode) {
|
|
sc->sc_mib_mgt.aScan_Mode = scan_mode;
|
|
error = awi_mib(sc, AWI_CMD_SET_MIB, AWI_MIB_MGT);
|
|
return error;
|
|
}
|
|
|
|
if (sc->sc_cmd_inprog) {
|
|
error = awi_cmd_wait(sc);
|
|
if (error)
|
|
return error;
|
|
}
|
|
sc->sc_cmd_inprog = 1;
|
|
awi_write_2(sc, AWI_CMD_PARAMS+AWI_CA_SCAN_DURATION,
|
|
sc->sc_active_scan ? AWI_ASCAN_DURATION : AWI_PSCAN_DURATION);
|
|
if (sc->sc_mib_phy.IEEE_PHY_Type == AWI_PHY_TYPE_FH) {
|
|
awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_SCAN_SET,
|
|
sc->sc_scan_set);
|
|
awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_SCAN_PATTERN,
|
|
sc->sc_scan_cur);
|
|
awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_SCAN_IDX, 1);
|
|
} else {
|
|
awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_SCAN_SET,
|
|
sc->sc_scan_cur);
|
|
awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_SCAN_PATTERN, 0);
|
|
awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_SCAN_IDX, 0);
|
|
}
|
|
awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_SCAN_SUSP, 0);
|
|
return awi_cmd(sc, AWI_CMD_SCAN);
|
|
}
|
|
|
|
static int
|
|
awi_cmd(sc, cmd)
|
|
struct awi_softc *sc;
|
|
u_int8_t cmd;
|
|
{
|
|
u_int8_t status;
|
|
int error = 0;
|
|
|
|
sc->sc_cmd_inprog = 1;
|
|
awi_write_1(sc, AWI_CMD_STATUS, AWI_STAT_IDLE);
|
|
awi_write_1(sc, AWI_CMD, cmd);
|
|
if (sc->sc_status != AWI_ST_INIT)
|
|
return 0;
|
|
error = awi_cmd_wait(sc);
|
|
if (error)
|
|
return error;
|
|
status = awi_read_1(sc, AWI_CMD_STATUS);
|
|
awi_write_1(sc, AWI_CMD, 0);
|
|
switch (status) {
|
|
case AWI_STAT_OK:
|
|
break;
|
|
case AWI_STAT_BADPARM:
|
|
return EINVAL;
|
|
default:
|
|
printf("%s: command %d failed %x\n",
|
|
sc->sc_dev.dv_xname, cmd, status);
|
|
return ENXIO;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
awi_cmd_done(sc)
|
|
struct awi_softc *sc;
|
|
{
|
|
u_int8_t cmd, status;
|
|
|
|
status = awi_read_1(sc, AWI_CMD_STATUS);
|
|
if (status == AWI_STAT_IDLE)
|
|
return; /* stray interrupt */
|
|
|
|
sc->sc_cmd_inprog = 0;
|
|
if (sc->sc_status == AWI_ST_INIT) {
|
|
wakeup(sc);
|
|
return;
|
|
}
|
|
cmd = awi_read_1(sc, AWI_CMD);
|
|
awi_write_1(sc, AWI_CMD, 0);
|
|
|
|
if (status != AWI_STAT_OK) {
|
|
printf("%s: command %d failed %x\n",
|
|
sc->sc_dev.dv_xname, cmd, status);
|
|
return;
|
|
}
|
|
switch (sc->sc_status) {
|
|
case AWI_ST_SCAN:
|
|
if (cmd == AWI_CMD_SET_MIB)
|
|
awi_cmd_scan(sc); /* retry */
|
|
break;
|
|
case AWI_ST_SETSS:
|
|
awi_try_sync(sc);
|
|
break;
|
|
case AWI_ST_SYNC:
|
|
awi_sync_done(sc);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int
|
|
awi_next_txd(sc, len, framep, ntxdp)
|
|
struct awi_softc *sc;
|
|
int len;
|
|
u_int32_t *framep, *ntxdp;
|
|
{
|
|
u_int32_t txd, ntxd, frame;
|
|
|
|
txd = sc->sc_txnext;
|
|
frame = txd + AWI_TXD_SIZE;
|
|
if (frame + len > sc->sc_txend)
|
|
frame = sc->sc_txbase;
|
|
ntxd = frame + len;
|
|
if (ntxd + AWI_TXD_SIZE > sc->sc_txend)
|
|
ntxd = sc->sc_txbase;
|
|
*framep = frame;
|
|
*ntxdp = ntxd;
|
|
/*
|
|
* Determine if there are any room in ring buffer.
|
|
* --- send wait, === new data, +++ conflict (ENOBUFS)
|
|
* base........................end
|
|
* done----txd=====ntxd OK
|
|
* --txd=====done++++ntxd-- full
|
|
* --txd=====ntxd done-- OK
|
|
* ==ntxd done----txd=== OK
|
|
* ==done++++ntxd----txd=== full
|
|
* ++ntxd txd=====done++ full
|
|
*/
|
|
if (txd < ntxd) {
|
|
if (txd < sc->sc_txdone && ntxd + AWI_TXD_SIZE > sc->sc_txdone)
|
|
return ENOBUFS;
|
|
} else {
|
|
if (txd < sc->sc_txdone || ntxd + AWI_TXD_SIZE > sc->sc_txdone)
|
|
return ENOBUFS;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
awi_lock(sc)
|
|
struct awi_softc *sc;
|
|
{
|
|
int error = 0;
|
|
|
|
if (sc->sc_invalid)
|
|
return ENXIO;
|
|
if (curproc == NULL) {
|
|
/*
|
|
* XXX
|
|
* Though driver ioctl should be called with context,
|
|
* KAME ipv6 stack calls ioctl in interrupt for now.
|
|
* We simply abort the request if there are other
|
|
* ioctl requests in progress.
|
|
*/
|
|
if (sc->sc_busy)
|
|
return EWOULDBLOCK;
|
|
sc->sc_busy = 1;
|
|
sc->sc_cansleep = 0;
|
|
return 0;
|
|
}
|
|
while (sc->sc_busy) {
|
|
sc->sc_sleep_cnt++;
|
|
error = tsleep(sc, PWAIT | PCATCH, "awilck", 0);
|
|
sc->sc_sleep_cnt--;
|
|
if (error)
|
|
return error;
|
|
if (sc->sc_invalid)
|
|
return ENXIO;
|
|
}
|
|
sc->sc_busy = 1;
|
|
sc->sc_cansleep = 1;
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
awi_unlock(sc)
|
|
struct awi_softc *sc;
|
|
{
|
|
sc->sc_busy = 0;
|
|
sc->sc_cansleep = 0;
|
|
if (sc->sc_sleep_cnt)
|
|
wakeup(sc);
|
|
}
|
|
|
|
static int
|
|
awi_intr_lock(sc)
|
|
struct awi_softc *sc;
|
|
{
|
|
u_int8_t status;
|
|
int i, retry;
|
|
|
|
status = 1;
|
|
for (retry = 0; retry < 10; retry++) {
|
|
for (i = 0; i < AWI_LOCKOUT_TIMEOUT*1000/5; i++) {
|
|
status = awi_read_1(sc, AWI_LOCKOUT_HOST);
|
|
if (status == 0)
|
|
break;
|
|
DELAY(5);
|
|
}
|
|
if (status != 0)
|
|
break;
|
|
awi_write_1(sc, AWI_LOCKOUT_MAC, 1);
|
|
status = awi_read_1(sc, AWI_LOCKOUT_HOST);
|
|
if (status == 0)
|
|
break;
|
|
awi_write_1(sc, AWI_LOCKOUT_MAC, 0);
|
|
}
|
|
if (status != 0) {
|
|
printf("%s: failed to lock interrupt\n",
|
|
sc->sc_dev.dv_xname);
|
|
return ENXIO;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
awi_intr_unlock(sc)
|
|
struct awi_softc *sc;
|
|
{
|
|
|
|
awi_write_1(sc, AWI_LOCKOUT_MAC, 0);
|
|
}
|
|
|
|
static int
|
|
awi_cmd_wait(sc)
|
|
struct awi_softc *sc;
|
|
{
|
|
int i, error = 0;
|
|
|
|
i = 0;
|
|
while (sc->sc_cmd_inprog) {
|
|
if (sc->sc_invalid)
|
|
return ENXIO;
|
|
if (sc->sc_cansleep) {
|
|
sc->sc_sleep_cnt++;
|
|
error = tsleep(sc, PWAIT, "awicmd",
|
|
AWI_CMD_TIMEOUT*hz/1000);
|
|
sc->sc_sleep_cnt--;
|
|
} else {
|
|
if (awi_read_1(sc, AWI_CMD_STATUS) != AWI_STAT_IDLE) {
|
|
awi_cmd_done(sc);
|
|
break;
|
|
}
|
|
if (i++ >= AWI_CMD_TIMEOUT*1000/10)
|
|
error = EWOULDBLOCK;
|
|
else
|
|
DELAY(10);
|
|
}
|
|
if (error)
|
|
break;
|
|
}
|
|
return error;
|
|
}
|
|
|
|
#ifdef AWI_DEBUG
|
|
static void
|
|
awi_dump_pkt(sc, m, rssi)
|
|
struct awi_softc *sc;
|
|
struct mbuf *m;
|
|
u_int8_t rssi;
|
|
{
|
|
struct ieee80211_frame *wh;
|
|
int i, l;
|
|
|
|
wh = mtod(m, struct ieee80211_frame *);
|
|
|
|
if (awi_dump_mask != 0 &&
|
|
((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK)==IEEE80211_FC1_DIR_NODS) &&
|
|
((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK)==IEEE80211_FC0_TYPE_MGT)) {
|
|
if ((AWI_DUMP_MASK(wh->i_fc[0]) & awi_dump_mask) != 0)
|
|
return;
|
|
}
|
|
if (awi_dump_mask < 0 &&
|
|
(wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK)==IEEE80211_FC0_TYPE_DATA)
|
|
return;
|
|
|
|
switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) {
|
|
case IEEE80211_FC1_DIR_NODS:
|
|
printf("rx: NODS %s", ether_sprintf(wh->i_addr2));
|
|
printf("->%s", ether_sprintf(wh->i_addr1));
|
|
printf("(%s)", ether_sprintf(wh->i_addr3));
|
|
break;
|
|
case IEEE80211_FC1_DIR_TODS:
|
|
printf("rx: TODS %s", ether_sprintf(wh->i_addr2));
|
|
printf("->%s", ether_sprintf(wh->i_addr3));
|
|
printf("(%s)", ether_sprintf(wh->i_addr1));
|
|
break;
|
|
case IEEE80211_FC1_DIR_FROMDS:
|
|
printf("rx: FRDS %s", ether_sprintf(wh->i_addr3));
|
|
printf("->%s", ether_sprintf(wh->i_addr1));
|
|
printf("(%s)", ether_sprintf(wh->i_addr2));
|
|
break;
|
|
case IEEE80211_FC1_DIR_DSTODS:
|
|
printf("rx: DSDS %s", ether_sprintf((u_int8_t *)&wh[1]));
|
|
printf("->%s", ether_sprintf(wh->i_addr3));
|
|
printf("(%s", ether_sprintf(wh->i_addr2));
|
|
printf("->%s)", ether_sprintf(wh->i_addr1));
|
|
break;
|
|
}
|
|
switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) {
|
|
case IEEE80211_FC0_TYPE_DATA:
|
|
printf(" data");
|
|
break;
|
|
case IEEE80211_FC0_TYPE_MGT:
|
|
switch (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) {
|
|
case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
|
|
printf(" probe_req");
|
|
break;
|
|
case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
|
|
printf(" probe_resp");
|
|
break;
|
|
case IEEE80211_FC0_SUBTYPE_BEACON:
|
|
printf(" beacon");
|
|
break;
|
|
case IEEE80211_FC0_SUBTYPE_AUTH:
|
|
printf(" auth");
|
|
break;
|
|
case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
|
|
printf(" assoc_req");
|
|
break;
|
|
case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
|
|
printf(" assoc_resp");
|
|
break;
|
|
case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
|
|
printf(" reassoc_req");
|
|
break;
|
|
case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
|
|
printf(" reassoc_resp");
|
|
break;
|
|
case IEEE80211_FC0_SUBTYPE_DEAUTH:
|
|
printf(" deauth");
|
|
break;
|
|
case IEEE80211_FC0_SUBTYPE_DISASSOC:
|
|
printf(" disassoc");
|
|
break;
|
|
default:
|
|
printf(" mgt#%d",
|
|
wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
printf(" type#%d",
|
|
wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK);
|
|
break;
|
|
}
|
|
printf(" +%d\n", rssi);
|
|
if (awi_dump_len > 0) {
|
|
l = m->m_len;
|
|
if (l > awi_dump_len + sizeof(*wh))
|
|
l = awi_dump_len + sizeof(*wh);
|
|
i = sizeof(*wh);
|
|
if (awi_dump_hdr)
|
|
i = 0;
|
|
for (; i < l; i++) {
|
|
if ((i & 1) == 0)
|
|
printf(" ");
|
|
printf("%02x", mtod(m, u_int8_t *)[i]);
|
|
}
|
|
printf("\n");
|
|
}
|
|
}
|
|
#endif
|