summaryrefslogtreecommitdiffstats
path: root/usr.sbin/pccard
diff options
context:
space:
mode:
authorphk <phk@FreeBSD.org>1995-08-24 09:03:04 +0000
committerphk <phk@FreeBSD.org>1995-08-24 09:03:04 +0000
commit6525e9352125ef84d8dc9be7527c97103bd99a46 (patch)
tree3500a990f00ee986fa4dedc4ee4179df82e89175 /usr.sbin/pccard
parentcb797b9f3a51481777017af541d45fbb632918bd (diff)
downloadFreeBSD-src-6525e9352125ef84d8dc9be7527c97103bd99a46.zip
FreeBSD-src-6525e9352125ef84d8dc9be7527c97103bd99a46.tar.gz
The userland part of Andrew McRae's PCMCIA/PCCARD code.
This is not quite finished yet, and therefore I have not added it to the usr.sbin/Makefile yet. I collected a bunch of Andrews small programs into one: pccardc /phk Reviewed by: phk Submitted by: Andrew McRae <andrew@mega.com.au>
Diffstat (limited to 'usr.sbin/pccard')
-rw-r--r--usr.sbin/pccard/misc/COPYRIGHT11
-rw-r--r--usr.sbin/pccard/misc/FILES12
-rw-r--r--usr.sbin/pccard/misc/INSTALL34
-rw-r--r--usr.sbin/pccard/misc/Makefile32
-rw-r--r--usr.sbin/pccard/misc/README.old62
-rw-r--r--usr.sbin/pccard/misc/rpti.c113
-rw-r--r--usr.sbin/pccard/misc/skeldrv.c37
-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
-rw-r--r--usr.sbin/pccard/pccardc/Makefile37
-rw-r--r--usr.sbin/pccard/pccardc/dumpcis.c79
-rw-r--r--usr.sbin/pccard/pccardc/enabler.c144
-rw-r--r--usr.sbin/pccard/pccardc/pccardc.c59
-rw-r--r--usr.sbin/pccard/pccardc/pccardmem.c38
-rw-r--r--usr.sbin/pccard/pccardc/printcis.c711
-rw-r--r--usr.sbin/pccard/pccardc/rdmap.c71
-rw-r--r--usr.sbin/pccard/pccardc/rdreg.c48
-rw-r--r--usr.sbin/pccard/pccardc/wrattr.c46
-rw-r--r--usr.sbin/pccard/pccardc/wrreg.c39
-rw-r--r--usr.sbin/pccard/pccardd/Makefile10
-rw-r--r--usr.sbin/pccard/pccardd/cardd.c701
-rw-r--r--usr.sbin/pccard/pccardd/cardd.h129
-rw-r--r--usr.sbin/pccard/pccardd/file.c873
-rw-r--r--usr.sbin/pccard/pccardd/pccard.conf.5183
-rw-r--r--usr.sbin/pccard/pccardd/pccardd.8133
-rw-r--r--usr.sbin/pccard/pccardd/readcis.c633
-rw-r--r--usr.sbin/pccard/pccardd/readcis.h116
-rw-r--r--usr.sbin/pccard/pccardd/sample.config22
-rw-r--r--usr.sbin/pccard/pccardd/util.c209
36 files changed, 10277 insertions, 0 deletions
diff --git a/usr.sbin/pccard/misc/COPYRIGHT b/usr.sbin/pccard/misc/COPYRIGHT
new file mode 100644
index 0000000..4f8c550
--- /dev/null
+++ b/usr.sbin/pccard/misc/COPYRIGHT
@@ -0,0 +1,11 @@
+Original work is copyright (C) Andrew McRae 1994,1995
+Permission is granted to freely copy as long as
+all attribution remains and copyright notices are
+included, and source made available upon request.
+
+Parts of this distribution is derived from work
+by Barry Jaspan and Keith Moore.
+
+DISCLAIMER:
+There is no warranty for this software.
+All responsibility is your own.
diff --git a/usr.sbin/pccard/misc/FILES b/usr.sbin/pccard/misc/FILES
new file mode 100644
index 0000000..dcb097a
--- /dev/null
+++ b/usr.sbin/pccard/misc/FILES
@@ -0,0 +1,12 @@
+The files are:
+
+ enabler Sample enabler for connecting drivers
+ dumpcis Reads and dumps CIS of card.
+ pccardmem Sets a memory area for use by the driver
+ rdmap Dumps the current memory and I/O card contexts
+ rdreg reads a PCIC register
+ wrreg Writes a PCIC register
+ wrattr Writes a byte into the card's attribute area.
+
+The cardd stuff has been updated for the new interfaces, but
+hasn't been tested to any real extent.
diff --git a/usr.sbin/pccard/misc/INSTALL b/usr.sbin/pccard/misc/INSTALL
new file mode 100644
index 0000000..c9f3659
--- /dev/null
+++ b/usr.sbin/pccard/misc/INSTALL
@@ -0,0 +1,34 @@
+INSTALLATION
+------------
+
+1. Copy the source files from the sys directory into your
+ /sys (or where-ever) tree. There are a number of .diff files
+ that can be used to modify the following files:
+ sys/conf/files
+ sys/i386/i386/autoconf.c
+ sys/i386/i386/conf.c
+ There is also a sys/pccard directory that should reside in
+ /sys/pccard
+
+2. Make a symbolic link like:
+ ln -s /sys/pccard /usr/include/pccard
+
+3. Add a line in the kernel config file:
+
+ controller crd0
+
+ and rebuild the kernel. Optionally, the PCIC slot driver can
+ and also built into the kernel by adding the line:
+
+ device pcic0
+
+4. Create a device for each slot e.g.
+
+ foreach i (0 1)
+ mknod /dev/card$i c 50 $i
+ end
+
+5. You can use lkm to load the PCIC controller. It can
+ be unloaded at will. Once loaded, you should assign
+ some memory via pccardmem for use with read/write.
+ Normally cardd does this, but I haven't quite finished yet.
diff --git a/usr.sbin/pccard/misc/Makefile b/usr.sbin/pccard/misc/Makefile
new file mode 100644
index 0000000..37d953b
--- /dev/null
+++ b/usr.sbin/pccard/misc/Makefile
@@ -0,0 +1,32 @@
+#
+# Makefile for PCMCIA card programs.
+#
+CFLAGS = -m486 -g # -DDEBUG
+
+PROGS = dumpcis rdmap rdreg wrreg pccardmem wrattr enabler
+
+all: $(PROGS)
+
+clean:
+ rm -f *.core core *.o $(PROGS)
+
+dumpcis: dumpcis.o readcis.o printcis.o
+ cc -o dumpcis -static dumpcis.o readcis.o printcis.o
+
+wrattr: wrattr.c
+ cc $(CFLAGS) -o wrattr wrattr.c
+
+pccardmem: pccardmem.c
+ cc $(CFLAGS) -o pccardmem pccardmem.c
+
+enabler: enabler.c
+ cc $(CFLAGS) -o enabler enabler.c
+
+rdmap: rdmap.c
+ cc $(CFLAGS) -o rdmap rdmap.c
+
+rdreg: rdreg.c
+ cc $(CFLAGS) -o rdreg rdreg.c
+
+wrreg: wrreg.c
+ cc $(CFLAGS) -o wrreg wrreg.c
diff --git a/usr.sbin/pccard/misc/README.old b/usr.sbin/pccard/misc/README.old
new file mode 100644
index 0000000..76e005a
--- /dev/null
+++ b/usr.sbin/pccard/misc/README.old
@@ -0,0 +1,62 @@
+PCMCIA Support for FreeBSD 2.0
+------------------------------
+
+This package contains the following:
+
+ - Driver for Intel 83265 PCIC PCMCIA controller
+ - A PCMCIA daemon for managing card insertions/removals
+ - Diffs to various source files for adding PCMCIA support
+ - A sample config file
+ - Some utility programs for reading card data tuples
+ - Some documentation.
+
+What it doesn't include is:
+
+ - Configuration for brand XYZ PCMCIA cards
+ - Drivers for brand XYZ PCMCIA cards
+ - Diffs to modify standard drivers to handle card events
+
+Essentially, this package contains everything required to
+add PCMCIA support to FreeBSD 2.0. It does this via a
+daemon that manages the PCMCIA slots via a PCIC driver. A configuration
+file provides the daemon with the information required to setup
+the specific cards, and to manage card insertion and removal.
+
+This package has been tested on a NEC Versa Laptop.
+The first version was developed on FreeBSD 1.1.5.1.
+
+The main idea behind the package is for pcmciad to detect
+inserted cards and match to a card ID in the config file,
+then set up the I/O ports and memory window to the card
+according to the data for the driver associated with the
+card, then attach the kernel driver to the device. Shell
+commands can be executed for both insertion and removal of cards,
+and different commands can be executed for different cards,
+drivers and devices.
+
+Different kernel drivers may have to have some mods
+done to recognise the card once installed; I have included
+some diffs to `ed' that allows recognition of a `generic'
+NS8390 card.
+
+Some minor changes were made to ifconfig(8) to allow a
+different ethernet address to be assigned to a network
+interface, since each card may have different locations
+the ethernet address is stored in.
+
+This is a snapshot of a release for FreeBSD 2.0. It is
+basically the 1.1.5.1 release ported for 2.0. I have only
+just received a developer's guide for PCMCIA, so I should
+have some more stable support soon.
+
+For instructions on installation, see INSTALL.
+There is a man entry on the daemon (pcmciad.8),
+and the config file (pcmcia.conf.5).
+
+Please send mail with any bugs or new card descriptions.
+
+Enjoy!
+Andrew McRae inet: andrew@mega.com.au
+MITS Real Time Ltd, uucp: ..!uunet!mega.com.au!andrew
+North Ryde 2113 Phone: +61 2 805 0899
+NSW AUSTRALIA Fax: +61 2 887 4847
diff --git a/usr.sbin/pccard/misc/rpti.c b/usr.sbin/pccard/misc/rpti.c
new file mode 100644
index 0000000..d97c490
--- /dev/null
+++ b/usr.sbin/pccard/misc/rpti.c
@@ -0,0 +1,113 @@
+/*
+ * Enabler for PCCARD. Used for testing drivers etc.
+ * Options:
+ * enabler slot driver [ -m card addr size ] [ -i iobase ] [ -q irq ]
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+
+#include <pccard/card.h>
+#include <pccard/cis.h>
+
+void usage();
+
+
+main(argc, argv)
+int argc;
+char *argv[];
+{
+struct drv_desc drv;
+struct mem_desc mem;
+struct io_desc io;
+int fd, err, slot, i, card_addr;
+char name[32], cmd[256];
+char *p;
+
+ if (argc == 2)
+ slot = atoi(argv[1]);
+ else
+ slot = 0;
+ if (slot < 0 || slot >= MAXSLOT)
+ usage("Illegal slot number");
+ sprintf(cmd, "util/wrattr %d 100 80", slot);
+ printf("%s\n", cmd);
+ system(cmd);
+ usleep(200*1000);
+ sprintf(cmd, "util/wrattr %d 100 0", slot);
+ printf("%s\n", cmd);
+ system(cmd);
+ usleep(200*1000);
+ sprintf(cmd, "util/wrattr %d 100 30", slot);
+ printf("%s\n", cmd);
+ system(cmd);
+ usleep(200*1000);
+ bzero(&drv, sizeof(drv));
+ drv.unit = 0;
+ strcpy(drv.name, "ed");
+ drv.irqmask = 1 << 5;
+ sprintf(name, "/dev/card%d", slot);
+ fd = open(name, 2);
+ if (fd < 0)
+ {
+ perror(name);
+ exit(1);
+ }
+/*
+ * Map the memory and I/O contexts.
+ */
+ drv.mem = 0xD4000;
+ if (drv.mem)
+ {
+ mem.window = 0;
+ mem.flags = MDF_ACTIVE;
+ mem.start = (caddr_t)drv.mem;
+ mem.size = 16*1024;
+ mem.card = 0x4000;
+ if (ioctl(fd, PIOCSMEM, &mem))
+ {
+ perror("Set memory context");
+ exit(1);
+ }
+ }
+ drv.iobase = 0x300;
+ if (drv.iobase)
+ {
+ io.window = 0;
+ io.flags = IODF_ACTIVE|IODF_CS16|IODF_WS;
+ io.start = drv.iobase;
+ io.size = 32; /* Blah... */
+ if (ioctl(fd, PIOCSIO, &io))
+ {
+ perror("Set I/O context");
+ exit(1);
+ }
+#ifdef 0
+ io.window = 1;
+ io.flags = IODF_ACTIVE|IODF_16BIT;
+ io.start = drv.iobase+16;
+ io.size = 16; /* Blah... */
+ if (ioctl(fd, PIOCSIO, &io))
+ {
+ perror("Set I/O context");
+ exit(1);
+ }
+#endif
+ }
+ if (ioctl(fd, PIOCSDRV, &drv))
+ perror("set driver");
+ close(fd);
+}
+/*
+ * usage - print usage and exit
+ */
+void
+usage(msg)
+char *msg;
+{
+ fprintf(stderr, "rpti: %s\n", msg);
+ fprintf(stderr, "Usage: rpti slot driver\n");
+ exit(1);
+}
diff --git a/usr.sbin/pccard/misc/skeldrv.c b/usr.sbin/pccard/misc/skeldrv.c
new file mode 100644
index 0000000..ba43861
--- /dev/null
+++ b/usr.sbin/pccard/misc/skeldrv.c
@@ -0,0 +1,37 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+
+#include <pccard/card.h>
+#include <pccard/cis.h>
+
+
+main(argc, argv)
+int argc;
+char *argv[];
+{
+struct drv_desc drv;
+int fd, err;
+
+ fd = open("/dev/card0", 0);
+ if (fd < 0)
+ {
+ perror("/dev/card0");
+ exit(1);
+ }
+ strcpy(drv.name, "skel");
+ if (argc == 2)
+ drv.unit = atoi(argv[1]);
+ else
+ drv.unit = 0;
+ drv.iobase = 0x300;
+ drv.mem = 0xD4000;
+ drv.memsize = 16*1024;
+ drv.irq = 0xFFFF;
+ drv.flags = 0x1234;
+ if (ioctl(fd, PIOCSDRV, &drv))
+ perror("set driver");
+ close(fd);
+}
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 */
diff --git a/usr.sbin/pccard/pccardc/Makefile b/usr.sbin/pccard/pccardc/Makefile
new file mode 100644
index 0000000..078fb4b
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/Makefile
@@ -0,0 +1,37 @@
+#
+# Makefile pccardc
+#
+# $Id$
+#
+.PATH: ${.CURDIR}/../pccardd
+PROG= pccardc
+SRCS= pccardc.c \
+ dumpcis.c readcis.c printcis.c \
+ enabler.c \
+ pccardmem.c \
+ rdmap.c \
+ rdreg.c \
+ wrattr.c \
+ wrreg.c
+
+CFLAGS+= -I. -I${.CURDIR}/../pccardd
+
+.include <bsd.prog.mk>
+
+##
+## Makefile for PCMCIA card programs.
+##
+#
+#PROGS = rdmap rdreg wrreg pccardmem wrattr enabler
+#
+#enabler: enabler.c
+# cc $(CFLAGS) -o enabler enabler.c
+#
+#rdmap: rdmap.c
+# cc $(CFLAGS) -o rdmap rdmap.c
+#
+#rdreg: rdreg.c
+# cc $(CFLAGS) -o rdreg rdreg.c
+#
+#wrreg: wrreg.c
+# cc $(CFLAGS) -o wrreg wrreg.c
diff --git a/usr.sbin/pccard/pccardc/dumpcis.c b/usr.sbin/pccard/pccardc/dumpcis.c
new file mode 100644
index 0000000..eacf73a
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/dumpcis.c
@@ -0,0 +1,79 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+
+#include <pccard/card.h>
+#include <pccard/cis.h>
+#include "readcis.h"
+
+int nocards;
+
+int
+dumpcis_main(int argc, char **argv)
+{
+int node;
+
+ for (node = 0; node < 8; node++)
+ scan(node);
+ printf("%d slots found\n", nocards);
+}
+scan(slot)
+int slot;
+{
+int fd, i;
+char name[64];
+struct cis *cp;
+struct slotstate st;
+
+ sprintf(name, "/dev/card%d", slot);
+ fd = open(name, 0);
+ if (fd < 0)
+ return;
+ nocards++;
+ ioctl(fd, PIOCGSTATE, &st);
+ if (st.state == filled)
+ {
+ cp = readcis(fd);
+ if (cp)
+ {
+ printf("Configuration data for card in slot %d\n",
+ slot);
+ dumpcis(cp);
+ freecis(cp);
+ }
+ }
+}
+dump(p, sz)
+unsigned char *p;
+int sz;
+{
+int ad = 0, i;
+ while (sz > 0)
+ {
+ printf("%03x: ", ad);
+ for (i = 0; i < ((sz < 16) ? sz : 16); i++)
+ printf(" %02x", p[i]);
+ printf("\n");
+ sz -= 16;
+ p += 16;
+ ad += 16;
+ }
+}
+void *
+xmalloc(int sz)
+{
+void *p;
+
+ sz = (sz + 7) & ~7;
+ p = malloc(sz);
+ if (p)
+ bzero(p, sz);
+ else
+ {
+ perror("malloc");
+ exit(1);
+ }
+ return(p);
+}
diff --git a/usr.sbin/pccard/pccardc/enabler.c b/usr.sbin/pccard/pccardc/enabler.c
new file mode 100644
index 0000000..c96b853
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/enabler.c
@@ -0,0 +1,144 @@
+/*
+ * Enabler for PCCARD. Used for testing drivers etc.
+ * Options:
+ * enabler slot driver [ -m card addr size ] [ -i iobase ] [ -q irq ]
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+
+#include <pccard/card.h>
+#include <pccard/cis.h>
+
+void usage();
+
+int
+enabler_main(argc, argv)
+int argc;
+char *argv[];
+{
+struct drv_desc drv;
+struct mem_desc mem;
+struct io_desc io;
+int fd, err, slot, i, card_addr;
+char name[32];
+char *p;
+
+ bzero(&drv, sizeof(drv));
+ if (argc < 3)
+ usage("arg count");
+ slot = atoi(argv[1]);
+ if (slot < 0 || slot >= MAXSLOT)
+ usage("Illegal slot number");
+ p = argv[2];
+ while (*p && (*p < '0' || *p > '9'))
+ p++;
+ if (*p == 0)
+ usage("No unit on device name");
+ drv.unit = atoi(p);
+ *p = 0;
+ strcpy(drv.name, argv[2]);
+ argv += 3;
+ argc -= 3;
+ while (argc > 1)
+ {
+ if (strcmp(argv[0], "-m")==0)
+ {
+ if (argc < 4)
+ usage("Memory argument error");
+ if (sscanf(argv[1], "%x", &card_addr)!=1)
+ usage("Bad card address");
+ if (sscanf(argv[2], "%x", &drv.mem)!=1)
+ usage("Bad memory address");
+ if (sscanf(argv[3], "%d", &i)!=1)
+ usage("Bad memory size");
+ drv.memsize = i * 1024;
+ argc -= 2;
+ argv += 2;
+ }
+ else if (strcmp(argv[0], "-f")==0)
+ {
+ if (sscanf(argv[1], "%x", &drv.flags)!=1)
+ usage("Bad driver flags");
+ }
+ else if (strcmp(argv[0], "-a")==0)
+ {
+ if (sscanf(argv[1], "%x", &drv.iobase)!=1)
+ usage("Bad I/O address");
+ }
+ else if (strcmp(argv[0], "-i")==0)
+ {
+ if (sscanf(argv[1], "%d", &i)!=1 ||
+ i < 1 || i > 15)
+ usage("Illegal IRQ");
+ drv.irqmask = 1 << i;
+ }
+ argc -= 2;
+ argv += 2;
+ }
+ if (argc)
+ usage("no parameter for argument");
+ printf("drv %s%d, mem 0x%x, size %d, io %d, irq 0x%x, flags 0x%x\n",
+ drv.name, drv.unit, drv.mem, drv.memsize, drv.iobase,
+ drv.irqmask, drv.flags);
+ sprintf(name, "/dev/card%d", slot);
+ fd = open(name, 2);
+ if (fd < 0)
+ {
+ perror(name);
+ exit(1);
+ }
+/*
+ * Map the memory and I/O contexts.
+ */
+ if (drv.mem)
+ {
+ mem.window = 0;
+ mem.flags = MDF_ACTIVE|MDF_16BITS;
+ mem.start = (caddr_t)drv.mem;
+ mem.size = drv.memsize;
+ mem.card = card_addr;
+ if (ioctl(fd, PIOCSMEM, &mem))
+ {
+ perror("Set memory context");
+ exit(1);
+ }
+ }
+ if (drv.iobase)
+ {
+ io.window = 0;
+ io.flags = IODF_ACTIVE|IODF_CS16;
+ io.start = drv.iobase;
+ io.size = 32; /* Blah... */
+ if (ioctl(fd, PIOCSIO, &io))
+ {
+ perror("Set I/O context");
+ exit(1);
+ }
+ }
+ if (ioctl(fd, PIOCSDRV, &drv))
+ perror("set driver");
+ close(fd);
+}
+/*
+ * usage - print usage and exit
+ */
+void
+usage(msg)
+char *msg;
+{
+ fprintf(stderr, "enabler: %s\n", msg);
+ fprintf(stderr,
+"Usage: enabler slot driver [ -m addr size ] [ -a iobase ] [ -i irq ]\n");
+ fprintf(stderr,
+" -m card addr size : Card address (hex), host address (hex) & size (Kb)\n");
+ fprintf(stderr,
+" -a iobase : I/O port address (hex)\n");
+ fprintf(stderr,
+" -i irq : Interrupt request number (1-15)\n");
+ fprintf(stderr,
+" Example: enabler 0 ed0 -m 2000 d4000 16 -a 300 -i 3\n");
+ exit(1);
+}
diff --git a/usr.sbin/pccard/pccardc/pccardc.c b/usr.sbin/pccard/pccardc/pccardc.c
new file mode 100644
index 0000000..e789be2
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/pccardc.c
@@ -0,0 +1,59 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+typedef int (*main_t)(int , char **);
+
+#define DECL(foo) int foo(int, char**);
+DECL(dumpcis_main);
+DECL(enabler_main);
+DECL(help_main);
+DECL(pccardmem_main);
+DECL(rdmap_main);
+DECL(rdreg_main);
+DECL(wrattr_main);
+DECL(wrreg_main);
+
+struct {
+ char *name;
+ main_t func;
+ char *help;
+} subcommands[] = {
+
+{ "dumpcis", dumpcis_main, "Prints CIS for all cards"},
+{ "enabler", enabler_main, "Device driver enabler"},
+{ "help", help_main, "Prints command summary"},
+{ "pccardmem", pccardmem_main, "Allocate memory for pccard driver"},
+{ "rdmap", rdmap_main, "Read pcic mappings"},
+{ "rdreg", rdreg_main, "Read pcic register"},
+{ "wrattr", wrattr_main, "Write byte to attribute memory"},
+{ "wrreg", wrreg_main, "Write pcic register"},
+{0, 0}
+};
+
+int
+main(int argc, char **argv)
+{
+ int i;
+ for(i=0; argc > 1 && subcommands[i].name; i++) {
+ if (!strcmp(argv[1],subcommands[i].name)) {
+ argv[1] = argv[0];
+ return (*subcommands[i].func)(argc-1,argv+1);
+ }
+ }
+ if (argc > 1)
+ fprintf(stderr,"Unknown Subcommand.\n");
+ return help_main(argc,argv);
+}
+
+int
+help_main(int argc, char **argv)
+{
+ int i;
+ fprintf(stderr,"Usage:\n");
+ fprintf(stderr,"\t%s <subcommand> <arg> ...\n",argv[0]);
+ fprintf(stderr,"Subcommands:\n");
+ for(i=0; subcommands[i].name; i++)
+ fprintf(stderr,"\t%s\n\t\t%s\n",
+ subcommands[i].name, subcommands[i].help);
+ return 1;
+}
diff --git a/usr.sbin/pccard/pccardc/pccardmem.c b/usr.sbin/pccard/pccardc/pccardmem.c
new file mode 100644
index 0000000..3c75f13
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/pccardmem.c
@@ -0,0 +1,38 @@
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <pccard/card.h>
+int
+pccardmem_main(argc, argv)
+int argc;
+char *argv[];
+{
+int addr = 0;
+int fd;
+
+ if (argc > 2)
+ {
+ fprintf(stderr, "usage: %s [ memory-address ]\n", argv[0]);
+ exit(1);
+ }
+ fd = open("/dev/card0", 0);
+ if (fd < 0)
+ {
+ perror("/dev/card0");
+ exit(1);
+ }
+ if (argc == 2)
+ {
+ if (sscanf(argv[1], "%x", &addr) != 1)
+ {
+ fprintf(stderr, "arg error\n");
+ exit(1);
+ }
+ }
+ if (ioctl(fd, PIOCRWMEM, &addr))
+ perror("ioctl");
+ else
+ printf("PCCARD Memory address set to 0x%x\n", addr);
+ exit(0);
+}
diff --git a/usr.sbin/pccard/pccardc/printcis.c b/usr.sbin/pccard/pccardc/printcis.c
new file mode 100644
index 0000000..2ee3ca0
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/printcis.c
@@ -0,0 +1,711 @@
+/* set tab=4
+ * dump CIS tuples.
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+
+#include <pccard/card.h>
+#include <pccard/cis.h>
+
+#include "readcis.h"
+
+int dump_pwr_desc(unsigned char *);
+void print_ext_speed(unsigned char, int);
+
+void
+dumpcis(struct cis *cp)
+{
+struct tuple *tp;
+struct tuple_list *tl;
+int count = 0, sz, ad, i;
+unsigned char *p;
+
+ for (tl = cp->tlist; tl; tl = tl->next)
+ for (tp = tl->tuples; tp; tp = tp->next)
+ {
+ printf("Tuple #%d, code = 0x%x (%s), length = %d\n",
+ ++count, tp->code, tuple_name(tp->code), tp->length);
+ p = tp->data;
+ sz = tp->length;
+ ad = 0;
+ while (sz > 0)
+ {
+ printf(" %03x: ", ad);
+ for (i = 0; i < ((sz < 16) ? sz : 16); i++)
+ printf(" %02x", p[i]);
+ printf("\n");
+ sz -= 16;
+ p += 16;
+ ad += 16;
+ }
+ switch(tp->code)
+ {
+ default:
+ break;
+ case CIS_MEM_COMMON: /* 0x01 */
+ dump_device_desc(tp->data, tp->length, "Common");
+ break;
+ case CIS_CHECKSUM: /* 0x10 */
+ if (tp->length == 5)
+ {
+ printf("\tChecksum from offset %d, length %d, value is 0x%x\n",
+ (short)((tp->data[1] << 8) | tp->data[0]),
+ (tp->data[3] << 8) | tp->data[2],
+ tp->data[4]);
+ }
+ else
+ printf("\tIllegal length for checksum!\n");
+ break;
+ case CIS_LONGLINK_A: /* 0x11 */
+ printf("\tLong link to attribute memory, address 0x%x\n",
+ (tp->data[3] << 24) |
+ (tp->data[2] << 16) |
+ (tp->data[1] << 8) |
+ tp->data[0]);
+ break;
+ case CIS_LONGLINK_C: /* 0x12 */
+ printf("\tLong link to common memory, address 0x%x\n",
+ (tp->data[3] << 24) |
+ (tp->data[2] << 16) |
+ (tp->data[1] << 8) |
+ tp->data[0]);
+ break;
+ break;
+ case CIS_INFO_V1: /* 0x15 */
+ dump_info_v1(tp->data, tp->length);
+ break;
+ case CIS_ALTSTR: /* 0x16 */
+ break;
+ case CIS_MEM_ATTR: /* 0x17 */
+ dump_device_desc(tp->data, tp->length, "Attribute");
+ break;
+ case CIS_JEDEC_C: /* 0x18 */
+ break;
+ case CIS_JEDEC_A: /* 0x19 */
+ break;
+ case CIS_CONF_MAP: /* 0x1A */
+ dump_config_map(tp);
+ break;
+ case CIS_CONFIG: /* 0x1B */
+ dump_cis_config(tp);
+ break;
+ case CIS_DEVICE_OC: /* 0x1C */
+ dump_other_cond(tp->data);
+ break;
+ case CIS_DEVICE_OA: /* 0x1D */
+ dump_other_cond(tp->data);
+ break;
+ case CIS_DEVICEGEO: /* 0x1E */
+ break;
+ case CIS_DEVICEGEO_A: /* 0x1F */
+ break;
+ case CIS_MANUF_ID: /* 0x20 */
+ printf("\tPCMCIA ID = 0x%x, OEM ID = 0x%x\n",
+ (tp->data[1] << 8) | tp->data[0],
+ (tp->data[3] << 8) | tp->data[2]);
+ break;
+ case CIS_FUNC_ID: /* 0x21 */
+ switch(tp->data[0])
+ {
+ default:
+ printf("\tUnknown function");
+ break;
+ case 0:
+ printf("\tMultifunction card");
+ break;
+ case 1:
+ printf("\tMemory card");
+ break;
+ case 2:
+ printf("\tSerial port/modem");
+ break;
+ case 3:
+ printf("\tParallel port");
+ break;
+ case 4:
+ printf("\tFixed disk card");
+ break;
+ case 5:
+ printf("\tVideo adapter");
+ break;
+ case 6:
+ printf("\tNetwork/LAN adapter");
+ break;
+ case 7:
+ printf("\tAIMS");
+ break;
+ }
+ printf("%s%s\n", (tp->data[1] & 1) ? " - POST initialize" : "",
+ (tp->data[1] & 2) ? " - Card has ROM" : "");
+ break;
+ case CIS_FUNC_EXT: /* 0x22 */
+ dump_func_ext(tp->data, tp->length);
+ break;
+ case CIS_VERS_2: /* 0x40 */
+ break;
+ }
+ }
+}
+/*
+ * Dump configuration map tuple.
+ */
+dump_config_map(struct tuple *tp)
+{
+unsigned char *p, x;
+int rlen, mlen;
+int i;
+union {
+ unsigned long l;
+ unsigned char b[4];
+ }u;
+
+ rlen = (tp->data[0] & 3)+1;
+ mlen = ((tp->data[0] >> 2) & 3)+1;
+ u.l = 0;
+ p = tp->data + 2;
+ for (i = 0 ; i < rlen; i++)
+ u.b[i] = *p++;
+ printf("\tReg len = %d, config register addr = 0x%x, last config = 0x%x\n",
+ rlen, u.l, tp->data[1]);
+ if (mlen)
+ printf("\tRegisters: ");
+ for (i = 0; i < mlen; i++, p++)
+ {
+ for (x = 0x1; x; x <<= 1)
+ printf("%c", x & *p ? 'X' : '-');
+ printf(" ");
+ }
+ printf("\n");
+}
+/*
+ * Dump a config entry.
+ */
+dump_cis_config(struct tuple *tp)
+{
+unsigned char *p, feat;
+int i, j;
+char c;
+union {
+ unsigned long l;
+ unsigned char b[4];
+ }u;
+
+ p = tp->data;
+ printf("\tConfig index = 0x%x%s\n", *p & 0x3F,
+ *p & 0x40 ? "(default)" : "");
+ if (*p & 0x80)
+ {
+ p++;
+ printf("\tInterface byte = 0x%x ", *p);
+ switch (*p & 0xF)
+ {
+ default:
+ printf("(reserved)");
+ break;
+ case 0:
+ printf("(memory)");
+ break;
+ case 1:
+ printf("(I/O)");
+ break;
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ printf("(custom)");
+ break;
+ }
+ c = ' ';
+ if (*p & 0x10)
+ {
+ printf(" BVD1/2 active");
+ c = ',';
+ }
+ if (*p & 0x20)
+ {
+ printf("%c card WP active", c); /* Write protect */
+ c = ',';
+ }
+ if (*p & 0x40)
+ {
+ printf("%c +RDY/-BSY active", c);
+ c = ',';
+ }
+ if (*p & 0x80)
+ printf("%c wait signal supported", c);
+ printf("\n");
+ }
+ p++;
+ feat = *p++;
+ switch(feat & 3)
+ {
+ case 0:
+ break;
+ case 1:
+ printf("\tVcc pwr:\n");
+ p += dump_pwr_desc(p);
+ break;
+ case 2:
+ printf("\tVcc pwr:\n");
+ p += dump_pwr_desc(p);
+ printf("\tVpp pwr:\n");
+ p += dump_pwr_desc(p);
+ break;
+ case 3:
+ printf("\tVcc pwr:\n");
+ p += dump_pwr_desc(p);
+ printf("\tVpp1 pwr:\n");
+ p += dump_pwr_desc(p);
+ printf("\tVpp2 pwr:\n");
+ p += dump_pwr_desc(p);
+ break;
+ }
+ if (feat & 0x4)
+ {
+ i = *p & 3;
+ j = (*p >> 2) & 7;
+ p++;
+ if (i != 3)
+ {
+ printf("\tWait scale ");
+ print_ext_speed(*p, i);
+ while (*p & 0x80)
+ p++;
+ printf("\n");
+ }
+ if (j != 7)
+ {
+ printf("\tRDY/BSY scale ");
+ print_ext_speed(*p, j);
+ while (*p & 0x80)
+ p++;
+ printf("\n");
+ }
+ }
+ if (feat & 0x8)
+ {
+ if (*p & 0x1F)
+ printf("\tCard decodes %d address lines", *p & 0x1F);
+ else
+ printf("\tCard provides address decode");
+ switch((*p >> 5) & 3)
+ {
+ case 0:
+ break;
+ case 1:
+ printf(", 8 Bit I/O only");
+ break;
+ case 2:
+ printf(", limited 8/16 Bit I/O");
+ break;
+ case 3:
+ printf(", full 8/16 Bit I/O");
+ break;
+ }
+ printf("\n");
+ if (*p & 0x80)
+ {
+ p++;
+ c = *p++;
+ for (i = 0; i <= (c & 0xF); i++)
+ {
+ printf("\t\tI/O address # %d: ", i + 1);
+ switch ((c >> 4) & 3)
+ {
+ case 0:
+ break;
+ case 1:
+ printf("block start = 0x%x", *p++);
+ break;
+ case 2:
+ printf("block start = 0x%x", (p[1] << 8) | *p);
+ p += 2;
+ break;
+ case 3:
+ printf("block start = 0x%x",
+ (p[3] << 24) | (p[2] << 16) |
+ (p[1] << 8) | *p);
+ p += 4;
+ break;
+ }
+ switch ((c >> 6) & 3)
+ {
+ case 0:
+ break;
+ case 1:
+ printf(" block length = 0x%x", *p++ + 1);
+ break;
+ case 2:
+ printf(" block length = 0x%x", ((p[1] << 8) | *p)+1);
+ p += 2;
+ break;
+ case 3:
+ printf(" block length = 0x%x",
+ ((p[3] << 24) | (p[2] << 16) |
+ (p[1] << 8) | *p) + 1);
+ p += 4;
+ break;
+ }
+ printf("\n");
+ }
+ }
+ }
+/*
+ * IRQ descriptor
+ */
+ if (feat & 0x10)
+ {
+ printf("\t\tIRQ modes:");
+ c = ' ';
+ if (*p & 0x20)
+ {
+ printf(" Level");
+ c = ',';
+ }
+ if (*p & 0x40)
+ {
+ printf("%c Pulse", c);
+ c = ',';
+ }
+ if (*p & 0x80)
+ printf("%c Shared", c);
+ printf("\n");
+ if (*p & 0x10)
+ {
+ i = p[0] | (p[1] << 8);
+ printf("\t\tIRQs: ");
+ if (*p & 1)
+ printf(" NMI");
+ if (*p & 0x2)
+ printf(" IOCK");
+ if (*p & 0x4)
+ printf(" BERR");
+ if (*p & 0x8)
+ printf(" VEND");
+ for ( j = 0; j < 16; j++)
+ if (i & (1 << j))
+ printf(" %d", j);
+ printf("\n");
+ p += 3;
+ }
+ else
+ {
+ printf("\t\tIRQ level = %d\n", *p & 0xF);
+ p++;
+ }
+ }
+ switch((feat >> 5) & 3)
+ {
+ case 0:
+ break;
+ case 1:
+ printf("\tMemory space length = 0x%x\n", (p[1] << 8) | p[0]);
+ p += 2;
+ break;
+ case 2:
+ printf("\tMemory space address = 0x%x, length = 0x%x\n",
+ (p[3] << 8) | p[2],
+ (p[1] << 8) | p[0]);
+ p += 4;
+ break;
+/*
+ * Memory descriptors.
+ */
+ case 3:
+ c = *p++;
+ for (i = 0; i <= (c & 7); i++)
+ {
+ printf("\tMemory descriptor %d\n\t\t", i+1);
+ switch ((c >> 3) & 3)
+ {
+ case 0:
+ break;
+ case 1:
+ printf(" blk length = 0x%x00", *p++);
+ break;
+ case 2:
+ printf(" blk length = 0x%x00", (p[1] << 8) | *p);
+ p += 2;
+ break;
+ case 3:
+ printf(" blk length = 0x%x00",
+ (p[3] << 24) | (p[2] << 16) |
+ (p[1] << 8) | *p);
+ p += 4;
+ break;
+ }
+ switch ((c >> 5) & 3)
+ {
+ case 0:
+ break;
+ case 1:
+ printf(" card addr = 0x%x00", *p++);
+ break;
+ case 2:
+ printf(" card addr = 0x%x00", (p[1] << 8) | *p);
+ p += 2;
+ break;
+ case 3:
+ printf(" card addr = 0x%x00",
+ (p[3] << 24) | (p[2] << 16) |
+ (p[1] << 8) | *p);
+ p += 4;
+ break;
+ }
+ if (c & 0x80)
+ switch ((c >> 5) & 3)
+ {
+ case 0:
+ break;
+ case 1:
+ printf(" host addr = 0x%x00", *p++);
+ break;
+ case 2:
+ printf(" host addr = 0x%x00", (p[1] << 8) | *p);
+ p += 2;
+ break;
+ case 3:
+ printf(" host addr = 0x%x00",
+ (p[3] << 24) | (p[2] << 16) |
+ (p[1] << 8) | *p);
+ p += 4;
+ break;
+ }
+ printf("\n");
+ }
+ break;
+ }
+ if (feat & 0x80)
+ {
+ printf("\tMax twin cards = %d\n", *p & 7);
+ printf("\tMisc attr:");
+ if (*p & 0x8)
+ printf(" (Audio-BVD2)");
+ if (*p & 0x10)
+ printf(" (Read-only)");
+ if (*p & 0x20)
+ printf(" (Power down supported)");
+ if (*p & 0x80)
+ {
+ printf(" (Ext byte = 0x%x)", p[1]);
+ p++;
+ }
+ printf("\n");
+ p++;
+ }
+}
+/*
+ * dump_other_cond - Dump other conditions.
+ */
+dump_other_cond(unsigned char *p)
+{
+ if (p[0])
+ {
+ printf("\t");
+ if (p[0] & 1)
+ printf("(MWAIT)");
+ if (p[0] & 2)
+ printf(" (3V card)");
+ if (p[0] & 0x80)
+ printf(" (Extension bytes follow)");
+ printf("\n");
+ }
+}
+/*
+ * Dump power descriptor.
+ */
+int
+dump_pwr_desc(unsigned char *p)
+{
+int len = 1, i;
+unsigned char mask;
+char **expp;
+static char *pname[] =
+ { "Nominal operating supply voltage",
+ "Minimum operating supply voltage",
+ "Maximum operating supply voltage",
+ "Continuous supply current",
+ "Max current average over 1 second",
+ "Max current average over 10 ms",
+ "Power down supply current",
+ "Reserved"
+ };
+static char *vexp[] =
+ { "10uV", "100uV", "1mV", "10mV", "100mV", "1V", "10V", "100V" };
+static char *cexp[] =
+ { "10nA", "1uA", "10uA", "100uA", "1mA", "10mA", "100mA", "1A" };
+static char *mant[] =
+ { "1", "1.2", "1.3", "1.5", "2", "2.5", "3", "3.5", "4", "4.5",
+ "5", "5.5", "6", "7", "8", "9" };
+
+ mask = *p++;
+ expp = vexp;
+ for (i = 0; i < 8; i++)
+ if (mask & (1 << i))
+ {
+ len++;
+ if (i >= 3)
+ expp = cexp;
+ printf("\t\t%s: ", pname[i]);
+ printf("%s x %s",
+ mant[(*p >> 3) & 0xF],
+ expp[*p & 7]);
+ while (*p & 0x80)
+ {
+ len++;
+ p++;
+ printf(", ext = 0x%x", *p);
+ }
+ printf("\n");
+ p++;
+ }
+ return(len);
+}
+
+dump_device_desc(unsigned char *p, int len, char *type)
+{
+static char *un_name[] =
+ { "512b", "2Kb", "8Kb", "32Kb",
+ "128Kb", "512Kb", "2Mb", "reserved"};
+static char *speed[] =
+ { "No speed", "250nS", "200nS", "150nS",
+ "100nS", "Reserved", "Reserved" };
+static char *dev[] =
+ { "No device", "Mask ROM", "OTPROM", "UV EPROM",
+ "EEPROM", "FLASH EEPROM", "SRAM", "DRAM",
+ "Reserved", "Reserved", "Reserved", "Reserved",
+ "Reserved", "Function specific", "Extended",
+ "Reserved" };
+int count = 0;
+
+ while (*p != 0xFF && len > 0)
+ {
+ unsigned char x;
+
+ x = *p++;
+ len -= 2;
+ if (count++ == 0)
+ printf("\t%s memory device information:\n", type);
+ printf("\t\tDevice number %d, type %s, WPS = %s\n",
+ count, dev[x >> 4], (x & 0x8) ? "ON" : "OFF");
+ if ((x & 7) == 7)
+ {
+ len--;
+ if (*p)
+ {
+ printf("\t\t");
+ print_ext_speed(*p, 0);
+ while (*p & 0x80)
+ {
+ p++;
+ len--;
+ }
+ }
+ p++;
+ }
+ else
+ printf("\t\tSpeed = %s", speed[x & 7]);
+ printf(", Memory block size = %s, %d units\n",
+ un_name[*p & 7], (*p >> 3) + 1);
+ p++;
+ }
+}
+/*
+ * Print version info
+ */
+dump_info_v1(unsigned char *p, int len)
+{
+ printf("\tVersion = %d.%d", p[0], p[1]);
+ p += 2;
+ printf(", Manuf = [%s],", p);
+ while (*p++)
+ ;
+ printf("card vers = [%s]\n", p);
+ while (*p++)
+ ;
+ printf("\tAddit. info = [%s]", p);
+ while (*p++)
+ ;
+ printf(",[%s]\n", p);
+}
+/*
+ * dump functional extension tuple.
+ */
+dump_func_ext(unsigned char *p, int len)
+{
+ if (len == 0)
+ return;
+ switch(p[0])
+ {
+ case 0:
+ case 8:
+ case 10:
+ if (len != 4)
+ {
+ printf("\tWrong length for serial extension\n");
+ return;
+ }
+ printf("\tSerial interface extension:\n");
+ switch(p[1] & 0x1F)
+ {
+ default:
+ printf("\t\tUnkn device");
+ break;
+ case 0:
+ printf("\t\t8250 UART");
+ break;
+ case 1:
+ printf("\t\t16450 UART");
+ break;
+ case 2:
+ printf("\t\t16550 UART");
+ break;
+ }
+ printf(", Parity - %s%s%s%s",
+ (p[2] & 1) ? "Space," : "",
+ (p[2] & 2) ? "Mark," : "",
+ (p[2] & 4) ? "Odd," : "",
+ (p[2] & 8) ? "Even," : "");
+ printf("\n");
+ break;
+ case 1:
+ case 5:
+ case 6:
+ case 7:
+ printf("\tModem interface capabilities:\n");
+ break;
+ case 2:
+ printf("\tData modem services available:\n");
+ break;
+ case 9:
+ printf("\tFax/modem services available:\n");
+ break;
+ case 4:
+ printf("\tVoice services available:\n");
+ break;
+ }
+}
+/*
+ * print_ext_speed - Print extended speed.
+ */
+void
+print_ext_speed(unsigned char x, int scale)
+{
+static char *mant[] =
+ { "Reserved", "1.0", "1.2", "1.3", "1.5", "2.0", "2.5", "3.0",
+ "3.5", "4.0", "4.5", "5.0", "5.5", "6.0", "7.0", "8.0" };
+static char *exp[] =
+ { "1 ns", "10 ns", "100 ns", "1 us", "10 us", "100 us",
+ "1 ms", "10 ms" };
+static char *scale_name[] =
+ { "None", "10", "100", "1,000", "10,000", "100,000",
+ "1,000,000", "10,000,000" };
+
+ printf("Speed = %s x %s", mant[(x >> 3) & 0xF], exp[x & 7]);
+ if (scale)
+ printf(", scaled by %s", scale_name[scale & 7]);
+}
diff --git a/usr.sbin/pccard/pccardc/rdmap.c b/usr.sbin/pccard/pccardc/rdmap.c
new file mode 100644
index 0000000..bfcce1f
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/rdmap.c
@@ -0,0 +1,71 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+
+#include <pccard/card.h>
+#include <pccard/cis.h>
+
+int
+rdmap_main(argc, argv)
+int argc;
+char *argv[];
+{
+int node, mask;
+struct card *cp;
+
+ for (node = 0; node < 8; node++)
+ scan(node);
+ exit(0);
+}
+static scan(slot)
+int slot;
+{
+int fd, mask;
+char blk[1024];
+char name[64];
+struct slotstate st;
+
+ sprintf(name, "/dev/card%d", slot);
+ fd = open(name, 0);
+ if (fd < 0)
+ return;
+ ioctl(fd, PIOCGSTATE, &st);
+/*
+ if (st.state == filled)
+ */
+ {
+ dump_mem(fd, st.maxmem);
+ dump_io(fd, st.maxio);
+ }
+ close(fd);
+}
+dump_mem(fd, nmem)
+int fd, nmem;
+{
+struct mem_desc mem;
+int i;
+
+ for (i = 0; i < nmem; i++)
+ {
+ mem.window = i;
+ ioctl(fd, PIOCGMEM, &mem);
+printf("Mem %d: flags 0x%03x host 0x%6x card %04x size %d bytes\n",
+ mem.window, mem.flags, mem.start, mem.card, mem.size);
+ }
+}
+dump_io(fd, nio)
+int fd, nio;
+{
+struct io_desc io;
+int i;
+
+ for (i = 0; i < nio; i++)
+ {
+ io.window = i;
+ ioctl(fd, PIOCGIO, &io);
+printf("I/O %d: flags 0x%03x port 0x%3x size %d bytes\n",
+ io.window, io.flags, io.start, io.size);
+ }
+}
diff --git a/usr.sbin/pccard/pccardc/rdreg.c b/usr.sbin/pccard/pccardc/rdreg.c
new file mode 100644
index 0000000..c278ae5
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/rdreg.c
@@ -0,0 +1,48 @@
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <pccard/card.h>
+int
+rdreg_main(argc, argv)
+int argc;
+char *argv[];
+{
+ if (argc != 2)
+ {
+ dumpslot(0);
+ dumpslot(1);
+ }
+ else
+ dumpslot(atoi(argv[1]));
+}
+dumpslot(sl)
+int sl;
+{
+char name[64];
+int fd;
+struct pcic_reg r;
+
+ sprintf(name, "/dev/card%d", sl);
+ fd = open(name, 2);
+ if (fd < 0)
+ {
+ perror(name);
+ return;
+ }
+ printf("Registers for slot %d\n", sl);
+ for (r.reg = 0; r.reg < 0x40; r.reg++)
+ {
+ if (ioctl(fd, PIOCGREG, &r))
+ {
+ perror("ioctl");
+ break;
+ }
+ if ((r.reg % 16)==0)
+ printf("%02x:", r.reg);
+ printf(" %02x", r.value);
+ if ((r.reg % 16)==15)
+ printf("\n");
+ }
+ close(fd);
+}
diff --git a/usr.sbin/pccard/pccardc/wrattr.c b/usr.sbin/pccard/pccardc/wrattr.c
new file mode 100644
index 0000000..d8e980f
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/wrattr.c
@@ -0,0 +1,46 @@
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <pccard/card.h>
+
+int
+wrattr_main(argc, argv)
+int argc;
+char *argv[];
+{
+int reg,value;
+char name[64], c;
+int fd;
+off_t offs;
+
+ if (argc != 4)
+ {
+ fprintf(stderr, "usage: wrmem slot offs value\n");
+ exit(1);
+ }
+ sprintf(name, "/dev/card%d", atoi(argv[1]));
+ fd = open(name, 2);
+ if (fd < 0)
+ {
+ perror(name);
+ exit(1);
+ }
+ reg = MDF_ATTR;
+ if (ioctl(fd, PIOCRWFLAG, &reg))
+ {
+ perror("ioctl (PIOCRWFLAG)");
+ exit(1);
+ }
+ if (sscanf(argv[2], "%x", &reg) != 1 ||
+ sscanf(argv[3], "%x", &value) != 1)
+ {
+ fprintf(stderr, "arg error\n");
+ exit(1);
+ }
+ offs = reg;
+ c = value;
+ lseek(fd, offs, SEEK_SET);
+ if (write(fd, &c, 1) != 1)
+ perror(name);
+}
diff --git a/usr.sbin/pccard/pccardc/wrreg.c b/usr.sbin/pccard/pccardc/wrreg.c
new file mode 100644
index 0000000..88a54ba
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/wrreg.c
@@ -0,0 +1,39 @@
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <pccard/card.h>
+
+int
+wrreg_main(argc, argv)
+int argc;
+char *argv[];
+{
+int reg,value;
+char name[64];
+int fd;
+struct pcic_reg r;
+
+ if (argc != 4)
+ {
+ fprintf(stderr, "usage: wrreg slot reg value\n");
+ exit(1);
+ }
+ sprintf(name, "/dev/card%d", atoi(argv[1]));
+ fd = open(name, 2);
+ if (fd < 0)
+ {
+ perror(name);
+ exit(1);
+ }
+ if (sscanf(argv[2], "%x", &reg) != 1 ||
+ sscanf(argv[3], "%x", &value) != 1)
+ {
+ fprintf(stderr, "arg error\n");
+ exit(1);
+ }
+ r.reg = reg;
+ r.value = value;
+ if (ioctl(fd, PIOCSREG, &r))
+ perror("ioctl");
+}
diff --git a/usr.sbin/pccard/pccardd/Makefile b/usr.sbin/pccard/pccardd/Makefile
new file mode 100644
index 0000000..4a2c192
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/Makefile
@@ -0,0 +1,10 @@
+# Makefile for pccardd
+
+PROG= pccardd
+SRCS= cardd.c file.c util.c readcis.c
+MAN8= pccardd.8
+MAN5= pccard.conf.5
+DPADD= ${LIBUTIL}
+LDADD= -lutil
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pccard/pccardd/cardd.c b/usr.sbin/pccard/pccardd/cardd.c
new file mode 100644
index 0000000..ce97520
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/cardd.c
@@ -0,0 +1,701 @@
+#define HERE() printf("<%d>\n",__LINE__)
+/*
+ * pcmciad
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <syslog.h>
+#include <varargs.h>
+#include "cardd.h"
+
+
+char *config_file = "/etc/card.conf";
+
+struct card_config *assign_driver(struct card *);
+int setup_slot(struct slot *);
+void read_ether(struct slot *);
+void dump_config_file();
+void pr_cmd(struct cmd *);
+void readslots();
+void slot_change(struct slot *);
+void card_removed(struct slot *);
+void card_inserted(struct slot *);
+
+/*
+ * mainline code for cardd
+ */
+main(int argc, char *argv[])
+{
+struct slot *sp;
+int mask, count, debug = 0, err = 0;
+int verbose = 0;
+extern char *optarg;
+extern int optind, optopt;
+
+ while ((count = getopt(argc, argv, ":dvf:")) != -1) {
+ switch(count) {
+ case 'd':
+ setbuf(stdout,0);
+ setbuf(stderr,0);
+ debug = 1;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 'f':
+ config_file = optarg;
+ break;
+ case ':':
+ die("No config file argument");
+ break;
+ case '?':
+ die("Illegal option");
+ break;
+ }
+ }
+#ifdef DEBUG
+ debug = 1;
+#endif
+ io_avail = bit_alloc(IOPORTS); /* Only supports ISA ports */
+/*
+ * Mem allocation done in MEMUNIT units.
+ */
+ mem_avail = bit_alloc(MEMBLKS);
+ readfile(config_file);
+ if (verbose)
+ dump_config_file();
+ if (!debug)
+ {
+ if (daemon(0, 0))
+ die("fork failed");
+ openlog("cardd", LOG_PID, LOG_DAEMON);
+ do_log = 1;
+ }
+ printf("Before readslots\n");
+ readslots();
+ printf("After readslots\n");
+ if (slots == 0)
+ die("No PC-CARD slots");
+ for (;;)
+ {
+ mask = 0;
+ for (sp = slots; sp; sp = sp->next)
+ mask |= sp->mask;
+printf("Doing select\n");
+ count = select(32, 0, 0, &mask, 0);
+ if (count == -1)
+ {
+ perror("Select");
+ continue;
+ }
+ if (count)
+ for (sp = slots; sp; sp = sp->next)
+ if (mask & sp->mask)
+ slot_change(sp);
+ }
+}
+/*
+ * Dump configuration file data.
+ */
+void
+dump_config_file()
+{
+struct driver *drvp;
+struct device *devp;
+struct card *cp;
+struct card_config *confp;
+
+ for (cp = cards; cp; cp = cp->next)
+ {
+ printf("Card manuf %s, vers %s\n", cp->manuf, cp->version);
+ printf("Configuration entries:\n");
+ for (confp = cp->config; confp; confp = confp->next)
+ printf("\tIndex code = 0x%x, driver name = %s\n",
+ confp->index, confp->driver->name);
+ if (cp->insert)
+ {
+ printf("Insert commands are:\n");
+ pr_cmd(cp->insert);
+ }
+ if (cp->remove)
+ {
+ printf("Remove commands are:\n");
+ pr_cmd(cp->remove);
+ }
+ }
+#if 0
+ for (devp = devlist; devp; devp = devp->next)
+ {
+ if (devp->insert)
+ {
+ printf("Insert commands are:\n");
+ pr_cmd(devp->insert);
+ }
+ if (devp->remove)
+ {
+ printf("Remove commands are:\n");
+ pr_cmd(devp->remove);
+ }
+ }
+#endif
+}
+void
+pr_cmd(struct cmd *cp)
+{
+ while (cp)
+ {
+ printf("\t%s\n", cp->line);
+ cp = cp->next;
+ }
+}
+/*
+ * readslots - read all the PCMCIA slots, and build
+ * a list of the slots.
+ */
+void
+readslots()
+{
+char name[128];
+int i, fd;
+struct slot *sp;
+
+ for (i = 0; i < MAXSLOT; i++)
+ {
+ sprintf(name, CARD_DEVICE, i);
+ fd = open(name, 2);
+ if (fd < 0)
+ continue;
+printf("opened %s\n",name);
+ sp = xmalloc(sizeof(*sp));
+ sp->fd = fd;
+ sp->mask = 1 << fd;
+ sp->name = newstr(name);
+ sp->slot = i;
+ sp->state = empty;
+/*
+ * Check to see if the controller memory has been set up.
+ */
+ if (slots == 0)
+ {
+ unsigned long mem = 0;
+
+HERE();
+ if (ioctl(fd, PIOCRWMEM, &mem))
+ perror("ioctl (PIOCRWMEM)");
+ if (mem == 0)
+ {
+HERE();
+ mem = alloc_memory(4*1024);
+HERE();
+ if (mem == 0)
+ die("Can't allocate memory for controller access");
+ if (ioctl(fd, PIOCRWMEM, &mem))
+ perror("ioctl (PIOCRWMEM)");
+ }
+ }
+ sp->next = slots;
+ slots = sp;
+HERE();
+#if 0
+ slot_change(sp);
+#endif
+HERE();
+ }
+}
+/*
+ * slot_change - Card status has changed.
+ * read new state and process.
+ */
+void
+slot_change(struct slot *sp)
+{
+int state;
+
+ current_slot = sp;
+HERE();
+ if (ioctl(sp->fd, PIOCGSTATE, &state))
+ {
+ perror("ioctl (PIOCGSTATE)");
+ return;
+ }
+HERE();
+ if (state == sp->state)
+ return;
+HERE();
+ sp->state = state;
+HERE();
+ switch (sp->state)
+ {
+ case empty:
+ case noslot:
+HERE();
+ card_removed(sp);
+HERE();
+ break;
+ case filled:
+HERE();
+ card_inserted(sp);
+HERE();
+ break;
+ }
+}
+/*
+ * card_removed - card has been removed from slot.
+ * Execute the remove commands, and clear the slot's state.
+ * Execute the device commands, then the driver commands
+ * and then the card commands. This is the reverse
+ * order to the insertion commands
+ */
+void
+card_removed(struct slot *sp)
+{
+struct driver *drvp;
+struct card *cp;
+
+HERE();
+ if (sp->cis)
+ freecis(sp->cis);
+ if (sp->config)
+ {
+ sp->config->inuse = 0;
+ sp->config->driver->inuse = 0;
+ }
+HERE();
+ if (cp = sp->card)
+ execute(cp->remove);
+HERE();
+ sp->cis = 0;
+ sp->config = 0;
+HERE();
+}
+/*
+ * card_inserted - Card has been inserted;
+ * - Read the CIS
+ * - match the card type.
+ * - Match the driver and allocate a driver instance.
+ * - Allocate I/O ports, memory and IRQ.
+ * - Set up the slot.
+ * - assign the driver (if failed, then terminate).
+ * - Run the card commands.
+ * - Run the driver commands
+ * - Run the device commands
+ */
+void
+card_inserted(struct slot *sp)
+{
+struct card *cp;
+
+ sp->cis = readcis(sp->fd);
+ if (sp->cis == 0)
+ {
+ log_1s("Error reading CIS on %s\n", sp->name);
+ return;
+ }
+ for (cp = cards; cp; cp = cp->next)
+ if (strcmp(cp->manuf, sp->cis->manuf) == 0 &&
+ strcmp(cp->version, sp->cis->vers) == 0)
+ break;
+ sp->card = cp;
+/*
+ reset_slot(sp);
+ */
+ if (cp == 0)
+ {
+ log_1s("No card in database for %s", sp->cis->manuf);
+ return;
+ }
+ if (cp->ether)
+ read_ether(sp);
+ sp->config = assign_driver(cp);
+ if (sp->config == 0)
+ {
+ execute(cp->insert);
+ return;
+ }
+ if (assign_io(sp))
+ {
+ log_1s("Resource allocation failure for %s", sp->cis->manuf);
+ return;
+ }
+/*
+ * Once assigned, then set up the I/O & mem contexts, and
+ * set up the windows, and then attach the driver.
+ */
+ if (setup_slot(sp))
+ execute(cp->insert);
+#if 0
+ else
+ reset_slot(sp);
+#endif
+}
+/*
+ * read_ether - read ethernet address from card. Offset is
+ * the offset into the attribute memory of the card.
+ */
+void
+read_ether(struct slot *sp)
+{
+unsigned char net_addr[12], *p;
+
+ lseek(sp->fd, (off_t)sp->card->ether, SEEK_SET);
+ if (read(sp->fd, net_addr, sizeof(net_addr)) != sizeof(net_addr))
+ {
+ logerr("read err on net addr");
+ return;
+ }
+ sp->eaddr[0] = net_addr[0];
+ sp->eaddr[1] = net_addr[2];
+ sp->eaddr[2] = net_addr[4];
+ sp->eaddr[3] = net_addr[6];
+ sp->eaddr[4] = net_addr[8];
+ sp->eaddr[5] = net_addr[10];
+}
+/*
+ * assign_driver - Assign driver to card.
+ * First, see if an existing driver is already setup.
+ */
+struct card_config *
+assign_driver(struct card *cp)
+{
+struct driver *drvp;
+struct card_config *conf;
+
+ for (conf = cp->config; conf; conf = conf->next)
+ if (conf->inuse == 0 && conf->driver->card == cp &&
+ conf->driver->config == conf)
+ {
+#ifdef DEBUG
+ fprintf(stderr, "Found existing driver (%s) for %s\n",
+ conf->driver->name, cp->manuf);
+#endif /* DEBUG */
+ return(conf);
+ }
+/*
+ * New driver must be allocated. Find one that matches the
+ * any configurations not in use.
+ */
+ for (conf = cp->config; conf; conf = conf->next)
+ if (conf->inuse == 0 && conf->driver->card == 0)
+ break;
+ if (conf == 0)
+ {
+ log_1s("No free configuration for card %s", cp->manuf);
+ return(0);
+ }
+/*
+ * Now we have a free driver and a matching configuration.
+ * Before assigning and allocating everything, check to
+ * see if a device class can be allocated to this.
+ */
+ drvp = conf->driver;
+/*
+ * If none available, then we can't use this card.
+ */
+ if (drvp->inuse)
+ {
+ log_1s("Driver already being used for %s", cp->manuf);
+ return(0);
+ }
+#if 0
+/*
+ * Allocate I/O, memory and IRQ resources.
+ */
+ for (ap = drvp->io; ap; ap = ap->next)
+ {
+ if (ap->addr == 0 && ap->size)
+ {
+ int i = bit_fns(io_avail, IOPORTS, ap->size);
+
+ if (i < 0)
+ {
+ log_1s("Failed to allocate I/O ports for %s\n",
+ cp->manuf);
+ return(0);
+ }
+ ap->addr = i;
+ bit_nclear(io_avail, i, ap->size);
+ }
+ }
+ if (drvp->irq == 0)
+ {
+ int i;
+ for (i = 1; i < 16; i++)
+ if (pool_irq[i])
+ {
+ drvp->irq = i;
+ pool_irq[i] = 0;
+ break;
+ }
+ if (drvp->irq == 0)
+ {
+ log_1s("Failed to allocate IRQ for %s\n", cp->manuf);
+ return(0);
+ }
+ }
+ for (ap = drvp->mem; ap; ap = ap->next)
+ {
+ if (ap->addr == 0 && ap->size)
+ {
+ ap->addr = alloc_memory(ap->size);
+ if (ap->addr == 0)
+ {
+ log_1s("Failed to allocate memory for %s\n",
+ cp->manuf);
+ return(0);
+ }
+ }
+ }
+#endif /* 0 */
+ drvp->card = cp;
+ drvp->config = conf;
+ drvp->inuse = 1;
+ conf->inuse = 1;
+ return(conf);
+}
+/*
+ * assign_io - Allocate resources to slot matching the
+ * configuration index selected.
+ */
+int
+assign_io(struct slot *sp)
+{
+struct cis *cis;
+struct cis_config *cisconf, *defconf;
+
+ cis = sp->cis;
+ defconf = cis->def_config;
+ for (cisconf = cis->conf; cisconf; cisconf = cisconf->next)
+ if (cisconf->id == sp->config->index)
+ break;
+ if (cisconf == 0)
+ return(-1);
+ sp->card_config = cisconf;
+/*
+ * Found a matching configuration. Now look at the I/O, memory and IRQ
+ * to create the desired parameters. Look at memory first.
+ */
+ if (cisconf->memspace || (defconf && defconf->memspace))
+ {
+ struct cis_memblk *mp;
+
+ mp = cisconf->mem;
+ if (!cisconf->memspace)
+ mp = defconf->mem;
+ sp->mem.size = mp->length;
+ sp->mem.cardaddr = mp->address;
+/*
+ * For now, we allocate our own memory from the pool.
+ */
+ sp->mem.addr = sp->config->driver->mem;
+/*
+ * Host memory address is required. Allocate one
+ * from our pool.
+ */
+ if (sp->mem.size && sp->mem.addr == 0)
+ {
+ sp->mem.addr = alloc_memory(mp->length);
+ if (sp->mem.addr == 0)
+ return(-1);
+ sp->config->driver->mem = sp->mem.addr;
+ }
+#ifdef DEBUG
+ fprintf(stderr, "Using mem addr 0x%x, size %d, card addr 0x%x\n",
+ sp->mem.addr, sp->mem.cardaddr, sp->mem.size);
+#endif /* DEBUG */
+ }
+/*
+ * Now look at I/O.
+ */
+ bzero(&sp->io, sizeof(sp->io));
+ if (cisconf->iospace || (defconf && defconf->iospace))
+ {
+ struct cis_config *cp;
+
+ cp = cisconf;
+ if (!cisconf->iospace)
+ cp = defconf;
+/*
+ * If # of I/O lines decoded == 10, then card does its
+ * own decoding.
+ */
+/*
+ * If an I/O block exists, then use it.
+ * If no address (but a length) is available, allocate
+ * from the pool.
+ */
+ if (cp->io)
+ {
+ sp->io.addr = cp->io->addr;
+ sp->io.size = cp->io->size;
+ }
+/*
+ * No I/O block, assume the address lines decode gives the size.
+ */
+ else
+ sp->io.size = 1 << cp->io_addr;
+ if (sp->io.addr == 0)
+ {
+ int i = bit_fns(io_avail, IOPORTS, sp->io.size);
+
+ if (i < 0)
+ return(-1);
+ sp->io.addr = i;
+ }
+ bit_nclear(io_avail, sp->io.addr, sp->io.size);
+/*
+ * Set up the size to take into account the decode lines.
+ */
+ sp->io.cardaddr = cp->io_addr;
+ switch(cp->io_bus)
+ {
+ case 0:
+ break;
+ case 1:
+ sp->io.flags = IODF_WS;
+ break;
+ case 2:
+ sp->io.flags = IODF_WS|IODF_CS16;
+ break;
+ case 3:
+ sp->io.flags = IODF_WS|IODF_CS16|IODF_16BIT;
+ break;
+ }
+#ifdef DEBUG
+ fprintf(stderr, "Using I/O addr 0x%x, size %d\n",
+ sp->io.addr, sp->io.size);
+#endif /* DEBUG */
+ }
+ sp->irq = sp->config->irq;
+ return(0);
+}
+/*
+ * setup_slot - Allocate the I/O and memory contexts
+ * return true if completed OK.
+ */
+int
+setup_slot(struct slot *sp)
+{
+struct mem_desc mem;
+struct io_desc io;
+struct drv_desc drv;
+struct allocblk *ap;
+struct driver *drvp = sp->config->driver;
+char c;
+off_t offs;
+int rw_flags;
+
+ offs = sp->cis->reg_addr;
+ rw_flags = MDF_ATTR;
+ ioctl(sp->fd, PIOCRWFLAG, &rw_flags);
+ lseek(sp->fd, offs, SEEK_SET);
+ c = 0x80;
+ write(sp->fd, &c, sizeof(c));
+ usleep(sp->card->reset_time*1000);
+ lseek(sp->fd, offs, SEEK_SET);
+ c = 0x00;
+ write(sp->fd, &c, sizeof(c));
+ usleep(sp->card->reset_time*1000);
+ lseek(sp->fd, offs, SEEK_SET);
+ c = sp->config->index;
+ write(sp->fd, &c, sizeof(c));
+#ifdef DEBUG
+ printf("Setting config reg at offs 0x%x to 0x%x\n",
+ sp->cis->reg_addr, c);
+ printf("Reset time = %d ms\n", sp->card->reset_time);
+#endif
+ usleep(sp->card->reset_time*1000);
+/*
+ * If other config registers exist, set them up.
+ */
+ if (sp->cis->ccrs & 2) /* CCSR */
+ {
+ c = 0;
+ if (sp->cis->def_config && sp->cis->def_config->misc_valid &&
+ (sp->cis->def_config->misc & 0x8))
+ c |= 0x08;
+ if (sp->card_config->io_bus == 1)
+ c |= 0x20;
+ lseek(sp->fd, offs+2, SEEK_SET);
+ write(sp->fd, &c, sizeof(c));
+#ifdef DEBUG
+ printf("Setting CCSR reg to 0x%x\n", c);
+#endif
+ }
+ mem.window = 0;
+ if (sp->mem.size)
+ {
+ mem.window = 0;
+ mem.flags = sp->mem.flags;
+ mem.start = (caddr_t)sp->mem.addr;
+ mem.card = sp->mem.cardaddr;
+ mem.size = sp->mem.size;
+ if (ioctl(sp->fd, PIOCSMEM, &mem))
+ {
+ logerr("ioctl (PIOCSMEM)");
+ return(0);
+ }
+ }
+ io.window = 0;
+ if (sp->io.size)
+ {
+ io.flags = sp->io.flags;
+ io.start = sp->io.addr;
+ io.size = sp->io.size;
+/*
+ io.start = sp->io.addr & ~((1 << sp->io.cardaddr)-1);
+ io.size = 1 << sp->io.cardaddr;
+ if (io.start < 0x100)
+ {
+ io.start = 0x100;
+ io.size = 0x300;
+ }
+ */
+#ifdef DEBUG
+ printf("Assigning I/O window 0, start 0x%x, size 0x%x\n",
+ io.start, io.size);
+#endif
+ if (ioctl(sp->fd, PIOCSIO, &io))
+ {
+ logerr("ioctl (PIOCSIO)");
+ return(0);
+ }
+ }
+ strcpy(drv.name, drvp->kernel);
+ drv.unit = drvp->unit;
+ drv.irqmask = 1 << sp->irq;
+ if (sp->mem.size)
+ {
+ drv.mem = sp->mem.addr;
+ drv.memsize = sp->mem.size;
+ }
+ else
+ {
+ drv.mem = 0;
+ drv.memsize = 0;
+ }
+ if (sp->io.size)
+ drv.iobase = sp->io.addr;
+ else
+ drv.iobase = 0;
+#ifdef DEBUG
+ fprintf(stderr, "Assign %s%d, io 0x%x, mem 0x%x, %d bytes, irq %x\n",
+ drv.name, drv.unit, drv.iobase, drv.mem, drv.memsize, drv.irqmask);
+#endif /* DEBUG */
+/*
+ * If the driver fails to be connected to the device,
+ * then it may mean that the driver did not recognise it.
+ */
+ if (ioctl(sp->fd, PIOCSDRV, &drv))
+ {
+#ifdef DEBUG
+ perror(sp->card->manuf);
+#endif
+ log_1s("driver allocation failed for %s", sp->card->manuf);
+ return(0);
+ }
+ return(1);
+}
diff --git a/usr.sbin/pccard/pccardd/cardd.h b/usr.sbin/pccard/pccardd/cardd.h
new file mode 100644
index 0000000..88561f5
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/cardd.h
@@ -0,0 +1,129 @@
+
+/*
+ * Common include file for PCMCIA daemon
+ */
+#include <bitstring.h>
+
+#include <pccard/card.h>
+#include <pccard/cis.h>
+
+#include "readcis.h"
+
+struct cmd
+ {
+ struct cmd *next;
+ char *line; /* Command line */
+ int macro; /* Contains macros */
+ };
+
+struct card_config
+ {
+ struct card_config *next;
+ unsigned char index;
+ struct driver *driver;
+ int irq;
+ int flags;
+ char inuse;
+ };
+
+struct card
+ {
+ struct card *next;
+ char *manuf;
+ char *version;
+ int ether; /* For net cards, ether at offset */
+ int reset_time; /* Reset time */
+ struct card_config *config; /* List of configs */
+ struct cmd *insert; /* Insert commands */
+ struct cmd *remove; /* Remove commands */
+ };
+
+struct driver
+ {
+ struct driver *next;
+ char *name;
+ char *kernel; /* Kernel driver base name */
+ int unit; /* Unit of driver */
+/*
+ * The rest of the structure is allocated dynamically.
+ * Once allocated, it stays allocated.
+ */
+ struct card *card; /* Current card, if any */
+ struct card_config *config; /* Config back ptr */
+/* struct device *device;*/ /* System device info */
+ unsigned int mem; /* Allocated host address (if any) */
+ int inuse;
+ };
+#if 0
+struct device
+ {
+ struct device *next; /* List of devices */
+ int inuse; /* Driver being used */
+ struct cmd *insert; /* Insert commands */
+ struct cmd *remove; /* Remove commands */
+ };
+#endif
+
+/*
+ * Defines one allocation block i.e a starting address
+ * and size. Used for either memory or I/O ports
+ */
+struct allocblk
+ {
+ struct allocblk *next;
+ int addr; /* Address */
+ int size; /* Size */
+ int flags; /* Flags for block */
+ int cardaddr; /* Card address */
+ };
+/*
+ * Slot structure - data held for each slot.
+ */
+struct slot
+ {
+ struct slot *next;
+ int fd;
+ int mask;
+ int slot;
+ char *name;
+ enum cardstate state;
+ struct cis *cis;
+ struct card *card; /* Current card */
+ struct card_config *config; /* Current configuration */
+ struct cis_config *card_config;
+ char devname[16];
+ unsigned char eaddr[6]; /* If any */
+ struct allocblk io; /* I/O block spec */
+ struct allocblk mem; /* Memory block spec */
+ int irq; /* Irq value */
+ };
+
+struct slot *slots, *current_slot;
+
+struct allocblk *pool_ioblks; /* I/O blocks in the pool */
+struct allocblk *pool_mem; /* Memory in the pool */
+int pool_irq[16]; /* IRQ allocations */
+struct driver *drivers; /* List of drivers */
+struct card *cards;
+/*struct device *devlist; */
+bitstr_t *mem_avail;
+bitstr_t *io_avail;
+
+int verbose, do_log;
+
+char *newstr();
+void die(char *);
+void *xmalloc(int);
+void log_1s(char *, char *);
+void logerr(char *);
+void reset_slot(struct slot *);
+void execute(struct cmd *);
+unsigned long alloc_memory(int size);
+
+#define IOPORTS 0x400
+#define MEMUNIT 0x1000
+#define MEMSTART 0xA0000
+#define MEMEND 0x100000
+#define MEMBLKS ((MEMEND-MEMSTART)/MEMUNIT)
+#define MEM2BIT(x) (((x)-MEMSTART)/MEMUNIT)
+#define BIT2MEM(x) (((x)*MEMUNIT)+MEMSTART)
diff --git a/usr.sbin/pccard/pccardd/file.c b/usr.sbin/pccard/pccardd/file.c
new file mode 100644
index 0000000..769fafc
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/file.c
@@ -0,0 +1,873 @@
+/*
+ * Decode pcmciad file.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include "cardd.h"
+
+static FILE *in;
+static int pushc, pusht;
+static int lineno;
+static char *filename;
+
+static char *keys[] =
+ {
+ "io", /* 1 */
+ "irq", /* 2 */
+ "memory", /* 3 */
+ "card", /* 4 */
+ "device", /* 5 */
+ "config", /* 6 */
+ "__EOF__", /* 7 */
+ "reset", /* 8 */
+ "ether", /* 9 */
+ "insert", /* 10 */
+ "remove", /* 11 */
+ "iosize", /* 12 */
+ "memsize", /* 13 */
+ 0
+ };
+
+struct flags
+ {
+ char *name;
+ int mask;
+ };
+
+void parsefile();
+char *token();
+char *getline();
+char *next_tok();
+int num_tok();
+void error(char *);
+int keyword(char *);
+struct allocblk *ioblk_tok(int);
+struct allocblk *memblk_tok(int);
+int irq_tok(int);
+void setflags(struct flags *, int *);
+struct driver *new_driver(char *);
+
+/*
+ * Read a file and parse the pcmcia configuration data.
+ * After parsing, verify the links.
+ */
+readfile(char *name)
+{
+struct card *cp;
+struct driver *drvp;
+
+ in = fopen(name, "r");
+ if (in == 0)
+ {
+ perror(name);
+ exit(1);
+ }
+ parsefile();
+ for (cp = cards; cp; cp = cp->next)
+ {
+ if (cp->config == 0)
+fprintf(stderr, "warning: card %s(%s) has no valid configuration\n",
+ cp->manuf, cp->version);
+ }
+}
+void
+parsefile()
+{
+int i;
+char *s;
+struct allocblk *bp;
+
+ pushc = 0;
+ lineno = 1;
+ for(;;)
+ switch(keyword(next_tok()))
+ {
+ default:
+ error("Syntax error");
+ pusht = 0;
+ break;
+ case 7:
+ return;
+/*
+ * reserved I/O blocks
+ */
+ case 1:
+ while (bp = ioblk_tok(0))
+ {
+ if (bp->size == 0 || bp->addr == 0)
+ {
+ free(bp);
+ continue;
+ }
+ bit_nset(io_avail, bp->addr, bp->addr+bp->size-1);
+ bp->next = pool_ioblks;
+ pool_ioblks = bp;
+ }
+ pusht = 1;
+ break;
+/*
+ * reserved irqs
+ */
+ case 2:
+ while ((i = irq_tok(0)) > 0)
+ pool_irq[i] = 1;
+ pusht = 1;
+ break;
+/*
+ * reserved memory blocks.
+ */
+ case 3:
+ while (bp = memblk_tok(0))
+ {
+ if (bp->size == 0 || bp->addr == 0)
+ {
+ free(bp);
+ continue;
+ }
+ bit_nset(mem_avail, MEM2BIT(bp->addr),
+ MEM2BIT(bp->addr+bp->size)-1);
+ bp->next = pool_mem;
+ pool_mem = bp;
+ }
+ pusht = 1;
+ break;
+/*
+ * Card definition.
+ */
+ case 4:
+ parse_card();
+ break;
+/*
+ * Device description
+ */
+#if 0
+ case 5:
+ parse_device();
+ break;
+#endif
+ }
+}
+/*
+ * Parse a card definition.
+ */
+parse_card()
+{
+char *man, *vers;
+struct card *cp;
+int i;
+struct card_config *confp, *lastp;
+
+ man = newstr(next_tok());
+ vers = newstr(next_tok());
+ cp = xmalloc(sizeof(*cp));
+ cp->manuf = man;
+ cp->version = vers;
+ cp->reset_time = 50;
+ cp->next = cards;
+ cards = cp;
+ for (;;)
+ {
+ switch(keyword(next_tok()))
+ {
+ default:
+ pusht = 1;
+ return;
+ case 8:
+ i = num_tok();
+ if (i == -1)
+ {
+ error("Illegal card reset time");
+ break;
+ }
+ cp->reset_time = i;
+ break;
+ case 6:
+ i = num_tok();
+ if (i == -1)
+ {
+ error("Illegal card config index");
+ break;
+ }
+ confp = xmalloc(sizeof(*confp));
+ man = next_tok();
+ confp->driver = new_driver(man);
+ confp->irq = num_tok();
+ confp->flags = num_tok();
+ if (confp->flags == -1)
+ {
+ pusht = 1;
+ confp->flags = 0;
+ }
+ if (confp->irq < 0 || confp->irq > 15)
+ {
+ error("Illegal card IRQ value");
+ break;
+ }
+ confp->index = i & 0x3F;
+/*
+ * If no valid driver for this config, then do not save
+ * this configuration entry.
+ */
+ if (confp->driver)
+ {
+ if (cp->config == 0)
+ cp->config = confp;
+ else
+ {
+ for (lastp = cp->config; lastp->next;
+ lastp = lastp->next)
+ ;
+ lastp->next = confp;
+ }
+ }
+ else
+ free(confp);
+ break;
+ case 9:
+ cp->ether = num_tok();
+ if (cp->ether == -1)
+ {
+ error("Illegal ether address offset");
+ cp->ether = 0;
+ }
+ break;
+ case 10:
+ addcmd(&cp->insert);
+ break;
+ case 11:
+ addcmd(&cp->remove);
+ break;
+ }
+ }
+}
+/*
+ * Generate a new driver structure. If one exists, use
+ * that one after confirming the correct class.
+ */
+struct driver *
+new_driver(char *name)
+{
+struct driver *drvp;
+char *p;
+
+ for (drvp = drivers; drvp; drvp = drvp->next)
+ if (strcmp(drvp->name, name)==0)
+ return(drvp);
+ drvp = xmalloc(sizeof(*drvp));
+ drvp->next = drivers;
+ drivers = drvp;
+ drvp->name = newstr(name);
+ drvp->kernel = newstr(name);
+ p = drvp->kernel;
+ while (*p++)
+ if (*p >= '0' && *p <= '9')
+ {
+ drvp->unit = atoi(p);
+ *p = 0;
+ break;
+ }
+#ifdef DEBUG
+ if (verbose)
+ printf("Drv %s%d created\n", drvp->kernel, drvp->unit);
+#endif
+ return(drvp);
+}
+#if 0
+/*
+ * Parse the device description.
+ */
+parse_device()
+{
+enum drvclass type = drvclass_tok();
+struct device *dp;
+static struct device *lastp;
+
+ if (type == drv_none)
+ {
+ error("Unknown driver class");
+ return;
+ }
+ dp = xmalloc(sizeof(*dp));
+ dp->type = type;
+ if (devlist == 0)
+ devlist = dp;
+ else
+ lastp->next = dp;
+ lastp = dp;
+ for (;;)
+ switch(keyword(next_tok()))
+ {
+ default:
+ pusht = 1;
+ return;
+ case 10:
+ addcmd(&dp->insert);
+ break;
+ case 11:
+ addcmd(&dp->remove);
+ break;
+ }
+}
+/*
+ * Parse the driver description.
+ */
+parse_driver()
+{
+char *name, *dev, *p;
+struct driver *dp;
+static struct driver *lastp;
+int i;
+struct allocblk *bp;
+static struct flags io_flags[] =
+{
+{ "ws", 0x01 },
+{ "16bit", 0x02 },
+{ "cs16", 0x04 },
+{ "zerows", 0x08 },
+{ 0, 0 }
+};
+static struct flags mem_flags[] =
+{
+{ "16bit", 0x01 },
+{ "zerows", 0x02 },
+{ "ws0", 0x04 },
+{ "ws1", 0x08 },
+{ 0, 0 }
+};
+
+ name = newstr(next_tok());
+ dev = newstr(next_tok());
+ type = drvclass_tok();
+ if (type == drv_none)
+ {
+ error("Unknown driver class");
+ return;
+ }
+ dp = xmalloc(sizeof(*dp));
+ dp->name = name;
+ dp->kernel = dev;
+ dp->type = type;
+ dp->unit = -1;
+ dp->irq = -1;
+/*
+ * Check for unit number in driver name.
+ */
+ p = dev;
+ while (*p++)
+ if (*p >= '0' && *p <= '9')
+ {
+ dp->unit = atoi(p);
+ *p = 0;
+ break;
+ }
+ if (dp->unit < 0)
+ error("Illegal kernel driver unit");
+/*
+ * Place at end of list.
+ */
+ if (lastp == 0)
+ drivers = dp;
+ else
+ lastp->next = dp;
+ lastp = dp;
+ for (;;)
+ switch(keyword(next_tok()))
+ {
+ default:
+ pusht = 1;
+ return;
+ case 1:
+ bp = ioblk_tok(1);
+ if (bp)
+ {
+ setflags(io_flags, &bp->flags);
+ if (dp->io)
+ {
+ error("Duplicate I/O spec");
+ free(bp);
+ }
+ else
+ {
+ bit_nclear(io_avail, bp->addr,
+ bp->addr+bp->size-1);
+ dp->io = bp;
+ }
+ }
+ break;
+ case 2:
+ dp->irq = irq_tok(1);
+ if (dp->irq > 0)
+ pool_irq[i] = 0;
+ break;
+ case 3:
+ bp = memblk_tok(1);
+ if (bp)
+ {
+ setflags(mem_flags, &bp->flags);
+ if (dp->mem)
+ {
+ error("Duplicate memory spec");
+ free(bp);
+ }
+ else
+ {
+ bit_nclear(mem_avail,
+ MEM2BIT(bp->addr),
+ MEM2BIT(bp->addr+bp->size)-1);
+ dp->mem = bp;
+ }
+ }
+ break;
+ case 10:
+ addcmd(&dp->insert);
+ break;
+ case 11:
+ addcmd(&dp->remove);
+ break;
+/*
+ * iosize - Don't allocate an I/O port, but specify
+ * a size for the range of ports. The actual port number
+ * will be allocated dynamically.
+ */
+ case 12:
+ i = num_tok();
+ if (i <= 0 || i > 128)
+ error("Illegal iosize");
+ else
+ {
+ int flags = 0;
+ setflags(io_flags, &flags);
+ if (dp->io)
+ error("Duplicate I/O spec");
+ else
+ {
+ dp->io = xmalloc(sizeof(*dp->io));
+ dp->io->flags = flags;
+ dp->io->size = i;
+ }
+ }
+ break;
+ case 13:
+ i = num_tok();
+ if (i <= 0 || i > 256*1024)
+ error("Illegal memsize");
+ else
+ {
+ int flags = 0;
+ setflags(mem_flags, &flags);
+ if (dp->mem)
+ error("Duplicate memory spec");
+ else
+ {
+ dp->mem = xmalloc(sizeof(*dp->mem));
+ dp->mem->flags = flags;
+ dp->mem->size = i;
+ }
+ }
+ break;
+ }
+}
+/*
+ * drvclass_tok - next token is expected to
+ * be a driver class.
+ */
+enum drvclass
+drvclass_tok()
+{
+char *s = next_tok();
+
+ if (strcmp(s, "tty")==0)
+ return(drv_tty);
+ else if (strcmp(s, "net")==0)
+ return(drv_net);
+ else if (strcmp(s, "bio")==0)
+ return(drv_bio);
+ else if (strcmp(s, "null")==0)
+ return(drv_null);
+ return(drv_none);
+}
+#endif /* 0 */
+/*
+ * Parse one I/O block.
+ */
+struct allocblk *
+ioblk_tok(int force)
+{
+struct allocblk *io;
+int i, j;
+
+ if ((i = num_tok()) >= 0)
+ {
+ if (strcmp("-", next_tok()) || (j = num_tok()) < 0 || j < i)
+ {
+ error("I/O block format error");
+ return(0);
+ }
+ io = xmalloc(sizeof(*io));
+ io->addr = i;
+ io->size = j - i + 1;
+ if (j > IOPORTS)
+ {
+ error("I/O port out of range");
+ if (force)
+ {
+ free(io);
+ io = 0;
+ }
+ else
+ io->addr = io->size = 0;
+ }
+ return(io);
+ }
+ if (force)
+ error("Illegal or missing I/O block spec");
+ return(0);
+}
+/*
+ * Parse a memory block.
+ */
+struct allocblk *
+memblk_tok(int force)
+{
+struct allocblk *mem;
+int i, j;
+
+ if ((i = num_tok()) >= 0)
+ if ((j = num_tok()) < 0)
+ error("Illegal memory block");
+ else
+ {
+ mem = xmalloc(sizeof(*mem));
+ mem->addr = i & ~(MEMUNIT-1);
+ mem->size = (j + MEMUNIT - 1) & ~(MEMUNIT-1);
+ if (i < MEMSTART || (i + j) > MEMEND)
+ {
+ error("Memory address out of range");
+ if (force)
+ {
+ free(mem);
+ mem = 0;
+ }
+ else
+ mem->addr = mem->size = 0;
+ }
+ return(mem);
+ }
+ if (force)
+ error("Illegal or missing memory block spec");
+ return(0);
+}
+/*
+ * IRQ token. Must be number > 0 && < 16.
+ * If force is set, IRQ must exist, and can also be '?'.
+ */
+int
+irq_tok(int force)
+{
+int i;
+
+ if (strcmp("?", next_tok())==0 && force)
+ return(0);
+ pusht = 1;
+ i = num_tok();
+ if (i > 0 && i < 16)
+ return(i);
+ if (force)
+ error("Illegal IRQ value");
+ return(-1);
+}
+/*
+ * search the table for a match.
+ */
+int
+keyword(char *str)
+{
+char **s;
+int i = 1;
+
+ for (s = keys; *s; s++, i++)
+ if (strcmp(*s, str)==0)
+ return(i);
+ return(0);
+}
+
+/*
+ * Set/clear flags
+ */
+void
+setflags(struct flags *flags, int *value)
+{
+char *s;
+struct flags *fp;
+int set = 1;
+
+ do {
+ s = next_tok();
+ if (*s == '!')
+ {
+ s++;
+ set = 0;
+ }
+ for (fp = flags; fp->name; fp++)
+ if (strcmp(s, fp->name)==0)
+ {
+ if (set)
+ *value |= fp->mask;
+ else
+ *value &= ~fp->mask;
+ break;
+ }
+ } while (fp->name);
+ pusht = 1;
+}
+/*
+ * addcmd - Append the command line to the list of
+ * commands.
+ */
+addcmd(struct cmd **cp)
+{
+char *s = getline();
+struct cmd *ncp;
+
+ if (*s)
+ {
+ ncp = xmalloc(sizeof(*ncp));
+ ncp->line = s;
+ while (*cp)
+ cp = &(*cp)->next;
+ *cp = ncp;
+ }
+}
+void
+error(char *msg)
+{
+ pusht = 1;
+ fprintf(stderr, "%s: %s at line %d, near %s\n",
+ filename, msg, lineno, next_tok());
+ pusht = 1;
+}
+int last_char;
+
+int
+get()
+{
+int c;
+
+ if (pushc)
+ c = pushc;
+ else
+ c = getc(in);
+ pushc = 0;
+ while (c == '\\')
+ {
+ c = getc(in);
+ switch(c)
+ {
+ case '#':
+ return(last_char = c);
+ case '\n':
+ lineno++;
+ c = getc(in);
+ continue;
+ }
+ pushc = c;
+ return('\\');
+ }
+ if (c == '\n')
+ lineno++;
+ if (c == '#')
+ {
+ while (get() != '\n')
+ ;
+ return(last_char = '\n');
+ }
+ return(last_char = c);
+}
+/*
+ * num_tok - expecting a number token. If not a number,
+ * return -1.
+ * Handles octal (who uses octal anymore?)
+ * hex
+ * decimal
+ * Looks for a 'k' at the end of decimal numbers
+ * and multiplies by 1024.
+ */
+int
+num_tok()
+{
+char *s = next_tok(), c;
+int val=0, base, term=0;
+
+ base = 10;
+ c = *s++;
+ if (c == '0')
+ {
+ base = 8;
+ c = *s++;
+ if (c == 'x' || c == 'X')
+ {
+ c = *s++;
+ base = 16;
+ }
+ }
+ do {
+ switch(c)
+ {
+ case 'k':
+ case 'K':
+ if (val && base == 10 && *s == 0)
+ return(val * 1024);
+ return(-1);
+ default:
+ return(-1);
+ case '0': case '1':
+ case '2': case '3':
+ case '4': case '5':
+ case '6': case '7':
+ val = val * base + c - '0';
+ break;
+
+ case '8': case '9':
+ if (base == 8)
+ return(-1);
+ else
+ val = val * base + c - '0';
+ break;
+ case 'a': case 'b':
+ case 'c': case 'd':
+ case 'e': case 'f':
+ if (base == 16)
+ val = val * base + c - 'a' + 10;
+ else
+ return(-1);
+ break;
+ case 'A': case 'B':
+ case 'C': case 'D':
+ case 'E': case 'F':
+ if (base == 16)
+ val = val * base + c - 'A' + 10;
+ else
+ return(-1);
+ break;
+ }
+ } while (c = *s++);
+ return(val);
+}
+char *_next_tok();
+char *
+next_tok()
+{
+char *s = _next_tok();
+#if 0
+ printf("Tok = %s\n", s);
+#endif
+ return(s);
+}
+/*
+ * get one token. Handles string quoting etc.
+ */
+char *
+_next_tok()
+{
+static char buf[1024];
+char *p = buf, instr = 0;
+int c;
+
+ if (pusht)
+ {
+ pusht = 0;
+ return(buf);
+ }
+ for(;;)
+ {
+ c = get();
+ switch(c)
+ {
+ default:
+ *p++ = c;
+ break;
+ case '"':
+ if (instr)
+ {
+ *p++ = 0;
+ return(buf);
+ }
+ instr = 1;
+ break;
+ case '\n':
+ if (instr)
+ {
+ error("Unterminated string");
+ break;
+ }
+/*
+ * Eat whitespace unless in a string.
+ */
+ case ' ':
+ case '\t':
+ if (!instr)
+ {
+ if (p!=buf)
+ {
+ *p++ = 0;
+ return(buf);
+ }
+ }
+ else
+ *p++ = c;
+ break;
+/*
+ * Special characters that must be tokens on their own.
+ */
+ case '-':
+ case '?':
+ case '*':
+ if (instr)
+ *p++ = c;
+ else
+ {
+ if (p != buf)
+ pushc = c;
+ else
+ *p++ = c;
+ *p++ = 0;
+ return(buf);
+ }
+ break;
+ case EOF:
+ if (p != buf)
+ {
+ *p++ = 0;
+ return(buf);
+ }
+ strcpy(buf, "__EOF__");
+ return(buf);
+ }
+ }
+}
+
+/*
+ * get the rest of the line. If the
+ * last character scanned was a newline, then
+ * return an empty line. If this isn't checked, then
+ * a getline may incorrectly return the next line.
+ */
+char *
+getline()
+{
+char buf[1024], *p = buf;
+int c, i = 0;
+
+ if (last_char == '\n')
+ return(newstr(""));
+ do {
+ c = get();
+ } while (c == ' ' || c == '\t');
+ for (;c != '\n' && c != EOF; c = get())
+ if (i++ < sizeof(buf)-10)
+ *p++ = c;
+ *p = 0;
+ return(newstr(buf));
+}
diff --git a/usr.sbin/pccard/pccardd/pccard.conf.5 b/usr.sbin/pccard/pccardd/pccard.conf.5
new file mode 100644
index 0000000..f4001b5
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/pccard.conf.5
@@ -0,0 +1,183 @@
+.\" Copyright (c) 1994 Andrew McRae
+.\" All rights reserved.
+.\"
+.Dd Novemeber 2, 1994
+.Dt CARD.CONF 5
+.Os FreeBSD
+.Sh NAME
+.Nm card.conf
+.Nd
+.Xr cardd 8
+configuration file
+.Sh DESCRIPTION
+The
+.Nm card.conf
+file is the configuration file for the
+.Xr cardd 8
+PC-CARD slot management daemon.
+It provides information to allow card
+identification, and the matching of drivers (along
+with driver resources) to the PC-CARD cards.
+.Pp
+There are four basic elements within the configuration file;
+An optional
+.Em "resource pool"
+preceding the other sections,
+and one or more
+.Em "card identifiers" ,
+and
+.Em "device instances" .
+The latter two may appear in any order, and may be
+interspersed as desired.
+.Pp
+Each PC-CARD card contains configuration tuples that provide
+the manufacturer and card version; these are used
+to identify the card specification in the configuration
+file, and from this find a driver that can be used to
+interface to the particular card. There is a many-to-one mapping
+between cards to drivers i.e a single driver may interface to
+multiple types of cards. To aid this, card parameters may be
+specified separately from the driver to initialise the card or
+extract (in the case of a network card) an ethernet address.
+.Pp
+Once a driver is allocated to a card, it stays
+allocated to that particular card.
+However, multiple instances of the same type of driver can be
+configured, so that if two cards are plugged in that map to a
+similar type of driver, other driver instances of the same name
+can be configured.
+.Pp
+The
+.Em insert
+and
+.Em remove
+commands allow a shell command line to be executed.
+The command to be executed is the rest of the line after
+the keyword. The line can be continued using a backslash.
+A simple
+macro substitution allows the current kernel device name
+.Em ( $device )
+and
+network card ethernet address
+.Em ( $ether )
+to be inserted into the command line.
+.Xr Pcmciad 8
+uses the
+.Xr system 3
+subroutine to execute the command line.
+.Pp
+Numeric values may be expressed as octal, hex or decimal.
+If a decimal number has
+.Em k
+or
+.Em K
+appended to it, the value is multiplied by 1024. Names may be
+quoted using double quotes if spaces are required.
+A hash character comments out the rest of the line.
+.Ss "Resource pool"
+The (optional) section specifies a pool of system resources
+such as ISA bus memory address space, Input/Output ports and
+interrupt request numbers. This resource pool is used
+to allocate address space and interrupt numbers dynamically
+according to the requirements specified in each driver
+description.
+.Pp
+The syntax of the resources is as follows:
+.Pp
+.Dl io Ar start - end ...
+.Dl memory Ar address size ...
+.Dl irq Ar irq-number ...
+.Pp
+Each of the statements define I/O, memory or IRQ
+blocks that can be used to allocate to drivers when
+they are initialised.
+.Pp
+Multiple lines of any of the above statements may be
+present to allow separate blocks of each resource to be
+defined.
+.Ss "Card Identifiers"
+The syntax for card identifiers is:
+.Pp
+.Dl card Ar manufacturer version class
+.Dl config Ar index driver interrupt [ flags ]
+.Dl ether Ar offset
+.Dl insert Ar command
+.Dl remove Ar command
+.Pp
+The first line is mandatory;
+the latter statements are optional and can appear in
+any order. There may be multiple
+.Em config
+lines.
+The
+.Em card
+parameters are the Manufacturer name and card version that
+is used to match the values from the card's CIS memory. The
+.Em class
+identifies the card as one of the device classes listed below.
+The
+.Em config
+parameters select the particular card's configuration index
+from the range available in the card's CIS, the driver that
+is to be associated with this configuration, and the interrupt
+level (if any) to be assigned. An optional set of flags may
+be assigned.
+.Pp
+The optional
+.Em ether
+keyword is used when network cards have their physical Ethernet address
+located within the attribute memory of the card. The parameter of this
+statement indicates the offset within the attribute memory of the
+Ethernet address. This value can be used within insert/remove
+commands using the
+.Em $ether
+macro.
+.Pp
+The
+.Em insert
+and
+.Em remove
+sections allow shell commands to be specified that are executed
+when the card is inserted or removed. Multiple
+.Em insert
+and
+.Em remove
+commands are allowed, and they are executed in the order they
+are listed.
+.Sh EXAMPLE
+A typical configuration file may appear thus:
+.Bd -literal
+#
+# Sample configuration file.
+#
+# Pool parameters.
+#
+io 0x280 - 0x2F0 0x300 - 0x360
+irq 5 6 8 9 10 15
+memory 0xd4000 96k
+memory 0xc4000 32k
+#
+# Card database.
+#
+card "RPTI LTD." "EP400" # NE2000 clone
+ ether 0x110
+ config 0x21 "ed0" 5
+ insert ifconfig $device physical $ether
+ insert ifconfig $device bean
+ remove ifconfig $device down
+
+card "XYZZY" "FAX/1.0" tty
+ config 0x30 "sio1" 11
+ insert echo start getty
+ remove echo stop getty
+
+.Ed
+.Sh FILES
+.Bl -tag -width /etc/card.conf -compact
+.It Pa /etc/card.conf
+The
+.Xr cardd 8
+configuration file.
+.El
+.Sh SEE ALSO
+.Xr cardd 8
diff --git a/usr.sbin/pccard/pccardd/pccardd.8 b/usr.sbin/pccard/pccardd/pccardd.8
new file mode 100644
index 0000000..8e9451d
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/pccardd.8
@@ -0,0 +1,133 @@
+.\" Copyright (c) 1994 Andrew McRae
+.\" All rights reserved.
+.\"
+.Dd November 1, 1994
+.Dt PC-CARD 8
+.Os FreeBSD
+.Sh NAME
+.Nm cardd
+.Nd PC-CARD (PCMCIA) management daemon
+.Sh SYNOPSIS
+.Nm cardd
+.Op Fl d
+.Op Fl v
+.Op Fl f Ar configfile
+.Sh DESCRIPTION
+.Nm Cardd
+is normally started at boot time, and manages the insertion
+and removal of PC-CARD cards.
+.Pp
+When started,
+.Nm cardd
+will read the configuration file (default name
+.Pa /etc/card.conf )
+and scans the available PC-CARD slots for cards.
+.Nm Cardd
+then waits for
+.Em "card events" ,
+such as the insertion of a new card or the removal
+of a card.
+.Pp
+When a card is inserted, the following
+actions are taken:
+.Bl -enum
+.It
+The kernel driver detects the card insertion and applies
+power to the card.
+.It
+.Nm Cardd
+reads the
+.Em CIS
+data from the attribute memory of the card, and uses
+the manufacturer name and card version to match
+the card description in the configuration file.
+.It
+Once matched, a driver is allocated.
+.It
+Once a free driver and device instance is located,
+.Nm cardd
+will (if required) allocate resources such as an ISA memory
+block and Input/Output ports from a common pool.
+.It
+The PC-CARD slot is configured with the I/O and memory
+contexts allocated, and the kernel driver is attached to
+this card.
+.It
+If the attach succeeds, then specific shell commands
+may be executed to configure the device, such as
+.Xr ifconfig 8
+to set up a network interface. Separate commands may be specified
+for each card, driver or device, and are executed in that order.
+.El
+.Pp
+When
+.Nm cardd
+detects that a card has been removed, the following sequence occurs:
+.Bl -enum
+.It
+The shell commands associated with card removal are executed. These
+are intended to reset any device associated with the removed card.
+Separate commands may exist for card, driver and device instances.
+.It
+The PC-CARD slot resources are freed.
+.El
+.Pp
+Once a card/driver instance is configured, the resources
+bound to that instance are remembered, and if the card is removed
+and reinserted, the same driver is allocated. The primary reason
+is that once a driver is associated with a card, the
+driver's
+.Fn probe
+routine has been called, and this usually causes driver specific
+data areas to be initialised with the I/O ports or memory resources
+allocated to the card. Most drivers are not designed to be
+disassociated from the hardware and then reassociated with different
+parameters. This will change significantly when loadable kernel
+modules are supported.
+.Pp
+The start options understood by
+.Nm cardd
+are:
+.Bl -tag -width Ds
+.It Fl d
+Do not run as a daemon, but run in the foreground and
+display error messages.
+.It Fl v
+After reading the configuration file, print out a summary
+of it.
+.It Fl f Ar configfile
+Specifies a different configuration file to be used
+in placed of the default file
+.Pa /etc/card.conf.
+The file format is detailed in
+.Xr card.conf 5 ,
+and lists the PC-CARD cards recognized by
+.Nm cardd ,
+and the kernel drivers and devices that are used to
+interface to the card.
+.Pp
+.Sh FILES
+.Bl -tag -width /etc/card.conf -compact
+.It Pa /etc/card.conf
+.El
+.Sh SEE ALSO
+.Xr card.conf 5
+.Xr ifconfig 8
+.Sh AUTHOR
+Developed by Andrew McRae (andrew@mega.com.au).
+.Sh BUGS
+.Nm Cardd
+can set up card parameters, but cannot guarantee that
+particular drivers can work with the card.
+.Pp
+Since
+.Nm FreeBSD
+does not currently support loadable kernel modules, any
+.Em irq
+specifications in the configuration file must match the
+.Nm config
+entry for the kernel.
+.Pp
+Removing cards may cause problems if system resources
+have been associated with the card, such as network
+mounted filesystems.
diff --git a/usr.sbin/pccard/pccardd/readcis.c b/usr.sbin/pccard/pccardd/readcis.c
new file mode 100644
index 0000000..880a4aa
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/readcis.c
@@ -0,0 +1,633 @@
+/* set tab=4
+ * Read/dump CIS tuples.
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+
+#include <pccard/card.h>
+#include <pccard/cis.h>
+
+#include "readcis.h"
+
+static int read_attr(int fd, char *bp, int len);
+struct tuple_list *read_one_tuplelist(int, int, off_t);
+int ck_linktarget(int, off_t, int);
+
+struct tuple_info tuple_info[] =
+{
+ "Null tuple", 0x00, 0,
+ "Common memory descriptor", 0x01, 255,
+ "Checksum", 0x10, 5,
+ "Long link to attribute memory", 0x11, 4,
+ "Long link to common memory", 0x12, 4,
+ "Link target", 0x13, 3,
+ "No link", 0x14, 0,
+ "Version 1 info", 0x15, 255,
+ "Alternate language string", 0x16, 255,
+ "Attribute memory descriptor", 0x17, 255,
+ "JEDEC descr for common memory", 0x18, 255,
+ "JEDEC descr for attribute memory", 0x19, 255,
+ "Configuration map", 0x1A, 255,
+ "Configuration entry", 0x1B, 255,
+ "Other conditions for common memory", 0x1C, 255,
+ "Other conditions for attribute memory", 0x1D, 255,
+ "Geometry info for common memory", 0x1E, 255,
+ "Geometry info for attribute memory", 0x1F, 255,
+ "Manufacturer ID", 0x20, 4,
+ "Functional ID", 0x21, 255,
+ "Functional EXT", 0x22, 255,
+ "Software interleave", 0x23, 2,
+ "Version 2 Info", 0x40, 255,
+ "Data format", 0x41, 255,
+ "Geometry", 0x42, 4,
+ "Byte order", 0x43, 2,
+ "Card init date", 0x44, 4,
+ "Battery replacement", 0x45, 4,
+ "Organisation", 0x46, 255,
+ "Terminator", 0xFF, 255,
+ 0, 0, 0
+ };
+
+/*
+ * After reading the tuples, decode the relevant ones.
+ */
+struct cis *
+readcis(int fd)
+{
+struct tuple_list *tl;
+struct tuple *tp;
+struct cis *cp;
+
+ cp = xmalloc(sizeof(*cp));
+ cp->tlist = read_tuples(fd);
+ if (cp->tlist == 0)
+ return(NULL);
+
+ for (tl = cp->tlist; tl; tl = tl->next)
+ for (tp = tl->tuples; tp; tp = tp->next)
+ {
+#if 0
+ printf("tuple code = 0x%02x, data is\n", tp->code);
+ dump(tp->data, tp->length);
+#endif
+ switch(tp->code)
+ {
+ case CIS_MEM_COMMON: /* 0x01 */
+ device_desc(tp->data, tp->length, &cp->common_mem);
+ break;
+ case CIS_INFO_V1: /* 0x15 */
+ cis_info(cp, tp->data, tp->length);
+ break;
+ case CIS_MEM_ATTR: /* 0x17 */
+ device_desc(tp->data, tp->length, &cp->attr_mem);
+ break;
+ case CIS_CONF_MAP: /* 0x1A */
+ config_map(cp, tp->data, tp->length);
+ break;
+ case CIS_CONFIG: /* 0x1B */
+ cis_config(cp, tp->data, tp->length);
+ break;
+ }
+ }
+ return(cp);
+}
+/*
+ * free_cis - delete cis entry.
+ */
+void
+freecis(struct cis *cp)
+{
+struct cis_ioblk *io;
+struct cis_memblk *mem;
+struct cis_config *conf;
+struct tuple *tp;
+struct tuple_list *tl;
+
+ while (tl = cp->tlist)
+ {
+ cp->tlist = tl->next;
+ while (tp = tl->tuples)
+ {
+ tl->tuples = tp->next;
+ if (tp->data)
+ free(tp->data);
+ }
+ }
+
+ while (conf = cp->conf)
+ {
+ cp->conf = conf->next;
+ while (io = conf->io)
+ {
+ conf->io = io->next;
+ free(io);
+ }
+ while (mem = conf->mem)
+ {
+ conf->mem = mem->next;
+ free(mem);
+ }
+ free(conf);
+ }
+ free(cp);
+}
+/*
+ * Fills in CIS version data.
+ */
+cis_info(struct cis *cp, unsigned char *p, int len)
+{
+ cp->maj_v = *p++;
+ cp->min_v = *p++;
+ strncpy(cp->manuf, p, MAXSTR-1);
+ while (*p++)
+ ;
+ strncpy(cp->vers, p, MAXSTR-1);
+ while (*p++)
+ ;
+ strncpy(cp->add_info1, p, MAXSTR-1);
+ while (*p++)
+ ;
+ strncpy(cp->add_info2, p, MAXSTR-1);
+}
+/*
+ * device_desc - decode device descriptor.
+ */
+device_desc(p, len, dp)
+unsigned char *p;
+int len;
+struct dev_mem *dp;
+{
+ while (len > 0 && *p != 0xFF)
+ {
+ dp->valid = 1;
+ dp->type = (*p & 0xF0) >> 4;
+ dp->wps = !!(*p & 0x8);
+ dp->speed = *p & 7;
+ p++;
+ if (*p != 0xFF)
+ {
+ dp->addr = *p >> 3;
+ dp->units = *p & 7;
+ }
+ p++;
+ len -= 2;
+ }
+}
+/*
+ * configuration map of card control register.
+ */
+config_map(cp, p, len)
+struct cis *cp;
+unsigned char *p;
+int len;
+{
+unsigned char *p1;
+int i;
+union {
+ unsigned long l;
+ unsigned char b[4];
+ }u;
+
+ p1 = p + 1;
+ cp->last_config = *p1++ & 0x3F;
+ u.l = 0;
+ for (i = 0 ; i <= (*p & 3); i++)
+ u.b[i] = *p1++;
+ cp->reg_addr = u.l;
+ cp->ccrs = *p1;
+}
+/*
+ * CIS config entry - Decode and build configuration entry.
+ */
+cis_config(cp, p, len)
+struct cis *cp;
+unsigned char *p;
+int len;
+{
+int blks, x;
+int i, j;
+union {
+ unsigned long l;
+ unsigned char b[4];
+ }u;
+struct cis_config *conf, *last;
+struct cis_memblk *mem;
+unsigned char feat;
+
+ conf = xmalloc(sizeof(*conf));
+ if (last = cp->conf)
+ {
+ while (last->next)
+ last = last->next;
+ last->next = conf;
+ }
+ else
+ cp->conf = conf;
+ conf->id = *p & 0x3F;
+ if (*p & 0x40)
+ cp->def_config = conf;
+ if (*p++ & 0x80)
+ p++;
+ feat = *p++;
+ for (i = 0; i < CIS_FEAT_POWER(feat); i++)
+ {
+ unsigned char parms = *p++;
+
+ conf->pwr = 1;
+ for (j = 0; j < 8; j++)
+ if (parms & (1 << j))
+ while (*p++ & 0x80)
+ ;
+ }
+ if (feat & CIS_FEAT_TIMING)
+ {
+ conf->timing = 1;
+ i = *p++;
+ if (CIS_WAIT_SCALE(i) != 3)
+ p++;
+ if (CIS_READY_SCALE(i) != 7)
+ p++;
+ if (CIS_RESERVED_SCALE(i) != 7)
+ p++;
+ }
+ if (feat & CIS_FEAT_I_O)
+ {
+ conf->iospace = 1;
+ if (CIS_IO_RANGE & *p)
+ conf->io_blks = CIS_IO_BLKS(p[1])+1;
+ conf->io_addr = CIS_IO_ADDR(*p);
+ conf->io_bus = (*p >> 5) & 3;
+ if (*p++ & CIS_IO_RANGE)
+ {
+ struct cis_ioblk *io, *last = 0;
+ i = CIS_IO_ADSZ(*p);
+ j = CIS_IO_BLKSZ(*p++);
+ for (x = 0; x < conf->io_blks; x++)
+ {
+ io = xmalloc(sizeof(*io));
+ if (last)
+ last->next = io;
+ else
+ conf->io = io;
+ last = io;
+ u.l = 0;
+ switch(i)
+ {
+ case 0:
+ break;
+ case 1:
+ u.b[0] = *p++;
+ break;
+ case 2:
+ u.b[0] = *p++;
+ u.b[1] = *p++;
+ break;
+ case 3:
+ u.b[0] = *p++;
+ u.b[1] = *p++;
+ u.b[2] = *p++;
+ u.b[3] = *p++;
+ break;
+ }
+ io->addr = u.l;
+ u.l = 0;
+ switch(j)
+ {
+ case 0:
+ break;
+ case 1:
+ u.b[0] = *p++;
+ u.l++;
+ break;
+ case 2:
+ u.b[0] = *p++;
+ u.b[1] = *p++;
+ u.l++;
+ break;
+ case 3:
+ u.b[0] = *p++;
+ u.b[1] = *p++;
+ u.b[2] = *p++;
+ u.b[3] = *p++;
+ u.l++;
+ break;
+ }
+ io->size = u.l;
+ }
+ }
+ }
+ if (feat & CIS_FEAT_IRQ)
+ {
+ conf->irq = 1;
+ conf->irqlevel = *p & 0xF;
+ conf->irq_flags = *p & 0xF0;
+ if (*p++ & CIS_IRQ_MASK)
+ {
+ conf->irq_mask = (p[1] << 8) | p[0];
+ p += 2;
+ }
+ }
+ switch(CIS_FEAT_MEMORY(feat))
+ {
+ case 0:
+ break;
+ case 1:
+ conf->memspace = 1;
+ conf->mem = xmalloc(sizeof(*conf->mem));
+ conf->mem->length = ((p[1] << 8) | p[0])<<8;
+ break;
+ case 2:
+ conf->memspace = 1;
+ conf->mem = xmalloc(sizeof(*conf->mem));
+ conf->mem->length = ((p[1] << 8) | p[0]) << 8;
+ conf->mem->address = ((p[3] << 8) | p[2]) << 8;
+ break;
+ case 3:
+ conf->memspace = 1;
+ x = *p++;
+ conf->memwins = CIS_MEM_WINS(x);
+ for (i = 0; i < conf->memwins; i++)
+ {
+ struct cis_memblk *last;
+
+ mem = xmalloc(sizeof(*mem));
+ if (i == 0)
+ conf->mem = mem;
+ else
+ last->next = mem;
+ last = mem;
+ u.l = 0;
+ for (j = 0 ; j < CIS_MEM_LENSZ(x); j++)
+ u.b[j] = *p++;
+ mem->length = u.l << 8;
+ u.l = 0;
+ for (j = 0 ; j < CIS_MEM_ADDRSZ(x); j++)
+ u.b[j] = *p++;
+ mem->address = u.l << 8;
+ if (x & CIS_MEM_HOST)
+ {
+ u.l = 0;
+ for (j = 0 ; j < CIS_MEM_ADDRSZ(x); j++)
+ u.b[j] = *p++;
+ mem->host_address = u.l << 8;
+ }
+ }
+ break;
+ }
+ if (feat & 0x80)
+ {
+ conf->misc_valid = 1;
+ conf->misc = *p++;
+ }
+}
+/*
+ * Read the tuples from the card.
+ * The processing of tuples is as follows:
+ * - Read tuples at attribute memory, offset 0.
+ * - If a CIS_END is the first tuple, look for
+ * a tuple list at common memory offset 0; this list
+ * must start with a LINKTARGET.
+ * - If a long link tuple was encountered, execute the long
+ * link.
+ * - If a no-link tuple was seen, terminate processing.
+ * - If no no-link tuple exists, and no long link tuple
+ * exists while processing the primary tuple list,
+ * then look for a LINKTARGET tuple in common memory.
+ * - If a long link tuple is found in any list, then process
+ * it. Only one link is allowed per list.
+ */
+static struct tuple_list *tlist;
+
+struct tuple_list *
+read_tuples(int fd)
+{
+struct tuple_list *tl = 0, *last_tl;
+struct tuple *tp;
+int flag;
+off_t offs;
+
+ tlist = 0;
+ last_tl = tlist = read_one_tuplelist(fd, MDF_ATTR, (off_t)0);
+/*
+ * Now start processing the links (if any).
+ */
+ do
+ {
+ flag = MDF_ATTR;
+ tp = find_tuple_in_list(last_tl, CIS_LONGLINK_A);
+ if (tp == 0)
+ {
+ flag = 0;
+ tp = find_tuple_in_list(last_tl, CIS_LONGLINK_C);
+ }
+ if (tp && tp->length == 4)
+ {
+ offs = tp->data[0] |
+ (tp->data[1] << 8) |
+ (tp->data[2] << 16) |
+ (tp->data[3] << 24);
+#ifdef DEBUG
+ printf("Checking long link at %ld (%s memory)\n",
+ offs, flag ? "Attribute" : "Common");
+#endif
+ if (ck_linktarget(fd, offs, flag))
+ {
+/*
+ * If a link was found, then read the tuple list from it.
+ */
+ tl = read_one_tuplelist(fd, flag, offs);
+ last_tl->next = tl;
+ last_tl = tl;
+ }
+ }
+ } while (tl);
+/*
+ * If the primary list had no NOLINK tuple, and no LINKTARGET,
+ * then try to read a tuple list at common memory (offset 0).
+ */
+ if (find_tuple_in_list(tlist, CIS_NOLINK)==0 && tlist->next == 0 &&
+ ck_linktarget(fd, (off_t)0, 0))
+ {
+#ifdef DEBUG
+ printf("Reading long link at %ld (%s memory)\n",
+ offs, flag ? "Attribute" : "Common");
+#endif
+ tlist->next = read_one_tuplelist(fd, 0, (off_t)0);
+ }
+ return(tlist);
+}
+/*
+ * Read one tuple list from the card.
+ */
+struct tuple_list *
+read_one_tuplelist(int fd, int flags, off_t offs)
+{
+struct tuple *tp, *last_tp, *first = 0;
+struct tuple_list *tl;
+struct tuple_info *tinfo;
+int i, total = 0;
+unsigned char code, length;
+
+/*
+ * Check to see if this memory has already been scanned.
+ */
+ for (tl = tlist; tl; tl = tl->next)
+ if (tl->offs == offs && tl->flags == (flags & MDF_ATTR))
+ return(0);
+ tl = xmalloc(sizeof(*tl));
+ tl->offs = offs;
+ tl->flags = flags & MDF_ATTR;
+ ioctl(fd, PIOCRWFLAG, &flags);
+ lseek(fd, offs, SEEK_SET);
+ do {
+ if (read_attr(fd, &code, 1) != 1)
+ {
+ perror("CIS code read");
+ break;
+ }
+ total++;
+ if (code == CIS_NULL)
+ continue;
+ tp = xmalloc(sizeof(*tp));
+ tp->code = code;
+ if (read_attr(fd, &length, 1) != 1)
+ {
+ perror("CIS len read");
+ break;
+ }
+ total++;
+ tp->length = length;
+#ifdef DEBUG
+ fprintf(stderr, "Tuple code = 0x%x, len = %d\n",
+ code, length);
+#endif
+ if (length == 0xFF)
+ {
+ length = tp->length = 0;
+ code = CIS_END;
+ }
+ if (length != 0)
+ {
+ total += length;
+ tp->data = xmalloc(length);
+ if (read_attr(fd, tp->data, length) != length)
+ {
+ perror("CIS read");
+ break;
+ }
+ }
+/*
+ * Check the tuple, and ignore it if it isn't in the table
+ * or the length is illegal.
+ */
+ tinfo = get_tuple_info(code);
+ if (tinfo == 0 || (tinfo->length != 255 && tinfo->length != length))
+ {
+ printf("code %s ignored\n", tuple_name(code));
+ tp->code = CIS_NULL;
+ }
+ if (tl->tuples==0)
+ tl->tuples = tp;
+ else
+ last_tp->next = tp;
+ last_tp = tp;
+ } while (code != CIS_END && total < 1024);
+ return(tl);
+}
+/*
+ * return true if the offset points to a LINKTARGET tuple.
+ */
+int
+ck_linktarget(int fd, off_t offs, int flag)
+{
+char blk[5];
+
+ ioctl(fd, PIOCRWFLAG, &flag);
+ lseek(fd, offs, SEEK_SET);
+ if (read_attr(fd, blk, 5) != 5)
+ return(0);
+ if (blk[0] == 0x13 &&
+ blk[1] == 0x3 &&
+ blk[2] == 'C' &&
+ blk[3] == 'I' &&
+ blk[4] == 'S')
+ return(1);
+ return(0);
+}
+/*
+ * find_tuple - find the indicated tuple in the CIS
+ */
+struct tuple *
+find_tuple(struct cis *sp, unsigned char code)
+{
+struct tuple_list *tl;
+struct tuple *tp;
+
+ for (tl = sp->tlist; tl; tl = tl->next)
+ if (tp = find_tuple_in_list(tl, code))
+ return(tp);
+ return(0);
+}
+/*
+ * find_tuple_in_list - find a tuple within a
+ * single tuple list.
+ */
+struct tuple *
+find_tuple_in_list(struct tuple_list *tl, unsigned char code)
+{
+struct tuple *tp;
+
+ for (tp = tl->tuples; tp; tp = tp->next)
+ if (tp->code == code)
+ break;
+ return(tp);
+}
+static int
+read_attr(int fd, char *bp, int len)
+{
+char blk[1024], *p = blk;
+int i,l;
+
+ if (len > sizeof(blk)/2)
+ len = sizeof(blk)/2;
+ l = i = read(fd, blk, len*2);
+ if (i <= 0)
+ {
+ printf("Read return %d bytes (expected %d)\n", i, len*2);
+ return(i);
+ }
+ while (i > 0)
+ {
+ *bp++ = *p++;
+ p++;
+ i -= 2;
+ }
+ return(l/2);
+}
+/*
+ * return table entry for code.
+ */
+struct tuple_info *
+get_tuple_info(unsigned char code)
+{
+struct tuple_info *tp;
+
+ for (tp = tuple_info; tp->name; tp++)
+ if (tp->code == code)
+ return(tp);
+ printf("Code %d not found\n", code);
+ return(0);
+}
+char *
+tuple_name(unsigned char code)
+{
+struct tuple_info *tp;
+
+ tp = get_tuple_info(code);
+ if (tp)
+ return(tp->name);
+ return("Unknown");
+}
diff --git a/usr.sbin/pccard/pccardd/readcis.h b/usr.sbin/pccard/pccardd/readcis.h
new file mode 100644
index 0000000..379435b
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/readcis.h
@@ -0,0 +1,116 @@
+
+#define MAXSTR 20
+/*
+ * Storage of one tuple.
+ */
+struct tuple
+ {
+ struct tuple *next;
+ unsigned char code;
+ int length;
+ unsigned char *data;
+ };
+
+struct tuple_list
+ {
+ struct tuple_list *next;
+ struct tuple *tuples;
+ off_t offs;
+ int flags;
+ };
+
+struct tuple_info
+ {
+ char *name;
+ unsigned char code;
+ unsigned char length; /* 255 means variable length */
+ };
+/*
+ * Memory device descriptor.
+ */
+struct dev_mem
+ {
+ unsigned char valid;
+ unsigned char type;
+ unsigned char speed;
+ unsigned char wps;
+ unsigned char addr;
+ unsigned char units;
+ };
+/*
+ * One I/O structure describing a possible I/O map
+ * of the card.
+ */
+struct cis_ioblk
+ {
+ struct cis_ioblk *next;
+ unsigned int addr;
+ unsigned int size;
+ };
+/*
+ * A structure storing a memory map for the card.
+ */
+struct cis_memblk
+ {
+ struct cis_memblk *next;
+ unsigned int address;
+ unsigned int length;
+ unsigned int host_address;
+ };
+/*
+ * One configuration entry for the card.
+ */
+struct cis_config
+ {
+ struct cis_config *next;
+ unsigned int pwr:1; /* Which values are defined. */
+ unsigned int timing:1;
+ unsigned int iospace:1;
+ unsigned int irq:1;
+ unsigned int memspace:1;
+ unsigned int misc_valid:1;
+ unsigned char id;
+ unsigned char io_blks;
+ unsigned char io_addr;
+ unsigned char io_bus;
+ struct cis_ioblk *io;
+ unsigned char irqlevel;
+ unsigned char irq_flags;
+ unsigned irq_mask;
+ unsigned char memwins;
+ struct cis_memblk *mem;
+ unsigned char misc;
+ };
+/*
+ * Structure holding all data retrieved from the
+ * CIS block on the card.
+ * The default configuration contains interface defaults
+ * not listed in each separate configuration.
+ */
+struct cis
+ {
+ struct tuple_list *tlist;
+ char manuf[MAXSTR];
+ char vers[MAXSTR];
+ char add_info1[MAXSTR];
+ char add_info2[MAXSTR];
+ unsigned char maj_v, min_v;
+ unsigned char last_config;
+ unsigned char ccrs;
+ unsigned long reg_addr;
+ struct dev_mem attr_mem;
+ struct dev_mem common_mem;
+ struct cis_config *def_config;
+ struct cis_config *conf;
+ };
+
+void *xmalloc(int);
+struct cis *readcis(int);
+void dumpcis(struct cis *);
+void freecis(struct cis *);
+struct tuple_list *read_tuples(int);
+struct tuple *find_tuple(struct cis *, unsigned char);
+struct tuple *find_tuple_in_list(struct tuple_list *, unsigned char);
+
+struct tuple_info *get_tuple_info(unsigned char);
+char *tuple_name(unsigned char);
diff --git a/usr.sbin/pccard/pccardd/sample.config b/usr.sbin/pccard/pccardd/sample.config
new file mode 100644
index 0000000..cb9552f
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/sample.config
@@ -0,0 +1,22 @@
+#
+# Sample configuration file.
+#
+# Pool parameters.
+#
+io 0x2F8 - 0x360
+irq 5 6 8 9 10 15
+memory 0xd4000 96k
+#
+# Card database.
+#
+card "RPTI LTD." "EP400" # NE2000 clone
+ ether 0x110
+ config 0x30 "ed0" 5
+ config 0x31 "ed1" 6
+ insert ifconfig $device physical $ether
+ insert ifconfig $device bean
+
+card "RIPICAA" "RC144ACL"
+ config 0x21 "sio1" 10 20
+ insert echo start getty here
+ remove echo stop getty
diff --git a/usr.sbin/pccard/pccardd/util.c b/usr.sbin/pccard/pccardd/util.c
new file mode 100644
index 0000000..6abacaa
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/util.c
@@ -0,0 +1,209 @@
+/*
+ * Utility subroutines.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <syslog.h>
+#include <varargs.h>
+#include "cardd.h"
+
+
+void
+log_1s(char *msg, char *arg)
+{
+ if (do_log)
+ syslog(LOG_ERR, msg, arg);
+ else
+ {
+ fprintf(stderr, "cardd: ");
+ fprintf(stderr, msg, arg);
+ fprintf(stderr, "\n");
+ }
+}
+void
+logerr(char *msg)
+{
+ if (do_log)
+ syslog(LOG_ERR, "%s: %m", msg);
+ else
+ perror(msg);
+}
+/*
+ * Deliver last will and testament, and die.
+ */
+void
+die(char *msg)
+{
+ if (do_log)
+ syslog(LOG_CRIT, "fatal error: %s", msg);
+ else
+ fprintf(stderr, "cardd fatal error: %s\n", msg);
+ closelog();
+ exit(1);
+}
+void *
+xmalloc(int sz)
+{
+void *p;
+
+ p = malloc(sz+8);
+ if (p)
+ bzero(p, sz);
+ else
+ die("malloc failed");
+ return(p);
+}
+char *
+newstr(char *p)
+{
+char *s;
+
+ s = strdup(p);
+ if (s == 0)
+ die("strdup failed");
+ return(s);
+}
+/*
+ * Find contiguous bit string (all set) of at
+ * least count number.
+ */
+int
+bit_fns(bitstr_t *nm, int nbits, int count)
+{
+int i;
+int found = 0;
+
+ for (i = 0; i < nbits; i++)
+ if (bit_test(nm, i))
+ {
+ if (++found == count)
+ return(i - count + 1);
+ }
+ else
+ found = 0;
+ return(-1);
+}
+/*
+ * Allocate a block of memory and return the address.
+ */
+unsigned long
+alloc_memory(int size)
+{
+int i;
+
+ i = bit_fns(mem_avail, MEMBLKS, size/MEMUNIT);
+ if (i < 0)
+ return(0);
+ bit_nclear(mem_avail, i, size/MEMUNIT);
+ return(BIT2MEM(i));
+}
+/*
+ * reset_slot - Power has been applied to the card.
+ * Now reset the card.
+ */
+void
+reset_slot(struct slot *sp)
+{
+struct card *cp = sp->card;
+char c;
+off_t offs;
+struct mem_desc mem;
+struct io_desc io;
+int rw_flags;
+
+ rw_flags = MDF_ATTR;
+ ioctl(sp->fd, PIOCRWFLAG, &rw_flags);
+#ifdef DEBUG
+ printf("Resetting card, writing 0x80 to offs 0x%x\n",
+ sp->cis->reg_addr);
+#endif
+ offs = sp->cis->reg_addr;
+ lseek(sp->fd, offs, SEEK_SET);
+ c = 0x80;
+ write(sp->fd, &c, sizeof(c));
+ usleep(10*1000);
+ c = 0;
+ lseek(sp->fd, offs, SEEK_SET);
+ write(sp->fd, &c, sizeof(c));
+/*
+ * Reset all the memory and I/O windows.
+ */
+ bzero((caddr_t)&mem, sizeof(mem));
+ bzero((caddr_t)&io, sizeof(io));
+ for (mem.window = 0; mem.window < NUM_MEM_WINDOWS; mem.window++)
+ ioctl(sp->fd, PIOCSMEM, &mem);
+ for (io.window = 0; io.window < NUM_IO_WINDOWS; io.window++)
+ ioctl(sp->fd, PIOCSIO, &io);
+}
+/*
+ * execute - Execute the command strings.
+ * For the current slot (if any) perform macro
+ * substitutions.
+ */
+void
+execute(struct cmd *cmdp)
+{
+char cmd[1024];
+char *p, *cp, *lp;
+
+ for (;cmdp; cmdp = cmdp->next)
+ {
+ cp = cmd;
+ lp = cmdp->line;
+ if (*lp == 0)
+ continue;
+ while (p = strchr(lp, '$'))
+ {
+/*
+ * copy over preceding string.
+ */
+ while (lp != p)
+ *cp++ = *lp++;
+/*
+ * stringify ethernet address and place here.
+ */
+ if (strncmp(p, "$ether", 6)==0)
+ {
+ sprintf(cp, "%x:%x:%x:%x:%x:%x",
+ current_slot->eaddr[0],
+ current_slot->eaddr[1],
+ current_slot->eaddr[2],
+ current_slot->eaddr[3],
+ current_slot->eaddr[4],
+ current_slot->eaddr[5]);
+ while (*++cp)
+ ;
+ lp += 6;
+ }
+/*
+ * replace device name
+ */
+ else if (strncmp(p, "$device", 7)==0)
+ {
+ sprintf(cp, "%s%d",
+ current_slot->config->driver->kernel,
+ current_slot->config->driver->unit);
+ while (*cp)
+ cp++;
+ lp += 7;
+ }
+/*
+ * Copy the `$' and rescan.
+ */
+ else
+ *cp++ = *lp++;
+ }
+/*
+ * No more replacements. Copy rest of string.
+ */
+ while (*cp++ = *lp++)
+ ;
+#ifdef DEBUG
+ fprintf(stderr, "Executing [%s]\n", cmd);
+#endif /* DEBUG */
+ system(cmd);
+ }
+}
OpenPOWER on IntegriCloud