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