From 6525e9352125ef84d8dc9be7527c97103bd99a46 Mon Sep 17 00:00:00 2001 From: phk Date: Thu, 24 Aug 1995 09:03:04 +0000 Subject: 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 --- usr.sbin/pccard/misc/COPYRIGHT | 11 + usr.sbin/pccard/misc/FILES | 12 + usr.sbin/pccard/misc/INSTALL | 34 + usr.sbin/pccard/misc/Makefile | 32 + usr.sbin/pccard/misc/README.old | 62 + usr.sbin/pccard/misc/rpti.c | 113 ++ usr.sbin/pccard/misc/skeldrv.c | 37 + usr.sbin/pccard/misc/sys/i386/conf/LAPTOP | 63 + usr.sbin/pccard/misc/sys/pccard/Makefile | 25 + usr.sbin/pccard/misc/sys/pccard/README | 13 + usr.sbin/pccard/misc/sys/pccard/bpfilter.h | 1 + usr.sbin/pccard/misc/sys/pccard/ed.h | 1 + usr.sbin/pccard/misc/sys/pccard/if_ed.c | 1997 ++++++++++++++++++++++ usr.sbin/pccard/misc/sys/pccard/if_edreg.h | 989 +++++++++++ usr.sbin/pccard/misc/sys/pccard/lkm_ed.c | 143 ++ usr.sbin/pccard/misc/sys/pccard/sio.c | 2463 ++++++++++++++++++++++++++++ usr.sbin/pccard/pccardc/Makefile | 37 + usr.sbin/pccard/pccardc/dumpcis.c | 79 + usr.sbin/pccard/pccardc/enabler.c | 144 ++ usr.sbin/pccard/pccardc/pccardc.c | 59 + usr.sbin/pccard/pccardc/pccardmem.c | 38 + usr.sbin/pccard/pccardc/printcis.c | 711 ++++++++ usr.sbin/pccard/pccardc/rdmap.c | 71 + usr.sbin/pccard/pccardc/rdreg.c | 48 + usr.sbin/pccard/pccardc/wrattr.c | 46 + usr.sbin/pccard/pccardc/wrreg.c | 39 + usr.sbin/pccard/pccardd/Makefile | 10 + usr.sbin/pccard/pccardd/cardd.c | 701 ++++++++ usr.sbin/pccard/pccardd/cardd.h | 129 ++ usr.sbin/pccard/pccardd/file.c | 873 ++++++++++ usr.sbin/pccard/pccardd/pccard.conf.5 | 183 +++ usr.sbin/pccard/pccardd/pccardd.8 | 133 ++ usr.sbin/pccard/pccardd/readcis.c | 633 +++++++ usr.sbin/pccard/pccardd/readcis.h | 116 ++ usr.sbin/pccard/pccardd/sample.config | 22 + usr.sbin/pccard/pccardd/util.c | 209 +++ 36 files changed, 10277 insertions(+) create mode 100644 usr.sbin/pccard/misc/COPYRIGHT create mode 100644 usr.sbin/pccard/misc/FILES create mode 100644 usr.sbin/pccard/misc/INSTALL create mode 100644 usr.sbin/pccard/misc/Makefile create mode 100644 usr.sbin/pccard/misc/README.old create mode 100644 usr.sbin/pccard/misc/rpti.c create mode 100644 usr.sbin/pccard/misc/skeldrv.c create mode 100644 usr.sbin/pccard/misc/sys/i386/conf/LAPTOP create mode 100644 usr.sbin/pccard/misc/sys/pccard/Makefile create mode 100644 usr.sbin/pccard/misc/sys/pccard/README create mode 100644 usr.sbin/pccard/misc/sys/pccard/bpfilter.h create mode 100644 usr.sbin/pccard/misc/sys/pccard/ed.h create mode 100644 usr.sbin/pccard/misc/sys/pccard/if_ed.c create mode 100644 usr.sbin/pccard/misc/sys/pccard/if_edreg.h create mode 100644 usr.sbin/pccard/misc/sys/pccard/lkm_ed.c create mode 100644 usr.sbin/pccard/misc/sys/pccard/sio.c create mode 100644 usr.sbin/pccard/pccardc/Makefile create mode 100644 usr.sbin/pccard/pccardc/dumpcis.c create mode 100644 usr.sbin/pccard/pccardc/enabler.c create mode 100644 usr.sbin/pccard/pccardc/pccardc.c create mode 100644 usr.sbin/pccard/pccardc/pccardmem.c create mode 100644 usr.sbin/pccard/pccardc/printcis.c create mode 100644 usr.sbin/pccard/pccardc/rdmap.c create mode 100644 usr.sbin/pccard/pccardc/rdreg.c create mode 100644 usr.sbin/pccard/pccardc/wrattr.c create mode 100644 usr.sbin/pccard/pccardc/wrreg.c create mode 100644 usr.sbin/pccard/pccardd/Makefile create mode 100644 usr.sbin/pccard/pccardd/cardd.c create mode 100644 usr.sbin/pccard/pccardd/cardd.h create mode 100644 usr.sbin/pccard/pccardd/file.c create mode 100644 usr.sbin/pccard/pccardd/pccard.conf.5 create mode 100644 usr.sbin/pccard/pccardd/pccardd.8 create mode 100644 usr.sbin/pccard/pccardd/readcis.c create mode 100644 usr.sbin/pccard/pccardd/readcis.h create mode 100644 usr.sbin/pccard/pccardd/sample.config create mode 100644 usr.sbin/pccard/pccardd/util.c (limited to 'usr.sbin/pccard') 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 +#include +#include +#include +#include + +#include +#include + +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 +#include +#include +#include +#include + +#include +#include + + +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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef INET +#include +#include +#include +#include +#include +#endif + +#ifdef NS +#include +#include +#endif + +#if NBPFILTER > 0 +#include +#include +#endif + +#include + +#include +#include + +#include +#include +#include + +/* 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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +/* + * 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 +#include +#include +#include +#define TTYDEFCHARS /* XXX TK2.0 */ +#include +#undef TTYDEFCHARS +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include /* XXX just to get at `imen' */ +#include +#include +#include +#include + +#include "crd.h" +#if NCRD > 0 +#include +#include +#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 + +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 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 + +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 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 + +## +## 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 +#include +#include +#include +#include + +#include +#include +#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 +#include +#include +#include +#include + +#include +#include + +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 +#include + +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 ...\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 +#include +#include + +#include +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 +#include +#include +#include +#include + +#include +#include + +#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 +#include +#include +#include +#include + +#include +#include + +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 +#include +#include + +#include +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 +#include +#include + +#include + +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, ®)) + { + perror("ioctl (PIOCRWFLAG)"); + exit(1); + } + if (sscanf(argv[2], "%x", ®) != 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 +#include +#include + +#include + +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", ®) != 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 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 +#include +#include +#include +#include +#include +#include +#include +#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 + +#include +#include + +#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 +#include +#include +#include +#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 +#include +#include +#include +#include + +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#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); + } +} -- cgit v1.1