/*- * Copyright (c) 1999-2001, Ivan Sharov, Vitaly Belekhov. * Copyright (c) 2004 Stanislav Svirid. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR 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 AUTHOR 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. * * $RISS: if_arl/dev/arl/if_arl.c,v 1.7 2004/03/16 04:43:27 count Exp $ */ #include __FBSDID("$FreeBSD$"); #include "opt_inet.h" #ifdef INET #define ARLCACHE #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef INET #include #include #include #include #endif #include #include #include /*#define DEBUG */ #ifdef DEBUG #define D(x) {printf("arl%d: ", sc->arl_unit); printf x; } #else #define D(x) #endif /* * channel attention */ #define ARL_CHANNEL(sc) \ { \ D(("channel ctrl %x reg %x\n", sc->arl_control, ar->controlRegister)); \ ar->controlRegister = (sc->arl_control ^= ARL_CHANNEL_ATTENTION); \ } /* * Check registration */ #define ARL_CHECKREG(sc) (ar->registrationMode && ar->registrationStatus == 0) #define GET_ARL_PARAM(name) (arcfg.name = ar->name) #define SET_ARL_PARAM(name) (ar->name = arcfg.name) #ifndef BPF_MTAP #define BPF_MTAP(_ifp,_m) \ do { \ if ((_ifp)->if_bpf) \ bpf_mtap((_ifp), (_m)); \ } while (0) #endif #if __FreeBSD_version < 500100 #define BROADCASTADDR (etherbroadcastaddr) #define _ARL_CURPROC (curproc) #else #define BROADCASTADDR (sc->arl_ifp->if_broadcastaddr) #define _ARL_CURPROC (curthread) #endif static void arl_hwreset (struct arl_softc *); static void arl_reset (struct arl_softc *); static int arl_ioctl (struct ifnet *, u_long, caddr_t); static void arl_init (void *); static void arl_start (struct ifnet *); static void arl_watchdog (struct ifnet *); static void arl_waitreg (struct ifnet *); static void arl_enable (struct arl_softc *); static void arl_config (struct arl_softc *); static int arl_command (struct arl_softc *); static void arl_put (struct arl_softc *); static void arl_read (struct arl_softc *, caddr_t, int); static void arl_recv (struct arl_softc *); static struct mbuf* arl_get (caddr_t, int, int, struct ifnet *); #ifdef ARLCACHE static void arl_cache_store (struct arl_softc *, struct ether_header *, u_int8_t, u_int8_t, int); #endif static int arl_media_change (struct ifnet *); static void arl_media_status (struct ifnet *, struct ifmediareq *); static void arl_read_config (struct arl_softc *); devclass_t arl_devclass; u_int8_t rate2media[4] = { IFM_IEEE80211_DS354k, IFM_IEEE80211_DS512k, IFM_IEEE80211_DS1, IFM_IEEE80211_DS2 }; /* * Copy config values to local cache */ static void arl_read_config(sc) struct arl_softc *sc; { bzero(&arcfg, sizeof(arcfg)); bcopy(ar->lanCardNodeId, arcfg.lanCardNodeId, sizeof(ar->lanCardNodeId)); bcopy(ar->specifiedRouter, arcfg.specifiedRouter, sizeof(ar->specifiedRouter)); GET_ARL_PARAM(hardwareType); GET_ARL_PARAM(majorHardwareVersion); GET_ARL_PARAM(minorHardwareVersion); GET_ARL_PARAM(radioModule); GET_ARL_PARAM(channelSet); if (!arcfg.channelSet) arcfg.channelSet = ar->defaultChannelSet; GET_ARL_PARAM(channelNumber); GET_ARL_PARAM(spreadingCode); GET_ARL_PARAM(priority); GET_ARL_PARAM(receiveMode); arcfg.registrationMode = 1; /* set default TMA mode */ arcfg.txRetry = 0; bcopy(ar->name, arcfg.name, ARLAN_NAME_SIZE); bcopy(ar->systemId, arcfg.sid, 4 * sizeof(arcfg.sid[0])); } /* * Attach device */ int arl_attach(dev) device_t dev; { struct arl_softc* sc = device_get_softc(dev); struct ifnet *ifp; int attached, configured = 0; D(("attach\n")); ifp = sc->arl_ifp = if_alloc(IFT_ETHER); if (ifp == NULL) return (ENOSPC); configured = ar->configuredStatusFlag; attached = (ifp->if_softc != 0); if (!configured && bootverbose) device_printf(dev, "card is not configured\n"); else arl_read_config(sc); arl_reset(sc); /* Read config for default values if card was not configured */ if (!configured) arl_read_config(sc); /* Initialize ifnet structure. */ ifp->if_softc = sc; #if __FreeBSD_version < 502000 ifp->if_unit = sc->arl_unit; ifp->if_name = "arl"; #else if_initname(ifp, device_get_name(dev), device_get_unit(dev)); #endif ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST | IFF_NEEDSGIANT; ifp->if_start = arl_start; ifp->if_ioctl = arl_ioctl; ifp->if_watchdog = arl_watchdog; ifp->if_init = arl_init; ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; ifp->if_baudrate = 2000000; ifp->if_timer = 0; ifmedia_init(&sc->arl_ifmedia, 0, arl_media_change, arl_media_status); #define ADD(s, o) ifmedia_add(&sc->arl_ifmedia, \ IFM_MAKEWORD(IFM_IEEE80211, (s), (o), 0), 0, NULL) ADD(IFM_IEEE80211_DS354k, 0); ADD(IFM_IEEE80211_DS354k, IFM_IEEE80211_ADHOC); ADD(IFM_IEEE80211_DS512k, 0); ADD(IFM_IEEE80211_DS512k, IFM_IEEE80211_ADHOC); ADD(IFM_IEEE80211_DS1, 0); ADD(IFM_IEEE80211_DS1, IFM_IEEE80211_ADHOC); ADD(IFM_IEEE80211_DS2, 0); ADD(IFM_IEEE80211_DS2, IFM_IEEE80211_ADHOC); ifmedia_set(&sc->arl_ifmedia, IFM_MAKEWORD(IFM_IEEE80211, rate2media[arcfg.spreadingCode - 1], 0, 0)); #undef ADD /* * Attach the interface */ if (!attached) { #if __FreeBSD_version < 500100 ether_ifattach(ifp, ETHER_BPF_SUPPORTED); #else ether_ifattach(ifp, ar->lanCardNodeId); #endif } return (0); } /* * Hardware reset * reset all setting to default (setted ARLANDGS) */ static void arl_hwreset(sc) struct arl_softc *sc; { D(("hw reset\n")); ar->controlRegister = 1; DELAY(ARDELAY1); if (arl_wait_reset(sc, 0x24, ARDELAY1)) arl_stop(sc); ar->controlRegister = (sc->arl_control = 1); DELAY(ARDELAY1); } /* * wait arlan command */ static int arl_command(sc) struct arl_softc *sc; { int i; /* long stuppid delay ??? */ D(("commandByte %x\n", ar->commandByte)); for (i = 100000; ar->commandByte && i; i--) ; if (i == 0) ar->commandByte = 0; return (i == 0); } /* * Enable for recieveng */ static void arl_enable(sc) struct arl_softc *sc; { D(("enable\n")); sc->arl_control = (ARL_INTERRUPT_ENABLE | ARL_CLEAR_INTERRUPT); ar->controlRegister = sc->arl_control; arl_command(sc); ar->rxStatusVector = 0; ar->commandByte = 0x83; ar->commandParameter[0] = 1; ARL_CHANNEL(sc); arl_command(sc); } /* * reset and set user parameters */ static void arl_reset(sc) struct arl_softc *sc; { D(("reset\n")); arl_hwreset(sc); ar->resetFlag1 = 1; bzero((ar), 0x1FF0); /* fill memory board with 0 */ ar->resetFlag1 = 0; sc->arl_control = 0; /* if (arl_wait_reset(sc, 0x168, ARDELAY1)) return; */ #if 1 { int cnt = 0x168; int delay = ARDELAY1; ar->resetFlag = 0xFF; /* wish reset */ ar->controlRegister = 0; /* unreeze - do it */ while (ar->resetFlag && cnt--) DELAY(delay); if (cnt == 0) { printf("arl%d: reset timeout\n", sc->arl_unit); return; } D(("reset wait %d\n", 0x168 - cnt)); } #endif if (ar->diagnosticInfo != 0xff) { printf("arl%d: reset error\n", sc->arl_unit); return; } arl_config(sc); } /* * configure radio parameters */ static void arl_config(sc) struct arl_softc *sc; { int i; D(("config\n")); SET_ARL_PARAM(spreadingCode); SET_ARL_PARAM(channelNumber); SET_ARL_PARAM(channelSet); SET_ARL_PARAM(registrationMode); SET_ARL_PARAM(priority); SET_ARL_PARAM(receiveMode); bcopy(arcfg.sid, ar->systemId, 4 * sizeof(ar->systemId[0])); bcopy(arcfg.specifiedRouter, ar->specifiedRouter, ETHER_ADDR_LEN); bcopy(arcfg.lanCardNodeId, ar->lanCardNodeId, ETHER_ADDR_LEN); bzero(ar->name, ARLAN_NAME_SIZE); /* clear name */ strncpy(ar->name, arcfg.name, ARLAN_NAME_SIZE); ar->diagnosticInfo = 0; ar->commandByte = 1; ARL_CHANNEL(sc); DELAY(ARDELAY1); if (arl_command(sc)) { D(("config failed\n")); return; } for (i = 0x168; ar->diagnosticInfo == 0 && i; i--) DELAY(ARDELAY1); if (i == 0) { D(("config timeout\n")); return; } if (ar->diagnosticInfo != 0xff) { D(("config error\n")); return; } D(("config lanCardNodeId %6D\n", ar->lanCardNodeId, ":")); D(("config channel set %d, frequency %d, spread %d, mode %d\n", ar->channelSet, ar->channelNumber, ar->spreadingCode, ar->registrationMode)); /* clear quality stat */ bzero(sc->arl_sigcache, MAXARLCACHE * sizeof(struct arl_sigcache)); } /* * Socket Ioctl's. */ static int arl_ioctl(ifp, cmd, data) register struct ifnet *ifp; u_long cmd; caddr_t data; { struct arl_softc *sc = ifp->if_softc; struct ifreq *ifr = (struct ifreq *)data; struct ieee80211req *ireq = (struct ieee80211req *)data; d_thread_t *td = _ARL_CURPROC; struct arl_req arlan_io; int count, s, error = 0; u_int8_t tmpstr[IEEE80211_NWID_LEN*2]; u_int8_t *tmpptr; u_int32_t newsid; caddr_t user; D(("ioctl %lx\n", cmd)); s = splimp(); switch (cmd) { case SIOCSIFADDR: case SIOCGIFADDR: case SIOCSIFMTU: error = ether_ioctl(ifp, cmd, data); break; case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { if (!(ifp->if_flags & IFF_RUNNING)) arl_init(sc); } else { if (ifp->if_flags & IFF_RUNNING) arl_stop(sc); } break; case SIOCSIFMEDIA: case SIOCGIFMEDIA: error = ifmedia_ioctl(ifp, ifr, &sc->arl_ifmedia, cmd); break; case SIOCG80211: switch (ireq->i_type) { case IEEE80211_IOC_SSID: if (ireq->i_val != -1) { error = EINVAL; break; } bzero(tmpstr, IEEE80211_NWID_LEN); snprintf(tmpstr, IEEE80211_NWID_LEN - 1, "0x%08x", *(int *)arcfg.sid); ireq->i_len = IEEE80211_NWID_LEN; error = copyout(tmpstr, ireq->i_data, IEEE80211_NWID_LEN); break; case IEEE80211_IOC_STATIONNAME: ireq->i_len = sizeof(arcfg.name); tmpptr = arcfg.name; bzero(tmpstr, IEEE80211_NWID_LEN); bcopy(tmpptr, tmpstr, ireq->i_len); error = copyout(tmpstr, ireq->i_data, IEEE80211_NWID_LEN); break; case IEEE80211_IOC_CHANNEL: ireq->i_val = arcfg.channelNumber; break; case IEEE80211_IOC_POWERSAVE: ireq->i_val = (arcfg.registrationMode == 2 ? IEEE80211_POWERSAVE_PSP : IEEE80211_POWERSAVE_OFF); break; default: error = EINVAL; break; } break; case SIOCS80211: if ((error = suser(td))) break; switch (ireq->i_type) { case IEEE80211_IOC_SSID: if (ireq->i_len > 4) { error = EINVAL; break; } bzero(&newsid, sizeof(newsid)); error = copyin(ireq->i_data, (u_char *)(&newsid) + 4 - ireq->i_len, ireq->i_len); if (error) break; newsid = htonl(newsid); if (newsid < 0 || newsid % 2) { error = EINVAL; break; } bcopy(&newsid, arcfg.sid, sizeof(arcfg.sid)); break; case IEEE80211_IOC_STATIONNAME: if (ireq->i_len > ARLAN_NAME_SIZE) { error = EINVAL; break; } bzero(arcfg.name, ARLAN_NAME_SIZE); error = copyin(ireq->i_data, arcfg.name, ireq->i_len); break; case IEEE80211_IOC_CHANNEL: if (ireq->i_val < 0 || ireq->i_val > 5) { error = EINVAL; break; } arcfg.channelNumber = ireq->i_val; break; case IEEE80211_IOC_POWERSAVE: switch (ireq->i_val) { case IEEE80211_POWERSAVE_OFF: if (arcfg.registrationMode == 2) arcfg.registrationMode = 1; break; case IEEE80211_POWERSAVE_ON: case IEEE80211_POWERSAVE_PSP: arcfg.registrationMode = 2; break; default: error = EINVAL; break; } break; default: error = EINVAL; break; } if (!error) arl_config(sc); break; #define GET_PARAM(name) (arlan_io.cfg.name = arcfg.name) #define GET_COPY_PARAM(name) \ { \ bzero(arlan_io.cfg.name, sizeof(arlan_io.cfg.name)); \ bcopy(arcfg.name, arlan_io.cfg.name, sizeof(arlan_io.cfg.name)); \ } case SIOCGARLALL: bzero(&arlan_io, sizeof(arlan_io)); if (!suser(td)) { bcopy(ar->systemId, arlan_io.cfg.sid, 4); } GET_COPY_PARAM(name); GET_COPY_PARAM(lanCardNodeId); GET_COPY_PARAM(specifiedRouter); GET_PARAM(channelNumber); GET_PARAM(channelSet); GET_PARAM(spreadingCode); GET_PARAM(registrationMode); GET_PARAM(hardwareType); GET_PARAM(majorHardwareVersion); GET_PARAM(minorHardwareVersion); GET_PARAM(radioModule); GET_PARAM(priority); GET_PARAM(receiveMode); GET_PARAM(txRetry); user = (void *)ifr->ifr_data; for (count = 0; count < sizeof(arlan_io); count++) if (subyte(user + count, ((char *)&arlan_io)[count])) return (EFAULT); break; #define SET_PARAM(name) \ do { \ if (arlan_io.what_set & ARLAN_SET_##name) \ arcfg.name = arlan_io.cfg.name; \ } while (0) #define SET_COPY_PARAM(name) \ do { \ if (arlan_io.what_set & ARLAN_SET_##name) { \ bzero(arcfg.name, sizeof(arcfg.name)); \ bcopy(arlan_io.cfg.name, arcfg.name, sizeof(arcfg.name)); \ } \ } while (0) case SIOCSARLALL: if (suser(td)) break; user = (void *)ifr->ifr_data; for (count = 0; count < sizeof(arlan_io); count++) { if (fubyte(user + count) < 0) return (EFAULT); } bcopy(user, (char *)&arlan_io, sizeof(arlan_io)); D(("need set 0x%04x\n", arlan_io.what_set)); if (arlan_io.what_set) { SET_COPY_PARAM(name); SET_COPY_PARAM(sid); SET_COPY_PARAM(specifiedRouter); SET_COPY_PARAM(lanCardNodeId); SET_PARAM(channelSet); SET_PARAM(channelNumber); SET_PARAM(spreadingCode); SET_PARAM(registrationMode); SET_PARAM(priority); SET_PARAM(receiveMode); SET_PARAM(txRetry); arl_config(sc); } #undef SET_COPY_PARAM #undef SET_PARAM #undef GET_COPY_PARAM #undef GET_PARAM break; #ifdef ARLCACHE case SIOCGARLQLT: user = (void *)ifr->ifr_data; for (count = 0; count < sizeof(sc->arl_sigcache); count++) { if (fubyte(user + count) < 0) return (EFAULT); } while (ar->interruptInProgress) ; /* wait */ bcopy(&(sc->arl_sigcache), (void *)ifr->ifr_data, sizeof(sc->arl_sigcache)); break; #endif case SIOCGARLSTB: user = (void *)ifr->ifr_data; for (count = 0; count < sizeof(struct arl_stats); count++) { if (fubyte(user + count) < 0) return (EFAULT); } while (ar->lancpuLock) ; ar->hostcpuLock = 1; bcopy(&(ar->stat), (void *)ifr->ifr_data, sizeof(struct arl_stats)); ar->hostcpuLock = 0; break; default: error = EINVAL; } splx(s); return (error); } /* * Wait registration */ static void arl_waitreg(ifp) struct ifnet *ifp; { struct arl_softc *sc = ifp->if_softc; D(("wait reg\n")); if (ifp->if_flags & IFF_RUNNING) { if (ARL_CHECKREG(sc)) { /* wait registration */ D(("wait registration\n")); ifp->if_watchdog = arl_waitreg; ifp->if_timer = 2; } else { /* registration restored */ D(("registration restored\n")); ifp->if_timer = 0; arl_init(sc); } } } /* * Handle transmit timeouts. */ static void arl_watchdog(ifp) struct ifnet *ifp; { struct arl_softc *sc = ifp->if_softc; if (!(ifp->if_flags & IFF_RUNNING)) return; D(("device timeout\n")); if (ARL_CHECKREG(sc)) { /* Lost registratoin */ D(("timeout lost registration\n")); ifp->if_watchdog = arl_waitreg; ifp->if_timer = 2; } } /* * Initialize */ static void arl_init(xsc) void *xsc; { struct arl_softc *sc = xsc; struct ifnet *ifp = sc->arl_ifp; int s; D(("init\n")); s = splimp(); if (ARL_CHECKREG(sc)) arl_reset(sc); arl_enable(sc); /* set flags */ ifp->if_flags |= IFF_RUNNING; ifp->if_flags &= ~IFF_OACTIVE; arl_start(ifp); splx(s); D(("init done\n")); } /* * Put buffer into arlan buffer and start transmit */ static void arl_put(sc) struct arl_softc *sc; { struct arl_tx_param txp; int i; if (ARL_CHECKREG(sc)) sc->arl_ifp->if_oerrors++; /* copy dst adr */ for(i = 0; i < 6; i++) txp.dest[i] = sc->arl_tx[i]; txp.clear = 0; txp.retries = arcfg.txRetry; /* use default value */ txp.routing = 1; txp.scrambled = 0; txp.offset = (intptr_t)ar->txBuffer - (intptr_t)(ar); txp.length = sc->tx_len - ARLAN_HEADER_SIZE; #ifdef SEND_ARLAN_HEADER if (ar->registrationMode != 1) txp.length = sc->tx_len; #endif /* copy from internal buffer to adapter memory */ #ifdef SEND_ARLAN_HEADER if (ar->registrationMode) #endif bcopy(sc->arl_tx + ARLAN_HEADER_SIZE, ar->txBuffer, sc->tx_len - ARLAN_HEADER_SIZE); #ifdef SEND_ARLAN_MODE else bcopy(sc->arl_tx, ar->txBuffer, sc->tx_len); #endif /* copy command parametr */ bcopy(&txp, ar->commandParameter, 14); ar->commandByte = 0x85; /* send command */ ARL_CHANNEL(sc); if (arl_command(sc)) sc->arl_ifp->if_oerrors++; } /* * start output */ static void arl_start(ifp) struct ifnet *ifp; { struct arl_softc *sc; struct mbuf *m; struct mbuf *m0 = NULL; sc = ifp->if_softc; D(("start\n")); /* Don't do anything if output is active */ if (ifp->if_flags & IFF_OACTIVE) return; /* Dequeue the next datagram */ IF_DEQUEUE(&ifp->if_snd, m0); /* If there's nothing to send, return. */ if (m0 != NULL) { ifp->if_flags |= IFF_OACTIVE; /* Copy the datagram to the buffer. */ sc->tx_len = 0; for(m = m0; m != NULL; m = m->m_next) { if (m->m_len == 0) continue; bcopy(mtod(m, caddr_t), sc->arl_tx + sc->tx_len, m->m_len); sc->tx_len += m->m_len; } /* if packet size is less than minimum ethernet frame size, * pad it with zeroes to that size */ if (sc->tx_len < ETHER_MIN_LEN) { bzero(sc->arl_tx + sc->tx_len, ETHER_MIN_LEN - sc->tx_len); sc->tx_len = ETHER_MIN_LEN; } /* Give the packet to the bpf, if any */ BPF_MTAP(ifp, m0); m_freem(m0); /* Now transmit the datagram */ ifp->if_timer = 1; /* wait 1 sec */ ifp->if_watchdog = arl_watchdog; arl_put(sc); } } /* * stop interface */ void arl_stop(sc) struct arl_softc *sc; { struct ifnet *ifp; int s; s = splimp(); ifp = sc->arl_ifp; ifp->if_timer = 0; /* disable timer */ ifp->if_flags &= ~(IFF_RUNNING|IFF_OACTIVE); /* arl_hwreset(unit); */ sc->rx_len = 0; sc->tx_len = 0; /* disable interrupt */ ar->controlRegister = 0; splx(s); } /* * Pull read data off a interface. * Len is length of data, with local net header stripped. */ static struct mbuf* arl_get(buf, totlen, off0, ifp) caddr_t buf; int totlen; int off0; struct ifnet * ifp; { struct mbuf *top, **mp, *m; int off = off0, len; caddr_t cp = buf; char *epkt; cp = buf; epkt = cp + totlen; if (off) { cp += off + 2 * sizeof(u_short); totlen -= 2 * sizeof(u_short); } MGETHDR(m, M_DONTWAIT, MT_DATA); if (m == 0) return (0); m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = totlen; m->m_len = MHLEN; top = 0; mp = ⊤ while (totlen > 0) { if (top) { MGET(m, M_DONTWAIT, MT_DATA); if (m == 0) { m_freem(top); return (0); } m->m_len = MLEN; } len = min(totlen, epkt - cp); if (len >= MINCLSIZE) { MCLGET(m, M_DONTWAIT); if (m->m_flags & M_EXT) m->m_len = len = min(len, MCLBYTES); else len = m->m_len; } else { /* * * Place initial small packet/header at end of mbuf. * */ if (len < m->m_len) { if (top == 0 && len + max_linkhdr <= m->m_len) m->m_data += max_linkhdr; m->m_len = len; } else len = m->m_len; } bcopy(cp, mtod(m, caddr_t), (unsigned)len); cp += len; *mp = m; mp = &m->m_next; totlen -= len; if (cp == epkt) cp = buf; } return (top); } /* ------------------------------------------------------------------ * * Pass a packet up to the higher levels. * */ static void arl_read(sc, buf, len) struct arl_softc *sc; caddr_t buf; int len; { register struct ether_header *eh; struct ifnet *ifp = sc->arl_ifp; struct mbuf *m; eh = (struct ether_header *)buf; /* * Check if there's a bpf filter listening on this interface. * If so, hand off the raw packet to bpf. */ if (ifp->if_bpf) { /* * Note that the interface cannot be in promiscuous mode if * there are no bpf listeners. And if el are in promiscuous * mode, el have to check if this packet is really ours. * * This test does not support multicasts. */ if ((ifp->if_flags & IFF_PROMISC) && bcmp(eh->ether_dhost, IFP2ENADDR(sc->arl_ifp), sizeof(eh->ether_dhost)) != 0 && bcmp(eh->ether_dhost, BROADCASTADDR, sizeof(eh->ether_dhost)) != 0) return; } /* * Pull packet off interface. */ #if __FreeBSD_version < 500100 buf += sizeof(struct ether_header); len -= sizeof(struct ether_header); #endif m = arl_get(buf, len, 0, ifp); if (m == 0) return; #ifdef ARLCACHE arl_cache_store(sc, eh, ar->rxQuality & 0x0f, (ar->rxQuality & 0xf0) >> 4, ARLCACHE_RX); #endif #if __FreeBSD_version < 500100 ether_input(ifp, eh, m); #else (*ifp->if_input)(ifp, m); #endif } /* * get packet from adapter */ static void arl_recv(sc) struct arl_softc *sc; { sc->rx_len = ar->rxLength; if (sc->rx_len) { #ifdef SEND_ARLAN_HEADER if (ar->registrationMode == 1) { #endif bcopy(ar->ultimateDestAddress, sc->arl_rx, 6); bcopy(ar->rxSrc, (char*)sc->arl_rx + 6, 6); bcopy((char *)(ar) + ar->rxOffset, (char *)sc->arl_rx + 12, sc->rx_len); sc->rx_len += ARLAN_HEADER_SIZE; #ifdef SEND_ARLAN_HEADER } else { bcopy((char *)(ar) + ar->rxOffset, (char *)sc->arl_rx, sc->rx_len); } #endif } } /* * Ethernet interface interrupt processor */ void arl_intr(arg) void *arg; { register struct arl_softc *sc = (struct arl_softc *) arg; struct ifnet *ifp = sc->arl_ifp; /* enable interrupt */ ar->controlRegister = (sc->arl_control & ~ARL_CLEAR_INTERRUPT); ar->controlRegister = (sc->arl_control | ARL_CLEAR_INTERRUPT); if (ar->txStatusVector) { if (ar->txStatusVector != 1) sc->arl_ifp->if_collisions++; ifp->if_timer = 0; /* disable timer */ ifp->if_flags &= ~IFF_OACTIVE; arl_start(ifp); ar->txStatusVector = 0; #ifdef ARLCACHE arl_cache_store(sc, (struct ether_header *)(sc->arl_tx), ar->txAckQuality & 0x0f, (ar->txAckQuality & 0xf0) >> 4, ARLCACHE_TX); #endif } if (ar->rxStatusVector) { if (ar->rxStatusVector == 1) { /* it is data frame */ arl_recv(sc); arl_read(sc, sc->arl_rx, sc->rx_len); ifp->if_opackets++; } ar->rxStatusVector = 0; ar->commandByte = 0x83; ar->commandParameter[0] = 1; ARL_CHANNEL(sc); if (arl_command(sc)) ifp->if_ierrors++; } return; } /* * waiting for resetFlag dropped */ int arl_wait_reset(sc, cnt, delay) struct arl_softc *sc; int cnt; int delay; { D(("wait_reset cnt=%d delay=%d\n", cnt, delay)); ar->resetFlag = 1; /* wish reset */ ar->controlRegister = 0; /* unreeze - do it */ while (ar->resetFlag && cnt--) DELAY(delay); D(("reset done. %d cycles left\n", cnt)); if (cnt == 0) printf("arl%d: reset failed\n", sc->arl_unit); return (cnt == 0); } /* * Allocate an irq resource with the given resource id */ int arl_alloc_irq(dev, rid, flags) device_t dev; int rid; int flags; { struct arl_softc *sc = device_get_softc(dev); struct resource *res; res = bus_alloc_resource_any( dev, SYS_RES_IRQ, &rid, (RF_ACTIVE | flags)); if (res) { sc->irq_rid = rid; sc->irq_res = res; return (0); } else return (ENOENT); } /* * Allocate an memory resource with the given resource id */ int arl_alloc_memory(dev, rid, size) device_t dev; int rid; int size; { struct arl_softc *sc = device_get_softc(dev); struct resource *res; res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0ul, ~0ul, size, RF_ACTIVE); if (res) { sc->mem_rid = rid; sc->mem_res = res; return (0); } else return (ENOENT); } /* * Release all resources */ void arl_release_resources(dev) device_t dev; { struct arl_softc *sc = device_get_softc(dev); if (sc->mem_res) { bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem_res); sc->mem_res = 0; } if (sc->irq_res) { bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq_res); sc->irq_res = 0; } } #ifdef ARLCACHE static void arl_cache_store(sc, eh, level, quality, dir) struct arl_softc *sc; struct ether_header *eh; u_int8_t level; u_int8_t quality; int dir; { int i; static int cache_slot = 0; static int wrapindex = 0; u_int8_t zero[6] = {0, 0, 0, 0, 0, 0}; u_char *mac; if ((ntohs(eh->ether_type) != ETHERTYPE_IP)) { return; } mac = (dir == ARLCACHE_RX ? eh->ether_shost : eh->ether_dhost); for (i = 0; i < MAXARLCACHE; i++) { if (!bcmp(zero, sc->arl_sigcache[i].macsrc, 6) || !bcmp(mac, sc->arl_sigcache[i].macsrc, 6)) break; } if (i < MAXARLCACHE) cache_slot = i; else { if (wrapindex == MAXARLCACHE) wrapindex = 0; cache_slot = wrapindex++; } bcopy(dir == ARLCACHE_RX ? eh->ether_shost : eh->ether_dhost, sc->arl_sigcache[cache_slot].macsrc, 6); sc->arl_sigcache[cache_slot].level[dir] = level; sc->arl_sigcache[cache_slot].quality[dir] = quality; } #endif static int arl_media_change(ifp) struct ifnet *ifp; { struct arl_softc *sc = ifp->if_softc; int otype = arcfg.registrationMode; int orate = arcfg.spreadingCode; int nrate, i; nrate = IFM_SUBTYPE(sc->arl_ifmedia.ifm_cur->ifm_media); for(i = 1; i <= 4; i++) { if (rate2media[i - 1] == nrate) break; } if (i == 5) return (EINVAL); arcfg.spreadingCode = i; /* XXX Need fix for PSP mode */ if ((sc->arl_ifmedia.ifm_cur->ifm_media & IFM_IEEE80211_ADHOC) != 0) arcfg.registrationMode = 0; else arcfg.registrationMode = 1; if (otype != arcfg.registrationMode || orate != arcfg.spreadingCode) arl_config(sc); return (0); } static void arl_media_status(ifp, imr) struct ifnet *ifp; struct ifmediareq *imr; { struct arl_softc *sc = ifp->if_softc; imr->ifm_active = IFM_IEEE80211; if (arcfg.registrationMode == 0) imr->ifm_active |= IFM_IEEE80211_ADHOC; imr->ifm_active |= IFM_MAKEWORD(IFM_IEEE80211, rate2media[arcfg.spreadingCode - 1], 0, 0); imr->ifm_status = IFM_AVALID; if (!ARL_CHECKREG(sc)) imr->ifm_status |= IFM_ACTIVE; }