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 + -
显示快捷键?