rc.c

来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 1,498 行 · 第 1/3 页

C
1,498
字号
/* * Copyright (C) 1995 by Pavel Antonov, Moscow, Russia. * Copyright (C) 1995 by Andrey A. Chernov, Moscow, Russia. * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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. *//* * SDL Communications Riscom/8 (based on Cirrus Logic CL-CD180) driver * */#include "rc.h"#if NRC > 0/*#define RCDEBUG*/#include <sys/param.h>#include <sys/systm.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/syslog.h>#include <sys/devconf.h>#include <sys/interrupt.h>#include <machine/clock.h>#include <i386/isa/isa.h>#include <i386/isa/isa_device.h>#include <i386/isa/sioreg.h>#include <i386/isa/ic/cd180.h>#include <i386/isa/rcreg.h>/* Prototypes */int     rcprobe         __P((struct isa_device *));int     rcattach        __P((struct isa_device *));int     rcopen          __P((dev_t, int, int, struct proc *));int     rcclose         __P((dev_t, int, int, struct proc *));int     rcread          __P((dev_t, struct uio *, int));int     rcwrite         __P((dev_t, struct uio *, int));void    rcintr          __P((int));void    rcpoll          __P((void));void    rcstop          __P((struct tty *, int));int     rcioctl         __P((dev_t, int, caddr_t, int, struct proc *));#define rcin(port)      RC_IN  (nec, port)#define rcout(port,v)   RC_OUT (nec, port, v)#define WAITFORCCR(u,c) rc_wait0(nec, (u), (c), __LINE__)#define CCRCMD(u,c,cmd) WAITFORCCR((u), (c)); rcout(CD180_CCR, (cmd))#define RC_IBUFSIZE     256#define RB_I_HIGH_WATER (TTYHOG - 2 * RC_IBUFSIZE)#define RC_OBUFSIZE     512#define RC_IHIGHWATER   (3 * RC_IBUFSIZE / 4)#define INPUT_FLAGS_SHIFT (2 * RC_IBUFSIZE)#define LOTS_OF_EVENTS  64#define RC_FAKEID       0x10#define RC_PROBED 1#define RC_ATTACHED 2#define GET_UNIT(dev)   (minor(dev) & 0x3F)#define CALLOUT(dev)    (minor(dev) & 0x80)/* For isa routines */struct isa_driver rcdriver = {	rcprobe, rcattach, "rc"};/* Per-board structure */static struct rc_softc {	u_int           rcb_probed;     /* 1 - probed, 2 - attached */	u_int           rcb_addr;       /* Base I/O addr        */	u_int           rcb_unit;       /* unit #               */	u_char          rcb_dtr;        /* DTR status           */	struct rc_chans *rcb_baserc;    /* base rc ptr          */} rc_softc[NRC];/* Per-channel structure */static struct rc_chans  {	struct rc_softc *rc_rcb;                /* back ptr             */	u_short          rc_flags;              /* Misc. flags          */	int              rc_chan;               /* Channel #            */	u_char           rc_ier;                /* intr. enable reg     */	u_char           rc_msvr;               /* modem sig. status    */	u_char           rc_cor2;               /* options reg          */	u_char           rc_pendcmd;            /* special cmd pending  */	u_int            rc_dtrwait;            /* dtr timeout          */	u_int            rc_dcdwaits;           /* how many waits DCD in open */	u_char		 rc_hotchar;		/* end packed optimize */	struct tty      *rc_tp;                 /* tty struct           */	u_char          *rc_iptr;               /* Chars input buffer         */	u_char          *rc_hiwat;              /* hi-water mark        */	u_char          *rc_bufend;             /* end of buffer        */	u_char          *rc_optr;               /* ptr in output buf    */	u_char          *rc_obufend;            /* end of output buf    */	u_char           rc_ibuf[4 * RC_IBUFSIZE];  /* input buffer         */	u_char           rc_obuf[RC_OBUFSIZE];  /* output buffer        */} rc_chans[NRC * CD180_NCHAN];static int rc_scheduled_event = 0;/* for pstat -t */struct tty rc_tty[NRC * CD180_NCHAN];int        nrc_tty = NRC * CD180_NCHAN;/* Flags */#define RC_DTR_OFF      0x0001          /* DTR wait, for close/open     */#define RC_ACTOUT       0x0002          /* Dial-out port active         */#define RC_RTSFLOW      0x0004          /* RTS flow ctl enabled         */#define RC_CTSFLOW      0x0008          /* CTS flow ctl enabled         */#define RC_DORXFER      0x0010          /* RXFER event planned          */#define RC_DOXXFER      0x0020          /* XXFER event planned          */#define RC_MODCHG       0x0040          /* Modem status changed         */#define RC_OSUSP        0x0080          /* Output suspended             */#define RC_OSBUSY       0x0100          /* start() routine in progress  */#define RC_WAS_BUFOVFL  0x0200          /* low-level buffer ovferflow   */#define RC_WAS_SILOVFL  0x0400          /* silo buffer overflow         */#define RC_SEND_RDY     0x0800          /* ready to send *//* Table for translation of RCSR status bits to internal form */static int rc_rcsrt[16] = {	0,             TTY_OE,               TTY_FE,	TTY_FE|TTY_OE, TTY_PE,               TTY_PE|TTY_OE,	TTY_PE|TTY_FE, TTY_PE|TTY_FE|TTY_OE, TTY_BI,	TTY_BI|TTY_OE, TTY_BI|TTY_FE,        TTY_BI|TTY_FE|TTY_OE,	TTY_BI|TTY_PE, TTY_BI|TTY_PE|TTY_OE, TTY_BI|TTY_PE|TTY_FE,	TTY_BI|TTY_PE|TTY_FE|TTY_OE};/* Static prototypes */static void rc_hwreset          __P((int, int, unsigned int));static int  rc_test             __P((int, int));static void rc_discard_output   __P((struct rc_chans *));static void rc_hardclose        __P((struct rc_chans *));static int  rc_modctl           __P((struct rc_chans *, int, int));static void rc_start            __P((struct tty *));static int  rc_param            __P((struct tty *, struct termios *));static void rc_registerdev      __P((struct isa_device *id));static void rc_reinit           __P((struct rc_softc *));#ifdef RCDEBUGstatic void printrcflags();#endifstatic timeout_t rc_dtrwakeup;static timeout_t rc_wakeup;static void disc_optim		__P((struct tty	*tp, struct termios *t,	struct rc_chans	*));static void rc_wait0            __P((int nec, int unit, int chan, int line));/**********************************************//* Quick device probing */int rcprobe(dvp)	struct  isa_device      *dvp;{	int             irq = ffs(dvp->id_irq) - 1;	register int    nec = dvp->id_iobase;	if (dvp->id_unit > NRC)		return 0;	if (!RC_VALIDADDR(nec)) {		printf("rc%d: illegal base address %x\n", nec);		return 0;	}	if (!RC_VALIDIRQ(irq)) {		printf("rc%d: illegal IRQ value %d\n", irq);		return 0;	}	rcout(CD180_PPRL, 0x22); /* Random values to Prescale reg. */	rcout(CD180_PPRH, 0x11);	if (rcin(CD180_PPRL) != 0x22 || rcin(CD180_PPRH) != 0x11)		return 0;	/* Now, test the board more thoroughly, with diagnostic */	if (rc_test(nec, dvp->id_unit))		return 0;	rc_softc[dvp->id_unit].rcb_probed = RC_PROBED;	return 0xF;}static struct kern_devconf kdc_rc[NRC] = { {	0, 0, 0,		/* filled in by dev_attach */	"rc", 0, { MDDT_ISA, 0, "tty" },	isa_generic_externalize, 0, 0, ISA_EXTERNALLEN,	&kdc_isa0,		/* parent */	0,			/* parentdata */	DC_UNCONFIGURED,        /* state */	"RISCom/8 multiport card",	DC_CLS_SERIAL		/* class */} };static voidrc_registerdev(id)	struct isa_device *id;{	int	unit;	unit = id->id_unit;	if (unit != 0)		kdc_rc[unit] = kdc_rc[0];	kdc_rc[unit].kdc_unit = unit;	kdc_rc[unit].kdc_isa = id;	kdc_rc[unit].kdc_state = DC_UNKNOWN;	dev_attach(&kdc_rc[unit]);}int rcattach(dvp)	struct  isa_device      *dvp;{	register int            i, chan, nec = dvp->id_iobase;	struct rc_softc         *rcb = &rc_softc[dvp->id_unit];	struct rc_chans         *rc  = &rc_chans[dvp->id_unit * CD180_NCHAN];	static int              rc_wakeup_started = 0;	struct tty              *tp;	/* Thorooughly test the device */	if (rcb->rcb_probed != RC_PROBED)		return 0;	rcb->rcb_addr   = nec;	rcb->rcb_dtr    = 0;	rcb->rcb_baserc = rc;	/*rcb->rcb_chipid = 0x10 + dvp->id_unit;*/	printf("rc%d: %d chans, firmware rev. %c\n", dvp->id_unit,		CD180_NCHAN, (rcin(CD180_GFRCR) & 0xF) + 'A');	rc_registerdev(dvp);	for (chan = 0; chan < CD180_NCHAN; chan++, rc++) {		rc->rc_rcb     = rcb;		rc->rc_chan    = chan;		rc->rc_iptr    = rc->rc_ibuf;		rc->rc_bufend  = &rc->rc_ibuf[RC_IBUFSIZE];		rc->rc_hiwat   = &rc->rc_ibuf[RC_IHIGHWATER];		rc->rc_flags   = rc->rc_ier = rc->rc_msvr = 0;		rc->rc_cor2    = rc->rc_pendcmd = 0;		rc->rc_optr    = rc->rc_obufend  = rc->rc_obuf;		rc->rc_dtrwait = 3 * hz;		rc->rc_dcdwaits= 0;		rc->rc_hotchar = 0;		tp = rc->rc_tp = &rc_tty[chan];		ttychars(tp);		tp->t_lflag = tp->t_iflag = tp->t_oflag = 0;		tp->t_cflag = TTYDEF_CFLAG;		tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;	}	rcb->rcb_probed = RC_ATTACHED;	if (!rc_wakeup_started) {		register_swi(SWI_TTY, rcpoll);		rc_wakeup((void *)NULL);		rc_wakeup_started = 0;	}	return 1;}/* RC interrupt handling */void    rcintr(unit)	int             unit;{	register struct rc_softc        *rcb = &rc_softc[unit];	register struct rc_chans        *rc;	register int                    nec, resid;	register u_char                 val, iack, bsr, ucnt, *optr;	int                             good_data, t_state;	if (rcb->rcb_probed != RC_ATTACHED) {		printf("rc%d: bogus interrupt\n", unit);		return;	}	nec = rcb->rcb_addr;	bsr = ~(rcin(RC_BSR));	if (!(bsr & (RC_BSR_TOUT|RC_BSR_RXINT|RC_BSR_TXINT|RC_BSR_MOINT))) {		printf("rc%d: extra interrupt\n", unit);		rcout(CD180_EOIR, 0);		return;	}	while (bsr & (RC_BSR_TOUT|RC_BSR_RXINT|RC_BSR_TXINT|RC_BSR_MOINT)) {#ifdef RCDEBUG_DETAILED		printf("rc%d: intr (%02x) %s%s%s%s\n", unit, bsr,			(bsr & RC_BSR_TOUT)?"TOUT ":"",			(bsr & RC_BSR_RXINT)?"RXINT ":"",			(bsr & RC_BSR_TXINT)?"TXINT ":"",			(bsr & RC_BSR_MOINT)?"MOINT":"");#endif		if (bsr & RC_BSR_TOUT) {			printf("rc%d: hardware failure, reset board\n", unit);			rcout(RC_CTOUT, 0);			rc_reinit(rcb);			return;		}		if (bsr & RC_BSR_RXINT) {			iack = rcin(RC_PILR_RX);			good_data = (iack == (GIVR_IT_RGDI | RC_FAKEID));			if (!good_data && iack != (GIVR_IT_REI | RC_FAKEID)) {				printf("rc%d: fake rxint: %02x\n", unit, iack);				goto more_intrs;			}			rc = rcb->rcb_baserc + ((rcin(CD180_GICR) & GICR_CHAN) >> GICR_LSH);			t_state = rc->rc_tp->t_state;			/* Do RTS flow control stuff */			if (  (rc->rc_flags & RC_RTSFLOW)			    || !(t_state & TS_ISOPEN)			   ) {				if (  (   !(t_state & TS_ISOPEN)				       || (t_state & TS_TBLOCK)				      )				    && (rc->rc_msvr & MSVR_RTS)				   )					rcout(CD180_MSVR,						rc->rc_msvr &= ~MSVR_RTS);				else if (!(rc->rc_msvr & MSVR_RTS))					rcout(CD180_MSVR,						rc->rc_msvr |= MSVR_RTS);			}			ucnt  = rcin(CD180_RDCR) & 0xF;			resid = 0;			if (t_state & TS_ISOPEN) {				/* check for input buffer overflow */				if ((rc->rc_iptr + ucnt) >= rc->rc_bufend) {					resid  = ucnt;					ucnt   = rc->rc_bufend - rc->rc_iptr;					resid -= ucnt;					if (!(rc->rc_flags & RC_WAS_BUFOVFL)) {						rc->rc_flags |= RC_WAS_BUFOVFL;						rc_scheduled_event++;					}				}				optr = rc->rc_iptr;				/* check foor good data */				if (good_data) {					while (ucnt-- > 0) {						val = rcin(CD180_RDR);						optr[0] = val;						optr[INPUT_FLAGS_SHIFT] = 0;						optr++;						rc_scheduled_event++;						if (val != 0 && val == rc->rc_hotchar)							setsofttty();					}				} else {					/* Store also status data */					while (ucnt-- > 0) {						iack = rcin(CD180_RCSR);						if (iack & RCSR_Timeout)							break;						if (   (iack & RCSR_OE)						    && !(rc->rc_flags & RC_WAS_SILOVFL)) {							rc->rc_flags |= RC_WAS_SILOVFL;							rc_scheduled_event++;						}						val = rcin(CD180_RDR);						/*						  Don't store PE if IGNPAR and BREAK if IGNBRK,						  this hack allows "raw" tty optimization						  works even if IGN* is set.						*/						if (   !(iack & (RCSR_PE|RCSR_FE|RCSR_Break))						    || (!(iack & (RCSR_PE|RCSR_FE))						    ||  !(rc->rc_tp->t_iflag & IGNPAR))						    && (!(iack & RCSR_Break)						    ||  !(rc->rc_tp->t_iflag & IGNBRK))) {							if (   (iack & (RCSR_PE|RCSR_FE))							    && (t_state & TS_CAN_BYPASS_L_RINT)							    && ((iack & RCSR_FE)							    ||  (iack & RCSR_PE)							    &&  (rc->rc_tp->t_iflag & INPCK)))								val = 0;							else if (val != 0 && val == rc->rc_hotchar)								setsofttty();							optr[0] = val;							optr[INPUT_FLAGS_SHIFT] = iack;							optr++;							rc_scheduled_event++;						}					}				}				rc->rc_iptr = optr;				rc->rc_flags |= RC_DORXFER;			} else				resid = ucnt;			/* Clear FIFO if necessary */			while (resid-- > 0) {				if (!good_data)					iack = rcin(CD180_RCSR);				else					iack = 0;				if (iack & RCSR_Timeout)					break;				(void) rcin(CD180_RDR);			}			goto more_intrs;		}		if (bsr & RC_BSR_MOINT) {			iack = rcin(RC_PILR_MODEM);			if (iack != (GIVR_IT_MSCI | RC_FAKEID)) {				printf("rc%d: fake moint: %02x\n", unit, iack);				goto more_intrs;			}			rc = rcb->rcb_baserc + ((rcin(CD180_GICR) & GICR_CHAN) >> GICR_LSH);			iack = rcin(CD180_MCR);			rc->rc_msvr = rcin(CD180_MSVR);			rcout(CD180_MCR, 0);#ifdef RCDEBUG			printrcflags(rc, "moint");#endif			if (rc->rc_flags & RC_CTSFLOW) {				if (rc->rc_msvr & MSVR_CTS)					rc->rc_flags |= RC_SEND_RDY;				else					rc->rc_flags &= ~RC_SEND_RDY;			} else				rc->rc_flags |= RC_SEND_RDY;			if ((iack & MCR_CDchg) && !(rc->rc_flags & RC_MODCHG)) {				rc_scheduled_event += LOTS_OF_EVENTS;				rc->rc_flags |= RC_MODCHG;				setsofttty();			}			goto more_intrs;		}		if (bsr & RC_BSR_TXINT) {			iack = rcin(RC_PILR_TX);			if (iack != (GIVR_IT_TDI | RC_FAKEID)) {				printf("rc%d: fake txint: %02x\n", unit, iack);				goto more_intrs;			}			rc = rcb->rcb_baserc + ((rcin(CD180_GICR) & GICR_CHAN) >> GICR_LSH);			if (    (rc->rc_flags & RC_OSUSP)			    || !(rc->rc_flags & RC_SEND_RDY)			   )				goto more_intrs;			/* Handle breaks and other stuff */			if (rc->rc_pendcmd) {				rcout(CD180_COR2, rc->rc_cor2 |= COR2_ETC);				rcout(CD180_TDR,  CD180_C_ESC);				rcout(CD180_TDR,  rc->rc_pendcmd);				rcout(CD180_COR2, rc->rc_cor2 &= ~COR2_ETC);				rc->rc_pendcmd = 0;				goto more_intrs;			}			optr = rc->rc_optr;			resid = rc->rc_obufend - optr;			if (resid > CD180_NFIFO)				resid = CD180_NFIFO;			while (resid-- > 0)				rcout(CD180_TDR, *optr++);			rc->rc_optr = optr;			/* output completed? */			if (optr >= rc->rc_obufend) {				rcout(CD180_IER, rc->rc_ier &= ~IER_TxRdy);#ifdef RCDEBUG				printf("rc%d/%d: output completed\n", unit, rc->rc_chan);#endif				if (!(rc->rc_flags & RC_DOXXFER)) {					rc_scheduled_event += LOTS_OF_EVENTS;					rc->rc_flags |= RC_DOXXFER;					setsofttty();				}			}		}	more_intrs:		rcout(CD180_EOIR, 0);   /* end of interrupt */		rcout(RC_CTOUT, 0);		bsr = ~(rcin(RC_BSR));	}}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?