cy.c

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

C
2,542
字号
		/*		 * XXX don't raise MCR_RTS if CTS_RTS_IFLOW is off.  Set it		 * appropriately in comparam() if RTS-flow is being changed.		 * Check for races.		 */		if (!(com->mcr_image & MCR_RTS) && com->iptr < com->ihighwater)#if 0			outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS);#else			cd_outb(iobase, CD1400_MSVR1,				com->mcr_image |= MCR_RTS);#endif	}	enable_intr();	if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) {		splx(s);		return;	}	if (tp->t_outq.c_cc != 0) {		struct lbq	*qp;		struct lbq	*next;		if (!com->obufs[0].l_queued) {#ifdef CyDebug			started = TRUE;#endif			com->obufs[0].l_tail			    = com->obuf1 + q_to_b(&tp->t_outq, com->obuf1,						  sizeof com->obuf1);			com->obufs[0].l_next = NULL;			com->obufs[0].l_queued = TRUE;			disable_intr();			if (com->state & CS_BUSY) {				qp = com->obufq.l_next;				while ((next = qp->l_next) != NULL)					qp = next;				qp->l_next = &com->obufs[0];			} else {				com->obufq.l_head = com->obufs[0].l_head;				com->obufq.l_tail = com->obufs[0].l_tail;				com->obufq.l_next = &com->obufs[0];				com->state |= CS_BUSY;				if (com->state >= (CS_BUSY | CS_TTGO						   | CS_ODEVREADY))					cd_outb(iobase, CD1400_SRER,						com->intr_enable						|= CD1400_SRER_TXRDY);			}			enable_intr();		}		if (tp->t_outq.c_cc != 0 && !com->obufs[1].l_queued) {#ifdef CyDebug			started = TRUE;#endif			com->obufs[1].l_tail			    = com->obuf2 + q_to_b(&tp->t_outq, com->obuf2,						  sizeof com->obuf2);			com->obufs[1].l_next = NULL;			com->obufs[1].l_queued = TRUE;			disable_intr();			if (com->state & CS_BUSY) {				qp = com->obufq.l_next;				while ((next = qp->l_next) != NULL)					qp = next;				qp->l_next = &com->obufs[1];			} else {				com->obufq.l_head = com->obufs[1].l_head;				com->obufq.l_tail = com->obufs[1].l_tail;				com->obufq.l_next = &com->obufs[1];				com->state |= CS_BUSY;				if (com->state >= (CS_BUSY | CS_TTGO						   | CS_ODEVREADY))					cd_outb(iobase, CD1400_SRER,						com->intr_enable						|= CD1400_SRER_TXRDY);			}			enable_intr();		}		tp->t_state |= TS_BUSY;	}#ifdef CyDebug	if (started)		++com->start_real;#endif#if 0	disable_intr();	if (com->state >= (CS_BUSY | CS_TTGO)) {		siointr1(com);	/* fake interrupt to start output */	enable_intr();#endif	ttwwakeup(tp);	splx(s);}voidsiostop(tp, rw)	struct tty	*tp;	int		rw;{	struct com_s	*com;	com = com_addr(DEV_TO_UNIT(tp->t_dev));	disable_intr();	if (rw & FWRITE) {		com->obufs[0].l_queued = FALSE;		com->obufs[1].l_queued = FALSE;		if (com->state & CS_ODONE)			com_events -= LOTS_OF_EVENTS;		com->state &= ~(CS_ODONE | CS_BUSY);		com->tp->t_state &= ~TS_BUSY;	}	if (rw & FREAD) {		com_events -= (com->iptr - com->ibuf);		com->iptr = com->ibuf;	}	enable_intr();	comstart(tp);	/* XXX should clear h/w fifos too. */}struct tty *siodevtotty(dev)	dev_t	dev;{	int	mynor;	int	unit;	mynor = minor(dev);	if (mynor & CONTROL_MASK)		return (NULL);	unit = MINOR_TO_UNIT(mynor);	if ((u_int) unit >= NSIO)		return (NULL);	return (&sio_tty[unit]);}static intcommctl(com, bits, how)	struct com_s	*com;	int		bits;	int		how;{	cy_addr	iobase;	int	mcr;	int	msr;	if (how == DMGET) {		if (com->channel_control & CD1400_CCR_RCVEN)			bits |= TIOCM_LE;		mcr = com->mcr_image;		if (mcr & MCR_DTR)			bits |= TIOCM_DTR;		if (mcr & MCR_RTS)			/* XXX wired on for Cyclom-8Ys */			bits |= TIOCM_RTS;		msr = com->prev_modem_status;		if (msr & MSR_CTS)			bits |= TIOCM_CTS;		if (msr & MSR_DCD)			bits |= TIOCM_CD;		if (msr & MSR_DSR)			bits |= TIOCM_DSR;		if (msr & MSR_RI)			/* XXX not connected except for Cyclom-16Y? */			bits |= TIOCM_RI;		return (bits);	}	iobase = com->iobase;	mcr = 0;	if (bits & TIOCM_DTR)		mcr |= MCR_DTR;	if (bits & TIOCM_RTS)		mcr |= MCR_RTS;	disable_intr();	switch (how) {	case DMSET:		com->mcr_image = mcr;		cd_outb(iobase, CD1400_MSVR1, mcr);		cd_outb(iobase, CD1400_MSVR2, mcr);		break;	case DMBIS:		com->mcr_image = mcr = com->mcr_image | mcr;		cd_outb(iobase, CD1400_MSVR1, mcr);		cd_outb(iobase, CD1400_MSVR2, mcr);		break;	case DMBIC:		com->mcr_image = mcr = com->mcr_image & ~mcr;		cd_outb(iobase, CD1400_MSVR1, mcr);		cd_outb(iobase, CD1400_MSVR2, mcr);		break;	}	enable_intr();	return (0);}static voidsiosettimeout(){	struct com_s	*com;	bool_t		someopen;	int		unit;	/*	 * Set our timeout period to 1 second if no polled devices are open.	 * Otherwise set it to max(1/200, 1/hz).	 * Enable timeouts iff some device is open.	 */	untimeout(comwakeup, (void *)NULL);	sio_timeout = hz;	someopen = FALSE;	for (unit = 0; unit < NSIO; ++unit) {		com = com_addr(unit);		if (com != NULL && com->tp != NULL		    && com->tp->t_state & TS_ISOPEN) {			someopen = TRUE;#if 0			if (com->poll || com->poll_output) {				sio_timeout = hz > 200 ? hz / 200 : 1;				break;			}#endif		}	}	if (someopen) {		sio_timeouts_until_log = hz / sio_timeout;		timeout(comwakeup, (void *)NULL, sio_timeout);	} else {		/* Flush error messages, if any. */		sio_timeouts_until_log = 1;		comwakeup((void *)NULL);		untimeout(comwakeup, (void *)NULL);	}}static voidcomwakeup(chan)	void	*chan;{	struct com_s	*com;	int		unit;	timeout(comwakeup, (void *)NULL, sio_timeout);#if 0	/*	 * Recover from lost output interrupts.	 * Poll any lines that don't use interrupts.	 */	for (unit = 0; unit < NSIO; ++unit) {		com = com_addr(unit);		if (com != NULL		    && (com->state >= (CS_BUSY | CS_TTGO) || com->poll)) {			disable_intr();			siointr1(com);			enable_intr();		}	}#endif	/*	 * Check for and log errors, but not too often.	 */	if (--sio_timeouts_until_log > 0)		return;	sio_timeouts_until_log = hz / sio_timeout;	for (unit = 0; unit < NSIO; ++unit) {		int	errnum;		com = com_addr(unit);		if (com == NULL)			continue;		for (errnum = 0; errnum < CE_NTYPES; ++errnum) {			u_int	delta;			u_long	total;			disable_intr();			delta = com->delta_error_counts[errnum];			com->delta_error_counts[errnum] = 0;			enable_intr();			if (delta == 0)				continue;			total = com->error_counts[errnum] += delta;			log(LOG_ERR, "cy%d: %u more %s%s (total %lu)\n",			    unit, delta, error_desc[errnum],			    delta == 1 ? "" : "s", total);#if 0			/*			 * XXX if we resurrect this then we should move			 * the dropping of the ftl to somewhere with less			 * latency.			 */			if (errnum == CE_OVERRUN && com->hasfifo			    && com->ftl > FIFO_TRIGGER_1) {				static	u_char	ftl_in_bytes[] =					{ 1, 4, 8, 14, };				com->ftl_init = FIFO_TRIGGER_8;#define	FIFO_TRIGGER_DELTA	FIFO_TRIGGER_4				com->ftl_max =				com->ftl -= FIFO_TRIGGER_DELTA;				outb(com->iobase + com_fifo,				     FIFO_ENABLE | com->ftl);				log(LOG_DEBUG,				    "sio%d: reduced fifo trigger level to %d\n",				    unit,				    ftl_in_bytes[com->ftl						 / FIFO_TRIGGER_DELTA]);			}#endif		}	}}static voiddisc_optim(tp, t, com)	struct tty	*tp;	struct termios	*t;	struct com_s	*com;{#ifndef SOFT_HOTCHAR	cy_addr	iobase;	u_char	opt;#endif	/*	 * XXX can skip a lot more cases if Smarts.  Maybe	 * (IGNCR | ISTRIP | IXON) in c_iflag.  But perhaps we	 * shouldn't skip if (TS_CNTTB | TS_LNCH) is set in t_state.	 */	if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON))	    && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK))	    && (!(t->c_iflag & PARMRK)		|| (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK))	    && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN))	    && linesw[tp->t_line].l_rint == ttyinput)		tp->t_state |= TS_CAN_BYPASS_L_RINT;	else		tp->t_state &= ~TS_CAN_BYPASS_L_RINT;	/*	 * Prepare to reduce input latency for packet	 * discplines with a end of packet character.	 */	if (tp->t_line == SLIPDISC)		com->hotchar = 0xc0;	else if (tp->t_line == PPPDISC)		com->hotchar = 0x7e;	else		com->hotchar = 0;#ifndef SOFT_HOTCHAR	iobase = com->iobase;	cd_outb(iobase, CD1400_CAR, com->unit & CD1400_CAR_CHAN);	opt = com->cor[2] & ~CD1400_COR3_SCD34;	if (com->hotchar != 0) {		cd_outb(iobase, CD1400_SCHR3, com->hotchar);		cd_outb(iobase, CD1400_SCHR4, com->hotchar);		opt |= CD1400_COR3_SCD34;	}	if (opt != com->cor[2]) {		cd_outb(iobase, CD1400_COR3, com->cor[2] = opt);		cd1400_channel_cmd(com->iobase,				   CD1400_CCR_CMDCORCHG | CD1400_CCR_COR3);	}#endif}#ifdef Smarts/* standard line discipline input routine */intcyinput(c, tp)	int		c;	struct tty	*tp;{	/* XXX duplicate ttyinput(), but without the IXOFF/IXON/ISTRIP/IPARMRK	 * bits, as they are done by the CD1400.  Hardly worth the effort,	 * given that high-throughput sessions are raw anyhow.	 */}#endif /* Smarts */static intcomspeed(speed, prescaler_io)	speed_t	speed;	int	*prescaler_io;{	int	actual;	int	error;	int	divider;	int	prescaler;	int	prescaler_unit;	if (speed == 0)		return (0);	if (speed < 0 || speed > 150000)		return (-1);	/* determine which prescaler to use */	for (prescaler_unit = 4, prescaler = 2048; prescaler_unit;		prescaler_unit--, prescaler >>= 2) {		if (CY_CLOCK / prescaler / speed > 63)			break;	}	divider = (CY_CLOCK / prescaler * 2 / speed + 1) / 2; /* round off */	if (divider > 255)		divider = 255;	actual = CY_CLOCK/prescaler/divider;	error = ((actual - speed) * 2000 / speed + 1) / 2;	/* percentage */	/* 3.0% max error tolerance */	if (error < -30 || error > 30)		return (-1);#if 0	printf("prescaler = %d (%d)\n", prescaler, prescaler_unit);	printf("divider = %d (%x)\n", divider, divider);	printf("actual = %d\n", actual);	printf("error = %d\n", error);#endif	*prescaler_io = prescaler_unit;	return (divider);}static voidcd1400_channel_cmd(iobase, cmd)	cy_addr	iobase;	int	cmd;{	/* XXX hsu@clinet.fi: This is always more dependent on ISA bus speed,	   as the card is probed every round?  Replaced delaycount with 8k.	   Either delaycount has to be implemented in FreeBSD or more sensible	   way of doing these should be implemented.  DELAY isn't enough here.	   */	u_int	maxwait = 5 * 8 * 1024;	/* approx. 5 ms */	/* wait for processing of previous command to complete */	while (cd_inb(iobase, CD1400_CCR) && maxwait--)		;	if (!maxwait)		log(LOG_ERR, "cy: channel command timeout (%d loops) - arrgh\n",		    5 * 8 * 1024);	cd_outb(iobase, CD1400_CCR, cmd);}#ifdef CyDebug/* useful in ddb */voidcystatus(unit)	int	unit;{	struct com_s	*com;	cy_addr		iobase;	u_int		ocount;	struct tty	*tp;	com = com_addr(unit);	printf("info for channel %d\n", unit);	printf("------------------\n");	printf("total cyclom service probes:\t%d\n", cy_svrr_probes);	printf("calls to upper layer:\t\t%d\n", cy_timeouts);	if (com == NULL)		return;	iobase = com->iobase;	printf("\n");	printf("cd1400 base address:\\tt%p\n", iobase);	cd_outb(iobase, CD1400_CAR, unit & CD1400_CAR_CHAN);	printf("saved channel_control:\t\t0x%02x\n", com->channel_control);	printf("saved cor1-3:\t\t\t0x%02x 0x%02x 0x%02x\n",	       com->cor[0], com->cor[1], com->cor[2]);	printf("service request enable reg:\t0x%02x (0x%02x cached)\n",	       cd_inb(iobase, CD1400_SRER), com->intr_enable);	printf("service request register:\t0x%02x\n",	       cd_inb(iobase, CD1400_SVRR));	printf("modem status:\t\t\t0x%02x (0x%02x cached)\n",	       cd_inb(iobase, CD1400_MSVR2), com->prev_modem_status);	printf("rx/tx/mdm interrupt registers:\t0x%02x 0x%02x 0x%02x\n",	       cd_inb(iobase, CD1400_RIR), cd_inb(iobase, CD1400_TIR),	       cd_inb(iobase, CD1400_MIR));	printf("\n");	printf("com state:\t\t\t0x%02x\n", com->state);	printf("calls to comstart():\t\t%d (%d useful)\n",	       com->start_count, com->start_real);	printf("rx buffer chars free:\t\t%d\n", com->iptr - com->ibuf);	ocount = 0;	if (com->obufs[0].l_queued)		ocount += com->obufs[0].l_tail - com->obufs[0].l_head;	if (com->obufs[1].l_queued)		ocount += com->obufs[1].l_tail - com->obufs[1].l_head;	printf("tx buffer chars:\t\t%u\n", ocount);	printf("received chars:\t\t\t%d\n", com->bytes_in);	printf("received exceptions:\t\t%d\n", com->recv_exception);	printf("modem signal deltas:\t\t%d\n", com->mdm);	printf("transmitted chars:\t\t%d\n", com->bytes_out);	printf("\n");	tp = com->tp;	if (tp != NULL) {		printf("tty state:\t\t\t0x%08x\n", tp->t_state);		printf("upper layer queue lengths:\t%d raw, %d canon, %d output\n",		       tp->t_rawq.c_cc, tp->t_canq.c_cc, tp->t_outq.c_cc);	} else		printf("tty state:\t\t\tclosed\n");}

⌨️ 快捷键说明

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