diff options
author | rgrimes <rgrimes@FreeBSD.org> | 1993-06-12 14:58:17 +0000 |
---|---|---|
committer | rgrimes <rgrimes@FreeBSD.org> | 1993-06-12 14:58:17 +0000 |
commit | 25062ba061871945759b3baa833fe64969383e40 (patch) | |
tree | 2d1c31051ed0dbaad984013c9fe695b1a01e1c39 /sys/i386/isa | |
parent | f078b88a160c467761b3f3641f05dfd0aa3f7753 (diff) | |
download | FreeBSD-src-25062ba061871945759b3baa833fe64969383e40.zip FreeBSD-src-25062ba061871945759b3baa833fe64969383e40.tar.gz |
Initial import, 0.1 + pk 0.2.4-B1
Diffstat (limited to 'sys/i386/isa')
-rw-r--r-- | sys/i386/isa/aha1542.c | 1644 | ||||
-rw-r--r-- | sys/i386/isa/clock.c | 271 | ||||
-rw-r--r-- | sys/i386/isa/fd.c | 903 | ||||
-rw-r--r-- | sys/i386/isa/fdreg.h | 72 | ||||
-rw-r--r-- | sys/i386/isa/ic/i8042.h | 23 | ||||
-rw-r--r-- | sys/i386/isa/ic/i8237.h | 9 | ||||
-rw-r--r-- | sys/i386/isa/ic/nec765.h | 71 | ||||
-rw-r--r-- | sys/i386/isa/ic/ns16450.h | 49 | ||||
-rw-r--r-- | sys/i386/isa/ic/ns16550.h | 50 | ||||
-rw-r--r-- | sys/i386/isa/icu.h | 109 | ||||
-rw-r--r-- | sys/i386/isa/icu.s | 376 | ||||
-rw-r--r-- | sys/i386/isa/isa.c | 766 | ||||
-rw-r--r-- | sys/i386/isa/isa.h | 188 | ||||
-rw-r--r-- | sys/i386/isa/isa_device.h | 85 | ||||
-rw-r--r-- | sys/i386/isa/kbd.h | 58 | ||||
-rw-r--r-- | sys/i386/isa/lpt.c | 455 | ||||
-rw-r--r-- | sys/i386/isa/lptreg.h | 34 | ||||
-rw-r--r-- | sys/i386/isa/npx.c | 564 | ||||
-rw-r--r-- | sys/i386/isa/rtc.h | 85 | ||||
-rw-r--r-- | sys/i386/isa/sio.c | 1721 | ||||
-rw-r--r-- | sys/i386/isa/sioreg.h | 113 | ||||
-rw-r--r-- | sys/i386/isa/spkr.c | 520 | ||||
-rw-r--r-- | sys/i386/isa/timerreg.h | 89 | ||||
-rw-r--r-- | sys/i386/isa/ultra14f.c | 1308 | ||||
-rw-r--r-- | sys/i386/isa/vector.s | 376 | ||||
-rw-r--r-- | sys/i386/isa/wd.c | 1352 | ||||
-rw-r--r-- | sys/i386/isa/wdreg.h | 143 | ||||
-rw-r--r-- | sys/i386/isa/wt.c | 1162 | ||||
-rw-r--r-- | sys/i386/isa/wtreg.h | 95 |
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 |