sio.c
来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 2,443 行 · 第 1/5 页
C
2,443 行
/*- * Copyright (c) 1991 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)com.c 7.5 (Berkeley) 5/16/91 * $\Id: sio.c,v 1.99.4.5 1996-11-15 16:36:37-07 jkh Exp $ */#include "sio.h"#if NSIO > 0/* * Serial driver, based on 386BSD-0.1 com driver. * Mostly rewritten to use pseudo-DMA. * Works for National Semiconductor NS8250-NS16550AF UARTs. * COM driver, based on HP dca driver. */#include <sys/param.h>#include <sys/systm.h>#include <sys/reboot.h>#include <sys/ioctl.h>#include <sys/tty.h>#include <sys/proc.h>#include <sys/user.h>#include <sys/conf.h>#include <sys/dkstat.h>#include <sys/file.h>#include <sys/uio.h>#include <sys/kernel.h>#include <sys/malloc.h>#include <sys/syslog.h>#include <sys/devconf.h>#include <sys/interrupt.h>#include <machine/clock.h>#include <i386/isa/icu.h> /* XXX just to get at `imen' */#include <i386/isa/isa.h>#include <i386/isa/isa_device.h>#include <i386/isa/sioreg.h>#include <i386/isa/ic/ns16550.h>#define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */#define RB_I_HIGH_WATER (TTYHOG - 2 * RS_IBUFSIZE)#define RS_IBUFSIZE 256#define CALLOUT_MASK 0x80#define CONTROL_MASK 0x60#define CONTROL_INIT_STATE 0x20#define CONTROL_LOCK_STATE 0x40#define DEV_TO_UNIT(dev) (MINOR_TO_UNIT(minor(dev)))#define MINOR_MAGIC_MASK (CALLOUT_MASK | CONTROL_MASK)#define MINOR_TO_UNIT(mynor) ((mynor) & ~MINOR_MAGIC_MASK)#ifdef COM_MULTIPORT/* checks in flags for multiport and which is multiport "master chip" * for a given card */#define COM_ISMULTIPORT(dev) ((dev)->id_flags & 0x01)#define COM_MPMASTER(dev) (((dev)->id_flags >> 8) & 0x0ff)#define COM_NOTAST4(dev) ((dev)->id_flags & 0x04)#endif /* COM_MULTIPORT */#define COM_LOSESOUTINTS(dev) ((dev)->id_flags & 0x08)#define COM_NOFIFO(dev) ((dev)->id_flags & 0x02)#define COM_VERBOSE(dev) ((dev)->id_flags & 0x80)#define com_scr 7 /* scratch register for 16450-16550 (R/W) *//* * Input buffer watermarks. * The external device is asked to stop sending when the buffer exactly reaches * high water, or when the high level requests it. * The high level is notified immediately (rather than at a later clock tick) * when this watermark is reached. * The buffer size is chosen so the watermark should almost never be reached. * The low watermark is invisibly 0 since the buffer is always emptied all at * once. */#define RS_IHIGHWATER (3 * RS_IBUFSIZE / 4)/* * com state bits. * (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher * than the other bits so that they can be tested as a group without masking * off the low bits. * * The following com and tty flags correspond closely: * CS_BUSY = TS_BUSY (maintained by comstart(), siopoll() and * siostop()) * CS_TTGO = ~TS_TTSTOP (maintained by comparam() and comstart()) * CS_CTS_OFLOW = CCTS_OFLOW (maintained by comparam()) * CS_RTS_IFLOW = CRTS_IFLOW (maintained by comparam()) * TS_FLUSH is not used. * XXX I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON. * XXX CS_*FLOW should be CF_*FLOW in com->flags (control flags not state). */#define CS_BUSY 0x80 /* output in progress */#define CS_TTGO 0x40 /* output not stopped by XOFF */#define CS_ODEVREADY 0x20 /* external device h/w ready (CTS) */#define CS_CHECKMSR 1 /* check of MSR scheduled */#define CS_CTS_OFLOW 2 /* use CTS output flow control */#define CS_DTR_OFF 0x10 /* DTR held off */#define CS_ODONE 4 /* output completed */#define CS_RTS_IFLOW 8 /* use RTS input flow control */static char const * const error_desc[] = {#define CE_OVERRUN 0 "silo overflow",#define CE_INTERRUPT_BUF_OVERFLOW 1 "interrupt-level buffer overflow",#define CE_TTY_BUF_OVERFLOW 2 "tty-level buffer overflow",};#define CE_NTYPES 3#define CE_RECORD(com, errnum) (++(com)->delta_error_counts[errnum])/* types. XXX - should be elsewhere */typedef u_int Port_t; /* hardware port */typedef u_char bool_t; /* boolean *//* queue of linear buffers */struct lbq { u_char *l_head; /* next char to process */ u_char *l_tail; /* one past the last char to process */ struct lbq *l_next; /* next in queue */ bool_t l_queued; /* nonzero if queued */};/* com device structure */struct com_s { u_char state; /* miscellaneous flag bits */ bool_t active_out; /* nonzero if the callout device is open */ u_char cfcr_image; /* copy of value written to CFCR */ u_char ftl; /* current rx fifo trigger level */ u_char ftl_init; /* ftl_max for next open() */ u_char ftl_max; /* maximum ftl for curent open() */ bool_t hasfifo; /* nonzero for 16550 UARTs */ bool_t loses_outints; /* nonzero if device loses output interrupts */ u_char mcr_image; /* copy of value written to MCR */#ifdef COM_MULTIPORT bool_t multiport; /* is this unit part of a multiport device? */#endif /* COM_MULTIPORT */ bool_t no_irq; /* nonzero if irq is not attached */ bool_t poll; /* nonzero if polling is required */ bool_t poll_output; /* nonzero if polling for output is required */ int unit; /* unit number */ int dtr_wait; /* time to hold DTR down on close (* 1/hz) */ u_int tx_fifo_size; u_int wopeners; /* # processes waiting for DCD in open() */ /* * The high level of the driver never reads status registers directly * because there would be too many side effects to handle conveniently. * Instead, it reads copies of the registers stored here by the * interrupt handler. */ u_char last_modem_status; /* last MSR read by intr handler */ u_char prev_modem_status; /* last MSR handled by high level */ u_char hotchar; /* ldisc-specific char to be handled ASAP */ u_char *ibuf; /* start of input buffer */ u_char *ibufend; /* end of input buffer */ u_char *ihighwater; /* threshold in input buffer */ u_char *iptr; /* next free spot in input buffer */ struct lbq obufq; /* head of queue of output buffers */ struct lbq obufs[2]; /* output buffers */ Port_t data_port; /* i/o ports */ Port_t int_id_port; Port_t iobase; Port_t modem_ctl_port; Port_t line_status_port; Port_t modem_status_port; struct tty *tp; /* cross reference */ /* Initial state. */ struct termios it_in; /* should be in struct tty */ struct termios it_out; /* Lock state. */ struct termios lt_in; /* should be in struct tty */ struct termios lt_out; bool_t do_timestamp; struct timeval timestamp; u_long bytes_in; /* statistics */ u_long bytes_out; u_int delta_error_counts[CE_NTYPES]; u_long error_counts[CE_NTYPES]; /* * Ping-pong input buffers. The extra factor of 2 in the sizes is * to allow for an error byte for each input byte. */#define CE_INPUT_OFFSET RS_IBUFSIZE u_char ibuf1[2 * RS_IBUFSIZE]; u_char ibuf2[2 * RS_IBUFSIZE]; /* * Data area for output buffers. Someday we should build the output * buffer queue without copying data. */ u_char obuf1[256]; u_char obuf2[256];};/* * XXX public functions in drivers should be declared in headers produced * by `config', not here. *//* Interrupt handling entry points. */void siointr __P((int unit));void siointrts __P((int unit));void siopoll __P((void));/* Device switch entry points. */int sioopen __P((dev_t dev, int oflags, int devtype, struct proc *p));int sioclose __P((dev_t dev, int fflag, int devtype, struct proc *p));int sioread __P((dev_t dev, struct uio *uio, int ioflag));int siowrite __P((dev_t dev, struct uio *uio, int ioflag));int sioioctl __P((dev_t dev, int cmd, caddr_t data, int fflag, struct proc *p));void siostop __P((struct tty *tp, int rw));#define sioreset noresetstruct tty *siodevtotty __P((dev_t dev));#define siommap nommap#define siostrategy nostrategystatic int sioattach __P((struct isa_device *dev));static timeout_t siodtrwakeup;static void comhardclose __P((struct com_s *com));static void siointr1 __P((struct com_s *com));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 sioregisterdev __P((struct isa_device *id));static void siosettimeout __P((void));static void comstart __P((struct tty *tp));static timeout_t comwakeup;static void disc_optim __P((struct tty *tp, struct termios *t, struct com_s *com));#ifdef DSI_SOFT_MODEMstatic int LoadSoftModem __P((int unit,int base_io, u_long size, u_char *ptr));#endif /* DSI_SOFT_MODEM *//* table and macro for fast conversion from a unit number to its com struct */static struct com_s *p_com_addr[NSIO];#define com_addr(unit) (p_com_addr[unit])static struct timeval intr_timestamp;struct isa_driver siodriver = { sioprobe, sioattach, "sio"};static int comconsole = -1;#ifdef CONSPEEDstatic speed_t comdefaultrate = CONSPEED;#elsestatic speed_t comdefaultrate = TTYDEF_SPEED;#endifstatic u_int com_events; /* input chars + weighted output completions */static int commajor;static int sio_timeout;static int sio_timeouts_until_log;#if 0 /* XXX */static struct tty *sio_tty[NSIO];#elsestatic struct tty sio_tty[NSIO];static int nsio_tty = NSIO;#endif#ifdef KGDB#include <machine/remote-sl.h>extern int kgdb_dev;extern int kgdb_rate;extern int kgdb_debug_init;#endifstatic 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};static struct kern_devconf kdc_sio[NSIO] = { { 0, 0, 0, /* filled in by dev_attach */ "sio", 0, { MDDT_ISA, 0, "tty" }, isa_generic_externalize, 0, 0, ISA_EXTERNALLEN, &kdc_isa0, /* parent */ 0, /* parentdata */ DC_UNCONFIGURED, /* state */ "RS-232 serial port", DC_CLS_SERIAL /* class */} };static voidsioregisterdev(id) struct isa_device *id;{ int unit; unit = id->id_unit; if (unit != 0) kdc_sio[unit] = kdc_sio[0]; kdc_sio[unit].kdc_unit = unit; kdc_sio[unit].kdc_isa = id; dev_attach(&kdc_sio[unit]);}static intsioprobe(dev) struct isa_device *dev;{ static bool_t already_init; bool_t failures[10]; int fn; struct isa_device *idev; Port_t iobase; u_char mcr_image; int result; struct isa_device *xdev; sioregisterdev(dev); if (!already_init) { /* * Turn off MCR_IENABLE for all likely serial ports. An unused * port with its MCR_IENABLE gate open will inhibit interrupts * from any used port that shares the interrupt vector. * XXX the gate enable is elsewhere for some multiports. */ for (xdev = isa_devtab_tty; xdev->id_driver != NULL; xdev++) if (xdev->id_driver == &siodriver && xdev->id_enabled) outb(xdev->id_iobase + com_mcr, 0); already_init = TRUE; } /* * If the device is on a multiport card and has an AST/4 * compatible interrupt control register, initialize this * register and prepare to leave MCR_IENABLE clear in the mcr. * Otherwise, prepare to set MCR_IENABLE in the mcr. * Point idev to the device struct giving the correct id_irq. * This is the struct for the master device if there is one. */ idev = dev; mcr_image = MCR_IENABLE;#ifdef COM_MULTIPORT if (COM_ISMULTIPORT(dev)) { idev = find_isadev(isa_devtab_tty, &siodriver, COM_MPMASTER(dev)); if (idev == NULL) { printf("sio%d: master device %d not configured\n", dev->id_unit, COM_MPMASTER(dev)); return (0); } if (!COM_NOTAST4(dev)) { outb(idev->id_iobase + com_scr, idev->id_irq ? 0x80 : 0); mcr_image = 0; } }#endif /* COM_MULTIPORT */ if (idev->id_irq == 0) mcr_image = 0; bzero(failures, sizeof failures); iobase = dev->id_iobase; /* * We don't want to get actual interrupts, just masked ones. * Interrupts from this line should already be masked in the ICU, * but mask them in the processor as well in case there are some * (misconfigured) shared interrupts. */ disable_intr();/* EXTRA DELAY? */#ifndef OSKIT /* * XXX DELAY() reenables CPU interrupts. This is a problem for * shared interrupts after the first device using one has been * successfully probed - config_isadev() has enabled the interrupt * in the ICU. */ outb(IO_ICU1 + 1, 0xff);#endif /* OSKIT */ /* * Initialize the speed and the word size and wait long enough to * drain the maximum of 16 bytes of junk in device output queues. * The speed is undefined after a master reset and must be set * before relying on anything related to output. There may be * junk after a (very fast) soft reboot and (apparently) after * master reset. * XXX what about the UART bug avoided by waiting in comparam()? * We don't want to to wait long enough to drain at 2 bps. */ outb(iobase + com_cfcr, CFCR_DLAB | CFCR_8BITS); outb(iobase + com_dlbl, COMBRD(9600) & 0xff); outb(iobase + com_dlbh, (u_int) COMBRD(9600) >> 8); outb(iobase + com_cfcr, CFCR_8BITS); DELAY((16 + 1) * 1000000 / (9600 / 10)); /* * Enable the interrupt gate and disable device interupts. This * should leave the device driving the interrupt line low and * guarantee an edge trigger if an interrupt can be generated. *//* EXTRA DELAY? */ outb(iobase + com_mcr, mcr_image); outb(iobase + com_ier, 0); /* * Attempt to set loopback mode so that we can send a null byte * without annoying any external device. *//* EXTRA DELAY? */ outb(iobase + com_mcr, mcr_image | MCR_LOOPBACK); /* * Attempt to generate an output interrupt. On 8250's, setting * IER_ETXRDY generates an interrupt independent of the current * setting and independent of whether the THR is empty. On 16450's, * setting IER_ETXRDY generates an interrupt independent of the * current setting. On 16550A's, setting IER_ETXRDY only * generates an interrupt when IER_ETXRDY is not already set. */ outb(iobase + com_ier, IER_ETXRDY); /* * On some 16x50 incompatibles, setting IER_ETXRDY doesn't generate * an interrupt. They'd better generate one for actually doing * output. Loopback may be broken on the same incompatibles but * it's unlikely to do more than allow the null byte out.
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?