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