sio.c
来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 2,448 行 · 第 1/5 页
C
2,448 行
/*- * 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.224.2.5 1999/04/13 18:54:43 jhay Exp $ */#include "opt_comconsole.h"#include "opt_compat.h"#include "opt_ddb.h"#include "opt_devfs.h"#include "opt_sio.h"#include "sio.h"#include "pnp.h"/* * Serial driver, based on 386BSD-0.1 com driver. * Mostly rewritten to use pseudo-DMA. * Works for National Semiconductor NS8250-NS16550AF UARTs. * COM driver, based on HP dca driver. * * Changes for PC-Card integration: * - Added PC-Card driver table and handlers */#include <sys/param.h>#include <sys/systm.h>#include <sys/reboot.h>#include <sys/malloc.h>#include <sys/tty.h>#include <sys/proc.h>#include <sys/conf.h>#include <sys/dkstat.h>#include <sys/fcntl.h>#include <sys/interrupt.h>#include <sys/kernel.h>#include <sys/syslog.h>#include <sys/sysctl.h>#ifdef DEVFS#include <sys/devfsext.h>#endif#include <sys/timepps.h>#include <machine/clock.h>#include <machine/ipl.h>#ifndef SMP#include <machine/lock.h>#endif#include <i386/isa/isa.h>#include <i386/isa/isa_device.h>#include <i386/isa/sioreg.h>#include <i386/isa/intr_machdep.h>#ifdef COM_ESP#include <i386/isa/ic/esp.h>#endif#include <i386/isa/ic/ns16550.h>#include "card.h"#if NCARD > 0#include <sys/module.h>#include <pccard/cardinfo.h>#include <pccard/slot.h>#endif#if NPNP > 0#include <i386/isa/pnp.h>#endif#ifdef SMP#define disable_intr() COM_DISABLE_INTR()#define enable_intr() COM_ENABLE_INTR()#endif /* SMP */#ifndef EXTRA_SIO#if NPNP > 0#define EXTRA_SIO MAX_PNP_CARDS#else#define EXTRA_SIO 0#endif#endif#define NSIOTOT (NSIO + EXTRA_SIO)#define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */#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_CONSOLE(dev) ((dev)->id_flags & 0x10)#define COM_FORCECONSOLE(dev) ((dev)->id_flags & 0x20)#define COM_LLCONSOLE(dev) ((dev)->id_flags & 0x40)#define COM_LOSESOUTINTS(dev) ((dev)->id_flags & 0x08)#define COM_NOFIFO(dev) ((dev)->id_flags & 0x02)#define COM_ST16650A(dev) ((dev)->id_flags & 0x20000)#define COM_C_NOPROBE (0x40000)#define COM_NOPROBE(dev) ((dev)->id_flags & COM_C_NOPROBE)#define COM_C_IIR_TXRDYBUG (0x80000)#define COM_IIR_TXRDYBUG(dev) ((dev)->id_flags & COM_C_IIR_TXRDYBUG)#define COM_FIFOSIZE(dev) (((dev)->id_flags & 0xff000000) >> 24)#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 */#define CSE_BUSYCHECK 1 /* siobusycheck() scheduled */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_int id_flags; /* Copy isa device falgas */ 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 */#ifdef COM_ESP bool_t esp; /* is this unit a hayes esp board? */#endif u_char extra_state; /* more flag bits, separate for order trick */ u_char fifo_image; /* copy of value written to FIFO */ bool_t hasfifo; /* nonzero for 16550 UARTs */ bool_t st16650a; /* Is a Startech 16650A or RTS/CTS compat */ 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 gone; /* hardware disappeared */ 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 */#ifdef COM_ESP Port_t esp_port;#endif Port_t int_id_port; Port_t iobase; Port_t modem_ctl_port; Port_t line_status_port; Port_t modem_status_port; Port_t intr_ctl_port; /* Ports of IIR register */ 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; bool_t do_dcd_timestamp; struct timeval timestamp; struct timeval dcd_timestamp; struct pps_state pps; 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];#ifdef DEVFS void *devfs_token_ttyd; void *devfs_token_ttyl; void *devfs_token_ttyi; void *devfs_token_cuaa; void *devfs_token_cual; void *devfs_token_cuai;#endif};#ifdef COM_ESPstatic int espattach __P((struct isa_device *isdp, struct com_s *com, Port_t esp_port));#endifstatic int sioattach __P((struct isa_device *dev));static timeout_t siobusycheck;static timeout_t siodtrwakeup;static void comhardclose __P((struct com_s *com));static ointhand2_t siointr;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 swihand_t siopoll;static int sioprobe __P((struct isa_device *dev));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));static char driver_name[] = "sio";/* table and macro for fast conversion from a unit number to its com struct */static struct com_s *p_com_addr[NSIOTOT];#define com_addr(unit) (p_com_addr[unit])struct isa_driver siodriver = { sioprobe, sioattach, driver_name};static d_open_t sioopen;static d_close_t sioclose;static d_read_t sioread;static d_write_t siowrite;static d_ioctl_t sioioctl;static d_stop_t siostop;static d_devtotty_t siodevtotty;#define CDEV_MAJOR 28static struct cdevsw sio_cdevsw = { sioopen, sioclose, sioread, siowrite, sioioctl, siostop, noreset, siodevtotty, ttpoll, nommap, NULL, driver_name, NULL, -1, nodump, nopsize, D_TTY,};static int comconsole = -1;static volatile speed_t comdefaultrate = CONSPEED;static u_int com_events; /* input chars + weighted output completions */static Port_t siocniobase;static bool_t sio_registered;static int sio_timeout;static int sio_timeouts_until_log;static struct callout_handle sio_timeout_handle = CALLOUT_HANDLE_INITIALIZER(&sio_timeout_handle);#if 0 /* XXX */static struct tty *sio_tty[NSIOTOT];#elsestatic struct tty sio_tty[NSIOTOT];#endifstatic const int nsio_tty = NSIOTOT;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 }};#ifdef COM_ESP/* XXX configure this properly. */static Port_t likely_com_ports[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, };static Port_t likely_esp_ports[] = { 0x140, 0x180, 0x280, 0 };#endif/* * handle sysctl read/write requests for console speed * * In addition to setting comdefaultrate for I/O through /dev/console, * also set the initial and lock values for the /dev/ttyXX device * if there is one associated with the console. Finally, if the /dev/tty * device has already been open, change the speed on the open running port * itself. */static intsysctl_machdep_comdefaultrate SYSCTL_HANDLER_ARGS{ int error, s; speed_t newspeed; struct com_s *com; struct tty *tp; newspeed = comdefaultrate; error = sysctl_handle_opaque(oidp, &newspeed, sizeof newspeed, req); if (error || !req->newptr) return (error); comdefaultrate = newspeed; if (comconsole < 0) /* serial console not selected? */ return (0); com = com_addr(comconsole); if (!com) return (ENXIO); /* * set the initial and lock rates for /dev/ttydXX and /dev/cuaXX * (note, the lock rates really are boolean -- if non-zero, disallow * speed changes) */ com->it_in.c_ispeed = com->it_in.c_ospeed = com->lt_in.c_ispeed = com->lt_in.c_ospeed = com->it_out.c_ispeed = com->it_out.c_ospeed = com->lt_out.c_ispeed = com->lt_out.c_ospeed = comdefaultrate; /* * if we're open, change the running rate too */ tp = com->tp; if (tp && (tp->t_state & TS_ISOPEN)) { tp->t_termios.c_ispeed = tp->t_termios.c_ospeed = comdefaultrate; s = spltty(); error = comparam(tp, &tp->t_termios); splx(s); } return error;}SYSCTL_PROC(_machdep, OID_AUTO, conspeed, CTLTYPE_INT | CTLFLAG_RW, 0, 0, sysctl_machdep_comdefaultrate, "I", "");#if NCARD > 0/* * PC-Card (PCMCIA) specific code. */static int sioinit __P((struct pccard_devinfo *));static void siounload __P((struct pccard_devinfo *));static int card_intr __P((struct pccard_devinfo *));PCCARD_MODULE(sio, sioinit, siounload, card_intr, 0, tty_imask);/* * Initialize the device - called from Slot manager. */intsioinit(struct pccard_devinfo *devi){ /* validate unit number. */ if (devi->isahd.id_unit >= (NSIOTOT)) return(ENODEV); /* Make sure it isn't already probed. */ if (com_addr(devi->isahd.id_unit)) return(EBUSY); /* It's already probed as serial by Upper */ devi->isahd.id_flags |= COM_C_NOPROBE; /*
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?