summaryrefslogtreecommitdiff
path: root/i386/i386at/if_pc586.c
diff options
context:
space:
mode:
authorThomas Bushnell <thomas@gnu.org>1997-02-25 21:28:37 +0000
committerThomas Bushnell <thomas@gnu.org>1997-02-25 21:28:37 +0000
commitf07a4c844da9f0ecae5bbee1ab94be56505f26f7 (patch)
tree12b07c7e578fc1a5f53dbfde2632408491ff2a70 /i386/i386at/if_pc586.c
Initial source
Diffstat (limited to 'i386/i386at/if_pc586.c')
-rw-r--r--i386/i386at/if_pc586.c2076
1 files changed, 2076 insertions, 0 deletions
diff --git a/i386/i386at/if_pc586.c b/i386/i386at/if_pc586.c
new file mode 100644
index 0000000..195ce7d
--- /dev/null
+++ b/i386/i386at/if_pc586.c
@@ -0,0 +1,2076 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 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.
+ */
+/*
+ * Olivetti PC586 Mach Ethernet driver v1.0
+ * Copyright Ing. C. Olivetti & C. S.p.A. 1988, 1989
+ * All rights reserved.
+ *
+ */
+
+/*
+ Copyright 1988, 1989 by Olivetti Advanced Technology Center, Inc.,
+Cupertino, California.
+
+ 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 Olivetti
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+ OLIVETTI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL OLIVETTI 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 OUR OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/*
+ Copyright 1988, 1989 by Intel Corporation, Santa Clara, California.
+
+ 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 Intel
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL INTEL 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.
+*/
+
+/*
+ * NOTE:
+ * by rvb:
+ * 1. The best book on the 82586 is:
+ * LAN Components User's Manual by Intel
+ * The copy I found was dated 1984. This really tells you
+ * what the state machines are doing
+ * 2. In the current design, we only do one write at a time,
+ * though the hardware is capable of chaining and possibly
+ * even batching. The problem is that we only make one
+ * transmit buffer available in sram space.
+ * 3.
+ * n. Board Memory Map
+ RFA/FD 0 - 227 0x228 bytes
+ 226 = 0x19 * 0x16 bytes
+ RBD 228 - 3813 0x35ec bytes
+ 35e8 = 0x19 * 0x228 bytes
+ == 0x0a bytes (bd) + 2 bytes + 21c bytes
+ CU 3814 - 3913 0x100 bytes
+ TBD 3914 - 39a3 0x90 bytes
+ 90 = No 18 * 0x08 bytes
+ TBUF 39a4 - 3fdd 0x63a bytes (= 1594(10))
+ SCB 3fde - 3fed 0x10 bytes
+ ISCP 3fee - 3ff5 0x08 bytes
+ SCP 3ff6 - 3fff 0x0a bytes
+ *
+ */
+
+/*
+ * NOTE:
+ *
+ * Currently this driver doesn't support trailer protocols for
+ * packets. Once that is added, please remove this comment.
+ *
+ * Also, some lacking material includes the DLI code. If you
+ * are compiling this driver with DLI set, lookout, that code
+ * has not been looked at.
+ *
+ */
+
+#define DEBUG
+#define IF_CNTRS MACH
+#define NDLI 0
+
+#include <pc586.h>
+
+#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>
+#else MACH_KERNEL
+#include <sys/param.h>
+#include <mach/machine/vm_param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.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 <net/if.h>
+#include <net/netisr.h>
+#include <net/route.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+#endif
+
+#ifdef NS
+#include <netns/ns.h>
+#include <netns/ns_if.h>
+#endif
+
+#if DLI
+#include <net/dli_var.h>
+struct dli_var de_dlv[NDE];
+#endif DLI
+#endif MACH_KERNEL
+
+#include <i386/ipl.h>
+#include <mach/vm_param.h>
+#include <vm/vm_kern.h>
+#include <chips/busses.h>
+#include <i386at/if_pc586.h>
+
+#define SPLNET spl6
+#if __STDC__
+#define CMD(x, y, unit) *(u_short *)(pc_softc[unit].prom + OFFSET_ ## x) = (u_short) (y)
+#else __STDC__
+#define CMD(x, y, unit) *(u_short *)(pc_softc[unit].prom + OFFSET_/**/x) = (u_short) (y)
+#endif __STDC__
+
+#define pc586chatt(unit) CMD(CHANATT, 0x0001, unit)
+#define pc586inton(unit) CMD(INTENAB, CMD_1, unit)
+#define pc586intoff(unit) CMD(INTENAB, CMD_0, unit)
+
+int pc586probe();
+void pc586attach();
+int pc586intr(), pc586init(), pc586output(), pc586ioctl(), pc586reset();
+int pc586watch(), pc586rcv(), pc586xmt(), pc586bldcu();
+int pc586diag(), pc586config();
+char *pc586bldru();
+char *ram_to_ptr();
+u_short ptr_to_ram();
+
+static vm_offset_t pc586_std[NPC586] = { 0 };
+static struct bus_device *pc586_info[NPC586];
+struct bus_driver pcdriver =
+ {pc586probe, 0, pc586attach, 0, pc586_std, "pc", pc586_info, 0, 0, 0};
+
+char t_packet[ETHERMTU + sizeof(struct ether_header) + sizeof(long)];
+int xmt_watch = 0;
+
+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 pc586_ac;
+#define ds_if pc586_ac.ac_if
+#define ds_addr pc586_ac.ac_enaddr
+#endif MACH_KERNEL
+ int flags;
+ int seated;
+ int timer;
+ int open;
+ fd_t *begin_fd;
+ fd_t *end_fd;
+ rbd_t *end_rbd;
+ char *prom;
+ char *sram;
+ int tbusy;
+ short mode;
+} pc_softc_t;
+pc_softc_t pc_softc[NPC586];
+
+struct pc586_cntrs {
+ struct {
+ u_int xmt, xmti;
+ u_int defer;
+ u_int busy;
+ u_int sleaze, intrinsic, intrinsic_count;
+ u_int chain;
+ } xmt;
+ struct {
+ u_int rcv;
+ u_int ovw;
+ u_int crc;
+ u_int frame;
+ u_int rscerrs, ovrnerrs;
+ u_int partial, bad_chain, fill;
+ } rcv;
+ u_int watch;
+} pc586_cntrs[NPC586];
+
+
+#ifdef IF_CNTRS
+int pc586_narp = 1, pc586_arp = 0;
+int pc586_ein[32], pc586_eout[32];
+int pc586_lin[128/8], pc586_lout[128/8];
+static
+log_2(no)
+unsigned long no;
+{
+ return ({ unsigned long _temp__;
+ asm("bsr %1, %0; jne 0f; xorl %0, %0; 0:" :
+ "=r" (_temp__) : "a" (no));
+ _temp__;});
+}
+#endif IF_CNTRS
+
+/*
+ * pc586probe:
+ *
+ * This function "probes" or checks for the pc586 board on the bus to see
+ * if it is there. As far as I can tell, the best break between this
+ * routine and the attach code is to simply determine whether the board
+ * is configured in properly. Currently my approach to this is to write
+ * and read a word from the SRAM on the board being probed. If the word
+ * comes back properly then we assume the board is there. The config
+ * code expects to see a successful return from the probe routine before
+ * attach will be called.
+ *
+ * input : address device is mapped to, and unit # being checked
+ * output : a '1' is returned if the board exists, and a 0 otherwise
+ *
+ */
+pc586probe(port, dev)
+struct bus_device *dev;
+{
+ caddr_t addr = (caddr_t)dev->address;
+ int unit = dev->unit;
+ int len = round_page(0x4000);
+ int sram_len = round_page(0x4000);
+ extern vm_offset_t phys_last_addr;
+ int i;
+ volatile char *b_prom;
+ volatile char *b_sram;
+ volatile u_short*t_ps;
+
+ if ((unit < 0) || (unit > NPC586)) {
+ printf("pc%d: board out of range [0..%d]\n",
+ unit, NPC586);
+ return(0);
+ }
+ if ((addr > (caddr_t)0x100000) && (addr < (caddr_t)phys_last_addr))
+ return 0;
+
+ if (kmem_alloc_pageable(kernel_map, (vm_offset_t *) &b_prom, len)
+ != KERN_SUCCESS) {
+ printf("pc%d: can not allocate memory for prom.\n", unit);
+ return 0;
+ }
+ if (kmem_alloc_pageable(kernel_map, (vm_offset_t *) &b_sram, sram_len)
+ != KERN_SUCCESS) {
+ printf("pc%d: can not allocate memory for sram.\n", unit);
+ return 0;
+ }
+ (void)pmap_map(b_prom, (vm_offset_t)addr,
+ (vm_offset_t)addr+len,
+ VM_PROT_READ | VM_PROT_WRITE);
+ if ((int)addr > 0x100000) /* stupid hardware */
+ addr += EXTENDED_ADDR;
+ addr += 0x4000; /* sram space */
+ (void)pmap_map(b_sram, (vm_offset_t)addr,
+ (vm_offset_t)addr+sram_len,
+ VM_PROT_READ | VM_PROT_WRITE);
+
+ *(b_prom + OFFSET_RESET) = 1;
+ { int i; for (i = 0; i < 1000; i++); /* 4 clocks at 6Mhz */}
+ *(b_prom + OFFSET_RESET) = 0;
+ t_ps = (u_short *)(b_sram + OFFSET_SCB);
+ *(t_ps) = (u_short)0x5a5a;
+ if (*(t_ps) != (u_short)0x5a5a) {
+ kmem_free(kernel_map, b_prom, len);
+ kmem_free(kernel_map, b_sram, sram_len);
+ return(0);
+ }
+ t_ps = (u_short *)(b_prom + + OFFSET_PROM);
+#define ETHER0 0x00
+#define ETHER1 0xaa
+#define ETHER2 0x00
+ if ((t_ps[0]&0xff) == ETHER0 &&
+ (t_ps[1]&0xff) == ETHER1 &&
+ (t_ps[2]&0xff) == ETHER2)
+ pc_softc[unit].seated = TRUE;
+#undef ETHER0
+#undef ETHER1
+#undef ETHER2
+#define ETHER0 0x00
+#define ETHER1 0x00
+#define ETHER2 0x1c
+ if ((t_ps[0]&0xff) == ETHER0 ||
+ (t_ps[1]&0xff) == ETHER1 ||
+ (t_ps[2]&0xff) == ETHER2)
+ pc_softc[unit].seated = TRUE;
+#undef ETHER0
+#undef ETHER1
+#undef ETHER2
+ if (pc_softc[unit].seated != TRUE) {
+ kmem_free(kernel_map, b_prom, len);
+ kmem_free(kernel_map, b_sram, sram_len);
+ return(0);
+ }
+ (volatile char *)pc_softc[unit].prom = (volatile char *)b_prom;
+ (volatile char *)pc_softc[unit].sram = (volatile char *)b_sram;
+ return(1);
+}
+
+/*
+ * pc586attach:
+ *
+ * This function attaches a PC586 board to the "system". The rest of
+ * runtime structures are initialized here (this routine is called after
+ * a successful probe of the board). Once the ethernet address is read
+ * and stored, the board's ifnet structure is attached and readied.
+ *
+ * input : bus_device structure setup in autoconfig
+ * output : board structs and ifnet is setup
+ *
+ */
+void pc586attach(dev)
+ struct bus_device *dev;
+{
+ struct ifnet *ifp;
+ u_char *addr_p;
+ u_short *b_addr;
+ u_char unit = (u_char)dev->unit;
+ pc_softc_t *sp = &pc_softc[unit];
+ volatile scb_t *scb_p;
+
+ take_dev_irq(dev);
+ printf(", port = %x, spl = %d, pic = %d. ",
+ dev->address, dev->sysdep, dev->sysdep1);
+
+ sp->timer = -1;
+ sp->flags = 0;
+ sp->mode = 0;
+ sp->open = 0;
+ CMD(RESET, CMD_1, unit);
+ { int i; for (i = 0; i < 1000; i++); /* 4 clocks at 6Mhz */}
+ CMD(RESET, CMD_0, unit);
+ b_addr = (u_short *)(sp->prom + OFFSET_PROM);
+ addr_p = (u_char *)sp->ds_addr;
+ addr_p[0] = b_addr[0];
+ addr_p[1] = b_addr[1];
+ addr_p[2] = b_addr[2];
+ addr_p[3] = b_addr[3];
+ addr_p[4] = b_addr[4];
+ addr_p[5] = b_addr[5];
+ printf("ethernet id [%x:%x:%x:%x:%x:%x]",
+ addr_p[0], addr_p[1], addr_p[2],
+ addr_p[3], addr_p[4], addr_p[5]);
+
+ scb_p = (volatile scb_t *)(sp->sram + OFFSET_SCB);
+ scb_p->scb_crcerrs = 0; /* initialize counters */
+ scb_p->scb_alnerrs = 0;
+ scb_p->scb_rscerrs = 0;
+ scb_p->scb_ovrnerrs = 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->ds_addr[0];
+ if_init_queues(ifp);
+#else MACH_KERNEL
+ ifp->if_name = "pc";
+ ifp->if_init = pc586init;
+ ifp->if_output = pc586output;
+ ifp->if_ioctl = pc586ioctl;
+ ifp->if_reset = pc586reset;
+ ifp->if_next = NULL;
+ if_attach(ifp);
+#endif MACH_KERNEL
+}
+
+/*
+ * pc586reset:
+ *
+ * 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 pc586attach().
+ *
+ * input : unit number or board number to reset
+ * output : board is reset
+ *
+ */
+pc586reset(unit)
+int unit;
+{
+ pc_softc[unit].ds_if.if_flags &= ~IFF_RUNNING;
+ pc_softc[unit].flags &= ~(DSF_LOCK|DSF_RUNNING);
+ return(pc586init(unit));
+
+}
+
+/*
+ * pc586init:
+ *
+ * Another routine that interfaces the "if" layer to this driver.
+ * Simply resets the structures that are used by "upper layers".
+ * As well as calling pc586hwrst that does reset the pc586 board.
+ *
+ * input : board number
+ * output : structures (if structs) and board are reset
+ *
+ */
+pc586init(unit)
+int unit;
+{
+ struct ifnet *ifp;
+ int stat;
+ spl_t oldpri;
+
+ ifp = &(pc_softc[unit].ds_if);
+#ifdef MACH_KERNEL
+#else MACH_KERNEL
+ if (ifp->if_addrlist == (struct ifaddr *)0) {
+ return;
+ }
+#endif MACH_KERNEL
+ oldpri = SPLNET();
+ if ((stat = pc586hwrst(unit)) == TRUE) {
+#ifdef MACH_KERNEL
+#undef HZ
+#define HZ hz
+#endif MACH_KERNEL
+ timeout(pc586watch, &(ifp->if_unit), 5*HZ);
+ pc_softc[unit].timer = 5;
+
+ pc_softc[unit].ds_if.if_flags |= IFF_RUNNING;
+ pc_softc[unit].flags |= DSF_RUNNING;
+ pc_softc[unit].tbusy = 0;
+ pc586start(unit);
+#if DLI
+ dli_init();
+#endif DLI
+ } else
+ printf("pc%d init(): trouble resetting board.\n", unit);
+ splx(oldpri);
+ return(stat);
+}
+
+#ifdef MACH_KERNEL
+/*ARGSUSED*/
+pc586open(dev, flag)
+ dev_t dev;
+ int flag;
+{
+ register int unit;
+ pc_softc_t *sp;
+
+ unit = minor(dev); /* XXX */
+ if (unit < 0 || unit >= NPC586 || !pc_softc[unit].seated)
+ return (ENXIO);
+
+ pc_softc[unit].ds_if.if_flags |= IFF_UP;
+ pc586init(unit);
+ return (0);
+}
+#endif MACH_KERNEL
+
+/*
+ * pc586start:
+ *
+ * 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
+ *
+ */
+pc586start(unit)
+int unit;
+{
+#ifdef MACH_KERNEL
+ io_req_t m;
+#else MACH_KERNEL
+ struct mbuf *m;
+#endif MACH_KERNEL
+ struct ifnet *ifp;
+ register pc_softc_t *is = &pc_softc[unit];
+ volatile scb_t *scb_p = (volatile scb_t *)(pc_softc[unit].sram + OFFSET_SCB);
+
+ if (is->tbusy) {
+ if (!(scb_p->scb_status & 0x0700)) { /* ! IDLE */
+ is->tbusy = 0;
+ pc586_cntrs[unit].xmt.busy++;
+ /*
+ * This is probably just a race. The xmt'r is just
+ * became idle but WE have masked interrupts so ...
+ */
+ if (xmt_watch) printf("!!");
+ } else
+ return;
+ }
+
+ ifp = &(pc_softc[unit].ds_if);
+ IF_DEQUEUE(&ifp->if_snd, m);
+#ifdef MACH_KERNEL
+ if (m != 0)
+#else MACH_KERNEL
+ if (m != (struct mbuf *)0)
+#endif MACH_KERNEL
+ {
+ is->tbusy++;
+ pc586_cntrs[unit].xmt.xmt++;
+ pc586xmt(unit, m);
+ }
+ return;
+}
+
+/*
+ * pc586read:
+ *
+ * This routine does the actual copy of data (including ethernet header
+ * structure) from the pc586 to an mbuf chain that will be passed up
+ * to the "if" (network interface) layer. NOTE: we currently
+ * don't handle trailer protocols, so if that is needed, it will
+ * (at least in part) be added here. For simplicities sake, this
+ * routine copies the receive buffers from the board into a local (stack)
+ * buffer until the frame has been copied from the board. Once in
+ * the local buffer, the contents are copied to an mbuf chain that
+ * is then enqueued onto the appropriate "if" queue.
+ *
+ * input : board number, and an frame descriptor pointer
+ * output : the packet is put into an mbuf chain, and passed up
+ * assumes : if any errors occur, packet is "dropped on the floor"
+ *
+ */
+pc586read(unit, fd_p)
+int unit;
+fd_t *fd_p;
+{
+ register pc_softc_t *is = &pc_softc[unit];
+ register struct ifnet *ifp = &is->ds_if;
+ struct ether_header eh;
+#ifdef MACH_KERNEL
+ ipc_kmsg_t new_kmsg;
+ struct ether_header *ehp;
+ struct packet_header *pkt;
+ char *dp;
+#else MACH_KERNEL
+ struct mbuf *m, *tm;
+#endif MACH_KERNEL
+ rbd_t *rbd_p;
+ u_char *buffer_p;
+ u_char *mb_p;
+ u_short mlen, len, clen;
+ u_short bytes_in_msg, bytes_in_mbuf, bytes;
+
+
+ if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
+ printf("pc%d read(): board is not running.\n", ifp->if_unit);
+ pc586intoff(ifp->if_unit);
+ }
+ pc586_cntrs[unit].rcv.rcv++;
+#ifdef MACH_KERNEL
+ new_kmsg = net_kmsg_get();
+ if (new_kmsg == IKM_NULL) {
+ /*
+ * Drop the received packet.
+ */
+ is->ds_if.if_rcvdrops++;
+
+ /*
+ * not only do we want to return, we need to drop the packet on
+ * the floor to clear the interrupt.
+ */
+ return 1;
+ }
+ ehp = (struct ether_header *) (&net_kmsg(new_kmsg)->header[0]);
+ pkt = (struct packet_header *)(&net_kmsg(new_kmsg)->packet[0]);
+
+ /*
+ * Get ether header.
+ */
+ ehp->ether_type = fd_p->length;
+ len = sizeof(struct ether_header);
+ bcopy16(fd_p->source, ehp->ether_shost, ETHER_ADD_SIZE);
+ bcopy16(fd_p->destination, ehp->ether_dhost, ETHER_ADD_SIZE);
+
+ /*
+ * Get packet body.
+ */
+ dp = (char *)(pkt + 1);
+
+ rbd_p = (rbd_t *)ram_to_ptr(fd_p->rbd_offset, unit);
+ if (rbd_p == 0) {
+ printf("pc%d read(): Invalid buffer\n", unit);
+ if (pc586hwrst(unit) != TRUE) {
+ printf("pc%d read(): hwrst trouble.\n", unit);
+ }
+ net_kmsg_put(new_kmsg);
+ return 0;
+ }
+
+ do {
+ buffer_p = (u_char *)(pc_softc[unit].sram + rbd_p->buffer_addr);
+ bytes_in_msg = rbd_p->status & RBD_SW_COUNT;
+ bcopy16((u_short *)buffer_p,
+ (u_short *)dp,
+ (bytes_in_msg + 1) & ~1); /* but we know it's even */
+ len += bytes_in_msg;
+ dp += bytes_in_msg;
+ if (rbd_p->status & RBD_SW_EOF)
+ break;
+ rbd_p = (rbd_t *)ram_to_ptr(rbd_p->next_rbd_offset, unit);
+ } while ((int) rbd_p);
+
+ pkt->type = ehp->ether_type;
+ pkt->length =
+ len - sizeof(struct ether_header)
+ + sizeof(struct packet_header);
+
+ /*
+ * Send the packet to the network module.
+ */
+ net_packet(ifp, new_kmsg, pkt->length, ethernet_priority(new_kmsg));
+ return 1;
+#else MACH_KERNEL
+ eh.ether_type = ntohs(fd_p->length);
+ bcopy16(fd_p->source, eh.ether_shost, ETHER_ADD_SIZE);
+ bcopy16(fd_p->destination, eh.ether_dhost, ETHER_ADD_SIZE);
+
+ if ((rbd_p =(rbd_t *)ram_to_ptr(fd_p->rbd_offset, unit))== (rbd_t *)NULL) {
+ printf("pc%d read(): Invalid buffer\n", unit);
+ if (pc586hwrst(unit) != TRUE) {
+ printf("pc%d read(): hwrst trouble.\n", unit);
+ }
+ return 0;
+ }
+
+ bytes_in_msg = rbd_p->status & RBD_SW_COUNT;
+ buffer_p = (u_char *)(pc_softc[unit].sram + rbd_p->buffer_addr);
+ MGET(m, M_DONTWAIT, MT_DATA);
+ tm = m;
+ if (m == (struct mbuf *)0) {
+ /*
+ * not only do we want to return, we need to drop the packet on
+ * the floor to clear the interrupt.
+ *
+ */
+ printf("pc%d read(): No mbuf 1st\n", unit);
+ if (pc586hwrst(unit) != TRUE) {
+ pc586intoff(unit);
+ printf("pc%d read(): hwrst trouble.\n", unit);
+ pc_softc[unit].timer = 0;
+ }
+ return 0;
+ }
+m->m_next = (struct mbuf *) 0;
+ m->m_len = MLEN;
+ if (bytes_in_msg > 2 * MLEN - sizeof (struct ifnet **)) {
+ MCLGET(m);
+ }
+ /*
+ * 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;
+ mlen = sizeof (struct ifnet **);
+ clen = mlen;
+ bytes_in_mbuf = m->m_len - sizeof(struct ifnet **);
+ mb_p = mtod(tm, u_char *) + sizeof (struct ifnet **);
+ bytes = min(bytes_in_mbuf, bytes_in_msg);
+ do {
+ if (bytes & 1)
+ len = bytes + 1;
+ else
+ len = bytes;
+ bcopy16(buffer_p, mb_p, len);
+ clen += bytes;
+ mlen += bytes;
+
+ if (!(bytes_in_mbuf -= bytes)) {
+ MGET(tm->m_next, M_DONTWAIT, MT_DATA);
+ tm = tm->m_next;
+ if (tm == (struct mbuf *)0) {
+ m_freem(m);
+ printf("pc%d read(): No mbuf nth\n", unit);
+ if (pc586hwrst(unit) != TRUE) {
+ pc586intoff(unit);
+ printf("pc%d read(): hwrst trouble.\n", unit);
+ pc_softc[unit].timer = 0;
+ }
+ return 0;
+ }
+ mlen = 0;
+ tm->m_len = MLEN;
+ bytes_in_mbuf = MLEN;
+ mb_p = mtod(tm, u_char *);
+ } else
+ mb_p += bytes;
+
+ if (!(bytes_in_msg -= bytes)) {
+ if (rbd_p->status & RBD_SW_EOF ||
+ (rbd_p = (rbd_t *)ram_to_ptr(rbd_p->next_rbd_offset, unit)) ==
+ NULL) {
+ tm->m_len = mlen;
+ break;
+ } else {
+ bytes_in_msg = rbd_p->status & RBD_SW_COUNT;
+ buffer_p = (u_char *)(pc_softc[unit].sram + rbd_p->buffer_addr);
+ }
+ } else
+ buffer_p += bytes;
+
+ bytes = min(bytes_in_mbuf, bytes_in_msg);
+ } while(1);
+#ifdef IF_CNTRS
+/* clen -= (sizeof (struct ifnet **)
+ clen += 4 /* crc */;
+ clen += sizeof (struct ether_header);
+ pc586_ein[log_2(clen)]++;
+ if (clen < 128) pc586_lin[clen>>3]++;
+
+ if (eh.ether_type == ETHERTYPE_ARP) {
+ pc586_arp++;
+ if (pc586_narp) {
+ pc586_ein[log_2(clen)]--;
+ if (clen < 128) pc586_lin[clen>>3]--;
+ }
+ }
+#endif IF_CNTRS
+ /*
+ * received packet is now in a chain of mbuf's. next step is
+ * to pass the packet upwards.
+ *
+ */
+ pc586send_packet_up(m, &eh, is);
+ return 1;
+#endif MACH_KERNEL
+}
+
+/*
+ * Send a packet composed of an mbuf chain to the higher levels
+ *
+ */
+#ifndef MACH_KERNEL
+pc586send_packet_up(m, eh, is)
+struct mbuf *m;
+struct ether_header *eh;
+pc_softc_t *is;
+{
+ register struct ifqueue *inq;
+ spl_t opri;
+
+ switch (eh->ether_type) {
+#ifdef INET
+ case ETHERTYPE_IP:
+ schednetisr(NETISR_IP);
+ inq = &ipintrq;
+ break;
+ case ETHERTYPE_ARP:
+ arpinput(&is->pc586_ac, m);
+ return;
+#endif
+#ifdef NS
+ case ETHERTYPE_NS:
+ schednetisr(NETISR_NS);
+ inq = &nsintrq;
+ break;
+#endif
+ default:
+#if DLI
+ {
+ eh.ether_type = htons(eh.ether_type);
+ dli_input(m,eh.ether_type,&eh.ether_shost[0],
+ &de_dlv[ds->ds_if.if_unit], &eh);
+ }
+#else DLI
+ m_freem(m);
+#endif DLI
+ return;
+ }
+ opri = SPLNET();
+ if (IF_QFULL(inq)) {
+ IF_DROP(inq);
+ splx(opri);
+ m_freem(m);
+ return;
+ }
+ IF_ENQUEUE(inq, m);
+ splx(opri);
+ return;
+}
+#endif MACH_KERNEL
+
+#ifdef MACH_KERNEL
+pc586output(dev, ior)
+ dev_t dev;
+ io_req_t ior;
+{
+ register int unit;
+
+ unit = minor(dev); /* XXX */
+ if (unit < 0 || unit >= NPC586 || !pc_softc[unit].seated)
+ return (ENXIO);
+
+ return (net_write(&pc_softc[unit].ds_if, pc586start, ior));
+}
+
+pc586setinput(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 (unit < 0 || unit >= NPC586 || !pc_softc[unit].seated)
+ return (ENXIO);
+
+ return (net_set_filter(&pc_softc[unit].ds_if,
+ receive_port, priority,
+ filter, filter_count));
+}
+#else MACH_KERNEL
+/*
+ * pc586output:
+ *
+ * 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. The routines that actually transmit the
+ * data (pc586xmt()) expect the ethernet structure to precede
+ * the data in the mbuf. This information is required by the
+ * 82586's transfer command segment, and thus mbuf's cannot
+ * be simply "slammed" out onto the network.
+ *
+ * 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
+ *
+ */
+pc586output(ifp, m0, dst)
+struct ifnet *ifp;
+struct mbuf *m0;
+struct sockaddr *dst;
+{
+ register pc_softc_t *is = &pc_softc[ifp->if_unit];
+ register struct mbuf *m = m0;
+ int type, error;
+ spl_t opri;
+ u_char edst[6];
+ struct in_addr idst;
+ register struct ether_header *eh;
+ register int off;
+ int usetrailers;
+
+ if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
+ printf("pc%d output(): board is not running.\n", ifp->if_unit);
+ pc586intoff(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(&is->pc586_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
+
+#if DLI
+ case AF_DLI:
+ if (m->m_len < sizeof(struct ether_header))
+ {
+ error = EMSGSIZE;
+ goto bad;
+ }
+ eh = mtod(m, struct ether_header *);
+ bcopy(dst->sa_data, (caddr_t)eh->ether_dhost,
+ sizeof (eh->ether_dhost));
+ goto gotheader;
+#endif DLI
+
+ 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("pc%d output(): 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)is->ds_addr,(caddr_t)eh->ether_shost, sizeof(edst));
+#if DLI
+gotheader:
+#endif DLI
+
+ /*
+ * 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);
+ /*
+ * Some action needs to be added here for checking whether the
+ * board is already transmitting. If it is, we don't want to
+ * start it up (ie call pc586start()). We will attempt to send
+ * packets that are queued up after an interrupt occurs. Some
+ * flag checking action has to happen here and/or in the start
+ * routine. This note is here to remind me that some thought
+ * is needed and there is a potential problem here.
+ *
+ */
+ pc586start(ifp->if_unit);
+ splx(opri);
+ return (0);
+bad:
+ m_freem(m0);
+ return (error);
+}
+#endif MACH_KERNEL
+
+#ifdef MACH_KERNEL
+pc586getstat(dev, flavor, status, count)
+ dev_t dev;
+ int flavor;
+ dev_status_t status; /* pointer to OUT array */
+ unsigned int *count; /* out */
+{
+ register int unit = minor(dev);
+ register pc_softc_t *sp;
+
+ if (unit < 0 || unit >= NPC586 || !pc_softc[unit].seated)
+ return (ENXIO);
+
+ sp = &pc_softc[unit];
+ return (net_getstat(&sp->ds_if, flavor, status, count));
+}
+
+pc586setstat(dev, flavor, status, count)
+ dev_t dev;
+ int flavor;
+ dev_status_t status;
+ unsigned int count;
+{
+ register int unit = minor(dev);
+ register pc_softc_t *sp;
+
+ if (unit < 0 || unit >= NPC586 || !pc_softc[unit].seated)
+ return (ENXIO);
+
+ sp = &pc_softc[unit];
+
+ 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_OPERATION);
+
+ if (ns->flags & IFF_ALLMULTI)
+ mode |= MOD_ENAL;
+ if (ns->flags & IFF_PROMISC)
+ mode |= MOD_PROM;
+
+ /*
+ * Force a complete 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);
+ pc586init(unit);
+ }
+ }
+ break;
+ }
+
+ default:
+ return (D_INVALID_OPERATION);
+ }
+ return (D_SUCCESS);
+
+}
+#else MACH_KERNEL
+/*
+ * pc586ioctl:
+ *
+ * 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
+ * pc586 board(s) or related structures
+ * return : error is returned containing exit conditions
+ *
+ */
+pc586ioctl(ifp, cmd, data)
+struct ifnet *ifp;
+int cmd;
+caddr_t data;
+{
+ register struct ifaddr *ifa = (struct ifaddr *)data;
+ int unit = ifp->if_unit;
+ register pc_softc_t *is = &pc_softc[unit];
+ short mode = 0;
+ int error = 0;
+ spl_t opri;
+
+ opri = SPLNET();
+ switch (cmd) {
+ case SIOCSIFADDR:
+ ifp->if_flags |= IFF_UP;
+ pc586init(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
+ pc586setaddr(ina->x_host.c_host, unit);
+ 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 (is->mode != mode) {
+ is->mode = mode;
+ if (is->flags & DSF_RUNNING) {
+ is->flags &= ~(DSF_LOCK|DSF_RUNNING);
+ pc586init(unit);
+ }
+ }
+ if ((ifp->if_flags & IFF_UP) == 0 && is->flags & DSF_RUNNING) {
+ printf("pc%d ioctl(): board is not running\n", unit);
+ is->flags &= ~(DSF_LOCK | DSF_RUNNING);
+ is->timer = -1;
+ pc586intoff(unit);
+ } else if (ifp->if_flags & IFF_UP && (is->flags & DSF_RUNNING) == 0) {
+ pc586init(unit);
+ }
+ break;
+#ifdef IF_CNTRS
+ case SIOCCIFCNTRS:
+ if (!suser()) {
+ error = EPERM;
+ break;
+ }
+ bzero((caddr_t)pc586_ein, sizeof (pc586_ein));
+ bzero((caddr_t)pc586_eout, sizeof (pc586_eout));
+ bzero((caddr_t)pc586_lin, sizeof (pc586_lin));
+ bzero((caddr_t)pc586_lout, sizeof (pc586_lout));
+ bzero((caddr_t)&pc586_arp, sizeof (int));
+ bzero((caddr_t)&pc586_cntrs, sizeof (pc586_cntrs));
+ break;
+#endif IF_CNTRS
+ default:
+ error = EINVAL;
+ }
+ splx(opri);
+ return (error);
+}
+#endif MACH_KERNEL
+
+/*
+ * pc586hwrst:
+ *
+ * This routine resets the pc586 board that corresponds to the
+ * board number passed in.
+ *
+ * input : board number to do a hardware reset
+ * output : board is reset
+ *
+ */
+pc586hwrst(unit)
+int unit;
+{
+ CMD(CHANATT, CMD_0, unit);
+ CMD(RESET, CMD_1, unit);
+ { int i; for (i = 0; i < 1000; i++); /* 4 clocks at 6Mhz */}
+ CMD(RESET,CMD_0, unit);
+
+/*
+ * for (i = 0; i < 1000000; i++);
+ * with this loop above and with the reset toggle also looping to
+ * 1000000. We don't see the reset behaving as advertised. DOES
+ * IT HAPPEN AT ALL. In particular, NORMODE, ENABLE, and XFER
+ * should all be zero and they have not changed at all.
+ */
+ CMD(INTENAB, CMD_0, unit);
+ CMD(NORMMODE, CMD_0, unit);
+ CMD(XFERMODE, CMD_1, unit);
+
+ pc586bldcu(unit);
+
+ if (pc586diag(unit) == FALSE)
+ return(FALSE);
+
+ if (pc586config(unit) == FALSE)
+ return(FALSE);
+ /*
+ * insert code for loopback test here
+ *
+ */
+ pc586rustrt(unit);
+
+ pc586inton(unit);
+ CMD(NORMMODE, CMD_1, unit);
+ return(TRUE);
+}
+
+/*
+ * pc586watch():
+ *
+ * This routine is the watchdog timer routine for the pc586 chip. If
+ * chip wedges, this routine will fire and cause a board reset and
+ * begin again.
+ *
+ * input : which board is timing out
+ * output : potential board reset if wedged
+ *
+ */
+int watch_dead = 0;
+pc586watch(b_ptr)
+caddr_t b_ptr;
+{
+ spl_t opri;
+ int unit = *b_ptr;
+
+ if ((pc_softc[unit].ds_if.if_flags & IFF_UP) == 0) {
+ return;
+ }
+ if (pc_softc[unit].timer == -1) {
+ timeout(pc586watch, b_ptr, 5*HZ);
+ return;
+ }
+ if (--pc_softc[unit].timer != -1) {
+ timeout(pc586watch, b_ptr, 1*HZ);
+ return;
+ }
+
+ opri = SPLNET();
+#ifdef notdef
+ printf("pc%d watch(): 6sec timeout no %d\n", unit, ++watch_dead);
+#endif notdef
+ pc586_cntrs[unit].watch++;
+ if (pc586hwrst(unit) != TRUE) {
+ printf("pc%d watch(): hwrst trouble.\n", unit);
+ pc_softc[unit].timer = 0;
+ } else {
+ timeout(pc586watch, b_ptr, 1*HZ);
+ pc_softc[unit].timer = 5;
+ }
+ splx(opri);
+}
+
+/*
+ * pc586intr:
+ *
+ * This function is the interrupt handler for the pc586 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
+ *
+ */
+pc586intr(unit)
+int unit;
+{
+ volatile scb_t *scb_p = (volatile scb_t *)(pc_softc[unit].sram + OFFSET_SCB);
+ volatile ac_t *cb_p = (volatile ac_t *)(pc_softc[unit].sram + OFFSET_CU);
+ int next, x;
+ int i;
+ u_short int_type;
+
+ if (pc_softc[unit].seated == FALSE) {
+ printf("pc%d intr(): board not seated\n", unit);
+ return(-1);
+ }
+
+ while ((int_type = (scb_p->scb_status & SCB_SW_INT)) != 0) {
+ pc586ack(unit);
+ if (int_type & SCB_SW_FR) {
+ pc586rcv(unit);
+ watch_dead=0;
+ }
+ if (int_type & SCB_SW_RNR) {
+ pc586_cntrs[unit].rcv.ovw++;
+#ifdef notdef
+ printf("pc%d intr(): receiver overrun! begin_fd = %x\n",
+ unit, pc_softc[unit].begin_fd);
+#endif notdef
+ pc586rustrt(unit);
+ }
+ if (int_type & SCB_SW_CNA) {
+ /*
+ * At present, we don't care about CNA's. We
+ * believe they are a side effect of XMT.
+ */
+ }
+ if (int_type & SCB_SW_CX) {
+ /*
+ * At present, we only request Interrupt for
+ * XMT.
+ */
+ if ((!(cb_p->ac_status & AC_SW_OK)) ||
+ (cb_p->ac_status & (0xfff^TC_SQE))) {
+ if (cb_p->ac_status & TC_DEFER) {
+ if (xmt_watch) printf("DF");
+ pc586_cntrs[unit].xmt.defer++;
+ } else if (cb_p->ac_status & (TC_COLLISION|0xf)) {
+ if (xmt_watch) printf("%x",cb_p->ac_status & 0xf);
+ } else if (xmt_watch)
+ printf("pc%d XMT: %x %x\n",
+ unit, cb_p->ac_status, cb_p->ac_command);
+ }
+ pc586_cntrs[unit].xmt.xmti++;
+ pc_softc[unit].tbusy = 0;
+ pc586start(unit);
+ }
+ pc_softc[unit].timer = 5;
+ }
+ return(0);
+}
+
+/*
+ * pc586rcv:
+ *
+ * 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 pc586. If so, the routine pc586read 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"
+ *
+ */
+pc586rcv(unit)
+int unit;
+{
+ fd_t *fd_p;
+
+ for (fd_p = pc_softc[unit].begin_fd; fd_p != (fd_t *)NULL;
+ fd_p = pc_softc[unit].begin_fd) {
+ if (fd_p->status == 0xffff || fd_p->rbd_offset == 0xffff) {
+ if (pc586hwrst(unit) != TRUE)
+ printf("pc%d rcv(): hwrst ffff trouble.\n",
+ unit);
+ return;
+ } else if (fd_p->status & AC_SW_C) {
+ fd_t *bfd = (fd_t *)ram_to_ptr(fd_p->link_offset, unit);
+
+ if (fd_p->status == (RFD_DONE|RFD_RSC)) {
+ /* lost one */;
+#ifdef notdef
+ printf("pc%d RCV: RSC %x\n",
+ unit, fd_p->status);
+#endif notdef
+ pc586_cntrs[unit].rcv.partial++;
+ } else if (!(fd_p->status & RFD_OK))
+ printf("pc%d RCV: !OK %x\n",
+ unit, fd_p->status);
+ else if (fd_p->status & 0xfff)
+ printf("pc%d RCV: ERRs %x\n",
+ unit, fd_p->status);
+ else
+ if (!pc586read(unit, fd_p))
+ return;
+ if (!pc586requeue(unit, fd_p)) { /* abort on chain error */
+ if (pc586hwrst(unit) != TRUE)
+ printf("pc%d rcv(): hwrst trouble.\n", unit);
+ return;
+ }
+ pc_softc[unit].begin_fd = bfd;
+ } else
+ break;
+ }
+ return;
+}
+
+/*
+ * pc586requeue:
+ *
+ * This routine puts rbd's used in the last receive back onto the
+ * free list for the next receive.
+ *
+ */
+pc586requeue(unit, fd_p)
+int unit;
+fd_t *fd_p;
+{
+ rbd_t *l_rbdp;
+ rbd_t *f_rbdp;
+
+#ifndef REQUEUE_DBG
+ if (bad_rbd_chain(fd_p->rbd_offset, unit))
+ return 0;
+#endif REQUEUE_DBG
+ f_rbdp = (rbd_t *)ram_to_ptr(fd_p->rbd_offset, unit);
+ if (f_rbdp != NULL) {
+ l_rbdp = f_rbdp;
+ while ( (!(l_rbdp->status & RBD_SW_EOF)) &&
+ (l_rbdp->next_rbd_offset != 0xffff))
+ {
+ l_rbdp->status = 0;
+ l_rbdp = (rbd_t *)ram_to_ptr(l_rbdp->next_rbd_offset,
+ unit);
+ }
+ l_rbdp->next_rbd_offset = PC586NULL;
+ l_rbdp->status = 0;
+ l_rbdp->size |= AC_CW_EL;
+ pc_softc[unit].end_rbd->next_rbd_offset =
+ ptr_to_ram((char *)f_rbdp, unit);
+ pc_softc[unit].end_rbd->size &= ~AC_CW_EL;
+ pc_softc[unit].end_rbd= l_rbdp;
+ }
+
+ fd_p->status = 0;
+ fd_p->command = AC_CW_EL;
+ fd_p->link_offset = PC586NULL;
+ fd_p->rbd_offset = PC586NULL;
+
+ pc_softc[unit].end_fd->link_offset = ptr_to_ram((char *)fd_p, unit);
+ pc_softc[unit].end_fd->command = 0;
+ pc_softc[unit].end_fd = fd_p;
+
+ return 1;
+}
+
+/*
+ * pc586xmt:
+ *
+ * This routine fills in the appropriate registers and memory
+ * locations on the PC586 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
+ *
+ */
+#ifdef DEBUG
+int xmt_debug = 0;
+#endif DEBUG
+pc586xmt(unit, m)
+int unit;
+#ifdef MACH_KERNEL
+io_req_t m;
+#else MACH_KERNEL
+struct mbuf *m;
+#endif MACH_KERNEL
+{
+ pc_softc_t *is = &pc_softc[unit];
+ register u_char *xmtdata_p = (u_char *)(is->sram + OFFSET_TBUF);
+ register u_short *xmtshort_p;
+#ifdef MACH_KERNEL
+ register struct ether_header *eh_p = (struct ether_header *)m->io_data;
+#else MACH_KERNEL
+ struct mbuf *tm_p = m;
+ register struct ether_header *eh_p = mtod(m, struct ether_header *);
+ u_char *mb_p = mtod(m, u_char *) + sizeof(struct ether_header);
+ u_short count = m->m_len - sizeof(struct ether_header);
+#endif MACH_KERNEL
+ volatile scb_t *scb_p = (volatile scb_t *)(is->sram + OFFSET_SCB);
+ volatile ac_t *cb_p = (volatile ac_t *)(is->sram + OFFSET_CU);
+ tbd_t *tbd_p = (tbd_t *)(is->sram + OFFSET_TBD);
+ u_short tbd = OFFSET_TBD;
+ u_short len, clen = 0;
+
+ cb_p->ac_status = 0;
+ cb_p->ac_command = (AC_CW_EL|AC_TRANSMIT|AC_CW_I);
+ cb_p->ac_link_offset = PC586NULL;
+ cb_p->cmd.transmit.tbd_offset = OFFSET_TBD;
+
+ bcopy16(eh_p->ether_dhost, cb_p->cmd.transmit.dest_addr, ETHER_ADD_SIZE);
+ cb_p->cmd.transmit.length = (u_short)(eh_p->ether_type);
+
+#ifndef MACH_KERNEL
+#ifdef DEBUG
+ if (xmt_debug)
+ printf("XMT mbuf: L%d @%x ", count, mb_p);
+#endif DEBUG
+#endif MACH_KERNEL
+ tbd_p->act_count = 0;
+ tbd_p->buffer_base = 0;
+ tbd_p->buffer_addr = ptr_to_ram(xmtdata_p, unit);
+#ifdef MACH_KERNEL
+ { int Rlen, Llen;
+ clen = m->io_count - sizeof(struct ether_header);
+ Llen = clen & 1;
+ Rlen = ((int)(m->io_data + sizeof(struct ether_header))) & 1;
+
+ bcopy16(m->io_data + sizeof(struct ether_header) - Rlen,
+ xmtdata_p,
+ clen + (Rlen + Llen) );
+ xmtdata_p += clen + Llen;
+ tbd_p->act_count = clen;
+ tbd_p->buffer_addr += Rlen;
+ }
+#else MACH_KERNEL
+ do {
+ if (count) {
+ if (clen + count > ETHERMTU)
+ break;
+ if (count & 1)
+ len = count + 1;
+ else
+ len = count;
+ bcopy16(mb_p, xmtdata_p, len);
+ clen += count;
+ tbd_p->act_count += count;
+ xmtdata_p += len;
+ if ((tm_p = tm_p->m_next) == (struct mbuf *)0)
+ break;
+ if (count & 1) {
+ /* go to the next descriptor */
+ tbd_p++->next_tbd_offset = (tbd += sizeof (tbd_t));
+ tbd_p->act_count = 0;
+ tbd_p->buffer_base = 0;
+ tbd_p->buffer_addr = ptr_to_ram(xmtdata_p, unit);
+ /* at the end -> coallesce remaining mbufs */
+ if (tbd == OFFSET_TBD + (N_TBD-1) * sizeof (tbd_t)) {
+ pc586sftwsleaze(&count, &mb_p, &tm_p, unit);
+ continue;
+ }
+ /* next mbuf short -> coallesce as needed */
+ if ( (tm_p->m_next == (struct mbuf *) 0) ||
+#define HDW_THRESHOLD 55
+ tm_p->m_len > HDW_THRESHOLD)
+ /* ok */;
+ else {
+ pc586hdwsleaze(&count, &mb_p, &tm_p, unit);
+ continue;
+ }
+ }
+ } else if ((tm_p = tm_p->m_next) == (struct mbuf *)0)
+ break;
+ count = tm_p->m_len;
+ mb_p = mtod(tm_p, u_char *);
+#ifdef DEBUG
+ if (xmt_debug)
+ printf("mbuf+ L%d @%x ", count, mb_p);
+#endif DEBUG
+ } while (1);
+#endif MACH_KERNEL
+#ifdef DEBUG
+ if (xmt_debug)
+ printf("CLEN = %d\n", clen);
+#endif DEBUG
+ if (clen < ETHERMIN) {
+ tbd_p->act_count += ETHERMIN - clen;
+ for (xmtshort_p = (u_short *)xmtdata_p;
+ clen < ETHERMIN;
+ clen += 2) *xmtshort_p++ = 0;
+ }
+ tbd_p->act_count |= TBD_SW_EOF;
+ tbd_p->next_tbd_offset = PC586NULL;
+#ifdef IF_CNTRS
+ clen += sizeof (struct ether_header) + 4 /* crc */;
+ pc586_eout[log_2(clen)]++;
+ if (clen < 128) pc586_lout[clen>>3]++;
+#endif IF_CNTRS
+#ifdef DEBUG
+ if (xmt_debug) {
+ pc586tbd(unit);
+ printf("\n");
+ }
+#endif DEBUG
+
+ while (scb_p->scb_command) ;
+ scb_p->scb_command = SCB_CU_STRT;
+ pc586chatt(unit);
+
+#ifdef MACH_KERNEL
+ iodone(m);
+#else MACH_KERNEL
+ for (count=0; ((count < 6) && (eh_p->ether_dhost[count] == 0xff)); count++) ;
+ if (count == 6) {
+ pc586send_packet_up(m, eh_p, is);
+ } else
+ m_freem(m);
+#endif MACH_KERNEL
+ return;
+}
+
+/*
+ * pc586bldcu:
+ *
+ * This function builds up the command unit structures. It inits
+ * the scp, iscp, scb, cb, tbd, and tbuf.
+ *
+ */
+pc586bldcu(unit)
+{
+ char *sram = pc_softc[unit].sram;
+ scp_t *scp_p = (scp_t *)(sram + OFFSET_SCP);
+ iscp_t *iscp_p = (iscp_t *)(sram + OFFSET_ISCP);
+ volatile scb_t *scb_p = (volatile scb_t *)(sram + OFFSET_SCB);
+ volatile ac_t *cb_p = (volatile ac_t *)(sram + OFFSET_CU);
+ tbd_t *tbd_p = (tbd_t *)(sram + OFFSET_TBD);
+ int i;
+
+ scp_p->scp_sysbus = 0;
+ scp_p->scp_iscp = OFFSET_ISCP;
+ scp_p->scp_iscp_base = 0;
+
+ iscp_p->iscp_busy = 1;
+ iscp_p->iscp_scb_offset = OFFSET_SCB;
+ iscp_p->iscp_scb = 0;
+ iscp_p->iscp_scb_base = 0;
+
+ pc586_cntrs[unit].rcv.crc += scb_p->scb_crcerrs;
+ pc586_cntrs[unit].rcv.frame += scb_p->scb_alnerrs;
+ pc586_cntrs[unit].rcv.rscerrs += scb_p->scb_rscerrs;
+ pc586_cntrs[unit].rcv.ovrnerrs += scb_p->scb_ovrnerrs;
+ scb_p->scb_status = 0;
+ scb_p->scb_command = 0;
+ scb_p->scb_cbl_offset = OFFSET_CU;
+ scb_p->scb_rfa_offset = OFFSET_RU;
+ scb_p->scb_crcerrs = 0;
+ scb_p->scb_alnerrs = 0;
+ scb_p->scb_rscerrs = 0;
+ scb_p->scb_ovrnerrs = 0;
+
+ scb_p->scb_command = SCB_RESET;
+ pc586chatt(unit);
+ for (i = 1000000; iscp_p->iscp_busy && (i-- > 0); );
+ if (!i) printf("pc%d bldcu(): iscp_busy timeout.\n", unit);
+ for (i = STATUS_TRIES; i-- > 0; ) {
+ if (scb_p->scb_status == (SCB_SW_CX|SCB_SW_CNA))
+ break;
+ }
+ if (!i)
+ printf("pc%d bldcu(): not ready after reset.\n", unit);
+ pc586ack(unit);
+
+ cb_p->ac_status = 0;
+ cb_p->ac_command = AC_CW_EL;
+ cb_p->ac_link_offset = OFFSET_CU;
+
+ tbd_p->act_count = 0;
+ tbd_p->next_tbd_offset = PC586NULL;
+ tbd_p->buffer_addr = 0;
+ tbd_p->buffer_base = 0;
+ return;
+}
+
+/*
+ * pc586bldru:
+ *
+ * This function builds the linear linked lists of fd's and
+ * rbd's. Based on page 4-32 of 1986 Intel microcom handbook.
+ *
+ */
+char *
+pc586bldru(unit)
+int unit;
+{
+ fd_t *fd_p = (fd_t *)(pc_softc[unit].sram + OFFSET_RU);
+ ru_t *rbd_p = (ru_t *)(pc_softc[unit].sram + OFFSET_RBD);
+ int i;
+
+ pc_softc[unit].begin_fd = fd_p;
+ for(i = 0; i < N_FD; i++, fd_p++) {
+ fd_p->status = 0;
+ fd_p->command = 0;
+ fd_p->link_offset = ptr_to_ram((char *)(fd_p + 1), unit);
+ fd_p->rbd_offset = PC586NULL;
+ }
+ pc_softc[unit].end_fd = --fd_p;
+ fd_p->link_offset = PC586NULL;
+ fd_p->command = AC_CW_EL;
+ fd_p = (fd_t *)(pc_softc[unit].sram + OFFSET_RU);
+
+ fd_p->rbd_offset = ptr_to_ram((char *)rbd_p, unit);
+ for(i = 0; i < N_RBD; i++, rbd_p = (ru_t *) &(rbd_p->rbuffer[RCVBUFSIZE])) {
+ rbd_p->r.status = 0;
+ rbd_p->r.buffer_addr = ptr_to_ram((char *)(rbd_p->rbuffer),
+ unit);
+ rbd_p->r.buffer_base = 0;
+ rbd_p->r.size = RCVBUFSIZE;
+ if (i != N_RBD-1) {
+ rbd_p->r.next_rbd_offset=ptr_to_ram(&(rbd_p->rbuffer[RCVBUFSIZE]),
+ unit);
+ } else {
+ rbd_p->r.next_rbd_offset = PC586NULL;
+ rbd_p->r.size |= AC_CW_EL;
+ pc_softc[unit].end_rbd = (rbd_t *)rbd_p;
+ }
+ }
+ return (char *)pc_softc[unit].begin_fd;
+}
+
+/*
+ * pc586rustrt:
+ *
+ * This routine starts the receive unit running. First checks if the
+ * board is actually ready, then the board is instructed to receive
+ * packets again.
+ *
+ */
+pc586rustrt(unit)
+int unit;
+{
+ volatile scb_t *scb_p = (volatile scb_t *)(pc_softc[unit].sram + OFFSET_SCB);
+ char *strt;
+
+ if ((scb_p->scb_status & SCB_RUS_READY) == SCB_RUS_READY)
+ return;
+
+ strt = pc586bldru(unit);
+ scb_p->scb_command = SCB_RU_STRT;
+ scb_p->scb_rfa_offset = ptr_to_ram(strt, unit);
+ pc586chatt(unit);
+ return;
+}
+
+/*
+ * pc586diag:
+ *
+ * This routine does a 586 op-code number 7, and obtains the
+ * diagnose status for the pc586.
+ *
+ */
+pc586diag(unit)
+int unit;
+{
+ volatile scb_t *scb_p = (volatile scb_t *)(pc_softc[unit].sram + OFFSET_SCB);
+ volatile ac_t *cb_p = (volatile ac_t *)(pc_softc[unit].sram + OFFSET_CU);
+ int i;
+
+ if (scb_p->scb_status & SCB_SW_INT) {
+ printf("pc%d diag(): bad initial state %\n",
+ unit, scb_p->scb_status);
+ pc586ack(unit);
+ }
+ cb_p->ac_status = 0;
+ cb_p->ac_command = (AC_DIAGNOSE|AC_CW_EL);
+ scb_p->scb_command = SCB_CU_STRT;
+ pc586chatt(unit);
+
+ for(i = 0; i < 0xffff; i++)
+ if ((cb_p->ac_status & AC_SW_C))
+ break;
+ if (i == 0xffff || !(cb_p->ac_status & AC_SW_OK)) {
+ printf("pc%d: diag failed; status = %x\n",
+ unit, cb_p->ac_status);
+ return(FALSE);
+ }
+
+ if ( (scb_p->scb_status & SCB_SW_INT) && (scb_p->scb_status != SCB_SW_CNA) ) {
+ printf("pc%d diag(): bad final state %x\n",
+ unit, scb_p->scb_status);
+ pc586ack(unit);
+ }
+ return(TRUE);
+}
+
+/*
+ * pc586config:
+ *
+ * This routine does a standard config of the pc586 board.
+ *
+ */
+pc586config(unit)
+int unit;
+{
+ volatile scb_t *scb_p = (volatile scb_t *)(pc_softc[unit].sram + OFFSET_SCB);
+ volatile ac_t *cb_p = (volatile ac_t *)(pc_softc[unit].sram + OFFSET_CU);
+ int i;
+
+
+/*
+ if ((scb_p->scb_status != SCB_SW_CNA) && (scb_p->scb_status & SCB_SW_INT) ) {
+ printf("pc%d config(): unexpected initial state %x\n",
+ unit, scb_p->scb_status);
+ }
+*/
+ pc586ack(unit);
+
+ cb_p->ac_status = 0;
+ cb_p->ac_command = (AC_CONFIGURE|AC_CW_EL);
+
+ /*
+ * below is the default board configuration from p2-28 from 586 book
+ */
+ cb_p->cmd.configure.fifolim_bytecnt = 0x080c;
+ cb_p->cmd.configure.addrlen_mode = 0x2600;
+ cb_p->cmd.configure.linprio_interframe = 0x6000;
+ cb_p->cmd.configure.slot_time = 0xf200;
+ cb_p->cmd.configure.hardware = 0x0000;
+ cb_p->cmd.configure.min_frame_len = 0x0040;
+
+ scb_p->scb_command = SCB_CU_STRT;
+ pc586chatt(unit);
+
+ for(i = 0; i < 0xffff; i++)
+ if ((cb_p->ac_status & AC_SW_C))
+ break;
+ if (i == 0xffff || !(cb_p->ac_status & AC_SW_OK)) {
+ printf("pc%d: config-configure failed; status = %x\n",
+ unit, cb_p->ac_status);
+ return(FALSE);
+ }
+/*
+ if (scb_p->scb_status & SCB_SW_INT) {
+ printf("pc%d configure(): bad configure state %x\n",
+ unit, scb_p->scb_status);
+ pc586ack(unit);
+ }
+*/
+ cb_p->ac_status = 0;
+ cb_p->ac_command = (AC_IASETUP|AC_CW_EL);
+
+ bcopy16(pc_softc[unit].ds_addr, cb_p->cmd.iasetup, ETHER_ADD_SIZE);
+
+ scb_p->scb_command = SCB_CU_STRT;
+ pc586chatt(unit);
+
+ for (i = 0; i < 0xffff; i++)
+ if ((cb_p->ac_status & AC_SW_C))
+ break;
+ if (i == 0xffff || !(cb_p->ac_status & AC_SW_OK)) {
+ printf("pc%d: config-address failed; status = %x\n",
+ unit, cb_p->ac_status);
+ return(FALSE);
+ }
+/*
+ if ((scb_p->scb_status & SCB_SW_INT) != SCB_SW_CNA) {
+ printf("pc%d configure(): unexpected final state %x\n",
+ unit, scb_p->scb_status);
+ }
+*/
+ pc586ack(unit);
+
+ return(TRUE);
+}
+
+/*
+ * pc586ack:
+ */
+pc586ack(unit)
+{
+ volatile scb_t *scb_p = (volatile scb_t *)(pc_softc[unit].sram + OFFSET_SCB);
+ int i;
+
+ if (!(scb_p->scb_command = scb_p->scb_status & SCB_SW_INT))
+ return;
+ CMD(CHANATT, 0x0001, unit);
+ for (i = 1000000; scb_p->scb_command && (i-- > 0); );
+ if (!i)
+ printf("pc%d pc586ack(): board not accepting command.\n", unit);
+}
+
+char *
+ram_to_ptr(offset, unit)
+int unit;
+u_short offset;
+{
+ if (offset == PC586NULL)
+ return(NULL);
+ if (offset > 0x3fff) {
+ printf("ram_to_ptr(%x, %d)\n", offset, unit);
+ panic("range");
+ return(NULL);
+ }
+ return(pc_softc[unit].sram + offset);
+}
+
+#ifndef REQUEUE_DBG
+bad_rbd_chain(offset, unit)
+{
+ rbd_t *rbdp;
+ char *sram = pc_softc[unit].sram;
+
+ for (;;) {
+ if (offset == PC586NULL)
+ return 0;
+ if (offset > 0x3fff) {
+ printf("pc%d: bad_rbd_chain offset = %x\n",
+ unit, offset);
+ pc586_cntrs[unit].rcv.bad_chain++;
+ return 1;
+ }
+
+ rbdp = (rbd_t *)(sram + offset);
+ offset = rbdp->next_rbd_offset;
+ }
+}
+#endif REQUEUE_DBG
+
+u_short
+ptr_to_ram(k_va, unit)
+char *k_va;
+int unit;
+{
+ return((u_short)(k_va - pc_softc[unit].sram));
+}
+
+pc586scb(unit)
+{
+ volatile scb_t *scb = (volatile scb_t *)(pc_softc[unit].sram + OFFSET_SCB);
+ volatile u_short*cmd = (volatile u_short *)(pc_softc[unit].prom + OFFSET_NORMMODE);
+ u_short i;
+
+ i = scb->scb_status;
+ printf("stat: stat %x, cus %x, rus %x //",
+ (i&0xf000)>>12, (i&0x0700)>>8, (i&0x0070)>>4);
+ i = scb->scb_command;
+ printf(" cmd: ack %x, cuc %x, ruc %x\n",
+ (i&0xf000)>>12, (i&0x0700)>>8, (i&0x0070)>>4);
+
+ printf("crc %d[%d], align %d[%d], rsc %d[%d], ovr %d[%d]\n",
+ scb->scb_crcerrs, pc586_cntrs[unit].rcv.crc,
+ scb->scb_alnerrs, pc586_cntrs[unit].rcv.frame,
+ scb->scb_rscerrs, pc586_cntrs[unit].rcv.rscerrs,
+ scb->scb_ovrnerrs, pc586_cntrs[unit].rcv.ovrnerrs);
+
+ printf("cbl %x, rfa %x //", scb->scb_cbl_offset, scb->scb_rfa_offset);
+ printf(" norm %x, ena %x, xfer %x //",
+ cmd[0] & 1, cmd[3] & 1, cmd[4] & 1);
+ printf(" atn %x, reset %x, type %x, stat %x\n",
+ cmd[1] & 1, cmd[2] & 1, cmd[5] & 1, cmd[6] & 1);
+}
+
+pc586tbd(unit)
+{
+ pc_softc_t *is = &pc_softc[unit];
+ tbd_t *tbd_p = (tbd_t *)(is->sram + OFFSET_TBD);
+ int i = 0;
+ int sum = 0;
+
+ do {
+ sum += (tbd_p->act_count & ~TBD_SW_EOF);
+ printf("%d: addr %x, count %d (%d), next %x, base %x\n",
+ i++, tbd_p->buffer_addr,
+ (tbd_p->act_count & ~TBD_SW_EOF), sum,
+ tbd_p->next_tbd_offset,
+ tbd_p->buffer_base);
+ if (tbd_p->act_count & TBD_SW_EOF)
+ break;
+ tbd_p = (tbd_t *)(is->sram + tbd_p->next_tbd_offset);
+ } while (1);
+}
+
+#ifndef MACH_KERNEL
+pc586hdwsleaze(countp, mb_pp, tm_pp, unit)
+struct mbuf **tm_pp;
+u_char **mb_pp;
+u_short *countp;
+{
+ struct mbuf *tm_p = *tm_pp;
+ u_char *mb_p = *mb_pp;
+ u_short count = 0;
+ u_char *cp;
+ int len;
+
+ pc586_cntrs[unit].xmt.sleaze++;
+ /*
+ * can we get a run that will be coallesced or
+ * that terminates before breaking
+ */
+ do {
+ count += tm_p->m_len;
+ if (tm_p->m_len & 1)
+ break;
+ } while ((tm_p = tm_p->m_next) != (struct mbuf *)0);
+ if ( (tm_p == (struct mbuf *)0) ||
+ count > HDW_THRESHOLD) {
+ *countp = (*tm_pp)->m_len;
+ *mb_pp = mtod((*tm_pp), u_char *);
+ printf("\n");
+ return;
+ }
+
+ /* we need to copy */
+ pc586_cntrs[unit].xmt.intrinsic++;
+ tm_p = *tm_pp;
+ mb_p = *mb_pp;
+ count = 0;
+ cp = (u_char *) t_packet;
+ do {
+ bcopy(mtod(tm_p, u_char *), cp, len = tm_p->m_len);
+ count += len;
+ if (count > HDW_THRESHOLD)
+ break;
+ cp += len;
+ if (tm_p->m_next == (struct mbuf *)0)
+ break;
+ tm_p = tm_p->m_next;
+ } while (1);
+ pc586_cntrs[unit].xmt.intrinsic_count += count;
+ *countp = count;
+ *mb_pp = (u_char *) t_packet;
+ *tm_pp = tm_p;
+ return;
+}
+
+pc586sftwsleaze(countp, mb_pp, tm_pp, unit)
+struct mbuf **tm_pp;
+u_char **mb_pp;
+u_short *countp;
+{
+ struct mbuf *tm_p = *tm_pp;
+ u_char *mb_p = *mb_pp;
+ u_short count = 0;
+ u_char *cp = (u_char *) t_packet;
+ int len;
+
+ pc586_cntrs[unit].xmt.chain++;
+ /* we need to copy */
+ do {
+ bcopy(mtod(tm_p, u_char *), cp, len = tm_p->m_len);
+ count += len;
+ cp += len;
+ if (tm_p->m_next == (struct mbuf *)0)
+ break;
+ tm_p = tm_p->m_next;
+ } while (1);
+
+ *countp = count;
+ *mb_pp = (u_char *) t_packet;
+ *tm_pp = tm_p;
+ return;
+}
+#endif MACH_KERNEL