diff options
author | Jonathan Gray <jsg@cvs.openbsd.org> | 2006-01-09 21:19:49 +0000 |
---|---|---|
committer | Jonathan Gray <jsg@cvs.openbsd.org> | 2006-01-09 21:19:49 +0000 |
commit | 4fb27973143f59ce36152403b7753b45da61e46f (patch) | |
tree | def687c2cded60e4d7cf33cfca709b70115d213a /sys/dev/ic/an.c | |
parent | 49a48702134ba52fa189ddb45b5af9200dd50c54 (diff) |
Move an(4) to a driver based on the NetBSD one.
This brings net80211 support and support for newer hardware.
In addition ancontrol is no longer needed.
This driver does not yet work on big endian archs like the previous
one did.
"do it" deraadt@
Diffstat (limited to 'sys/dev/ic/an.c')
-rw-r--r-- | sys/dev/ic/an.c | 2342 |
1 files changed, 1183 insertions, 1159 deletions
diff --git a/sys/dev/ic/an.c b/sys/dev/ic/an.c index 84993437ffe..23d22df9bad 100644 --- a/sys/dev/ic/an.c +++ b/sys/dev/ic/an.c @@ -1,5 +1,5 @@ -/* $OpenBSD: an.c,v 1.44 2005/11/09 05:50:25 brad Exp $ */ - +/* $OpenBSD: an.c,v 1.45 2006/01/09 21:19:47 jsg Exp $ */ +/* $NetBSD: an.c,v 1.34 2005/06/20 02:49:18 atatat Exp $ */ /* * Copyright (c) 1997, 1998, 1999 * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. @@ -31,7 +31,37 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/an/if_an.c,v 1.21 2001/09/10 02:05:09 brooks Exp $ + * $FreeBSD: src/sys/dev/an/if_an.c,v 1.12 2000/11/13 23:04:12 wpaul Exp $ + */ +/* + * Copyright (c) 2004, 2005 David Young. All rights reserved. + * Copyright (c) 2004, 2005 OJC Technologies. All rights reserved. + * Copyright (c) 2004, 2005 Dayton Data Center Services, LLC. All + * rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY David Young 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 David Young AND 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. */ /* @@ -43,67 +73,32 @@ */ /* - * The Aironet 4500/4800 series cards come in PCMCIA, ISA and PCI form. - * This driver supports all three device types (PCI devices are supported - * through an extra PCI shim: /sys/pci/if_an_p.c). ISA devices can be - * supported either using hard-coded IO port/IRQ settings or via Plug - * and Play. The 4500 series devices support 1Mbps and 2Mbps data rates. - * The 4800 devices support 1, 2, 5.5 and 11Mbps rates. - * - * Like the WaveLAN/IEEE cards, the Aironet NICs are all essentially - * PCMCIA devices. The ISA and PCI cards are a combination of a PCMCIA - * device and a PCMCIA to ISA or PCMCIA to PCI adapter card. There are - * a couple of important differences though: - * - * - Lucent ISA card looks to the host like a PCMCIA controller with - * a PCMCIA WaveLAN card inserted. This means that even desktop - * machines need to be configured with PCMCIA support in order to - * use WaveLAN/IEEE ISA cards. The Aironet cards on the other hand - * actually look like normal ISA and PCI devices to the host, so - * no PCMCIA controller support is needed - * - * The latter point results in a small gotcha. The Aironet PCMCIA - * cards can be configured for one of two operating modes depending - * on how the Vpp1 and Vpp2 programming voltages are set when the - * card is activated. In order to put the card in proper PCMCIA - * operation (where the CIS table is visible and the interface is - * programmed for PCMCIA operation), both Vpp1 and Vpp2 have to be - * set to 5 volts. FreeBSD by default doesn't set the Vpp voltages, - * which leaves the card in ISA/PCI mode, which prevents it from - * being activated as an PCMCIA device. - * - * Note that some PCMCIA controller software packages for Windows NT - * fail to set the voltages as well. - * - * The Aironet devices can operate in both station mode and access point - * mode. Typically, when programmed for station mode, the card can be set - * to automatically perform encapsulation/decapsulation of Ethernet II - * and 802.3 frames within 802.11 frames so that the host doesn't have - * to do it itself. This driver doesn't program the card that way: the - * driver handles all of the encapsulation/decapsulation itself. + * Ported to NetBSD from FreeBSD by Atsushi Onoe at the San Diego + * IETF meeting. */ -#ifdef INET -#define ANCACHE /* enable signal strength cache */ -#endif +#include <sys/cdefs.h> + +#include "bpfilter.h" #include <sys/param.h> #include <sys/systm.h> -#include <sys/device.h> #include <sys/sockio.h> #include <sys/mbuf.h> -#include <sys/malloc.h> #include <sys/kernel.h> -#include <sys/proc.h> +#include <sys/ucred.h> #include <sys/socket.h> #include <sys/timeout.h> -#ifdef ANCACHE -#include <sys/syslog.h> -#include <sys/sysctl.h> -#endif +#include <sys/device.h> +#include <sys/proc.h> +#include <sys/endian.h> +#include <sys/tree.h> + +#include <machine/bus.h> #include <net/if.h> #include <net/if_dl.h> +#include <net/if_llc.h> #include <net/if_media.h> #include <net/if_types.h> @@ -117,727 +112,674 @@ #include <net80211/ieee80211_var.h> -#include "bpfilter.h" #if NBPFILTER > 0 #include <net/bpf.h> #endif -#include <machine/bus.h> - -#include <dev/ic/anvar.h> #include <dev/ic/anreg.h> - -#define TIMEOUT(handle,func,sc,time) timeout_add(&(handle), (time)) -#define UNTIMEOUT(func,sc,handle) timeout_del(&(handle)) -#define BPF_MTAP(if,mbuf) bpf_mtap((if)->if_bpf, (mbuf)) -#define BPFATTACH(if_bpf,if,dlt,sz) +#include <dev/ic/anvar.h> struct cfdriver an_cd = { NULL, "an", DV_IFNET }; -void an_reset(struct an_softc *); -int an_ioctl(struct ifnet *, u_long, caddr_t); -int an_init_tx_ring(struct an_softc *); -void an_start(struct ifnet *); -void an_watchdog(struct ifnet *); -void an_rxeof(struct an_softc *); -void an_txeof(struct an_softc *, int); - -void an_promisc(struct an_softc *, int); -int an_cmd(struct an_softc *, int, int); -int an_read_record(struct an_softc *, struct an_ltv_gen *); -int an_write_record(struct an_softc *, struct an_ltv_gen *); -int an_read_data(struct an_softc *, int, - int, caddr_t, int); -int an_write_data(struct an_softc *, int, - int, caddr_t, int); -int an_seek(struct an_softc *, int, int, int); -int an_alloc_nicmem(struct an_softc *, int, int *); -void an_stats_update(void *); -void an_setdef(struct an_softc *, struct an_req *); -#ifdef ANCACHE -void an_cache_store(struct an_softc *, struct ether_header *, - struct mbuf *, unsigned short); +int an_reset(struct an_softc *); +void an_wait(struct an_softc *); +int an_init(struct ifnet *); +void an_stop(struct ifnet *, int); +void an_start(struct ifnet *); +void an_watchdog(struct ifnet *); +int an_ioctl(struct ifnet *, u_long, caddr_t); +int an_media_change(struct ifnet *); +void an_media_status(struct ifnet *, struct ifmediareq *); + +int an_set_nwkey(struct an_softc *, struct ieee80211_nwkey *); +int an_set_nwkey_wep(struct an_softc *, struct ieee80211_nwkey *); +int an_get_nwkey(struct an_softc *, struct ieee80211_nwkey *); +int an_write_wepkey(struct an_softc *, int, struct an_wepkey *, + int); + +void an_rxeof(struct an_softc *); +void an_txeof(struct an_softc *, int); +void an_linkstat_intr(struct an_softc *); + +int an_cmd(struct an_softc *, int, int); +int an_seek_bap(struct an_softc *, int, int); +int an_read_bap(struct an_softc *, int, int, void *, int); +int an_write_bap(struct an_softc *, int, int, void *, int); +int an_mwrite_bap(struct an_softc *, int, int, struct mbuf *, int); +int an_read_rid(struct an_softc *, int, void *, int *); +int an_write_rid(struct an_softc *, int, void *, int); + +int an_alloc_nicmem(struct an_softc *, int, int *); + +int an_newstate(struct ieee80211com *, enum ieee80211_state, int); + +#ifdef AN_DEBUG +int an_debug = 0; + +#define DPRINTF(X) if (an_debug) printf X +#define DPRINTF2(X) if (an_debug > 1) printf X +#else +#define DPRINTF(X) +#define DPRINTF2(X) #endif -int an_media_change(struct ifnet *); -void an_media_status(struct ifnet *, struct ifmediareq *); static __inline void an_swap16(u_int16_t *p, int cnt) { - for (; cnt--; p++) - *p = swap16(*p); + for (; cnt--; p++) + *p = swap16(*p); } int -an_attach(sc) - struct an_softc *sc; +an_attach(struct an_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ifnet *ifp = &ic->ic_if; + int i; + struct an_rid_wepkey *akey; + int buflen, kid, rid; + int chan, chan_min, chan_max; - sc->an_gone = 0; - sc->an_associated = 0; + sc->sc_invalid = 0; /* disable interrupts */ CSR_WRITE_2(sc, AN_INT_EN, 0); CSR_WRITE_2(sc, AN_EVENT_ACK, 0xffff); - /* Reset the NIC. */ - an_reset(sc); +// an_wait(sc); + if (an_reset(sc) != 0) { + sc->sc_invalid = 1; + return 1; + } /* Load factory config */ - if (an_cmd(sc, AN_CMD_READCFG, 0)) { - printf("%s: failed to load config data\n", ifp->if_xname); - return(EIO); + if (an_cmd(sc, AN_CMD_READCFG, 0) != 0) { + printf("%s: failed to load config data\n", + sc->sc_dev.dv_xname); + return (EIO); } /* Read the current configuration */ - sc->an_config.an_type = AN_RID_GENCONFIG; - sc->an_config.an_len = sizeof(struct an_ltv_genconfig); - if (an_read_record(sc, (struct an_ltv_gen *)&sc->an_config)) { - printf("%s: read record failed\n", ifp->if_xname); + buflen = sizeof(sc->sc_config); + if (an_read_rid(sc, AN_RID_GENCONFIG, &sc->sc_config, &buflen) != 0) { + printf("%s: read config failed\n", sc->sc_dev.dv_xname); return(EIO); } /* Read the card capabilities */ - sc->an_caps.an_type = AN_RID_CAPABILITIES; - sc->an_caps.an_len = sizeof(struct an_ltv_caps); - if (an_read_record(sc, (struct an_ltv_gen *)&sc->an_caps)) { - printf("%s: read record failed\n", ifp->if_xname); - return(EIO); - } - - /* Read ssid list */ - sc->an_ssidlist.an_type = AN_RID_SSIDLIST; - sc->an_ssidlist.an_len = sizeof(struct an_ltv_ssidlist); - if (an_read_record(sc, (struct an_ltv_gen *)&sc->an_ssidlist)) { - printf("%s: read record failed\n", ifp->if_xname); + buflen = sizeof(sc->sc_caps); + if (an_read_rid(sc, AN_RID_CAPABILITIES, &sc->sc_caps, &buflen) != 0) { + printf("%s: read caps failed\n", sc->sc_dev.dv_xname); return(EIO); } - /* Read AP list */ - sc->an_aplist.an_type = AN_RID_APLIST; - sc->an_aplist.an_len = sizeof(struct an_ltv_aplist); - if (an_read_record(sc, (struct an_ltv_gen *)&sc->an_aplist)) { - printf("%s: read record failed\n", ifp->if_xname); - return(EIO); + /* Read WEP settings from persistent memory */ + akey = &sc->sc_buf.sc_wepkey; + buflen = sizeof(struct an_rid_wepkey); + rid = AN_RID_WEP_VOLATILE; /* first persistent key */ + while (an_read_rid(sc, rid, akey, &buflen) == 0) { + kid = letoh16(akey->an_key_index); + DPRINTF(("an_attach: wep rid=0x%x len=%d(%d) index=0x%04x " + "mac[0]=%02x keylen=%d\n", + rid, buflen, sizeof(*akey), kid, + akey->an_mac_addr[0], letoh16(akey->an_key_len))); + if (kid == 0xffff) { + sc->sc_tx_perskey = akey->an_mac_addr[0]; + sc->sc_tx_key = -1; + break; + } + if (kid >= IEEE80211_WEP_NKID) + break; + sc->sc_perskeylen[kid] = letoh16(akey->an_key_len); + sc->sc_wepkeys[kid].an_wep_keylen = -1; + rid = AN_RID_WEP_PERSISTENT; /* for next key */ + buflen = sizeof(struct an_rid_wepkey); } - bcopy((char *)&sc->an_caps.an_oemaddr, - (char *)&sc->sc_ic.ic_ac.ac_enaddr, ETHER_ADDR_LEN); + IEEE80211_ADDR_COPY(ic->ic_myaddr, sc->sc_caps.an_oemaddr); + bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ); - printf(": address %6s\n", ether_sprintf(sc->sc_ic.ic_ac.ac_enaddr)); + printf("%s: Firmware %x.%02x.%02x, Radio: ", ifp->if_xname, + htole16(sc->sc_caps.an_fwrev) >> 8, + htole16(sc->sc_caps.an_fwrev) & 0xff, + sc->sc_caps.an_fwsubrev); + + if (sc->sc_config.an_radiotype & AN_RADIOTYPE_80211_FH) + printf("802.11 FH"); + else if (sc->sc_config.an_radiotype & AN_RADIOTYPE_80211_DS) + printf("802.11 DS"); + else if (sc->sc_config.an_radiotype & AN_RADIOTYPE_LM2000_DS) + printf("LM2000 DS"); + else + printf("unknown (%x)", sc->sc_config.an_radiotype); - bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ); + printf(", address: %s\n", ether_sprintf(ic->ic_myaddr)); + ifp->if_softc = sc; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = an_ioctl; ifp->if_start = an_start; + ifp->if_init = an_init; ifp->if_watchdog = an_watchdog; - ifp->if_baudrate = 10000000; IFQ_SET_READY(&ifp->if_snd); - bzero(sc->an_config.an_nodename, sizeof(sc->an_config.an_nodename)); - strlcpy(sc->an_config.an_nodename, AN_DEFAULT_NODENAME, - sizeof(sc->an_config.an_nodename)); - - bzero(sc->an_ssidlist.an_ssid1, sizeof(sc->an_ssidlist.an_ssid1)); - strlcpy(sc->an_ssidlist.an_ssid1, AN_DEFAULT_NETNAME, - sizeof(sc->an_ssidlist.an_ssid1)); - sc->an_ssidlist.an_ssid1_len = strlen(sc->an_ssidlist.an_ssid1); + ic->ic_phytype = IEEE80211_T_DS; + ic->ic_opmode = IEEE80211_M_STA; + ic->ic_caps = IEEE80211_C_WEP | IEEE80211_C_PMGT | IEEE80211_C_IBSS | + IEEE80211_C_MONITOR; + ic->ic_state = IEEE80211_S_INIT; + IEEE80211_ADDR_COPY(ic->ic_myaddr, sc->sc_caps.an_oemaddr); - sc->an_config.an_opmode = AN_OPMODE_INFRASTRUCTURE_STATION; - - sc->an_tx_rate = 0; - bzero((char *)&sc->an_stats, sizeof(sc->an_stats)); -#ifdef ANCACHE - sc->an_sigitems = sc->an_nextitem = 0; -#endif + switch (letoh16(sc->sc_caps.an_regdomain)) { + default: + case AN_REGDOMAIN_USA: + case AN_REGDOMAIN_CANADA: + chan_min = 1; chan_max = 11; break; + case AN_REGDOMAIN_EUROPE: + case AN_REGDOMAIN_AUSTRALIA: + chan_min = 1; chan_max = 13; break; + case AN_REGDOMAIN_JAPAN: + chan_min = 14; chan_max = 14; break; + case AN_REGDOMAIN_SPAIN: + chan_min = 10; chan_max = 11; break; + case AN_REGDOMAIN_FRANCE: + chan_min = 10; chan_max = 13; break; + case AN_REGDOMAIN_JAPANWIDE: + chan_min = 1; chan_max = 14; break; + } - ifmedia_init(&sc->an_ifmedia, 0, an_media_change, an_media_status); -#define ADD(m, c) ifmedia_add(&sc->an_ifmedia, (m), (c), NULL) - ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS1, - IFM_IEEE80211_ADHOC, 0), 0); - ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS1, 0, 0), 0); - ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS2, - IFM_IEEE80211_ADHOC, 0), 0); - ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS2, 0, 0), 0); - if (sc->an_caps.an_rates[2] == AN_RATE_5_5MBPS) { - ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS5, - IFM_IEEE80211_ADHOC, 0), 0); - ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS5, 0, 0), 0); - } - if (sc->an_caps.an_rates[3] == AN_RATE_11MBPS) { - ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS11, - IFM_IEEE80211_ADHOC, 0), 0); - ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS11, 0, 0), 0); - } - ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_AUTO, - IFM_IEEE80211_ADHOC, 0), 0); - ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_AUTO, 0, 0), 0); -#undef ADD - ifmedia_set(&sc->an_ifmedia, IFM_MAKEWORD(IFM_IEEE80211, IFM_AUTO, - 0, 0)); + for (chan = chan_min; chan <= chan_max; chan++) { + ic->ic_channels[chan].ic_freq = + ieee80211_ieee2mhz(chan, IEEE80211_CHAN_2GHZ); + ic->ic_channels[chan].ic_flags = IEEE80211_CHAN_B; + } + ic->ic_ibss_chan = &ic->ic_channels[chan_min]; + + /* Find supported rate */ + for (i = 0; i < sizeof(sc->sc_caps.an_rates); i++) { + if (sc->sc_caps.an_rates[i] == 0) + continue; + ic->ic_sup_rates[IEEE80211_MODE_11B].rs_rates[ + ic->ic_sup_rates[IEEE80211_MODE_11B].rs_nrates++] = + sc->sc_caps.an_rates[i]; + } /* - * Call MI attach routines. + * Call MI attach routine. */ if_attach(ifp); - ether_ifattach(ifp); - timeout_set(&sc->an_stat_ch, an_stats_update, sc); -#if NBPFILTER > 0 - BPFATTACH(&sc->sc_ic.ic_ac.ac_if.if_bpf, ifp, DLT_EN10MB, - sizeof(struct ether_header)); -#endif + ieee80211_ifattach(ifp); + + sc->sc_newstate = ic->ic_newstate; + ic->ic_newstate = an_newstate; + + ieee80211_media_init(ifp, an_media_change, an_media_status); shutdownhook_establish(an_shutdown, sc); - an_reset(sc); - an_init(sc); + sc->sc_attached = 1; return(0); } void -an_rxeof(sc) - struct an_softc *sc; +an_rxeof(struct an_softc *sc) { - struct ieee80211com *ic = &sc->sc_ic; - struct ifnet *ifp = &ic->ic_if; - struct ether_header *eh; -#ifdef ANCACHE - struct an_rxframe rx_frame; -#endif - struct an_rxframe_802_3 rx_frame_802_3; - struct mbuf *m; - int id, len, error = 0; + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; + struct ieee80211_frame *wh; + struct ieee80211_node *ni; + struct an_rxframe frmhdr; + struct mbuf *m; + u_int16_t status; + int fid, gaplen, len, off; + uint8_t *gap; - id = CSR_READ_2(sc, AN_RX_FID); + fid = CSR_READ_2(sc, AN_RX_FID); -#ifdef ANCACHE - /* Read NIC frame header */ - if (an_read_data(sc, id, 0, (caddr_t)&rx_frame, sizeof(rx_frame))) { - ifp->if_ierrors++; - return; - } -#endif - /* Read in the 802_3 frame header */ - if (an_read_data(sc, id, 0x34, (caddr_t)&rx_frame_802_3, - sizeof(rx_frame_802_3))) { + /* First read in the frame header */ + if (an_read_bap(sc, fid, 0, &frmhdr, sizeof(frmhdr)) != 0) { + CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_RX); ifp->if_ierrors++; + DPRINTF(("an_rxeof: read fid %x failed\n", fid)); return; } - if (rx_frame_802_3.an_rx_802_3_status != 0) { + status = letoh16(frmhdr.an_rx_status); + if ((status & AN_STAT_ERRSTAT) != 0 && + ic->ic_opmode != IEEE80211_M_MONITOR) { + CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_RX); ifp->if_ierrors++; + DPRINTF(("an_rxeof: fid %x status %x\n", fid, status)); return; } - /* Check for insane frame length */ - len = letoh16(rx_frame_802_3.an_rx_802_3_payload_len); - if (len + ETHER_HDR_LEN + 2 > MCLBYTES) { - ifp->if_ierrors++; - return; + /* the payload length field includes a 16-bit "mystery field" */ + len = letoh16(frmhdr.an_rx_payload_len) - sizeof(uint16_t); + off = ALIGN(sizeof(struct ieee80211_frame)); + + if (off + len > MCLBYTES) { + if (ic->ic_opmode != IEEE80211_M_MONITOR) { + CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_RX); + ifp->if_ierrors++; + DPRINTF(("an_rxeof: oversized packet %d\n", len)); + return; + } + len = 0; } MGETHDR(m, M_DONTWAIT, MT_DATA); if (m == NULL) { + CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_RX); ifp->if_ierrors++; + DPRINTF(("an_rxeof: MGET failed\n")); return; } - MCLGET(m, M_DONTWAIT); - if (!(m->m_flags & M_EXT)) { - m_freem(m); - ifp->if_ierrors++; - return; + if (off + len + AN_GAPLEN_MAX > MHLEN) { + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_RX); + m_freem(m); + ifp->if_ierrors++; + DPRINTF(("an_rxeof: MCLGET failed\n")); + return; + } } - + m->m_data += off - sizeof(struct ieee80211_frame); + + if (ic->ic_opmode != IEEE80211_M_MONITOR) { + gaplen = letoh16(frmhdr.an_gaplen); + if (gaplen > AN_GAPLEN_MAX) { + CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_RX); + m_freem(m); + ifp->if_ierrors++; + DPRINTF(("%s: gap too long\n", __func__)); + return; + } + /* + * We don't need the 16-bit mystery field (payload length?), + * so read it into the region reserved for the 802.11 header. + * + * When Cisco Aironet 350 cards w/ firmware version 5 or + * greater operate with certain Cisco 350 APs, + * the "gap" is filled with the SNAP header. Read + * it in after the 802.11 header. + */ + gap = m->m_data + sizeof(struct ieee80211_frame) - + sizeof(uint16_t); + an_read_bap(sc, fid, -1, gap, gaplen + sizeof(u_int16_t)); + } else + gaplen = 0; + + an_read_bap(sc, fid, -1, + m->m_data + sizeof(struct ieee80211_frame) + gaplen, len); + m->m_pkthdr.len = m->m_len = sizeof(struct ieee80211_frame) + gaplen + + len; + + memcpy(m->m_data, &frmhdr.an_whdr, sizeof(struct ieee80211_frame)); m->m_pkthdr.rcvif = ifp; - m->m_pkthdr.len = m->m_len = len + 12; - m->m_data += ETHER_ALIGN; - eh = mtod(m, struct ether_header *); - - bcopy((char *)&rx_frame_802_3.an_rx_dst_addr, - (char *)&eh->ether_dhost, ETHER_ADDR_LEN); - bcopy((char *)&rx_frame_802_3.an_rx_src_addr, - (char *)&eh->ether_shost, ETHER_ADDR_LEN); - - /* in mbuf header type is just before payload */ - error = an_read_data(sc, id, 0x44, (caddr_t)&(eh->ether_type), len); - if (error) { - m_freem(m); - ifp->if_ierrors++; - return; - } - - ifp->if_ipackets++; + CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_RX); -#if NBPFILTER > 0 - /* Handle BPF listeners. */ - if (ifp->if_bpf) - BPF_MTAP(ifp, m); -#endif + wh = mtod(m, struct ieee80211_frame *); + if (wh->i_fc[1] & IEEE80211_FC1_WEP) { + /* + * WEP is decrypted by hardware. Clear WEP bit + * header for ieee80211_input(). + */ + wh->i_fc[1] &= ~IEEE80211_FC1_WEP; + } - /* Receive packet. */ -#ifdef ANCACHE - an_cache_store(sc, eh, m, rx_frame.an_rx_signal_strength); -#endif - ether_input_mbuf(ifp, m); + ni = ieee80211_find_rxnode(ic, wh); + ieee80211_input(ifp, m, ni, frmhdr.an_rx_signal_strength, + letoh32(frmhdr.an_rx_time)); + ieee80211_release_node(ic, ni); } void -an_txeof(sc, status) - struct an_softc *sc; - int status; +an_txeof(struct an_softc *sc, int status) { - struct ieee80211com *ic = &sc->sc_ic; - struct ifnet *ifp = &ic->ic_if; - int id; + struct ifnet *ifp = &sc->sc_ic.ic_if; + int cur, id; - ifp->if_timer = 0; + sc->sc_tx_timer = 0; ifp->if_flags &= ~IFF_OACTIVE; id = CSR_READ_2(sc, AN_TX_CMP_FID); + CSR_WRITE_2(sc, AN_EVENT_ACK, status & (AN_EV_TX | AN_EV_TX_EXC)); if (status & AN_EV_TX_EXC) ifp->if_oerrors++; else ifp->if_opackets++; - if (id != sc->an_rdata.an_tx_ring[sc->an_rdata.an_tx_cons]) - printf("%s: id mismatch: expected %x, got %x\n", ifp->if_xname, - sc->an_rdata.an_tx_ring[sc->an_rdata.an_tx_cons], id); - - sc->an_rdata.an_tx_ring[sc->an_rdata.an_tx_cons] = 0; - AN_INC(sc->an_rdata.an_tx_cons, AN_TX_RING_CNT); -} - -/* - * We abuse the stats updater to check the current NIC status. This - * is important because we don't want to allow transmissions until - * the NIC has synchronized to the current cell (either as the master - * in an ad-hoc group, or as a station connected to an access point). - */ -void -an_stats_update(xsc) - void *xsc; -{ - struct an_softc *sc = xsc; - struct ieee80211com *ic = &sc->sc_ic; - struct ifnet *ifp = &ic->ic_if; - int s; - - s = splnet(); - - sc->an_status.an_type = AN_RID_STATUS; - sc->an_status.an_len = sizeof(struct an_ltv_status); - an_read_record(sc, (struct an_ltv_gen *)&sc->an_status); - - if (sc->an_status.an_opmode & AN_STATUS_OPMODE_IN_SYNC) - sc->an_associated = 1; - else - sc->an_associated = 0; - - /* Don't do this while we're transmitting */ - if (!(ifp->if_flags & IFF_OACTIVE)) { - sc->an_stats.an_len = sizeof(struct an_ltv_stats); - sc->an_stats.an_type = AN_RID_32BITS_CUM; - an_read_record(sc, (struct an_ltv_gen *)&sc->an_stats.an_len); + cur = sc->sc_txcur; + if (sc->sc_txd[cur].d_fid == id) { + sc->sc_txd[cur].d_inuse = 0; + DPRINTF2(("an_txeof: sent %x/%d\n", id, cur)); + AN_INC(cur, AN_TX_RING_CNT); + sc->sc_txcur = cur; + } else { + for (cur = 0; cur < AN_TX_RING_CNT; cur++) { + if (id == sc->sc_txd[cur].d_fid) { + sc->sc_txd[cur].d_inuse = 0; + break; + } + } + if (ifp->if_flags & IFF_DEBUG) + printf("%s: tx mismatch: " + "expected %x(%d), actual %x(%d)\n", + sc->sc_dev.dv_xname, + sc->sc_txd[sc->sc_txcur].d_fid, sc->sc_txcur, + id, cur); } - - splx(s); - TIMEOUT(sc->an_stat_ch, an_stats_update, sc, hz); } int -an_intr(xsc) - void *xsc; +an_intr(void *arg) { - struct an_softc *sc = (struct an_softc*)xsc; - struct ieee80211com *ic = &sc->sc_ic; - struct ifnet *ifp = &ic->ic_if; - u_int16_t status; - - if (sc->an_gone) + struct an_softc *sc = arg; + struct ifnet *ifp = &sc->sc_ic.ic_if; + int i; + u_int16_t status; + + if (!sc->sc_enabled || sc->sc_invalid || + (sc->sc_dev.dv_flags & DVF_ACTIVE) == 0 || + (ifp->if_flags & IFF_RUNNING) == 0) return 0; - if (!(ifp->if_flags & IFF_UP)) { - CSR_WRITE_2(sc, AN_EVENT_ACK, 0xFFFF); + if ((ifp->if_flags & IFF_UP) == 0) { CSR_WRITE_2(sc, AN_INT_EN, 0); - return 0; + CSR_WRITE_2(sc, AN_EVENT_ACK, ~0); + return 1; } - /* Disable interrupts. */ - CSR_WRITE_2(sc, AN_INT_EN, 0); - - status = CSR_READ_2(sc, AN_EVENT_STAT); - CSR_WRITE_2(sc, AN_EVENT_ACK, ~AN_INTRS); - - if (status & AN_EV_AWAKE) { - CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_AWAKE); - } + /* maximum 10 loops per interrupt */ + for (i = 0; i < 10; i++) { + if (!sc->sc_enabled || sc->sc_invalid) + return 1; + if (CSR_READ_2(sc, AN_SW0) != AN_MAGIC) { + DPRINTF(("an_intr: magic number changed: %x\n", + CSR_READ_2(sc, AN_SW0))); + sc->sc_invalid = 1; + return 1; + } + status = CSR_READ_2(sc, AN_EVENT_STAT); + CSR_WRITE_2(sc, AN_EVENT_ACK, status & ~(AN_INTRS)); + if ((status & AN_INTRS) == 0) + break; - if (status & AN_EV_LINKSTAT) { - if (CSR_READ_2(sc, AN_LINKSTAT) == AN_LINKSTAT_ASSOCIATED) - sc->an_associated = 1; - else - sc->an_associated = 0; - CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_LINKSTAT); - } + if (status & AN_EV_RX) + an_rxeof(sc); - if (status & AN_EV_RX) { - an_rxeof(sc); - CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_RX); - } + if (status & (AN_EV_TX | AN_EV_TX_EXC)) + an_txeof(sc, status); - if (status & AN_EV_TX) { - an_txeof(sc, status); - CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_TX); - } + if (status & AN_EV_LINKSTAT) + an_linkstat_intr(sc); - if (status & AN_EV_TX_EXC) { - an_txeof(sc, status); - CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_TX_EXC); + if ((ifp->if_flags & IFF_OACTIVE) == 0 && + sc->sc_ic.ic_state == IEEE80211_S_RUN && + !IFQ_IS_EMPTY(&ifp->if_snd)) + an_start(ifp); } - if (status & AN_EV_ALLOC) - CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_ALLOC); - - /* Re-enable interrupts. */ - CSR_WRITE_2(sc, AN_INT_EN, AN_INTRS); - - if (!IFQ_IS_EMPTY(&ifp->if_snd)) - an_start(ifp); - return 1; } +/* Must be called at proper protection level! */ int -an_cmd(sc, cmd, val) - struct an_softc *sc; - int cmd; - int val; +an_cmd(struct an_softc *sc, int cmd, int val) { int i, stat; /* make sure previous command completed */ if (CSR_READ_2(sc, AN_COMMAND) & AN_CMD_BUSY) { - printf("%s: command busy\n", sc->sc_dev.dv_xname); + if (sc->sc_ic.ic_if.if_flags & IFF_DEBUG) + printf("%s: command 0x%x busy\n", sc->sc_dev.dv_xname, + CSR_READ_2(sc, AN_COMMAND)); CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_CLR_STUCK_BUSY); } CSR_WRITE_2(sc, AN_PARAM0, val); CSR_WRITE_2(sc, AN_PARAM1, 0); CSR_WRITE_2(sc, AN_PARAM2, 0); - DELAY(10); CSR_WRITE_2(sc, AN_COMMAND, cmd); - DELAY(10); - for (i = AN_TIMEOUT; i--; DELAY(10)) { + if (cmd == AN_CMD_FW_RESTART) { + /* XXX: should sleep here */ + DELAY(100*1000); + } + + for (i = 0; i < AN_TIMEOUT; i++) { if (CSR_READ_2(sc, AN_EVENT_STAT) & AN_EV_CMD) break; - else { - if (CSR_READ_2(sc, AN_COMMAND) == cmd) { - DELAY(10); - CSR_WRITE_2(sc, AN_COMMAND, cmd); - } - } + DELAY(10); } stat = CSR_READ_2(sc, AN_STATUS); - /* clear stuck command busy if needed */ - if (CSR_READ_2(sc, AN_COMMAND) & AN_CMD_BUSY) { + /* clear stuck command busy if necessary */ + if (CSR_READ_2(sc, AN_COMMAND) & AN_CMD_BUSY) CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_CLR_STUCK_BUSY); - } /* Ack the command */ CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_CMD); - if (i <= 0) - return(ETIMEDOUT); - - if (stat & AN_STAT_CMD_RESULT) - return(EIO); + if (i == AN_TIMEOUT) { + if (sc->sc_ic.ic_if.if_flags & IFF_DEBUG) + printf("%s: command 0x%x param 0x%x timeout\n", + sc->sc_dev.dv_xname, cmd, val); + return ETIMEDOUT; + } + if (stat & AN_STAT_CMD_RESULT) { + if (sc->sc_ic.ic_if.if_flags & IFF_DEBUG) + printf("%s: command 0x%x param 0x%x status 0x%x " + "resp 0x%x 0x%x 0x%x\n", + sc->sc_dev.dv_xname, cmd, val, stat, + CSR_READ_2(sc, AN_RESP0), CSR_READ_2(sc, AN_RESP1), + CSR_READ_2(sc, AN_RESP2)); + return EIO; + } - return(0); + return 0; } -/* - * This reset sequence may look a little strange, but this is the - * most reliable method I've found to really kick the NIC in the - * head and force it to reboot correctly. - */ -void -an_reset(sc) - struct an_softc *sc; +int +an_reset(struct an_softc *sc) { - if (sc->an_gone) - return; -/*printf("ena ");*/ + + DPRINTF(("an_reset\n")); + + if (!sc->sc_enabled) + return ENXIO; + an_cmd(sc, AN_CMD_ENABLE, 0); -/* printf("rst ");*/ an_cmd(sc, AN_CMD_FW_RESTART, 0); -/*printf("nop ");*/ an_cmd(sc, AN_CMD_NOOP2, 0); - if (an_cmd(sc, AN_CMD_FORCE_SYNCLOSS, 0) == ETIMEDOUT) + if (an_cmd(sc, AN_CMD_FORCE_SYNCLOSS, 0) == ETIMEDOUT) { printf("%s: reset failed\n", sc->sc_dev.dv_xname); + return ETIMEDOUT; + } an_cmd(sc, AN_CMD_DISABLE, 0); + return 0; } -/* - * Read an LTV record from the NIC. - */ -int -an_read_record(sc, ltv) - struct an_softc *sc; - struct an_ltv_gen *ltv; +void +an_linkstat_intr(struct an_softc *sc) { - u_int16_t *ptr, len, rlen, ltv_data_length; - volatile u_int16_t v; - int i; + struct ieee80211com *ic = &sc->sc_ic; + u_int16_t status; - if (ltv->an_len < 4 || ltv->an_type == 0) - return(EINVAL); + status = CSR_READ_2(sc, AN_LINKSTAT); + CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_LINKSTAT); + DPRINTF(("an_linkstat_intr: status 0x%x\n", status)); - /* Tell the NIC to enter record read mode. */ - if (an_cmd(sc, AN_CMD_ACCESS|AN_ACCESS_READ, ltv->an_type)) { - printf("%s: RID 0x%04x access failed\n", - sc->sc_dev.dv_xname, ltv->an_type); - return(EIO); - } - - /* Seek to the record. */ - if (an_seek(sc, ltv->an_type, 0, AN_BAP1)) { - printf("%s: RID 0x%04x seek to record failed\n", - sc->sc_dev.dv_xname, ltv->an_type); - return(EIO); + if (status == AN_LINKSTAT_ASSOCIATED) { + if (ic->ic_state != IEEE80211_S_RUN || + ic->ic_opmode == IEEE80211_M_IBSS) + ieee80211_new_state(ic, IEEE80211_S_RUN, -1); + } else { + if (ic->ic_opmode == IEEE80211_M_STA) + ieee80211_new_state(ic, IEEE80211_S_INIT, -1); } +} - /* - * Read the length to make sure it - * matches what we expect (this verifies that we have enough - * room to hold all of the returned data). - */ - rlen = len = CSR_READ_2(sc, AN_DATA1); - - /* - * Work out record's data length, which is struct length - type word - * as we have just read the length. - */ - ltv_data_length = ltv->an_len - sizeof(u_int16_t); - - if (rlen > ltv_data_length) - rlen = ltv_data_length; - - /* Now read the data. */ - len -= 2; rlen -= 2; /* skip the type */ - ptr = ltv->an_val; - for (i = 0; (rlen - i) > 1; i += 2) - *ptr++ = CSR_READ_2(sc, AN_DATA1); - if (rlen - i == 1) - *(u_int8_t *)ptr = CSR_READ_1(sc, AN_DATA1); - for (; i < len; i++) - v = CSR_READ_1(sc, AN_DATA1); +/* + * Wait for firmware come up after power enabled. + */ +void +an_wait(struct an_softc *sc) +{ + int i; -#if BYTE_ORDER == BIG_ENDIAN - switch (ltv->an_type) { - case AN_RID_GENCONFIG: - case AN_RID_ACTUALCFG: - an_swap16(<v->an_val[4], 7); /* an_macaddr, an_rates */ - an_swap16(<v->an_val[63], 8); /* an_nodename */ - break; - case AN_RID_SSIDLIST: - an_swap16(<v->an_val[1], 16); /* an_ssid1 */ - an_swap16(<v->an_val[18], 16); /* an_ssid2 */ - an_swap16(<v->an_val[35], 16); /* an_ssid3 */ - break; - case AN_RID_APLIST: - an_swap16(ltv->an_val, 12); - break; - case AN_RID_DRVNAME: - an_swap16(ltv->an_val, 8); - break; - case AN_RID_CAPABILITIES: - an_swap16(ltv->an_val, 2); /* an_oui */ - an_swap16(<v->an_val[3], 34); /* an_manufname .. an_aironetaddr */ - an_swap16(<v->an_val[39], 8); /* an_callid .. an_tx_diversity */ - break; - case AN_RID_STATUS: - an_swap16(<v->an_val[0], 3); /* an_macaddr */ - an_swap16(<v->an_val[7], 36); /* an_ssid .. an_prev_bssid3 */ - an_swap16(<v->an_val[0x74/2], 2); /* an_ap_ip_addr */ - break; - case AN_RID_WEP_VOLATILE: - case AN_RID_WEP_PERMANENT: - an_swap16(<v->an_val[1], 3); /* an_mac_addr */ - an_swap16(<v->an_val[5], 7); - break; - case AN_RID_32BITS_CUM: - for (i = 0x60; i--;) { - u_int16_t t = ltv->an_val[i * 2] ^ ltv->an_val[i * 2 + 1]; - ltv->an_val[i * 2] ^= t; - ltv->an_val[i * 2 + 1] ^= t; - } - break; + CSR_WRITE_2(sc, AN_COMMAND, AN_CMD_NOOP2); + for (i = 0; i < 3*hz; i++) { + if (CSR_READ_2(sc, AN_EVENT_STAT) & AN_EV_CMD) + break; + (void)tsleep(sc, PWAIT, "anatch", 1); } -#endif - return(0); + CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_CMD); } -/* - * Same as read, except we inject data instead of reading it. - */ int -an_write_record(sc, ltv) - struct an_softc *sc; - struct an_ltv_gen *ltv; +an_read_bap(struct an_softc *sc, int id, int off, void *buf, int buflen) { - u_int16_t *ptr; - int i; + int error, cnt; - if (an_cmd(sc, AN_CMD_ACCESS|AN_ACCESS_READ, ltv->an_type)) - return(EIO); + if (buflen == 0) + return 0; + if (off == -1) + off = sc->sc_bap_off; + if (id != sc->sc_bap_id || off != sc->sc_bap_off) { + if ((error = an_seek_bap(sc, id, off)) != 0) + return EIO; + } - if (an_seek(sc, ltv->an_type, 0, AN_BAP1)) - return(EIO); + cnt = (buflen + 1) / 2; + CSR_READ_MULTI_STREAM_2(sc, AN_DATA0, (u_int16_t *)buf, cnt); + sc->sc_bap_off += cnt * 2; #if BYTE_ORDER == BIG_ENDIAN - switch (ltv->an_type) { + switch(id) { case AN_RID_GENCONFIG: case AN_RID_ACTUALCFG: - an_swap16(<v->an_val[4], 7); /* an_macaddr, an_rates */ - an_swap16(<v->an_val[63], 8); /* an_nodename */ break; case AN_RID_SSIDLIST: - an_swap16(<v->an_val[1], 16); /* an_ssid1 */ - an_swap16(<v->an_val[18], 16); /* an_ssid2 */ - an_swap16(<v->an_val[35], 16); /* an_ssid3 */ break; case AN_RID_APLIST: - an_swap16(ltv->an_val, 12); break; case AN_RID_DRVNAME: - an_swap16(ltv->an_val, 8); break; case AN_RID_CAPABILITIES: - an_swap16(ltv->an_val, 2); /* an_oui */ - an_swap16(<v->an_val[3], 34); /* an_manufname .. an_aironetaddr */ - an_swap16(<v->an_val[39], 8); /* an_callid .. an_tx_diversity */ break; case AN_RID_STATUS: - an_swap16(<v->an_val[0], 3); /* an_macaddr */ - an_swap16(<v->an_val[7], 36); /* an_ssid .. an_prev_bssid3 */ - an_swap16(<v->an_val[0x74/2], 2); /* an_ap_ip_addr */ break; case AN_RID_WEP_VOLATILE: case AN_RID_WEP_PERMANENT: - an_swap16(<v->an_val[1], 3); /* an_mac_addr */ - an_swap16(<v->an_val[5], 7); break; + case AN_RID_32BITS_CUM: + break; + } #endif - CSR_WRITE_2(sc, AN_DATA1, ltv->an_len); - - ptr = ltv->an_val; - for (i = 0; i < (ltv->an_len - 1) >> 1; i++) - CSR_WRITE_2(sc, AN_DATA1, ptr[i]); - - if (an_cmd(sc, AN_CMD_ACCESS|AN_ACCESS_WRITE, ltv->an_type)) - return(EIO); - - return(0); + return 0; } int -an_seek(sc, id, off, chan) - struct an_softc *sc; - int id, off, chan; +an_write_bap(struct an_softc *sc, int id, int off, void *buf, int buflen) { - int i; - int selreg, offreg; - - switch (chan) { - case AN_BAP0: - selreg = AN_SEL0; - offreg = AN_OFF0; - break; - case AN_BAP1: - selreg = AN_SEL1; - offreg = AN_OFF1; - break; - default: - printf("%s: invalid data path: %x\n", - sc->sc_dev.dv_xname, chan); - return (EIO); - } - - CSR_WRITE_2(sc, selreg, id); - CSR_WRITE_2(sc, offreg, off); + int error, cnt; - for (i = AN_TIMEOUT; i--; DELAY(10)) { - if (!(CSR_READ_2(sc, offreg) & (AN_OFF_BUSY|AN_OFF_ERR))) - break; + if (buflen == 0) + return 0; + if (off == -1) + off = sc->sc_bap_off; + if (id != sc->sc_bap_id || off != sc->sc_bap_off) { + if ((error = an_seek_bap(sc, id, off)) != 0) + return EIO; } - if (i <= 0) - return(ETIMEDOUT); - - return (0); + cnt = (buflen + 1) / 2; + CSR_WRITE_MULTI_STREAM_2(sc, AN_DATA0, (u_int16_t *)buf, cnt); + sc->sc_bap_off += cnt * 2; + return 0; } int -an_read_data(sc, id, off, buf, len) - struct an_softc *sc; - int id, off; - caddr_t buf; - int len; +an_seek_bap(struct an_softc *sc, int id, int off) { - if (off != -1 && an_seek(sc, id, off, AN_BAP1)) - return(EIO); + int i, status; - bus_space_read_raw_multi_2(sc->an_btag, sc->an_bhandle, - AN_DATA1, buf, len & ~1); - if (len & 1) - ((u_int8_t *)buf)[len - 1] = CSR_READ_1(sc, AN_DATA1); + CSR_WRITE_2(sc, AN_SEL0, id); + CSR_WRITE_2(sc, AN_OFF0, off); - return (0); + for (i = 0; ; i++) { + status = CSR_READ_2(sc, AN_OFF0); + if ((status & AN_OFF_BUSY) == 0) + break; + if (i == AN_TIMEOUT) { + printf("%s: timeout in an_seek_bap to 0x%x/0x%x\n", + sc->sc_dev.dv_xname, id, off); + sc->sc_bap_off = AN_OFF_ERR; /* invalidate */ + return ETIMEDOUT; + } + DELAY(10); + } + if (status & AN_OFF_ERR) { + printf("%s: failed in an_seek_bap to 0x%x/0x%x\n", + sc->sc_dev.dv_xname, id, off); + sc->sc_bap_off = AN_OFF_ERR; /* invalidate */ + return EIO; + } + sc->sc_bap_id = id; + sc->sc_bap_off = off; + return 0; } int -an_write_data(sc, id, off, buf, len) - struct an_softc *sc; - int id, off; - caddr_t buf; - int len; +an_mwrite_bap(struct an_softc *sc, int id, int off, struct mbuf *m, int totlen) { - if (off != -1 && an_seek(sc, id, off, AN_BAP0)) - return(EIO); + int error, len, cnt; - bus_space_write_raw_multi_2(sc->an_btag, sc->an_bhandle, - AN_DATA0, buf, len & ~1); - if (len & 1) - CSR_WRITE_1(sc, AN_DATA0, ((u_int8_t *)buf)[len - 1]); + if (off == -1) + off = sc->sc_bap_off; + if (id != sc->sc_bap_id || off != sc->sc_bap_off) { + if ((error = an_seek_bap(sc, id, off)) != 0) + return EIO; + } - return (0); + for (len = 0; m != NULL; m = m->m_next) { + if (m->m_len == 0) + continue; + len = min(m->m_len, totlen); + + if ((mtod(m, u_long) & 0x1) || (len & 0x1)) { + m_copydata(m, 0, totlen, (caddr_t)&sc->sc_buf.sc_txbuf); + cnt = (totlen + 1) / 2; + CSR_WRITE_MULTI_STREAM_2(sc, AN_DATA0, + sc->sc_buf.sc_val, cnt); + off += cnt * 2; + break; + } + cnt = len / 2; + CSR_WRITE_MULTI_STREAM_2(sc, AN_DATA0, mtod(m, u_int16_t *), + cnt); + off += len; + totlen -= len; + } + sc->sc_bap_off = off; + return 0; } -/* - * Allocate a region of memory inside the NIC and zero - * it out. - */ int -an_alloc_nicmem(sc, len, id) - struct an_softc *sc; - int len; - int *id; +an_alloc_nicmem(struct an_softc *sc, int len, int *idp) { - int i; + int i; if (an_cmd(sc, AN_CMD_ALLOC_MEM, len)) { printf("%s: failed to allocate %d bytes on NIC\n", @@ -845,162 +787,78 @@ an_alloc_nicmem(sc, len, id) return(ENOMEM); } - for (i = AN_TIMEOUT; i--; DELAY(10)) { + for (i = 0; i < AN_TIMEOUT; i++) { if (CSR_READ_2(sc, AN_EVENT_STAT) & AN_EV_ALLOC) break; + if (i == AN_TIMEOUT) { + printf("%s: timeout in alloc\n", sc->sc_dev.dv_xname); + return ETIMEDOUT; + } + DELAY(10); } - if (i <= 0) - return(ETIMEDOUT); - + *idp = CSR_READ_2(sc, AN_ALLOC_FID); CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_ALLOC); - *id = CSR_READ_2(sc, AN_ALLOC_FID); - - if (an_seek(sc, *id, 0, AN_BAP0)) - return(EIO); - - bus_space_set_multi_2(sc->an_btag, sc->an_bhandle, - AN_DATA0, 0, len / 2); - CSR_WRITE_1(sc, AN_DATA0, 0); - - return(0); + return 0; } -void -an_setdef(sc, areq) - struct an_softc *sc; - struct an_req *areq; +int +an_read_rid(struct an_softc *sc, int rid, void *buf, int *buflenp) { - struct ieee80211com *ic = &sc->sc_ic; - struct ifnet *ifp = &ic->ic_if; - struct an_ltv_genconfig *cfg; - struct an_ltv_ssidlist *ssid; - struct an_ltv_aplist *ap; - struct an_ltv_gen *sp; - - switch (areq->an_type) { - case AN_RID_GENCONFIG: - cfg = (struct an_ltv_genconfig *)areq; - bcopy((char *)&cfg->an_macaddr, - (char *)&sc->sc_ic.ic_ac.ac_enaddr, ETHER_ADDR_LEN); - bcopy((char *)&cfg->an_macaddr, LLADDR(ifp->if_sadl), - ETHER_ADDR_LEN); - - bcopy((char *)cfg, (char *)&sc->an_config, - sizeof(struct an_ltv_genconfig)); - break; - case AN_RID_SSIDLIST: - ssid = (struct an_ltv_ssidlist *)areq; - bcopy((char *)ssid, (char *)&sc->an_ssidlist, - sizeof(struct an_ltv_ssidlist)); - break; - case AN_RID_APLIST: - ap = (struct an_ltv_aplist *)areq; - bcopy((char *)ap, (char *)&sc->an_aplist, - sizeof(struct an_ltv_aplist)); - break; - case AN_RID_TX_SPEED: - sp = (struct an_ltv_gen *)areq; - sc->an_tx_rate = sp->an_val[0]; - break; - case AN_RID_WEP_VOLATILE: - /* Disable the MAC */ - an_cmd(sc, AN_CMD_DISABLE, 0); - - /* Just write the key, we don't want to save it */ - an_write_record(sc, (struct an_ltv_gen *)areq); + int error; + u_int16_t len; - /* Turn the MAC back on */ - an_cmd(sc, AN_CMD_ENABLE, 0); - - break; - case AN_RID_WEP_PERMANENT: - /* Disable the MAC */ - an_cmd(sc, AN_CMD_DISABLE, 0); - - /* Just write the key, the card will save it in this mode */ - an_write_record(sc, (struct an_ltv_gen *)areq); + /* Tell the NIC to enter record read mode. */ + error = an_cmd(sc, AN_CMD_ACCESS | AN_ACCESS_READ, rid); + if (error) + return error; - /* Turn the MAC back on */ - an_cmd(sc, AN_CMD_ENABLE, 0); + /* length in byte, including length itself */ + error = an_read_bap(sc, rid, 0, &len, sizeof(len)); + if (error) + return error; - break; - default: - printf("%s: unknown RID: %x\n", - sc->sc_dev.dv_xname, areq->an_type); - return; + len = letoh16(len) - 2; + if (*buflenp < len) { + printf("%s: record buffer is too small, " + "rid=%x, size=%d, len=%d\n", + sc->sc_dev.dv_xname, rid, *buflenp, len); + return ENOSPC; } - - /* Reinitialize the card. */ - if (ifp->if_flags & IFF_UP) - an_init(sc); + *buflenp = len; + return an_read_bap(sc, rid, sizeof(len), buf, len); } -/* - * We can't change the NIC configuration while the MAC is enabled, - * so in order to turn on RX monitor mode, we have to turn the MAC - * off first. - */ -void -an_promisc(sc, promisc) - struct an_softc *sc; - int promisc; +int +an_write_rid(struct an_softc *sc, int rid, void *buf, int buflen) { - struct an_ltv_genconfig genconf; + int error; + u_int16_t len; - /* Disable the MAC. */ - an_cmd(sc, AN_CMD_DISABLE, 0); + /* length in byte, including length itself */ + len = htole16(buflen + 2); - /* Set RX mode. */ - if (promisc && - !(sc->an_config.an_rxmode & AN_RXMODE_LAN_MONITOR_CURBSS)) { - sc->an_rxmode = sc->an_config.an_rxmode; - sc->an_config.an_rxmode |= - AN_RXMODE_LAN_MONITOR_CURBSS; - } else { - sc->an_config.an_rxmode = sc->an_rxmode; - } + error = an_write_bap(sc, rid, 0, &len, sizeof(len)); + if (error) + return error; + error = an_write_bap(sc, rid, sizeof(len), buf, buflen); + if (error) + return error; - /* Transfer the configuration to the NIC */ - genconf = sc->an_config; - genconf.an_len = sizeof(struct an_ltv_genconfig); - genconf.an_type = AN_RID_GENCONFIG; - if (an_write_record(sc, (struct an_ltv_gen *)&genconf)) { - printf("%s: failed to set configuration\n", - sc->sc_dev.dv_xname); - return; - } - /* Turn the MAC back on. */ - an_cmd(sc, AN_CMD_ENABLE, 0); + return an_cmd(sc, AN_CMD_ACCESS | AN_ACCESS_WRITE, rid); } int -an_ioctl(ifp, command, data) - struct ifnet *ifp; - u_long command; - caddr_t data; +an_ioctl(struct ifnet *ifp, u_long command, caddr_t data) { - int s, error = 0; - struct an_softc *sc; - struct an_req areq; - struct ifreq *ifr; - struct proc *p = curproc; - struct ifaddr *ifa = (struct ifaddr *)data; - - s = splnet(); + struct an_softc *sc = ifp->if_softc; + struct ifaddr *ifa = (struct ifaddr *)data; + int s, error = 0; - sc = ifp->if_softc; - ifr = (struct ifreq *)data; + if ((sc->sc_dev.dv_flags & DVF_ACTIVE) == 0) + return ENXIO; - if (sc->an_gone) { - splx(s); - return(ENODEV); - } - - if ((error = ether_ioctl(ifp, &sc->sc_ic.ic_ac, command, data)) > 0) { - splx(s); - return error; - } + s = splnet(); switch(command) { case SIOCSIFADDR: @@ -1008,593 +866,759 @@ an_ioctl(ifp, command, data) switch (ifa->ifa_addr->sa_family) { #ifdef INET case AF_INET: - an_init(sc); + error = an_init(ifp); arp_ifinit(&sc->sc_ic.ic_ac, ifa); break; #endif default: - an_init(sc); + error = an_init(ifp); break; } break; case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { - if (ifp->if_flags & IFF_RUNNING && - ifp->if_flags & IFF_PROMISC && - !(sc->an_if_flags & IFF_PROMISC)) { - an_promisc(sc, 1); - } else if (ifp->if_flags & IFF_RUNNING && - !(ifp->if_flags & IFF_PROMISC) && - sc->an_if_flags & IFF_PROMISC) { - an_promisc(sc, 0); - an_reset(sc); - } - an_init(sc); - } else { - if (ifp->if_flags & IFF_RUNNING) - an_stop(sc); - } - sc->an_if_flags = ifp->if_flags; - error = 0; - break; - case SIOCSIFMEDIA: - case SIOCGIFMEDIA: - error = ifmedia_ioctl(ifp, ifr, &sc->an_ifmedia, command); + if (sc->sc_enabled) { + /* + * To avoid rescanning another access point, + * do not call an_init() here. Instead, only + * reflect promisc mode settings. + */ + error = an_cmd(sc, AN_CMD_SET_MODE, + (ifp->if_flags & IFF_PROMISC) ? 0xffff : 0); + } else + error = an_init(ifp); + } else if (sc->sc_enabled) + an_stop(ifp, 1); break; case SIOCADDMULTI: case SIOCDELMULTI: /* The Aironet has no multicast filter. */ error = 0; break; - case SIOCGAIRONET: - error = copyin(ifr->ifr_data, &areq, sizeof(areq)); - if (error) - break; -#ifdef ANCACHE - if (areq.an_type == AN_RID_ZERO_CACHE) { - error = suser(p, 0); - if (error) - break; - sc->an_sigitems = sc->an_nextitem = 0; - break; - } else if (areq.an_type == AN_RID_READ_CACHE) { - char *pt = (char *)&areq.an_val; - bcopy((char *)&sc->an_sigitems, (char *)pt, - sizeof(int)); - pt += sizeof(int); - areq.an_len = sizeof(int) / 2; - bcopy((char *)&sc->an_sigcache, (char *)pt, - sizeof(struct an_sigcache) * sc->an_sigitems); - areq.an_len += ((sizeof(struct an_sigcache) * - sc->an_sigitems) / 2) + 1; - } else -#endif - if (an_read_record(sc, (struct an_ltv_gen *)&areq)) { - error = EINVAL; + case SIOCS80211NWKEY: + error = an_set_nwkey(sc, (struct ieee80211_nwkey *)data); break; - } - error = copyout(&areq, ifr->ifr_data, sizeof(areq)); - break; - case SIOCSAIRONET: - error = suser(p, 0); - if (error) - break; - error = copyin(ifr->ifr_data, &areq, sizeof(areq)); - if (error) - break; - an_setdef(sc, &areq); + case SIOCG80211NWKEY: + error = an_get_nwkey(sc, (struct ieee80211_nwkey *)data); break; default: - error = EINVAL; + error = ieee80211_ioctl(ifp, command, data); break; } - + if (error == ENETRESET) { + if (sc->sc_enabled) + error = an_init(ifp); + else + error = 0; + } splx(s); - return(error); } int -an_init_tx_ring(sc) - struct an_softc *sc; +an_init(struct ifnet *ifp) { - int i; - int id; - - if (sc->an_gone) - return (0); - - for (i = 0; i < AN_TX_RING_CNT; i++) { - if (an_alloc_nicmem(sc, ETHER_MAX_LEN + 0x44, &id)) - return(ENOMEM); - sc->an_rdata.an_tx_fids[i] = id; - sc->an_rdata.an_tx_ring[i] = 0; + struct an_softc *sc = ifp->if_softc; + struct ieee80211com *ic = &sc->sc_ic; + int i, error, fid; + + DPRINTF(("an_init: enabled %d\n", sc->sc_enabled)); + if (!sc->sc_enabled) { + if (sc->sc_enable) + (*sc->sc_enable)(sc); + an_wait(sc); + sc->sc_enabled = 1; + } else { + an_stop(ifp, 0); + if ((error = an_reset(sc)) != 0) { + printf("%s: failed to reset\n", ifp->if_xname); + an_stop(ifp, 1); + return error; + } } - - sc->an_rdata.an_tx_prod = 0; - sc->an_rdata.an_tx_cons = 0; - - return(0); -} - -void -an_init(sc) - struct an_softc *sc; -{ - struct ieee80211com *ic = &sc->sc_ic; - struct ifnet *ifp = &ic->ic_if; - struct an_ltv_ssidlist ssid; - struct an_ltv_aplist aplist; - struct an_ltv_genconfig genconf; - int s; - - if (sc->an_gone) - return; - - s = splnet(); - - if (ifp->if_flags & IFF_RUNNING) - an_stop(sc); - - sc->an_associated = 0; + CSR_WRITE_2(sc, AN_SW0, AN_MAGIC); /* Allocate the TX buffers */ - if (an_init_tx_ring(sc)) { - printf("%s: tx buffer allocation failed\n", - sc->sc_dev.dv_xname); - splx(s); - return; + for (i = 0; i < AN_TX_RING_CNT; i++) { + if ((error = an_alloc_nicmem(sc, AN_TX_MAX_LEN, &fid)) != 0) { + printf("%s: failed to allocate nic memory\n", + ifp->if_xname); + an_stop(ifp, 1); + return error; + } + DPRINTF2(("an_init: txbuf %d allocated %x\n", i, fid)); + sc->sc_txd[i].d_fid = fid; + sc->sc_txd[i].d_inuse = 0; } - - /* Set our MAC address. */ - bcopy((char *)&sc->sc_ic.ic_ac.ac_enaddr, - (char *)&sc->an_config.an_macaddr, ETHER_ADDR_LEN); - - if (ifp->if_flags & IFF_BROADCAST) - sc->an_config.an_rxmode = AN_RXMODE_BC_ADDR; + sc->sc_txcur = sc->sc_txnext = 0; + + IEEE80211_ADDR_COPY(sc->sc_config.an_macaddr, ic->ic_myaddr); + sc->sc_config.an_scanmode = htole16(AN_SCANMODE_ACTIVE); + sc->sc_config.an_authtype = htole16(AN_AUTHTYPE_OPEN); /*XXX*/ + if (ic->ic_flags & IEEE80211_F_WEPON) { + sc->sc_config.an_authtype |= + htole16(AN_AUTHTYPE_PRIVACY_IN_USE); + } + sc->sc_config.an_listen_interval = htole16(ic->ic_lintval); + sc->sc_config.an_beacon_period = htole16(ic->ic_lintval); + if (ic->ic_flags & IEEE80211_F_PMGTON) + sc->sc_config.an_psave_mode = htole16(AN_PSAVE_PSP); else - sc->an_config.an_rxmode = AN_RXMODE_ADDR; - - if (ifp->if_flags & IFF_MULTICAST) - sc->an_config.an_rxmode = AN_RXMODE_BC_MC_ADDR; - - /* Initialize promisc mode. */ - if (ifp->if_flags & IFF_PROMISC) - sc->an_config.an_rxmode |= AN_RXMODE_LAN_MONITOR_CURBSS; - - sc->an_rxmode = sc->an_config.an_rxmode; + sc->sc_config.an_psave_mode = htole16(AN_PSAVE_CAM); + sc->sc_config.an_ds_channel = + htole16(ieee80211_chan2ieee(ic, ic->ic_ibss_chan)); + + switch (ic->ic_opmode) { + case IEEE80211_M_STA: + sc->sc_config.an_opmode = + htole16(AN_OPMODE_INFRASTRUCTURE_STATION); + sc->sc_config.an_rxmode = htole16(AN_RXMODE_BC_MC_ADDR); + break; + case IEEE80211_M_IBSS: + sc->sc_config.an_opmode = htole16(AN_OPMODE_IBSS_ADHOC); + sc->sc_config.an_rxmode = htole16(AN_RXMODE_BC_MC_ADDR); + break; + case IEEE80211_M_MONITOR: + sc->sc_config.an_opmode = + htole16(AN_OPMODE_INFRASTRUCTURE_STATION); + sc->sc_config.an_rxmode = + htole16(AN_RXMODE_80211_MONITOR_ANYBSS); + sc->sc_config.an_authtype = htole16(AN_AUTHTYPE_NONE); + if (ic->ic_flags & IEEE80211_F_WEPON) + sc->sc_config.an_authtype |= + htole16(AN_AUTHTYPE_PRIVACY_IN_USE | + AN_AUTHTYPE_ALLOW_UNENCRYPTED); + break; + default: + printf("%s: bad opmode %d\n", ifp->if_xname, ic->ic_opmode); + an_stop(ifp, 1); + return EIO; + } + sc->sc_config.an_rxmode |= htole16(AN_RXMODE_NO_8023_HEADER); /* Set the ssid list */ - ssid = sc->an_ssidlist; - ssid.an_type = AN_RID_SSIDLIST; - ssid.an_len = sizeof(struct an_ltv_ssidlist); - if (an_write_record(sc, (struct an_ltv_gen *)&ssid)) { - printf("%s: failed to set ssid list\n", sc->sc_dev.dv_xname); - splx(s); - return; + memset(&sc->sc_buf, 0, sizeof(sc->sc_buf.sc_ssidlist)); + sc->sc_buf.sc_ssidlist.an_entry[0].an_ssid_len = + htole16(ic->ic_des_esslen); + if (ic->ic_des_esslen) + memcpy(sc->sc_buf.sc_ssidlist.an_entry[0].an_ssid, + ic->ic_des_essid, ic->ic_des_esslen); + if (an_write_rid(sc, AN_RID_SSIDLIST, &sc->sc_buf, + sizeof(sc->sc_buf.sc_ssidlist)) != 0) { + printf("%s: failed to write ssid list\n", ifp->if_xname); + an_stop(ifp, 1); + return error; } /* Set the AP list */ - aplist = sc->an_aplist; - aplist.an_type = AN_RID_APLIST; - aplist.an_len = sizeof(struct an_ltv_aplist); - if (an_write_record(sc, (struct an_ltv_gen *)&aplist)) { - printf("%s: failed to set AP list\n", sc->sc_dev.dv_xname); - splx(s); - return; + memset(&sc->sc_buf, 0, sizeof(sc->sc_buf.sc_aplist)); + (void)an_write_rid(sc, AN_RID_APLIST, &sc->sc_buf, + sizeof(sc->sc_buf.sc_aplist)); + + /* Set the encapsulation */ + for (i = 0; i < AN_ENCAP_NENTS; i++) { + sc->sc_buf.sc_encap.an_entry[i].an_ethertype = htole16(0); + sc->sc_buf.sc_encap.an_entry[i].an_action = + htole16(AN_RXENCAP_RFC1024 | AN_TXENCAP_RFC1024); } - - /* Set the configuration in the NIC */ - genconf = sc->an_config; - genconf.an_len = sizeof(struct an_ltv_genconfig); - genconf.an_type = AN_RID_GENCONFIG; - if (an_write_record(sc, (struct an_ltv_gen *)&genconf)) { - printf("%s: failed to set configuration\n", - sc->sc_dev.dv_xname); - splx(s); - return; + (void)an_write_rid(sc, AN_RID_ENCAP, &sc->sc_buf, + sizeof(sc->sc_buf.sc_encap)); + + /* Set the WEP Keys */ + if (ic->ic_flags & IEEE80211_F_WEPON) + an_write_wepkey(sc, AN_RID_WEP_VOLATILE, sc->sc_wepkeys, + sc->sc_tx_key); + + /* Set the configuration */ + if (an_write_rid(sc, AN_RID_GENCONFIG, &sc->sc_config, + sizeof(sc->sc_config)) != 0) { + printf("%s: failed to write config\n", ifp->if_xname); + an_stop(ifp, 1); + return error; } /* Enable the MAC */ if (an_cmd(sc, AN_CMD_ENABLE, 0)) { printf("%s: failed to enable MAC\n", sc->sc_dev.dv_xname); - splx(s); - return; + an_stop(ifp, 1); + return ENXIO; } - - /* enable interrupts */ - CSR_WRITE_2(sc, AN_INT_EN, AN_INTRS); - - splx(s); + if (ifp->if_flags & IFF_PROMISC) + an_cmd(sc, AN_CMD_SET_MODE, 0xffff); ifp->if_flags |= IFF_RUNNING; ifp->if_flags &= ~IFF_OACTIVE; + ic->ic_state = IEEE80211_S_INIT; + if (ic->ic_opmode == IEEE80211_M_MONITOR) + ieee80211_new_state(ic, IEEE80211_S_RUN, -1); - TIMEOUT(sc->an_stat_ch, an_stats_update, sc, hz); + /* enable interrupts */ + CSR_WRITE_2(sc, AN_INT_EN, AN_INTRS); + return 0; } void -an_start(ifp) - struct ifnet *ifp; +an_start(struct ifnet *ifp) { - struct an_softc *sc; - struct mbuf *m0 = NULL; - struct an_txframe_802_3 tx_frame_802_3; - struct ether_header *eh; - u_int16_t len; - int id; - int idx; - unsigned char txcontrol; - int pkts = 0; - - sc = ifp->if_softc; - - if (sc->an_gone) - return; - - if (ifp->if_flags & IFF_OACTIVE) - return; + struct an_softc *sc = (struct an_softc *)ifp->if_softc; + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_node *ni; + struct ieee80211_frame *wh; + struct an_txframe frmhdr; + struct mbuf *m; + u_int16_t len; + int cur, fid; - if (!sc->an_associated) + if (!sc->sc_enabled || sc->sc_invalid) { + DPRINTF(("an_start: noop: enabled %d invalid %d\n", + sc->sc_enabled, sc->sc_invalid)); return; + } - idx = sc->an_rdata.an_tx_prod; - bzero((char *)&tx_frame_802_3, sizeof(tx_frame_802_3)); - - while(sc->an_rdata.an_tx_ring[idx] == 0) { - IFQ_DEQUEUE(&ifp->if_snd, m0); - if (m0 == NULL) + memset(&frmhdr, 0, sizeof(frmhdr)); + cur = sc->sc_txnext; + for (;;) { + if (ic->ic_state != IEEE80211_S_RUN) { + DPRINTF(("an_start: not running %d\n", ic->ic_state)); break; - - pkts++; - id = sc->an_rdata.an_tx_fids[idx]; - eh = mtod(m0, struct ether_header *); - - bcopy((char *)&eh->ether_dhost, - (char *)&tx_frame_802_3.an_tx_dst_addr, ETHER_ADDR_LEN); - bcopy((char *)&eh->ether_shost, - (char *)&tx_frame_802_3.an_tx_src_addr, ETHER_ADDR_LEN); - - len = m0->m_pkthdr.len - 12; /* minus src/dest mac & type */ - tx_frame_802_3.an_tx_802_3_payload_len = htole16(len); - - m_copydata(m0, sizeof(struct ether_header) - 2, len, - (caddr_t)&sc->an_txbuf); - - txcontrol=AN_TXCTL_8023; - /* write the txcontrol only */ - an_write_data(sc, id, 0x08, (caddr_t)&txcontrol, - sizeof(txcontrol)); - - /* 802_3 header */ - an_write_data(sc, id, 0x34, (caddr_t)&tx_frame_802_3, - sizeof(struct an_txframe_802_3)); - - /* in mbuf header type is just before payload */ - an_write_data(sc, id, 0x44, (caddr_t)&sc->an_txbuf, len); - - /* - * If there's a BPF listener, bounce a copy of - * this frame to him. - */ + } + IFQ_POLL(&ifp->if_snd, m); + if (m == NULL) { + DPRINTF2(("an_start: no pending mbuf\n")); + break; + } + if (sc->sc_txd[cur].d_inuse) { + DPRINTF2(("an_start: %x/%d busy\n", + sc->sc_txd[cur].d_fid, cur)); + ifp->if_flags |= IFF_OACTIVE; + break; + } + IFQ_DEQUEUE(&ifp->if_snd, m); + ifp->if_opackets++; #if NBPFILTER > 0 if (ifp->if_bpf) - BPF_MTAP(ifp, m0); + bpf_mtap(ifp->if_bpf, m); +#endif + if ((m = ieee80211_encap(ifp, m, &ni)) == NULL) { + ifp->if_oerrors++; + continue; + } + if (ni != NULL) + ieee80211_release_node(ic, ni); +#if NBPFILTER > 0 + if (ic->ic_rawbpf) + bpf_mtap(ic->ic_rawbpf, m); #endif - m_freem(m0); - m0 = NULL; + wh = mtod(m, struct ieee80211_frame *); + if (ic->ic_flags & IEEE80211_F_WEPON) + wh->i_fc[1] |= IEEE80211_FC1_WEP; + m_copydata(m, 0, sizeof(struct ieee80211_frame), + (caddr_t)&frmhdr.an_whdr); + + /* insert payload length in front of llc/snap */ + len = htons(m->m_pkthdr.len - sizeof(struct ieee80211_frame)); + m_adj(m, sizeof(struct ieee80211_frame) - sizeof(len)); + if (mtod(m, u_long) & 0x01) + memcpy(mtod(m, caddr_t), &len, sizeof(len)); + else + *mtod(m, u_int16_t *) = len; - sc->an_rdata.an_tx_ring[idx] = id; - if (an_cmd(sc, AN_CMD_TX, id)) - printf("%s: xmit failed\n", sc->sc_dev.dv_xname); + /* + * XXX Aironet firmware apparently convert the packet + * with longer than 1500 bytes in length into LLC/SNAP. + * If we have 1500 bytes in ethernet payload, it is + * 1508 bytes including LLC/SNAP and will be inserted + * additional LLC/SNAP header with 1501-1508 in its + * ethertype !! + * So we skip LLC/SNAP header and force firmware to + * convert it to LLC/SNAP again. + */ + m_adj(m, sizeof(struct llc)); - AN_INC(idx, AN_TX_RING_CNT); - } - if (pkts == 0) - return; + frmhdr.an_tx_ctl = htole16(AN_TXCTL_80211); + frmhdr.an_tx_payload_len = htole16(m->m_pkthdr.len); + frmhdr.an_gaplen = htole16(AN_TXGAP_802_11); - if (m0 != NULL) - ifp->if_flags |= IFF_OACTIVE; + if (ic->ic_fixed_rate != -1) + frmhdr.an_tx_rate = + ic->ic_sup_rates[IEEE80211_MODE_11B].rs_rates[ + ic->ic_fixed_rate] & IEEE80211_RATE_VAL; + else + frmhdr.an_tx_rate = 0; - sc->an_rdata.an_tx_prod = idx; + if (sizeof(frmhdr) + AN_TXGAP_802_11 + sizeof(len) + + m->m_pkthdr.len > AN_TX_MAX_LEN) { + ifp->if_oerrors++; + m_freem(m); + continue; + } - /* - * Set a timeout in case the chip goes out to lunch. - */ - ifp->if_timer = 5; + fid = sc->sc_txd[cur].d_fid; + if (an_write_bap(sc, fid, 0, &frmhdr, sizeof(frmhdr)) != 0) { + ifp->if_oerrors++; + m_freem(m); + continue; + } + /* dummy write to avoid seek. */ + an_write_bap(sc, fid, -1, &frmhdr, AN_TXGAP_802_11); + an_mwrite_bap(sc, fid, -1, m, m->m_pkthdr.len); + m_freem(m); + + DPRINTF2(("an_start: send %d byte via %x/%d\n", + ntohs(len) + sizeof(struct ieee80211_frame), + fid, cur)); + sc->sc_txd[cur].d_inuse = 1; + if (an_cmd(sc, AN_CMD_TX, fid)) { + printf("%s: xmit failed\n", ifp->if_xname); + sc->sc_txd[cur].d_inuse = 0; + continue; + } + sc->sc_tx_timer = 5; + ifp->if_timer = 1; + AN_INC(cur, AN_TX_RING_CNT); + sc->sc_txnext = cur; + } } void -an_stop(sc) - struct an_softc *sc; +an_stop(struct ifnet *ifp, int disable) { - struct ieee80211com *ic = &sc->sc_ic; - struct ifnet *ifp = &ic->ic_if; - int i; + struct an_softc *sc = ifp->if_softc; + int i, s; - if (sc->an_gone) + if (!sc->sc_enabled) return; - an_cmd(sc, AN_CMD_FORCE_SYNCLOSS, 0); - CSR_WRITE_2(sc, AN_INT_EN, 0); - an_cmd(sc, AN_CMD_DISABLE, 0); + DPRINTF(("an_stop: disable %d\n", disable)); - for (i = 0; i < AN_TX_RING_CNT; i++) - an_cmd(sc, AN_CMD_DEALLOC_MEM, sc->an_rdata.an_tx_fids[i]); + s = splnet(); + ieee80211_new_state(&sc->sc_ic, IEEE80211_S_INIT, -1); + if (!sc->sc_invalid) { + an_cmd(sc, AN_CMD_FORCE_SYNCLOSS, 0); + CSR_WRITE_2(sc, AN_INT_EN, 0); + an_cmd(sc, AN_CMD_DISABLE, 0); - UNTIMEOUT(an_stats_update, sc, sc->an_stat_ch); + for (i = 0; i < AN_TX_RING_CNT; i++) + an_cmd(sc, AN_CMD_DEALLOC_MEM, sc->sc_txd[i].d_fid); + } + sc->sc_tx_timer = 0; + ifp->if_timer = 0; ifp->if_flags &= ~(IFF_RUNNING|IFF_OACTIVE); + + if (disable) { + if (sc->sc_disable) + (*sc->sc_disable)(sc); + sc->sc_enabled = 0; + } + splx(s); } void -an_watchdog(ifp) - struct ifnet *ifp; +an_watchdog(struct ifnet *ifp) { - struct an_softc *sc; - - sc = ifp->if_softc; + struct an_softc *sc = ifp->if_softc; - if (sc->an_gone) + if (!sc->sc_enabled) return; - printf("%s: device timeout\n", sc->sc_dev.dv_xname); - - an_reset(sc); - an_init(sc); - - ifp->if_oerrors++; + if (sc->sc_tx_timer) { + if (--sc->sc_tx_timer == 0) { + printf("%s: device timeout\n", ifp->if_xname); + ifp->if_oerrors++; + an_init(ifp); + return; + } + ifp->if_timer = 1; + } + ieee80211_watchdog(ifp); } void -an_shutdown(self) - void *self; +an_shutdown(void *self) { - an_stop(self); -} + struct an_softc *sc = (struct an_softc *)self; -#ifdef ANCACHE -/* Aironet signal strength cache code. - * store signal/noise/quality on per MAC src basis in - * a small fixed cache. The cache wraps if > MAX slots - * used. The cache may be zeroed out to start over. - * Two simple filters exist to reduce computation: - * 1. ip only (literally 0x800, ETHERTYPE_IP) which may be used - * to ignore some packets. It defaults to ip only. - * it could be used to focus on broadcast, non-IP 802.11 beacons. - * 2. multicast/broadcast only. This may be used to - * ignore unicast packets and only cache signal strength - * for multicast/broadcast packets (beacons); e.g., Mobile-IP - * beacons and not unicast traffic. - * - * The cache stores (MAC src(index), IP src (major clue), signal, - * quality, noise) - * - * No apologies for storing IP src here. It's easy and saves much - * trouble elsewhere. The cache is assumed to be INET dependent, - * although it need not be. - * - * Note: the Aironet only has a single byte of signal strength value - * in the rx frame header, and it's not scaled to anything sensible. - * This is kind of lame, but it's all we've got. - */ - -#ifdef documentation + if (sc->sc_attached) + an_stop(&sc->sc_ic.ic_if, 1); +} -int an_sigitems; /* number of cached entries */ -struct an_sigcache an_sigcache[MAXANCACHE]; /* array of cache entries */ -int an_nextitem; /* index/# of entries */ +/* TBD factor with ieee80211_media_change */ +int +an_media_change(struct ifnet *ifp) +{ + struct an_softc *sc = ifp->if_softc; + struct ieee80211com *ic = &sc->sc_ic; + struct ifmedia_entry *ime; + enum ieee80211_opmode newmode; + int i, rate, error = 0; + ime = ic->ic_media.ifm_cur; + if (IFM_SUBTYPE(ime->ifm_media) == IFM_AUTO) { + i = -1; + } else { + struct ieee80211_rateset *rs = + &ic->ic_sup_rates[IEEE80211_MODE_11B]; + rate = ieee80211_media2rate(ime->ifm_media); + if (rate == 0) + return EINVAL; + for (i = 0; i < rs->rs_nrates; i++) { + if ((rs->rs_rates[i] & IEEE80211_RATE_VAL) == rate) + break; + } + if (i == rs->rs_nrates) + return EINVAL; + } + if (ic->ic_fixed_rate != i) { + ic->ic_fixed_rate = i; + error = ENETRESET; + } -#endif + if (ime->ifm_media & IFM_IEEE80211_ADHOC) + newmode = IEEE80211_M_IBSS; + else if (ime->ifm_media & IFM_IEEE80211_HOSTAP) + newmode = IEEE80211_M_HOSTAP; + else if (ime->ifm_media & IFM_IEEE80211_MONITOR) + newmode = IEEE80211_M_MONITOR; + else + newmode = IEEE80211_M_STA; + if (ic->ic_opmode != newmode) { + ic->ic_opmode = newmode; + error = ENETRESET; + } + if (error == ENETRESET) { + if (sc->sc_enabled) + error = an_init(ifp); + else + error = 0; + } + ifp->if_baudrate = ifmedia_baudrate(ic->ic_media.ifm_cur->ifm_media); -/* control variables for cache filtering. Basic idea is - * to reduce cost (e.g., to only Mobile-IP agent beacons - * which are broadcast or multicast). Still you might - * want to measure signal strength anth unicast ping packets - * on a pt. to pt. ant. setup. - */ -/* set true if you want to limit cache items to broadcast/mcast - * only packets (not unicast). Useful for mobile-ip beacons which - * are broadcast/multicast at network layer. Default is all packets - * so ping/unicast anll work say anth pt. to pt. antennae setup. - */ -#if 0 -static int an_cache_mcastonly = 0; -SYSCTL_INT(_machdep, OID_AUTO, an_cache_mcastonly, CTLFLAG_RW, - &an_cache_mcastonly, 0, ""); - -/* set true if you want to limit cache items to IP packets only -*/ -static int an_cache_iponly = 1; -SYSCTL_INT(_machdep, OID_AUTO, an_cache_iponly, CTLFLAG_RW, - &an_cache_iponly, 0, ""); -#endif + return error; +} -/* - * an_cache_store, per rx packet store signal - * strength in MAC (src) indexed cache. - */ void -an_cache_store(sc, eh, m, rx_quality) - struct an_softc *sc; - struct ether_header *eh; - struct mbuf *m; - unsigned short rx_quality; +an_media_status(struct ifnet *ifp, struct ifmediareq *imr) { - static int cache_slot = 0; /* use this cache entry */ - static int wrapindex = 0; /* next "free" cache entry */ - int i, type_ipv4 = 0; - - /* filters: - * 1. ip only - * 2. configurable filter to throw out unicast packets, - * keep multicast only. - */ - - if ((ntohs(eh->ether_type) == ETHERTYPE_IP)) - type_ipv4 = 1; + struct an_softc *sc = ifp->if_softc; + struct ieee80211com *ic = &sc->sc_ic; + int rate, buflen; - /* filter for ip packets only */ - if (sc->an_cache_iponly && !type_ipv4) + if (sc->sc_enabled == 0) { + imr->ifm_active = IFM_IEEE80211 | IFM_NONE; + imr->ifm_status = 0; return; + } - /* filter for broadcast/multicast only */ - if (sc->an_cache_mcastonly && ((eh->ether_dhost[0] & 1) == 0)) - return; + imr->ifm_status = IFM_AVALID; + imr->ifm_active = IFM_IEEE80211; + if (ic->ic_state == IEEE80211_S_RUN) + imr->ifm_status |= IFM_ACTIVE; + buflen = sizeof(sc->sc_buf); + if (ic->ic_fixed_rate != -1) + rate = ic->ic_sup_rates[IEEE80211_MODE_11B].rs_rates[ + ic->ic_fixed_rate] & IEEE80211_RATE_VAL; + else if (an_read_rid(sc, AN_RID_STATUS, &sc->sc_buf, &buflen) != 0) + rate = 0; + else + rate = letoh16(sc->sc_buf.sc_status.an_current_tx_rate); + imr->ifm_active |= ieee80211_rate2media(ic, rate, IEEE80211_MODE_11B); + switch (ic->ic_opmode) { + case IEEE80211_M_STA: + break; + case IEEE80211_M_IBSS: + imr->ifm_active |= IFM_IEEE80211_ADHOC; + break; + case IEEE80211_M_HOSTAP: + imr->ifm_active |= IFM_IEEE80211_HOSTAP; + break; + case IEEE80211_M_MONITOR: + imr->ifm_active |= IFM_IEEE80211_MONITOR; + break; + default: + break; + } +} -#ifdef SIGDEBUG - printf("an: q value %x (MSB=0x%x, LSB=0x%x) \n", - rx_quality & 0xffff, rx_quality >> 8, rx_quality & 0xff); -#endif - /* do a linear search for a matching MAC address - * in the cache table - * . MAC address is 6 bytes, - * . var w_nextitem holds total number of entries already cached - */ - for(i = 0; i < sc->an_nextitem; i++) - if (!bcmp(eh->ether_shost, sc->an_sigcache[i].macsrc, 6)) - /* Match!, - * so we already have this entry, update the data - */ - break; +int +an_set_nwkey(struct an_softc *sc, struct ieee80211_nwkey *nwkey) +{ + int error; + struct ieee80211com *ic = &sc->sc_ic; + u_int16_t prevauth; - /* did we find a matching mac address? - * if yes, then overwrite a previously existing cache entry - */ - if (i < sc->an_nextitem) - cache_slot = i; + error = 0; + prevauth = sc->sc_config.an_authtype; - /* else, have a new address entry,so - * add this new entry, - * if table full, then we need to replace LRU entry - */ - else { + switch (nwkey->i_wepon) { + case IEEE80211_NWKEY_OPEN: + sc->sc_config.an_authtype = AN_AUTHTYPE_OPEN; + ic->ic_flags &= ~IEEE80211_F_WEPON; + break; - /* check for space in cache table - * note: an_nextitem also holds number of entries - * added in the cache table - */ - if (sc->an_nextitem < MAXANCACHE) { - cache_slot = sc->an_nextitem; - sc->an_nextitem++; - sc->an_sigitems = sc->an_nextitem; + case IEEE80211_NWKEY_WEP: + case IEEE80211_NWKEY_WEP | IEEE80211_NWKEY_PERSIST: + error = an_set_nwkey_wep(sc, nwkey); + if (error == 0 || error == ENETRESET) { + sc->sc_config.an_authtype = + AN_AUTHTYPE_OPEN | AN_AUTHTYPE_PRIVACY_IN_USE; + ic->ic_flags |= IEEE80211_F_WEPON; } - /* no space found, so simply wrap anth wrap index - * and "zap" the next entry - */ - else { - if (wrapindex == MAXANCACHE) - wrapindex = 0; - cache_slot = wrapindex++; - } - } + break; - /* invariant: cache_slot now points at some slot - * in cache. - */ - if (cache_slot < 0 || cache_slot >= MAXANCACHE) { - log(LOG_ERR, "an_cache_store, bad index: %d of " - "[0..%d], gross cache error\n", - cache_slot, MAXANCACHE); - return; + default: + error = EINVAL; + break; } + if (error == 0 && prevauth != sc->sc_config.an_authtype) + error = ENETRESET; + return error; +} - /* store items in cache - * .ip source address - * .mac src - * .signal, etc. - */ - if (type_ipv4) { - struct ip *ip = (struct ip *)(mtod(m, char *) + ETHER_HDR_LEN); - sc->an_sigcache[cache_slot].ipsrc = ntohl(ip->ip_src.s_addr); +int +an_set_nwkey_wep(struct an_softc *sc, struct ieee80211_nwkey *nwkey) +{ + int i, txkey, anysetkey, needreset, error; + struct an_wepkey keys[IEEE80211_WEP_NKID]; + + error = 0; + memset(keys, 0, sizeof(keys)); + anysetkey = needreset = 0; + + /* load argument and sanity check */ + for (i = 0; i < IEEE80211_WEP_NKID; i++) { + keys[i].an_wep_keylen = nwkey->i_key[i].i_keylen; + if (keys[i].an_wep_keylen < 0) + continue; + if (keys[i].an_wep_keylen != 0 && + keys[i].an_wep_keylen < IEEE80211_WEP_KEYLEN) + return EINVAL; + if (keys[i].an_wep_keylen > sizeof(keys[i].an_wep_key)) + return EINVAL; + if ((error = copyin(nwkey->i_key[i].i_keydat, + keys[i].an_wep_key, keys[i].an_wep_keylen)) != 0) + return error; + anysetkey++; } - bcopy(eh->ether_shost, sc->an_sigcache[cache_slot].macsrc, 6); - - sc->an_sigcache[cache_slot].signal = rx_quality; + txkey = nwkey->i_defkid - 1; + if (txkey >= 0) { + if (txkey >= IEEE80211_WEP_NKID) + return EINVAL; + /* default key must have a valid value */ + if (keys[txkey].an_wep_keylen == 0 || + (keys[txkey].an_wep_keylen < 0 && + sc->sc_perskeylen[txkey] == 0)) + return EINVAL; + anysetkey++; + } + DPRINTF(("an_set_nwkey_wep: %s: %sold(%d:%d,%d,%d,%d) " + "pers(%d:%d,%d,%d,%d) new(%d:%d,%d,%d,%d)\n", + sc->sc_dev.dv_xname, + ((nwkey->i_wepon & IEEE80211_NWKEY_PERSIST) ? "persist: " : ""), + sc->sc_tx_key, + sc->sc_wepkeys[0].an_wep_keylen, sc->sc_wepkeys[1].an_wep_keylen, + sc->sc_wepkeys[2].an_wep_keylen, sc->sc_wepkeys[3].an_wep_keylen, + sc->sc_tx_perskey, + sc->sc_perskeylen[0], sc->sc_perskeylen[1], + sc->sc_perskeylen[2], sc->sc_perskeylen[3], + txkey, + keys[0].an_wep_keylen, keys[1].an_wep_keylen, + keys[2].an_wep_keylen, keys[3].an_wep_keylen)); + if (!(nwkey->i_wepon & IEEE80211_NWKEY_PERSIST)) { + /* set temporary keys */ + sc->sc_tx_key = txkey; + for (i = 0; i < IEEE80211_WEP_NKID; i++) { + if (keys[i].an_wep_keylen < 0) + continue; + memcpy(&sc->sc_wepkeys[i], &keys[i], sizeof(keys[i])); + } + } else { + /* set persist keys */ + if (anysetkey) { + /* prepare to write nvram */ + if (!sc->sc_enabled) { + if (sc->sc_enable) + (*sc->sc_enable)(sc); + an_wait(sc); + sc->sc_enabled = 1; + error = an_write_wepkey(sc, + AN_RID_WEP_PERSISTENT, keys, txkey); + if (sc->sc_disable) + (*sc->sc_disable)(sc); + sc->sc_enabled = 0; + } else { + an_cmd(sc, AN_CMD_DISABLE, 0); + error = an_write_wepkey(sc, + AN_RID_WEP_PERSISTENT, keys, txkey); + an_cmd(sc, AN_CMD_ENABLE, 0); + } + if (error) + return error; + } + if (txkey >= 0) + sc->sc_tx_perskey = txkey; + if (sc->sc_tx_key >= 0) { + sc->sc_tx_key = -1; + needreset++; + } + for (i = 0; i < IEEE80211_WEP_NKID; i++) { + if (sc->sc_wepkeys[i].an_wep_keylen >= 0) { + memset(&sc->sc_wepkeys[i].an_wep_key, 0, + sizeof(sc->sc_wepkeys[i].an_wep_key)); + sc->sc_wepkeys[i].an_wep_keylen = -1; + needreset++; + } + if (keys[i].an_wep_keylen >= 0) + sc->sc_perskeylen[i] = keys[i].an_wep_keylen; + } + } + if (needreset) { + /* firmware restart to reload persistent key */ + an_reset(sc); + } + if (anysetkey || needreset) + error = ENETRESET; + return error; } -#endif int -an_media_change(ifp) - struct ifnet *ifp; +an_get_nwkey(struct an_softc *sc, struct ieee80211_nwkey *nwkey) { - struct an_softc *sc = ifp->if_softc; - int otype = sc->an_config.an_opmode; - int orate = sc->an_tx_rate; + int i, error; - if ((sc->an_ifmedia.ifm_cur->ifm_media & IFM_IEEE80211_ADHOC) != 0) - sc->an_config.an_opmode = AN_OPMODE_IBSS_ADHOC; + error = 0; + if (sc->sc_config.an_authtype & AN_AUTHTYPE_LEAP) + nwkey->i_wepon = IEEE80211_NWKEY_EAP; + else if (sc->sc_config.an_authtype & AN_AUTHTYPE_PRIVACY_IN_USE) + nwkey->i_wepon = IEEE80211_NWKEY_WEP; else - sc->an_config.an_opmode = AN_OPMODE_INFRASTRUCTURE_STATION; - - switch (IFM_SUBTYPE(sc->an_ifmedia.ifm_cur->ifm_media)) { - case IFM_IEEE80211_DS1: - sc->an_tx_rate = AN_RATE_1MBPS; - break; - case IFM_IEEE80211_DS2: - sc->an_tx_rate = AN_RATE_2MBPS; - break; - case IFM_IEEE80211_DS5: - sc->an_tx_rate = AN_RATE_5_5MBPS; - break; - case IFM_IEEE80211_DS11: - sc->an_tx_rate = AN_RATE_11MBPS; - break; - case IFM_AUTO: - sc->an_tx_rate = 0; - break; + nwkey->i_wepon = IEEE80211_NWKEY_OPEN; + if (sc->sc_tx_key == -1) + nwkey->i_defkid = sc->sc_tx_perskey + 1; + else + nwkey->i_defkid = sc->sc_tx_key + 1; + if (nwkey->i_key[0].i_keydat == NULL) + return 0; + for (i = 0; i < IEEE80211_WEP_NKID; i++) { + if (nwkey->i_key[i].i_keydat == NULL) + continue; + /* do not show any keys to non-root user */ + if ((error = suser(curproc, 0)) != 0) + break; + nwkey->i_key[i].i_keylen = sc->sc_wepkeys[i].an_wep_keylen; + if (nwkey->i_key[i].i_keylen < 0) { + if (sc->sc_perskeylen[i] == 0) + nwkey->i_key[i].i_keylen = 0; + continue; + } + if ((error = copyout(sc->sc_wepkeys[i].an_wep_key, + nwkey->i_key[i].i_keydat, + sc->sc_wepkeys[i].an_wep_keylen)) != 0) + break; } + return error; +} - if (otype != sc->an_config.an_opmode || - orate != sc->an_tx_rate) - an_init(sc); - - return(0); +int +an_write_wepkey(struct an_softc *sc, int type, struct an_wepkey *keys, int kid) +{ + int i, error; + struct an_rid_wepkey *akey; + + error = 0; + akey = &sc->sc_buf.sc_wepkey; + memset(akey, 0, sizeof(struct an_rid_wepkey)); + for (i = 0; i < IEEE80211_WEP_NKID; i++) { + if (keys[i].an_wep_keylen < 0 || + keys[i].an_wep_keylen > sizeof(akey->an_key)) + continue; + akey->an_key_len = htole16(keys[i].an_wep_keylen); + akey->an_key_index = htole16(i); + akey->an_mac_addr[0] = 1; /* default mac */ + memcpy(akey->an_key, keys[i].an_wep_key, keys[i].an_wep_keylen); + if ((error = an_write_rid(sc, type, akey, sizeof(*akey))) != 0) + return error; + } + if (kid >= 0) { + akey->an_key_index = htole16(0xffff); + akey->an_mac_addr[0] = kid; + akey->an_key_len = htole16(0); + memset(akey->an_key, 0, sizeof(akey->an_key)); + error = an_write_rid(sc, type, akey, sizeof(*akey)); + } + return error; } -void -an_media_status(ifp, imr) - struct ifnet *ifp; - struct ifmediareq *imr; +int +an_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) { - struct an_ltv_status status; - struct an_softc *sc = ifp->if_softc; - - status.an_len = sizeof(status); - status.an_type = AN_RID_STATUS; - if (an_read_record(sc, (struct an_ltv_gen *)&status)) { - /* If the status read fails, just lie. */ - imr->ifm_active = sc->an_ifmedia.ifm_cur->ifm_media; - imr->ifm_status = IFM_AVALID|IFM_ACTIVE; - } - - if (sc->an_tx_rate == 0) { - imr->ifm_active = IFM_IEEE80211|IFM_AUTO; - if (sc->an_config.an_opmode == AN_OPMODE_IBSS_ADHOC) - imr->ifm_active |= IFM_IEEE80211_ADHOC; - switch (status.an_current_tx_rate) { - case AN_RATE_1MBPS: - imr->ifm_active |= IFM_IEEE80211_DS1; - break; - case AN_RATE_2MBPS: - imr->ifm_active |= IFM_IEEE80211_DS2; - break; - case AN_RATE_5_5MBPS: - imr->ifm_active |= IFM_IEEE80211_DS5; - break; - case AN_RATE_11MBPS: - imr->ifm_active |= IFM_IEEE80211_DS11; - break; + struct an_softc *sc = ic->ic_softc; + struct ieee80211_node *ni = ic->ic_bss; + enum ieee80211_state ostate; + int buflen; + + ostate = ic->ic_state; + DPRINTF(("an_newstate: %s -> %s\n", ieee80211_state_name[ostate], + ieee80211_state_name[nstate])); + + switch (nstate) { + case IEEE80211_S_INIT: + ic->ic_flags &= ~IEEE80211_F_IBSSON; + return (*sc->sc_newstate)(ic, nstate, arg); + + case IEEE80211_S_RUN: + buflen = sizeof(sc->sc_buf); + an_read_rid(sc, AN_RID_STATUS, &sc->sc_buf, &buflen); + IEEE80211_ADDR_COPY(ni->ni_bssid, + sc->sc_buf.sc_status.an_cur_bssid); + IEEE80211_ADDR_COPY(ni->ni_macaddr, ni->ni_bssid); + ni->ni_chan = &ic->ic_channels[ + letoh16(sc->sc_buf.sc_status.an_cur_channel)]; + ni->ni_esslen = letoh16(sc->sc_buf.sc_status.an_ssidlen); + if (ni->ni_esslen > IEEE80211_NWID_LEN) + ni->ni_esslen = IEEE80211_NWID_LEN; /*XXX*/ + memcpy(ni->ni_essid, sc->sc_buf.sc_status.an_ssid, + ni->ni_esslen); + ni->ni_rates = ic->ic_sup_rates[IEEE80211_MODE_11B]; /*XXX*/ + if (ic->ic_if.if_flags & IFF_DEBUG) { + printf("%s: ", sc->sc_dev.dv_xname); + if (ic->ic_opmode == IEEE80211_M_STA) + printf("associated "); + else + printf("synchronized "); + printf("with %s ssid ", ether_sprintf(ni->ni_bssid)); + ieee80211_print_essid(ni->ni_essid, ni->ni_esslen); + printf(" channel %u start %uMb\n", + letoh16(sc->sc_buf.sc_status.an_cur_channel), + letoh16(sc->sc_buf.sc_status.an_current_tx_rate)/2); } - } else { - imr->ifm_active = sc->an_ifmedia.ifm_cur->ifm_media; + break; + + default: + break; } + ic->ic_state = nstate; + /* skip standard ieee80211 handling */ + return 0; +} - imr->ifm_status = IFM_AVALID; - if (sc->an_config.an_opmode == AN_OPMODE_IBSS_ADHOC) - imr->ifm_status |= IFM_ACTIVE; - else if (status.an_opmode & AN_STATUS_OPMODE_ASSOCIATED) - imr->ifm_status |= IFM_ACTIVE; +int +an_detach(struct an_softc *sc) +{ + struct ifnet *ifp = &sc->sc_ic.ic_if; + int s; + + if (!sc->sc_attached) + return 0; + + s = splnet(); + sc->sc_invalid = 1; + an_stop(ifp, 1); + ifmedia_delete_instance(&sc->sc_ic.ic_media, IFM_INST_ANY); + ieee80211_ifdetach(ifp); + if_detach(ifp); + splx(s); + return 0; } + |