diff --git a/sys/dev/ic/an.c b/sys/dev/ic/an.c index 6c90df03d8a8..e6f8958f373e 100644 --- a/sys/dev/ic/an.c +++ b/sys/dev/ic/an.c @@ -1,4 +1,4 @@ -/* $NetBSD: an.c,v 1.14 2001/05/28 00:46:35 onoe Exp $ */ +/* $NetBSD: an.c,v 1.15 2001/06/21 12:49:06 onoe Exp $ */ /* * Copyright (c) 1997, 1998, 1999 * Bill Paul . All rights reserved. @@ -94,7 +94,11 @@ #include "bpfilter.h" #ifdef INET -#define ANCACHE /* enable signal strength cache */ +/* + * It is designed for IPv4 only. + * no one use it and disabled for now. -- onoe + */ +#undef ANCACHE /* enable signal strength cache */ #endif #include @@ -106,6 +110,7 @@ #include #include #include +#include #ifdef ANCACHE #include #include @@ -144,6 +149,12 @@ static const char rcsid[] = static void an_reset __P((struct an_softc *)); static void an_wait __P((struct an_softc *)); static int an_ioctl __P((struct ifnet *, u_long, caddr_t)); +static int an_set_nwkey __P((struct an_softc *, + struct ieee80211_nwkey *)); +static int an_get_nwkey __P((struct an_softc *, + struct ieee80211_nwkey *)); +static int an_write_wepkey __P((struct an_softc *sc, int type, + struct an_wepkey *keys, int kid)); static int an_init __P((struct ifnet *)); static void an_stop __P((struct ifnet *, int)); static int an_init_tx_ring __P((struct an_softc *)); @@ -152,7 +163,6 @@ static void an_watchdog __P((struct ifnet *)); static void an_rxeof __P((struct an_softc *)); static void an_txeof __P((struct an_softc *, int)); -static void an_promisc __P((struct an_softc *, int)); static int an_cmd __P((struct an_softc *, int, int)); static int an_read_record __P((struct an_softc *, struct an_ltv_gen *)); static int an_write_record __P((struct an_softc *, struct an_ltv_gen *)); @@ -163,7 +173,7 @@ static int an_write_data __P((struct an_softc *, int, static int an_seek __P((struct an_softc *, int, int, int)); static int an_alloc_nicmem __P((struct an_softc *, int, int *)); static void an_stats_update __P((void *)); -static void an_setdef __P((struct an_softc *, struct an_req *)); +static int an_setdef __P((struct an_softc *, struct an_req *)); #ifdef ANCACHE static void an_cache_store __P((struct an_softc *, struct ether_header *, struct mbuf *, unsigned short)); @@ -176,8 +186,9 @@ static void an_media_status __P((struct ifnet *ifp, struct ifmediareq *imr)); int an_attach(struct an_softc *sc) { - struct ifnet *ifp = &sc->arpcom.ec_if; + struct ifnet *ifp = &sc->arpcom.ec_if; int i, s; + struct an_ltv_wepkey *akey; #ifdef IFM_IEEE80211 int mtype; struct ifmediareq imr; @@ -230,12 +241,31 @@ an_attach(struct an_softc *sc) return(EIO); } + /* Read WEP settings from persistent memory */ + akey = (struct an_ltv_wepkey *)&sc->an_reqbuf; + akey->an_type = AN_RID_WEP_VOLATILE; + akey->an_len = sizeof(struct an_ltv_wepkey); + while (an_read_record(sc, (struct an_ltv_gen *)akey) == 0) { + if (akey->an_key_index == 0xffff) { + sc->an_tx_perskey = akey->an_mac_addr[0]; + sc->an_tx_key = -1; + break; + } + if (akey->an_key_index >= IEEE80211_WEP_NKID) + break; + sc->an_perskeylen[akey->an_key_index] = akey->an_key_len; + sc->an_wepkeys[akey->an_key_index].an_wep_keylen = -1; + akey->an_type = AN_RID_WEP_PERSISTENT; /* for next key */ + akey->an_len = sizeof(*akey); + } + /* XXX not sure if persistent key settings should be printed here */ + printf("%s: 802.11 address: %s\n", sc->an_dev.dv_xname, ether_sprintf(sc->an_caps.an_oemaddr)); ifp->if_softc = sc; - ifp->if_flags = - IFF_BROADCAST | IFF_NOTRAILERS | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_flags = IFF_BROADCAST | IFF_NOTRAILERS | IFF_SIMPLEX | + IFF_MULTICAST | IFF_ALLMULTI; ifp->if_ioctl = an_ioctl; ifp->if_start = an_start; ifp->if_init = an_init; @@ -245,19 +275,21 @@ an_attach(struct an_softc *sc) memcpy(ifp->if_xname, sc->an_dev.dv_xname, IFNAMSIZ); - bzero(sc->an_config.an_nodename, sizeof(sc->an_config.an_nodename)); - bcopy(AN_DEFAULT_NODENAME, sc->an_config.an_nodename, + memset(sc->an_config.an_nodename, 0, sizeof(sc->an_config.an_nodename)); + memcpy(sc->an_config.an_nodename, AN_DEFAULT_NODENAME, sizeof(AN_DEFAULT_NODENAME) - 1); - bzero(sc->an_ssidlist.an_ssid1, sizeof(sc->an_ssidlist.an_ssid1)); - bcopy(AN_DEFAULT_NETNAME, sc->an_ssidlist.an_ssid1, + memset(sc->an_ssidlist.an_ssid1, 0, sizeof(sc->an_ssidlist.an_ssid1)); + memcpy(sc->an_ssidlist.an_ssid1, AN_DEFAULT_NETNAME, sizeof(AN_DEFAULT_NETNAME) - 1); sc->an_ssidlist.an_ssid1_len = strlen(AN_DEFAULT_NETNAME); sc->an_config.an_opmode = AN_OPMODE_INFRASTRUCTURE_STATION; sc->an_tx_rate = 0; - bzero((char *)&sc->an_stats, sizeof(sc->an_stats)); +#if 0 + memset(&sc->an_stats, 0, sizeof(sc->an_stats)); +#endif /* * Call MI attach routine. @@ -345,16 +377,14 @@ an_power(int why, void *arg) struct an_softc *sc = arg; struct ifnet *ifp = &sc->arpcom.ec_if; - if (!sc->sc_enabled) - return; - s = splnet(); switch (why) { case PWR_SUSPEND: case PWR_STANDBY: - an_stop(ifp, 0); + an_stop(ifp, 1); break; case PWR_RESUME: + an_init(ifp); break; case PWR_SOFTSUSPEND: case PWR_SOFTSTANDBY: @@ -367,672 +397,75 @@ an_power(int why, void *arg) void an_shutdown(void *arg) { - struct an_softc *sc = arg; + struct an_softc *sc = arg; an_stop(&sc->arpcom.ec_if, 1); return; } -static void an_rxeof(sc) - struct an_softc *sc; -{ - struct ifnet *ifp; - struct ether_header *eh; -#ifdef ANCACHE - struct an_rxframe rx_frame; -#endif - struct an_rxframe_802_3 rx_frame_802_3; - struct mbuf *m; - int id, error = 0; - - ifp = &sc->arpcom.ec_if; - - id = CSR_READ_2(sc, AN_RX_FID); - - MGETHDR(m, M_DONTWAIT, MT_DATA); - if (m == NULL) { - ifp->if_ierrors++; - return; - } - MCLGET(m, M_DONTWAIT); - if (!(m->m_flags & M_EXT)) { - m_freem(m); - ifp->if_ierrors++; - return; - } - - m->m_pkthdr.rcvif = ifp; - - /* Align the data after the ethernet header */ - m->m_data = (caddr_t) ALIGN(m->m_data + sizeof(struct ether_header)) - - sizeof(struct ether_header); - - eh = mtod(m, struct ether_header *); - -#ifdef ANCACHE - /* Read NIC frame header */ - if (an_read_data(sc, id, 0, (caddr_t)&rx_frame, sizeof(rx_frame))) { - ifp->if_ierrors++; - return; - } -#endif - /* Read in the 802_3 frame header */ - if (an_read_data(sc, id, 0x34, (caddr_t)&rx_frame_802_3, - sizeof(rx_frame_802_3))) { - ifp->if_ierrors++; - return; - } - - if (rx_frame_802_3.an_rx_802_3_status != 0) { - ifp->if_ierrors++; - return; - } - - /* Check for insane frame length */ - if (rx_frame_802_3.an_rx_802_3_payload_len > MCLBYTES) { - ifp->if_ierrors++; - return; - } - - m->m_pkthdr.len = m->m_len = - rx_frame_802_3.an_rx_802_3_payload_len + 12; - - - bcopy((char *)&rx_frame_802_3.an_rx_dst_addr, - (char *)&eh->ether_dhost, ETHER_ADDR_LEN); - bcopy((char *)&rx_frame_802_3.an_rx_src_addr, - (char *)&eh->ether_shost, ETHER_ADDR_LEN); - - /* in mbuf header type is just before payload */ - error = an_read_data(sc, id, 0x44, (caddr_t)&(eh->ether_type), - rx_frame_802_3.an_rx_802_3_payload_len); - - if (error) { - m_freem(m); - ifp->if_ierrors++; - return; - } - - ifp->if_ipackets++; - - /* Receive packet. */ -#ifdef ANCACHE - an_cache_store(sc, eh, m, rx_frame.an_rx_signal_strength); -#endif -#if NBPFILTER > 0 - if (ifp->if_bpf) - bpf_mtap(ifp->if_bpf, m); -#endif - (*ifp->if_input)(ifp, m); -} - -static void an_txeof(sc, status) - struct an_softc *sc; - int status; -{ - struct ifnet *ifp; - int id; - - /* TX DONE enable lan monitor DJA - an_enable_sniff(); - */ - - ifp = &sc->arpcom.ec_if; - - ifp->if_timer = 0; - ifp->if_flags &= ~IFF_OACTIVE; - - id = CSR_READ_2(sc, AN_TX_CMP_FID); - - if (status & AN_EV_TX_EXC) { - ifp->if_oerrors++; - } else - ifp->if_opackets++; - -#if 0 /*XXX*/ - if (id != sc->an_rdata.an_tx_ring[sc->an_rdata.an_tx_cons]) - printf("%s: id mismatch: expected %x, got %x\n", - sc->an_dev.dv_xname, - sc->an_rdata.an_tx_ring[sc->an_rdata.an_tx_cons], id); -#endif - - sc->an_rdata.an_tx_ring[sc->an_rdata.an_tx_cons] = 0; - AN_INC(sc->an_rdata.an_tx_cons, AN_TX_RING_CNT); - - return; -} - -/* - * We abuse the stats updater to check the current NIC status. This - * is important because we don't want to allow transmissions until - * the NIC has synchronized to the current cell (either as the master - * in an ad-hoc group, or as a station connected to an access point). - */ -void an_stats_update(xsc) - void *xsc; -{ - struct an_softc *sc; - struct ifnet *ifp; - - sc = xsc; - ifp = &sc->arpcom.ec_if; - - sc->an_status.an_type = AN_RID_STATUS; - sc->an_status.an_len = sizeof(struct an_ltv_status); - an_read_record(sc, (struct an_ltv_gen *)&sc->an_status); - - if (sc->an_status.an_opmode & AN_STATUS_OPMODE_IN_SYNC) - sc->an_associated = 1; - else - sc->an_associated = 0; - - /* Don't do this while we're transmitting */ - if (ifp->if_flags & IFF_OACTIVE) { - callout_reset(&sc->an_stat_ch, hz, an_stats_update, sc); - return; - } - - sc->an_stats.an_len = sizeof(struct an_ltv_stats); - sc->an_stats.an_type = AN_RID_32BITS_CUM; - an_read_record(sc, (struct an_ltv_gen *)&sc->an_stats.an_len); - - callout_reset(&sc->an_stat_ch, hz, an_stats_update, sc); - - return; -} - -int an_intr(void *arg) -{ - struct an_softc *sc = arg; - struct ifnet *ifp; - u_int16_t status; - - if (!sc->sc_enabled) - return 0; - - ifp = &sc->arpcom.ec_if; - - if (!(ifp->if_flags & IFF_UP)) { - CSR_WRITE_2(sc, AN_EVENT_ACK, 0xFFFF); - CSR_WRITE_2(sc, AN_INT_EN, 0); - return 0; - } - - /* Disable interrupts. */ - CSR_WRITE_2(sc, AN_INT_EN, 0); - - status = CSR_READ_2(sc, AN_EVENT_STAT); - CSR_WRITE_2(sc, AN_EVENT_ACK, ~AN_INTRS); - - if (status & AN_EV_AWAKE) { - CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_AWAKE); - } - - if (status & AN_EV_LINKSTAT) { - if (CSR_READ_2(sc, AN_LINKSTAT) == AN_LINKSTAT_ASSOCIATED) - sc->an_associated = 1; - else - sc->an_associated = 0; - CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_LINKSTAT); - } - - if (status & AN_EV_RX) { - an_rxeof(sc); - CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_RX); - } - - if (status & AN_EV_TX) { - an_txeof(sc, status); - CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_TX); - } - - if (status & AN_EV_TX_EXC) { - an_txeof(sc, status); - CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_TX_EXC); - } - - if (status & AN_EV_ALLOC) - CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_ALLOC); - - if (status & AN_EV_CMD) { - wakeup(sc); - CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_CMD); - } - - /* Re-enable interrupts. */ - CSR_WRITE_2(sc, AN_INT_EN, AN_INTRS); - - if (IFQ_IS_EMPTY(&ifp->if_snd) == 0) - an_start(ifp); - - return 1; -} - static int -an_cmd(struct an_softc *sc, int cmd, int val) +an_setdef(struct an_softc *sc, struct an_req *areq) { - int i, stat; - - /* make sure that previous command completed */ - if (CSR_READ_2(sc, AN_COMMAND) & AN_CMD_BUSY) { - printf("%s: command busy\n", sc->an_dev.dv_xname); - CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_CLR_STUCK_BUSY); - } - - CSR_WRITE_2(sc, AN_PARAM0, val); - CSR_WRITE_2(sc, AN_PARAM1, 0); - CSR_WRITE_2(sc, AN_PARAM2, 0); - CSR_WRITE_2(sc, AN_COMMAND, cmd); - - for (i = 0; i < AN_TIMEOUT; i++) { - if (CSR_READ_2(sc, AN_EVENT_STAT) & AN_EV_CMD) - break; - /* make sure the command is accepted */ - if (CSR_READ_2(sc, AN_COMMAND) == cmd) - CSR_WRITE_2(sc, AN_COMMAND, cmd); - DELAY(10); - } - - stat = CSR_READ_2(sc, AN_STATUS); -#if 0 - CSR_READ_2(sc, AN_RESP0); - CSR_READ_2(sc, AN_RESP1); - CSR_READ_2(sc, AN_RESP2); -#endif - - /* clear stuck command busy if necessary */ - if (CSR_READ_2(sc, AN_COMMAND) & AN_CMD_BUSY) - CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_CLR_STUCK_BUSY); - - /* Ack the command */ - CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_CMD); - - if (i == AN_TIMEOUT) - return ETIMEDOUT; - if (stat & AN_STAT_CMD_RESULT) - return EIO; - - return 0; -} - -/* - * This reset sequence may look a little strange, but this is the - * most reliable method I've found to really kick the NIC in the - * head and force it to reboot correctly. - */ -static void -an_reset(struct an_softc *sc) -{ - if (!sc->sc_enabled) - return; - - an_cmd(sc, AN_CMD_ENABLE, 0); - an_cmd(sc, AN_CMD_FW_RESTART, 0); - an_cmd(sc, AN_CMD_NOOP2, 0); - - if (an_cmd(sc, AN_CMD_FORCE_SYNCLOSS, 0) == ETIMEDOUT) - printf("%s: reset failed\n", sc->an_dev.dv_xname); - - an_cmd(sc, AN_CMD_DISABLE, 0); - - return; -} - -/* - * Wait for firmware come up after power enabled. - */ -static void -an_wait(struct an_softc *sc) -{ - int i; - - if (!sc->sc_enabled) - return; - - CSR_WRITE_2(sc, AN_COMMAND, AN_CMD_NOOP2); - for (i = 0; i < 3*hz; i++) { - if (CSR_READ_2(sc, AN_EVENT_STAT) & AN_EV_CMD) - break; - (void)tsleep(sc, PWAIT, "anatch", 1); - } - CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_CMD); -} - -/* - * Read an LTV record from the NIC. - */ -static int an_read_record(sc, ltv) - struct an_softc *sc; - struct an_ltv_gen *ltv; -{ - u_int16_t *ptr; - int i, len; - - if (ltv->an_len == 0 || ltv->an_type == 0) - return(EINVAL); - - /* Tell the NIC to enter record read mode. */ - if (an_cmd(sc, AN_CMD_ACCESS|AN_ACCESS_READ, ltv->an_type)) { - printf("%s: RID 0x%04x access failed\n", sc->an_dev.dv_xname, - ltv->an_type); - return(EIO); - } - - /* Seek to the record. */ - if (an_seek(sc, ltv->an_type, 0, AN_BAP1)) { - printf("%s: RID 0x%04x seek to record failed\n", - sc->an_dev.dv_xname, ltv->an_type); - return(EIO); - } - - /* - * Read the length and record type and make sure they - * match what we expect (this verifies that we have enough - * room to hold all of the returned data). - */ - len = CSR_READ_2(sc, AN_DATA1); - if (len > ltv->an_len) { - printf("%s: RID 0x%04x record length mismatch -- expected %d, " - "got %d\n", sc->an_dev.dv_xname, - ltv->an_type, ltv->an_len, len); - return(ENOSPC); - } - - ltv->an_len = len; - - /* Now read the data. */ - ptr = <v->an_val; - for (i = 0; i < (ltv->an_len - 2) >> 1; i++) - ptr[i] = CSR_READ_2(sc, AN_DATA1); - - return(0); -} - -/* - * Same as read, except we inject data instead of reading it. - */ -static int an_write_record(sc, ltv) - struct an_softc *sc; - struct an_ltv_gen *ltv; -{ - u_int16_t *ptr; - int i; - - if (an_cmd(sc, AN_CMD_ACCESS|AN_ACCESS_READ, ltv->an_type)) - return(EIO); - - if (an_seek(sc, ltv->an_type, 0, AN_BAP1)) - return(EIO); - - CSR_WRITE_2(sc, AN_DATA1, ltv->an_len-2); - - ptr = <v->an_val; - for (i = 0; i < (ltv->an_len - 4) >> 1; i++) - CSR_WRITE_2(sc, AN_DATA1, ptr[i]); - - if (an_cmd(sc, AN_CMD_ACCESS|AN_ACCESS_WRITE, ltv->an_type)) - return(EIO); - - return(0); -} - -static int an_seek(sc, id, off, chan) - struct an_softc *sc; - int id, off, chan; -{ - int i; - int selreg, offreg; - - switch (chan) { - case AN_BAP0: - selreg = AN_SEL0; - offreg = AN_OFF0; - break; - case AN_BAP1: - selreg = AN_SEL1; - offreg = AN_OFF1; - break; - default: - printf("%s: invalid data path: %x\n", sc->an_dev.dv_xname, chan); - return(EIO); - } - - CSR_WRITE_2(sc, selreg, id); - CSR_WRITE_2(sc, offreg, off); - - for (i = 0; i < AN_TIMEOUT; i++) { - if (!(CSR_READ_2(sc, offreg) & (AN_OFF_BUSY|AN_OFF_ERR))) - break; - DELAY(10); - } - - if (i == AN_TIMEOUT) - return(ETIMEDOUT); - - return(0); -} - -static int an_read_data(sc, id, off, buf, len) - struct an_softc *sc; - int id, off; - caddr_t buf; - int len; -{ - int i; - u_int16_t *ptr; - u_int8_t *ptr2; - - if (off != -1) { - if (an_seek(sc, id, off, AN_BAP1)) - return(EIO); - } - - ptr = (u_int16_t *)buf; - for (i = 0; i < len / 2; i++) - ptr[i] = CSR_READ_2(sc, AN_DATA1); - i*=2; - if (ian_dev.dv_xname, len); - return(ENOMEM); - } - - for (i = 0; i < AN_TIMEOUT; i++) { - if (CSR_READ_2(sc, AN_EVENT_STAT) & AN_EV_ALLOC) - break; - DELAY(10); - } - - if (i == AN_TIMEOUT) - return(ETIMEDOUT); - - CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_ALLOC); - *id = CSR_READ_2(sc, AN_ALLOC_FID); - - if (an_seek(sc, *id, 0, AN_BAP0)) - return(EIO); - - for (i = 0; i < len / 2; i++) - CSR_WRITE_2(sc, AN_DATA0, 0); - - return(0); -} - -static void an_setdef(sc, areq) - struct an_softc *sc; - struct an_req *areq; -{ - struct ifnet *ifp; + int error; + struct ifnet *ifp = &sc->arpcom.ec_if; struct an_ltv_genconfig *cfg; - struct an_ltv_ssidlist *ssid; - struct an_ltv_aplist *ap; - struct an_ltv_gen *sp; + struct an_ltv_gen *sp; - ifp = &sc->arpcom.ec_if; + error = 0; switch (areq->an_type) { case AN_RID_GENCONFIG: cfg = (struct an_ltv_genconfig *)areq; - - bcopy((char *)&cfg->an_macaddr, - (char *)&sc->an_caps.an_oemaddr, ETHER_ADDR_LEN); - bcopy((char *)&cfg->an_macaddr, LLADDR(ifp->if_sadl), - ETHER_ADDR_LEN); - - bcopy((char *)cfg, (char *)&sc->an_config, - sizeof(struct an_ltv_genconfig)); + memcpy(sc->an_caps.an_oemaddr, cfg->an_macaddr, ETHER_ADDR_LEN); + memcpy(LLADDR(ifp->if_sadl), cfg->an_macaddr, ETHER_ADDR_LEN); + memcpy(&sc->an_config, areq, sizeof(struct an_ltv_genconfig)); + error = ENETRESET; break; case AN_RID_SSIDLIST: - ssid = (struct an_ltv_ssidlist *)areq; - bcopy((char *)ssid, (char *)&sc->an_ssidlist, - sizeof(struct an_ltv_ssidlist)); + memcpy(&sc->an_ssidlist, areq, sizeof(struct an_ltv_ssidlist)); + error = ENETRESET; break; case AN_RID_APLIST: - ap = (struct an_ltv_aplist *)areq; - bcopy((char *)ap, (char *)&sc->an_aplist, - sizeof(struct an_ltv_aplist)); + memcpy(&sc->an_aplist, areq, sizeof(struct an_ltv_aplist)); + error = ENETRESET; break; case AN_RID_TX_SPEED: sp = (struct an_ltv_gen *)areq; sc->an_tx_rate = sp->an_val; break; case AN_RID_WEP_VOLATILE: - memcpy(&sc->an_temp_keys, areq, sizeof(sc->an_temp_keys)); - - /* Disable the MAC. */ - an_cmd(sc, AN_CMD_DISABLE, 0); - - /* Just write the Key, we don't want to save it */ - an_write_record(sc, (struct an_ltv_gen *)areq); - - /* Turn the MAC back on. */ - an_cmd(sc, AN_CMD_ENABLE, 0); - - break; case AN_RID_WEP_PERSISTENT: - - /* Disable the MAC. */ + case AN_RID_LEAP_USER: + case AN_RID_LEAP_PASS: + if (!sc->sc_enabled) { + error = ENXIO; + break; + } an_cmd(sc, AN_CMD_DISABLE, 0); - - /* Just write the Key, the card will save it in this mode */ an_write_record(sc, (struct an_ltv_gen *)areq); - - /* Turn the MAC back on. */ - an_cmd(sc, AN_CMD_ENABLE, 0); - + if (an_cmd(sc, AN_CMD_ENABLE, 0)) + error = EIO; break; default: - printf("%s: unknown RID: %x\n", sc->an_dev.dv_xname, areq->an_type); - return; + if (ifp->if_flags & IFF_DEBUG) + printf("%s: unknown RID: %x\n", sc->an_dev.dv_xname, + areq->an_type); + error = EINVAL; break; } - - - /* Reinitialize the card. */ - if (ifp->if_flags & IFF_UP) - an_init(ifp); - - return; + return error; } -/* - * We can't change the NIC configuration while the MAC is enabled, - * so in order to turn on RX monitor mode, we have to turn the MAC - * off first. - */ -static void an_promisc(sc, promisc) - struct an_softc *sc; - int promisc; +static int +an_ioctl(struct ifnet *ifp, u_long command, caddr_t data) { - /* Disable the MAC. */ - an_cmd(sc, AN_CMD_DISABLE, 0); - - /* Set RX mode. */ - if (promisc && - !(sc->an_config.an_rxmode & AN_RXMODE_LAN_MONITOR_CURBSS) - ) { - sc->an_rxmode = sc->an_config.an_rxmode; - /* kills card DJA, if in sniff mode can't TX packets - sc->an_config.an_rxmode |= - AN_RXMODE_LAN_MONITOR_CURBSS; - */ - } else { - sc->an_config.an_rxmode = sc->an_rxmode; - } - - /* Transfer the configuration to the NIC */ - sc->an_config.an_len = sizeof(struct an_ltv_genconfig); - sc->an_config.an_type = AN_RID_GENCONFIG; - if (an_write_record(sc, (struct an_ltv_gen *)&sc->an_config)) { - printf("%s: failed to set configuration\n", sc->an_dev.dv_xname); - return; - } - /* Turn the MAC back on. */ - an_cmd(sc, AN_CMD_ENABLE, 0); - - return; -} - -static int an_ioctl(ifp, command, data) - struct ifnet *ifp; - u_long command; - caddr_t data; -{ - int i, s; - int error = 0; - struct an_softc *sc; - struct an_req areq; - struct ifreq *ifr; - struct an_ltv_wepkey *akey; - struct ieee80211_nwid nwid; - struct ieee80211_nwkey *nwkey; - struct ieee80211_power *power; + int s; + int error = 0; + struct an_softc *sc; + struct an_req *areq; + struct ifreq *ifr; + struct ieee80211_nwid nwid; + struct ieee80211_power *power; sc = ifp->if_softc; ifr = (struct ifreq *)data; @@ -1046,55 +479,55 @@ static int an_ioctl(ifp, command, data) * reassociation to another access point. It is really * helpful for tcpdump(8). */ - if ((ifp->if_flags & sc->an_if_flags & - (IFF_UP | IFF_RUNNING)) == (IFF_UP | IFF_RUNNING)) { - if (ifp->if_flags & IFF_PROMISC && - !(sc->an_if_flags & IFF_PROMISC)) { - an_promisc(sc, 1); - break; - } - if (!(ifp->if_flags & IFF_PROMISC) && - sc->an_if_flags & IFF_PROMISC) { - an_promisc(sc, 0); - break; - } - } - error = ether_ioctl(ifp, command, data); + if (sc->sc_enabled && + (ifp->if_flags ^ sc->an_if_flags) == IFF_PROMISC) + error = an_cmd(sc, AN_CMD_SET_MODE, + (ifp->if_flags & IFF_PROMISC) ? 0xffff : 0); + else if (ifp->if_flags & IFF_UP) + error = an_init(ifp); + else if (sc->sc_enabled && !(ifp->if_flags & IFF_UP)) + an_stop(ifp, 1); sc->an_if_flags = ifp->if_flags; break; case SIOCGAIRONET: - error = copyin(ifr->ifr_data, &areq, sizeof(areq)); + areq = &sc->an_reqbuf; + error = copyin(ifr->ifr_data, areq, sizeof(struct an_req)); if (error) break; + switch (areq->an_type) { #ifdef ANCACHE - if (areq.an_type == AN_RID_ZERO_CACHE) { + case AN_RID_ZERO_CACHE: + /* XXX suser()? -- should belong to SIOCSAIRONET */ sc->an_sigitems = sc->an_nextitem = 0; - break; - } else if (areq.an_type == AN_RID_READ_CACHE) { - char *pt = (char *)&areq.an_val; - bcopy((char *)&sc->an_sigitems, (char *)pt, - sizeof(int)); + goto out; + case AN_RID_READ_CACHE: + caddr_t pt = (char *)&areq->an_val; + memcpy(pt, &sc->an_sigitems, sizeof(int)); pt += sizeof(int); - areq.an_len = sizeof(int) / 2; - bcopy((char *)&sc->an_sigcache, (char *)pt, + areq->an_len = sizeof(int) / 2; + memcpy(pt, &sc->an_sigcache, sizeof(struct an_sigcache) * sc->an_sigitems); - areq.an_len += ((sizeof(struct an_sigcache) * + areq->an_len += ((sizeof(struct an_sigcache) * sc->an_sigitems) / 2) + 1; - } else + break; #endif - if (an_read_record(sc, (struct an_ltv_gen *)&areq)) { - error = EINVAL; + default: + if (an_read_record(sc, (struct an_ltv_gen *)areq)) { + error = EINVAL; + break; + } break; } - error = copyout(&areq, ifr->ifr_data, sizeof(areq)); + error = copyout(areq, ifr->ifr_data, sizeof(struct an_req)); break; case SIOCSAIRONET: if ((error = suser(curproc->p_ucred, &curproc->p_acflag))) - goto out; - error = copyin(ifr->ifr_data, &areq, sizeof(areq)); + break; + areq = &sc->an_reqbuf; + error = copyin(ifr->ifr_data, areq, sizeof(struct an_req)); if (error) break; - an_setdef(sc, &areq); + error = an_setdef(sc, areq); break; case SIOCS80211NWID: error = copyin(ifr->ifr_data, &nwid, sizeof(nwid)); @@ -1111,12 +544,11 @@ static int an_ioctl(ifp, command, data) memset(sc->an_ssidlist.an_ssid1, 0, IEEE80211_NWID_LEN); sc->an_ssidlist.an_ssid1_len = nwid.i_len; memcpy(sc->an_ssidlist.an_ssid1, nwid.i_nwid, nwid.i_len); - if (sc->sc_enabled) - an_init(ifp); + error = ENETRESET; break; case SIOCG80211NWID: memset(&nwid, 0, sizeof(nwid)); - if ((ifp->if_flags & IFF_RUNNING) && sc->an_associated) { + if (sc->sc_enabled && sc->an_associated) { nwid.i_len = sc->an_status.an_ssidlen; memcpy(nwid.i_nwid, sc->an_status.an_ssid, nwid.i_len); } else { @@ -1127,54 +559,17 @@ static int an_ioctl(ifp, command, data) error = copyout(&nwid, ifr->ifr_data, sizeof(nwid)); break; case SIOCS80211NWKEY: - nwkey = (struct ieee80211_nwkey *)data; - sc->an_config.an_authtype &= AN_AUTHTYPE_MASK; - if (nwkey->i_wepon) - sc->an_config.an_authtype |= (AN_AUTHTYPE_MASK + 1); - akey = (struct an_ltv_wepkey *)&areq; - memset(akey, 0, sizeof(struct an_ltv_wepkey)); - akey->an_type = AN_RID_WEP_VOLATILE; - akey->an_len = sizeof(struct an_ltv_wepkey); - akey->an_key_index = 0; - akey->an_mac_addr[0] = 1; /* default mac */ - akey->an_key_len = nwkey->i_key[0].i_keylen; - if (akey->an_key_len > sizeof(akey->an_key)) { - error = EINVAL; - break; - } - if (nwkey->i_key[0].i_keydat != NULL) { - if ((error = copyin(nwkey->i_key[0].i_keydat, - akey->an_key, akey->an_key_len)) != 0) - break; - } - memcpy(&sc->an_temp_keys, akey, sizeof(struct an_ltv_wepkey)); - if (sc->sc_enabled) - an_init(ifp); + error = an_set_nwkey(sc, (struct ieee80211_nwkey *)data); break; case SIOCG80211NWKEY: - nwkey = (struct ieee80211_nwkey *)data; - nwkey->i_wepon = - sc->an_config.an_authtype & ~AN_AUTHTYPE_MASK ? 1 : 0; - nwkey->i_defkid = 1; - if (nwkey->i_key[0].i_keydat == NULL) - break; - /* do not show any keys to non-root user */ - if ((error = suser(curproc->p_ucred, &curproc->p_acflag)) != 0) - break; - akey = &sc->an_temp_keys; - nwkey->i_key[0].i_keylen = akey->an_key_len; - for (i = 1; i < IEEE80211_WEP_NKID; i++) - nwkey->i_key[i].i_keylen = 0; - error = copyout(akey->an_key, nwkey->i_key[0].i_keydat, - akey->an_key_len); + error = an_get_nwkey(sc, (struct ieee80211_nwkey *)data); break; case SIOCS80211POWER: power = (struct ieee80211_power *)data; sc->an_config.an_psave_mode = power->i_enabled ? AN_PSAVE_PSP : AN_PSAVE_NONE; sc->an_config.an_listen_interval = power->i_maxsleep; - if (sc->sc_enabled) - an_init(ifp); + error = ENETRESET; break; case SIOCG80211POWER: power = (struct ieee80211_power *)data; @@ -1190,14 +585,9 @@ static int an_ioctl(ifp, command, data) #endif case SIOCADDMULTI: case SIOCDELMULTI: - error = (command == SIOCADDMULTI) ? - ether_addmulti(ifr, &sc->arpcom) : - ether_delmulti(ifr, &sc->arpcom); + error = ether_ioctl(ifp, command, data); if (error == ENETRESET) { - /* - * Multicast list has changed. Should set the - * hardware filter accordingly here. - */ + /* we don't have multicast filter. */ error = 0; } break; @@ -1205,15 +595,244 @@ static int an_ioctl(ifp, command, data) error = ether_ioctl(ifp, command, data); break; } + if (error == ENETRESET) { + if (sc->sc_enabled) + error = an_init(ifp); + else + error = 0; + } +#ifdef ANCACHE out: +#endif splx(s); - return(error); + return error; +} + +static int +an_set_nwkey(struct an_softc *sc, struct ieee80211_nwkey *nwkey) +{ + int i, len, anysetkey, needreset, error; + u_int16_t prevauth; + struct an_req *areq; + struct an_wepkey keys[IEEE80211_WEP_NKID], *key; + + error = 0; + prevauth = sc->an_config.an_authtype; + + switch (nwkey->i_wepon) { + case IEEE80211_NWKEY_OPEN: + sc->an_config.an_authtype = AN_AUTHTYPE_OPEN; + break; + + case IEEE80211_NWKEY_WEP: + case IEEE80211_NWKEY_WEP | IEEE80211_NWKEY_PERSIST: + memset(keys, 0, sizeof(keys)); + anysetkey = 0; + for (i = 0, key = keys; i < IEEE80211_WEP_NKID; i++, key++) { + key->an_wep_keylen = nwkey->i_key[i].i_keylen; + if (key->an_wep_keylen < 0) + continue; + if (key->an_wep_keylen != 0 && + key->an_wep_keylen < IEEE80211_WEP_KEYLEN) + return EINVAL; + if (key->an_wep_keylen > sizeof(key->an_wep_key)) + return EINVAL; + if ((error = copyin(nwkey->i_key[i].i_keydat, + key->an_wep_key, key->an_wep_keylen)) != 0) + return error; + anysetkey++; + } + i = nwkey->i_defkid - 1; + if (i != -1) { + if (i < 0 || i >= IEEE80211_WEP_NKID) + return EINVAL; + /* default key must have a valid value */ + if (keys[i].an_wep_keylen == 0 || + (keys[i].an_wep_keylen < 0 && + sc->an_perskeylen[i] == 0)) + return EINVAL; + anysetkey++; + } + if (!(nwkey->i_wepon & IEEE80211_NWKEY_PERSIST)) { + sc->an_config.an_authtype = + AN_AUTHTYPE_OPEN | AN_AUTHTYPE_PRIVACY_IN_USE; + if (anysetkey) { + /* set temporary keys */ + sc->an_tx_key = i; + for (i = 0; i < IEEE80211_WEP_NKID; i++) { + if (keys[i].an_wep_keylen >= 0) + memcpy(&sc->an_wepkeys[i], + &keys[i], sizeof(keys[i])); + } + error = ENETRESET; + } + break; + } + /* program persist keys */ + needreset = 0; + if (anysetkey) { + /* prepare to program nvram */ + if (!sc->sc_enabled) { + if (sc->sc_enable) { + (*sc->sc_enable)(sc); + an_wait(sc); + } + sc->sc_enabled = 1; + error = an_write_wepkey(sc, + AN_RID_WEP_PERSISTENT, keys, i); + if (sc->sc_disable) + (*sc->sc_disable)(sc); + sc->sc_enabled = 0; + } else { + an_cmd(sc, AN_CMD_DISABLE, 0); + error = an_write_wepkey(sc, + AN_RID_WEP_PERSISTENT, keys, i); + an_cmd(sc, AN_CMD_ENABLE, 0); + } + if (error) + break; + } + if (i != -1) + sc->an_tx_perskey = i; + if (sc->an_tx_key != -1 && sc->an_tx_key != i) + needreset++; + sc->an_tx_key = -1; + for (i = 0; i < IEEE80211_WEP_NKID; i++) { + if (sc->an_wepkeys[i].an_wep_keylen != -1) { + needreset++; + memset(&sc->an_wepkeys[i].an_wep_key, 0, + sizeof(sc->an_wepkeys[i].an_wep_key)); + sc->an_wepkeys[i].an_wep_keylen = -1; + } + if (keys[i].an_wep_keylen >= 0) + sc->an_perskeylen[i] = keys[i].an_wep_keylen; + } + sc->an_config.an_authtype = + AN_AUTHTYPE_OPEN | AN_AUTHTYPE_PRIVACY_IN_USE; + if (needreset) { + /* firmware restart to reload persistent key */ + an_reset(sc); + } + if (anysetkey || needreset) + error = ENETRESET; + break; + + case IEEE80211_NWKEY_EAP: + sc->an_config.an_authtype = AN_AUTHTYPE_OPEN | + AN_AUTHTYPE_PRIVACY_IN_USE | AN_AUTHTYPE_LEAP; + if (nwkey->i_key[0].i_keydat == NULL && + nwkey->i_key[1].i_keydat == NULL) + break; + if (!sc->sc_enabled) + return ENXIO; + an_cmd(sc, AN_CMD_DISABLE, 0); + areq = &sc->an_reqbuf; + for (i = 0; i < 2; i++) { + if (nwkey->i_key[i].i_keydat == NULL) + continue; + len = i ? AN_LEAP_PASS_MAX : AN_LEAP_USER_MAX; + memset(areq, 0, len + 4); + areq->an_type = i ? AN_RID_LEAP_PASS : AN_RID_LEAP_USER; + areq->an_len = len + 4; + areq->an_val[i] = nwkey->i_key[i].i_keylen; + if ((error = copyin(nwkey->i_key[i].i_keydat, + areq->an_val + 1, nwkey->i_key[i].i_keylen)) != 0) + break; + an_write_record(sc, (struct an_ltv_gen *)areq); + } + if (error) + break; + error = an_cmd(sc, AN_CMD_ENABLE, 0); + if (error) { + printf("%s: an_set_nwkey: failed to enable MAC\n", + sc->an_dev.dv_xname); + break; + } + error = ENETRESET; + break; + default: + error = EINVAL; + break; + } + if (error == 0 && prevauth != sc->an_config.an_authtype) + error = ENETRESET; + return error; +} + +static int +an_get_nwkey(struct an_softc *sc, struct ieee80211_nwkey *nwkey) +{ + int i, error; + + error = 0; + if (sc->an_config.an_authtype & AN_AUTHTYPE_LEAP) + nwkey->i_wepon = IEEE80211_NWKEY_EAP; + else if (sc->an_config.an_authtype & AN_AUTHTYPE_PRIVACY_IN_USE) + nwkey->i_wepon = IEEE80211_NWKEY_WEP; + else + nwkey->i_wepon = IEEE80211_NWKEY_OPEN; + if (sc->an_tx_key == -1) + nwkey->i_defkid = sc->an_tx_perskey + 1; + else + nwkey->i_defkid = sc->an_tx_key + 1; + if (nwkey->i_key[0].i_keydat == NULL) + return 0; + for (i = 0; i < IEEE80211_WEP_NKID; i++) { + if (nwkey->i_key[i].i_keydat == NULL) + continue; + /* do not show any keys to non-root user */ + if ((error = suser(curproc->p_ucred, &curproc->p_acflag)) != 0) + break; + nwkey->i_key[i].i_keylen = sc->an_wepkeys[i].an_wep_keylen; + if (nwkey->i_key[i].i_keylen < 0) { + if (sc->an_perskeylen[i] == 0) + nwkey->i_key[i].i_keylen = 0; + continue; + } + if ((error = copyout(sc->an_wepkeys[i].an_wep_key, + nwkey->i_key[i].i_keydat, + sc->an_wepkeys[i].an_wep_keylen)) != 0) + break; + } + return error; +} + +static int +an_write_wepkey(struct an_softc *sc, int type, struct an_wepkey *keys, int kid) +{ + int i, error; + struct an_ltv_wepkey *akey; + + error = 0; + akey = (struct an_ltv_wepkey *)&sc->an_reqbuf; + memset(akey, 0, sizeof(struct an_ltv_wepkey)); + akey->an_type = type; + akey->an_len = sizeof(struct an_ltv_wepkey); + for (i = 0; i < IEEE80211_WEP_NKID; i++) { + if (keys[i].an_wep_keylen < 0 || + keys[i].an_wep_keylen > sizeof(akey->an_key)) + continue; + akey->an_key_len = keys[i].an_wep_keylen; + akey->an_key_index = i; + akey->an_mac_addr[0] = 1; /* default mac */ + memcpy(akey->an_key, keys[i].an_wep_key, akey->an_key_len); + error = an_write_record(sc, (struct an_ltv_gen *)akey); + if (error) + return error; + } + if (kid >= 0) { + akey->an_key_index = 0xffff; + akey->an_mac_addr[0] = kid; + akey->an_key_len = 0; + memset(akey->an_key, 0, sizeof(akey->an_key)); + error = an_write_record(sc, (struct an_ltv_gen *)akey); + } + return error; } #ifdef IFM_IEEE80211 static int -an_media_change(ifp) - struct ifnet *ifp; +an_media_change(struct ifnet *ifp) { struct an_softc *sc = ifp->if_softc; struct ifmedia_entry *ime; @@ -1247,8 +866,10 @@ an_media_change(ifp) * There is a struct defined as an_txframe, which is used nowhere. * Perhaps we need to change the transmit mode from 802.3 to native. */ + + /* we cannot return ENETRESET here */ if (sc->sc_enabled) - an_init(ifp); + error = an_init(ifp); return error; } @@ -1260,52 +881,48 @@ an_media_status(ifp, imr) struct an_softc *sc = ifp->if_softc; imr->ifm_status = IFM_AVALID; - if (sc->an_associated) - imr->ifm_status |= IFM_ACTIVE; imr->ifm_active = IFM_IEEE80211; - switch (sc->an_tx_rate) { - case 0: - imr->ifm_active |= IFM_AUTO; - break; - case AN_RATE_1MBPS: - imr->ifm_active |= IFM_IEEE80211_DS1; - break; - case AN_RATE_2MBPS: - imr->ifm_active |= IFM_IEEE80211_DS2; - break; - case AN_RATE_5_5MBPS: - imr->ifm_active |= IFM_IEEE80211_DS5; - break; - case AN_RATE_11MBPS: - imr->ifm_active |= IFM_IEEE80211_DS11; - break; + if (sc->sc_enabled && sc->an_associated) { + imr->ifm_status |= IFM_ACTIVE; + switch (sc->an_status.an_current_tx_rate) { + case 0: + imr->ifm_active |= IFM_AUTO; + break; + case AN_RATE_1MBPS: + imr->ifm_active |= IFM_IEEE80211_DS1; + break; + case AN_RATE_2MBPS: + imr->ifm_active |= IFM_IEEE80211_DS2; + break; + case AN_RATE_5_5MBPS: + imr->ifm_active |= IFM_IEEE80211_DS5; + break; + case AN_RATE_11MBPS: + imr->ifm_active |= IFM_IEEE80211_DS11; + break; + } } if ((sc->an_config.an_opmode & 0x0f) == AN_OPMODE_IBSS_ADHOC) imr->ifm_active |= IFM_IEEE80211_ADHOC; } #endif /* IFM_IEEE80211 */ -static int an_init_tx_ring(sc) - struct an_softc *sc; +static int +an_init_tx_ring(struct an_softc *sc) { - int i; - int id; - - if (!sc->sc_enabled) - return (0); + int i, id; for (i = 0; i < AN_TX_RING_CNT; i++) { - if (an_alloc_nicmem(sc, 1518 + - 0x44, &id)) - return(ENOMEM); + if (an_alloc_nicmem(sc, + ETHER_MAX_LEN + ETHER_TYPE_LEN + AN_802_11_OFFSET, &id)) + return ENOMEM; sc->an_rdata.an_tx_fids[i] = id; sc->an_rdata.an_tx_ring[i] = 0; } sc->an_rdata.an_tx_prod = 0; sc->an_rdata.an_tx_cons = 0; - - return(0); + return 0; } static int @@ -1313,59 +930,45 @@ an_init(struct ifnet *ifp) { struct an_softc *sc = (struct an_softc *)ifp->if_softc; - if (ifp->if_flags & IFF_RUNNING) + if (sc->sc_enabled) { an_stop(ifp, 0); - else if (sc->sc_enabled) { - /* re-enable to power on after resume ... */ - if (sc->sc_disable) - (*sc->sc_disable)(sc); - sc->sc_enabled = 0; - } - - sc->an_associated = 0; - - if (!sc->sc_enabled) { + } else { if (sc->sc_enable) (*sc->sc_enable)(sc); sc->sc_enabled = 1; an_wait(sc); } + sc->an_associated = 0; + /* Allocate the TX buffers */ if (an_init_tx_ring(sc)) { an_reset(sc); if (an_init_tx_ring(sc)) { - printf("%s: tx buffer allocation " - "failed\n", sc->an_dev.dv_xname); + printf("%s: tx buffer allocation failed\n", + sc->an_dev.dv_xname); + an_stop(ifp, 1); return ENOMEM; } } /* Set our MAC address. */ - bcopy((char *)&sc->an_caps.an_oemaddr, - (char *)&sc->an_config.an_macaddr, ETHER_ADDR_LEN); - - if (ifp->if_flags & IFF_BROADCAST) - sc->an_config.an_rxmode = AN_RXMODE_BC_ADDR; - else - sc->an_config.an_rxmode = AN_RXMODE_ADDR; + memcpy(sc->an_config.an_macaddr, sc->an_caps.an_oemaddr, + ETHER_ADDR_LEN); if (ifp->if_flags & IFF_MULTICAST) sc->an_config.an_rxmode = AN_RXMODE_BC_MC_ADDR; - - /* Initialize promisc mode. */ - /* Kills card DJA can't TX packet in sniff mode - if (ifp->if_flags & IFF_PROMISC) - sc->an_config.an_rxmode |= AN_RXMODE_LAN_MONITOR_CURBSS; - */ - - sc->an_rxmode = sc->an_config.an_rxmode; + else if (ifp->if_flags & IFF_BROADCAST) + sc->an_config.an_rxmode = AN_RXMODE_BC_ADDR; + else + sc->an_config.an_rxmode = AN_RXMODE_ADDR; /* Set the ssid list */ sc->an_ssidlist.an_type = AN_RID_SSIDLIST; sc->an_ssidlist.an_len = sizeof(struct an_ltv_ssidlist); if (an_write_record(sc, (struct an_ltv_gen *)&sc->an_ssidlist)) { printf("%s: failed to set ssid list\n", sc->an_dev.dv_xname); + an_stop(ifp, 1); return ENXIO; } @@ -1374,6 +977,7 @@ an_init(struct ifnet *ifp) sc->an_aplist.an_len = sizeof(struct an_ltv_aplist); if (an_write_record(sc, (struct an_ltv_gen *)&sc->an_aplist)) { printf("%s: failed to set AP list\n", sc->an_dev.dv_xname); + an_stop(ifp, 1); return ENXIO; } @@ -1382,22 +986,24 @@ an_init(struct ifnet *ifp) sc->an_config.an_type = AN_RID_GENCONFIG; if (an_write_record(sc, (struct an_ltv_gen *)&sc->an_config)) { printf("%s: failed to set configuration\n", sc->an_dev.dv_xname); + an_stop(ifp, 1); return ENXIO; } /* Set the WEP Keys */ - if ((sc->an_config.an_authtype & ~AN_AUTHTYPE_MASK) != 0) { - sc->an_temp_keys.an_len = sizeof(struct an_ltv_wepkey); - sc->an_temp_keys.an_type = AN_RID_WEP_VOLATILE; - an_write_record(sc, (struct an_ltv_gen *)&sc->an_temp_keys); - } + if ((sc->an_config.an_authtype & AN_AUTHTYPE_PRIVACY_IN_USE) != 0) + an_write_wepkey(sc, AN_RID_WEP_VOLATILE, sc->an_wepkeys, + sc->an_tx_key); /* Enable the MAC */ if (an_cmd(sc, AN_CMD_ENABLE, 0)) { printf("%s: failed to enable MAC\n", sc->an_dev.dv_xname); + an_stop(ifp, 1); return ENXIO; } + an_cmd(sc, AN_CMD_SET_MODE, (ifp->if_flags & IFF_PROMISC) ? 0xffff : 0); + /* enable interrupts */ CSR_WRITE_2(sc, AN_INT_EN, AN_INTRS); @@ -1408,18 +1014,15 @@ an_init(struct ifnet *ifp) return 0; } -static void an_start(ifp) - struct ifnet *ifp; +static void +an_start(struct ifnet *ifp) { - struct an_softc *sc; - struct mbuf *m0 = NULL; - struct an_txframe_802_3 tx_frame_802_3; - struct ether_header *eh; - int id; - int idx; - unsigned char txcontrol; - - sc = ifp->if_softc; + struct an_softc *sc = (struct an_softc *)ifp->if_softc; + struct mbuf *m0 = NULL, *m; + struct an_txframe_802_3 tx_frame_802_3; + struct ether_header *eh; + int id, idx; + u_int16_t txctl; if (!sc->sc_enabled) return; @@ -1431,40 +1034,16 @@ static void an_start(ifp) return; idx = sc->an_rdata.an_tx_prod; - bzero((char *)&tx_frame_802_3, sizeof(tx_frame_802_3)); + memset(&tx_frame_802_3, 0, sizeof(tx_frame_802_3)); - while (sc->an_rdata.an_tx_ring[idx] == 0) { - IFQ_DEQUEUE(&ifp->if_snd, m0); + for (;;) { + IFQ_POLL(&ifp->if_snd, m0); if (m0 == NULL) break; - + if (sc->an_rdata.an_tx_ring[idx] != 0) + break; id = sc->an_rdata.an_tx_fids[idx]; - eh = mtod(m0, struct ether_header *); - - bcopy((char *)&eh->ether_dhost, - (char *)&tx_frame_802_3.an_tx_dst_addr, ETHER_ADDR_LEN); - bcopy((char *)&eh->ether_shost, - (char *)&tx_frame_802_3.an_tx_src_addr, ETHER_ADDR_LEN); - - tx_frame_802_3.an_tx_802_3_payload_len = - m0->m_pkthdr.len - 12; /* minus src/dest mac & type */ - - m_copydata(m0, sizeof(struct ether_header) - 2 , - tx_frame_802_3.an_tx_802_3_payload_len, - (caddr_t)&sc->an_txbuf); - - txcontrol=AN_TXCTL_8023; - /* write the txcontrol only */ - an_write_data(sc, id, 0x08, (caddr_t)&txcontrol, - sizeof(txcontrol)); - - /* 802_3 header */ - an_write_data(sc, id, 0x34, (caddr_t)&tx_frame_802_3, - sizeof(struct an_txframe_802_3)); - - /* in mbuf header type is just before payload */ - an_write_data(sc, id, 0x44, (caddr_t)&sc->an_txbuf, - tx_frame_802_3.an_tx_802_3_payload_len); + IFQ_DEQUEUE(&ifp->if_snd, m0); #if NBPFILTER > 0 /* * If there's a BPF listner, bounce a copy of @@ -1474,12 +1053,27 @@ static void an_start(ifp) bpf_mtap(ifp->if_bpf, m0); #endif + txctl = AN_TXCTL_8023; + /* write the txctl only */ + an_write_data(sc, id, 0x08, (caddr_t)&txctl, sizeof(txctl)); + + eh = mtod(m0, struct ether_header *); + memcpy(tx_frame_802_3.an_tx_dst_addr, eh->ether_dhost, + ETHER_ADDR_LEN); + memcpy(tx_frame_802_3.an_tx_src_addr, eh->ether_shost, + ETHER_ADDR_LEN); + tx_frame_802_3.an_tx_802_3_payload_len = + m0->m_pkthdr.len - ETHER_ADDR_LEN * 2; + m_adj(m0, ETHER_ADDR_LEN * 2); + + /* 802_3 header */ + an_write_data(sc, id, AN_802_3_OFFSET, + (caddr_t)&tx_frame_802_3, sizeof(struct an_txframe_802_3)); + for (m = m0; m != NULL; m = m->m_next) + an_write_data(sc, id, -1, mtod(m, caddr_t), m->m_len); m_freem(m0); m0 = NULL; - /* TX START disable lan monitor ? DJA - an_disable_sniff(): - */ sc->an_rdata.an_tx_ring[idx] = id; if (an_cmd(sc, AN_CMD_TX, id)) printf("%s: xmit failed\n", sc->an_dev.dv_xname); @@ -1496,18 +1090,19 @@ static void an_start(ifp) * Set a timeout in case the chip goes out to lunch. */ ifp->if_timer = 5; - - return; } -void an_stop(ifp, disable) - struct ifnet *ifp; - int disable; +void +an_stop(struct ifnet *ifp, int disable) { - struct an_softc *sc = (struct an_softc *)ifp->if_softc; - int i; + struct an_softc *sc = (struct an_softc *)ifp->if_softc; + int i; callout_stop(&sc->an_stat_ch); + + ifp->if_flags &= ~(IFF_RUNNING|IFF_OACTIVE); + ifp->if_timer = 0; + if (!sc->sc_enabled) return; @@ -1518,22 +1113,17 @@ void an_stop(ifp, disable) for (i = 0; i < AN_TX_RING_CNT; i++) an_cmd(sc, AN_CMD_DEALLOC_MEM, sc->an_rdata.an_tx_fids[i]); - ifp->if_flags &= ~(IFF_RUNNING|IFF_OACTIVE); - ifp->if_timer = 0; - if (disable) { if (sc->sc_disable) (*sc->sc_disable)(sc); sc->sc_enabled = 0; } - - return; } -static void an_watchdog(ifp) - struct ifnet *ifp; +static void +an_watchdog(struct ifnet *ifp) { - struct an_softc *sc; + struct an_softc *sc; sc = ifp->if_softc; if (!sc->sc_enabled) @@ -1548,6 +1138,515 @@ static void an_watchdog(ifp) return; } +/* + * Low level functions + */ + +static void +an_rxeof(struct an_softc *sc) +{ + struct ifnet *ifp = &sc->arpcom.ec_if; + struct ether_header *eh; +#ifdef ANCACHE + struct an_rxframe rx_frame; +#endif + struct an_rxframe_802_3 rx_frame_802_3; + struct mbuf *m; + int id, error = 0; + + + id = CSR_READ_2(sc, AN_RX_FID); + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) { + ifp->if_ierrors++; + return; + } + MCLGET(m, M_DONTWAIT); + if (!(m->m_flags & M_EXT)) { + m_freem(m); + ifp->if_ierrors++; + return; + } + + m->m_pkthdr.rcvif = ifp; + + /* Align the data after the ethernet header */ + m->m_data = (caddr_t) ALIGN(m->m_data + sizeof(struct ether_header)) - + sizeof(struct ether_header); + + eh = mtod(m, struct ether_header *); + +#ifdef ANCACHE + /* Read NIC frame header */ + if (an_read_data(sc, id, 0, (caddr_t)&rx_frame, sizeof(rx_frame))) { + ifp->if_ierrors++; + return; + } +#endif + /* Read in the 802_3 frame header */ + if (an_read_data(sc, id, AN_802_3_OFFSET, (caddr_t)&rx_frame_802_3, + sizeof(rx_frame_802_3))) { + ifp->if_ierrors++; + return; + } + + if (rx_frame_802_3.an_rx_802_3_status != 0) { + ifp->if_ierrors++; + return; + } + + /* Check for insane frame length */ + if (rx_frame_802_3.an_rx_802_3_payload_len > MCLBYTES) { + ifp->if_ierrors++; + return; + } + + m->m_pkthdr.len = m->m_len = + rx_frame_802_3.an_rx_802_3_payload_len + ETHER_ADDR_LEN * 2; + + memcpy(&eh->ether_dhost, &rx_frame_802_3.an_rx_dst_addr, + ETHER_ADDR_LEN); + memcpy(&eh->ether_shost, &rx_frame_802_3.an_rx_src_addr, + ETHER_ADDR_LEN); + + /* in mbuf header type is just before payload */ + error = an_read_data(sc, id, -1, (caddr_t)&(eh->ether_type), + rx_frame_802_3.an_rx_802_3_payload_len); + + if (error) { + m_freem(m); + ifp->if_ierrors++; + return; + } + + ifp->if_ipackets++; + + /* Receive packet. */ +#ifdef ANCACHE + an_cache_store(sc, eh, m, rx_frame.an_rx_signal_strength); +#endif +#if NBPFILTER > 0 + if (ifp->if_bpf) + bpf_mtap(ifp->if_bpf, m); +#endif + (*ifp->if_input)(ifp, m); +} + +static void +an_txeof(struct an_softc *sc, int status) +{ + struct ifnet *ifp = &sc->arpcom.ec_if; + int i, id; + + ifp->if_timer = 0; + ifp->if_flags &= ~IFF_OACTIVE; + + id = CSR_READ_2(sc, AN_TX_CMP_FID); + + if (status & AN_EV_TX_EXC) + ifp->if_oerrors++; + else + ifp->if_opackets++; + + /* fix from Doug Ambrisko -wsr */ + for (i = 0; i < AN_TX_RING_CNT; i++) { + if (id == sc->an_rdata.an_tx_ring[i]) { + sc->an_rdata.an_tx_ring[i] = 0; + break; + } + } + if (i != sc->an_rdata.an_tx_cons) { + if (ifp->if_flags & IFF_DEBUG) + printf("%s: id mismatch: id %x, " + "expected %x(%d), actual %x(%d)\n", + sc->an_dev.dv_xname, id, + sc->an_rdata.an_tx_ring[sc->an_rdata.an_tx_cons], + sc->an_rdata.an_tx_cons, id, i); + } + + AN_INC(sc->an_rdata.an_tx_cons, AN_TX_RING_CNT); + + return; +} + +/* + * We abuse the stats updater to check the current NIC status. This + * is important because we don't want to allow transmissions until + * the NIC has synchronized to the current cell (either as the master + * in an ad-hoc group, or as a station connected to an access point). + */ +void +an_stats_update(void *xsc) +{ + struct an_softc *sc = xsc; + + if (sc->sc_enabled) { + sc->an_status.an_type = AN_RID_STATUS; + sc->an_status.an_len = sizeof(struct an_ltv_status); + if (an_read_record(sc, (struct an_ltv_gen *)&sc->an_status) + == 0) { + if (sc->an_status.an_opmode & AN_STATUS_OPMODE_IN_SYNC) + sc->an_associated = 1; + else + sc->an_associated = 0; +#if 0 + /* Don't do this while we're transmitting */ + if (sc->arpcom.ec_if.if_flags & IFF_OACTIVE) { + sc->an_stats.an_len = + sizeof(struct an_ltv_stats); + sc->an_stats.an_type = AN_RID_32BITS_CUM; + an_read_record(sc, + (struct an_ltv_gen *)&sc->an_stats.an_len); + } +#endif + } + } + callout_reset(&sc->an_stat_ch, hz, an_stats_update, sc); +} + +int +an_intr(void *arg) +{ + struct an_softc *sc = arg; + struct ifnet *ifp = &sc->arpcom.ec_if; + u_int16_t status; + + if (!sc->sc_enabled) + return 0; + + if (!(ifp->if_flags & IFF_UP)) { + CSR_WRITE_2(sc, AN_EVENT_ACK, 0xFFFF); + CSR_WRITE_2(sc, AN_INT_EN, 0); + return 0; + } + + /* Disable interrupts. */ + CSR_WRITE_2(sc, AN_INT_EN, 0); + + while ((status = (CSR_READ_2(sc, AN_EVENT_STAT) & AN_INTRS)) != 0) { + if (status & AN_EV_RX) { + an_rxeof(sc); + CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_RX); + status &= ~AN_EV_RX; + } + if (status & (AN_EV_TX | AN_EV_TX_EXC)) { + an_txeof(sc, status); + CSR_WRITE_2(sc, AN_EVENT_ACK, + status & (AN_EV_TX | AN_EV_TX_EXC)); + status &= ~(AN_EV_TX | AN_EV_TX_EXC); + } + if (status & AN_EV_LINKSTAT) { + if (CSR_READ_2(sc, AN_LINKSTAT) == + AN_LINKSTAT_ASSOCIATED) + sc->an_associated = 1; + else + sc->an_associated = 0; + CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_LINKSTAT); + status &= ~AN_EV_LINKSTAT; + } +#if 0 + if (status & AN_EV_CMD) { + wakeup(sc); + CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_CMD); + status &= ~AN_EV_CMD; + } +#endif + if (status) + CSR_WRITE_2(sc, AN_EVENT_ACK, status); + } + + /* Re-enable interrupts. */ + CSR_WRITE_2(sc, AN_INT_EN, AN_INTRS); + + if (IFQ_IS_EMPTY(&ifp->if_snd) == 0) + an_start(ifp); + + return 1; +} + +static int +an_cmd(struct an_softc *sc, int cmd, int val) +{ + int i, stat; + + /* make sure that previous command completed */ + if (CSR_READ_2(sc, AN_COMMAND) & AN_CMD_BUSY) { + if (sc->arpcom.ec_if.if_flags & IFF_DEBUG) + printf("%s: command 0x%x busy\n", sc->an_dev.dv_xname, + CSR_READ_2(sc, AN_COMMAND)); + CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_CLR_STUCK_BUSY); + } + + CSR_WRITE_2(sc, AN_PARAM0, val); + CSR_WRITE_2(sc, AN_PARAM1, 0); + CSR_WRITE_2(sc, AN_PARAM2, 0); + CSR_WRITE_2(sc, AN_COMMAND, cmd); + + for (i = 0; i < AN_TIMEOUT; i++) { + if (CSR_READ_2(sc, AN_EVENT_STAT) & AN_EV_CMD) + break; + /* make sure the command is accepted */ + if (CSR_READ_2(sc, AN_COMMAND) == cmd) + CSR_WRITE_2(sc, AN_COMMAND, cmd); + DELAY(10); + } + + stat = CSR_READ_2(sc, AN_STATUS); + + /* clear stuck command busy if necessary */ + if (CSR_READ_2(sc, AN_COMMAND) & AN_CMD_BUSY) + CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_CLR_STUCK_BUSY); + + /* Ack the command */ + CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_CMD); + + if (i == AN_TIMEOUT) { + if (sc->arpcom.ec_if.if_flags & IFF_DEBUG) + printf("%s: command 0x%x param 0x%x timeout\n", + sc->an_dev.dv_xname, cmd, val); + return ETIMEDOUT; + } + if (stat & AN_STAT_CMD_RESULT) { + if (sc->arpcom.ec_if.if_flags & IFF_DEBUG) + printf("%s: command 0x%x param 0x%x stat 0x%x\n", + sc->an_dev.dv_xname, cmd, val, stat); + return EIO; + } + + return 0; +} + +/* + * This reset sequence may look a little strange, but this is the + * most reliable method I've found to really kick the NIC in the + * head and force it to reboot correctly. + */ +static void +an_reset(struct an_softc *sc) +{ + + if (!sc->sc_enabled) + return; + + an_cmd(sc, AN_CMD_ENABLE, 0); + an_cmd(sc, AN_CMD_FW_RESTART, 0); + an_cmd(sc, AN_CMD_NOOP2, 0); + + if (an_cmd(sc, AN_CMD_FORCE_SYNCLOSS, 0) == ETIMEDOUT) + printf("%s: reset failed\n", sc->an_dev.dv_xname); + + an_cmd(sc, AN_CMD_DISABLE, 0); +} + +/* + * Wait for firmware come up after power enabled. + */ +static void +an_wait(struct an_softc *sc) +{ + int i; + + CSR_WRITE_2(sc, AN_COMMAND, AN_CMD_NOOP2); + for (i = 0; i < 3*hz; i++) { + if (CSR_READ_2(sc, AN_EVENT_STAT) & AN_EV_CMD) + break; + (void)tsleep(sc, PWAIT, "anatch", 1); + } + CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_CMD); +} + +/* + * Read an LTV record from the NIC. + */ +static int +an_read_record(struct an_softc *sc, struct an_ltv_gen *ltv) +{ + u_int16_t *ptr; + int i, len; + + if (ltv->an_len == 0 || ltv->an_type == 0) + return EINVAL; + + /* Tell the NIC to enter record read mode. */ + if (an_cmd(sc, AN_CMD_ACCESS|AN_ACCESS_READ, ltv->an_type)) + return EIO; + + /* Seek to the record. */ + if (an_seek(sc, ltv->an_type, 0, AN_BAP1)) + return EIO; + + /* + * Read the length and record type and make sure they + * match what we expect (this verifies that we have enough + * room to hold all of the returned data). + */ + len = CSR_READ_2(sc, AN_DATA1); + if (len > ltv->an_len) { + if (sc->arpcom.ec_if.if_flags & IFF_DEBUG) + printf("%s: RID 0x%04x record length mismatch" + "-- expected %d, got %d\n", sc->an_dev.dv_xname, + ltv->an_type, ltv->an_len, len); + return ENOSPC; + } + + ltv->an_len = len; + + /* Now read the data. */ + ptr = <v->an_val; + for (i = 0; i < (ltv->an_len - 2) >> 1; i++) + ptr[i] = CSR_READ_2(sc, AN_DATA1); + + return 0; +} + +/* + * Same as read, except we inject data instead of reading it. + */ +static int +an_write_record(struct an_softc *sc, struct an_ltv_gen *ltv) +{ + u_int16_t *ptr; + int i; + + if (an_cmd(sc, AN_CMD_ACCESS|AN_ACCESS_READ, ltv->an_type)) + return EIO; + + if (an_seek(sc, ltv->an_type, 0, AN_BAP1)) + return EIO; + + CSR_WRITE_2(sc, AN_DATA1, ltv->an_len-2); + + ptr = <v->an_val; + for (i = 0; i < (ltv->an_len - 4) >> 1; i++) + CSR_WRITE_2(sc, AN_DATA1, ptr[i]); + + if (an_cmd(sc, AN_CMD_ACCESS|AN_ACCESS_WRITE, ltv->an_type)) + return EIO; + + return 0; +} + +static int +an_seek(struct an_softc *sc, int id, int off, int chan) +{ + int i, selreg, offreg; + + switch (chan) { + case AN_BAP0: + selreg = AN_SEL0; + offreg = AN_OFF0; + break; + case AN_BAP1: + selreg = AN_SEL1; + offreg = AN_OFF1; + break; + default: + panic("%s: invalid chan: %x\n", sc->an_dev.dv_xname, chan); + } + + CSR_WRITE_2(sc, selreg, id); + CSR_WRITE_2(sc, offreg, off); + + for (i = 0; i < AN_TIMEOUT; i++) { + if (!(CSR_READ_2(sc, offreg) & (AN_OFF_BUSY|AN_OFF_ERR))) + break; + DELAY(10); + } + if (i == AN_TIMEOUT) { + if (sc->arpcom.ec_if.if_flags & IFF_DEBUG) + printf("%s: seek(0x%x, 0x%x, 0x%x) timeout\n", + sc->an_dev.dv_xname, id, off, chan); + return ETIMEDOUT; + } + + return 0; +} + +static int +an_read_data(struct an_softc *sc, int id, int off, caddr_t buf, int len) +{ + int i; + u_int16_t *ptr; + u_int8_t *ptr2; + + if (off != -1) { + if (an_seek(sc, id, off, AN_BAP1)) + return EIO; + } + + ptr = (u_int16_t *)buf; + for (i = 0; i < len / 2; i++) + ptr[i] = CSR_READ_2(sc, AN_DATA1); + i *= 2; + if (i < len){ + ptr2 = (u_int8_t *)buf; + ptr2[i] = CSR_READ_1(sc, AN_DATA1); + } + + return 0; +} + +static int +an_write_data(struct an_softc *sc, int id, int off, caddr_t buf, int len) +{ + int i; + u_int16_t *ptr; + u_int8_t *ptr2; + + if (off != -1) { + if (an_seek(sc, id, off, AN_BAP0)) + return EIO; + } + + ptr = (u_int16_t *)buf; + for (i = 0; i < (len / 2); i++) + CSR_WRITE_2(sc, AN_DATA0, ptr[i]); + i *= 2; + if (i < len){ + ptr2 = (u_int8_t *)buf; + CSR_WRITE_1(sc, AN_DATA0, ptr2[i]); + } + + return 0; +} + +/* + * Allocate a region of memory inside the NIC and zero + * it out. + */ +static int +an_alloc_nicmem(struct an_softc *sc, int len, int *id) +{ + int i; + + if (an_cmd(sc, AN_CMD_ALLOC_MEM, len)) { + printf("%s: failed to allocate %d bytes on NIC\n", + sc->an_dev.dv_xname, len); + return ENOMEM; + } + + for (i = 0; i < AN_TIMEOUT; i++) { + if (CSR_READ_2(sc, AN_EVENT_STAT) & AN_EV_ALLOC) + break; + DELAY(10); + } + + if (i == AN_TIMEOUT) + return(ETIMEDOUT); + + CSR_WRITE_2(sc, AN_EVENT_ACK, AN_EV_ALLOC); + *id = CSR_READ_2(sc, AN_ALLOC_FID); + + if (an_seek(sc, *id, 0, AN_BAP0)) + return EIO; + + for (i = 0; i < len / 2; i++) + CSR_WRITE_2(sc, AN_DATA0, 0); + + return 0; +} + #ifdef ANCACHE /* Aironet signal strength cache code. * store signal/noise/quality on per MAC src basis in @@ -1656,7 +1755,7 @@ void an_cache_store (sc, eh, m, rx_quality) * address. */ if (saanp) { - ip = mtod(m, struct ip *); + ip = (struct ip *)(mtod(m, caddr_t) + 14); } /* do a linear search for a matching MAC address diff --git a/sys/dev/ic/anvar.h b/sys/dev/ic/anvar.h index f38ad9682082..0adf70b88a9a 100644 --- a/sys/dev/ic/anvar.h +++ b/sys/dev/ic/anvar.h @@ -1,4 +1,4 @@ -/* $NetBSD: anvar.h,v 1.5 2000/12/20 23:30:36 onoe Exp $ */ +/* $NetBSD: anvar.h,v 1.6 2001/06/21 12:49:06 onoe Exp $ */ /* * Copyright (c) 1997, 1998, 1999 * Bill Paul . All rights reserved. @@ -138,6 +138,11 @@ struct an_tx_ring_data { int an_tx_cons; }; +struct an_wepkey { + int an_wep_key[16]; + int an_wep_keylen; +}; + struct an_softc { struct device an_dev; struct ethercom arpcom; @@ -153,20 +158,20 @@ struct an_softc { struct an_ltv_caps an_caps; struct an_ltv_ssidlist an_ssidlist; struct an_ltv_aplist an_aplist; - struct an_ltv_wepkey an_temp_keys; - struct an_ltv_wepkey an_perm_keys; + struct an_wepkey an_wepkeys[IEEE80211_WEP_NKID]; + int an_tx_key; + int an_perskeylen[IEEE80211_WEP_NKID]; + int an_tx_perskey; int an_tx_rate; - int an_rxmode; int an_if_flags; - u_int8_t an_txbuf[1536]; struct an_tx_ring_data an_rdata; - struct an_ltv_stats an_stats; struct an_ltv_status an_status; u_int8_t an_associated; int an_sigitems; struct an_sigcache an_sigcache[MAXANCACHE]; int an_nextitem; struct callout an_stat_ch; + struct an_req an_reqbuf; }; int an_attach __P((struct an_softc *));