📄 if_an.c
字号:
/*
* Copyright (c) 1997, 1998, 1999
* Bill Paul <wpaul@ctr.columbia.edu>. 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. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Bill Paul.
* 4. 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 Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
* 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.
*
* $FreeBSD: src/sys/dev/an/if_an.c,v 1.2.2.8 2001/09/26 01:02:01 brooks Exp $
*/
/*
* Aironet 4500/4800 802.11 PCMCIA/ISA/PCI driver for FreeBSD.
*
* Written by Bill Paul <wpaul@ctr.columbia.edu>
* Electrical Engineering Department
* Columbia University, New York City
*/
/*
* The Aironet 4500/4800 series cards some 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 doesn't currently offer a PCI card, however Aironet does
* - 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. Consequently, /sys/pccard/pccard.c
* has to be patched slightly in order to enable the Vpp voltages in
* order to make the Aironet PCMCIA cards work.
*
* 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.
*/
#include "opt_inet.h"
#ifdef INET
#define ANCACHE /* enable signal strength cache */
#endif
#include <sys/param.h>
#include <sys/systm.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>
#ifdef ANCACHE
#include <sys/syslog.h>
#include <sys/sysctl.h>
#endif
#include <sys/module.h>
#include <sys/sysctl.h>
#include <sys/bus.h>
#include <machine/bus.h>
#include <sys/rman.h>
#include <machine/resource.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <net/ethernet.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <net/if_ieee80211.h>
#include <net/if_media.h>
#ifdef INET
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/in_var.h>
#include <netinet/ip.h>
#endif
#include <net/bpf.h>
#include <machine/clock.h>
#include <machine/md_var.h>
#include <dev/an/if_aironet_ieee.h>
#include <dev/an/if_anreg.h>
#if !defined(lint)
static const char rcsid[] =
"$FreeBSD: src/sys/dev/an/if_an.c,v 1.2.2.8 2001/09/26 01:02:01 brooks Exp $";
#endif
/* These are global because we need them in sys/pci/if_an_p.c. */
static void an_reset __P((struct an_softc *));
static int an_ioctl __P((struct ifnet *, u_long, caddr_t));
static void an_init __P((void *));
static int an_init_tx_ring __P((struct an_softc *));
static void an_start __P((struct ifnet *));
static void an_watchdog __P((struct ifnet *));
static void an_rxeof __P((struct an_softc *));
static void an_txeof __P((struct an_softc *, int));
static void an_promisc __P((struct an_softc *, int));
static int an_cmd __P((struct an_softc *, int, int));
static int an_read_record __P((struct an_softc *, struct an_ltv_gen *));
static int an_write_record __P((struct an_softc *, struct an_ltv_gen *));
static int an_read_data __P((struct an_softc *, int,
int, caddr_t, int));
static int an_write_data __P((struct an_softc *, int,
int, caddr_t, int));
static int an_seek __P((struct an_softc *, int, int, int));
static int an_alloc_nicmem __P((struct an_softc *, int, int *));
static void an_stats_update __P((void *));
static void an_setdef __P((struct an_softc *, struct an_req *));
#ifdef ANCACHE
static void an_cache_store __P((struct an_softc *, struct ether_header *,
struct mbuf *, unsigned short));
#endif
static void an_dump_record __P((struct an_softc *,struct an_ltv_gen *,
char *));
static int an_media_change __P((struct ifnet *));
static void an_media_status __P((struct ifnet *, struct ifmediareq *));
static int an_dump = 0;
static char an_conf[256];
/* sysctl vars */
SYSCTL_NODE(_machdep, OID_AUTO, an, CTLFLAG_RD, 0, "dump RID");
static int
sysctl_an_dump(SYSCTL_HANDLER_ARGS)
{
int error, r, last;
char *s = an_conf;
last = an_dump;
bzero(an_conf, sizeof(an_conf));
switch (an_dump) {
case 0:
strcat(an_conf, "off");
break;
case 1:
strcat(an_conf, "type");
break;
case 2:
strcat(an_conf, "dump");
break;
default:
snprintf(an_conf, 5, "%x", an_dump);
break;
}
error = sysctl_handle_string(oidp, an_conf, sizeof(an_conf), req);
if (strncmp(an_conf,"off", 4) == 0) {
an_dump = 0;
}
if (strncmp(an_conf,"dump", 4) == 0) {
an_dump = 1;
}
if (strncmp(an_conf,"type", 4) == 0) {
an_dump = 2;
}
if (*s == 'f') {
r = 0;
for (;;s++) {
if ((*s >= '0') && (*s <= '9')) {
r = r * 16 + (*s - '0');
} else if ((*s >= 'a') && (*s <= 'f')) {
r = r * 16 + (*s - 'a' + 10);
} else {
break;
}
}
an_dump = r;
}
if (an_dump != last)
printf("Sysctl changed for Aironet driver\n");
return error;
}
SYSCTL_PROC(_machdep, OID_AUTO, an_dump, CTLTYPE_STRING | CTLFLAG_RW,
0, sizeof(an_conf), sysctl_an_dump, "A", "");
/*
* We probe for an Aironet 4500/4800 card by attempting to
* read the default SSID list. On reset, the first entry in
* the SSID list will contain the name "tsunami." If we don't
* find this, then there's no card present.
*/
int
an_probe(dev)
device_t dev;
{
struct an_softc *sc = device_get_softc(dev);
struct an_ltv_ssidlist ssid;
int error;
bzero((char *)&ssid, sizeof(ssid));
error = an_alloc_port(dev, 0, AN_IOSIZ);
if (error != 0)
return (0);
/* can't do autoprobing */
if (rman_get_start(sc->port_res) == -1)
return(0);
/*
* We need to fake up a softc structure long enough
* to be able to issue commands and call some of the
* other routines.
*/
sc->an_bhandle = rman_get_bushandle(sc->port_res);
sc->an_btag = rman_get_bustag(sc->port_res);
sc->an_unit = device_get_unit(dev);
ssid.an_len = sizeof(ssid);
ssid.an_type = AN_RID_SSIDLIST;
/* Make sure interrupts are disabled. */
CSR_WRITE_2(sc, AN_INT_EN, 0);
CSR_WRITE_2(sc, AN_EVENT_ACK, 0xFFFF);
an_reset(sc);
if (an_cmd(sc, AN_CMD_READCFG, 0))
return(0);
if (an_read_record(sc, (struct an_ltv_gen *)&ssid))
return(0);
/* See if the ssid matches what we expect ... but doesn't have to */
if (strcmp(ssid.an_ssid1, AN_DEF_SSID))
return(0);
return(AN_IOSIZ);
}
/*
* Allocate a port resource with the given resource id.
*/
int
an_alloc_port(dev, rid, size)
device_t dev;
int rid;
int size;
{
struct an_softc *sc = device_get_softc(dev);
struct resource *res;
res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid,
0ul, ~0ul, size, RF_ACTIVE);
if (res) {
sc->port_rid = rid;
sc->port_res = res;
return (0);
} else {
return (ENOENT);
}
}
/*
* Allocate an irq resource with the given resource id.
*/
int
an_alloc_irq(dev, rid, flags)
device_t dev;
int rid;
int flags;
{
struct an_softc *sc = device_get_softc(dev);
struct resource *res;
res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid,
0ul, ~0ul, 1, (RF_ACTIVE | flags));
if (res) {
sc->irq_rid = rid;
sc->irq_res = res;
return (0);
} else {
return (ENOENT);
}
}
/*
* Release all resources
*/
void
an_release_resources(dev)
device_t dev;
{
struct an_softc *sc = device_get_softc(dev);
if (sc->port_res) {
bus_release_resource(dev, SYS_RES_IOPORT,
sc->port_rid, sc->port_res);
sc->port_res = 0;
}
if (sc->irq_res) {
bus_release_resource(dev, SYS_RES_IRQ,
sc->irq_rid, sc->irq_res);
sc->irq_res = 0;
}
}
int
an_attach(sc, unit, flags)
struct an_softc *sc;
int unit;
int flags;
{
struct ifnet *ifp = &sc->arpcom.ac_if;
sc->an_gone = 0;
sc->an_associated = 0;
sc->an_monitor = 0;
sc->an_was_monitor = 0;
/* Reset the NIC. */
an_reset(sc);
/* Load factory config */
if (an_cmd(sc, AN_CMD_READCFG, 0)) {
printf("an%d: failed to load config data\n", sc->an_unit);
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("an%d: read record failed\n", sc->an_unit);
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("an%d: read record failed\n", sc->an_unit);
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("an%d: read record failed\n", sc->an_unit);
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("an%d: read record failed\n", sc->an_unit);
return(EIO);
}
bcopy((char *)&sc->an_caps.an_oemaddr,
(char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN);
printf("an%d: Ethernet address: %6D\n", sc->an_unit,
sc->arpcom.ac_enaddr, ":");
ifp->if_softc = sc;
ifp->if_unit = sc->an_unit = unit;
ifp->if_name = "an";
ifp->if_mtu = ETHERMTU;
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
ifp->if_ioctl = an_ioctl;
ifp->if_output = ether_output;
ifp->if_start = an_start;
ifp->if_watchdog = an_watchdog;
ifp->if_init = an_init;
ifp->if_baudrate = 10000000;
ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
bzero(sc->an_config.an_nodename, sizeof(sc->an_config.an_nodename));
bcopy(AN_DEFAULT_NODENAME, sc->an_config.an_nodename,
sizeof(AN_DEFAULT_NODENAME) - 1);
bzero(sc->an_ssidlist.an_ssid1, sizeof(sc->an_ssidlist.an_ssid1));
bcopy(AN_DEFAULT_NETNAME, sc->an_ssidlist.an_ssid1,
sizeof(AN_DEFAULT_NETNAME) - 1);
sc->an_ssidlist.an_ssid1_len = strlen(AN_DEFAULT_NETNAME);
sc->an_config.an_opmode =
AN_OPMODE_INFRASTRUCTURE_STATION;
sc->an_tx_rate = 0;
bzero((char *)&sc->an_stats, sizeof(sc->an_stats));
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));
/*
* Call MI attach routine.
*/
ether_ifattach(ifp, ETHER_BPF_SUPPORTED);
callout_handle_init(&sc->an_stat_ch);
return(0);
}
static void
an_rxeof(sc)
struct an_softc *sc;
{
struct ifnet *ifp;
struct ether_header *eh;
struct ieee80211_frame *ih;
struct an_rxframe rx_frame;
struct an_rxframe_802_3 rx_frame_802_3;
struct mbuf *m;
int len, id, error = 0;
int ieee80211_header_len;
uint8_t *bpf_buf;
u_short fc1;
ifp = &sc->arpcom.ac_if;
id = CSR_READ_2(sc, AN_RX_FID);
if (sc->an_monitor && (ifp->if_flags & IFF_PROMISC)) {
/* read raw 802.11 packet */
bpf_buf = sc->buf_802_11;
/* read header */
if (an_read_data(sc, id, 0x0, (caddr_t)&rx_frame,
sizeof(rx_frame))) {
ifp->if_ierrors++;
return;
}
/*
* skip beacon by default since this increases the
* system load a lot
*/
if (!(sc->an_monitor & AN_MONITOR_INCLUDE_BEACON) &&
(rx_frame.an_frame_ctl & IEEE80211_FC0_SUBTYPE_BEACON)) {
return;
}
if (sc->an_monitor & AN_MONITOR_AIRONET_HEADER) {
len = rx_frame.an_rx_payload_len
+ sizeof(rx_frame);
/* Check for insane frame length */
if (len > sizeof(sc->buf_802_11)) {
printf("an%d: oversized packet received (%d, %d)\n",
sc->an_unit, len, MCLBYTES);
ifp->if_ierrors++;
return;
}
bcopy((char *)&rx_frame,
bpf_buf, sizeof(rx_frame));
error = an_read_data(sc, id, sizeof(rx_frame),
(caddr_t)bpf_buf+sizeof(rx_frame),
rx_frame.an_rx_payload_len);
} else {
fc1=rx_frame.an_frame_ctl >> 8;
ieee80211_header_len = sizeof(struct ieee80211_frame);
if ((fc1 & IEEE80211_FC1_DIR_TODS) &&
(fc1 & IEEE80211_FC1_DIR_FROMDS)) {
ieee80211_header_len += ETHER_ADDR_LEN;
}
len = rx_frame.an_rx_payload_len
+ ieee80211_header_len;
/* Check for insane frame length */
if (len > sizeof(sc->buf_802_11)) {
printf("an%d: oversized packet received (%d, %d)\n",
sc->an_unit, len, MCLBYTES);
ifp->if_ierrors++;
return;
}
ih = (struct ieee80211_frame *)bpf_buf;
bcopy((char *)&rx_frame.an_frame_ctl,
(char *)ih, ieee80211_header_len);
error = an_read_data(sc, id, sizeof(rx_frame) +
rx_frame.an_gaplen,
(caddr_t)ih +ieee80211_header_len,
rx_frame.an_rx_payload_len);
}
/* dump raw 802.11 packet to bpf and skip ip stack */
if (ifp->if_bpf != NULL) {
bpf_tap(ifp, bpf_buf, len);
}
} else {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -