summaryrefslogtreecommitdiffstats
path: root/sys/i386/isa
diff options
context:
space:
mode:
Diffstat (limited to 'sys/i386/isa')
-rw-r--r--sys/i386/isa/aha1542.c1644
-rw-r--r--sys/i386/isa/clock.c271
-rw-r--r--sys/i386/isa/fd.c903
-rw-r--r--sys/i386/isa/fdreg.h72
-rw-r--r--sys/i386/isa/ic/i8042.h23
-rw-r--r--sys/i386/isa/ic/i8237.h9
-rw-r--r--sys/i386/isa/ic/nec765.h71
-rw-r--r--sys/i386/isa/ic/ns16450.h49
-rw-r--r--sys/i386/isa/ic/ns16550.h50
-rw-r--r--sys/i386/isa/icu.h109
-rw-r--r--sys/i386/isa/icu.s376
-rw-r--r--sys/i386/isa/isa.c766
-rw-r--r--sys/i386/isa/isa.h188
-rw-r--r--sys/i386/isa/isa_device.h85
-rw-r--r--sys/i386/isa/kbd.h58
-rw-r--r--sys/i386/isa/lpt.c455
-rw-r--r--sys/i386/isa/lptreg.h34
-rw-r--r--sys/i386/isa/npx.c564
-rw-r--r--sys/i386/isa/rtc.h85
-rw-r--r--sys/i386/isa/sio.c1721
-rw-r--r--sys/i386/isa/sioreg.h113
-rw-r--r--sys/i386/isa/spkr.c520
-rw-r--r--sys/i386/isa/timerreg.h89
-rw-r--r--sys/i386/isa/ultra14f.c1308
-rw-r--r--sys/i386/isa/vector.s376
-rw-r--r--sys/i386/isa/wd.c1352
-rw-r--r--sys/i386/isa/wdreg.h143
-rw-r--r--sys/i386/isa/wt.c1162
-rw-r--r--sys/i386/isa/wtreg.h95
29 files changed, 12691 insertions, 0 deletions
diff --git a/sys/i386/isa/aha1542.c b/sys/i386/isa/aha1542.c
new file mode 100644
index 0000000..85271fc
--- /dev/null
+++ b/sys/i386/isa/aha1542.c
@@ -0,0 +1,1644 @@
+/*
+ * (Mostly) Written by Julian Elischer (julian@tfs.com)
+ * for TRW Financial Systems for use under the MACH(2.5) operating system.
+ *
+ * TRW Financial Systems, in accordance with their agreement with Carnegie
+ * Mellon University, makes this software available to CMU to distribute
+ * or use in any manner that they see fit as long as this message is kept with
+ * the software. For this reason TFS also grants any other persons or
+ * organisations permission to use or modify this software.
+ *
+ * TFS supplies this software to be publicly redistributed
+ * on the understanding that TFS is not responsible for the correct
+ * functioning of this software in any circumstances.
+ *
+ *
+ * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE
+ * -------------------- ----- ----------------------
+ * CURRENT PATCH LEVEL: 1 00098
+ * -------------------- ----- ----------------------
+ *
+ * 16 Feb 93 Julian Elischer ADDED for SCSI system
+ */
+
+/*
+ * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
+ */
+
+/*
+ * HISTORY
+ * $Log: aha1542.c,v $
+ * Revision 1.6 1992/08/24 21:01:58 jason
+ * many changes and bugfixes for osf1
+ *
+ * Revision 1.5 1992/07/31 01:22:03 julian
+ * support improved scsi.h layout
+ *
+ * Revision 1.4 1992/07/25 03:11:26 julian
+ * check each request fro sane flags.
+ *
+ * Revision 1.3 1992/07/24 00:52:45 julian
+ * improved timeout handling.
+ * added support for two arguments to the sd_done (or equiv) call so that
+ * they can pre-queue several arguments.
+ * slightly clean up error handling
+ *
+ * Revision 1.2 1992/07/17 22:03:54 julian
+ * upgraded the timeout code.
+ * added support for UIO-based i/o (as used for pmem operations)
+ *
+ * Revision 1.1 1992/05/27 00:51:12 balsup
+ * machkern/cor merge
+ */
+
+/*
+ * a FEW lines in this driver come from a MACH adaptec-disk driver
+ * so the copyright below is included:
+ *
+ * Copyright 1990 by Open Software Foundation,
+ * Grenoble, FRANCE
+ *
+ * All Rights Reserved
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both the copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of OSF or Open Software
+ * Foundation not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission.
+ *
+ * OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+ * IN NO EVENT SHALL OSF BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+ * NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+
+#include <sys/types.h>
+#include <aha.h>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/user.h>
+
+#ifdef MACH /* EITHER CMU OR OSF */
+#include <i386/ipl.h>
+#include <i386at/scsi.h>
+#include <i386at/scsiconf.h>
+
+#ifdef OSF /* OSF ONLY */
+#include <sys/table.h>
+#include <i386/handler.h>
+#include <i386/dispatcher.h>
+#include <i386/AT386/atbus.h>
+
+#else OSF /* CMU ONLY */
+#include <i386at/atbus.h>
+#include <i386/pio.h>
+#endif OSF
+#endif MACH /* end of MACH specific */
+
+#ifdef __386BSD__ /* 386BSD specific */
+#define isa_dev isa_device
+#define dev_unit id_unit
+#define dev_addr id_iobase
+
+#include <i386/isa/isa_device.h>
+#include <scsi/scsi_all.h>
+#include <scsi/scsiconf.h>
+#endif __386BSD__
+
+
+#ifdef __386BSD__
+#include "ddb.h"
+#if NDDB > 0
+int Debugger();
+#else NDDB
+#define Debugger() panic("should call debugger here (adaptec.c)")
+#endif NDDB
+#endif __386BSD__
+extern int delaycount; /* from clock setup code */
+
+/************************** board definitions *******************************/
+/*
+ * I/O Port Interface
+ */
+
+#define AHA_BASE aha_base[unit]
+#define AHA_CTRL_STAT_PORT (AHA_BASE + 0x0) /* control & status */
+#define AHA_CMD_DATA_PORT (AHA_BASE + 0x1) /* cmds and datas */
+#define AHA_INTR_PORT (AHA_BASE + 0x2) /* Intr. stat */
+
+/*
+ * AHA_CTRL_STAT bits (write)
+ */
+
+#define AHA_HRST 0x80 /* Hardware reset */
+#define AHA_SRST 0x40 /* Software reset */
+#define AHA_IRST 0x20 /* Interrupt reset */
+#define AHA_SCRST 0x10 /* SCSI bus reset */
+
+/*
+ * AHA_CTRL_STAT bits (read)
+ */
+
+#define AHA_STST 0x80 /* Self test in Progress */
+#define AHA_DIAGF 0x40 /* Diagnostic Failure */
+#define AHA_INIT 0x20 /* Mbx Init required */
+#define AHA_IDLE 0x10 /* Host Adapter Idle */
+#define AHA_CDF 0x08 /* cmd/data out port full */
+#define AHA_DF 0x04 /* Data in port full */
+#define AHA_INVDCMD 0x01 /* Invalid command */
+
+/*
+ * AHA_CMD_DATA bits (write)
+ */
+
+#define AHA_NOP 0x00 /* No operation */
+#define AHA_MBX_INIT 0x01 /* Mbx initialization */
+#define AHA_START_SCSI 0x02 /* start scsi command */
+#define AHA_START_BIOS 0x03 /* start bios command */
+#define AHA_INQUIRE 0x04 /* Adapter Inquiry */
+#define AHA_MBO_INTR_EN 0x05 /* Enable MBO available interrupt */
+#define AHA_SEL_TIMEOUT_SET 0x06 /* set selection time-out */
+#define AHA_BUS_ON_TIME_SET 0x07 /* set bus-on time */
+#define AHA_BUS_OFF_TIME_SET 0x08 /* set bus-off time */
+#define AHA_SPEED_SET 0x09 /* set transfer speed */
+#define AHA_DEV_GET 0x0a /* return installed devices */
+#define AHA_CONF_GET 0x0b /* return configuration data */
+#define AHA_TARGET_EN 0x0c /* enable target mode */
+#define AHA_SETUP_GET 0x0d /* return setup data */
+#define AHA_WRITE_CH2 0x1a /* write channel 2 buffer */
+#define AHA_READ_CH2 0x1b /* read channel 2 buffer */
+#define AHA_WRITE_FIFO 0x1c /* write fifo buffer */
+#define AHA_READ_FIFO 0x1d /* read fifo buffer */
+#define AHA_ECHO 0x1e /* Echo command data */
+
+struct aha_cmd_buf {
+ u_char byte[16];
+};
+
+/*
+ * AHA_INTR_PORT bits (read)
+ */
+
+#define AHA_ANY_INTR 0x80 /* Any interrupt */
+#define AHA_SCRD 0x08 /* SCSI reset detected */
+#define AHA_HACC 0x04 /* Command complete */
+#define AHA_MBOA 0x02 /* MBX out empty */
+#define AHA_MBIF 0x01 /* MBX in full */
+
+/*
+ * Mail box defs
+ */
+
+#define AHA_MBX_SIZE 16 /* mail box size */
+
+struct aha_mbx {
+ struct aha_mbx_out {
+ unsigned char cmd;
+ unsigned char ccb_addr[3];
+ } mbo [AHA_MBX_SIZE];
+ struct aha_mbx_in{
+ unsigned char stat;
+ unsigned char ccb_addr[3];
+ } mbi[AHA_MBX_SIZE];
+};
+
+/*
+ * mbo.cmd values
+ */
+
+#define AHA_MBO_FREE 0x0 /* MBO entry is free */
+#define AHA_MBO_START 0x1 /* MBO activate entry */
+#define AHA_MBO_ABORT 0x2 /* MBO abort entry */
+
+#define AHA_MBI_FREE 0x0 /* MBI entry is free */
+#define AHA_MBI_OK 0x1 /* completed without error */
+#define AHA_MBI_ABORT 0x2 /* aborted ccb */
+#define AHA_MBI_UNKNOWN 0x3 /* Tried to abort invalid CCB */
+#define AHA_MBI_ERROR 0x4 /* Completed with error */
+
+extern struct aha_mbx aha_mbx[];
+
+/* FOR OLD VERSIONS OF THE !%$@ this may have to be 16 (yuk) */
+#define AHA_NSEG 17 /* Number of scatter gather segments <= 16 */
+ /* allow 64 K i/o (min) */
+
+struct aha_ccb {
+ unsigned char opcode;
+ unsigned char lun:3;
+ unsigned char data_in:1; /* must be 0 */
+ unsigned char data_out:1; /* must be 0 */
+ unsigned char target:3;
+ unsigned char scsi_cmd_length;
+ unsigned char req_sense_length;
+ unsigned char data_length[3];
+ unsigned char data_addr[3];
+ unsigned char link_addr[3];
+ unsigned char link_id;
+ unsigned char host_stat;
+ unsigned char target_stat;
+ unsigned char reserved[2];
+ struct scsi_generic scsi_cmd;
+ struct scsi_sense_data scsi_sense;
+ struct aha_scat_gath {
+ unsigned char seg_len[3];
+ unsigned char seg_addr[3];
+ } scat_gath[AHA_NSEG];
+ struct aha_ccb *next;
+ struct scsi_xfer *xfer; /* the scsi_xfer for this cmd */
+ struct aha_mbx_out *mbx; /* pointer to mail box */
+ long int delta; /* difference from previous*/
+ struct aha_ccb *later,*sooner;
+ int flags;
+#define CCB_FREE 0
+#define CCB_ACTIVE 1
+#define CCB_ABORTED 2
+
+};
+
+struct aha_ccb *aha_soonest = (struct aha_ccb *)0;
+struct aha_ccb *aha_latest = (struct aha_ccb *)0;
+long int aha_furtherest = 0; /* longest time in the timeout queue */
+
+/*
+ * opcode fields
+ */
+
+#define AHA_INITIATOR_CCB 0x00 /* SCSI Initiator CCB */
+#define AHA_TARGET_CCB 0x01 /* SCSI Target CCB */
+#define AHA_INIT_SCAT_GATH_CCB 0x02 /* SCSI Initiator with scattter gather*/
+#define AHA_RESET_CCB 0x81 /* SCSI Bus reset */
+
+
+/*
+ * aha_ccb.host_stat values
+ */
+
+#define AHA_OK 0x00 /* cmd ok */
+#define AHA_LINK_OK 0x0a /* Link cmd ok */
+#define AHA_LINK_IT 0x0b /* Link cmd ok + int */
+#define AHA_SEL_TIMEOUT 0x11 /* Selection time out */
+#define AHA_OVER_UNDER 0x12 /* Data over/under run */
+#define AHA_BUS_FREE 0x13 /* Bus dropped at unexpected time */
+#define AHA_INV_BUS 0x14 /* Invalid bus phase/sequence */
+#define AHA_BAD_MBO 0x15 /* Incorrect MBO cmd */
+#define AHA_BAD_CCB 0x16 /* Incorrect ccb opcode */
+#define AHA_BAD_LINK 0x17 /* Not same values of LUN for links */
+#define AHA_INV_TARGET 0x18 /* Invalid target direction */
+#define AHA_CCB_DUP 0x19 /* Duplicate CCB received */
+#define AHA_INV_CCB 0x1a /* Invalid CCB or segment list */
+#define AHA_ABORTED 42
+
+
+
+
+struct aha_setup
+{
+ u_char sync_neg:1;
+ u_char parity:1;
+ u_char :6;
+ u_char speed;
+ u_char bus_on;
+ u_char bus_off;
+ u_char num_mbx;
+ u_char mbx[3];
+ struct
+ {
+ u_char offset:4;
+ u_char period:3;
+ u_char valid:1;
+ }sync[8];
+ u_char disc_sts;
+};
+
+struct aha_config
+{
+ u_char chan;
+ u_char intr;
+ u_char scsi_dev:3;
+ u_char :5;
+};
+
+#define INT9 0x01
+#define INT10 0x02
+#define INT11 0x04
+#define INT12 0x08
+#define INT14 0x20
+#define INT15 0x40
+
+#define CHAN0 0x01
+#define CHAN5 0x20
+#define CHAN6 0x40
+#define CHAN7 0x80
+
+
+/*********************************** end of board definitions***************/
+
+
+#ifdef MACH
+#define PHYSTOKV(x) phystokv(x)
+#define KVTOPHYS(x) kvtophys(x)
+#else MACH
+#ifdef __386BSD__
+#define PHYSTOKV(x) (x | 0xFE000000)
+#define KVTOPHYS(x) vtophys(x)
+#else __386BSD__
+#endif __386BSD__
+#endif MACH
+#define AHA_DMA_PAGES AHA_NSEG
+
+#define PAGESIZ 4096
+#define INVALIDATE_CACHE {asm volatile( ".byte 0x0F ;.byte 0x08" ); }
+
+
+u_char aha_scratch_buf[256];
+#ifdef MACH
+caddr_t aha_base[NAHA]; /* base port for each board */
+#else
+short aha_base[NAHA]; /* base port for each board */
+#endif
+struct aha_mbx aha_mbx[NAHA];
+struct aha_ccb *aha_ccb_free[NAHA];
+struct aha_ccb aha_ccb[NAHA][AHA_MBX_SIZE];
+struct scsi_xfer aha_scsi_xfer[NAHA];
+struct isa_dev *ahainfo[NAHA];
+struct aha_ccb *aha_get_ccb();
+int aha_int[NAHA];
+int aha_dma[NAHA];
+int aha_scsi_dev[NAHA];
+int aha_initialized[NAHA];
+#ifdef OSF
+int aha_attached[NAHA];
+#endif OSF
+int aha_debug = 0;
+
+int ahaprobe(), ahaattach(), ahaintr();
+#ifdef MACH
+struct isa_driver ahadriver = { ahaprobe, 0, ahaattach, "aha", 0, 0, 0};
+int (*ahaintrs[])() = {ahaintr, 0};
+#endif
+#ifdef __386BSD__
+struct isa_driver ahadriver = { ahaprobe, ahaattach, "aha",};
+#endif __386BSD__
+static int ahaunit = 0;
+
+
+#define aha_abortmbx(mbx) \
+ (mbx)->cmd = AHA_MBO_ABORT; \
+ outb(AHA_CMD_DATA_PORT, AHA_START_SCSI);
+#define aha_startmbx(mbx) \
+ (mbx)->cmd = AHA_MBO_START; \
+ outb(AHA_CMD_DATA_PORT, AHA_START_SCSI);
+
+
+
+int aha_scsi_cmd();
+int aha_timeout();
+void ahaminphys();
+long int aha_adapter_info();
+
+struct scsi_switch aha_switch =
+{
+ aha_scsi_cmd,
+ ahaminphys,
+ 0,
+ 0,
+ aha_adapter_info,
+ 0,0,0
+};
+#define AHA_CMD_TIMEOUT_FUDGE 200 /* multiplied to get Secs */
+#define AHA_RESET_TIMEOUT 1000000 /* time to wait for reset */
+#define AHA_SCSI_TIMEOUT_FUDGE 20 /* divided by for mSecs */
+
+
+/***********************************************************************\
+* aha_cmd(unit,icnt, ocnt,wait, retval, opcode, args) *
+* Activate Adapter command *
+* icnt: number of args (outbound bytes written after opcode) *
+* ocnt: number of expected returned bytes *
+* wait: number of seconds to wait for response *
+* retval: buffer where to place returned bytes *
+* opcode: opcode AHA_NOP, AHA_MBX_INIT, AHA_START_SCSI ... *
+* args: parameters *
+* *
+* Performs an adapter command through the ports. Not to be confused *
+* with a scsi command, which is read in via the dma *
+* One of the adapter commands tells it to read in a scsi command *
+\***********************************************************************/
+
+
+aha_cmd(unit,icnt, ocnt, wait,retval, opcode, args)
+
+u_char *retval;
+unsigned opcode;
+u_char args;
+{
+ unsigned *ic = &opcode;
+ u_char oc;
+ register i;
+ int sts;
+
+ /*******************************************************\
+ * multiply the wait argument by a big constant *
+ * zero defaults to 1 *
+ \*******************************************************/
+ if(!wait)
+ wait = AHA_CMD_TIMEOUT_FUDGE * delaycount;
+ else
+ wait *= AHA_CMD_TIMEOUT_FUDGE * delaycount;
+ /*******************************************************\
+ * Wait for the adapter to go idle, unless it's one of *
+ * the commands which don't need this *
+ \*******************************************************/
+ if (opcode != AHA_MBX_INIT && opcode != AHA_START_SCSI)
+ {
+ i = AHA_CMD_TIMEOUT_FUDGE * delaycount; /* 1 sec?*/
+ while (--i)
+ {
+ sts = inb(AHA_CTRL_STAT_PORT);
+ if (sts & AHA_IDLE)
+ {
+ break;
+ }
+ }
+ if (!i)
+ {
+ printf("aha_cmd: aha1542 host not idle(0x%x)\n",sts);
+ return(ENXIO);
+ }
+ }
+ /*******************************************************\
+ * Now that it is idle, if we expect output, preflush the*
+ * queue feeding to us. *
+ \*******************************************************/
+ if (ocnt)
+ {
+ while((inb(AHA_CTRL_STAT_PORT)) & AHA_DF)
+ inb(AHA_CMD_DATA_PORT);
+ }
+
+ /*******************************************************\
+ * Output the command and the number of arguments given *
+ * for each byte, first check the port is empty. *
+ \*******************************************************/
+ icnt++; /* include the command */
+ while (icnt--)
+ {
+ sts = inb(AHA_CTRL_STAT_PORT);
+ for (i=0; i< wait; i++)
+ {
+ sts = inb(AHA_CTRL_STAT_PORT);
+ if (!(sts & AHA_CDF))
+ break;
+ }
+ if (i >= wait)
+ {
+ printf("aha_cmd: aha1542 cmd/data port full\n");
+ outb(AHA_CTRL_STAT_PORT, AHA_SRST);
+ return(ENXIO);
+ }
+ outb(AHA_CMD_DATA_PORT, (u_char)(*ic++));
+ }
+ /*******************************************************\
+ * If we expect input, loop that many times, each time, *
+ * looking for the data register to have valid data *
+ \*******************************************************/
+ while (ocnt--)
+ {
+ sts = inb(AHA_CTRL_STAT_PORT);
+ for (i=0; i< wait; i++)
+ {
+ sts = inb(AHA_CTRL_STAT_PORT);
+ if (sts & AHA_DF)
+ break;
+ }
+ if (i >= wait)
+ {
+ printf("aha_cmd: aha1542 cmd/data port empty %d\n",ocnt);
+ return(ENXIO);
+ }
+ oc = inb(AHA_CMD_DATA_PORT);
+ if (retval)
+ *retval++ = oc;
+ }
+ /*******************************************************\
+ * Wait for the board to report a finised instruction *
+ \*******************************************************/
+ i=AHA_CMD_TIMEOUT_FUDGE * delaycount; /* 1 sec? */
+ while (--i)
+ {
+ sts = inb(AHA_INTR_PORT);
+ if (sts & AHA_HACC)
+ {
+ break;
+ }
+ }
+ if (!i)
+ {
+ printf("aha_cmd: aha1542 host not finished(0x%x)\n",sts);
+ return(ENXIO);
+ }
+ outb(AHA_CTRL_STAT_PORT, AHA_IRST);
+ return(0);
+}
+
+/*******************************************************\
+* Check if the device can be found at the port given *
+* and if so, set it up ready for further work *
+* as an argument, takes the isa_dev structure from *
+* autoconf.c *
+\*******************************************************/
+ahaprobe(dev)
+struct isa_dev *dev;
+{
+ int unit = ahaunit;
+#if defined(OSF)
+ static ihandler_t aha_handler[NAHA];
+ static ihandler_id_t *aha_handler_id[NAHA];
+ register ihandler_t *chp = &aha_handler[unit];;
+#endif /* defined(OSF) */
+
+ /***********************************************\
+ /***********************************************\
+ * find unit and check we have that many defined *
+ \***********************************************/
+ dev->dev_unit = unit;
+ aha_base[unit] = dev->dev_addr;
+ if(unit >= NAHA)
+ {
+ printf("aha: unit number (%d) too high\n",unit);
+ return(0);
+ }
+ /***********************************************\
+ * Try initialise a unit at this location *
+ * sets up dma and bus speed, loads aha_int[unit]*
+ \***********************************************/
+ if (aha_init(unit) != 0)
+ {
+ return(0);
+ }
+
+ /***********************************************\
+ * If it's there, put in it's interrupt vectors *
+ \***********************************************/
+#if !defined(OSF)
+#if defined MACH
+ iunit[aha_int[unit]] =unit;
+ ivect[aha_int[unit]] = ahaintr;
+ intpri[aha_int[unit]] = dev->dev_spl;
+ form_pic_mask();
+ /*take_dev_irq(dev);*/
+#else
+#ifdef __386BSD__
+ dev->id_irq = (1 << aha_int[unit]);
+ dev->id_drq = aha_dma[unit];
+#endif __386BSD__
+#endif
+#else /* !defined(OSF) */
+
+ chp->ih_level = dev->dev_pic;
+ chp->ih_handler = dev->dev_intr[0];
+ chp->ih_resolver = i386_resolver;
+ chp->ih_rdev = dev;
+ chp->ih_stats.intr_type = INTR_DEVICE;
+ chp->ih_stats.intr_cnt = 0;
+ chp->ih_hparam[0].intparam = unit;
+ if ((aha_handler_id[unit] = handler_add(chp)) != NULL)
+ handler_enable(aha_handler_id[unit]);
+ else
+ panic("Unable to add aha interrupt handler");
+#endif /* !defined(OSF) */
+#ifdef __386BSD__
+ printf("\n **");
+#else __386BSD__
+ printf("port=%x spl=%d\n",
+ dev->dev_addr, dev->dev_spl);
+#endif __386BSD__
+ ahaunit ++;
+ return(1);
+}
+
+/***********************************************\
+* Attach all the sub-devices we can find *
+\***********************************************/
+ahaattach(dev)
+struct isa_dev *dev;
+{
+ int unit = dev->dev_unit;
+
+#ifdef __386BSD__
+ printf(" probing for scsi devices**\n");
+#endif __386BSD__
+ /***********************************************\
+ * ask the adapter what subunits are present *
+ \***********************************************/
+ scsi_attachdevs( unit, aha_scsi_dev[unit], &aha_switch);
+#if defined(OSF)
+ aha_attached[unit]=1;
+#endif /* defined(OSF) */
+ if(!unit) /* only one for all boards */
+ {
+ aha_timeout(0);
+ }
+#ifdef __386BSD__
+ printf("aha%d",unit);
+#endif __386BSD__
+ return;
+
+}
+
+
+/***********************************************\
+* Return some information to the caller about *
+* the adapter and it's capabilities *
+\***********************************************/
+long int aha_adapter_info(unit)
+int unit;
+{
+ return(2); /* 2 outstanding requests at a time per device */
+}
+
+/***********************************************\
+* Catch an interrupt from the adaptor *
+\***********************************************/
+ahaintr(unit)
+{
+ struct aha_ccb *ccb;
+ unsigned char stat;
+ register i;
+
+ if(scsi_debug & PRINTROUTINES)
+ printf("ahaintr ");
+ /***********************************************\
+ * First acknowlege the interrupt, Then if it's *
+ * not telling about a completed operation *
+ * just return. *
+ \***********************************************/
+ stat = inb(AHA_INTR_PORT);
+ outb(AHA_CTRL_STAT_PORT, AHA_IRST);
+ if(scsi_debug & TRACEINTERRUPTS)
+ printf("int ");
+ if (! (stat & AHA_MBIF))
+ return(1);
+ if(scsi_debug & TRACEINTERRUPTS)
+ printf("b ");
+#if defined(OSF)
+ if (!aha_attached[unit])
+ {
+ return(1);
+ }
+#endif /* defined(OSF) */
+ /***********************************************\
+ * If it IS then process the competed operation *
+ \***********************************************/
+ for (i = 0; i < AHA_MBX_SIZE; i++)
+ {
+ if (aha_mbx[unit].mbi[i].stat != AHA_MBI_FREE)
+ {
+ ccb = (struct aha_ccb *)PHYSTOKV(
+ (_3btol(aha_mbx[unit].mbi[i].ccb_addr)));
+
+ if((stat = aha_mbx[unit].mbi[i].stat) != AHA_MBI_OK)
+ {
+ switch(stat)
+ {
+ case AHA_MBI_ABORT:
+ if(aha_debug)
+ printf("abort");
+ ccb->host_stat = AHA_ABORTED;
+ break;
+
+ case AHA_MBI_UNKNOWN:
+ ccb = (struct aha_ccb *)0;
+ if(aha_debug)
+ printf("unknown ccb for abort ");
+ /* may have missed it */
+ /* no such ccb known for abort */
+
+ case AHA_MBI_ERROR:
+ break;
+
+ default:
+ panic("Impossible mbxi status");
+
+ }
+ if( aha_debug && ccb )
+ {
+ u_char *cp;
+ cp = (u_char *)(&(ccb->scsi_cmd));
+ printf("op=%x %x %x %x %x %x\n",
+ cp[0], cp[1], cp[2],
+ cp[3], cp[4], cp[5]);
+ printf("stat %x for mbi[%d]\n"
+ , aha_mbx[unit].mbi[i].stat, i);
+ printf("addr = 0x%x\n", ccb);
+ }
+ }
+ if(ccb)
+ {
+ aha_remove_timeout(ccb);
+ aha_done(unit,ccb);
+ }
+ aha_mbx[unit].mbi[i].stat = AHA_MBI_FREE;
+ }
+ }
+ return(1);
+}
+
+/***********************************************\
+* A ccb (and hence a mbx-out is put onto the *
+* free list. *
+\***********************************************/
+aha_free_ccb(unit,ccb, flags)
+struct aha_ccb *ccb;
+{
+ unsigned int opri;
+
+ if(scsi_debug & PRINTROUTINES)
+ printf("ccb%d(0x%x)> ",unit,flags);
+ if (!(flags & SCSI_NOMASK))
+ opri = splbio();
+
+ ccb->next = aha_ccb_free[unit];
+ aha_ccb_free[unit] = ccb;
+ ccb->flags = CCB_FREE;
+ if(ccb->sooner || ccb->later)
+ {
+ printf("yikes, still in timeout queue\n");
+ aha_remove_timeout(ccb);
+ }
+ /***********************************************\
+ * If there were none, wake abybody waiting for *
+ * one to come free, starting with queued entries*
+ \***********************************************/
+ if (!ccb->next) {
+ wakeup(&aha_ccb_free[unit]);
+ }
+ if (!(flags & SCSI_NOMASK))
+ splx(opri);
+}
+
+/***********************************************\
+* Get a free ccb (and hence mbox-out entry) *
+\***********************************************/
+struct aha_ccb *
+aha_get_ccb(unit,flags)
+{
+ unsigned opri;
+ struct aha_ccb *rc;
+
+ if(scsi_debug & PRINTROUTINES)
+ printf("<ccb%d(0x%x) ",unit,flags);
+ if (!(flags & SCSI_NOMASK))
+ opri = splbio();
+ /***********************************************\
+ * If we can and have to, sleep waiting for one *
+ * to come free *
+ \***********************************************/
+ while ((!(rc = aha_ccb_free[unit])) && (!(flags & SCSI_NOSLEEP)))
+ {
+ sleep(&aha_ccb_free[unit], PRIBIO);
+ }
+ if (rc)
+ {
+ aha_ccb_free[unit] = aha_ccb_free[unit]->next;
+ rc->flags = CCB_ACTIVE;
+ }
+ if (!(flags & SCSI_NOMASK))
+ splx(opri);
+ return(rc);
+}
+
+
+/***********************************************\
+* We have a ccb which has been processed by the *
+* adaptor, now we look to see how the operation *
+* went. Wake up the owner if waiting *
+\***********************************************/
+aha_done(unit,ccb)
+struct aha_ccb *ccb;
+{
+ struct scsi_sense_data *s1,*s2;
+ struct scsi_xfer *xs = ccb->xfer;
+
+ if(scsi_debug & PRINTROUTINES )
+ printf("aha_done ");
+ /***********************************************\
+ * Otherwise, put the results of the operation *
+ * into the xfer and call whoever started it *
+ \***********************************************/
+ if(!(xs->flags & INUSE))
+ {
+ printf("exiting but not in use! ");
+ Debugger();
+ }
+ if ( ( ccb->host_stat != AHA_OK
+ || ccb->target_stat != SCSI_OK)
+ && (!(xs->flags & SCSI_ERR_OK)))
+ {
+ s1 = (struct scsi_sense_data *)(((char *)(&ccb->scsi_cmd))
+ + ccb->scsi_cmd_length);
+ s2 = &(xs->sense);
+
+ if(ccb->host_stat)
+ {
+ switch(ccb->host_stat)
+ {
+ case AHA_ABORTED:
+ case AHA_SEL_TIMEOUT: /* No response */
+ xs->error = XS_TIMEOUT;
+ break;
+ default: /* Other scsi protocol messes */
+ xs->error = XS_DRIVER_STUFFUP;
+ if (aha_debug > 1)
+ {
+ printf("host_stat%x\n",
+ ccb->host_stat);
+ }
+ }
+
+ }
+ else
+ {
+ switch(ccb->target_stat)
+ {
+ case 0x02:
+ /* structure copy!!!!!*/
+ *s2=*s1;
+ xs->error = XS_SENSE;
+ break;
+ case 0x08:
+ xs->error = XS_BUSY;
+ break;
+ default:
+ if (aha_debug > 1)
+ {
+ printf("target_stat%x\n",
+ ccb->target_stat);
+ }
+ xs->error = XS_DRIVER_STUFFUP;
+ }
+ }
+ }
+ else /* All went correctly OR errors expected */
+ {
+ xs->resid = 0;
+ }
+ xs->flags |= ITSDONE;
+ aha_free_ccb(unit,ccb, xs->flags);
+ if(xs->when_done)
+ (*(xs->when_done))(xs->done_arg,xs->done_arg2);
+}
+
+
+/***********************************************\
+* Start the board, ready for normal operation *
+\***********************************************/
+aha_init(unit)
+int unit;
+{
+ unsigned char ad[3];
+ volatile int i,sts;
+ struct aha_config conf;
+
+ /***********************************************\
+ * reset board, If it doesn't respond, assume *
+ * that it's not there.. good for the probe *
+ \***********************************************/
+
+ outb(AHA_CTRL_STAT_PORT, AHA_HRST|AHA_SRST);
+
+ for (i=0; i < AHA_RESET_TIMEOUT; i++)
+ {
+ sts = inb(AHA_CTRL_STAT_PORT) ;
+ if ( sts == (AHA_IDLE | AHA_INIT))
+ break;
+ }
+ if (i >= AHA_RESET_TIMEOUT)
+ {
+ if (aha_debug)
+ printf("aha_init: No answer from adaptec board\n");
+ return(ENXIO);
+ }
+
+ /***********************************************\
+ * Assume we have a board at this stage *
+ * setup dma channel from jumpers and save int *
+ * level *
+ \***********************************************/
+#ifdef __386BSD__
+ printf("aha%d reading board settings, ",unit);
+#define PRNT(x)
+#else __386BSD__
+ printf("aha%d:",unit);
+#define PRNT(x) printf(x)
+#endif __386BSD__
+ aha_cmd(unit,0, sizeof(conf), 0 ,&conf, AHA_CONF_GET);
+ switch(conf.chan)
+ {
+ case CHAN0:
+ outb(0x0b, 0x0c);
+ outb(0x0a, 0x00);
+ aha_dma[unit] = 0;
+ PRNT("dma=0 ");
+ break;
+ case CHAN5:
+ outb(0xd6, 0xc1);
+ outb(0xd4, 0x01);
+ aha_dma[unit] = 5;
+ PRNT("dma=5 ");
+ break;
+ case CHAN6:
+ outb(0xd6, 0xc2);
+ outb(0xd4, 0x02);
+ aha_dma[unit] = 6;
+ PRNT("dma=6 ");
+ break;
+ case CHAN7:
+ outb(0xd6, 0xc3);
+ outb(0xd4, 0x03);
+ aha_dma[unit] = 7;
+ PRNT("dma=7 ");
+ break;
+ default:
+ printf("illegal dma jumper setting\n");
+ return(EIO);
+ }
+ switch(conf.intr)
+ {
+ case INT9:
+ aha_int[unit] = 9;
+ PRNT("int=9 ");
+ break;
+ case INT10:
+ aha_int[unit] = 10;
+ PRNT("int=10 ");
+ break;
+ case INT11:
+ aha_int[unit] = 11;
+ PRNT("int=11 ");
+ break;
+ case INT12:
+ aha_int[unit] = 12;
+ PRNT("int=12 ");
+ break;
+ case INT14:
+ aha_int[unit] = 14;
+ PRNT("int=14 ");
+ break;
+ case INT15:
+ aha_int[unit] = 15;
+ PRNT("int=15 ");
+ break;
+ default:
+ printf("illegal int jumper setting\n");
+ return(EIO);
+ }
+ /* who are we on the scsi bus */
+ aha_scsi_dev[unit] = conf.scsi_dev;
+
+
+ /***********************************************\
+ * Initialize memory transfer speed *
+ \***********************************************/
+ if(!(aha_set_bus_speed(unit)))
+ {
+ return(EIO);
+ }
+
+
+ /***********************************************\
+ * Initialize mail box *
+ \***********************************************/
+
+ lto3b(KVTOPHYS(&aha_mbx[unit]), ad);
+
+ aha_cmd(unit,4, 0, 0, 0, AHA_MBX_INIT,
+ AHA_MBX_SIZE,
+ ad[0],
+ ad[1],
+ ad[2]);
+
+
+ /***********************************************\
+ * link the ccb's with the mbox-out entries and *
+ * into a free-list *
+ \***********************************************/
+ for (i=0; i < AHA_MBX_SIZE; i++) {
+ aha_ccb[unit][i].next = aha_ccb_free[unit];
+ aha_ccb_free[unit] = &aha_ccb[unit][i];
+ aha_ccb_free[unit]->flags = CCB_FREE;
+ aha_ccb_free[unit]->mbx = &aha_mbx[unit].mbo[i];
+ lto3b(KVTOPHYS(aha_ccb_free[unit]), aha_mbx[unit].mbo[i].ccb_addr);
+ }
+
+ /***********************************************\
+ * Note that we are going and return (to probe) *
+ \***********************************************/
+ aha_initialized[unit]++;
+ return(0);
+}
+
+
+
+
+
+void ahaminphys(bp)
+struct buf *bp;
+{
+#ifdef MACH
+#if !defined(OSF)
+ bp->b_flags |= B_NPAGES; /* can support scat/gather */
+#endif /* !defined(OSF) */
+#endif MACH
+/* aha seems to explode with 17 segs (64k may require 17 segs) */
+/* on old boards so use a max of 16 segs if you have problems here*/
+ if(bp->b_bcount > ((AHA_NSEG - 1) * PAGESIZ))
+ {
+ bp->b_bcount = ((AHA_NSEG - 1) * PAGESIZ);
+ }
+}
+
+/***********************************************\
+* start a scsi operation given the command and *
+* the data address. Also needs the unit, target *
+* and lu *
+\***********************************************/
+int aha_scsi_cmd(xs)
+struct scsi_xfer *xs;
+{
+ struct scsi_sense_data *s1,*s2;
+ struct aha_ccb *ccb;
+ struct aha_scat_gath *sg;
+ int seg; /* scatter gather seg being worked on */
+ int i = 0;
+ int rc = 0;
+ int thiskv;
+ int thisphys,nextphys;
+ int unit =xs->adapter;
+ int bytes_this_seg,bytes_this_page,datalen,flags;
+ struct iovec *iovp;
+ int s;
+
+ if(scsi_debug & PRINTROUTINES)
+ printf("aha_scsi_cmd ");
+ /***********************************************\
+ * get a ccb (mbox-out) to use. If the transfer *
+ * is from a buf (possibly from interrupt time) *
+ * then we can't allow it to sleep *
+ \***********************************************/
+ flags = xs->flags;
+ if(!(flags & INUSE))
+ {
+ printf("not in use!");
+ Debugger();
+ xs->flags |= INUSE;
+ }
+ if(flags & ITSDONE)
+ {
+ printf("Already done! check device retry code ");
+ Debugger();
+ xs->flags &= ~ITSDONE;
+ }
+ if(xs->bp) flags |= (SCSI_NOSLEEP); /* just to be sure */
+ if (!(ccb = aha_get_ccb(unit,flags)))
+ {
+ xs->error = XS_DRIVER_STUFFUP;
+ return(TRY_AGAIN_LATER);
+ }
+
+ if (ccb->mbx->cmd != AHA_MBO_FREE)
+ printf("MBO not free\n");
+
+ /***********************************************\
+ * Put all the arguments for the xfer in the ccb *
+ \***********************************************/
+ ccb->xfer = xs;
+ if(flags & SCSI_RESET)
+ {
+ ccb->opcode = AHA_RESET_CCB;
+ }
+ else
+ {
+ /* can't use S/G if zero length */
+ ccb->opcode = (xs->datalen?
+ AHA_INIT_SCAT_GATH_CCB
+ :AHA_INITIATOR_CCB);
+ }
+ ccb->target = xs->targ;;
+ ccb->data_out = 0;
+ ccb->data_in = 0;
+ ccb->lun = xs->lu;
+ ccb->scsi_cmd_length = xs->cmdlen;
+ ccb->req_sense_length = sizeof(ccb->scsi_sense);
+
+ if((xs->datalen) && (!(flags & SCSI_RESET)))
+ { /* can use S/G only if not zero length */
+ lto3b(KVTOPHYS(ccb->scat_gath), ccb->data_addr );
+ sg = ccb->scat_gath ;
+ seg = 0;
+ if(flags & SCSI_DATA_UIO)
+ {
+ iovp = ((struct uio *)xs->data)->uio_iov;
+ datalen = ((struct uio *)xs->data)->uio_iovcnt;
+ while ((datalen) && (seg < AHA_NSEG))
+ {
+ lto3b(iovp->iov_base,&(sg->seg_addr));
+ lto3b(iovp->iov_len,&(sg->seg_len));
+ if(scsi_debug & SHOWSCATGATH)
+ printf("(0x%x@0x%x)"
+ ,iovp->iov_len
+ ,iovp->iov_base);
+ sg++;
+ iovp++;
+ seg++;
+ datalen--;
+ }
+ }
+ else
+ {
+ /***********************************************\
+ * Set up the scatter gather block *
+ \***********************************************/
+
+ if(scsi_debug & SHOWSCATGATH)
+ printf("%d @0x%x:- ",xs->datalen,xs->data);
+ datalen = xs->datalen;
+ thiskv = (int)xs->data;
+ thisphys = KVTOPHYS(thiskv);
+
+ while ((datalen) && (seg < AHA_NSEG))
+ {
+ bytes_this_seg = 0;
+
+ /* put in the base address */
+ lto3b(thisphys,&(sg->seg_addr));
+
+ if(scsi_debug & SHOWSCATGATH)
+ printf("0x%x",thisphys);
+
+ /* do it at least once */
+ nextphys = thisphys;
+ while ((datalen) && (thisphys == nextphys))
+ /***************************************\
+ * This page is contiguous (physically) *
+ * with the the last, just extend the *
+ * length *
+ \***************************************/
+ {
+ /** how far to the end of the page ***/
+ nextphys = (thisphys & (~(PAGESIZ - 1)))
+ + PAGESIZ;
+ bytes_this_page = nextphys - thisphys;
+ /**** or the data ****/
+ bytes_this_page = min(bytes_this_page
+ ,datalen);
+ bytes_this_seg += bytes_this_page;
+ datalen -= bytes_this_page;
+
+ /**** get more ready for the next page ****/
+ thiskv = (thiskv & (~(PAGESIZ - 1)))
+ + PAGESIZ;
+ if(datalen)
+ thisphys = KVTOPHYS(thiskv);
+ }
+ /***************************************\
+ * next page isn't contiguous, finish the seg*
+ \***************************************/
+ if(scsi_debug & SHOWSCATGATH)
+ printf("(0x%x)",bytes_this_seg);
+ lto3b(bytes_this_seg,&(sg->seg_len));
+ sg++;
+ seg++;
+ }
+ }
+ lto3b(seg * sizeof(struct aha_scat_gath),ccb->data_length);
+ if(scsi_debug & SHOWSCATGATH)
+ printf("\n");
+ if (datalen)
+ { /* there's still data, must have run out of segs! */
+ printf("aha_scsi_cmd%d: more than %d DMA segs\n",
+ unit,AHA_NSEG);
+ xs->error = XS_DRIVER_STUFFUP;
+ aha_free_ccb(unit,ccb,flags);
+ return(HAD_ERROR);
+ }
+
+ }
+ else
+ { /* No data xfer, use non S/G values */
+ lto3b(0, ccb->data_addr );
+ lto3b(0,ccb->data_length);
+ }
+ lto3b(0, ccb->link_addr );
+ /***********************************************\
+ * Put the scsi command in the ccb and start it *
+ \***********************************************/
+ if(!(flags & SCSI_RESET))
+ bcopy(xs->cmd, &ccb->scsi_cmd, ccb->scsi_cmd_length);
+ if(scsi_debug & SHOWCOMMANDS)
+ {
+ u_char *b = (u_char *)&ccb->scsi_cmd;
+ if(!(flags & SCSI_RESET))
+ {
+ int i = 0;
+ printf("aha%d:%d:%d-"
+ ,unit
+ ,ccb->target
+ ,ccb->lun );
+ while(i < ccb->scsi_cmd_length )
+ {
+ if(i) printf(",");
+ printf("%x",b[i++]);
+ }
+ }
+ else
+ {
+ printf("aha%d:%d:%d-RESET- "
+ ,unit
+ ,ccb->target
+ ,ccb->lun
+ );
+ }
+ }
+ if (!(flags & SCSI_NOMASK))
+ {
+ s= splbio(); /* stop instant timeouts */
+ aha_add_timeout(ccb,xs->timeout);
+ aha_startmbx(ccb->mbx);
+ /***********************************************\
+ * Usually return SUCCESSFULLY QUEUED *
+ \***********************************************/
+ splx(s);
+ if(scsi_debug & TRACEINTERRUPTS)
+ printf("sent ");
+ return(SUCCESSFULLY_QUEUED);
+ }
+ aha_startmbx(ccb->mbx);
+ if(scsi_debug & TRACEINTERRUPTS)
+ printf("cmd_sent, waiting ");
+ /***********************************************\
+ * If we can't use interrupts, poll on completion*
+ \***********************************************/
+ {
+ int done = 0;
+ int count = delaycount * xs->timeout / AHA_SCSI_TIMEOUT_FUDGE;
+ while((!done) && count)
+ {
+ i=0;
+ while ( (!done) && i<AHA_MBX_SIZE)
+ {
+ if ((aha_mbx[unit].mbi[i].stat != AHA_MBI_FREE )
+ && (PHYSTOKV(_3btol(aha_mbx[unit].mbi[i].ccb_addr)
+ == (int)ccb)))
+ {
+ aha_mbx[unit].mbi[i].stat = AHA_MBI_FREE;
+ aha_done(unit,ccb);
+ done++;
+ }
+ i++;
+ }
+ count--;
+ }
+ if (!count)
+ {
+ if (!(xs->flags & SCSI_SILENT))
+ printf("cmd fail\n");
+ aha_abortmbx(ccb->mbx);
+ count = delaycount * 2000 / AHA_SCSI_TIMEOUT_FUDGE;
+ while((!done) && count)
+ {
+ i=0;
+ while ( (!done) && i<AHA_MBX_SIZE)
+ {
+ if ((aha_mbx[unit].mbi[i].stat != AHA_MBI_FREE )
+ && (PHYSTOKV(_3btol(aha_mbx[unit].mbi[i].ccb_addr)
+ == (int)ccb)))
+ {
+ aha_mbx[unit].mbi[i].stat = AHA_MBI_FREE;
+ aha_done(unit,ccb);
+ done++;
+ }
+ i++;
+ }
+ count--;
+ }
+ if(!count)
+ {
+ printf("abort failed in wait\n");
+ ccb->mbx->cmd = AHA_MBO_FREE;
+ }
+ aha_free_ccb(unit,ccb,flags);
+ ahaintr(unit);
+ xs->error = XS_DRIVER_STUFFUP;
+ return(HAD_ERROR);
+ }
+ ahaintr(unit);
+ if(xs->error) return(HAD_ERROR);
+ return(COMPLETE);
+
+ }
+}
+/***************************************************************\
+* try each speed in turn, when we find one that works, use *
+* the NEXT one for a safety margin, unless that doesn't exist *
+* or doesn't work. returns the nSEC value of the time used *
+* or 0 if it could get a working speed ( or the NEXT speed *
+* failed) *
+\***************************************************************/
+
+int aha_set_bus_speed(unit)
+int unit;
+{
+ int speed;
+ int retval,retval2;
+
+#ifdef EISA
+ speed = 0; /* start at the fastest */
+#else EISA
+ speed = 1; /* 100 ns can crash some ISA busses (!?!) */
+#endif EISA
+ while (1)
+ {
+ retval = aha_bus_speed_check(unit,speed);
+ if(retval == HAD_ERROR)
+ {
+ printf("no working bus speed!!!\n");
+ return(0);
+ }
+ if(retval == 0)
+ {
+ speed++;
+ }
+ else /* Go one slower to be safe */
+ { /* unless eisa at 100 ns.. trust it */
+ if(speed != 0)
+ {
+ speed++;
+ }
+ printf("%d nSEC ok, use ",retval);
+ retval2 = aha_bus_speed_check(unit,speed);
+ if(retval2 == HAD_ERROR) /* retval is slowest already */
+ {
+ printf("marginal ");
+ retval2 = retval;
+ }
+ if(retval2)
+ {
+ printf("%d nSEC ",retval2);
+ return(retval2);
+ }
+ else
+ {
+ printf(".. slower failed, abort.\n",retval);
+ return(0);
+ }
+
+ }
+ }
+}
+
+/***************************************************************\
+* Set the DMA speed to the Nth speed and try an xfer. If it *
+* fails return 0, if it succeeds return the nSec value selected *
+* If there is no such speed return HAD_ERROR. *
+\***************************************************************/
+static struct bus_speed
+{
+ char arg;
+ int nsecs;
+}aha_bus_speeds[] =
+{
+ {0x88,100},
+ {0x99,150},
+ {0xaa,200},
+ {0xbb,250},
+ {0xcc,300},
+ {0xdd,350},
+ {0xee,400},
+ {0xff,450}
+};
+static char aha_test_string[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdefghijklmnopqrstuvwxyz!@";
+
+int aha_bus_speed_check(unit,speed)
+int unit,speed;
+{
+ int numspeeds = sizeof(aha_bus_speeds)/sizeof(struct bus_speed);
+ u_char ad[3];
+
+ /*******************************************************\
+ * Check we have such an entry *
+ \*******************************************************/
+ if(speed >= numspeeds) return(HAD_ERROR); /* illegal speed */
+
+ /*******************************************************\
+ * Set the dma-speed *
+ \*******************************************************/
+ aha_cmd(unit,1, 0, 0, 0, AHA_SPEED_SET,aha_bus_speeds[speed].arg);
+
+ /*******************************************************\
+ * put the test data into the buffer and calculate *
+ * it's address. Read it onto the board *
+ \*******************************************************/
+ strcpy(aha_scratch_buf,aha_test_string);
+ lto3b(KVTOPHYS(aha_scratch_buf),ad);
+
+ aha_cmd(unit,3, 0, 0, 0, AHA_WRITE_FIFO, ad[0], ad[1], ad[2]);
+
+ /*******************************************************\
+ * clear the buffer then copy the contents back from the *
+ * board. *
+ \*******************************************************/
+ bzero(aha_scratch_buf,54); /* 54 bytes transfered by test */
+
+ aha_cmd(unit,3, 0, 0, 0, AHA_READ_FIFO, ad[0], ad[1], ad[2]);
+
+ /*******************************************************\
+ * Compare the original data and the final data and *
+ * return the correct value depending upon the result *
+ \*******************************************************/
+ if(strcmp(aha_test_string,aha_scratch_buf))
+ { /* copy failed.. assume too fast */
+ return(0);
+ }
+ else
+ { /* copy succeded assume speed ok */
+ return(aha_bus_speeds[speed].nsecs);
+ }
+}
+
+
+/*
+ * +----------+ +----------+ +----------+
+ * aha_soonest--->| later |--->| later|--->| later|-->0
+ * | [Delta] | | [Delta] | | [Delta] |
+ * 0<---|sooner |<---|sooner |<---|sooner |<---aha_latest
+ * +----------+ +----------+ +----------+
+ *
+ * aha_furtherest = sum(Delta[1..n])
+ */
+aha_add_timeout(ccb,time)
+struct aha_ccb *ccb;
+int time;
+{
+ int timeprev;
+ struct aha_ccb *prev;
+ int s = splbio();
+
+ if(prev = aha_latest) /* yes, an assign */
+ {
+ timeprev = aha_furtherest;
+ }
+ else
+ {
+ timeprev = 0;
+ }
+ while(prev && (timeprev > time))
+ {
+ timeprev -= prev->delta;
+ prev = prev->sooner;
+ }
+ if(prev)
+ {
+ ccb->delta = time - timeprev;
+ if( ccb->later = prev->later) /* yes an assign */
+ {
+ ccb->later->sooner = ccb;
+ ccb->later->delta -= ccb->delta;
+ }
+ else
+ {
+ aha_furtherest = time;
+ aha_latest = ccb;
+ }
+ ccb->sooner = prev;
+ prev->later = ccb;
+ }
+ else
+ {
+ if( ccb->later = aha_soonest) /* yes, an assign*/
+ {
+ ccb->later->sooner = ccb;
+ ccb->later->delta -= time;
+ }
+ else
+ {
+ aha_furtherest = time;
+ aha_latest = ccb;
+ }
+ ccb->delta = time;
+ ccb->sooner = (struct aha_ccb *)0;
+ aha_soonest = ccb;
+ }
+ splx(s);
+}
+
+aha_remove_timeout(ccb)
+struct aha_ccb *ccb;
+{
+ int s = splbio();
+
+ if(ccb->sooner)
+ {
+ ccb->sooner->later = ccb->later;
+ }
+ else
+ {
+ aha_soonest = ccb->later;
+ }
+ if(ccb->later)
+ {
+ ccb->later->sooner = ccb->sooner;
+ ccb->later->delta += ccb->delta;
+ }
+ else
+ {
+ aha_latest = ccb->sooner;
+ aha_furtherest -= ccb->delta;
+ }
+ ccb->sooner = ccb->later = (struct aha_ccb *)0;
+ splx(s);
+}
+
+
+extern int hz;
+#define ONETICK 500 /* milliseconds */
+#define SLEEPTIME ((hz * 1000) / ONETICK)
+aha_timeout(arg)
+int arg;
+{
+ struct aha_ccb *ccb;
+ int unit;
+ int s = splbio();
+
+ while( ccb = aha_soonest )
+ {
+ if(ccb->delta <= ONETICK)
+ /***********************************************\
+ * It has timed out, we need to do some work *
+ \***********************************************/
+ {
+ unit = ccb->xfer->adapter;
+ printf("aha%d: device %d timed out ",unit
+ ,ccb->xfer->targ);
+
+ /***************************************\
+ * Unlink it from the queue *
+ \***************************************/
+ aha_remove_timeout(ccb);
+
+ /***************************************\
+ * If The ccb's mbx is not free, then *
+ * the board has gone south *
+ \***************************************/
+ if(ccb->mbx->cmd != AHA_MBO_FREE)
+ {
+ printf("aha%d not taking commands!\n"
+ ,unit);
+ Debugger();
+ }
+ /***************************************\
+ * If it has been through before, then *
+ * a previous abort has failed, don't *
+ * try abort again *
+ \***************************************/
+ if(ccb->flags == CCB_ABORTED) /* abort timed out */
+ {
+ printf(" AGAIN\n");
+ ccb->xfer->retries = 0; /* I MEAN IT ! */
+ ccb->host_stat = AHA_ABORTED;
+ aha_done(unit,ccb);
+ }
+ else /* abort the operation that has timed out */
+ {
+ printf("\n");
+ aha_abortmbx(ccb->mbx);
+ /* 2 secs for the abort */
+ aha_add_timeout(ccb,2000 + ONETICK);
+ ccb->flags = CCB_ABORTED;
+ }
+ }
+ else
+ /***********************************************\
+ * It has not timed out, adjust and leave *
+ \***********************************************/
+ {
+ ccb->delta -= ONETICK;
+ aha_furtherest -= ONETICK;
+ break;
+ }
+ }
+ splx(s);
+ timeout(aha_timeout,arg,SLEEPTIME);
+}
diff --git a/sys/i386/isa/clock.c b/sys/i386/isa/clock.c
new file mode 100644
index 0000000..0fb7701
--- /dev/null
+++ b/sys/i386/isa/clock.c
@@ -0,0 +1,271 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz and Don Ahn.
+ *
+ * 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.
+ *
+ * @(#)clock.c 7.2 (Berkeley) 5/12/91
+ *
+ * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE
+ * -------------------- ----- ----------------------
+ * CURRENT PATCH LEVEL: 5 00158
+ * -------------------- ----- ----------------------
+ *
+ * 14 Aug 92 Arne Henrik Juul Added code in the kernel to
+ * allow for DST in the BIOS.
+ * 17 Jan 93 Bruce Evans Fixed leap year and second
+ * calculations
+ * 01 Feb 93 Julian Elischer Added code to for the cpu
+ * speed independent spinwait()
+ * function, (used by scsi and others)
+ * 25 Mar 93 Sean Eric Fagan Add microtimer support using timer 1
+ * 08 Apr 93 Poul-Henning Kamp/P-HK Fixes, and support for dcfclock
+ * 26 Apr 93 Bruce Evans Eliminate findspeed, new spinwait
+ * 26 Apr 93 Rodney W. Grimes I merged in Bruce changes and hope I
+ * still kept the other fixes... Had to
+ * add back in findcpuspeed that Bruce
+ * had removed.
+ */
+
+/*
+ * Primitive clock interrupt routines.
+ */
+#include "param.h"
+#include "systm.h"
+#include "time.h"
+#include "kernel.h"
+#include "machine/segments.h"
+#include "i386/isa/icu.h"
+#include "i386/isa/isa.h"
+#include "i386/isa/rtc.h"
+#include "i386/isa/timerreg.h"
+
+#define DAYST 119
+#define DAYEN 303
+
+/* X-tals being what they are, it's nice to be able to fudge this one... */
+/* Note, the name changed here from XTALSPEED to TIMER_FREQ rgrimes 4/26/93 */
+#ifndef TIMER_FREQ
+#define TIMER_FREQ 1193182 /* XXX - should be in isa.h */
+#endif
+
+startrtclock() {
+ int s;
+
+ findcpuspeed(); /* use the clock (while it's free)
+ to find the cpu speed */
+ /* initialize 8253 clock */
+ outb(TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT);
+
+ /* Correct rounding will buy us a better precision in timekeeping */
+ outb (IO_TIMER1, (TIMER_FREQ+hz/2)/hz);
+ outb (IO_TIMER1, ((TIMER_FREQ+hz/2)/hz)/256);
+
+ /* initialize brain-dead battery powered clock */
+ outb (IO_RTC, RTC_STATUSA);
+ outb (IO_RTC+1, 0x26);
+ outb (IO_RTC, RTC_STATUSB);
+ outb (IO_RTC+1, 2);
+
+ outb (IO_RTC, RTC_DIAG);
+ if (s = inb (IO_RTC+1))
+ printf("RTC BIOS diagnostic error %b\n", s, RTCDG_BITS);
+ outb (IO_RTC, RTC_DIAG);
+ outb (IO_RTC+1, 0);
+}
+
+unsigned int delaycount; /* calibrated loop variable (1 millisecond) */
+
+#define FIRST_GUESS 0x2000
+findcpuspeed()
+{
+ unsigned char low;
+ unsigned int remainder;
+
+ /* Put counter in count down mode */
+ outb(IO_TIMER1+3, 0x34);
+ outb(IO_TIMER1, 0xff);
+ outb(IO_TIMER1, 0xff);
+ delaycount = FIRST_GUESS;
+ spinwait(1);
+ /* Read the value left in the counter */
+ low = inb(IO_TIMER1); /* least siginifcant */
+ remainder = inb(IO_TIMER1); /* most significant */
+ remainder = (remainder<<8) + low ;
+ /* Formula for delaycount is :
+ * (loopcount * timer clock speed)/ (counter ticks * 1000)
+ */
+ delaycount = (FIRST_GUESS * (TIMER_FREQ/1000)) / (0xffff-remainder);
+}
+
+
+/* convert 2 digit BCD number */
+bcd(i)
+int i;
+{
+ return ((i/16)*10 + (i%16));
+}
+
+/* convert years to seconds (from 1970) */
+unsigned long
+ytos(y)
+int y;
+{
+ int i;
+ unsigned long ret;
+
+ ret = 0;
+ for(i = 1970; i < y; i++) {
+ if (i % 4) ret += 365*24*60*60;
+ else ret += 366*24*60*60;
+ }
+ return ret;
+}
+
+/* convert months to seconds */
+unsigned long
+mtos(m,leap)
+int m,leap;
+{
+ int i;
+ unsigned long ret;
+
+ ret = 0;
+ for(i=1;i<m;i++) {
+ switch(i){
+ case 1: case 3: case 5: case 7: case 8: case 10: case 12:
+ ret += 31*24*60*60; break;
+ case 4: case 6: case 9: case 11:
+ ret += 30*24*60*60; break;
+ case 2:
+ if (leap) ret += 29*24*60*60;
+ else ret += 28*24*60*60;
+ }
+ }
+ return ret;
+}
+
+
+/*
+ * Initialize the time of day register, based on the time base which is, e.g.
+ * from a filesystem.
+ */
+inittodr(base)
+ time_t base;
+{
+ unsigned long sec;
+ int leap,day_week,t,yd;
+ int sa,s;
+
+ /* do we have a realtime clock present? (otherwise we loop below) */
+ sa = rtcin(RTC_STATUSA);
+ if (sa == 0xff || sa == 0) return;
+
+ /* ready for a read? */
+ while ((sa&RTCSA_TUP) == RTCSA_TUP)
+ sa = rtcin(RTC_STATUSA);
+
+ sec = bcd(rtcin(RTC_YEAR)) + 1900;
+ if (sec < 1970)
+ sec += 100;
+ leap = !(sec % 4); sec = ytos(sec); /* year */
+ yd = mtos(bcd(rtcin(RTC_MONTH)),leap); sec += yd; /* month */
+ t = (bcd(rtcin(RTC_DAY))-1) * 24*60*60; sec += t; yd += t; /* date */
+ day_week = rtcin(RTC_WDAY); /* day */
+ sec += bcd(rtcin(RTC_HRS)) * 60*60; /* hour */
+ sec += bcd(rtcin(RTC_MIN)) * 60; /* minutes */
+ sec += bcd(rtcin(RTC_SEC)); /* seconds */
+
+ /* XXX off by one? Need to calculate DST on SUNDAY */
+ /* Perhaps we should have the RTC hold GMT time to save */
+ /* us the bother of converting. */
+ yd = yd / (24*60*60);
+ if ((yd >= DAYST) && ( yd <= DAYEN)) {
+ sec -= 60*60;
+ }
+ sec += tz.tz_minuteswest * 60;
+
+ time.tv_sec = sec;
+}
+
+#ifdef garbage
+/*
+ * Initialze the time of day register, based on the time base which is, e.g.
+ * from a filesystem.
+ */
+test_inittodr(base)
+ time_t base;
+{
+
+ outb(IO_RTC,9); /* year */
+ printf("%d ",bcd(inb(IO_RTC+1)));
+ outb(IO_RTC,8); /* month */
+ printf("%d ",bcd(inb(IO_RTC+1)));
+ outb(IO_RTC,7); /* day */
+ printf("%d ",bcd(inb(IO_RTC+1)));
+ outb(IO_RTC,4); /* hour */
+ printf("%d ",bcd(inb(IO_RTC+1)));
+ outb(IO_RTC,2); /* minutes */
+ printf("%d ",bcd(inb(IO_RTC+1)));
+ outb(IO_RTC,0); /* seconds */
+ printf("%d\n",bcd(inb(IO_RTC+1)));
+
+ time.tv_sec = base;
+}
+#endif
+
+/*
+ * Restart the clock.
+ */
+resettodr()
+{
+}
+
+/*
+ * Wire clock interrupt in.
+ */
+#define V(s) __CONCAT(V, s)
+extern V(clk)();
+enablertclock() {
+ setidt(ICU_OFFSET+0, &V(clk), SDT_SYS386IGT, SEL_KPL);
+ INTREN(IRQ0);
+}
+
+/*
+ * Delay for some number of milliseconds.
+ */
+void
+spinwait(millisecs)
+ int millisecs;
+{
+ DELAY(1000 * millisecs);
+}
diff --git a/sys/i386/isa/fd.c b/sys/i386/isa/fd.c
new file mode 100644
index 0000000..a461e81
--- /dev/null
+++ b/sys/i386/isa/fd.c
@@ -0,0 +1,903 @@
+/*#define DEBUG 1*/
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Don Ahn.
+ *
+ * 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.
+ *
+ * @(#)fd.c 7.4 (Berkeley) 5/25/91
+ *
+ * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE
+ * -------------------- ----- ----------------------
+ * CURRENT PATCH LEVEL: 1 00153
+ * -------------------- ----- ----------------------
+ *
+ * 20 Apr 93 Julian Elischer Heavily re worked, see notes below
+ *
+ * Largely rewritten to handle multiple controllers and drives
+ * By Julian Elischer, Sun Apr 4 16:34:33 WST 1993
+ */
+char rev[] = "$Revision: 1.10 $";
+/*
+ * $Header: /usr/src/sys.386bsd/i386/isa/RCS/fd.c,v 1.10 93/04/13 16:53:29 root Exp $
+ */
+/*
+ * $Log: fd.c,v $
+ * Revision 1.10 93/04/13 16:53:29 root
+ * make sure turning off a drive motor doesn't deselect another
+ * drive active at the time.
+ * Also added a pointer from the fd_data to it's fd_type.
+ *
+ * Revision 1.9 93/04/13 15:31:02 root
+ * make all seeks go through DOSEEK state so are sure of being done right.
+ *
+ * Revision 1.8 93/04/12 21:20:13 root
+ * only check if old fd is the one we are working on if there IS
+ * an old fd pointer. (in fdstate())
+ *
+ * Revision 1.7 93/04/11 17:05:35 root
+ * cleanup timeouts etc.
+ * also fix bug to select teh correct drive when running > 1 drive
+ * at a time.
+ *
+ * Revision 1.6 93/04/05 00:48:45 root
+ * change a timeout and add version to banner message
+ *
+ * Revision 1.5 93/04/04 16:39:08 root
+ * first working version.. some floppy controllers don't seem to
+ * like 2 int. status inquiries in a row.
+ *
+ */
+
+#include "fd.h"
+#if NFD > 0
+
+#include "param.h"
+#include "dkbad.h"
+#include "systm.h"
+#include "conf.h"
+#include "file.h"
+#include "ioctl.h"
+#include "buf.h"
+#include "uio.h"
+#include "i386/isa/isa.h"
+#include "i386/isa/isa_device.h"
+#include "i386/isa/fdreg.h"
+#include "i386/isa/icu.h"
+#include "i386/isa/rtc.h"
+#undef NFD
+#define NFD 2
+
+#define FDUNIT(s) ((s>>3)&1)
+#define FDTYPE(s) ((s)&7)
+
+#define b_cylin b_resid
+#define FDBLK 512
+#define NUMTYPES 4
+
+struct fd_type {
+ int sectrac; /* sectors per track */
+ int secsize; /* size code for sectors */
+ int datalen; /* data len when secsize = 0 */
+ int gap; /* gap len between sectors */
+ int tracks; /* total num of tracks */
+ int size; /* size of disk in sectors */
+ int steptrac; /* steps per cylinder */
+ int trans; /* transfer speed code */
+ int heads; /* number of heads */
+};
+
+struct fd_type fd_types[NUMTYPES] =
+{
+ { 18,2,0xFF,0x1B,80,2880,1,0,2 }, /* 1.44 meg HD 3.5in floppy */
+ { 15,2,0xFF,0x1B,80,2400,1,0,2 }, /* 1.2 meg HD floppy */
+ { 9,2,0xFF,0x23,40,720,2,1,2 }, /* 360k floppy in 1.2meg drive */
+ { 9,2,0xFF,0x2A,40,720,1,1,2 }, /* 360k floppy in DD drive */
+};
+
+#define DRVS_PER_CTLR 2
+/***********************************************************************\
+* Per controller structure. *
+\***********************************************************************/
+struct fdc_data
+{
+ int fdcu; /* our unit number */
+ int baseport;
+ int dmachan;
+ int flags;
+#define FDC_ATTACHED 0x01
+ struct fd_data *fd;
+ int fdu; /* the active drive */
+ struct buf head; /* Head of buf chain */
+ struct buf rhead; /* Raw head of buf chain */
+ int state;
+ int retry;
+ int status[7]; /* copy of the registers */
+}fdc_data[(NFD+1)/DRVS_PER_CTLR];
+
+/***********************************************************************\
+* Per drive structure. *
+* N per controller (presently 2) (DRVS_PER_CTLR) *
+\***********************************************************************/
+struct fd_data {
+ struct fdc_data *fdc;
+ int fdu; /* this unit number */
+ int fdsu; /* this units number on this controller */
+ int type; /* Drive type (HD, DD */
+ struct fd_type *ft; /* pointer to the type descriptor */
+ int flags;
+#define FD_OPEN 0x01 /* it's open */
+#define FD_ACTIVE 0x02 /* it's active */
+#define FD_MOTOR 0x04 /* motor should be on */
+#define FD_MOTOR_WAIT 0x08 /* motor coming up */
+ int skip;
+ int hddrv;
+ int track; /* where we think the head is */
+} fd_data[NFD];
+
+/***********************************************************************\
+* Throughout this file the following conventions will be used: *
+* fd is a pointer to the fd_data struct for the drive in question *
+* fdc is a pointer to the fdc_data struct for the controller *
+* fdu is the floppy drive unit number *
+* fdcu is the floppy controller unit number *
+* fdsu is the floppy drive unit number on that controller. (sub-unit) *
+\***********************************************************************/
+typedef int fdu_t;
+typedef int fdcu_t;
+typedef int fdsu_t;
+typedef struct fd_data *fd_p;
+typedef struct fdc_data *fdc_p;
+
+#define DEVIDLE 0
+#define FINDWORK 1
+#define DOSEEK 2
+#define SEEKCOMPLETE 3
+#define IOCOMPLETE 4
+#define RECALCOMPLETE 5
+#define STARTRECAL 6
+#define RESETCTLR 7
+#define SEEKWAIT 8
+#define RECALWAIT 9
+#define MOTORWAIT 10
+#define IOTIMEDOUT 11
+
+#ifdef DEBUG
+char *fdstates[] =
+{
+"DEVIDLE",
+"FINDWORK",
+"DOSEEK",
+"SEEKCOMPLETE",
+"IOCOMPLETE",
+"RECALCOMPLETE",
+"STARTRECAL",
+"RESETCTLR",
+"SEEKWAIT",
+"RECALWAIT",
+"MOTORWAIT",
+"IOTIMEDOUT"
+};
+
+
+int fd_debug = 1;
+#define TRACE0(arg) if(fd_debug) printf(arg)
+#define TRACE1(arg1,arg2) if(fd_debug) printf(arg1,arg2)
+#else DEBUG
+#define TRACE0(arg)
+#define TRACE1(arg1,arg2)
+#endif DEBUG
+
+extern int hz;
+/* state needed for current transfer */
+
+/****************************************************************************/
+/* autoconfiguration stuff */
+/****************************************************************************/
+int fdprobe(), fdattach(), fd_turnoff();
+
+struct isa_driver fddriver = {
+ fdprobe, fdattach, "fd",
+};
+
+/*
+ * probe for existance of controller
+ */
+fdprobe(dev)
+struct isa_device *dev;
+{
+ fdcu_t fdcu = dev->id_unit;
+ if(fdc_data[fdcu].flags & FDC_ATTACHED)
+ {
+ printf("fdc: same unit (%d) used multiple times\n",fdcu);
+ return 0;
+ }
+
+ fdc_data[fdcu].baseport = dev->id_iobase;
+
+ /* see if it can handle a command */
+ if (out_fdc(fdcu,NE7CMD_SPECIFY) < 0)
+ {
+ return(0);
+ }
+ out_fdc(fdcu,0xDF);
+ out_fdc(fdcu,2);
+ return (IO_FDCSIZE);
+}
+
+/*
+ * wire controller into system, look for floppy units
+ */
+fdattach(dev)
+struct isa_device *dev;
+{
+ unsigned fdt,st0, cyl;
+ int hdr;
+ fdu_t fdu;
+ fdcu_t fdcu = dev->id_unit;
+ fdc_p fdc = fdc_data + fdcu;
+ fd_p fd;
+ int fdsu;
+
+ fdc->fdcu = fdcu;
+ fdc->flags |= FDC_ATTACHED;
+ fdc->dmachan = dev->id_drq;
+ fdc->state = DEVIDLE;
+
+ fdt = rtcin(RTC_FDISKETTE);
+ hdr = 0;
+
+ /* check for each floppy drive */
+ for (fdu = (fdcu * DRVS_PER_CTLR),fdsu = 0;
+ ((fdu < NFD) && (fdsu < DRVS_PER_CTLR));
+ fdu++,fdsu++)
+ {
+ /* is there a unit? */
+ if ((fdt & 0xf0) == RTCFDT_NONE)
+ continue;
+
+#ifdef notyet
+ /* select it */
+ fd_turnon1(fdu);
+ spinwait(1000); /* 1 sec */
+ out_fdc(fdcu,NE7CMD_RECAL); /* Recalibrate Function */
+ out_fdc(fdcu,fdsu);
+ spinwait(1000); /* 1 sec */
+
+ /* anything responding */
+ out_fdc(fdcu,NE7CMD_SENSEI);
+ st0 = in_fdc(fdcu);
+ cyl = in_fdc(fdcu);
+ if (st0 & 0xd0)
+ continue;
+
+#endif
+ fd_data[fdu].track = -2;
+ fd_data[fdu].fdc = fdc;
+ fd_data[fdu].fdsu = fdsu;
+ /* yes, announce it */
+ if (!hdr)
+ printf(" drives ");
+ else
+ printf(", ");
+ printf("%d: ", fdu);
+
+
+ if ((fdt & 0xf0) == RTCFDT_12M) {
+ printf("1.2M");
+ fd_data[fdu].type = 1;
+ fd_data[fdu].ft = fd_types + 1;
+
+ }
+ if ((fdt & 0xf0) == RTCFDT_144M) {
+ printf("1.44M");
+ fd_data[fdu].type = 0;
+ fd_data[fdu].ft = fd_types + 0;
+ }
+
+ fdt <<= 4;
+ fd_turnoff(fdu);
+ hdr = 1;
+ }
+
+ printf(" %s ",rev);
+ /* Set transfer to 500kbps */
+ outb(fdc->baseport+fdctl,0); /*XXX*/
+}
+
+int
+fdsize(dev)
+dev_t dev;
+{
+ return(0);
+}
+
+/****************************************************************************/
+/* fdstrategy */
+/****************************************************************************/
+fdstrategy(bp)
+ register struct buf *bp; /* IO operation to perform */
+{
+ register struct buf *dp,*dp0,*dp1;
+ long nblocks,blknum;
+ int s;
+ fdcu_t fdcu;
+ fdu_t fdu;
+ fdc_p fdc;
+ fd_p fd;
+
+ fdu = FDUNIT(minor(bp->b_dev));
+ fd = &fd_data[fdu];
+ fdc = fd->fdc;
+ fdcu = fdc->fdcu;
+ /*type = FDTYPE(minor(bp->b_dev));*/
+
+ if ((fdu >= NFD) || (bp->b_blkno < 0)) {
+ printf("fdstrat: fdu = %d, blkno = %d, bcount = %d\n",
+ fdu, bp->b_blkno, bp->b_bcount);
+ pg("fd:error in fdstrategy");
+ bp->b_error = EINVAL;
+ bp->b_flags |= B_ERROR;
+ goto bad;
+ }
+ /*
+ * Set up block calculations.
+ */
+ blknum = (unsigned long) bp->b_blkno * DEV_BSIZE/FDBLK;
+ nblocks = fd->ft->size;
+ if (blknum + (bp->b_bcount / FDBLK) > nblocks) {
+ if (blknum == nblocks) {
+ bp->b_resid = bp->b_bcount;
+ } else {
+ bp->b_error = ENOSPC;
+ bp->b_flags |= B_ERROR;
+ }
+ goto bad;
+ }
+ bp->b_cylin = blknum / (fd->ft->sectrac * fd->ft->heads);
+ dp = &(fdc->head);
+ s = splbio();
+ disksort(dp, bp);
+ untimeout(fd_turnoff,fdu); /* a good idea */
+ fdstart(fdcu);
+ splx(s);
+ return;
+
+bad:
+ biodone(bp);
+}
+
+/****************************************************************************/
+/* motor control stuff */
+/* remember to not deselect the drive we're working on */
+/****************************************************************************/
+set_motor(fdcu_t fdcu, fdu_t fdu, int reset)
+{
+ int m0,m1;
+ int selunit;
+ fd_p fd;
+ if(fd = fdc_data[fdcu].fd)/* yes an assign! */
+ {
+ selunit = fd->fdsu;
+ }
+ else
+ {
+ selunit = 0;
+ }
+ m0 = fd_data[fdcu * DRVS_PER_CTLR + 0].flags & FD_MOTOR;
+ m1 = fd_data[fdcu * DRVS_PER_CTLR + 1].flags & FD_MOTOR;
+ outb(fdc_data[fdcu].baseport+fdout,
+ selunit
+ | (reset ? 0 : (FDO_FRST|FDO_FDMAEN))
+ | (m0 ? FDO_MOEN0 : 0)
+ | (m1 ? FDO_MOEN1 : 0));
+ TRACE1("[0x%x->fdout]",(
+ selunit
+ | (reset ? 0 : (FDO_FRST|FDO_FDMAEN))
+ | (m0 ? FDO_MOEN0 : 0)
+ | (m1 ? FDO_MOEN1 : 0)));
+}
+
+fd_turnoff(fdu_t fdu)
+{
+ fd_p fd = fd_data + fdu;
+ fd->flags &= ~FD_MOTOR;
+ set_motor(fd->fdc->fdcu,fd->fdsu,0);
+}
+
+fd_motor_on(fdu_t fdu)
+{
+ fd_p fd = fd_data + fdu;
+ fd->flags &= ~FD_MOTOR_WAIT;
+ if((fd->fdc->fd == fd) && (fd->fdc->state == MOTORWAIT))
+ {
+ fd_pseudointr(fd->fdc->fdcu);
+ }
+}
+
+fd_turnon(fdu_t fdu)
+{
+ fd_p fd = fd_data + fdu;
+ if(!(fd->flags & FD_MOTOR))
+ {
+ fd_turnon1(fdu);
+ fd->flags |= FD_MOTOR_WAIT;
+ timeout(fd_motor_on,fdu,hz); /* in 1 sec its ok */
+ }
+}
+
+fd_turnon1(fdu_t fdu)
+{
+ fd_p fd = fd_data + fdu;
+ fd->flags |= FD_MOTOR;
+ set_motor(fd->fdc->fdcu,fd->fdsu,0);
+}
+
+/****************************************************************************/
+/* fdc in/out */
+/****************************************************************************/
+int
+in_fdc(fdcu_t fdcu)
+{
+ int baseport = fdc_data[fdcu].baseport;
+ int i, j = 100000;
+ while ((i = inb(baseport+fdsts) & (NE7_DIO|NE7_RQM))
+ != (NE7_DIO|NE7_RQM) && j-- > 0)
+ if (i == NE7_RQM) return -1;
+ if (j <= 0)
+ return(-1);
+#ifdef DEBUG
+ i = inb(baseport+fddata);
+ TRACE1("[fddata->0x%x]",(unsigned char)i);
+ return(i);
+#else
+ return inb(baseport+fddata);
+#endif
+}
+
+out_fdc(fdcu_t fdcu,int x)
+{
+ int baseport = fdc_data[fdcu].baseport;
+ int i = 100000;
+
+ while ((inb(baseport+fdsts) & NE7_DIO) && i-- > 0);
+ while ((inb(baseport+fdsts) & NE7_RQM) == 0 && i-- > 0);
+ if (i <= 0) return (-1);
+ outb(baseport+fddata,x);
+ TRACE1("[0x%x->fddata]",x);
+ return (0);
+}
+
+static fdopenf;
+/****************************************************************************/
+/* fdopen/fdclose */
+/****************************************************************************/
+Fdopen(dev, flags)
+ dev_t dev;
+ int flags;
+{
+ fdu_t fdu = FDUNIT(minor(dev));
+ /*int type = FDTYPE(minor(dev));*/
+ int s;
+
+ /* check bounds */
+ if (fdu >= NFD) return(ENXIO);
+ /*if (type >= NUMTYPES) return(ENXIO);*/
+ fd_data[fdu].flags |= FD_OPEN;
+
+ return 0;
+}
+
+fdclose(dev, flags)
+ dev_t dev;
+{
+ fdu_t fdu = FDUNIT(minor(dev));
+ fd_data[fdu].flags &= ~FD_OPEN;
+ return(0);
+}
+
+
+/***************************************************************\
+* fdstart *
+* We have just queued something.. if the controller is not busy *
+* then simulate the case where it has just finished a command *
+* So that it (the interrupt routine) looks on the queue for more*
+* work to do and picks up what we just added. *
+* If the controller is already busy, we need do nothing, as it *
+* will pick up our work when the present work completes *
+\***************************************************************/
+fdstart(fdcu_t fdcu)
+{
+ register struct buf *dp,*bp;
+ int s;
+ fdu_t fdu;
+
+ s = splbio();
+ if(fdc_data[fdcu].state == DEVIDLE)
+ {
+ fdintr(fdcu);
+ }
+ splx(s);
+}
+
+fd_timeout(fdcu_t fdcu)
+{
+ fdu_t fdu = fdc_data[fdcu].fdu;
+ int st0, st3, cyl;
+ struct buf *dp,*bp;
+
+ dp = &fdc_data[fdcu].head;
+ bp = dp->b_actf;
+
+ out_fdc(fdcu,NE7CMD_SENSED);
+ out_fdc(fdcu,fd_data[fdu].hddrv);
+ st3 = in_fdc(fdcu);
+
+ out_fdc(fdcu,NE7CMD_SENSEI);
+ st0 = in_fdc(fdcu);
+ cyl = in_fdc(fdcu);
+ printf("fd%d: Operation timeout ST0 %b cyl %d ST3 %b\n",
+ fdu,
+ st0,
+ NE7_ST0BITS,
+ cyl,
+ st3,
+ NE7_ST3BITS);
+
+ if (bp)
+ {
+ retrier(fdcu);
+ fdc_data[fdcu].status[0] = 0xc0;
+ fdc_data[fdcu].state = IOTIMEDOUT;
+ if( fdc_data[fdcu].retry < 6)
+ fdc_data[fdcu].retry = 6;
+ }
+ else
+ {
+ fdc_data[fdcu].fd = (fd_p) 0;
+ fdc_data[fdcu].fdu = -1;
+ fdc_data[fdcu].state = DEVIDLE;
+ }
+ fd_pseudointr(fdcu);
+}
+
+/* just ensure it has the right spl */
+fd_pseudointr(fdcu_t fdcu)
+{
+ int s;
+ s = splbio();
+ fdintr(fdcu);
+ splx(s);
+}
+
+/***********************************************************************\
+* fdintr *
+* keep calling the state machine until it returns a 0 *
+* ALWAYS called at SPLBIO *
+\***********************************************************************/
+fdintr(fdcu_t fdcu)
+{
+ fdc_p fdc = fdc_data + fdcu;
+ while(fdstate(fdcu, fdc));
+}
+
+/***********************************************************************\
+* The controller state machine. *
+* if it returns a non zero value, it should be called again immediatly *
+\***********************************************************************/
+int fdstate(fdcu_t fdcu, fdc_p fdc)
+{
+ int read,head,trac,sec,i,s,sectrac,cyl,st0;
+ unsigned long blknum;
+ fdu_t fdu = fdc->fdu;
+ fd_p fd;
+ register struct buf *dp,*bp;
+
+ dp = &(fdc->head);
+ bp = dp->b_actf;
+ if(!bp)
+ {
+ /***********************************************\
+ * nothing left for this controller to do *
+ * Force into the IDLE state, *
+ \***********************************************/
+ fdc->state = DEVIDLE;
+ if(fdc->fd)
+ {
+ printf("unexpected valid fd pointer (fdu = %d)\n"
+ ,fdc->fdu);
+ fdc->fd = (fd_p) 0;
+ fdc->fdu = -1;
+ }
+ TRACE1("[fdc%d IDLE]",fdcu);
+ return(0);
+ }
+ fdu = FDUNIT(minor(bp->b_dev));
+ fd = fd_data + fdu;
+ if (fdc->fd && (fd != fdc->fd))
+ {
+ printf("confused fd pointers\n");
+ }
+ read = bp->b_flags & B_READ;
+ TRACE1("fd%d",fdu);
+ TRACE1("[%s]",fdstates[fdc->state]);
+ TRACE1("(0x%x)",fd->flags);
+ untimeout(fd_turnoff, fdu);
+ timeout(fd_turnoff,fdu,4 * hz);
+ switch (fdc->state)
+ {
+ case DEVIDLE:
+ case FINDWORK: /* we have found new work */
+ fdc->retry = 0;
+ fd->skip = 0;
+ fdc->fd = fd;
+ fdc->fdu = fdu;
+ /*******************************************************\
+ * If the next drive has a motor startup pending, then *
+ * it will start up in it's own good time *
+ \*******************************************************/
+ if(fd->flags & FD_MOTOR_WAIT)
+ {
+ fdc->state = MOTORWAIT;
+ return(0); /* come back later */
+ }
+ /*******************************************************\
+ * Maybe if it's not starting, it SHOULD be starting *
+ \*******************************************************/
+ if (!(fd->flags & FD_MOTOR))
+ {
+ fdc->state = MOTORWAIT;
+ fd_turnon(fdu);
+ return(0);
+ }
+ else /* at least make sure we are selected */
+ {
+ set_motor(fdcu,fd->fdsu,0);
+ }
+ fdc->state = DOSEEK;
+ break;
+ case DOSEEK:
+ if (bp->b_cylin == fd->track)
+ {
+ fdc->state = SEEKCOMPLETE;
+ break;
+ }
+ out_fdc(fdcu,NE7CMD_SEEK); /* Seek function */
+ out_fdc(fdcu,fd->fdsu); /* Drive number */
+ out_fdc(fdcu,bp->b_cylin * fd->ft->steptrac);
+ fd->track = -2;
+ fdc->state = SEEKWAIT;
+ return(0); /* will return later */
+ case SEEKWAIT:
+ /* allow heads to settle */
+ timeout(fd_pseudointr,fdcu,hz/50);
+ fdc->state = SEEKCOMPLETE;
+ return(0); /* will return later */
+ break;
+
+ case SEEKCOMPLETE : /* SEEK DONE, START DMA */
+ /* Make sure seek really happened*/
+ if(fd->track == -2)
+ {
+ int descyl = bp->b_cylin * fd->ft->steptrac;
+ out_fdc(fdcu,NE7CMD_SENSEI);
+ i = in_fdc(fdcu);
+ cyl = in_fdc(fdcu);
+ if (cyl != descyl)
+ {
+ printf("fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n", fdu,
+ descyl, cyl, i, NE7_ST0BITS);
+ return(retrier(fdcu));
+ }
+ }
+
+ fd->track = bp->b_cylin;
+ isa_dmastart(bp->b_flags, bp->b_un.b_addr+fd->skip,
+ FDBLK, fdc->dmachan);
+ blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/FDBLK
+ + fd->skip/FDBLK;
+ sectrac = fd->ft->sectrac;
+ sec = blknum % (sectrac * fd->ft->heads);
+ head = sec / sectrac;
+ sec = sec % sectrac + 1;
+/*XXX*/ fd->hddrv = ((head&1)<<2)+fdu;
+
+ if (read)
+ {
+ out_fdc(fdcu,NE7CMD_READ); /* READ */
+ }
+ else
+ {
+ out_fdc(fdcu,NE7CMD_WRITE); /* WRITE */
+ }
+ out_fdc(fdcu,head << 2 | fdu); /* head & unit */
+ out_fdc(fdcu,fd->track); /* track */
+ out_fdc(fdcu,head);
+ out_fdc(fdcu,sec); /* sector XXX +1? */
+ out_fdc(fdcu,fd->ft->secsize); /* sector size */
+ out_fdc(fdcu,sectrac); /* sectors/track */
+ out_fdc(fdcu,fd->ft->gap); /* gap size */
+ out_fdc(fdcu,fd->ft->datalen); /* data length */
+ fdc->state = IOCOMPLETE;
+ timeout(fd_timeout,fdcu,2 * hz);
+ return(0); /* will return later */
+ case IOCOMPLETE: /* IO DONE, post-analyze */
+ untimeout(fd_timeout,fdcu);
+ for(i=0;i<7;i++)
+ {
+ fdc->status[i] = in_fdc(fdcu);
+ }
+ case IOTIMEDOUT: /*XXX*/
+ isa_dmadone(bp->b_flags, bp->b_un.b_addr+fd->skip,
+ FDBLK, fdc->dmachan);
+ if (fdc->status[0]&0xF8)
+ {
+ return(retrier(fdcu));
+ }
+ /* All OK */
+ fd->skip += FDBLK;
+ if (fd->skip < bp->b_bcount)
+ {
+ /* set up next transfer */
+ blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/FDBLK
+ + fd->skip/FDBLK;
+ bp->b_cylin = (blknum / (fd->ft->sectrac * fd->ft->heads));
+ fdc->state = DOSEEK;
+ }
+ else
+ {
+ /* ALL DONE */
+ fd->skip = 0;
+ bp->b_resid = 0;
+ dp->b_actf = bp->av_forw;
+ biodone(bp);
+ fdc->fd = (fd_p) 0;
+ fdc->fdu = -1;
+ fdc->state = FINDWORK;
+ }
+ return(1);
+ case RESETCTLR:
+ /* Try a reset, keep motor on */
+ set_motor(fdcu,fd->fdsu,1);
+ DELAY(100);
+ set_motor(fdcu,fd->fdsu,0);
+ outb(fdc->baseport+fdctl,fd->ft->trans);
+ TRACE1("[0x%x->fdctl]",fd->ft->trans);
+ fdc->retry++;
+ fdc->state = STARTRECAL;
+ break;
+ case STARTRECAL:
+ out_fdc(fdcu,NE7CMD_SPECIFY); /* specify command */
+ out_fdc(fdcu,0xDF);
+ out_fdc(fdcu,2);
+ out_fdc(fdcu,NE7CMD_RECAL); /* Recalibrate Function */
+ out_fdc(fdcu,fdu);
+ fdc->state = RECALWAIT;
+ return(0); /* will return later */
+ case RECALWAIT:
+ /* allow heads to settle */
+ timeout(fd_pseudointr,fdcu,hz/30);
+ fdc->state = RECALCOMPLETE;
+ return(0); /* will return later */
+ case RECALCOMPLETE:
+ out_fdc(fdcu,NE7CMD_SENSEI);
+ st0 = in_fdc(fdcu);
+ cyl = in_fdc(fdcu);
+ if (cyl != 0)
+ {
+ printf("fd%d: recal failed ST0 %b cyl %d\n", fdu,
+ st0, NE7_ST0BITS, cyl);
+ return(retrier(fdcu));
+ }
+ fd->track = 0;
+ /* Seek (probably) necessary */
+ fdc->state = DOSEEK;
+ return(1); /* will return immediatly */
+ case MOTORWAIT:
+ if(fd->flags & FD_MOTOR_WAIT)
+ {
+ return(0); /* time's not up yet */
+ }
+ fdc->state = DOSEEK;
+ return(1); /* will return immediatly */
+ default:
+ printf("Unexpected FD int->");
+ out_fdc(fdcu,NE7CMD_SENSEI);
+ st0 = in_fdc(fdcu);
+ cyl = in_fdc(fdcu);
+ printf("ST0 = %lx, PCN = %lx\n",i,sec);
+ out_fdc(fdcu,0x4A);
+ out_fdc(fdcu,fd->fdsu);
+ for(i=0;i<7;i++) {
+ fdc->status[i] = in_fdc(fdcu);
+ }
+ printf("intr status :%lx %lx %lx %lx %lx %lx %lx ",
+ fdc->status[0],
+ fdc->status[1],
+ fdc->status[2],
+ fdc->status[3],
+ fdc->status[4],
+ fdc->status[5],
+ fdc->status[6] );
+ return(0);
+ }
+ return(1); /* Come back immediatly to new state */
+}
+
+retrier(fdcu_t fdcu)
+{
+ fdc_p fdc = fdc_data + fdcu;
+ register struct buf *dp,*bp;
+
+ dp = &(fdc->head);
+ bp = dp->b_actf;
+
+ switch(fdc->retry)
+ {
+ case 0: case 1: case 2:
+ fdc->state = SEEKCOMPLETE;
+ break;
+ case 3: case 4: case 5:
+ fdc->state = STARTRECAL;
+ break;
+ case 6:
+ fdc->state = RESETCTLR;
+ break;
+ case 7:
+ break;
+ default:
+ {
+ printf("fd%d: hard error (ST0 %b ",
+ fdc->fdu, fdc->status[0], NE7_ST0BITS);
+ printf(" ST1 %b ", fdc->status[1], NE7_ST1BITS);
+ printf(" ST2 %b ", fdc->status[2], NE7_ST2BITS);
+ printf(" ST3 %b ", fdc->status[3], NE7_ST3BITS);
+ printf("cyl %d hd %d sec %d)\n",
+ fdc->status[4], fdc->status[5], fdc->status[6]);
+ }
+ bp->b_flags |= B_ERROR;
+ bp->b_error = EIO;
+ bp->b_resid = bp->b_bcount - fdc->fd->skip;
+ dp->b_actf = bp->av_forw;
+ fdc->fd->skip = 0;
+ biodone(bp);
+ fdc->state = FINDWORK;
+ fdc->fd = (fd_p) 0;
+ fdc->fdu = -1;
+ return(1);
+ }
+ fdc->retry++;
+ return(1);
+}
+
+#endif
+
diff --git a/sys/i386/isa/fdreg.h b/sys/i386/isa/fdreg.h
new file mode 100644
index 0000000..948f566
--- /dev/null
+++ b/sys/i386/isa/fdreg.h
@@ -0,0 +1,72 @@
+/*-
+ * 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.
+ *
+ * @(#)fdreg.h 7.1 (Berkeley) 5/9/91
+ *
+ * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE
+ * -------------------- ----- ----------------------
+ * CURRENT PATCH LEVEL: 1 00153
+ * -------------------- ----- ----------------------
+ *
+ * 20 Apr 93 Julian Elischer Heavily re worked, see notes below
+ */
+
+/*
+ * AT floppy controller registers and bitfields
+ */
+
+/* uses NEC765 controller */
+#include "../i386/isa/ic/nec765.h"
+
+/* registers */
+#define fdout 2 /* Digital Output Register (W) */
+#define FDO_FDSEL 0x03 /* floppy device select */
+#define FDO_FRST 0x04 /* floppy controller reset */
+#define FDO_FDMAEN 0x08 /* enable floppy DMA and Interrupt */
+#define FDO_MOEN0 0x10 /* motor enable drive 0 */
+#define FDO_MOEN1 0x20 /* motor enable drive 1 */
+#define FDO_MOEN2 0x30 /* motor enable drive 2 */
+#define FDO_MOEN3 0x40 /* motor enable drive 3 */
+
+#define fdsts 4 /* NEC 765 Main Status Register (R) */
+#define fddata 5 /* NEC 765 Data Register (R/W) */
+
+#define fdctl 7 /* Control Register (W) */
+#define FDC_500KBPS 0x00 /* 500KBPS MFM drive transfer rate */
+#define FDC_300KBPS 0x01 /* 300KBPS MFM drive transfer rate */
+#define FDC_250KBPS 0x02 /* 250KBPS MFM drive transfer rate */
+#define FDC_125KBPS 0x03 /* 125KBPS FM drive transfer rate */
+
+#define fdin 7 /* Digital Input Register (R) */
+#define FDI_DCHG 0x80 /* diskette has been changed */
+
+
diff --git a/sys/i386/isa/ic/i8042.h b/sys/i386/isa/ic/i8042.h
new file mode 100644
index 0000000..4d04ce5
--- /dev/null
+++ b/sys/i386/isa/ic/i8042.h
@@ -0,0 +1,23 @@
+#define KBSTATP 0x64 /* kbd controller status port (I) */
+#define KBS_DIB 0x01 /* kbd data in buffer */
+#define KBS_IBF 0x02 /* kbd input buffer low */
+#define KBS_WARM 0x04 /* kbd input buffer low */
+#define KBS_OCMD 0x08 /* kbd output buffer has command */
+#define KBS_NOSEC 0x10 /* kbd security lock not engaged */
+#define KBS_TERR 0x20 /* kbd transmission error */
+#define KBS_RERR 0x40 /* kbd receive error */
+#define KBS_PERR 0x80 /* kbd parity error */
+
+#define KBCMDP 0x64 /* kbd controller port (O) */
+#define KBDATAP 0x60 /* kbd data port (I) */
+#define KBOUTP 0x60 /* kbd data port (O) */
+
+#define K_LDCMDBYTE 0x60
+
+#define KC8_TRANS 0x40 /* convert to old scan codes */
+#define KC8_OLDPC 0x20 /* old 9bit codes instead of new 11bit */
+#define KC8_DISABLE 0x10 /* disable keyboard */
+#define KC8_IGNSEC 0x08 /* ignore security lock */
+#define KC8_CPU 0x04 /* exit from protected mode reset */
+#define KC8_IEN 0x01 /* enable interrupt */
+#define CMDBYTE (KC8_TRANS|KC8_IGNSEC|KC8_CPU|KC8_IEN)
diff --git a/sys/i386/isa/ic/i8237.h b/sys/i386/isa/ic/i8237.h
new file mode 100644
index 0000000..dc269f2
--- /dev/null
+++ b/sys/i386/isa/ic/i8237.h
@@ -0,0 +1,9 @@
+/*
+ * Intel 8237 DMA Controller
+ */
+
+#define DMA37MD_SINGLE 0x40 /* single pass mode */
+#define DMA37MD_CASCADE 0xc0 /* cascade mode */
+#define DMA37MD_WRITE 0x04 /* read the device, write memory operation */
+#define DMA37MD_READ 0x08 /* write the device, read memory operation */
+
diff --git a/sys/i386/isa/ic/nec765.h b/sys/i386/isa/ic/nec765.h
new file mode 100644
index 0000000..b84b46e
--- /dev/null
+++ b/sys/i386/isa/ic/nec765.h
@@ -0,0 +1,71 @@
+/*-
+ * 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.
+ *
+ * @(#)nec765.h 7.1 (Berkeley) 5/9/91
+ */
+
+/*
+ * Nec 765 floppy disc controller definitions
+ */
+
+/* Main status register */
+#define NE7_DAB 0x01 /* Diskette drive A is seeking, thus busy */
+#define NE7_DBB 0x02 /* Diskette drive B is seeking, thus busy */
+#define NE7_CB 0x10 /* Diskette Controller Busy */
+#define NE7_NDM 0x20 /* Diskette Controller in Non Dma Mode */
+#define NE7_DIO 0x40 /* Diskette Controller Data register I/O */
+#define NE7_RQM 0x80 /* Diskette Controller ReQuest for Master */
+
+/* Status register ST0 */
+#define NE7_ST0BITS "\020\010invld\007abnrml\006seek_cmplt\005drv_chck\004drive_rdy\003top_head"
+
+/* Status register ST1 */
+#define NE7_ST1BITS "\020\010end_of_cyl\006bad_crc\005data_overrun\003sec_not_fnd\002write_protect\001no_am"
+
+/* Status register ST2 */
+#define NE7_ST2BITS "\020\007ctrl_mrk\006bad_crc\005wrong_cyl\004scn_eq\003scn_not_fnd\002bad_cyl\001no_dam"
+
+/* Status register ST3 */
+#define NE7_ST3BITS "\020\010fault\007write_protect\006drdy\005tk0\004two_side\003side_sel\002"
+
+/* Commands */
+#define NE7CMD_SPECIFY 3 /* specify drive parameters - requires unit
+ parameters byte */
+#define NE7CMD_SENSED 4 /* sense drive - requires unit select byte */
+#define NE7CMD_WRITE 0xc5 /* write - requires eight additional bytes */
+#define NE7CMD_READ 0xe6 /* read - requires eight additional bytes */
+#define NE7CMD_FORMAT 0x4c /* format - requires five additional bytes */
+#define NE7CMD_RECAL 7 /* recalibrate drive - requires
+ unit select byte */
+#define NE7CMD_SENSEI 8 /* sense controller interrupt status */
+#define NE7CMD_SEEK 15 /* seek drive - requires unit select byte
+ and new cyl byte */
diff --git a/sys/i386/isa/ic/ns16450.h b/sys/i386/isa/ic/ns16450.h
new file mode 100644
index 0000000..f059035
--- /dev/null
+++ b/sys/i386/isa/ic/ns16450.h
@@ -0,0 +1,49 @@
+/*-
+ * 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.
+ *
+ * @(#)ns16450.h 7.1 (Berkeley) 5/9/91
+ */
+
+/*
+ * NS16450 UART registers
+ */
+
+#define com_data 0 /* data register (R/W) */
+#define com_dlbl 0 /* divisor latch low (W) */
+#define com_dlbh 1 /* divisor latch high (W) */
+#define com_ier 1 /* interrupt enable (W) */
+#define com_iir 2 /* interrupt identification (R) */
+#define com_lctl 3 /* line control register (R/W) */
+#define com_cfcr 3 /* line control register (R/W) */
+#define com_mcr 4 /* modem control register (R/W) */
+#define com_lsr 5 /* line status register (R/W) */
+#define com_msr 6 /* modem status register (R/W) */
diff --git a/sys/i386/isa/ic/ns16550.h b/sys/i386/isa/ic/ns16550.h
new file mode 100644
index 0000000..e20e9aa
--- /dev/null
+++ b/sys/i386/isa/ic/ns16550.h
@@ -0,0 +1,50 @@
+/*-
+ * 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.
+ *
+ * @(#)ns16550.h 7.1 (Berkeley) 5/9/91
+ */
+
+/*
+ * NS16550 UART registers
+ */
+
+#define com_data 0 /* data register (R/W) */
+#define com_dlbl 0 /* divisor latch low (W) */
+#define com_dlbh 1 /* divisor latch high (W) */
+#define com_ier 1 /* interrupt enable (W) */
+#define com_iir 2 /* interrupt identification (R) */
+#define com_fifo 2 /* FIFO control (W) */
+#define com_lctl 3 /* line control register (R/W) */
+#define com_cfcr 3 /* line control register (R/W) */
+#define com_mcr 4 /* modem control register (R/W) */
+#define com_lsr 5 /* line status register (R/W) */
+#define com_msr 6 /* modem status register (R/W) */
diff --git a/sys/i386/isa/icu.h b/sys/i386/isa/icu.h
new file mode 100644
index 0000000..4866d8d
--- /dev/null
+++ b/sys/i386/isa/icu.h
@@ -0,0 +1,109 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * 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.
+ *
+ * @(#)icu.h 5.6 (Berkeley) 5/9/91
+ *
+ * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE
+ * -------------------- ----- ----------------------
+ * CURRENT PATCH LEVEL: 1 00158
+ * -------------------- ----- ----------------------
+ *
+ * 25 Apr 93 Bruce Evans New fast interrupt code (intr-0.1)
+ */
+
+/*
+ * AT/386 Interrupt Control constants
+ * W. Jolitz 8/89
+ */
+
+#ifndef __ICU__
+#define __ICU__
+
+#ifndef LOCORE
+
+/*
+ * Interrupt "level" mechanism variables, masks, and macros
+ */
+extern unsigned imen; /* interrupt mask enable */
+extern unsigned cpl; /* current priority level mask */
+
+extern unsigned highmask; /* group of interrupts masked with splhigh() */
+extern unsigned ttymask; /* group of interrupts masked with spltty() */
+extern unsigned biomask; /* group of interrupts masked with splbio() */
+extern unsigned netmask; /* group of interrupts masked with splimp() */
+
+#define INTREN(s) (imen &= ~(s), SET_ICUS())
+#define INTRDIS(s) (imen |= (s), SET_ICUS())
+#define INTRMASK(msk,s) (msk |= (s))
+#if 0
+#define SET_ICUS() (outb(IO_ICU1 + 1, imen), outb(IU_ICU2 + 1, imen >> 8))
+#else
+/*
+ * XXX - IO_ICU* are defined in isa.h, not icu.h, and nothing much bothers to
+ * include isa.h, while too many things include icu.h.
+ */
+#define SET_ICUS() (outb(0x21, imen), outb(0xa1, imen >> 8))
+#endif
+
+#endif
+
+/*
+ * Interrupt enable bits -- in order of priority
+ */
+#define IRQ0 0x0001 /* highest priority - timer */
+#define IRQ1 0x0002
+#define IRQ_SLAVE 0x0004
+#define IRQ8 0x0100
+#define IRQ9 0x0200
+#define IRQ2 IRQ9
+#define IRQ10 0x0400
+#define IRQ11 0x0800
+#define IRQ12 0x1000
+#define IRQ13 0x2000
+#define IRQ14 0x4000
+#define IRQ15 0x8000
+#define IRQ3 0x0008
+#define IRQ4 0x0010
+#define IRQ5 0x0020
+#define IRQ6 0x0040
+#define IRQ7 0x0080 /* lowest - parallel printer */
+
+/*
+ * Interrupt Control offset into Interrupt descriptor table (IDT)
+ */
+#define ICU_OFFSET 32 /* 0-31 are processor exceptions */
+#define ICU_LEN 16 /* 32-47 are ISA interrupts */
+
+#endif __ICU__
diff --git a/sys/i386/isa/icu.s b/sys/i386/isa/icu.s
new file mode 100644
index 0000000..1b93c65
--- /dev/null
+++ b/sys/i386/isa/icu.s
@@ -0,0 +1,376 @@
+/*-
+ * Copyright (c) 1989, 1990 William F. Jolitz.
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * 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.
+ *
+ * @(#)icu.s 7.2 (Berkeley) 5/21/91
+ *
+ * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE
+ * -------------------- ----- ----------------------
+ * CURRENT PATCH LEVEL: 5 00167
+ * -------------------- ----- ----------------------
+ *
+ * 28 Nov 92 Frank MacLachlan Aligned addresses and data
+ * on 32bit boundaries.
+ * 24 Mar 93 Rodney W. Grimes Added interrupt counters for vmstat
+ * also stray and false intr counters added
+ * 20 Apr 93 Bruce Evans New npx-0.5 code
+ * 25 Apr 93 Bruce Evans Support new interrupt code (intr-0.1)
+ * Rodney W. Grimes Reimplement above patches..
+ * 17 May 93 Rodney W. Grimes Redid the interrupt counter stuff
+ * moved the counters to vectors.s so
+ * they are next to the name tables.
+ * 04 Jun 93 Bruce Evans Fixed irq_num vs id_num for multiple
+ * devices configed on the same irq with
+ * respect to ipending. Restructured
+ * not to use BUILD_VECTORS.
+ * Rodney W. Grimes softsio1 only works if you have sio
+ * serial driver, added #include sio.h
+ * and #ifdef NSIO > 0 to protect it.
+ */
+
+/*
+ * AT/386
+ * Vector interrupt control section
+ */
+
+/*
+ * XXX - this file is now misnamed. All spls are now soft and the only thing
+ * related to the hardware icu is that the bit numbering is the same in the
+ * soft priority masks as in the hard ones.
+ */
+
+#include "sio.h"
+#define HIGHMASK 0xffff
+#define SOFTCLOCKMASK 0x8000
+
+ .data
+ .globl _cpl
+_cpl: .long 0xffff # current priority (all off)
+ .globl _imen
+_imen: .long 0xffff # interrupt mask enable (all off)
+# .globl _highmask
+_highmask: .long HIGHMASK
+ .globl _ttymask
+_ttymask: .long 0
+ .globl _biomask
+_biomask: .long 0
+ .globl _netmask
+_netmask: .long 0
+ .globl _ipending
+_ipending: .long 0
+vec:
+ .long vec0, vec1, vec2, vec3, vec4, vec5, vec6, vec7
+ .long vec8, vec9, vec10, vec11, vec12, vec13, vec14, vec15
+
+#define GENSPL(name, mask, event) \
+ .globl _spl/**/name ; \
+ ALIGN_TEXT ; \
+_spl/**/name: ; \
+ COUNT_EVENT(_intrcnt_spl, event) ; \
+ movl _cpl,%eax ; \
+ movl %eax,%edx ; \
+ orl mask,%edx ; \
+ movl %edx,_cpl ; \
+ SHOW_CPL ; \
+ ret
+
+#define FASTSPL(mask) \
+ movl mask,_cpl ; \
+ SHOW_CPL
+
+#define FASTSPL_VARMASK(varmask) \
+ movl varmask,%eax ; \
+ movl %eax,_cpl ; \
+ SHOW_CPL
+
+ .text
+
+ ALIGN_TEXT
+unpend_v:
+ COUNT_EVENT(_intrcnt_spl, 0)
+ bsfl %eax,%eax # slow, but not worth optimizing
+ btrl %eax,_ipending
+ jnc unpend_v_next # some intr cleared the in-memory bit
+ SHOW_IPENDING
+ movl Vresume(,%eax,4),%eax
+ testl %eax,%eax
+ je noresume
+ jmp %eax
+
+ ALIGN_TEXT
+/*
+ * XXX - must be some fastintr, need to register those too.
+ */
+noresume:
+#if NSIO > 0
+ call _softsio1
+#endif
+unpend_v_next:
+ movl _cpl,%eax
+ movl %eax,%edx
+ notl %eax
+ andl _ipending,%eax
+ je none_to_unpend
+ jmp unpend_v
+
+/*
+ * Handle return from interrupt after device handler finishes
+ */
+ ALIGN_TEXT
+doreti:
+ COUNT_EVENT(_intrcnt_spl, 1)
+ addl $4,%esp # discard unit arg
+ popl %eax # get previous priority
+/*
+ * Now interrupt frame is a trap frame!
+ *
+ * XXX - setting up the interrupt frame to be almost a stack frame is mostly
+ * a waste of time.
+ */
+ movl %eax,_cpl
+ SHOW_CPL
+ movl %eax,%edx
+ notl %eax
+ andl _ipending,%eax
+ jne unpend_v
+none_to_unpend:
+ testl %edx,%edx # returning to zero priority?
+ jne 1f # nope, going to non-zero priority
+ movl _netisr,%eax
+ testl %eax,%eax # check for softint s/traps
+ jne 2f # there are some
+ jmp test_resched # XXX - schedule jumps better
+ COUNT_EVENT(_intrcnt_spl, 2) # XXX
+
+ ALIGN_TEXT # XXX
+1: # XXX
+ COUNT_EVENT(_intrcnt_spl, 3)
+ popl %es
+ popl %ds
+ popal
+ addl $8,%esp
+ iret
+
+#include "../net/netisr.h"
+
+#define DONET(s, c, event) ; \
+ .globl c ; \
+ btrl $s,_netisr ; \
+ jnc 1f ; \
+ COUNT_EVENT(_intrcnt_spl, event) ; \
+ call c ; \
+1:
+
+ ALIGN_TEXT
+2:
+ COUNT_EVENT(_intrcnt_spl, 4)
+/*
+ * XXX - might need extra locking while testing reg copy of netisr, but
+ * interrupt routines setting it would not cause any new problems (since we
+ * don't loop, fresh bits will not be processed until the next doreti or spl0).
+ */
+ testl $~((1 << NETISR_SCLK) | (1 << NETISR_AST)),%eax
+ je test_ASTs # no net stuff, just temporary AST's
+ FASTSPL_VARMASK(_netmask)
+ DONET(NETISR_RAW, _rawintr, 5)
+#ifdef INET
+ DONET(NETISR_IP, _ipintr, 6)
+#endif
+#ifdef IMP
+ DONET(NETISR_IMP, _impintr, 7)
+#endif
+#ifdef NS
+ DONET(NETISR_NS, _nsintr, 8)
+#endif
+ FASTSPL($0)
+test_ASTs:
+ btrl $NETISR_SCLK,_netisr
+ jnc test_resched
+ COUNT_EVENT(_intrcnt_spl, 9)
+ FASTSPL($SOFTCLOCKMASK)
+/*
+ * Back to an interrupt frame for a moment.
+ */
+ pushl $0 # previous cpl (probably not used)
+ pushl $0x7f # dummy unit number
+ call _softclock
+ addl $8,%esp # discard dummies
+ FASTSPL($0)
+test_resched:
+#ifdef notused1
+ btrl $NETISR_AST,_netisr
+ jnc 2f
+#endif
+#ifdef notused2
+ cmpl $0,_want_resched
+ je 2f
+#endif
+ cmpl $0,_astpending # XXX - put it back in netisr to
+ je 2f # reduce the number of tests
+ testb $SEL_RPL_MASK,TRAPF_CS_OFF(%esp)
+ # to non-kernel (i.e., user)?
+ je 2f # nope, leave
+ COUNT_EVENT(_intrcnt_spl, 10)
+ movl $0,_astpending
+ call _trap
+2:
+ COUNT_EVENT(_intrcnt_spl, 11)
+ popl %es
+ popl %ds
+ popal
+ addl $8,%esp
+ iret
+
+/*
+ * Interrupt priority mechanism
+ * -- soft splXX masks with group mechanism (cpl)
+ * -- h/w masks for currently active or unused interrupts (imen)
+ * -- ipending = active interrupts currently masked by cpl
+ */
+
+ GENSPL(bio, _biomask, 12)
+ GENSPL(clock, $HIGHMASK, 13) /* splclock == splhigh ex for count */
+ GENSPL(high, $HIGHMASK, 14)
+ GENSPL(imp, _netmask, 15) /* splimp == splnet except for count */
+ GENSPL(net, _netmask, 16)
+ GENSPL(softclock, $SOFTCLOCKMASK, 17)
+ GENSPL(tty, _ttymask, 18)
+
+ .globl _splnone
+ .globl _spl0
+ ALIGN_TEXT
+_splnone:
+_spl0:
+ COUNT_EVENT(_intrcnt_spl, 19)
+in_spl0:
+ movl _cpl,%eax
+ pushl %eax # save old priority
+ testl $(1 << NETISR_RAW) | (1 << NETISR_IP),_netisr
+ je over_net_stuff_for_spl0
+ movl _netmask,%eax # mask off those network devices
+ movl %eax,_cpl # set new priority
+ SHOW_CPL
+/*
+ * XXX - what about other net intrs?
+ */
+ DONET(NETISR_RAW, _rawintr, 20)
+#ifdef INET
+ DONET(NETISR_IP, _ipintr, 21)
+#endif
+over_net_stuff_for_spl0:
+ movl $0,_cpl # set new priority
+ SHOW_CPL
+ movl _ipending,%eax
+ testl %eax,%eax
+ jne unpend_V
+ popl %eax # return old priority
+ ret
+
+ .globl _splx
+ ALIGN_TEXT
+_splx:
+ COUNT_EVENT(_intrcnt_spl, 22)
+ movl 4(%esp),%eax # new priority
+ testl %eax,%eax
+ je in_spl0 # going to "zero level" is special
+ COUNT_EVENT(_intrcnt_spl, 23)
+ movl _cpl,%edx # save old priority
+ movl %eax,_cpl # set new priority
+ SHOW_CPL
+ notl %eax
+ andl _ipending,%eax
+ jne unpend_V_result_edx
+ movl %edx,%eax # return old priority
+ ret
+
+ ALIGN_TEXT
+unpend_V_result_edx:
+ pushl %edx
+unpend_V:
+ COUNT_EVENT(_intrcnt_spl, 24)
+ bsfl %eax,%eax
+ btrl %eax,_ipending
+ jnc unpend_V_next
+ SHOW_IPENDING
+ movl Vresume(,%eax,4),%edx
+ testl %edx,%edx
+ je noresumeV
+/*
+ * We would prefer to call the intr handler directly here but that doesn't
+ * work for badly behaved handlers that want the interrupt frame. Also,
+ * there's a problem determining the unit number. We should change the
+ * interface so that the unit number is not determined at config time.
+ */
+ jmp *vec(,%eax,4)
+
+ ALIGN_TEXT
+/*
+ * XXX - must be some fastintr, need to register those too.
+ */
+noresumeV:
+#if NSIO > 0
+ call _softsio1
+#endif
+unpend_V_next:
+ movl _cpl,%eax
+ notl %eax
+ andl _ipending,%eax
+ jne unpend_V
+ popl %eax
+ ret
+
+#define BUILD_VEC(irq_num) \
+ ALIGN_TEXT ; \
+vec/**/irq_num: ; \
+ int $ICU_OFFSET + (irq_num) ; \
+ popl %eax ; \
+ ret
+
+ BUILD_VEC(0)
+ BUILD_VEC(1)
+ BUILD_VEC(2)
+ BUILD_VEC(3)
+ BUILD_VEC(4)
+ BUILD_VEC(5)
+ BUILD_VEC(6)
+ BUILD_VEC(7)
+ BUILD_VEC(8)
+ BUILD_VEC(9)
+ BUILD_VEC(10)
+ BUILD_VEC(11)
+ BUILD_VEC(12)
+ BUILD_VEC(13)
+ BUILD_VEC(14)
+ BUILD_VEC(15)
diff --git a/sys/i386/isa/isa.c b/sys/i386/isa/isa.c
new file mode 100644
index 0000000..8707b43
--- /dev/null
+++ b/sys/i386/isa/isa.c
@@ -0,0 +1,766 @@
+/*-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * 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.
+ *
+ * @(#)isa.c 7.2 (Berkeley) 5/13/91
+ *
+ * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE
+ * -------------------- ----- ----------------------
+ * CURRENT PATCH LEVEL: 4 00163
+ * -------------------- ----- ----------------------
+ *
+ * 18 Aug 92 Frank Maclachlan *See comments below
+ * 25 Mar 93 Rodney W. Grimes Added counter for stray interrupt,
+ * turned on logging of stray interrupts,
+ * Now prints maddr, msize, and flags
+ * after finding a device.
+ * 26 Apr 93 Bruce Evans New intr-0.1 code
+ * Rodney W. Grimes Only print io address if id_alive != -1
+ * 17 May 93 Rodney W. Grimes renamed stray interrupt counters to
+ * work with new intr-0.1 code.
+ * Enabled printf for interrupt masks to
+ * aid in bug reports.
+ * 27 May 93 Guido van Rooij New routine add find_isa_dev
+ */
+static char rcsid[] = "$Header: /usr/src/sys.386bsd/i386/isa/RCS/isa.c,v 1.2 92/01/21 14:34:23 william Exp Locker: root $";
+
+/*
+ * code to manage AT bus
+ *
+ * 92/08/18 Frank P. MacLachlan (fpm@crash.cts.com):
+ * Fixed uninitialized variable problem and added code to deal
+ * with DMA page boundaries in isa_dmarangecheck(). Fixed word
+ * mode DMA count compution and reorganized DMA setup code in
+ * isa_dmastart()
+ */
+
+#include "param.h"
+#include "systm.h"
+#include "conf.h"
+#include "file.h"
+#include "buf.h"
+#include "uio.h"
+#include "syslog.h"
+#include "malloc.h"
+#include "rlist.h"
+#include "machine/segments.h"
+#include "vm/vm.h"
+#include "i386/isa/isa_device.h"
+#include "i386/isa/isa.h"
+#include "i386/isa/icu.h"
+#include "i386/isa/ic/i8237.h"
+#include "i386/isa/ic/i8042.h"
+
+/*
+** Register definitions for DMA controller 1 (channels 0..3):
+*/
+#define DMA1_CHN(c) (IO_DMA1 + 1*(2*(c))) /* addr reg for channel c */
+#define DMA1_SMSK (IO_DMA1 + 1*10) /* single mask register */
+#define DMA1_MODE (IO_DMA1 + 1*11) /* mode register */
+#define DMA1_FFC (IO_DMA1 + 1*12) /* clear first/last FF */
+
+/*
+** Register definitions for DMA controller 2 (channels 4..7):
+*/
+#define DMA2_CHN(c) (IO_DMA1 + 2*(2*(c))) /* addr reg for channel c */
+#define DMA2_SMSK (IO_DMA2 + 2*10) /* single mask register */
+#define DMA2_MODE (IO_DMA2 + 2*11) /* mode register */
+#define DMA2_FFC (IO_DMA2 + 2*12) /* clear first/last FF */
+
+int config_isadev __P((struct isa_device *, u_int *));
+
+#ifdef notyet
+struct rlist *isa_iomem;
+
+/*
+ * Configure all ISA devices
+ */
+isa_configure() {
+ struct isa_device *dvp;
+ struct isa_driver *dp;
+
+ splhigh();
+ INTREN(IRQ_SLAVE);
+ /*rlist_free(&isa_iomem, 0xa0000, 0xfffff);*/
+ for (dvp = isa_devtab_tty; dvp; dvp++)
+ (void) config_isadev(dvp, &ttymask);
+ for (dvp = isa_devtab_bio; dvp; dvp++)
+ (void) config_isadev(dvp, &biomask);
+ for (dvp = isa_devtab_net; dvp; dvp++)
+ (void) config_isadev(dvp, &netmask);
+ for (dvp = isa_devtab_null; dvp; dvp++)
+ (void) config_isadev(dvp, (u_int *) NULL);
+#include "sl.h"
+#if NSL > 0
+ netmask |= ttymask;
+ ttymask |= netmask;
+#endif
+/* printf("biomask %x ttymask %x netmask %x\n", biomask, ttymask, netmask); */
+ splnone();
+}
+
+/*
+ * Configure an ISA device.
+ */
+config_isadev(isdp, mp)
+ struct isa_device *isdp;
+ u_int *mp;
+{
+ struct isa_driver *dp;
+ static short drqseen, irqseen;
+
+ if (dp = isdp->id_driver) {
+ /* if a device with i/o memory, convert to virtual address */
+ if (isdp->id_maddr) {
+ extern unsigned int atdevbase;
+
+ isdp->id_maddr -= IOM_BEGIN;
+ isdp->id_maddr += atdevbase;
+ }
+ isdp->id_alive = (*dp->probe)(isdp);
+ if (isdp->id_alive) {
+
+ printf("%s%d at port 0x%x ", dp->name,
+ isdp->id_unit, isdp->id_iobase);
+
+ /* check for conflicts */
+ if (irqseen & isdp->id_irq) {
+ printf("INTERRUPT CONFLICT - irq%d\n",
+ ffs(isdp->id_irq) - 1);
+ return (0);
+ }
+ if (isdp->id_drq != -1
+ && (drqseen & (1<<isdp->id_drq))) {
+ printf("DMA CONFLICT - drq%d\n", isdp->id_drq);
+ return (0);
+ }
+ /* NEED TO CHECK IOMEM CONFLICT HERE */
+
+ /* allocate and wire in device */
+ if(isdp->id_irq) {
+ int intrno;
+
+ intrno = ffs(isdp->id_irq)-1;
+ printf("irq %d ", intrno);
+ INTREN(isdp->id_irq);
+ if(mp)INTRMASK(*mp,isdp->id_irq);
+ setidt(NRSVIDT + intrno, isdp->id_intr,
+ SDT_SYS386IGT, SEL_KPL);
+ irqseen |= isdp->id_irq;
+ }
+ if (isdp->id_drq != -1) {
+ printf("drq %d ", isdp->id_drq);
+ drqseen |= 1 << isdp->id_drq;
+ }
+
+ (*dp->attach)(isdp);
+
+ printf("on isa\n");
+ }
+ return (1);
+ } else return(0);
+}
+#else /* notyet */
+/*
+ * Configure all ISA devices
+ */
+isa_configure() {
+ struct isa_device *dvp;
+ struct isa_driver *dp;
+
+ enable_intr();
+ splhigh();
+ INTREN(IRQ_SLAVE);
+ for (dvp = isa_devtab_tty; config_isadev(dvp,&ttymask); dvp++);
+ for (dvp = isa_devtab_bio; config_isadev(dvp,&biomask); dvp++);
+ for (dvp = isa_devtab_net; config_isadev(dvp,&netmask); dvp++);
+ for (dvp = isa_devtab_null; config_isadev(dvp,(u_int *) NULL); dvp++);
+#include "sl.h"
+#if NSL > 0
+ netmask |= ttymask;
+ ttymask |= netmask;
+#endif
+ /* biomask |= ttymask ; can some tty devices use buffers? */
+ printf("biomask %x ttymask %x netmask %x\n", biomask, ttymask, netmask);
+ splnone();
+}
+
+/*
+ * Configure an ISA device.
+ */
+config_isadev(isdp, mp)
+ struct isa_device *isdp;
+ u_int *mp;
+{
+ struct isa_driver *dp;
+
+ if (dp = isdp->id_driver) {
+ if (isdp->id_maddr) {
+ extern u_int atdevbase;
+
+ isdp->id_maddr -= 0xa0000; /* XXX should be a define */
+ isdp->id_maddr += atdevbase;
+ }
+ isdp->id_alive = (*dp->probe)(isdp);
+ if (isdp->id_alive) {
+ printf("%s%d", dp->name, isdp->id_unit);
+ /*
+ * The attach should really be after all the printf's
+ * but until all the drivers are fixed do it here.
+ * There is a comment below that shows where this
+ * really belongs. Rod Grimes 04/10/93
+ */
+ (*dp->attach)(isdp);
+ /*
+ * Only print the I/O address range if id_alive != -1
+ * Right now this is a temporary fix just for the new
+ * NPX code so that if it finds a 486 that can use trap
+ * 16 it will not report I/O addresses.
+ * Rod Grimes 04/26/94
+ */
+ if (isdp->id_alive != -1) {
+ printf(" at 0x%x", isdp->id_iobase);
+ if ((isdp->id_iobase + isdp->id_alive - 1) !=
+ isdp->id_iobase)
+ printf("-0x%x",
+ isdp->id_iobase +
+ isdp->id_alive - 1);
+ }
+ if(isdp->id_irq)
+ printf(" irq %d", ffs(isdp->id_irq)-1);
+ if (isdp->id_drq != -1)
+ printf(" drq %d", isdp->id_drq);
+ if (isdp->id_maddr != 0)
+ printf(" maddr 0x%x", kvtop(isdp->id_maddr));
+ if (isdp->id_msize != 0)
+ printf(" msize %d", isdp->id_msize);
+ if (isdp->id_flags != 0)
+ printf(" flags 0x%x", isdp->id_flags);
+ printf(" on isa\n");
+
+ /* This is the place the attach should be done! */
+ if(isdp->id_irq) {
+ int intrno;
+
+ intrno = ffs(isdp->id_irq)-1;
+ setidt(ICU_OFFSET+intrno, isdp->id_intr,
+ SDT_SYS386IGT, SEL_KPL);
+ if(mp)
+ INTRMASK(*mp,isdp->id_irq);
+ INTREN(isdp->id_irq);
+ }
+ }
+ return (1);
+ } else return(0);
+}
+#endif /* (!) notyet */
+
+#define IDTVEC(name) __CONCAT(X,name)
+/* default interrupt vector table entries */
+extern IDTVEC(intr0), IDTVEC(intr1), IDTVEC(intr2), IDTVEC(intr3),
+ IDTVEC(intr4), IDTVEC(intr5), IDTVEC(intr6), IDTVEC(intr7),
+ IDTVEC(intr8), IDTVEC(intr9), IDTVEC(intr10), IDTVEC(intr11),
+ IDTVEC(intr12), IDTVEC(intr13), IDTVEC(intr14), IDTVEC(intr15);
+
+static *defvec[16] = {
+ &IDTVEC(intr0), &IDTVEC(intr1), &IDTVEC(intr2), &IDTVEC(intr3),
+ &IDTVEC(intr4), &IDTVEC(intr5), &IDTVEC(intr6), &IDTVEC(intr7),
+ &IDTVEC(intr8), &IDTVEC(intr9), &IDTVEC(intr10), &IDTVEC(intr11),
+ &IDTVEC(intr12), &IDTVEC(intr13), &IDTVEC(intr14), &IDTVEC(intr15) };
+
+/* out of range default interrupt vector gate entry */
+extern IDTVEC(intrdefault);
+
+/*
+ * Fill in default interrupt table (in case of spuruious interrupt
+ * during configuration of kernel, setup interrupt control unit
+ */
+isa_defaultirq() {
+ int i;
+
+ /* icu vectors */
+ for (i = NRSVIDT ; i < NRSVIDT+ICU_LEN ; i++)
+ setidt(i, defvec[i], SDT_SYS386IGT, SEL_KPL);
+
+ /* out of range vectors */
+ for (i = NRSVIDT; i < NIDT; i++)
+ setidt(i, &IDTVEC(intrdefault), SDT_SYS386IGT, SEL_KPL);
+
+ /* initialize 8259's */
+ outb(IO_ICU1, 0x11); /* reset; program device, four bytes */
+ outb(IO_ICU1+1, NRSVIDT); /* starting at this vector index */
+ outb(IO_ICU1+1, 1<<2); /* slave on line 2 */
+#ifdef AUTO_EOI_1
+ outb(IO_ICU1+1, 2 | 1); /* auto EOI, 8086 mode */
+#else
+ outb(IO_ICU1+1, 1); /* 8086 mode */
+#endif
+ outb(IO_ICU1+1, 0xff); /* leave interrupts masked */
+ outb(IO_ICU1, 0x0a); /* default to IRR on read */
+ outb(IO_ICU1, 0xc0 | (3 - 1)); /* pri order 3-7, 0-2 (com2 first) */
+
+ outb(IO_ICU2, 0x11); /* reset; program device, four bytes */
+ outb(IO_ICU2+1, NRSVIDT+8); /* staring at this vector index */
+ outb(IO_ICU2+1,2); /* my slave id is 2 */
+#ifdef AUTO_EOI_2
+ outb(IO_ICU2+1, 2 | 1); /* auto EOI, 8086 mode */
+#else
+ outb(IO_ICU2+1,1); /* 8086 mode */
+#endif
+ outb(IO_ICU2+1, 0xff); /* leave interrupts masked */
+ outb(IO_ICU2, 0x0a); /* default to IRR on read */
+}
+
+/* region of physical memory known to be contiguous */
+vm_offset_t isaphysmem;
+static caddr_t dma_bounce[8]; /* XXX */
+static char bounced[8]; /* XXX */
+#define MAXDMASZ 512 /* XXX */
+
+/* high byte of address is stored in this port for i-th dma channel */
+static short dmapageport[8] =
+ { 0x87, 0x83, 0x81, 0x82, 0x8f, 0x8b, 0x89, 0x8a };
+
+/*
+ * isa_dmacascade(): program 8237 DMA controller channel to accept
+ * external dma control by a board.
+ */
+void isa_dmacascade(unsigned chan)
+{
+ if (chan > 7)
+ panic("isa_dmacascade: impossible request");
+
+ /* set dma channel mode, and set dma channel mode */
+ if ((chan & 4) == 0) {
+ outb(DMA1_MODE, DMA37MD_CASCADE | chan);
+ outb(DMA1_SMSK, chan);
+ } else {
+ outb(DMA2_MODE, DMA37MD_CASCADE | (chan & 3));
+ outb(DMA2_SMSK, chan & 3);
+ }
+}
+
+/*
+ * isa_dmastart(): program 8237 DMA controller channel, avoid page alignment
+ * problems by using a bounce buffer.
+ */
+void isa_dmastart(int flags, caddr_t addr, unsigned nbytes, unsigned chan)
+{ vm_offset_t phys;
+ int waport;
+ caddr_t newaddr;
+
+ if ( chan > 7
+ || (chan < 4 && nbytes > (1<<16))
+ || (chan >= 4 && (nbytes > (1<<17) || (u_int)addr & 1)))
+ panic("isa_dmastart: impossible request");
+
+ if (isa_dmarangecheck(addr, nbytes, chan)) {
+ if (dma_bounce[chan] == 0)
+ dma_bounce[chan] =
+ /*(caddr_t)malloc(MAXDMASZ, M_TEMP, M_WAITOK);*/
+ (caddr_t) isaphysmem + NBPG*chan;
+ bounced[chan] = 1;
+ newaddr = dma_bounce[chan];
+ *(int *) newaddr = 0; /* XXX */
+
+ /* copy bounce buffer on write */
+ if (!(flags & B_READ))
+ bcopy(addr, newaddr, nbytes);
+ addr = newaddr;
+ }
+
+ /* translate to physical */
+ phys = pmap_extract(pmap_kernel(), (vm_offset_t)addr);
+
+ if ((chan & 4) == 0) {
+ /*
+ * Program one of DMA channels 0..3. These are
+ * byte mode channels.
+ */
+ /* set dma channel mode, and reset address ff */
+ if (flags & B_READ)
+ outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_WRITE|chan);
+ else
+ outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_READ|chan);
+ outb(DMA1_FFC, 0);
+
+ /* send start address */
+ waport = DMA1_CHN(chan);
+ outb(waport, phys);
+ outb(waport, phys>>8);
+ outb(dmapageport[chan], phys>>16);
+
+ /* send count */
+ outb(waport + 1, --nbytes);
+ outb(waport + 1, nbytes>>8);
+
+ /* unmask channel */
+ outb(DMA1_SMSK, chan);
+ } else {
+ /*
+ * Program one of DMA channels 4..7. These are
+ * word mode channels.
+ */
+ /* set dma channel mode, and reset address ff */
+ if (flags & B_READ)
+ outb(DMA2_MODE, DMA37MD_SINGLE|DMA37MD_WRITE|(chan&3));
+ else
+ outb(DMA2_MODE, DMA37MD_SINGLE|DMA37MD_READ|(chan&3));
+ outb(DMA2_FFC, 0);
+
+ /* send start address */
+ waport = DMA2_CHN(chan - 4);
+ outb(waport, phys>>1);
+ outb(waport, phys>>9);
+ outb(dmapageport[chan], phys>>16);
+
+ /* send count */
+ nbytes >>= 1;
+ outb(waport + 2, --nbytes);
+ outb(waport + 2, nbytes>>8);
+
+ /* unmask channel */
+ outb(DMA2_SMSK, chan & 3);
+ }
+}
+
+void isa_dmadone(int flags, caddr_t addr, int nbytes, int chan)
+{
+
+ /* copy bounce buffer on read */
+ /*if ((flags & (B_PHYS|B_READ)) == (B_PHYS|B_READ))*/
+ if (bounced[chan]) {
+ bcopy(dma_bounce[chan], addr, nbytes);
+ bounced[chan] = 0;
+ }
+}
+
+/*
+ * Check for problems with the address range of a DMA transfer
+ * (non-contiguous physical pages, outside of bus address space,
+ * crossing DMA page boundaries).
+ * Return true if special handling needed.
+ */
+
+isa_dmarangecheck(caddr_t va, unsigned length, unsigned chan) {
+ vm_offset_t phys, priorpage = 0, endva;
+ u_int dma_pgmsk = (chan & 4) ? ~(128*1024-1) : ~(64*1024-1);
+
+ endva = (vm_offset_t)round_page(va + length);
+ for (; va < (caddr_t) endva ; va += NBPG) {
+ phys = trunc_page(pmap_extract(pmap_kernel(), (vm_offset_t)va));
+#define ISARAM_END RAM_END
+ if (phys == 0)
+ panic("isa_dmacheck: no physical page present");
+ if (phys > ISARAM_END)
+ return (1);
+ if (priorpage) {
+ if (priorpage + NBPG != phys)
+ return (1);
+ /* check if crossing a DMA page boundary */
+ if (((u_int)priorpage ^ (u_int)phys) & dma_pgmsk)
+ return (1);
+ }
+ priorpage = phys;
+ }
+ return (0);
+}
+
+/* head of queue waiting for physmem to become available */
+struct buf isa_physmemq;
+
+/* blocked waiting for resource to become free for exclusive use */
+static isaphysmemflag;
+/* if waited for and call requested when free (B_CALL) */
+static void (*isaphysmemunblock)(); /* needs to be a list */
+
+/*
+ * Allocate contiguous physical memory for transfer, returning
+ * a *virtual* address to region. May block waiting for resource.
+ * (assumed to be called at splbio())
+ */
+caddr_t
+isa_allocphysmem(caddr_t va, unsigned length, void (*func)()) {
+
+ isaphysmemunblock = func;
+ while (isaphysmemflag & B_BUSY) {
+ isaphysmemflag |= B_WANTED;
+ sleep(&isaphysmemflag, PRIBIO);
+ }
+ isaphysmemflag |= B_BUSY;
+
+ return((caddr_t)isaphysmem);
+}
+
+/*
+ * Free contiguous physical memory used for transfer.
+ * (assumed to be called at splbio())
+ */
+void
+isa_freephysmem(caddr_t va, unsigned length) {
+
+ isaphysmemflag &= ~B_BUSY;
+ if (isaphysmemflag & B_WANTED) {
+ isaphysmemflag &= B_WANTED;
+ wakeup(&isaphysmemflag);
+ if (isaphysmemunblock)
+ (*isaphysmemunblock)();
+ }
+}
+
+/*
+ * Handle a NMI, possibly a machine check.
+ * return true to panic system, false to ignore.
+ */
+isa_nmi(cd) {
+
+ log(LOG_CRIT, "\nNMI port 61 %x, port 70 %x\n", inb(0x61), inb(0x70));
+ return(0);
+}
+
+/*
+ * Caught a stray interrupt, notify
+ */
+isa_strayintr(d) {
+
+ /* DON'T BOTHER FOR NOW! */
+ /* for some reason, we get bursts of intr #7, even if not enabled! */
+ /*
+ * Well the reason you got bursts of intr #7 is because someone
+ * raised an interrupt line and dropped it before the 8259 could
+ * prioritize it. This is documented in the intel data book. This
+ * means you have BAD hardware! I have changed this so that only
+ * the first 5 get logged, then it quits logging them, and puts
+ * out a special message. rgrimes 3/25/1993
+ */
+ extern u_long intrcnt_stray;
+
+ intrcnt_stray++;
+ if (intrcnt_stray <= 5)
+ log(LOG_ERR,"ISA strayintr %x\n", d);
+ if (intrcnt_stray == 5)
+ log(LOG_CRIT,"Too many ISA strayintr not logging any more\n");
+}
+
+/*
+ * Wait "n" microseconds.
+ * Relies on timer 1 counting down from (TIMER_FREQ / hz) at
+ * (2 * TIMER_FREQ) Hz.
+ * Note: timer had better have been programmed before this is first used!
+ * (The standard programming causes the timer to generate a square wave and
+ * the counter is decremented twice every cycle.)
+ */
+#define CF (2 * TIMER_FREQ)
+#define TIMER_FREQ 1193182 /* XXX - should be elsewhere */
+
+extern int hz; /* XXX - should be elsewhere */
+
+int DELAY(n)
+ int n;
+{
+ int counter_limit;
+ int prev_tick;
+ int tick;
+ int ticks_left;
+ int sec;
+ int usec;
+
+#ifdef DELAYDEBUG
+ int getit_calls = 1;
+ int n1;
+ static int state = 0;
+
+ if (state == 0) {
+ state = 1;
+ for (n1 = 1; n1 <= 10000000; n1 *= 10)
+ DELAY(n1);
+ state = 2;
+ }
+ if (state == 1)
+ printf("DELAY(%d)...", n);
+#endif
+
+ /*
+ * Read the counter first, so that the rest of the setup overhead is
+ * counted. Guess the initial overhead is 20 usec (on most systems it
+ * takes about 1.5 usec for each of the i/o's in getit(). The loop
+ * takes about 6 usec on a 486/33 and 13 usec on a 386/20. The
+ * multiplications and divisions to scale the count take a while).
+ */
+ prev_tick = getit(0, 0);
+ n -= 20;
+
+ /*
+ * Calculate (n * (CF / 1e6)) without using floating point and without
+ * any avoidable overflows.
+ */
+ sec = n / 1000000;
+ usec = n - sec * 1000000;
+ ticks_left = sec * CF
+ + usec * (CF / 1000000)
+ + usec * ((CF % 1000000) / 1000) / 1000
+ + usec * (CF % 1000) / 1000000;
+
+ counter_limit = TIMER_FREQ / hz;
+ while (ticks_left > 0) {
+ tick = getit(0, 0);
+#ifdef DELAYDEBUG
+ ++getit_calls;
+#endif
+ if (tick > prev_tick)
+ ticks_left -= prev_tick - (tick - counter_limit);
+ else
+ ticks_left -= prev_tick - tick;
+ prev_tick = tick;
+ }
+#ifdef DELAYDEBUG
+ if (state == 1)
+ printf(" %d calls to getit() at %d usec each\n",
+ getit_calls, (n + 5) / getit_calls);
+#endif
+}
+
+getit(unit, timer) {
+ int high;
+ int low;
+
+ /*
+ * XXX - isa.h defines bogus timers. There's no such timer as
+ * IO_TIMER_2 = 0x48. There's a timer in the CMOS RAM chip but
+ * its interface is quite different. Neither timer is an 8252.
+ * We actually only call this with unit = 0 and timer = 0. It
+ * could be static...
+ */
+ /*
+ * Protect ourself against interrupts.
+ * XXX - sysbeep() and sysbeepstop() need protection.
+ */
+ disable_intr();
+ /*
+ * Latch the count for 'timer' (cc00xxxx, c = counter, x = any).
+ */
+ outb(IO_TIMER1 + 3, timer << 6);
+
+ low = inb(IO_TIMER1 + timer);
+ high = inb(IO_TIMER1 + timer);
+ enable_intr();
+ return ((high << 8) | low);
+}
+
+static beeping;
+static
+sysbeepstop(f)
+{
+ /* disable counter 2 */
+ outb(0x61, inb(0x61) & 0xFC);
+ if (f)
+ timeout(sysbeepstop, 0, f);
+ else
+ beeping = 0;
+}
+
+void sysbeep(int pitch, int period)
+{
+
+ outb(0x61, inb(0x61) | 3); /* enable counter 2 */
+ /*
+ * XXX - move timer stuff to clock.c.
+ * Program counter 2:
+ * ccaammmb, c counter, a = access, m = mode, b = BCD
+ * 1011x110, 11 for aa = LSB then MSB, x11 for mmm = square wave.
+ */
+ outb(0x43, 0xb6); /* set command for counter 2, 2 byte write */
+
+ outb(0x42, pitch);
+ outb(0x42, (pitch>>8));
+
+ if (!beeping) {
+ beeping = period;
+ timeout(sysbeepstop, period/2, period);
+ }
+}
+
+/*
+ * Pass command to keyboard controller (8042)
+ */
+unsigned kbc_8042cmd(val) {
+
+ while (inb(KBSTATP)&KBS_IBF);
+ if (val) outb(KBCMDP, val);
+ while (inb(KBSTATP)&KBS_IBF);
+ return (inb(KBDATAP));
+}
+
+/*
+ * find an ISA device in a given isa_devtab_* table, given
+ * the table to search, the expected id_driver entry, and the unit number.
+ *
+ * this function is defined in isa_device.h, and this location is debatable;
+ * i put it there because it's useless w/o, and directly operates on
+ * the other stuff in that file.
+ *
+ */
+
+struct isa_device *find_isadev(table, driverp, unit)
+ struct isa_device *table;
+ struct isa_driver *driverp;
+ int unit;
+{
+ if (driverp == NULL) /* sanity check */
+ return NULL;
+
+ while ((table->id_driver != driverp) || (table->id_unit != unit)) {
+ if (table->id_driver == 0)
+ return NULL;
+
+ table++;
+ }
+
+ return table;
+}
+
+/*
+ * Return nonzero if a (masked) irq is pending for a given device.
+ */
+int
+isa_irq_pending(dvp)
+ struct isa_device *dvp;
+{
+ unsigned id_irq;
+
+ id_irq = (unsigned short) dvp->id_irq; /* XXX silly type in struct */
+ if (id_irq & 0xff)
+ return (inb(IO_ICU1) & id_irq);
+ return (inb(IO_ICU2) & (id_irq >> 8));
+}
diff --git a/sys/i386/isa/isa.h b/sys/i386/isa/isa.h
new file mode 100644
index 0000000..a9c042d
--- /dev/null
+++ b/sys/i386/isa/isa.h
@@ -0,0 +1,188 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * 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.
+ *
+ * @(#)isa.h 5.7 (Berkeley) 5/9/91
+ * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE
+ * -------------------- ----- ----------------------
+ * CURRENT PATCH LEVEL: 3 00158
+ * -------------------- ----- ----------------------
+ *
+ * 15 Feb 93 Julian Elischer Added entries for some scsi adapters
+ * 06 Apr 93 Rodney W. Grimes Added com3 and com4, added IO_ISASIZES
+ * section
+ * 26 Apr 93 Bruce Evans Support for intr-0.1
+ */
+
+/*
+ * ISA Bus conventions
+ */
+
+#ifndef LOCORE
+#include <sys/cdefs.h>
+
+unsigned char rtcin __P((int));
+extern unsigned int atdevbase; /* offset in virtual memory of ISA io mem */
+void sysbeep __P((int, int));
+unsigned kbd_8042cmd __P((int));
+struct isa_device;
+int isa_irq_pending __P((struct isa_device *dvp));
+#endif
+
+
+/*
+ * Input / Output Port Assignments
+ */
+
+#ifndef IO_BEGIN
+#define IO_ISABEGIN 0x000 /* 0x000 - Beginning of I/O Registers */
+
+ /* CPU Board */
+#define IO_DMA1 0x000 /* 8237A DMA Controller #1 */
+#define IO_ICU1 0x020 /* 8259A Interrupt Controller #1 */
+#define IO_TIMER1 0x040 /* 8252 Timer #1 */
+#define IO_TIMER2 0x048 /* 8252 Timer #2 */
+#define IO_KBD 0x060 /* 8042 Keyboard */
+#define IO_RTC 0x070 /* RTC */
+#define IO_NMI IO_RTC /* NMI Control */
+#define IO_DMAPG 0x080 /* DMA Page Registers */
+#define IO_ICU2 0x0A0 /* 8259A Interrupt Controller #2 */
+#define IO_DMA2 0x0C0 /* 8237A DMA Controller #2 */
+#define IO_NPX 0x0F0 /* Numeric Coprocessor */
+
+ /* Cards */
+ /* 0x100 - 0x16F Open */
+
+#define IO_WD2 0x170 /* Secondary Fixed Disk Controller */
+
+ /* 0x178 - 0x1EF Open */
+
+#define IO_WD1 0x1f0 /* Primary Fixed Disk Controller */
+#define IO_GAME 0x200 /* Game Controller */
+
+ /* 0x208 - 0x277 Open */
+
+#define IO_LPT2 0x278 /* Parallel Port #2 */
+
+ /* 0x280 - 0x2E7 Open */
+
+#define IO_COM4 0x2e8 /* COM4 i/o address */
+
+ /* 0x2F0 - 0x2F7 Open */
+
+#define IO_COM2 0x2f8 /* COM2 i/o address */
+ /* 0x300 - 0x32F Open */
+
+#define IO_BT0 0x330 /* bustek 742a default addr. */
+#define IO_AHA0 0x330 /* adaptec 1542 default addr. */
+#define IO_UHA0 0x330 /* ultrastore 14f default addr. */
+#define IO_BT1 0x334 /* bustek 742a default addr. */
+#define IO_AHA1 0x334 /* adaptec 1542 default addr. */
+ /* 0x338 - 0x36F Open */
+
+#define IO_FD2 0x370 /* secondary base i/o address */
+#define IO_LPT1 0x378 /* Parallel Port #1 */
+
+ /* 0x380 - 0x3AF Open */
+
+#define IO_MDA 0x3B0 /* Monochome Adapter */
+#define IO_LPT3 0x3BC /* Monochome Adapter Printer Port */
+#define IO_VGA 0x3C0 /* E/VGA Ports */
+#define IO_CGA 0x3D0 /* CGA Ports */
+
+ /* 0x3E0 - 0x3E7 Open */
+
+#define IO_COM3 0x3e8 /* COM3 i/o address */
+#define IO_FD1 0x3f0 /* primary base i/o address */
+#define IO_COM1 0x3f8 /* COM1 i/o address */
+
+#define IO_ISAEND 0x3FF /* - 0x3FF End of I/O Registers */
+#endif IO_ISABEGIN
+
+/*
+ * Input / Output Port Sizes - these are from several sources, and tend
+ * to be the larger of what was found, ie COM ports can be 4, but some
+ * boards do not fully decode the address, thus 8 ports are used.
+ */
+
+#ifndef IO_ISASIZES
+#define IO_ISASIZES
+
+#define IO_COMSIZE 8 /* 8250, 16X50 com controllers (4?) */
+#define IO_CGASIZE 16 /* CGA controllers */
+#define IO_DMASIZE 16 /* 8237 DMA controllers */
+#define IO_DPGSIZE 32 /* 74LS612 DMA page reisters */
+#define IO_FDCSIZE 8 /* Nec765 floppy controllers */
+#define IO_WDCSIZE 8 /* WD compatible disk controllers */
+#define IO_GAMSIZE 16 /* AT compatible game controllers */
+#define IO_ICUSIZE 16 /* 8259A interrupt controllers */
+#define IO_KBDSIZE 16 /* 8042 Keyboard controllers */
+#define IO_LPTSIZE 8 /* LPT controllers, some use only 4 */
+#define IO_MDASIZE 16 /* Monochrome display controllers */
+#define IO_RTCSIZE 16 /* CMOS real time clock, NMI control */
+#define IO_TMRSIZE 16 /* 8253 programmable timers */
+#define IO_NPXSIZE 16 /* 80387/80487 NPX registers */
+#define IO_VGASIZE 16 /* VGA controllers */
+
+#endif /* IO_ISASIZES */
+
+/*
+ * Input / Output Memory Physical Addresses
+ */
+
+#ifndef IOM_BEGIN
+#define IOM_BEGIN 0x0a0000 /* Start of I/O Memory "hole" */
+#define IOM_END 0x100000 /* End of I/O Memory "hole" */
+#define IOM_SIZE (IOM_END - IOM_BEGIN)
+#endif IOM_BEGIN
+
+/*
+ * RAM Physical Address Space (ignoring the above mentioned "hole")
+ */
+
+#ifndef RAM_BEGIN
+#define RAM_BEGIN 0x0000000 /* Start of RAM Memory */
+#define RAM_END 0x1000000 /* End of RAM Memory */
+#define RAM_SIZE (RAM_END - RAM_BEGIN)
+#endif RAM_BEGIN
+
+/*
+ * Oddball Physical Memory Addresses
+ */
+#ifndef COMPAQ_RAMRELOC
+#define COMPAQ_RAMRELOC 0x80c00000 /* Compaq RAM relocation/diag */
+#define COMPAQ_RAMSETUP 0x80c00002 /* Compaq RAM setup */
+#define WEITEK_FPU 0xC0000000 /* WTL 2167 */
+#define CYRIX_EMC 0xC0000000 /* Cyrix EMC */
+#endif COMPAQ_RAMRELOC
diff --git a/sys/i386/isa/isa_device.h b/sys/i386/isa/isa_device.h
new file mode 100644
index 0000000..eea71ec
--- /dev/null
+++ b/sys/i386/isa/isa_device.h
@@ -0,0 +1,85 @@
+/*-
+ * 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.
+ *
+ * @(#)isa_device.h 7.1 (Berkeley) 5/9/91
+ *
+ * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE
+ * -------------------- ----- ----------------------
+ * CURRENT PATCH LEVEL: 2 00163
+ * -------------------- ----- ----------------------
+ *
+ * 27 Feb 93 Chris Demetriou Add proper flag handling.
+ * 10 Mar 93 Rodney W. Grimes Fixed isa_device->id_irq to be
+ * the u_short instead of short. This
+ * enables us to use irq15!
+ * 27 May 93 Guido van Rooij Add prototype find_isadev()
+ *
+ */
+
+/*
+ * ISA Bus Autoconfiguration
+ */
+
+/*
+ * Per device structure.
+ */
+struct isa_device {
+ struct isa_driver *id_driver;
+ short id_iobase; /* base i/o address */
+ u_short id_irq; /* interrupt request */
+ short id_drq; /* DMA request */
+ caddr_t id_maddr; /* physical i/o memory address on bus (if any)*/
+ int id_msize; /* size of i/o memory */
+ int (*id_intr)(); /* interrupt interface routine */
+ int id_unit; /* unit number */
+ int id_flags; /* flags */
+ int id_scsiid; /* scsi id if needed */
+ int id_alive; /* device is present */
+};
+
+/*
+ * Per-driver structure.
+ *
+ * Each device driver defines entries for a set of routines
+ * as well as an array of types which are acceptable to it.
+ * These are used at boot time by the configuration program.
+ */
+struct isa_driver {
+ int (*probe)(); /* test whether device is present */
+ int (*attach)(); /* setup driver for a device */
+ char *name; /* device name */
+};
+
+extern struct isa_device isa_devtab_bio[], isa_devtab_tty[], isa_devtab_net[],
+ isa_devtab_null[];
+
+extern struct isa_device *find_isadev(/* table, driver, unit*/);
diff --git a/sys/i386/isa/kbd.h b/sys/i386/isa/kbd.h
new file mode 100644
index 0000000..f60e8c2
--- /dev/null
+++ b/sys/i386/isa/kbd.h
@@ -0,0 +1,58 @@
+/*
+ *
+ * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE
+ * -------------------- ----- ----------------------
+ * CURRENT PATCH LEVEL: 1 00162
+ * -------------------- ----- ----------------------
+ *
+ * 26 May 93 Holger Veit added more 8042 defines
+ *
+ * Keyboard definitions
+ */
+
+/* Reference: IBM AT Technical Reference Manual,
+ * pp. 1-38 to 1-43, 4-3 to 4-22
+ */
+
+/* commands sent to KBCMDP */
+
+#define KBC_CMDREAD 0x20 /* read kbd cntrl command byte */
+#define KBC_CMDWRITE 0x60 /* == LD_CMDBYTE in kd.h, write command */
+#define KBC_SELFTEST 0xAA /* perform self test, returns 55 when ok */
+#define KBC_IFTEST 0xAB /* perform interface test */
+#define KBC_DIAGDUMP 0xAC /* send 19 status bytes to system */
+#define KBC_DISKBD 0xAD /* disable keyboard */
+#define KBC_ENAKBD 0xAE /* enable keyboard */
+#define KBC_RDINP 0xC0 /* read input port */
+#define KBC_RDID 0xC4 /* read keyboard ID */
+#define KBC_RDOUTP 0xD0 /* read output port */
+#define KBC_WROUTP 0xD1 /* write output port */
+#define KBC_RDTINP 0xE0 /* read test inputs */
+
+/* commands sent to KBDATAP */
+#define KBC_STSIND 0xED /* set keyboard status indicators */
+#define KBC_ECHO 0xEE /* reply with 0xEE */
+#define KBC_SETTPM 0xF3 /* Set typematic rate/delay */
+#define KBC_ENABLE 0xF4 /* Start scanning */
+#define KBC_SETDEFD 0xF5 /* =KBC_SETDEF, but disable scanning */
+#define KBC_SETDEF 0xF6 /* Set power on defaults */
+#define KBC_RESEND 0xFE /* system wants keyboard to resend last code */
+#define KBC_RESET 0xFF /* Reset the keyboard */
+
+/* responses */
+#define KBR_OVERRUN 0x00 /* Keyboard flooded */
+#define KBR_STOK 0x55 /* Selftest ok response */
+#define KBR_IFOK 0x00 /* Interface test ok */
+#define KBR_IFCL_SA0 0x01 /* Clock Stuck-at-0 fault */
+#define KBR_IFCL_SA1 0x02 /* Clock Stuck-at-1 fault */
+#define KBR_IFDA_SA0 0x03 /* Data Stuck-at-0 fault */
+#define KBR_IFDA_SA1 0x04 /* Data Stuck-at-1 fault */
+#define KBR_RSTDONE 0xAA /* Keyboard reset (BAT) complete */
+#define KBR_E0 0xE0 /* Extended prefix */
+#define KBR_E1 0xE1 /* BREAK'S HIT :-( */
+#define KBR_ECHO 0xEE /* Echo response */
+#define KBR_F0 0xF0 /* Break code prefix */
+#define KBR_ACK 0xFA /* Keyboard did receive command */
+#define KBR_BATFAIL 0xFC /* BAT failed */
+#define KBR_DIAGFAIL 0xFD /* Diagnostic failed response */
+#define KBR_RESEND 0xFE /* Keyboard needs resend of command */
diff --git a/sys/i386/isa/lpt.c b/sys/i386/isa/lpt.c
new file mode 100644
index 0000000..46d1e47
--- /dev/null
+++ b/sys/i386/isa/lpt.c
@@ -0,0 +1,455 @@
+/*
+ * Copyright (c) 1990 William F. Jolitz, TeleMuse
+ * 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 software is a component of "386BSD" developed by
+ * William F. Jolitz, TeleMuse.
+ * 4. Neither the name of the developer nor the name "386BSD"
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS A COMPONENT OF 386BSD DEVELOPED BY WILLIAM F. JOLITZ
+ * AND IS INTENDED FOR RESEARCH AND EDUCATIONAL PURPOSES ONLY. THIS
+ * SOFTWARE SHOULD NOT BE CONSIDERED TO BE A COMMERCIAL PRODUCT.
+ * THE DEVELOPER URGES THAT USERS WHO REQUIRE A COMMERCIAL PRODUCT
+ * NOT MAKE USE OF THIS WORK.
+ *
+ * FOR USERS WHO WISH TO UNDERSTAND THE 386BSD SYSTEM DEVELOPED
+ * BY WILLIAM F. JOLITZ, WE RECOMMEND THE USER STUDY WRITTEN
+ * REFERENCES SUCH AS THE "PORTING UNIX TO THE 386" SERIES
+ * (BEGINNING JANUARY 1991 "DR. DOBBS JOURNAL", USA AND BEGINNING
+ * JUNE 1991 "UNIX MAGAZIN", GERMANY) BY WILLIAM F. JOLITZ AND
+ * LYNNE GREER JOLITZ, AS WELL AS OTHER BOOKS ON UNIX AND THE
+ * ON-LINE 386BSD USER MANUAL BEFORE USE. A BOOK DISCUSSING THE INTERNALS
+ * OF 386BSD ENTITLED "386BSD FROM THE INSIDE OUT" WILL BE AVAILABLE LATE 1992.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``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 DEVELOPER 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.
+ *
+ * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE
+ * -------------------- ----- ----------------------
+ * CURRENT PATCH LEVEL: 2 00164
+ * -------------------- ----- ----------------------
+ *
+ * 06 Apr 93 Eric Haug Fixed comments and includes. [Ed: I did
+ * not include the unit-1 thing, that is a
+ * DOSism, fixed the config file instead]
+ * 06 Apr 93 Rodney W. Grimes A real probe routine, may even cause on
+ * interrupt if a printer is attached.
+ *
+ * 01 Jun 93 Rodney W. Grimes Made lpflag uniq now is lptflag
+ * Added timeout loop to lpt_port_test.
+ * lpt_port_test should move to a common
+ * routine..
+ *
+ */
+
+/*
+ * Device Driver for AT parallel printer port
+ * Written by William Jolitz 12/18/90
+ */
+
+#include "lpt.h"
+#if NLPT > 0
+
+#include "param.h"
+#include "systm.h"
+#include "proc.h"
+#include "user.h"
+#include "buf.h"
+#include "kernel.h"
+#include "ioctl.h"
+#include "tty.h"
+#include "uio.h"
+
+#include "i386/isa/isa.h"
+#include "i386/isa/isa_device.h"
+#include "i386/isa/lptreg.h"
+
+#define LPINITRDY 4 /* wait up to 4 seconds for a ready */
+#define LPTOUTTIME 4 /* wait up to 4 seconds for a ready */
+#define LPPRI (PZERO+8)
+#define BUFSIZE 1024
+
+#ifndef DEBUG
+#define lprintf
+#else
+#define lprintf if (lptflag) printf
+int lptflag = 1;
+#endif
+
+int lptout();
+#ifdef DEBUG
+int lptflag = 1;
+#endif
+
+int lptprobe(), lptattach(), lptintr();
+
+struct isa_driver lptdriver = {
+ lptprobe, lptattach, "lpt"
+};
+
+#define LPTUNIT(s) (((s)>>6)&0x3)
+#define LPTFLAGS(s) ((s)&0x3f)
+
+struct lpt_softc {
+ short sc_port;
+ short sc_state;
+ /* default case: negative prime, negative ack, handshake strobe,
+ prime once */
+ u_char sc_control;
+ char sc_flags;
+#define LP_POS_INIT 0x01 /* if we are a postive init signal */
+#define LP_POS_ACK 0x02 /* if we are a positive going ack */
+#define LP_NO_PRIME 0x04 /* don't prime the printer at all */
+#define LP_PRIMEOPEN 0x08 /* prime on every open */
+#define LP_AUTOLF 0x10 /* tell printer to do an automatic lf */
+#define LP_BYPASS 0x20 /* bypass printer ready checks */
+ struct buf *sc_inbuf;
+ short sc_xfercnt ;
+ char sc_primed;
+ char *sc_cp ;
+} lpt_sc[NLPT] ;
+
+/* bits for state */
+#define OPEN (1<<0) /* device is open */
+#define ASLP (1<<1) /* awaiting draining of printer */
+#define ERROR (1<<2) /* error was received from printer */
+#define OBUSY (1<<3) /* printer is busy doing output */
+#define LPTOUT (1<<4) /* timeout while not selected */
+#define TOUT (1<<5) /* timeout while not selected */
+#define INIT (1<<6) /* waiting to initialize for open */
+
+/*
+ * Internal routine to lptprobe to do port tests of one byte value
+ */
+int
+lpt_port_test(short port, u_char data, u_char mask)
+ {
+ int temp, timeout;
+
+ data = data & mask;
+ outb(port, data);
+ timeout = 100;
+ do
+ temp = inb(port) & mask;
+ while (temp != data && --timeout);
+ lprintf("Port 0x%x\tout=%x\tin=%x\n", port, data, temp);
+ return (temp == data);
+ }
+
+/*
+ * New lptprobe routine written by Rodney W. Grimes, 3/25/1993
+ *
+ * Logic:
+ * 1) You should be able to write to and read back the same value
+ * to the data port. Do an alternating zeros, alternating ones,
+ * walking zero, and walking one test to check for stuck bits.
+ *
+ * 2) You should be able to write to and read back the same value
+ * to the control port lower 5 bits, the upper 3 bits are reserved
+ * per the IBM PC technical reference manauls and different boards
+ * do different things with them. Do an alternating zeros, alternating
+ * ones, walking zero, and walking one test to check for stuck bits.
+ *
+ * Some printers drag the strobe line down when the are powered off
+ * so this bit has been masked out of the control port test.
+ *
+ * XXX Some printers may not like a fast pulse on init or strobe, I
+ * don't know at this point, if that becomes a problem these bits
+ * should be turned off in the mask byte for the control port test.
+ *
+ * 3) Set the data and control ports to a value of 0
+ */
+
+int
+lptprobe(struct isa_device *dvp)
+ {
+ int status;
+ short port;
+ u_char data;
+ u_char mask;
+ int i;
+
+ status = IO_LPTSIZE;
+
+ port = dvp->id_iobase + lpt_data;
+ mask = 0xff;
+ while (mask != 0)
+ {
+ data = 0x55; /* Alternating zeros */
+ if (!lpt_port_test(port, data, mask)) status = 0;
+
+ data = 0xaa; /* Alternating ones */
+ if (!lpt_port_test(port, data, mask)) status = 0;
+
+ for (i = 0; i < 8; i++) /* Walking zero */
+ {
+ data = ~(1 << i);
+ if (!lpt_port_test(port, data, mask)) status = 0;
+ }
+
+ for (i = 0; i < 8; i++) /* Walking one */
+ {
+ data = (1 << i);
+ if (!lpt_port_test(port, data, mask)) status = 0;
+ }
+
+ if (port == dvp->id_iobase + lpt_data)
+ {
+ port = dvp->id_iobase + lpt_control;
+ mask = 0x1e;
+ }
+ else
+ mask = 0;
+ }
+ outb(dvp->id_iobase+lpt_data, 0);
+ outb(dvp->id_iobase+lpt_control, 0);
+ return (status);
+ }
+
+lptattach(isdp)
+ struct isa_device *isdp;
+{
+ struct lpt_softc *sc;
+
+ sc = lpt_sc + isdp->id_unit;
+ sc->sc_port = isdp->id_iobase;
+ outb(sc->sc_port+lpt_control, LPC_NINIT);
+ return (1);
+}
+
+/*
+ * lptopen -- reset the printer, then wait until it's selected and not busy.
+ */
+
+lptopen(dev, flag)
+ dev_t dev;
+ int flag;
+{
+ struct lpt_softc *sc = lpt_sc + LPTUNIT(minor(dev));
+ int s;
+ int trys, port;
+
+ if (sc->sc_state) {
+lprintf("lp: still open\n") ;
+printf("still open %x\n", sc->sc_state);
+ return(EBUSY);
+ } else sc->sc_state |= INIT;
+
+ s = spltty();
+ sc->sc_flags = LPTFLAGS(minor(dev));
+lprintf("lp flags 0x%x\n", sc->sc_flags);
+ port = sc->sc_port;
+
+ /* init printer */
+ if((sc->sc_flags & LP_NO_PRIME) == 0) {
+ if((sc->sc_flags & LP_PRIMEOPEN) || sc->sc_primed == 0) {
+ outb(port+lpt_control, 0);
+ sc->sc_primed++;
+ DELAY(500);
+ }
+ }
+ outb(port+lpt_control, LPC_SEL|LPC_NINIT);
+
+ /* wait till ready (printer running diagnostics) */
+ trys = 0;
+ do {
+ /* ran out of waiting for the printer */
+ if (trys++ >= LPINITRDY*4) {
+ splx(s);
+ sc->sc_state = 0;
+printf ("status %x\n", inb(port+lpt_status) );
+ return (EBUSY);
+ }
+
+ /* wait 1/4 second, give up if we get a signal */
+ if (tsleep (sc, LPPRI|PCATCH, "lptinit", hz/4) != EWOULDBLOCK) {
+ sc->sc_state = 0;
+ splx(s);
+ return (EBUSY);
+ }
+
+ /* is printer online and ready for output */
+ } while ((inb(port+lpt_status) & (LPS_SEL|LPS_OUT|LPS_NBSY|LPS_NERR)) !=
+ (LPS_SEL|LPS_NBSY|LPS_NERR));
+
+ if(sc->sc_flags&LP_AUTOLF) {
+ outb(port+lpt_control, LPC_SEL|LPC_NINIT|LPC_ENA|LPC_AUTOL);
+ sc->sc_control = LPC_SEL|LPC_NINIT|LPC_ENA|LPC_AUTOL;
+ } else {
+ outb(port+lpt_control, LPC_SEL|LPC_NINIT|LPC_ENA);
+ sc->sc_control = LPC_SEL|LPC_NINIT|LPC_ENA;
+ }
+
+ sc->sc_state = OPEN | TOUT;
+ sc->sc_inbuf = geteblk(BUFSIZE);
+ sc->sc_xfercnt = 0;
+ splx(s);
+ timeout (lptout, sc, hz/2);
+lprintf("opened.\n");
+ return(0);
+}
+
+lptout (sc)
+ struct lpt_softc *sc;
+{ int pl;
+
+lprintf ("T %x ", inb(sc->sc_port+lpt_status));
+ if (sc->sc_state&OPEN)
+ timeout (lptout, sc, hz/2);
+ else sc->sc_state &= ~TOUT;
+
+ if (sc->sc_state & ERROR)
+ sc->sc_state &= ~ERROR;
+
+ /*
+ * Avoid possible hangs do to missed interrupts
+ */
+ if (sc->sc_xfercnt) {
+ pl = spltty();
+ lptintr(sc - lpt_sc);
+ splx(pl);
+ } else {
+ sc->sc_state &= ~OBUSY;
+ wakeup((caddr_t)sc);
+ }
+}
+
+/*
+ * lptclose -- close the device, free the local line buffer.
+ */
+
+lptclose(dev, flag)
+ int flag;
+{
+ struct lpt_softc *sc = lpt_sc + LPTUNIT(minor(dev));
+ int port = sc->sc_port;
+
+ sc->sc_state &= ~OPEN;
+ while ((inb(port+lpt_status) & (LPS_SEL|LPS_OUT|LPS_NBSY|LPS_NERR)) !=
+ (LPS_SEL|LPS_NBSY|LPS_NERR) || sc->sc_xfercnt)
+ /* wait 1/4 second, give up if we get a signal */
+ if (tsleep (sc, LPPRI|PCATCH, "lpclose", hz) != EWOULDBLOCK)
+ break;
+
+ sc->sc_state = 0;
+ sc->sc_xfercnt = 0;
+ outb(sc->sc_port+lpt_control, LPC_NINIT);
+ brelse(sc->sc_inbuf);
+lprintf("closed.\n");
+ return(0);
+}
+
+/*
+ * lptwrite --copy a line from user space to a local buffer, then call
+ * putc to get the chars moved to the output queue.
+ */
+
+lptwrite(dev, uio)
+ dev_t dev;
+ struct uio *uio;
+{
+ register unsigned n;
+ int pl, err;
+ struct lpt_softc *sc = lpt_sc + LPTUNIT(minor(dev));
+
+ while (n = MIN(BUFSIZE, uio->uio_resid)) {
+ sc->sc_cp = sc->sc_inbuf->b_un.b_addr ;
+ uiomove(sc->sc_cp, n, uio);
+ sc->sc_xfercnt = n ;
+ while (sc->sc_xfercnt > 0) {
+ /* if the printer is ready for a char, give it one */
+ if ((sc->sc_state & OBUSY) == 0){
+lprintf("\nC %d. ", sc->sc_xfercnt);
+ pl = spltty();
+ lptintr(sc - lpt_sc);
+ (void) splx(pl);
+ }
+lprintf("W ");
+ if (err = tsleep (sc, LPPRI|PCATCH, "lpwrite", 0))
+ return(err);
+ }
+ }
+ return(0);
+}
+
+/*
+ * lptintr -- handle printer interrupts which occur when the printer is
+ * ready to accept another char.
+ */
+
+lptintr(unit)
+{
+ struct lpt_softc *sc = lpt_sc + unit;
+ int port = sc->sc_port,sts;
+
+ /* is printer online and ready for output */
+ if (((sts=inb(port+lpt_status)) & (LPS_SEL|LPS_OUT|LPS_NBSY|LPS_NERR/*|LPS_NACK*/)) ==
+ (LPS_SEL|LPS_NBSY|LPS_NERR)) {
+ /* is this a false interrupt ? */
+ if ((sc->sc_state & OBUSY)
+ && (sts & LPS_NACK) == 0) return;
+ sc->sc_state |= OBUSY; sc->sc_state &= ~ERROR;
+
+ if (sc->sc_xfercnt) {
+ /* send char */
+/*lprintf("%x ", *sc->sc_cp); */
+ outb(port+lpt_data, *sc->sc_cp++) ; sc->sc_xfercnt-- ;
+ outb(port+lpt_control, sc->sc_control|LPC_STB);
+ /* DELAY(X) */
+ outb(port+lpt_control, sc->sc_control);
+ }
+
+ /* any more bytes for the printer? */
+ if (sc->sc_xfercnt > 0) return;
+
+ /* none, wake up the top half to get more */
+ sc->sc_state &= ~OBUSY;
+ wakeup((caddr_t)sc);
+lprintf("w ");
+return;
+ } else sc->sc_state |= ERROR;
+lprintf("sts %x ", sts);
+}
+
+int
+lptioctl(dev_t dev, int cmd, caddr_t data, int flag)
+{
+ int error;
+
+ error = 0;
+ switch (cmd) {
+#ifdef THISISASAMPLE
+ case XXX:
+ dothis; andthis; andthat;
+ error=x;
+ break;
+#endif /* THISISASAMPLE */
+ default:
+ error = ENODEV;
+ }
+
+ return(error);
+}
+
+#endif /* NLPT */
diff --git a/sys/i386/isa/lptreg.h b/sys/i386/isa/lptreg.h
new file mode 100644
index 0000000..2605fa0
--- /dev/null
+++ b/sys/i386/isa/lptreg.h
@@ -0,0 +1,34 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * %sccs.include.noredist.c%
+ *
+ * @(#)lptreg.h 1.1 (Berkeley) 12/19/90
+ */
+
+/*
+ * AT Parallel Port (for lineprinter)
+ * Interface port and bit definitions
+ * Written by William Jolitz 12/18/90
+ * Copyright (C) William Jolitz 1990
+ */
+
+#define lpt_data 0 /* Data to/from printer (R/W) */
+
+#define lpt_status 1 /* Status of printer (R) */
+#define LPS_NERR 0x08 /* printer no error */
+#define LPS_SEL 0x10 /* printer selected */
+#define LPS_OUT 0x20 /* printer out of paper */
+#define LPS_NACK 0x40 /* printer no ack of data */
+#define LPS_NBSY 0x80 /* printer no ack of data */
+
+#define lpt_control 2 /* Control printer (R/W) */
+#define LPC_STB 0x01 /* strobe data to printer */
+#define LPC_AUTOL 0x02 /* automatic linefeed */
+#define LPC_NINIT 0x04 /* initialize printer */
+#define LPC_SEL 0x08 /* printer selected */
+#define LPC_ENA 0x10 /* printer out of paper */
diff --git a/sys/i386/isa/npx.c b/sys/i386/isa/npx.c
new file mode 100644
index 0000000..73392fa
--- /dev/null
+++ b/sys/i386/isa/npx.c
@@ -0,0 +1,564 @@
+/*-
+ * Copyright (c) 1990 William Jolitz.
+ * 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.
+ *
+ * @(#)npx.c 7.2 (Berkeley) 5/12/91
+ *
+ * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE
+ * -------------------- ----- ----------------------
+ * CURRENT PATCH LEVEL: 1 00154
+ * -------------------- ----- ----------------------
+ *
+ * 20 Apr 93 Bruce Evans New npx-0.5 code
+ * 23 May 93 Rodney W. Grimes Return a special value of -1 from
+ * the probe code to keep isa_config from
+ * printing out the I/O address when we
+ * are using trap 16 handling.
+ *
+ */
+static char rcsid[] = "$Header: /usr/bill/working/sys/i386/isa/RCS/npx.c,v 1.2 92/01/21 14:34:27 william Exp $";
+
+#include "npx.h"
+#if NNPX > 0
+
+#include "param.h"
+#include "systm.h"
+#include "conf.h"
+#include "file.h"
+#include "proc.h"
+#include "machine/cpu.h"
+#include "machine/pcb.h"
+#include "machine/trap.h"
+#include "ioctl.h"
+#include "machine/specialreg.h"
+#include "i386/isa/icu.h"
+#include "i386/isa/isa_device.h"
+#include "i386/isa/isa.h"
+
+/*
+ * 387 and 287 Numeric Coprocessor Extension (NPX) Driver.
+ */
+
+#ifdef __GNUC__
+
+#define disable_intr() __asm("cli")
+#define enable_intr() __asm("sti")
+#define fldcw(addr) __asm("fldcw %0" : : "m" (*addr))
+#define fnclex() __asm("fnclex")
+#define fninit() __asm("fninit")
+#define fnsave(addr) __asm("fnsave %0" : "=m" (*addr) : "0" (*addr))
+#define fnstcw(addr) __asm("fnstcw %0" : "=m" (*addr) : "0" (*addr))
+#define fnstsw(addr) __asm("fnstsw %0" : "=m" (*addr) : "0" (*addr))
+#define fp_divide_by_0() __asm("fldz; fld1; fdiv %st,%st(1); fwait")
+#define frstor(addr) __asm("frstor %0" : : "m" (*addr))
+#define fwait() __asm("fwait")
+#define read_eflags() ({u_long ef; \
+ __asm("pushf; popl %0" : "=a" (ef)); \
+ ef; })
+#define start_emulating() __asm("smsw %%ax; orb %0,%%al; lmsw %%ax" \
+ : : "n" (CR0_TS) : "ax")
+#define stop_emulating() __asm("clts")
+#define write_eflags(ef) __asm("pushl %0; popf" : : "a" ((u_long) ef))
+
+#else /* not __GNUC__ */
+
+void disable_intr __P((void));
+void enable_intr __P((void));
+void fldcw __P((caddr_t addr));
+void fnclex __P((void));
+void fninit __P((void));
+void fnsave __P((caddr_t addr));
+void fnstcw __P((caddr_t addr));
+void fnstsw __P((caddr_t addr));
+void fp_divide_by_0 __P((void));
+void frstor __P((caddr_t addr));
+void fwait __P((void));
+u_long read_eflags __P((void));
+void start_emulating __P((void));
+void stop_emulating __P((void));
+void write_eflags __P((u_long ef));
+
+#endif /* __GNUC__ */
+
+typedef u_char bool_t;
+
+extern struct gate_descriptor idt[];
+
+int npxdna __P((void));
+void npxexit __P((struct proc *p));
+void npxinit __P((u_int control));
+void npxintr __P((struct intrframe frame));
+void npxsave __P((struct save87 *addr));
+static int npxattach __P((struct isa_device *dvp));
+static int npxprobe __P((struct isa_device *dvp));
+static int npxprobe1 __P((struct isa_device *dvp));
+
+struct isa_driver npxdriver = {
+ npxprobe, npxattach, "npx",
+};
+
+u_int npx0mask;
+struct proc *npxproc;
+
+static bool_t npx_ex16;
+static bool_t npx_exists;
+static struct gate_descriptor npx_idt_probeintr;
+static int npx_intrno;
+static volatile u_int npx_intrs_while_probing;
+static bool_t npx_irq13;
+static volatile u_int npx_traps_while_probing;
+
+/*
+ * Special interrupt handlers. Someday intr0-intr15 will be used to count
+ * interrupts. We'll still need a special exception 16 handler. The busy
+ * latch stuff in probintr() can be moved to npxprobe().
+ */
+void probeintr(void);
+asm
+("
+ .text
+_probeintr:
+ ss
+ incl _npx_intrs_while_probing
+ pushl %eax
+ movb $0x20,%al /* EOI (asm in strings loses cpp features) */
+ outb %al,$0xa0 /* IO_ICU2 */
+ outb %al,$0x20 /* IO_ICU1 */
+ movb $0,%al
+ outb %al,$0xf0 /* clear BUSY# latch */
+ popl %eax
+ iret
+");
+
+void probetrap(void);
+asm
+("
+ .text
+_probetrap:
+ ss
+ incl _npx_traps_while_probing
+ fnclex
+ iret
+");
+
+/*
+ * Probe routine. Initialize cr0 to give correct behaviour for [f]wait
+ * whether the device exists or not (XXX should be elsewhere). Set flags
+ * to tell npxattach() what to do. Modify device struct if npx doesn't
+ * need to use interrupts. Return 1 if device exists.
+ */
+static int
+npxprobe(dvp)
+ struct isa_device *dvp;
+{
+ int result;
+ u_long save_eflags;
+ u_char save_icu1_mask;
+ u_char save_icu2_mask;
+ struct gate_descriptor save_idt_npxintr;
+ struct gate_descriptor save_idt_npxtrap;
+ /*
+ * This routine is now just a wrapper for npxprobe1(), to install
+ * special npx interrupt and trap handlers, to enable npx interrupts
+ * and to disable other interrupts. Someday isa_configure() will
+ * install suitable handlers and run with interrupts enabled so we
+ * won't need to do so much here.
+ */
+ npx_intrno = NRSVIDT + ffs(dvp->id_irq) - 1;
+ save_eflags = read_eflags();
+ disable_intr();
+ save_icu1_mask = inb(IO_ICU1 + 1);
+ save_icu2_mask = inb(IO_ICU2 + 1);
+ save_idt_npxintr = idt[npx_intrno];
+ save_idt_npxtrap = idt[16];
+ outb(IO_ICU1 + 1, ~(IRQ_SLAVE | dvp->id_irq));
+ outb(IO_ICU2 + 1, ~(dvp->id_irq >> 8));
+ setidt(16, probetrap, SDT_SYS386TGT, SEL_KPL);
+ setidt(npx_intrno, probeintr, SDT_SYS386IGT, SEL_KPL);
+ npx_idt_probeintr = idt[npx_intrno];
+ enable_intr();
+ result = npxprobe1(dvp);
+ disable_intr();
+ outb(IO_ICU1 + 1, save_icu1_mask);
+ outb(IO_ICU2 + 1, save_icu2_mask);
+ idt[npx_intrno] = save_idt_npxintr;
+ idt[16] = save_idt_npxtrap;
+ write_eflags(save_eflags);
+ return (result);
+}
+
+static int
+npxprobe1(dvp)
+ struct isa_device *dvp;
+{
+ int control;
+ int status;
+#ifdef lint
+ npxintr();
+#endif
+ /*
+ * Partially reset the coprocessor, if any. Some BIOS's don't reset
+ * it after a warm boot.
+ */
+ outb(0xf1, 0); /* full reset on some systems, NOP on others */
+ outb(0xf0, 0); /* clear BUSY# latch */
+ /*
+ * Prepare to trap all ESC (i.e., NPX) instructions and all WAIT
+ * instructions. We must set the CR0_MP bit and use the CR0_TS
+ * bit to control the trap, because setting the CR0_EM bit does
+ * not cause WAIT instructions to trap. It's important to trap
+ * WAIT instructions - otherwise the "wait" variants of no-wait
+ * control instructions would degenerate to the "no-wait" variants
+ * after FP context switches but work correctly otherwise. It's
+ * particularly important to trap WAITs when there is no NPX -
+ * otherwise the "wait" variants would always degenerate.
+ *
+ * Try setting CR0_NE to get correct error reporting on 486DX's.
+ * Setting it should fail or do nothing on lesser processors.
+ */
+ load_cr0(rcr0() | CR0_MP | CR0_NE);
+ /*
+ * But don't trap while we're probing.
+ */
+ stop_emulating();
+ /*
+ * Finish resetting the coprocessor, if any. If there is an error
+ * pending, then we may get a bogus IRQ13, but probeintr() will handle
+ * it OK. Bogus halts have never been observed, but we enabled
+ * IRQ13 and cleared the BUSY# latch early to handle them anyway.
+ */
+ fninit();
+ DELAY(1000); /* wait for any IRQ13 (fwait might hang) */
+#ifdef DIAGNOSTIC
+ if (npx_intrs_while_probing != 0)
+ printf("fninit caused %u bogus npx interrupt(s)\n",
+ npx_intrs_while_probing);
+ if (npx_traps_while_probing != 0)
+ printf("fninit caused %u bogus npx trap(s)\n",
+ npx_traps_while_probing);
+#endif
+ /*
+ * Check for a status of mostly zero.
+ */
+ status = 0x5a5a;
+ fnstsw(&status);
+ if ((status & 0xb8ff) == 0) {
+ /*
+ * Good, now check for a proper control word.
+ */
+ control = 0x5a5a;
+ fnstcw(&control);
+ if ((control & 0x1f3f) == 0x033f) {
+ npx_exists = 1;
+ /*
+ * We have an npx, now divide by 0 to see if exception
+ * 16 works.
+ */
+ control &= ~(1 << 2); /* enable divide by 0 trap */
+ fldcw(&control);
+ npx_traps_while_probing = npx_intrs_while_probing = 0;
+ fp_divide_by_0();
+ if (npx_traps_while_probing != 0) {
+ /*
+ * Good, exception 16 works.
+ */
+ npx_ex16 = 1;
+ dvp->id_irq = 0; /* zap the interrupt */
+ /*
+ * special return value to flag that we do not
+ * actually use any I/O registers
+ */
+ return (-1);
+ }
+ if (npx_intrs_while_probing != 0) {
+ /*
+ * Bad, we are stuck with IRQ13.
+ */
+ npx_irq13 = 1;
+ npx0mask = dvp->id_irq; /* npxattach too late */
+ return (IO_NPXSIZE);
+ }
+ /*
+ * Worse, even IRQ13 is broken. Use emulator.
+ */
+ }
+ }
+ /*
+ * Probe failed, but we want to get to npxattach to initialize the
+ * emulator and say that it has been installed. XXX handle devices
+ * that aren't really devices better.
+ */
+ dvp->id_irq = 0;
+ return (IO_NPXSIZE);
+}
+
+/*
+ * Attach routine - announce which it is, and wire into system
+ */
+int
+npxattach(dvp)
+ struct isa_device *dvp;
+{
+ if (npx_ex16)
+ printf(" <Errors reported via Exception 16>");
+ else if (npx_irq13)
+ printf(" <Errors reported via IRQ 13>");
+ else if (npx_exists)
+ printf(" <Error reporting broken, using 387 emulator>");
+ else
+ printf(" <387 Emulator>");
+ npxinit(__INITIAL_NPXCW__);
+ return (1); /* XXX unused */
+}
+
+/*
+ * Initialize floating point unit.
+ */
+void
+npxinit(control)
+ u_int control;
+{
+ struct save87 dummy;
+
+ if (!npx_exists)
+ return;
+ /*
+ * fninit has the same h/w bugs as fnsave. Use the detoxified
+ * fnsave to throw away any junk in the fpu. fnsave initializes
+ * the fpu and sets npxproc = NULL as important side effects.
+ */
+ npxsave(&dummy);
+ stop_emulating();
+ fldcw(&control);
+ if (curpcb != NULL)
+ fnsave(&curpcb->pcb_savefpu);
+ start_emulating();
+}
+
+/*
+ * Free coprocessor (if we have it).
+ */
+void
+npxexit(p)
+ struct proc *p;
+{
+
+ if (p == npxproc) {
+ start_emulating();
+ npxproc = NULL;
+ }
+}
+
+/*
+ * Record the FPU state and reinitialize it all except for the control word.
+ * Then generate a SIGFPE.
+ *
+ * Reinitializing the state allows naive SIGFPE handlers to longjmp without
+ * doing any fixups.
+ *
+ * XXX there is currently no way to pass the full error state to signal
+ * handlers, and if this is a nested interrupt there is no way to pass even
+ * a status code! So there is no way to have a non-naive SIGFPE handler. At
+ * best a handler could do an fninit followed by an fldcw of a static value.
+ * fnclex would be of little use because it would leave junk on the FPU stack.
+ * Returning from the handler would be even less safe than usual because
+ * IRQ13 exception handling makes exceptions even less precise than usual.
+ */
+void
+npxintr(frame)
+ struct intrframe frame;
+{
+ int code;
+
+ if (npxproc == NULL || !npx_exists) {
+ /* XXX no %p in stand/printf.c. Cast to quiet gcc -Wall. */
+ printf("npxintr: npxproc = %lx, curproc = %lx, npx_exists = %d\n",
+ (u_long) npxproc, (u_long) curproc, npx_exists);
+ panic("npxintr from nowhere");
+ }
+ if (npxproc != curproc) {
+ printf("npxintr: npxproc = %lx, curproc = %lx, npx_exists = %d\n",
+ (u_long) npxproc, (u_long) curproc, npx_exists);
+ panic("npxintr from non-current process");
+ }
+ /*
+ * Save state. This does an implied fninit. It had better not halt
+ * the cpu or we'll hang.
+ */
+ outb(0xf0, 0);
+ fnsave(&curpcb->pcb_savefpu);
+ fwait();
+ /*
+ * Restore control word (was clobbered by fnsave).
+ */
+ fldcw(&curpcb->pcb_savefpu.sv_env.en_cw);
+ fwait();
+ /*
+ * Remember the exception status word and tag word. The current
+ * (almost fninit'ed) fpu state is in the fpu and the exception
+ * state just saved will soon be junk. However, the implied fninit
+ * doesn't change the error pointers or register contents, and we
+ * preserved the control word and will copy the status and tag
+ * words, so the complete exception state can be recovered.
+ */
+ curpcb->pcb_savefpu.sv_ex_sw = curpcb->pcb_savefpu.sv_env.en_sw;
+ curpcb->pcb_savefpu.sv_ex_tw = curpcb->pcb_savefpu.sv_env.en_tw;
+
+ /*
+ * Pass exception to process.
+ */
+ if (ISPL(frame.if_cs) == SEL_UPL) {
+ /*
+ * Interrupt is essentially a trap, so we can afford to call
+ * the SIGFPE handler (if any) as soon as the interrupt
+ * returns.
+ *
+ * XXX little or nothing is gained from this, and plenty is
+ * lost - the interrupt frame has to contain the trap frame
+ * (this is otherwise only necessary for the rescheduling trap
+ * in doreti, and the frame for that could easily be set up
+ * just before it is used).
+ */
+ curproc->p_regs = (int *)&frame.if_es;
+ curpcb->pcb_flags |= FM_TRAP; /* used by sendsig */
+#ifdef notyet
+ /*
+ * Encode the appropriate code for detailed information on
+ * this exception.
+ */
+ code = XXX_ENCODE(curpcb->pcb_savefpu.sv_ex_sw);
+#else
+ code = 0; /* XXX */
+#endif
+ trapsignal(curproc, SIGFPE, code);
+ curpcb->pcb_flags &= ~FM_TRAP;
+ } else {
+ /*
+ * Nested interrupt. These losers occur when:
+ * o an IRQ13 is bogusly generated at a bogus time, e.g.:
+ * o immediately after an fnsave or frstor of an
+ * error state.
+ * o a couple of 386 instructions after
+ * "fstpl _memvar" causes a stack overflow.
+ * These are especially nasty when combined with a
+ * trace trap.
+ * o an IRQ13 occurs at the same time as another higher-
+ * priority interrupt.
+ *
+ * Treat them like a true async interrupt.
+ */
+ psignal(npxproc, SIGFPE);
+ }
+}
+
+/*
+ * Implement device not available (DNA) exception
+ *
+ * It would be better to switch FP context here (only). This would require
+ * saving the state in the proc table instead of in the pcb.
+ */
+int
+npxdna()
+{
+ if (!npx_exists)
+ return (0);
+ if (npxproc != NULL) {
+ printf("npxdna: npxproc = %lx, curproc = %lx\n",
+ (u_long) npxproc, (u_long) curproc);
+ panic("npxdna");
+ }
+ stop_emulating();
+ /*
+ * Record new context early in case frstor causes an IRQ13.
+ */
+ npxproc = curproc;
+ /*
+ * The following frstor may cause an IRQ13 when the state being
+ * restored has a pending error. The error will appear to have been
+ * triggered by the current (npx) user instruction even when that
+ * instruction is a no-wait instruction that should not trigger an
+ * error (e.g., fnclex). On at least one 486 system all of the
+ * no-wait instructions are broken the same as frstor, so our
+ * treatment does not amplify the breakage. On at least one
+ * 386/Cyrix 387 system, fnclex works correctly while frstor and
+ * fnsave are broken, so our treatment breaks fnclex if it is the
+ * first FPU instruction after a context switch.
+ */
+ frstor(&curpcb->pcb_savefpu);
+
+ return (1);
+}
+
+/*
+ * Wrapper for fnsave instruction to handle h/w bugs. If there is an error
+ * pending, then fnsave generates a bogus IRQ13 on some systems. Force
+ * any IRQ13 to be handled immediately, and then ignore it. This routine is
+ * often called at splhigh so it must not use many system services. In
+ * particular, it's much easier to install a special handler than to
+ * guarantee that it's safe to use npxintr() and its supporting code.
+ */
+void
+npxsave(addr)
+ struct save87 *addr;
+{
+ u_char icu1_mask;
+ u_char icu2_mask;
+ u_char old_icu1_mask;
+ u_char old_icu2_mask;
+ struct gate_descriptor save_idt_npxintr;
+
+ disable_intr();
+ old_icu1_mask = inb(IO_ICU1 + 1);
+ old_icu2_mask = inb(IO_ICU2 + 1);
+ save_idt_npxintr = idt[npx_intrno];
+ outb(IO_ICU1 + 1, old_icu1_mask & ~(IRQ_SLAVE | npx0mask));
+ outb(IO_ICU2 + 1, old_icu2_mask & ~(npx0mask >> 8));
+ idt[npx_intrno] = npx_idt_probeintr;
+ enable_intr();
+ stop_emulating();
+ fnsave(addr);
+ fwait();
+ start_emulating();
+ npxproc = NULL;
+ disable_intr();
+ icu1_mask = inb(IO_ICU1 + 1); /* masks may have changed */
+ icu2_mask = inb(IO_ICU2 + 1);
+ outb(IO_ICU1 + 1,
+ (icu1_mask & ~npx0mask) | (old_icu1_mask & npx0mask));
+ outb(IO_ICU2 + 1,
+ (icu2_mask & ~(npx0mask >> 8))
+ | (old_icu2_mask & (npx0mask >> 8)));
+ idt[npx_intrno] = save_idt_npxintr;
+ enable_intr(); /* back to usual state */
+}
+
+#endif /* NNPX > 0 */
diff --git a/sys/i386/isa/rtc.h b/sys/i386/isa/rtc.h
new file mode 100644
index 0000000..2de060d
--- /dev/null
+++ b/sys/i386/isa/rtc.h
@@ -0,0 +1,85 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * 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.
+ *
+ * @(#)rtc.h 7.1 (Berkeley) 5/12/91
+ */
+
+/*
+ * RTC Register locations
+ */
+
+#define RTC_SEC 0x00 /* seconds */
+#define RTC_SECALRM 0x01 /* seconds alarm */
+#define RTC_MIN 0x02 /* minutes */
+#define RTC_MINALRM 0x03 /* minutes alarm */
+#define RTC_HRS 0x04 /* hours */
+#define RTC_HRSALRM 0x05 /* hours alarm */
+#define RTC_WDAY 0x06 /* week day */
+#define RTC_DAY 0x07 /* day of month */
+#define RTC_MONTH 0x08 /* month of year */
+#define RTC_YEAR 0x09 /* month of year */
+#define RTC_STATUSA 0x0a /* status register A */
+#define RTCSA_TUP 0x80 /* time update, don't look now */
+
+#define RTC_STATUSB 0x0b /* status register B */
+
+#define RTC_INTR 0x0c /* status register C (R) interrupt source */
+#define RTCIR_UPDATE 0x10 /* update intr */
+#define RTCIR_ALARM 0x20 /* alarm intr */
+#define RTCIR_PERIOD 0x40 /* periodic intr */
+#define RTCIR_INT 0x80 /* interrupt output signal */
+
+#define RTC_STATUSD 0x0d /* status register D (R) Lost Power */
+#define RTCSD_PWR 0x80 /* clock lost power */
+
+#define RTC_DIAG 0x0e /* status register E - bios diagnostic */
+#define RTCDG_BITS "\020\010clock_battery\007ROM_cksum\006config_unit\005memory_size\004fixed_disk\003invalid_time"
+
+#define RTC_RESET 0x0f /* status register F - reset code byte */
+#define RTCRS_RST 0x00 /* normal reset */
+#define RTCRS_LOAD 0x04 /* load system */
+
+#define RTC_FDISKETTE 0x10 /* diskette drive type in upper/lower nibble */
+#define RTCFDT_NONE 0 /* none present */
+#define RTCFDT_360K 0x10 /* 360K */
+#define RTCFDT_12M 0x20 /* 1.2M */
+#define RTCFDT_144M 0x40 /* 1.44M */
+
+#define RTC_BASELO 0x15 /* low byte of basemem size */
+#define RTC_BASEHI 0x16 /* high byte of basemem size */
+#define RTC_EXTLO 0x17 /* low byte of extended mem size */
+#define RTC_EXTHI 0x18 /* low byte of extended mem size */
+
+#define RTC_CENTURY 0x32 /* current century - please increment in Dec99*/
diff --git a/sys/i386/isa/sio.c b/sys/i386/isa/sio.c
new file mode 100644
index 0000000..38503fd
--- /dev/null
+++ b/sys/i386/isa/sio.c
@@ -0,0 +1,1721 @@
+/*-
+ * 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.
+ *
+ * @(#)sio.c 7.5 (Berkeley) 5/16/91
+ *
+ * 27 May 93 Bruce Evans From com-0.2 package, fast interrupt
+ * com port driver.
+ * 27 May 93 Guido van Rooij Ported in Chris Demetriou's BIDIR
+ * code, add multiport support.
+ * 27 May 93 Rodney W. Grimes I then renamed it to sio.c for putting
+ * into the patch kit. Added in sioselect
+ * from com.c. Added port 4 support.
+ */
+static char rcsid[] = "$Header: /usr/bill/working/sys/i386/isa/RCS/com.c,v 1.2 92/01/21 14:34:11 william Exp $";
+
+#include "sio.h"
+#if NSIO > 0
+/*
+ * COM driver, based on HP dca driver.
+ * Mostly rewritten to use pseudo-DMA.
+ * Works for National Semiconductor NS8250-NS16550AF UARTs.
+ */
+#include "param.h"
+#include "systm.h"
+#include "ioctl.h"
+#include "tty.h"
+#include "proc.h"
+#include "user.h"
+#include "conf.h"
+#include "file.h"
+#include "uio.h"
+#include "kernel.h"
+#include "syslog.h"
+
+#include "i386/isa/isa.h"
+#include "i386/isa/isa_device.h"
+#include "i386/isa/comreg.h"
+#include "i386/isa/ic/ns16550.h"
+
+#undef CRTS_IFLOW
+#define CRTS_IFLOW CRTSCTS /* XXX, CCTS_OFLOW = CRTSCTS already */
+#define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */
+#define RB_I_HIGH_WATER (RBSZ - 2 * RS_IBUFSIZE)
+#define RB_I_LOW_WATER ((RBSZ - 2 * RS_IBUFSIZE) * 7 / 8)
+#define RS_IBUFSIZE 256
+#define TS_RTSBLOCK TS_TBLOCK /* XXX */
+#define TTY_BI TTY_FE /* XXX */
+#define TTY_OE TTY_PE /* XXX */
+#ifndef COM_BIDIR
+#define UNIT(x) (minor(x)) /* XXX */
+#else /* COM_BIDIR */
+#define COM_UNITMASK 0x7f
+#define COM_CALLOUTMASK 0x80
+
+#define UNIT(x) (minor(x) & COM_UNITMASK)
+#define CALLOUT(x) (minor(x) & COM_CALLOUTMASK)
+#endif /* COM_BIDIR */
+
+#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)
+#endif /* COM_MULTIPORT */
+
+#define com_scr 7 /* scratch register for 16450-16550 (R/W) */
+#define schedsoftcom() (ipending |= 1 << 4) /* XXX */
+
+/*
+ * 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:
+ * TS_BUSY = CS_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.
+ * Bug: I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON.
+ */
+#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_ODONE 4 /* output completed */
+#define CS_RTS_IFLOW 8 /* use RTS input flow control */
+
+static char *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 int Bool_t; /* promoted boolean */
+typedef u_char bool_t; /* boolean */
+
+/* com device structure */
+struct com_s {
+ u_char state; /* miscellaneous flag bits */
+ u_char cfcr_image; /* copy of value written to CFCR */
+ bool_t hasfifo; /* nonzero for 16550 UARTs */
+ u_char mcr_image; /* copy of value written to MCR */
+ bool_t softDCD; /* nonzero for faked carrier detect */
+#ifdef COM_BIDIR
+ bool_t bidir; /* is this unit bidirectional? */
+ bool_t active; /* is the port active _at all_? */
+ bool_t active_in; /* is the incoming port in use? */
+ bool_t active_out; /* is the outgoing port in use? */
+#endif /* COM_BIDIR */
+#ifdef COM_MULTIPORT
+ bool_t multiport; /* is this unit part of a multiport device? */
+#endif /* COM_MULTIPORT */
+
+ /*
+ * 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 *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 */
+ int ocount; /* original count for current output */
+ 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 */
+
+ u_long bytes_in; /* statistics */
+ u_long bytes_out;
+ u_int delta_error_counts[CE_NTYPES];
+ u_int 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];
+};
+
+
+/*
+ * These functions in the com module ought to be declared (with a prototype)
+ * in a com-driver system header. The void ones may need to be int to match
+ * ancient devswitch declarations, but they don't actually return anything.
+ */
+#define Dev_t int /* promoted dev_t */
+struct consdev;
+
+int sioclose __P((Dev_t dev, int fflag, int devtype,
+ struct proc *p));
+void siointr __P((int unit));
+#ifdef COM_MULTIPORT
+bool_t comintr1 __P((struct com_s *com));
+#endif /* COM_MULTIPORT */
+int sioioctl __P((Dev_t dev, int cmd, caddr_t data,
+ int fflag, struct proc *p));
+int siocngetc __P((Dev_t dev));
+void siocninit __P((struct consdev *cp));
+void siocnprobe __P((struct consdev *cp));
+void siocnputc __P((Dev_t dev, int c));
+int sioopen __P((Dev_t dev, int oflags, int devtype,
+ struct proc *p));
+/*
+ * sioopen gets compared to the d_open entry in struct cdevsw. d_open and
+ * other functions are declared in <sys/conf.h> with short types like dev_t
+ * in the prototype. Such declarations are broken because they vary with
+ * __P (significantly in theory - the compiler is allowed to push a short
+ * arg if it has seen the prototype; insignificantly in practice - gcc
+ * doesn't push short args and it would be slower on 386's to do so).
+ *
+ * Also, most of the device switch functions are still declared old-style
+ * so they take a Dev_t arg and shorten it to a dev_t. It would be simpler
+ * and faster if dev_t's were always promoted (to ints or whatever) as
+ * early as possible.
+ *
+ * Until <sys/conf.h> is fixed, we cast sioopen to the following `wrong' type
+ * when comparing it to the d_open entry just to avoid compiler warnings.
+ */
+typedef int (*bogus_open_t) __P((dev_t dev, int oflags, int devtype,
+ struct proc *p));
+int sioread __P((Dev_t dev, struct uio *uio, int ioflag));
+void siostop __P((struct tty *tp, int rw));
+int siowrite __P((Dev_t dev, struct uio *uio, int ioflag));
+void softsio0 __P((void));
+void softsio1 __P((void));
+void softsio2 __P((void));
+void softsio3 __P((void));
+void softsio4 __P((void));
+void softsio5 __P((void));
+void softsio6 __P((void));
+void softsio7 __P((void));
+void softsio8 __P((void));
+
+static int sioattach __P((struct isa_device *dev));
+static void comflush __P((struct com_s *com));
+static void comhardclose __P((struct com_s *com));
+static void cominit __P((int unit, int rate));
+static int 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 compoll __P((void));
+static int comstart __P((struct tty *tp));
+static void comwakeup __P((void));
+
+/* 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 com_s com_structs[NSIO];
+
+struct isa_driver siodriver = {
+ sioprobe, sioattach, "sio"
+};
+
+#ifdef COMCONSOLE
+static int comconsole = COMCONSOLE;
+#else
+static int comconsole = -1;
+#endif
+static bool_t comconsinit;
+static speed_t comdefaultrate = TTYDEF_SPEED;
+static u_int com_events; /* input chars + weighted output completions */
+static int commajor;
+struct tty sio_tty[NSIO];
+extern struct tty *constty;
+extern u_int ipending; /* XXX */
+extern int tk_nin; /* XXX */
+extern int tk_rawcc; /* XXX */
+
+#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 int
+sioprobe(dev)
+ struct isa_device *dev;
+{
+ static bool_t already_init;
+ Port_t *com_ptr;
+ Port_t iobase;
+ int result;
+
+ 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.
+ */
+ 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);
+ already_init = TRUE;
+ }
+ iobase = dev->id_iobase;
+ result = 1;
+
+ /*
+ * 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();
+
+ /*
+ * Enable output interrupts (only) and check the following:
+ * 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.
+ */
+ outb(iobase + com_cfcr, CFCR_8BITS); /* ensure IER is addressed */
+ outb(iobase + com_mcr, MCR_IENABLE); /* open gate early */
+ outb(iobase + com_ier, 0); /* ensure edge on next intr */
+ outb(iobase + com_ier, IER_ETXRDY); /* generate interrupt */
+ if ( inb(iobase + com_cfcr) != CFCR_8BITS
+ || inb(iobase + com_ier) != IER_ETXRDY
+ || inb(iobase + com_mcr) != MCR_IENABLE
+ || !isa_irq_pending(dev)
+ || (inb(iobase + com_iir) & IIR_IMASK) != IIR_TXRDY
+ || isa_irq_pending(dev)
+ || (inb(iobase + com_iir) & IIR_IMASK) != IIR_NOPEND)
+ result = 0;
+
+ /*
+ * Turn off all device interrupts and check that they go off properly.
+ * Leave MCR_IENABLE set. 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_mcr, MCR_IENABLE); /* dummy to avoid bus echo */
+ if ( inb(iobase + com_ier) != 0
+ || isa_irq_pending(dev)
+ || (inb(iobase + com_iir) & IIR_IMASK) != IIR_NOPEND)
+ result = 0;
+
+ enable_intr();
+
+ return (result);
+}
+
+static int /* XXX - should be void */
+sioattach(isdp)
+ struct isa_device *isdp;
+{
+ struct com_s *com;
+ static bool_t comwakeup_started = FALSE;
+ Port_t iobase;
+ int s;
+ u_char scr;
+ u_char scr1;
+ u_char scr2;
+ int unit;
+
+ iobase = isdp->id_iobase;
+ unit = isdp->id_unit;
+ if (unit == comconsole)
+ DELAY(1000); /* XXX */
+ s = spltty();
+
+ /*
+ * 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.
+ * Keeping MCR_DTR and MCR_RTS off might stop the external
+ * device from sending before we are ready.
+ */
+
+ com = &com_structs[unit];
+ com->cfcr_image = CFCR_8BITS;
+ com->mcr_image = MCR_IENABLE;
+#if 0
+ com->softDCD = TRUE;
+#endif
+ 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->line_status_port = iobase + com_lsr;
+ com->modem_status_port = iobase + com_msr;
+ com->tp = &sio_tty[unit];
+#ifdef COM_BIDIR
+ /*
+ * if bidirectional ports possible, clear the bidir port info;
+ */
+ com->bidir = FALSE;
+ com->active = FALSE;
+ com->active_in = com->active_out = FALSE;
+#endif /* COM_BIDIR */
+
+ /* attempt to determine UART type */
+ 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>");
+ else {
+ outb(iobase + com_fifo, FIFO_ENABLE | FIFO_TRIGGER_14);
+ DELAY(100);
+ switch (inb(iobase + com_iir) & IIR_FIFO_MASK) {
+ case FIFO_TRIGGER_1:
+ printf(" <16450>");
+ break;
+ case FIFO_TRIGGER_4:
+ printf(" <16450?>");
+ break;
+ case FIFO_TRIGGER_8:
+ printf(" <16550?>");
+ break;
+ case FIFO_TRIGGER_14:
+ com->hasfifo = TRUE;
+ printf(" <16550A>");
+ break;
+ }
+ outb(iobase + com_fifo, 0);
+ }
+#ifdef COM_MULTIPORT
+ if (COM_ISMULTIPORT(isdp)) {
+ struct isa_device *masterdev;
+
+ com->multiport = TRUE;
+ printf(" (multiport)");
+
+ /* set the master's common-interrupt-enable reg.,
+ * as appropriate. YYY See your manual
+ */
+ /* enable only common interrupt for port */
+ outb(iobase + com_mcr, 0);
+
+ masterdev = find_isadev(isa_devtab_tty, &siodriver,
+ COM_MPMASTER(isdp));
+ outb(masterdev->id_iobase+com_scr, 0x80);
+ }
+ else
+ com->multiport = FALSE;
+#endif /* COM_MULTIPORT */
+
+#ifdef KGDB
+ if (kgdb_dev == makedev(commajor, unit)) {
+ if (comconsole == unit)
+ kgdb_dev = -1; /* can't debug over console port */
+ else {
+ cominit(unit, kgdb_rate);
+ if (kgdb_debug_init) {
+ /*
+ * Print prefix of device name,
+ * let kgdb_connect print the rest.
+ */
+ printf("com%d: ", unit);
+ kgdb_connect(1);
+ }
+ else
+ printf("com%d: kgdb enabled\n", unit);
+ }
+ }
+#endif
+
+ /*
+ * Need to reset baud rate, etc. of next print so reset comconsinit.
+ * Also make sure console is always "hardwired"
+ */
+ if (unit == comconsole) {
+ comconsinit = FALSE;
+ com->softDCD = TRUE;
+ }
+
+ com_addr(unit) = com;
+
+ splx(s);
+
+ if (!comwakeup_started) {
+ comwakeup();
+ 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 = 0;
+ Port_t iobase;
+ int s;
+ struct tty *tp;
+ int unit = UNIT(dev);
+#ifdef COM_BIDIR
+ bool_t callout = CALLOUT(dev);
+#endif /* COM_BIDIR */
+ if ((u_int) unit >= NSIO || (com = com_addr(unit)) == NULL)
+ return (ENXIO);
+#ifdef COM_BIDIR
+ /* if it's a callout device, and bidir not possible on that dev, die */
+ if (callout && !(com->bidir))
+ return (ENXIO);
+#endif /* COM_BIDIR */
+
+ tp = com->tp;
+ s = spltty();
+
+#ifdef COM_BIDIR
+
+bidir_open_top:
+ /* if it's bidirectional, we've gotta deal with it... */
+ if (com->bidir) {
+ if (callout) {
+ if (com->active_in) {
+ /* it's busy. die */
+ splx(s);
+ return (EBUSY);
+ } else {
+ /* it's ours. lock it down, and set it up */
+ com->active_out = TRUE;
+ com->softDCD = TRUE;
+ }
+ } else {
+ if (com->active_out) {
+ /* it's busy, outgoing. wait, if possible */
+ if (flag & O_NONBLOCK) {
+ /* can't wait; bail */
+ splx(s);
+ return (EBUSY);
+ } else {
+ /* wait for it... */
+ error = tsleep(&com->active_out,
+ TTIPRI|PCATCH,
+ "comoth",
+ 0);
+ /* if there was an error, take off. */
+ if (error != 0) {
+ splx(s);
+ return (error);
+ }
+ /* else take it from the top */
+ goto bidir_open_top;
+ }
+ } else if (com->last_modem_status & MSR_DCD) {
+ /* there's a carrier on the line; we win */
+ com->active_in = TRUE;
+ com->softDCD = FALSE;
+ } else {
+ /* there is no carrier on the line */
+ if (flag & O_NONBLOCK) {
+ /* can't wait; let it open */
+ com->active_in = TRUE;
+ com->softDCD = FALSE;
+ } else {
+ /* put DTR & RTS up */
+ /* NOTE: cgd'sdriver used the ier register
+ * to enable/disable interrupts. This one
+ * uses both ier and IENABLE in the mcr.
+ */
+ (void) commctl(com, MCR_DTR | MCR_RTS, DMSET);
+ outb(com->iobase + com_ier, IER_EMSC);
+ /* wait for it... */
+ error = tsleep(&com->active_in,
+ TTIPRI|PCATCH,
+ "comdcd",
+ 0);
+
+ /* if not active, turn DTR & RTS off */
+ if (!com->active)
+ (void) commctl(com, MCR_DTR | MCR_RTS, DMBIC);
+
+ /* if there was an error, take off. */
+ if (error != 0) {
+ splx(s);
+ return (error);
+ }
+ /* else take it from the top */
+ goto bidir_open_top;
+ }
+ }
+ }
+ }
+
+ com->active = TRUE;
+#endif /* COM_BIDIR */
+
+ tp->t_oproc = comstart;
+ tp->t_param = comparam;
+ tp->t_dev = dev;
+ if (!(tp->t_state & TS_ISOPEN)) {
+ tp->t_state |= TS_WOPEN;
+ ttychars(tp);
+ if (tp->t_ispeed == 0) {
+ /*
+ * We no longer use the flags from <sys/ttydefaults.h>
+ * since those 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. It's useful to have clocal on
+ * initially so that "stty changed-defaults </dev/comx"
+ * doesn't hang waiting for carrier.
+ */
+ tp->t_iflag = 0;
+ tp->t_oflag = 0;
+ tp->t_cflag = CREAD | CS8 | CLOCAL;
+ tp->t_lflag = 0;
+ tp->t_ispeed = tp->t_ospeed = comdefaultrate;
+ }
+ (void) commctl(com, MCR_DTR | MCR_RTS, DMSET);
+ error = comparam(tp, &tp->t_termios);
+ if (error != 0)
+ goto out;
+ ttsetwater(tp);
+ iobase = com->iobase;
+ disable_intr();
+ if (com->hasfifo)
+ /* (re)enable and drain FIFO */
+ outb(iobase + com_fifo, FIFO_ENABLE | FIFO_TRIGGER_14
+ | FIFO_RCV_RST | FIFO_XMT_RST);
+ (void) inb(com->line_status_port);
+ (void) inb(com->data_port);
+ com->last_modem_status =
+ com->prev_modem_status = inb(com->modem_status_port);
+ outb(iobase + com_ier, IER_ERXRDY | IER_ETXRDY | IER_ERLS
+ | IER_EMSC);
+ enable_intr();
+ if (com->softDCD || com->prev_modem_status & MSR_DCD)
+ tp->t_state |= TS_CARR_ON;
+ }
+ else if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) {
+ splx(s);
+ return (EBUSY);
+ }
+ while (!(flag & O_NONBLOCK) && !(tp->t_cflag & CLOCAL)
+#ifdef COM_BIDIR
+ /* We went through a lot of trouble to open it,
+ * but it's certain we have a carrier now, so
+ * don't spend any time on it now.
+ */
+ && !(com->bidir)
+#endif /* COM_BIDIR */
+ && !(tp->t_state & TS_CARR_ON)) {
+ tp->t_state |= TS_WOPEN;
+ error = ttysleep(tp, (caddr_t)&tp->t_raw, TTIPRI | PCATCH,
+ ttopen, 0);
+ if (error != 0)
+ break;
+ }
+out:
+ splx(s);
+ if (error == 0)
+ error = (*linesw[tp->t_line].l_open)(dev, tp);
+
+#ifdef COM_BIDIR
+ /* wakeup sleepers */
+ wakeup((caddr_t) &com->active_in);
+#endif /* COM_BIDIR */
+
+ /*
+ * XXX - the next step was once not done, so interrupts, DTR and RTS
+ * remainded hot if the process was killed while it was sleeping
+ * waiting for carrier. Now there is the opposite problem. If several
+ * processes are sleeping waiting for carrier on the same line and one
+ * is killed, interrupts are turned off so the other processes will
+ * never see the carrier rise.
+ */
+ if (error != 0 && !(tp->t_state & TS_ISOPEN))
+{
+ comhardclose(com);
+}
+ tp->t_state &= ~TS_WOPEN;
+
+ return (error);
+}
+
+/*ARGSUSED*/
+int
+sioclose(dev, flag, mode, p)
+ dev_t dev;
+ int flag;
+ int mode;
+ struct proc *p;
+{
+ struct com_s *com;
+ struct tty *tp;
+
+ com = com_addr(UNIT(dev));
+ tp = com->tp;
+ (*linesw[tp->t_line].l_close)(tp, flag);
+ comhardclose(com);
+ ttyclose(tp);
+ return (0);
+}
+
+void
+comhardclose(com)
+ struct com_s *com;
+{
+ Port_t iobase;
+ int s;
+ struct tty *tp;
+
+ s = spltty();
+ iobase = com->iobase;
+ outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK);
+#ifdef KGDB
+ /* do not disable interrupts if debugging */
+ if (kgdb_dev != makedev(commajor, com - &com_structs[0]))
+#endif
+ outb(iobase + com_ier, 0);
+ tp = com->tp;
+ if (tp->t_cflag & HUPCL || tp->t_state & TS_WOPEN
+ || !(tp->t_state & TS_ISOPEN))
+ (void) commctl(com, 0, DMSET);
+#ifdef COM_BIDIR
+ com->active = com->active_in = com->active_out = FALSE;
+ com->softDCD = FALSE;
+
+ /* wakeup sleepers who are waiting for out to finish */
+ wakeup((caddr_t) &com->active_out);
+#endif /* COM_BIDIR */
+
+ splx(s);
+}
+
+int
+sioread(dev, uio, flag)
+ dev_t dev;
+ struct uio *uio;
+ int flag;
+{
+ struct tty *tp = com_addr(UNIT(dev))->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 unit = UNIT(dev);
+ struct tty *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)
+ constty = NULL;
+ return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
+}
+
+void
+siointr(unit)
+ int unit;
+{
+ struct com_s *com;
+#ifndef COM_MULTIPORT
+ u_char line_status;
+ u_char modem_status;
+ u_char *ioptr;
+ u_char recv_data;
+
+ com = com_addr(unit);
+#else /* COM_MULTIPORT */
+ int i;
+ bool_t donesomething;
+
+ do {
+ donesomething = FALSE;
+ for(i=0;i<NSIO;i++) {
+ com=com_addr(i);
+ if(com != NULL) {
+ /* XXX When siointr is called
+ * to start output, maybe
+ * it should be changed to a
+ * call to comintr1. Doesn't
+ * seem a good idea: interrupts
+ * are disabled all the time.
+ */
+enable_intr();
+disable_intr();
+ donesomething = comintr1(com);
+ }
+ }
+ } while (donesomething);
+ return;
+}
+
+bool_t
+comintr1(struct com_s *com)
+{
+ u_char line_status;
+ u_char modem_status;
+ u_char *ioptr;
+ u_char recv_data;
+ bool_t donesomething;
+
+ donesomething = FALSE;
+#endif /* COM_MULTIPORT */
+
+ 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? */
+#ifdef COM_MULTIPORT
+ donesomething = TRUE;
+#endif /* COM_MULTIPORT */
+ if (!(line_status & LSR_RXRDY))
+ recv_data = 0;
+ else
+ recv_data = inb(com->data_port);
+ ++com->bytes_in;
+#ifdef KGDB
+ /* trap into kgdb? (XXX - needs testing and optim) */
+ if (recv_data == FRAME_END
+ && !(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;
+ 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);
+ }
+
+ /*
+ * "& 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.
+ */
+#ifdef COM_MULTIPORT
+ donesomething = TRUE;
+#endif /* COM_MULTIPORT */
+ com->last_modem_status = modem_status;
+ if (!(com->state & CS_CHECKMSR)) {
+ com_events += LOTS_OF_EVENTS;
+ com->state |= CS_CHECKMSR;
+ schedsoftcom();
+ }
+
+ /* 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)) {
+#ifdef COM_MULTIPORT
+ donesomething = TRUE;
+#endif /* COM_MULTIPORT */
+ ioptr = com->optr;
+ 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);
+ schedsoftcom(); /* handle at high level ASAP */
+ }
+ }
+
+ /* finished? */
+ if ((inb(com->int_id_port) & IIR_IMASK) == IIR_NOPEND)
+#ifdef COM_MULTIPORT
+ return (donesomething);
+#else
+ return;
+#endif /* COM_MULTIPORT */
+ }
+}
+
+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 s;
+ struct tty *tp;
+
+ com = com_addr(UNIT(dev));
+ tp = com->tp;
+ error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag);
+ if (error >= 0)
+ return (error);
+ error = ttioctl(tp, cmd, data, flag);
+ if (error >= 0)
+ return (error);
+
+ iobase = com->iobase;
+ s = spltty();
+ 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:
+ (void) commctl(com, MCR_DTR | MCR_RTS, DMBIS);
+ break;
+ case TIOCCDTR:
+ (void) commctl(com, MCR_DTR | MCR_RTS, DMBIC);
+ break;
+ case TIOCMSET:
+ (void) commctl(com, *(int *)data, DMSET);
+ break;
+ case TIOCMBIS:
+ (void) commctl(com, *(int *)data, DMBIS);
+ break;
+ case TIOCMBIC:
+ (void) commctl(com, *(int *)data, DMBIC);
+ break;
+ case TIOCMGET:
+ *(int *)data = commctl(com, 0, DMGET);
+ break;
+#ifdef COM_BIDIR
+ case TIOCMSBIDIR:
+ /* must be root to set bidir. capability */
+ if (p->p_ucred->cr_uid != 0)
+ return(EPERM);
+
+ /* if it's the console, can't do it */
+ if (UNIT(dev) == comconsole)
+ return(ENOTTY);
+
+ /* can't do the next, for obvious reasons...
+ * but there are problems to be looked at...
+ */
+
+ /* if the port is active, don't do it */
+ /* if (com->active)
+ return(EBUSY); */
+
+ com->bidir = *(int *)data;
+ break;
+ case TIOCMGBIDIR:
+ *(int *)data = com->bidir;
+ break;
+#endif /* COM_BIDIR */
+ default:
+ splx(s);
+ return (ENOTTY);
+ }
+ splx(s);
+ return (0);
+}
+
+/* cancel pending output */
+static void
+comflush(com)
+ struct com_s *com;
+{
+ struct ringb *rbp;
+
+ disable_intr();
+ if (com->state & CS_ODONE)
+ com_events -= LOTS_OF_EVENTS;
+ com->state &= ~(CS_ODONE | CS_BUSY);
+ enable_intr();
+ rbp = &com->tp->t_out;
+ rbp->rb_hd += com->ocount;
+ rbp->rb_hd = RB_ROLLOVER(rbp, rbp->rb_hd);
+ com->ocount = 0;
+ com->tp->t_state &= ~TS_BUSY;
+}
+
+static void
+compoll()
+{
+ static bool_t awake = FALSE;
+ struct com_s *com;
+ int s;
+ int unit;
+
+ if (com_events == 0)
+ return;
+ disable_intr();
+ if (awake) {
+ enable_intr();
+ return;
+ }
+ awake = TRUE;
+ enable_intr();
+ s = spltty();
+repeat:
+ for (unit = 0; unit < NSIO; ++unit) {
+ u_char *buf;
+ u_char *ibuf;
+ int incc;
+ struct tty *tp;
+
+ com = com_addr(unit);
+ if (com == NULL)
+ continue;
+ tp = com->tp;
+
+ /* switch the role of the low-level input buffers */
+ if (com->iptr == (ibuf = com->ibuf))
+ 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.
+ */
+ if (!(com->mcr_image & MCR_RTS)
+ && !(tp->t_state & TS_RTSBLOCK))
+ 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 &&
+ unit != comconsole) {
+#ifdef COM_BIDIR
+ if (com->prev_modem_status & MSR_DCD) {
+ (*linesw[tp->t_line].l_modem)(tp, 1);
+ com->softDCD = FALSE;
+ wakeup((caddr_t) &com->active_in);
+ }
+#else
+ if (com->prev_modem_status & MSR_DCD)
+ (*linesw[tp->t_line].l_modem)(tp, 1);
+#endif /* COM_BIDIR */
+ else if ((*linesw[tp->t_line].l_modem)(tp, 0)
+ == 0) {
+ disable_intr();
+ outb(com->modem_ctl_port,
+ com->mcr_image
+ &= ~(MCR_DTR | MCR_RTS));
+ enable_intr();
+ }
+ }
+ }
+
+ /* XXX */
+ if (TRUE) {
+ u_int delta;
+ u_int delta_error_counts[CE_NTYPES];
+ int errnum;
+ u_long total;
+
+ disable_intr();
+ bcopy(com->delta_error_counts, delta_error_counts,
+ sizeof delta_error_counts);
+ bzero(com->delta_error_counts,
+ sizeof delta_error_counts);
+ enable_intr();
+ for (errnum = 0; errnum < CE_NTYPES; ++errnum) {
+ delta = delta_error_counts[errnum];
+ if (delta != 0) {
+ total =
+ com->error_counts[errnum] += delta;
+ log(LOG_WARNING,
+ "com%d: %u more %s%s (total %lu)\n",
+ unit, delta, error_desc[errnum],
+ delta == 1 ? "" : "s", total);
+ }
+ }
+ }
+ if (com->state & CS_ODONE) {
+ comflush(com);
+ /* XXX - why isn't the table used for t_line == 0? */
+ if (tp->t_line != 0)
+ (*linesw[tp->t_line].l_start)(tp);
+ else
+ comstart(tp);
+ }
+ if (incc <= 0 || !(tp->t_state & TS_ISOPEN))
+ continue;
+ if (com->state & CS_RTS_IFLOW
+ && RB_LEN(&tp->t_raw) + incc >= RB_I_HIGH_WATER
+ && !(tp->t_state & TS_RTSBLOCK)
+ /*
+ * XXX - need RTS flow control for all line disciplines.
+ * Only have it in standard one now.
+ */
+ && linesw[tp->t_line].l_rint == ttyinput) {
+ tp->t_state |= TS_RTSBLOCK;
+ ttstart(tp);
+ }
+ /*
+ * 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_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP
+ | IXOFF | IXON))
+ && !(tp->t_lflag & (ECHO | ECHONL | ICANON | IEXTEN | ISIG
+ | PENDIN))
+ && !(tp->t_state & (TS_CNTTB | TS_LNCH))
+ && linesw[tp->t_line].l_rint == ttyinput) {
+ tk_nin += incc;
+ tk_rawcc += incc;
+ tp->t_rawcc += incc;
+ com->delta_error_counts[CE_TTY_BUF_OVERFLOW]
+ += incc - rb_write(&tp->t_raw, (char *) buf,
+ incc);
+ 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;
+ splx(s);
+ awake = FALSE;
+}
+
+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 (divisor < 0 || t->c_ispeed != 0 && t->c_ispeed != t->c_ospeed)
+ return (EINVAL);
+
+ /* parameters are OK, convert them to the com struct and the device */
+ unit = UNIT(tp->t_dev);
+ com = com_addr(unit);
+ iobase = com->iobase;
+ s = spltty();
+ if (divisor == 0) {
+ (void) commctl(com, 0, DMSET); /* hang up line */
+ splx(s);
+ return (0);
+ }
+ 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;
+
+ /*
+ * 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 siointr() might lose some receiver
+ * error bits, but that is acceptable here.
+ */
+ disable_intr();
+ com->state &= ~CS_TTGO;
+ enable_intr();
+ while ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY))
+ != (LSR_TSRE | LSR_TXRDY)) {
+ error = ttysleep(tp, (caddr_t)&tp->t_raw, TTIPRI | PCATCH,
+ "comparam", 1);
+ 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 */
+ 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 16+ msec latency, while CTS flow has 50- usec latency.
+ * Note that DCD flow control stupidly uses the same state flag
+ * (TS_TTSTOP) as XON/XOFF flow control.
+ */
+ com->state &= ~CS_CTS_OFLOW;
+ com->state |= CS_ODEVREADY;
+ if (cflag & CCTS_OFLOW) {
+ com->state |= CS_CTS_OFLOW;
+ if (!(com->prev_modem_status & MSR_CTS))
+ com->state &= ~CS_ODEVREADY;
+ }
+
+ enable_intr();
+ siointr(unit); /* recover from fiddling with CS_TTGO */
+ splx(s);
+ return (0);
+}
+
+static int /* XXX - should be void */
+comstart(tp)
+ struct tty *tp;
+{
+ struct com_s *com;
+ int s;
+ int unit;
+
+ unit = 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_RTSBLOCK) {
+ if (com->mcr_image & MCR_RTS && com->state & CS_RTS_IFLOW)
+ outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS);
+ }
+ else {
+ 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 (RB_LEN(&tp->t_out) <= tp->t_lowat) {
+ if (tp->t_state & TS_ASLEEP) {
+ tp->t_state &= ~TS_ASLEEP;
+ wakeup((caddr_t)&tp->t_out);
+ }
+ if (tp->t_wsel) {
+ selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL);
+ tp->t_wsel = 0;
+ tp->t_state &= ~TS_WCOLL;
+ }
+ }
+ if (com->ocount != 0) {
+ disable_intr();
+ siointr(unit);
+ enable_intr();
+ }
+ else if (RB_LEN(&tp->t_out) != 0) {
+ tp->t_state |= TS_BUSY;
+ com->ocount = RB_CONTIGGET(&tp->t_out);
+ disable_intr();
+ com->obufend = (com->optr = (u_char *) tp->t_out.rb_hd)
+ + com->ocount;
+ com->state |= CS_BUSY;
+ siointr(unit); /* fake interrupt to start output */
+ enable_intr();
+ }
+out:
+ splx(s);
+ return (1);
+}
+
+void
+siostop(tp, rw)
+ struct tty *tp;
+ int rw;
+{
+ struct com_s *com;
+
+ com = com_addr(UNIT(tp->t_dev));
+ if (rw & FWRITE)
+ comflush(com);
+ disable_intr();
+ if (tp->t_state & TS_TTSTOP)
+ com->state &= ~CS_TTGO;
+ else
+ com->state |= CS_TTGO;
+ enable_intr();
+}
+
+static int
+commctl(com, bits, how)
+ struct com_s *com;
+ int bits;
+ int how;
+{
+ disable_intr();
+ switch (how) {
+ case DMSET:
+#ifdef COM_MULTIPORT
+ /* YYY maybe your card doesn't want IENABLE to be reset? */
+ if(com->multiport)
+ outb(com->modem_ctl_port,
+ com->mcr_image = bits);
+ else
+#endif /* COM_MULTIPORT */
+ outb(com->modem_ctl_port,
+ com->mcr_image = bits | MCR_IENABLE);
+ break;
+ case DMBIS:
+ outb(com->modem_ctl_port, com->mcr_image |= bits);
+ break;
+ case DMBIC:
+#ifdef COM_MULTIPORT
+ /* YYY maybe your card doesn't want IENABLE to be reset? */
+ if(com->multiport)
+ outb(com->modem_ctl_port,
+ com->mcr_image &= ~(bits));
+ else
+#endif /* COM_MULTIPORT */
+ outb(com->modem_ctl_port,
+ com->mcr_image &= ~(bits & ~MCR_IENABLE));
+ break;
+ case DMGET:
+ bits = com->prev_modem_status;
+ break;
+ }
+ enable_intr();
+ return (bits);
+}
+
+static void
+comwakeup()
+{
+ struct com_s *com;
+ int unit;
+
+ timeout((timeout_func_t) comwakeup, (caddr_t) NULL, 1);
+ if (com_events != 0)
+ /* schedule compoll() to run when the cpl allows */
+ schedsoftcom();
+
+ /* recover from lost output interrupts */
+ for (unit = 0; unit < NSIO; ++unit) {
+ com = com_addr(unit);
+ if (com != NULL && com->state >= (CS_BUSY | CS_TTGO)) {
+ disable_intr();
+ siointr(unit);
+ enable_intr();
+ }
+ }
+}
+
+void
+softsio0() { compoll(); }
+
+void
+softsio1() { compoll(); }
+
+void
+softsio2() { compoll(); }
+
+void
+softsio3() { compoll(); }
+
+void
+softsio4() { compoll(); }
+
+void
+softsio5() { compoll(); }
+
+void
+softsio6() { compoll(); }
+
+void
+softsio7() { compoll(); }
+
+void
+softsio8() { compoll(); }
+
+/*
+ * Following are all routines needed for COM to act as console
+ * XXX - not tested in this version
+ * XXX - check that the corresponding serial interrupts are never enabled
+ */
+#include "i386/i386/cons.h"
+
+void
+siocnprobe(cp)
+ struct consdev *cp;
+{
+ int unit;
+
+ /* locate the major number */
+ for (commajor = 0; commajor < nchrdev; commajor++)
+ if (cdevsw[commajor].d_open == (bogus_open_t) sioopen)
+ break;
+
+ /* XXX: ick */
+ unit = CONUNIT;
+ com_addr(unit) = &com_structs[unit];
+ com_addr(unit)->iobase = CONADDR;
+
+ /* make sure hardware exists? XXX */
+
+ /* initialize required fields */
+ cp->cn_dev = makedev(commajor, unit);
+ cp->cn_tp = &sio_tty[unit];
+#ifdef COMCONSOLE
+ cp->cn_pri = CN_REMOTE; /* Force a serial port console */
+#else
+ cp->cn_pri = CN_NORMAL;
+#endif
+}
+
+void
+siocninit(cp)
+ struct consdev *cp;
+{
+ int unit;
+
+ unit = UNIT(cp->cn_dev);
+ cominit(unit, comdefaultrate);
+ comconsole = unit;
+ comconsinit = TRUE;
+}
+
+static void
+cominit(unit, rate)
+ int unit;
+ int rate;
+{
+ Port_t iobase;
+ int s;
+
+ iobase = com_addr(unit)->iobase;
+ s = splhigh();
+ outb(iobase + com_cfcr, CFCR_DLAB);
+ rate = ttspeedtab(comdefaultrate, comspeedtab);
+ outb(iobase + com_data, rate & 0xFF);
+ outb(iobase + com_ier, rate >> 8);
+ outb(iobase + com_cfcr, CFCR_8BITS);
+
+ /*
+ * XXX - fishy to enable interrupts and then poll.
+ * It shouldn't be necessary to ready the iir.
+ */
+ outb(iobase + com_ier, IER_ERXRDY | IER_ETXRDY | IER_ERLS | IER_EMSC);
+ outb(iobase + com_fifo,
+ FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST | FIFO_TRIGGER_14);
+ (void) inb(iobase + com_iir);
+ splx(s);
+}
+
+int
+siocngetc(dev)
+ dev_t dev;
+{
+ int c;
+ Port_t iobase;
+ int s;
+
+ iobase = com_addr(UNIT(dev))->iobase;
+ s = splhigh();
+ while (!(inb(iobase + com_lsr) & LSR_RXRDY))
+ ;
+ c = inb(iobase + com_data);
+ (void) inb(iobase + com_iir);
+ splx(s);
+ return (c);
+}
+
+void
+siocnputc(dev, c)
+ dev_t dev;
+ int c;
+{
+ Port_t iobase;
+ int s;
+ int timo;
+
+ iobase = com_addr(UNIT(dev))->iobase;
+ s = splhigh();
+#ifdef KGDB
+ if (dev != kgdb_dev)
+#endif
+ if (!comconsinit) {
+ (void) cominit(UNIT(dev), comdefaultrate);
+ comconsinit = TRUE;
+ }
+ /* wait for any pending transmission to finish */
+ timo = 50000;
+ while (!(inb(iobase + com_lsr) & LSR_TXRDY) && --timo)
+ ;
+ outb(iobase + com_data, c);
+ /* wait for this transmission to complete */
+ timo = 1500000;
+ while (!(inb(iobase + com_lsr) & LSR_TXRDY) && --timo)
+ ;
+ /* clear any interrupts generated by this transmission */
+ (void) inb(iobase + com_iir);
+ splx(s);
+}
+
+/*
+ * 10 Feb 93 Jordan K. Hubbard Added select code
+ * 27 May 93 Rodney W. Grimes Stole the select code from com.c.pl5
+ */
+
+int
+sioselect(dev, rw, p)
+ dev_t dev;
+ int rw;
+ struct proc *p;
+{
+ register struct tty *tp = &sio_tty[UNIT(dev)];
+ int nread;
+ int s = spltty();
+ struct proc *selp;
+
+ switch (rw) {
+
+ case FREAD:
+ nread = ttnread(tp);
+ if (nread > 0 ||
+ ((tp->t_cflag&CLOCAL) == 0 && (tp->t_state&TS_CARR_ON) == 0))
+ goto win;
+ if (tp->t_rsel && (selp = pfind(tp->t_rsel)) && selp->p_wchan == (caddr_t)&selwait)
+ tp->t_state |= TS_RCOLL;
+ else
+ tp->t_rsel = p->p_pid;
+ break;
+
+ case FWRITE:
+ if (RB_LEN(&tp->t_out) <= tp->t_lowat)
+ goto win;
+ if (tp->t_wsel && (selp = pfind(tp->t_wsel)) && selp->p_wchan == (caddr_t)&selwait)
+ tp->t_state |= TS_WCOLL;
+ else
+ tp->t_wsel = p->p_pid;
+ break;
+ }
+ splx(s);
+ return (0);
+ win:
+ splx(s);
+ return (1);
+}
+
+#endif /* NSIO > 0 */
diff --git a/sys/i386/isa/sioreg.h b/sys/i386/isa/sioreg.h
new file mode 100644
index 0000000..2a62683
--- /dev/null
+++ b/sys/i386/isa/sioreg.h
@@ -0,0 +1,113 @@
+/*-
+ * 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.
+ *
+ * @(#)comreg.h 7.2 (Berkeley) 5/9/91
+ */
+
+
+/* 16 bit baud rate divisor (lower byte in dca_data, upper in dca_ier) */
+#define COMBRD(x) (1843200 / (16*(x)))
+
+/* interrupt enable register */
+#define IER_ERXRDY 0x1
+#define IER_ETXRDY 0x2
+#define IER_ERLS 0x4
+#define IER_EMSC 0x8
+
+/* interrupt identification register */
+#define IIR_IMASK 0xf
+#define IIR_RXTOUT 0xc
+#define IIR_RLS 0x6
+#define IIR_RXRDY 0x4
+#define IIR_TXRDY 0x2
+#define IIR_NOPEND 0x1
+#define IIR_MLSC 0x0
+#define IIR_FIFO_MASK 0xc0 /* set if FIFOs are enabled */
+
+/* fifo control register */
+#define FIFO_ENABLE 0x01
+#define FIFO_RCV_RST 0x02
+#define FIFO_XMT_RST 0x04
+#define FIFO_DMA_MODE 0x08
+#define FIFO_TRIGGER_1 0x00
+#define FIFO_TRIGGER_4 0x40
+#define FIFO_TRIGGER_8 0x80
+#define FIFO_TRIGGER_14 0xc0
+
+/* character format control register */
+#define CFCR_DLAB 0x80
+#define CFCR_SBREAK 0x40
+#define CFCR_PZERO 0x30
+#define CFCR_PONE 0x20
+#define CFCR_PEVEN 0x10
+#define CFCR_PODD 0x00
+#define CFCR_PENAB 0x08
+#define CFCR_STOPB 0x04
+#define CFCR_8BITS 0x03
+#define CFCR_7BITS 0x02
+#define CFCR_6BITS 0x01
+#define CFCR_5BITS 0x00
+
+/* modem control register */
+#define MCR_LOOPBACK 0x10
+#define MCR_IENABLE 0x08
+#define MCR_DRS 0x04
+#define MCR_RTS 0x02
+#define MCR_DTR 0x01
+
+/* line status register */
+#define LSR_RCV_FIFO 0x80
+#define LSR_TSRE 0x40
+#define LSR_TXRDY 0x20
+#define LSR_BI 0x10
+#define LSR_FE 0x08
+#define LSR_PE 0x04
+#define LSR_OE 0x02
+#define LSR_RXRDY 0x01
+#define LSR_RCV_MASK 0x1f
+
+/* modem status register */
+#define MSR_DCD 0x80
+#define MSR_RI 0x40
+#define MSR_DSR 0x20
+#define MSR_CTS 0x10
+#define MSR_DDCD 0x08
+#define MSR_TERI 0x04
+#define MSR_DDSR 0x02
+#define MSR_DCTS 0x01
+
+/*
+ * WARNING: Serial console is assumed to be at COM1 address
+ * and CONUNIT must be 0.
+ */
+#define CONADDR (0x3f8)
+#define CONUNIT (0)
diff --git a/sys/i386/isa/spkr.c b/sys/i386/isa/spkr.c
new file mode 100644
index 0000000..ffeec08
--- /dev/null
+++ b/sys/i386/isa/spkr.c
@@ -0,0 +1,520 @@
+/*
+ * spkr.c -- device driver for console speaker on 80386
+ *
+ * v1.1 by Eric S. Raymond (esr@snark.thyrsus.com) Feb 1990
+ * modified for 386bsd by Andrew A. Chernov <ache@astral.msk.su>
+ * 386bsd only clean version, all SYSV stuff removed
+ * use hz value from param.c
+ */
+
+#include "speaker.h"
+
+#if NSPEAKER > 0
+
+#include "param.h"
+#include "kernel.h"
+#include "errno.h"
+#include "buf.h"
+#include "uio.h"
+#include "spkr.h"
+
+/**************** MACHINE DEPENDENT PART STARTS HERE *************************
+ *
+ * This section defines a function tone() which causes a tone of given
+ * frequency and duration from the 80x86's console speaker.
+ * Another function endtone() is defined to force sound off, and there is
+ * also a rest() entry point to do pauses.
+ *
+ * Audible sound is generated using the Programmable Interval Timer (PIT) and
+ * Programmable Peripheral Interface (PPI) attached to the 80x86's speaker. The
+ * PPI controls whether sound is passed through at all; the PIT's channel 2 is
+ * used to generate clicks (a square wave) of whatever frequency is desired.
+ */
+
+/*
+ * PIT and PPI port addresses and control values
+ *
+ * Most of the magic is hidden in the TIMER_PREP value, which selects PIT
+ * channel 2, frequency LSB first, square-wave mode and binary encoding.
+ * The encoding is as follows:
+ *
+ * +----------+----------+---------------+-----+
+ * | 1 0 | 1 1 | 0 1 1 | 0 |
+ * | SC1 SC0 | RW1 RW0 | M2 M1 M0 | BCD |
+ * +----------+----------+---------------+-----+
+ * Counter Write Mode 3 Binary
+ * Channel 2 LSB first, (Square Wave) Encoding
+ * MSB second
+ */
+#define PPI 0x61 /* port of Programmable Peripheral Interface */
+#define PPI_SPKR 0x03 /* turn these PPI bits on to pass sound */
+#define PIT_CTRL 0x43 /* PIT control address */
+#define PIT_COUNT 0x42 /* PIT count address */
+#define PIT_MODE 0xB6 /* set timer mode for sound generation */
+
+/*
+ * Magic numbers for timer control.
+ */
+#define TIMER_CLK 1193180L /* corresponds to 18.2 MHz tick rate */
+
+static int endtone()
+/* turn off the speaker, ending current tone */
+{
+ wakeup((caddr_t)endtone);
+ outb(PPI, inb(PPI) & ~PPI_SPKR);
+}
+
+static void tone(hz, ticks)
+/* emit tone of frequency hz for given number of ticks */
+unsigned int hz, ticks;
+{
+ unsigned int divisor = TIMER_CLK / hz;
+ int sps;
+
+#ifdef DEBUG
+ printf("tone: hz=%d ticks=%d\n", hz, ticks);
+#endif /* DEBUG */
+
+ /* set timer to generate clicks at given frequency in Hertz */
+ sps = spltty();
+ outb(PIT_CTRL, PIT_MODE); /* prepare timer */
+ outb(PIT_COUNT, (unsigned char) divisor); /* send lo byte */
+ outb(PIT_COUNT, (divisor >> 8)); /* send hi byte */
+ splx(sps);
+
+ /* turn the speaker on */
+ outb(PPI, inb(PPI) | PPI_SPKR);
+
+ /*
+ * Set timeout to endtone function, then give up the timeslice.
+ * This is so other processes can execute while the tone is being
+ * emitted.
+ */
+ timeout((caddr_t)endtone, (caddr_t)NULL, ticks);
+ sleep((caddr_t)endtone, PZERO - 1);
+}
+
+static int endrest()
+/* end a rest */
+{
+ wakeup((caddr_t)endrest);
+}
+
+static void rest(ticks)
+/* rest for given number of ticks */
+int ticks;
+{
+ /*
+ * Set timeout to endrest function, then give up the timeslice.
+ * This is so other processes can execute while the rest is being
+ * waited out.
+ */
+#ifdef DEBUG
+ printf("rest: %d\n", ticks);
+#endif /* DEBUG */
+ timeout((caddr_t)endrest, (caddr_t)NULL, ticks);
+ sleep((caddr_t)endrest, PZERO - 1);
+}
+
+/**************** PLAY STRING INTERPRETER BEGINS HERE **********************
+ *
+ * Play string interpretation is modelled on IBM BASIC 2.0's PLAY statement;
+ * M[LNS] are missing and the ~ synonym and octave-tracking facility is added.
+ * Requires tone(), rest(), and endtone(). String play is not interruptible
+ * except possibly at physical block boundaries.
+ */
+
+typedef int bool;
+#define TRUE 1
+#define FALSE 0
+
+#define toupper(c) ((c) - ' ' * (((c) >= 'a') && ((c) <= 'z')))
+#define isdigit(c) (((c) >= '0') && ((c) <= '9'))
+#define dtoi(c) ((c) - '0')
+
+static int octave; /* currently selected octave */
+static int whole; /* whole-note time at current tempo, in ticks */
+static int value; /* whole divisor for note time, quarter note = 1 */
+static int fill; /* controls spacing of notes */
+static bool octtrack; /* octave-tracking on? */
+static bool octprefix; /* override current octave-tracking state? */
+
+/*
+ * Magic number avoidance...
+ */
+#define SECS_PER_MIN 60 /* seconds per minute */
+#define WHOLE_NOTE 4 /* quarter notes per whole note */
+#define MIN_VALUE 64 /* the most we can divide a note by */
+#define DFLT_VALUE 4 /* default value (quarter-note) */
+#define FILLTIME 8 /* for articulation, break note in parts */
+#define STACCATO 6 /* 6/8 = 3/4 of note is filled */
+#define NORMAL 7 /* 7/8ths of note interval is filled */
+#define LEGATO 8 /* all of note interval is filled */
+#define DFLT_OCTAVE 4 /* default octave */
+#define MIN_TEMPO 32 /* minimum tempo */
+#define DFLT_TEMPO 120 /* default tempo */
+#define MAX_TEMPO 255 /* max tempo */
+#define NUM_MULT 3 /* numerator of dot multiplier */
+#define DENOM_MULT 2 /* denominator of dot multiplier */
+
+/* letter to half-tone: A B C D E F G */
+static int notetab[8] = {9, 11, 0, 2, 4, 5, 7};
+
+/*
+ * This is the American Standard A440 Equal-Tempered scale with frequencies
+ * rounded to nearest integer. Thank Goddess for the good ol' CRC Handbook...
+ * our octave 0 is standard octave 2.
+ */
+#define OCTAVE_NOTES 12 /* semitones per octave */
+static int pitchtab[] =
+{
+/* C C# D D# E F F# G G# A A# B*/
+/* 0 */ 65, 69, 73, 78, 82, 87, 93, 98, 103, 110, 117, 123,
+/* 1 */ 131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247,
+/* 2 */ 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494,
+/* 3 */ 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988,
+/* 4 */ 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1975,
+/* 5 */ 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951,
+/* 6 */ 4186, 4435, 4698, 4978, 5274, 5588, 5920, 6272, 6644, 7040, 7459, 7902,
+};
+
+static void playinit()
+{
+ octave = DFLT_OCTAVE;
+ whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / DFLT_TEMPO;
+ fill = NORMAL;
+ value = DFLT_VALUE;
+ octtrack = FALSE;
+ octprefix = TRUE; /* act as though there was an initial O(n) */
+}
+
+static void playtone(pitch, value, sustain)
+/* play tone of proper duration for current rhythm signature */
+int pitch, value, sustain;
+{
+ register int sound, silence, snum = 1, sdenom = 1;
+
+ /* this weirdness avoids floating-point arithmetic */
+ for (; sustain; sustain--)
+ {
+ snum *= NUM_MULT;
+ sdenom *= DENOM_MULT;
+ }
+
+ if (pitch == -1)
+ rest(whole * snum / (value * sdenom));
+ else
+ {
+ sound = (whole * snum) / (value * sdenom)
+ - (whole * (FILLTIME - fill)) / (value * FILLTIME);
+ silence = whole * (FILLTIME-fill) * snum / (FILLTIME * value * sdenom);
+
+#ifdef DEBUG
+ printf("playtone: pitch %d for %d ticks, rest for %d ticks\n",
+ pitch, sound, silence);
+#endif /* DEBUG */
+
+ tone(pitchtab[pitch], sound);
+ if (fill != LEGATO)
+ rest(silence);
+ }
+}
+
+static int abs(n)
+int n;
+{
+ if (n < 0)
+ return(-n);
+ else
+ return(n);
+}
+
+static void playstring(cp, slen)
+/* interpret and play an item from a notation string */
+char *cp;
+size_t slen;
+{
+ int pitch, lastpitch = OCTAVE_NOTES * DFLT_OCTAVE;
+
+#define GETNUM(cp, v) for(v=0; isdigit(cp[1]) && slen > 0; ) \
+ {v = v * 10 + (*++cp - '0'); slen--;}
+ for (; slen--; cp++)
+ {
+ int sustain, timeval, tempo;
+ register char c = toupper(*cp);
+
+#ifdef DEBUG
+ printf("playstring: %c (%x)\n", c, c);
+#endif /* DEBUG */
+
+ switch (c)
+ {
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
+
+ /* compute pitch */
+ pitch = notetab[c - 'A'] + octave * OCTAVE_NOTES;
+
+ /* this may be followed by an accidental sign */
+ if (cp[1] == '#' || cp[1] == '+')
+ {
+ ++pitch;
+ ++cp;
+ slen--;
+ }
+ else if (cp[1] == '-')
+ {
+ --pitch;
+ ++cp;
+ slen--;
+ }
+
+ /*
+ * If octave-tracking mode is on, and there has been no octave-
+ * setting prefix, find the version of the current letter note
+ * closest to the last regardless of octave.
+ */
+ if (octtrack && !octprefix)
+ {
+ if (abs(pitch-lastpitch) > abs(pitch+OCTAVE_NOTES-lastpitch))
+ {
+ ++octave;
+ pitch += OCTAVE_NOTES;
+ }
+
+ if (abs(pitch-lastpitch) > abs((pitch-OCTAVE_NOTES)-lastpitch))
+ {
+ --octave;
+ pitch -= OCTAVE_NOTES;
+ }
+ }
+ octprefix = FALSE;
+ lastpitch = pitch;
+
+ /* ...which may in turn be followed by an override time value */
+ GETNUM(cp, timeval);
+ if (timeval <= 0 || timeval > MIN_VALUE)
+ timeval = value;
+
+ /* ...and/or sustain dots */
+ for (sustain = 0; cp[1] == '.'; cp++)
+ {
+ slen--;
+ sustain++;
+ }
+
+ /* time to emit the actual tone */
+ playtone(pitch, timeval, sustain);
+ break;
+
+ case 'O':
+ if (cp[1] == 'N' || cp[1] == 'n')
+ {
+ octprefix = octtrack = FALSE;
+ ++cp;
+ slen--;
+ }
+ else if (cp[1] == 'L' || cp[1] == 'l')
+ {
+ octtrack = TRUE;
+ ++cp;
+ slen--;
+ }
+ else
+ {
+ GETNUM(cp, octave);
+ if (octave >= sizeof(pitchtab) / OCTAVE_NOTES)
+ octave = DFLT_OCTAVE;
+ octprefix = TRUE;
+ }
+ break;
+
+ case '>':
+ if (octave < sizeof(pitchtab) / OCTAVE_NOTES - 1)
+ octave++;
+ octprefix = TRUE;
+ break;
+
+ case '<':
+ if (octave > 0)
+ octave--;
+ octprefix = TRUE;
+ break;
+
+ case 'N':
+ GETNUM(cp, pitch);
+ for (sustain = 0; cp[1] == '.'; cp++)
+ {
+ slen--;
+ sustain++;
+ }
+ playtone(pitch - 1, value, sustain);
+ break;
+
+ case 'L':
+ GETNUM(cp, value);
+ if (value <= 0 || value > MIN_VALUE)
+ value = DFLT_VALUE;
+ break;
+
+ case 'P':
+ case '~':
+ /* this may be followed by an override time value */
+ GETNUM(cp, timeval);
+ if (timeval <= 0 || timeval > MIN_VALUE)
+ timeval = value;
+ for (sustain = 0; cp[1] == '.'; cp++)
+ {
+ slen--;
+ sustain++;
+ }
+ playtone(-1, timeval, sustain);
+ break;
+
+ case 'T':
+ GETNUM(cp, tempo);
+ if (tempo < MIN_TEMPO || tempo > MAX_TEMPO)
+ tempo = DFLT_TEMPO;
+ whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / tempo;
+ break;
+
+ case 'M':
+ if (cp[1] == 'N' || cp[1] == 'n')
+ {
+ fill = NORMAL;
+ ++cp;
+ slen--;
+ }
+ else if (cp[1] == 'L' || cp[1] == 'l')
+ {
+ fill = LEGATO;
+ ++cp;
+ slen--;
+ }
+ else if (cp[1] == 'S' || cp[1] == 's')
+ {
+ fill = STACCATO;
+ ++cp;
+ slen--;
+ }
+ break;
+ }
+ }
+}
+
+/******************* UNIX DRIVER HOOKS BEGIN HERE **************************
+ *
+ * This section implements driver hooks to run playstring() and the tone(),
+ * endtone(), and rest() functions defined above.
+ */
+
+static int spkr_active; /* exclusion flag */
+static struct buf *spkr_inbuf; /* incoming buf */
+
+int spkropen(dev)
+dev_t dev;
+{
+#ifdef DEBUG
+ printf("spkropen: entering with dev = %x\n", dev);
+#endif /* DEBUG */
+
+ if (minor(dev) != 0)
+ return(ENXIO);
+ else if (spkr_active)
+ return(EBUSY);
+ else
+ {
+ playinit();
+ spkr_inbuf = geteblk(DEV_BSIZE);
+ spkr_active = 1;
+ }
+ return(0);
+}
+
+int spkrwrite(dev, uio)
+dev_t dev;
+struct uio *uio;
+{
+ register unsigned n;
+ char *cp;
+ int error;
+#ifdef DEBUG
+ printf("spkrwrite: entering with dev = %x, count = %d\n",
+ dev, uio->uio_resid);
+#endif /* DEBUG */
+
+ if (minor(dev) != 0)
+ return(ENXIO);
+ else
+ {
+ n = MIN(DEV_BSIZE, uio->uio_resid);
+ cp = spkr_inbuf->b_un.b_addr;
+ error = uiomove(cp, n, uio);
+ if (!error)
+ playstring(cp, n);
+ return(error);
+ }
+}
+
+int spkrclose(dev)
+dev_t dev;
+{
+#ifdef DEBUG
+ printf("spkrclose: entering with dev = %x\n", dev);
+#endif /* DEBUG */
+
+ if (minor(dev) != 0)
+ return(ENXIO);
+ else
+ {
+ endtone();
+ brelse(spkr_inbuf);
+ spkr_active = 0;
+ }
+ return(0);
+}
+
+int spkrioctl(dev, cmd, cmdarg)
+dev_t dev;
+int cmd;
+caddr_t cmdarg;
+{
+#ifdef DEBUG
+ printf("spkrioctl: entering with dev = %x, cmd = %x\n", dev, cmd);
+#endif /* DEBUG */
+
+ if (minor(dev) != 0)
+ return(ENXIO);
+ else if (cmd == SPKRTONE)
+ {
+ tone_t *tp = (tone_t *)cmdarg;
+
+ if (tp->frequency == 0)
+ rest(tp->duration);
+ else
+ tone(tp->frequency, tp->duration);
+ }
+ else if (cmd == SPKRTUNE)
+ {
+ tone_t *tp = (tone_t *)(*(caddr_t *)cmdarg);
+ tone_t ttp;
+ int error;
+
+ for (; ; tp++) {
+ error = copyin(tp, &ttp, sizeof(tone_t));
+ if (error)
+ return(error);
+ if (ttp.duration == 0)
+ break;
+ if (ttp.frequency == 0)
+ rest(ttp.duration);
+ else
+ tone(ttp.frequency, ttp.duration);
+ }
+ }
+ else
+ return(EINVAL);
+ return(0);
+}
+
+#endif /* NSPEAKER > 0 */
+/* spkr.c ends here */
diff --git a/sys/i386/isa/timerreg.h b/sys/i386/isa/timerreg.h
new file mode 100644
index 0000000..72c7022
--- /dev/null
+++ b/sys/i386/isa/timerreg.h
@@ -0,0 +1,89 @@
+/*-
+ * Copyright (c) 1993 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.
+ *
+ * $Header: timerreg.h,v 1.2 93/02/28 15:08:58 mccanne Exp $
+ *
+ * Register definitions for the Intel 8253 Programmable Interval Timer.
+ *
+ * This chip has three independent 16-bit down counters that can be
+ * read on the fly. There are three mode registers and three countdown
+ * registers. The countdown registers are addressed directly, via the
+ * first three I/O ports. The three mode registers are accessed via
+ * the fourth I/O port, with two bits in the mode byte indicating the
+ * register. (Why are hardware interfaces always so braindead?).
+ *
+ * To write a value into the countdown register, the mode register
+ * is first programmed with a command indicating the which byte of
+ * the two byte register is to be modified. The three possibilities
+ * are load msb (TMR_MR_MSB), load lsb (TMR_MR_LSB), or load lsb then
+ * msb (TMR_MR_BOTH).
+ *
+ * To read the current value ("on the fly") from the countdown register,
+ * you write a "latch" command into the mode register, then read the stable
+ * value from the corresponding I/O port. For example, you write
+ * TMR_MR_LATCH into the corresponding mode register. Presumably,
+ * after doing this, a write operation to the I/O port would result
+ * in undefined behavior (but hopefully not fry the chip).
+ * Reading in this manner has no side effects.
+ *
+ * The outputs of the three timers are connected as follows:
+ *
+ * timer 0 -> irq 0
+ * timer 1 -> dma chan 0 (for dram refresh)
+ * timer 2 -> speaker (via keyboard controller)
+ *
+ * Timer 0 is used to call hardclock.
+ * Timer 2 is used to generate console beeps.
+ */
+
+/*
+ * Macros for specifying values to be written into a mode register.
+ */
+#define TIMER_CNTR0 (IO_TIMER1 + 0) /* timer 0 counter port */
+#define TIMER_CNTR1 (IO_TIMER1 + 1) /* timer 1 counter port */
+#define TIMER_CNTR2 (IO_TIMER1 + 2) /* timer 2 counter port */
+#define TIMER_MODE (IO_TIMER1 + 3) /* timer mode port */
+#define TIMER_SEL0 0x00 /* select counter 0 */
+#define TIMER_SEL1 0x40 /* select counter 1 */
+#define TIMER_SEL2 0x80 /* select counter 2 */
+#define TIMER_INTTC 0x00 /* mode 0, intr on terminal cnt */
+#define TIMER_ONESHOT 0x02 /* mode 1, one shot */
+#define TIMER_RATEGEN 0x04 /* mode 2, rate generator */
+#define TIMER_SQWAVE 0x06 /* mode 3, square wave */
+#define TIMER_SWSTROBE 0x08 /* mode 4, s/w triggered strobe */
+#define TIMER_HWSTROBE 0x0a /* mode 5, h/w triggered strobe */
+#define TIMER_LATCH 0x00 /* latch counter for reading */
+#define TIMER_LSB 0x10 /* r/w counter LSB */
+#define TIMER_MSB 0x20 /* r/w counter MSB */
+#define TIMER_16BIT 0x30 /* r/w counter 16 bits, LSB first */
+#define TIMER_BCD 0x01 /* count in BCD */
+
diff --git a/sys/i386/isa/ultra14f.c b/sys/i386/isa/ultra14f.c
new file mode 100644
index 0000000..46626b7
--- /dev/null
+++ b/sys/i386/isa/ultra14f.c
@@ -0,0 +1,1308 @@
+/*
+ * Ported for use with the UltraStor 14f by Gary Close (gclose@wvnvms.wvnet.edu)
+ * Thanks to Julian Elischer for advice and help with this port.
+ *
+ * Written by Julian Elischer (julian@tfs.com)
+ * for TRW Financial Systems for use under the MACH(2.5) operating system.
+ *
+ * TRW Financial Systems, in accordance with their agreement with Carnegie
+ * Mellon University, makes this software available to CMU to distribute
+ * or use in any manner that they see fit as long as this message is kept with
+ * the software. For this reason TFS also grants any other persons or
+ * organisations permission to use or modify this software.
+ *
+ * TFS supplies this software to be publicly redistributed
+ * on the understanding that TFS is not responsible for the correct
+ * functioning of this software in any circumstances.
+ *
+ *
+ * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE
+ * -------------------- ----- ----------------------
+ * CURRENT PATCH LEVEL: 1 00098
+ * -------------------- ----- ----------------------
+ *
+ * 16 Feb 93 Julian Elischer ADDED for SCSI system
+ * commenced: Sun Sep 27 18:14:01 PDT 1992
+ */
+
+#include <sys/types.h>
+#include <uha.h>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/user.h>
+
+#ifdef MACH /* EITHER CMU OR OSF */
+#include <i386/ipl.h>
+#include <i386at/scsi.h>
+#include <i386at/scsiconf.h>
+
+#ifdef OSF /* OSF ONLY */
+#include <sys/table.h>
+#include <i386/handler.h>
+#include <i386/dispatcher.h>
+#include <i386/AT386/atbus.h>
+
+#else OSF /* CMU ONLY */
+#include <i386at/atbus.h>
+#include <i386/pio.h>
+#endif OSF
+#endif MACH /* end of MACH specific */
+
+#ifdef __386BSD__ /* 386BSD specific */
+#define isa_dev isa_device
+#define dev_unit id_unit
+#define dev_addr id_iobase
+
+#include <i386/include/pio.h>
+#include <i386/isa/isa_device.h>
+#include <scsi/scsi_all.h>
+#include <scsi/scsiconf.h>
+#endif __386BSD__
+
+/* */
+
+#ifdef __386BSD__
+#include "ddb.h"
+#if NDDB > 0
+int Debugger();
+#else NDDB
+#define Debugger() panic("should call debugger here")
+#endif NDDB
+#endif __386BSD__
+
+#ifdef MACH
+int Debugger();
+#endif MACH
+
+typedef struct {unsigned char addr[4]; } physaddr;
+typedef struct {unsigned char len[4]; } physlen;
+
+
+#ifdef MACH
+extern physaddr kvtophys();
+#define PHYSTOKV(x) phystokv(x)
+#define KVTOPHYS(x) kvtophys(x)
+#endif MACH
+
+#ifdef __386BSD__
+#define PHYSTOKV(x) (x | 0xFE000000)
+#define KVTOPHYS(x) vtophys(x)
+#endif __386BSD__
+
+extern int delaycount; /* from clock setup code */
+#define NUM_CONCURRENT 16 /* number of concurrent ops per board */
+#define UHA_NSEG 33 /* number of dma segments supported */
+#define FUDGE(X) (X>>1) /* our loops are slower than spinwait() */
+/* */
+/************************** board definitions *******************************/
+/*
+ * I/O Port Interface
+*/
+ #define UHA_LMASK (0x000) /* local doorbell mask reg */
+ #define UHA_LINT (0x001) /* local doorbell int/stat reg */
+ #define UHA_SMASK (0x002) /* system doorbell mask reg */
+ #define UHA_SINT (0x003) /* system doorbell int/stat reg */
+ #define UHA_ID0 (0x004) /* product id reg 0 */
+ #define UHA_ID1 (0x005) /* product id reg 1 */
+ #define UHA_CONF1 (0x006) /* config reg 1 */
+ #define UHA_CONF2 (0x007) /* config reg 2 */
+ #define UHA_OGM0 (0x008) /* outgoing mail ptr 0 least sig */
+ #define UHA_OGM1 (0x009) /* outgoing mail ptr 1 least mid */
+ #define UHA_OGM2 (0x00a) /* outgoing mail ptr 2 most mid */
+ #define UHA_OGM3 (0x00b) /* outgoing mail ptr 3 most sig */
+ #define UHA_ICM0 (0x00c) /* incoming mail ptr 0 */
+ #define UHA_ICM1 (0x00d) /* incoming mail ptr 1 */
+ #define UHA_ICM2 (0x00e) /* incoming mail ptr 2 */
+ #define UHA_ICM3 (0x00f) /* incoming mail ptr 3 */
+
+ /*
+* UHA_LMASK bits (read only)
+*/
+
+#define UHA_LDIE 0x80 /* local doorbell int enabled */
+#define UHA_SRSTE 0x40 /* soft reset enabled */
+#define UHA_ABORTEN 0x10 /* abort MSCP enabled */
+#define UHA_OGMINTEN 0x01 /* outgoing mail interrupt enabled */
+
+/*
+* UHA_LINT bits (read)
+*/
+
+#define UHA_LDIP 0x80 /* local doorbell int pending */
+
+/*
+* UHA_LINT bits (write)
+*/
+
+#define UHA_ADRST 0x40 /* adapter soft reset */
+#define UHA_SBRST 0x20 /* scsi bus reset */
+#define UHA_ASRST 0x60 /* adapter and scsi reset */
+#define UHA_ABORT 0x10 /* abort MSCP */
+#define UHA_OGMINT 0x01 /* tell adapter to get mail */
+
+/*
+* UHA_SMASK bits (read)
+*/
+
+#define UHA_SINTEN 0x80 /* system doorbell interupt Enabled */
+#define UHA_ABORT_COMPLETE_EN 0x10 /* abort MSCP command complete int Enabled */
+#define UHA_ICM_ENABLED 0x01 /* ICM interrupt enabled
+
+/*
+* UHA_SMASK bits (write)
+*/
+
+#define UHA_ENSINT 0x80 /* enable system doorbell interrupt */
+#define UHA_EN_ABORT_COMPLETE 0x10 /* enable abort MSCP complete int */
+#define UHA_ENICM 0x01 /* enable ICM interrupt */
+
+/*
+* UHA_SINT bits (read)
+*/
+
+#define UHA_SINTP 0x80 /* system doorbell int pending */
+#define UHA_ABORT_SUCC 0x10 /* abort MSCP successful */
+#define UHA_ABORT_FAIL 0x18 /* abort MSCP failed */
+
+/*
+* UHA_SINT bits (write)
+*/
+
+#define UHA_ABORT_ACK 0x18 /* acknowledge status and clear */
+#define UHA_ICM_ACK 0x01 /* acknowledge ICM and clear */
+
+/*
+* UHA_CONF1 bits (read only)
+*/
+
+#define UHA_DMA_CH5 0x00 /* DMA channel 5 */
+#define UHA_DMA_CH6 0x40 /* 6 */
+#define UHA_DMA_CH7 0x80 /* 7 */
+#define UHA_IRQ15 0x00 /* IRQ 15 */
+#define UHA_IRQ14 0x10 /* 14 */
+#define UHA_IRQ11 0x20 /* 11 */
+#define UHA_IRQ10 0x30 /* 10 */
+
+/***********************************
+* ha_status error codes
+\***********************************/
+
+#define UHA_NO_ERR 0x00 /* No error supposedly */
+#define UHA_SBUS_ABORT_ERR 0x84 /* scsi bus abort error */
+#define UHA_SBUS_TIMEOUT 0x91 /* scsi bus selection timeout */
+#define UHA_SBUS_OVER_UNDER 0x92 /* scsi bus over/underrun */
+#define UHA_BAD_SCSI_CMD 0x96 /* illegal scsi command */
+#define UHA_AUTO_SENSE_ERR 0x9b /* auto request sense err */
+#define UHA_SBUS_RES_ERR 0xa3 /* scsi bus reset error */
+#define UHA_BAD_SG_LIST 0xff /* invalid scatter gath list */
+
+/* */
+
+struct uha_dma_seg
+{
+ physaddr addr;
+ physlen len;
+};
+/* */
+
+struct mscp
+{
+ unsigned char opcode:3;
+ #define U14_HAC 0x01 /*host adapter command*/
+ #define U14_TSP 0x02 /*target scsi pass through command*/
+ #define U14_SDR 0x04 /*scsi device reset*/
+ unsigned char xdir:2; /*xfer direction*/
+ #define U14_SDET 0x00 /*determined by scsi command*/
+ #define U14_SDIN 0x01 /*scsi data in*/
+ #define U14_SDOUT 0x02 /*scsi data out*/
+ #define U14_NODATA 0x03 /*no data xfer*/
+ unsigned char dcn:1; /*disable disconnect for this command*/
+ unsigned char ca:1; /*Cache control*/
+ unsigned char sgth:1; /*scatter gather flag*/
+ unsigned char target:3;
+ unsigned char chan:2; /*scsi channel (always 0 for 14f)*/
+ unsigned char lun:3;
+ physaddr data;
+ physlen datalen;
+ physaddr link;
+ unsigned char link_id;
+ unsigned char sg_num; /*number of scat gath segs */
+ /*in s-g list if sg flag is*/
+ /*set. starts at 1, 8bytes per*/
+ unsigned char senselen;
+ unsigned char cdblen;
+ unsigned char cdb[12];
+ unsigned char ha_status;
+ unsigned char targ_status;
+ physaddr sense; /* if 0 no auto sense */
+ /*-----------------end of hardware supported fields----------------*/
+ struct mscp *next; /* in free list */
+ struct scsi_xfer *xs; /* the scsi_xfer for this cmd */
+ long int delta; /* difference from previous*/
+ struct mscp *later,*sooner;
+ int flags;
+#define MSCP_FREE 0
+#define MSCP_ACTIVE 1
+#define MSCP_ABORTED 2
+ struct uha_dma_seg uha_dma[UHA_NSEG];
+ struct scsi_sense_data mscp_sense;
+};
+
+struct mscp *uha_soonest = (struct mscp *)0;
+struct mscp *uha_latest = (struct mscp *)0;
+long int uha_furtherest = 0; /* longest time in the timeout queue */
+/* */
+
+struct uha_data
+{
+ int flags;
+#define UHA_INIT 0x01;
+ int baseport;
+ struct mscp mscps[NUM_CONCURRENT];
+ struct mscp *free_mscp;
+ int our_id; /* our scsi id */
+ int vect;
+ int dma;
+} uha_data[NUHA];
+
+int uhaprobe();
+int uha_attach();
+int uhaintr();
+int uha_scsi_cmd();
+int uha_timeout();
+int uha_abort();
+struct mscp *cheat;
+void uhaminphys();
+long int uha_adapter_info();
+
+unsigned long int scratch;
+
+#ifdef MACH
+struct isa_driver uhadriver = { uhaprobe, 0, uha_attach, "uha", 0, 0, 0};
+int (*uhaintrs[])() = {uhaintr, 0};
+#endif MACH
+
+#ifdef __386BSD__
+struct isa_driver uhadriver = { uhaprobe, uha_attach, "uha"};
+#endif __386BSD__
+
+static uha_unit = 0;
+int uha_debug = 0;
+#define UHA_SHOWMSCPS 0x01
+#define UHA_SHOWINTS 0x02
+#define UHA_SHOWCMDS 0x04
+#define UHA_SHOWMISC 0x08
+#define FAIL 1
+#define SUCCESS 0
+#define PAGESIZ 4096
+
+struct scsi_switch uha_switch =
+{
+ uha_scsi_cmd,
+ uhaminphys,
+ 0,
+ 0,
+ uha_adapter_info,
+ 0,0,0
+};
+
+/* */
+/***********************************************************************\
+* Function to send a command out through a mailbox *
+\***********************************************************************/
+uha_send_mbox( int unit
+ ,struct mscp *mscp)
+{
+ int port = uha_data[unit].baseport;
+ int spincount = FUDGE(delaycount) * 1; /* 1ms should be enough */
+ int s = splbio();
+
+ while( ((inb(port + UHA_LINT) & (UHA_LDIP))
+ != (0))
+ && (spincount--));
+ if(spincount == -1)
+ {
+ printf("uha%d: board not responding\n",unit);
+ Debugger();
+ }
+
+ outl(port + UHA_OGM0,KVTOPHYS(mscp));
+ outb(port + UHA_LINT, (UHA_OGMINT));
+ splx(s);
+}
+
+/***********************************************************************\
+* Function to send abort to 14f *
+\***********************************************************************/
+
+uha_abort( int unit
+ ,struct mscp *mscp)
+{
+ int port = uha_data[unit].baseport;
+ int spincount = FUDGE(delaycount) * 1;
+ int abortcount = FUDGE(delaycount) * 2000;
+ int s = splbio();
+
+ while(((inb(port + UHA_LINT) & (UHA_LDIP))
+ != (0))
+ && (spincount--));
+ if(spincount == -1);
+ {
+ printf("uha%d: board not responding\n",unit);
+ Debugger();
+ }
+
+ outl(port + UHA_OGM0,KVTOPHYS(mscp));
+ outb(port + UHA_LINT,UHA_ABORT);
+
+ while((abortcount--) && (!(inb(port + UHA_SINT) & UHA_ABORT_FAIL)));
+ if(abortcount == -1)
+ {
+ printf("uha%d: board not responding\n",unit);
+ Debugger();
+ }
+ if((inb(port + UHA_SINT) & 0x10) != 0)
+ {
+ outb(port + UHA_SINT,UHA_ABORT_ACK);
+ return(1);
+ }
+ else
+ {
+ outb(port + UHA_SINT,UHA_ABORT_ACK);
+ return(0);
+ }
+}
+
+/***********************************************************************\
+* Function to poll for command completion when in poll mode *
+\***********************************************************************/
+uha_poll(int unit ,int wait) /* in msec */
+{
+ int port = uha_data[unit].baseport;
+ int spincount = FUDGE(delaycount) * wait; /* in msec */
+ int stport = port + UHA_SINT;
+ int start = spincount;
+
+retry:
+ while( (spincount--) && (!(inb(stport) & UHA_SINTP)));
+ if(spincount == -1)
+ {
+ printf("uha%d: board not responding\n",unit);
+ return(EIO);
+ }
+if ((int)cheat != PHYSTOKV(inl(port + UHA_ICM0)))
+{
+ printf("discarding %x ",inl(port + UHA_ICM0));
+ outb(port + UHA_SINT, UHA_ICM_ACK);
+ spinwait(50);
+ goto retry;
+}/* don't know this will work */
+ uhaintr(unit);
+ return(0);
+}
+
+/*******************************************************\
+* Check if the device can be found at the port given *
+* and if so, set it up ready for further work *
+* as an argument, takes the isa_dev structure from *
+* autoconf.c *
+\*******************************************************/
+uhaprobe(dev)
+struct isa_dev *dev;
+{
+ int unit = uha_unit;
+ dev->dev_unit = unit;
+ uha_data[unit].baseport = dev->dev_addr;
+ if(unit >= NUHA)
+ {
+ printf("uha: unit number (%d) too high\n",unit);
+ return(0);
+ }
+
+ /*try and initialize unit at this location*/
+ if (uha_init(unit) != 0)
+ {
+ return(0);
+ }
+
+ /* if its there put in it's interrupt and DRQ vectors */
+
+ dev->id_irq = (1 << uha_data[unit].vect);
+ dev->id_drq = uha_data[unit].dma;
+
+
+ uha_unit ++;
+return(1);
+}
+
+/***********************************************\
+* Attach all the sub-devices we can find *
+\***********************************************/
+uha_attach(dev)
+struct isa_dev *dev;
+{
+ int unit = dev->dev_unit;
+
+
+#ifdef __386BSD__
+ printf(" probing for scsi devices**\n");
+#endif __386BSD__
+
+ /***********************************************\
+ * ask the adapter what subunits are present *
+ \***********************************************/
+ scsi_attachdevs( unit, uha_data[unit].our_id, &uha_switch);
+
+#if defined(OSF)
+ uha_attached[unit]=1;
+#endif /* defined(OSF) */
+ if(!unit) /* only one for all boards */
+ {
+ uha_timeout(0);
+ }
+
+
+#ifdef __386BSD__
+ printf("uha%d",unit);
+#endif __386BSD__
+ return;
+}
+
+/***********************************************\
+* Return some information to the caller about *
+* the adapter and it's capabilities *
+\***********************************************/
+long int uha_adapter_info(unit)
+int unit;
+{
+ return(2); /* 2 outstanding requests at a time per device */
+}
+
+/***********************************************\
+* Catch an interrupt from the adaptor *
+\***********************************************/
+uhaintr(unit)
+{
+ struct mscp *mscp;
+ u_char uhastat;
+ unsigned long int mboxval;
+
+ int port = uha_data[unit].baseport;
+
+
+ if(scsi_debug & PRINTROUTINES)
+ printf("uhaintr ");
+
+#if defined(OSF)
+ if (!uha_attached[unit])
+ {
+ return(1);
+ }
+#endif /* defined(OSF) */
+ while(inb(port + UHA_SINT) & UHA_SINTP)
+ {
+ /***********************************************\
+ * First get all the information and then *
+ * acknowlege the interrupt *
+ \***********************************************/
+ uhastat = inb(port + UHA_SINT);
+ mboxval = inl(port + UHA_ICM0);
+ outb(port + UHA_SINT,UHA_ICM_ACK);
+
+ if(scsi_debug & TRACEINTERRUPTS)
+ printf("status = 0x%x ",uhastat);
+ /***********************************************\
+ * Process the completed operation *
+ \***********************************************/
+
+ mscp = (struct mscp *)(PHYSTOKV(mboxval));
+
+ if(uha_debug & UHA_SHOWCMDS )
+ {
+ uha_show_scsi_cmd(mscp->xs);
+ }
+ if((uha_debug & UHA_SHOWMSCPS) && mscp)
+ printf("<int mscp(%x)>",mscp);
+ uha_remove_timeout(mscp);
+
+ uha_done(unit,mscp);
+ }
+ return(1);
+}
+
+/***********************************************\
+* We have a mscp which has been processed by the *
+* adaptor, now we look to see how the operation *
+* went. *
+\***********************************************/
+
+uha_done(unit,mscp)
+int unit;
+struct mscp *mscp;
+{
+ struct scsi_sense_data *s1,*s2;
+ struct scsi_xfer *xs = mscp->xs;
+
+ if(scsi_debug & (PRINTROUTINES | TRACEINTERRUPTS))
+ printf("uha_done ");
+ /***********************************************\
+ * Otherwise, put the results of the operation *
+ * into the xfer and call whoever started it *
+ \***********************************************/
+ if ( (mscp->ha_status == UHA_NO_ERR) || (xs->flags & SCSI_ERR_OK))
+ { /* All went correctly OR errors expected */
+ xs->resid = 0;
+ xs->error = 0;
+ }
+ else
+ {
+
+ s1 = &(mscp->mscp_sense);
+ s2 = &(xs->sense);
+
+ if(mscp->ha_status != UHA_NO_ERR)
+ {
+ switch(mscp->ha_status)
+ {
+ case UHA_SBUS_TIMEOUT: /* No response */
+ if (uha_debug & UHA_SHOWMISC)
+ {
+ printf("timeout reported back\n");
+ }
+ xs->error = XS_TIMEOUT;
+ break;
+ case UHA_SBUS_OVER_UNDER:
+ if (uha_debug & UHA_SHOWMISC)
+ {
+ printf("scsi bus xfer over/underrun\n");
+ }
+ xs->error = XS_DRIVER_STUFFUP;
+ break;
+ case UHA_BAD_SG_LIST:
+ if (uha_debug & UHA_SHOWMISC)
+ {
+ printf("bad sg list reported back\n");
+ }
+ xs->error = XS_DRIVER_STUFFUP;
+ break;
+ default: /* Other scsi protocol messes */
+ xs->error = XS_DRIVER_STUFFUP;
+ if (uha_debug & UHA_SHOWMISC)
+ {
+ printf("unexpected ha_status: %x\n",
+ mscp->ha_status);
+ }
+ }
+
+ }
+ else
+ {
+
+ if (mscp->targ_status != 0)
+/**************************************************************************\
+* I have no information for any possible value of target status field *
+* other than 0 means no error!! So I guess any error is unexpected in that *
+* event!! *
+\**************************************************************************/
+
+ {
+ if (uha_debug & UHA_SHOWMISC)
+ {
+ printf("unexpected targ_status: %x\n",
+ mscp->targ_status);
+ }
+ xs->error = XS_DRIVER_STUFFUP;
+ }
+ }
+ }
+done: xs->flags |= ITSDONE;
+ uha_free_mscp(unit,mscp, xs->flags);
+ if(xs->when_done)
+ (*(xs->when_done))(xs->done_arg,xs->done_arg2);
+}
+
+/***********************************************\
+* A mscp (and hence a mbx-out is put onto the *
+* free list. *
+\***********************************************/
+uha_free_mscp(unit,mscp, flags)
+struct mscp *mscp;
+{
+ unsigned int opri;
+
+ if(scsi_debug & PRINTROUTINES)
+ printf("mscp%d(0x%x)> ",unit,flags);
+ if (!(flags & SCSI_NOMASK))
+ opri = splbio();
+
+ mscp->next = uha_data[unit].free_mscp;
+ uha_data[unit].free_mscp = mscp;
+ mscp->flags = MSCP_FREE;
+ /***********************************************\
+ * If there were none, wake abybody waiting for *
+ * one to come free, starting with queued entries*
+ \***********************************************/
+ if (!mscp->next) {
+ wakeup(&uha_data[unit].free_mscp);
+ }
+ if (!(flags & SCSI_NOMASK))
+ splx(opri);
+}
+
+/***********************************************\
+* Get a free mscp (and hence mbox-out entry) *
+\***********************************************/
+struct mscp *
+uha_get_mscp(unit,flags)
+{
+ unsigned opri;
+ struct mscp *rc;
+
+ if(scsi_debug & PRINTROUTINES)
+ printf("<mscp%d(0x%x) ",unit,flags);
+ if (!(flags & SCSI_NOMASK))
+ opri = splbio();
+ /***********************************************\
+ * If we can and have to, sleep waiting for one *
+ * to come free *
+ \***********************************************/
+ while ((!(rc = uha_data[unit].free_mscp)) && (!(flags & SCSI_NOSLEEP)))
+ {
+ sleep(&uha_data[unit].free_mscp, PRIBIO);
+ }
+ if (rc)
+ {
+ uha_data[unit].free_mscp = rc->next;
+ rc->flags = MSCP_ACTIVE;
+ }
+ if (!(flags & SCSI_NOMASK))
+ splx(opri);
+ return(rc);
+}
+
+
+
+/***********************************************\
+* Start the board, ready for normal operation *
+\***********************************************/
+
+uha_init(unit)
+int unit;
+{
+ unsigned char ad[4];
+ volatile unsigned char model;
+ volatile unsigned char submodel;
+ unsigned char config_reg1;
+ unsigned char config_reg2;
+ unsigned char dma_ch;
+ unsigned char irq_ch;
+ unsigned char uha_id;
+ int port = uha_data[unit].baseport;
+ int i;
+ int resetcount = FUDGE(delaycount) * 4000;
+
+ model = inb(port + UHA_ID0);
+ submodel = inb(port + UHA_ID1);
+ if ((model != 0x56) & (submodel != 0x40))
+ { printf("ultrastor 14f not responding\n");
+ return(ENXIO); }
+
+ printf("uha%d reading board settings, ",unit);
+
+ config_reg1 = inb(port + UHA_CONF1);
+ config_reg2 = inb(port + UHA_CONF2);
+ dma_ch = (config_reg1 & 0xc0);
+ irq_ch = (config_reg1 & 0x30);
+ uha_id = (config_reg2 & 0x07);
+
+ switch(dma_ch)
+ {
+ case UHA_DMA_CH5:
+ uha_data[unit].dma = 5;
+ printf("dma=5 ");
+ break;
+ case UHA_DMA_CH6:
+ uha_data[unit].dma = 6;
+ printf("dma=6 ");
+ break;
+ case UHA_DMA_CH7:
+ uha_data[unit].dma = 7;
+ printf("dma=7 ");
+ break;
+ default:
+ printf("illegal dma jumper setting\n");
+ return(EIO);
+ }
+ switch(irq_ch)
+ {
+ case UHA_IRQ10:
+ uha_data[unit].vect = 10;
+ printf("int=10 ");
+ break;
+ case UHA_IRQ11:
+ uha_data[unit].vect = 11;
+ printf("int=11 ");
+ break;
+ case UHA_IRQ14:
+ uha_data[unit].vect = 14;
+ printf("int=14 ");
+ break;
+ case UHA_IRQ15:
+ uha_data[unit].vect = 15;
+ printf("int=15 ");
+ break;
+ default:
+ printf("illegal int jumper setting\n");
+ return(EIO);
+ }
+ /* who are we on the scsi bus */
+ printf("id=%x\n",uha_id);
+ uha_data[unit].our_id = uha_id;
+
+
+ /***********************************************\
+ * link up all our MSCPs into a free list *
+ \***********************************************/
+ for (i=0; i < NUM_CONCURRENT; i++)
+ {
+ uha_data[unit].mscps[i].next = uha_data[unit].free_mscp;
+ uha_data[unit].free_mscp = &uha_data[unit].mscps[i];
+ uha_data[unit].free_mscp->flags = MSCP_FREE;
+ }
+
+ /***********************************************\
+ * Note that we are going and return (to probe) *
+ \***********************************************/
+ outb(port + UHA_LINT, UHA_ASRST);
+ while( (resetcount--) && (!(inb(port + UHA_LINT))));
+ if(resetcount == -1)
+ {
+ printf("uha%d: board timed out during reset\n",unit);
+ return(ENXIO);
+ }
+
+ outb(port + UHA_SMASK, 0x81); /* make sure interrupts are enabled */
+ uha_data[unit].flags |= UHA_INIT;
+ return(0);
+}
+
+
+
+#ifndef min
+#define min(x,y) (x < y ? x : y)
+#endif min
+
+
+void uhaminphys(bp)
+struct buf *bp;
+{
+#ifdef MACH
+#if !defined(OSF)
+ bp->b_flags |= B_NPAGES; /* can support scat/gather */
+#endif /* defined(OSF) */
+#endif MACH
+ if(bp->b_bcount > ((UHA_NSEG-1) * PAGESIZ))
+ {
+ bp->b_bcount = ((UHA_NSEG-1) * PAGESIZ);
+ }
+}
+
+/***********************************************\
+* start a scsi operation given the command and *
+* the data address. Also needs the unit, target *
+* and lu *
+\***********************************************/
+int uha_scsi_cmd(xs)
+struct scsi_xfer *xs;
+{
+ struct scsi_sense_data *s1,*s2;
+ struct mscp *mscp;
+ struct uha_dma_seg *sg;
+ int seg; /* scatter gather seg being worked on */
+ int i = 0;
+ int rc = 0;
+ int thiskv;
+ unsigned long int thisphys,nextphys;
+ int unit =xs->adapter;
+ int bytes_this_seg,bytes_this_page,datalen,flags;
+ struct iovec *iovp;
+ int s;
+ unsigned int stat;
+ int port = uha_data[unit].baseport;
+ unsigned long int templen;
+
+
+ if(scsi_debug & PRINTROUTINES)
+ printf("uha_scsi_cmd ");
+ /***********************************************\
+ * get a mscp (mbox-out) to use. If the transfer *
+ * is from a buf (possibly from interrupt time) *
+ * then we can't allow it to sleep *
+ \***********************************************/
+ flags = xs->flags;
+ if(xs->bp) flags |= (SCSI_NOSLEEP); /* just to be sure */
+ if(flags & ITSDONE)
+ {
+ printf("Already done?");
+ xs->flags &= ~ITSDONE;
+ }
+ if(!(flags & INUSE))
+ {
+ printf("Not in use?");
+ xs->flags |= INUSE;
+ }
+ if (!(mscp = uha_get_mscp(unit,flags)))
+ {
+ xs->error = XS_DRIVER_STUFFUP;
+ return(TRY_AGAIN_LATER);
+ }
+
+cheat = mscp;
+ if(uha_debug & UHA_SHOWMSCPS)
+ printf("<start mscp(%x)>",mscp);
+ if(scsi_debug & SHOWCOMMANDS)
+ {
+ uha_show_scsi_cmd(xs);
+ }
+ mscp->xs = xs;
+ /***********************************************\
+ * Put all the arguments for the xfer in the mscp *
+ \***********************************************/
+
+ if (flags & SCSI_RESET)
+ {
+ mscp->opcode = 0x04;
+ mscp->ca = 0x01;
+ }
+ else
+ {
+ mscp->opcode = 0x02;
+ mscp->ca = 0x01;
+ }
+
+ if (flags & SCSI_DATA_IN)
+ {
+ mscp->xdir = 0x01;
+ }
+ if (flags & SCSI_DATA_OUT)
+ {
+ mscp->xdir = 0x02;
+ }
+
+ if (xs->lu != 0)
+ {
+ xs->error = XS_DRIVER_STUFFUP;
+ uha_free_mscp(unit,mscp,flags);
+ return(HAD_ERROR);
+ }
+
+ mscp->dcn = 0x00;
+ mscp->chan = 0x00;
+ mscp->target = xs->targ;
+ mscp->lun = xs->lu;
+ mscp->link.addr[0] = 0x00;
+ mscp->link.addr[1] = 0x00;
+ mscp->link.addr[2] = 0x00;
+ mscp->link.addr[3] = 0x00;
+ mscp->link_id = 0x00;
+ mscp->cdblen = xs->cmdlen;
+ scratch = KVTOPHYS(&(mscp->mscp_sense));
+ mscp->sense.addr[0] = (scratch & 0xff);
+ mscp->sense.addr[1] = ((scratch >> 8) & 0xff);
+ mscp->sense.addr[2] = ((scratch >> 16) & 0xff);
+ mscp->sense.addr[3] = ((scratch >> 24) & 0xff);
+ mscp->senselen = sizeof(mscp->mscp_sense);
+ mscp->ha_status = 0x00;
+ mscp->targ_status = 0x00;
+
+ if(xs->datalen)
+ { /* should use S/G only if not zero length */
+ scratch = KVTOPHYS(mscp->uha_dma);
+ mscp->data.addr[0] = (scratch & 0xff);
+ mscp->data.addr[1] = ((scratch >> 8) & 0xff);
+ mscp->data.addr[2] = ((scratch >> 16) & 0xff);
+ mscp->data.addr[3] = ((scratch >> 24) & 0xff);
+ sg = mscp->uha_dma ;
+ seg = 0;
+ mscp->sgth = 0x01;
+
+ if(flags & SCSI_DATA_UIO)
+ {
+ iovp = ((struct uio *)xs->data)->uio_iov;
+ datalen = ((struct uio *)xs->data)->uio_iovcnt;
+ xs->datalen = 0;
+ while ((datalen) && (seg < UHA_NSEG))
+ {
+ scratch = (unsigned long)iovp->iov_base;
+ sg->addr.addr[0] = (scratch & 0xff);
+ sg->addr.addr[1] = ((scratch >> 8) & 0xff);
+ sg->addr.addr[2] = ((scratch >> 16) & 0xff);
+ sg->addr.addr[3] = ((scratch >> 24) & 0xff);
+ xs->datalen += *(unsigned long *)sg->len.len = iovp->iov_len;
+ if(scsi_debug & SHOWSCATGATH)
+ printf("(0x%x@0x%x)"
+ ,iovp->iov_len
+ ,iovp->iov_base);
+ sg++;
+ iovp++;
+ seg++;
+ datalen--;
+ }
+ }
+ else
+ {
+ /***********************************************\
+ * Set up the scatter gather block *
+ \***********************************************/
+
+ if(scsi_debug & SHOWSCATGATH)
+ printf("%d @0x%x:- ",xs->datalen,xs->data);
+ datalen = xs->datalen;
+ thiskv = (int)xs->data;
+ thisphys = KVTOPHYS(thiskv);
+ templen = 0;
+
+ while ((datalen) && (seg < UHA_NSEG))
+ {
+ bytes_this_seg = 0;
+
+ /* put in the base address */
+ sg->addr.addr[0] = (thisphys & 0xff);
+ sg->addr.addr[1] = ((thisphys >> 8) & 0xff);
+ sg->addr.addr[2] = ((thisphys >> 16) & 0xff);
+ sg->addr.addr[3] = ((thisphys >> 24) & 0xff);
+
+ if(scsi_debug & SHOWSCATGATH)
+ printf("0x%x",thisphys);
+
+ /* do it at least once */
+ nextphys = thisphys;
+ while ((datalen) && (thisphys == nextphys))
+ /*********************************************\
+ * This page is contiguous (physically) with *
+ * the the last, just extend the length *
+ \*********************************************/
+ {
+ /* how far to the end of the page */
+ nextphys = (thisphys & (~(PAGESIZ - 1)))
+ + PAGESIZ;
+ bytes_this_page = nextphys - thisphys;
+ /**** or the data ****/
+ bytes_this_page = min(bytes_this_page
+ ,datalen);
+ bytes_this_seg += bytes_this_page;
+ datalen -= bytes_this_page;
+
+ /* get more ready for the next page */
+ thiskv = (thiskv & (~(PAGESIZ - 1)))
+ + PAGESIZ;
+ if(datalen)
+ thisphys = KVTOPHYS(thiskv);
+ }
+ /********************************************\
+ * next page isn't contiguous, finish the seg *
+ \********************************************/
+ if(scsi_debug & SHOWSCATGATH)
+ printf("(0x%x)",bytes_this_seg);
+ sg->len.len[0] = (bytes_this_seg & 0xff);
+ sg->len.len[1] = ((bytes_this_seg >> 8) & 0xff);
+ sg->len.len[2] = ((bytes_this_seg >> 16) & 0xff);
+ sg->len.len[3] = ((bytes_this_seg >> 24) & 0xff);
+ templen += bytes_this_seg;
+ sg++;
+ seg++;
+ }
+ } /*end of iov/kv decision */
+ mscp->datalen.len[0] = (templen & 0xff);
+ mscp->datalen.len[1] = ((templen >> 8) & 0xff);
+ mscp->datalen.len[2] = ((templen >> 16) & 0xff);
+ mscp->datalen.len[3] = ((templen >> 24) & 0xff);
+ mscp->sg_num = seg;
+
+ if(scsi_debug & SHOWSCATGATH)
+ printf("\n");
+ if (datalen)
+ { /* there's still data, must have run out of segs! */
+ printf("uha_scsi_cmd%d: more than %d DMA segs\n",
+ unit,UHA_NSEG);
+ xs->error = XS_DRIVER_STUFFUP;
+ uha_free_mscp(unit,mscp,flags);
+ return(HAD_ERROR);
+ }
+
+ }
+ else
+ { /* No data xfer, use non S/G values */
+ mscp->data.addr[0] = 0x00;
+ mscp->data.addr[1] = 0x00;
+ mscp->data.addr[2] = 0x00;
+ mscp->data.addr[3] = 0x00;
+ mscp->datalen.len[0] = 0x00;
+ mscp->datalen.len[1] = 0x00;
+ mscp->datalen.len[2] = 0x00;
+ mscp->datalen.len[3] = 0x00;
+ mscp->xdir = 0x03;
+ mscp->sgth = 0x00;
+ mscp->sg_num = 0x00;
+ }
+
+ /***********************************************\
+ * Put the scsi command in the mscp and start it *
+ \***********************************************/
+ bcopy(xs->cmd, mscp->cdb, xs->cmdlen);
+
+ /***********************************************\
+ * Usually return SUCCESSFULLY QUEUED *
+ \***********************************************/
+ if (!(flags & SCSI_NOMASK))
+ {
+ s = splbio();
+ uha_send_mbox(unit,mscp);
+ uha_add_timeout(mscp,xs->timeout);
+ splx(s);
+ if(scsi_debug & TRACEINTERRUPTS)
+ printf("cmd_sent ");
+ return(SUCCESSFULLY_QUEUED);
+ }
+ /***********************************************\
+ * If we can't use interrupts, poll on completion*
+ \***********************************************/
+ uha_send_mbox(unit,mscp);
+ if(scsi_debug & TRACEINTERRUPTS)
+ printf("cmd_wait ");
+ do
+ {
+ if(uha_poll(unit,xs->timeout))
+ {
+ if (!(xs->flags & SCSI_SILENT)) printf("cmd fail\n");
+ if(!(uha_abort(unit,mscp)))
+ {
+ printf("abort failed in wait\n");
+ uha_free_mscp(unit,mscp,flags);
+ }
+ xs->error = XS_DRIVER_STUFFUP;
+ splx(s);
+ return(HAD_ERROR);
+ }
+ } while (!(xs->flags & ITSDONE));/* something (?) else finished */
+ splx(s);
+scsi_debug = 0;uha_debug = 0;
+ if(xs->error)
+ {
+ return(HAD_ERROR);
+ }
+ return(COMPLETE);
+}
+
+/*
+ * +----------+ +----------+ +----------+
+ * uha_soonest--->| later |--->| later|--->| later|--->0
+ * | [Delta] | | [Delta] | | [Delta] |
+ * 0<---|sooner |<---|sooner |<---|sooner |<---uha_latest
+ * +----------+ +----------+ +----------+
+ *
+ * uha_furtherest = sum(Delta[1..n])
+ */
+uha_add_timeout(mscp,time)
+struct mscp *mscp;
+int time;
+{
+ int timeprev;
+ struct mscp *prev;
+ int s = splbio();
+
+ if(prev = uha_latest) /* yes, an assign */
+ {
+ timeprev = uha_furtherest;
+ }
+ else
+ {
+ timeprev = 0;
+ }
+ while(prev && (timeprev > time))
+ {
+ timeprev -= prev->delta;
+ prev = prev->sooner;
+ }
+ if(prev)
+ {
+ mscp->delta = time - timeprev;
+ if( mscp->later = prev->later) /* yes an assign */
+ {
+ mscp->later->sooner = mscp;
+ mscp->later->delta -= mscp->delta;
+ }
+ else
+ {
+ uha_furtherest = time;
+ uha_latest = mscp;
+ }
+ mscp->sooner = prev;
+ prev->later = mscp;
+ }
+ else
+ {
+ if( mscp->later = uha_soonest) /* yes, an assign*/
+ {
+ mscp->later->sooner = mscp;
+ mscp->later->delta -= time;
+ }
+ else
+ {
+ uha_furtherest = time;
+ uha_latest = mscp;
+ }
+ mscp->delta = time;
+ mscp->sooner = (struct mscp *)0;
+ uha_soonest = mscp;
+ }
+ splx(s);
+}
+
+uha_remove_timeout(mscp)
+struct mscp *mscp;
+{
+ int s = splbio();
+
+ if(mscp->sooner)
+ {
+ mscp->sooner->later = mscp->later;
+ }
+ else
+ {
+ uha_soonest = mscp->later;
+ }
+ if(mscp->later)
+ {
+ mscp->later->sooner = mscp->sooner;
+ mscp->later->delta += mscp->delta;
+ }
+ else
+ {
+ uha_latest = mscp->sooner;
+ uha_furtherest -= mscp->delta;
+ }
+ mscp->sooner = mscp->later = (struct mscp *)0;
+ splx(s);
+}
+
+
+extern int hz;
+#define ONETICK 500 /* milliseconds */
+#define SLEEPTIME ((hz * 1000) / ONETICK)
+uha_timeout(arg)
+int arg;
+{
+ struct mscp *mscp;
+ int unit;
+ int s = splbio();
+ unsigned int stat;
+ int port = uha_data[unit].baseport;
+
+ while( mscp = uha_soonest )
+ {
+ if(mscp->delta <= ONETICK)
+ /***********************************************\
+ * It has timed out, we need to do some work *
+ \***********************************************/
+ {
+ unit = mscp->xs->adapter;
+ printf("uha%d:%d device timed out\n",unit
+ ,mscp->xs->targ);
+ if(uha_debug & UHA_SHOWMSCPS)
+ uha_print_active_mscp();
+
+ /***************************************\
+ * Unlink it from the queue *
+ \***************************************/
+ uha_remove_timeout(mscp);
+
+ if((uha_abort(unit,mscp) !=1) || (mscp->flags = MSCP_ABORTED))
+ {
+ printf("AGAIN");
+ mscp->xs->retries = 0; /* I MEAN IT ! */
+ uha_done(unit,mscp,FAIL);
+ }
+ else /* abort the operation that has timed out */
+ {
+ printf("\n");
+ uha_add_timeout(mscp,2000 + ONETICK);
+ mscp->flags = MSCP_ABORTED;
+ }
+ }
+ else
+ /***********************************************\
+ * It has not timed out, adjust and leave *
+ \***********************************************/
+ {
+ mscp->delta -= ONETICK;
+ uha_furtherest -= ONETICK;
+ break;
+ }
+ }
+ splx(s);
+ timeout(uha_timeout,arg,SLEEPTIME);
+}
+
+uha_show_scsi_cmd(struct scsi_xfer *xs)
+{
+ u_char *b = (u_char *)xs->cmd;
+ int i = 0;
+ if(!(xs->flags & SCSI_RESET))
+ {
+ printf("uha%d:%d:%d-"
+ ,xs->adapter
+ ,xs->targ
+ ,xs->lu);
+ while(i < xs->cmdlen )
+ {
+ if(i) printf(",");
+ printf("%x",b[i++]);
+ }
+ printf("-\n");
+ }
+ else
+ {
+ printf("uha%d:%d:%d-RESET-\n"
+ ,xs->adapter
+ ,xs->targ
+ ,xs->lu
+ );
+ }
+}
+uha_print_mscp(mscp)
+struct mscp *mscp;
+{
+ printf("mscp:%x op:%x cmdlen:%d senlen:%d\n"
+ ,mscp
+ ,mscp->opcode
+ ,mscp->cdblen
+ ,mscp->senselen);
+ printf(" sg:%d sgnum:%x datlen:%d hstat:%x tstat:%x delta:%d flags:%x\n"
+ ,mscp->sgth
+ ,mscp->sg_num
+ ,mscp->datalen
+ ,mscp->ha_status
+ ,mscp->targ_status
+ ,mscp->delta
+ ,mscp->flags);
+ uha_show_scsi_cmd(mscp->xs);
+}
+
+uha_print_active_mscp()
+{
+ struct mscp *mscp;
+ mscp = uha_soonest;
+
+ while(mscp)
+ {
+ uha_print_mscp(mscp);
+ mscp = mscp->later;
+ }
+ printf("Furtherest = %d\n",uha_furtherest);
+}
diff --git a/sys/i386/isa/vector.s b/sys/i386/isa/vector.s
new file mode 100644
index 0000000..38ac79c
--- /dev/null
+++ b/sys/i386/isa/vector.s
@@ -0,0 +1,376 @@
+/* vector.s */
+/*
+ * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE
+ * -------------------- ----- ----------------------
+ * CURRENT PATCH LEVEL: 1 00167
+ * -------------------- ----- ----------------------
+ *
+ * 04 Jun 93 Bruce Evans Fixed irq_num vs id_num for multiple
+ * devices configed on the same irq with
+ * respect to ipending.
+ *
+ */
+
+#include "i386/isa/icu.h"
+#include "i386/isa/isa.h"
+#include "vector.h"
+
+#define ICU_EOI 0x20 /* XXX - define elsewhere */
+
+#define IRQ_BIT(irq_num) (1 << ((irq_num) % 8))
+#define IRQ_BYTE(irq_num) ((irq_num) / 8)
+
+#define ENABLE_ICU1 \
+ movb $ICU_EOI,%al ; /* as soon as possible send EOI ... */ \
+ FASTER_NOP ; /* ... ASAP ... */ \
+ outb %al,$IO_ICU1 /* ... to clear in service bit */
+#ifdef AUTO_EOI_1
+#undef ENABLE_ICU1 /* we now use auto-EOI to reduce i/o */
+#define ENABLE_ICU1
+#endif
+
+#define ENABLE_ICU1_AND_2 \
+ movb $ICU_EOI,%al ; /* as above */ \
+ FASTER_NOP ; \
+ outb %al,$IO_ICU2 ; /* but do second icu first */ \
+ FASTER_NOP ; \
+ outb %al,$IO_ICU1 /* then first icu */
+#ifdef AUTO_EOI_2
+#undef ENABLE_ICU1_AND_2 /* data sheet says no auto-EOI on slave ... */
+#define ENABLE_ICU1_AND_2 /* ... but it works */
+#endif
+
+/*
+ * Macros for interrupt interrupt entry, call to handler, and exit.
+ *
+ * XXX - the interrupt frame is set up to look like a trap frame. This is
+ * usually a waste of time. The only interrupt handlers that want a frame
+ * are the clock handler (it wants a clock frame), the npx handler (it's
+ * easier to do right all in assembler). The interrupt return routine
+ * needs a trap frame for rare AST's (it could easily convert the frame).
+ * The direct costs of setting up a trap frame are two pushl's (error
+ * code and trap number), an addl to get rid of these, and pushing and
+ * popping the call-saved regs %esi, %edi and %ebp twice, The indirect
+ * costs are making the driver interface nonuniform so unpending of
+ * interrupts is more complicated and slower (call_driver(unit) would
+ * be easier than ensuring an interrupt frame for all handlers. Finally,
+ * there are some struct copies in the npx handler and maybe in the clock
+ * handler that could be avoided by working more with pointers to frames
+ * instead of frames.
+ *
+ * XXX - should we do a cld on every system entry to avoid the requirement
+ * for scattered cld's?
+ *
+ * Coding notes for *.s:
+ *
+ * If possible, avoid operations that involve an operand size override.
+ * Word-sized operations might be smaller, but the operand size override
+ * makes them slower on on 486's and no faster on 386's unless perhaps
+ * the instruction pipeline is depleted. E.g.,
+ *
+ * Use movl to seg regs instead of the equivalent but more descriptive
+ * movw - gas generates an irelevant (slower) operand size override.
+ *
+ * Use movl to ordinary regs in preference to movw and especially
+ * in preference to movz[bw]l. Use unsigned (long) variables with the
+ * top bits clear instead of unsigned short variables to provide more
+ * opportunities for movl.
+ *
+ * If possible, use byte-sized operations. They are smaller and no slower.
+ *
+ * Use (%reg) instead of 0(%reg) - gas generates larger code for the latter.
+ *
+ * If the interrupt frame is made more flexible, INTR can push %eax first
+ * and decide the ipending case with less overhead, e.g., by avoiding
+ * loading segregs.
+ */
+
+#define FAST_INTR(unit, irq_num, id_num, handler, enable_icus) \
+ pushl %eax ; /* save only call-used registers */ \
+ pushl %ecx ; \
+ pushl %edx ; \
+ pushl %ds ; \
+ /* pushl %es ; know compiler doesn't do string insns */ \
+ movl $KDSEL,%eax ; \
+ movl %ax,%ds ; \
+ /* movl %ax,%es ; */ \
+ SHOW_CLI ; /* although it interferes with "ASAP" */ \
+ pushl $unit ; \
+ call handler ; /* do the work ASAP */ \
+ enable_icus ; /* (re)enable ASAP (helps edge trigger?) */ \
+ addl $4,%esp ; \
+ incl _cnt+V_INTR ; /* book-keeping can wait */ \
+ COUNT_EVENT(_intrcnt_actv, id_num) ; \
+ SHOW_STI ; \
+ /* popl %es ; */ \
+ popl %ds ; \
+ popl %edx; \
+ popl %ecx; \
+ popl %eax; \
+ iret
+
+#define INTR(unit, irq_num, id_num, mask, handler, icu, enable_icus, reg, stray) \
+ pushl $0 ; /* dummy error code */ \
+ pushl $T_ASTFLT ; \
+ pushal ; \
+ pushl %ds ; /* save our data and extra segments ... */ \
+ pushl %es ; \
+ movl $KDSEL,%eax ; /* ... and reload with kernel's own ... */ \
+ movl %ax,%ds ; /* ... early in case SHOW_A_LOT is on */ \
+ movl %ax,%es ; \
+ SHOW_CLI ; /* interrupt did an implicit cli */ \
+ movb _imen + IRQ_BYTE(irq_num),%al ; \
+ orb $IRQ_BIT(irq_num),%al ; \
+ movb %al,_imen + IRQ_BYTE(irq_num) ; \
+ SHOW_IMEN ; \
+ FASTER_NOP ; \
+ outb %al,$icu+1 ; \
+ enable_icus ; \
+ incl _cnt+V_INTR ; /* tally interrupts */ \
+ movl _cpl,%eax ; \
+ testb $IRQ_BIT(irq_num),%reg ; \
+ jne 2f ; \
+1: ; \
+ COUNT_EVENT(_intrcnt_actv, id_num) ; \
+ movl _cpl,%eax ; \
+ pushl %eax ; \
+ pushl $unit ; \
+ orl mask,%eax ; \
+ movl %eax,_cpl ; \
+ SHOW_CPL ; \
+ SHOW_STI ; \
+ sti ; \
+ call handler ; \
+ movb _imen + IRQ_BYTE(irq_num),%al ; \
+ andb $~IRQ_BIT(irq_num),%al ; \
+ movb %al,_imen + IRQ_BYTE(irq_num) ; \
+ SHOW_IMEN ; \
+ FASTER_NOP ; \
+ outb %al,$icu+1 ; \
+ jmp doreti ; \
+; \
+ ALIGN_TEXT ; \
+2: ; \
+ COUNT_EVENT(_intrcnt_pend, id_num) ; \
+ movl $1b,%eax ; /* register resume address */ \
+ /* XXX - someday do it at attach time */ \
+ movl %eax,Vresume + (irq_num) * 4 ; \
+ orb $IRQ_BIT(irq_num),_ipending + IRQ_BYTE(irq_num) ; \
+ SHOW_IPENDING ; \
+ popl %es ; \
+ popl %ds ; \
+ popal ; \
+ addl $4+4,%esp ; \
+ iret
+
+/*
+ * vector.h has defined a macro 'BUILD_VECTORS' containing a big list of info
+ * about vectors, including a submacro 'BUILD_VECTOR' that operates on the
+ * info about each vector. We redefine 'BUILD_VECTOR' to expand the info
+ * in different ways. Here we expand it to a list of interrupt handlers.
+ * This order is of course unimportant. Elsewhere we expand it to inline
+ * linear search code for which the order is a little more important and
+ * concatenating the code with no holes is very important.
+ *
+ * XXX - now there is BUILD_FAST_VECTOR as well as BUILD_VECTOR.
+ *
+ * The info consists of the following items for each vector:
+ *
+ * name (identifier): name of the vector; used to build labels
+ * unit (expression): unit number to call the device driver with
+ * irq_num (number): number of the IRQ to handled (0-15)
+ * id_num (number): uniq numeric id for handler (assigned by config)
+ * mask (blank-ident): priority mask used
+ * handler (blank-ident): interrupt handler to call
+ * icu_num (number): (1 + irq_num / 8) converted for label building
+ * icu_enables (number): 1 for icu_num == 1, 1_AND_2 for icu_num == 2
+ * reg (blank-ident): al for icu_num == 1, ah for icu_num == 2
+ *
+ * 'irq_num' is converted in several ways at config time to get around
+ * limitations in cpp. The macros have blanks after commas iff they would
+ * not mess up identifiers and numbers.
+ */
+
+#undef BUILD_FAST_VECTOR
+#define BUILD_FAST_VECTOR(name, unit, irq_num, id_num, mask, handler, \
+ icu_num, icu_enables, reg) \
+ .globl handler ; \
+ .text ; \
+ .globl _V/**/name ; \
+ SUPERALIGN_TEXT ; \
+_V/**/name: ; \
+ FAST_INTR(unit, irq_num, id_num, handler, ENABLE_ICU/**/icu_enables)
+
+#undef BUILD_VECTOR
+#define BUILD_VECTOR(name, unit, irq_num, id_num, mask, handler, \
+ icu_num, icu_enables, reg) \
+ .globl handler ; \
+ .text ; \
+ .globl _V/**/name ; \
+ SUPERALIGN_TEXT ; \
+_V/**/name: ; \
+ INTR(unit,irq_num,id_num, mask, handler, IO_ICU/**/icu_num, \
+ ENABLE_ICU/**/icu_enables, reg,)
+
+ BUILD_VECTORS
+
+ /* hardware interrupt catcher (IDT 32 - 47) */
+ .globl _isa_strayintr
+
+#define STRAYINTR(irq_num, icu_num, icu_enables, reg) \
+IDTVEC(intr/**/irq_num) ; \
+ INTR(irq_num,irq_num,irq_num, _highmask, _isa_strayintr, \
+ IO_ICU/**/icu_num, ENABLE_ICU/**/icu_enables, reg,stray)
+
+/*
+ * XXX - the mask (1 << 2) == IRQ_SLAVE will be generated for IRQ 2, instead
+ * of the mask IRQ2 (defined as IRQ9 == (1 << 9)). But IRQ 2 "can't happen".
+ * In fact, all stray interrupts "can't happen" except for bugs. The
+ * "stray" IRQ 7 is documented behaviour of the 8259. It happens when there
+ * is a glitch on any of its interrupt inputs. Does it really interrupt when
+ * IRQ 7 is masked?
+ *
+ * XXX - unpend doesn't work for these, it sends them to the real handler.
+ *
+ * XXX - the race bug during initialization may be because I changed the
+ * order of switching from the stray to the real interrupt handler to before
+ * enabling interrupts. The old order looked unsafe but maybe it is OK with
+ * the stray interrupt handler installed. But these handlers only reduce
+ * the window of vulnerability - it is still open at the end of
+ * isa_configure().
+ *
+ * XXX - many comments are stale.
+ */
+
+ STRAYINTR(0,1,1, al)
+ STRAYINTR(1,1,1, al)
+ STRAYINTR(2,1,1, al)
+ STRAYINTR(3,1,1, al)
+ STRAYINTR(4,1,1, al)
+ STRAYINTR(5,1,1, al)
+ STRAYINTR(6,1,1, al)
+ STRAYINTR(8,2,1_AND_2, ah)
+ STRAYINTR(9,2,1_AND_2, ah)
+ STRAYINTR(10,2,1_AND_2, ah)
+ STRAYINTR(11,2,1_AND_2, ah)
+ STRAYINTR(12,2,1_AND_2, ah)
+ STRAYINTR(13,2,1_AND_2, ah)
+ STRAYINTR(14,2,1_AND_2, ah)
+ STRAYINTR(15,2,1_AND_2, ah)
+IDTVEC(intrdefault)
+ STRAYINTR(7,1,1, al) /* XXX */
+#if 0
+ INTRSTRAY(255, _highmask, 255) ; call _isa_strayintr ; INTREXIT2
+#endif
+/*
+ * These are the interrupt counters, I moved them here from icu.s so that
+ * they are with the name table. rgrimes
+ *
+ * There are now lots of counters, this has been redone to work with
+ * Bruce Evans intr-0.1 code, which I modified some more to make it all
+ * work with vmstat.
+ */
+ .data
+Vresume: .space 16 * 4 /* where to resume intr handler after unpend */
+ .globl _intrcnt
+_intrcnt: /* used by vmstat to calc size of table */
+ .globl _intrcnt_bad7
+_intrcnt_bad7: .space 4 /* glitches on irq 7 */
+ .globl _intrcnt_bad15
+_intrcnt_bad15: .space 4 /* glitches on irq 15 */
+ .globl _intrcnt_stray
+_intrcnt_stray: .space 4 /* total count of stray interrupts */
+ .globl _intrcnt_actv
+_intrcnt_actv: .space NR_REAL_INT_HANDLERS * 4 /* active interrupts */
+ .globl _intrcnt_pend
+_intrcnt_pend: .space NR_REAL_INT_HANDLERS * 4 /* pending interrupts */
+ .globl _intrcnt_spl
+_intrcnt_spl: .space 32 * 4 /* XXX 32 should not be hard coded ? */
+ .globl _intrcnt_show
+_intrcnt_show: .space 8 * 4 /* XXX 16 should not be hard coded ? */
+ .globl _eintrcnt
+_eintrcnt: /* used by vmstat to calc size of table */
+
+/*
+ * Build the interrupt name table for vmstat
+ */
+
+#undef BUILD_FAST_VECTOR
+#define BUILD_FAST_VECTOR BUILD_VECTOR
+
+#undef BUILD_VECTOR
+#define BUILD_VECTOR(name, unit, irq_num, id_num, mask, handler, \
+ icu_num, icu_enables, reg) \
+ .ascii "name irq" ; \
+ .asciz "irq_num"
+/*
+ * XXX - use the STRING and CONCAT macros from <sys/cdefs.h> to stringize
+ * and concatenate names above and elsewhere.
+ */
+
+ .text
+ .globl _intrnames, _eintrnames
+_intrnames:
+ BUILD_VECTOR(bad,,7,,,,,,)
+ BUILD_VECTOR(bad,,15,,,,,,)
+ BUILD_VECTOR(stray,,,,,,,,)
+ BUILD_VECTORS
+
+#undef BUILD_FAST_VECTOR
+#define BUILD_FAST_VECTOR BUILD_VECTOR
+
+#undef BUILD_VECTOR
+#define BUILD_VECTOR(name, unit, irq_num, id_num, mask, handler, \
+ icu_num, icu_enables, reg) \
+ .asciz "name pend"
+
+ BUILD_VECTORS
+
+/*
+ * now the spl names
+ */
+ .asciz "unpend_v"
+ .asciz "doreti"
+ .asciz "p0!ni"
+ .asciz "!p0!ni"
+ .asciz "p0ni"
+ .asciz "netisr_raw"
+ .asciz "netisr_ip"
+ .asciz "netisr_imp"
+ .asciz "netisr_ns"
+ .asciz "softclock"
+ .asciz "trap"
+ .asciz "doreti_exit2"
+ .asciz "splbio"
+ .asciz "splclock"
+ .asciz "splhigh"
+ .asciz "splimp"
+ .asciz "splnet"
+ .asciz "splsoftclock"
+ .asciz "spltty"
+ .asciz "spl0"
+ .asciz "netisr_raw2"
+ .asciz "netisr_ip2"
+ .asciz "splx"
+ .asciz "splx!0"
+ .asciz "unpend_V"
+ .asciz "spl25" /* spl25-spl31 are spares */
+ .asciz "spl26"
+ .asciz "spl27"
+ .asciz "spl28"
+ .asciz "spl29"
+ .asciz "spl30"
+ .asciz "spl31"
+/*
+ * now the mask names
+ */
+ .asciz "cli"
+ .asciz "cpl"
+ .asciz "imen"
+ .asciz "ipending"
+ .asciz "sti"
+ .asciz "mask5" /* mask5-mask7 are spares */
+ .asciz "mask6"
+ .asciz "mask7"
+
+_eintrnames:
diff --git a/sys/i386/isa/wd.c b/sys/i386/isa/wd.c
new file mode 100644
index 0000000..bbec45b
--- /dev/null
+++ b/sys/i386/isa/wd.c
@@ -0,0 +1,1352 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * 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:@(#)wd.c 7.2 (Berkeley) 5/9/91
+ *
+ * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE
+ * -------------------- ----- ----------------------
+ * CURRENT PATCH LEVEL: 6 00155
+ * -------------------- ----- ----------------------
+ *
+ * 17 Sep 92 Frank Maclachlan Fixed I/O error reporting on raw device
+ * 31 Jul 92 Christoph Robitschko Fixed second disk recognition,
+ * bzero of malloced memory for warm
+ * boot problem.
+ * 19 Aug 92 Frank Maclachlan Fixed bug when first sector of a
+ * multisector read is in bad144 table.
+ * 17 Jan 93 B. Evans & A.Chernov Fixed bugs from previous patches,
+ * driver initialization, and cylinder
+ * boundary conditions.
+ * 28 Mar 93 Charles Hannum Add missing splx calls.
+ * 20 Apr 93 Terry Lee Always report disk errors
+ * 20 Apr 93 Brett Lymn Change infinite while loops to
+ * timeouts
+ * 17 May 93 Rodney W. Grimes Fixed all 1000000 to use WDCTIMEOUT,
+ * and increased to 1000000*10 for new
+ * intr-0.1 code.
+ */
+
+/* TODO:peel out buffer at low ipl, speed improvement */
+
+
+#include "wd.h"
+#if NWD > 0
+
+#include "param.h"
+#include "dkbad.h"
+#include "systm.h"
+#include "conf.h"
+#include "file.h"
+#include "stat.h"
+#include "ioctl.h"
+#include "disklabel.h"
+#include "buf.h"
+#include "uio.h"
+#include "malloc.h"
+#include "machine/cpu.h"
+#include "i386/isa/isa_device.h"
+#include "i386/isa/icu.h"
+#include "i386/isa/wdreg.h"
+#include "syslog.h"
+#include "vm/vm.h"
+
+#define _NWD (NWD - 1) /* One is for the controller XXX 31 Jul 92*/
+
+#ifndef WDCTIMEOUT
+#define WDCTIMEOUT 10000000 /* arbitrary timeout for drive ready waits */
+#endif
+
+#define RETRIES 5 /* number of retries before giving up */
+#define MAXTRANSFER 32 /* max size of transfer in page clusters */
+
+#define wdnoreloc(dev) (minor(dev) & 0x80) /* ignore partition table */
+#define wddospart(dev) (minor(dev) & 0x40) /* use dos partitions */
+#define wdunit(dev) ((minor(dev) & 0x38) >> 3)
+#define wdpart(dev) (minor(dev) & 0x7)
+#define makewddev(maj, unit, part) (makedev(maj,((unit<<3)+part)))
+#define WDRAW 3 /* 'd' partition isn't a partition! */
+
+#define b_cylin b_resid /* cylinder number for doing IO to */
+ /* shares an entry in the buf struct */
+
+/*
+ * Drive states. Used to initialize drive.
+ */
+
+#define CLOSED 0 /* disk is closed. */
+#define WANTOPEN 1 /* open requested, not started */
+#define RECAL 2 /* doing restore */
+#define OPEN 3 /* done with open */
+
+/*
+ * The structure of a disk drive.
+ */
+struct disk {
+ long dk_bc; /* byte count left */
+ short dk_skip; /* blocks already transferred */
+ char dk_unit; /* physical unit number */
+ char dk_state; /* control state */
+ u_char dk_status; /* copy of status reg. */
+ u_char dk_error; /* copy of error reg. */
+ short dk_port; /* i/o port base */
+
+ u_long dk_copenpart; /* character units open on this drive */
+ u_long dk_bopenpart; /* block units open on this drive */
+ u_long dk_openpart; /* all units open on this drive */
+ short dk_wlabel; /* label writable? */
+ short dk_flags; /* drive characteistics found */
+#define DKFL_DOSPART 0x00001 /* has DOS partition table */
+#define DKFL_QUIET 0x00002 /* report errors back, but don't complain */
+#define DKFL_SINGLE 0x00004 /* sector at a time mode */
+#define DKFL_ERROR 0x00008 /* processing a disk error */
+#define DKFL_BSDLABEL 0x00010 /* has a BSD disk label */
+#define DKFL_BADSECT 0x00020 /* has a bad144 badsector table */
+#define DKFL_WRITEPROT 0x00040 /* manual unit write protect */
+ struct wdparams dk_params; /* ESDI/IDE drive/controller parameters */
+ struct disklabel dk_dd; /* device configuration data */
+ struct dos_partition
+ dk_dospartitions[NDOSPART]; /* DOS view of disk */
+ struct dkbad dk_bad; /* bad sector table */
+};
+
+struct disk *wddrives[_NWD]; /* table of units */
+struct buf wdtab;
+struct buf wdutab[_NWD]; /* head of queue per drive */
+struct buf rwdbuf[_NWD]; /* buffers for raw IO */
+long wdxfer[_NWD]; /* count of transfers */
+#ifdef WDDEBUG
+int wddebug;
+#endif
+
+struct isa_driver wddriver = {
+ wdprobe, wdattach, "wd",
+};
+
+void wdustart(struct disk *);
+void wdstart();
+int wdcommand(struct disk *, int);
+int wdcontrol(struct buf *);
+int wdsetctlr(dev_t, struct disk *);
+int wdgetctlr(int, struct disk *);
+
+/*
+ * Probe for controller.
+ */
+int
+wdprobe(struct isa_device *dvp)
+{
+ int unit = dvp->id_unit;
+ struct disk *du;
+ int wdc;
+
+ if (unit >= _NWD) /* 31 Jul 92*/
+ return(0);
+
+ if ((du = wddrives[unit]) == 0) {
+ du = wddrives[unit] = (struct disk *)
+ malloc (sizeof(struct disk), M_TEMP, M_NOWAIT);
+ bzero (du, sizeof(struct disk)); /* 31 Jul 92*/
+ du->dk_unit = unit;
+ }
+
+ wdc = du->dk_port = dvp->id_iobase;
+
+ /* check if we have registers that work */
+ outb(wdc+wd_cyl_lo, 0xa5) ; /* wd_cyl_lo is read/write */
+ if(inb(wdc+wd_cyl_lo) != 0xa5)
+ goto nodevice;
+
+ /* reset the device */
+ outb(wdc+wd_ctlr, (WDCTL_RST|WDCTL_IDS));
+ DELAY(1000);
+ outb(wdc+wd_ctlr, WDCTL_IDS);
+ DELAY(1000);
+
+ /* execute a controller only command */
+ if (wdcommand(du, WDCC_DIAGNOSE) < 0)
+ goto nodevice;
+
+ (void) inb(wdc+wd_error); /* XXX! */
+ outb(wdc+wd_ctlr, WDCTL_4BIT);
+ return (1);
+
+nodevice:
+ free(du, M_TEMP);
+ wddrives[unit] = 0;
+ return (0);
+}
+
+/*
+ * Attach each drive if possible.
+ */
+int
+wdattach(struct isa_device *dvp)
+{
+ int unit;
+/* int unit = dvp->id_unit;*/
+
+ for (unit=0; unit< _NWD; unit++) {
+ struct disk *du;
+ if ((du = wddrives[unit]) == 0) {
+ du = wddrives[unit] = (struct disk *)
+ malloc (sizeof(struct disk), M_TEMP, M_NOWAIT);
+ bzero (du, sizeof(struct disk));
+ du->dk_unit = unit;
+ du->dk_port = dvp->id_iobase;
+ }
+
+ /* print out description of drive, suppressing multiple blanks*/
+ if(wdgetctlr(unit, du) == 0) {
+ int i, blank;
+ char c;
+ printf(" %d:<", unit);
+ for (i = blank = 0 ; i < sizeof(du->dk_params.wdp_model); i++) {
+ char c = du->dk_params.wdp_model[i];
+
+ if (blank && c == ' ') continue;
+ if (blank && c != ' ') {
+ printf(" %c", c);
+ blank = 0;
+ continue;
+ }
+ if (c == ' ')
+ blank = 1;
+ else
+ printf("%c", c);
+ }
+ printf(">");
+ du->dk_unit = unit;
+ }
+ else {
+ /* old ST506 controller */
+ printf(" %d:<wdgetctlr failed, assuming OK>",
+ unit);
+ }
+ }
+ return(1);
+}
+
+/* Read/write routine for a buffer. Finds the proper unit, range checks
+ * arguments, and schedules the transfer. Does not wait for the transfer
+ * to complete. Multi-page transfers are supported. All I/O requests must
+ * be a multiple of a sector in length.
+ */
+int
+wdstrategy(register struct buf *bp)
+{
+ register struct buf *dp;
+ struct disklabel *lp;
+ register struct partition *p;
+ struct disk *du; /* Disk unit to do the IO. */
+ long maxsz, sz;
+ int unit = wdunit(bp->b_dev);
+ int s;
+
+ /* valid unit, controller, and request? */
+ if (unit >= _NWD || bp->b_blkno < 0 || (du = wddrives[unit]) == 0) {
+
+ bp->b_error = EINVAL;
+ bp->b_flags |= B_ERROR;
+ goto done;
+ }
+
+ /* "soft" write protect check */
+ if ((du->dk_flags & DKFL_WRITEPROT) && (bp->b_flags & B_READ) == 0) {
+ bp->b_error = EROFS;
+ bp->b_flags |= B_ERROR;
+ goto done;
+ }
+
+ /* have partitions and want to use them? */
+ if ((du->dk_flags & DKFL_BSDLABEL) != 0 && wdpart(bp->b_dev) != WDRAW) {
+
+ /*
+ * do bounds checking, adjust transfer. if error, process.
+ * if end of partition, just return
+ */
+ if (bounds_check_with_label(bp, &du->dk_dd, du->dk_wlabel) <= 0)
+ goto done;
+ /* otherwise, process transfer request */
+ }
+
+q:
+ /* queue transfer on drive, activate drive and controller if idle */
+ dp = &wdutab[unit];
+ s = splbio();
+ disksort(dp, bp);
+ if (dp->b_active == 0)
+ wdustart(du); /* start drive */
+ if (wdtab.b_active == 0)
+ wdstart(s); /* start controller */
+ splx(s);
+ return;
+
+done:
+ /* toss transfer, we're done early */
+ biodone(bp);
+}
+
+/*
+ * Routine to queue a command to the controller. The unit's
+ * request is linked into the active list for the controller.
+ * If the controller is idle, the transfer is started.
+ */
+static void
+wdustart(register struct disk *du)
+{
+ register struct buf *bp, *dp = &wdutab[du->dk_unit];
+
+ /* unit already active? */
+ if (dp->b_active)
+ return;
+
+ /* anything to start? */
+ bp = dp->b_actf;
+ if (bp == NULL)
+ return;
+
+ /* link onto controller queue */
+ dp->b_forw = NULL;
+ if (wdtab.b_actf == NULL)
+ wdtab.b_actf = dp;
+ else
+ wdtab.b_actl->b_forw = dp;
+ wdtab.b_actl = dp;
+
+ /* mark the drive unit as busy */
+ dp->b_active = 1;
+}
+
+/*
+ * Controller startup routine. This does the calculation, and starts
+ * a single-sector read or write operation. Called to start a transfer,
+ * or from the interrupt routine to continue a multi-sector transfer.
+ * RESTRICTIONS:
+ * 1. The transfer length must be an exact multiple of the sector size.
+ */
+
+static void
+wdstart()
+{
+ register struct disk *du; /* disk unit for IO */
+ register struct buf *bp;
+ struct disklabel *lp;
+ struct buf *dp;
+ register struct bt_bad *bt_ptr;
+ long blknum, pagcnt, cylin, head, sector;
+ long secpertrk, secpercyl, addr, i, timeout;
+ int unit, s, wdc;
+
+loop:
+ /* is there a drive for the controller to do a transfer with? */
+ dp = wdtab.b_actf;
+ if (dp == NULL)
+ return;
+
+ /* is there a transfer to this drive ? if so, link it on
+ the controller's queue */
+ bp = dp->b_actf;
+ if (bp == NULL) {
+ wdtab.b_actf = dp->b_forw;
+ goto loop;
+ }
+
+ /* obtain controller and drive information */
+ unit = wdunit(bp->b_dev);
+ du = wddrives[unit];
+
+ /* if not really a transfer, do control operations specially */
+ if (du->dk_state < OPEN) {
+ (void) wdcontrol(bp);
+ return;
+ }
+
+ /* calculate transfer details */
+ blknum = bp->b_blkno + du->dk_skip;
+/*if(wddebug)printf("bn%d ", blknum);*/
+#ifdef WDDEBUG
+ if (du->dk_skip == 0)
+ printf("\nwdstart %d: %s %d@%d; map ", unit,
+ (bp->b_flags & B_READ) ? "read" : "write",
+ bp->b_bcount, blknum);
+ else
+ printf(" %d)%x", du->dk_skip, inb(wdc+wd_altsts));
+#endif
+ addr = (int) bp->b_un.b_addr;
+ if (du->dk_skip == 0)
+ du->dk_bc = bp->b_bcount;
+
+ lp = &du->dk_dd;
+ secpertrk = lp->d_nsectors;
+ secpercyl = lp->d_secpercyl;
+ if ((du->dk_flags & DKFL_BSDLABEL) != 0 && wdpart(bp->b_dev) != WDRAW)
+ blknum += lp->d_partitions[wdpart(bp->b_dev)].p_offset;
+ cylin = blknum / secpercyl;
+ head = (blknum % secpercyl) / secpertrk;
+ sector = blknum % secpertrk;
+
+ /*
+ * See if the current block is in the bad block list.
+ * (If we have one, and not formatting.)
+ */
+ if ((du->dk_flags & (DKFL_SINGLE|DKFL_BADSECT)) /* 19 Aug 92*/
+ == (DKFL_SINGLE|DKFL_BADSECT))
+ for (bt_ptr = du->dk_bad.bt_bad; bt_ptr->bt_cyl != -1; bt_ptr++) {
+ if (bt_ptr->bt_cyl > cylin)
+ /* Sorted list, and we passed our cylinder. quit. */
+ break;
+ if (bt_ptr->bt_cyl == cylin &&
+ bt_ptr->bt_trksec == (head << 8) + sector) {
+ /*
+ * Found bad block. Calculate new block addr.
+ * This starts at the end of the disk (skip the
+ * last track which is used for the bad block list),
+ * and works backwards to the front of the disk.
+ */
+#ifdef WDDEBUG
+ printf("--- badblock code -> Old = %d; ",
+ blknum);
+#endif
+ blknum = lp->d_secperunit - lp->d_nsectors
+ - (bt_ptr - du->dk_bad.bt_bad) - 1;
+ cylin = blknum / secpercyl;
+ head = (blknum % secpercyl) / secpertrk;
+ sector = blknum % secpertrk;
+#ifdef WDDEBUG
+ printf("new = %d\n", blknum);
+#endif
+ break;
+ }
+ }
+/*if(wddebug)pg("c%d h%d s%d ", cylin, head, sector);*/
+ sector += 1; /* sectors begin with 1, not 0 */
+
+ wdtab.b_active = 1; /* mark controller active */
+ wdc = du->dk_port;
+
+RETRY:
+ /* if starting a multisector transfer, or doing single transfers */
+ if (du->dk_skip == 0 || (du->dk_flags & DKFL_SINGLE)) {
+ if (wdtab.b_errcnt && (bp->b_flags & B_READ) == 0)
+ du->dk_bc += DEV_BSIZE;
+
+ /* controller idle? */
+ timeout = 0;
+ while (inb(wdc+wd_status) & WDCS_BUSY)
+ {
+ if (++timeout > WDCTIMEOUT)
+ {
+ printf("wd.c: Controller busy too long!\n");
+ /* reset the device */
+ outb(wdc+wd_ctlr, (WDCTL_RST|WDCTL_IDS));
+ DELAY(1000);
+ outb(wdc+wd_ctlr, WDCTL_IDS);
+ DELAY(1000);
+ (void) inb(wdc+wd_error); /* XXX! */
+ outb(wdc+wd_ctlr, WDCTL_4BIT);
+ break;
+ }
+ }
+
+ /* stuff the task file */
+ outb(wdc+wd_precomp, lp->d_precompcyl / 4);
+#ifdef B_FORMAT
+ if (bp->b_flags & B_FORMAT) {
+ outb(wdc+wd_sector, lp->d_gap3);
+ outb(wdc+wd_seccnt, lp->d_nsectors);
+ } else {
+#endif
+ if (du->dk_flags & DKFL_SINGLE)
+ outb(wdc+wd_seccnt, 1);
+ else
+ outb(wdc+wd_seccnt, howmany(du->dk_bc, DEV_BSIZE));
+ outb(wdc+wd_sector, sector);
+
+#ifdef B_FORMAT
+ }
+#endif
+
+ outb(wdc+wd_cyl_lo, cylin);
+ outb(wdc+wd_cyl_hi, cylin >> 8);
+
+ /* set up the SDH register (select drive) */
+ outb(wdc+wd_sdh, WDSD_IBM | (unit<<4) | (head & 0xf));
+
+ /* wait for drive to become ready */
+ timeout = 0;
+ while ((inb(wdc+wd_status) & WDCS_READY) == 0)
+ {
+ if (++timeout > WDCTIMEOUT)
+ {
+ printf("wd.c: Drive busy too long!\n");
+ /* reset the device */
+ outb(wdc+wd_ctlr, (WDCTL_RST|WDCTL_IDS));
+ DELAY(1000);
+ outb(wdc+wd_ctlr, WDCTL_IDS);
+ DELAY(1000);
+ (void) inb(wdc+wd_error); /* XXX! */
+ outb(wdc+wd_ctlr, WDCTL_4BIT);
+ goto RETRY;
+ }
+ }
+
+ /* initiate command! */
+#ifdef B_FORMAT
+ if (bp->b_flags & B_FORMAT)
+ outb(wdc+wd_command, WDCC_FORMAT);
+ else
+#endif
+ outb(wdc+wd_command,
+ (bp->b_flags & B_READ)? WDCC_READ : WDCC_WRITE);
+#ifdef WDDEBUG
+ printf("sector %d cylin %d head %d addr %x sts %x\n",
+ sector, cylin, head, addr, inb(wdc+wd_altsts));
+#endif
+ }
+
+ /* if this is a read operation, just go away until it's done. */
+ if (bp->b_flags & B_READ) return;
+
+ /* ready to send data? */
+ timeout = 0;
+ while ((inb(wdc+wd_status) & WDCS_DRQ) == 0)
+ {
+ if (++timeout > WDCTIMEOUT)
+ {
+ printf("wd.c: Drive not ready for too long!\n");
+ /* reset the device */
+ outb(wdc+wd_ctlr, (WDCTL_RST|WDCTL_IDS));
+ DELAY(1000);
+ outb(wdc+wd_ctlr, WDCTL_IDS);
+ DELAY(1000);
+ (void) inb(wdc+wd_error); /* XXX! */
+ outb(wdc+wd_ctlr, WDCTL_4BIT);
+ goto RETRY;
+ }
+ }
+
+ /* then send it! */
+ outsw (wdc+wd_data, addr+du->dk_skip * DEV_BSIZE,
+ DEV_BSIZE/sizeof(short));
+ du->dk_bc -= DEV_BSIZE;
+}
+
+/* Interrupt routine for the controller. Acknowledge the interrupt, check for
+ * errors on the current operation, mark it done if necessary, and start
+ * the next request. Also check for a partially done transfer, and
+ * continue with the next chunk if so.
+ */
+void
+wdintr(struct intrframe wdif)
+{
+ register struct disk *du;
+ register struct buf *bp, *dp;
+ int status, wdc;
+ char partch ;
+
+ if (!wdtab.b_active) {
+#ifdef nyet
+ printf("wd: extra interrupt\n");
+#endif
+ return;
+ }
+
+ dp = wdtab.b_actf;
+ bp = dp->b_actf;
+ du = wddrives[wdunit(bp->b_dev)];
+ wdc = du->dk_port;
+
+#ifdef WDDEBUG
+ printf("I ");
+#endif
+
+ while ((status = inb(wdc+wd_status)) & WDCS_BUSY) ;
+
+ /* is it not a transfer, but a control operation? */
+ if (du->dk_state < OPEN) {
+ if (wdcontrol(bp))
+ wdstart();
+ return;
+ }
+
+ /* have we an error? */
+ if (status & (WDCS_ERR | WDCS_ECCCOR)) {
+
+ du->dk_status = status;
+ du->dk_error = inb(wdc + wd_error);
+#ifdef WDDEBUG
+ printf("status %x error %x\n", status, du->dk_error);
+#endif
+ if((du->dk_flags & DKFL_SINGLE) == 0) {
+ du->dk_flags |= DKFL_ERROR;
+ goto outt;
+ }
+#ifdef B_FORMAT
+ if (bp->b_flags & B_FORMAT) {
+ bp->b_error = EIO; /* 17 Sep 92*/
+ bp->b_flags |= B_ERROR;
+ goto done;
+ }
+#endif
+
+ /* error or error correction? */
+ if (status & WDCS_ERR) {
+ if (++wdtab.b_errcnt < RETRIES) {
+ wdtab.b_active = 0;
+ } else {
+ if((du->dk_flags&DKFL_QUIET) == 0) {
+ diskerr(bp, "wd", "hard error",
+ LOG_PRINTF, du->dk_skip,
+ &du->dk_dd);
+#ifdef WDDEBUG
+ printf( "status %b error %b\n",
+ status, WDCS_BITS,
+ inb(wdc+wd_error), WDERR_BITS);
+#endif
+ }
+ bp->b_error = EIO; /* 17 Sep 92*/
+ bp->b_flags |= B_ERROR; /* flag the error */
+ }
+ } else if((du->dk_flags&DKFL_QUIET) == 0) {
+ diskerr(bp, "wd", "soft ecc", 0,
+ du->dk_skip, &du->dk_dd);
+ }
+ }
+outt:
+
+ /*
+ * If this was a successful read operation, fetch the data.
+ */
+ if (((bp->b_flags & (B_READ | B_ERROR)) == B_READ) && wdtab.b_active) {
+ int chk, dummy;
+
+ chk = min(DEV_BSIZE / sizeof(short), du->dk_bc / sizeof(short));
+
+ /* ready to receive data? */
+ while ((inb(wdc+wd_status) & WDCS_DRQ) == 0)
+ ;
+
+ /* suck in data */
+ insw (wdc+wd_data,
+ (int)bp->b_un.b_addr + du->dk_skip * DEV_BSIZE, chk);
+ du->dk_bc -= chk * sizeof(short);
+
+ /* for obselete fractional sector reads */
+ while (chk++ < 256) insw (wdc+wd_data, &dummy, 1);
+ }
+
+ wdxfer[du->dk_unit]++;
+ if (wdtab.b_active) {
+ if ((bp->b_flags & B_ERROR) == 0) {
+ du->dk_skip++; /* Add to successful sectors. */
+ if (wdtab.b_errcnt && (du->dk_flags & DKFL_QUIET) == 0)
+ diskerr(bp, "wd", "soft error", 0,
+ du->dk_skip, &du->dk_dd);
+ wdtab.b_errcnt = 0;
+
+ /* see if more to transfer */
+ if (du->dk_bc > 0 && (du->dk_flags & DKFL_ERROR) == 0) {
+ wdstart();
+ return; /* next chunk is started */
+ } else if ((du->dk_flags & (DKFL_SINGLE|DKFL_ERROR))
+ == DKFL_ERROR) {
+ du->dk_skip = 0;
+ du->dk_flags &= ~DKFL_ERROR;
+ du->dk_flags |= DKFL_SINGLE;
+ wdstart();
+ return; /* redo xfer sector by sector */
+ }
+ }
+
+done:
+ /* done with this transfer, with or without error */
+ du->dk_flags &= ~DKFL_SINGLE;
+ wdtab.b_actf = dp->b_forw;
+ wdtab.b_errcnt = 0;
+ du->dk_skip = 0;
+ dp->b_active = 0;
+ dp->b_actf = bp->av_forw;
+ dp->b_errcnt = 0;
+ bp->b_resid = 0;
+ biodone(bp);
+ }
+
+ /* controller idle */
+ wdtab.b_active = 0;
+
+ /* anything more on drive queue? */
+ if (dp->b_actf)
+ wdustart(du);
+ /* anything more for controller to do? */
+ if (wdtab.b_actf)
+ wdstart();
+}
+
+/*
+ * Initialize a drive.
+ */
+int
+wdopen(dev_t dev, int flags, int fmt, struct proc *p)
+{
+ register unsigned int unit;
+ register struct disk *du;
+ int part = wdpart(dev), mask = 1 << part;
+ struct partition *pp;
+ struct dkbad *db;
+ int i, error = 0;
+ char *msg;
+
+ unit = wdunit(dev);
+ if (unit >= _NWD) return (ENXIO) ;
+
+ du = wddrives[unit];
+ if (du == 0) return (ENXIO) ;
+
+ if ((du->dk_flags & DKFL_BSDLABEL) == 0) {
+ du->dk_flags |= DKFL_WRITEPROT;
+ wdutab[unit].b_actf = NULL;
+
+ /*
+ * Use the default sizes until we've read the label,
+ * or longer if there isn't one there.
+ */
+ bzero(&du->dk_dd, sizeof(du->dk_dd));
+#undef d_type /* fix goddamn segments.h! XXX */
+ du->dk_dd.d_type = DTYPE_ST506;
+ du->dk_dd.d_ncylinders = 1024;
+ du->dk_dd.d_secsize = DEV_BSIZE;
+ du->dk_dd.d_ntracks = 8;
+ du->dk_dd.d_nsectors = 17;
+ du->dk_dd.d_secpercyl = 17*8;
+ du->dk_state = WANTOPEN;
+ du->dk_unit = unit;
+ du->dk_flags &= ~DKFL_QUIET;
+
+ /* read label using "c" partition */
+ if (msg = readdisklabel(makewddev(major(dev), wdunit(dev), WDRAW),
+ wdstrategy, &du->dk_dd, du->dk_dospartitions,
+ &du->dk_bad, 0)) {
+ if((du->dk_flags&DKFL_QUIET) == 0) {
+ log(LOG_WARNING, "wd%d: cannot find label (%s)\n",
+ unit, msg);
+ error = EINVAL; /* XXX needs translation */
+ }
+ goto done;
+ } else {
+
+ wdsetctlr(dev, du);
+ du->dk_flags |= DKFL_BSDLABEL;
+ du->dk_flags &= ~DKFL_WRITEPROT;
+ if (du->dk_dd.d_flags & D_BADSECT)
+ du->dk_flags |= DKFL_BADSECT;
+ }
+
+done:
+ if (error)
+ return(error);
+
+ }
+ /*
+ * Warn if a partion is opened
+ * that overlaps another partition which is open
+ * unless one is the "raw" partition (whole disk).
+ */
+ if ((du->dk_openpart & mask) == 0 /*&& part != RAWPART*/ && part != WDRAW) {
+ int start, end;
+
+ pp = &du->dk_dd.d_partitions[part];
+ start = pp->p_offset;
+ end = pp->p_offset + pp->p_size;
+ for (pp = du->dk_dd.d_partitions;
+ pp < &du->dk_dd.d_partitions[du->dk_dd.d_npartitions];
+ pp++) {
+ if (pp->p_offset + pp->p_size <= start ||
+ pp->p_offset >= end)
+ continue;
+ /*if (pp - du->dk_dd.d_partitions == RAWPART)
+ continue; */
+ if (pp - du->dk_dd.d_partitions == WDRAW)
+ continue;
+ if (du->dk_openpart & (1 << (pp -
+ du->dk_dd.d_partitions)))
+ log(LOG_WARNING,
+ "wd%d%c: overlaps open partition (%c)\n",
+ unit, part + 'a',
+ pp - du->dk_dd.d_partitions + 'a');
+ }
+ }
+ if (part >= du->dk_dd.d_npartitions && part != WDRAW)
+ return (ENXIO);
+
+ /* insure only one open at a time */
+ du->dk_openpart |= mask;
+ switch (fmt) {
+ case S_IFCHR:
+ du->dk_copenpart |= mask;
+ break;
+ case S_IFBLK:
+ du->dk_bopenpart |= mask;
+ break;
+ }
+ return (0);
+}
+
+/*
+ * Implement operations other than read/write.
+ * Called from wdstart or wdintr during opens and formats.
+ * Uses finite-state-machine to track progress of operation in progress.
+ * Returns 0 if operation still in progress, 1 if completed.
+ */
+static int
+wdcontrol(register struct buf *bp)
+{
+ register struct disk *du;
+ register unit;
+ unsigned char stat;
+ int s, cnt;
+ extern int bootdev;
+ int cyl, trk, sec, i, wdc;
+ struct wdparams foo;
+
+ du = wddrives[wdunit(bp->b_dev)];
+ unit = du->dk_unit;
+ wdc = du->dk_port;
+
+ switch (du->dk_state) {
+
+ tryagainrecal:
+ case WANTOPEN: /* set SDH, step rate, do restore */
+#ifdef WDDEBUG
+ printf("wd%d: recal ", unit);
+#endif
+ s = splbio(); /* not called from intr level ... */
+ wdgetctlr(unit, du);
+
+ outb(wdc+wd_sdh, WDSD_IBM | (unit << 4));
+ wdtab.b_active = 1;
+
+ /* wait for drive and controller to become ready */
+ for (i = WDCTIMEOUT; (inb(wdc+wd_status) & (WDCS_READY|WDCS_BUSY))
+ != WDCS_READY && i-- != 0; )
+ ;
+ outb(wdc+wd_command, WDCC_RESTORE | WD_STEP);
+ du->dk_state++;
+ splx(s);
+ return(0);
+
+ case RECAL:
+ if ((stat = inb(wdc+wd_status)) & WDCS_ERR) {
+ if ((du->dk_flags & DKFL_QUIET) == 0) {
+ printf("wd%d: recal", du->dk_unit);
+ printf(": status %b error %b\n",
+ stat, WDCS_BITS, inb(wdc+wd_error),
+ WDERR_BITS);
+ }
+ if (++wdtab.b_errcnt < RETRIES) {
+ du->dk_state = WANTOPEN;
+ goto tryagainrecal;
+ }
+ bp->b_error = ENXIO; /* XXX needs translation */
+ goto badopen;
+ }
+
+ /* some controllers require this ... */
+ wdsetctlr(bp->b_dev, du);
+
+ wdtab.b_errcnt = 0;
+ du->dk_state = OPEN;
+ /*
+ * The rest of the initialization can be done
+ * by normal means.
+ */
+ return(1);
+
+ default:
+ panic("wdcontrol");
+ }
+ /* NOTREACHED */
+
+badopen:
+ if ((du->dk_flags & DKFL_QUIET) == 0)
+ printf(": status %b error %b\n",
+ stat, WDCS_BITS, inb(wdc + wd_error), WDERR_BITS);
+ bp->b_flags |= B_ERROR;
+ return(1);
+}
+
+/*
+ * send a command and wait uninterruptibly until controller is finished.
+ * return -1 if controller busy for too long, otherwise
+ * return status. intended for brief controller commands at critical points.
+ * assumes interrupts are blocked.
+ */
+static int
+wdcommand(struct disk *du, int cmd) {
+ int timeout = WDCTIMEOUT, stat, wdc;
+
+ /* controller ready for command? */
+ wdc = du->dk_port;
+ while (((stat = inb(wdc + wd_status)) & WDCS_BUSY) && timeout > 0)
+ timeout--;
+ if (timeout <= 0)
+ return(-1);
+
+ /* send command, await results */
+ outb(wdc+wd_command, cmd);
+ while (((stat = inb(wdc+wd_status)) & WDCS_BUSY) && timeout > 0)
+ timeout--;
+ if (timeout <= 0)
+ return(-1);
+ if (cmd != WDCC_READP)
+ return (stat);
+
+ /* is controller ready to return data? */
+ while (((stat = inb(wdc+wd_status)) & (WDCS_ERR|WDCS_DRQ)) == 0 &&
+ timeout > 0)
+ timeout--;
+ if (timeout <= 0)
+ return(-1);
+
+ return (stat);
+}
+
+/*
+ * issue IDC to drive to tell it just what geometry it is to be.
+ */
+static int
+wdsetctlr(dev_t dev, struct disk *du) {
+ int stat, x, wdc;
+
+/*printf("C%dH%dS%d ", du->dk_dd.d_ncylinders, du->dk_dd.d_ntracks,
+ du->dk_dd.d_nsectors);*/
+
+ x = splbio();
+ wdc = du->dk_port;
+ outb(wdc+wd_cyl_lo, du->dk_dd.d_ncylinders+1);
+ outb(wdc+wd_cyl_hi, (du->dk_dd.d_ncylinders+1)>>8);
+ outb(wdc+wd_sdh, WDSD_IBM | (wdunit(dev) << 4) + du->dk_dd.d_ntracks-1);
+ outb(wdc+wd_seccnt, du->dk_dd.d_nsectors);
+ stat = wdcommand(du, WDCC_IDC);
+
+ if (stat < 0) {
+ splx(x);
+ return(stat);
+ }
+ if (stat & WDCS_ERR)
+ printf("wdsetctlr: status %b error %b\n",
+ stat, WDCS_BITS, inb(wdc+wd_error), WDERR_BITS);
+ splx(x);
+ return(stat);
+}
+
+/*
+ * issue READP to drive to ask it what it is.
+ */
+static int
+wdgetctlr(int u, struct disk *du) {
+ int stat, x, i, wdc;
+ char tb[DEV_BSIZE];
+ struct wdparams *wp;
+
+ x = splbio(); /* not called from intr level ... */
+ wdc = du->dk_port;
+ outb(wdc+wd_sdh, WDSD_IBM | (u << 4));
+ stat = wdcommand(du, WDCC_READP);
+
+ if (stat < 0) {
+ splx(x);
+ return(stat);
+ }
+ if (stat & WDCS_ERR) {
+ stat = inb(wdc+wd_error);
+ splx(x);
+ return(stat);
+ }
+
+ /* obtain parameters */
+ wp = &du->dk_params;
+ insw(wdc+wd_data, tb, sizeof(tb)/sizeof(short));
+ bcopy(tb, wp, sizeof(struct wdparams));
+
+ /* shuffle string byte order */
+ for (i=0; i < sizeof(wp->wdp_model) ;i+=2) {
+ u_short *p;
+ p = (u_short *) (wp->wdp_model + i);
+ *p = ntohs(*p);
+ }
+/*printf("gc %x cyl %d trk %d sec %d type %d sz %d model %s\n", wp->wdp_config,
+wp->wdp_fixedcyl+wp->wdp_removcyl, wp->wdp_heads, wp->wdp_sectors,
+wp->wdp_cntype, wp->wdp_cnsbsz, wp->wdp_model);*/
+
+ /* update disklabel given drive information */
+ du->dk_dd.d_ncylinders = wp->wdp_fixedcyl + wp->wdp_removcyl /*+- 1*/;
+ du->dk_dd.d_ntracks = wp->wdp_heads;
+ du->dk_dd.d_nsectors = wp->wdp_sectors;
+ du->dk_dd.d_secpercyl = du->dk_dd.d_ntracks * du->dk_dd.d_nsectors;
+ du->dk_dd.d_partitions[1].p_size = du->dk_dd.d_secpercyl *
+ wp->wdp_sectors;
+ du->dk_dd.d_partitions[1].p_offset = 0;
+ /* dubious ... */
+ bcopy("ESDI/IDE", du->dk_dd.d_typename, 9);
+ bcopy(wp->wdp_model+20, du->dk_dd.d_packname, 14-1);
+ /* better ... */
+ du->dk_dd.d_type = DTYPE_ESDI;
+ du->dk_dd.d_subtype |= DSTYPE_GEOMETRY;
+
+ /* XXX sometimes possibly needed */
+ (void) inb(wdc+wd_status);
+ return (0);
+}
+
+
+/* ARGSUSED */
+int
+wdclose(dev_t dev, int flags, int fmt)
+{
+ register struct disk *du;
+ int part = wdpart(dev), mask = 1 << part;
+
+ du = wddrives[wdunit(dev)];
+
+ /* insure only one open at a time */
+ du->dk_openpart &= ~mask;
+ switch (fmt) {
+ case S_IFCHR:
+ du->dk_copenpart &= ~mask;
+ break;
+ case S_IFBLK:
+ du->dk_bopenpart &= ~mask;
+ break;
+ }
+ return(0);
+}
+
+int
+wdioctl(dev_t dev, int cmd, caddr_t addr, int flag)
+{
+ int unit = wdunit(dev);
+ register struct disk *du;
+ int error = 0;
+ struct uio auio;
+ struct iovec aiov;
+
+ du = wddrives[unit];
+
+ switch (cmd) {
+
+ case DIOCSBAD:
+ if ((flag & FWRITE) == 0)
+ error = EBADF;
+ else
+ du->dk_bad = *(struct dkbad *)addr;
+ break;
+
+ case DIOCGDINFO:
+ *(struct disklabel *)addr = du->dk_dd;
+ break;
+
+ case DIOCGPART:
+ ((struct partinfo *)addr)->disklab = &du->dk_dd;
+ ((struct partinfo *)addr)->part =
+ &du->dk_dd.d_partitions[wdpart(dev)];
+ break;
+
+ case DIOCSDINFO:
+ if ((flag & FWRITE) == 0)
+ error = EBADF;
+ else
+ error = setdisklabel(&du->dk_dd,
+ (struct disklabel *)addr,
+ /*(du->dk_flags & DKFL_BSDLABEL) ? du->dk_openpart : */0,
+ du->dk_dospartitions);
+ if (error == 0) {
+ du->dk_flags |= DKFL_BSDLABEL;
+ wdsetctlr(dev, du);
+ }
+ break;
+
+ case DIOCWLABEL:
+ du->dk_flags &= ~DKFL_WRITEPROT;
+ if ((flag & FWRITE) == 0)
+ error = EBADF;
+ else
+ du->dk_wlabel = *(int *)addr;
+ break;
+
+ case DIOCWDINFO:
+ du->dk_flags &= ~DKFL_WRITEPROT;
+ if ((flag & FWRITE) == 0)
+ error = EBADF;
+ else if ((error = setdisklabel(&du->dk_dd, (struct disklabel *)addr,
+ /*(du->dk_flags & DKFL_BSDLABEL) ? du->dk_openpart :*/ 0,
+ du->dk_dospartitions)) == 0) {
+ int wlab;
+
+ du->dk_flags |= DKFL_BSDLABEL;
+ wdsetctlr(dev, du);
+
+ /* simulate opening partition 0 so write succeeds */
+ du->dk_openpart |= (1 << 0); /* XXX */
+ wlab = du->dk_wlabel;
+ du->dk_wlabel = 1;
+ error = writedisklabel(dev, wdstrategy,
+ &du->dk_dd, du->dk_dospartitions);
+ du->dk_openpart = du->dk_copenpart | du->dk_bopenpart;
+ du->dk_wlabel = wlab;
+ }
+ break;
+
+#ifdef notyet
+ case DIOCGDINFOP:
+ *(struct disklabel **)addr = &(du->dk_dd);
+ break;
+
+ case DIOCWFORMAT:
+ if ((flag & FWRITE) == 0)
+ error = EBADF;
+ else {
+ register struct format_op *fop;
+
+ fop = (struct format_op *)addr;
+ aiov.iov_base = fop->df_buf;
+ aiov.iov_len = fop->df_count;
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_resid = fop->df_count;
+ auio.uio_segflg = 0;
+ auio.uio_offset =
+ fop->df_startblk * du->dk_dd.d_secsize;
+ error = physio(wdformat, &rwdbuf[unit], dev, B_WRITE,
+ minphys, &auio);
+ fop->df_count -= auio.uio_resid;
+ fop->df_reg[0] = du->dk_status;
+ fop->df_reg[1] = du->dk_error;
+ }
+ break;
+#endif
+
+ default:
+ error = ENOTTY;
+ break;
+ }
+ return (error);
+}
+
+#ifdef B_FORMAT
+int
+wdformat(struct buf *bp)
+{
+
+ bp->b_flags |= B_FORMAT;
+ return (wdstrategy(bp));
+}
+#endif
+
+int
+wdsize(dev_t dev)
+{
+ int unit = wdunit(dev), part = wdpart(dev), val;
+ struct disk *du;
+
+ if (unit >= _NWD) /* 31 Jul 92*/
+ return(-1);
+
+ du = wddrives[unit];
+ if (du == 0 || du->dk_state == 0)
+ val = wdopen (makewddev(major(dev), unit, WDRAW), FREAD, S_IFBLK, 0);
+ if (du == 0 || val != 0 || du->dk_flags & DKFL_WRITEPROT)
+ return (-1);
+
+ return((int)du->dk_dd.d_partitions[part].p_size);
+}
+
+extern char *vmmap; /* poor name! */
+
+int
+wddump(dev_t dev) /* dump core after a system crash */
+{
+ register struct disk *du; /* disk unit to do the IO */
+ register struct bt_bad *bt_ptr;
+ long num; /* number of sectors to write */
+ int unit, part, wdc;
+ long blkoff, blknum, blkcnt;
+ long cylin, head, sector, stat;
+ long secpertrk, secpercyl, nblocks, i;
+ char *addr;
+ extern int Maxmem;
+ static wddoingadump = 0 ;
+ extern caddr_t CADDR1;
+
+ addr = (char *) 0; /* starting address */
+
+ /* toss any characters present prior to dump */
+ while (sgetc(1))
+ ;
+
+ /* size of memory to dump */
+ num = Maxmem;
+ unit = wdunit(dev); /* eventually support floppies? */
+ part = wdpart(dev); /* file system */
+ /* check for acceptable drive number */
+ if (unit >= _NWD) return(ENXIO); /* 31 Jul 92*/
+
+ du = wddrives[unit];
+ if (du == 0) return(ENXIO);
+ /* was it ever initialized ? */
+ if (du->dk_state < OPEN) return (ENXIO) ;
+ if (du->dk_flags & DKFL_WRITEPROT) return(ENXIO);
+ wdc = du->dk_port;
+
+ /* Convert to disk sectors */
+ num = (u_long) num * NBPG / du->dk_dd.d_secsize;
+
+ /* check if controller active */
+ /*if (wdtab.b_active) return(EFAULT); */
+ if (wddoingadump) return(EFAULT);
+
+ secpertrk = du->dk_dd.d_nsectors;
+ secpercyl = du->dk_dd.d_secpercyl;
+ nblocks = du->dk_dd.d_partitions[part].p_size;
+ blkoff = du->dk_dd.d_partitions[part].p_offset;
+
+/*pg("xunit %x, nblocks %d, dumplo %d num %d\n", part,nblocks,dumplo,num);*/
+ /* check transfer bounds against partition size */
+ if ((dumplo < 0) || ((dumplo + num) > nblocks))
+ return(EINVAL);
+
+ /*wdtab.b_active = 1; /* mark controller active for if we
+ panic during the dump */
+ wddoingadump = 1 ; i = 100000 ;
+ while ((inb(wdc+wd_status) & WDCS_BUSY) && (i-- > 0)) ;
+ outb(wdc+wd_sdh, WDSD_IBM | (unit << 4));
+ outb(wdc+wd_command, WDCC_RESTORE | WD_STEP);
+ while (inb(wdc+wd_status) & WDCS_BUSY) ;
+
+ /* some compaq controllers require this ... */
+ wdsetctlr(dev, du);
+
+ blknum = dumplo + blkoff;
+ while (num > 0) {
+#ifdef notdef
+ if (blkcnt > MAXTRANSFER) blkcnt = MAXTRANSFER;
+ if ((blknum + blkcnt - 1) / secpercyl != blknum / secpercyl)
+ blkcnt = secpercyl - (blknum % secpercyl);
+ /* keep transfer within current cylinder */
+#endif
+ pmap_enter(kernel_pmap, CADDR1, trunc_page(addr), VM_PROT_READ, TRUE);
+
+ /* compute disk address */
+ cylin = blknum / secpercyl;
+ head = (blknum % secpercyl) / secpertrk;
+ sector = blknum % secpertrk;
+
+#ifdef notyet
+ /*
+ * See if the current block is in the bad block list.
+ * (If we have one.)
+ */
+ for (bt_ptr = du->dk_bad.bt_bad;
+ bt_ptr->bt_cyl != -1; bt_ptr++) {
+ if (bt_ptr->bt_cyl > cylin)
+ /* Sorted list, and we passed our cylinder.
+ quit. */
+ break;
+ if (bt_ptr->bt_cyl == cylin &&
+ bt_ptr->bt_trksec == (head << 8) + sector) {
+ /*
+ * Found bad block. Calculate new block addr.
+ * This starts at the end of the disk (skip the
+ * last track which is used for the bad block list),
+ * and works backwards to the front of the disk.
+ */
+ blknum = (du->dk_dd.d_secperunit)
+ - du->dk_dd.d_nsectors
+ - (bt_ptr - du->dk_bad.bt_bad) - 1;
+ cylin = blknum / secpercyl;
+ head = (blknum % secpercyl) / secpertrk;
+ sector = blknum % secpertrk;
+ break;
+ }
+
+#endif
+ sector++; /* origin 1 */
+
+ /* select drive. */
+ outb(wdc+wd_sdh, WDSD_IBM | (unit<<4) | (head & 0xf));
+ while ((inb(wdc+wd_status) & WDCS_READY) == 0) ;
+
+ /* transfer some blocks */
+ outb(wdc+wd_sector, sector);
+ outb(wdc+wd_seccnt,1);
+ outb(wdc+wd_cyl_lo, cylin);
+ outb(wdc+wd_cyl_hi, cylin >> 8);
+#ifdef notdef
+ /* lets just talk about this first...*/
+ pg ("sdh 0%o sector %d cyl %d addr 0x%x",
+ inb(wdc+wd_sdh), inb(wdc+wd_sector),
+ inb(wdc+wd_cyl_hi)*256+inb(wdc+wd_cyl_lo), addr) ;
+#endif
+ outb(wdc+wd_command, WDCC_WRITE);
+
+ /* Ready to send data? */
+ while ((inb(wdc+wd_status) & WDCS_DRQ) == 0) ;
+ if (inb(wdc+wd_status) & WDCS_ERR) return(EIO) ;
+
+ outsw (wdc+wd_data, CADDR1+((int)addr&(NBPG-1)), 256);
+
+ if (inb(wdc+wd_status) & WDCS_ERR) return(EIO) ;
+ /* Check data request (should be done). */
+ if (inb(wdc+wd_status) & WDCS_DRQ) return(EIO) ;
+
+ /* wait for completion */
+ for ( i = WDCTIMEOUT ; inb(wdc+wd_status) & WDCS_BUSY ; i--) {
+ if (i < 0) return (EIO) ;
+ }
+ /* error check the xfer */
+ if (inb(wdc+wd_status) & WDCS_ERR) return(EIO) ;
+
+ if ((unsigned)addr % (1024*1024) == 0) printf("%d ", num/2048) ;
+ /* update block count */
+ num--;
+ blknum++ ;
+ (int) addr += 512;
+
+ /* operator aborting dump? */
+ if (sgetc(1))
+ return(EINTR);
+ }
+ return(0);
+}
+#endif
diff --git a/sys/i386/isa/wdreg.h b/sys/i386/isa/wdreg.h
new file mode 100644
index 0000000..d938915
--- /dev/null
+++ b/sys/i386/isa/wdreg.h
@@ -0,0 +1,143 @@
+/*-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * 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.
+ *
+ * @(#)wdreg.h 7.1 (Berkeley) 5/9/91
+ */
+
+/*
+ * Disk Controller register definitions.
+ */
+#define wd_data 0x0 /* data register (R/W - 16 bits) */
+#define wd_error 0x1 /* error register (R) */
+#define wd_precomp wd_error /* write precompensation (W) */
+#define wd_seccnt 0x2 /* sector count (R/W) */
+#define wd_sector 0x3 /* first sector number (R/W) */
+#define wd_cyl_lo 0x4 /* cylinder address, low byte (R/W) */
+#define wd_cyl_hi 0x5 /* cylinder address, high byte (R/W)*/
+#define wd_sdh 0x6 /* sector size/drive/head (R/W)*/
+#define wd_command 0x7 /* command register (W) */
+#define wd_status wd_command /* immediate status (R) */
+
+#define wd_altsts 0x206 /*alternate fixed disk status(via 1015) (R)*/
+#define wd_ctlr 0x206 /*fixed disk controller control(via 1015) (W)*/
+#define WDCTL_4BIT 0x8 /* use four head bits (wd1003) */
+#define WDCTL_RST 0x4 /* reset the controller */
+#define WDCTL_IDS 0x2 /* disable controller interrupts */
+#define wd_digin 0x207 /* disk controller input(via 1015) (R)*/
+
+/*
+ * Status Bits.
+ */
+#define WDCS_BUSY 0x80 /* Controller busy bit. */
+#define WDCS_READY 0x40 /* Selected drive is ready */
+#define WDCS_WRTFLT 0x20 /* Write fault */
+#define WDCS_SEEKCMPLT 0x10 /* Seek complete */
+#define WDCS_DRQ 0x08 /* Data request bit. */
+#define WDCS_ECCCOR 0x04 /* ECC correction made in data */
+#define WDCS_INDEX 0x02 /* Index pulse from selected drive */
+#define WDCS_ERR 0x01 /* Error detect bit. */
+
+#define WDCS_BITS "\020\010busy\006rdy\006wrtflt\005seekdone\004drq\003ecc_cor\002index\001err"
+
+#define WDERR_BITS "\020\010badblk\007uncorr\006id_crc\005no_id\003abort\002tr000\001no_dam"
+
+/*
+ * Commands for Disk Controller.
+ */
+#define WDCC_RESTORE 0x10 /* disk restore code -- resets cntlr */
+
+#define WDCC_READ 0x20 /* disk read code */
+#define WDCC_WRITE 0x30 /* disk write code */
+#define WDCC__LONG 0x02 /* modifier -- access ecc bytes */
+#define WDCC__NORETRY 0x01 /* modifier -- no retrys */
+
+#define WDCC_FORMAT 0x50 /* disk format code */
+#define WDCC_DIAGNOSE 0x90 /* controller diagnostic */
+#define WDCC_IDC 0x91 /* initialize drive command */
+
+#define WDCC_EXTDCMD 0xE0 /* send extended command */
+#define WDCC_READP 0xEC /* read parameters from controller */
+#define WDCC_CACHEC 0xEF /* cache control */
+
+#define WD_STEP 0 /* winchester- default 35us step */
+
+#define WDSD_IBM 0xa0 /* forced to 512 byte sector, ecc */
+
+
+#ifdef KERNEL
+/*
+ * read parameters command returns this:
+ */
+struct wdparams {
+ /* drive info */
+ short wdp_config; /* general configuration */
+ short wdp_fixedcyl; /* number of non-removable cylinders */
+ short wdp_removcyl; /* number of removable cylinders */
+ short wdp_heads; /* number of heads */
+ short wdp_unfbytespertrk; /* number of unformatted bytes/track */
+ short wdp_unfbytes; /* number of unformatted bytes/sector */
+ short wdp_sectors; /* number of sectors */
+ short wdp_minisg; /* minimum bytes in inter-sector gap*/
+ short wdp_minplo; /* minimum bytes in postamble */
+ short wdp_vendstat; /* number of words of vendor status */
+ /* controller info */
+ char wdp_cnsn[20]; /* controller serial number */
+ short wdp_cntype; /* controller type */
+#define WDTYPE_SINGLEPORTSECTOR 1 /* single port, single sector buffer */
+#define WDTYPE_DUALPORTMULTI 2 /* dual port, multiple sector buffer */
+#define WDTYPE_DUALPORTMULTICACHE 3 /* above plus track cache */
+ short wdp_cnsbsz; /* sector buffer size, in sectors */
+ short wdp_necc; /* ecc bytes appended */
+ char wdp_rev[8]; /* firmware revision */
+ char wdp_model[40]; /* model name */
+ short wdp_nsecperint; /* sectors per interrupt */
+ short wdp_usedmovsd; /* can use double word read/write? */
+};
+
+/*
+ * wd driver entry points
+ */
+int wdprobe(struct isa_device *);
+int wdattach(struct isa_device *);
+int wdstrategy(struct buf *);
+void wdintr(struct intrframe);
+int wdopen(dev_t, int, int, struct proc *);
+int wdclose(dev_t dev, int flags, int fmt);
+int wdioctl(dev_t, int, caddr_t, int);
+/* int wdformat(struct buf *bp); */
+int wdsize(dev_t);
+int wddump(dev_t);
+
+#endif KERNEL
diff --git a/sys/i386/isa/wt.c b/sys/i386/isa/wt.c
new file mode 100644
index 0000000..4376395
--- /dev/null
+++ b/sys/i386/isa/wt.c
@@ -0,0 +1,1162 @@
+/*-
+ * 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.
+ *
+ * @(#)wt.c 7.1 (Berkeley) 5/9/91
+ */
+
+/*
+ *
+ * Copyright (c) 1989 Carnegie-Mellon University.
+ * All rights reserved.
+ *
+ * Authors: Robert Baron
+ *
+ * Permission to use, copy, modify and distribute this software and
+ * its documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
+ * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+#include "wt.h"
+#if NWT > 0
+/*
+ * HISTORY
+ * $Log: wt.c,v $
+ * Revision 2.2.1.3 90/01/08 13:29:38 rvb
+ * Add Intel copyright.
+ * [90/01/08 rvb]
+ *
+ * Revision 2.2.1.2 89/12/21 18:00:09 rvb
+ * Change WTPRI to make the streamer tape read/write
+ * interruptible. [lin]
+ *
+ * Revision 2.2.1.1 89/11/10 09:49:49 rvb
+ * ORC likes their streamer port at 0x288.
+ * [89/11/08 rvb]
+ *
+ * Revision 2.2 89/09/25 12:33:02 rvb
+ * Driver was provided by Intel 9/18/89.
+ * [89/09/23 rvb]
+ *
+ */
+
+/*
+ *
+ * Copyright 1988, 1989 by Intel Corporation
+ *
+ * Support Bell Tech QIC-02 and WANGTEK QIC-36 or QIC-02
+ */
+
+/*#include <sys/errno.h>
+#include <sys/signal.h>
+#include <sys/types.h>*/
+#include "sys/param.h"
+#include "sys/buf.h"
+#include "sys/file.h"
+#include "sys/proc.h"
+#include "sys/user.h"
+#include "i386/isa/wtreg.h"
+
+#ifdef ORC
+unsigned wtport = 0x288; /* base I/O port of controller */
+#else ORC
+unsigned wtport = 0x300; /* base I/O port of controller */
+#endif ORC
+ /* standard = 0x300 */
+ /* alternate = 0x338 */
+
+unsigned wtchan = 1; /* DMA channel number */
+ /* stardard = 1 */
+ /* hardware permits 1, 2 or 3. */
+ /* (Avoid DMA 2: used by disks) */
+
+int first_wtopen_ever = 1;
+
+
+#define ERROR 1 /* return from tape routines */
+#define SUCCESS 0 /* return from tape routines */
+
+int wci = 0;
+int exflag = 0;
+int bytes = 0;
+
+static unsigned char eqdma = 0x8;
+static unsigned char pagereg = 0x83;
+static unsigned char dmareg = 2;
+static unsigned char dma_write = 0x49;
+static unsigned char dma_read = 0x45;
+static unsigned char dma_done = 2;
+static unsigned char mode = 0;
+static unsigned char mbits; /* map bits into each other */
+static long bufptr;
+static unsigned numbytes;
+/*
+_wci dw 0 ; interrupt chain finished normally
+_exflag dw 0 ; exception variable
+_bytes dw 0 ; current bytes
+
+eqdma db 8h ; enable dma command: ch1,ch2=8h, ch3=10h
+pagereg db 83h ; ch1=83h, ch2=81h, ch3=82h
+dmareg db 2 ; ch1=2, ch2=4, ch3=6
+dma_write db 49h ; write dma command: 48h+_wtchan
+dma_read db 45h ; read dma command: 44h+_wtchan
+dma_done db 2 ; dma done flag: 1<<_wtchan
+mode db 0 ; dma operation mode
+lbufptr dw 0 ; buffer pointer to data buffers, low word
+hbufptr dw 0 ; buffer pointer to data buffers, high word
+numbytes dw 0 ; number of bytes to read or write (new)
+*/
+
+#define PAGESIZ 4096
+#define HZ 60
+
+/* tape controller ports */
+#define STATPORT wtport
+#define CTLPORT STATPORT
+#define CMDPORT (wtport+1)
+#define DATAPORT CMDPORT
+
+/* defines for reading out status from wangtek tape controller */
+#define READY 0x01 /* ready bit define */
+#define EXCEP 0x02 /* exception bit define */
+#define STAT (READY|EXCEP)
+#define RESETMASK 0x7
+#define RESETVAL (RESETMASK & ~EXCEP)
+
+/* tape controller control bits (CTLPORT) */
+#define ONLINE 0x01
+#define RESET 0x02
+#define REQUEST 0x04 /* request command */
+#define CMDOFF 0xC0
+
+/* QIC-02 commands (CMDPORT) */
+#define RDDATA 0x80 /* read data */
+#define READFM 0xA0 /* read file mark */
+#define WRTDATA 0x40 /* write data */
+#define WRITEFM 0x60 /* write file mark */
+#define RDSTAT 0xC0 /* read status command */
+#define REWIND 0x21 /* rewind command (position+bot) */
+
+/* 8237 DMA controller regs */
+#define STATUSREG 0x8
+#define MASKREG 0xA
+#define MODEREG 0xB
+#define CLEARFF 0xC
+
+/* streamer tape block size */
+#define BLKSIZE 512
+
+/* Tape characteristics */
+#define NBPS 512 /* 512-byte blocks */
+#define ERROR 1 /* return from tape routines */
+#define SUCCESS 0 /* return from tape routines */
+
+/* Minor devs */
+#define TP_REWCLOSE(d) ((minor(d)&04) == 0) /* Rewind tape on close if read/write */
+#define TP_DENS(dev) ((minor(dev) >> 3) & 03) /* set density */
+#define TPHOG(d) 0 /* use Hogproc during tape I/O */
+
+/* defines for wtflags */
+#define TPINUSE 0x0001 /* tape is already open */
+#define TPREAD 0x0002 /* tape is only open for reading */
+#define TPWRITE 0x0004 /* tape is only open for writing */
+#define TPSTART 0x0008 /* tape must be rewound and reset */
+#define TPDEAD 0x0010 /* tape drive does not work or driver error */
+#define TPSESS 0x0020 /* no more reads or writes allowed in session */
+ /* for example, when tape has to be changed */
+#define TPSTOP 0x0040 /* Stop command outstanding */
+#define TPREW 0x0080 /* Rewind command outstanding, see wtdsl2() */
+#define TPVOL 0x0100 /* Read file mark, or hit end of tape */
+#define TPWO 0x0200 /* write command outstanding */
+#define TPRO 0x0400 /* read command outstanding */
+#define TPWANY 0x0800 /* write command requested */
+#define TPRANY 0x1000 /* read command requested */
+#define TPWP 0x2000 /* write protect error seen */
+
+unsigned int wtflags = TPSTART; /* state of tape drive */
+
+struct buf rwtbuf; /* header for raw i/o */
+struct proc *myproc; /* process which opened tape driver */
+
+char wtimeron; /* wtimer() active flag */
+char wtio; /* dma (i/o) active flag */
+char isrlock; /* isr() flag */
+
+struct proc * Hogproc; /* no Hogproc on Microport */
+#define ftoseg(x) ((unsigned) (x >> 16))
+
+struct wtstatus {
+ ushort wt_err; /* code for error encountered */
+ ushort wt_ercnt; /* number of error blocks */
+ ushort wt_urcnt; /* number of underruns */
+} wterror;
+
+/* defines for wtstatus.wt_err */
+#define TP_POR 0x100 /* Power on/reset occurred */
+#define TP_RES1 0x200 /* Reserved for end of media */
+#define TP_RES2 0x400 /* Reserved for bus parity */
+#define TP_BOM 0x800 /* Beginning of media */
+#define TP_MBD 0x1000 /* Marginal block detected */
+#define TP_NDT 0x2000 /* No data detected */
+#define TP_ILL 0x4000 /* Illegal command */
+#define TP_ST1 0x8000 /* Status byte 1 bits */
+#define TP_FIL 0x01 /* File mark detected */
+#define TP_BNL 0x02 /* Bad block not located */
+#define TP_UDA 0x04 /* Unrecoverable data error */
+#define TP_EOM 0x08 /* End of media */
+#define TP_WRP 0x10 /* Write protected cartridge */
+#define TP_USL 0x20 /* Unselected drive */
+#define TP_CNI 0x40 /* Cartridge not in place */
+#define TP_ST0 0x80 /* Status byte 0 bits */
+
+/* Grounds for reporting I/O error to user */
+#define TP_ERR0 (TP_BNL|TP_UDA|TP_WRP|TP_CNI|TP_FIL|TP_EOM|TP_USL)
+#define TP_ERR1 (TP_MBD|TP_NDT|TP_ILL)
+/* TP_ILL should never happen! */
+/*
+#define TP_ERR0 0x7f
+#define TP_ERR1 0x7700
+*/
+
+/* defines for reading out status from wangtek tape controller */
+#define READY 0x01 /* ready bit define */
+#define EXCEP 0x02 /* exception bit define */
+
+/* sleep priority */
+#define WTPRI (PZERO+10)
+
+char pagebuf[NBPS]; /* buffer of size NBPS */
+unsigned long pageaddr; /* physical addr of pagebuf */
+ /* pageaddr is used with DMA controller */
+time_t Hogtime; /* lbolt when Hog timer started */
+extern time_t lbolt;
+
+#define debug printf
+
+/*
+ * Strategy routine.
+ *
+ * Arguments:
+ * Pointer to buffer structure
+ * Function:
+ * Start transfer.
+ *
+ * It would be nice to have this multiple-threaded.
+ * There is a version of dump from Berkeley that works with multiple processes
+ * trading off with disk & tape I/O.
+ */
+
+int
+wtstrategy(bp)
+register struct buf *bp;
+{
+ unsigned ucnt1, ucnt2, finished;
+ unsigned long adr1, adr2;
+ int bad;
+
+ adr1 = kvtop(bp->b_un.b_addr);
+#ifdef DEBUG
+ debug("bpaddr %x\n", adr1);
+#endif
+ ucnt1 = bp->b_bcount % NBPG;
+ ucnt2 = 0;
+ adr2 = 0;
+#ifdef DEBUG
+ debug("WTstart: adr1 %lx cnt %x\n", adr1, ucnt1);
+#endif
+ /* 64K boundary? (XXX) */
+ if (ftoseg(adr1) != ftoseg(adr1 + (unsigned) ucnt1 - 1))
+ {
+ adr2 = (adr1 & 0xffff0000L) + 0x10000L;
+ ucnt2 = (adr1 + ucnt1) - adr2;
+ ucnt1 -= ucnt2;
+ }
+ /* page boundary? */
+ if (trunc_page(adr1) != trunc_page(adr1 + (unsigned) ucnt1 - 1))
+ { unsigned u;
+ u = NBPG - ((unsigned)bp->b_un.b_addr & (NBPG-1));
+ adr2 = kvtop(bp->b_un.b_addr + u);
+ ucnt2 = ucnt1 - u;
+ ucnt1 = u;
+ }
+ /* at file marks and end of tape, we just return '0 bytes available' */
+ if (wtflags & TPVOL) {
+ bp->b_resid = bp->b_bcount;
+ goto xit;
+ }
+ if ((Hogproc == (struct proc *) 0) && TPHOG(bp->b_dev))
+ {
+#ifdef DEBUG
+ printf("setting Hogproc\n");
+#endif
+ Hogtime = 0;
+ Hogproc = myproc;
+ }
+ if (bp->b_flags & B_READ) {
+ bad = 0;
+
+ /* For now, we assume that all data will be copied out */
+ /* If read command outstanding, just skip down */
+ if (!(wtflags & TPRO)) {
+ if (ERROR == wtsense(TP_WRP)) /* clear status */
+ goto errxit;
+#ifdef DEBUG
+ debug("WTread: Start read\n");
+#endif
+ if (!(wtflags & TPREAD) || (wtflags & TPWANY) ||
+ (rstart() == ERROR)) {
+#ifdef DEBUG
+ debug("Tpstart: read init error\n"); /* */
+#endif
+ goto errxit;
+ }
+ wtflags |= TPRO|TPRANY;
+ }
+
+ finished = 0;
+ /* Take a deep breath */
+ if (ucnt1) {
+ if ((rtape(adr1, ucnt1) == ERROR) &&
+ (wtsense(TP_WRP) == ERROR))
+ goto endio;
+ /* wait for it */
+ bad = pollrdy();
+ finished = bytes;
+ if (bad)
+ goto endio;
+ }
+ /* if a second I/O region, start it */
+ if (ucnt2) {
+ if ((rtape(adr2, ucnt2) == ERROR) &&
+ (wtsense(TP_WRP) == ERROR))
+ ucnt2 = 0; /* don't poll for me */
+ }
+
+ /* if second i/o pending wait for it */
+ if (ucnt2) {
+ pollrdy();
+ /* whether pollrdy is ok or not */
+ finished += bytes;
+ }
+ } else {
+ if (wtflags & TPWP) /* write protected */
+ goto errxit;
+
+ /* If write command outstanding, just skip down */
+ if (!(wtflags & TPWO)) {
+ if (ERROR == wtsense(0)) /* clear status */
+ {
+#ifdef DEBUG
+ debug("TPstart: sense 0\n");
+#endif
+ goto errxit;
+ }
+ if (!(wtflags & TPWRITE) || (wtflags & TPRANY) ||
+ (wstart() == ERROR)) {
+#ifdef DEBUG
+ debug("Tpstart: write init error\n"); /* */
+#endif
+ wtsense(0);
+
+errxit: bp->b_flags |= B_ERROR;
+ bp->b_resid = bp->b_bcount;
+ goto xit;
+ }
+ wtflags |= TPWO|TPWANY;
+ }
+
+ /* and hold your nose */
+ if (ucnt1 && ((wtape(adr1, ucnt1) == ERROR)
+ && (wtsense(0) == ERROR)))
+ finished = bytes;
+
+ else if (ucnt2 &&
+ (((ucnt1 && pollrdy()) ||
+ (wtape(adr2, ucnt2) == ERROR)) &&
+ (wtsense(0) == ERROR)))
+ finished = ucnt1 + NBPS + bytes;
+ /* All writes and/or copyins were fine! */
+ else
+ finished = bp->b_bcount;
+ bad = pollrdy();
+ }
+
+ endio:
+ if(bad == EIO) bad = 0;
+ wterror.wt_err = 0;
+ if (exflag && wtsense((bp->b_flags & B_READ) ? TP_WRP : 0)) {
+ if ((wterror.wt_err & TP_ST0)
+ && (wterror.wt_err & (TP_FIL|TP_EOM))) {
+#ifdef DEBUG
+ debug("WTsta: Hit end of tape\n"); /* */
+#endif
+ wtflags |= TPVOL;
+ if (wterror.wt_err & TP_FIL) {
+ if (wtflags & TPRO)
+ /* interrupter is bogus */
+ rstart(); /* restart read command */
+ else
+ wtflags &= ~TPWO;
+ finished += NBPS;
+ }
+ /* Reading file marks or writing end of tape return 0 bytes */
+ } else {
+ bp->b_flags |= B_ERROR;
+ wtflags &= ~(TPWO|TPRO);
+ }
+ }
+
+ if(bad) {
+ bp->b_flags |= B_ERROR;
+ bp->b_error = bad;
+ }
+ bp->b_resid = bp->b_bcount - finished;
+xit:
+ biodone(bp);
+ if (wtimeron)
+ Hogtime = lbolt;
+ else if (Hogproc == myproc)
+ Hogproc = (struct proc *) 0;
+}
+
+/*
+ * simulate an interrupt periodically while I/O is going
+ * this is necessary in case interrupts get eaten due to
+ * multiple devices on a single IRQ line
+ */
+wtimer()
+{
+ /* If I/O going and not in isr(), simulate interrupt
+ * If no I/O for at least 1 second, stop being a Hog
+ * If I/O done and not a Hog, turn off wtimer()
+ */
+ if (wtio && !isrlock)
+ isr();
+
+ if ((Hogproc == myproc) && Hogtime && (lbolt-Hogtime > HZ))
+ Hogproc = (struct proc *) 0;
+
+ if (wtio || (Hogproc == myproc))
+ timeout(wtimer, (caddr_t) 0, HZ);
+ else
+ wtimeron = 0;
+}
+
+
+wtrawio(bp)
+struct buf *bp;
+{
+ wtstrategy(bp);
+ biowait(bp);
+ return(0);
+}
+
+/*
+ * ioctl routine
+ * for user level QIC commands only
+ */
+wtioctl(dev, cmd, arg, mode)
+int dev, cmd;
+unsigned long arg;
+int mode;
+{
+ if (cmd == WTQICMD)
+ {
+ if ((qicmd((int)arg) == ERROR) || (rdyexc(HZ) == ERROR))
+ {
+ wtsense(0);
+ return(EIO);
+ }
+ return(0);
+ }
+ return(EINVAL);
+}
+
+/*
+ * open routine
+ * called on every device open
+ */
+wtopen(dev, flag)
+int dev, flag;
+{
+ if (first_wtopen_ever) {
+ wtinit();
+ first_wtopen_ever = 0;
+ }
+#ifdef DEBUG
+ printf("wtopen ...\n");
+#endif
+ if (!pageaddr) {
+ return(ENXIO);
+ }
+ if (wtflags & (TPINUSE)) {
+ return(ENXIO);
+ }
+ if (wtflags & (TPDEAD)) {
+ return(EIO);
+ }
+ /* If a rewind from the last session is going on, wait */
+ while(wtflags & TPREW) {
+#ifdef DEBUG
+ debug("Waiting for rew to finish\n");
+#endif
+ DELAY(1000000); /* delay one second */
+ }
+ /* Only do reset and select when tape light is off, and tape is rewound.
+ * This allows multiple volumes. */
+ if (wtflags & TPSTART) {
+ if (t_reset() != SUCCESS) {
+ return(ENXIO);
+ }
+#ifdef DEBUG
+ debug("reset done. calling wtsense\n");
+#endif
+ if (wtsense(TP_WRP) == ERROR) {
+ return (EIO);
+ }
+#ifdef DEBUG
+ debug("wtsense done\n");
+#endif
+ wtflags &= ~TPSTART;
+ }
+
+ wtflags = TPINUSE;
+ if (flag & FREAD)
+ wtflags |= TPREAD;
+ if (flag & FWRITE)
+ wtflags |= TPWRITE;
+ rwtbuf.b_flags = 0;
+ myproc = curproc; /* for comparison */
+#ifdef not
+ switch(TP_DENS(dev)) {
+case 0:
+cmds(0x28);
+break;
+case 1:
+cmds(0x29);
+break;
+case 2:
+cmds(0x27);
+break;
+case 3:
+cmds(0x24);
+ }
+#endif
+ return(0);
+}
+
+/*
+ * close routine
+ * called on last device close
+ * If not rewind-on-close, leave read or write command intact.
+ */
+wtclose(dev)
+{
+ int wtdsl2();
+
+#ifdef DEBUG
+ debug("WTclose:\n");
+#endif
+ if (Hogproc == myproc)
+ Hogproc = (struct proc *) 0;
+ if (!exflag && (wtflags & TPWANY) && !(wtflags & (TPSESS|TPDEAD))) {
+ if (!(wtflags & TPWO))
+ wstart();
+#ifdef DEBUG
+ debug("WT: Writing file mark\n");
+#endif
+ wmark(); /* write file mark */
+#ifdef DEBUG
+ debug("WT: Wrote file mark, going to wait\n");
+#endif
+ if (rdyexc(HZ/10) == ERROR) {
+ wtsense(0);
+ }
+ }
+ if (TP_REWCLOSE(dev) || (wtflags & (TPSESS|TPDEAD))) {
+ /* rewind tape to beginning of tape, deselect tape, and make a note */
+ /* don't wait until rewind, though */
+ /* Ending read or write causes rewind to happen, if no error,
+ * and READY and EXCEPTION stay up until it finishes */
+ if (wtflags & (TPRO|TPWO))
+ {
+#ifdef DEBUG
+ debug("End read or write\n");
+#endif
+ rdyexc(HZ/10);
+ ioend();
+ wtflags &= ~(TPRO|TPWO);
+ }
+ else wtwind();
+ wtflags |= TPSTART | TPREW;
+ timeout(wtdsl2, 0, HZ);
+ }
+ else if (!(wtflags & (TPVOL|TPWANY)))
+ {
+ /* space forward to after next file mark no writing done */
+ /* This allows skipping data without reading it.*/
+#ifdef DEBUG
+ debug("Reading past file mark\n");
+#endif
+ if (!(wtflags & TPRO))
+ rstart();
+ rmark();
+ if (rdyexc(HZ/10))
+ {
+ wtsense(TP_WRP);
+ }
+ }
+ wtflags &= TPREW|TPDEAD|TPSTART|TPRO|TPWO;
+ return(0);
+}
+
+/* return ERROR if user I/O request should receive an I/O error code */
+
+wtsense(ignor)
+{
+ wtflags &= ~(TPRO|TPWO);
+#ifdef DEBUGx
+ debug("WTsense: start ");
+#endif
+ if (rdstatus(&wterror) == ERROR)
+ {
+#ifdef DEBUG
+ debug("WTsense: Can't read status\n");
+#endif
+ return(ERROR);
+ }
+#ifdef DEBUG
+ if (wterror.wt_err & (TP_ST0|TP_ST1))
+ {
+ debug("Tperror: status %x error %d underruns %d\n",
+ wterror.wt_err, wterror.wt_ercnt, wterror.wt_urcnt);
+ }
+ else
+ debug("done. no error\n");
+#endif
+ wterror.wt_err &= ~ignor; /* ignore certain errors */
+ reperr(wterror.wt_err);
+ if (((wterror.wt_err & TP_ST0) && (wterror.wt_err & TP_ERR0)) ||
+ ((wterror.wt_err & TP_ST1) && (wterror.wt_err & TP_ERR1)))
+ return ERROR;
+
+ return SUCCESS;
+}
+
+/* lifted from tdriver.c from Wangtek */
+reperr(srb0)
+int srb0;
+{
+ int s0 = srb0 & (TP_ERR0|TP_ERR1); /* find out which exception to report */
+
+ if (s0) {
+ if (s0 & TP_USL)
+ sterr("Drive not online");
+ else if (s0 & TP_CNI)
+ sterr("No cartridge");
+ else if ((s0 & TP_WRP) && !(wtflags & TPWP))
+ {
+ sterr("Tape is write protected");
+ wtflags |= TPWP;
+ }
+ /*
+ if (s0 & TP_FIL)
+ sterr("Filemark detected");
+ */
+ else if (s0 & TP_BNL)
+ sterr("Block in error not located");
+ else if (s0 & TP_UDA)
+ sterr("Unrecoverable data error");
+ /*
+ else if (s0 & TP_EOM)
+ sterr("End of tape");
+ */
+ else if (s0 & TP_NDT)
+ sterr("No data detected");
+ /*
+ if (s0 & TP_POR)
+ sterr("Reset occured");
+ */
+ else if (s0 & TP_BOM)
+ sterr("Beginning of tape");
+ else if (s0 & TP_ILL)
+ sterr("Illegal command");
+ }
+}
+
+sterr(errstr)
+char *errstr;
+{
+ printf("Streamer: %s\n", errstr);
+}
+
+/* Wait until rewind finishes, and deselect drive */
+wtdsl2() {
+ int stat;
+
+ stat = inb(wtport) & (READY|EXCEP);
+#ifdef DEBUG
+ debug("Timeout: Waiting for rewind to finish: stat %x\n", stat);
+#endif
+ switch (stat) {
+ /* They're active low, ya'know */
+ case READY|EXCEP:
+ timeout(wtdsl2, (caddr_t) 0, HZ);
+ return;
+ case EXCEP:
+ wtflags &= ~TPREW;
+ return;
+ case READY:
+ case 0:
+ wtflags &= ~TPREW;
+ sterr("Rewind failed");
+ wtsense(TP_WRP);
+ return;
+ }
+ }
+
+wtwind() {
+#ifdef DEBUG
+ debug("WT: About to rewind\n");
+#endif
+ rwind(); /* actually start rewind */
+}
+
+wtintr(unit) {
+ if (wtflags & (TPWO|TPRO))
+ {
+ isrlock = 1;
+ if (wtio) isr();
+ isrlock = 0;
+ }
+}
+
+wtinit() {
+ if (wtchan < 1 || wtchan > 3)
+ {
+ sterr("Bad DMA channel, cannot init driver");
+ return;
+ }
+ wtlinit(); /* init assembly language variables */
+ pageset();
+}
+
+rdyexc(ticks)
+{
+ int s;
+#ifdef DEBUG
+ int os = 0xffff; /* force printout first time */
+#endif
+ for (;;) { /* loop until ready or exception */
+ s=(inb(wtport) & 0xff); /* read the status register */
+#ifdef DEBUG
+ if (os != s) {
+ debug("Status reg = %x\n", s); /* */
+ os = s;
+ }
+#endif
+ if (!(s & EXCEP)) /* check if exception have occured */
+ break;
+ if (!(s & READY)) /* check if controller is ready */
+ break;
+ s = splbio();
+ DELAY((ticks/HZ)*1000000); /* */
+ splx(s);
+ }
+#ifdef DEBUG
+ debug("Status reg = %x on return\n", s); /* */
+#endif
+ return((s & EXCEP)?SUCCESS:ERROR); /* return exception if it occured */
+}
+
+pollrdy()
+{
+ int sps;
+#ifdef DEBUG
+ debug("Pollrdy\n");
+#endif
+ sps = splbio();
+ while (wtio) {
+ int error;
+
+ if (error = tsleep((caddr_t)&wci, WTPRI | PCATCH,
+ "wtpoll", 0)) {
+ splx(sps);
+ return(error);
+ }
+ }
+ splx(sps);
+#ifdef DEBUG
+ debug("Finish poll, wci %d exflag %d\n", wci, exflag);
+#endif
+ return (EIO);
+}
+
+wtdma() /* start up i/o operation, called from dma() in wtlib1.s */
+{
+ wtio = 1;
+ if (!wtimeron)
+ {
+ wtimeron = 1;
+ timeout(wtimer, (caddr_t) 0, HZ/2);
+ }
+}
+
+wtwake() /* end i/o operation, called from isr() in wtlib1.s */
+{
+ wtio = 0;
+ wakeup(&wci);
+}
+
+pageset()
+{
+ unsigned long pp;
+
+ pp = (unsigned long) pagebuf;
+ pageaddr = kvtop(pp);
+#ifdef DEBUG
+ debug("pageset: addr %lx\n", pageaddr);
+#endif
+}
+
+
+
+#define near
+
+static near
+sendcmd()
+{
+ /* desired command in global mbits */
+
+ outb(CTLPORT, mbits | REQUEST); /* set request */
+ while (inb(STATPORT) & READY); /* wait for ready */
+ outb(CTLPORT, mbits & ~REQUEST); /* reset request */
+ while ((inb(STATPORT) & READY) == 0); /* wait for not ready */
+}
+
+static near /* execute command */
+cmds(cmd)
+{
+ register s;
+
+ do s = inb(STATPORT);
+ while ((s & STAT) == STAT); /* wait for ready */
+
+ if ((s & EXCEP) == 0) /* if exception */
+ return ERROR; /* error */
+
+ outb(CMDPORT, cmd); /* output the command */
+
+ outb(CTLPORT, mbits=ONLINE); /* set & send ONLINE */
+ sendcmd();
+
+ return SUCCESS;
+}
+
+qicmd(cmd)
+{
+ return cmds(cmd);
+}
+
+rstart()
+{
+ return cmds(RDDATA);
+}
+
+rmark()
+{
+ return cmds(READFM);
+}
+
+wstart()
+{
+ return cmds(WRTDATA);
+}
+
+ioend()
+{
+ register s;
+ register rval = SUCCESS;
+
+ do s = inb(STATPORT);
+ while ((s & STAT) == STAT); /* wait for ready */
+
+ if ((s & EXCEP) == 0) /* if exception */
+ rval = ERROR; /* error */
+
+ mbits &= ~ONLINE;
+ outb(CTLPORT, mbits); /* reset ONLINE */
+ outb(MASKREG, wtchan+4); /* turn off dma */
+ outb(CLEARFF, 0); /* reset direction flag */
+
+ return rval;
+}
+
+wmark()
+{
+ register s;
+
+ if (cmds(WRITEFM) == ERROR)
+ return ERROR;
+
+ do s = inb(STATPORT);
+ while ((s & STAT) == STAT); /* wait for ready */
+
+ if ((s & EXCEP) == 0) /* if exception */
+ return ERROR; /* error */
+
+ return SUCCESS;
+}
+
+rwind()
+{
+ register s;
+
+ mbits = CMDOFF;
+
+ do s = inb(STATPORT);
+ while ((s & STAT) == STAT); /* wait for ready */
+
+ outb(CMDPORT, REWIND);
+ sendcmd();
+
+ return SUCCESS;
+}
+
+rdstatus(stp)
+char *stp; /* pointer to 6 byte buffer */
+{
+ register s;
+ int n;
+
+ do s = inb(STATPORT);
+ while ((s & STAT) == STAT); /* wait for ready or exception */
+
+ outb(CMDPORT, RDSTAT);
+ sendcmd(); /* send read status command */
+
+ for (n=0; n<6; n++)
+ {
+#ifdef DEBUGx
+ debug("rdstatus: waiting, byte %d\n", n);
+#endif
+ do s = inb(STATPORT);
+ while ((s & STAT) == STAT); /* wait for ready */
+#ifdef DEBUGx
+ debug("rdstatus: done\n");
+#endif
+ if ((s & EXCEP) == 0) /* if exception */
+ return ERROR; /* error */
+
+ *stp++ = inb(DATAPORT); /* read status byte */
+
+ outb(CTLPORT, mbits | REQUEST); /* set request */
+#ifdef DEBUGx
+ debug("rdstatus: waiting after request, byte %d\n", n);
+#endif
+ while ((inb(STATPORT)&READY) == 0); /* wait for not ready */
+ for (s=100; s>0; s--); /* wait an additional time */
+
+ outb(CTLPORT, mbits & ~REQUEST);/* unset request */
+#ifdef DEBUGx
+ debug("rdstatus: done\n");
+#endif
+ }
+ return SUCCESS;
+}
+
+t_reset()
+{
+ register i;
+ mbits |= RESET;
+ outb(CTLPORT, mbits); /* send reset */
+ DELAY(20);
+ mbits &= ~RESET;
+ outb(CTLPORT, mbits); /* turn off reset */
+ if ((inb(STATPORT) & RESETMASK) == RESETVAL)
+ return SUCCESS;
+ return ERROR;
+}
+
+static
+dma()
+{
+ int x=splbio();
+ wtdma();
+ outb(CLEARFF, 0);
+ outb(MODEREG, mode); /* set dma mode */
+ outb(dmareg, bufptr & 0xFF);
+ outb(dmareg, (bufptr>>8) & 0xFF);
+ outb(pagereg, (bufptr>>16) & 0xFF);
+ outb(dmareg+1, (BLKSIZE-1) & 0xFF);
+ outb(dmareg+1, (BLKSIZE-1) >> 8);
+ outb(wtport, eqdma+ONLINE);
+ outb(MASKREG, wtchan); /* enable command to 8237, start dma */
+ splx(x);
+}
+
+static near
+wtstart(buf, cnt)
+long buf;
+int cnt;
+{
+ register s;
+
+ bufptr = buf; /* init statics */
+ numbytes = cnt;
+ wci = 0; /* init flags */
+ exflag = 0;
+ bytes = 0; /* init counter */
+
+ do s = inb(STATPORT) & STAT;
+ while (s == STAT); /* wait for ready or error */
+
+ if (s & EXCEP) /* no error */
+ {
+ dma();
+ return SUCCESS;
+ }
+ return ERROR; /* error */
+}
+
+rtape(buf, cnt)
+long buf; /* physical address */
+int cnt; /* number of bytes */
+{
+ mode = dma_read;
+ return wtstart(buf,cnt);
+}
+
+wtape(buf, cnt)
+long buf; /* physical address */
+int cnt; /* number of bytes */
+{
+ mode = dma_write;
+ return wtstart(buf,cnt);
+}
+
+isr()
+{
+ int stat = inb(wtport);
+ if (!(stat & EXCEP)) /* exception during I/O */
+ {
+ if (bytes + BLKSIZE >= numbytes) wci = 1;
+ exflag = 1;
+ goto isrwake;
+ }
+ if ((stat & READY) || !(inb(STATUSREG) & dma_done))
+ return;
+ exflag = 0;
+ outb(wtport, ONLINE);
+ bytes += BLKSIZE;
+ if (bytes >= numbytes) /* normal completion of I/O */
+ {
+ wci = 1;
+isrwake:
+ outb(MASKREG, 4+wtchan); /* turn off dma */
+ wtwake(); /* wake up user level */
+ }
+ else
+ { /* continue I/O */
+ bufptr += BLKSIZE;
+ dma();
+ }
+}
+
+wtlinit()
+{
+ switch (wtchan) {
+ case 1:
+ return;
+ case 2:
+ pagereg = 0x81;
+ dma_done = 4;
+ break;
+ case 3:
+ eqdma = 0x10;
+ pagereg = 0x82;
+ dma_done = 8;
+ break;
+ }
+ dma_write = wtchan+0x48;
+ dma_read = wtchan+0x44;
+ dmareg = wtchan+wtchan;
+}
+
+wtsize()
+{
+}
+
+wtdump()
+{
+}
+
+#include "i386/isa/isa_device.h"
+#include "i386/isa/icu.h"
+
+int wtprobe(), wtattach();
+struct isa_driver wtdriver = {
+ wtprobe, wtattach, "wt",
+};
+
+wtprobe(dvp)
+ struct isa_device *dvp;
+{
+ int val,i,s;
+
+#ifdef lint
+ wtintr(0);
+#endif
+
+ wtport = dvp->id_iobase;
+ if(t_reset() != SUCCESS) return(0);
+ return(1);
+}
+
+wtattach() { }
+
+#endif NWT
diff --git a/sys/i386/isa/wtreg.h b/sys/i386/isa/wtreg.h
new file mode 100644
index 0000000..8f79ca1
--- /dev/null
+++ b/sys/i386/isa/wtreg.h
@@ -0,0 +1,95 @@
+/*-
+ * 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.
+ *
+ * @(#)wtreg.h 7.1 (Berkeley) 5/9/91
+ */
+
+/*
+ *
+ * Copyright (c) 1989 Carnegie-Mellon University.
+ * All rights reserved.
+ *
+ * Authors: Robert Baron
+ *
+ * Permission to use, copy, modify and distribute this software and
+ * its documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
+ * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+/*
+ * HISTORY
+ * $Log: wtreg.h,v $
+ * Revision 2.2.1.1 90/01/08 13:29:25 rvb
+ * Add Intel copyright.
+ * [90/01/08 rvb]
+ *
+ * Revision 2.2 89/09/25 12:33:09 rvb
+ * Driver was provided by Intel 9/18/89.
+ * [89/09/23 rvb]
+ *
+ */
+
+/*
+ *
+ * Copyright 1988, 1989 by Intel Corporation
+ *
+ */
+
+/*
+ * wtioctl.h
+ * defines ioctl parameters for direct QIC commands
+ */
+
+#define WTIOC ('W'<<8)
+#define WTQICMD (WTIOC|0)
+
+/* QIC commands allowed */
+#define SELECT 0x01
+#define REWIND 0x21
+#define ERASE 0x22
+#define RETENS 0x24
OpenPOWER on IntegriCloud