diff options
Diffstat (limited to 'i386/i386at/if_ne.c')
-rw-r--r-- | i386/i386at/if_ne.c | 1081 |
1 files changed, 1081 insertions, 0 deletions
diff --git a/i386/i386at/if_ne.c b/i386/i386at/if_ne.c new file mode 100644 index 00000000..9a950d69 --- /dev/null +++ b/i386/i386at/if_ne.c @@ -0,0 +1,1081 @@ +/*- + * Copyright (c) 1990, 1991 William F. Jolitz. + * Copyright (c) 1990 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. + * + * @(#)if_ne.c 7.4 (Berkeley) 5/21/91 + */ + +/* + * NE2000 Ethernet driver + * + * Parts inspired from Tim Tucker's if_wd driver for the wd8003, + * insight on the ne2000 gained from Robert Clements PC/FTP driver. + */ + +#include <ne.h> + + +#if NNE > 0 +#ifdef MACH_KERNEL + +#include <kern/time_out.h> +#include <device/device_types.h> +#include <device/errno.h> +#include <device/io_req.h> +#include <device/if_hdr.h> +#include <device/if_ether.h> +#include <device/net_status.h> +#include <device/net_io.h> +#include <i386at/ds8390.h> +#include <i386at/if_nereg.h> +#include <i386/ipl.h> +#include <chips/busses.h> +#ifdef FIPC +#include <ipc/fipc.h> +#endif /* FIPC */ + +#else MACH_KERNEL + +#include <sys/param.h> +#include <mach/vm_param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/table.h> +#include <sys/buf.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#include <sys/vmmac.h> +#include <sys/ioctl.h> +#include <sys/errno.h> +#include <sys/syslog.h> +#include <vm/vm_kern.h> + +#include <net/if.h> +#include <net/if_types.h> +#include <net/route.h> + +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/ip.h> +#include <netinet/if_ether.h> + +#include <netns/ns.h> +#include <netns/ns_if.h> + + +#include <i386/ipl.h> +#include <i386at/atbus.h> +#include <i386at/ds8390.h> +#include <i386at/if_nereg.h> +#include <i386/handler.h> +#include <i386/dispatcher.h> +int ether_output(); +int neioctl(); + +#endif + + +int neprobe(); +void neattach(); +int neintr(); +int nestart(); +int neinit(); + +static vm_offset_t ne_std[NNE] = {0}; +static struct bus_device *ne_info[NNE]; +struct bus_driver nedriver = + { neprobe, 0, neattach, 0, ne_std, "ne", ne_info, 0, 0, 0 }; + +#define ETHER_MIN_LEN 64 +#define ETHER_MAX_LEN 1536 +#define SPLNET spl6 +/* + * Ethernet software status per interface. + * + * Each interface is referenced by a network interface structure, + * ns_if, which the routing code uses to locate the interface. + * This structure contains the output queue for the interface, its address, ... + */ +typedef struct { + +#ifdef MACH_KERNEL + struct ifnet ds_if; + u_char ds_addr[6]; +#else MACH_KERNEL + struct arpcom ns_ac; /* Ethernet common part */ +#define ds_if ns_ac.ac_if /* network-visible interface */ +#define ds_addr ns_ac.ac_enaddr /* hardware Ethernet address */ +#endif MACH_KERNEL + + int ns_flags; +#define DSF_LOCK 1 /* block re-entering enstart */ +#define DSF_RUNNING 2 + int ns_oactive ; + int ns_mask ; + int ns_ba; /* byte addr in buffer ram of inc pkt */ + int ns_cur; /* current page being filled */ + struct prhdr ns_ph; /* hardware header of incoming packet*/ + struct ether_header ns_eh; /* header of incoming packet */ + u_char ns_pb[2048 /*ETHERMTU+sizeof(long)*/]; + short ns_txstart; /* transmitter buffer start */ + short ns_rxend; /* recevier buffer end */ + short ns_rxbndry; /* recevier buffer boundary */ + caddr_t ns_port; /* i/o port base */ + short ns_mode; /* word/byte mode */ + int mode; + short card_present; +#ifndef MACH_KERNEL + ihandler_t handler; + ihandler_id_t *handler_id; +#endif MACH_KERNEL + +} ne_softc_t; +ne_softc_t ne_softc[NNE]; + +#define PAT(n) (0xa55a + 37*(n)) + +u_short boarddata[16]; + +/* + * Fetch from onboard ROM/RAM + */ +nefetch (ns, up, ad, len) ne_softc_t *ns; caddr_t up; { + u_char cmd; + caddr_t nec = ns->ns_port; + int counter = 10000; + int t_len; + unsigned char last_word[2]; + char odd; + + cmd = inb (nec + ds_cmd); + outb (nec + ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_START); + + /* Setup remote dma */ + outb (nec + ds0_isr, DSIS_RDC); + + t_len = len; + if ((ns->ns_mode & DSDC_WTS) && len&1) { + odd=1; + t_len++; /* roundup to words */ + } else odd=0; + + outb (nec+ds0_rbcr0, t_len); + outb (nec+ds0_rbcr1, t_len>>8); + outb (nec+ds0_rsar0, ad); + outb (nec+ds0_rsar1, ad>>8); + + /* Execute & extract from card */ + outb (nec+ds_cmd, DSCM_RREAD|DSCM_PG0|DSCM_START); + + if (ns->ns_mode & DSDC_WTS) + if (odd) { + linw (nec+ne_data, up, len/2); + *(last_word) = inw(nec+ne_data); /* get last word */ + *(up+len-1) = last_word[0]; /* last byte */ + } else { + linw (nec+ne_data, up, len/2); + } + else + linb (nec+ne_data, up, len); + + + /* Wait till done, then shutdown feature */ + while ((inb (nec+ds0_isr) & DSIS_RDC) == 0 && counter-- > 0) + ; + + outb (nec+ds0_isr, DSIS_RDC); + outb (nec+ds_cmd, cmd); +} + +/* + * Put to onboard RAM + */ +neput (ns, up, ad, len) ne_softc_t *ns; caddr_t up; { + u_char cmd; + caddr_t nec = ns->ns_port; + int counter = 10000; + int t_len; + int odd; + unsigned char last_word[2]; + + cmd = inb(nec+ds_cmd); + outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_START); + + /* Setup for remote dma */ + outb (nec+ds0_isr, DSIS_RDC); + + t_len = len; + if ((ns->ns_mode & DSDC_WTS) && len&1) { + odd = 1; + t_len++; /* roundup to words */ + } else odd = 0; + + outb (nec+ds0_rbcr0, t_len); + outb (nec+ds0_rbcr1, t_len>>8); + outb (nec+ds0_rsar0, ad); + outb (nec+ds0_rsar1, ad>>8); + + /* Execute & stuff to card */ + outb (nec+ds_cmd, DSCM_RWRITE|DSCM_PG0|DSCM_START); + if (ns->ns_mode & DSDC_WTS) { + if (odd) { + loutw (nec+ne_data, up, len/2); + last_word[0] = *(up+len-1); + outw (nec+ne_data, (unsigned short) *(last_word)); + } + else { + loutw (nec+ne_data, up, len/2); + } + } + else + loutb (nec+ne_data, up, len); + + + /* Wait till done, then shutdown feature */ + while ((inb (nec+ds0_isr) & DSIS_RDC) == 0 && counter-- > 0) + ; + + outb (nec+ds0_isr, DSIS_RDC); + outb (nec+ds_cmd, cmd); +} + +int +neprobe(port, dev) +struct bus_device *dev; +{ + int val, i, sum, bytemode = 1, pat; + int unit = dev->unit; + ne_softc_t *ns = &ne_softc[unit]; + caddr_t nec; + + if ((unsigned) unit >= NNE) + return(0); + + nec = (caddr_t) ns->ns_port = dev->address; + + if (ns->card_present) { + printf("ne%s : card already present in port %x\n", + unit, nec); + return(0); + } + + if (bytemode) { + /* Byte Transfers, Burst Mode Select, Fifo at 8 bytes */ + ns->ns_mode = DSDC_BMS|DSDC_FT1; + ns->ns_txstart = TBUF8; + ns->ns_rxend = RBUFEND8; + } else { +word: + /* Word Transfers, Burst Mode Select, Fifo at 8 bytes */ + ns->ns_mode = DSDC_WTS|DSDC_BMS|DSDC_FT1; + ns->ns_txstart = TBUF16; + ns->ns_rxend = RBUFEND16; + bytemode = 0; + } + + /* Reset the bastard */ + val = inb(nec + ne_reset); + delay(200); + outb(nec + ne_reset, val); + delay(200); + + outb(nec + ds_cmd, DSCM_STOP|DSCM_NODMA); + + i = 10000; + while ((inb(nec + ds0_isr) & DSIS_RESET) == 0 && i-- > 0); + if (i < 0) return (0); + + outb(nec + ds0_isr, 0xff); + outb(nec + ds0_dcr, ns->ns_mode); + outb(nec + ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_STOP); + delay(1000); + + /* Check cmd reg and fail if not right */ + if ((i = inb(nec + ds_cmd)) != (DSCM_NODMA|DSCM_PG0|DSCM_STOP)) + return(0); + + outb(nec + ds0_tcr, DSTC_LB0); + outb(nec + ds0_rcr, DSRC_MON); + outb(nec + ds0_pstart, ns->ns_txstart+PKTSZ); + outb(nec + ds0_pstop, ns->ns_rxend); + outb(nec + ds0_bnry, ns->ns_rxend); + outb(nec + ds0_imr, 0); + outb(nec + ds0_isr, 0); + outb(nec + ds_cmd, DSCM_NODMA|DSCM_PG1|DSCM_STOP); + outb(nec + ds1_curr, ns->ns_txstart+PKTSZ); + outb(nec + ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_STOP); + +#ifdef NEDEBUG +#define RCON 37 + { int i, rom; + + rom=1; + printf("ne ram "); + + for (i = 0; i < 0xfff0; i+=4) { + pat = PAT(i); + neput(ns, &pat,i,4); + nefetch(ns, &pat,i,4); + if (pat == PAT(i)) { + if (rom) { + rom=0; + printf(" %x", i); + } + } else { + if (!rom) { + rom=1; + printf("..%x ", i); + } + } + pat=0; + neput(ns, &pat,i,4); + } + printf("\n"); + } +#endif + + /* + * <groan> detect difference between units + * solely by where the RAM is decoded. + */ + pat = PAT(0); + neput (ns, &pat, ns->ns_txstart*DS_PGSIZE, 4); + nefetch(ns, &pat, ns->ns_txstart*DS_PGSIZE, 4); + if (pat != PAT(0)) { + if (bytemode) + goto word; + else return (0); + } + + + /* Extract board address */ + nefetch (ns, (caddr_t)boarddata, 0, sizeof(boarddata)); + + for(i=0; i < 6; i++) + ns->ds_addr[i] = boarddata[i]; + ns->card_present = 1; + return (1); +} + +/* + * Interface exists: make available by filling in network interface + * record. System will initialize the interface when it is ready + * to accept packets. We get the ethernet address here. + */ +void +neattach(dev) +struct bus_device *dev; +{ + short unit = dev->unit; + ne_softc_t *ns = &ne_softc[unit]; + register struct ifnet *ifp = &(ns->ds_if); + + if ((unsigned) unit >= NNE) + return; + +#ifdef MACH_KERNEL + take_dev_irq(dev); +#else MACH_KERNEL + /* setup intr handler */ + ns->handler.ih_level = dev->dev_pic; + ns->handler.ih_handler = dev->dev_intr[0]; + ns->handler.ih_resolver = i386_resolver; + ns->handler.ih_rdev = dev; + ns->handler.ih_stats.intr_type = INTR_DEVICE; + ns->handler.ih_stats.intr_cnt = 0; + ns->handler.ih_hparam[0].intparam = unit; + if ((ns->handler_id = handler_add(&ns->handler)) != NULL) + handler_enable(ns->handler_id); + else + panic("Unable to add NEx000 interrupt handler"); +#endif MACH_KERNEL + printf (", port = %x, spl = %d, pic = %d, [%s].", + dev->address, dev->sysdep, dev->sysdep1, + ether_sprintf(ns->ds_addr)); +#ifndef MACH_KERNEL + ns->ns_ac.ac_bcastaddr = (u_char *)etherbroadcastaddr; + ns->ns_ac.ac_arphrd = ARPHRD_ETHER; +#endif MACH_KERNEL + ns->ns_flags = 0; + ns->mode = 0; + 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 *)&ns->ds_addr[0]; + if_init_queues(ifp); +#else MACH_KERNEL + ifp->if_name = nedriver.driver_dname; + ifp->if_init = neinit; + ifp->if_output = ether_output; + ifp->if_start = nestart; + ifp->if_ioctl = neioctl; + ifp->if_reset = nereset; + ifp->if_watchdog= 0; + if_attach(ifp); +#endif MACH_KERNEL +} + +/* + * Initialization of interface; set up initialization block + * and transmit/receive descriptor rings. + */ +neinit(unit) + int unit; +{ + ne_softc_t *ns = &ne_softc[unit]; + struct ifnet *ifp = &ns->ds_if; + int i; char *cp; + int oldpri; + caddr_t nec = ns->ns_port; + +#ifndef MACH_KERNEL + if (ifp->if_addrlist == (struct ifaddr *)0) return; +#endif MACH_KERNEL + + oldpri = SPLNET(); + + outb(nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_STOP); + + /* Word Transfer select, Burst Mode Select, Fifo at 8 bytes */ + outb(nec+ds0_dcr, ns->ns_mode); + + /* clear remote byte count resigters */ + outb (nec+ds0_rbcr0, 0); + outb (nec+ds0_rbcr1, 0); + + /* don't store incoming packets into memory for now */ + outb (nec+ds0_rcr, DSRC_MON); + + /* place NIC in internal loopback mode */ + outb(nec+ds0_tcr, DSTC_LB0); + + /* initialize transmit/recieve (ring-buffer) Page Start */ + outb (nec+ds0_tpsr, 0); + outb (nec+ds0_pstart, ns->ns_txstart+PKTSZ); + + /* initialize reciever (ring-buffer) Page Stop and Boundary */ + outb (nec+ds0_pstop, ns->ns_rxend); + outb (nec+ds0_bnry, ns->ns_txstart+PKTSZ); + + /* clear all interrupts */ + outb (nec+ds0_isr, 0xff); + + /* enable the interrupts that we care about */ + outb (nec+ds0_imr, IMR_ENABLE); + + /* set physical address on ethernet */ + outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG1|DSCM_STOP); + for (i=0 ; i < 6 ; i++) outb(nec+ds1_par0+i,ns->ds_addr[i]); + + ns->ns_cur = ns->ns_txstart+PKTSZ + 1; + outb (nec+ds1_curr, ns->ns_cur); + + /* XXX deal with Reciever Configuration Register */ + /* clr logical address hash filter for now */ + for (i=0 ; i < 8 ; i++) outb(nec+ds1_mar0+i,0xff); + + /* set page 0 registers */ + outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_STOP); + outb (nec+ds0_rcr, DSRC_AB); + + /* take unit out of loopback mode */ + outb (nec+ds0_tcr, 0); + + ns->ds_if.if_flags |= IFF_RUNNING; + ns->ns_flags &= ~(DSF_LOCK|DSF_RUNNING); + ns->ns_oactive = 0; ns->ns_mask = ~0; + splx(oldpri); + nestart(unit); + return(1); +} + +/* + * Setup output on interface. + * Get another datagram to send off of the interface queue, + * and map it to the interface before starting the output. + * called only at splimp or interrupt level. + */ +nestart(unit) +int unit; +{ + ne_softc_t *ns = &ne_softc[unit]; + struct ifnet *ifp = &ns->ds_if; + int buffer; + int len, i, total,t; + caddr_t nec = ns->ns_port; +#ifdef MACH_KERNEL + io_req_t m; + +#else MACH_KERNEL + struct mbuf *m0, *m; +#endif MACH_KERNEL + + /* + * The DS8390 has only one transmit buffer, if it is busy we + * must wait until the transmit interrupt completes. + */ + outb(nec+ds_cmd,DSCM_NODMA|DSCM_START); + + if (ns->ns_flags & DSF_LOCK) + goto done; + + if (inb(nec+ds_cmd) & DSCM_TRANS) + goto done; + + if ((ns->ds_if.if_flags & IFF_RUNNING) == 0) + goto done; + + IF_DEQUEUE(&ns->ds_if.if_snd, m); + if (m == 0) + goto done; + + /* + * Copy the mbuf chain into the transmit buffer + */ + + ns->ns_flags |= DSF_LOCK; /* prevent entering nestart */ + buffer = ns->ns_txstart*DS_PGSIZE; +#ifdef MACH_KERNEL + total = m->io_count; + neput(ns, m->io_data, buffer, total); +#else MACH_KERNEL + t = 0; len = i = 0; + for (m0 = m; m != 0; m = m->m_next) + t += m->m_len; + + m = m0; + total = t; + for (m0 = m; m != 0; ) { + + if (m->m_len&1 && t > m->m_len) { + neput(ns, mtod(m, caddr_t), buffer, m->m_len - 1); + t -= m->m_len - 1; + buffer += m->m_len - 1; + m->m_data += m->m_len - 1; + m->m_len = 1; + m = m_pullup(m, 2); + } else { + neput(ns, mtod(m, caddr_t), buffer, m->m_len); + buffer += m->m_len; + t -= m->m_len; + MFREE(m, m0); + m = m0; + } + } +#endif MACH_KERNEL + /* + * Init transmit length registers, and set transmit start flag. + */ + + len = total; + if (len < ETHER_MIN_LEN) len = ETHER_MIN_LEN; + outb(nec+ds0_tbcr0,len&0xff); + outb(nec+ds0_tbcr1,(len>>8)&0xff); + outb(nec+ds0_tpsr, ns->ns_txstart); + outb(nec+ds_cmd, DSCM_TRANS|DSCM_NODMA|DSCM_START); + +#ifdef MACH_KERNEL + iodone(m); + m = 0; +done: +#endif MACH_KERNEL +} + +/* buffer successor/predecessor in ring? */ +#define succ(n) (((n)+1 >= ns->ns_rxend) ? (ns->ns_txstart+PKTSZ) : (n)+1) +#define pred(n) (((n)-1 < (ns->ns_txstart+PKTSZ)) ? ns->ns_rxend-1 : (n)-1) + +/* + * Controller interrupt. + */ +neintr(unit) +{ + ne_softc_t *ns = &ne_softc[unit]; + u_char cmd,isr; + caddr_t nec = ns->ns_port; + + /* Save cmd, clear interrupt */ + cmd = inb (nec+ds_cmd); + isr = inb (nec+ds0_isr); +loop: + outb(nec+ds_cmd,DSCM_NODMA|DSCM_START); + outb(nec+ds0_isr, isr); + + /* Receiver error */ + if (isr & DSIS_RXE) { + (void) inb(nec+ds0_rsr); + /* need to read these registers to clear status */ + ns->ds_if.if_ierrors++; + } + + /* Counters overflowed, reading the registers resets them */ + if (isr & DSIS_CTRS) { + (void) inb(nec+ds0_cntr0); + (void) inb(nec+ds0_cntr1); + (void) inb(nec+ds0_cntr2); + } + + + /* We received something; rummage thru tiny ring buffer */ + if (isr & (DSIS_RX|DSIS_RXE|DSIS_ROVRN)) { + u_char pend,lastfree; + + outb(nec+ds_cmd, DSCM_START|DSCM_NODMA|DSCM_PG1); + pend = inb(nec+ds1_curr); + outb(nec+ds_cmd, DSCM_START|DSCM_NODMA|DSCM_PG0); + + /* Something in the buffer? */ + while (pend != ns->ns_cur) { + /* Extract header from microcephalic board */ + nefetch(ns, &ns->ns_ph,ns->ns_cur*DS_PGSIZE, + sizeof(ns->ns_ph)); + ns->ns_ba = ns->ns_cur*DS_PGSIZE+sizeof(ns->ns_ph); + + /* Incipient paranoia */ + if (ns->ns_ph.pr_status == DSRS_RPC || + /* for dequna's */ + ns->ns_ph.pr_status == 0x21) { + if (nerecv(ns)) + ns->ns_cur = ns->ns_ph.pr_nxtpg ; + else { + outb(nec+ds0_bnry, pred(ns->ns_cur)); + goto short_load; + } + } +#ifdef NEDEBUG + else { + printf("cur %x pnd %x lfr %x ", + ns->ns_cur, pend, lastfree); + printf("nxt %x len %x ", ns->ns_ph.pr_nxtpg, + (ns->ns_ph.pr_sz1<<8)+ ns->ns_ph.pr_sz0); + printf("Bogus Sts %x\n", ns->ns_ph.pr_status); + ns->ns_cur = pend; + } +#endif + outb(nec+ds0_bnry, pred(ns->ns_cur)); + outb(nec+ds_cmd, DSCM_START|DSCM_NODMA|DSCM_PG1); + pend = inb(nec+ds1_curr); + outb(nec+ds_cmd, DSCM_START|DSCM_NODMA|DSCM_PG0); + } +short_load: + outb(nec+ds_cmd, DSCM_START|DSCM_NODMA); + } + + /* Transmit error */ + if (isr & DSIS_TXE) { + ns->ns_flags &= ~DSF_LOCK; + /* Need to read these registers to clear status */ + ns->ds_if.if_collisions += inb(nec+ds0_tbcr0); + ns->ds_if.if_oerrors++; + } + + /* Packet Transmitted */ + if (isr & DSIS_TX) { + ns->ns_flags &= ~DSF_LOCK; + ++ns->ds_if.if_opackets; + ns->ds_if.if_collisions += inb(nec+ds0_tbcr0); + } + + /* Receiver ovverun? */ + if (isr & DSIS_ROVRN) { + outb(nec+ds0_rbcr0, 0); + outb(nec+ds0_rbcr1, 0); + outb(nec+ds0_tcr, DSTC_LB0); + outb(nec+ds0_rcr, DSRC_MON); + outb(nec+ds_cmd, DSCM_START|DSCM_NODMA); + outb(nec+ds0_rcr, DSRC_AB); + outb(nec+ds0_tcr, 0); + } + + /* Any more to send? */ + outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_START); + nestart(unit); + outb (nec+ds_cmd, cmd); + outb (nec+ds0_imr, IMR_ENABLE); + + /* Still more to do? */ + isr = inb (nec+ds0_isr); + if(isr) goto loop; + + return 0; +} + +/* + * Ethernet interface receiver interface. + * If input error just drop packet. + * Otherwise examine packet to determine type. If can't determine length + * from type, then have to drop packet. Othewise decapsulate + * packet based on type and pass to type specific higher-level + * input routine. + */ +nerecv(ns) + ne_softc_t *ns; +{ +#ifdef MACH_KERNEL + ipc_kmsg_t new_kmsg; + struct ether_header *ehp; + struct packet_header *pkt; + register struct ifnet *ifp = &ns->ds_if; +#ifdef FIPC + char *fipc_buf; +#endif +#else MACH_KERNEL + struct mbuf *top, **mp, *m, *p; +#endif MACH_KERNEL + int len, l; + int epkt; + + ns->ds_if.if_ipackets++; + len = ns->ns_ph.pr_sz0 + (ns->ns_ph.pr_sz1<<8); + if(len < ETHER_MIN_LEN || len > ETHER_MAX_LEN) + return 0; + + nefetch(ns, &ns->ns_eh, ns->ns_ba, sizeof(struct ether_header)); + +#ifndef MACH_KERNEL + ns->ns_eh.ether_type = ntohs((u_short)ns->ns_eh.ether_type); +#endif MACH_KERNEL + ns->ns_ba += sizeof(struct ether_header); + + /* don't forget checksum! */ + len -= (sizeof(struct ether_header) + sizeof(long)); +#ifdef MACH_KERNEL +#ifdef FIPC + if (ns->ns_eh.ether_type == FIPC_MSG_TYPE) /* fipc packet */ + { + /* We need to hand the whole packet to the handler. */ + + fipc_recvs++; + + fipc_buf = get_fipc_buffer (len, TRUE, TRUE); + + if (fipc_buf == NULL) + { + ns->ds_if.if_rcvdrops++; + return(0); + } + nefetch (ns, fipc_buf, ns->ns_ba, len); + + fipc_packet (fipc_buf, ns->ns_eh); + } + else /* net_kmsg */ + { +#endif /* FIPC */ + new_kmsg = net_kmsg_get(); + + if (new_kmsg == IKM_NULL) { + ns->ds_if.if_rcvdrops++; + return(0); + } + + ehp = (struct ether_header *) (&net_kmsg(new_kmsg)->header[0]); + pkt = (struct packet_header *)(&net_kmsg(new_kmsg)->packet[0]); + *ehp = ns->ns_eh; + + nefetch(ns, (char *) (pkt + 1), ns->ns_ba, len); + + pkt->type = ehp->ether_type; + + pkt->length = len + sizeof(struct packet_header); + net_packet(ifp, new_kmsg, pkt->length, ethernet_priority(new_kmsg)); +#ifdef FIPC + } +#endif + +#else MACH_KERNEL +/**/ + epkt = ns->ns_ba + len; + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == 0) + return (0); + + m->m_pkthdr.rcvif = &ns->ds_if; + m->m_pkthdr.len = len; + m->m_len = MHLEN; + + top = 0; + mp = ⊤ + while (len > 0) { + if (top) { + MGET(m, M_DONTWAIT, MT_DATA); + if (m == 0) { + m_freem(top); + return (0); + } + m->m_len = MLEN; + } + l = min(len, epkt - ns->ns_ba); + if (l >= MINCLSIZE) { + MCLGET(m, M_DONTWAIT); + if (m->m_flags & M_EXT) + m->m_len = l = min(len, MCLBYTES); + else + l = m->m_len; + } else { + /* + * Place initial small packet/header at end of mbuf. + */ + if (l < m->m_len) { + if (top == 0 && len + max_linkhdr <= m->m_len) + m->m_data += max_linkhdr; + m->m_len = l; + } else + l = m->m_len; + } + nefetch(ns, mtod(m, caddr_t), ns->ns_ba, l); + ns->ns_ba += l; + *mp = m; + mp = &m->m_next; + len -= l; + } +/**/ + if (top == 0) return 0; /* NEED MODIFY HERE !!! */ + + ether_input(&ns->ds_if, &ns->ns_eh, top); +#endif MACH_KERNEL + return 1; +} + +#ifdef MACH_KERNEL +neopen(dev, flag) +dev_t dev; +int flag; +{ + register int unit = minor(dev); + + if (!ne_softc[unit].card_present || unit < 0 || unit >= NNE) + return (ENXIO); + + ne_softc[unit].ds_if.if_flags |= IFF_UP; + neinit(unit); + return(0); +} + +#ifdef FIPC +nefoutput(dev, ior) +dev_t dev; +io_req_t ior; +{ + register int unit = minor(dev); + + if (!ne_softc[unit].card_present || unit < 0 || unit >= NNE) + return (ENXIO); + + return (net_fwrite(&ne_softc[unit].ds_if, nestart, ior)); +} +#endif + +neoutput(dev, ior) +dev_t dev; +io_req_t ior; +{ + register int unit = minor(dev); + + if (!ne_softc[unit].card_present || unit < 0 || unit >= NNE) + return (ENXIO); + + return (net_write(&ne_softc[unit].ds_if, nestart, ior)); +} + +nesetinput(dev, receive_port, priority, filter, filter_count) +dev_t dev; +mach_port_t receive_port; +int priority; +filter_t filter[]; +unsigned int filter_count; +{ + register int unit = minor(dev); + + if (!ne_softc[unit].card_present || unit < 0 || unit >= NNE) + return (ENXIO); + + return (net_set_filter(&ne_softc[unit].ds_if, + receive_port, priority, + filter, filter_count)); +} + +negetstat(dev, flavor, status, count) +dev_t dev; +int flavor; +dev_status_t status; +unsigned int *count; +{ + register int unit = minor(dev); + + if (!ne_softc[unit].card_present || unit < 0 || unit >= NNE) + return (ENXIO); + + return (net_getstat(&ne_softc[unit].ds_if, + flavor, + status, + count)); +} + +nesetstat(dev, flavor, status, count) +dev_t dev; +int flavor; +dev_status_t status; +unsigned int count; +{ + register int unit = minor(dev); + register ne_softc_t *ns; + + if (!ne_softc[unit].card_present || unit < 0 || unit >= NNE) + return (ENXIO); + + ns = &ne_softc[unit]; + + switch(flavor) { + case NET_STATUS: { + register struct net_status *s = (struct net_status *)status; + int mode = 0; + if (count < NET_STATUS_COUNT) + return(D_INVALID_SIZE); +#define MOD_ENAL 1 +#define MOD_PROM 2 + if (s->flags & IFF_ALLMULTI) + mode |= MOD_ENAL; + if (s->flags & IFF_PROMISC) + mode |= MOD_PROM; + + if (ns->mode != mode) { + ns->mode = mode; + if (ns->ns_flags & DSF_RUNNING) { + ns->ns_flags &= ~(DSF_LOCK | DSF_RUNNING); + neinit(unit); + } + } + break; + } + default : + return (D_INVALID_OPERATION); + } + return (D_SUCCESS); +} + +#else MACH_KERNEL + +/* + * Process an ioctl request. + */ +neioctl(ifp, cmd, data) + register struct ifnet *ifp; + int cmd; + caddr_t data; +{ + register struct ifaddr *ifa = (struct ifaddr *)data; + ne_softc_t *ns = &ne_softc[ifp->if_unit]; + struct ifreq *ifr = (struct ifreq *)data; + int s = splimp(), error = 0; + + + switch (cmd) { + + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + + switch (ifa->ifa_addr->sa_family) { + case AF_INET: + neinit(ifp->if_unit); /* before arpwhohas */ + ((struct arpcom *)ifp)->ac_ipaddr = + IA_SIN(ifa)->sin_addr; + arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr); + break; + case AF_NS: + { + register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); + + if (ns_nullhost(*ina)) + ina->x_host = *(union ns_host *)(ns->ds_addr); + else { + /* + * The manual says we can't change the address + * while the receiver is armed, + * so reset everything + */ + ifp->if_flags &= ~IFF_RUNNING; + bcopy((caddr_t)ina->x_host.c_host, + (caddr_t)ns->ds_addr, sizeof(ns->ds_addr)); + } + neinit(ifp->if_unit); /* does ne_setaddr() */ + break; + } + default: + neinit(ifp->if_unit); + break; + } + break; + + case SIOCSIFFLAGS: + if ((ifp->if_flags & IFF_UP) == 0 && + ifp->if_flags & IFF_RUNNING) { + ifp->if_flags &= ~IFF_RUNNING; + outb(ns->ns_port + ds_cmd, DSCM_STOP|DSCM_NODMA); + } else if (ifp->if_flags & IFF_UP && + (ifp->if_flags & IFF_RUNNING) == 0) + neinit(ifp->if_unit); + break; + +#ifdef notdef + case SIOCGHWADDR: + bcopy((caddr_t)ns->ds_addr, (caddr_t) &ifr->ifr_data, + sizeof(ns->ds_addr)); + break; +#endif + + default: + error = EINVAL; + } + splx(s); + return (error); +} + +/* + * Reset of interface. + */ +nereset(unit, uban) + int unit, uban; +{ + if (unit >= NNE) + return; + printf("ne%d: reset\n", unit); + ne_softc[unit].ns_flags &= ~DSF_LOCK; + neinit(unit); +} +#endif MACH_KERNEL +#endif |