if_an.c revision 78639
155992Swpaul/*
255992Swpaul * Copyright (c) 1997, 1998, 1999
355992Swpaul *	Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
455992Swpaul *
555992Swpaul * Redistribution and use in source and binary forms, with or without
655992Swpaul * modification, are permitted provided that the following conditions
755992Swpaul * are met:
855992Swpaul * 1. Redistributions of source code must retain the above copyright
955992Swpaul *    notice, this list of conditions and the following disclaimer.
1055992Swpaul * 2. Redistributions in binary form must reproduce the above copyright
1155992Swpaul *    notice, this list of conditions and the following disclaimer in the
1255992Swpaul *    documentation and/or other materials provided with the distribution.
1355992Swpaul * 3. All advertising materials mentioning features or use of this software
1455992Swpaul *    must display the following acknowledgement:
1555992Swpaul *	This product includes software developed by Bill Paul.
1655992Swpaul * 4. Neither the name of the author nor the names of any co-contributors
1755992Swpaul *    may be used to endorse or promote products derived from this software
1855992Swpaul *    without specific prior written permission.
1955992Swpaul *
2055992Swpaul * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
2155992Swpaul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2255992Swpaul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2355992Swpaul * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
2455992Swpaul * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2555992Swpaul * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2655992Swpaul * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2755992Swpaul * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2855992Swpaul * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2955992Swpaul * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
3055992Swpaul * THE POSSIBILITY OF SUCH DAMAGE.
3155992Swpaul *
3255992Swpaul * $FreeBSD: head/sys/dev/an/if_an.c 78639 2001-06-22 23:35:24Z brooks $
3355992Swpaul */
3455992Swpaul
3555992Swpaul/*
3655992Swpaul * Aironet 4500/4800 802.11 PCMCIA/ISA/PCI driver for FreeBSD.
3755992Swpaul *
3855992Swpaul * Written by Bill Paul <wpaul@ctr.columbia.edu>
3955992Swpaul * Electrical Engineering Department
4055992Swpaul * Columbia University, New York City
4155992Swpaul */
4255992Swpaul
4355992Swpaul/*
4455992Swpaul * The Aironet 4500/4800 series cards some in PCMCIA, ISA and PCI form.
4555992Swpaul * This driver supports all three device types (PCI devices are supported
4655992Swpaul * through an extra PCI shim: /sys/pci/if_an_p.c). ISA devices can be
4755992Swpaul * supported either using hard-coded IO port/IRQ settings or via Plug
4855992Swpaul * and Play. The 4500 series devices support 1Mbps and 2Mbps data rates.
4955992Swpaul * The 4800 devices support 1, 2, 5.5 and 11Mbps rates.
5055992Swpaul *
5155992Swpaul * Like the WaveLAN/IEEE cards, the Aironet NICs are all essentially
5255992Swpaul * PCMCIA devices. The ISA and PCI cards are a combination of a PCMCIA
5355992Swpaul * device and a PCMCIA to ISA or PCMCIA to PCI adapter card. There are
5455992Swpaul * a couple of important differences though:
5555992Swpaul *
5655992Swpaul * - Lucent doesn't currently offer a PCI card, however Aironet does
5755992Swpaul * - Lucent ISA card looks to the host like a PCMCIA controller with
5855992Swpaul *   a PCMCIA WaveLAN card inserted. This means that even desktop
5955992Swpaul *   machines need to be configured with PCMCIA support in order to
6055992Swpaul *   use WaveLAN/IEEE ISA cards. The Aironet cards on the other hand
6155992Swpaul *   actually look like normal ISA and PCI devices to the host, so
6255992Swpaul *   no PCMCIA controller support is needed
6355992Swpaul *
6455992Swpaul * The latter point results in a small gotcha. The Aironet PCMCIA
6555992Swpaul * cards can be configured for one of two operating modes depending
6655992Swpaul * on how the Vpp1 and Vpp2 programming voltages are set when the
6755992Swpaul * card is activated. In order to put the card in proper PCMCIA
6855992Swpaul * operation (where the CIS table is visible and the interface is
6955992Swpaul * programmed for PCMCIA operation), both Vpp1 and Vpp2 have to be
7055992Swpaul * set to 5 volts. FreeBSD by default doesn't set the Vpp voltages,
7155992Swpaul * which leaves the card in ISA/PCI mode, which prevents it from
7255992Swpaul * being activated as an PCMCIA device. Consequently, /sys/pccard/pccard.c
7355992Swpaul * has to be patched slightly in order to enable the Vpp voltages in
7455992Swpaul * order to make the Aironet PCMCIA cards work.
7555992Swpaul *
7655992Swpaul * Note that some PCMCIA controller software packages for Windows NT
7755992Swpaul * fail to set the voltages as well.
7855992Swpaul *
7955992Swpaul * The Aironet devices can operate in both station mode and access point
8055992Swpaul * mode. Typically, when programmed for station mode, the card can be set
8155992Swpaul * to automatically perform encapsulation/decapsulation of Ethernet II
8255992Swpaul * and 802.3 frames within 802.11 frames so that the host doesn't have
8355992Swpaul * to do it itself. This driver doesn't program the card that way: the
8455992Swpaul * driver handles all of the encapsulation/decapsulation itself.
8555992Swpaul */
8655992Swpaul
8755992Swpaul#include "opt_inet.h"
8855992Swpaul
8955992Swpaul#ifdef INET
9055992Swpaul#define ANCACHE			/* enable signal strength cache */
9155992Swpaul#endif
9255992Swpaul
9355992Swpaul#include <sys/param.h>
9455992Swpaul#include <sys/systm.h>
9555992Swpaul#include <sys/sockio.h>
9655992Swpaul#include <sys/mbuf.h>
9755992Swpaul#include <sys/kernel.h>
9855992Swpaul#include <sys/socket.h>
9955992Swpaul#ifdef ANCACHE
10055992Swpaul#include <sys/syslog.h>
10155992Swpaul#include <sys/sysctl.h>
10255992Swpaul#endif
10355992Swpaul
10455992Swpaul#include <sys/module.h>
10555992Swpaul#include <sys/bus.h>
10655992Swpaul#include <machine/bus.h>
10755992Swpaul#include <sys/rman.h>
10867365Sjhb#include <sys/mutex.h>
10955992Swpaul#include <machine/resource.h>
11055992Swpaul
11155992Swpaul#include <net/if.h>
11255992Swpaul#include <net/if_arp.h>
11355992Swpaul#include <net/ethernet.h>
11455992Swpaul#include <net/if_dl.h>
11555992Swpaul#include <net/if_types.h>
11677217Sphk#include <net/if_ieee80211.h>
11777217Sphk#include <net/if_media.h>
11855992Swpaul
11955992Swpaul#ifdef INET
12055992Swpaul#include <netinet/in.h>
12155992Swpaul#include <netinet/in_systm.h>
12255992Swpaul#include <netinet/in_var.h>
12355992Swpaul#include <netinet/ip.h>
12455992Swpaul#endif
12555992Swpaul
12655992Swpaul#include <net/bpf.h>
12755992Swpaul
12855992Swpaul#include <machine/md_var.h>
12955992Swpaul
13055992Swpaul#include <dev/an/if_aironet_ieee.h>
13155992Swpaul#include <dev/an/if_anreg.h>
13255992Swpaul
13355992Swpaul#if !defined(lint)
13455992Swpaulstatic const char rcsid[] =
13555992Swpaul  "$FreeBSD: head/sys/dev/an/if_an.c 78639 2001-06-22 23:35:24Z brooks $";
13655992Swpaul#endif
13755992Swpaul
13855992Swpaul/* These are global because we need them in sys/pci/if_an_p.c. */
13955992Swpaulstatic void an_reset		__P((struct an_softc *));
14055992Swpaulstatic int an_ioctl		__P((struct ifnet *, u_long, caddr_t));
14155992Swpaulstatic void an_init		__P((void *));
14255992Swpaulstatic int an_init_tx_ring	__P((struct an_softc *));
14355992Swpaulstatic void an_start		__P((struct ifnet *));
14455992Swpaulstatic void an_watchdog		__P((struct ifnet *));
14555992Swpaulstatic void an_rxeof		__P((struct an_softc *));
14655992Swpaulstatic void an_txeof		__P((struct an_softc *, int));
14755992Swpaul
14855992Swpaulstatic void an_promisc		__P((struct an_softc *, int));
14955992Swpaulstatic int an_cmd		__P((struct an_softc *, int, int));
15055992Swpaulstatic int an_read_record	__P((struct an_softc *, struct an_ltv_gen *));
15155992Swpaulstatic int an_write_record	__P((struct an_softc *, struct an_ltv_gen *));
15255992Swpaulstatic int an_read_data		__P((struct an_softc *, int,
15355992Swpaul					int, caddr_t, int));
15455992Swpaulstatic int an_write_data	__P((struct an_softc *, int,
15555992Swpaul					int, caddr_t, int));
15655992Swpaulstatic int an_seek		__P((struct an_softc *, int, int, int));
15755992Swpaulstatic int an_alloc_nicmem	__P((struct an_softc *, int, int *));
15855992Swpaulstatic void an_stats_update	__P((void *));
15955992Swpaulstatic void an_setdef		__P((struct an_softc *, struct an_req *));
16055992Swpaul#ifdef ANCACHE
16155992Swpaulstatic void an_cache_store	__P((struct an_softc *, struct ether_header *,
16255992Swpaul					struct mbuf *, unsigned short));
16355992Swpaul#endif
16455992Swpaul
16578639Sbrooksstatic void an_dump_record	__P((struct an_softc *,struct an_ltv_gen *,
16678639Sbrooks				    char *));
16778639Sbrooks
16877217Sphkstatic int an_media_change	__P((struct ifnet *));
16977217Sphkstatic void an_media_status	__P((struct ifnet *, struct ifmediareq *));
17077217Sphk
17178639Sbrooksstatic int	an_dump = 0;
17255992Swpaul/*
17355992Swpaul * We probe for an Aironet 4500/4800 card by attempting to
17455992Swpaul * read the default SSID list. On reset, the first entry in
17555992Swpaul * the SSID list will contain the name "tsunami." If we don't
17655992Swpaul * find this, then there's no card present.
17755992Swpaul */
17855992Swpaulint an_probe(dev)
17955992Swpaul	device_t		dev;
18055992Swpaul{
18155992Swpaul        struct an_softc *sc = device_get_softc(dev);
18255992Swpaul	struct an_ltv_ssidlist	ssid;
18355992Swpaul	int	error;
18455992Swpaul
18555992Swpaul	bzero((char *)&ssid, sizeof(ssid));
18655992Swpaul
18755992Swpaul	error = an_alloc_port(dev, 0, AN_IOSIZ);
18878639Sbrooks	if (error != 0)
18955992Swpaul		return (0);
19055992Swpaul
19155992Swpaul	/* can't do autoprobing */
19255992Swpaul	if (rman_get_start(sc->port_res) == -1)
19355992Swpaul		return(0);
19455992Swpaul
19555992Swpaul	/*
19655992Swpaul	 * We need to fake up a softc structure long enough
19755992Swpaul	 * to be able to issue commands and call some of the
19855992Swpaul	 * other routines.
19955992Swpaul	 */
20056094Swpaul	sc->an_bhandle = rman_get_bushandle(sc->port_res);
20155992Swpaul	sc->an_btag = rman_get_bustag(sc->port_res);
20255992Swpaul	sc->an_unit = device_get_unit(dev);
20355992Swpaul
20455992Swpaul	ssid.an_len = sizeof(ssid);
20555992Swpaul	ssid.an_type = AN_RID_SSIDLIST;
20655992Swpaul
20755992Swpaul        /* Make sure interrupts are disabled. */
20855992Swpaul        CSR_WRITE_2(sc, AN_INT_EN, 0);
20955992Swpaul        CSR_WRITE_2(sc, AN_EVENT_ACK, 0xFFFF);
21055992Swpaul
21155992Swpaul	an_reset(sc);
21255992Swpaul
21355992Swpaul	if (an_cmd(sc, AN_CMD_READCFG, 0))
21455992Swpaul		return(0);
21555992Swpaul
21655992Swpaul	if (an_read_record(sc, (struct an_ltv_gen *)&ssid))
21755992Swpaul		return(0);
21855992Swpaul
21968692Swpaul	/* See if the ssid matches what we expect ... but doesn't have to */
22055992Swpaul	if (strcmp(ssid.an_ssid1, AN_DEF_SSID))
22155992Swpaul		return(0);
22255992Swpaul
22355992Swpaul	return(AN_IOSIZ);
22455992Swpaul}
22555992Swpaul
22655992Swpaul/*
22755992Swpaul * Allocate a port resource with the given resource id.
22855992Swpaul */
22955992Swpaulint
23055992Swpaulan_alloc_port(dev, rid, size)
23155992Swpaul	device_t dev;
23255992Swpaul	int rid;
23355992Swpaul	int size;
23455992Swpaul{
23555992Swpaul	struct an_softc *sc = device_get_softc(dev);
23655992Swpaul	struct resource *res;
23755992Swpaul
23855992Swpaul	res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid,
23955992Swpaul				 0ul, ~0ul, size, RF_ACTIVE);
24055992Swpaul	if (res) {
24155992Swpaul		sc->port_rid = rid;
24255992Swpaul		sc->port_res = res;
24355992Swpaul		return (0);
24455992Swpaul	} else {
24555992Swpaul		return (ENOENT);
24655992Swpaul	}
24755992Swpaul}
24855992Swpaul
24955992Swpaul/*
25055992Swpaul * Allocate an irq resource with the given resource id.
25155992Swpaul */
25255992Swpaulint
25355992Swpaulan_alloc_irq(dev, rid, flags)
25455992Swpaul	device_t dev;
25555992Swpaul	int rid;
25655992Swpaul	int flags;
25755992Swpaul{
25855992Swpaul	struct an_softc *sc = device_get_softc(dev);
25955992Swpaul	struct resource *res;
26055992Swpaul
26155992Swpaul	res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid,
26255992Swpaul				 0ul, ~0ul, 1, (RF_ACTIVE | flags));
26355992Swpaul	if (res) {
26455992Swpaul		sc->irq_rid = rid;
26555992Swpaul		sc->irq_res = res;
26655992Swpaul		return (0);
26755992Swpaul	} else {
26855992Swpaul		return (ENOENT);
26955992Swpaul	}
27055992Swpaul}
27155992Swpaul
27255992Swpaul/*
27355992Swpaul * Release all resources
27455992Swpaul */
27555992Swpaulvoid
27655992Swpaulan_release_resources(dev)
27755992Swpaul	device_t dev;
27855992Swpaul{
27955992Swpaul	struct an_softc *sc = device_get_softc(dev);
28055992Swpaul
28155992Swpaul	if (sc->port_res) {
28255992Swpaul		bus_release_resource(dev, SYS_RES_IOPORT,
28355992Swpaul				     sc->port_rid, sc->port_res);
28455992Swpaul		sc->port_res = 0;
28555992Swpaul	}
28655992Swpaul	if (sc->irq_res) {
28755992Swpaul		bus_release_resource(dev, SYS_RES_IRQ,
28855992Swpaul				     sc->irq_rid, sc->irq_res);
28955992Swpaul		sc->irq_res = 0;
29055992Swpaul	}
29155992Swpaul}
29255992Swpaul
29355992Swpaulint an_attach(sc, unit, flags)
29455992Swpaul	struct an_softc *sc;
29555992Swpaul	int unit;
29655992Swpaul	int flags;
29755992Swpaul{
29855992Swpaul	struct ifnet		*ifp = &sc->arpcom.ac_if;
29955992Swpaul
30071228Sbmilekic	mtx_init(&sc->an_mtx, device_get_nameunit(sc->an_dev), MTX_DEF |
30171228Sbmilekic	    MTX_RECURSE);
30267094Swpaul	AN_LOCK(sc);
30367094Swpaul
30455992Swpaul	sc->an_gone = 0;
30555992Swpaul	sc->an_associated = 0;
30655992Swpaul
30755992Swpaul	/* Reset the NIC. */
30855992Swpaul	an_reset(sc);
30955992Swpaul
31055992Swpaul	/* Load factory config */
31155992Swpaul	if (an_cmd(sc, AN_CMD_READCFG, 0)) {
31255992Swpaul		printf("an%d: failed to load config data\n", sc->an_unit);
31367094Swpaul		AN_UNLOCK(sc);
31467094Swpaul		mtx_destroy(&sc->an_mtx);
31555992Swpaul		return(EIO);
31655992Swpaul	}
31755992Swpaul
31855992Swpaul	/* Read the current configuration */
31955992Swpaul	sc->an_config.an_type = AN_RID_GENCONFIG;
32055992Swpaul	sc->an_config.an_len = sizeof(struct an_ltv_genconfig);
32155992Swpaul	if (an_read_record(sc, (struct an_ltv_gen *)&sc->an_config)) {
32255992Swpaul		printf("an%d: read record failed\n", sc->an_unit);
32367094Swpaul		AN_UNLOCK(sc);
32467094Swpaul		mtx_destroy(&sc->an_mtx);
32555992Swpaul		return(EIO);
32655992Swpaul	}
32755992Swpaul
32855992Swpaul	/* Read the card capabilities */
32955992Swpaul	sc->an_caps.an_type = AN_RID_CAPABILITIES;
33055992Swpaul	sc->an_caps.an_len = sizeof(struct an_ltv_caps);
33155992Swpaul	if (an_read_record(sc, (struct an_ltv_gen *)&sc->an_caps)) {
33255992Swpaul		printf("an%d: read record failed\n", sc->an_unit);
33367094Swpaul		AN_UNLOCK(sc);
33467094Swpaul		mtx_destroy(&sc->an_mtx);
33555992Swpaul		return(EIO);
33655992Swpaul	}
33755992Swpaul
33855992Swpaul	/* Read ssid list */
33955992Swpaul	sc->an_ssidlist.an_type = AN_RID_SSIDLIST;
34055992Swpaul	sc->an_ssidlist.an_len = sizeof(struct an_ltv_ssidlist);
34155992Swpaul	if (an_read_record(sc, (struct an_ltv_gen *)&sc->an_ssidlist)) {
34255992Swpaul		printf("an%d: read record failed\n", sc->an_unit);
34367094Swpaul		AN_UNLOCK(sc);
34467094Swpaul		mtx_destroy(&sc->an_mtx);
34555992Swpaul		return(EIO);
34655992Swpaul	}
34755992Swpaul
34855992Swpaul	/* Read AP list */
34955992Swpaul	sc->an_aplist.an_type = AN_RID_APLIST;
35055992Swpaul	sc->an_aplist.an_len = sizeof(struct an_ltv_aplist);
35155992Swpaul	if (an_read_record(sc, (struct an_ltv_gen *)&sc->an_aplist)) {
35255992Swpaul		printf("an%d: read record failed\n", sc->an_unit);
35367094Swpaul		AN_UNLOCK(sc);
35467094Swpaul		mtx_destroy(&sc->an_mtx);
35555992Swpaul		return(EIO);
35655992Swpaul	}
35755992Swpaul
35855992Swpaul	bcopy((char *)&sc->an_caps.an_oemaddr,
35955992Swpaul	   (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN);
36055992Swpaul
36155992Swpaul	printf("an%d: Ethernet address: %6D\n", sc->an_unit,
36255992Swpaul	    sc->arpcom.ac_enaddr, ":");
36355992Swpaul
36455992Swpaul	ifp->if_softc = sc;
36555992Swpaul	ifp->if_unit = sc->an_unit = unit;
36655992Swpaul	ifp->if_name = "an";
36755992Swpaul	ifp->if_mtu = ETHERMTU;
36855992Swpaul	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
36955992Swpaul	ifp->if_ioctl = an_ioctl;
37055992Swpaul	ifp->if_output = ether_output;
37155992Swpaul	ifp->if_start = an_start;
37255992Swpaul	ifp->if_watchdog = an_watchdog;
37355992Swpaul	ifp->if_init = an_init;
37455992Swpaul	ifp->if_baudrate = 10000000;
37555992Swpaul	ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
37655992Swpaul
37755992Swpaul	bzero(sc->an_config.an_nodename, sizeof(sc->an_config.an_nodename));
37855992Swpaul	bcopy(AN_DEFAULT_NODENAME, sc->an_config.an_nodename,
37955992Swpaul	    sizeof(AN_DEFAULT_NODENAME) - 1);
38055992Swpaul
38155992Swpaul	bzero(sc->an_ssidlist.an_ssid1, sizeof(sc->an_ssidlist.an_ssid1));
38255992Swpaul	bcopy(AN_DEFAULT_NETNAME, sc->an_ssidlist.an_ssid1,
38355992Swpaul	    sizeof(AN_DEFAULT_NETNAME) - 1);
38455992Swpaul	sc->an_ssidlist.an_ssid1_len = strlen(AN_DEFAULT_NETNAME);
38555992Swpaul
38655992Swpaul	sc->an_config.an_opmode =
38774144Sassar	    AN_OPMODE_INFRASTRUCTURE_STATION;
38855992Swpaul
38955992Swpaul	sc->an_tx_rate = 0;
39055992Swpaul	bzero((char *)&sc->an_stats, sizeof(sc->an_stats));
39155992Swpaul
39277217Sphk	ifmedia_init(&sc->an_ifmedia, 0, an_media_change, an_media_status);
39377217Sphk#define	ADD(m, c)	ifmedia_add(&sc->an_ifmedia, (m), (c), NULL)
39477217Sphk	ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS1,
39577217Sphk	    IFM_IEEE80211_ADHOC, 0), 0);
39677217Sphk	ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS1, 0, 0), 0);
39777217Sphk	ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS2,
39877217Sphk	    IFM_IEEE80211_ADHOC, 0), 0);
39977217Sphk	ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS2, 0, 0), 0);
40078639Sbrooks	if (sc->an_caps.an_rates[2] == AN_RATE_5_5MBPS) {
40177217Sphk		ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS5,
40277217Sphk		    IFM_IEEE80211_ADHOC, 0), 0);
40377217Sphk		ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS5, 0, 0), 0);
40477217Sphk	}
40578639Sbrooks	if (sc->an_caps.an_rates[3] == AN_RATE_11MBPS) {
40677217Sphk		ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS11,
40777217Sphk		    IFM_IEEE80211_ADHOC, 0), 0);
40877217Sphk		ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS11, 0, 0), 0);
40977217Sphk	}
41077217Sphk	ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_AUTO,
41177217Sphk	    IFM_IEEE80211_ADHOC, 0), 0);
41277217Sphk	ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_AUTO, 0, 0), 0);
41377217Sphk#undef	ADD
41477217Sphk	ifmedia_set(&sc->an_ifmedia, IFM_MAKEWORD(IFM_IEEE80211, IFM_AUTO,
41577217Sphk	    0, 0));
41677217Sphk
41755992Swpaul	/*
41863090Sarchie	 * Call MI attach routine.
41955992Swpaul	 */
42063090Sarchie	ether_ifattach(ifp, ETHER_BPF_SUPPORTED);
42155992Swpaul	callout_handle_init(&sc->an_stat_ch);
42267094Swpaul	AN_UNLOCK(sc);
42355992Swpaul
42455992Swpaul	return(0);
42555992Swpaul}
42655992Swpaul
42755992Swpaulstatic void an_rxeof(sc)
42855992Swpaul	struct an_softc		*sc;
42955992Swpaul{
43055992Swpaul	struct ifnet		*ifp;
43155992Swpaul	struct ether_header	*eh;
43255992Swpaul#ifdef ANCACHE
43355992Swpaul	struct an_rxframe	rx_frame;
43455992Swpaul#endif
43555992Swpaul	struct an_rxframe_802_3	rx_frame_802_3;
43655992Swpaul	struct mbuf		*m;
43755992Swpaul	int			id, error = 0;
43855992Swpaul
43955992Swpaul	ifp = &sc->arpcom.ac_if;
44055992Swpaul
44155992Swpaul	id = CSR_READ_2(sc, AN_RX_FID);
44255992Swpaul
44355992Swpaul	MGETHDR(m, M_DONTWAIT, MT_DATA);
44455992Swpaul	if (m == NULL) {
44555992Swpaul		ifp->if_ierrors++;
44655992Swpaul		return;
44755992Swpaul	}
44855992Swpaul	MCLGET(m, M_DONTWAIT);
44955992Swpaul	if (!(m->m_flags & M_EXT)) {
45055992Swpaul		m_freem(m);
45155992Swpaul		ifp->if_ierrors++;
45255992Swpaul		return;
45355992Swpaul	}
45455992Swpaul
45555992Swpaul	m->m_pkthdr.rcvif = ifp;
45655992Swpaul
45755992Swpaul	eh = mtod(m, struct ether_header *);
45855992Swpaul
45955992Swpaul#ifdef ANCACHE
46055992Swpaul	/* Read NIC frame header */
46155992Swpaul	if (an_read_data(sc, id, 0, (caddr_t)&rx_frame, sizeof(rx_frame))) {
46255992Swpaul		ifp->if_ierrors++;
46355992Swpaul		return;
46455992Swpaul	}
46555992Swpaul#endif
46655992Swpaul	/* Read in the 802_3 frame header */
46755992Swpaul	if (an_read_data(sc, id, 0x34, (caddr_t)&rx_frame_802_3,
46855992Swpaul			 sizeof(rx_frame_802_3))) {
46955992Swpaul		ifp->if_ierrors++;
47055992Swpaul		return;
47155992Swpaul	}
47255992Swpaul
47355992Swpaul	if (rx_frame_802_3.an_rx_802_3_status != 0) {
47455992Swpaul		ifp->if_ierrors++;
47555992Swpaul		return;
47655992Swpaul	}
47755992Swpaul
47855992Swpaul	/* Check for insane frame length */
47955992Swpaul	if (rx_frame_802_3.an_rx_802_3_payload_len > MCLBYTES) {
48055992Swpaul		ifp->if_ierrors++;
48155992Swpaul		return;
48255992Swpaul	}
48355992Swpaul
48455992Swpaul	m->m_pkthdr.len = m->m_len =
48555992Swpaul	    rx_frame_802_3.an_rx_802_3_payload_len + 12;
48655992Swpaul
48755992Swpaul
48855992Swpaul	bcopy((char *)&rx_frame_802_3.an_rx_dst_addr,
48955992Swpaul	    (char *)&eh->ether_dhost, ETHER_ADDR_LEN);
49055992Swpaul	bcopy((char *)&rx_frame_802_3.an_rx_src_addr,
49155992Swpaul	    (char *)&eh->ether_shost, ETHER_ADDR_LEN);
49255992Swpaul
49355992Swpaul	/* in mbuf header type is just before payload */
49455992Swpaul	error = an_read_data(sc, id, 0x44, (caddr_t)&(eh->ether_type),
49555992Swpaul			     rx_frame_802_3.an_rx_802_3_payload_len);
49655992Swpaul
49778639Sbrooks	if (error != 0) {
49855992Swpaul		m_freem(m);
49955992Swpaul		ifp->if_ierrors++;
50055992Swpaul		return;
50155992Swpaul	}
50255992Swpaul
50355992Swpaul	ifp->if_ipackets++;
50455992Swpaul
50555992Swpaul	/* Receive packet. */
50655992Swpaul	m_adj(m, sizeof(struct ether_header));
50755992Swpaul#ifdef ANCACHE
50855992Swpaul	an_cache_store(sc, eh, m, rx_frame.an_rx_signal_strength);
50955992Swpaul#endif
51055992Swpaul	ether_input(ifp, eh, m);
51155992Swpaul}
51255992Swpaul
51355992Swpaulstatic void an_txeof(sc, status)
51455992Swpaul	struct an_softc		*sc;
51555992Swpaul	int			status;
51655992Swpaul{
51755992Swpaul	struct ifnet		*ifp;
51878639Sbrooks	int			id, i;
51955992Swpaul
52068692Swpaul	/* TX DONE enable lan monitor DJA
52168692Swpaul	   an_enable_sniff();
52268692Swpaul	 */
52368692Swpaul
52455992Swpaul	ifp = &sc->arpcom.ac_if;
52555992Swpaul
52655992Swpaul	ifp->if_timer = 0;
52755992Swpaul	ifp->if_flags &= ~IFF_OACTIVE;
52855992Swpaul
52955992Swpaul	id = CSR_READ_2(sc, AN_TX_CMP_FID);
53055992Swpaul
53155992Swpaul	if (status & AN_EV_TX_EXC) {
53255992Swpaul		ifp->if_oerrors++;
53355992Swpaul	} else
53455992Swpaul		ifp->if_opackets++;
53555992Swpaul
53678639Sbrooks	for (i = 0; i < AN_TX_RING_CNT; i++) {
53778639Sbrooks		if (id == sc->an_rdata.an_tx_ring[i]) {
53878639Sbrooks			sc->an_rdata.an_tx_ring[i] = 0;
53978639Sbrooks			break;
54078639Sbrooks		}
54178639Sbrooks	}
54255992Swpaul
54355992Swpaul	AN_INC(sc->an_rdata.an_tx_cons, AN_TX_RING_CNT);
54455992Swpaul
54555992Swpaul	return;
54655992Swpaul}
54755992Swpaul
54855992Swpaul/*
54955992Swpaul * We abuse the stats updater to check the current NIC status. This
55055992Swpaul * is important because we don't want to allow transmissions until
55155992Swpaul * the NIC has synchronized to the current cell (either as the master
55255992Swpaul * in an ad-hoc group, or as a station connected to an access point).
55355992Swpaul */
55455992Swpaulvoid an_stats_update(xsc)
55555992Swpaul	void			*xsc;
55655992Swpaul{
55755992Swpaul	struct an_softc		*sc;
55855992Swpaul	struct ifnet		*ifp;
55955992Swpaul
56055992Swpaul	sc = xsc;
56167094Swpaul	AN_LOCK(sc);
56255992Swpaul	ifp = &sc->arpcom.ac_if;
56355992Swpaul
56455992Swpaul	sc->an_status.an_type = AN_RID_STATUS;
56555992Swpaul	sc->an_status.an_len = sizeof(struct an_ltv_status);
56655992Swpaul	an_read_record(sc, (struct an_ltv_gen *)&sc->an_status);
56755992Swpaul
56855992Swpaul	if (sc->an_status.an_opmode & AN_STATUS_OPMODE_IN_SYNC)
56955992Swpaul		sc->an_associated = 1;
57055992Swpaul	else
57155992Swpaul		sc->an_associated = 0;
57255992Swpaul
57355992Swpaul	/* Don't do this while we're transmitting */
57455992Swpaul	if (ifp->if_flags & IFF_OACTIVE) {
57555992Swpaul		sc->an_stat_ch = timeout(an_stats_update, sc, hz);
57667094Swpaul		AN_UNLOCK(sc);
57755992Swpaul		return;
57855992Swpaul	}
57955992Swpaul
58055992Swpaul	sc->an_stats.an_len = sizeof(struct an_ltv_stats);
58155992Swpaul	sc->an_stats.an_type = AN_RID_32BITS_CUM;
58255992Swpaul	an_read_record(sc, (struct an_ltv_gen *)&sc->an_stats.an_len);
58355992Swpaul
58455992Swpaul	sc->an_stat_ch = timeout(an_stats_update, sc, hz);
58567094Swpaul	AN_UNLOCK(sc);
58655992Swpaul
58755992Swpaul	return;
58855992Swpaul}
58955992Swpaul
59055992Swpaulvoid an_intr(xsc)
59155992Swpaul	void			*xsc;
59255992Swpaul{
59355992Swpaul	struct an_softc		*sc;
59455992Swpaul	struct ifnet		*ifp;
59555992Swpaul	u_int16_t		status;
59655992Swpaul
59755992Swpaul	sc = (struct an_softc*)xsc;
59855992Swpaul
59967094Swpaul	AN_LOCK(sc);
60067094Swpaul
60167094Swpaul	if (sc->an_gone) {
60267094Swpaul		AN_UNLOCK(sc);
60355992Swpaul		return;
60467094Swpaul	}
60555992Swpaul
60655992Swpaul	ifp = &sc->arpcom.ac_if;
60755992Swpaul
60855992Swpaul	/* Disable interrupts. */
60955992Swpaul	CSR_WRITE_2(sc, AN_INT_EN, 0);
61055992Swpaul
61155992Swpaul	status = CSR_READ_2(sc, AN_EVENT_STAT);
61255992Swpaul	CSR_WRITE_2(sc, AN_EVENT_ACK, ~AN_INTRS);
61355992Swpaul
61455992Swpaul	if (status & AN_EV_AWAKE) {
61555992Swpaul		CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_AWAKE);
61655992Swpaul	}
61755992Swpaul
61855992Swpaul	if (status & AN_EV_LINKSTAT) {
61955992Swpaul		if (CSR_READ_2(sc, AN_LINKSTAT) == AN_LINKSTAT_ASSOCIATED)
62055992Swpaul			sc->an_associated = 1;
62155992Swpaul		else
62255992Swpaul			sc->an_associated = 0;
62355992Swpaul		CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_LINKSTAT);
62455992Swpaul	}
62555992Swpaul
62655992Swpaul	if (status & AN_EV_RX) {
62755992Swpaul		an_rxeof(sc);
62855992Swpaul		CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_RX);
62955992Swpaul	}
63055992Swpaul
63155992Swpaul	if (status & AN_EV_TX) {
63255992Swpaul		an_txeof(sc, status);
63355992Swpaul		CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_TX);
63455992Swpaul	}
63555992Swpaul
63655992Swpaul	if (status & AN_EV_TX_EXC) {
63755992Swpaul		an_txeof(sc, status);
63855992Swpaul		CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_TX_EXC);
63955992Swpaul	}
64055992Swpaul
64155992Swpaul	if (status & AN_EV_ALLOC)
64255992Swpaul		CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_ALLOC);
64355992Swpaul
64455992Swpaul	/* Re-enable interrupts. */
64555992Swpaul	CSR_WRITE_2(sc, AN_INT_EN, AN_INTRS);
64655992Swpaul
64774698Sarchie	if ((ifp->if_flags & IFF_UP) && (ifp->if_snd.ifq_head != NULL))
64855992Swpaul		an_start(ifp);
64955992Swpaul
65067094Swpaul	AN_UNLOCK(sc);
65167094Swpaul
65255992Swpaul	return;
65355992Swpaul}
65455992Swpaul
65555992Swpaulstatic int an_cmd(sc, cmd, val)
65655992Swpaul	struct an_softc		*sc;
65755992Swpaul	int			cmd;
65855992Swpaul	int			val;
65955992Swpaul{
66055992Swpaul	int			i, s = 0;
66155992Swpaul
66255992Swpaul	CSR_WRITE_2(sc, AN_PARAM0, val);
66355992Swpaul	CSR_WRITE_2(sc, AN_PARAM1, 0);
66455992Swpaul	CSR_WRITE_2(sc, AN_PARAM2, 0);
66555992Swpaul	CSR_WRITE_2(sc, AN_COMMAND, cmd);
66655992Swpaul
66755992Swpaul	for (i = 0; i < AN_TIMEOUT; i++) {
66855992Swpaul		if (CSR_READ_2(sc, AN_EVENT_STAT) & AN_EV_CMD)
66955992Swpaul			break;
67055992Swpaul		else {
67155992Swpaul			if (CSR_READ_2(sc, AN_COMMAND) == cmd)
67255992Swpaul				CSR_WRITE_2(sc, AN_COMMAND, cmd);
67355992Swpaul		}
67455992Swpaul	}
67555992Swpaul
67655992Swpaul	for (i = 0; i < AN_TIMEOUT; i++) {
67755992Swpaul		CSR_READ_2(sc, AN_RESP0);
67855992Swpaul		CSR_READ_2(sc, AN_RESP1);
67955992Swpaul		CSR_READ_2(sc, AN_RESP2);
68055992Swpaul		s = CSR_READ_2(sc, AN_STATUS);
68155992Swpaul		if ((s & AN_STAT_CMD_CODE) == (cmd & AN_STAT_CMD_CODE))
68255992Swpaul			break;
68355992Swpaul	}
68455992Swpaul
68555992Swpaul	/* Ack the command */
68655992Swpaul	CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_CMD);
68755992Swpaul
68855992Swpaul	if (CSR_READ_2(sc, AN_COMMAND) & AN_CMD_BUSY)
68955992Swpaul		CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_CLR_STUCK_BUSY);
69055992Swpaul
69155992Swpaul	if (i == AN_TIMEOUT)
69255992Swpaul		return(ETIMEDOUT);
69355992Swpaul
69455992Swpaul	return(0);
69555992Swpaul}
69655992Swpaul
69755992Swpaul/*
69855992Swpaul * This reset sequence may look a little strange, but this is the
69955992Swpaul * most reliable method I've found to really kick the NIC in the
70055992Swpaul * head and force it to reboot correctly.
70155992Swpaul */
70255992Swpaulstatic void an_reset(sc)
70355992Swpaul	struct an_softc		*sc;
70455992Swpaul{
70555992Swpaul	if (sc->an_gone)
70655992Swpaul		return;
70755992Swpaul
70855992Swpaul	an_cmd(sc, AN_CMD_ENABLE, 0);
70955992Swpaul	an_cmd(sc, AN_CMD_FW_RESTART, 0);
71055992Swpaul	an_cmd(sc, AN_CMD_NOOP2, 0);
71155992Swpaul
71255992Swpaul	if (an_cmd(sc, AN_CMD_FORCE_SYNCLOSS, 0) == ETIMEDOUT)
71355992Swpaul		printf("an%d: reset failed\n", sc->an_unit);
71455992Swpaul
71555992Swpaul	an_cmd(sc, AN_CMD_DISABLE, 0);
71655992Swpaul
71755992Swpaul	return;
71855992Swpaul}
71955992Swpaul
72055992Swpaul/*
72155992Swpaul * Read an LTV record from the NIC.
72255992Swpaul */
72355992Swpaulstatic int an_read_record(sc, ltv)
72455992Swpaul	struct an_softc		*sc;
72555992Swpaul	struct an_ltv_gen	*ltv;
72655992Swpaul{
72755992Swpaul	u_int16_t		*ptr;
72878639Sbrooks	u_int8_t		*ptr2;
72955992Swpaul	int			i, len;
73055992Swpaul
73178639Sbrooks	if (ltv->an_len < 4 || ltv->an_type == 0)
73255992Swpaul		return(EINVAL);
73355992Swpaul
73455992Swpaul	/* Tell the NIC to enter record read mode. */
73555992Swpaul	if (an_cmd(sc, AN_CMD_ACCESS|AN_ACCESS_READ, ltv->an_type)) {
73655992Swpaul		printf("an%d: RID access failed\n", sc->an_unit);
73755992Swpaul		return(EIO);
73855992Swpaul	}
73955992Swpaul
74055992Swpaul	/* Seek to the record. */
74155992Swpaul	if (an_seek(sc, ltv->an_type, 0, AN_BAP1)) {
74255992Swpaul		printf("an%d: seek to record failed\n", sc->an_unit);
74355992Swpaul		return(EIO);
74455992Swpaul	}
74555992Swpaul
74655992Swpaul	/*
74755992Swpaul	 * Read the length and record type and make sure they
74855992Swpaul	 * match what we expect (this verifies that we have enough
74955992Swpaul	 * room to hold all of the returned data).
75078639Sbrooks	 * Length includes type but not length.
75155992Swpaul	 */
75255992Swpaul	len = CSR_READ_2(sc, AN_DATA1);
75377217Sphk	if (len > (ltv->an_len - 2)) {
75455992Swpaul		printf("an%d: record length mismatch -- expected %d, "
75578639Sbrooks		    "got %d for Rid %x\n", sc->an_unit,
75678639Sbrooks		    ltv->an_len - 2, len, ltv->an_type);
75778639Sbrooks		len = ltv->an_len - 2;
75878639Sbrooks	} else {
75978639Sbrooks		ltv->an_len = len + 2;
76055992Swpaul	}
76155992Swpaul
76255992Swpaul	/* Now read the data. */
76378639Sbrooks	len -= 2;	/* skip the type */
76455992Swpaul	ptr = &ltv->an_val;
76578639Sbrooks	for (i = len; i > 1; i -= 2)
76678639Sbrooks		*ptr++ = CSR_READ_2(sc, AN_DATA1);
76778639Sbrooks	if (i) {
76878639Sbrooks		ptr2 = (u_int8_t *)ptr;
76978639Sbrooks		*ptr2 = CSR_READ_1(sc, AN_DATA1);
77078639Sbrooks	}
77178639Sbrooks	if (an_dump)
77278639Sbrooks		an_dump_record(sc, ltv, "Read");
77355992Swpaul
77455992Swpaul	return(0);
77555992Swpaul}
77655992Swpaul
77755992Swpaul/*
77855992Swpaul * Same as read, except we inject data instead of reading it.
77955992Swpaul */
78055992Swpaulstatic int an_write_record(sc, ltv)
78155992Swpaul	struct an_softc		*sc;
78255992Swpaul	struct an_ltv_gen	*ltv;
78355992Swpaul{
78455992Swpaul	u_int16_t		*ptr;
78578639Sbrooks	u_int8_t		*ptr2;
78678639Sbrooks	int			i, len;
78755992Swpaul
78878639Sbrooks	if (an_dump)
78978639Sbrooks		an_dump_record(sc, ltv, "Write");
79078639Sbrooks
79155992Swpaul	if (an_cmd(sc, AN_CMD_ACCESS|AN_ACCESS_READ, ltv->an_type))
79255992Swpaul		return(EIO);
79378639Sbrooks
79455992Swpaul	if (an_seek(sc, ltv->an_type, 0, AN_BAP1))
79555992Swpaul		return(EIO);
79655992Swpaul
79778639Sbrooks	/*
79878639Sbrooks	 * Length includes type but not length.
79978639Sbrooks	 */
80078639Sbrooks	len = ltv->an_len - 2;
80178639Sbrooks	CSR_WRITE_2(sc, AN_DATA1, len);
80268692Swpaul
80378639Sbrooks	len -= 2;	/* skip the type */
80455992Swpaul	ptr = &ltv->an_val;
80578639Sbrooks	for (i = len; i > 1; i -= 2)
80678639Sbrooks		CSR_WRITE_2(sc, AN_DATA1, *ptr++);
80778639Sbrooks	if (i) {
80878639Sbrooks		ptr2 = (u_int8_t *)ptr;
80978639Sbrooks		CSR_WRITE_1(sc, AN_DATA0, *ptr2);
81078639Sbrooks	}
81155992Swpaul
81255992Swpaul	if (an_cmd(sc, AN_CMD_ACCESS|AN_ACCESS_WRITE, ltv->an_type))
81355992Swpaul		return(EIO);
81455992Swpaul
81555992Swpaul	return(0);
81655992Swpaul}
81755992Swpaul
81878639Sbrooksstatic void an_dump_record(sc, ltv, string)
81978639Sbrooks	struct an_softc		*sc;
82078639Sbrooks	struct an_ltv_gen	*ltv;
82178639Sbrooks	char			*string;
82278639Sbrooks{
82378639Sbrooks	u_int8_t		*ptr2;
82478639Sbrooks	int			len;
82578639Sbrooks	int			i;
82678639Sbrooks	int			count = 0;
82778639Sbrooks	char			buf[17], temp;
82878639Sbrooks
82978639Sbrooks	len = ltv->an_len - 4;
83078639Sbrooks	printf("an%d: RID %4x, Length %4d, Mode %s\n",
83178639Sbrooks		sc->an_unit, ltv->an_type, ltv->an_len - 4, string);
83278639Sbrooks
83378639Sbrooks	if (an_dump == 1 || (an_dump == ltv->an_type)) {
83478639Sbrooks		printf("an%d:\t", sc->an_unit);
83578639Sbrooks		bzero(buf,sizeof(buf));
83678639Sbrooks
83778639Sbrooks		ptr2 = (u_int8_t *)&ltv->an_val;
83878639Sbrooks		for (i = len; i > 0; i--) {
83978639Sbrooks			printf("%02x ", *ptr2);
84078639Sbrooks
84178639Sbrooks			temp = *ptr2++;
84278639Sbrooks			if (temp >= ' ' && temp <= '~')
84378639Sbrooks				buf[count] = temp;
84478639Sbrooks			else if (temp >= 'A' && temp <= 'Z')
84578639Sbrooks				buf[count] = temp;
84678639Sbrooks			else
84778639Sbrooks				buf[count] = '.';
84878639Sbrooks			if (++count == 16) {
84978639Sbrooks				count = 0;
85078639Sbrooks				printf("%s\n",buf);
85178639Sbrooks				printf("an%d:\t", sc->an_unit);
85278639Sbrooks				bzero(buf,sizeof(buf));
85378639Sbrooks			}
85478639Sbrooks		}
85578639Sbrooks		for (; count != 16; count++) {
85678639Sbrooks			printf("   ");
85778639Sbrooks		}
85878639Sbrooks		printf(" %s\n",buf);
85978639Sbrooks	}
86078639Sbrooks}
86178639Sbrooks
86255992Swpaulstatic int an_seek(sc, id, off, chan)
86355992Swpaul	struct an_softc		*sc;
86455992Swpaul	int			id, off, chan;
86555992Swpaul{
86655992Swpaul	int			i;
86755992Swpaul	int			selreg, offreg;
86855992Swpaul
86955992Swpaul	switch (chan) {
87055992Swpaul	case AN_BAP0:
87155992Swpaul		selreg = AN_SEL0;
87255992Swpaul		offreg = AN_OFF0;
87355992Swpaul		break;
87455992Swpaul	case AN_BAP1:
87555992Swpaul		selreg = AN_SEL1;
87655992Swpaul		offreg = AN_OFF1;
87755992Swpaul		break;
87855992Swpaul	default:
87955992Swpaul		printf("an%d: invalid data path: %x\n", sc->an_unit, chan);
88055992Swpaul		return(EIO);
88155992Swpaul	}
88255992Swpaul
88355992Swpaul	CSR_WRITE_2(sc, selreg, id);
88455992Swpaul	CSR_WRITE_2(sc, offreg, off);
88555992Swpaul
88655992Swpaul	for (i = 0; i < AN_TIMEOUT; i++) {
88755992Swpaul		if (!(CSR_READ_2(sc, offreg) & (AN_OFF_BUSY|AN_OFF_ERR)))
88855992Swpaul			break;
88955992Swpaul	}
89055992Swpaul
89155992Swpaul	if (i == AN_TIMEOUT)
89255992Swpaul		return(ETIMEDOUT);
89355992Swpaul
89455992Swpaul	return(0);
89555992Swpaul}
89655992Swpaul
89755992Swpaulstatic int an_read_data(sc, id, off, buf, len)
89855992Swpaul	struct an_softc		*sc;
89955992Swpaul	int			id, off;
90055992Swpaul	caddr_t			buf;
90155992Swpaul	int			len;
90255992Swpaul{
90355992Swpaul	int			i;
90455992Swpaul	u_int16_t		*ptr;
90555992Swpaul	u_int8_t		*ptr2;
90655992Swpaul
90755992Swpaul	if (off != -1) {
90855992Swpaul		if (an_seek(sc, id, off, AN_BAP1))
90955992Swpaul			return(EIO);
91055992Swpaul	}
91155992Swpaul
91255992Swpaul	ptr = (u_int16_t *)buf;
91378639Sbrooks	for (i = len; i > 1; i -= 2)
91478639Sbrooks		*ptr++ = CSR_READ_2(sc, AN_DATA1);
91578639Sbrooks	if (i) {
91678639Sbrooks		ptr2 = (u_int8_t *)ptr;
91778639Sbrooks		*ptr2 = CSR_READ_1(sc, AN_DATA1);
91855992Swpaul	}
91955992Swpaul
92055992Swpaul	return(0);
92155992Swpaul}
92255992Swpaul
92355992Swpaulstatic int an_write_data(sc, id, off, buf, len)
92455992Swpaul	struct an_softc		*sc;
92555992Swpaul	int			id, off;
92655992Swpaul	caddr_t			buf;
92755992Swpaul	int			len;
92855992Swpaul{
92955992Swpaul	int			i;
93055992Swpaul	u_int16_t		*ptr;
93155992Swpaul	u_int8_t		*ptr2;
93255992Swpaul
93355992Swpaul	if (off != -1) {
93455992Swpaul		if (an_seek(sc, id, off, AN_BAP0))
93555992Swpaul			return(EIO);
93655992Swpaul	}
93755992Swpaul
93855992Swpaul	ptr = (u_int16_t *)buf;
93978639Sbrooks	for (i = len; i > 1; i -= 2)
94078639Sbrooks		CSR_WRITE_2(sc, AN_DATA0, *ptr++);
94178639Sbrooks	if (i) {
94278639Sbrooks	        ptr2 = (u_int8_t *)ptr;
94378639Sbrooks	        CSR_WRITE_1(sc, AN_DATA0, *ptr2);
94455992Swpaul	}
94555992Swpaul
94655992Swpaul	return(0);
94755992Swpaul}
94855992Swpaul
94955992Swpaul/*
95055992Swpaul * Allocate a region of memory inside the NIC and zero
95155992Swpaul * it out.
95255992Swpaul */
95355992Swpaulstatic int an_alloc_nicmem(sc, len, id)
95455992Swpaul	struct an_softc		*sc;
95555992Swpaul	int			len;
95655992Swpaul	int			*id;
95755992Swpaul{
95855992Swpaul	int			i;
95955992Swpaul
96055992Swpaul	if (an_cmd(sc, AN_CMD_ALLOC_MEM, len)) {
96155992Swpaul		printf("an%d: failed to allocate %d bytes on NIC\n",
96255992Swpaul		    sc->an_unit, len);
96355992Swpaul		return(ENOMEM);
96455992Swpaul	}
96555992Swpaul
96655992Swpaul	for (i = 0; i < AN_TIMEOUT; i++) {
96755992Swpaul		if (CSR_READ_2(sc, AN_EVENT_STAT) & AN_EV_ALLOC)
96855992Swpaul			break;
96955992Swpaul	}
97055992Swpaul
97155992Swpaul	if (i == AN_TIMEOUT)
97255992Swpaul		return(ETIMEDOUT);
97355992Swpaul
97455992Swpaul	CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_ALLOC);
97555992Swpaul	*id = CSR_READ_2(sc, AN_ALLOC_FID);
97655992Swpaul
97755992Swpaul	if (an_seek(sc, *id, 0, AN_BAP0))
97855992Swpaul		return(EIO);
97955992Swpaul
98055992Swpaul	for (i = 0; i < len / 2; i++)
98155992Swpaul		CSR_WRITE_2(sc, AN_DATA0, 0);
98255992Swpaul
98355992Swpaul	return(0);
98455992Swpaul}
98555992Swpaul
98655992Swpaulstatic void an_setdef(sc, areq)
98755992Swpaul	struct an_softc		*sc;
98855992Swpaul	struct an_req		*areq;
98955992Swpaul{
99055992Swpaul	struct sockaddr_dl	*sdl;
99155992Swpaul	struct ifaddr		*ifa;
99255992Swpaul	struct ifnet		*ifp;
99355992Swpaul	struct an_ltv_genconfig	*cfg;
99455992Swpaul	struct an_ltv_ssidlist	*ssid;
99555992Swpaul	struct an_ltv_aplist	*ap;
99655992Swpaul	struct an_ltv_gen	*sp;
99755992Swpaul
99855992Swpaul	ifp = &sc->arpcom.ac_if;
99955992Swpaul
100055992Swpaul	switch (areq->an_type) {
100155992Swpaul	case AN_RID_GENCONFIG:
100255992Swpaul		cfg = (struct an_ltv_genconfig *)areq;
100355992Swpaul
100455992Swpaul		ifa = ifnet_addrs[ifp->if_index - 1];
100555992Swpaul		sdl = (struct sockaddr_dl *)ifa->ifa_addr;
100655992Swpaul		bcopy((char *)&cfg->an_macaddr, (char *)&sc->arpcom.ac_enaddr,
100755992Swpaul		    ETHER_ADDR_LEN);
100855992Swpaul		bcopy((char *)&cfg->an_macaddr, LLADDR(sdl), ETHER_ADDR_LEN);
100955992Swpaul
101055992Swpaul		bcopy((char *)cfg, (char *)&sc->an_config,
101155992Swpaul			sizeof(struct an_ltv_genconfig));
101255992Swpaul		break;
101355992Swpaul	case AN_RID_SSIDLIST:
101455992Swpaul		ssid = (struct an_ltv_ssidlist *)areq;
101555992Swpaul		bcopy((char *)ssid, (char *)&sc->an_ssidlist,
101655992Swpaul			sizeof(struct an_ltv_ssidlist));
101755992Swpaul		break;
101855992Swpaul	case AN_RID_APLIST:
101955992Swpaul		ap = (struct an_ltv_aplist *)areq;
102055992Swpaul		bcopy((char *)ap, (char *)&sc->an_aplist,
102155992Swpaul			sizeof(struct an_ltv_aplist));
102255992Swpaul		break;
102355992Swpaul	case AN_RID_TX_SPEED:
102455992Swpaul		sp = (struct an_ltv_gen *)areq;
102555992Swpaul		sc->an_tx_rate = sp->an_val;
102655992Swpaul		break;
102768692Swpaul	case AN_RID_WEP_TEMP:
102868692Swpaul		/* Disable the MAC. */
102968692Swpaul		an_cmd(sc, AN_CMD_DISABLE, 0);
103068692Swpaul
103168692Swpaul		/* Just write the Key, we don't want to save it */
103268692Swpaul		an_write_record(sc, (struct an_ltv_gen *)areq);
103368692Swpaul
103468692Swpaul		/* Turn the MAC back on. */
103568692Swpaul		an_cmd(sc, AN_CMD_ENABLE, 0);
103668692Swpaul
103768692Swpaul		break;
103868692Swpaul	case AN_RID_WEP_PERM:
103968692Swpaul
104068692Swpaul		/* Disable the MAC. */
104168692Swpaul		an_cmd(sc, AN_CMD_DISABLE, 0);
104268692Swpaul
104368692Swpaul		/* Just write the Key, the card will save it in this mode */
104468692Swpaul		an_write_record(sc, (struct an_ltv_gen *)areq);
104568692Swpaul
104668692Swpaul		/* Turn the MAC back on. */
104768692Swpaul		an_cmd(sc, AN_CMD_ENABLE, 0);
104868692Swpaul
104968692Swpaul		break;
105055992Swpaul	default:
105155992Swpaul		printf("an%d: unknown RID: %x\n", sc->an_unit, areq->an_type);
105255992Swpaul		return;
105355992Swpaul		break;
105455992Swpaul	}
105555992Swpaul
105655992Swpaul
105755992Swpaul	/* Reinitialize the card. */
105874698Sarchie	if (ifp->if_flags)
105955992Swpaul		an_init(sc);
106055992Swpaul
106155992Swpaul	return;
106255992Swpaul}
106355992Swpaul
106455992Swpaul/*
106555992Swpaul * We can't change the NIC configuration while the MAC is enabled,
106655992Swpaul * so in order to turn on RX monitor mode, we have to turn the MAC
106755992Swpaul * off first.
106855992Swpaul */
106955992Swpaulstatic void an_promisc(sc, promisc)
107055992Swpaul	struct an_softc		*sc;
107155992Swpaul	int			promisc;
107255992Swpaul{
107374698Sarchie	an_cmd(sc, AN_CMD_SET_MODE, promisc ? 0xffff : 0);
107455992Swpaul
107555992Swpaul	return;
107655992Swpaul}
107755992Swpaul
107855992Swpaulstatic int an_ioctl(ifp, command, data)
107955992Swpaul	struct ifnet		*ifp;
108055992Swpaul	u_long			command;
108155992Swpaul	caddr_t			data;
108255992Swpaul{
108367094Swpaul	int			error = 0;
108477217Sphk	int			len;
108577217Sphk	int			i;
108655992Swpaul	struct an_softc		*sc;
108755992Swpaul	struct an_req		areq;
108855992Swpaul	struct ifreq		*ifr;
108961816Sroberto	struct proc		*p = curproc;
109077217Sphk	struct ieee80211req	*ireq;
109177217Sphk	u_int8_t		tmpstr[IEEE80211_NWID_LEN*2];
109277217Sphk	u_int8_t		*tmpptr;
109377217Sphk	struct an_ltv_genconfig	*config;
109477217Sphk	struct an_ltv_key	*key;
109577217Sphk	struct an_ltv_status	*status;
109677217Sphk	struct an_ltv_ssidlist	*ssids;
109755992Swpaul
109855992Swpaul	sc = ifp->if_softc;
109967094Swpaul	AN_LOCK(sc);
110055992Swpaul	ifr = (struct ifreq *)data;
110177217Sphk	ireq = (struct ieee80211req *)data;
110255992Swpaul
110377217Sphk	config = (struct an_ltv_genconfig *)&areq;
110477217Sphk	key = (struct an_ltv_key *)&areq;
110577217Sphk	status = (struct an_ltv_status *)&areq;
110677217Sphk	ssids = (struct an_ltv_ssidlist *)&areq;
110777217Sphk
110864429Speter	if (sc->an_gone) {
110961816Sroberto		error = ENODEV;
111061816Sroberto		goto out;
111161816Sroberto	}
111255992Swpaul
111355992Swpaul	switch(command) {
111455992Swpaul	case SIOCSIFADDR:
111555992Swpaul	case SIOCGIFADDR:
111655992Swpaul	case SIOCSIFMTU:
111755992Swpaul		error = ether_ioctl(ifp, command, data);
111855992Swpaul		break;
111955992Swpaul	case SIOCSIFFLAGS:
112055992Swpaul		if (ifp->if_flags & IFF_UP) {
112155992Swpaul			if (ifp->if_flags & IFF_RUNNING &&
112255992Swpaul			    ifp->if_flags & IFF_PROMISC &&
112355992Swpaul			    !(sc->an_if_flags & IFF_PROMISC)) {
112455992Swpaul				an_promisc(sc, 1);
112555992Swpaul			} else if (ifp->if_flags & IFF_RUNNING &&
112655992Swpaul			    !(ifp->if_flags & IFF_PROMISC) &&
112755992Swpaul			    sc->an_if_flags & IFF_PROMISC) {
112855992Swpaul				an_promisc(sc, 0);
112955992Swpaul			} else
113055992Swpaul				an_init(sc);
113155992Swpaul		} else {
113255992Swpaul			if (ifp->if_flags & IFF_RUNNING)
113355992Swpaul				an_stop(sc);
113455992Swpaul		}
113555992Swpaul		sc->an_if_flags = ifp->if_flags;
113655992Swpaul		error = 0;
113755992Swpaul		break;
113877217Sphk	case SIOCSIFMEDIA:
113977217Sphk	case SIOCGIFMEDIA:
114077217Sphk		error = ifmedia_ioctl(ifp, ifr, &sc->an_ifmedia, command);
114177217Sphk		break;
114255992Swpaul	case SIOCADDMULTI:
114355992Swpaul	case SIOCDELMULTI:
114455992Swpaul		/* The Aironet has no multicast filter. */
114555992Swpaul		error = 0;
114655992Swpaul		break;
114755992Swpaul	case SIOCGAIRONET:
114855992Swpaul		error = copyin(ifr->ifr_data, &areq, sizeof(areq));
114978639Sbrooks		if (error != 0)
115055992Swpaul			break;
115155992Swpaul#ifdef ANCACHE
115255992Swpaul		if (areq.an_type == AN_RID_ZERO_CACHE) {
115355992Swpaul			sc->an_sigitems = sc->an_nextitem = 0;
115455992Swpaul			break;
115555992Swpaul		} else if (areq.an_type == AN_RID_READ_CACHE) {
115655992Swpaul			char *pt = (char *)&areq.an_val;
115755992Swpaul			bcopy((char *)&sc->an_sigitems, (char *)pt,
115855992Swpaul			    sizeof(int));
115955992Swpaul			pt += sizeof(int);
116055992Swpaul			areq.an_len = sizeof(int) / 2;
116155992Swpaul			bcopy((char *)&sc->an_sigcache, (char *)pt,
116255992Swpaul			    sizeof(struct an_sigcache) * sc->an_sigitems);
116355992Swpaul			areq.an_len += ((sizeof(struct an_sigcache) *
116455992Swpaul			    sc->an_sigitems) / 2) + 1;
116555992Swpaul		} else
116655992Swpaul#endif
116755992Swpaul		if (an_read_record(sc, (struct an_ltv_gen *)&areq)) {
116855992Swpaul			error = EINVAL;
116955992Swpaul			break;
117055992Swpaul		}
117155992Swpaul		error = copyout(&areq, ifr->ifr_data, sizeof(areq));
117255992Swpaul		break;
117355992Swpaul	case SIOCSAIRONET:
117464429Speter		if ((error = suser(p)))
117564429Speter			goto out;
117655992Swpaul		error = copyin(ifr->ifr_data, &areq, sizeof(areq));
117778639Sbrooks		if (error != 0)
117855992Swpaul			break;
117955992Swpaul		an_setdef(sc, &areq);
118055992Swpaul		break;
118177217Sphk	case SIOCG80211:
118277217Sphk		areq.an_len = sizeof(areq);
118377217Sphk		switch(ireq->i_type) {
118477217Sphk		case IEEE80211_IOC_SSID:
118578639Sbrooks			if (ireq->i_val == -1) {
118677217Sphk				areq.an_type = AN_RID_STATUS;
118777217Sphk				if (an_read_record(sc,
118877217Sphk				    (struct an_ltv_gen *)&areq)) {
118977217Sphk					error = EINVAL;
119077217Sphk					break;
119177217Sphk				}
119277217Sphk				len = status->an_ssidlen;
119377217Sphk				tmpptr = status->an_ssid;
119478639Sbrooks			} else if (ireq->i_val >= 0) {
119577217Sphk				areq.an_type = AN_RID_SSIDLIST;
119677217Sphk				if (an_read_record(sc,
119777217Sphk				    (struct an_ltv_gen *)&areq)) {
119877217Sphk					error = EINVAL;
119977217Sphk					break;
120077217Sphk				}
120178639Sbrooks				if (ireq->i_val == 0) {
120277217Sphk					len = ssids->an_ssid1_len;
120377217Sphk					tmpptr = ssids->an_ssid1;
120478639Sbrooks				} else if (ireq->i_val == 1) {
120577217Sphk					len = ssids->an_ssid2_len;
120677217Sphk					tmpptr = ssids->an_ssid3;
120778639Sbrooks				} else if (ireq->i_val == 1) {
120877217Sphk					len = ssids->an_ssid3_len;
120977217Sphk					tmpptr = ssids->an_ssid3;
121077217Sphk				} else {
121177217Sphk					error = EINVAL;
121277217Sphk					break;
121377217Sphk				}
121477217Sphk			} else {
121577217Sphk				error = EINVAL;
121677217Sphk				break;
121777217Sphk			}
121878639Sbrooks			if (len > IEEE80211_NWID_LEN) {
121977217Sphk				error = EINVAL;
122077217Sphk				break;
122177217Sphk			}
122277217Sphk			ireq->i_len = len;
122377217Sphk			bzero(tmpstr, IEEE80211_NWID_LEN);
122477217Sphk			bcopy(tmpptr, tmpstr, len);
122577217Sphk			error = copyout(tmpstr, ireq->i_data,
122677217Sphk			    IEEE80211_NWID_LEN);
122777217Sphk			break;
122877217Sphk		case IEEE80211_IOC_NUMSSIDS:
122977217Sphk			ireq->i_val = 3;
123077217Sphk			break;
123177217Sphk		case IEEE80211_IOC_WEP:
123277217Sphk			areq.an_type = AN_RID_ACTUALCFG;
123377217Sphk			if (an_read_record(sc,
123477217Sphk			    (struct an_ltv_gen *)&areq)) {
123577217Sphk				error = EINVAL;
123677217Sphk				break;
123777217Sphk			}
123878639Sbrooks			if (config->an_authtype & AN_AUTHTYPE_PRIVACY_IN_USE) {
123978639Sbrooks				if (config->an_authtype &
124077217Sphk				    AN_AUTHTYPE_ALLOW_UNENCRYPTED)
124177217Sphk					ireq->i_val = IEEE80211_WEP_MIXED;
124277217Sphk				else
124377217Sphk					ireq->i_val = IEEE80211_WEP_ON;
124477217Sphk
124577217Sphk			} else {
124677217Sphk				ireq->i_val = IEEE80211_WEP_OFF;
124777217Sphk			}
124877217Sphk			break;
124977217Sphk		case IEEE80211_IOC_WEPKEY:
125077217Sphk			/*
125177217Sphk			 * XXX: I'm not entierly convinced this is
125277217Sphk			 * correct, but it's what is implemented in
125377217Sphk			 * ancontrol so it will have to do until we get
125477217Sphk			 * access to actual Cisco code.
125577217Sphk			 */
125678639Sbrooks			if (ireq->i_val < 0 || ireq->i_val > 7) {
125777217Sphk				error = EINVAL;
125877217Sphk				break;
125977217Sphk			}
126077217Sphk			len = 0;
126178639Sbrooks			if (ireq->i_val < 4) {
126277217Sphk				areq.an_type = AN_RID_WEP_TEMP;
126378639Sbrooks				for (i = 0; i < 5; i++) {
126477217Sphk					if (an_read_record(sc,
126577217Sphk					    (struct an_ltv_gen *)&areq)) {
126677217Sphk						error = EINVAL;
126777217Sphk						break;
126877217Sphk					}
126978639Sbrooks					if (key->kindex == 0xffff)
127077217Sphk						break;
127178639Sbrooks					if (key->kindex == ireq->i_val)
127278639Sbrooks						len = key->klen;
127377217Sphk					/* Required to get next entry */
127477217Sphk					areq.an_type = AN_RID_WEP_PERM;
127577217Sphk				}
127678639Sbrooks				if (error != 0)
127777217Sphk					break;
127877217Sphk			}
127977217Sphk			/* We aren't allowed to read the value of the
128077217Sphk			 * key from the card so we just output zeros
128177217Sphk			 * like we would if we could read the card, but
128277217Sphk			 * denied the user access.
128377217Sphk			 */
128477217Sphk			bzero(tmpstr, len);
128577217Sphk			ireq->i_len = len;
128677217Sphk			error = copyout(tmpstr, ireq->i_data, len);
128777217Sphk			break;
128877217Sphk		case IEEE80211_IOC_NUMWEPKEYS:
128977217Sphk			ireq->i_val = 8;
129077217Sphk			break;
129177217Sphk		case IEEE80211_IOC_WEPTXKEY:
129278639Sbrooks			/*
129378639Sbrooks			 * For some strange reason, you have to read all
129478639Sbrooks			 * keys before you can read the txkey.
129578639Sbrooks			 */
129678639Sbrooks			areq.an_type = AN_RID_WEP_TEMP;
129778639Sbrooks			for (i = 0; i < 5; i++) {
129878639Sbrooks				if (an_read_record(sc,
129978639Sbrooks				    (struct an_ltv_gen *)&areq)) {
130078639Sbrooks					error = EINVAL;
130178639Sbrooks					break;
130278639Sbrooks				}
130378639Sbrooks				if (key->kindex == 0xffff)
130478639Sbrooks					break;
130578639Sbrooks				/* Required to get next entry */
130678639Sbrooks				areq.an_type = AN_RID_WEP_PERM;
130778639Sbrooks			}
130878639Sbrooks			if (error != 0)
130978639Sbrooks				break;
131078639Sbrooks
131177217Sphk			areq.an_type = AN_RID_WEP_PERM;
131277217Sphk			key->kindex = 0xffff;
131377217Sphk			if (an_read_record(sc,
131477217Sphk			    (struct an_ltv_gen *)&areq)) {
131577217Sphk				error = EINVAL;
131677217Sphk				break;
131777217Sphk			}
131877217Sphk			ireq->i_val = key->mac[0];
131977217Sphk			break;
132077217Sphk		case IEEE80211_IOC_AUTHMODE:
132177217Sphk			areq.an_type = AN_RID_ACTUALCFG;
132277217Sphk			if (an_read_record(sc,
132377217Sphk			    (struct an_ltv_gen *)&areq)) {
132477217Sphk				error = EINVAL;
132577217Sphk				break;
132677217Sphk			}
132777217Sphk			if ((config->an_authtype & AN_AUTHTYPE_MASK) ==
132877217Sphk			    AN_AUTHTYPE_NONE) {
132977217Sphk			    ireq->i_val = IEEE80211_AUTH_NONE;
133077217Sphk			} else if ((config->an_authtype & AN_AUTHTYPE_MASK) ==
133177217Sphk			    AN_AUTHTYPE_OPEN) {
133277217Sphk			    ireq->i_val = IEEE80211_AUTH_OPEN;
133377217Sphk			} else if ((config->an_authtype & AN_AUTHTYPE_MASK) ==
133477217Sphk			    AN_AUTHTYPE_SHAREDKEY) {
133577217Sphk			    ireq->i_val = IEEE80211_AUTH_SHARED;
133677217Sphk			} else
133777217Sphk				error = EINVAL;
133877217Sphk			break;
133977217Sphk		case IEEE80211_IOC_STATIONNAME:
134077217Sphk			areq.an_type = AN_RID_ACTUALCFG;
134177217Sphk			if (an_read_record(sc,
134277217Sphk			    (struct an_ltv_gen *)&areq)) {
134377217Sphk				error = EINVAL;
134477217Sphk				break;
134577217Sphk			}
134677217Sphk			ireq->i_len = sizeof(config->an_nodename);
134777217Sphk			tmpptr = config->an_nodename;
134877217Sphk			bzero(tmpstr, IEEE80211_NWID_LEN);
134977217Sphk			bcopy(tmpptr, tmpstr, ireq->i_len);
135077217Sphk			error = copyout(tmpstr, ireq->i_data,
135177217Sphk			    IEEE80211_NWID_LEN);
135277217Sphk			break;
135377217Sphk		case IEEE80211_IOC_CHANNEL:
135477217Sphk			areq.an_type = AN_RID_STATUS;
135577217Sphk			if (an_read_record(sc,
135677217Sphk			    (struct an_ltv_gen *)&areq)) {
135777217Sphk				error = EINVAL;
135877217Sphk				break;
135977217Sphk			}
136077217Sphk			ireq->i_val = status->an_cur_channel;
136177217Sphk			break;
136277217Sphk		case IEEE80211_IOC_POWERSAVE:
136377217Sphk			areq.an_type = AN_RID_ACTUALCFG;
136477217Sphk			if (an_read_record(sc,
136577217Sphk			    (struct an_ltv_gen *)&areq)) {
136677217Sphk				error = EINVAL;
136777217Sphk				break;
136877217Sphk			}
136978639Sbrooks			if (config->an_psave_mode == AN_PSAVE_NONE) {
137077217Sphk				ireq->i_val = IEEE80211_POWERSAVE_OFF;
137178639Sbrooks			} else if (config->an_psave_mode == AN_PSAVE_CAM) {
137277217Sphk				ireq->i_val = IEEE80211_POWERSAVE_CAM;
137378639Sbrooks			} else if (config->an_psave_mode == AN_PSAVE_PSP) {
137477217Sphk				ireq->i_val = IEEE80211_POWERSAVE_PSP;
137578639Sbrooks			} else if (config->an_psave_mode == AN_PSAVE_PSP_CAM) {
137677217Sphk				ireq->i_val = IEEE80211_POWERSAVE_PSP_CAM;
137777217Sphk			} else
137877217Sphk				error = EINVAL;
137977217Sphk			break;
138077217Sphk		case IEEE80211_IOC_POWERSAVESLEEP:
138177217Sphk			areq.an_type = AN_RID_ACTUALCFG;
138277217Sphk			if (an_read_record(sc,
138377217Sphk			    (struct an_ltv_gen *)&areq)) {
138477217Sphk				error = EINVAL;
138577217Sphk				break;
138677217Sphk			}
138777217Sphk			ireq->i_val = config->an_listen_interval;
138877217Sphk			break;
138977217Sphk		}
139077217Sphk		break;
139177217Sphk	case SIOCS80211:
139277217Sphk		if ((error = suser(p)))
139377217Sphk			goto out;
139477217Sphk		areq.an_len = sizeof(areq);
139577217Sphk		/*
139677217Sphk		 * We need a config structure for everything but the WEP
139777217Sphk		 * key management and SSIDs so we get it now so avoid
139877217Sphk		 * duplicating this code every time.
139977217Sphk		 */
140077217Sphk		if (ireq->i_type != IEEE80211_IOC_SSID &&
140177217Sphk		    ireq->i_type != IEEE80211_IOC_WEPKEY &&
140277217Sphk		    ireq->i_type != IEEE80211_IOC_WEPTXKEY) {
140377217Sphk			areq.an_type = AN_RID_GENCONFIG;
140477217Sphk			if (an_read_record(sc,
140577217Sphk			    (struct an_ltv_gen *)&areq)) {
140677217Sphk				error = EINVAL;
140777217Sphk				break;
140877217Sphk			}
140977217Sphk		}
141077217Sphk		switch(ireq->i_type) {
141177217Sphk		case IEEE80211_IOC_SSID:
141277217Sphk			areq.an_type = AN_RID_SSIDLIST;
141377217Sphk			if (an_read_record(sc,
141477217Sphk			    (struct an_ltv_gen *)&areq)) {
141577217Sphk				error = EINVAL;
141677217Sphk				break;
141777217Sphk			}
141878639Sbrooks			if (ireq->i_len > IEEE80211_NWID_LEN) {
141977217Sphk				error = EINVAL;
142077217Sphk				break;
142177217Sphk			}
142277217Sphk			switch (ireq->i_val) {
142377217Sphk			case 0:
142477217Sphk				error = copyin(ireq->i_data,
142577217Sphk				    ssids->an_ssid1, ireq->i_len);
142677217Sphk				ssids->an_ssid1_len = ireq->i_len;
142777217Sphk				break;
142877217Sphk			case 1:
142977217Sphk				error = copyin(ireq->i_data,
143077217Sphk				    ssids->an_ssid2, ireq->i_len);
143177217Sphk				ssids->an_ssid2_len = ireq->i_len;
143277217Sphk				break;
143377217Sphk			case 2:
143477217Sphk				error = copyin(ireq->i_data,
143577217Sphk				    ssids->an_ssid3, ireq->i_len);
143677217Sphk				ssids->an_ssid3_len = ireq->i_len;
143777217Sphk				break;
143877217Sphk			default:
143977217Sphk				error = EINVAL;
144077217Sphk				break;
144177217Sphk			}
144277217Sphk			break;
144377217Sphk		case IEEE80211_IOC_WEP:
144477217Sphk			switch (ireq->i_val) {
144577217Sphk			case IEEE80211_WEP_OFF:
144677217Sphk				config->an_authtype &=
144777217Sphk				    ~(AN_AUTHTYPE_PRIVACY_IN_USE |
144877217Sphk				    AN_AUTHTYPE_ALLOW_UNENCRYPTED);
144977217Sphk				break;
145077217Sphk			case IEEE80211_WEP_ON:
145177217Sphk				config->an_authtype |=
145277217Sphk				    AN_AUTHTYPE_PRIVACY_IN_USE;
145377217Sphk				config->an_authtype &=
145477217Sphk				    ~AN_AUTHTYPE_ALLOW_UNENCRYPTED;
145577217Sphk				break;
145677217Sphk			case IEEE80211_WEP_MIXED:
145777217Sphk				config->an_authtype |=
145877217Sphk				    AN_AUTHTYPE_PRIVACY_IN_USE |
145977217Sphk				    AN_AUTHTYPE_ALLOW_UNENCRYPTED;
146077217Sphk				break;
146177217Sphk			default:
146277217Sphk				error = EINVAL;
146377217Sphk				break;
146477217Sphk			}
146577217Sphk			break;
146677217Sphk		case IEEE80211_IOC_WEPKEY:
146777217Sphk			if (ireq->i_val < 0 || ireq->i_val > 7 ||
146877217Sphk			    ireq->i_len > 13) {
146977217Sphk				error = EINVAL;
147077217Sphk				break;
147177217Sphk			}
147277217Sphk			error = copyin(ireq->i_data, tmpstr, 13);
147378639Sbrooks			if (error != 0)
147477217Sphk				break;
147577217Sphk			bzero(&areq, sizeof(struct an_ltv_key));
147677217Sphk			areq.an_len = sizeof(struct an_ltv_key);
147777217Sphk			key->mac[0] = 1;	/* The others are 0. */
147877217Sphk			key->kindex = ireq->i_val % 4;
147978639Sbrooks			if (ireq->i_val < 4)
148077217Sphk				areq.an_type = AN_RID_WEP_TEMP;
148177217Sphk			else
148277217Sphk				areq.an_type = AN_RID_WEP_PERM;
148377217Sphk			key->klen = ireq->i_len;
148477217Sphk			bcopy(tmpstr, key->key, key->klen);
148577217Sphk			break;
148677217Sphk		case IEEE80211_IOC_WEPTXKEY:
148778639Sbrooks			if (ireq->i_val < 0 || ireq->i_val > 3) {
148877217Sphk				error = EINVAL;
148977217Sphk				break;
149077217Sphk			}
149177217Sphk			bzero(&areq, sizeof(struct an_ltv_key));
149277217Sphk			areq.an_len = sizeof(struct an_ltv_key);
149377217Sphk			areq.an_type = AN_RID_WEP_PERM;
149477217Sphk			key->kindex = 0xffff;
149577217Sphk			key->mac[0] = ireq->i_val;
149677217Sphk			break;
149777217Sphk		case IEEE80211_IOC_AUTHMODE:
149877217Sphk			switch (ireq->i_val) {
149977217Sphk			case IEEE80211_AUTH_NONE:
150077217Sphk				config->an_authtype = AN_AUTHTYPE_NONE |
150177217Sphk				    (config->an_authtype & ~AN_AUTHTYPE_MASK);
150277217Sphk				break;
150377217Sphk			case IEEE80211_AUTH_OPEN:
150477217Sphk				config->an_authtype = AN_AUTHTYPE_OPEN |
150577217Sphk				    (config->an_authtype & ~AN_AUTHTYPE_MASK);
150677217Sphk				break;
150777217Sphk			case IEEE80211_AUTH_SHARED:
150877217Sphk				config->an_authtype = AN_AUTHTYPE_SHAREDKEY |
150977217Sphk				    (config->an_authtype & ~AN_AUTHTYPE_MASK);
151077217Sphk				break;
151177217Sphk			default:
151277217Sphk				error = EINVAL;
151377217Sphk			}
151477217Sphk			break;
151577217Sphk		case IEEE80211_IOC_STATIONNAME:
151678639Sbrooks			if (ireq->i_len > 16) {
151777217Sphk				error = EINVAL;
151877217Sphk				break;
151977217Sphk			}
152077217Sphk			bzero(config->an_nodename, 16);
152177217Sphk			error = copyin(ireq->i_data,
152277217Sphk			    config->an_nodename, ireq->i_len);
152377217Sphk			break;
152477217Sphk		case IEEE80211_IOC_CHANNEL:
152577217Sphk			/*
152677217Sphk			 * The actual range is 1-14, but if you set it
152777217Sphk			 * to 0 you get the default so we let that work
152877217Sphk			 * too.
152977217Sphk			 */
153077217Sphk			if (ireq->i_val < 0 || ireq->i_val >14) {
153177217Sphk				error = EINVAL;
153277217Sphk				break;
153377217Sphk			}
153477217Sphk			config->an_ds_channel = ireq->i_val;
153577217Sphk			break;
153677217Sphk		case IEEE80211_IOC_POWERSAVE:
153777217Sphk			switch (ireq->i_val) {
153877217Sphk			case IEEE80211_POWERSAVE_OFF:
153977217Sphk				config->an_psave_mode = AN_PSAVE_NONE;
154077217Sphk				break;
154177217Sphk			case IEEE80211_POWERSAVE_CAM:
154277217Sphk				config->an_psave_mode = AN_PSAVE_CAM;
154377217Sphk				break;
154477217Sphk			case IEEE80211_POWERSAVE_PSP:
154577217Sphk				config->an_psave_mode = AN_PSAVE_PSP;
154677217Sphk				break;
154777217Sphk			case IEEE80211_POWERSAVE_PSP_CAM:
154877217Sphk				config->an_psave_mode = AN_PSAVE_PSP_CAM;
154977217Sphk				break;
155077217Sphk			default:
155177217Sphk				error = EINVAL;
155277217Sphk				break;
155377217Sphk			}
155477217Sphk			break;
155577217Sphk		case IEEE80211_IOC_POWERSAVESLEEP:
155677217Sphk			config->an_listen_interval = ireq->i_val;
155777217Sphk			break;
155877217Sphk		}
155977217Sphk
156077217Sphk		if (!error)
156177217Sphk			an_setdef(sc, &areq);
156277217Sphk		break;
156355992Swpaul	default:
156455992Swpaul		error = EINVAL;
156555992Swpaul		break;
156655992Swpaul	}
156761816Srobertoout:
156867094Swpaul	AN_UNLOCK(sc);
156955992Swpaul
157078639Sbrooks	return(error != 0);
157155992Swpaul}
157255992Swpaul
157355992Swpaulstatic int an_init_tx_ring(sc)
157455992Swpaul	struct an_softc		*sc;
157555992Swpaul{
157655992Swpaul	int			i;
157755992Swpaul	int			id;
157855992Swpaul
157955992Swpaul	if (sc->an_gone)
158055992Swpaul		return (0);
158155992Swpaul
158255992Swpaul	for (i = 0; i < AN_TX_RING_CNT; i++) {
158355992Swpaul		if (an_alloc_nicmem(sc, 1518 +
158455992Swpaul		    0x44, &id))
158555992Swpaul			return(ENOMEM);
158655992Swpaul		sc->an_rdata.an_tx_fids[i] = id;
158755992Swpaul		sc->an_rdata.an_tx_ring[i] = 0;
158855992Swpaul	}
158955992Swpaul
159055992Swpaul	sc->an_rdata.an_tx_prod = 0;
159155992Swpaul	sc->an_rdata.an_tx_cons = 0;
159255992Swpaul
159355992Swpaul	return(0);
159455992Swpaul}
159555992Swpaul
159655992Swpaulstatic void an_init(xsc)
159755992Swpaul	void			*xsc;
159855992Swpaul{
159955992Swpaul	struct an_softc		*sc = xsc;
160055992Swpaul	struct ifnet		*ifp = &sc->arpcom.ac_if;
160155992Swpaul
160267094Swpaul	AN_LOCK(sc);
160367094Swpaul
160467094Swpaul	if (sc->an_gone) {
160567094Swpaul		AN_UNLOCK(sc);
160655992Swpaul		return;
160767094Swpaul	}
160855992Swpaul
160955992Swpaul	if (ifp->if_flags & IFF_RUNNING)
161055992Swpaul		an_stop(sc);
161155992Swpaul
161255992Swpaul	sc->an_associated = 0;
161355992Swpaul
161455992Swpaul	/* Allocate the TX buffers */
161555992Swpaul	if (an_init_tx_ring(sc)) {
161655992Swpaul		an_reset(sc);
161755992Swpaul		if (an_init_tx_ring(sc)) {
161855992Swpaul			printf("an%d: tx buffer allocation "
161955992Swpaul			    "failed\n", sc->an_unit);
162067094Swpaul			AN_UNLOCK(sc);
162155992Swpaul			return;
162255992Swpaul		}
162355992Swpaul	}
162455992Swpaul
162555992Swpaul	/* Set our MAC address. */
162655992Swpaul	bcopy((char *)&sc->arpcom.ac_enaddr,
162755992Swpaul	    (char *)&sc->an_config.an_macaddr, ETHER_ADDR_LEN);
162855992Swpaul
162955992Swpaul	if (ifp->if_flags & IFF_BROADCAST)
163055992Swpaul		sc->an_config.an_rxmode = AN_RXMODE_BC_ADDR;
163155992Swpaul	else
163255992Swpaul		sc->an_config.an_rxmode = AN_RXMODE_ADDR;
163355992Swpaul
163455992Swpaul	if (ifp->if_flags & IFF_MULTICAST)
163555992Swpaul		sc->an_config.an_rxmode = AN_RXMODE_BC_MC_ADDR;
163655992Swpaul
163755992Swpaul	/* Initialize promisc mode. */
163868692Swpaul	/* Kills card DJA can't TX packet in sniff mode
163968692Swpaul 	if (ifp->if_flags & IFF_PROMISC)
164055992Swpaul		sc->an_config.an_rxmode |= AN_RXMODE_LAN_MONITOR_CURBSS;
164168692Swpaul	*/
164255992Swpaul
164355992Swpaul	sc->an_rxmode = sc->an_config.an_rxmode;
164455992Swpaul
164555992Swpaul	/* Set the ssid list */
164655992Swpaul	sc->an_ssidlist.an_type = AN_RID_SSIDLIST;
164755992Swpaul	sc->an_ssidlist.an_len = sizeof(struct an_ltv_ssidlist);
164855992Swpaul	if (an_write_record(sc, (struct an_ltv_gen *)&sc->an_ssidlist)) {
164955992Swpaul		printf("an%d: failed to set ssid list\n", sc->an_unit);
165067094Swpaul		AN_UNLOCK(sc);
165155992Swpaul		return;
165255992Swpaul	}
165355992Swpaul
165455992Swpaul	/* Set the AP list */
165555992Swpaul	sc->an_aplist.an_type = AN_RID_APLIST;
165655992Swpaul	sc->an_aplist.an_len = sizeof(struct an_ltv_aplist);
165755992Swpaul	if (an_write_record(sc, (struct an_ltv_gen *)&sc->an_aplist)) {
165855992Swpaul		printf("an%d: failed to set AP list\n", sc->an_unit);
165967094Swpaul		AN_UNLOCK(sc);
166055992Swpaul		return;
166155992Swpaul	}
166255992Swpaul
166355992Swpaul	/* Set the configuration in the NIC */
166455992Swpaul	sc->an_config.an_len = sizeof(struct an_ltv_genconfig);
166555992Swpaul	sc->an_config.an_type = AN_RID_GENCONFIG;
166655992Swpaul	if (an_write_record(sc, (struct an_ltv_gen *)&sc->an_config)) {
166755992Swpaul		printf("an%d: failed to set configuration\n", sc->an_unit);
166867094Swpaul		AN_UNLOCK(sc);
166955992Swpaul		return;
167055992Swpaul	}
167155992Swpaul
167255992Swpaul	/* Enable the MAC */
167355992Swpaul	if (an_cmd(sc, AN_CMD_ENABLE, 0)) {
167455992Swpaul		printf("an%d: failed to enable MAC\n", sc->an_unit);
167567094Swpaul		AN_UNLOCK(sc);
167655992Swpaul		return;
167755992Swpaul	}
167855992Swpaul
167974698Sarchie	if (ifp->if_flags & IFF_PROMISC)
168074698Sarchie		an_cmd(sc, AN_CMD_SET_MODE, 0xffff);
168174698Sarchie
168255992Swpaul	/* enable interrupts */
168355992Swpaul	CSR_WRITE_2(sc, AN_INT_EN, AN_INTRS);
168455992Swpaul
168555992Swpaul	ifp->if_flags |= IFF_RUNNING;
168655992Swpaul	ifp->if_flags &= ~IFF_OACTIVE;
168755992Swpaul
168855992Swpaul	sc->an_stat_ch = timeout(an_stats_update, sc, hz);
168967094Swpaul	AN_UNLOCK(sc);
169055992Swpaul
169155992Swpaul	return;
169255992Swpaul}
169355992Swpaul
169455992Swpaulstatic void an_start(ifp)
169555992Swpaul	struct ifnet		*ifp;
169655992Swpaul{
169755992Swpaul	struct an_softc		*sc;
169855992Swpaul	struct mbuf		*m0 = NULL;
169955992Swpaul	struct an_txframe_802_3	tx_frame_802_3;
170055992Swpaul	struct ether_header	*eh;
170155992Swpaul	int			id;
170255992Swpaul	int			idx;
170355992Swpaul	unsigned char           txcontrol;
170455992Swpaul
170555992Swpaul	sc = ifp->if_softc;
170655992Swpaul
170755992Swpaul	if (sc->an_gone)
170855992Swpaul		return;
170955992Swpaul
171055992Swpaul	if (ifp->if_flags & IFF_OACTIVE)
171155992Swpaul		return;
171255992Swpaul
171355992Swpaul	if (!sc->an_associated)
171455992Swpaul		return;
171555992Swpaul
171655992Swpaul	idx = sc->an_rdata.an_tx_prod;
171755992Swpaul	bzero((char *)&tx_frame_802_3, sizeof(tx_frame_802_3));
171855992Swpaul
171955992Swpaul	while(sc->an_rdata.an_tx_ring[idx] == 0) {
172055992Swpaul		IF_DEQUEUE(&ifp->if_snd, m0);
172155992Swpaul		if (m0 == NULL)
172255992Swpaul			break;
172355992Swpaul
172455992Swpaul		id = sc->an_rdata.an_tx_fids[idx];
172555992Swpaul		eh = mtod(m0, struct ether_header *);
172655992Swpaul
172755992Swpaul		bcopy((char *)&eh->ether_dhost,
172855992Swpaul		    (char *)&tx_frame_802_3.an_tx_dst_addr, ETHER_ADDR_LEN);
172955992Swpaul		bcopy((char *)&eh->ether_shost,
173055992Swpaul		    (char *)&tx_frame_802_3.an_tx_src_addr, ETHER_ADDR_LEN);
173155992Swpaul
173255992Swpaul		tx_frame_802_3.an_tx_802_3_payload_len =
173355992Swpaul		  m0->m_pkthdr.len - 12;  /* minus src/dest mac & type */
173455992Swpaul
173555992Swpaul                m_copydata(m0, sizeof(struct ether_header) - 2 ,
173655992Swpaul                    tx_frame_802_3.an_tx_802_3_payload_len,
173755992Swpaul                    (caddr_t)&sc->an_txbuf);
173855992Swpaul
173978639Sbrooks		txcontrol = AN_TXCTL_8023;
174055992Swpaul		/* write the txcontrol only */
174155992Swpaul		an_write_data(sc, id, 0x08, (caddr_t)&txcontrol,
174255992Swpaul			      sizeof(txcontrol));
174355992Swpaul
174455992Swpaul		/* 802_3 header */
174555992Swpaul		an_write_data(sc, id, 0x34, (caddr_t)&tx_frame_802_3,
174655992Swpaul			      sizeof(struct an_txframe_802_3));
174755992Swpaul
174855992Swpaul		/* in mbuf header type is just before payload */
174955992Swpaul		an_write_data(sc, id, 0x44, (caddr_t)&sc->an_txbuf,
175055992Swpaul			    tx_frame_802_3.an_tx_802_3_payload_len);
175155992Swpaul
175255992Swpaul		/*
175355992Swpaul		 * If there's a BPF listner, bounce a copy of
175455992Swpaul		 * this frame to him.
175555992Swpaul		 */
175655992Swpaul		if (ifp->if_bpf)
175755992Swpaul			bpf_mtap(ifp, m0);
175855992Swpaul
175955992Swpaul		m_freem(m0);
176055992Swpaul		m0 = NULL;
176155992Swpaul
176255992Swpaul		sc->an_rdata.an_tx_ring[idx] = id;
176355992Swpaul		if (an_cmd(sc, AN_CMD_TX, id))
176455992Swpaul			printf("an%d: xmit failed\n", sc->an_unit);
176555992Swpaul
176655992Swpaul		AN_INC(idx, AN_TX_RING_CNT);
176755992Swpaul	}
176855992Swpaul
176955992Swpaul	if (m0 != NULL)
177055992Swpaul		ifp->if_flags |= IFF_OACTIVE;
177155992Swpaul
177255992Swpaul	sc->an_rdata.an_tx_prod = idx;
177355992Swpaul
177455992Swpaul	/*
177555992Swpaul	 * Set a timeout in case the chip goes out to lunch.
177655992Swpaul	 */
177755992Swpaul	ifp->if_timer = 5;
177855992Swpaul
177955992Swpaul	return;
178055992Swpaul}
178155992Swpaul
178255992Swpaulvoid an_stop(sc)
178355992Swpaul	struct an_softc		*sc;
178455992Swpaul{
178555992Swpaul	struct ifnet		*ifp;
178655992Swpaul	int			i;
178755992Swpaul
178867094Swpaul	AN_LOCK(sc);
178967094Swpaul
179067094Swpaul	if (sc->an_gone) {
179167094Swpaul		AN_UNLOCK(sc);
179255992Swpaul		return;
179367094Swpaul	}
179455992Swpaul
179555992Swpaul	ifp = &sc->arpcom.ac_if;
179655992Swpaul
179755992Swpaul	an_cmd(sc, AN_CMD_FORCE_SYNCLOSS, 0);
179855992Swpaul	CSR_WRITE_2(sc, AN_INT_EN, 0);
179955992Swpaul	an_cmd(sc, AN_CMD_DISABLE, 0);
180055992Swpaul
180155992Swpaul	for (i = 0; i < AN_TX_RING_CNT; i++)
180255992Swpaul		an_cmd(sc, AN_CMD_DEALLOC_MEM, sc->an_rdata.an_tx_fids[i]);
180355992Swpaul
180455992Swpaul	untimeout(an_stats_update, sc, sc->an_stat_ch);
180555992Swpaul
180655992Swpaul	ifp->if_flags &= ~(IFF_RUNNING|IFF_OACTIVE);
180755992Swpaul
180867094Swpaul	AN_UNLOCK(sc);
180967094Swpaul
181055992Swpaul	return;
181155992Swpaul}
181255992Swpaul
181355992Swpaulstatic void an_watchdog(ifp)
181455992Swpaul	struct ifnet		*ifp;
181555992Swpaul{
181655992Swpaul	struct an_softc		*sc;
181755992Swpaul
181855992Swpaul	sc = ifp->if_softc;
181967094Swpaul	AN_LOCK(sc);
182055992Swpaul
182167094Swpaul	if (sc->an_gone) {
182267094Swpaul		AN_UNLOCK(sc);
182355992Swpaul		return;
182467094Swpaul	}
182555992Swpaul
182655992Swpaul	printf("an%d: device timeout\n", sc->an_unit);
182755992Swpaul
182855992Swpaul	an_reset(sc);
182955992Swpaul	an_init(sc);
183055992Swpaul
183155992Swpaul	ifp->if_oerrors++;
183267094Swpaul	AN_UNLOCK(sc);
183355992Swpaul
183455992Swpaul	return;
183555992Swpaul}
183655992Swpaul
183755992Swpaulvoid an_shutdown(dev)
183855992Swpaul	device_t		dev;
183955992Swpaul{
184055992Swpaul	struct an_softc		*sc;
184155992Swpaul
184255992Swpaul	sc = device_get_softc(dev);
184355992Swpaul	an_stop(sc);
184455992Swpaul
184555992Swpaul	return;
184655992Swpaul}
184755992Swpaul
184855992Swpaul#ifdef ANCACHE
184955992Swpaul/* Aironet signal strength cache code.
185055992Swpaul * store signal/noise/quality on per MAC src basis in
185155992Swpaul * a small fixed cache.  The cache wraps if > MAX slots
185255992Swpaul * used.  The cache may be zeroed out to start over.
185355992Swpaul * Two simple filters exist to reduce computation:
185455992Swpaul * 1. ip only (literally 0x800) which may be used
185555992Swpaul * to ignore some packets.  It defaults to ip only.
185655992Swpaul * it could be used to focus on broadcast, non-IP 802.11 beacons.
185755992Swpaul * 2. multicast/broadcast only.  This may be used to
185855992Swpaul * ignore unicast packets and only cache signal strength
185955992Swpaul * for multicast/broadcast packets (beacons); e.g., Mobile-IP
186055992Swpaul * beacons and not unicast traffic.
186155992Swpaul *
186255992Swpaul * The cache stores (MAC src(index), IP src (major clue), signal,
186355992Swpaul *	quality, noise)
186455992Swpaul *
186555992Swpaul * No apologies for storing IP src here.  It's easy and saves much
186655992Swpaul * trouble elsewhere.  The cache is assumed to be INET dependent,
186755992Swpaul * although it need not be.
186855992Swpaul *
186955992Swpaul * Note: the Aironet only has a single byte of signal strength value
187055992Swpaul * in the rx frame header, and it's not scaled to anything sensible.
187155992Swpaul * This is kind of lame, but it's all we've got.
187255992Swpaul */
187355992Swpaul
187455992Swpaul#ifdef documentation
187555992Swpaul
187655992Swpaulint an_sigitems;                                /* number of cached entries */
187755992Swpaulstruct an_sigcache an_sigcache[MAXANCACHE];  /*  array of cache entries */
187855992Swpaulint an_nextitem;                                /*  index/# of entries */
187955992Swpaul
188055992Swpaul
188155992Swpaul#endif
188255992Swpaul
188355992Swpaul/* control variables for cache filtering.  Basic idea is
188455992Swpaul * to reduce cost (e.g., to only Mobile-IP agent beacons
188555992Swpaul * which are broadcast or multicast).  Still you might
188655992Swpaul * want to measure signal strength anth unicast ping packets
188755992Swpaul * on a pt. to pt. ant. setup.
188855992Swpaul */
188955992Swpaul/* set true if you want to limit cache items to broadcast/mcast
189055992Swpaul * only packets (not unicast).  Useful for mobile-ip beacons which
189155992Swpaul * are broadcast/multicast at network layer.  Default is all packets
189255992Swpaul * so ping/unicast anll work say anth pt. to pt. antennae setup.
189355992Swpaul */
189455992Swpaulstatic int an_cache_mcastonly = 0;
189555992SwpaulSYSCTL_INT(_machdep, OID_AUTO, an_cache_mcastonly, CTLFLAG_RW,
189655992Swpaul	&an_cache_mcastonly, 0, "");
189755992Swpaul
189855992Swpaul/* set true if you want to limit cache items to IP packets only
189955992Swpaul*/
190055992Swpaulstatic int an_cache_iponly = 1;
190155992SwpaulSYSCTL_INT(_machdep, OID_AUTO, an_cache_iponly, CTLFLAG_RW,
190255992Swpaul	&an_cache_iponly, 0, "");
190355992Swpaul
190455992Swpaul/*
190555992Swpaul * an_cache_store, per rx packet store signal
190655992Swpaul * strength in MAC (src) indexed cache.
190755992Swpaul */
190855992Swpaulstatic
190955992Swpaulvoid an_cache_store (sc, eh, m, rx_quality)
191055992Swpaul	struct an_softc *sc;
191155992Swpaul	struct ether_header *eh;
191255992Swpaul	struct mbuf *m;
191355992Swpaul	unsigned short rx_quality;
191455992Swpaul{
191555992Swpaul	struct ip *ip = 0;
191655992Swpaul	int i;
191755992Swpaul	static int cache_slot = 0; 	/* use this cache entry */
191855992Swpaul	static int wrapindex = 0;       /* next "free" cache entry */
191978639Sbrooks	int saanp = 0;
192055992Swpaul
192155992Swpaul	/* filters:
192255992Swpaul	 * 1. ip only
192355992Swpaul	 * 2. configurable filter to throw out unicast packets,
192455992Swpaul	 * keep multicast only.
192555992Swpaul	 */
192655992Swpaul
192755992Swpaul	if ((ntohs(eh->ether_type) == 0x800)) {
192855992Swpaul		saanp = 1;
192955992Swpaul	}
193055992Swpaul
193155992Swpaul	/* filter for ip packets only
193255992Swpaul	*/
193355992Swpaul	if ( an_cache_iponly && !saanp) {
193455992Swpaul		return;
193555992Swpaul	}
193655992Swpaul
193755992Swpaul	/* filter for broadcast/multicast only
193855992Swpaul	 */
193955992Swpaul	if (an_cache_mcastonly && ((eh->ether_dhost[0] & 1) == 0)) {
194055992Swpaul		return;
194155992Swpaul	}
194255992Swpaul
194355992Swpaul#ifdef SIGDEBUG
194455992Swpaul	printf("an: q value %x (MSB=0x%x, LSB=0x%x) \n",
194555992Swpaul	    rx_quality & 0xffff, rx_quality >> 8, rx_quality & 0xff);
194655992Swpaul#endif
194755992Swpaul
194855992Swpaul	/* find the ip header.  we want to store the ip_src
194955992Swpaul	 * address.
195055992Swpaul	 */
195155992Swpaul	if (saanp) {
195255992Swpaul		ip = mtod(m, struct ip *);
195355992Swpaul	}
195455992Swpaul
195555992Swpaul	/* do a linear search for a matching MAC address
195655992Swpaul	 * in the cache table
195755992Swpaul	 * . MAC address is 6 bytes,
195855992Swpaul	 * . var w_nextitem holds total number of entries already cached
195955992Swpaul	 */
196078639Sbrooks	for (i = 0; i < sc->an_nextitem; i++) {
196155992Swpaul		if (! bcmp(eh->ether_shost , sc->an_sigcache[i].macsrc,  6 )) {
196255992Swpaul			/* Match!,
196355992Swpaul			 * so we already have this entry,
196455992Swpaul			 * update the data
196555992Swpaul			 */
196655992Swpaul			break;
196755992Swpaul		}
196855992Swpaul	}
196955992Swpaul
197055992Swpaul	/* did we find a matching mac address?
197155992Swpaul	 * if yes, then overwrite a previously existing cache entry
197255992Swpaul	 */
197355992Swpaul	if (i < sc->an_nextitem )   {
197455992Swpaul		cache_slot = i;
197555992Swpaul	}
197655992Swpaul	/* else, have a new address entry,so
197755992Swpaul	 * add this new entry,
197855992Swpaul	 * if table full, then we need to replace LRU entry
197955992Swpaul	 */
198055992Swpaul	else    {
198155992Swpaul
198255992Swpaul		/* check for space in cache table
198355992Swpaul		 * note: an_nextitem also holds number of entries
198455992Swpaul		 * added in the cache table
198555992Swpaul		 */
198655992Swpaul		if ( sc->an_nextitem < MAXANCACHE ) {
198755992Swpaul			cache_slot = sc->an_nextitem;
198855992Swpaul			sc->an_nextitem++;
198955992Swpaul			sc->an_sigitems = sc->an_nextitem;
199055992Swpaul		}
199155992Swpaul        	/* no space found, so simply wrap anth wrap index
199255992Swpaul		 * and "zap" the next entry
199355992Swpaul		 */
199455992Swpaul		else {
199555992Swpaul			if (wrapindex == MAXANCACHE) {
199655992Swpaul				wrapindex = 0;
199755992Swpaul			}
199855992Swpaul			cache_slot = wrapindex++;
199955992Swpaul		}
200055992Swpaul	}
200155992Swpaul
200255992Swpaul	/* invariant: cache_slot now points at some slot
200355992Swpaul	 * in cache.
200455992Swpaul	 */
200555992Swpaul	if (cache_slot < 0 || cache_slot >= MAXANCACHE) {
200655992Swpaul		log(LOG_ERR, "an_cache_store, bad index: %d of "
200755992Swpaul		    "[0..%d], gross cache error\n",
200855992Swpaul		    cache_slot, MAXANCACHE);
200955992Swpaul		return;
201055992Swpaul	}
201155992Swpaul
201255992Swpaul	/*  store items in cache
201355992Swpaul	 *  .ip source address
201455992Swpaul	 *  .mac src
201555992Swpaul	 *  .signal, etc.
201655992Swpaul	 */
201755992Swpaul	if (saanp) {
201855992Swpaul		sc->an_sigcache[cache_slot].ipsrc = ip->ip_src.s_addr;
201955992Swpaul	}
202055992Swpaul	bcopy( eh->ether_shost, sc->an_sigcache[cache_slot].macsrc,  6);
202155992Swpaul
202255992Swpaul	sc->an_sigcache[cache_slot].signal = rx_quality;
202355992Swpaul
202455992Swpaul	return;
202555992Swpaul}
202655992Swpaul#endif
202777217Sphk
202877217Sphkstatic int an_media_change(ifp)
202977217Sphk	struct ifnet		*ifp;
203077217Sphk{
203177217Sphk	struct an_softc *sc = ifp->if_softc;
203277217Sphk	int otype = sc->an_config.an_opmode;
203377217Sphk	int orate = sc->an_tx_rate;
203477217Sphk
203577217Sphk	if ((sc->an_ifmedia.ifm_cur->ifm_media & IFM_IEEE80211_ADHOC) != 0)
203677217Sphk		sc->an_config.an_opmode = AN_OPMODE_IBSS_ADHOC;
203777217Sphk	else
203877217Sphk		sc->an_config.an_opmode = AN_OPMODE_INFRASTRUCTURE_STATION;
203977217Sphk
204077217Sphk	switch (IFM_SUBTYPE(sc->an_ifmedia.ifm_cur->ifm_media)) {
204177217Sphk	case IFM_IEEE80211_DS1:
204277217Sphk		sc->an_tx_rate = AN_RATE_1MBPS;
204377217Sphk		break;
204477217Sphk	case IFM_IEEE80211_DS2:
204577217Sphk		sc->an_tx_rate = AN_RATE_2MBPS;
204677217Sphk		break;
204777217Sphk	case IFM_IEEE80211_DS5:
204877217Sphk		sc->an_tx_rate = AN_RATE_5_5MBPS;
204977217Sphk		break;
205077217Sphk	case IFM_IEEE80211_DS11:
205177217Sphk		sc->an_tx_rate = AN_RATE_11MBPS;
205277217Sphk		break;
205377217Sphk	case IFM_AUTO:
205477217Sphk		sc->an_tx_rate = 0;
205577217Sphk		break;
205677217Sphk	}
205777217Sphk
205877217Sphk	if (otype != sc->an_config.an_opmode ||
205977217Sphk	    orate != sc->an_tx_rate)
206077217Sphk		an_init(sc);
206177217Sphk
206277217Sphk	return(0);
206377217Sphk}
206477217Sphk
206577217Sphkstatic void an_media_status(ifp, imr)
206677217Sphk	struct ifnet		*ifp;
206777217Sphk	struct ifmediareq	*imr;
206877217Sphk{
206977217Sphk	struct an_ltv_status	status;
207077217Sphk	struct an_softc		*sc = ifp->if_softc;
207177217Sphk
207277217Sphk	status.an_len = sizeof(status);
207377217Sphk	status.an_type = AN_RID_STATUS;
207477217Sphk	if (an_read_record(sc, (struct an_ltv_gen *)&status)) {
207577217Sphk		/* If the status read fails, just lie. */
207677217Sphk		imr->ifm_active = sc->an_ifmedia.ifm_cur->ifm_media;
207777217Sphk		imr->ifm_status = IFM_AVALID|IFM_ACTIVE;
207877217Sphk	}
207977217Sphk
208078639Sbrooks	if (sc->an_tx_rate == 0) {
208177217Sphk		imr->ifm_active = IFM_IEEE80211|IFM_AUTO;
208277217Sphk		if (sc->an_config.an_opmode == AN_OPMODE_IBSS_ADHOC)
208377217Sphk			imr->ifm_active |= IFM_IEEE80211_ADHOC;
208477217Sphk		switch(status.an_current_tx_rate) {
208577217Sphk		case AN_RATE_1MBPS:
208677217Sphk			imr->ifm_active |= IFM_IEEE80211_DS1;
208777217Sphk			break;
208877217Sphk		case AN_RATE_2MBPS:
208977217Sphk			imr->ifm_active |= IFM_IEEE80211_DS2;
209077217Sphk			break;
209177217Sphk		case AN_RATE_5_5MBPS:
209277217Sphk			imr->ifm_active |= IFM_IEEE80211_DS5;
209377217Sphk			break;
209477217Sphk		case AN_RATE_11MBPS:
209577217Sphk			imr->ifm_active |= IFM_IEEE80211_DS11;
209677217Sphk			break;
209777217Sphk		}
209877217Sphk	} else {
209977217Sphk		imr->ifm_active = sc->an_ifmedia.ifm_cur->ifm_media;
210077217Sphk	}
210177217Sphk
210277217Sphk	imr->ifm_status = IFM_AVALID;
210377217Sphk	if (sc->an_config.an_opmode == AN_OPMODE_IBSS_ADHOC)
210477217Sphk		imr->ifm_status |= IFM_ACTIVE;
210577217Sphk	else if (status.an_opmode & AN_STATUS_OPMODE_ASSOCIATED)
210677217Sphk			imr->ifm_status |= IFM_ACTIVE;
210777217Sphk}
2108