diff options
author | fjoe <fjoe@FreeBSD.org> | 2002-01-08 20:03:13 +0000 |
---|---|---|
committer | fjoe <fjoe@FreeBSD.org> | 2002-01-08 20:03:13 +0000 |
commit | 2053aff54edfb4c3ac126a8b88700dbc70a7969f (patch) | |
tree | bb285377f57f0c2656c28eaca30fbc3434b7cb8a /sys/net/if_arcsubr.c | |
parent | 849d50be93de0733f49a8209a1729f2787710113 (diff) | |
download | FreeBSD-src-2053aff54edfb4c3ac126a8b88700dbc70a7969f.zip FreeBSD-src-2053aff54edfb4c3ac126a8b88700dbc70a7969f.tar.gz |
- generic Arcnet framework
- device driver for SMC COM90cx6 Arcnet network adapters
Obtained from: NetBSD
Diffstat (limited to 'sys/net/if_arcsubr.c')
-rw-r--r-- | sys/net/if_arcsubr.c | 780 |
1 files changed, 780 insertions, 0 deletions
diff --git a/sys/net/if_arcsubr.c b/sys/net/if_arcsubr.c new file mode 100644 index 0000000..d47dee9 --- /dev/null +++ b/sys/net/if_arcsubr.c @@ -0,0 +1,780 @@ +/* $NetBSD: if_arcsubr.c,v 1.36 2001/06/14 05:44:23 itojun Exp $ */ +/* $FreeBSD$ */ + +/* + * Copyright (c) 1994, 1995 Ignatios Souvatzis + * Copyright (c) 1982, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * from: NetBSD: if_ethersubr.c,v 1.9 1994/06/29 06:36:11 cgd Exp + * @(#)if_ethersubr.c 8.1 (Berkeley) 6/10/93 + * + */ +#include "opt_inet.h" +#include "opt_inet6.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/errno.h> +#include <sys/syslog.h> + +#include <machine/cpu.h> + +#include <net/if.h> +#include <net/netisr.h> +#include <net/route.h> +#include <net/if_dl.h> +#include <net/if_types.h> +#include <net/if_arc.h> +#include <net/if_arp.h> +#include <net/bpf.h> + +#if defined(INET) || defined(INET6) +#include <netinet/in.h> +#include <netinet/in_var.h> +#include <netinet/if_ether.h> +#endif + +#ifdef INET6 +#include <netinet6/nd6.h> +#endif + +MODULE_VERSION(arcnet, 1); + +#define ARCNET_ALLOW_BROKEN_ARP + +static struct mbuf *arc_defrag __P((struct ifnet *, struct mbuf *)); + +u_int8_t arcbroadcastaddr = 0; + +#define senderr(e) { error = (e); goto bad;} +#define SIN(s) ((struct sockaddr_in *)s) + +/* + * ARCnet output routine. + * Encapsulate a packet of type family for the local net. + * Assumes that ifp is actually pointer to arccom structure. + */ +int +arc_output(ifp, m, dst, rt0) + struct ifnet *ifp; + struct mbuf *m; + struct sockaddr *dst; + struct rtentry *rt0; +{ + struct mbuf *mcopy; + struct rtentry *rt; + struct arccom *ac; + struct arc_header *ah; + struct arphdr *arph; + int error; + u_int8_t atype, adst; +#if __FreeBSD_version < 500000 + int s; +#endif + + if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) + return(ENETDOWN); /* m, m1 aren't initialized yet */ + + error = 0; + ac = (struct arccom *)ifp; + mcopy = NULL; + + if ((rt = rt0)) { + if ((rt->rt_flags & RTF_UP) == 0) { + if ((rt0 = rt = rtalloc1(dst, 1, 0UL))) + rt->rt_refcnt--; + else + senderr(EHOSTUNREACH); + } + if (rt->rt_flags & RTF_GATEWAY) { + if (rt->rt_gwroute == 0) + goto lookup; + if (((rt = rt->rt_gwroute)->rt_flags & RTF_UP) == 0) { + rtfree(rt); rt = rt0; + lookup: rt->rt_gwroute = rtalloc1(rt->rt_gateway, 1, 0UL); + if ((rt = rt->rt_gwroute) == 0) + senderr(EHOSTUNREACH); + } + } + if (rt->rt_flags & RTF_REJECT) + if (rt->rt_rmx.rmx_expire == 0 || + time_second < rt->rt_rmx.rmx_expire) + senderr(rt == rt0 ? EHOSTDOWN : EHOSTUNREACH); + } + + switch (dst->sa_family) { +#ifdef INET + case AF_INET: + + /* + * For now, use the simple IP addr -> ARCnet addr mapping + */ + if (m->m_flags & (M_BCAST|M_MCAST)) + adst = arcbroadcastaddr; /* ARCnet broadcast address */ + else if (ifp->if_flags & IFF_NOARP) + adst = ntohl(SIN(dst)->sin_addr.s_addr) & 0xFF; + else if (!arpresolve(ifp, rt, m, dst, &adst, rt0)) + return 0; /* not resolved yet */ + + /* If broadcasting on a simplex interface, loopback a copy */ + if ((m->m_flags & (M_BCAST|M_MCAST)) && + (ifp->if_flags & IFF_SIMPLEX)) + mcopy = m_copy(m, 0, (int)M_COPYALL); + atype = (ifp->if_flags & IFF_LINK0) ? + ARCTYPE_IP_OLD : ARCTYPE_IP; + break; +#endif +#ifdef INET6 + case AF_INET6: +#ifdef OLDIP6OUTPUT + if (!nd6_resolve(ifp, rt, m, dst, (u_char *)&adst)) + return(0); /* if not yet resolves */ +#else + if (!nd6_storelladdr(ifp, rt, m, dst, (u_char *)&adst)) + return(0); /* it must be impossible, but... */ +#endif /* OLDIP6OUTPUT */ + atype = ARCTYPE_INET6; + break; +#endif + + case AF_UNSPEC: + ah = (struct arc_header *)dst->sa_data; + adst = ah->arc_dhost; + atype = ah->arc_type; + + if (atype == ARCTYPE_ARP) { + atype = (ifp->if_flags & IFF_LINK0) ? + ARCTYPE_ARP_OLD: ARCTYPE_ARP; + +#ifdef ARCNET_ALLOW_BROKEN_ARP + /* + * XXX It's not clear per RFC826 if this is needed, but + * "assigned numbers" say this is wrong. + * However, e.g., AmiTCP 3.0Beta used it... we make this + * switchable for emergency cases. Not perfect, but... + */ + arph = mtod(m, struct arphdr *); + if (ifp->if_flags & IFF_LINK2) + arph->ar_pro = atype - 1; +#endif + } + break; + + default: + printf("%s%d: can't handle af%d\n", ifp->if_name, ifp->if_unit, + dst->sa_family); + senderr(EAFNOSUPPORT); + } + + if (mcopy) + (void) if_simloop(ifp, mcopy, dst->sa_family, 0); + + M_PREPEND(m, ARC_HDRLEN, M_DONTWAIT); + if (m == 0) + senderr(ENOBUFS); + ah = mtod(m, struct arc_header *); + ah->arc_type = atype; + ah->arc_dhost = adst; + ah->arc_shost = *IF_LLADDR(ifp); + + if (ifp->if_bpf) + bpf_mtap(ifp, m); + +#if __FreeBSD_version < 500000 + s = splimp(); + + /* + * Queue message on interface, and start output if interface + * not yet active. + */ + if (IF_QFULL(&ifp->if_snd)) { + IF_DROP(&ifp->if_snd); + splx(s); + senderr(ENOBUFS); + } + ifp->if_obytes += m->m_pkthdr.len; + IF_ENQUEUE(&ifp->if_snd, m); + if ((ifp->if_flags & IFF_OACTIVE) == 0) + (*ifp->if_start)(ifp); + splx(s); +#else + if (!IF_HANDOFF(&ifp->if_snd, m, ifp)) { + m = 0; + senderr(ENOBUFS); + } +#endif + + return (error); + +bad: + if (m) + m_freem(m); + return (error); +} + +void +arc_frag_init(ifp) + struct ifnet *ifp; +{ + struct arccom *ac; + + ac = (struct arccom *)ifp; + ac->curr_frag = 0; +} + +struct mbuf * +arc_frag_next(ifp) + struct ifnet *ifp; +{ + struct arccom *ac; + struct mbuf *m; + struct arc_header *ah; + + ac = (struct arccom *)ifp; + if ((m = ac->curr_frag) == 0) { + int tfrags; + + /* dequeue new packet */ + IF_DEQUEUE(&ifp->if_snd, m); + if (m == 0) + return 0; + + ah = mtod(m, struct arc_header *); + if (!arc_isphds(ah->arc_type)) + return m; + + ++ac->ac_seqid; /* make the seqid unique */ + tfrags = (m->m_pkthdr.len + 503) / 504; + ac->fsflag = 2 * tfrags - 3; + ac->sflag = 0; + ac->rsflag = ac->fsflag; + ac->arc_dhost = ah->arc_dhost; + ac->arc_shost = ah->arc_shost; + ac->arc_type = ah->arc_type; + + m_adj(m, ARC_HDRLEN); + ac->curr_frag = m; + } + + /* split out next fragment and return it */ + if (ac->sflag < ac->fsflag) { + /* we CAN'T have short packets here */ + ac->curr_frag = m_split(m, 504, M_DONTWAIT); + if (ac->curr_frag == 0) { + m_free(m); + return 0; + } + + M_PREPEND(m, ARC_HDRNEWLEN, M_DONTWAIT); + if (m == 0) { + m_free(ac->curr_frag); + ac->curr_frag = 0; + + m_free(m); + return 0; + } + + ah = mtod(m, struct arc_header *); + ah->arc_flag = ac->rsflag; + ah->arc_seqid = ac->ac_seqid; + + ac->sflag += 2; + ac->rsflag = ac->sflag; + } else if ((m->m_pkthdr.len >= + ARC_MIN_FORBID_LEN - ARC_HDRNEWLEN + 2) && + (m->m_pkthdr.len <= + ARC_MAX_FORBID_LEN - ARC_HDRNEWLEN + 2)) { + ac->curr_frag = 0; + + M_PREPEND(m, ARC_HDRNEWLEN_EXC, M_DONTWAIT); + if (m == 0) + return 0; + + ah = mtod(m, struct arc_header *); + ah->arc_flag = 0xFF; + ah->arc_seqid = 0xFFFF; + ah->arc_type2 = ac->arc_type; + ah->arc_flag2 = ac->sflag; + ah->arc_seqid2 = ac->ac_seqid; + } else { + ac->curr_frag = 0; + + M_PREPEND(m, ARC_HDRNEWLEN, M_DONTWAIT); + if (m == 0) + return 0; + + ah = mtod(m, struct arc_header *); + ah->arc_flag = ac->sflag; + ah->arc_seqid = ac->ac_seqid; + } + + ah->arc_dhost = ac->arc_dhost; + ah->arc_shost = ac->arc_shost; + ah->arc_type = ac->arc_type; + + return m; +} + +/* + * Defragmenter. Returns mbuf if last packet found, else + * NULL. frees imcoming mbuf as necessary. + */ + +__inline struct mbuf * +arc_defrag(ifp, m) + struct ifnet *ifp; + struct mbuf *m; +{ + struct arc_header *ah, *ah1; + struct arccom *ac; + struct ac_frag *af; + struct mbuf *m1; + char *s; + int newflen; + u_char src,dst,typ; + + ac = (struct arccom *)ifp; + + if (m->m_len < ARC_HDRNEWLEN) { + m = m_pullup(m, ARC_HDRNEWLEN); + if (m == NULL) { + ++ifp->if_ierrors; + return NULL; + } + } + + ah = mtod(m, struct arc_header *); + typ = ah->arc_type; + + if (!arc_isphds(typ)) + return m; + + src = ah->arc_shost; + dst = ah->arc_dhost; + + if (ah->arc_flag == 0xff) { + m_adj(m, 4); + + if (m->m_len < ARC_HDRNEWLEN) { + m = m_pullup(m, ARC_HDRNEWLEN); + if (m == NULL) { + ++ifp->if_ierrors; + return NULL; + } + } + + ah = mtod(m, struct arc_header *); + } + + af = &ac->ac_fragtab[src]; + m1 = af->af_packet; + s = "debug code error"; + + if (ah->arc_flag & 1) { + /* + * first fragment. We always initialize, which is + * about the right thing to do, as we only want to + * accept one fragmented packet per src at a time. + */ + if (m1 != NULL) + m_freem(m1); + + af->af_packet = m; + m1 = m; + af->af_maxflag = ah->arc_flag; + af->af_lastseen = 0; + af->af_seqid = ah->arc_seqid; + + return NULL; + /* notreached */ + } else { + /* check for unfragmented packet */ + if (ah->arc_flag == 0) + return m; + + /* do we have a first packet from that src? */ + if (m1 == NULL) { + s = "no first frag"; + goto outofseq; + } + + ah1 = mtod(m1, struct arc_header *); + + if (ah->arc_seqid != ah1->arc_seqid) { + s = "seqid differs"; + goto outofseq; + } + + if (typ != ah1->arc_type) { + s = "type differs"; + goto outofseq; + } + + if (dst != ah1->arc_dhost) { + s = "dest host differs"; + goto outofseq; + } + + /* typ, seqid and dst are ok here. */ + + if (ah->arc_flag == af->af_lastseen) { + m_freem(m); + return NULL; + } + + if (ah->arc_flag == af->af_lastseen + 2) { + /* ok, this is next fragment */ + af->af_lastseen = ah->arc_flag; + m_adj(m,ARC_HDRNEWLEN); + + /* + * m_cat might free the first mbuf (with pkthdr) + * in 2nd chain; therefore: + */ + + newflen = m->m_pkthdr.len; + + m_cat(m1,m); + + m1->m_pkthdr.len += newflen; + + /* is it the last one? */ + if (af->af_lastseen > af->af_maxflag) { + af->af_packet = NULL; + return(m1); + } else + return NULL; + } + s = "other reason"; + /* if all else fails, it is out of sequence, too */ + } +outofseq: + if (m1) { + m_freem(m1); + af->af_packet = NULL; + } + + if (m) + m_freem(m); + + log(LOG_INFO,"%s%d: got out of seq. packet: %s\n", + ifp->if_name, ifp->if_unit, s); + + return NULL; +} + +/* + * return 1 if Packet Header Definition Standard, else 0. + * For now: old IP, old ARP aren't obviously. Lacking correct information, + * we guess that besides new IP and new ARP also IPX and APPLETALK are PHDS. + * (Apple and Novell corporations were involved, among others, in PHDS work). + * Easiest is to assume that everybody else uses that, too. + */ +int +arc_isphds(type) + u_int8_t type; +{ + return (type != ARCTYPE_IP_OLD && + type != ARCTYPE_ARP_OLD && + type != ARCTYPE_DIAGNOSE); +} + +/* + * Process a received Arcnet packet; + * the packet is in the mbuf chain m with + * the ARCnet header. + */ +void +arc_input(ifp, m) + struct ifnet *ifp; + struct mbuf *m; +{ + struct arc_header *ah; + struct ifqueue *inq; + u_int8_t atype; +#ifdef INET + struct arphdr *arph; +#endif +#if __FreeBSD_version < 500000 + int s; +#endif + + if ((ifp->if_flags & IFF_UP) == 0) { + m_freem(m); + return; + } + + /* possibly defragment: */ + m = arc_defrag(ifp, m); + if (m == NULL) + return; + + if (ifp->if_bpf) + bpf_mtap(ifp, m); + + ah = mtod(m, struct arc_header *); + + ifp->if_ibytes += m->m_pkthdr.len; + + if (arcbroadcastaddr == ah->arc_dhost) { + m->m_flags |= M_BCAST|M_MCAST; + ifp->if_imcasts++; + } + + atype = ah->arc_type; + switch (atype) { +#ifdef INET + case ARCTYPE_IP: + m_adj(m, ARC_HDRNEWLEN); + schednetisr(NETISR_IP); + inq = &ipintrq; + break; + + case ARCTYPE_IP_OLD: + m_adj(m, ARC_HDRLEN); + schednetisr(NETISR_IP); + inq = &ipintrq; + break; + + case ARCTYPE_ARP: + if (ifp->if_flags & IFF_NOARP) { + /* Discard packet if ARP is disabled on interface */ + m_freem(m); + return; + } + m_adj(m, ARC_HDRNEWLEN); + schednetisr(NETISR_ARP); + inq = &arpintrq; +#ifdef ARCNET_ALLOW_BROKEN_ARP + mtod(m, struct arphdr *)->ar_pro = htons(ETHERTYPE_IP); +#endif + break; + + case ARCTYPE_ARP_OLD: + if (ifp->if_flags & IFF_NOARP) { + /* Discard packet if ARP is disabled on interface */ + m_freem(m); + return; + } + m_adj(m, ARC_HDRLEN); + schednetisr(NETISR_ARP); + inq = &arpintrq; + arph = mtod(m, struct arphdr *); +#ifdef ARCNET_ALLOW_BROKEN_ARP + mtod(m, struct arphdr *)->ar_pro = htons(ETHERTYPE_IP); +#endif + break; +#endif +#ifdef INET6 + case ARCTYPE_INET6: + m_adj(m, ARC_HDRNEWLEN); + schednetisr(NETISR_IPV6); + inq = &ip6intrq; + break; +#endif + default: + m_freem(m); + return; + } + +#if __FreeBSD_version < 500000 + s = splimp(); + if (IF_QFULL(inq)) { + IF_DROP(inq); + m_freem(m); + } else + IF_ENQUEUE(inq, m); + splx(s); +#else + IF_HANDOFF(inq, m, NULL); +#endif +} + +/* + * Convert Arcnet address to printable (loggable) representation. + */ +static char digits[] = "0123456789abcdef"; +char * +arc_sprintf(ap) + u_int8_t *ap; +{ + static char arcbuf[3]; + char *cp = arcbuf; + + *cp++ = digits[*ap >> 4]; + *cp++ = digits[*ap++ & 0xf]; + *cp = 0; + return (arcbuf); +} + +/* + * Register (new) link level address. + */ +void +arc_storelladdr(ifp, lla) + struct ifnet *ifp; + u_int8_t lla; +{ + *IF_LLADDR(ifp) = lla; +} + +/* + * Perform common duties while attaching to interface list + */ +void +arc_ifattach(ifp, lla) + struct ifnet *ifp; + u_int8_t lla; +{ + struct ifaddr *ifa; + struct sockaddr_dl *sdl; + struct arccom *ac; + + if_attach(ifp); + ifp->if_type = IFT_ARCNET; + ifp->if_addrlen = 1; + ifp->if_hdrlen = ARC_HDRLEN; + ifp->if_mtu = 1500; + if (ifp->if_baudrate == 0) + ifp->if_baudrate = 2500000; +#if __FreeBSD_version < 500000 + ifa = ifnet_addrs[ifp->if_index - 1]; +#else + ifa = ifaddr_byindex(ifp->if_index); +#endif + KASSERT(ifa != NULL, ("%s: no lladdr!\n", __FUNCTION__)); + sdl = (struct sockaddr_dl *)ifa->ifa_addr; + sdl->sdl_type = IFT_ARCNET; + sdl->sdl_alen = ifp->if_addrlen; + + if (ifp->if_flags & IFF_BROADCAST) + ifp->if_flags |= IFF_MULTICAST|IFF_ALLMULTI; + + ac = (struct arccom *)ifp; + ac->ac_seqid = (time_second) & 0xFFFF; /* try to make seqid unique */ + if (lla == 0) { + /* XXX this message isn't entirely clear, to me -- cgd */ + log(LOG_ERR,"%s%d: link address 0 reserved for broadcasts. Please change it and ifconfig %s%d down up\n", + ifp->if_name, ifp->if_unit, ifp->if_name, ifp->if_unit); + } + arc_storelladdr(ifp, lla); + + ifp->if_broadcastaddr = &arcbroadcastaddr; + + bpfattach(ifp, DLT_ARCNET, ARC_HDRLEN); +} + +void +arc_ifdetach(ifp) + struct ifnet *ifp; +{ + bpfdetach(ifp); + if_detach(ifp); +} + +int +arc_ioctl(ifp, command, data) + struct ifnet *ifp; + int command; + caddr_t data; +{ + struct ifaddr *ifa = (struct ifaddr *) data; + struct ifreq *ifr = (struct ifreq *) data; + int error = 0; + + switch (command) { + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + switch (ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: + ifp->if_init(ifp->if_softc); /* before arpwhohas */ + arp_ifinit(ifp, ifa); + break; +#endif + default: + ifp->if_init(ifp->if_softc); + break; + } + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: + if (ifr == NULL) + error = EAFNOSUPPORT; + else { + switch (ifr->ifr_addr.sa_family) { + case AF_INET: + case AF_INET6: + error = 0; + break; + default: + error = EAFNOSUPPORT; + break; + } + } + break; + + case SIOCSIFMTU: + /* + * Set the interface MTU. + * mtu can't be larger than ARCMTU for RFC1051 + * and can't be larger than ARC_PHDS_MTU + */ + if (((ifp->if_flags & IFF_LINK0) && ifr->ifr_mtu > ARCMTU) || + ifr->ifr_mtu > ARC_PHDS_MAXMTU) + error = EINVAL; + else + ifp->if_mtu = ifr->ifr_mtu; + break; + +#if 0 + case SIOCGIFADDR: + { + struct sockaddr *sa; + + sa = (struct sockaddr *) & ifr->ifr_data; + bcopy(IFP2AC(ifp)->ac_enaddr, + (caddr_t) sa->sa_data, ETHER_ADDR_LEN); + } + break; +#endif + } + + return (error); +} |