#define DEBUG 1 /* * Mach Operating System * Copyright (c) 1994,1993,1992 Carnegie Mellon University * All Rights Reserved. * * Permission to use, copy, modify and distribute this software and its * documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie Mellon * the rights to redistribute these changes. */ /* * HISTORY * 17-Feb-94 David Golub (dbg) at Carnegie-Mellon University * Fix from Bob Baron to fix transmitter problems. * * $Log: if_de6c.c,v $ * Revision 1.1.1.1 1996/10/30 01:39:26 thomas * Imported from UK22 * * Revision 1.1 1994/11/08 20:47:24 baford * merged in CMU's MK83-MK83a diffs * * Revision 2.2 93/11/17 18:29:25 dbg * Moved source into kernel/i386at/DLINK/if_de6c.c, since we * can't release it but don't want to lose it. * [93/11/17 dbg] * * Removed u_long. * [93/03/25 dbg] * * Created. * I have used if_3c501.c as a typical driver template and * spliced in the appropriate particulars for the * d-link 600. * [92/08/13 rvb] * * * File: if_de6c.c * Author: Robert V. Baron */ /* * File: if_3c501.c * Author: Philippe Bernadat * Date: 1989 * Copyright (c) 1989 OSF Research Institute * * 3COM Etherlink d-link "600" Mach Ethernet drvier */ /* Copyright 1990 by Open Software Foundation, Cambridge, MA. All Rights Reserved Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appears in all copies and that both the copyright notice and this permission notice appear in supporting documentation, and that the name of OSF or Open Software Foundation not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL OSF BE LIABLE FOR ANY SPECIAL, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * I have tried to make it clear what is device specific code * and what code supports the general BSD ethernet interface. d-link * specific code is preceded by a line or two of * "d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON" * and followed by a line or two of * "d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF" * * The main routines that do device specific processing are: * de6cintr - interrupt dispatcher * de6crcv - rcv packets and switch to new buffers * de6cxmt - xmt packet and wait for xmtbusy to clear * de6calive - probe for device * de6cinit - device initialization * de6cintoff - turn it off. * There are a couple of interesting macros at the head of this * file and some support subroutines at the end. * * Lastly, to get decent performance on i386SX class machines, it * was necessary to recode the read and write d-link memory routines in * assembler. The deread and dewrite routines that are used are in * if_de6s.s * */ /* Questions: Make sure that iopl maps 378, 278 and 3bc. If you set command w/o MODE and page bit, what happens? Could I get xmt interrupts; currently I spin - this is not an issue? enable promiscuous? Can you assert TXEN and RXen simulatneously? */ #include #include #ifdef MACH_KERNEL #include #include #include #include #include #include #include #include #include #else MACH_KERNEL #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef INET #include #include #include #include #include #endif #ifdef NS #include #include #endif #endif MACH_KERNEL #include #include #include #include #define SPLNET spl6 /* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */ /* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */ #define de6cwrite(sp, addr, buf, len) \ de6cwriteasm(addr, buf, len, DATA(sp->port), sp->latency) #define de6cread(sp, addr, buf, len) \ de6creadasm(addr, buf, len, DATA(sp->port), sp->latency) #define DATA_OUT(sp, p, f, z) \ de6coutb(sp, DATA(p), ((z)<<4) | f);\ de6coutb(sp, DATA(p), ((z)&0xf0)| f | STROBE) #define STAT_IN(sp, p, in) \ de6coutb(sp, DATA(p), STATUS); \ in = inb(STAT(port)); \ de6coutb(sp, DATA(p), NUL_CMD | STROBE) #define XMTidx 3 #define XMT_BSY_WAIT 10000 /* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */ /* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */ int de6cprobe(); void de6cattach(); int de6cintr(); int de6cinit(); int de6coutput(); int de6cioctl(); int de6creset(); void de6cwatch(); static vm_offset_t de6c_std[NDE6C] = { 0 }; static struct bus_device *de6c_info[NDE6C]; #ifdef MACH_KERNEL struct bus_driver de6cdriver = {de6cprobe, 0, de6cattach, 0, de6c_std, "de", de6c_info, }; extern struct bus_device *lprinfo[]; #define MM io_req_t #define PIC sysdep1 #define DEV bus_device #else MACH_KERNEL int (*de6cintrs[])() = { de6cintr, 0}; struct isa_driver de6cdriver = {de6cprobe, 0, de6cattach, "de", 0, 0, 0}; extern struct isa_dev *lprinfo[]; #define MM struct mbuf * #define PIC dev_pic #define DEV isa_dev #endif MACH_KERNEL int watchdog_id; typedef struct { #ifdef MACH_KERNEL struct ifnet ds_if; /* generic interface header */ u_char ds_addr[6]; /* Ethernet hardware address */ #else MACH_KERNEL struct arpcom de6c_ac; #define ds_if de6c_ac.ac_if #define ds_addr de6c_ac.ac_enaddr #endif MACH_KERNEL int flags; int timer; u_char address[6]; short mode; int port; int latency; int xmt; int rcv; int rcvoff; int rcvspin; /* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */ /* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */ int produce; int consume; int rcvlen[XMTidx]; int alive; /* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */ /* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */ int (*oldvect)(); int oldunit; } de6c_softc_t; de6c_softc_t de6c_softc[NDE6C]; int de6cactive[NDE6C]; /* * Patch to change latency value */ int de6c_latency = 30; /* works on NEC Versa (pottsylvania.mach) */ #ifdef DEBUG int de6crcv0, de6crcv1, de6crcv2, de6crcv3; int de6cdo_rcvintr = 0, de6cdo_watch = 0; int de6cdo_xmt = 0; #define D(X) X #else /* DEBUG */ #define D(X) #endif /* DEBUG */ /* * de6cprobe: * We are not directly probed. The lprattach will call de6cattach. * But what we have is plausible for a probe. */ de6cprobe(port, dev) struct DEV *dev; { #ifdef MACH_KERNEL int unit = dev->unit; #else MACH_KERNEL int unit = dev->dev_unit; #endif MACH_KERNEL if ((unit < 0) || (unit >= NDE6C)) { return(0); } return(1); } /* * de6cattach: * * Called from lprattach * */ void de6cattach(dev) #ifdef MACH_KERNEL struct bus_device *dev; #else MACH_KERNEL struct isa_dev *dev; #endif MACH_KERNEL { de6c_softc_t *sp; struct ifnet *ifp; #ifdef MACH_KERNEL int unit = dev->unit; int port = (int)dev->address; #else MACH_KERNEL int unit = dev->dev_unit; int port = (int)dev->dev_addr; #endif MACH_KERNEL sp = &de6c_softc[unit]; sp->port = port; sp->timer = -1; sp->flags = 0; sp->mode = 0; ifp = &(sp->ds_if); ifp->if_unit = unit; ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST; #ifdef MACH_KERNEL ifp->if_header_size = sizeof(struct ether_header); ifp->if_header_format = HDR_ETHERNET; ifp->if_address_size = 6; ifp->if_address = (char *)&sp->address[0]; if_init_queues(ifp); #else MACH_KERNEL ifp->if_name = "de"; ifp->if_init = de6cinit; ifp->if_output = de6coutput; ifp->if_ioctl = de6cioctl; ifp->if_reset = de6creset; ifp->if_next = NULL; if_attach(ifp); #endif MACH_KERNEL sp->alive = de6calive(sp); } de6calive(sp) de6c_softc_t *sp; { int port = sp->port; int unit = sp->ds_if.if_unit; struct DEV *dev = lprinfo[unit]; int i; #ifdef MACH_KERNEL #else /* MACH_KERNEL */ extern int tcp_recvspace; /* empircal messure */ #endif /* MACH_KERNEL */ /* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */ /* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */ de6coutb(sp, CMD(port), SLT_NIC); DATA_OUT(sp, port, COMMAND, RESET); DATA_OUT(sp, port, COMMAND, STOP_RESET); sp->latency = 101; if (!de6cgetid(sp, sp->ds_addr)) { de6coutb(sp, CMD(port), SLT_PRN); return 0; } #ifdef MACH_KERNEL #else /* MACH_KERNEL */ tcp_recvspace = 0x300; /* empircal messure */ #endif /* MACH_KERNEL */ #ifdef de6cwrite sp->latency = de6c_latency; #else /* de6cwrite */ sp->latency = 0; #endif /* de6cwrite */ for (i = 0; i++ < 10;) { if (de6cmemcheck(sp)) break; sp->latency += 10; } de6cgetid(sp, sp->ds_addr); de6cgetid(sp, sp->address); de6csetid(sp, sp->address); de6coutb(sp, CMD(port), SLT_PRN); /* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */ /* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */ #ifdef MACH_KERNEL #if NPAR > 0 printf("\n"); #endif /* NPAR > 0 */ printf(" de%d: at lpr%d, port = %x, spl = %d, pic = %d. ", unit, unit, dev->address, dev->sysdep, dev->sysdep1); printf("ethernet id [%x:%x:%x:%x:%x:%x]", sp->address[0],sp->address[1],sp->address[2], sp->address[3],sp->address[4],sp->address[5]); if (sp->latency > 1) { printf("\n"); printf(" LATENCY = %d", sp->latency); printf(" LATENCY = %d", sp->latency); printf(" LATENCY = %d", sp->latency); printf(" LATENCY = %d", sp->latency); } #else MACH_KERNEL printf("de%d: port = %x, spl = %d, pic = %d. ", unit, dev->dev_addr, dev->dev_spl, dev->dev_pic); printf("ethernet id [%x:%x:%x:%x:%x:%x]\n", sp->address[0],sp->address[1],sp->address[2], sp->address[3],sp->address[4],sp->address[5]); if (sp->latency > 1) { printf("de%d:", unit); printf(" LATENCY = %d", sp->latency); printf(" LATENCY = %d", sp->latency); printf(" LATENCY = %d", sp->latency); printf(" LATENCY = %d", sp->latency); printf("\n"); } #endif MACH_KERNEL return 1; } /* * de6cwatch(): * */ void de6cwatch(b_ptr) short *b_ptr; { #ifdef DEBUG_MORE int unit = *b_ptr; de6c_softc_t *sp = &de6c_softc[unit]; if(!de6cdo_watch) return; de6cintr(unit); if (sp->ds_if.if_flags & IFF_RUNNING) timeout(de6cwatch, b_ptr, de6cdo_watch); #endif /* DEBUG_MORE */ } #ifdef MACH_KERNEL void de6cstart(int); /* forward */ de6coutput(dev, ior) dev_t dev; io_req_t ior; { register int unit = minor(dev); if (unit < 0 || unit >= NDE6C || de6c_softc[unit].port == 0) return (ENXIO); return (net_write(&de6c_softc[unit].ds_if, de6cstart, ior)); } io_return_t de6csetinput( dev_t dev, mach_port_t receive_port, int priority, filter_t filter[], natural_t filter_count) { register int unit = minor(dev); if (unit < 0 || unit >= NDE6C || de6c_softc[unit].port == 0) return ENXIO; return net_set_filter(&de6c_softc[unit].ds_if, receive_port, priority, filter, filter_count); } #else MACH_KERNEL /* * de6coutput: * * This routine is called by the "if" layer to output a packet to * the network. This code resolves the local ethernet address, and * puts it into the mbuf if there is room. If not, then a new mbuf * is allocated with the header information and precedes the data * to be transmitted. * * input: ifnet structure pointer, an mbuf with data, and address * to be resolved * output: mbuf is updated to hold enet address, or a new mbuf * with the address is added * */ de6coutput(ifp, m0, dst) struct ifnet *ifp; struct mbuf *m0; struct sockaddr *dst; { register de6c_softc_t *sp = &de6c_softc[ifp->if_unit]; int type, opri, error; u_char edst[6]; struct in_addr idst; register struct mbuf *m = m0; register struct ether_header *eh; register int off; int usetrailers; if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) { de6cintoff(ifp->if_unit); error = ENETDOWN; goto bad; } switch (dst->sa_family) { #ifdef INET case AF_INET: idst = ((struct sockaddr_in *)dst)->sin_addr; if (!arpresolve(&sp->de6c_ac, m, &idst, edst, &usetrailers)){ return (0); /* if not yet resolved */ } off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len; if (usetrailers && off > 0 && (off & 0x1ff) == 0 && m->m_off >= MMINOFF + 2 * sizeof (u_short)) { type = ETHERTYPE_TRAIL + (off>>9); m->m_off -= 2 * sizeof (u_short); m->m_len += 2 * sizeof (u_short); *mtod(m, u_short *) = htons((u_short)ETHERTYPE_IP); *(mtod(m, u_short *) + 1) = htons((u_short)m->m_len); goto gottrailertype; } type = ETHERTYPE_IP; off = 0; goto gottype; #endif #ifdef NS case AF_NS: type = ETHERTYPE_NS; bcopy((caddr_t)&(((struct sockaddr_ns *)dst)->sns_addr.x_host), (caddr_t)edst, sizeof (edst)); off = 0; goto gottype; #endif case AF_UNSPEC: eh = (struct ether_header *)dst->sa_data; bcopy((caddr_t)eh->ether_dhost, (caddr_t)edst, sizeof (edst)); type = eh->ether_type; goto gottype; default: printf("de6c%d: can't handle af%d\n", ifp->if_unit, dst->sa_family); error = EAFNOSUPPORT; goto bad; } gottrailertype: /* * Packet to be sent as trailer: move first packet * (control information) to end of chain. */ while (m->m_next) m = m->m_next; m->m_next = m0; m = m0->m_next; m0->m_next = 0; m0 = m; gottype: /* * Add local net header. If no space in first mbuf, * allocate another. */ if (m->m_off > MMAXOFF || MMINOFF + sizeof (struct ether_header) > m->m_off) { m = m_get(M_DONTWAIT, MT_HEADER); if (m == 0) { error = ENOBUFS; goto bad; } m->m_next = m0; m->m_off = MMINOFF; m->m_len = sizeof (struct ether_header); } else { m->m_off -= sizeof (struct ether_header); m->m_len += sizeof (struct ether_header); } eh = mtod(m, struct ether_header *); eh->ether_type = htons((u_short)type); bcopy((caddr_t)edst, (caddr_t)eh->ether_dhost, sizeof (edst)); bcopy((caddr_t)sp->address,(caddr_t)eh->ether_shost, sizeof(edst)); /* * Queue message on interface, and start output if interface * not yet active. */ opri = SPLNET(); if (IF_QFULL(&ifp->if_snd)) { IF_DROP(&ifp->if_snd); splx(opri); m_freem(m); return (ENOBUFS); } IF_ENQUEUE(&ifp->if_snd, m); de6cstart(ifp->if_unit); splx(opri); return (0); bad: m_freem(m0); return (error); } #endif MACH_KERNEL /* * de6creset: * * This routine is in part an entry point for the "if" code. Since most * of the actual initialization has already (we hope already) been done * by calling de6cattach(). * * input : unit number or board number to reset * output : board is reset * */ de6creset(unit) int unit; { de6c_softc[unit].ds_if.if_flags &= ~IFF_RUNNING; return(de6cinit(unit)); } /* * de6cinit: * * Another routine that interfaces the "if" layer to this driver. * Simply resets the structures that are used by "upper layers". * * input : board number * output : structures (if structs) and board are reset * */ de6cinit(unit) int unit; { de6c_softc_t *sp = &de6c_softc[unit]; struct ifnet *ifp = &(sp->ds_if); int port = sp->port; int pic = lprinfo[unit]->PIC; spl_t oldpri; #ifdef MACH_KERNEL #else MACH_KERNEL if (ifp->if_addrlist == (struct ifaddr *)0) { return; } #endif MACH_KERNEL oldpri = SPLNET(); if (ivect[pic] != de6cintr) { sp->oldvect = ivect[pic]; ivect[pic] = de6cintr; sp->oldunit = iunit[pic]; iunit[pic] = unit; } /* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */ /* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */ sp->consume = 0; sp->produce = 0; de6coutb(sp, CMD(port), SLT_NIC); DATA_OUT(sp, port, COMMAND, RESET); DATA_OUT(sp, port, COMMAND, STOP_RESET); de6coutb(sp, CMD(port), IRQEN); DATA_OUT(sp, port, COMMAND, RX_BP|(sp->produce<<4)); DATA_OUT(sp, port, COMMAND, RX_BP|(sp->produce<<4)|RXEN); de6coutb(sp, CMD(port), SLT_PRN); #if 0 if (sp->mode & IFF_PROMISC) { /* handle promiscuous case */; } #endif 0 /* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */ /* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */ sp->ds_if.if_flags |= IFF_RUNNING; sp->flags |= DSF_RUNNING; sp->timer = 5; timeout(de6cwatch, &(ifp->if_unit), 3); de6cstart(unit); splx(oldpri); } #ifdef MACH_KERNEL /*ARGSUSED*/ de6copen(dev, flag) dev_t dev; int flag; { register int unit = minor(dev); if (unit < 0 || unit >= NDE6C || de6c_softc[unit].port == 0) return (ENXIO); de6c_softc[unit].ds_if.if_flags |= IFF_UP; de6cinit(unit); return(0); } #endif MACH_KERNEL /* * de6cstart: * * This is yet another interface routine that simply tries to output a * in an mbuf after a reset. * * input : board number * output : stuff sent to board if any there * */ /* NOTE: called at SPLNET */ void de6cstart( int unit) { struct ifnet *ifp = &(de6c_softc[unit].ds_if); MM m; for(;;) { IF_DEQUEUE(&ifp->if_snd, m); if (m != (MM) 0) de6cxmt(unit, m); else return; } } #ifdef MACH_KERNEL /*ARGSUSED*/ io_return_t de6cgetstat( dev_t dev, int flavor, dev_status_t status, /* pointer to OUT array */ natural_t *count) /* out */ { register int unit = minor(dev); register de6c_softc_t *sp; if (unit < 0 || unit >= NDE6C || de6c_softc[unit].port == 0) return (ENXIO); sp = &de6c_softc[unit]; if (! sp->alive) if (! (sp->alive = de6calive(sp))) return ENXIO; return (net_getstat(&de6c_softc[unit].ds_if, flavor, status, count)); } io_return_t de6csetstat( dev_t dev, int flavor, dev_status_t status, natural_t count) { register int unit = minor(dev); register de6c_softc_t *sp; if (unit < 0 || unit >= NDE6C || de6c_softc[unit].port == 0) return (ENXIO); sp = &de6c_softc[unit]; if (! sp->alive) if (! (sp->alive = de6calive(sp))) return ENXIO; switch (flavor) { case NET_STATUS: { /* * All we can change are flags, and not many of those. */ register struct net_status *ns = (struct net_status *)status; int mode = 0; if (count < NET_STATUS_COUNT) return (D_INVALID_SIZE); if (ns->flags & IFF_ALLMULTI) mode |= MOD_ENAL; if (ns->flags & IFF_PROMISC) mode |= MOD_PROM; /* * Force a compilete reset if the receive mode changes * so that these take effect immediately. */ if (sp->mode != mode) { sp->mode = mode; if (sp->flags & DSF_RUNNING) { sp->flags &= ~(DSF_LOCK | DSF_RUNNING); de6cinit(unit); } } break; } case NET_ADDRESS: { register union ether_cvt { char addr[6]; int lwd[2]; } *ec = (union ether_cvt *)status; if (count < sizeof(*ec)/sizeof(int)) return (D_INVALID_SIZE); ec->lwd[0] = ntohl(ec->lwd[0]); ec->lwd[1] = ntohl(ec->lwd[1]); de6csetid(sp->port, ec->addr); break; } default: return (D_INVALID_OPERATION); } return (D_SUCCESS); } #else MACH_KERNEL /* * de6cioctl: * * This routine processes an ioctl request from the "if" layer * above. * * input : pointer the appropriate "if" struct, command, and data * output : based on command appropriate action is taken on the * de6c board(s) or related structures * return : error is returned containing exit conditions * */ de6cioctl(ifp, cmd, data) struct ifnet *ifp; int cmd; caddr_t data; { register struct ifaddr *ifa = (struct ifaddr *)data; register de6c_softc_t *sp = &de6c_softc[ifp->if_unit]; int opri, error; short mode = 0; if (! sp->alive) if (! (sp->alive = de6calive(sp))) return ENXIO; opri = SPLNET(); error = 0; switch (cmd) { case SIOCSIFADDR: ifp->if_flags |= IFF_UP; de6cinit(ifp->if_unit); switch (ifa->ifa_addr.sa_family) { #ifdef INET case AF_INET: ((struct arpcom *)ifp)->ac_ipaddr = IA_SIN(ifa)->sin_addr; arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr); break; #endif #ifdef NS case AF_NS: { register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); if (ns_nullhost(*ina)) ina->x_host = *(union ns_host *)(ds->ds_addr); else de6cseteh(ina->x_host.c_host, de6c_softc[ifp->if_unit].port); break; } #endif } break; case SIOCSIFFLAGS: if (ifp->if_flags & IFF_ALLMULTI) mode |= MOD_ENAL; if (ifp->if_flags & IFF_PROMISC) mode |= MOD_PROM; /* * force a complete reset if the receive multicast/ * promiscuous mode changes so that these take * effect immediately. * */ if (sp->mode != mode) { sp->mode = mode; if (sp->flags & DSF_RUNNING) { sp->flags &= ~(DSF_LOCK|DSF_RUNNING); de6cinit(ifp->if_unit); } } if ((ifp->if_flags & IFF_UP) == 0 && sp->flags & DSF_RUNNING) { sp->timer = -1; de6cintoff(ifp->if_unit); } else if (ifp->if_flags & IFF_UP && (sp->flags & DSF_RUNNING) == 0) de6cinit(ifp->if_unit); break; default: error = EINVAL; } splx(opri); return (error); } #endif MACH_KERNEL /* * de6cintr: * * This function is the interrupt handler for the de6c ethernet * board. This routine will be called whenever either a packet * is received, or a packet has successfully been transfered and * the unit is ready to transmit another packet. * * input : board number that interrupted * output : either a packet is received, or a packet is transfered * */ /* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */ /* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */ #ifdef DEBUG_MORE de6crcvintr(unit) int unit; { if(!de6cdo_rcvintr) return; de6cintr(unit); } #endif /* DEBUG_MORE */ de6cintr(unit) int unit; { register de6c_softc_t *sp = &de6c_softc[unit]; int port = sp->port; int in; if (de6cactive[unit] || !(sp->flags & DSF_RUNNING)) return; de6cactive[unit]++; de6coutb(sp, CMD(port), SLT_NIC); STAT_IN(sp, port, in); if ((in & (GOOD|TXBUSY)) == (GOOD|TXBUSY)) { /* on L40's means that we are disconnected */ printf("de6intr%d: Card was disconnected; turning off network.\n", unit); de6cintoff(unit); de6cactive[unit]--; return; } if (in & GOOD) de6crcv(unit, in); else /*rvb:tmp printf("intr: %x\n", in)*/; de6coutb(sp, CMD(port), SLT_PRN); de6cactive[unit]--; } /* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */ /* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */ /* * de6crcv: * * This routine is called by the interrupt handler to initiate a * packet transfer from the board to the "if" layer above this * driver. This routine checks if a buffer has been successfully * received by the de6c. If so, the routine de6cread is called * to do the actual transfer of the board data (including the * ethernet header) into a packet (consisting of an mbuf chain). * * input : number of the board to check * output : if a packet is available, it is "sent up" * */ de6crcv(unit, in) int unit, in; { register de6c_softc_t *sp = &de6c_softc[unit]; register struct ifnet *ifp = &sp->ds_if; int port = sp->port; int bo; int collision = 0; int spins = 0; u_short len; struct ether_header header; int tlen; register struct ifqueue *inq; int opri; struct ether_header eh; #ifdef MACH_KERNEL ipc_kmsg_t new_kmsg; struct ether_header *ehp; struct packet_header *pkt; #else MACH_KERNEL struct mbuf *m, *tm; #endif MACH_KERNEL sp->rcv++; /* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */ /* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */ D(de6crcv0++); #define MT (sp->consume == sp->produce) while (in & GOOD || !MT) { spins++; D(de6crcv1++); if (in & GOOD) { sp->rcvlen[sp->produce] = de6clen(sp); if ( ((sp->produce + 1) % XMTidx) != sp->consume) { if (++sp->produce == XMTidx) sp->produce = 0; DATA_OUT(sp, port, COMMAND, RX_BP|(sp->produce<<4)); DATA_OUT(sp, port, COMMAND, RX_BP|(sp->produce<<4)|RXEN); } else collision = 1; } len = sp->rcvlen[sp->consume]; bo = sp->consume*BFRSIZ; if (len < 60) { printf("de%d: len(%d) < 60\n", unit, len); goto out; return; } de6cread(sp, bo, &eh, sizeof(struct ether_header)); bo += sizeof(struct ether_header); len -= 18; /* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */ /* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */ #ifdef MACH_KERNEL new_kmsg = net_kmsg_get(); if (new_kmsg == IKM_NULL) { /* * Drop the packet. */ sp->ds_if.if_rcvdrops++; goto out; return; } ehp = (struct ether_header *) (&net_kmsg(new_kmsg)->header[0]); pkt = (struct packet_header *) (&net_kmsg(new_kmsg)->packet[0]); /* * Get header. */ *ehp = eh; /* * Get body */ /* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */ de6cread(sp, bo, (char *)(pkt + 1), len); /* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */ pkt->type = ehp->ether_type; pkt->length = len + sizeof(struct packet_header); /* * Hand the packet to the network module. */ net_packet(ifp, new_kmsg, pkt->length, ethernet_priority(new_kmsg)); #else MACH_KERNEL eh.ether_type = htons(eh.ether_type); m =(struct mbuf *)0; while ( len ) { if (m == (struct mbuf *)0) { m = m_get(M_DONTWAIT, MT_DATA); if (m == (struct mbuf *)0) { printf("de6crcv: Lost frame\n"); goto out; return; } tm = m; tm->m_off = MMINOFF; /* * first mbuf in the packet must contain a pointer to the * ifnet structure. other mbufs that follow and make up * the packet do not need this pointer in the mbuf. * */ *(mtod(tm, struct ifnet **)) = ifp; tm->m_len = sizeof(struct ifnet **); } else { tm->m_next = m_get(M_DONTWAIT, MT_DATA); tm = tm->m_next; tm->m_off = MMINOFF; tm->m_len = 0; if (tm == (struct mbuf *)0) { m_freem(m); printf("de6crcv: No mbufs, lost frame\n"); goto out; return; } } tlen = MIN( MLEN - tm->m_len, len ); tm->m_next = (struct mbuf *)0; /* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */ de6cread(sp, bo, mtod(tm, char *)+tm->m_len, tlen); bo += tlen; /* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */ tm->m_len += tlen; len -= tlen; } /* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */ /* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */ STAT_IN(sp, port, in); if (in & GOOD) { /* got another */ D(de6crcv2++); sp->rcvlen[sp->produce] = de6clen(sp); if ( ((sp->produce + 1) % XMTidx) != sp->consume) { if (++sp->produce == XMTidx) sp->produce = 0; DATA_OUT(sp, port, COMMAND, RX_BP|(sp->produce<<4)); DATA_OUT(sp, port, COMMAND, RX_BP|(sp->produce<<4)|RXEN); } else collision = 1; } /* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */ /* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */ /* * received packet is now in a chain of mbuf's. next step is * to pass the packet upwards. * */ switch (eh.ether_type) { #ifdef INET case ETHERTYPE_IP: schednetisr(NETISR_IP); inq = &ipintrq; break; case ETHERTYPE_ARP: arpinput(&sp->de6c_ac, m); goto out; return; #endif #ifdef NS case ETHERTYPE_NS: schednetisr(NETISR_NS); inq = &nsintrq; break; #endif default: m_freem(m); goto out; return; } opri = SPLNET(); if (IF_QFULL(inq)) { IF_DROP(inq); splx(opri); m_freem(m); goto out; return; } IF_ENQUEUE(inq, m); splx(opri); #endif MACH_KERNEL out: /* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */ /* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */ STAT_IN(sp, port, in); if (in & GOOD) { /* got another */ D(de6crcv3++); } /*2*/ /* implies wrap and pause */ if (collision) { collision = 0; D(printf("*C* ")); sp->rcvoff++; if (++sp->produce == XMTidx) sp->produce = 0; DATA_OUT(sp, port, COMMAND, RX_BP|(sp->produce<<4)); DATA_OUT(sp, port, COMMAND, RX_BP|(sp->produce<<4)|RXEN); } /*2*/ /* implies wrap and pause */ if (++sp->consume == XMTidx) sp->consume = 0; if (spins > 10) { spins = 0; D(printf("*R* ")); sp->rcvspin++; /* how should we recover here ??? */; /* return does not work */; /* de6cinit(unit) gets ugly if we are called from de6cxmt */; } /* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */ /* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */ } } /* * de6cxmt: * * This routine fills in the appropriate registers and memory * locations on the d-link "600" board and starts the board off on * the transmit. * * input : board number of interest, and a pointer to the mbuf * output : board memory and registers are set for xfer and attention * */ /* NOTE: called at SPLNET */ /* * This implies that rcv interrupts will be blocked. */ #define max(a,b) (((a) > (b)) ? (a) : (b)) char de6mt[ETHERMIN]; de6cxmt(unit, m) int unit; MM m; { #ifdef MACH_KERNEL #else MACH_KERNEL register struct mbuf *tm_p; #endif MACH_KERNEL int i; int in; int bo, boo; de6c_softc_t *sp = &de6c_softc[unit]; int port = sp->port; u_short count = 0; u_short bytes_in_msg; static int m_length(); /* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */ /* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */ if (de6cactive[unit] >= 2) /* a funny loop caused by */ return; /* a flood of arps */ if (de6cactive[unit]++ == 0) de6coutb(sp, CMD(port), SLT_NIC); STAT_IN(sp, port, in); D(if (de6cdo_xmt) printf("xmt: stat[-] = %x\n", in)); if (in & GOOD) { de6crcv(unit, in); } sp->xmt++; /* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */ /* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */ #ifdef MACH_KERNEL count = m->io_count; bytes_in_msg = max(count, ETHERMIN + sizeof(struct ether_header)); /* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */ boo = bo = XMTidx*BFRSIZ+(BFRSIZ-bytes_in_msg); de6cwrite(sp, bo, m->io_data, count); /* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */ bo += count; iodone(m); #else MACH_KERNEL bytes_in_msg = max(m_length(m), ETHERMIN + sizeof(struct ether_header)); /* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */ boo = bo = XMTidx*BFRSIZ+(BFRSIZ-bytes_in_msg); /* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */ for (tm_p = m; tm_p != (struct mbuf *)0; tm_p = tm_p->m_next) { if (count + tm_p->m_len > ETHERMTU + sizeof(struct ether_header)) break; if (tm_p->m_len == 0) continue; /* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */ de6cwrite(sp, bo, mtod(tm_p, caddr_t), tm_p->m_len); bo += tm_p->m_len; /* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */ count += tm_p->m_len; } m_freem(m); #endif MACH_KERNEL /* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */ /* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */ if (bytes_in_msg - count > 0) de6cwrite(sp, bo, de6mt, bytes_in_msg - count); DATA_OUT(sp, port, TX_ADR, boo & 0xff); DATA_OUT(sp, port, TX_ADR, (boo >> 8) & 0xff); DATA_OUT(sp, port, COMMAND, RX_BP|(sp->produce<<4)); DATA_OUT(sp, port, COMMAND, RX_BP|(sp->produce<<4)|TXEN); for (i = 0; i < XMT_BSY_WAIT; i++) { STAT_IN(sp, port, in); D(if (de6cdo_xmt) printf("xmt: stat[%d] = %x\n", i, in)); if (in & GOOD) { /* * this does indeed happen * printf("!#"); */ de6crcv(unit, in); } if (!(in & TXBUSY)) { goto out; return; } } printf("dexmt: stat[??] = %x\n", i, in); out: DATA_OUT(sp, port, COMMAND, RX_BP|(sp->produce<<4)); DATA_OUT(sp, port, COMMAND, RX_BP|(sp->produce<<4)|RXEN); if (--de6cactive[unit] == 0) de6coutb(sp, CMD(port), SLT_PRN); /* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */ /* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */ } /* * de6cintoff: * * This function turns interrupts off for the de6c board indicated. * */ de6cintoff(unit) int unit; { de6c_softc_t *sp = &de6c_softc[unit]; int port = sp->port; int pic = lprinfo[unit]->PIC; printf("de%d: Turning off d-link \"600\"\n", unit); sp->ds_if.if_flags &= ~(IFF_UP|IFF_RUNNING); sp->flags &= ~(DSF_LOCK | DSF_RUNNING); /* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */ /* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */ de6coutb(sp, CMD(port), SLT_NIC); DATA_OUT(sp, port, COMMAND, RESET); DATA_OUT(sp, port, COMMAND, STOP_RESET); de6coutb(sp, CMD(port), SLT_PRN); /* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */ /* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */ outb(CMD(sp->port), 0x07); ivect[pic] = sp->oldvect; iunit[pic] = sp->oldunit; } #ifdef MACH_KERNEL #else MACH_KERNEL /* * The length of an mbuf chain */ static m_length(m) register struct mbuf *m; { register int len = 0; while (m) { len += m->m_len; m = m->m_next; } return len; } #endif MACH_KERNEL /* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */ /* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */ de6cgetid(sp, buf) de6c_softc_t *sp; u_char *buf; { de6cread(sp, EADDR, buf, 6); if ((buf[0] != 0x00) || (buf[1] != 0xde) || (buf[2] != 0x15)) return 0; buf[0] = 0x80; buf[1] = 0x00; buf[2] = 0xc8; /* for this model d-link we assert 0x70 as the high mfr's nibble. */ buf[3] = 0x70 | (buf[3] & 0xf); return 1; } de6csetid(sp, buf) de6c_softc_t *sp; char *buf; { de6cwrite(sp, EADDR, buf, 6); } /* * get length of packet just rcv'd. * includes ether header and crc */ de6clen(sp) de6c_softc_t *sp; { int port = sp->port; int in; int i; de6coutb(sp, DATA(port), RX_LEN); in = inb(STAT(port)); de6coutb(sp, DATA(port), RX_LEN|STROBE); i = ((in>>4) | (inb(STAT(port)) & 0xf0)); de6coutb(sp, DATA(port), RX_LEN); in = inb(STAT(port)); de6coutb(sp, DATA(port), RX_LEN|STROBE); i |= ((in>>4) | (inb(STAT(port)) & 0xf0)) << 8; return i; } #if 0 de6cread(sp, address, buf, len) de6c_softc_t *sp; unsigned char *buf; { int port = sp->port; u_char in; DATA_OUT(sp, port, RW_ADR, address & 0xff); DATA_OUT(sp, port, RW_ADR, (address >> 8) & 0xff); while (len--) { de6coutb(sp, DATA(port), READ); in = inb(STAT(port)); de6coutb(sp, DATA(port), READ|STROBE); *buf++ = ((in>>4) | (inb(STAT(port)) & 0xf0)); } } de6cwrite(sp, address, buf, len) de6c_softc_t *sp; unsigned char *buf; { int port = sp->port; int out; DATA_OUT(sp, port, RW_ADR, address & 0xff); DATA_OUT(sp, port, RW_ADR, (address >> 8) & 0xff); while (len--) { out = *buf++; DATA_OUT(sp, port, WRITE, out); } } #endif 0 #ifndef de6cread de6cread(sp, address, buf, len) de6c_softc_t *sp; unsigned char *buf; { int port = sp->port; register volatile int i; unsigned char in; outb(port, ((address)<<4) | RW_ADR); i = sp->latency; while (i-- > 0); outb(port, ((address)&0xf0)| RW_ADR | 0x8); i = sp->latency; while (i-- > 0); outb(port, ((address>>8)<<4) | RW_ADR); i = sp->latency; while (i-- > 0); outb(port, ((address>>8)&0xf0)| RW_ADR | 0x8); i = sp->latency; while (i-- > 0); while (len--) { outb(port, READ); i = sp->latency; while (i-- > 0); in = inb(STAT(port)); outb(port, READ|0x08); i = sp->latency; while (i-- > 0); *buf++ = ((in>>4) | (inb(STAT(port)) & 0xf0)); } } #endif /* de6cread */ #ifndef de6cwrite de6cwrite(sp, address, buf, len) de6c_softc_t *sp; unsigned char *buf; { int port = sp->port; register volatile int i; unsigned char out; outb(port, ((address)<<4) | RW_ADR); i = sp->latency; while (i-- > 0); outb(port, ((address)&0xf0)| RW_ADR | 0x8); i = sp->latency; while (i-- > 0); outb(port, ((address>>8)<<4) | RW_ADR); i = sp->latency; while (i-- > 0); outb(port, ((address>>8)&0xf0)| RW_ADR | 0x8); i = sp->latency; while (i-- > 0); while (len--) { out = *buf++; outb(port, ((out)<<4) | WRITE); i = sp->latency; while (i-- > 0); outb(port, ((out)&0xf0)| WRITE | 0x8); i = sp->latency; while (i-- > 0); } } #endif /* de6cwrite */ de6coutb(sp, p, v) de6c_softc_t *sp; { register volatile int i = sp->latency; outb(p, v); while (i-- > 0); } de6cmemcheck(sp) de6c_softc_t *sp; { int i; int off = 0; int ret = 1; #ifdef MACH_KERNEL unsigned short *memchk; unsigned short *chkmem; if (kmem_alloc(kernel_map, (vm_offset_t *)&memchk, BFRS * BFRSIZ) != KERN_SUCCESS || kmem_alloc(kernel_map, (vm_offset_t *)&chkmem, BFRS * BFRSIZ) != KERN_SUCCESS) { printf("de6c: memory allocation failure!!\n"); return 0; } #else /* MACH_KERNEL */ unsigned short *memchk = (unsigned short *) kmem_alloc(kernel_map, BFRS * BFRSIZ); unsigned short *chkmem = (unsigned short *) kmem_alloc(kernel_map, BFRS * BFRSIZ); if ( ! ((int) memchk) || ! ((int) chkmem)) { printf("de6c: memory allocation failure!!\n"); return 0; } #endif /* MACH_KERNEL */ for (i = 0; i < BFRS * BFRSIZ/sizeof (short); i++) memchk[i] = i; bzero(chkmem, BFRS * BFRSIZ); for (off = 0; off < BFRS * BFRSIZ; off += BFRSIZ/2) { de6cwrite(sp, off, memchk+(off/sizeof (short)), BFRSIZ/2); de6cread (sp, off, chkmem+(off/sizeof (short)), BFRSIZ/2); } for (i = 0; i < BFRS * (BFRSIZ/sizeof (short)); i++) if (memchk[i] != chkmem[i]) { printf("de: tilt:seq [%x:%d] %x != %x\n", i, i, memchk[i], chkmem[i]); ret = 0; break; } kmem_free(kernel_map, (vm_offset_t) memchk, BFRS * BFRSIZ); kmem_free(kernel_map, (vm_offset_t) chkmem, BFRS * BFRSIZ); return ret; } /* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */ /* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */ #ifdef DEBUG #define STATIC STATIC int print_pkt(), print_bdy(); STATIC int print_e_hdr(), print_ip_hdr(), print_ip(); STATIC int print_ipa(), print_arp(), print_e(), print_chars(); STATIC print_pkt(p, len) unsigned char *p; { int j, k; int type; if (len < 18) printf("print_pkt: too small %d\n", len); type = print_e_hdr(p); switch (type) { case 0x806: print_arp(p+14); break; case 0x800: print_ip(p+14, len - 18); break; default: for (j = 14; j < len; j +=20) { for (k = 0; k < 20; k++) printf("%2x ", p[j+k]); printf("\n"); } } } STATIC print_bdy(p, len, type) unsigned char *p; { int j, k; if (len < 18) printf("print_pkt: too small %d|n", len); switch (type) { case 0x806: print_arp(p); break; case 0x800: print_ip(p, len); break; default: for (j = 0; j < len; j +=20) { for (k = 0; k < 20; k++) printf("%2x ", p[j+k]); printf("\n"); } } } STATIC print_e_hdr(p) unsigned char *p; { int type = ntohs(((unsigned short *)p)[6]); printf("S=%x:%x:%x:%x:%x:%x, ", p[6], p[7], p[8], p[9], p[10], p[11]); printf("D=%x:%x:%x:%x:%x:%x, ", p[0], p[1], p[2], p[3], p[4], p[5]); printf("T=%x\n", type); return type; } STATIC print_ip_hdr(u) u_char *u; { int l = ntohs(*(u_short *)(u+2)); print_ipa(u+12); printf(" -> "); print_ipa(u+12+4); printf(" L%d(0x%x)\n", l, l); } STATIC print_ip(p, len) unsigned char *p; { int j,k; print_ip_hdr(p); for (k =0; k < 12; k++) printf("%2x ", p[k]); print_ipa(p+12); printf(" "); print_ipa(p+12+4); printf("\n"); for (j = 20; j < len; j +=16) { for (k = 0; k < 16; k++) printf("%2x ", p[j+k]); print_chars(&p[j], 16); printf("\n"); } } STATIC print_ipa(u) u_char *u; { printf("%d.%d.%d.%d", u[0], u[1], u[2], u[3]); } STATIC print_arp(p) #ifdef MACH_KERNEL {} #else MACH_KERNEL struct arphdr *p; { u_char *u = (u_char *)(p+1); printf("op = %x, pro = %x, hln = %x, pln = %x\n", ntohs(p->ar_op), ntohs(p->ar_pro), p->ar_hln, p->ar_pln); print_e(u); print_ipa(u+p->ar_hln); printf(" seeks\n"); print_e(u+p->ar_hln+p->ar_pln); print_ipa(u+p->ar_hln+p->ar_pln+p->ar_hln); printf("\n"); } #endif MACH_KERNEL STATIC print_e(u) u_char *u; { printf("%x:%x:%x:%x:%x:%x ", u[0], u[1], u[2], u[3], u[4], u[5]); } STATIC print_chars(u, len) u_char *u; { int c; printf("|>"); while (len--) { c = *u++; if (c < 0x7f && c > 0x1f) printf("%c", c); else printf(" "); } printf("<|"); } #endif DEBUG