summaryrefslogtreecommitdiffstats
path: root/sys/dev
diff options
context:
space:
mode:
authorrgrimes <rgrimes@FreeBSD.org>1993-06-12 14:58:17 +0000
committerrgrimes <rgrimes@FreeBSD.org>1993-06-12 14:58:17 +0000
commit25062ba061871945759b3baa833fe64969383e40 (patch)
tree2d1c31051ed0dbaad984013c9fe695b1a01e1c39 /sys/dev
parentf078b88a160c467761b3f3641f05dfd0aa3f7753 (diff)
downloadFreeBSD-src-25062ba061871945759b3baa833fe64969383e40.zip
FreeBSD-src-25062ba061871945759b3baa833fe64969383e40.tar.gz
Initial import, 0.1 + pk 0.2.4-B1
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/fdc/fdc.c903
-rw-r--r--sys/dev/fdc/fdcreg.h72
-rw-r--r--sys/dev/ic/i8237.h9
-rw-r--r--sys/dev/ic/nec765.h71
-rw-r--r--sys/dev/ic/ns16550.h50
-rw-r--r--sys/dev/sio/sio.c1721
-rw-r--r--sys/dev/sio/sioreg.h113
-rw-r--r--sys/dev/speaker/spkr.c520
8 files changed, 3459 insertions, 0 deletions
diff --git a/sys/dev/fdc/fdc.c b/sys/dev/fdc/fdc.c
new file mode 100644
index 0000000..a461e81
--- /dev/null
+++ b/sys/dev/fdc/fdc.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/dev/fdc/fdcreg.h b/sys/dev/fdc/fdcreg.h
new file mode 100644
index 0000000..948f566
--- /dev/null
+++ b/sys/dev/fdc/fdcreg.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/dev/ic/i8237.h b/sys/dev/ic/i8237.h
new file mode 100644
index 0000000..dc269f2
--- /dev/null
+++ b/sys/dev/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/dev/ic/nec765.h b/sys/dev/ic/nec765.h
new file mode 100644
index 0000000..b84b46e
--- /dev/null
+++ b/sys/dev/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/dev/ic/ns16550.h b/sys/dev/ic/ns16550.h
new file mode 100644
index 0000000..e20e9aa
--- /dev/null
+++ b/sys/dev/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/dev/sio/sio.c b/sys/dev/sio/sio.c
new file mode 100644
index 0000000..38503fd
--- /dev/null
+++ b/sys/dev/sio/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/dev/sio/sioreg.h b/sys/dev/sio/sioreg.h
new file mode 100644
index 0000000..2a62683
--- /dev/null
+++ b/sys/dev/sio/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/dev/speaker/spkr.c b/sys/dev/speaker/spkr.c
new file mode 100644
index 0000000..ffeec08
--- /dev/null
+++ b/sys/dev/speaker/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 */
OpenPOWER on IntegriCloud