/* $NetBSD: awi.c,v 1.9 2000/02/17 15:58:33 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 #include #include #include #include #include #include #include #include #include #if NRND > 0 #include #endif #include #include #include #include #ifdef INET #include #include #include #include #include #endif #ifdef NS #include #include #endif #if NBPFILTER > 0 #include #include #endif #include #include #include #include #include #include #include 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)); 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<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; isc_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; isc_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<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; isc_ntxd; i++) { sc->sc_txd[i].descr = offset; offset += AWI_TXD_SIZE; } /* now, allocate buffer space to each txd.. */ for (i=0; isc_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; isc_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; isc_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; { struct awi_softc *sc = (struct awi_softc *)self; struct ifnet *ifp = sc->sc_ifp; int s = splnet(); int rv = 0; switch (act) { case DVACT_ACTIVATE: rv = EOPNOTSUPP; break; case DVACT_DEACTIVATE: awi_disable(sc); if_deactivate(ifp); break; } splx(s); return rv; } 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; } int awi_detach (sc) struct awi_softc *sc; { struct ifnet *ifp = sc->sc_ifp; #if NBPFILTER > 0 bpfdetach(ifp); #endif ether_ifdetach(ifp); if_detach(ifp); return 0; } void awi_zero (sc, from, to) struct awi_softc *sc; u_int32_t from, to; { u_int32_t i; for (i=from; isc_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; isc_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); }