NetBSD/sys/dev/ic/awi.c
sommerfeld dea88f1c20 Completely rewrite receive processing to avoid sleep-deprivation-induced
brain damage and infinite loops, and lousy RTTs

Also, flush tx queues when reassociating with AP and when taking the
interface down.
1999-11-09 14:58:07 +00:00

2625 lines
58 KiB
C

/* $NetBSD: awi.c,v 1.8 1999/11/09 14:58:07 sommerfeld Exp $ */
/*-
* Copyright (c) 1999 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Bill Sommerfeld
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the NetBSD
* Foundation, Inc. and its contributors.
* 4. Neither the name of The NetBSD Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Driver for AMD 802.11 firmware.
* Uses am79c930 chip driver to talk to firmware running on the am79c930.
*
* More-or-less a generic ethernet-like if driver, with 802.11 gorp added.
*/
/*
* todo:
* - flush tx queue on resynch.
* - clear oactive on "down".
* - rewrite copy-into-mbuf code
* - mgmt state machine gets stuck retransmitting assoc requests.
* - multicast filter.
* - fix device reset so it's more likely to work
* - show status goo through ifmedia.
*
* more todo:
* - deal with more 802.11 frames.
* - send reassoc request
* - deal with reassoc response
* - send/deal with disassociation
* - deal with "full" access points (no room for me).
* - power save mode
*
* later:
* - SSID preferences
* - need ioctls for poking at the MIBs
* - implement ad-hoc mode (including bss creation).
* - decide when to do "ad hoc" vs. infrastructure mode (IFF_LINK flags?)
* (focus on inf. mode since that will be needed for ietf)
* - deal with DH vs. FH versions of the card
* - deal with faster cards (2mb/s)
* - ?WEP goo (mmm, rc4) (it looks not particularly useful).
* - ifmedia revision.
* - common 802.11 mibish things.
* - common 802.11 media layer.
*/
#include "opt_inet.h"
#include "opt_ns.h"
#include "bpfilter.h"
#include "rnd.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/errno.h>
#include <sys/syslog.h>
#include <sys/select.h>
#include <sys/device.h>
#if NRND > 0
#include <sys/rnd.h>
#endif
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_ether.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>
#include <netinet/if_inarp.h>
#endif
#ifdef NS
#include <netns/ns.h>
#include <netns/ns_if.h>
#endif
#if NBPFILTER > 0
#include <net/bpf.h>
#include <net/bpfdesc.h>
#endif
#include <machine/cpu.h>
#include <machine/bus.h>
#include <machine/intr.h>
#include <dev/ic/am79c930reg.h>
#include <dev/ic/am79c930var.h>
#include <dev/ic/awireg.h>
#include <dev/ic/awivar.h>
void awi_insane __P((struct awi_softc *sc));
int awi_intlock __P((struct awi_softc *sc));
void awi_intunlock __P((struct awi_softc *sc));
void awi_intrinit __P((struct awi_softc *sc));
u_int8_t awi_read_intst __P((struct awi_softc *sc));
void awi_stop __P((struct awi_softc *sc));
void awi_flush __P((struct awi_softc *sc));
void awi_init __P((struct awi_softc *sc));
void awi_set_mc __P((struct awi_softc *sc));
void awi_rxint __P((struct awi_softc *));
void awi_txint __P((struct awi_softc *));
void awi_tx_packet __P((struct awi_softc *, int, struct mbuf *));
void awi_rcv __P((struct awi_softc *, struct mbuf *, u_int32_t, u_int8_t));
void awi_rcv_mgt __P((struct awi_softc *, struct mbuf *, u_int32_t, u_int8_t));
void awi_rcv_data __P((struct awi_softc *, struct mbuf *));
void awi_rcv_ctl __P((struct awi_softc *, struct mbuf *));
int awi_enable __P((struct awi_softc *sc));
void awi_disable __P((struct awi_softc *sc));
void awi_zero __P((struct awi_softc *, u_int32_t, u_int32_t));
void awi_cmd __P((struct awi_softc *, u_int8_t));
void awi_cmd_test_if __P((struct awi_softc *));
void awi_cmd_get_mib __P((struct awi_softc *sc, u_int8_t, u_int8_t, u_int8_t));
void awi_cmd_txinit __P((struct awi_softc *sc));
void awi_cmd_scan __P((struct awi_softc *sc));
void awi_scan_next __P((struct awi_softc *sc));
void awi_try_sync __P((struct awi_softc *sc));
void awi_cmd_set_ss __P((struct awi_softc *sc));
void awi_cmd_set_promisc __P((struct awi_softc *sc));
void awi_cmd_set_allmulti __P((struct awi_softc *sc));
void awi_cmd_set_infra __P((struct awi_softc *sc));
void awi_cmd_set_notap __P((struct awi_softc *sc));
void awi_cmd_get_myaddr __P((struct awi_softc *sc));
void awi_cmd_scan_done __P((struct awi_softc *sc, u_int8_t));
void awi_cmd_sync_done __P((struct awi_softc *sc, u_int8_t));
void awi_cmd_set_ss_done __P((struct awi_softc *sc, u_int8_t));
void awi_cmd_set_allmulti_done __P((struct awi_softc *sc, u_int8_t));
void awi_cmd_set_promisc_done __P((struct awi_softc *sc, u_int8_t));
void awi_cmd_set_infra_done __P((struct awi_softc *sc, u_int8_t));
void awi_cmd_set_notap_done __P((struct awi_softc *sc, u_int8_t));
void awi_cmd_get_myaddr_done __P((struct awi_softc *sc, u_int8_t));
void awi_reset __P((struct awi_softc *));
void awi_init_1 __P((struct awi_softc *));
void awi_init_2 __P((struct awi_softc *, u_int8_t));
void awi_mibdump __P((struct awi_softc *, u_int8_t));
void awi_init_read_bufptrs_done __P((struct awi_softc *, u_int8_t));
void awi_init_4 __P((struct awi_softc *, u_int8_t));
void awi_init_5 __P((struct awi_softc *, u_int8_t));
void awi_init_6 __P((struct awi_softc *, u_int8_t));
void awi_running __P((struct awi_softc *));
void awi_init_txdescr __P((struct awi_softc *));
void awi_init_txd __P((struct awi_softc *, int, int, int, int));
void awi_watchdog __P((struct ifnet *));
void awi_start __P((struct ifnet *));
int awi_ioctl __P((struct ifnet *, u_long, caddr_t));
void awi_dump_rxchain __P((struct awi_softc *, char *, u_int32_t *));
void awi_send_frame __P((struct awi_softc *, struct mbuf *));
void awi_send_authreq __P((struct awi_softc *));
void awi_send_assocreq __P((struct awi_softc *));
void awi_parse_tlv __P((u_int8_t *base, u_int8_t *end, u_int8_t **vals, u_int8_t *lens, size_t nattr));
u_int8_t *awi_add_rates __P((struct awi_softc *, struct mbuf *, u_int8_t *));
u_int8_t *awi_add_ssid __P((struct awi_softc *, struct mbuf *, u_int8_t *));
void * awi_init_hdr __P((struct awi_softc *, struct mbuf *, int, int));
void awi_hexdump __P((char *tag, u_int8_t *data, int len));
void awi_card_hexdump __P((struct awi_softc *, char *tag, u_int32_t offset, int len));
int awi_drop_output __P((struct ifnet *, struct mbuf *,
struct sockaddr *, struct rtentry *));
void awi_drop_input __P((struct ifnet *, struct mbuf *));
struct mbuf *awi_output_kludge __P((struct awi_softc *, struct mbuf *));
void awi_set_timer __P((struct awi_softc *));
void awi_restart_scan __P((struct awi_softc *));
struct awi_rxd
{
u_int32_t next;
u_int16_t len;
u_int8_t state, rate, rssi, index;
u_int32_t frame;
u_int32_t rxts;
};
void awi_copy_rxd __P((struct awi_softc *, u_int32_t, struct awi_rxd *));
u_int32_t awi_parse_rxd __P((struct awi_softc *, u_int32_t, struct awi_rxd *));
static const u_int8_t snap_magic[] = { 0xaa, 0xaa, 3, 0, 0, 0 };
int awi_scan_keepalive = 10;
/*
* attach (called by bus-specific front end)
*
* look for banner message
* wait for selftests to complete (up to 2s??? eeee.)
* (do this with a timeout!!??!!)
* on timeout completion:
* issue test_interface command.
* get_mib command to locate TX buffer.
* set_mib command to set any non-default variables.
* init tx first.
* init rx second with enable receiver command
*
* mac mgmt portion executes sync command to start BSS
*
*/
/*
* device shutdown routine.
*/
/*
* device appears to be insane. rather than hanging, whap device upside
* the head on next timeout.
*/
void
awi_insane(sc)
struct awi_softc *sc;
{
struct ifnet *ifp = sc->sc_ifp;
printf("%s: device timeout\n", sc->sc_dev.dv_xname);
/* whap device on next timeout. */
sc->sc_state = AWI_ST_INSANE;
ifp->if_timer = 1;
}
void
awi_set_timer (sc)
struct awi_softc *sc;
{
if (sc->sc_tx_timer || sc->sc_scan_timer ||
sc->sc_mgt_timer || sc->sc_cmd_timer)
sc->sc_ifp->if_timer = 1;
}
/*
* Copy m0 into the given TX descriptor and give the descriptor to the
* device so it starts transmiting..
*/
void
awi_tx_packet (sc, txd, m0)
struct awi_softc *sc;
int txd;
struct mbuf *m0;
{
u_int32_t frame = sc->sc_txd[txd].frame;
u_int32_t len = sc->sc_txd[txd].len;
struct mbuf *m;
for (m = m0; m != NULL; m = m->m_next) {
u_int32_t nmove;
nmove = min(len, m->m_len);
awi_write_bytes (sc, frame, m->m_data, nmove);
if (nmove != m->m_len) {
printf("%s: large frame truncated\n",
sc->sc_dev.dv_xname);
break;
}
frame += nmove;
len -= nmove;
}
awi_init_txd (sc,
txd,
AWI_TXD_ST_OWN,
frame - sc->sc_txd[txd].frame,
AWI_RATE_1MBIT);
#if 0
awi_card_hexdump (sc, "txd to go", sc->sc_txd[txd].descr,
AWI_TXD_SIZE);
#endif
}
/*
* XXX KLUDGE XXX
*
* Convert ethernet-formatted frame into 802.11 data frame
* for infrastructure mode.
*/
struct mbuf *
awi_output_kludge (sc, m0)
struct awi_softc *sc;
struct mbuf *m0;
{
u_int8_t *framehdr;
u_int8_t *llchdr;
u_int8_t dstaddr[ETHER_ADDR_LEN];
struct awi_mac_header *amhdr;
u_int16_t etype;
struct ether_header *eh = mtod(m0, struct ether_header *);
#if 0
awi_hexdump("etherframe", m0->m_data, m0->m_len);
#endif
memcpy(dstaddr, eh->ether_dhost, sizeof(dstaddr));
etype = eh->ether_type;
m_adj(m0, sizeof(struct ether_header));
M_PREPEND(m0, sizeof(struct awi_mac_header) + 8, M_DONTWAIT);
if (m0 == NULL) {
printf("oops, prepend failed\n");
return NULL;
}
if (m0->m_len < 32) {
printf("oops, prepend only left %d bytes\n", m0->m_len);
m_freem(m0);
return NULL;
}
framehdr = mtod(m0, u_int8_t *);
amhdr = mtod(m0, struct awi_mac_header *);
amhdr->awi_fc = IEEEWL_FC_VERS |
IEEEWL_FC_TYPE_DATA<<IEEEWL_FC_TYPE_SHIFT;
amhdr->awi_f2 = IEEEWL_FC2_TODS;
memcpy(amhdr->awi_addr3, dstaddr, ETHER_ADDR_LEN); /* ether DST */
memcpy(amhdr->awi_addr1, sc->sc_active_bss.bss_id, ETHER_ADDR_LEN);
memcpy(amhdr->awi_addr2, sc->sc_my_addr, ETHER_ADDR_LEN);
amhdr->awi_duration = 0;
amhdr->awi_seqctl = 0;
llchdr = (u_int8_t *) (amhdr + 1);
memcpy(llchdr, snap_magic, 6);
memcpy(llchdr+6, &etype, 2);
return m0;
}
/*
* device start routine
*
* loop while there are free tx buffer descriptors and mbufs in the queue:
* -> copy mbufs to tx buffer and free mbufs.
* -> mark txd as good to go (OWN bit set, all others clear)
*/
void
awi_start(ifp)
struct ifnet *ifp;
{
struct awi_softc *sc = ifp->if_softc;
struct mbuf *m0;
int opending;
if ((ifp->if_flags & IFF_RUNNING) == 0) {
printf("%s: start called while not running\n",
sc->sc_dev.dv_xname);
return;
}
/*
* loop through send queue, setting up tx descriptors
* until we either run out of stuff to send, or descriptors
* to send them in.
*/
opending = sc->sc_txpending;
while (sc->sc_txpending < sc->sc_ntxd) {
/*
* Grab a packet off the queue.
*/
IF_DEQUEUE (&sc->sc_mgtq, m0);
if (m0 == NULL) {
/* XXX defer sending if not synched yet? */
IF_DEQUEUE (&ifp->if_snd, m0);
if (m0 == NULL)
break;
#if NBPFILTER > 0
/*
* Pass packet to bpf if there is a listener.
*/
if (ifp->if_bpf)
bpf_mtap(ifp->if_bpf, m0);
#endif
/*
* We've got an ethernet-format frame.
* we need to mangle it into 802.11 form..
*/
m0 = awi_output_kludge(sc, m0);
if (m0 == NULL)
continue;
}
awi_tx_packet(sc, sc->sc_txnext, m0);
sc->sc_txpending++;
sc->sc_txnext = (sc->sc_txnext + 1) % sc->sc_ntxd;
m_freem(m0);
}
if (sc->sc_txpending >= sc->sc_ntxd) {
/* no more slots available.. */
ifp->if_flags |= IFF_OACTIVE;
}
if (sc->sc_txpending != opending) {
/* set watchdog timer in case unit flakes out */
if (sc->sc_tx_timer == 0)
sc->sc_tx_timer = 5;
awi_set_timer(sc);
}
}
int
awi_enable(sc)
struct awi_softc *sc;
{
if (sc->sc_enabled == 0) {
if ((sc->sc_enable != NULL) && ((*sc->sc_enable)(sc) != 0)) {
printf("%s: device enable failed\n",
sc->sc_dev.dv_xname);
return (EIO);
}
awi_init(sc);
}
sc->sc_enabled = 1;
return 0;
}
void
awi_disable(sc)
struct awi_softc *sc;
{
if (sc->sc_enabled != 0 && sc->sc_disable != NULL) {
(*sc->sc_disable)(sc);
sc->sc_enabled = 0;
}
}
int
awi_intlock(sc)
struct awi_softc *sc;
{
int i, j;
u_int8_t lockout;
DELAY(5);
for (j=0; j<10; j++) {
for (i=0; i<AWI_LOCKOUT_SPIN; i++) {
lockout = awi_read_1(sc, AWI_LOCKOUT_HOST);
if (!lockout)
break;
DELAY(5);
}
if (lockout)
break;
awi_write_1 (sc, AWI_LOCKOUT_MAC, 1);
lockout = awi_read_1(sc, AWI_LOCKOUT_HOST);
if (!lockout)
break;
/* oops, lost the race.. try again */
awi_write_1 (sc, AWI_LOCKOUT_MAC, 0);
}
if (lockout) {
awi_insane(sc);
return 0;
}
return 1;
}
void
awi_intunlock(sc)
struct awi_softc *sc;
{
awi_write_1 (sc, AWI_LOCKOUT_MAC, 0);
}
void
awi_intrinit(sc)
struct awi_softc *sc;
{
u_int8_t intmask;
am79c930_gcr_setbits(&sc->sc_chip, AM79C930_GCR_ENECINT);
intmask = AWI_INT_GROGGY|AWI_INT_SCAN_CMPLT|
AWI_INT_TX|AWI_INT_RX|AWI_INT_CMD;
intmask = ~intmask;
if (!awi_intlock(sc))
return;
awi_write_1(sc, AWI_INTMASK, intmask);
awi_write_1(sc, AWI_INTMASK2, 0);
awi_intunlock(sc);
}
void awi_hexdump (char *tag, u_int8_t *data, int len)
{
int i;
printf("%s:", tag);
for (i=0; i<len; i++) {
printf(" %02x", data[i]);
}
printf("\n");
}
void awi_card_hexdump (sc, tag, offset, len)
struct awi_softc *sc;
char *tag;
u_int32_t offset;
int len;
{
int i;
printf("%s:", tag);
for (i=0; i<len; i++) {
printf(" %02x", awi_read_1(sc, offset+i));
}
printf("\n");
}
u_int8_t
awi_read_intst(sc)
struct awi_softc *sc;
{
u_int8_t state;
if (!awi_intlock(sc))
return 0;
/* we have int lock.. */
state = awi_read_1 (sc, AWI_INTSTAT);
awi_write_1(sc, AWI_INTSTAT, 0);
awi_intunlock(sc);
return state;
}
void
awi_parse_tlv (u_int8_t *base, u_int8_t *end, u_int8_t **vals, u_int8_t *lens, size_t nattr)
{
u_int8_t tag, len;
int i;
for (i=0; i<nattr; i++) {
vals[i] = NULL;
lens[i] = 0;
}
while (base < end) {
tag = base[0];
len = base[1];
base += 2;
if (tag < nattr) {
lens[tag] = len;
vals[tag] = base;
}
base += len;
}
}
void
awi_send_frame (sc, m)
struct awi_softc *sc;
struct mbuf *m;
{
IF_ENQUEUE(&sc->sc_mgtq, m);
awi_start(sc->sc_ifp);
}
void *
awi_init_hdr (sc, m, f1, f2)
struct awi_softc *sc;
struct mbuf *m;
int f1;
int f2;
{
struct awi_mac_header *amhp;
/*
* initialize 802.11 mac header in mbuf, return pointer to next byte..
*/
amhp = mtod(m, struct awi_mac_header *);
amhp->awi_fc = f1;
amhp->awi_f2 = f2;
amhp->awi_duration = 0;
memcpy(amhp->awi_addr1, sc->sc_active_bss.bss_id, ETHER_ADDR_LEN);
memcpy(amhp->awi_addr2, sc->sc_my_addr, ETHER_ADDR_LEN);
memcpy(amhp->awi_addr3, sc->sc_active_bss.bss_id, ETHER_ADDR_LEN);
amhp->awi_seqctl = 0;
return amhp+1;
}
u_int8_t *
awi_add_rates (sc, m, ptr)
struct awi_softc *sc;
struct mbuf *m;
u_int8_t *ptr;
{
*ptr++ = 1; /* XXX */
*ptr++ = 1; /* XXX */
*ptr++ = 0x82; /* XXX */
return ptr;
}
u_int8_t *
awi_add_ssid (sc, m, ptr)
struct awi_softc *sc;
struct mbuf *m;
u_int8_t *ptr;
{
int len = sc->sc_active_bss.sslen;
*ptr++ = 0; /* XXX */
*ptr++ = len;
memcpy(ptr, sc->sc_active_bss.ssid, len);
ptr += len;
return ptr;
}
void
awi_send_authreq (sc)
struct awi_softc *sc;
{
struct mbuf *m;
struct awi_auth_hdr *amahp;
u_int8_t *tlvptr;
MGETHDR(m, M_DONTWAIT, MT_DATA);
/*
* form an "association request" message.
*/
/*
* auth alg number. 2 bytes. = 0
* auth txn seq number = 2 bytes = 1
* status code = 2 bytes = 0
* challenge text (not present)
*/
if (m == 0)
return; /* we'll try again later.. */
amahp = awi_init_hdr (sc, m,
(IEEEWL_FC_VERS |
(IEEEWL_FC_TYPE_MGT << IEEEWL_FC_TYPE_SHIFT) |
(IEEEWL_SUBTYPE_AUTH << IEEEWL_FC_SUBTYPE_SHIFT)),
0);
amahp->awi_algno[0] = 0;
amahp->awi_algno[1] = 0;
amahp->awi_seqno[0] = 1;
amahp->awi_seqno[1] = 0;
amahp->awi_status[0] = 0;
amahp->awi_status[1] = 0;
/*
* form an "authentication" message.
*/
tlvptr = (u_int8_t *)(amahp+1);
tlvptr = awi_add_ssid(sc, m, tlvptr);
tlvptr = awi_add_rates(sc, m, tlvptr);
m->m_len = tlvptr - mtod(m, u_int8_t *);
if (sc->sc_ifp->if_flags & IFF_DEBUG) {
printf("%s: sending auth request\n",
sc->sc_dev.dv_xname);
awi_hexdump("frame", m->m_data, m->m_len);
}
awi_send_frame(sc, m);
sc->sc_mgt_timer = 2;
awi_set_timer(sc);
}
void
awi_send_assocreq (sc)
struct awi_softc *sc;
{
struct mbuf *m;
struct awi_assoc_hdr *amahp;
u_int8_t *tlvptr;
MGETHDR(m, M_DONTWAIT, MT_DATA);
/*
* form an "association request" message.
*/
if (m == 0)
return; /* we'll try again later.. */
/*
* cap info (2 bytes)
* listen interval (2 bytes)
* ssid (variable)
* supported rates (variable)
*/
amahp = awi_init_hdr (sc, m,
IEEEWL_FC_TYPE_MGT, IEEEWL_SUBTYPE_ASSOCREQ);
amahp->awi_cap_info[0] = 4; /* XXX magic (CF-pollable) */
amahp->awi_cap_info[1] = 0;
amahp->awi_li[0] = 1;
amahp->awi_li[1] = 0;
tlvptr = (u_int8_t *)(amahp+1);
tlvptr = awi_add_ssid(sc, m, tlvptr);
tlvptr = awi_add_rates(sc, m, tlvptr);
m->m_len = tlvptr - mtod(m, u_int8_t *);
if (sc->sc_ifp->if_flags & IFF_DEBUG) {
printf("%s: sending assoc request\n",
sc->sc_dev.dv_xname);
awi_hexdump("frame", m->m_data, m->m_len);
}
awi_send_frame(sc, m);
sc->sc_mgt_timer = 2;
awi_set_timer(sc);
}
#if 0
void
awi_send_reassocreq (sc)
{
/*
* form an "reassociation request" message.
*/
/* 2 bytes frame control
00100000 00000000
2 bytes goo
00000000 00000000
address 1: bssid
address 2: my address
address 3: bssid
2 bytes seq/ctl
00000000 00000000
cap info (2 bytes)
listen interval (2 bytes)
current ap address (6 bytes)
ssid (variable)
supported rates (va
*/
}
#endif
void
awi_rcv_ctl (sc, m)
struct awi_softc *sc;
struct mbuf *m;
{
printf("%s: ctl\n", sc->sc_dev.dv_xname);
}
void
awi_rcv_data (sc, m)
struct awi_softc *sc;
struct mbuf *m;
{
struct ifnet *ifp = sc->sc_ifp;
u_int8_t *llc;
u_int8_t *to, *from;
struct awi_mac_header *amhp;
sc->sc_scan_timer = awi_scan_keepalive; /* user data is as good
as a beacon as a keepalive.. */
amhp = mtod(m, struct awi_mac_header *);
/*
* we have: 4 bytes useless goo.
* 3 x 6 bytes MAC addresses.
* 2 bytes goo.
* 802.x LLC header, SNAP header, and data.
*
* for now, we fake up a "normal" ethernet header and feed
* this to the appropriate input routine.
*/
llc = (u_int8_t *)(amhp+1);
if (amhp->awi_f2 & IEEEWL_FC2_TODS) {
printf("drop packet to DS\n");
goto drop;
}
to = amhp->awi_addr1;
if (amhp->awi_f2 & IEEEWL_FC2_FROMDS)
from = amhp->awi_addr3;
else
from = amhp->awi_addr2;
if (memcmp (llc, snap_magic, 6) != 0)
goto drop;
/* XXX overwrite llc with "from" address */
/* XXX overwrite llc-6 with "to" address */
memcpy(llc, from, ETHER_ADDR_LEN);
memcpy(llc-6, to, ETHER_ADDR_LEN);
m_adj(m, sizeof(struct awi_mac_header) + sizeof(struct awi_llc_header)
- sizeof(struct ether_header));
#if NBPFILTER > 0
/*
* Pass packet to bpf if there is a listener.
*/
if (ifp->if_bpf)
bpf_mtap(ifp->if_bpf, m);
#endif
#if __NetBSD_Version__ > 104010000
m->m_flags |= M_HASFCS;
(*ifp->if_input)(ifp, m);
#else
{
struct ether_header *eh;
eh = mtod(m, struct ether_header *);
m_adj(m, sizeof(*eh));
m_adj(m, -ETHER_CRC_LEN);
ether_input(ifp, eh, m);
}
#endif
return;
drop:
m_freem(m);
}
void
awi_rcv_mgt (sc, m, rxts, rssi)
struct awi_softc *sc;
struct mbuf *m;
u_int32_t rxts;
u_int8_t rssi;
{
u_int8_t subtype;
u_int8_t *framehdr, *mgthdr, *end, *timestamp;
struct awi_auth_hdr *auhp;
struct ifnet *ifp = sc->sc_ifp;
#define IEEEWL_MGT_NATTR 10 /* XXX */
u_int8_t *attr[IEEEWL_MGT_NATTR];
u_int8_t attrlen[IEEEWL_MGT_NATTR];
u_int8_t *addr1, *addr2, *addr3;
u_int8_t *sa, *da, *bss;
framehdr = mtod(m, u_int8_t *);
/*
* mgt frame:
* 2 bytes frame goo
* 2 bytes duration
* 6 bytes a1
* 6 bytes a2
* 6 bytes a3
* 2 bytes seq control.
* --
* 24 bytes goo.
*/
subtype = (framehdr[IEEEWL_FC] & IEEEWL_FC_SUBTYPE_MASK)
>> IEEEWL_FC_SUBTYPE_SHIFT;
addr1 = framehdr + 4; /* XXX */
addr2 = addr1+ETHER_ADDR_LEN;
addr3 = addr2+ETHER_ADDR_LEN;
/* XXX look at to/from DS bits here!! */
da = addr1;
sa = addr3;
bss = addr2;
framehdr = mtod(m, u_int8_t *);
end = framehdr + m->m_len;
end -= 4; /* trim TLV */
mgthdr = framehdr + 24; /* XXX magic */
switch (subtype) {
case IEEEWL_SUBTYPE_ASSOCRESP:
/*
* this acknowledges that the AP will be forwarding traffic
* for us..
*
* contains:
* cap info
* status code
* AId
* supported rates.
*/
if (ifp->if_flags & IFF_DEBUG) {
printf("%s: got assoc resp\n",
sc->sc_dev.dv_xname);
awi_hexdump("assocresp", m->m_data, m->m_len);
}
awi_drvstate (sc, AWI_DRV_INFASSOC);
sc->sc_state = AWI_ST_RUNNING;
sc->sc_mgt_timer = AWI_ASSOC_REFRESH;
awi_set_timer(sc);
if (sc->sc_new_bss) {
printf("%s: associated with %s, SSID: %s\n",
sc->sc_dev.dv_xname,
ether_sprintf(sc->sc_active_bss.bss_id),
sc->sc_active_bss.ssid);
sc->sc_new_bss = 0;
}
/* XXX set media status to "i see carrier" */
break;
case IEEEWL_SUBTYPE_REASSOCRESP:
/*
* this indicates that we've moved from one AP to another
* within the same DS.
*/
printf("reassoc_resp\n");
break;
case IEEEWL_SUBTYPE_PROBEREQ:
/* discard */
break;
case IEEEWL_SUBTYPE_PROBERESP:
/*
* 8 bytes timestamp.
* 2 bytes beacon intvl.
* 2 bytes cap info.
* then tlv data..
*/
timestamp = mgthdr;
if (ifp->if_flags & IFF_DEBUG) {
printf("%s: got probe resp\n",
sc->sc_dev.dv_xname);
awi_hexdump("proberesp", m->m_data, m->m_len);
}
/* now, into the tlv goo.. */
mgthdr += 12; /* XXX magic */
awi_parse_tlv (mgthdr, end, attr, attrlen, IEEEWL_MGT_NATTR);
if (attr[IEEEWL_MGT_TLV_SSID] &&
attr[IEEEWL_MGT_TLV_FHPARMS] &&
attrlen[IEEEWL_MGT_TLV_SSID] < AWI_SSID_LEN) {
struct awi_bss_binding *bp = NULL;
int i;
for (i=0; i< sc->sc_nbindings; i++) {
struct awi_bss_binding *bp1 =
&sc->sc_bindings[i];
if (memcmp(bp1->bss_id, bss, ETHER_ADDR_LEN) == 0) {
bp = bp1;
break;
}
}
if (bp == NULL && sc->sc_nbindings < NBND) {
bp = &sc->sc_bindings[sc->sc_nbindings++];
}
if (bp != NULL) {
u_int8_t *fhparms =
attr[IEEEWL_MGT_TLV_FHPARMS];
bp->sslen = attrlen[IEEEWL_MGT_TLV_SSID];
memcpy(bp->ssid, attr[IEEEWL_MGT_TLV_SSID],
bp->sslen);
bp->ssid[bp->sslen] = 0;
memcpy(bp->bss_id, bss, ETHER_ADDR_LEN);
/* XXX more magic numbers.. */
bp->dwell_time = fhparms[0] | (fhparms[1]<<8);
bp->chanset = fhparms[2];
bp->pattern = fhparms[3];
bp->index = fhparms[4];
bp->rssi = rssi;
bp->rxtime = rxts;
memcpy(bp->bss_timestamp, timestamp, 8);
}
}
break;
case IEEEWL_SUBTYPE_BEACON:
if ((ifp->if_flags & (IFF_DEBUG|IFF_LINK2)) ==
(IFF_DEBUG|IFF_LINK2)) {
printf("%s: beacon from %s\n",
sc->sc_dev.dv_xname,
ether_sprintf(addr2));
awi_hexdump("beacon", m->m_data, m->m_len);
}
/*
* Note that AP is still alive so we don't have to go looking
* for one for a while.
*
* XXX Beacons from other AP's should be recorded for
* potential use if we lose this AP.. (also, may want
* to notice if rssi of new AP is significantly
* stronger than old one and jump ship..)
*/
if ((sc->sc_state >= AWI_ST_SYNCED) &&
(memcmp (addr2, sc->sc_active_bss.bss_id,
ETHER_ADDR_LEN) == 0)) {
sc->sc_scan_timer = awi_scan_keepalive;
awi_set_timer(sc);
}
break;
case IEEEWL_SUBTYPE_DISSOC:
printf("dissoc\n");
break;
case IEEEWL_SUBTYPE_AUTH:
if (ifp->if_flags & IFF_DEBUG) {
printf("%s: got auth\n",
sc->sc_dev.dv_xname);
awi_hexdump("auth", m->m_data, m->m_len);
}
/*
* woohoo! somebody likes us!
*/
auhp = (struct awi_auth_hdr *)mgthdr;
if ((auhp->awi_status[0] == 0) && (auhp->awi_status[1] == 0))
{
awi_drvstate (sc, AWI_DRV_INFAUTH);
sc->sc_state = AWI_ST_AUTHED;
awi_send_assocreq (sc);
}
break;
case IEEEWL_SUBTYPE_DEAUTH:
if (ifp->if_flags & IFF_DEBUG) {
printf("%s: got deauth\n",
sc->sc_dev.dv_xname);
awi_hexdump("deauth", m->m_data, m->m_len);
}
sc->sc_state = AWI_ST_SYNCED;
sc->sc_new_bss = 1;
awi_send_authreq(sc);
break;
default:
printf("unk mgt subtype %x\n", subtype);
break;
}
m_freem(m); /* done.. */
}
/*
* Do 802.11 receive processing. "m" contains a receive frame;
* rxts is the local receive timestamp
*/
void
awi_rcv (sc, m, rxts, rssi)
struct awi_softc *sc;
struct mbuf *m;
u_int32_t rxts;
u_int8_t rssi;
{
u_int8_t *framehdr;
u_int8_t framectl;
framehdr = mtod(m, u_int8_t *);
/*
* peek at first byte of frame header.
* check version subfield (must be zero)
* check type subfield (00 = mgt, 01 = ctl, 10 = data)
* check subtype field (next four bits)
*/
/*
* Not counting WDS mode, the IEEE 802.11 frame header format
* has *three* MAC addresses.
* (source, destination, and BSS).
*
* The BSS indicates which wireless "cable segment" we're part of;
* we discover this dynamically..
*
* Not content to put them in a fixed order, the exact
* ordering of these addresses depends on other attribute bits
* in the frame control word!
*
* an alternate presentation which is more self-consistent:
* address 1 is the "wireless destination" -- either the
* station address,
* for wireless->wireless traffic, or the BSS id of an AP.
*
* address 2 is the "wireless source" -- either the
* station address of a wireless node, or the BSS id of an AP.
*
* address 3 is the "other address" -- for STA->AP, the
* eventual destination; for AP->STA, the original source, and
* for ad-hoc mode, the BSS id..
*/
framectl = framehdr[IEEEWL_FC];
if ((framectl & IEEEWL_FC_VERS_MASK) != IEEEWL_FC_VERS) {
printf("wrong vers. drop");
goto drop;
}
switch (framectl & IEEEWL_FC_TYPE_MASK) {
case IEEEWL_FC_TYPE_MGT << IEEEWL_FC_TYPE_SHIFT:
awi_rcv_mgt (sc, m, rxts, rssi);
m = 0;
break;
case IEEEWL_FC_TYPE_DATA << IEEEWL_FC_TYPE_SHIFT:
awi_rcv_data (sc, m);
m = 0;
break;
case IEEEWL_FC_TYPE_CTL << IEEEWL_FC_TYPE_SHIFT:
awi_rcv_ctl (sc, m);
default:
goto drop;
}
drop:
if (m) m_freem(m);
}
void
awi_copy_rxd (sc, cur, rxd)
struct awi_softc *sc;
u_int32_t cur;
struct awi_rxd *rxd;
{
char bitbuf[64];
if (sc->sc_ifp->if_flags & IFF_LINK0) {
printf("%x: ", cur);
awi_card_hexdump(sc, "rxd", cur, AWI_RXD_SIZE);
}
rxd->next = awi_read_4(sc, cur + AWI_RXD_NEXT);
rxd->state = awi_read_1(sc, cur + AWI_RXD_HOST_DESC_STATE);
rxd->len = awi_read_2 (sc, cur + AWI_RXD_LEN);
rxd->rate = awi_read_1 (sc, cur + AWI_RXD_RATE);
rxd->rssi = awi_read_1 (sc, cur + AWI_RXD_RSSI);
rxd->index = awi_read_1 (sc, cur + AWI_RXD_INDEX);
rxd->frame = awi_read_4 (sc, cur + AWI_RXD_START_FRAME);
rxd->rxts = awi_read_4 (sc, cur + AWI_RXD_LOCALTIME);
/*
* only the low order bits of "frame" and "next" are valid.
* (the documentation doesn't mention this).
*/
rxd->frame &= 0xffff;
rxd->next &= (0xffff | AWI_RXD_NEXT_LAST);
/*
* XXX after masking, sanity check that rxd->frame and
* rxd->next lie within the receive area.
*/
if (sc->sc_ifp->if_flags & IFF_LINK0) {
printf("nxt %x frame %x state %s len %d\n",
rxd->next, rxd->frame,
bitmask_snprintf(rxd->state, AWI_RXD_ST_BITS,
bitbuf, sizeof(bitbuf)),
rxd->len);
}
}
u_int32_t
awi_parse_rxd (sc, cur, rxd)
struct awi_softc *sc;
u_int32_t cur;
struct awi_rxd *rxd;
{
struct mbuf *top;
struct ifnet *ifp = sc->sc_ifp;
u_int32_t next;
if ((rxd->state & AWI_RXD_ST_CONSUMED) == 0) {
if (ifp->if_flags & IFF_LINK1) {
int xx = awi_read_1(sc, rxd->frame);
if (xx != (IEEEWL_FC_VERS |
(IEEEWL_FC_TYPE_MGT<<IEEEWL_FC_TYPE_SHIFT) |
(IEEEWL_SUBTYPE_BEACON << IEEEWL_FC_SUBTYPE_SHIFT))) {
char bitbuf[64];
printf("floosh: %d state ", sc->sc_flushpkt);
awi_card_hexdump(sc,
bitmask_snprintf(rxd->state,
AWI_RXD_ST_BITS,
bitbuf, sizeof(bitbuf)),
rxd->frame, rxd->len);
}
}
if ((sc->sc_flushpkt == 0) &&
(sc->sc_nextpkt == NULL)) {
MGETHDR(top, M_DONTWAIT, MT_DATA);
if (top == NULL) {
sc->sc_flushpkt = 1;
sc->sc_m = NULL;
sc->sc_mptr = NULL;
sc->sc_mleft = 0;
} else {
if (rxd->len >= MINCLSIZE)
MCLGET(top, M_DONTWAIT);
top->m_pkthdr.rcvif = ifp;
top->m_pkthdr.len = 0;
top->m_len = 0;
sc->sc_mleft = (top->m_flags & M_EXT) ?
MCLBYTES : MHLEN;
sc->sc_mptr = mtod(top, u_int8_t *);
sc->sc_m = top;
sc->sc_nextpkt = top;
}
}
if (sc->sc_flushpkt == 0) {
/* copy data into mbuf */
while (rxd->len > 0) {
int nmove = min (rxd->len, sc->sc_mleft);
awi_read_bytes (sc, rxd->frame, sc->sc_mptr,
nmove);
rxd->len -= nmove;
rxd->frame += nmove;
sc->sc_mleft -= nmove;
sc->sc_mptr += nmove;
sc->sc_nextpkt->m_pkthdr.len += nmove;
sc->sc_m->m_len += nmove;
if ((rxd->len > 0) && (sc->sc_mleft == 0)) {
struct mbuf *m1;
/* Get next mbuf.. */
MGET(m1, M_DONTWAIT, MT_DATA);
if (m1 == NULL) {
m_freem(sc->sc_nextpkt);
sc->sc_nextpkt = NULL;
sc->sc_flushpkt = 1;
sc->sc_m = NULL;
sc->sc_mptr = NULL;
sc->sc_mleft = 0;
break;
}
sc->sc_m->m_next = m1;
sc->sc_m = m1;
m1->m_len = 0;
sc->sc_mleft = MLEN;
sc->sc_mptr = mtod(m1, u_int8_t *);
}
}
}
if (rxd->state & AWI_RXD_ST_LF) {
if (sc->sc_flushpkt) {
sc->sc_flushpkt = 0;
}
else if (sc->sc_nextpkt != NULL) {
struct mbuf *m = sc->sc_nextpkt;
sc->sc_nextpkt = NULL;
sc->sc_flushpkt = 0;
sc->sc_m = NULL;
sc->sc_mptr = NULL;
sc->sc_mleft = 0;
awi_rcv(sc, m, rxd->rxts, rxd->rssi);
}
}
}
rxd->state |= AWI_RXD_ST_CONSUMED;
awi_write_1(sc, cur + AWI_RXD_HOST_DESC_STATE, rxd->state);
next = cur;
if ((rxd->next & AWI_RXD_NEXT_LAST) == 0) {
rxd->state |= AWI_RXD_ST_OWN;
awi_write_1(sc, cur + AWI_RXD_HOST_DESC_STATE, rxd->state);
next = rxd->next;
}
return next;
}
void
awi_dump_rxchain (sc, what, descr)
struct awi_softc *sc;
char *what;
u_int32_t *descr;
{
u_int32_t cur, next;
struct awi_rxd rxd;
cur = *descr;
if (cur & AWI_RXD_NEXT_LAST)
return;
do {
awi_copy_rxd(sc, cur, &rxd);
next = awi_parse_rxd(sc, cur, &rxd);
if ((rxd.state & AWI_RXD_ST_OWN) && (next == cur)) {
printf("%s: loop in rxd list?",
sc->sc_dev.dv_xname);
break;
}
cur = next;
} while (rxd.state & AWI_RXD_ST_OWN);
*descr = cur;
}
void
awi_rxint (sc)
struct awi_softc *sc;
{
awi_dump_rxchain (sc, "mgt", &sc->sc_rx_mgt_desc);
awi_dump_rxchain (sc, "data", &sc->sc_rx_data_desc);
}
void
awi_init_txd (sc, tx, flag, len, rate)
struct awi_softc *sc;
int tx;
int flag;
int len;
int rate;
{
u_int32_t txdbase = sc->sc_txd[tx].descr;
u_int32_t framebase = sc->sc_txd[tx].frame;
u_int32_t nextbase = sc->sc_txd[(tx+1)%sc->sc_ntxd].descr;
awi_write_4 (sc, txdbase + AWI_TXD_START, framebase);
awi_write_4 (sc, txdbase + AWI_TXD_NEXT, nextbase);
awi_write_4 (sc, txdbase + AWI_TXD_LENGTH, len);
awi_write_1 (sc, txdbase + AWI_TXD_RATE, rate);
/* zeroize tail end of txd */
awi_write_4 (sc, txdbase + AWI_TXD_NDA, 0);
awi_write_4 (sc, txdbase + AWI_TXD_NRA, 0);
/* Init state last; firmware keys off of this to know when to start tx */
awi_write_1 (sc, txdbase + AWI_TXD_STATE, flag);
}
void
awi_init_txdescr (sc)
struct awi_softc *sc;
{
int i;
u_int32_t offset = sc->sc_txbase;
sc->sc_txfirst = 0;
sc->sc_txnext = 0;
sc->sc_ntxd = sc->sc_txlen / (AWI_FRAME_SIZE + AWI_TXD_SIZE);
if (sc->sc_ntxd > NTXD) {
sc->sc_ntxd = NTXD;
printf("oops, no, only %d\n", sc->sc_ntxd);
}
/* Allocate TXD's */
for (i=0; i<sc->sc_ntxd; i++) {
sc->sc_txd[i].descr = offset;
offset += AWI_TXD_SIZE;
}
/* now, allocate buffer space to each txd.. */
for (i=0; i<sc->sc_ntxd; i++) {
sc->sc_txd[i].frame = offset;
sc->sc_txd[i].len = AWI_FRAME_SIZE;
offset += AWI_FRAME_SIZE;
}
/* now, initialize the TX descriptors into a circular linked list. */
for (i= 0; i<sc->sc_ntxd; i++) {
awi_init_txd(sc, i, 0, 0, 0);
}
}
void
awi_txint (sc)
struct awi_softc *sc;
{
struct ifnet *ifp = sc->sc_ifp;
int txfirst;
sc->sc_tx_timer = 0;
txfirst = sc->sc_txfirst;
while (sc->sc_txpending > 0) {
u_int8_t flags = awi_read_1 (sc, sc->sc_txd[txfirst].descr +
AWI_TXD_STATE);
if (flags & AWI_TXD_ST_OWN)
break;
if (flags & AWI_TXD_ST_ERROR) {
/* increment oerrs */;
}
txfirst = (txfirst + 1) % sc->sc_ntxd;
sc->sc_txpending--;
}
sc->sc_txfirst = txfirst;
if (sc->sc_txpending < sc->sc_ntxd)
ifp->if_flags &= ~IFF_OACTIVE;
/*
* see which descriptors are done..
*/
awi_start(sc->sc_ifp);
}
/*
* device interrupt routine.
*
* lock out MAC
* loop:
* look at intr status, DTRT.
*
* on tx done, reclaim free buffers from tx, call start.
* on rx done, look at rx queue, copy to mbufs, mark as free,
* hand to ether media layer rx routine.
* on cmd done, call cmd cmpl continuation.
*
*/
int
awi_intr(arg)
void *arg;
{
struct awi_softc *sc = arg;
int handled = 0;
if (sc->sc_state == AWI_ST_OFF) {
u_int8_t intstate = awi_read_intst (sc);
return intstate != 0;
}
/* disable power down, (and implicitly ack interrupt) */
am79c930_gcr_setbits(&sc->sc_chip, AM79C930_GCR_DISPWDN);
awi_write_1(sc, AWI_DIS_PWRDN, 1);
for (;;) {
u_int8_t intstate = awi_read_intst (sc);
if (!intstate)
break;
handled = 1;
if (intstate & AWI_INT_RX)
awi_rxint(sc);
if (intstate & AWI_INT_TX)
awi_txint(sc);
if (intstate & AWI_INT_CMD) {
u_int8_t status;
if (!(sc->sc_flags & AWI_FL_CMD_INPROG))
printf("%s: no command in progress?\n",
sc->sc_dev.dv_xname);
status = awi_read_1(sc, AWI_CMD_STATUS);
awi_write_1 (sc, AWI_CMD, 0);
sc->sc_cmd_timer = 0;
sc->sc_flags &= ~AWI_FL_CMD_INPROG;
if (sc->sc_completion)
(*sc->sc_completion)(sc, status);
}
if (intstate & AWI_INT_SCAN_CMPLT) {
if (sc->sc_flags & AWI_FL_CMD_INPROG) {
panic("i can't take it any more");
}
/*
* scan completion heuristic..
*/
if ((sc->sc_nbindings >= NBND)
|| ((sc->sc_scan_timer == 0) &&
(sc->sc_nbindings > 0)))
awi_try_sync(sc);
else
awi_scan_next(sc);
}
}
/* reenable power down */
am79c930_gcr_clearbits(&sc->sc_chip, AM79C930_GCR_DISPWDN);
awi_write_1(sc, AWI_DIS_PWRDN, 0);
return handled;
}
/*
* flush tx queues..
*/
void
awi_flush(sc)
struct awi_softc *sc;
{
struct ifnet *ifp = sc->sc_ifp;
struct mbuf *m;
do {
IF_DEQUEUE (&sc->sc_mgtq, m);
m_freem(m);
} while (m != NULL);
do {
IF_DEQUEUE (&ifp->if_snd, m);
m_freem(m);
} while (m != NULL);
}
/*
* device stop routine
*/
void
awi_stop(sc)
struct awi_softc *sc;
{
struct ifnet *ifp = sc->sc_ifp;
awi_flush(sc);
/* Turn off timer.. */
ifp->if_timer = 0;
sc->sc_state = AWI_ST_OFF;
(void) awi_read_intst (sc);
/*
* XXX for pcmcia, there's no point in disabling the device,
* as it's about to be powered off..
* for non-PCMCIA attachments, we should, however, stop
* the receiver and transmitter here.
*/
}
/*
* Watchdog routine, triggered by timer.
* This does periodic maintainance-type tasks on the interface.
*/
void
awi_watchdog(ifp)
struct ifnet *ifp;
{
struct awi_softc *sc = ifp->if_softc;
u_int8_t test;
int i;
if (sc->sc_state == AWI_ST_OFF)
/* nothing to do */
return;
else if (sc->sc_state == AWI_ST_INSANE) {
awi_reset(sc);
return;
} else if (sc->sc_state == AWI_ST_SELFTEST) {
/* check for selftest completion.. */
test = awi_read_1(sc, AWI_SELFTEST);
if ((test & 0xf0) == 0xf0) { /* XXX magic numbers */
if (test == AWI_SELFTEST_PASSED) {
awi_init_1(sc);
} else {
printf("%s: selftest failed (code %x)\n",
sc->sc_dev.dv_xname, test);
awi_reset(sc);
}
}
sc->sc_selftest_tries++;
/* still running. try again on next tick */
if (sc->sc_selftest_tries < 5) {
ifp->if_timer = 1;
} else {
/*
* XXX should power down card, wait 1s, power it back
* up again..
*/
printf("%s: device failed to complete selftest (code %x)\n",
sc->sc_dev.dv_xname, test);
ifp->if_timer = 0;
}
return;
}
/*
* command timer: if it goes to zero, device failed to respond.
* boot to the head.
*/
if (sc->sc_cmd_timer) {
sc->sc_cmd_timer--;
if (sc->sc_cmd_timer == 0) {
sc->sc_flags &= ~AWI_FL_CMD_INPROG;
printf("%s: timeout waiting for command completion\n",
sc->sc_dev.dv_xname);
test = awi_read_1(sc, AWI_CMD_STATUS);
printf("%s: cmd status: %x\n", sc->sc_dev.dv_xname, test);
test = awi_read_1(sc, AWI_CMD);
printf("%s: cmd: %x\n", sc->sc_dev.dv_xname, test);
awi_card_hexdump(sc, "CSB", AWI_CSB, 16);
awi_reset(sc);
return;
}
}
/*
* Transmit timer. If it goes to zero, device failed to deliver a
* tx complete interrupt. boot to the head.
*/
if (sc->sc_tx_timer) {
sc->sc_tx_timer--;
if ((sc->sc_tx_timer == 0) && (sc->sc_txpending)) {
awi_card_hexdump(sc, "CSB", AWI_CSB, 16);
printf("%s: transmit timeout\n", sc->sc_dev.dv_xname);
awi_card_hexdump(sc, "last_txd", AWI_LAST_TXD, 5*4);
for (i=0; i<sc->sc_ntxd; i++) {
awi_card_hexdump(sc, "txd",
sc->sc_txd[i].descr, AWI_TXD_SIZE);
}
awi_reset(sc);
return;
}
}
/*
* Scan timer.
* When synched, this is used to notice when we've stopped
* receiving beacons and should attempt to resynch.
*
* When unsynched, this is used to notice if we've received an
* interesting probe response and should synch up.
*/
if (sc->sc_scan_timer) {
sc->sc_scan_timer--;
if (sc->sc_scan_timer == 0) {
if (sc->sc_state == AWI_ST_SCAN) {
/*
* XXX what if device fails to deliver
* a scan-completion interrupt?
*/
} else {
printf("%s: no recent beacon from %s; rescanning\n",
sc->sc_dev.dv_xname,
ether_sprintf(sc->sc_active_bss.bss_id));
awi_restart_scan(sc);
}
}
}
/*
* Management timer. Used to know when to send auth
* requests and associate requests.
*/
if (sc->sc_mgt_timer) {
sc->sc_mgt_timer--;
if (sc->sc_mgt_timer == 0) {
switch (sc->sc_state)
{
case AWI_ST_SYNCED:
case AWI_ST_RUNNING:
sc->sc_state = AWI_ST_SYNCED;
awi_send_authreq(sc);
break;
case AWI_ST_AUTHED:
awi_send_assocreq(sc);
break;
default:
printf("weird state for mgt timeout!\n");
break;
}
}
}
awi_set_timer(sc);
}
void
awi_set_mc (sc)
struct awi_softc *sc;
{
/* XXX not implemented yet.. */
}
/*
* init routine
*/
/*
* ioctl routine
* SIOCSIFADDR sets IFF_UP
* SIOCIFMTU
* SIOCSIFFLAGS
* SIOCADDMULTI/SIOCDELMULTI
*/
int
awi_ioctl(ifp, cmd, data)
register struct ifnet *ifp;
u_long cmd;
caddr_t data;
{
struct awi_softc *sc = ifp->if_softc;
struct ifaddr *ifa = (struct ifaddr *)data;
struct ifreq *ifr = (struct ifreq *)data;
int s, error = 0;
s = splnet();
switch (cmd) {
case SIOCSIFADDR:
if ((error = awi_enable(sc)) != 0)
break;
ifp->if_flags |= IFF_UP;
/* XXX other AF support: inet6, NS, ... */
switch (ifa->ifa_addr->sa_family) {
#ifdef INET
case AF_INET:
arp_ifinit(sc->sc_ifp, ifa);
break;
#endif
default:
break;
}
break;
case SIOCSIFFLAGS:
if ((ifp->if_flags & IFF_UP) == 0 &&
(sc->sc_state != AWI_ST_OFF)) {
/*
* If interface is marked down and it is enabled, then
* stop it.
*/
ifp->if_flags &= ~IFF_RUNNING;
awi_stop(sc);
awi_disable(sc);
} else if ((ifp->if_flags & IFF_UP) != 0 &&
(ifp->if_flags & IFF_RUNNING) == 0) {
/*
* If interface is marked up and it is stopped, then
* start it.
*/
if ((error = awi_enable(sc)) != 0)
break;
} else if ((ifp->if_flags & IFF_UP) != 0) {
/*
* Deal with other flags that change hardware
* state, i.e. IFF_PROMISC.
*/
awi_set_mc(sc);
}
break;
case SIOCADDMULTI:
case SIOCDELMULTI:
error = (cmd == SIOCADDMULTI) ?
ether_addmulti(ifr, &sc->sc_ec) :
ether_delmulti(ifr, &sc->sc_ec);
if (error == ENETRESET) {
error = 0;
awi_set_mc(sc);
}
break;
default:
error = EINVAL;
break;
}
splx(s);
return error;
}
int awi_activate (self, act)
struct device *self;
enum devact act;
{
int s = splnet();
panic("awi_activate");
#if 0
switch (act) {
case DVACT_ACTIVATE:
rv = EOPNOTSUPP;
break;
case DVACT_DEACTIVATE:
#ifdef notyet
/* First, kill off the interface. */
if_detach(sc->sc_ethercom.ec_if);
#endif
/* Now disable the interface. */
awidisable(sc);
break;
}
#endif
splx(s);
}
int
awi_drop_output (ifp, m0, dst, rt0)
struct ifnet *ifp;
struct mbuf *m0;
struct sockaddr *dst;
struct rtentry *rt0;
{
m_freem(m0);
return 0;
}
void
awi_drop_input (ifp, m0)
struct ifnet *ifp;
struct mbuf *m0;
{
m_freem(m0);
}
int awi_attach (sc, macaddr)
struct awi_softc *sc;
u_int8_t *macaddr;
{
struct ifnet *ifp = &sc->sc_ec.ec_if;
u_int8_t version[AWI_BANNER_LEN];
sc->sc_ifp = ifp;
sc->sc_nextpkt = NULL;
sc->sc_m = NULL;
sc->sc_mptr = NULL;
sc->sc_mleft = 0;
sc->sc_flushpkt = 0;
awi_read_bytes (sc, AWI_BANNER, version, AWI_BANNER_LEN);
printf("%s: firmware %s\n", sc->sc_dev.dv_xname, version);
memcpy(sc->sc_my_addr, macaddr, ETHER_ADDR_LEN);
printf("%s: 802.11 address %s\n", sc->sc_dev.dv_xname,
ether_sprintf(sc->sc_my_addr));
memcpy(ifp->if_xname, sc->sc_dev.dv_xname, IFNAMSIZ);
ifp->if_softc = sc;
ifp->if_start = awi_start;
ifp->if_ioctl = awi_ioctl;
ifp->if_watchdog = awi_watchdog;
ifp->if_mtu = ETHERMTU;
/* XXX simplex may not be correct here.. */
ifp->if_flags =
IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS | IFF_MULTICAST;
sc->sc_mgtq.ifq_maxlen = 5;
if_attach(ifp);
ether_ifattach(ifp, sc->sc_my_addr);
ifp->if_hdrlen = 32; /* 802.11 headers are bigger.. */
#if NBPFILTER > 0
bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB, sizeof(struct ether_header));
#endif
return 0;
}
void
awi_zero (sc, from, to)
struct awi_softc *sc;
u_int32_t from, to;
{
u_int32_t i;
for (i=from; i<to; i++)
awi_write_1(sc, i, 0);
}
void
awi_init (sc)
struct awi_softc *sc;
{
struct ifnet *ifp = sc->sc_ifp;
sc->sc_scan_duration = 100; /* scan for 100ms */
/*
* Maybe we should randomize these....
*/
sc->sc_scan_chanset = IEEEWL_FH_CHANSET_MIN;
sc->sc_scan_pattern = IEEEWL_FH_PATTERN_MIN;
sc->sc_flags &= ~AWI_FL_CMD_INPROG;
ifp->if_flags &= ~(IFF_RUNNING|IFF_OACTIVE);
ifp->if_timer = 0;
sc->sc_cmd_timer = 0;
sc->sc_tx_timer = 0;
sc->sc_mgt_timer = 0;
sc->sc_scan_timer = 0;
sc->sc_nbindings = 0;
/*
* this reset sequence doesn't seem to always do the trick.
* hard-power-cycling the card may do it..
*/
/*
* reset the hardware, just to be sure.
* (bring out the big hammer here..)
*/
/* XXX insert delay here? */
am79c930_gcr_setbits (&sc->sc_chip, AM79C930_GCR_CORESET);
delay(10); /* XXX arbitrary value */
/*
* clear control memory regions (firmware should do this but...)
*/
awi_zero(sc, AWI_LAST_TXD, AWI_BUFFERS);
awi_drvstate(sc, AWI_DRV_RESET);
sc->sc_selftest_tries = 0;
/*
* release reset
*/
am79c930_gcr_clearbits (&sc->sc_chip, AM79C930_GCR_CORESET);
delay(10);
sc->sc_state = AWI_ST_SELFTEST;
ifp->if_timer = 1;
}
void
awi_cmd (sc, opcode)
struct awi_softc *sc;
u_int8_t opcode;
{
if (sc->sc_flags & AWI_FL_CMD_INPROG)
panic("%s: command reentered", sc->sc_dev.dv_xname);
sc->sc_flags |= AWI_FL_CMD_INPROG;
/* issue test-interface command */
awi_write_1(sc, AWI_CMD, opcode);
awi_write_1(sc, AWI_CMD_STATUS, 0);
sc->sc_cmd_timer = 2;
awi_set_timer(sc);
}
void
awi_cmd_test_if (sc)
struct awi_softc *sc;
{
awi_cmd (sc, AWI_CMD_NOP);
}
void
awi_cmd_get_mib (sc, var, offset, len)
struct awi_softc *sc;
u_int8_t var;
u_int8_t offset;
u_int8_t len;
{
awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_TYPE, var);
awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_SIZE, len);
awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_INDEX, offset);
awi_cmd (sc, AWI_CMD_GET_MIB);
}
void
awi_cmd_txinit (sc)
struct awi_softc *sc;
{
awi_write_4(sc, AWI_CMD_PARAMS+AWI_CA_TX_DATA, sc->sc_txbase);
awi_write_4(sc, AWI_CMD_PARAMS+AWI_CA_TX_MGT, 0);
awi_write_4(sc, AWI_CMD_PARAMS+AWI_CA_TX_BCAST, 0);
awi_write_4(sc, AWI_CMD_PARAMS+AWI_CA_TX_PS, 0);
awi_write_4(sc, AWI_CMD_PARAMS+AWI_CA_TX_CF, 0);
awi_cmd (sc, AWI_CMD_INIT_TX);
}
int awi_max_chan = -1;
int awi_min_chan = 1000;
int awi_max_pattern = -1;
int awi_min_pattern = 1000;
/*
* timeout-driven routine: complete device init once device has passed
* selftest.
*/
void awi_init_1 (sc)
struct awi_softc *sc;
{
struct ifnet *ifp = sc->sc_ifp;
awi_intrinit(sc);
sc->sc_state = AWI_ST_IFTEST;
if (ifp->if_flags & IFF_DEBUG) {
awi_card_hexdump(sc, "init_1 CSB", AWI_CSB, 16);
sc->sc_completion = awi_mibdump;
} else
sc->sc_completion = awi_init_2;
sc->sc_curmib = 0;
awi_cmd_test_if (sc);
}
void awi_mibdump (sc, status)
struct awi_softc *sc;
u_int8_t status;
{
u_int8_t mibblk[256];
if (status != AWI_STAT_OK) {
printf("%s: pre-mibread failed (card unhappy?)\n",
sc->sc_dev.dv_xname);
awi_reset(sc);
return;
}
if (sc->sc_curmib != 0) {
awi_read_bytes(sc, AWI_CMD_PARAMS+AWI_CA_MIB_DATA,
mibblk, 72);
awi_hexdump("mib", mibblk, 72);
}
if (sc->sc_curmib > AWI_MIB_LAST) {
awi_init_2 (sc, status);
} else {
sc->sc_completion = awi_mibdump;
printf("mib %d\n", sc->sc_curmib);
awi_cmd_get_mib (sc, sc->sc_curmib, 0, 30);
sc->sc_curmib++;
/* skip over reserved MIB's.. */
if ((sc->sc_curmib == 1) || (sc->sc_curmib == 6))
sc->sc_curmib++;
}
}
/*
* called on completion of test-interface command in first-stage init.
*/
void awi_init_2 (sc, status)
struct awi_softc *sc;
u_int8_t status;
{
/* did it succeed? */
if (status != AWI_STAT_OK) {
printf("%s: nop failed (card unhappy?)\n",
sc->sc_dev.dv_xname);
awi_reset(sc);
}
sc->sc_state = AWI_ST_MIB_GET;
sc->sc_completion = awi_init_read_bufptrs_done;
awi_cmd_get_mib (sc, AWI_MIB_LOCAL, 0, AWI_MIB_LOCAL_SIZE);
}
void awi_init_read_bufptrs_done (sc, status)
struct awi_softc *sc;
u_int8_t status;
{
if (status != AWI_STAT_OK) {
printf("%s: get_mib failed (card unhappy?)\n",
sc->sc_dev.dv_xname);
awi_reset(sc);
}
sc->sc_txbase = awi_read_4 (sc,
AWI_CMD_PARAMS+AWI_CA_MIB_DATA+AWI_MIB_LOCAL_TXB_OFFSET);
sc->sc_txlen = awi_read_4 (sc,
AWI_CMD_PARAMS+AWI_CA_MIB_DATA+AWI_MIB_LOCAL_TXB_SIZE);
sc->sc_rxbase = awi_read_4 (sc,
AWI_CMD_PARAMS+AWI_CA_MIB_DATA+AWI_MIB_LOCAL_RXB_OFFSET);
sc->sc_rxlen = awi_read_4 (sc,
AWI_CMD_PARAMS+AWI_CA_MIB_DATA+AWI_MIB_LOCAL_RXB_SIZE);
/*
* XXX consider repartitioning buffer space to allow for
* more efficient usage.
* 6144: 3 txds, 1476 waste (current partition)
* better splits:
* 4864: 3 txds, 196 waste
* 6400: 4 txds, 176 waste
* 7936: 5 txds, 156 waste
*/
#if 0
printf("tx offset: %x\n", sc->sc_txbase);
printf("tx size: %x\n", sc->sc_txlen);
printf("rx offset: %x\n", sc->sc_rxbase);
printf("rx size: %x\n", sc->sc_rxlen);
#endif
sc->sc_state = AWI_ST_MIB_SET;
awi_cmd_set_notap(sc);
}
void awi_cmd_set_notap (sc)
struct awi_softc *sc;
{
awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_TYPE, AWI_MIB_LOCAL);
awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_SIZE, 1);
awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_INDEX,
AWI_MIB_LOCAL_ACTING_AS_AP);
awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_DATA, 0);
sc->sc_completion = awi_cmd_set_notap_done;
awi_cmd (sc, AWI_CMD_SET_MIB);
}
void awi_cmd_set_notap_done (sc, status)
struct awi_softc *sc;
u_int8_t status;
{
if (status != AWI_STAT_OK) {
int erroffset = awi_read_1 (sc, AWI_ERROR_OFFSET);
printf("%s: set_infra failed (card unhappy?); erroffset %d\n",
sc->sc_dev.dv_xname,
erroffset);
awi_reset(sc);
return;
}
awi_cmd_set_infra (sc);
}
void awi_cmd_set_infra (sc)
struct awi_softc *sc;
{
awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_TYPE, AWI_MIB_LOCAL);
awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_SIZE, 1);
awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_INDEX,
AWI_MIB_LOCAL_INFRA_MODE);
awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_DATA, 1);
sc->sc_completion = awi_cmd_set_infra_done;
awi_cmd (sc, AWI_CMD_SET_MIB);
}
void awi_cmd_set_infra_done (sc, status)
struct awi_softc *sc;
u_int8_t status;
{
#if 0
printf("set_infra done\n");
#endif
if (status != AWI_STAT_OK) {
int erroffset = awi_read_1 (sc, AWI_ERROR_OFFSET);
printf("%s: set_infra failed (card unhappy?); erroffset %d\n",
sc->sc_dev.dv_xname,
erroffset);
awi_reset(sc);
return;
}
#if 0
printf("%s: set_infra done\n", sc->sc_dev.dv_xname);
#endif
awi_cmd_set_allmulti (sc);
}
void awi_cmd_set_allmulti (sc)
struct awi_softc *sc;
{
awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_TYPE, AWI_MIB_LOCAL);
awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_SIZE, 1);
awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_INDEX,
AWI_MIB_LOCAL_FILTMULTI);
awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_DATA, 0);
sc->sc_completion = awi_cmd_set_allmulti_done;
awi_cmd (sc, AWI_CMD_SET_MIB);
}
void awi_cmd_set_allmulti_done (sc, status)
struct awi_softc *sc;
u_int8_t status;
{
if (status != AWI_STAT_OK) {
int erroffset = awi_read_1 (sc, AWI_ERROR_OFFSET);
printf("%s: set_almulti_done failed (card unhappy?); erroffset %d\n",
sc->sc_dev.dv_xname,
erroffset);
awi_reset(sc);
return;
}
awi_cmd_set_promisc (sc);
}
void awi_cmd_set_promisc (sc)
struct awi_softc *sc;
{
awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_TYPE, AWI_MIB_MAC);
awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_SIZE, 1);
awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_INDEX,
AWI_MIB_MAC_PROMISC);
awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_DATA, 0); /* XXX */
sc->sc_completion = awi_cmd_set_promisc_done;
awi_cmd (sc, AWI_CMD_SET_MIB);
}
void awi_cmd_set_promisc_done (sc, status)
struct awi_softc *sc;
u_int8_t status;
{
#if 0
printf("set promisc_done\n");
#endif
if (status != AWI_STAT_OK) {
int erroffset = awi_read_1 (sc, AWI_ERROR_OFFSET);
printf("%s: set_promisc_done failed (card unhappy?); erroffset %d\n",
sc->sc_dev.dv_xname,
erroffset);
awi_reset(sc);
return;
}
#if 0
printf("%s: set_promisc done\n", sc->sc_dev.dv_xname);
#endif
awi_init_txdescr(sc);
sc->sc_state = AWI_ST_TXINIT;
sc->sc_completion = awi_init_4;
awi_cmd_txinit(sc);
}
void
awi_init_4 (sc, status)
struct awi_softc *sc;
u_int8_t status;
{
#if 0
printf("%s: awi_init_4, st %x\n", sc->sc_dev.dv_xname, status);
awi_card_hexdump(sc, "init_4 CSB", AWI_CSB, 16);
#endif
if (status != AWI_STAT_OK) {
int erroffset = awi_read_1 (sc, AWI_ERROR_OFFSET);
printf("%s: init_tx failed (card unhappy?); erroffset %d\n",
sc->sc_dev.dv_xname,
erroffset);
awi_reset(sc);
return;
}
sc->sc_state = AWI_ST_RXINIT;
sc->sc_completion = awi_init_5;
awi_cmd (sc, AWI_CMD_INIT_RX);
}
void awi_init_5 (sc, status)
struct awi_softc *sc;
u_int8_t status;
{
#if 0
struct ifnet *ifp = sc->sc_ifp;
#endif
#if 0
printf("%s: awi_init_5, st %x\n", sc->sc_dev.dv_xname, status);
awi_card_hexdump(sc, "init_5 CSB", AWI_CSB, 16);
#endif
if (status != AWI_STAT_OK) {
printf("%s: init_rx failed (card unhappy?)\n",
sc->sc_dev.dv_xname);
awi_reset(sc);
return;
}
sc->sc_rx_data_desc = awi_read_4(sc, AWI_CMD_PARAMS+AWI_CA_IRX_DATA_DESC);
sc->sc_rx_mgt_desc = awi_read_4(sc, AWI_CMD_PARAMS+AWI_CA_IRX_PS_DESC);
#if 0
printf("%s: data desc %x, mgt desc %x\n", sc->sc_dev.dv_xname,
sc->sc_rx_data_desc, sc->sc_rx_mgt_desc);
#endif
awi_restart_scan(sc);
}
void awi_restart_scan (sc)
struct awi_softc *sc;
{
if (sc->sc_ifp->if_flags & IFF_DEBUG) {
printf("%s: starting scan\n", sc->sc_dev.dv_xname);
}
sc->sc_scan_timer = 2;
sc->sc_mgt_timer = 0;
awi_set_timer(sc);
sc->sc_nbindings = 0;
sc->sc_state = AWI_ST_SCAN;
awi_drvstate (sc, AWI_DRV_INFSC);
awi_cmd_scan (sc);
}
void
awi_cmd_scan (sc)
struct awi_softc *sc;
{
awi_write_2 (sc, AWI_CMD_PARAMS+AWI_CA_SCAN_DURATION,
sc->sc_scan_duration);
awi_write_1 (sc, AWI_CMD_PARAMS+AWI_CA_SCAN_SET,
sc->sc_scan_chanset);
awi_write_1 (sc, AWI_CMD_PARAMS+AWI_CA_SCAN_PATTERN,
sc->sc_scan_pattern);
awi_write_1 (sc, AWI_CMD_PARAMS+AWI_CA_SCAN_IDX, 1);
awi_write_1 (sc, AWI_CMD_PARAMS+AWI_CA_SCAN_SUSP, 0);
sc->sc_completion = awi_cmd_scan_done;
awi_cmd (sc, AWI_CMD_SCAN);
}
void
awi_cmd_scan_done (sc, status)
struct awi_softc *sc;
u_int8_t status;
{
#if 0
int erroffset;
#endif
if (status == AWI_STAT_OK) {
if (sc->sc_scan_chanset > awi_max_chan)
awi_max_chan = sc->sc_scan_chanset;
if (sc->sc_scan_chanset < awi_min_chan)
awi_min_chan = sc->sc_scan_chanset;
if (sc->sc_scan_pattern > awi_max_pattern)
awi_max_pattern = sc->sc_scan_pattern;
if (sc->sc_scan_pattern < awi_min_pattern)
awi_min_pattern = sc->sc_scan_pattern;
return;
}
#if 0
erroffset = awi_read_1 (sc, AWI_ERROR_OFFSET);
printf("%s: scan failed; erroffset %d\n", sc->sc_dev.dv_xname,
erroffset);
#endif
/* wait for response or scan timeout.. */
}
void
awi_scan_next (sc)
struct awi_softc *sc;
{
sc->sc_scan_pattern++;
if (sc->sc_scan_pattern > IEEEWL_FH_PATTERN_MAX) {
sc->sc_scan_pattern = IEEEWL_FH_PATTERN_MIN;
sc->sc_scan_chanset++;
if (sc->sc_scan_chanset > IEEEWL_FH_CHANSET_MAX)
sc->sc_scan_chanset = IEEEWL_FH_CHANSET_MIN;
}
#if 0
printf("scan: pattern %x chanset %x\n", sc->sc_scan_pattern,
sc->sc_scan_chanset);
#endif
awi_cmd_scan(sc);
}
void
awi_try_sync (sc)
struct awi_softc *sc;
{
int max_rssi = 0, best = 0;
int i;
struct awi_bss_binding *bp = NULL;
awi_flush(sc);
if (sc->sc_ifp->if_flags & IFF_DEBUG) {
printf("%s: looking for best of %d\n",
sc->sc_dev.dv_xname, sc->sc_nbindings);
}
/* pick one with best rssi */
for (i=0; i<sc->sc_nbindings; i++) {
bp = &sc->sc_bindings[i];
if (bp->rssi > max_rssi) {
max_rssi = bp->rssi;
best = i;
}
}
if (bp == NULL) {
printf("%s: no beacons seen\n", sc->sc_dev.dv_xname);
awi_scan_next(sc);
return;
}
if (sc->sc_ifp->if_flags & IFF_DEBUG) {
printf("%s: best %d\n", sc->sc_dev.dv_xname, best);
}
sc->sc_scan_timer = awi_scan_keepalive;
bp = &sc->sc_bindings[best];
memcpy(&sc->sc_active_bss, bp, sizeof(*bp));
sc->sc_new_bss = 1;
awi_write_1 (sc, AWI_CMD_PARAMS+AWI_CA_SYNC_SET, bp->chanset);
awi_write_1 (sc, AWI_CMD_PARAMS+AWI_CA_SYNC_PATTERN, bp->pattern);
awi_write_1 (sc, AWI_CMD_PARAMS+AWI_CA_SYNC_IDX, bp->index);
awi_write_1 (sc, AWI_CMD_PARAMS+AWI_CA_SYNC_STARTBSS, 0);
awi_write_2 (sc, AWI_CMD_PARAMS+AWI_CA_SYNC_DWELL, bp->dwell_time);
awi_write_2 (sc, AWI_CMD_PARAMS+AWI_CA_SYNC_MBZ, 0);
awi_write_bytes (sc, AWI_CMD_PARAMS+AWI_CA_SYNC_TIMESTAMP,
bp->bss_timestamp, 8);
awi_write_4 (sc, AWI_CMD_PARAMS+AWI_CA_SYNC_REFTIME, bp->rxtime);
sc->sc_completion = awi_cmd_sync_done;
awi_cmd (sc, AWI_CMD_SYNC);
}
void
awi_cmd_sync_done (sc, status)
struct awi_softc *sc;
u_int8_t status;
{
if (status != AWI_STAT_OK) {
int erroffset = awi_read_1 (sc, AWI_ERROR_OFFSET);
printf("%s: sync_done failed (card unhappy?); erroffset %d\n",
sc->sc_dev.dv_xname,
erroffset);
awi_reset(sc);
return;
}
/*
* at this point, the card should be synchronized with the AP
* we heard from. tell the card what BSS and ESS it's running in..
*/
awi_drvstate (sc, AWI_DRV_INFSY);
if (sc->sc_ifp->if_flags & IFF_DEBUG) {
printf("%s: sync done, setting bss/iss parameters\n",
sc->sc_dev.dv_xname);
awi_hexdump ("bss", sc->sc_active_bss.bss_id, ETHER_ADDR_LEN);
printf("ssid: %s\n", sc->sc_active_bss.ssid);
}
awi_cmd_set_ss (sc);
}
void awi_cmd_set_ss (sc)
struct awi_softc *sc;
{
awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_TYPE, AWI_MIB_MAC_MGT);
awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_SIZE,
ETHER_ADDR_LEN + AWI_MIB_MGT_ESS_SIZE);
awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_INDEX,
AWI_MIB_MGT_BSS_ID);
awi_write_bytes(sc, AWI_CMD_PARAMS+AWI_CA_MIB_DATA,
sc->sc_active_bss.bss_id, ETHER_ADDR_LEN);
awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_DATA+ETHER_ADDR_LEN,
0); /* XXX */
awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_DATA+ETHER_ADDR_LEN+1,
sc->sc_active_bss.sslen);
awi_write_bytes(sc, AWI_CMD_PARAMS+AWI_CA_MIB_DATA+8,
sc->sc_active_bss.ssid, AWI_MIB_MGT_ESS_SIZE-2);
sc->sc_completion = awi_cmd_set_ss_done;
awi_cmd (sc, AWI_CMD_SET_MIB);
}
void awi_cmd_set_ss_done (sc, status)
struct awi_softc *sc;
u_int8_t status;
{
if (status != AWI_STAT_OK) {
int erroffset = awi_read_1 (sc, AWI_ERROR_OFFSET);
printf("%s: set_ss_done failed (card unhappy?); erroffset %d\n",
sc->sc_dev.dv_xname,
erroffset);
awi_reset(sc);
return;
}
#if 0
printf("%s: set_ss done\n", sc->sc_dev.dv_xname);
#endif
awi_running (sc);
/*
* now, we *should* be getting broadcast frames..
*/
sc->sc_state = AWI_ST_SYNCED;
awi_send_authreq (sc);
}
void awi_running (sc)
struct awi_softc *sc;
{
struct ifnet *ifp = sc->sc_ifp;
/*
* Who knows what it is to be running?
* Only he who is running knows..
*/
ifp->if_flags |= IFF_RUNNING;
awi_start(ifp);
}
void awi_reset (sc)
struct awi_softc *sc;
{
printf("%s: reset\n", sc->sc_dev.dv_xname);
}