cy.c

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

C
2,542
字号
}static intsioattach(isdp)	struct isa_device	*isdp;{	int	cyu;	cy_addr	cy_iobase;	cy_addr	iobase;	int	ncyu;	int	unit;	unit = isdp->id_unit;	if ((u_int)unit >= NCY)		return (0);	ncyu = cy_nr_cd1400s[unit];	if (ncyu == 0)		return (0);	isdp->id_ri_flags |= RI_FAST;	cy_iobase = (cy_addr)isdp->id_maddr;	unit *= CY_MAX_PORTS;	for (cyu = 0, iobase = cy_iobase; cyu < ncyu;	     ++cyu, iobase += CY_CD1400_MEMSIZE) {		int	cdu;		/* Set up a receive timeout period of than 1+ ms. */		cd_outb(iobase, CD1400_PPR,			howmany(CY_CLOCK / CD1400_PPR_PRESCALER, 1000));		for (cdu = 0; cdu < CD1400_NO_OF_CHANNELS; ++cdu, ++unit) {			struct com_s	*com;			int		s;	com = malloc(sizeof *com, M_DEVBUF, M_NOWAIT);	if (com == NULL)		break;	bzero(com, sizeof *com);	com->unit = unit;	com->dtr_wait = 3 * hz;	com->iptr = com->ibuf = com->ibuf1;	com->ibufend = com->ibuf1 + RS_IBUFSIZE;	com->ihighwater = com->ibuf1 + RS_IHIGHWATER;	com->obufs[0].l_head = com->obuf1;	com->obufs[1].l_head = com->obuf2;			com->cy_iobase = cy_iobase;	com->iobase = iobase;	/*	 * We don't use all the flags from <sys/ttydefaults.h> since they	 * are only relevant for logins.  It's important to have echo off	 * initially so that the line doesn't start blathering before the	 * echo flag can be turned off.	 */	com->it_in.c_iflag = 0;	com->it_in.c_oflag = 0;	com->it_in.c_cflag = TTYDEF_CFLAG;	com->it_in.c_lflag = 0;	if (unit == comconsole && (COMCONSOLE || boothowto & RB_SERIAL)) {		com->it_in.c_iflag = TTYDEF_IFLAG;		com->it_in.c_oflag = TTYDEF_OFLAG;		com->it_in.c_cflag = TTYDEF_CFLAG | CLOCAL;		com->it_in.c_lflag = TTYDEF_LFLAG;		com->lt_out.c_cflag = com->lt_in.c_cflag = CLOCAL;	}	termioschars(&com->it_in);	com->it_in.c_ispeed = com->it_in.c_ospeed = comdefaultrate;	com->it_out = com->it_in;			com->kdc = kdc_sio[0];			com->kdc.kdc_name = "cy";			com->kdc.kdc_unit = unit;			com->kdc.kdc_isa = isdp;			com->kdc.kdc_parent = &kdc_sio[isdp->id_unit];			com->kdc.kdc_state = DC_IDLE;			com->kdc.kdc_description =			  "Serial port: Cirrus Logic CD1400";			com->kdc.kdc_class = DC_CLS_SERIAL;			dev_attach(&com->kdc);	s = spltty();	com_addr(unit) = com;	register_swi(SWI_TTY, siopoll);	splx(s);		}	}	kdc_sio[isdp->id_unit].kdc_state = DC_BUSY;	/* XXX */	/* ensure an edge for the next interrupt */	cy_outb(cy_iobase, CY_CLEAR_INTR, 0);	return (1);}intsioopen(dev, flag, mode, p)	dev_t		dev;	int		flag;	int		mode;	struct proc	*p;{	struct com_s	*com;	int		error;	cy_addr		iobase;	int		mynor;	int		s;	struct tty	*tp;	int		unit;	mynor = minor(dev);	unit = MINOR_TO_UNIT(mynor);	if ((u_int) unit >= NSIO || (com = com_addr(unit)) == NULL)		return (ENXIO);	if (mynor & CONTROL_MASK)		return (0);#if 0 /* XXX */	tp = com->tp = sio_tty[unit] = ttymalloc(sio_tty[unit]);#else	tp = com->tp = &sio_tty[unit];#endif	s = spltty();	/*	 * We jump to this label after all non-interrupted sleeps to pick	 * up any changes of the device state.	 */open_top:	while (com->state & CS_DTR_OFF) {		error = tsleep(&com->dtr_wait, TTIPRI | PCATCH, "cydtr", 0);		if (error != 0)			goto out;	}	com->kdc.kdc_state = DC_BUSY;	if (tp->t_state & TS_ISOPEN) {		/*		 * The device is open, so everything has been initialized.		 * Handle conflicts.		 */		if (mynor & CALLOUT_MASK) {			if (!com->active_out) {				error = EBUSY;				goto out;			}		} else {			if (com->active_out) {				if (flag & O_NONBLOCK) {					error = EBUSY;					goto out;				}				error =	tsleep(&com->active_out,					       TTIPRI | PCATCH, "cybi", 0);				if (error != 0)					goto out;				goto open_top;			}		}		if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) {			error = EBUSY;			goto out;		}	} else {		/*		 * The device isn't open, so there are no conflicts.		 * Initialize it.  Initialization is done twice in many		 * cases: to preempt sleeping callin opens if we are		 * callout, and to complete a callin open after DCD rises.		 */		tp->t_oproc = comstart;		tp->t_param = comparam;		tp->t_dev = dev;		tp->t_termios = mynor & CALLOUT_MASK				? com->it_out : com->it_in;#if 0		(void)commctl(com, TIOCM_DTR | TIOCM_RTS, DMSET);		com->ftl_max = com->ftl_init;		com->poll = com->no_irq;		com->poll_output = com->loses_outints;#endif		++com->wopeners;		iobase = com->iobase;		/* reset this channel */		cd_outb(iobase, CD1400_CAR, unit & CD1400_CAR_CHAN);		cd1400_channel_cmd(iobase, CD1400_CCR_CMDRESET);		/*		 * Resetting disables the transmitter and receiver as well as		 * flushing the fifos so some of our cached state becomes		 * invalid.  The documentation suggests that all registers		 * for the current channel are reset to defaults, but		 * apparently none are.  We wouldn't want DTR cleared.		 */		com->channel_control = 0;		/* Encode per-board unit in LIVR for access in intr routines. */		cd_outb(iobase, CD1400_LIVR,			(unit & CD1400_xIVR_CHAN) << CD1400_xIVR_CHAN_SHIFT);		/*		 * raise dtr and generally set things up correctly.  this		 * has the side-effect of selecting the appropriate cd1400		 * channel, to help us with subsequent channel control stuff		 */		error = comparam(tp, &tp->t_termios);		--com->wopeners;		if (error != 0)			goto out;		/*		 * XXX we should goto open_top if comparam() slept.		 */		ttsetwater(tp);#if 0		if (com->hasfifo) {			/*			 * (Re)enable and drain fifos.			 *			 * Certain SMC chips cause problems if the fifos			 * are enabled while input is ready.  Turn off the			 * fifo if necessary to clear the input.  We test			 * the input ready bit after enabling the fifos			 * since we've already enabled them in comparam()			 * and to handle races between enabling and fresh			 * input.			 */			while (TRUE) {				outb(iobase + com_fifo,				     FIFO_RCV_RST | FIFO_XMT_RST				     | FIFO_ENABLE | com->ftl);				DELAY(100);				if (!(inb(com->line_status_port) & LSR_RXRDY))					break;				outb(iobase + com_fifo, 0);				DELAY(100);				(void) inb(com->data_port);			}		}		disable_intr();		(void) inb(com->line_status_port);		(void) inb(com->data_port);		com->prev_modem_status = com->last_modem_status		    = inb(com->modem_status_port);		outb(iobase + com_ier, IER_ERXRDY | IER_ETXRDY | IER_ERLS				       | IER_EMSC);		enable_intr();#else /* !0 */		/* XXX raise RTS too */		(void)commctl(com, TIOCM_DTR | TIOCM_RTS, DMSET);		disable_intr();		com->prev_modem_status = com->last_modem_status		    = cd_inb(iobase, CD1400_MSVR2);		cd_outb(iobase, CD1400_SRER,			com->intr_enable			    = CD1400_SRER_MDMCH | CD1400_SRER_RXDATA);		enable_intr();#endif /* 0 */		/*		 * Handle initial DCD.  Callout devices get a fake initial		 * DCD (trapdoor DCD).  If we are callout, then any sleeping		 * callin opens get woken up and resume sleeping on "cybi"		 * instead of "cydcd".		 */		/*		 * XXX `mynor & CALLOUT_MASK' should be		 * `tp->t_cflag & (SOFT_CARRIER | TRAPDOOR_CARRIER) where		 * TRAPDOOR_CARRIER is the default initial state for callout		 * devices and SOFT_CARRIER is like CLOCAL except it hides		 * the true carrier.		 */		if (com->prev_modem_status & MSR_DCD || mynor & CALLOUT_MASK)			(*linesw[tp->t_line].l_modem)(tp, 1);	}	/*	 * Wait for DCD if necessary.	 */	if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK)	    && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) {		++com->wopeners;		error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "cydcd", 0);		--com->wopeners;		if (error != 0)			goto out;		goto open_top;	}	error =	(*linesw[tp->t_line].l_open)(dev, tp);	disc_optim(tp, &tp->t_termios, com);	if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK)		com->active_out = TRUE;	siosettimeout();out:	splx(s);	if (!(tp->t_state & TS_ISOPEN) && com->wopeners == 0)		comhardclose(com);	return (error);}intsioclose(dev, flag, mode, p)	dev_t		dev;	int		flag;	int		mode;	struct proc	*p;{	struct com_s	*com;	int		mynor;	int		s;	struct tty	*tp;	mynor = minor(dev);	if (mynor & CONTROL_MASK)		return (0);	com = com_addr(MINOR_TO_UNIT(mynor));	tp = com->tp;	s = spltty();	(*linesw[tp->t_line].l_close)(tp, flag);	disc_optim(tp, &tp->t_termios, com);	siostop(tp, FREAD | FWRITE);	comhardclose(com);	ttyclose(tp);	siosettimeout();	splx(s);#ifdef broken /* session holds a ref to the tty; can't deallocate */	ttyfree(tp);	com->tp = sio_tty[unit] = NULL;#endif	return (0);}static voidcomhardclose(com)	struct com_s	*com;{	cy_addr		iobase;	int		s;	struct tty	*tp;	int		unit;	unit = com->unit;	iobase = com->iobase;	s = spltty();#if 0	com->poll = FALSE;	com->poll_output = FALSE;#endif	com->do_timestamp = 0;	cd_outb(iobase, CD1400_CAR, unit & CD1400_CAR_CHAN);#if 0	outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK);#endif#ifdef KGDB	/* do not disable interrupts or hang up if debugging */	if (kgdb_dev != makedev(commajor, unit))#endif	{#if 0		outb(iobase + com_ier, 0);#else		disable_intr();		cd_outb(iobase, CD1400_SRER, com->intr_enable = 0);		enable_intr();#endif		tp = com->tp;		if (tp->t_cflag & HUPCL		    /*		     * XXX we will miss any carrier drop between here and the		     * next open.  Perhaps we should watch DCD even when the		     * port is closed; it is not sufficient to check it at		     * the next open because it might go up and down while		     * we're not watching.		     */		    || !com->active_out		       && !(com->prev_modem_status & MSR_DCD)		       && !(com->it_in.c_cflag & CLOCAL)		    || !(tp->t_state & TS_ISOPEN)) {			(void)commctl(com, TIOCM_DTR, DMBIC);			/* Disable receiver (leave transmitter enabled). */			com->channel_control = CD1400_CCR_CMDCHANCTL					       | CD1400_CCR_XMTEN					       | CD1400_CCR_RCVDIS;			cd1400_channel_cmd(iobase, com->channel_control);			if (com->dtr_wait != 0) {				timeout(siodtrwakeup, com, com->dtr_wait);				com->state |= CS_DTR_OFF;			}		}	}	com->active_out = FALSE;	wakeup(&com->active_out);	wakeup(TSA_CARR_ON(tp));	/* restart any wopeners */	if (!(com->state & CS_DTR_OFF)	    && !(unit == comconsole && (COMCONSOLE || boothowto & RB_SERIAL)))		com->kdc.kdc_state = DC_IDLE;	splx(s);}intsioread(dev, uio, flag)	dev_t		dev;	struct uio	*uio;	int		flag;{	int		mynor;	struct tty	*tp;	mynor = minor(dev);	if (mynor & CONTROL_MASK)		return (ENODEV);	tp = com_addr(MINOR_TO_UNIT(mynor))->tp;	return ((*linesw[tp->t_line].l_read)(tp, uio, flag));}intsiowrite(dev, uio, flag)	dev_t		dev;	struct uio	*uio;	int		flag;{	int		mynor;	struct tty	*tp;	int		unit;	mynor = minor(dev);	if (mynor & CONTROL_MASK)		return (ENODEV);	unit = MINOR_TO_UNIT(mynor);	tp = com_addr(unit)->tp;	/*	 * (XXX) We disallow virtual consoles if the physical console is	 * a serial port.  This is in case there is a display attached that	 * is not the console.  In that situation we don't need/want the X	 * server taking over the console.	 */	if (constty && unit == comconsole	    && (COMCONSOLE || boothowto & RB_SERIAL))		constty = NULL;#ifdef Smarts	/* XXX duplicate ttwrite(), but without so much output processing on	 * CR & LF chars.  Hardly worth the effort, given that high-throughput	 * sessions are raw anyhow.	 */#else	return ((*linesw[tp->t_line].l_write)(tp, uio, flag));#endif}static voidsiodtrwakeup(chan)	void	*chan;{	struct com_s	*com;	com = (struct com_s *)chan;	com->state &= ~CS_DTR_OFF;	if (!(com->unit == comconsole && (COMCONSOLE || boothowto & RB_SERIAL)))		com->kdc.kdc_state = DC_IDLE;	wakeup(&com->dtr_wait);}/* Interrupt routine for timekeeping purposes */voidsiointrts(unit)	int	unit;{#ifdef OSKIT	microtime(&intr_timestamp);#else	/*	 * XXX microtime() reenables CPU interrupts.  We can't afford to	 * be interrupted and don't want to slow down microtime(), so lock	 * out interrupts in another way.	 */	outb(IO_ICU1 + 1, 0xff);	microtime(&intr_timestamp);	disable_intr();	outb(IO_ICU1 + 1, imen);#endif	siointr(unit);}voidsiointr(unit)	int	unit;{	int	baseu;	cy_addr	cy_iobase;	int	cyu;	cy_addr	iobase;	u_char	status;	baseu = unit * CY_MAX_PORTS;	cy_iobase = com_addr(baseu)->cy_iobase;	/* check each CD1400 in turn */	for (cyu = 0, iobase = cy_iobase; cyu < cy_nr_cd1400s[unit];	     ++cyu, iobase += CY_CD1400_MEMSIZE) {		/* poll to see if it has any work */		status = cd_inb(iobase, CD1400_SVRR);		if (status == 0)			continue;#ifdef CyDebug		++cy_svrr_probes;#endif		/* service requests as appropriate, giving priority to RX */		if (status & CD1400_SVRR_RXRDY) {

⌨️ 快捷键说明

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