summaryrefslogtreecommitdiffstats
path: root/usr.sbin/pccard/misc/sys
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/pccard/misc/sys')
-rw-r--r--usr.sbin/pccard/misc/sys/i386/conf/LAPTOP63
-rw-r--r--usr.sbin/pccard/misc/sys/pccard/Makefile25
-rw-r--r--usr.sbin/pccard/misc/sys/pccard/README13
-rw-r--r--usr.sbin/pccard/misc/sys/pccard/bpfilter.h1
-rw-r--r--usr.sbin/pccard/misc/sys/pccard/ed.h1
-rw-r--r--usr.sbin/pccard/misc/sys/pccard/if_ed.c1997
-rw-r--r--usr.sbin/pccard/misc/sys/pccard/if_edreg.h989
-rw-r--r--usr.sbin/pccard/misc/sys/pccard/lkm_ed.c143
-rw-r--r--usr.sbin/pccard/misc/sys/pccard/sio.c2463
9 files changed, 5695 insertions, 0 deletions
diff --git a/usr.sbin/pccard/misc/sys/i386/conf/LAPTOP b/usr.sbin/pccard/misc/sys/i386/conf/LAPTOP
new file mode 100644
index 0000000..9b26789
--- /dev/null
+++ b/usr.sbin/pccard/misc/sys/i386/conf/LAPTOP
@@ -0,0 +1,63 @@
+#
+# GENERIC -- Generic machine with WD/AHx/NCR/BTx family disks
+#
+# GENERIC,v 1.20 1994/11/18 19:10:25 jkh Exp
+#
+
+machine "i386"
+cpu "I386_CPU"
+cpu "I486_CPU"
+cpu "I586_CPU"
+ident LAPTOP
+maxusers 4
+
+options MATH_EMULATE #Support for x87 emulation
+options INET #InterNETworking
+options FFS #Berkeley Fast Filesystem
+options NFS #Network Filesystem
+options MSDOSFS #MSDOS Filesystem
+options PROCFS #Process filesystem
+options "COMPAT_43" #Compatible with BSD 4.3
+options UCONSOLE #X Console support
+options "FAT_CURSOR" #block cursor in syscons or pccons
+options "NCONS=4" #4 virtual consoles
+options ALLOW_CONFLICT_IOADDR
+options PSM_NO_RESET
+
+config kernel root on wd0 swap on wd0 and wd1 and sd0 and sd1 dumps on wd0
+
+controller crd0
+#device pcic0
+
+
+controller isa0
+
+controller fdc0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr
+disk fd0 at fdc0 drive 0
+
+controller wdc0 at isa? port "IO_WD1" bio irq 14 vector wdintr
+disk wd0 at wdc0 drive 0
+
+device sc0 at isa? port "IO_KBD" tty irq 1 vector scintr
+device npx0 at isa? port "IO_NPX" irq 13 vector npxintr
+device psm0 at isa? port "IO_KBD" tty irq 12 vector psmintr
+
+#device pcic0 at isa? port 0x3E0 tty irq 3 iomem 0xd0000
+
+device sio0 at isa? port "IO_COM1" tty irq 4 vector siointr
+device sio1 at isa? port "IO_COM2" tty irq 10 vector siointr
+#device sio1 at isa? port "IO_COM2" tty flags 0x80 irq 10 vector siointr
+
+device lpt0 at isa? port? tty irq 7 vector lptintr
+
+#device ed0 at isa? port 0x300 net flags 0x20 irq 5 iomem 0xd4000 vector edintr
+
+device apm0 at isa?
+
+pseudo-device loop
+pseudo-device ether
+pseudo-device log
+pseudo-device sl 2
+pseudo-device pty 16
+pseudo-device speaker
+pseudo-device gzip # Exec gzipped a.out's
diff --git a/usr.sbin/pccard/misc/sys/pccard/Makefile b/usr.sbin/pccard/misc/sys/pccard/Makefile
new file mode 100644
index 0000000..e78a156
--- /dev/null
+++ b/usr.sbin/pccard/misc/sys/pccard/Makefile
@@ -0,0 +1,25 @@
+all: pcic.o ed.o
+
+CFLAGS = -O -DINET -DKERNEL -DLKM -I/sys
+
+pcic.o: pcic.c
+ cc -c $(CFLAGS) pcic.c
+
+load: pcic.o
+ modload -e lkm_pcic pcic.o
+ pccardmem d0000
+
+unload:
+ modunload -n pcic
+
+skel.o: skel.c
+ cc -c $(CFLAGS) skel.c
+
+ldskel: skel.o
+ modload -e lkm_skel skel.o
+
+ed.o: if_ed.o lkm_ed.o
+ ld -r -o ed.o if_ed.o lkm_ed.o
+
+lded: ed.o
+ modload -e lkm_ed ed.o
diff --git a/usr.sbin/pccard/misc/sys/pccard/README b/usr.sbin/pccard/misc/sys/pccard/README
new file mode 100644
index 0000000..cc1ea70
--- /dev/null
+++ b/usr.sbin/pccard/misc/sys/pccard/README
@@ -0,0 +1,13 @@
+
+This directory contains:
+
+ card.h Include file for driver interface
+ cis.h CIS defines
+ slot.h Kernel interface for card/slot driver/devices interface
+ i82365.h Defines for PCIC device
+
+ pccard.c Mainline PC-Card support functions (card services).
+ pcic.c PCIC or compatible slot driver (loadable).
+ skel.c Skeleton loadable driver interfacing to card services.
+
+The other files are working copies of drivers.
diff --git a/usr.sbin/pccard/misc/sys/pccard/bpfilter.h b/usr.sbin/pccard/misc/sys/pccard/bpfilter.h
new file mode 100644
index 0000000..21d2ed8
--- /dev/null
+++ b/usr.sbin/pccard/misc/sys/pccard/bpfilter.h
@@ -0,0 +1 @@
+#define NBPFILTER 0
diff --git a/usr.sbin/pccard/misc/sys/pccard/ed.h b/usr.sbin/pccard/misc/sys/pccard/ed.h
new file mode 100644
index 0000000..d2d7bac
--- /dev/null
+++ b/usr.sbin/pccard/misc/sys/pccard/ed.h
@@ -0,0 +1 @@
+#define NED 2
diff --git a/usr.sbin/pccard/misc/sys/pccard/if_ed.c b/usr.sbin/pccard/misc/sys/pccard/if_ed.c
new file mode 100644
index 0000000..f2c134f
--- /dev/null
+++ b/usr.sbin/pccard/misc/sys/pccard/if_ed.c
@@ -0,0 +1,1997 @@
+/*
+ * Device driver for National Semiconductor DS8390/WD83C690 based ethernet
+ * adapters. By David Greenman, 29-April-1993
+ *
+ * Copied 3 July 1995 from if_ed.c
+ *
+ *-------------------------------------------------------------------------
+ *
+ * Copyright (c) 1995 Andrew McRae. 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. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * Copyright (C) 1993, David Greenman. This software may be used, modified,
+ * copied, distributed, and sold, in both source and binary form provided
+ * that the above copyright and these terms are retained. Under no
+ * circumstances is the author responsible for the proper functioning
+ * of this software, nor does the author assume any responsibility
+ * for damages incurred with its use.
+ *
+ * $Id: if_ed.c,v 1.69 1995/04/12 20:47:45 wollman Exp $
+ */
+
+#include "ed.h"
+#include "bpfilter.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/syslog.h>
+#include <sys/devconf.h>
+#include <sys/proc.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_types.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 NBPFILTER > 0
+#include <net/bpf.h>
+#include <net/bpfdesc.h>
+#endif
+
+#include <machine/clock.h>
+
+#include <i386/isa/isa.h>
+#include <i386/isa/isa_device.h>
+
+#include <pccard/card.h>
+#include <pccard/slot.h>
+#include <pccard/if_edreg.h>
+
+/* For backwards compatibility */
+#ifndef IFF_ALTPHYS
+#define IFF_ALTPHYS IFF_LINK0
+#endif
+
+/*
+ * ed_softc: per line info and status
+ */
+static struct ed_softc {
+ struct arpcom arpcom; /* ethernet common */
+
+ char *type_str; /* pointer to type string */
+ u_char vendor; /* interface vendor */
+ u_char type; /* interface type code */
+
+ u_short asic_addr; /* ASIC I/O bus address */
+ u_short nic_addr; /* NIC (DS8390) I/O bus address */
+
+/*
+ * The following 'proto' variable is part of a work-around for 8013EBT asics
+ * being write-only. It's sort of a prototype/shadow of the real thing.
+ */
+ u_char wd_laar_proto;
+ u_char cr_proto;
+ u_char isa16bit; /* width of access to card 0=8 or 1=16 */
+ int is790; /* set by the probe code if the card is 790
+ * based */
+
+ caddr_t bpf; /* BPF "magic cookie" */
+ caddr_t mem_start; /* NIC memory start address */
+ caddr_t mem_end; /* NIC memory end address */
+ u_long mem_size; /* total NIC memory size */
+ caddr_t mem_ring; /* start of RX ring-buffer (in NIC mem) */
+
+ u_char mem_shared; /* NIC memory is shared with host */
+ u_char xmit_busy; /* transmitter is busy */
+ u_char txb_cnt; /* number of transmit buffers */
+ u_char txb_inuse; /* number of TX buffers currently in-use */
+
+ u_char txb_new; /* pointer to where new buffer will be added */
+ u_char txb_next_tx; /* pointer to next buffer ready to xmit */
+ u_short txb_len[8]; /* buffered xmit buffer lengths */
+ u_char tx_page_start; /* first page of TX buffer area */
+ u_char rec_page_start; /* first page of RX ring-buffer */
+ u_char rec_page_stop; /* last page of RX ring-buffer */
+ u_char next_packet; /* pointer to next unread RX packet */
+ struct kern_devconf kdc; /* kernel configuration database info */
+} ed_softc[NED];
+
+int ed_attach(struct isa_device *);
+void ed_init(int);
+int edintr(struct pccard_dev *);
+int ed_ioctl(struct ifnet *, int, caddr_t);
+int ed_probe(struct isa_device *);
+void ed_start(struct ifnet *);
+void ed_reset(int);
+void ed_watchdog(int);
+int ed_probe_generic8390(struct ed_softc *);
+int ed_probe_Novell(struct isa_device *);
+
+void ds_getmcaf();
+
+static void ed_get_packet(struct ed_softc *, char *, int /* u_short */ , int);
+static void ed_stop(int);
+
+static inline void ed_rint();
+static inline void ed_xmit();
+static inline char *ed_ring_copy();
+
+void ed_pio_readmem(), ed_pio_writemem();
+u_short ed_pio_write_mbufs();
+
+void ed_setrcr(struct ifnet *, struct ed_softc *);
+
+#define ETHER_MIN_LEN 64
+#define ETHER_MAX_LEN 1518
+#define ETHER_ADDR_LEN 6
+#define ETHER_HDR_SIZE 14
+
+/*
+ * The device entry is being removed. Shut it down,
+ * and turn off interrupts etc. Not called unless
+ * the device was successfully installed.
+ */
+void
+edunload(struct pccard_dev *dp)
+{
+ printf("ed%d: unload\n", dp->isahd.id_unit);
+ ed_stop(dp->isahd.id_unit);
+}
+/*
+ * Called when a power down is wanted. Shuts down the
+ * device and configures the device as unavailable (but
+ * still loaded...). A resume is done by calling
+ * edinit with first=0.
+ */
+void
+edsuspend(struct pccard_dev *dp)
+{
+ printf("ed%d: suspending\n", dp->isahd.id_unit);
+}
+/*
+ * Initialize the device.
+ * if first is set, then initially check for
+ * the device's existence before initialising it.
+ * Once initialised, the device table may be set up.
+ */
+int
+edinit(struct pccard_dev *dp, int first)
+{
+ if (first)
+ return(ed_probe(&dp->isahd));
+ return(0);
+}
+/*
+ * Determine if the device is present
+ *
+ * on entry:
+ * a point to a pccard_dev structure
+ * on exit:
+ * NULL if device not found
+ * or # of i/o addresses used (if found)
+ */
+int
+ed_probe(struct isa_device *pdev)
+{
+int nports;
+
+ nports = ed_probe_pccard(pdev);
+ if (nports == 0)
+ {
+ nports = ed_probe_Novell(pdev);
+ }
+ if (nports)
+ {
+ (void)ed_attach(pdev);
+ return (0);
+ }
+ return(ENODEV);
+}
+int
+ed_probe_pccard(struct isa_device *pdev)
+{
+ struct ed_softc *sc = &ed_softc[pdev->id_unit];
+ int n, memsize;
+ unsigned int *p;
+ unsigned char tmp;
+
+ sc->asic_addr = pdev->id_iobase + 0x10;
+ sc->nic_addr = pdev->id_iobase;
+ printf("Probing at 0x%x, mem 0x%x\n", pdev->id_iobase,
+ pdev->id_maddr);
+/*
+ * Reset the board by reading/writing a location.
+ * This may not exist on all boards, but hopefully
+ * is not invasive.
+ */
+#if 1
+ tmp = inb(sc->asic_addr + ED_PCMCIA_RESET);
+ DELAY(5000);
+ outb(sc->asic_addr + ED_PCMCIA_RESET, tmp);
+ DELAY(5000);
+#endif
+
+ /* Make sure that we really have an 8390 based board */
+ if (!ed_probe_generic8390(sc))
+ return (0);
+/*
+ * Check for shared memory. If we can write to it, then
+ * set up this as a generic controller with shared memory.
+ */
+ memsize = 0;
+ if (pdev->id_maddr == 0)
+ return(0);
+/*
+ * Write a bit pattern to memory.
+ */
+ p = (unsigned int *)pdev->id_maddr;
+ for (n = 0; n < pdev->id_msize; p++, n += sizeof(*p))
+ *p = 0xAA5500FF;
+/*
+ * If it verifies, then shared memory is OK.
+ * Clear memory after check.
+ */
+ p = (unsigned int *)pdev->id_maddr;
+ for (n = 0; n < pdev->id_msize; n += sizeof(*p)) {
+ if (*p != 0xAA5500FF)
+ break;
+ *p++ = 0;
+ }
+/*
+ * If no memory, then can't be a shared memory card.
+ */
+ if (n < 8*1024)
+ return(0);
+ if (n < 16*1024)
+ memsize = 8*1024;
+ else
+ memsize = 16*1024;
+ sc->mem_start = (caddr_t) pdev->id_maddr;
+ sc->mem_shared = 1;
+ /*
+ * allocate one xmit buffer if < 16k, two buffers otherwise
+ */
+ if ((memsize < 16384) || (pdev->id_flags & ED_FLAGS_NO_MULTI_BUFFERING)) {
+ sc->mem_ring = sc->mem_start + (ED_PAGE_SIZE * ED_TXBUF_SIZE);
+ sc->txb_cnt = 1;
+ sc->rec_page_start = ED_TXBUF_SIZE + ED_PCMCIA_PAGE_OFFSET;
+ } else {
+ sc->mem_ring = sc->mem_start + (ED_PAGE_SIZE * ED_TXBUF_SIZE * 2);
+ sc->txb_cnt = 2;
+ sc->rec_page_start = ED_TXBUF_SIZE * 2 + ED_PCMCIA_PAGE_OFFSET;
+ }
+ sc->mem_size = memsize;
+ sc->mem_end = sc->mem_start + memsize;
+ sc->rec_page_stop = memsize / ED_PAGE_SIZE;
+ sc->tx_page_start = ED_PCMCIA_PAGE_OFFSET;
+/*
+ * Run 16 bit by default, but allow override.
+ */
+ sc->isa16bit = 1;
+ if (pdev->id_flags & ED_FLAGS_FORCE_8BIT_MODE)
+ sc->isa16bit = 0;
+
+#if 0
+ sc->vendor = ED_VENDOR_PCMCIA;
+#endif
+ sc->type_str = "PCMCIA";
+ sc->type = ED_TYPE_PCMCIA;
+ return (4);
+}
+/*
+ * Probe and vendor-specific initialization routine for NE1000/2000 boards
+ */
+int
+ed_probe_Novell(isa_dev)
+ struct isa_device *isa_dev;
+{
+ struct ed_softc *sc = &ed_softc[isa_dev->id_unit];
+ u_int memsize, n;
+ u_char romdata[16], tmp;
+ static char test_pattern[32] = "THIS is A memory TEST pattern";
+ char test_buffer[32];
+
+ sc->asic_addr = isa_dev->id_iobase + ED_NOVELL_ASIC_OFFSET;
+ sc->nic_addr = isa_dev->id_iobase + ED_NOVELL_NIC_OFFSET;
+
+ /* XXX - do Novell-specific probe here */
+
+ /* Reset the board */
+#ifdef GWETHER
+ outb(sc->asic_addr + ED_NOVELL_RESET, 0);
+ DELAY(200);
+#endif /* GWETHER */
+ tmp = inb(sc->asic_addr + ED_NOVELL_RESET);
+
+ /*
+ * I don't know if this is necessary; probably cruft leftover from
+ * Clarkson packet driver code. Doesn't do a thing on the boards I've
+ * tested. -DG [note that a outb(0x84, 0) seems to work here, and is
+ * non-invasive...but some boards don't seem to reset and I don't have
+ * complete documentation on what the 'right' thing to do is...so we
+ * do the invasive thing for now. Yuck.]
+ */
+ outb(sc->asic_addr + ED_NOVELL_RESET, tmp);
+ DELAY(5000);
+
+ /*
+ * This is needed because some NE clones apparently don't reset the
+ * NIC properly (or the NIC chip doesn't reset fully on power-up) XXX
+ * - this makes the probe invasive! ...Done against my better
+ * judgement. -DLG
+ */
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STP);
+
+ DELAY(5000);
+
+ /* Make sure that we really have an 8390 based board */
+ if (!ed_probe_generic8390(sc))
+ return (0);
+
+ sc->vendor = ED_VENDOR_NOVELL;
+ sc->mem_shared = 0;
+ sc->cr_proto = ED_CR_RD2;
+ isa_dev->id_maddr = 0;
+
+ /*
+ * Test the ability to read and write to the NIC memory. This has the
+ * side affect of determining if this is an NE1000 or an NE2000.
+ */
+
+ /*
+ * This prevents packets from being stored in the NIC memory when the
+ * readmem routine turns on the start bit in the CR.
+ */
+ outb(sc->nic_addr + ED_P0_RCR, ED_RCR_MON);
+
+ /* Temporarily initialize DCR for byte operations */
+ outb(sc->nic_addr + ED_P0_DCR, ED_DCR_FT1 | ED_DCR_LS);
+
+ outb(sc->nic_addr + ED_P0_PSTART, 8192 / ED_PAGE_SIZE);
+ outb(sc->nic_addr + ED_P0_PSTOP, 16384 / ED_PAGE_SIZE);
+
+ sc->isa16bit = 0;
+
+ /*
+ * Write a test pattern in byte mode. If this fails, then there
+ * probably isn't any memory at 8k - which likely means that the board
+ * is an NE2000.
+ */
+ ed_pio_writemem(sc, test_pattern, 8192, sizeof(test_pattern));
+ ed_pio_readmem(sc, 8192, test_buffer, sizeof(test_pattern));
+
+ if (bcmp(test_pattern, test_buffer, sizeof(test_pattern))) {
+ /* not an NE1000 - try NE2000 */
+
+ outb(sc->nic_addr + ED_P0_RCR, ED_RCR_MON);
+ outb(sc->nic_addr + ED_P0_PSTART, 16384 / ED_PAGE_SIZE);
+ outb(sc->nic_addr + ED_P0_PSTOP, 32768 / ED_PAGE_SIZE);
+
+ if (isa_dev->id_flags & ED_FLAGS_FORCE_8BIT_MODE)
+ {
+ sc->isa16bit = 0;
+ outb(sc->nic_addr + ED_P0_DCR, ED_DCR_FT1 | ED_DCR_LS);
+ }
+ else
+ {
+ sc->isa16bit = 1;
+ outb(sc->nic_addr + ED_P0_DCR, ED_DCR_WTS | ED_DCR_FT1 | ED_DCR_LS);
+ }
+
+ /*
+ * Write a test pattern in word mode. If this also fails, then
+ * we don't know what this board is.
+ */
+ ed_pio_writemem(sc, test_pattern, 16384, sizeof(test_pattern));
+ ed_pio_readmem(sc, 16384, test_buffer, sizeof(test_pattern));
+
+ if (bcmp(test_pattern, test_buffer, sizeof(test_pattern)))
+ return (0); /* not an NE2000 either */
+
+ sc->type = ED_TYPE_NE2000;
+ sc->type_str = "NE2000";
+ sc->kdc.kdc_description = "Ethernet adapter: NE2000";
+ } else {
+ sc->type = ED_TYPE_NE1000;
+ sc->type_str = "NE1000";
+ sc->kdc.kdc_description = "Ethernet adapter: NE1000";
+ }
+
+ /* 8k of memory plus an additional 8k if 16bit */
+ memsize = 8192 + sc->isa16bit * 8192;
+
+#if 0 /* probably not useful - NE boards only come two ways */
+ /* allow kernel config file overrides */
+ if (isa_dev->id_msize)
+ memsize = isa_dev->id_msize;
+#endif
+
+ sc->mem_size = memsize;
+
+ /* NIC memory doesn't start at zero on an NE board */
+ /* The start address is tied to the bus width */
+ sc->mem_start = (char *) 8192 + sc->isa16bit * 8192;
+ sc->mem_end = sc->mem_start + memsize;
+ sc->tx_page_start = memsize / ED_PAGE_SIZE;
+
+#ifdef GWETHER
+ {
+ int x, i, mstart = 0, msize = 0;
+ char pbuf0[ED_PAGE_SIZE], pbuf[ED_PAGE_SIZE], tbuf[ED_PAGE_SIZE];
+
+ for (i = 0; i < ED_PAGE_SIZE; i++)
+ pbuf0[i] = 0;
+
+ /* Clear all the memory. */
+ for (x = 1; x < 256; x++)
+ ed_pio_writemem(sc, pbuf0, x * 256, ED_PAGE_SIZE);
+
+ /* Search for the start of RAM. */
+ for (x = 1; x < 256; x++) {
+ ed_pio_readmem(sc, x * 256, tbuf, ED_PAGE_SIZE);
+ if (memcmp(pbuf0, tbuf, ED_PAGE_SIZE) == 0) {
+ for (i = 0; i < ED_PAGE_SIZE; i++)
+ pbuf[i] = 255 - x;
+ ed_pio_writemem(sc, pbuf, x * 256, ED_PAGE_SIZE);
+ ed_pio_readmem(sc, x * 256, tbuf, ED_PAGE_SIZE);
+ if (memcmp(pbuf, tbuf, ED_PAGE_SIZE) == 0) {
+ mstart = x * ED_PAGE_SIZE;
+ msize = ED_PAGE_SIZE;
+ break;
+ }
+ }
+ }
+
+ if (mstart == 0) {
+ printf("ed%d: Cannot find start of RAM.\n", isa_dev->id_unit);
+ return 0;
+ }
+ /* Search for the start of RAM. */
+ for (x = (mstart / ED_PAGE_SIZE) + 1; x < 256; x++) {
+ ed_pio_readmem(sc, x * 256, tbuf, ED_PAGE_SIZE);
+ if (memcmp(pbuf0, tbuf, ED_PAGE_SIZE) == 0) {
+ for (i = 0; i < ED_PAGE_SIZE; i++)
+ pbuf[i] = 255 - x;
+ ed_pio_writemem(sc, pbuf, x * 256, ED_PAGE_SIZE);
+ ed_pio_readmem(sc, x * 256, tbuf, ED_PAGE_SIZE);
+ if (memcmp(pbuf, tbuf, ED_PAGE_SIZE) == 0)
+ msize += ED_PAGE_SIZE;
+ else {
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+
+ if (msize == 0) {
+ printf("ed%d: Cannot find any RAM, start : %d, x = %d.\n", isa_dev->id_unit, mstart, x);
+ return 0;
+ }
+ printf("ed%d: RAM start at %d, size : %d.\n", isa_dev->id_unit, mstart, msize);
+
+ sc->mem_size = msize;
+ sc->mem_start = (char *) mstart;
+ sc->mem_end = (char *) (msize + mstart);
+ sc->tx_page_start = mstart / ED_PAGE_SIZE;
+ }
+#endif /* GWETHER */
+
+ /*
+ * Use one xmit buffer if < 16k, two buffers otherwise (if not told
+ * otherwise).
+ */
+ if ((memsize < 16384) || (isa_dev->id_flags & ED_FLAGS_NO_MULTI_BUFFERING))
+ sc->txb_cnt = 1;
+ else
+ sc->txb_cnt = 2;
+
+ sc->rec_page_start = sc->tx_page_start + sc->txb_cnt * ED_TXBUF_SIZE;
+ sc->rec_page_stop = sc->tx_page_start + memsize / ED_PAGE_SIZE;
+
+ sc->mem_ring = sc->mem_start + sc->txb_cnt * ED_PAGE_SIZE * ED_TXBUF_SIZE;
+
+ ed_pio_readmem(sc, 0, romdata, 16);
+ for (n = 0; n < ETHER_ADDR_LEN; n++)
+ sc->arpcom.ac_enaddr[n] = romdata[n * (sc->isa16bit + 1)];
+
+#ifdef GWETHER
+ if (sc->arpcom.ac_enaddr[2] == 0x86) {
+ sc->type_str = "Gateway AT";
+ sc->kdc.kdc_description = "Ethernet adapter: Gateway AT";
+ }
+#endif /* GWETHER */
+
+ /* clear any pending interrupts that might have occurred above */
+ outb(sc->nic_addr + ED_P0_ISR, 0xff);
+
+ return (ED_NOVELL_IO_PORTS);
+}
+
+
+/*
+ * Generic probe routine for testing for the existance of a DS8390.
+ * Must be called after the NIC has just been reset. This routine
+ * works by looking at certain register values that are guaranteed
+ * to be initialized a certain way after power-up or reset. Seems
+ * not to currently work on the 83C690.
+ *
+ * Specifically:
+ *
+ * Register reset bits set bits
+ * Command Register (CR) TXP, STA RD2, STP
+ * Interrupt Status (ISR) RST
+ * Interrupt Mask (IMR) All bits
+ * Data Control (DCR) LAS
+ * Transmit Config. (TCR) LB1, LB0
+ *
+ * We only look at the CR and ISR registers, however, because looking at
+ * the others would require changing register pages (which would be
+ * intrusive if this isn't an 8390).
+ *
+ * Return 1 if 8390 was found, 0 if not.
+ */
+
+int
+ed_probe_generic8390(sc)
+ struct ed_softc *sc;
+{
+ if ((inb(sc->nic_addr + ED_P0_CR) &
+ (ED_CR_RD2 | ED_CR_TXP | ED_CR_STA | ED_CR_STP)) !=
+ (ED_CR_RD2 | ED_CR_STP))
+ return (0);
+ if ((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RST) != ED_ISR_RST)
+ return (0);
+
+ return (1);
+}
+/*
+ * Install interface into kernel networking data structures
+ */
+int
+ed_attach(struct isa_device *pdev)
+{
+ struct ed_softc *sc = &ed_softc[pdev->id_unit];
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+
+ /*
+ * Set interface to stopped condition (reset)
+ */
+ ed_stop(pdev->id_unit);
+
+ /*
+ * Initialize ifnet structure
+ */
+ ifp->if_unit = pdev->id_unit;
+ ifp->if_name = "ed";
+ ifp->if_init = ed_init;
+ ifp->if_output = ether_output;
+ ifp->if_start = ed_start;
+ ifp->if_ioctl = ed_ioctl;
+ ifp->if_reset = ed_reset;
+ ifp->if_watchdog = ed_watchdog;
+
+ /*
+ * Set default state for ALTPHYS flag (used to disable the tranceiver
+ * for AUI operation), based on compile-time config option.
+ */
+ if (pdev->id_flags & ED_FLAGS_DISABLE_TRANCEIVER)
+ ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS |
+ IFF_MULTICAST | IFF_ALTPHYS);
+ else
+ ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS |
+ IFF_MULTICAST);
+
+ /*
+ * Attach the interface
+ */
+ if_attach(ifp);
+
+ /*
+ * Print additional info when attached
+ */
+ printf("ed%d: address %s, ", pdev->id_unit,
+ ether_sprintf(sc->arpcom.ac_enaddr));
+
+ if (sc->type_str && (*sc->type_str != 0))
+ printf("type %s ", sc->type_str);
+ else
+ printf("type unknown (0x%x) ", sc->type);
+
+ printf("%s ", sc->isa16bit ? "(16 bit)" : "(8 bit)");
+
+ printf("%s\n", ((sc->vendor == ED_VENDOR_3COM) &&
+ (ifp->if_flags & IFF_ALTPHYS)) ? " tranceiver disabled" : "");
+
+ /*
+ * If BPF is in the kernel, call the attach for it
+ */
+#if NBPFILTER > 0
+ bpfattach(&sc->bpf, ifp, DLT_EN10MB, sizeof(struct ether_header));
+#endif
+ return 1;
+}
+
+/*
+ * Reset interface.
+ */
+void
+ed_reset(unit)
+ int unit;
+{
+ int s;
+
+ s = splimp();
+
+ /*
+ * Stop interface and re-initialize.
+ */
+ ed_stop(unit);
+ ed_init(unit);
+
+ (void) splx(s);
+}
+
+/*
+ * Take interface offline.
+ */
+void
+ed_stop(unit)
+ int unit;
+{
+ struct ed_softc *sc = &ed_softc[unit];
+ int n = 5000;
+
+ /*
+ * Stop everything on the interface, and select page 0 registers.
+ */
+ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_STP);
+
+ /*
+ * Wait for interface to enter stopped state, but limit # of checks to
+ * 'n' (about 5ms). It shouldn't even take 5us on modern DS8390's, but
+ * just in case it's an old one.
+ */
+ while (((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RST) == 0) && --n);
+}
+
+/*
+ * Device timeout/watchdog routine. Entered if the device neglects to
+ * generate an interrupt after a transmit has been started on it.
+ */
+void
+ed_watchdog(unit)
+ int unit;
+{
+ struct ed_softc *sc = &ed_softc[unit];
+
+ log(LOG_ERR, "ed%d: device timeout\n", unit);
+ ++sc->arpcom.ac_if.if_oerrors;
+
+ ed_reset(unit);
+}
+
+/*
+ * Initialize device.
+ */
+void
+ed_init(unit)
+ int unit;
+{
+ struct ed_softc *sc = &ed_softc[unit];
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ int i, s;
+
+
+ /* address not known */
+ if (ifp->if_addrlist == (struct ifaddr *) 0)
+ return;
+
+ /*
+ * Initialize the NIC in the exact order outlined in the NS manual.
+ * This init procedure is "mandatory"...don't change what or when
+ * things happen.
+ */
+ s = splimp();
+
+ /* reset transmitter flags */
+ sc->xmit_busy = 0;
+ sc->arpcom.ac_if.if_timer = 0;
+
+ sc->txb_inuse = 0;
+ sc->txb_new = 0;
+ sc->txb_next_tx = 0;
+
+ /* This variable is used below - don't move this assignment */
+ sc->next_packet = sc->rec_page_start + 1;
+
+ /*
+ * Set interface for page 0, Remote DMA complete, Stopped
+ */
+ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_STP);
+
+ if (sc->isa16bit) {
+
+ /*
+ * Set FIFO threshold to 8, No auto-init Remote DMA, byte
+ * order=80x86, word-wide DMA xfers,
+ */
+ outb(sc->nic_addr + ED_P0_DCR, ED_DCR_FT1 | ED_DCR_WTS | ED_DCR_LS);
+ } else {
+
+ /*
+ * Same as above, but byte-wide DMA xfers
+ */
+ outb(sc->nic_addr + ED_P0_DCR, ED_DCR_FT1 | ED_DCR_LS);
+ }
+
+ /*
+ * Clear Remote Byte Count Registers
+ */
+ outb(sc->nic_addr + ED_P0_RBCR0, 0);
+ outb(sc->nic_addr + ED_P0_RBCR1, 0);
+
+ /*
+ * For the moment, don't store incoming packets in memory.
+ */
+ outb(sc->nic_addr + ED_P0_RCR, ED_RCR_MON);
+
+ /*
+ * Place NIC in internal loopback mode
+ */
+ outb(sc->nic_addr + ED_P0_TCR, ED_TCR_LB0);
+
+ /*
+ * Initialize transmit/receive (ring-buffer) Page Start
+ */
+ outb(sc->nic_addr + ED_P0_TPSR, sc->tx_page_start);
+ outb(sc->nic_addr + ED_P0_PSTART, sc->rec_page_start);
+ /* Set lower bits of byte addressable framing to 0 */
+ if (sc->is790)
+ outb(sc->nic_addr + 0x09, 0);
+
+ /*
+ * Initialize Receiver (ring-buffer) Page Stop and Boundry
+ */
+ outb(sc->nic_addr + ED_P0_PSTOP, sc->rec_page_stop);
+ outb(sc->nic_addr + ED_P0_BNRY, sc->rec_page_start);
+
+ /*
+ * Clear all interrupts. A '1' in each bit position clears the
+ * corresponding flag.
+ */
+ outb(sc->nic_addr + ED_P0_ISR, 0xff);
+
+ /*
+ * Enable the following interrupts: receive/transmit complete,
+ * receive/transmit error, and Receiver OverWrite.
+ *
+ * Counter overflow and Remote DMA complete are *not* enabled.
+ */
+ outb(sc->nic_addr + ED_P0_IMR,
+ ED_IMR_PRXE | ED_IMR_PTXE | ED_IMR_RXEE | ED_IMR_TXEE | ED_IMR_OVWE);
+
+ /*
+ * Program Command Register for page 1
+ */
+ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_PAGE_1 | ED_CR_STP);
+
+ /*
+ * Copy out our station address
+ */
+ for (i = 0; i < ETHER_ADDR_LEN; ++i)
+ outb(sc->nic_addr + ED_P1_PAR0 + i, sc->arpcom.ac_enaddr[i]);
+
+ /*
+ * Set Current Page pointer to next_packet (initialized above)
+ */
+ outb(sc->nic_addr + ED_P1_CURR, sc->next_packet);
+
+ /*
+ * Program Receiver Configuration Register and multicast filter. CR is
+ * set to page 0 on return.
+ */
+ ed_setrcr(ifp, sc);
+
+ /*
+ * Take interface out of loopback
+ */
+ outb(sc->nic_addr + ED_P0_TCR, 0);
+
+ /*
+ * If this is a 3Com board, the tranceiver must be software enabled
+ * (there is no settable hardware default).
+ */
+ if (sc->vendor == ED_VENDOR_3COM) {
+ if (ifp->if_flags & IFF_ALTPHYS) {
+ outb(sc->asic_addr + ED_3COM_CR, 0);
+ } else {
+ outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_XSEL);
+ }
+ }
+
+ /*
+ * Set 'running' flag, and clear output active flag.
+ */
+ ifp->if_flags |= IFF_RUNNING;
+ ifp->if_flags &= ~IFF_OACTIVE;
+
+ /*
+ * ...and attempt to start output
+ */
+ ed_start(ifp);
+
+ (void) splx(s);
+}
+
+/*
+ * This routine actually starts the transmission on the interface
+ */
+static inline void
+ed_xmit(ifp)
+ struct ifnet *ifp;
+{
+ struct ed_softc *sc = &ed_softc[ifp->if_unit];
+ unsigned short len;
+
+ len = sc->txb_len[sc->txb_next_tx];
+
+ /*
+ * Set NIC for page 0 register access
+ */
+ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_STA);
+
+ /*
+ * Set TX buffer start page
+ */
+ outb(sc->nic_addr + ED_P0_TPSR, sc->tx_page_start +
+ sc->txb_next_tx * ED_TXBUF_SIZE);
+
+ /*
+ * Set TX length
+ */
+ outb(sc->nic_addr + ED_P0_TBCR0, len);
+ outb(sc->nic_addr + ED_P0_TBCR1, len >> 8);
+
+ /*
+ * Set page 0, Remote DMA complete, Transmit Packet, and *Start*
+ */
+ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_TXP | ED_CR_STA);
+ sc->xmit_busy = 1;
+
+ /*
+ * Point to next transmit buffer slot and wrap if necessary.
+ */
+ sc->txb_next_tx++;
+ if (sc->txb_next_tx == sc->txb_cnt)
+ sc->txb_next_tx = 0;
+
+ /*
+ * Set a timer just in case we never hear from the board again
+ */
+ ifp->if_timer = 2;
+}
+
+/*
+ * Start output on interface.
+ * We make two assumptions here:
+ * 1) that the current priority is set to splimp _before_ this code
+ * is called *and* is returned to the appropriate priority after
+ * return
+ * 2) that the IFF_OACTIVE flag is checked before this code is called
+ * (i.e. that the output part of the interface is idle)
+ */
+void
+ed_start(ifp)
+ struct ifnet *ifp;
+{
+ struct ed_softc *sc = &ed_softc[ifp->if_unit];
+ struct mbuf *m0, *m;
+ caddr_t buffer;
+ int len;
+
+outloop:
+
+ /*
+ * First, see if there are buffered packets and an idle transmitter -
+ * should never happen at this point.
+ */
+ if (sc->txb_inuse && (sc->xmit_busy == 0)) {
+ printf("ed: packets buffered, but transmitter idle\n");
+ ed_xmit(ifp);
+ }
+
+ /*
+ * See if there is room to put another packet in the buffer.
+ */
+ if (sc->txb_inuse == sc->txb_cnt) {
+
+ /*
+ * No room. Indicate this to the outside world and exit.
+ */
+ ifp->if_flags |= IFF_OACTIVE;
+ return;
+ }
+ IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, m);
+ if (m == 0) {
+
+ /*
+ * We are using the !OACTIVE flag to indicate to the outside
+ * world that we can accept an additional packet rather than
+ * that the transmitter is _actually_ active. Indeed, the
+ * transmitter may be active, but if we haven't filled all the
+ * buffers with data then we still want to accept more.
+ */
+ ifp->if_flags &= ~IFF_OACTIVE;
+ return;
+ }
+
+ /*
+ * Copy the mbuf chain into the transmit buffer
+ */
+
+ m0 = m;
+
+ /* txb_new points to next open buffer slot */
+ buffer = sc->mem_start + (sc->txb_new * ED_TXBUF_SIZE * ED_PAGE_SIZE);
+
+ if (sc->mem_shared) {
+
+ /*
+ * Special case setup for 16 bit boards...
+ */
+ if (sc->isa16bit) {
+ switch (sc->vendor) {
+
+ /*
+ * For 16bit 3Com boards (which have 16k of
+ * memory), we have the xmit buffers in a
+ * different page of memory ('page 0') - so
+ * change pages.
+ */
+ case ED_VENDOR_3COM:
+ outb(sc->asic_addr + ED_3COM_GACFR,
+ ED_3COM_GACFR_RSEL);
+ break;
+
+ /*
+ * Enable 16bit access to shared memory on
+ * WD/SMC boards.
+ */
+ case ED_VENDOR_WD_SMC:{
+ outb(sc->asic_addr + ED_WD_LAAR,
+ (sc->wd_laar_proto | ED_WD_LAAR_M16EN));
+ if (sc->is790) {
+ outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_MENB);
+ }
+ break;
+ }
+ }
+ }
+ for (len = 0; m != 0; m = m->m_next) {
+ bcopy(mtod(m, caddr_t), buffer, m->m_len);
+ buffer += m->m_len;
+ len += m->m_len;
+ }
+
+ /*
+ * Restore previous shared memory access
+ */
+ if (sc->isa16bit) {
+ switch (sc->vendor) {
+ case ED_VENDOR_3COM:
+ outb(sc->asic_addr + ED_3COM_GACFR,
+ ED_3COM_GACFR_RSEL | ED_3COM_GACFR_MBS0);
+ break;
+ case ED_VENDOR_WD_SMC:{
+ if (sc->is790) {
+ outb(sc->asic_addr + ED_WD_MSR, 0x00);
+ }
+ outb(sc->asic_addr + ED_WD_LAAR, sc->wd_laar_proto);
+ break;
+ }
+ }
+ }
+ } else {
+ len = ed_pio_write_mbufs(sc, m, buffer);
+ if (len == 0)
+ goto outloop;
+ }
+
+ sc->txb_len[sc->txb_new] = max(len, ETHER_MIN_LEN);
+
+ sc->txb_inuse++;
+
+ /*
+ * Point to next buffer slot and wrap if necessary.
+ */
+ sc->txb_new++;
+ if (sc->txb_new == sc->txb_cnt)
+ sc->txb_new = 0;
+
+ if (sc->xmit_busy == 0)
+ ed_xmit(ifp);
+
+ /*
+ * Tap off here if there is a bpf listener.
+ */
+#if NBPFILTER > 0
+ if (sc->bpf) {
+ bpf_mtap(sc->bpf, m0);
+ }
+#endif
+
+ m_freem(m0);
+
+ /*
+ * Loop back to the top to possibly buffer more packets
+ */
+ goto outloop;
+}
+
+/*
+ * Ethernet interface receiver interrupt.
+ */
+static inline void
+ed_rint(unit)
+ int unit;
+{
+ register struct ed_softc *sc = &ed_softc[unit];
+ u_char boundry;
+ u_short len;
+ struct ed_ring packet_hdr;
+ char *packet_ptr;
+
+ /*
+ * Set NIC to page 1 registers to get 'current' pointer
+ */
+ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_PAGE_1 | ED_CR_STA);
+
+ /*
+ * 'sc->next_packet' is the logical beginning of the ring-buffer -
+ * i.e. it points to where new data has been buffered. The 'CURR'
+ * (current) register points to the logical end of the ring-buffer -
+ * i.e. it points to where additional new data will be added. We loop
+ * here until the logical beginning equals the logical end (or in
+ * other words, until the ring-buffer is empty).
+ */
+ while (sc->next_packet != inb(sc->nic_addr + ED_P1_CURR)) {
+
+ /* get pointer to this buffer's header structure */
+ packet_ptr = sc->mem_ring +
+ (sc->next_packet - sc->rec_page_start) * ED_PAGE_SIZE;
+
+ /*
+ * The byte count includes a 4 byte header that was added by
+ * the NIC.
+ */
+ if (sc->mem_shared)
+ packet_hdr = *(struct ed_ring *) packet_ptr;
+ else
+ ed_pio_readmem(sc, packet_ptr, (char *) &packet_hdr,
+ sizeof(packet_hdr));
+ len = packet_hdr.count;
+ if (len > ETHER_MAX_LEN) {
+ /*
+ * Length is a wild value. There's a good chance that
+ * this was caused by the NIC being old and buggy.
+ * The bug is that the length low byte is duplicated in
+ * the high byte. Try to recalculate the length based on
+ * the pointer to the next packet.
+ */
+ /*
+ * NOTE: sc->next_packet is pointing at the current packet.
+ */
+ len &= ED_PAGE_SIZE - 1; /* preserve offset into page */
+ if (packet_hdr.next_packet >= sc->next_packet) {
+ len += (packet_hdr.next_packet - sc->next_packet) * ED_PAGE_SIZE;
+ } else {
+ len += ((packet_hdr.next_packet - sc->rec_page_start) +
+ (sc->rec_page_stop - sc->next_packet)) * ED_PAGE_SIZE;
+ }
+ }
+ /*
+ * Be fairly liberal about what we allow as a "reasonable" length
+ * so that a [crufty] packet will make it to BPF (and can thus
+ * be analyzed). Note that all that is really important is that
+ * we have a length that will fit into one mbuf cluster or less;
+ * the upper layer protocols can then figure out the length from
+ * their own length field(s).
+ */
+ if ((len <= MCLBYTES) &&
+ (packet_hdr.next_packet >= sc->rec_page_start) &&
+ (packet_hdr.next_packet < sc->rec_page_stop)) {
+ /*
+ * Go get packet.
+ */
+ ed_get_packet(sc, packet_ptr + sizeof(struct ed_ring),
+ len - sizeof(struct ed_ring), packet_hdr.rsr & ED_RSR_PHY);
+ ++sc->arpcom.ac_if.if_ipackets;
+ } else {
+ /*
+ * Really BAD. The ring pointers are corrupted.
+ */
+ log(LOG_ERR,
+ "ed%d: NIC memory corrupt - invalid packet length %d\n",
+ unit, len);
+ ++sc->arpcom.ac_if.if_ierrors;
+ ed_reset(unit);
+ return;
+ }
+
+ /*
+ * Update next packet pointer
+ */
+ sc->next_packet = packet_hdr.next_packet;
+
+ /*
+ * Update NIC boundry pointer - being careful to keep it one
+ * buffer behind. (as recommended by NS databook)
+ */
+ boundry = sc->next_packet - 1;
+ if (boundry < sc->rec_page_start)
+ boundry = sc->rec_page_stop - 1;
+
+ /*
+ * Set NIC to page 0 registers to update boundry register
+ */
+ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_STA);
+
+ outb(sc->nic_addr + ED_P0_BNRY, boundry);
+
+ /*
+ * Set NIC to page 1 registers before looping to top (prepare
+ * to get 'CURR' current pointer)
+ */
+ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_PAGE_1 | ED_CR_STA);
+ }
+}
+
+/*
+ * Ethernet interface interrupt processor
+ */
+int
+edintr(struct pccard_dev *pdev)
+{
+ struct ed_softc *sc = &ed_softc[pdev->isahd.id_unit];
+ u_char isr;
+ int ret = 0;
+
+ /*
+ * Set NIC to page 0 registers
+ */
+ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_STA);
+
+ /*
+ * loop until there are no more new interrupts
+ */
+ while ((isr = inb(sc->nic_addr + ED_P0_ISR)) != 0) {
+
+ ret = 1;
+ /*
+ * reset all the bits that we are 'acknowledging' by writing a
+ * '1' to each bit position that was set (writing a '1'
+ * *clears* the bit)
+ */
+ outb(sc->nic_addr + ED_P0_ISR, isr);
+
+ /*
+ * Handle transmitter interrupts. Handle these first because
+ * the receiver will reset the board under some conditions.
+ */
+ if (isr & (ED_ISR_PTX | ED_ISR_TXE)) {
+ u_char collisions = inb(sc->nic_addr + ED_P0_NCR) & 0x0f;
+
+ /*
+ * Check for transmit error. If a TX completed with an
+ * error, we end up throwing the packet away. Really
+ * the only error that is possible is excessive
+ * collisions, and in this case it is best to allow
+ * the automatic mechanisms of TCP to backoff the
+ * flow. Of course, with UDP we're screwed, but this
+ * is expected when a network is heavily loaded.
+ */
+ (void) inb(sc->nic_addr + ED_P0_TSR);
+ if (isr & ED_ISR_TXE) {
+
+ /*
+ * Excessive collisions (16)
+ */
+ if ((inb(sc->nic_addr + ED_P0_TSR) & ED_TSR_ABT)
+ && (collisions == 0)) {
+
+ /*
+ * When collisions total 16, the
+ * P0_NCR will indicate 0, and the
+ * TSR_ABT is set.
+ */
+ collisions = 16;
+ }
+
+ /*
+ * update output errors counter
+ */
+ ++sc->arpcom.ac_if.if_oerrors;
+ } else {
+
+ /*
+ * Update total number of successfully
+ * transmitted packets.
+ */
+ ++sc->arpcom.ac_if.if_opackets;
+ }
+
+ /*
+ * reset tx busy and output active flags
+ */
+ sc->xmit_busy = 0;
+ sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE;
+
+ /*
+ * clear watchdog timer
+ */
+ sc->arpcom.ac_if.if_timer = 0;
+
+ /*
+ * Add in total number of collisions on last
+ * transmission.
+ */
+ sc->arpcom.ac_if.if_collisions += collisions;
+
+ /*
+ * Decrement buffer in-use count if not zero (can only
+ * be zero if a transmitter interrupt occured while
+ * not actually transmitting). If data is ready to
+ * transmit, start it transmitting, otherwise defer
+ * until after handling receiver
+ */
+ if (sc->txb_inuse && --sc->txb_inuse)
+ ed_xmit(&sc->arpcom.ac_if);
+ }
+
+ /*
+ * Handle receiver interrupts
+ */
+ if (isr & (ED_ISR_PRX | ED_ISR_RXE | ED_ISR_OVW)) {
+
+ /*
+ * Overwrite warning. In order to make sure that a
+ * lockup of the local DMA hasn't occurred, we reset
+ * and re-init the NIC. The NSC manual suggests only a
+ * partial reset/re-init is necessary - but some chips
+ * seem to want more. The DMA lockup has been seen
+ * only with early rev chips - Methinks this bug was
+ * fixed in later revs. -DG
+ */
+ if (isr & ED_ISR_OVW) {
+ ++sc->arpcom.ac_if.if_ierrors;
+#ifdef DIAGNOSTIC
+ log(LOG_WARNING,
+ "ed%d: warning - receiver ring buffer overrun\n",
+ pdev->isahd.id_unit);
+#endif
+
+ /*
+ * Stop/reset/re-init NIC
+ */
+ ed_reset(pdev->isahd.id_unit);
+ } else {
+
+ /*
+ * Receiver Error. One or more of: CRC error,
+ * frame alignment error FIFO overrun, or
+ * missed packet.
+ */
+ if (isr & ED_ISR_RXE) {
+ ++sc->arpcom.ac_if.if_ierrors;
+#ifdef ED_DEBUG
+ printf("ed%d: receive error %x\n", pdev->isahd.id_unit,
+ inb(sc->nic_addr + ED_P0_RSR));
+#endif
+ }
+
+ /*
+ * Go get the packet(s) XXX - Doing this on an
+ * error is dubious because there shouldn't be
+ * any data to get (we've configured the
+ * interface to not accept packets with
+ * errors).
+ */
+
+ /*
+ * Enable 16bit access to shared memory first
+ * on WD/SMC boards.
+ */
+ if (sc->isa16bit &&
+ (sc->vendor == ED_VENDOR_WD_SMC)) {
+
+ outb(sc->asic_addr + ED_WD_LAAR,
+ (sc->wd_laar_proto |=
+ ED_WD_LAAR_M16EN));
+ if (sc->is790) {
+ outb(sc->asic_addr + ED_WD_MSR,
+ ED_WD_MSR_MENB);
+ }
+ }
+ ed_rint(pdev->isahd.id_unit);
+
+ /* disable 16bit access */
+ if (sc->isa16bit &&
+ (sc->vendor == ED_VENDOR_WD_SMC)) {
+
+ if (sc->is790) {
+ outb(sc->asic_addr + ED_WD_MSR, 0x00);
+ }
+ outb(sc->asic_addr + ED_WD_LAAR,
+ (sc->wd_laar_proto &=
+ ~ED_WD_LAAR_M16EN));
+ }
+ }
+ }
+
+ /*
+ * If it looks like the transmitter can take more data,
+ * attempt to start output on the interface. This is done
+ * after handling the receiver to give the receiver priority.
+ */
+ if ((sc->arpcom.ac_if.if_flags & IFF_OACTIVE) == 0)
+ ed_start(&sc->arpcom.ac_if);
+
+ /*
+ * return NIC CR to standard state: page 0, remote DMA
+ * complete, start (toggling the TXP bit off, even if was just
+ * set in the transmit routine, is *okay* - it is 'edge'
+ * triggered from low to high)
+ */
+ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_STA);
+
+ /*
+ * If the Network Talley Counters overflow, read them to reset
+ * them. It appears that old 8390's won't clear the ISR flag
+ * otherwise - resulting in an infinite loop.
+ */
+ if (isr & ED_ISR_CNT) {
+ (void) inb(sc->nic_addr + ED_P0_CNTR0);
+ (void) inb(sc->nic_addr + ED_P0_CNTR1);
+ (void) inb(sc->nic_addr + ED_P0_CNTR2);
+ }
+ }
+ return(ret);
+}
+
+/*
+ * Process an ioctl request. This code needs some work - it looks
+ * pretty ugly.
+ */
+int
+ed_ioctl(ifp, command, data)
+ register struct ifnet *ifp;
+ int command;
+ caddr_t data;
+{
+ register struct ifaddr *ifa = (struct ifaddr *) data;
+ struct ed_softc *sc = &ed_softc[ifp->if_unit];
+ struct ifreq *ifr = (struct ifreq *) data;
+ int s, error = 0;
+
+ s = splimp();
+
+ switch (command) {
+
+ case SIOCSIFADDR:
+ ifp->if_flags |= IFF_UP;
+ /* netifs are BUSY when UP */
+
+ switch (ifa->ifa_addr->sa_family) {
+#ifdef INET
+ case AF_INET:
+ ed_init(ifp->if_unit); /* before arpwhohas */
+ arp_ifinit((struct arpcom *)ifp, ifa);
+ break;
+#endif
+#ifdef NS
+
+ /*
+ * XXX - This code is probably wrong
+ */
+ case AF_NS:
+ {
+ register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
+
+ if (ns_nullhost(*ina))
+ ina->x_host =
+ *(union ns_host *) (sc->arpcom.ac_enaddr);
+ else {
+ bcopy((caddr_t) ina->x_host.c_host,
+ (caddr_t) sc->arpcom.ac_enaddr,
+ sizeof(sc->arpcom.ac_enaddr));
+ }
+
+ /*
+ * Set new address
+ */
+ ed_init(ifp->if_unit);
+ break;
+ }
+#endif
+ default:
+ ed_init(ifp->if_unit);
+ break;
+ }
+ break;
+
+ case SIOCGIFADDR:
+ {
+ struct sockaddr *sa;
+
+ sa = (struct sockaddr *) & ifr->ifr_data;
+ bcopy((caddr_t) sc->arpcom.ac_enaddr,
+ (caddr_t) sa->sa_data, ETHER_ADDR_LEN);
+ }
+ break;
+
+ case SIOCSIFFLAGS:
+
+ /*
+ * If interface is marked down and it is running, then stop it
+ */
+ if (((ifp->if_flags & IFF_UP) == 0) &&
+ (ifp->if_flags & IFF_RUNNING)) {
+ ed_stop(ifp->if_unit);
+ ifp->if_flags &= ~IFF_RUNNING;
+ } else {
+
+ /*
+ * If interface is marked up and it is stopped, then
+ * start it
+ */
+ if ((ifp->if_flags & IFF_UP) &&
+ ((ifp->if_flags & IFF_RUNNING) == 0))
+ ed_init(ifp->if_unit);
+ }
+
+#if NBPFILTER > 0
+
+ /*
+ * Promiscuous flag may have changed, so reprogram the RCR.
+ */
+ ed_setrcr(ifp, sc);
+#endif
+
+ /*
+ * An unfortunate hack to provide the (required) software
+ * control of the tranceiver for 3Com boards. The ALTPHYS flag
+ * disables the tranceiver if set.
+ */
+ if (sc->vendor == ED_VENDOR_3COM) {
+ if (ifp->if_flags & IFF_ALTPHYS) {
+ outb(sc->asic_addr + ED_3COM_CR, 0);
+ } else {
+ outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_XSEL);
+ }
+ }
+ break;
+
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ /*
+ * Update out multicast list.
+ */
+ error = (command == SIOCADDMULTI) ?
+ ether_addmulti(ifr, &sc->arpcom) :
+ ether_delmulti(ifr, &sc->arpcom);
+
+ if (error == ENETRESET) {
+
+ /*
+ * Multicast list has changed; set the hardware filter
+ * accordingly.
+ */
+ ed_setrcr(ifp, sc);
+ error = 0;
+ }
+ break;
+
+ case SIOCSIFMTU:
+ /*
+ * Set the interface MTU.
+ */
+ if (ifr->ifr_mtu > ETHERMTU) {
+ error = EINVAL;
+ } else {
+ ifp->if_mtu = ifr->ifr_mtu;
+ }
+ break;
+
+ default:
+ error = EINVAL;
+ }
+ (void) splx(s);
+ return (error);
+}
+
+/*
+ * Retreive packet from shared memory and send to the next level up via
+ * ether_input(). If there is a BPF listener, give a copy to BPF, too.
+ */
+static void
+ed_get_packet(sc, buf, len, multicast)
+ struct ed_softc *sc;
+ char *buf;
+ u_short len;
+ int multicast;
+{
+ struct ether_header *eh;
+ struct mbuf *m;
+
+ /* Allocate a header mbuf */
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL)
+ return;
+ m->m_pkthdr.rcvif = &sc->arpcom.ac_if;
+ m->m_pkthdr.len = m->m_len = len;
+
+ /* Attach an mbuf cluster */
+ MCLGET(m, M_DONTWAIT);
+
+ /* Insist on getting a cluster */
+ if ((m->m_flags & M_EXT) == 0) {
+ m_freem(m);
+ return;
+ }
+
+ /*
+ * The +2 is to longword align the start of the real packet.
+ * This is important for NFS.
+ */
+ m->m_data += 2;
+ eh = mtod(m, struct ether_header *);
+
+ /*
+ * Get packet, including link layer address, from interface.
+ */
+ ed_ring_copy(sc, buf, (char *)eh, len);
+
+#if NBPFILTER > 0
+
+ /*
+ * Check if there's a BPF listener on this interface. If so, hand off
+ * the raw packet to bpf.
+ */
+ if (sc->bpf) {
+ bpf_mtap(sc->bpf, m);
+
+ /*
+ * Note that the interface cannot be in promiscuous mode if
+ * there are no BPF listeners. And if we are in promiscuous
+ * mode, we have to check if this packet is really ours.
+ */
+ if ((sc->arpcom.ac_if.if_flags & IFF_PROMISC) &&
+ bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr,
+ sizeof(eh->ether_dhost)) != 0 && multicast == 0) {
+ m_freem(m);
+ return;
+ }
+ }
+#endif
+
+ /*
+ * Remove link layer address.
+ */
+ m->m_pkthdr.len = m->m_len = len - sizeof(struct ether_header);
+ m->m_data += sizeof(struct ether_header);
+
+ ether_input(&sc->arpcom.ac_if, eh, m);
+ return;
+}
+
+/*
+ * Supporting routines
+ */
+
+/*
+ * Given a NIC memory source address and a host memory destination
+ * address, copy 'amount' from NIC to host using Programmed I/O.
+ * The 'amount' is rounded up to a word - okay as long as mbufs
+ * are word sized.
+ * This routine is currently Novell-specific.
+ */
+void
+ed_pio_readmem(sc, src, dst, amount)
+ struct ed_softc *sc;
+ unsigned short src;
+ unsigned char *dst;
+ unsigned short amount;
+{
+ /* select page 0 registers */
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STA);
+
+ /* round up to a word */
+ if (amount & 1)
+ ++amount;
+
+ /* set up DMA byte count */
+ outb(sc->nic_addr + ED_P0_RBCR0, amount);
+ outb(sc->nic_addr + ED_P0_RBCR1, amount >> 8);
+
+ /* set up source address in NIC mem */
+ outb(sc->nic_addr + ED_P0_RSAR0, src);
+ outb(sc->nic_addr + ED_P0_RSAR1, src >> 8);
+
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD0 | ED_CR_STA);
+
+ if (sc->isa16bit) {
+ insw(sc->asic_addr + ED_NOVELL_DATA, dst, amount / 2);
+ } else
+ insb(sc->asic_addr + ED_NOVELL_DATA, dst, amount);
+
+}
+
+/*
+ * Stripped down routine for writing a linear buffer to NIC memory.
+ * Only used in the probe routine to test the memory. 'len' must
+ * be even.
+ */
+void
+ed_pio_writemem(sc, src, dst, len)
+ struct ed_softc *sc;
+ char *src;
+ unsigned short dst;
+ unsigned short len;
+{
+ int maxwait = 200; /* about 240us */
+
+ /* select page 0 registers */
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STA);
+
+ /* reset remote DMA complete flag */
+ outb(sc->nic_addr + ED_P0_ISR, ED_ISR_RDC);
+
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD0 | ED_CR_STA);
+ /* set up DMA byte count */
+ outb(sc->nic_addr + ED_P0_RBCR0, len);
+ outb(sc->nic_addr + ED_P0_RBCR1, len >> 8);
+
+ /* set up destination address in NIC mem */
+ outb(sc->nic_addr + ED_P0_RSAR0, dst);
+ outb(sc->nic_addr + ED_P0_RSAR1, dst >> 8);
+
+ /* set remote DMA write */
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD1 | ED_CR_STA);
+
+ if (sc->isa16bit)
+ outsw(sc->asic_addr + ED_NOVELL_DATA, src, len / 2);
+ else
+ outsb(sc->asic_addr + ED_NOVELL_DATA, src, len);
+
+ /*
+ * Wait for remote DMA complete. This is necessary because on the
+ * transmit side, data is handled internally by the NIC in bursts and
+ * we can't start another remote DMA until this one completes. Not
+ * waiting causes really bad things to happen - like the NIC
+ * irrecoverably jamming the ISA bus.
+ */
+ while (((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RDC) != ED_ISR_RDC) && --maxwait);
+}
+my_outsw(int ad, unsigned short *src, int len)
+{
+ while(--len >= 0)
+ {
+ outw(ad, *src++);
+ printf("*");
+ }
+ printf("<\n");
+}
+
+/*
+ * Write an mbuf chain to the destination NIC memory address using
+ * programmed I/O.
+ */
+u_short
+ed_pio_write_mbufs(sc, m, dst)
+ struct ed_softc *sc;
+ struct mbuf *m;
+ unsigned short dst;
+{
+ unsigned short total_len, dma_len;
+ struct mbuf *mp;
+ int maxwait = 200; /* about 240us */
+
+ /* First, count up the total number of bytes to copy */
+ for (total_len = 0, mp = m; mp; mp = mp->m_next)
+ total_len += mp->m_len;
+
+ dma_len = total_len;
+ if (sc->isa16bit && (dma_len & 1))
+ dma_len++;
+
+ /* select page 0 registers */
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STA);
+
+ /* reset remote DMA complete flag */
+ outb(sc->nic_addr + ED_P0_ISR, ED_ISR_RDC);
+
+ /* set up DMA byte count */
+ outb(sc->nic_addr + ED_P0_RBCR0, dma_len);
+ outb(sc->nic_addr + ED_P0_RBCR1, dma_len >> 8);
+
+ /* set up destination address in NIC mem */
+ outb(sc->nic_addr + ED_P0_RSAR0, dst);
+ outb(sc->nic_addr + ED_P0_RSAR1, dst >> 8);
+
+ /* set remote DMA write */
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD1 | ED_CR_STA);
+
+ /*
+ * Transfer the mbuf chain to the NIC memory.
+ * 16-bit cards require that data be transferred as words, and only words.
+ * So that case requires some extra code to patch over odd-length mbufs.
+ */
+
+ if (!sc->isa16bit) {
+ /* NE1000s are easy */
+ while (m) {
+ if (m->m_len) {
+ outsb(sc->asic_addr + ED_NOVELL_DATA,
+ m->m_data, m->m_len);
+ }
+ m = m->m_next;
+ }
+ } else {
+ /* NE2000s are a pain */
+ unsigned char *data;
+ int len, wantbyte;
+ unsigned char savebyte[2];
+
+ wantbyte = 0;
+
+ while (m) {
+ len = m->m_len;
+ if (len) {
+ data = mtod(m, caddr_t);
+ /* finish the last word */
+ if (wantbyte) {
+ savebyte[1] = *data;
+ outw(sc->asic_addr + ED_NOVELL_DATA, *(u_short *)savebyte);
+ data++;
+ len--;
+ wantbyte = 0;
+ }
+ /* output contiguous words */
+ if (len > 1) {
+ outsw(sc->asic_addr + ED_NOVELL_DATA,
+ data, len >> 1);
+ data += len & ~1;
+ len &= 1;
+ }
+ /* save last byte, if necessary */
+ if (len == 1) {
+ savebyte[0] = *data;
+ wantbyte = 1;
+ }
+ }
+ m = m->m_next;
+ }
+ /* spit last byte */
+ if (wantbyte) {
+ outw(sc->asic_addr + ED_NOVELL_DATA, *(u_short *)savebyte);
+ }
+ }
+
+ /*
+ * Wait for remote DMA complete. This is necessary because on the
+ * transmit side, data is handled internally by the NIC in bursts and
+ * we can't start another remote DMA until this one completes. Not
+ * waiting causes really bad things to happen - like the NIC
+ * irrecoverably jamming the ISA bus.
+ */
+ while (((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RDC) != ED_ISR_RDC) && --maxwait);
+
+ if (!maxwait) {
+ log(LOG_WARNING, "ed%d: remote transmit DMA failed to complete\n",
+ sc->arpcom.ac_if.if_unit);
+ ed_reset(sc->arpcom.ac_if.if_unit);
+ return(0);
+ }
+ return (total_len);
+}
+
+/*
+ * Given a source and destination address, copy 'amount' of a packet from
+ * the ring buffer into a linear destination buffer. Takes into account
+ * ring-wrap.
+ */
+static inline char *
+ed_ring_copy(sc, src, dst, amount)
+ struct ed_softc *sc;
+ char *src;
+ char *dst;
+ u_short amount;
+{
+ u_short tmp_amount;
+
+ /* does copy wrap to lower addr in ring buffer? */
+ if (src + amount > sc->mem_end) {
+ tmp_amount = sc->mem_end - src;
+
+ /* copy amount up to end of NIC memory */
+ if (sc->mem_shared)
+ bcopy(src, dst, tmp_amount);
+ else
+ ed_pio_readmem(sc, src, dst, tmp_amount);
+
+ amount -= tmp_amount;
+ src = sc->mem_ring;
+ dst += tmp_amount;
+ }
+ if (sc->mem_shared)
+ bcopy(src, dst, amount);
+ else
+ ed_pio_readmem(sc, src, dst, amount);
+
+ return (src + amount);
+}
+
+void
+ed_setrcr(ifp, sc)
+ struct ifnet *ifp;
+ struct ed_softc *sc;
+{
+ int i;
+
+ /* set page 1 registers */
+ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_PAGE_1 | ED_CR_STP);
+
+ if (ifp->if_flags & IFF_PROMISC) {
+
+ /*
+ * Reconfigure the multicast filter.
+ */
+ for (i = 0; i < 8; i++)
+ outb(sc->nic_addr + ED_P1_MAR0 + i, 0xff);
+
+ /*
+ * And turn on promiscuous mode. Also enable reception of
+ * runts and packets with CRC & alignment errors.
+ */
+ /* Set page 0 registers */
+ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_STP);
+
+ outb(sc->nic_addr + ED_P0_RCR, ED_RCR_PRO | ED_RCR_AM |
+ ED_RCR_AB | ED_RCR_AR | ED_RCR_SEP);
+ } else {
+ /* set up multicast addresses and filter modes */
+ if (ifp->if_flags & IFF_MULTICAST) {
+ u_long mcaf[2];
+
+ if (ifp->if_flags & IFF_ALLMULTI) {
+ mcaf[0] = 0xffffffff;
+ mcaf[1] = 0xffffffff;
+ } else
+ ds_getmcaf(sc, mcaf);
+
+ /*
+ * Set multicast filter on chip.
+ */
+ for (i = 0; i < 8; i++)
+ outb(sc->nic_addr + ED_P1_MAR0 + i, ((u_char *) mcaf)[i]);
+
+ /* Set page 0 registers */
+ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_STP);
+
+ outb(sc->nic_addr + ED_P0_RCR, ED_RCR_AM | ED_RCR_AB);
+ } else {
+
+ /*
+ * Initialize multicast address hashing registers to
+ * not accept multicasts.
+ */
+ for (i = 0; i < 8; ++i)
+ outb(sc->nic_addr + ED_P1_MAR0 + i, 0x00);
+
+ /* Set page 0 registers */
+ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_STP);
+
+ outb(sc->nic_addr + ED_P0_RCR, ED_RCR_AB);
+ }
+ }
+
+ /*
+ * Start interface.
+ */
+ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_STA);
+}
+
+/*
+ * Compute crc for ethernet address
+ */
+u_long
+ds_crc(ep)
+ u_char *ep;
+{
+#define POLYNOMIAL 0x04c11db6
+ register u_long crc = 0xffffffffL;
+ register int carry, i, j;
+ register u_char b;
+
+ for (i = 6; --i >= 0;) {
+ b = *ep++;
+ for (j = 8; --j >= 0;) {
+ carry = ((crc & 0x80000000L) ? 1 : 0) ^ (b & 0x01);
+ crc <<= 1;
+ b >>= 1;
+ if (carry)
+ crc = ((crc ^ POLYNOMIAL) | carry);
+ }
+ }
+ return crc;
+#undef POLYNOMIAL
+}
+
+/*
+ * Compute the multicast address filter from the
+ * list of multicast addresses we need to listen to.
+ */
+void
+ds_getmcaf(sc, mcaf)
+ struct ed_softc *sc;
+ u_long *mcaf;
+{
+ register u_int index;
+ register u_char *af = (u_char *) mcaf;
+ register struct ether_multi *enm;
+ register struct ether_multistep step;
+
+ mcaf[0] = 0;
+ mcaf[1] = 0;
+
+ ETHER_FIRST_MULTI(step, &sc->arpcom, enm);
+ while (enm != NULL) {
+ if (bcmp(enm->enm_addrlo, enm->enm_addrhi, 6) != 0) {
+ mcaf[0] = 0xffffffff;
+ mcaf[1] = 0xffffffff;
+ return;
+ }
+ index = ds_crc(enm->enm_addrlo, 6) >> 26;
+ af[index >> 3] |= 1 << (index & 7);
+
+ ETHER_NEXT_MULTI(step, enm);
+ }
+}
diff --git a/usr.sbin/pccard/misc/sys/pccard/if_edreg.h b/usr.sbin/pccard/misc/sys/pccard/if_edreg.h
new file mode 100644
index 0000000..9644aaf
--- /dev/null
+++ b/usr.sbin/pccard/misc/sys/pccard/if_edreg.h
@@ -0,0 +1,989 @@
+/*
+ * Copyright (C) 1993, David Greenman. This software may be used, modified,
+ * copied, distributed, and sold, in both source and binary form provided
+ * that the above copyright and these terms are retained. Under no
+ * circumstances is the author responsible for the proper functioning
+ * of this software, nor does the author assume any responsibility
+ * for damages incurred with its use.
+ *
+ * $Id: if_edreg.h,v 1.17 1995/01/23 19:06:08 davidg Exp $
+ */
+/*
+ * National Semiconductor DS8390 NIC register definitions
+ *
+ *
+ * Modification history
+ *
+ * Revision 2.2 1993/11/29 16:33:39 davidg
+ * From Thomas Sandford <t.d.g.sandford@comp.brad.ac.uk>
+ * Add support for the 8013W board type
+ *
+ * Revision 2.1 1993/11/22 10:52:33 davidg
+ * patch to add support for SMC8216 (Elite-Ultra) boards
+ * from Glen H. Lowe
+ *
+ * Revision 2.0 93/09/29 00:37:15 davidg
+ * changed double buffering flag to multi buffering
+ * made changes/additions for 3c503 multi-buffering
+ * ...companion to Rev. 2.0 of 'ed' driver.
+ *
+ * Revision 1.1 93/06/23 03:01:07 davidg
+ * Initial revision
+ *
+ */
+
+/*
+ * Page 0 register offsets
+ */
+#define ED_P0_CR 0x00 /* Command Register */
+
+#define ED_P0_CLDA0 0x01 /* Current Local DMA Addr low (read) */
+#define ED_P0_PSTART 0x01 /* Page Start register (write) */
+
+#define ED_P0_CLDA1 0x02 /* Current Local DMA Addr high (read) */
+#define ED_P0_PSTOP 0x02 /* Page Stop register (write) */
+
+#define ED_P0_BNRY 0x03 /* Boundary Pointer */
+
+#define ED_P0_TSR 0x04 /* Transmit Status Register (read) */
+#define ED_P0_TPSR 0x04 /* Transmit Page Start (write) */
+
+#define ED_P0_NCR 0x05 /* Number of Collisions Reg (read) */
+#define ED_P0_TBCR0 0x05 /* Transmit Byte count, low (write) */
+
+#define ED_P0_FIFO 0x06 /* FIFO register (read) */
+#define ED_P0_TBCR1 0x06 /* Transmit Byte count, high (write) */
+
+#define ED_P0_ISR 0x07 /* Interrupt Status Register */
+
+#define ED_P0_CRDA0 0x08 /* Current Remote DMA Addr low (read) */
+#define ED_P0_RSAR0 0x08 /* Remote Start Address low (write) */
+
+#define ED_P0_CRDA1 0x09 /* Current Remote DMA Addr high (read) */
+#define ED_P0_RSAR1 0x09 /* Remote Start Address high (write) */
+
+#define ED_P0_RBCR0 0x0a /* Remote Byte Count low (write) */
+
+#define ED_P0_RBCR1 0x0b /* Remote Byte Count high (write) */
+
+#define ED_P0_RSR 0x0c /* Receive Status (read) */
+#define ED_P0_RCR 0x0c /* Receive Configuration Reg (write) */
+
+#define ED_P0_CNTR0 0x0d /* frame alignment error counter (read) */
+#define ED_P0_TCR 0x0d /* Transmit Configuration Reg (write) */
+
+#define ED_P0_CNTR1 0x0e /* CRC error counter (read) */
+#define ED_P0_DCR 0x0e /* Data Configuration Reg (write) */
+
+#define ED_P0_CNTR2 0x0f /* missed packet counter (read) */
+#define ED_P0_IMR 0x0f /* Interrupt Mask Register (write) */
+
+/*
+ * Page 1 register offsets
+ */
+#define ED_P1_CR 0x00 /* Command Register */
+#define ED_P1_PAR0 0x01 /* Physical Address Register 0 */
+#define ED_P1_PAR1 0x02 /* Physical Address Register 1 */
+#define ED_P1_PAR2 0x03 /* Physical Address Register 2 */
+#define ED_P1_PAR3 0x04 /* Physical Address Register 3 */
+#define ED_P1_PAR4 0x05 /* Physical Address Register 4 */
+#define ED_P1_PAR5 0x06 /* Physical Address Register 5 */
+#define ED_P1_CURR 0x07 /* Current RX ring-buffer page */
+#define ED_P1_MAR0 0x08 /* Multicast Address Register 0 */
+#define ED_P1_MAR1 0x09 /* Multicast Address Register 1 */
+#define ED_P1_MAR2 0x0a /* Multicast Address Register 2 */
+#define ED_P1_MAR3 0x0b /* Multicast Address Register 3 */
+#define ED_P1_MAR4 0x0c /* Multicast Address Register 4 */
+#define ED_P1_MAR5 0x0d /* Multicast Address Register 5 */
+#define ED_P1_MAR6 0x0e /* Multicast Address Register 6 */
+#define ED_P1_MAR7 0x0f /* Multicast Address Register 7 */
+
+/*
+ * Page 2 register offsets
+ */
+#define ED_P2_CR 0x00 /* Command Register */
+#define ED_P2_PSTART 0x01 /* Page Start (read) */
+#define ED_P2_CLDA0 0x01 /* Current Local DMA Addr 0 (write) */
+#define ED_P2_PSTOP 0x02 /* Page Stop (read) */
+#define ED_P2_CLDA1 0x02 /* Current Local DMA Addr 1 (write) */
+#define ED_P2_RNPP 0x03 /* Remote Next Packet Pointer */
+#define ED_P2_TPSR 0x04 /* Transmit Page Start (read) */
+#define ED_P2_LNPP 0x05 /* Local Next Packet Pointer */
+#define ED_P2_ACU 0x06 /* Address Counter Upper */
+#define ED_P2_ACL 0x07 /* Address Counter Lower */
+#define ED_P2_RCR 0x0c /* Receive Configuration Register (read) */
+#define ED_P2_TCR 0x0d /* Transmit Configuration Register (read) */
+#define ED_P2_DCR 0x0e /* Data Configuration Register (read) */
+#define ED_P2_IMR 0x0f /* Interrupt Mask Register (read) */
+
+/*
+ * Command Register (CR) definitions
+ */
+
+/*
+ * STP: SToP. Software reset command. Takes the controller offline. No
+ * packets will be received or transmitted. Any reception or
+ * transmission in progress will continue to completion before
+ * entering reset state. To exit this state, the STP bit must
+ * reset and the STA bit must be set. The software reset has
+ * executed only when indicated by the RST bit in the ISR being
+ * set.
+ */
+#define ED_CR_STP 0x01
+
+/*
+ * STA: STArt. This bit is used to activate the NIC after either power-up,
+ * or when the NIC has been put in reset mode by software command
+ * or error.
+ */
+#define ED_CR_STA 0x02
+
+/*
+ * TXP: Transmit Packet. This bit must be set to indicate transmission of
+ * a packet. TXP is internally reset either after the transmission is
+ * completed or aborted. This bit should be set only after the Transmit
+ * Byte Count and Transmit Page Start register have been programmed.
+ */
+#define ED_CR_TXP 0x04
+
+/*
+ * RD0, RD1, RD2: Remote DMA Command. These three bits control the operation
+ * of the remote DMA channel. RD2 can be set to abort any remote DMA
+ * command in progress. The Remote Byte Count registers should be cleared
+ * when a remote DMA has been aborted. The Remote Start Addresses are not
+ * restored to the starting address if the remote DMA is aborted.
+ *
+ * RD2 RD1 RD0 function
+ * 0 0 0 not allowed
+ * 0 0 1 remote read
+ * 0 1 0 remote write
+ * 0 1 1 send packet
+ * 1 X X abort
+ */
+#define ED_CR_RD0 0x08
+#define ED_CR_RD1 0x10
+#define ED_CR_RD2 0x20
+
+/*
+ * PS0, PS1: Page Select. The two bits select which register set or 'page' to
+ * access.
+ *
+ * PS1 PS0 page
+ * 0 0 0
+ * 0 1 1
+ * 1 0 2
+ * 1 1 reserved
+ */
+#define ED_CR_PS0 0x40
+#define ED_CR_PS1 0x80
+/* bit encoded aliases */
+#define ED_CR_PAGE_0 0x00 /* (for consistency) */
+#define ED_CR_PAGE_1 0x40
+#define ED_CR_PAGE_2 0x80
+
+/*
+ * Interrupt Status Register (ISR) definitions
+ */
+
+/*
+ * PRX: Packet Received. Indicates packet received with no errors.
+ */
+#define ED_ISR_PRX 0x01
+
+/*
+ * PTX: Packet Transmitted. Indicates packet transmitted with no errors.
+ */
+#define ED_ISR_PTX 0x02
+
+/*
+ * RXE: Receive Error. Indicates that a packet was received with one or more
+ * the following errors: CRC error, frame alignment error, FIFO overrun,
+ * missed packet.
+ */
+#define ED_ISR_RXE 0x04
+
+/*
+ * TXE: Transmission Error. Indicates that an attempt to transmit a packet
+ * resulted in one or more of the following errors: excessive
+ * collisions, FIFO underrun.
+ */
+#define ED_ISR_TXE 0x08
+
+/*
+ * OVW: OverWrite. Indicates a receive ring-buffer overrun. Incoming network
+ * would exceed (has exceeded?) the boundry pointer, resulting in data
+ * that was previously received and not yet read from the buffer to be
+ * overwritten.
+ */
+#define ED_ISR_OVW 0x10
+
+/*
+ * CNT: Counter Overflow. Set when the MSB of one or more of the Network Talley
+ * Counters has been set.
+ */
+#define ED_ISR_CNT 0x20
+
+/*
+ * RDC: Remote Data Complete. Indicates that a Remote DMA operation has completed.
+ */
+#define ED_ISR_RDC 0x40
+
+/*
+ * RST: Reset status. Set when the NIC enters the reset state and cleared when a
+ * Start Command is issued to the CR. This bit is also set when a receive
+ * ring-buffer overrun (OverWrite) occurs and is cleared when one or more
+ * packets have been removed from the ring. This is a read-only bit.
+ */
+#define ED_ISR_RST 0x80
+
+/*
+ * Interrupt Mask Register (IMR) definitions
+ */
+
+/*
+ * PRXE: Packet Received interrupt Enable. If set, a received packet will cause
+ * an interrupt.
+ */
+#define ED_IMR_PRXE 0x01
+
+/*
+ * PTXE: Packet Transmit interrupt Enable. If set, an interrupt is generated when
+ * a packet transmission completes.
+ */
+#define ED_IMR_PTXE 0x02
+
+/*
+ * RXEE: Receive Error interrupt Enable. If set, an interrupt will occur whenever a
+ * packet is received with an error.
+ */
+#define ED_IMR_RXEE 0x04
+
+/*
+ * TXEE: Transmit Error interrupt Enable. If set, an interrupt will occur whenever
+ * a transmission results in an error.
+ */
+#define ED_IMR_TXEE 0x08
+
+/*
+ * OVWE: OverWrite error interrupt Enable. If set, an interrupt is generated whenever
+ * the receive ring-buffer is overrun. i.e. when the boundry pointer is exceeded.
+ */
+#define ED_IMR_OVWE 0x10
+
+/*
+ * CNTE: Counter overflow interrupt Enable. If set, an interrupt is generated whenever
+ * the MSB of one or more of the Network Statistics counters has been set.
+ */
+#define ED_IMR_CNTE 0x20
+
+/*
+ * RDCE: Remote DMA Complete interrupt Enable. If set, an interrupt is generated
+ * when a remote DMA transfer has completed.
+ */
+#define ED_IMR_RDCE 0x40
+
+/*
+ * bit 7 is unused/reserved
+ */
+
+/*
+ * Data Configuration Register (DCR) definitions
+ */
+
+/*
+ * WTS: Word Transfer Select. WTS establishes byte or word transfers for
+ * both remote and local DMA transfers
+ */
+#define ED_DCR_WTS 0x01
+
+/*
+ * BOS: Byte Order Select. BOS sets the byte order for the host.
+ * Should be 0 for 80x86, and 1 for 68000 series processors
+ */
+#define ED_DCR_BOS 0x02
+
+/*
+ * LAS: Long Address Select. When LAS is 1, the contents of the remote
+ * DMA registers RSAR0 and RSAR1 are used to provide A16-A31
+ */
+#define ED_DCR_LAS 0x04
+
+/*
+ * LS: Loopback Select. When 0, loopback mode is selected. Bits D1 and D2
+ * of the TCR must also be programmed for loopback operation.
+ * When 1, normal operation is selected.
+ */
+#define ED_DCR_LS 0x08
+
+/*
+ * AR: Auto-initialize Remote. When 0, data must be removed from ring-buffer
+ * under program control. When 1, remote DMA is automatically initiated
+ * and the boundry pointer is automatically updated
+ */
+#define ED_DCR_AR 0x10
+
+/*
+ * FT0, FT1: Fifo Threshold select.
+ * FT1 FT0 Word-width Byte-width
+ * 0 0 1 word 2 bytes
+ * 0 1 2 words 4 bytes
+ * 1 0 4 words 8 bytes
+ * 1 1 8 words 12 bytes
+ *
+ * During transmission, the FIFO threshold indicates the number of bytes
+ * or words that the FIFO has filled from the local DMA before BREQ is
+ * asserted. The transmission threshold is 16 bytes minus the receiver
+ * threshold.
+ */
+#define ED_DCR_FT0 0x20
+#define ED_DCR_FT1 0x40
+
+/*
+ * bit 7 (0x80) is unused/reserved
+ */
+
+/*
+ * Transmit Configuration Register (TCR) definitions
+ */
+
+/*
+ * CRC: Inhibit CRC. If 0, CRC will be appended by the transmitter, if 0, CRC
+ * is not appended by the transmitter.
+ */
+#define ED_TCR_CRC 0x01
+
+/*
+ * LB0, LB1: Loopback control. These two bits set the type of loopback that is
+ * to be performed.
+ *
+ * LB1 LB0 mode
+ * 0 0 0 - normal operation (DCR_LS = 0)
+ * 0 1 1 - internal loopback (DCR_LS = 0)
+ * 1 0 2 - external loopback (DCR_LS = 1)
+ * 1 1 3 - external loopback (DCR_LS = 0)
+ */
+#define ED_TCR_LB0 0x02
+#define ED_TCR_LB1 0x04
+
+/*
+ * ATD: Auto Transmit Disable. Clear for normal operation. When set, allows
+ * another station to disable the NIC's transmitter by transmitting to
+ * a multicast address hashing to bit 62. Reception of a multicast address
+ * hashing to bit 63 enables the transmitter.
+ */
+#define ED_TCR_ATD 0x08
+
+/*
+ * OFST: Collision Offset enable. This bit when set modifies the backoff
+ * algorithm to allow prioritization of nodes.
+ */
+#define ED_TCR_OFST 0x10
+
+/*
+ * bits 5, 6, and 7 are unused/reserved
+ */
+
+/*
+ * Transmit Status Register (TSR) definitions
+ */
+
+/*
+ * PTX: Packet Transmitted. Indicates successful transmission of packet.
+ */
+#define ED_TSR_PTX 0x01
+
+/*
+ * bit 1 (0x02) is unused/reserved
+ */
+
+/*
+ * COL: Transmit Collided. Indicates that the transmission collided at least
+ * once with another station on the network.
+ */
+#define ED_TSR_COL 0x04
+
+/*
+ * ABT: Transmit aborted. Indicates that the transmission was aborted due to
+ * excessive collisions.
+ */
+#define ED_TSR_ABT 0x08
+
+/*
+ * CRS: Carrier Sense Lost. Indicates that carrier was lost during the
+ * transmission of the packet. (Transmission is not aborted because
+ * of a loss of carrier)
+ */
+#define ED_TSR_CRS 0x10
+
+/*
+ * FU: FIFO Underrun. Indicates that the NIC wasn't able to access bus/
+ * transmission memory before the FIFO emptied. Transmission of the
+ * packet was aborted.
+ */
+#define ED_TSR_FU 0x20
+
+/*
+ * CDH: CD Heartbeat. Indicates that the collision detection circuitry
+ * isn't working correctly during a collision heartbeat test.
+ */
+#define ED_TSR_CDH 0x40
+
+/*
+ * OWC: Out of Window Collision: Indicates that a collision occurred after
+ * a slot time (51.2us). The transmission is rescheduled just as in
+ * normal collisions.
+ */
+#define ED_TSR_OWC 0x80
+
+/*
+ * Receiver Configuration Register (RCR) definitions
+ */
+
+/*
+ * SEP: Save Errored Packets. If 0, error packets are discarded. If set to 1,
+ * packets with CRC and frame errors are not discarded.
+ */
+#define ED_RCR_SEP 0x01
+
+/*
+ * AR: Accept Runt packet. If 0, packet with less than 64 byte are discarded.
+ * If set to 1, packets with less than 64 byte are not discarded.
+ */
+#define ED_RCR_AR 0x02
+
+/*
+ * AB: Accept Broadcast. If set, packets sent to the broadcast address will be
+ * accepted.
+ */
+#define ED_RCR_AB 0x04
+
+/*
+ * AM: Accept Multicast. If set, packets sent to a multicast address are checked
+ * for a match in the hashing array. If clear, multicast packets are ignored.
+ */
+#define ED_RCR_AM 0x08
+
+/*
+ * PRO: Promiscuous Physical. If set, all packets with a physical addresses are
+ * accepted. If clear, a physical destination address must match this
+ * station's address. Note: for full promiscuous mode, RCR_AB and RCR_AM
+ * must also be set. In addition, the multicast hashing array must be set
+ * to all 1's so that all multicast addresses are accepted.
+ */
+#define ED_RCR_PRO 0x10
+
+/*
+ * MON: Monitor Mode. If set, packets will be checked for good CRC and framing,
+ * but are not stored in the ring-buffer. If clear, packets are stored (normal
+ * operation).
+ */
+#define ED_RCR_MON 0x20
+
+/*
+ * bits 6 and 7 are unused/reserved.
+ */
+
+/*
+ * Receiver Status Register (RSR) definitions
+ */
+
+/*
+ * PRX: Packet Received without error.
+ */
+#define ED_RSR_PRX 0x01
+
+/*
+ * CRC: CRC error. Indicates that a packet has a CRC error. Also set for frame
+ * alignment errors.
+ */
+#define ED_RSR_CRC 0x02
+
+/*
+ * FAE: Frame Alignment Error. Indicates that the incoming packet did not end on
+ * a byte boundry and the CRC did not match at the last byte boundry.
+ */
+#define ED_RSR_FAE 0x04
+
+/*
+ * FO: FIFO Overrun. Indicates that the FIFO was not serviced (during local DMA)
+ * causing it to overrun. Reception of the packet is aborted.
+ */
+#define ED_RSR_FO 0x08
+
+/*
+ * MPA: Missed Packet. Indicates that the received packet couldn't be stored in
+ * the ring-buffer because of insufficient buffer space (exceeding the
+ * boundry pointer), or because the transfer to the ring-buffer was inhibited
+ * by RCR_MON - monitor mode.
+ */
+#define ED_RSR_MPA 0x10
+
+/*
+ * PHY: Physical address. If 0, the packet received was sent to a physical address.
+ * If 1, the packet was accepted because of a multicast/broadcast address
+ * match.
+ */
+#define ED_RSR_PHY 0x20
+
+/*
+ * DIS: Receiver Disabled. Set to indicate that the receiver has enetered monitor
+ * mode. Cleared when the receiver exits monitor mode.
+ */
+#define ED_RSR_DIS 0x40
+
+/*
+ * DFR: Deferring. Set to indicate a 'jabber' condition. The CRS and COL inputs
+ * are active, and the transceiver has set the CD line as a result of the
+ * jabber.
+ */
+#define ED_RSR_DFR 0x80
+
+/*
+ * receive ring discriptor
+ *
+ * The National Semiconductor DS8390 Network interface controller uses
+ * the following receive ring headers. The way this works is that the
+ * memory on the interface card is chopped up into 256 bytes blocks.
+ * A contiguous portion of those blocks are marked for receive packets
+ * by setting start and end block #'s in the NIC. For each packet that
+ * is put into the receive ring, one of these headers (4 bytes each) is
+ * tacked onto the front. The first byte is a copy of the receiver status
+ * register at the time the packet was received.
+ */
+struct ed_ring {
+ u_char rsr; /* receiver status */
+ u_char next_packet; /* pointer to next packet */
+ u_short count; /* bytes in packet (length + 4) */
+};
+
+/*
+ * Common constants
+ */
+#define ED_PAGE_SIZE 256 /* Size of RAM pages in bytes */
+#define ED_TXBUF_SIZE 6 /* Size of TX buffer in pages */
+
+/*
+ * Vendor types
+ */
+#define ED_VENDOR_WD_SMC 0x00 /* Western Digital/SMC */
+#define ED_VENDOR_3COM 0x01 /* 3Com */
+#define ED_VENDOR_NOVELL 0x02 /* Novell */
+#define ED_VENDOR_PCMCIA 0x03 /* Generic pcmcia */
+
+/*
+ * Compile-time config flags
+ */
+/*
+ * this sets the default for enabling/disablng the tranceiver
+ */
+#define ED_FLAGS_DISABLE_TRANCEIVER 0x0001
+
+/*
+ * This forces the board to be used in 8/16bit mode even if it
+ * autoconfigs differently
+ */
+#define ED_FLAGS_FORCE_8BIT_MODE 0x0002
+#define ED_FLAGS_FORCE_16BIT_MODE 0x0004
+
+/*
+ * This disables the use of double transmit buffers.
+ */
+#define ED_FLAGS_NO_MULTI_BUFFERING 0x0008
+
+/*
+ * This forces all operations with the NIC memory to use Programmed
+ * I/O (i.e. not via shared memory)
+ */
+#define ED_FLAGS_FORCE_PIO 0x0010
+
+/*
+ * Definitions for Western digital/SMC WD80x3 series ASIC
+ */
+/*
+ * Memory Select Register (MSR)
+ */
+#define ED_WD_MSR 0
+
+/* next three definitions for Toshiba */
+#define ED_WD_MSR_POW 0x02 /* 0 = power save, 1 = normal (R/W) */
+#define ED_WD_MSR_BSY 0x04 /* gate array busy (R) */
+#define ED_WD_MSR_LEN 0x20 /* data bus width, 0 = 16 bits,
+ 1 = 8 bits (R/W) */
+#define ED_WD_MSR_ADDR 0x3f /* Memory decode bits 18-13 */
+#define ED_WD_MSR_MENB 0x40 /* Memory enable */
+#define ED_WD_MSR_RST 0x80 /* Reset board */
+
+/*
+ * Interface Configuration Register (ICR)
+ */
+#define ED_WD_ICR 1
+
+#define ED_WD_ICR_16BIT 0x01 /* 16-bit interface */
+#define ED_WD_ICR_OAR 0x02 /* select register. 0=BIO 1=EAR */
+#define ED_WD_ICR_IR2 0x04 /* high order bit of encoded IRQ */
+#define ED_WD_ICR_MSZ 0x08 /* memory size (0=8k 1=32k) */
+#define ED_WD_ICR_RLA 0x10 /* recall LAN address */
+#define ED_WD_ICR_RX7 0x20 /* recall all but i/o and LAN address */
+#define ED_WD_ICR_RIO 0x40 /* recall i/o address */
+#define ED_WD_ICR_STO 0x80 /* store to non-volatile memory */
+#ifdef TOSH_ETHER
+#define ED_WD_ICR_MEM 0xe0 /* shared mem address A15-A13 (R/W) */
+#define ED_WD_ICR_MSZ1 0x0f /* memory size, 0x08 = 64K, 0x04 = 32K,
+ 0x02 = 16K, 0x01 = 8K */
+ /* 64K can only be used if mem address
+ above 1Mb */
+ /* IAR holds address A23-A16 (R/W) */
+#endif
+
+/*
+ * IO Address Register (IAR)
+ */
+#define ED_WD_IAR 2
+
+/*
+ * EEROM Address Register
+ */
+#define ED_WD_EAR 3
+
+/*
+ * Interrupt Request Register (IRR)
+ */
+#define ED_WD_IRR 4
+
+#define ED_WD_IRR_0WS 0x01 /* use 0 wait-states on 8 bit bus */
+#define ED_WD_IRR_OUT1 0x02 /* WD83C584 pin 1 output */
+#define ED_WD_IRR_OUT2 0x04 /* WD83C584 pin 2 output */
+#define ED_WD_IRR_OUT3 0x08 /* WD83C584 pin 3 output */
+#define ED_WD_IRR_FLASH 0x10 /* Flash RAM is in the ROM socket */
+
+/*
+ * The three bits of the encoded IRQ are decoded as follows:
+ *
+ * IR2 IR1 IR0 IRQ
+ * 0 0 0 2/9
+ * 0 0 1 3
+ * 0 1 0 5
+ * 0 1 1 7
+ * 1 0 0 10
+ * 1 0 1 11
+ * 1 1 0 15
+ * 1 1 1 4
+ */
+#define ED_WD_IRR_IR0 0x20 /* bit 0 of encoded IRQ */
+#define ED_WD_IRR_IR1 0x40 /* bit 1 of encoded IRQ */
+#define ED_WD_IRR_IEN 0x80 /* Interrupt enable */
+
+/*
+ * LA Address Register (LAAR)
+ */
+#define ED_WD_LAAR 5
+
+#define ED_WD_LAAR_ADDRHI 0x1f /* bits 23-19 of RAM address */
+#define ED_WD_LAAR_0WS16 0x20 /* enable 0 wait-states on 16 bit bus */
+#define ED_WD_LAAR_L16EN 0x40 /* enable 16-bit operation */
+#define ED_WD_LAAR_M16EN 0x80 /* enable 16-bit memory access */
+
+/* i/o base offset to station address/card-ID PROM */
+#define ED_WD_PROM 8
+
+/*
+ * 83C790 specific registers
+ */
+/*
+ * Hardware Support Register (HWR) ('790)
+ */
+#define ED_WD790_HWR 4
+
+#define WD_WD790_HWR_NUKE 0x10 /* hardware reset */
+#define ED_WD790_HWR_LPRM 0x40 /* LAN PROM select */
+#define ED_WD790_HWR_SWH 0x80 /* switch register set */
+
+/*
+ * ICR790 Interrupt Control Register for the 83C790
+ */
+#define ED_WD790_ICR 6
+
+#define ED_WD790_ICR_EIL 0x01 /* enable interrupts */
+
+/*
+ * REV/IOPA Revision / I/O Pipe register for the 83C79X
+ */
+#define ED_WD790_REV 7
+
+#define ED_WD790 0x20
+#define ED_WD795 0x40
+
+/*
+ * 79X RAM Address Register (RAR)
+ * Enabled with SWH bit=1 in HWR register
+ */
+#define ED_WD790_RAR 0x0b
+
+#define ED_WD790_RAR_SZ8 0x00 /* 8k memory buffer */
+#define ED_WD790_RAR_SZ16 0x10 /* 16k memory buffer */
+#define ED_WD790_RAR_SZ32 0x20 /* 32k memory buffer */
+#define ED_WD790_RAR_SZ64 0x30 /* 64k memory buffer */
+
+/*
+ * General Control Register (GCR)
+ * Enabled with SWH bit=1 in HWR register
+ */
+#define ED_WD790_GCR 0x0d
+
+#define ED_WD790_GCR_IR0 0x04 /* bit 0 of encoded IRQ */
+#define ED_WD790_GCR_IR1 0x08 /* bit 1 of encoded IRQ */
+#define ED_WD790_GCR_ZWSEN 0x20 /* zero wait state enable */
+#define ED_WD790_GCR_IR2 0x40 /* bit 2 of encoded IRQ */
+#define ED_WD790_GCR_LIT 0x01 /* Link Integrity Test Enable */
+/*
+ * The three bits of the encoded IRQ are decoded as follows:
+ *
+ * IR2 IR1 IR0 IRQ
+ * 0 0 0 none
+ * 0 0 1 9
+ * 0 1 0 3
+ * 0 1 1 5
+ * 1 0 0 7
+ * 1 0 1 10
+ * 1 1 0 11
+ * 1 1 1 15
+ */
+
+/* i/o base offset to CARD ID */
+#define ED_WD_CARD_ID ED_WD_PROM+6
+
+/* Board type codes in card ID */
+#define ED_TYPE_WD8003S 0x02
+#define ED_TYPE_WD8003E 0x03
+#define ED_TYPE_WD8013EBT 0x05
+#define ED_TYPE_TOSHIBA1 0x11 /* named PCETA1 */
+#define ED_TYPE_TOSHIBA2 0x12 /* named PCETA2 */
+#define ED_TYPE_TOSHIBA3 0x13 /* named PCETB */
+#define ED_TYPE_TOSHIBA4 0x14 /* named PCETC */
+#define ED_TYPE_WD8003W 0x24
+#define ED_TYPE_WD8003EB 0x25
+#define ED_TYPE_WD8013W 0x26
+#define ED_TYPE_WD8013EP 0x27
+#define ED_TYPE_WD8013WC 0x28
+#define ED_TYPE_WD8013EPC 0x29
+#define ED_TYPE_SMC8216T 0x2a
+#define ED_TYPE_SMC8216C 0x2b
+#define ED_TYPE_WD8013EBP 0x2c
+
+/* Bit definitions in card ID */
+#define ED_WD_REV_MASK 0x1f /* Revision mask */
+#define ED_WD_SOFTCONFIG 0x20 /* Soft config */
+#define ED_WD_LARGERAM 0x40 /* Large RAM */
+#define ED_MICROCHANEL 0x80 /* Microchannel bus (vs. isa) */
+
+/*
+ * Checksum total. All 8 bytes in station address PROM will add up to this
+ */
+#ifdef TOSH_ETHER
+#define ED_WD_ROM_CHECKSUM_TOTAL 0xA5
+#else
+#define ED_WD_ROM_CHECKSUM_TOTAL 0xFF
+#endif
+
+#define ED_WD_NIC_OFFSET 0x10 /* I/O base offset to NIC */
+#define ED_WD_ASIC_OFFSET 0 /* I/O base offset to ASIC */
+#define ED_WD_IO_PORTS 32 /* # of i/o addresses used */
+
+#define ED_WD_PAGE_OFFSET 0 /* page offset for NIC access to mem */
+
+/*
+ * Definitions for 3Com 3c503
+ */
+#define ED_3COM_NIC_OFFSET 0
+#define ED_3COM_ASIC_OFFSET 0x400 /* offset to nic i/o regs */
+
+/*
+ * XXX - The I/O address range is fragmented in the 3c503; this is the
+ * number of regs at iobase.
+ */
+#define ED_3COM_IO_PORTS 16 /* # of i/o addresses used */
+
+/* tx memory starts in second bank on 8bit cards */
+#define ED_3COM_TX_PAGE_OFFSET_8BIT 0x20
+
+/* tx memory starts in first bank on 16bit cards */
+#define ED_3COM_TX_PAGE_OFFSET_16BIT 0x0
+
+/* ...and rx memory starts in second bank */
+#define ED_3COM_RX_PAGE_OFFSET_16BIT 0x20
+
+
+/*
+ * Page Start Register. Must match PSTART in NIC
+ */
+#define ED_3COM_PSTR 0
+
+/*
+ * Page Stop Register. Must match PSTOP in NIC
+ */
+#define ED_3COM_PSPR 1
+
+/*
+ * Drq Timer Register. Determines number of bytes to be transfered during
+ * a DMA burst.
+ */
+#define ED_3COM_DQTR 2
+
+/*
+ * Base Configuration Register. Read-only register which contains the
+ * board-configured I/O base address of the adapter. Bit encoded.
+ */
+#define ED_3COM_BCFR 3
+
+#define ED_3COM_BCFR_2E0 0x01
+#define ED_3COM_BCFR_2A0 0x02
+#define ED_3COM_BCFR_280 0x04
+#define ED_3COM_BCFR_250 0x08
+#define ED_3COM_BCFR_350 0x10
+#define ED_3COM_BCFR_330 0x20
+#define ED_3COM_BCFR_310 0x40
+#define ED_3COM_BCFR_300 0x80
+
+/*
+ * EPROM Configuration Register. Read-only register which contains the
+ * board-configured memory base address. Bit encoded.
+ */
+#define ED_3COM_PCFR 4
+
+#define ED_3COM_PCFR_C8000 0x10
+#define ED_3COM_PCFR_CC000 0x20
+#define ED_3COM_PCFR_D8000 0x40
+#define ED_3COM_PCFR_DC000 0x80
+
+/*
+ * GA Configuration Register. Gate-Array Configuration Register.
+ */
+#define ED_3COM_GACFR 5
+
+/*
+ * mbs2 mbs1 mbs0 start address
+ * 0 0 0 0x0000
+ * 0 0 1 0x2000
+ * 0 1 0 0x4000
+ * 0 1 1 0x6000
+ *
+ * Note that with adapters with only 8K, the setting for 0x2000 must
+ * always be used.
+ */
+#define ED_3COM_GACFR_MBS0 0x01
+#define ED_3COM_GACFR_MBS1 0x02
+#define ED_3COM_GACFR_MBS2 0x04
+
+#define ED_3COM_GACFR_RSEL 0x08 /* enable shared memory */
+#define ED_3COM_GACFR_TEST 0x10 /* for GA testing */
+#define ED_3COM_GACFR_OWS 0x20 /* select 0WS access to GA */
+#define ED_3COM_GACFR_TCM 0x40 /* Mask DMA interrupts */
+#define ED_3COM_GACFR_NIM 0x80 /* Mask NIC interrupts */
+
+/*
+ * Control Register. Miscellaneous control functions.
+ */
+#define ED_3COM_CR 6
+
+#define ED_3COM_CR_RST 0x01 /* Reset GA and NIC */
+#define ED_3COM_CR_XSEL 0x02 /* Transceiver select. BNC=1(def) AUI=0 */
+#define ED_3COM_CR_EALO 0x04 /* window EA PROM 0-15 to I/O base */
+#define ED_3COM_CR_EAHI 0x08 /* window EA PROM 16-31 to I/O base */
+#define ED_3COM_CR_SHARE 0x10 /* select interrupt sharing option */
+#define ED_3COM_CR_DBSEL 0x20 /* Double buffer select */
+#define ED_3COM_CR_DDIR 0x40 /* DMA direction select */
+#define ED_3COM_CR_START 0x80 /* Start DMA controller */
+
+/*
+ * Status Register. Miscellaneous status information.
+ */
+#define ED_3COM_STREG 7
+
+#define ED_3COM_STREG_REV 0x07 /* GA revision */
+#define ED_3COM_STREG_DIP 0x08 /* DMA in progress */
+#define ED_3COM_STREG_DTC 0x10 /* DMA terminal count */
+#define ED_3COM_STREG_OFLW 0x20 /* Overflow */
+#define ED_3COM_STREG_UFLW 0x40 /* Underflow */
+#define ED_3COM_STREG_DPRDY 0x80 /* Data port ready */
+
+/*
+ * Interrupt/DMA Configuration Register
+ */
+#define ED_3COM_IDCFR 8
+
+#define ED_3COM_IDCFR_DRQ0 0x01 /* DMA request 1 select */
+#define ED_3COM_IDCFR_DRQ1 0x02 /* DMA request 2 select */
+#define ED_3COM_IDCFR_DRQ2 0x04 /* DMA request 3 select */
+#define ED_3COM_IDCFR_UNUSED 0x08 /* not used */
+#define ED_3COM_IDCFR_IRQ2 0x10 /* Interrupt request 2 select */
+#define ED_3COM_IDCFR_IRQ3 0x20 /* Interrupt request 3 select */
+#define ED_3COM_IDCFR_IRQ4 0x40 /* Interrupt request 4 select */
+#define ED_3COM_IDCFR_IRQ5 0x80 /* Interrupt request 5 select */
+
+/*
+ * DMA Address Register MSB
+ */
+#define ED_3COM_DAMSB 9
+
+/*
+ * DMA Address Register LSB
+ */
+#define ED_3COM_DALSB 0x0a
+
+/*
+ * Vector Pointer Register 2
+ */
+#define ED_3COM_VPTR2 0x0b
+
+/*
+ * Vector Pointer Register 1
+ */
+#define ED_3COM_VPTR1 0x0c
+
+/*
+ * Vector Pointer Register 0
+ */
+#define ED_3COM_VPTR0 0x0d
+
+/*
+ * Register File Access MSB
+ */
+#define ED_3COM_RFMSB 0x0e
+
+/*
+ * Register File Access LSB
+ */
+#define ED_3COM_RFLSB 0x0f
+
+/*
+ * Definitions for Novell NE1000/2000 boards
+ */
+
+/*
+ * Board type codes
+ */
+#define ED_TYPE_NE1000 0x01
+#define ED_TYPE_NE2000 0x02
+
+/*
+ * Register offsets/total
+ */
+#define ED_NOVELL_NIC_OFFSET 0x00
+#define ED_NOVELL_ASIC_OFFSET 0x10
+#define ED_NOVELL_IO_PORTS 32
+
+/*
+ * Remote DMA data register; for reading or writing to the NIC mem
+ * via programmed I/O (offset from ASIC base)
+ */
+#define ED_NOVELL_DATA 0x00
+
+/*
+ * Reset register; reading from this register causes a board reset
+ */
+#define ED_NOVELL_RESET 0x0f
+
+#define ED_TYPE_PCMCIA 0x01
+#define ED_PCMCIA_PAGE_OFFSET 0x40 /* True for all cards I have seen */
+#define ED_PCMCIA_IO_PORTS 32
+#define ED_PCMCIA_RESET 0xF /* Reset port */
diff --git a/usr.sbin/pccard/misc/sys/pccard/lkm_ed.c b/usr.sbin/pccard/misc/sys/pccard/lkm_ed.c
new file mode 100644
index 0000000..7bc1ec7
--- /dev/null
+++ b/usr.sbin/pccard/misc/sys/pccard/lkm_ed.c
@@ -0,0 +1,143 @@
+/*
+ * Loadable kernel module if_ed driver
+ * 11 July 1995 Andrew McRae
+ *
+ *-------------------------------------------------------------------------
+ *
+ * Copyright (c) 1995 Andrew McRae. 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. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/conf.h>
+#include <sys/mount.h>
+#include <sys/sysent.h>
+#include <sys/exec.h>
+#include <sys/lkm.h>
+#include <sys/errno.h>
+
+#include <i386/isa/isa.h>
+#include <i386/isa/isa_device.h>
+
+#include <pccard/card.h>
+#include <pccard/slot.h>
+
+/*
+ * This defines the lkm_misc module use by modload
+ * to define the module name.
+ */
+ MOD_MISC( "ed")
+
+
+int edintr(struct pccard_dev *); /* Interrupt handler */
+void edunload(struct pccard_dev *); /* Disable driver */
+void edsuspend(struct pccard_dev *); /* Suspend driver */
+int edinit(struct pccard_dev *, int); /* init device */
+
+static struct pccard_drv ed_info =
+ {
+ "ed",
+ edintr,
+ edunload,
+ edsuspend,
+ edinit,
+ 0,
+ &net_imask
+ };
+static int opened; /* Rather minimal device state... */
+
+/*
+ * Module handler that processes loads and unloads.
+ * Once the module is loaded, the add driver routine is called
+ * to register the driver.
+ * If an unload is requested the remove driver routine is
+ * called to deregister the driver before unloading.
+ */
+static int
+ed_handle( lkmtp, cmd)
+struct lkm_table *lkmtp;
+int cmd;
+{
+ int i;
+ struct lkm_misc *args = lkmtp->private.lkm_misc;
+ int err = 0; /* default = success*/
+
+ switch( cmd) {
+ case LKM_E_LOAD:
+
+ /*
+ * Don't load twice! (lkmexists() is exported by kern_lkm.c)
+ */
+ if( lkmexists( lkmtp))
+ return( EEXIST);
+/*
+ * Now register the driver
+ */
+ pccard_add_driver(&ed_info);
+ break; /* Success*/
+/*
+ * Attempt to deregister the driver.
+ */
+ case LKM_E_UNLOAD:
+ pccard_remove_driver(&ed_info);
+ break; /* Success*/
+
+ default: /* we only understand load/unload*/
+ err = EINVAL;
+ break;
+ }
+
+ return( err);
+}
+
+
+/*
+ * External entry point; should generally match name of .o file. The
+ * arguments are always the same for all loaded modules. The "load",
+ * "unload", and "stat" functions in "DISPATCH" will be called under
+ * their respective circumstances unless their value is "nosys". If
+ * called, they are called with the same arguments (cmd is included to
+ * allow the use of a single function, ver is included for version
+ * matching between modules and the kernel loader for the modules).
+ *
+ * Since we expect to link in the kernel and add external symbols to
+ * the kernel symbol name space in a future version, generally all
+ * functions used in the implementation of a particular module should
+ * be static unless they are expected to be seen in other modules or
+ * to resolve unresolved symbols alread existing in the kernel (the
+ * second case is not likely to ever occur).
+ *
+ * The entry point should return 0 unless it is refusing load (in which
+ * case it should return an errno from errno.h).
+ */
+int
+lkm_ed(lkmtp, cmd, ver)
+struct lkm_table *lkmtp;
+int cmd;
+int ver;
+{
+ DISPATCH(lkmtp,cmd,ver,ed_handle,ed_handle,nosys)
+}
diff --git a/usr.sbin/pccard/misc/sys/pccard/sio.c b/usr.sbin/pccard/misc/sys/pccard/sio.c
new file mode 100644
index 0000000..73d0da2
--- /dev/null
+++ b/usr.sbin/pccard/misc/sys/pccard/sio.c
@@ -0,0 +1,2463 @@
+/*-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)com.c 7.5 (Berkeley) 5/16/91
+ * $Id: sio.c,v 1.95 1995/04/15 21:45:16 bde Exp $
+ */
+
+#include "sio.h"
+#if NSIO > 0
+/*
+ * Serial driver, based on 386BSD-0.1 com driver.
+ * Mostly rewritten to use pseudo-DMA.
+ * Works for National Semiconductor NS8250-NS16550AF UARTs.
+ * COM driver, based on HP dca driver.
+ *
+ * Changes for PC-Card integration:
+ * - Added PC-Card driver table and handlers
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/reboot.h>
+#include <sys/ioctl.h>
+#define TTYDEFCHARS /* XXX TK2.0 */
+#include <sys/tty.h>
+#undef TTYDEFCHARS
+#include <sys/proc.h>
+#include <sys/user.h>
+#include <sys/conf.h>
+#include <sys/dkstat.h>
+#include <sys/file.h>
+#include <sys/uio.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/syslog.h>
+#include <sys/devconf.h>
+
+#include <machine/clock.h>
+
+#include <i386/isa/icu.h> /* XXX just to get at `imen' */
+#include <i386/isa/isa.h>
+#include <i386/isa/isa_device.h>
+#include <i386/isa/sioreg.h>
+#include <i386/isa/ic/ns16550.h>
+
+#include "crd.h"
+#if NCRD > 0
+#include <pccard/card.h>
+#include <pccard/slot.h>
+#endif /* NCRD > 0 */
+
+/*
+ * XXX temporary kludges for 2.0 (XXX TK2.0).
+ */
+#define TSA_CARR_ON(tp) ((void *)&(tp)->t_rawq)
+#define TSA_OCOMPLETE(tp) ((void *)&(tp)->t_outq)
+#define TSA_OLOWAT(tp) ((void *)&(tp)->t_outq)
+void
+termioschars(t)
+ struct termios *t;
+{
+
+ bcopy(ttydefchars, t->c_cc, sizeof t->c_cc);
+}
+
+#define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */
+#define RB_I_HIGH_WATER (TTYHOG - 2 * RS_IBUFSIZE)
+#define RS_IBUFSIZE 256
+
+#define CALLOUT_MASK 0x80
+#define CONTROL_MASK 0x60
+#define CONTROL_INIT_STATE 0x20
+#define CONTROL_LOCK_STATE 0x40
+#define DEV_TO_UNIT(dev) (MINOR_TO_UNIT(minor(dev)))
+#define MINOR_MAGIC_MASK (CALLOUT_MASK | CONTROL_MASK)
+#define MINOR_TO_UNIT(mynor) ((mynor) & ~MINOR_MAGIC_MASK)
+
+#ifdef COM_MULTIPORT
+/* checks in flags for multiport and which is multiport "master chip"
+ * for a given card
+ */
+#define COM_ISMULTIPORT(dev) ((dev)->id_flags & 0x01)
+#define COM_MPMASTER(dev) (((dev)->id_flags >> 8) & 0x0ff)
+#define COM_NOTAST4(dev) ((dev)->id_flags & 0x04)
+#endif /* COM_MULTIPORT */
+
+#define COM_NOFIFO(dev) ((dev)->id_flags & 0x02)
+#define COM_VERBOSE(dev) ((dev)->id_flags & 0x80)
+
+#define com_scr 7 /* scratch register for 16450-16550 (R/W) */
+
+/*
+ * Input buffer watermarks.
+ * The external device is asked to stop sending when the buffer exactly reaches
+ * high water, or when the high level requests it.
+ * The high level is notified immediately (rather than at a later clock tick)
+ * when this watermark is reached.
+ * The buffer size is chosen so the watermark should almost never be reached.
+ * The low watermark is invisibly 0 since the buffer is always emptied all at
+ * once.
+ */
+#define RS_IHIGHWATER (3 * RS_IBUFSIZE / 4)
+
+/*
+ * com state bits.
+ * (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher
+ * than the other bits so that they can be tested as a group without masking
+ * off the low bits.
+ *
+ * The following com and tty flags correspond closely:
+ * CS_BUSY = TS_BUSY (maintained by comstart() and comflush())
+ * CS_TTGO = ~TS_TTSTOP (maintained by comstart() and siostop())
+ * CS_CTS_OFLOW = CCTS_OFLOW (maintained by comparam())
+ * CS_RTS_IFLOW = CRTS_IFLOW (maintained by comparam())
+ * TS_FLUSH is not used.
+ * XXX I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON.
+ * XXX CS_*FLOW should be CF_*FLOW in com->flags (control flags not state).
+ */
+#define CS_BUSY 0x80 /* output in progress */
+#define CS_TTGO 0x40 /* output not stopped by XOFF */
+#define CS_ODEVREADY 0x20 /* external device h/w ready (CTS) */
+#define CS_CHECKMSR 1 /* check of MSR scheduled */
+#define CS_CTS_OFLOW 2 /* use CTS output flow control */
+#define CS_DTR_OFF 0x10 /* DTR held off */
+#define CS_ODONE 4 /* output completed */
+#define CS_RTS_IFLOW 8 /* use RTS input flow control */
+
+static char const * const error_desc[] = {
+#define CE_OVERRUN 0
+ "silo overflow",
+#define CE_INTERRUPT_BUF_OVERFLOW 1
+ "interrupt-level buffer overflow",
+#define CE_TTY_BUF_OVERFLOW 2
+ "tty-level buffer overflow",
+};
+
+#define CE_NTYPES 3
+#define CE_RECORD(com, errnum) (++(com)->delta_error_counts[errnum])
+
+/* types. XXX - should be elsewhere */
+typedef u_int Port_t; /* hardware port */
+typedef u_char bool_t; /* boolean */
+
+/* com device structure */
+struct com_s {
+ u_char state; /* miscellaneous flag bits */
+ bool_t active_out; /* nonzero if the callout device is open */
+ u_char cfcr_image; /* copy of value written to CFCR */
+ u_char ftl; /* current rx fifo trigger level */
+ u_char ftl_init; /* ftl_max for next open() */
+ u_char ftl_max; /* maximum ftl for curent open() */
+ bool_t hasfifo; /* nonzero for 16550 UARTs */
+ u_char mcr_image; /* copy of value written to MCR */
+#ifdef COM_MULTIPORT
+ bool_t multiport; /* is this unit part of a multiport device? */
+#endif /* COM_MULTIPORT */
+ bool_t no_irq; /* nonzero if irq is not attached */
+ bool_t poll; /* nonzero if polling is required */
+ int unit; /* unit number */
+ int dtr_wait; /* time to hold DTR down on close (* 1/hz) */
+ u_int tx_fifo_size;
+ u_int wopeners; /* # processes waiting for DCD in open() */
+
+ /*
+ * The high level of the driver never reads status registers directly
+ * because there would be too many side effects to handle conveniently.
+ * Instead, it reads copies of the registers stored here by the
+ * interrupt handler.
+ */
+ u_char last_modem_status; /* last MSR read by intr handler */
+ u_char prev_modem_status; /* last MSR handled by high level */
+
+ u_char hotchar; /* ldisc-specific char to be handled ASAP */
+ u_char *ibuf; /* start of input buffer */
+ u_char *ibufend; /* end of input buffer */
+ u_char *ihighwater; /* threshold in input buffer */
+ u_char *iptr; /* next free spot in input buffer */
+
+ u_char *obufend; /* end of output buffer */
+ u_char *optr; /* next char to output */
+
+ Port_t data_port; /* i/o ports */
+ Port_t int_id_port;
+ Port_t iobase;
+ Port_t modem_ctl_port;
+ Port_t line_status_port;
+ Port_t modem_status_port;
+
+ struct tty *tp; /* cross reference */
+
+ /* Initial state. */
+ struct termios it_in; /* should be in struct tty */
+ struct termios it_out;
+
+ /* Lock state. */
+ struct termios lt_in; /* should be in struct tty */
+ struct termios lt_out;
+
+ bool_t do_timestamp;
+ struct timeval timestamp;
+
+ u_long bytes_in; /* statistics */
+ u_long bytes_out;
+ u_int delta_error_counts[CE_NTYPES];
+ u_long error_counts[CE_NTYPES];
+
+ /*
+ * Ping-pong input buffers. The extra factor of 2 in the sizes is
+ * to allow for an error byte for each input byte.
+ */
+#define CE_INPUT_OFFSET RS_IBUFSIZE
+ u_char ibuf1[2 * RS_IBUFSIZE];
+ u_char ibuf2[2 * RS_IBUFSIZE];
+
+ /*
+ * Output buffer. Someday we should avoid copying. Twice.
+ */
+ u_char obuf[256];
+};
+
+/*
+ * The public functions in the com module ought to be declared in a com-driver
+ * system header.
+ */
+
+/* Interrupt handling entry points. */
+void siointr __P((int unit));
+void siopoll __P((void));
+
+/* Device switch entry points. */
+int sioopen __P((dev_t dev, int oflags, int devtype,
+ struct proc *p));
+int sioclose __P((dev_t dev, int fflag, int devtype,
+ struct proc *p));
+int sioread __P((dev_t dev, struct uio *uio, int ioflag));
+int siowrite __P((dev_t dev, struct uio *uio, int ioflag));
+int sioioctl __P((dev_t dev, int cmd, caddr_t data,
+ int fflag, struct proc *p));
+void siostop __P((struct tty *tp, int rw));
+#define sioreset noreset
+int sioselect __P((dev_t dev, int rw, struct proc *p));
+#define siommap nommap
+#define siostrategy nostrategy
+
+/* Console device entry points. */
+int siocncheckc __P((dev_t dev));
+int siocngetc __P((dev_t dev));
+struct consdev;
+void siocninit __P((struct consdev *cp));
+void siocnprobe __P((struct consdev *cp));
+void siocnputc __P((dev_t dev, int c));
+
+static int sioattach __P((struct isa_device *dev));
+static timeout_t siodtrwakeup;
+static void comflush __P((struct com_s *com));
+static void comhardclose __P((struct com_s *com));
+static void siointr1 __P((struct com_s *com));
+static void commctl __P((struct com_s *com, int bits, int how));
+static int comparam __P((struct tty *tp, struct termios *t));
+static int sioprobe __P((struct isa_device *dev));
+static void sioregisterdev __P((struct isa_device *id));
+static void comstart __P((struct tty *tp));
+static timeout_t comwakeup;
+static int tiocm_xxx2mcr __P((int tiocm_xxx));
+static void disc_optim __P((struct tty *tp, struct termios *t, struct com_s *com));
+
+#ifdef DSI_SOFT_MODEM
+static int LoadSoftModem __P((int unit,int base_io, u_long size, u_char *ptr));
+#endif /* DSI_SOFT_MODEM */
+
+/* table and macro for fast conversion from a unit number to its com struct */
+static struct com_s *p_com_addr[NSIO];
+#define com_addr(unit) (p_com_addr[unit])
+
+static struct timeval intr_timestamp;
+
+struct isa_driver siodriver = {
+ sioprobe, sioattach, "sio"
+};
+
+#ifdef COMCONSOLE
+#undef COMCONSOLE
+#define COMCONSOLE 1
+#else
+#define COMCONSOLE 0
+#endif
+
+static int comconsole = CONUNIT;
+static speed_t comdefaultrate = TTYDEF_SPEED;
+static u_int com_events; /* input chars + weighted output completions */
+static int commajor;
+#if 0 /* XXX TK2.0 */
+struct tty *sio_tty[NSIO];
+#else
+struct tty sio_tty[NSIO];
+#endif
+
+#ifdef KGDB
+#include <machine/remote-sl.h>
+
+extern int kgdb_dev;
+extern int kgdb_rate;
+extern int kgdb_debug_init;
+#endif
+
+static struct speedtab comspeedtab[] = {
+ 0, 0,
+ 50, COMBRD(50),
+ 75, COMBRD(75),
+ 110, COMBRD(110),
+ 134, COMBRD(134),
+ 150, COMBRD(150),
+ 200, COMBRD(200),
+ 300, COMBRD(300),
+ 600, COMBRD(600),
+ 1200, COMBRD(1200),
+ 1800, COMBRD(1800),
+ 2400, COMBRD(2400),
+ 4800, COMBRD(4800),
+ 9600, COMBRD(9600),
+ 19200, COMBRD(19200),
+ 38400, COMBRD(38400),
+ 57600, COMBRD(57600),
+ 115200, COMBRD(115200),
+ -1, -1
+};
+
+/* XXX - configure this list */
+static Port_t likely_com_ports[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, };
+
+static struct kern_devconf kdc_sio[NSIO] = { {
+ 0, 0, 0, /* filled in by dev_attach */
+ "sio", 0, { MDDT_ISA, 0, "tty" },
+ isa_generic_externalize, 0, 0, ISA_EXTERNALLEN,
+ &kdc_isa0, /* parent */
+ 0, /* parentdata */
+ DC_UNCONFIGURED, /* state */
+ "RS-232 serial port",
+ DC_CLS_SERIAL /* class */
+} };
+#if NCRD > 0
+/*
+ * PC-Card (PCMCIA) specific code.
+ */
+static int card_intr(struct pccard_dev *); /* Interrupt handler */
+void siounload(struct pccard_dev *); /* Disable driver */
+void siosuspend(struct pccard_dev *); /* Suspend driver */
+static int sioinit(struct pccard_dev *, int); /* init device */
+
+static struct pccard_drv sio_info =
+ {
+ "sio",
+ card_intr,
+ siounload,
+ siosuspend,
+ sioinit,
+ 0, /* Attributes - presently unused */
+ &tty_imask /* Interrupt mask for device */
+ /* This should also include net_imask?? */
+ };
+/*
+ * Called when a power down is wanted. Shuts down the
+ * device and configures the device as unavailable (but
+ * still loaded...). A resume is done by calling
+ * sioinit with first=0. This is called when the user suspends
+ * the system, or the APM code suspends the system.
+ */
+void
+siosuspend(struct pccard_dev *dp)
+{
+ printf("sio%d: suspending\n", dp->isahd.id_unit);
+}
+/*
+ * Initialize the device - called from Slot manager.
+ * if first is set, then initially check for
+ * the device's existence before initialising it.
+ * Once initialised, the device table may be set up.
+ */
+int
+sioinit(struct pccard_dev *dp, int first)
+{
+/*
+ * validate unit number.
+ */
+ if (first)
+ {
+ if (dp->isahd.id_unit >= NSIO)
+ return(ENODEV);
+/*
+ * Make sure it isn't already probed.
+ */
+ if (com_addr(dp->isahd.id_unit))
+ return(EBUSY);
+/*
+ * Probe the device. If a value is returned, the
+ * device was found at the location.
+ */
+ if (sioprobe(&dp->isahd)==0)
+ return(ENXIO);
+ if (sioattach(&dp->isahd)==0)
+ return(ENXIO);
+ }
+/*
+ * XXX TODO:
+ * If it was already inited before, the device structure
+ * should be already initialised. Here we should
+ * reset (and possibly restart) the hardware, but
+ * I am not sure of the best way to do this...
+ */
+ return(0);
+}
+/*
+ * siounload - unload the driver and clear the table.
+ * XXX TODO:
+ * This is called usually when the card is ejected, but
+ * can be caused by the modunload of a controller driver.
+ * The idea is reset the driver's view of the device
+ * and ensure that any driver entry points such as
+ * read and write do not hang.
+ */
+void
+siounload(struct pccard_dev *dp)
+{
+ printf("sio%d: unload\n", dp->isahd.id_unit);
+}
+
+/*
+ * card_intr - Shared interrupt called from
+ * front end of PC-Card handler.
+ */
+static int
+card_intr(struct pccard_dev *dp)
+{
+ siointr1(com_addr(dp->isahd.id_unit));
+ return(1);
+}
+#endif /* NCRD > 0 */
+
+static void
+sioregisterdev(id)
+ struct isa_device *id;
+{
+ int unit;
+
+ unit = id->id_unit;
+/*
+ * If already registered, don't try to re-register.
+ */
+ if (kdc_sio[unit].kdc_isa)
+ return;
+ if (unit != 0)
+ kdc_sio[unit] = kdc_sio[0];
+ kdc_sio[unit].kdc_unit = unit;
+ kdc_sio[unit].kdc_isa = id;
+ dev_attach(&kdc_sio[unit]);
+}
+
+static int
+sioprobe(dev)
+ struct isa_device *dev;
+{
+ static bool_t already_init;
+ Port_t *com_ptr;
+ bool_t failures[10];
+ int fn;
+ struct isa_device *idev;
+ Port_t iobase;
+ u_char mcr_image;
+ int result;
+
+ sioregisterdev(dev);
+
+ if (!already_init) {
+ /*
+ * Turn off MCR_IENABLE for all likely serial ports. An unused
+ * port with its MCR_IENABLE gate open will inhibit interrupts
+ * from any used port that shares the interrupt vector.
+ * XXX the gate enable is elsewhere for some multiports.
+ */
+ for (com_ptr = likely_com_ports;
+ com_ptr < &likely_com_ports[sizeof likely_com_ports
+ / sizeof likely_com_ports[0]];
+ ++com_ptr)
+ outb(*com_ptr + com_mcr, 0);
+#if NCRD > 0
+/*
+ * If PC-Card probe required, then register driver with
+ * slot manager.
+ */
+ pccard_add_driver(&sio_info);
+#endif /* NCRD > 0 */
+ already_init = TRUE;
+ }
+
+ /*
+ * If the device is on a multiport card and has an AST/4
+ * compatible interrupt control register, initialize this
+ * register and prepare to leave MCR_IENABLE clear in the mcr.
+ * Otherwise, prepare to set MCR_IENABLE in the mcr.
+ * Point idev to the device struct giving the correct id_irq.
+ * This is the struct for the master device if there is one.
+ */
+ idev = dev;
+ mcr_image = MCR_IENABLE;
+#ifdef COM_MULTIPORT
+ if (COM_ISMULTIPORT(dev)) {
+ idev = find_isadev(isa_devtab_tty, &siodriver,
+ COM_MPMASTER(dev));
+ if (idev == NULL) {
+ printf("sio%d: master device %d not configured\n",
+ dev->id_unit, COM_MPMASTER(dev));
+ return (0);
+ }
+ if (!COM_NOTAST4(dev)) {
+ outb(idev->id_iobase + com_scr,
+ idev->id_irq ? 0x80 : 0);
+ mcr_image = 0;
+ }
+ }
+#endif /* COM_MULTIPORT */
+ if (idev->id_irq == 0)
+ mcr_image = 0;
+
+ bzero(failures, sizeof failures);
+ iobase = dev->id_iobase;
+
+ /*
+ * We don't want to get actual interrupts, just masked ones.
+ * Interrupts from this line should already be masked in the ICU,
+ * but mask them in the processor as well in case there are some
+ * (misconfigured) shared interrupts.
+ */
+ disable_intr();
+/* EXTRA DELAY? */
+
+ /*
+ * XXX DELAY() reenables CPU interrupts. This is a problem for
+ * shared interrupts after the first device using one has been
+ * successfully probed - config_isadev() has enabled the interrupt
+ * in the ICU.
+ */
+ outb(IO_ICU1 + 1, 0xff);
+
+ /*
+ * Initialize the speed and the word size and wait long enough to
+ * drain the maximum of 16 bytes of junk in device output queues.
+ * The speed is undefined after a master reset and must be set
+ * before relying on anything related to output. There may be
+ * junk after a (very fast) soft reboot and (apparently) after
+ * master reset.
+ * XXX what about the UART bug avoided by waiting in comparam()?
+ * We don't want to to wait long enough to drain at 2 bps.
+ */
+ outb(iobase + com_cfcr, CFCR_DLAB);
+ outb(iobase + com_dlbl, COMBRD(9600) & 0xff);
+ outb(iobase + com_dlbh, (u_int) COMBRD(9600) >> 8);
+ outb(iobase + com_cfcr, CFCR_8BITS);
+ DELAY((16 + 1) * 1000000 / (9600 / 10));
+
+ /*
+ * Enable the interrupt gate and disable device interupts. This
+ * should leave the device driving the interrupt line low and
+ * guarantee an edge trigger if an interrupt can be generated.
+ */
+/* EXTRA DELAY? */
+ outb(iobase + com_mcr, mcr_image);
+ outb(iobase + com_ier, 0);
+
+ /*
+ * Attempt to set loopback mode so that we can send a null byte
+ * without annoying any external device.
+ */
+/* EXTRA DELAY? */
+ outb(iobase + com_mcr, mcr_image | MCR_LOOPBACK);
+
+ /*
+ * Attempt to generate an output interrupt. On 8250's, setting
+ * IER_ETXRDY generates an interrupt independent of the current
+ * setting and independent of whether the THR is empty. On 16450's,
+ * setting IER_ETXRDY generates an interrupt independent of the
+ * current setting. On 16550A's, setting IER_ETXRDY only
+ * generates an interrupt when IER_ETXRDY is not already set.
+ */
+ outb(iobase + com_ier, IER_ETXRDY);
+
+ /*
+ * On some 16x50 incompatibles, setting IER_ETXRDY doesn't generate
+ * an interrupt. They'd better generate one for actually doing
+ * output. Loopback may be broken on the same incompatibles but
+ * it's unlikely to do more than allow the null byte out.
+ */
+ outb(iobase + com_data, 0);
+ DELAY((1 + 2) * 1000000 / (9600 / 10));
+
+ /*
+ * Turn off loopback mode so that the interrupt gate works again
+ * (MCR_IENABLE was hidden). This should leave the device driving
+ * an interrupt line high. It doesn't matter if the interrupt
+ * line oscillates while we are not looking at it, since interrupts
+ * are disabled.
+ */
+/* EXTRA DELAY? */
+ outb(iobase + com_mcr, mcr_image);
+
+ /*
+ * Check that
+ * o the CFCR, IER and MCR in UART hold the values written to them
+ * (the values happen to be all distinct - this is good for
+ * avoiding false positive tests from bus echoes).
+ * o an output interrupt is generated and its vector is correct.
+ * o the interrupt goes away when the IIR in the UART is read.
+ */
+/* EXTRA DELAY? */
+ failures[0] = inb(iobase + com_cfcr) - CFCR_8BITS;
+ failures[1] = inb(iobase + com_ier) - IER_ETXRDY;
+ failures[2] = inb(iobase + com_mcr) - mcr_image;
+ if (idev->id_irq != 0)
+ failures[3] = isa_irq_pending(idev) ? 0 : 1;
+ failures[4] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_TXRDY;
+ if (idev->id_irq != 0)
+ failures[5] = isa_irq_pending(idev) ? 1 : 0;
+ failures[6] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND;
+
+ /*
+ * Turn off all device interrupts and check that they go off properly.
+ * Leave MCR_IENABLE alone. For ports without a master port, it gates
+ * the OUT2 output of the UART to
+ * the ICU input. Closing the gate would give a floating ICU input
+ * (unless there is another device driving at) and spurious interrupts.
+ * (On the system that this was first tested on, the input floats high
+ * and gives a (masked) interrupt as soon as the gate is closed.)
+ */
+ outb(iobase + com_ier, 0);
+ outb(iobase + com_cfcr, CFCR_8BITS); /* dummy to avoid bus echo */
+ failures[7] = inb(iobase + com_ier);
+ if (idev->id_irq != 0)
+ failures[8] = isa_irq_pending(idev) ? 1 : 0;
+ failures[9] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND;
+
+ outb(IO_ICU1 + 1, imen); /* XXX */
+ enable_intr();
+
+ result = IO_COMSIZE;
+ for (fn = 0; fn < sizeof failures; ++fn)
+ if (failures[fn]) {
+ outb(iobase + com_mcr, 0);
+ result = 0;
+ if (COM_VERBOSE(dev))
+ printf("sio%d: probe test %d failed\n",
+ dev->id_unit, fn);
+ }
+ return (result);
+}
+
+static int
+sioattach(isdp)
+ struct isa_device *isdp;
+{
+ struct com_s *com;
+ static bool_t comwakeup_started = FALSE;
+ Port_t iobase;
+ int s;
+ int unit;
+
+ isdp->id_ri_flags |= RI_FAST;
+ iobase = isdp->id_iobase;
+ unit = isdp->id_unit;
+ com = malloc(sizeof *com, M_TTYS, M_NOWAIT);
+ if (com == NULL)
+ return (0);
+
+ /*
+ * sioprobe() has initialized the device registers as follows:
+ * o cfcr = CFCR_8BITS.
+ * It is most important that CFCR_DLAB is off, so that the
+ * data port is not hidden when we enable interrupts.
+ * o ier = 0.
+ * Interrupts are only enabled when the line is open.
+ * o mcr = MCR_IENABLE, or 0 if the port has AST/4 compatible
+ * interrupt control register or the config specifies no irq.
+ * Keeping MCR_DTR and MCR_RTS off might stop the external
+ * device from sending before we are ready.
+ */
+ bzero(com, sizeof *com);
+ com->unit = unit;
+ com->cfcr_image = CFCR_8BITS;
+ com->dtr_wait = 3 * hz;
+ com->no_irq = isdp->id_irq == 0;
+ com->tx_fifo_size = 1;
+ com->iptr = com->ibuf = com->ibuf1;
+ com->ibufend = com->ibuf1 + RS_IBUFSIZE;
+ com->ihighwater = com->ibuf1 + RS_IHIGHWATER;
+ com->iobase = iobase;
+ com->data_port = iobase + com_data;
+ com->int_id_port = iobase + com_iir;
+ com->modem_ctl_port = iobase + com_mcr;
+ com->mcr_image = inb(com->modem_ctl_port);
+ com->line_status_port = iobase + com_lsr;
+ com->modem_status_port = iobase + com_msr;
+
+ /*
+ * We don't use all the flags from <sys/ttydefaults.h> since they
+ * are only relevant for logins. It's important to have echo off
+ * initially so that the line doesn't start blathering before the
+ * echo flag can be turned off.
+ */
+ com->it_in.c_iflag = 0;
+ com->it_in.c_oflag = 0;
+ com->it_in.c_cflag = TTYDEF_CFLAG;
+ com->it_in.c_lflag = 0;
+ if (unit == comconsole && (COMCONSOLE || boothowto & RB_SERIAL)) {
+ com->it_in.c_iflag = TTYDEF_IFLAG;
+ com->it_in.c_oflag = TTYDEF_OFLAG;
+ com->it_in.c_cflag = TTYDEF_CFLAG | CLOCAL;
+ com->it_in.c_lflag = TTYDEF_LFLAG;
+ com->lt_out.c_cflag = com->lt_in.c_cflag = CLOCAL;
+ }
+ termioschars(&com->it_in);
+ com->it_in.c_ispeed = com->it_in.c_ospeed = comdefaultrate;
+ com->it_out = com->it_in;
+
+ /* attempt to determine UART type */
+ printf("sio%d: type", unit);
+
+#ifdef DSI_SOFT_MODEM
+ if((inb(iobase+7) ^ inb(iobase+7)) & 0x80) {
+ printf(" Digicom Systems, Inc. SoftModem");
+ kdc_sio[unit].kdc_description =
+ "Serial port: Digicom Systems SoftModem";
+ goto determined_type;
+ }
+#endif /* DSI_SOFT_MODEM */
+
+#ifdef COM_MULTIPORT
+ if (!COM_ISMULTIPORT(isdp))
+#endif
+ {
+ u_char scr;
+ u_char scr1;
+ u_char scr2;
+
+ scr = inb(iobase + com_scr);
+ outb(iobase + com_scr, 0xa5);
+ scr1 = inb(iobase + com_scr);
+ outb(iobase + com_scr, 0x5a);
+ scr2 = inb(iobase + com_scr);
+ outb(iobase + com_scr, scr);
+ if (scr1 != 0xa5 || scr2 != 0x5a) {
+ printf(" 8250");
+ kdc_sio[unit].kdc_description =
+ "Serial port: National 8250 or compatible";
+ goto determined_type;
+ }
+ }
+ outb(iobase + com_fifo, FIFO_ENABLE | FIFO_TRIGGER_14);
+ DELAY(100);
+ switch (inb(com->int_id_port) & IIR_FIFO_MASK) {
+ case FIFO_TRIGGER_1:
+ printf(" 16450");
+ kdc_sio[unit].kdc_description =
+ "Serial port: National 16450 or compatible";
+ break;
+ case FIFO_TRIGGER_4:
+ printf(" 16450?");
+ kdc_sio[unit].kdc_description =
+ "Serial port: maybe National 16450";
+ break;
+ case FIFO_TRIGGER_8:
+ printf(" 16550?");
+ kdc_sio[unit].kdc_description =
+ "Serial port: maybe National 16550";
+ break;
+ case FIFO_TRIGGER_14:
+ printf(" 16550A");
+ if (COM_NOFIFO(isdp)) {
+ printf(" fifo disabled");
+ kdc_sio[unit].kdc_description =
+ "Serial port: National 16550A, FIFO disabled";
+ } else {
+ com->hasfifo = TRUE;
+ com->ftl_init = FIFO_TRIGGER_14;
+ com->tx_fifo_size = 16;
+ kdc_sio[unit].kdc_description =
+ "Serial port: National 16550A or compatible";
+ }
+ break;
+ }
+ outb(iobase + com_fifo, 0);
+determined_type: ;
+
+#ifdef COM_MULTIPORT
+ if (COM_ISMULTIPORT(isdp)) {
+ com->multiport = TRUE;
+ printf(" (multiport");
+ if (unit == COM_MPMASTER(isdp))
+ printf(" master");
+ printf(")");
+ com->no_irq = find_isadev(isa_devtab_tty, &siodriver,
+ COM_MPMASTER(isdp))->id_irq == 0;
+ }
+#endif /* COM_MULTIPORT */
+ printf("\n");
+
+ kdc_sio[unit].kdc_state =
+ (unit == comconsole && (COMCONSOLE || boothowto & RB_SERIAL))
+ ? DC_BUSY : DC_IDLE;
+
+#ifdef KGDB
+ if (kgdb_dev == makedev(commajor, unit)) {
+ if (unit == comconsole && (COMCONSOLE || boothowto & RB_SERIAL))
+ kgdb_dev = -1; /* can't debug over console port */
+ else {
+ int divisor;
+
+ /*
+ * XXX now unfinished and broken. Need to do
+ * something more like a full open(). There's no
+ * suitable interrupt handler so don't enable device
+ * interrupts. Watch out for null tp's.
+ */
+ outb(iobase + com_cfcr, CFCR_DLAB);
+ divisor = ttspeedtab(kgdb_rate, comspeedtab);
+ outb(iobase + com_dlbl, divisor & 0xFF);
+ outb(iobase + com_dlbh, (u_int) divisor >> 8);
+ outb(iobase + com_cfcr, CFCR_8BITS);
+ outb(com->modem_status_port,
+ com->mcr_image |= MCR_DTR | MCR_RTS);
+
+ if (kgdb_debug_init) {
+ /*
+ * Print prefix of device name,
+ * let kgdb_connect print the rest.
+ */
+ printf("sio%d: ", unit);
+ kgdb_connect(1);
+ } else
+ printf("sio%d: kgdb enabled\n", unit);
+ }
+ }
+#endif
+
+ s = spltty();
+ com_addr(unit) = com;
+ splx(s);
+ if (!comwakeup_started) {
+ comwakeup((void *)NULL);
+ comwakeup_started = TRUE;
+ }
+ return (1);
+}
+
+/* ARGSUSED */
+int
+sioopen(dev, flag, mode, p)
+ dev_t dev;
+ int flag;
+ int mode;
+ struct proc *p;
+{
+ struct com_s *com;
+ int error;
+ Port_t iobase;
+ int mynor;
+ int s;
+ struct tty *tp;
+ int unit;
+
+ mynor = minor(dev);
+ unit = MINOR_TO_UNIT(mynor);
+ if ((u_int) unit >= NSIO || (com = com_addr(unit)) == NULL)
+ return (ENXIO);
+ if (mynor & CONTROL_MASK)
+ return (0);
+#if 0 /* XXX TK2.0 */
+ tp = com->tp = sio_tty[unit] = ttymalloc(sio_tty[unit]);
+#else
+ tp = com->tp = &sio_tty[unit];
+#endif
+ s = spltty();
+ /*
+ * We jump to this label after all non-interrupted sleeps to pick
+ * up any changes of the device state.
+ */
+open_top:
+ while (com->state & CS_DTR_OFF) {
+ error = tsleep(&com->dtr_wait, TTIPRI | PCATCH, "siodtr", 0);
+ if (error != 0)
+ goto out;
+ }
+ kdc_sio[unit].kdc_state = DC_BUSY;
+ if (tp->t_state & TS_ISOPEN) {
+ /*
+ * The device is open, so everything has been initialized.
+ * Handle conflicts.
+ */
+ if (mynor & CALLOUT_MASK) {
+ if (!com->active_out) {
+ error = EBUSY;
+ goto out;
+ }
+ } else {
+ if (com->active_out) {
+ if (flag & O_NONBLOCK) {
+ error = EBUSY;
+ goto out;
+ }
+ error = tsleep(&com->active_out,
+ TTIPRI | PCATCH, "siobi", 0);
+ if (error != 0)
+ goto out;
+ goto open_top;
+ }
+ }
+ if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) {
+ error = EBUSY;
+ goto out;
+ }
+ } else {
+ /*
+ * The device isn't open, so there are no conflicts.
+ * Initialize it. Initialization is done twice in many
+ * cases: to preempt sleeping callin opens if we are
+ * callout, and to complete a callin open after DCD rises.
+ */
+ tp->t_oproc = comstart;
+ tp->t_param = comparam;
+ tp->t_dev = dev;
+ tp->t_termios = mynor & CALLOUT_MASK
+ ? com->it_out : com->it_in;
+ commctl(com, MCR_DTR | MCR_RTS, DMSET);
+ com->ftl_max = com->ftl_init;
+ com->poll = com->no_irq;
+ ++com->wopeners;
+ error = comparam(tp, &tp->t_termios);
+ --com->wopeners;
+ if (error != 0)
+ goto out;
+ /*
+ * XXX we should goto open_top if comparam() slept.
+ */
+ ttsetwater(tp);
+ iobase = com->iobase;
+ if (com->hasfifo) {
+ /*
+ * (Re)enable and drain fifos.
+ *
+ * Certain SMC chips cause problems if the fifos
+ * are enabled while input is ready. Turn off the
+ * fifo if necessary to clear the input. We test
+ * the input ready bit after enabling the fifos
+ * since we've already enabled them in comparam()
+ * and to handle races between enabling and fresh
+ * input.
+ */
+ while (TRUE) {
+ outb(iobase + com_fifo,
+ FIFO_RCV_RST | FIFO_XMT_RST
+ | FIFO_ENABLE | com->ftl);
+ DELAY(100);
+ if (!(inb(com->line_status_port) & LSR_RXRDY))
+ break;
+ outb(iobase + com_fifo, 0);
+ DELAY(100);
+ (void) inb(com->data_port);
+ }
+ }
+
+ disable_intr();
+ (void) inb(com->line_status_port);
+ (void) inb(com->data_port);
+ com->prev_modem_status =
+ com->last_modem_status = inb(com->modem_status_port);
+ outb(iobase + com_ier, IER_ERXRDY | IER_ETXRDY | IER_ERLS
+ | IER_EMSC);
+ enable_intr();
+ /*
+ * Handle initial DCD. Callout devices get a fake initial
+ * DCD (trapdoor DCD). If we are callout, then any sleeping
+ * callin opens get woken up and resume sleeping on "siobi"
+ * instead of "siodcd".
+ */
+ if (com->prev_modem_status & MSR_DCD || mynor & CALLOUT_MASK)
+ (*linesw[tp->t_line].l_modem)(tp, 1);
+ }
+ /*
+ * Wait for DCD if necessary.
+ */
+ if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK)
+ && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) {
+ ++com->wopeners;
+ error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "siodcd", 0);
+ --com->wopeners;
+ if (error != 0)
+ goto out;
+ goto open_top;
+ }
+ error = (*linesw[tp->t_line].l_open)(dev, tp);
+ disc_optim(tp, &(tp->t_termios), com);
+ if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK)
+ com->active_out = TRUE;
+out:
+ splx(s);
+ if (!(tp->t_state & TS_ISOPEN) && com->wopeners == 0)
+ comhardclose(com);
+ return (error);
+}
+
+/*ARGSUSED*/
+int
+sioclose(dev, flag, mode, p)
+ dev_t dev;
+ int flag;
+ int mode;
+ struct proc *p;
+{
+ struct com_s *com;
+ int mynor;
+ int s;
+ struct tty *tp;
+
+ mynor = minor(dev);
+ if (mynor & CONTROL_MASK)
+ return (0);
+ com = com_addr(MINOR_TO_UNIT(mynor));
+ tp = com->tp;
+ s = spltty();
+ (*linesw[tp->t_line].l_close)(tp, flag);
+ disc_optim(tp, &(tp->t_termios), com);
+ siostop(tp, FREAD | FWRITE);
+ comhardclose(com);
+ ttyclose(tp);
+ splx(s);
+ return (0);
+}
+
+static void
+comhardclose(com)
+ struct com_s *com;
+{
+ Port_t iobase;
+ int s;
+ struct tty *tp;
+ int unit;
+
+ unit = com->unit;
+ iobase = com->iobase;
+ s = spltty();
+ com->poll = FALSE;
+ com->do_timestamp = 0;
+ outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK);
+#ifdef KGDB
+ /* do not disable interrupts or hang up if debugging */
+ if (kgdb_dev != makedev(commajor, unit))
+#endif
+ {
+ outb(iobase + com_ier, 0);
+ tp = com->tp;
+ if (tp->t_cflag & HUPCL
+ /*
+ * XXX we will miss any carrier drop between here and the
+ * next open. Perhaps we should watch DCD even when the
+ * port is closed; it is not sufficient to check it at
+ * the next open because it might go up and down while
+ * we're not watching.
+ */
+ || !com->active_out
+ && !(com->prev_modem_status & MSR_DCD)
+ && !(com->it_in.c_cflag & CLOCAL)
+ || !(tp->t_state & TS_ISOPEN)) {
+ commctl(com, MCR_RTS, DMSET);
+ if (com->dtr_wait != 0) {
+ timeout(siodtrwakeup, com, com->dtr_wait);
+ com->state |= CS_DTR_OFF;
+ }
+ }
+ }
+ com->active_out = FALSE;
+ wakeup(&com->active_out);
+ wakeup(TSA_CARR_ON(tp)); /* restart any wopeners */
+ if (!(com->state & CS_DTR_OFF)
+ && !(unit == comconsole && (COMCONSOLE || boothowto & RB_SERIAL)))
+ kdc_sio[unit].kdc_state = DC_IDLE;
+ splx(s);
+}
+
+int
+sioread(dev, uio, flag)
+ dev_t dev;
+ struct uio *uio;
+ int flag;
+{
+ int mynor;
+ struct tty *tp;
+
+ mynor = minor(dev);
+ if (mynor & CONTROL_MASK)
+ return (ENODEV);
+ tp = com_addr(MINOR_TO_UNIT(mynor))->tp;
+ return ((*linesw[tp->t_line].l_read)(tp, uio, flag));
+}
+
+int
+siowrite(dev, uio, flag)
+ dev_t dev;
+ struct uio *uio;
+ int flag;
+{
+ int mynor;
+ struct tty *tp;
+ int unit;
+
+ mynor = minor(dev);
+ if (mynor & CONTROL_MASK)
+ return (ENODEV);
+
+ unit = MINOR_TO_UNIT(mynor);
+ tp = com_addr(unit)->tp;
+ /*
+ * (XXX) We disallow virtual consoles if the physical console is
+ * a serial port. This is in case there is a display attached that
+ * is not the console. In that situation we don't need/want the X
+ * server taking over the console.
+ */
+ if (constty && unit == comconsole
+ && (COMCONSOLE || boothowto & RB_SERIAL))
+ constty = NULL;
+ return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
+}
+
+static void
+siodtrwakeup(chan)
+ void *chan;
+{
+ struct com_s *com;
+
+ com = (struct com_s *)chan;
+ com->state &= ~CS_DTR_OFF;
+ if (!(com->unit == comconsole && (COMCONSOLE || boothowto & RB_SERIAL)))
+ kdc_sio[com->unit].kdc_state = DC_IDLE;
+ wakeup(&com->dtr_wait);
+}
+
+/* Interrupt routine for timekeeping purposes */
+void
+siointrts(unit)
+ int unit;
+{
+ /*
+ * XXX microtime() reenables CPU interrupts. We can't afford to
+ * be interrupted and don't want to slow down microtime(), so lock
+ * out interrupts in another way.
+ */
+ outb(IO_ICU1 + 1, 0xff);
+ microtime(&intr_timestamp);
+ disable_intr();
+ outb(IO_ICU1 + 1, imen);
+
+ siointr(unit);
+}
+
+void
+siointr(unit)
+ int unit;
+{
+#ifndef COM_MULTIPORT
+ siointr1(com_addr(unit));
+#else /* COM_MULTIPORT */
+ struct com_s *com;
+ bool_t possibly_more_intrs;
+
+ /*
+ * Loop until there is no activity on any port. This is necessary
+ * to get an interrupt edge more than to avoid another interrupt.
+ * If the IRQ signal is just an OR of the IRQ signals from several
+ * devices, then the edge from one may be lost because another is
+ * on.
+ */
+ do {
+ possibly_more_intrs = FALSE;
+ for (unit = 0; unit < NSIO; ++unit) {
+ com = com_addr(unit);
+ if (com != NULL
+ && (inb(com->int_id_port) & IIR_IMASK)
+ != IIR_NOPEND) {
+ siointr1(com);
+ possibly_more_intrs = TRUE;
+ }
+ }
+ } while (possibly_more_intrs);
+#endif /* COM_MULTIPORT */
+}
+
+static void
+siointr1(com)
+ struct com_s *com;
+{
+ u_char line_status;
+ u_char modem_status;
+ u_char *ioptr;
+ u_char recv_data;
+
+ if (com->do_timestamp)
+ /* XXX a little bloat here... */
+ com->timestamp = intr_timestamp;
+ while (TRUE) {
+ line_status = inb(com->line_status_port);
+
+ /* input event? (check first to help avoid overruns) */
+ while (line_status & LSR_RCV_MASK) {
+ /* break/unnattached error bits or real input? */
+ if (!(line_status & LSR_RXRDY))
+ recv_data = 0;
+ else
+ recv_data = inb(com->data_port);
+ if (line_status & (LSR_PE|LSR_FE|LSR_BI)) {
+#ifdef DDB
+#ifdef BREAK_TO_DEBUGGER
+ if ( (line_status & LSR_BI)
+ && (COMCONSOLE || boothowto & RB_SERIAL)
+ && com->unit == comconsole) {
+ Debugger("serial console break");
+ goto cont;
+ }
+#endif
+#endif
+ /*
+ Don't store PE if IGNPAR and BI if IGNBRK,
+ this hack allows "raw" tty optimization
+ works even if IGN* is set.
+ */
+ if ( com->tp == NULL
+ || !(com->tp->t_state & TS_ISOPEN)
+ || (line_status & (LSR_PE|LSR_FE))
+ && (com->tp->t_iflag & IGNPAR)
+ || (line_status & LSR_BI)
+ && (com->tp->t_iflag & IGNBRK))
+ goto cont;
+ if ( (line_status & (LSR_PE|LSR_FE))
+ && (com->tp->t_state & TS_CAN_BYPASS_L_RINT)
+ && ((line_status & LSR_FE)
+ || (line_status & LSR_PE)
+ && (com->tp->t_iflag & INPCK)))
+ recv_data = 0;
+ }
+ ++com->bytes_in;
+ if (com->hotchar != 0 && recv_data == com->hotchar)
+ setsofttty();
+#ifdef KGDB
+ /* trap into kgdb? (XXX - needs testing and optim) */
+ if (recv_data == FRAME_END
+ && ( com->tp == NULL
+ || !(com->tp->t_state & TS_ISOPEN))
+ && kgdb_dev == makedev(commajor, unit)) {
+ kgdb_connect(0);
+ continue;
+ }
+#endif /* KGDB */
+ ioptr = com->iptr;
+ if (ioptr >= com->ibufend)
+ CE_RECORD(com, CE_INTERRUPT_BUF_OVERFLOW);
+ else {
+ ++com_events;
+#if 0 /* for testing input latency vs efficiency */
+if (com->iptr - com->ibuf == 8)
+ setsofttty();
+#endif
+ ioptr[0] = recv_data;
+ ioptr[CE_INPUT_OFFSET] = line_status;
+ com->iptr = ++ioptr;
+ if (ioptr == com->ihighwater
+ && com->state & CS_RTS_IFLOW)
+ outb(com->modem_ctl_port,
+ com->mcr_image &= ~MCR_RTS);
+ if (line_status & LSR_OE)
+ CE_RECORD(com, CE_OVERRUN);
+ }
+ cont:
+ /*
+ * "& 0x7F" is to avoid the gcc-1.40 generating a slow
+ * jump from the top of the loop to here
+ */
+ line_status = inb(com->line_status_port) & 0x7F;
+ }
+
+ /* modem status change? (always check before doing output) */
+ modem_status = inb(com->modem_status_port);
+ if (modem_status != com->last_modem_status) {
+ /*
+ * Schedule high level to handle DCD changes. Note
+ * that we don't use the delta bits anywhere. Some
+ * UARTs mess them up, and it's easy to remember the
+ * previous bits and calculate the delta.
+ */
+ com->last_modem_status = modem_status;
+ if (!(com->state & CS_CHECKMSR)) {
+ com_events += LOTS_OF_EVENTS;
+ com->state |= CS_CHECKMSR;
+ setsofttty();
+ }
+
+ /* handle CTS change immediately for crisp flow ctl */
+ if (com->state & CS_CTS_OFLOW) {
+ if (modem_status & MSR_CTS)
+ com->state |= CS_ODEVREADY;
+ else
+ com->state &= ~CS_ODEVREADY;
+ }
+ }
+
+ /* output queued and everything ready? */
+ if (line_status & LSR_TXRDY
+ && com->state >= (CS_ODEVREADY | CS_BUSY | CS_TTGO)) {
+ ioptr = com->optr;
+ if (com->tx_fifo_size > 1) {
+ u_int ocount;
+
+ ocount = com->obufend - ioptr;
+ if (ocount > com->tx_fifo_size)
+ ocount = com->tx_fifo_size;
+ com->bytes_out += ocount;
+ do
+ outb(com->data_port, *ioptr++);
+ while (--ocount != 0);
+ } else {
+ outb(com->data_port, *ioptr++);
+ ++com->bytes_out;
+ }
+ com->optr = ioptr;
+ if (ioptr >= com->obufend) {
+ /* output just completed */
+ com_events += LOTS_OF_EVENTS;
+ com->state ^= (CS_ODONE | CS_BUSY);
+ setsofttty(); /* handle at high level ASAP */
+ }
+ }
+
+ /* finished? */
+#ifndef COM_MULTIPORT
+ if ((inb(com->int_id_port) & IIR_IMASK) == IIR_NOPEND)
+#endif /* COM_MULTIPORT */
+ return;
+ }
+}
+
+static int
+tiocm_xxx2mcr(tiocm_xxx)
+ int tiocm_xxx;
+{
+ int mcr;
+
+ mcr = 0;
+ if (tiocm_xxx & TIOCM_DTR)
+ mcr |= MCR_DTR;
+ if (tiocm_xxx & TIOCM_RTS)
+ mcr |= MCR_RTS;
+ return (mcr);
+}
+
+int
+sioioctl(dev, cmd, data, flag, p)
+ dev_t dev;
+ int cmd;
+ caddr_t data;
+ int flag;
+ struct proc *p;
+{
+ struct com_s *com;
+ int error;
+ Port_t iobase;
+ int mcr;
+ int msr;
+ int mynor;
+ int s;
+ int tiocm_xxx;
+ struct tty *tp;
+#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
+ int oldcmd;
+ struct termios term;
+#endif
+
+ mynor = minor(dev);
+ com = com_addr(MINOR_TO_UNIT(mynor));
+ iobase = com->iobase;
+ if (mynor & CONTROL_MASK) {
+ struct termios *ct;
+
+ switch (mynor & CONTROL_MASK) {
+ case CONTROL_INIT_STATE:
+ ct = mynor & CALLOUT_MASK ? &com->it_out : &com->it_in;
+ break;
+ case CONTROL_LOCK_STATE:
+ ct = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in;
+ break;
+ default:
+ return (ENODEV); /* /dev/nodev */
+ }
+ switch (cmd) {
+ case TIOCSETA:
+ error = suser(p->p_ucred, &p->p_acflag);
+ if (error != 0)
+ return (error);
+ *ct = *(struct termios *)data;
+ return (0);
+ case TIOCGETA:
+ *(struct termios *)data = *ct;
+ return (0);
+ case TIOCGETD:
+ *(int *)data = TTYDISC;
+ return (0);
+ case TIOCGWINSZ:
+ bzero(data, sizeof(struct winsize));
+ return (0);
+#ifdef DSI_SOFT_MODEM
+ /*
+ * Download micro-code to Digicom modem.
+ */
+ case TIOCDSIMICROCODE:
+ {
+ u_long l;
+ u_char *p,*pi;
+
+ pi = (u_char*)(*(caddr_t*)data);
+ error = copyin(pi,&l,sizeof l);
+ if(error)
+ {return error;};
+ pi += sizeof l;
+
+ p = malloc(l,M_TEMP,M_NOWAIT);
+ if(!p)
+ {return ENOBUFS;}
+ error = copyin(pi,p,l);
+ if(error)
+ {free(p,M_TEMP); return error;};
+ if(error = LoadSoftModem(
+ MINOR_TO_UNIT(mynor),iobase,l,p))
+ {free(p,M_TEMP); return error;}
+ free(p,M_TEMP);
+ return(0);
+ }
+#endif /* DSI_SOFT_MODEM */
+ default:
+ return (ENOTTY);
+ }
+ }
+ tp = com->tp;
+#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
+ term = tp->t_termios;
+ oldcmd = cmd;
+ error = ttsetcompat(tp, &cmd, data, &term);
+ if (error != 0)
+ return (error);
+ if (cmd != oldcmd)
+ data = (caddr_t)&term;
+#endif
+ if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) {
+ int cc;
+ struct termios *dt = (struct termios *)data;
+ struct termios *lt = mynor & CALLOUT_MASK
+ ? &com->lt_out : &com->lt_in;
+
+ dt->c_iflag = (tp->t_iflag & lt->c_iflag)
+ | (dt->c_iflag & ~lt->c_iflag);
+ dt->c_oflag = (tp->t_oflag & lt->c_oflag)
+ | (dt->c_oflag & ~lt->c_oflag);
+ dt->c_cflag = (tp->t_cflag & lt->c_cflag)
+ | (dt->c_cflag & ~lt->c_cflag);
+ dt->c_lflag = (tp->t_lflag & lt->c_lflag)
+ | (dt->c_lflag & ~lt->c_lflag);
+ for (cc = 0; cc < NCCS; ++cc)
+ if (lt->c_cc[cc] != 0)
+ dt->c_cc[cc] = tp->t_cc[cc];
+ if (lt->c_ispeed != 0)
+ dt->c_ispeed = tp->t_ispeed;
+ if (lt->c_ospeed != 0)
+ dt->c_ospeed = tp->t_ospeed;
+ }
+ error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
+ if (error >= 0)
+ return (error);
+ s = spltty();
+ error = ttioctl(tp, cmd, data, flag);
+ disc_optim(tp, &(tp->t_termios), com);
+ if (error >= 0) {
+ splx(s);
+ return (error);
+ }
+ switch (cmd) {
+ case TIOCSBRK:
+ outb(iobase + com_cfcr, com->cfcr_image |= CFCR_SBREAK);
+ break;
+ case TIOCCBRK:
+ outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK);
+ break;
+ case TIOCSDTR:
+ commctl(com, MCR_DTR, DMBIS);
+ break;
+ case TIOCCDTR:
+ commctl(com, MCR_DTR, DMBIC);
+ break;
+ case TIOCMSET:
+ commctl(com, tiocm_xxx2mcr(*(int *)data), DMSET);
+ break;
+ case TIOCMBIS:
+ commctl(com, tiocm_xxx2mcr(*(int *)data), DMBIS);
+ break;
+ case TIOCMBIC:
+ commctl(com, tiocm_xxx2mcr(*(int *)data), DMBIC);
+ break;
+ case TIOCMGET:
+ tiocm_xxx = TIOCM_LE; /* XXX - always enabled while open */
+ mcr = com->mcr_image;
+ if (mcr & MCR_DTR)
+ tiocm_xxx |= TIOCM_DTR;
+ if (mcr & MCR_RTS)
+ tiocm_xxx |= TIOCM_RTS;
+ msr = com->prev_modem_status;
+ if (msr & MSR_CTS)
+ tiocm_xxx |= TIOCM_CTS;
+ if (msr & MSR_DCD)
+ tiocm_xxx |= TIOCM_CD;
+ if (msr & MSR_DSR)
+ tiocm_xxx |= TIOCM_DSR;
+ /*
+ * XXX - MSR_RI is naturally volatile, and we make MSR_TERI
+ * more volatile by reading the modem status a lot. Perhaps
+ * we should latch both bits until the status is read here.
+ */
+ if (msr & (MSR_RI | MSR_TERI))
+ tiocm_xxx |= TIOCM_RI;
+ *(int *)data = tiocm_xxx;
+ break;
+ case TIOCMSDTRWAIT:
+ /* must be root since the wait applies to following logins */
+ error = suser(p->p_ucred, &p->p_acflag);
+ if (error != 0) {
+ splx(s);
+ return (error);
+ }
+ com->dtr_wait = *(int *)data * hz / 100;
+ break;
+ case TIOCMGDTRWAIT:
+ *(int *)data = com->dtr_wait * 100 / hz;
+ break;
+ case TIOCTIMESTAMP:
+ com->do_timestamp = TRUE;
+ *(struct timeval *)data = com->timestamp;
+ break;
+ default:
+ splx(s);
+ return (ENOTTY);
+ }
+ splx(s);
+ return (0);
+}
+
+/* cancel pending output */
+static void
+comflush(com)
+ struct com_s *com;
+{
+ disable_intr();
+ if (com->state & CS_ODONE)
+ com_events -= LOTS_OF_EVENTS;
+ com->state &= ~(CS_ODONE | CS_BUSY);
+ enable_intr();
+ com->tp->t_state &= ~TS_BUSY;
+}
+
+void
+siopoll()
+{
+ int unit;
+
+ if (com_events == 0)
+ return;
+repeat:
+ for (unit = 0; unit < NSIO; ++unit) {
+ u_char *buf;
+ struct com_s *com;
+ u_char *ibuf;
+ int incc;
+ struct tty *tp;
+
+ com = com_addr(unit);
+ if (com == NULL)
+ continue;
+ tp = com->tp;
+ if (tp == NULL) {
+ /*
+ * XXX forget any events related to closed devices
+ * (actually never opened devices) so that we don't
+ * loop.
+ */
+ disable_intr();
+ incc = com->iptr - com->ibuf;
+ com->iptr = com->ibuf;
+ if (com->state & CS_CHECKMSR) {
+ incc += LOTS_OF_EVENTS;
+ com->state &= ~CS_CHECKMSR;
+ }
+ com_events -= incc;
+ enable_intr();
+ if (incc != 0)
+ log(LOG_DEBUG,
+ "sio%d: %d events for device with no tp\n",
+ unit, incc);
+ continue;
+ }
+
+ /* switch the role of the low-level input buffers */
+ if (com->iptr == (ibuf = com->ibuf)) {
+ buf = NULL; /* not used, but compiler can't tell */
+ incc = 0;
+ } else {
+ buf = ibuf;
+ disable_intr();
+ incc = com->iptr - buf;
+ com_events -= incc;
+ if (ibuf == com->ibuf1)
+ ibuf = com->ibuf2;
+ else
+ ibuf = com->ibuf1;
+ com->ibufend = ibuf + RS_IBUFSIZE;
+ com->ihighwater = ibuf + RS_IHIGHWATER;
+ com->iptr = ibuf;
+
+ /*
+ * There is now room for another low-level buffer full
+ * of input, so enable RTS if it is now disabled and
+ * there is room in the high-level buffer.
+ */
+ /*
+ * XXX this used not to look at CS_RTS_IFLOW. The
+ * change is to allow full control of MCR_RTS via
+ * ioctls after turning CS_RTS_IFLOW off. Check
+ * for races. We shouldn't allow the ioctls while
+ * CS_RTS_IFLOW is on.
+ */
+ if ((com->state & CS_RTS_IFLOW)
+ && !(com->mcr_image & MCR_RTS)
+ && !(tp->t_state & TS_TBLOCK))
+ outb(com->modem_ctl_port,
+ com->mcr_image |= MCR_RTS);
+ enable_intr();
+ com->ibuf = ibuf;
+ }
+
+ if (com->state & CS_CHECKMSR) {
+ u_char delta_modem_status;
+
+ disable_intr();
+ delta_modem_status = com->last_modem_status
+ ^ com->prev_modem_status;
+ com->prev_modem_status = com->last_modem_status;
+ com_events -= LOTS_OF_EVENTS;
+ com->state &= ~CS_CHECKMSR;
+ enable_intr();
+ if (delta_modem_status & MSR_DCD)
+ (*linesw[tp->t_line].l_modem)
+ (tp, com->prev_modem_status & MSR_DCD);
+ }
+ if (com->state & CS_ODONE) {
+ comflush(com);
+ (*linesw[tp->t_line].l_start)(tp);
+ }
+ if (incc <= 0 || !(tp->t_state & TS_ISOPEN))
+ continue;
+ /*
+ * XXX only do this when we bypass ttyinput.
+ */
+ if (tp->t_rawq.c_cc + incc >= RB_I_HIGH_WATER
+ && (com->state & CS_RTS_IFLOW || tp->t_iflag & IXOFF)
+ && !(tp->t_state & TS_TBLOCK)
+ /*
+ * XXX - need flow control for all line disciplines.
+ * Only have it in standard one now.
+ */
+ && linesw[tp->t_line].l_rint == ttyinput) {
+ int putc_status = FALSE;
+
+ if ((tp->t_iflag & IXOFF
+ && tp->t_cc[VSTOP] != _POSIX_VDISABLE
+ && (putc_status = putc(tp->t_cc[VSTOP],
+ &tp->t_outq)) == 0)
+ || com->state & CS_RTS_IFLOW) {
+ tp->t_state |= TS_TBLOCK;
+ ttstart(tp);
+ if (putc_status != 0)
+ /* Try again later. */
+ tp->t_state &= ~TS_TBLOCK;
+ }
+ }
+ /*
+ * Avoid the grotesquely inefficient lineswitch routine
+ * (ttyinput) in "raw" mode. It usually takes about 450
+ * instructions (that's without canonical processing or echo!).
+ * slinput is reasonably fast (usually 40 instructions plus
+ * call overhead).
+ */
+ if (tp->t_state & TS_CAN_BYPASS_L_RINT) {
+ tk_nin += incc;
+ tk_rawcc += incc;
+ tp->t_rawcc += incc;
+ com->delta_error_counts[CE_TTY_BUF_OVERFLOW]
+ += b_to_q((char *)buf, incc, &tp->t_rawq);
+ ttwakeup(tp);
+ if (tp->t_state & TS_TTSTOP
+ && (tp->t_iflag & IXANY
+ || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) {
+ tp->t_state &= ~TS_TTSTOP;
+ tp->t_lflag &= ~FLUSHO;
+ ttstart(tp);
+ }
+ } else {
+ do {
+ u_char line_status;
+ int recv_data;
+
+ line_status = (u_char) buf[CE_INPUT_OFFSET];
+ recv_data = (u_char) *buf++;
+ if (line_status
+ & (LSR_BI | LSR_FE | LSR_OE | LSR_PE)) {
+ if (line_status & LSR_BI)
+ recv_data |= TTY_BI;
+ if (line_status & LSR_FE)
+ recv_data |= TTY_FE;
+ if (line_status & LSR_OE)
+ recv_data |= TTY_OE;
+ if (line_status & LSR_PE)
+ recv_data |= TTY_PE;
+ }
+ (*linesw[tp->t_line].l_rint)(recv_data, tp);
+ } while (--incc > 0);
+ }
+ if (com_events == 0)
+ break;
+ }
+ if (com_events >= LOTS_OF_EVENTS)
+ goto repeat;
+}
+
+static int
+comparam(tp, t)
+ struct tty *tp;
+ struct termios *t;
+{
+ u_int cfcr;
+ int cflag;
+ struct com_s *com;
+ int divisor;
+ int error;
+ Port_t iobase;
+ int s;
+ int unit;
+
+ /* check requested parameters */
+ divisor = ttspeedtab(t->c_ospeed, comspeedtab);
+ if (t->c_ispeed == 0)
+ t->c_ispeed = t->c_ospeed;
+ if (divisor < 0 || divisor > 0 && t->c_ispeed != t->c_ospeed)
+ return (EINVAL);
+
+ /* parameters are OK, convert them to the com struct and the device */
+ unit = DEV_TO_UNIT(tp->t_dev);
+ com = com_addr(unit);
+ iobase = com->iobase;
+ s = spltty();
+ if (divisor == 0)
+ commctl(com, MCR_DTR, DMBIC); /* hang up line */
+ else
+ commctl(com, MCR_DTR, DMBIS);
+ cflag = t->c_cflag;
+ switch (cflag & CSIZE) {
+ case CS5:
+ cfcr = CFCR_5BITS;
+ break;
+ case CS6:
+ cfcr = CFCR_6BITS;
+ break;
+ case CS7:
+ cfcr = CFCR_7BITS;
+ break;
+ default:
+ cfcr = CFCR_8BITS;
+ break;
+ }
+ if (cflag & PARENB) {
+ cfcr |= CFCR_PENAB;
+ if (!(cflag & PARODD))
+ cfcr |= CFCR_PEVEN;
+ }
+ if (cflag & CSTOPB)
+ cfcr |= CFCR_STOPB;
+
+ if (com->hasfifo) {
+ /*
+ * Use a fifo trigger level low enough so that the input
+ * latency from the fifo is less than about 16 msec and
+ * the total latency is less than about 30 msec. These
+ * latencies are reasonable for humans. Serial comms
+ * protocols shouldn't expect anything better since modem
+ * latencies are larger.
+ */
+ com->ftl = t->c_ospeed <= 4800
+ ? FIFO_TRIGGER_1 : FIFO_TRIGGER_14;
+ if (com->ftl > com->ftl_max)
+ com->ftl = com->ftl_max;
+ outb(iobase + com_fifo, FIFO_ENABLE | com->ftl);
+ }
+
+ /*
+ * Some UARTs lock up if the divisor latch registers are selected
+ * while the UART is doing output (they refuse to transmit anything
+ * more until given a hard reset). Fix this by stopping filling
+ * the device buffers and waiting for them to drain. Reading the
+ * line status port outside of siointr1() might lose some receiver
+ * error bits, but that is acceptable here.
+ */
+ disable_intr();
+retry:
+ com->state &= ~CS_TTGO;
+ enable_intr();
+ while ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY))
+ != (LSR_TSRE | LSR_TXRDY)) {
+ error = ttysleep(tp, TSA_OCOMPLETE(tp), TTIPRI | PCATCH,
+ "siotx", hz / 100);
+ if (error != 0 && error != EAGAIN) {
+ if (!(tp->t_state & TS_TTSTOP)) {
+ disable_intr();
+ com->state |= CS_TTGO;
+ enable_intr();
+ }
+ splx(s);
+ return (error);
+ }
+ }
+
+ disable_intr(); /* very important while com_data is hidden */
+
+ /*
+ * XXX - clearing CS_TTGO is not sufficient to stop further output,
+ * because siopoll() calls comstart() which usually sets it again
+ * because TS_TTSTOP is clear. Setting TS_TTSTOP would not be
+ * sufficient, for similar reasons.
+ */
+ if ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY))
+ != (LSR_TSRE | LSR_TXRDY))
+ goto retry;
+
+ if (divisor != 0) {
+ outb(iobase + com_cfcr, cfcr | CFCR_DLAB);
+ outb(iobase + com_dlbl, divisor & 0xFF);
+ outb(iobase + com_dlbh, (u_int) divisor >> 8);
+ }
+ outb(iobase + com_cfcr, com->cfcr_image = cfcr);
+ if (!(tp->t_state & TS_TTSTOP))
+ com->state |= CS_TTGO;
+ if (cflag & CRTS_IFLOW)
+ com->state |= CS_RTS_IFLOW; /* XXX - secondary changes? */
+ else
+ com->state &= ~CS_RTS_IFLOW;
+
+ /*
+ * Set up state to handle output flow control.
+ * XXX - worth handling MDMBUF (DCD) flow control at the lowest level?
+ * Now has 10+ msec latency, while CTS flow has 50- usec latency.
+ */
+ com->state &= ~CS_CTS_OFLOW;
+ com->state |= CS_ODEVREADY;
+ if (cflag & CCTS_OFLOW) {
+ com->state |= CS_CTS_OFLOW;
+ if (!(com->last_modem_status & MSR_CTS))
+ com->state &= ~CS_ODEVREADY;
+ }
+ disc_optim(tp, t, com);
+ /*
+ * Recover from fiddling with CS_TTGO. We used to call siointr1()
+ * unconditionally, but that defeated the careful discarding of
+ * stale input in sioopen().
+ */
+ if (com->state >= (CS_BUSY | CS_TTGO))
+ siointr1(com);
+
+ enable_intr();
+ splx(s);
+ return (0);
+}
+
+static void
+comstart(tp)
+ struct tty *tp;
+{
+ struct com_s *com;
+ int s;
+ int unit;
+
+ unit = DEV_TO_UNIT(tp->t_dev);
+ com = com_addr(unit);
+ s = spltty();
+ disable_intr();
+ if (tp->t_state & TS_TTSTOP)
+ com->state &= ~CS_TTGO;
+ else
+ com->state |= CS_TTGO;
+ if (tp->t_state & TS_TBLOCK) {
+ if (com->mcr_image & MCR_RTS && com->state & CS_RTS_IFLOW)
+ outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS);
+ } else {
+ /*
+ * XXX don't raise MCR_RTS if CTS_RTS_IFLOW is off. Set it
+ * appropriately in comparam() if RTS-flow is being changed.
+ * Check for races.
+ */
+ if (!(com->mcr_image & MCR_RTS) && com->iptr < com->ihighwater)
+ outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS);
+ }
+ enable_intr();
+ if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP))
+ goto out;
+#if 0 /* XXX TK2.0 */
+ if (tp->t_state & (TS_SO_OCOMPLETE | TS_SO_OLOWAT) || tp->t_wsel)
+ ttwwakeup(tp);
+#else
+ if (tp->t_outq.c_cc <= tp->t_lowat) {
+ if (tp->t_state & TS_ASLEEP) {
+ tp->t_state &= ~TS_ASLEEP;
+ wakeup(TSA_OLOWAT(tp));
+ }
+ selwakeup(&tp->t_wsel);
+ }
+#endif
+ if (tp->t_state & TS_BUSY) {
+ disable_intr();
+ siointr1(com);
+ enable_intr();
+ } else if (tp->t_outq.c_cc != 0) {
+ u_int ocount;
+
+ tp->t_state |= TS_BUSY;
+ ocount = q_to_b(&tp->t_outq, com->obuf, sizeof com->obuf);
+ disable_intr();
+ com->obufend = (com->optr = com->obuf) + ocount;
+ com->state |= CS_BUSY;
+ siointr1(com); /* fake interrupt to start output */
+ enable_intr();
+ }
+out:
+ splx(s);
+}
+
+void
+siostop(tp, rw)
+ struct tty *tp;
+ int rw;
+{
+ struct com_s *com;
+
+ com = com_addr(DEV_TO_UNIT(tp->t_dev));
+ if (rw & FWRITE)
+ comflush(com);
+ disable_intr();
+ if (rw & FREAD) {
+ com_events -= (com->iptr - com->ibuf);
+ com->iptr = com->ibuf;
+ }
+ if (tp->t_state & TS_TTSTOP)
+ com->state &= ~CS_TTGO;
+ else
+ com->state |= CS_TTGO;
+ enable_intr();
+}
+
+struct tty *
+siodevtotty(dev)
+ dev_t dev;
+{
+ int mynor;
+ int unit;
+
+ mynor = minor(dev);
+ if (mynor & CONTROL_MASK)
+ return (NULL);
+ unit = MINOR_TO_UNIT(mynor);
+ if ((u_int) unit >= NSIO)
+ return (NULL);
+ return (&sio_tty[unit]);
+}
+
+static void
+commctl(com, bits, how)
+ struct com_s *com;
+ int bits;
+ int how;
+{
+ disable_intr();
+ switch (how) {
+ case DMSET:
+ outb(com->modem_ctl_port,
+ com->mcr_image = bits | (com->mcr_image & MCR_IENABLE));
+ break;
+ case DMBIS:
+ outb(com->modem_ctl_port, com->mcr_image |= bits);
+ break;
+ case DMBIC:
+ outb(com->modem_ctl_port, com->mcr_image &= ~bits);
+ break;
+ }
+ enable_intr();
+}
+
+static void
+comwakeup(chan)
+ void *chan;
+{
+ struct com_s *com;
+ static int log_countdown = 1;
+ int unit;
+
+ timeout(comwakeup, (caddr_t)NULL, hz > 200 ? hz / 200 : 1);
+
+ if (com_events != 0) {
+ int s;
+
+ s = splsofttty();
+ siopoll();
+ splx(s);
+ }
+
+ /*
+ * Recover from lost output interrupts.
+ * Poll any lines that don't use interrupts.
+ */
+ for (unit = 0; unit < NSIO; ++unit) {
+ com = com_addr(unit);
+ if (com != NULL
+ && (com->state >= (CS_BUSY | CS_TTGO) || com->poll)) {
+ disable_intr();
+ siointr1(com);
+ enable_intr();
+ }
+ }
+
+ /*
+ * Check for and log errors, but not too often.
+ */
+ if (--log_countdown > 0)
+ return;
+ log_countdown = hz > 200 ? 200 : hz;
+ for (unit = 0; unit < NSIO; ++unit) {
+ int errnum;
+
+ com = com_addr(unit);
+ if (com == NULL)
+ continue;
+ for (errnum = 0; errnum < CE_NTYPES; ++errnum) {
+ u_int delta;
+ u_long total;
+
+ disable_intr();
+ delta = com->delta_error_counts[errnum];
+ com->delta_error_counts[errnum] = 0;
+ enable_intr();
+ if (delta == 0)
+ continue;
+ total = com->error_counts[errnum] += delta;
+ log(LOG_ERR, "sio%d: %u more %s%s (total %lu)\n",
+ unit, delta, error_desc[errnum],
+ delta == 1 ? "" : "s", total);
+#if 0
+ /*
+ * XXX if we resurrect this then we should move
+ * the dropping of the ftl to somewhere with less
+ * latency.
+ */
+ if (errnum == CE_OVERRUN && com->hasfifo
+ && com->ftl > FIFO_TRIGGER_1) {
+ static u_char ftl_in_bytes[] =
+ { 1, 4, 8, 14, };
+
+ com->ftl_init = FIFO_TRIGGER_8;
+#define FIFO_TRIGGER_DELTA FIFO_TRIGGER_4
+ com->ftl_max =
+ com->ftl -= FIFO_TRIGGER_DELTA;
+ outb(com->iobase + com_fifo,
+ FIFO_ENABLE | com->ftl);
+ log(LOG_DEBUG,
+ "sio%d: reduced fifo trigger level to %d\n",
+ unit,
+ ftl_in_bytes[com->ftl
+ / FIFO_TRIGGER_DELTA]);
+ }
+#endif
+ }
+ }
+}
+
+static void
+disc_optim(tp, t, com)
+ struct tty *tp;
+ struct termios *t;
+ struct com_s *com;
+{
+
+ if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP
+ | IXOFF | IXON))
+ && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK))
+ && (!(t->c_iflag & PARMRK) ||
+ (t->c_iflag & (IGNPAR|IGNBRK)) == (IGNPAR|IGNBRK))
+ && !(t->c_lflag & (ECHO | ECHONL | ICANON | IEXTEN | ISIG
+ | PENDIN))
+ && linesw[tp->t_line].l_rint == ttyinput)
+ tp->t_state |= TS_CAN_BYPASS_L_RINT;
+ else
+ tp->t_state &= ~TS_CAN_BYPASS_L_RINT;
+ /*
+ * Prepare to reduce input latency for packet
+ * discplines with a end of packet character.
+ */
+ if (tp->t_line == SLIPDISC)
+ com->hotchar = 0xc0;
+ else if (tp->t_line == PPPDISC)
+ com->hotchar = 0x7e;
+ else
+ com->hotchar = 0;
+}
+
+/*
+ * Following are all routines needed for SIO to act as console
+ */
+#include <i386/i386/cons.h>
+
+struct siocnstate {
+ u_char dlbl;
+ u_char dlbh;
+ u_char ier;
+ u_char cfcr;
+ u_char mcr;
+};
+
+static Port_t siocniobase;
+
+static void siocnclose __P((struct siocnstate *sp));
+static void siocnopen __P((struct siocnstate *sp));
+static void siocntxwait __P((void));
+
+static void
+siocntxwait()
+{
+ int timo;
+
+ /*
+ * Wait for any pending transmission to finish. Required to avoid
+ * the UART lockup bug when the speed is changed, and for normal
+ * transmits.
+ */
+ timo = 100000;
+ while ((inb(siocniobase + com_lsr) & (LSR_TSRE | LSR_TXRDY))
+ != (LSR_TSRE | LSR_TXRDY) && --timo != 0)
+ ;
+}
+
+static void
+siocnopen(sp)
+ struct siocnstate *sp;
+{
+ int divisor;
+ Port_t iobase;
+
+ /*
+ * Save all the device control registers except the fifo register
+ * and set our default ones (cs8 -parenb speed=comdefaultrate).
+ * We can't save the fifo register since it is read-only.
+ */
+ iobase = siocniobase;
+ sp->ier = inb(iobase + com_ier);
+ outb(iobase + com_ier, 0); /* spltty() doesn't stop siointr() */
+ siocntxwait();
+ sp->cfcr = inb(iobase + com_cfcr);
+ outb(iobase + com_cfcr, CFCR_DLAB);
+ sp->dlbl = inb(iobase + com_dlbl);
+ sp->dlbh = inb(iobase + com_dlbh);
+ divisor = ttspeedtab(comdefaultrate, comspeedtab);
+ outb(iobase + com_dlbl, divisor & 0xFF);
+ outb(iobase + com_dlbh, (u_int) divisor >> 8);
+ outb(iobase + com_cfcr, CFCR_8BITS);
+ sp->mcr = inb(iobase + com_mcr);
+ /*
+ * We don't want interrupts, but must be careful not to "disable"
+ * them by clearing the MCR_IENABLE bit, since that might cause
+ * an interrupt by floating the IRQ line.
+ */
+ outb(iobase + com_mcr, (sp->mcr & MCR_IENABLE) | MCR_DTR | MCR_RTS);
+}
+
+static void
+siocnclose(sp)
+ struct siocnstate *sp;
+{
+ Port_t iobase;
+
+ /*
+ * Restore the device control registers.
+ */
+ siocntxwait();
+ iobase = siocniobase;
+ outb(iobase + com_cfcr, CFCR_DLAB);
+ outb(iobase + com_dlbl, sp->dlbl);
+ outb(iobase + com_dlbh, sp->dlbh);
+ outb(iobase + com_cfcr, sp->cfcr);
+ /*
+ * XXX damp oscillations of MCR_DTR and MCR_RTS by not restoring them.
+ */
+ outb(iobase + com_mcr, sp->mcr | MCR_DTR | MCR_RTS);
+ outb(iobase + com_ier, sp->ier);
+}
+
+void
+siocnprobe(cp)
+ struct consdev *cp;
+{
+ int unit;
+
+ /* locate the major number */
+ /* XXX - should be elsewhere since KGDB uses it */
+ for (commajor = 0; commajor < nchrdev; commajor++)
+ if (cdevsw[commajor].d_open == sioopen)
+ break;
+
+ /* XXX: ick */
+ unit = DEV_TO_UNIT(CONUNIT);
+ siocniobase = CONADDR;
+
+ /* make sure hardware exists? XXX */
+
+ /* initialize required fields */
+ cp->cn_dev = makedev(commajor, unit);
+
+ if (COMCONSOLE || boothowto & RB_SERIAL)
+ cp->cn_pri = CN_REMOTE; /* Force a serial port console */
+ else
+ cp->cn_pri = CN_NORMAL;
+
+}
+
+void
+siocninit(cp)
+ struct consdev *cp;
+{
+ /*
+ * XXX can delete more comconsole stuff now that i/o routines are
+ * fairly reentrant.
+ */
+ comconsole = DEV_TO_UNIT(cp->cn_dev);
+}
+
+int
+siocncheckc(dev)
+ dev_t dev;
+{
+ int c;
+ Port_t iobase;
+ int s;
+ struct siocnstate sp;
+
+ iobase = siocniobase;
+ s = spltty();
+ siocnopen(&sp);
+ if (inb(iobase + com_lsr) & LSR_RXRDY)
+ c = inb(iobase + com_data);
+ else
+ c = 0;
+ siocnclose(&sp);
+ splx(s);
+ return (c);
+}
+
+
+int
+siocngetc(dev)
+ dev_t dev;
+{
+ int c;
+ Port_t iobase;
+ int s;
+ struct siocnstate sp;
+
+ iobase = siocniobase;
+ s = spltty();
+ siocnopen(&sp);
+ while (!(inb(iobase + com_lsr) & LSR_RXRDY))
+ ;
+ c = inb(iobase + com_data);
+ siocnclose(&sp);
+ splx(s);
+ return (c);
+}
+
+void
+siocnputc(dev, c)
+ dev_t dev;
+ int c;
+{
+ int s;
+ struct siocnstate sp;
+
+ s = spltty();
+ siocnopen(&sp);
+ siocntxwait();
+ outb(siocniobase + com_data, c);
+ siocnclose(&sp);
+ splx(s);
+}
+
+#ifdef DSI_SOFT_MODEM
+/*
+ * The magic code to download microcode to a "Connection 14.4+Fax"
+ * modem from Digicom Systems Inc. Very magic.
+ */
+
+#define DSI_ERROR(str) { ptr = str; goto error; }
+static int
+LoadSoftModem(int unit, int base_io, u_long size, u_char *ptr)
+{
+ int int_c,int_k;
+ int data_0188, data_0187;
+
+ /*
+ * First see if it is a DSI SoftModem
+ */
+ if(!((inb(base_io+7) ^ inb(base_io+7) & 0x80)))
+ return ENODEV;
+
+ data_0188 = inb(base_io+4);
+ data_0187 = inb(base_io+3);
+ outb(base_io+3,0x80);
+ outb(base_io+4,0x0C);
+ outb(base_io+0,0x31);
+ outb(base_io+1,0x8C);
+ outb(base_io+7,0x10);
+ outb(base_io+7,0x19);
+
+ if(0x18 != (inb(base_io+7) & 0x1A))
+ DSI_ERROR("dsp bus not granted");
+
+ if(0x01 != (inb(base_io+7) & 0x01)) {
+ outb(base_io+7,0x18);
+ outb(base_io+7,0x19);
+ if(0x01 != (inb(base_io+7) & 0x01))
+ DSI_ERROR("program mem not granted");
+ }
+
+ int_c = 0;
+
+ while(1) {
+ if(int_c >= 7 || size <= 0x1800)
+ break;
+
+ for(int_k = 0 ; int_k < 0x800; int_k++) {
+ outb(base_io+0,*ptr++);
+ outb(base_io+1,*ptr++);
+ outb(base_io+2,*ptr++);
+ }
+
+ size -= 0x1800;
+ int_c++;
+ }
+
+ if(size > 0x1800) {
+ outb(base_io+7,0x18);
+ outb(base_io+7,0x19);
+ if(0x00 != (inb(base_io+7) & 0x01))
+ DSI_ERROR("program data not granted");
+
+ for(int_k = 0 ; int_k < 0x800; int_k++) {
+ outb(base_io+1,*ptr++);
+ outb(base_io+2,0);
+ outb(base_io+1,*ptr++);
+ outb(base_io+2,*ptr++);
+ }
+
+ size -= 0x1800;
+
+ while(size > 0x1800) {
+ for(int_k = 0 ; int_k < 0xC00; int_k++) {
+ outb(base_io+1,*ptr++);
+ outb(base_io+2,*ptr++);
+ }
+ size -= 0x1800;
+ }
+
+ if(size < 0x1800) {
+ for(int_k=0;int_k<size/2;int_k++) {
+ outb(base_io+1,*ptr++);
+ outb(base_io+2,*ptr++);
+ }
+ }
+
+ } else if (size > 0) {
+ if(int_c == 7) {
+ outb(base_io+7,0x18);
+ outb(base_io+7,0x19);
+ if(0x00 != (inb(base_io+7) & 0x01))
+ DSI_ERROR("program data not granted");
+ for(int_k = 0 ; int_k < size/3; int_k++) {
+ outb(base_io+1,*ptr++);
+ outb(base_io+2,0);
+ outb(base_io+1,*ptr++);
+ outb(base_io+2,*ptr++);
+ }
+ } else {
+ for(int_k = 0 ; int_k < size/3; int_k++) {
+ outb(base_io+0,*ptr++);
+ outb(base_io+1,*ptr++);
+ outb(base_io+2,*ptr++);
+ }
+ }
+ }
+ outb(base_io+7,0x11);
+ outb(base_io+7,3);
+
+ outb(base_io+4,data_0188 & 0xfb);
+
+ outb(base_io+3,data_0187);
+
+ return 0;
+error:
+ printf("sio%d: DSI SoftModem microcode load failed: <%s>\n",ptr);
+ outb(base_io+7,0x00); \
+ outb(base_io+3,data_0187); \
+ outb(base_io+4,data_0188); \
+ return EIO;
+}
+#endif /* DSI_SOFT_MODEM */
+
+#endif /* NSIO > 0 */
OpenPOWER on IntegriCloud