cy_isa.c revision 9406
19406Sbde/*-
26261Sjkh * cyclades cyclom-y serial driver
36261Sjkh *	Andrew Herbert <andrew@werple.apana.org.au>, 17 August 1993
46261Sjkh *
56261Sjkh * Copyright (c) 1993 Andrew Herbert.
66261Sjkh * All rights reserved.
76261Sjkh *
86261Sjkh * Redistribution and use in source and binary forms, with or without
96261Sjkh * modification, are permitted provided that the following conditions
106261Sjkh * are met:
116261Sjkh * 1. Redistributions of source code must retain the above copyright
126261Sjkh *    notice, this list of conditions and the following disclaimer.
136261Sjkh * 2. Redistributions in binary form must reproduce the above copyright
146261Sjkh *    notice, this list of conditions and the following disclaimer in the
156261Sjkh *    documentation and/or other materials provided with the distribution.
166261Sjkh * 3. The name Andrew Herbert may not be used to endorse or promote products
176261Sjkh *    derived from this software without specific prior written permission.
186261Sjkh *
196261Sjkh * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED
206261Sjkh * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
216261Sjkh * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
226261Sjkh * NO EVENT SHALL I BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
236261Sjkh * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
246261Sjkh * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
256261Sjkh * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
266261Sjkh * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
276261Sjkh * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
286261Sjkh * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
296261Sjkh *
309406Sbde *	$Id: cy.c,v 1.7 1995/05/30 08:01:34 rgrimes Exp $
316261Sjkh */
326261Sjkh
339406Sbde#include "cy.h"
349406Sbde#if NCY > 0
356261Sjkh/*
369406Sbde * TODO:
379406Sbde * Check that cy16's work.
389406Sbde * Implement BREAK.
399406Sbde * Fix overflows when closing line.
409406Sbde * Atomic COR change.
419406Sbde * Don't report individual ports in devconf; busy flag for board should be
429406Sbde * union of the current individual busy flags.
439406Sbde * Consoles.
446261Sjkh */
456261Sjkh
466261Sjkh/*
479406Sbde * Temporary compile-time configuration options.
486261Sjkh */
499406Sbde#define	RxFifoThreshold	(CD1400_RX_FIFO_SIZE / 2)
509406Sbde			/* Number of chars in the receiver FIFO before an
519406Sbde			 * an interrupt is generated.  Should depend on
529406Sbde			 * line speed.  Needs to be about 6 on a 486DX33
539406Sbde			 * for 4 active ports at 115200 bps.  Why doesn't
549406Sbde			 * 10 work?
559406Sbde			 */
569406Sbde#define	PollMode	/* Use polling-based irq service routine, not the
579406Sbde			 * hardware svcack lines.  Must be defined for
589406Sbde			 * Cyclom-16Y boards.  Less efficient for Cyclom-8Ys,
599406Sbde			 * and stops 4 * 115200 bps from working.
609406Sbde			 */
619406Sbde#undef	Smarts		/* Enable slightly more CD1400 intelligence.  Mainly
629406Sbde			 * the output CR/LF processing, plus we can avoid a
639406Sbde			 * few checks usually done in ttyinput().
649406Sbde			 *
659406Sbde			 * XXX not fully implemented, and not particularly
669406Sbde			 * worthwhile.
679406Sbde			 */
689406Sbde#undef	CyDebug		/* Include debugging code (not very expensive). */
696261Sjkh
709406Sbde/* These will go away. */
719406Sbde#undef	SOFT_CTS_OFLOW
729406Sbde#define	SOFT_HOTCHAR
736261Sjkh
746261Sjkh#include <sys/param.h>
756261Sjkh#include <sys/systm.h>
769406Sbde#include <sys/reboot.h>
776261Sjkh#include <sys/ioctl.h>
789406Sbde#define	TTYDEFCHARS		/* XXX TK2.0 */
796261Sjkh#include <sys/tty.h>
809406Sbde#undef	TTYDEFCHARS
816261Sjkh#include <sys/proc.h>
826261Sjkh#include <sys/user.h>
836261Sjkh#include <sys/conf.h>
849406Sbde#include <sys/dkstat.h>
856261Sjkh#include <sys/file.h>
866261Sjkh#include <sys/uio.h>
876261Sjkh#include <sys/kernel.h>
889406Sbde#include <sys/malloc.h>
896261Sjkh#include <sys/syslog.h>
909406Sbde#include <sys/devconf.h>
916261Sjkh
927442Sbde#include <machine/clock.h>
936261Sjkh
949406Sbde#include <i386/isa/icu.h>	/* XXX just to get at `imen' */
959406Sbde#include <i386/isa/isa.h>
966261Sjkh#include <i386/isa/isa_device.h>
979406Sbde#include <i386/isa/cyreg.h>
986261Sjkh#include <i386/isa/ic/cd1400.h>
996261Sjkh
1009406Sbde/*
1019406Sbde * Dictionary so that I can name everything *sio* or *com* to compare with
1029406Sbde * sio.c.  There is also lots of ugly formatting and unnecessary ifdefs to
1039406Sbde * simplify the comparision.  These will go away.
1049406Sbde */
1059406Sbde#define	LSR_BI		CD1400_RDSR_BREAK
1069406Sbde#define	LSR_FE		CD1400_RDSR_FE
1079406Sbde#define	LSR_OE		CD1400_RDSR_OE
1089406Sbde#define	LSR_PE		CD1400_RDSR_PE
1099406Sbde#define	MCR_DTR		CD1400_MSVR2_DTR
1109406Sbde#define	MCR_RTS		CD1400_MSVR1_RTS
1119406Sbde#define	MSR_CTS		CD1400_MSVR2_CTS
1129406Sbde#define	MSR_DCD		CD1400_MSVR2_CD
1139406Sbde#define	MSR_DSR		CD1400_MSVR2_DSR
1149406Sbde#define	MSR_RI		CD1400_MSVR2_RI
1159406Sbde#define	NSIO		(NCY * CY_MAX_PORTS)
1169406Sbde#define	comconsole	cyconsole
1179406Sbde#define	comdefaultrate	cydefaultrate
1189406Sbde#define	com_events	cy_events
1199406Sbde#define	comhardclose	cyhardclose
1209406Sbde#define	commajor	cymajor
1219406Sbde#define	commctl		cymctl
1229406Sbde#define	comparam	cyparam
1239406Sbde#define	comspeed	cyspeed
1249406Sbde#define	comstart	cystart
1259406Sbde#define	comwakeup	cywakeup
1269406Sbde#define	kdc_sio		kdc_cy
1279406Sbde#define	nsio_tty	ncy_tty
1289406Sbde#define	p_com_addr	p_cy_addr
1299406Sbde#define	sioattach	cyattach
1309406Sbde#define	sioclose	cyclose
1319406Sbde#define	siodevtotty	cydevtotty
1329406Sbde#define	siodriver	cydriver
1339406Sbde#define	siodtrwakeup	cydtrwakeup
1349406Sbde#define	sioioctl	cyioctl
1359406Sbde#define	siointr		cyintr
1369406Sbde#define	siointr1	cyintr1
1379406Sbde#define	siointrts	cyintrts
1389406Sbde#define	sioopen		cyopen
1399406Sbde#define	siopoll		cypoll
1409406Sbde#define	sioprobe	cyprobe
1419406Sbde#define	sioread		cyread
1429406Sbde#define	sioregisterdev	cyregisterdev
1439406Sbde#define	siosettimeout	cysettimeout
1449406Sbde#define	siostop		cystop
1459406Sbde#define	siowrite	cywrite
1469406Sbde#define	sio_timeout	cy_timeout
1479406Sbde#define	sio_timeouts_until_log	cy_timeouts_until_log
1489406Sbde#define	sio_tty		cy_tty
1496261Sjkh
1509406Sbde#define	CY_MAX_PORTS		(CD1400_NO_OF_CHANNELS * CY_MAX_CD1400s)
1516261Sjkh
1529406Sbde/* We encode the cyclom unit number (cyu) in spare bits in the IVR's. */
1539406Sbde#define	CD1400_xIVR_CHAN_SHIFT	3
1549406Sbde#define	CD1400_xIVR_CHAN	0x0F	/* XXX reduce to pack Cyclom-8Ys */
1556261Sjkh
1569406Sbde/*
1579406Sbde * XXX temporary kludges for 2.0 (XXX TK2.0).
1589406Sbde */
1599406Sbde#define	TSA_CARR_ON(tp)		((void *)&(tp)->t_rawq)
1609406Sbde#define	TSA_OCOMPLETE(tp)	((void *)&(tp)->t_outq)
1619406Sbde#define	TSA_OLOWAT(tp)		((void *)&(tp)->t_outq)
1629406Sbdestatic	void	termioschars	__P((struct termios *t));
1639406Sbdestatic void
1649406Sbdetermioschars(t)
1659406Sbde	struct termios *t;
1669406Sbde{
1676261Sjkh
1689406Sbde	bcopy(ttydefchars, t->c_cc, sizeof t->c_cc);
1699406Sbde}
1706261Sjkh
1719406Sbde#define	LOTS_OF_EVENTS	64	/* helps separate urgent events from input */
1729406Sbde#define	RB_I_HIGH_WATER	(TTYHOG - 2 * RS_IBUFSIZE)
1739406Sbde#define	RS_IBUFSIZE	256
1746261Sjkh
1759406Sbde#define	CALLOUT_MASK		0x80
1769406Sbde#define	CONTROL_MASK		0x60
1779406Sbde#define	CONTROL_INIT_STATE	0x20
1789406Sbde#define	CONTROL_LOCK_STATE	0x40
1799406Sbde#define	DEV_TO_UNIT(dev)	(MINOR_TO_UNIT(minor(dev)))
1809406Sbde#define	MINOR_MAGIC_MASK	(CALLOUT_MASK | CONTROL_MASK)
1819406Sbde#define	MINOR_TO_UNIT(mynor)	((mynor) & ~MINOR_MAGIC_MASK)
1826261Sjkh
1839406Sbde/*
1849406Sbde * Input buffer watermarks.
1859406Sbde * The external device is asked to stop sending when the buffer exactly reaches
1869406Sbde * high water, or when the high level requests it.
1879406Sbde * The high level is notified immediately (rather than at a later clock tick)
1889406Sbde * when this watermark is reached.
1899406Sbde * The buffer size is chosen so the watermark should almost never be reached.
1909406Sbde * The low watermark is invisibly 0 since the buffer is always emptied all at
1919406Sbde * once.
1929406Sbde */
1939406Sbde#define	RS_IHIGHWATER (3 * RS_IBUFSIZE / 4)
1946261Sjkh
1959406Sbde/*
1969406Sbde * com state bits.
1979406Sbde * (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher
1989406Sbde * than the other bits so that they can be tested as a group without masking
1999406Sbde * off the low bits.
2009406Sbde *
2019406Sbde * The following com and tty flags correspond closely:
2029406Sbde *	CS_BUSY		= TS_BUSY (maintained by comstart(), siopoll() and
2039406Sbde *				   siostop())
2049406Sbde *	CS_TTGO		= ~TS_TTSTOP (maintained by comstart() and siostop())
2059406Sbde *	CS_CTS_OFLOW	= CCTS_OFLOW (maintained by comparam())
2069406Sbde *	CS_RTS_IFLOW	= CRTS_IFLOW (maintained by comparam())
2079406Sbde * TS_FLUSH is not used.
2089406Sbde * XXX I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON.
2099406Sbde * XXX CS_*FLOW should be CF_*FLOW in com->flags (control flags not state).
2109406Sbde */
2119406Sbde#define	CS_BUSY		0x80	/* output in progress */
2129406Sbde#define	CS_TTGO		0x40	/* output not stopped by XOFF */
2139406Sbde#define	CS_ODEVREADY	0x20	/* external device h/w ready (CTS) */
2149406Sbde#define	CS_CHECKMSR	1	/* check of MSR scheduled */
2159406Sbde#define	CS_CTS_OFLOW	2	/* use CTS output flow control */
2169406Sbde#define	CS_DTR_OFF	0x10	/* DTR held off */
2179406Sbde#define	CS_ODONE	4	/* output completed */
2189406Sbde#define	CS_RTS_IFLOW	8	/* use RTS input flow control */
2196261Sjkh
2209406Sbdestatic	char const * const	error_desc[] = {
2219406Sbde#define	CE_OVERRUN			0
2229406Sbde	"silo overflow",
2239406Sbde#define	CE_INTERRUPT_BUF_OVERFLOW	1
2249406Sbde	"interrupt-level buffer overflow",
2259406Sbde#define	CE_TTY_BUF_OVERFLOW		2
2269406Sbde	"tty-level buffer overflow",
2279406Sbde};
2286261Sjkh
2299406Sbde#define	CE_NTYPES			3
2309406Sbde#define	CE_RECORD(com, errnum)		(++(com)->delta_error_counts[errnum])
2316261Sjkh
2329406Sbde/* types.  XXX - should be elsewhere */
2339406Sbdetypedef u_char	bool_t;		/* boolean */
2349406Sbdetypedef u_char volatile *cy_addr;
2359406Sbde
2369406Sbde/* queue of linear buffers */
2379406Sbdestruct lbq {
2389406Sbde	u_char	*l_head;	/* next char to process */
2399406Sbde	u_char	*l_tail;	/* one past the last char to process */
2409406Sbde	struct lbq *l_next;	/* next in queue */
2419406Sbde	bool_t	l_queued;	/* nonzero if queued */
2429406Sbde};
2439406Sbde
2449406Sbde/* com device structure */
2459406Sbdestruct com_s {
2469406Sbde	u_char	state;		/* miscellaneous flag bits */
2479406Sbde	bool_t  active_out;	/* nonzero if the callout device is open */
2486261Sjkh#if 0
2499406Sbde	u_char	cfcr_image;	/* copy of value written to CFCR */
2509406Sbde	u_char	ftl;		/* current rx fifo trigger level */
2519406Sbde	u_char	ftl_init;	/* ftl_max for next open() */
2529406Sbde	u_char	ftl_max;	/* maximum ftl for curent open() */
2539406Sbde	bool_t	hasfifo;	/* nonzero for 16550 UARTs */
2549406Sbde	bool_t	loses_outints;	/* nonzero if device loses output interrupts */
2556261Sjkh#endif
2569406Sbde	u_char	mcr_image;	/* copy of value written to MCR */
2579406Sbde#if 0
2589406Sbde#ifdef COM_MULTIPORT
2599406Sbde	bool_t	multiport;	/* is this unit part of a multiport device? */
2609406Sbde#endif /* COM_MULTIPORT */
2619406Sbde	bool_t	no_irq;		/* nonzero if irq is not attached */
2629406Sbde	bool_t	poll;		/* nonzero if polling is required */
2639406Sbde	bool_t	poll_output;	/* nonzero if polling for output is required */
2649406Sbde#endif
2659406Sbde	int	unit;		/* unit	number */
2669406Sbde	int	dtr_wait;	/* time to hold DTR down on close (* 1/hz) */
2679406Sbde#if 0
2689406Sbde	u_int	tx_fifo_size;
2699406Sbde#endif
2709406Sbde	u_int	wopeners;	/* # processes waiting for DCD in open() */
2716261Sjkh
2729406Sbde	/*
2739406Sbde	 * The high level of the driver never reads status registers directly
2749406Sbde	 * because there would be too many side effects to handle conveniently.
2759406Sbde	 * Instead, it reads copies of the registers stored here by the
2769406Sbde	 * interrupt handler.
2779406Sbde	 */
2789406Sbde	u_char	last_modem_status;	/* last MSR read by intr handler */
2799406Sbde	u_char	prev_modem_status;	/* last MSR handled by high level */
2806261Sjkh
2819406Sbde	u_char	hotchar;	/* ldisc-specific char to be handled ASAP */
2829406Sbde	u_char	*ibuf;		/* start of input buffer */
2839406Sbde	u_char	*ibufend;	/* end of input buffer */
2849406Sbde	u_char	*ihighwater;	/* threshold in input buffer */
2859406Sbde	u_char	*iptr;		/* next free spot in input buffer */
2866261Sjkh
2879406Sbde	struct lbq	obufq;	/* head of queue of output buffers */
2889406Sbde	struct lbq	obufs[2];	/* output buffers */
2896261Sjkh
2909406Sbde	cy_addr	cy_iobase;	/* base address of this port's cyclom */
2919406Sbde	cy_addr	iobase;		/* base address of this port's cd1400 */
2926261Sjkh
2939406Sbde	struct tty	*tp;	/* cross reference */
2949406Sbde
2959406Sbde	/* Initial state. */
2969406Sbde	struct termios	it_in;	/* should be in struct tty */
2979406Sbde	struct termios	it_out;
2989406Sbde
2999406Sbde	/* Lock state. */
3009406Sbde	struct termios	lt_in;	/* should be in struct tty */
3019406Sbde	struct termios	lt_out;
3029406Sbde
3039406Sbde	bool_t	do_timestamp;
3049406Sbde	struct timeval	timestamp;
3059406Sbde
3069406Sbde	u_long	bytes_in;	/* statistics */
3079406Sbde	u_long	bytes_out;
3089406Sbde	u_int	delta_error_counts[CE_NTYPES];
3099406Sbde	u_long	error_counts[CE_NTYPES];
3109406Sbde
3119406Sbde	u_int	recv_exception;	/* exception chars received */
3129406Sbde	u_int	mdm;		/* modem signal changes */
3139406Sbde#ifdef CyDebug
3149406Sbde	u_int	start_count;	/* no. of calls to comstart() */
3159406Sbde	u_int	start_real;	/* no. of calls that did something */
3166261Sjkh#endif
3179406Sbde	u_char	channel_control;/* CD1400 CCR control command shadow */
3189406Sbde	u_char	cor[3];		/* CD1400 COR1-3 shadows */
3199406Sbde	u_char	intr_enable;	/* CD1400 SRER shadow */
3206261Sjkh
3219406Sbde	/*
3229406Sbde	 * Ping-pong input buffers.  The extra factor of 2 in the sizes is
3239406Sbde	 * to allow for an error byte for each input byte.
3249406Sbde	 */
3259406Sbde#define	CE_INPUT_OFFSET		RS_IBUFSIZE
3269406Sbde	u_char	ibuf1[2 * RS_IBUFSIZE];
3279406Sbde	u_char	ibuf2[2 * RS_IBUFSIZE];
3286261Sjkh
3299406Sbde	/*
3309406Sbde	 * Data area for output buffers.  Someday we should build the output
3319406Sbde	 * buffer queue without copying data.
3329406Sbde	 */
3339406Sbde	u_char	obuf1[256];
3349406Sbde	u_char	obuf2[256];
3359406Sbde
3369406Sbde	struct kern_devconf kdc;
3379406Sbde};
3389406Sbde
3396261Sjkh/*
3409406Sbde * XXX public functions in drivers should be declared in headers produced
3419406Sbde * by `config', not here.
3426261Sjkh */
3436261Sjkh
3449406Sbde/* Interrupt handling entry points. */
3459406Sbdevoid	siointr		__P((int unit));
3469406Sbdevoid	siointrts	__P((int unit));
3479406Sbdevoid	siopoll		__P((void));
3489406Sbde
3499406Sbde/* Device switch entry points. */
3509406Sbdeint	sioopen		__P((dev_t dev, int oflags, int devtype,
3519406Sbde			     struct proc *p));
3529406Sbdeint	sioclose	__P((dev_t dev, int fflag, int devtype,
3539406Sbde			     struct proc *p));
3549406Sbdeint	sioread		__P((dev_t dev, struct uio *uio, int ioflag));
3559406Sbdeint	siowrite	__P((dev_t dev, struct uio *uio, int ioflag));
3569406Sbdeint	sioioctl	__P((dev_t dev, int cmd, caddr_t data,
3579406Sbde			     int fflag, struct proc *p));
3589406Sbdevoid	siostop		__P((struct tty *tp, int rw));
3599406Sbde#define	sioreset	noreset
3609406Sbdestruct tty *siodevtotty	__P((dev_t dev));
3619406Sbde#define	siommap		nommap
3629406Sbde#define	siostrategy	nostrategy
3639406Sbde
3649406Sbdestatic	int	sioattach	__P((struct isa_device *dev));
3659406Sbdestatic	void	cd1400_channel_cmd __P((cy_addr iobase, int cmd));
3669406Sbdestatic	timeout_t siodtrwakeup;
3679406Sbdestatic	void	comhardclose	__P((struct com_s *com));
3689406Sbdestatic	void	siointr1	__P((struct com_s *com));
3699406Sbdestatic	int	commctl		__P((struct com_s *com, int bits, int how));
3709406Sbdestatic	int	comparam	__P((struct tty *tp, struct termios *t));
3719406Sbdestatic	int	sioprobe	__P((struct isa_device *dev));
3729406Sbdestatic	void	sioregisterdev	__P((struct isa_device *id));
3739406Sbdestatic	void	siosettimeout	__P((void));
3749406Sbdestatic	int	comspeed	__P((speed_t speed, int *prescaler_io));
3759406Sbdestatic	void	comstart	__P((struct tty *tp));
3769406Sbdestatic	timeout_t comwakeup;
3779406Sbdestatic	void	disc_optim	__P((struct tty	*tp, struct termios *t,
3789406Sbde				     struct com_s *com));
3799406Sbde
3806261Sjkh#ifdef CyDebug
3819406Sbdevoid	cystatus	__P((int unit));
3826261Sjkh#endif
3839406Sbde
3849406Sbde/* table and macro for fast conversion from a unit number to its com struct */
3859406Sbdestatic	struct com_s	*p_com_addr[NSIO];
3869406Sbde#define	com_addr(unit)	(p_com_addr[unit])
3879406Sbde
3889406Sbdestatic  struct timeval	intr_timestamp;
3899406Sbde
3909406Sbdestruct isa_driver	siodriver = {
3919406Sbde	sioprobe, sioattach, "cy"
3929406Sbde};
3939406Sbde
3949406Sbde#ifdef COMCONSOLE
3959406Sbde#undef COMCONSOLE
3969406Sbde#define	COMCONSOLE	1
3979406Sbde#else
3989406Sbde#define	COMCONSOLE	0
3996261Sjkh#endif
4009406Sbde
4019406Sbde#ifndef CONUNIT
4029406Sbde#define	CONUNIT	(0)
4036261Sjkh#endif
4046261Sjkh
4059406Sbdestatic	int	comconsole = CONUNIT;
4069406Sbdestatic	speed_t	comdefaultrate = TTYDEF_SPEED;
4079406Sbdestatic	u_int	com_events;	/* input chars + weighted output completions */
4089406Sbdestatic	int	commajor;
4099406Sbdestatic	int	sio_timeout;
4109406Sbdestatic	int	sio_timeouts_until_log;
4119406Sbde#if 0 /* XXX TK2.0 */
4129406Sbdestatic struct tty	*sio_tty[NSIO];
4136454Sbde#else
4149406Sbdestatic struct tty	sio_tty[NSIO];
4159406Sbdestatic	int	nsio_tty = NSIO;
4166454Sbde#endif
4176261Sjkh
4189406Sbde#ifdef KGDB
4199406Sbde#include <machine/remote-sl.h>
4209406Sbde
4219406Sbdeextern	int	kgdb_dev;
4229406Sbdeextern	int	kgdb_rate;
4239406Sbdeextern	int	kgdb_debug_init;
4249406Sbde#endif
4259406Sbde
4266261Sjkh#ifdef CyDebug
4279406Sbdestatic	u_int	cd_inbs;
4289406Sbdestatic	u_int	cy_inbs;
4299406Sbdestatic	u_int	cd_outbs;
4309406Sbdestatic	u_int	cy_outbs;
4319406Sbdestatic	u_int	cy_svrr_probes;
4329406Sbdestatic	u_int	cy_timeouts;
4336261Sjkh#endif
4346261Sjkh
4359406Sbdestatic	int	cy_nr_cd1400s[NCY];
4369406Sbde#undef	RxFifoThreshold
4379406Sbdestatic	int	volatile RxFifoThreshold = (CD1400_RX_FIFO_SIZE / 2);
4386261Sjkh
4399406Sbdestatic struct kern_devconf kdc_sio[NCY] = { {
4409406Sbde	0, 0, 0,		/* filled in by dev_attach */
4419406Sbde	"cyc", 0, { MDDT_ISA, 0, "tty" },
4429406Sbde	isa_generic_externalize, 0, 0, ISA_EXTERNALLEN,
4439406Sbde	&kdc_isa0,		/* parent */
4449406Sbde	0,			/* parentdata */
4459406Sbde	DC_UNCONFIGURED,	/* state */
4469406Sbde	"Cyclades multiport board",
4479406Sbde	DC_CLS_MISC		/* just an ordinary device */
4489406Sbde} };
4499406Sbde
4509406Sbdestatic void
4519406Sbdesioregisterdev(id)
4529406Sbde	struct isa_device *id;
4536261Sjkh{
4549406Sbde	int	unit;
4556261Sjkh
4569406Sbde	unit = id->id_unit;
4579406Sbde	if (unit != 0)
4589406Sbde		kdc_sio[unit] = kdc_sio[0];
4599406Sbde	kdc_sio[unit].kdc_unit = unit;
4609406Sbde	kdc_sio[unit].kdc_isa = id;
4619406Sbde	dev_attach(&kdc_sio[unit]);
4629406Sbde}
4636261Sjkh
4649406Sbdestatic int
4659406Sbdesioprobe(dev)
4669406Sbde	struct isa_device	*dev;
4679406Sbde{
4689406Sbde	int	cyu;
4699406Sbde	u_char	firmware_version;
4709406Sbde	cy_addr	iobase;
4719406Sbde	int	unit;
4726261Sjkh
4739406Sbde	unit = dev->id_unit;
4749406Sbde	if ((u_int)unit >= NCY)
4759406Sbde		return (0);
4769406Sbde	cy_nr_cd1400s[unit] = 0;
4779406Sbde	sioregisterdev(dev);
4786261Sjkh
4799406Sbde	/* Cyclom-16Y hardware reset (Cyclom-8Ys don't care) */
4809406Sbde	cy_inb((cy_addr)dev->id_maddr, CY16_RESET);	/* XXX? */
4816261Sjkh
4829406Sbde	DELAY(500);	/* wait for the board to get its act together */
4836261Sjkh
4849406Sbde	for (cyu = 0, iobase = (cy_addr)dev->id_maddr; cyu < CY_MAX_CD1400s;
4859406Sbde	     ++cyu, iobase += CY_CD1400_MEMSIZE) {
4869406Sbde		int	i;
4876261Sjkh
4889406Sbde		/* wait for chip to become ready for new command */
4899406Sbde		for (i = 0; i < 100; i += 50) {
4909406Sbde			DELAY(50);
4919406Sbde			if (!cd_inb(iobase, CD1400_CCR))
4929406Sbde				break;
4939406Sbde		}
4946261Sjkh
4959406Sbde		/* clear the GFRCR register */
4969406Sbde		cd_outb(iobase, CD1400_GFRCR, 0);
4976261Sjkh
4989406Sbde		/* issue a reset command */
4999406Sbde		cd_outb(iobase, CD1400_CCR,
5009406Sbde			CD1400_CCR_CMDRESET | CD1400_CCR_FULLRESET);
5016261Sjkh
5029406Sbde		/* wait for the CD1400 to initialize itself */
5039406Sbde		for (i = 0; i < 1000; i += 50) {
5049406Sbde			DELAY(50);
5059406Sbde
5069406Sbde			/* retrieve firmware version */
5079406Sbde			firmware_version = cd_inb(iobase, CD1400_GFRCR);
5089406Sbde			if (firmware_version != 0)
5099406Sbde				break;
5109406Sbde		}
5119406Sbde
5129406Sbde		/*
5139406Sbde		 * Anything in the 0x40-0x4F range is fine.
5149406Sbde		 * If one CD1400 is bad then we don't support higher
5159406Sbde		 * numbered good ones on this board.
5169406Sbde		 */
5179406Sbde		if ((firmware_version & 0xF0) != 0x40)
5189406Sbde			break;
5199406Sbde		++cy_nr_cd1400s[unit];
5206261Sjkh	}
5219406Sbde	return (cy_nr_cd1400s[unit] == 0 ? 0 : -1);
5226261Sjkh}
5236261Sjkh
5249406Sbdestatic int
5259406Sbdesioattach(isdp)
5269406Sbde	struct isa_device	*isdp;
5276261Sjkh{
5289406Sbde	int	cyu;
5299406Sbde	cy_addr	cy_iobase;
5309406Sbde	cy_addr	iobase;
5319406Sbde	int	ncyu;
5329406Sbde	int	unit;
5336261Sjkh
5349406Sbde	unit = isdp->id_unit;
5359406Sbde	if ((u_int)unit >= NCY)
5369406Sbde		return (0);
5379406Sbde	ncyu = cy_nr_cd1400s[unit];
5389406Sbde	if (ncyu == 0)
5399406Sbde		return (0);
5409406Sbde	isdp->id_ri_flags |= RI_FAST;
5416261Sjkh
5429406Sbde	cy_iobase = (cy_addr)isdp->id_maddr;
5439406Sbde	unit *= CY_MAX_PORTS;
5449406Sbde	for (cyu = 0, iobase = cy_iobase; cyu < ncyu;
5459406Sbde	     ++cyu, iobase += CY_CD1400_MEMSIZE) {
5469406Sbde		int	cdu;
5476261Sjkh
5489406Sbde		/* Set up a receive timeout period of than 1+ ms. */
5499406Sbde		cd_outb(iobase, CD1400_PPR,
5509406Sbde			howmany(CY_CLOCK / CD1400_PPR_PRESCALER, 1000));
5516261Sjkh
5529406Sbde		for (cdu = 0; cdu < CD1400_NO_OF_CHANNELS; ++cdu, ++unit) {
5539406Sbde			struct com_s	*com;
5549406Sbde			int		s;
5556261Sjkh
5569406Sbde	com = malloc(sizeof *com, M_DEVBUF, M_NOWAIT);
5579406Sbde	if (com == NULL)
5589406Sbde		break;
5599406Sbde	bzero(com, sizeof *com);
5609406Sbde	com->unit = unit;
5619406Sbde	com->dtr_wait = 3 * hz;
5629406Sbde	com->iptr = com->ibuf = com->ibuf1;
5639406Sbde	com->ibufend = com->ibuf1 + RS_IBUFSIZE;
5649406Sbde	com->ihighwater = com->ibuf1 + RS_IHIGHWATER;
5659406Sbde	com->obufs[0].l_head = com->obuf1;
5669406Sbde	com->obufs[1].l_head = com->obuf2;
5676261Sjkh
5689406Sbde			com->cy_iobase = cy_iobase;
5699406Sbde	com->iobase = iobase;
5706261Sjkh
5719406Sbde	/*
5729406Sbde	 * We don't use all the flags from <sys/ttydefaults.h> since they
5739406Sbde	 * are only relevant for logins.  It's important to have echo off
5749406Sbde	 * initially so that the line doesn't start blathering before the
5759406Sbde	 * echo flag can be turned off.
5769406Sbde	 */
5779406Sbde	com->it_in.c_iflag = 0;
5789406Sbde	com->it_in.c_oflag = 0;
5799406Sbde	com->it_in.c_cflag = TTYDEF_CFLAG;
5809406Sbde	com->it_in.c_lflag = 0;
5819406Sbde	if (unit == comconsole && (COMCONSOLE || boothowto & RB_SERIAL)) {
5829406Sbde		com->it_in.c_iflag = TTYDEF_IFLAG;
5839406Sbde		com->it_in.c_oflag = TTYDEF_OFLAG;
5849406Sbde		com->it_in.c_cflag = TTYDEF_CFLAG | CLOCAL;
5859406Sbde		com->it_in.c_lflag = TTYDEF_LFLAG;
5869406Sbde		com->lt_out.c_cflag = com->lt_in.c_cflag = CLOCAL;
5879406Sbde	}
5889406Sbde	termioschars(&com->it_in);
5899406Sbde	com->it_in.c_ispeed = com->it_in.c_ospeed = comdefaultrate;
5909406Sbde	com->it_out = com->it_in;
5916261Sjkh
5929406Sbde			com->kdc = kdc_sio[0];
5939406Sbde			com->kdc.kdc_name = "cy";
5949406Sbde			com->kdc.kdc_unit = unit;
5959406Sbde			com->kdc.kdc_isa = isdp;
5969406Sbde			com->kdc.kdc_parent = &kdc_sio[isdp->id_unit];
5979406Sbde			com->kdc.kdc_state = DC_IDLE;
5989406Sbde			com->kdc.kdc_description =
5999406Sbde			  "Serial port: Cirrus Logic CD1400";
6009406Sbde			com->kdc.kdc_class = DC_CLS_SERIAL;
6019406Sbde			dev_attach(&com->kdc);
6029406Sbde
6039406Sbde	s = spltty();
6049406Sbde	com_addr(unit) = com;
6059406Sbde	splx(s);
6069406Sbde		}
6076261Sjkh	}
6089406Sbde	kdc_sio[isdp->id_unit].kdc_state = DC_BUSY;	/* XXX */
6096261Sjkh
6109406Sbde	/* ensure an edge for the next interrupt */
6119406Sbde	cy_outb(cy_iobase, CY_CLEAR_INTR, 0);
6126261Sjkh
6139406Sbde	return (1);
6146261Sjkh}
6156261Sjkh
6166261Sjkhint
6179406Sbdesioopen(dev, flag, mode, p)
6189406Sbde	dev_t		dev;
6199406Sbde	int		flag;
6209406Sbde	int		mode;
6219406Sbde	struct proc	*p;
6226261Sjkh{
6239406Sbde	struct com_s	*com;
6249406Sbde	int		error;
6259406Sbde	cy_addr		iobase;
6269406Sbde	int		mynor;
6279406Sbde	int		s;
6286261Sjkh	struct tty	*tp;
6299406Sbde	int		unit;
6306261Sjkh
6319406Sbde	mynor = minor(dev);
6329406Sbde	unit = MINOR_TO_UNIT(mynor);
6339406Sbde	if ((u_int) unit >= NSIO || (com = com_addr(unit)) == NULL)
6346261Sjkh		return (ENXIO);
6359406Sbde	if (mynor & CONTROL_MASK)
6369406Sbde		return (0);
6379406Sbde#if 0 /* XXX TK2.0 */
6389406Sbde	tp = com->tp = sio_tty[unit] = ttymalloc(sio_tty[unit]);
6396454Sbde#else
6409406Sbde	tp = com->tp = &sio_tty[unit];
6416454Sbde#endif
6429406Sbde	s = spltty();
6439406Sbde	/*
6449406Sbde	 * We jump to this label after all non-interrupted sleeps to pick
6459406Sbde	 * up any changes of the device state.
6469406Sbde	 */
6479406Sbdeopen_top:
6489406Sbde	while (com->state & CS_DTR_OFF) {
6499406Sbde		error = tsleep(&com->dtr_wait, TTIPRI | PCATCH, "cydtr", 0);
6509406Sbde		if (error != 0)
6519406Sbde			goto out;
6529406Sbde	}
6539406Sbde	com->kdc.kdc_state = DC_BUSY;
6549406Sbde	if (tp->t_state & TS_ISOPEN) {
6559406Sbde		/*
6569406Sbde		 * The device is open, so everything has been initialized.
6579406Sbde		 * Handle conflicts.
6589406Sbde		 */
6599406Sbde		if (mynor & CALLOUT_MASK) {
6609406Sbde			if (!com->active_out) {
6619406Sbde				error = EBUSY;
6629406Sbde				goto out;
6639406Sbde			}
6649406Sbde		} else {
6659406Sbde			if (com->active_out) {
6669406Sbde				if (flag & O_NONBLOCK) {
6679406Sbde					error = EBUSY;
6689406Sbde					goto out;
6699406Sbde				}
6709406Sbde				error =	tsleep(&com->active_out,
6719406Sbde					       TTIPRI | PCATCH, "cybi", 0);
6729406Sbde				if (error != 0)
6739406Sbde					goto out;
6749406Sbde				goto open_top;
6759406Sbde			}
6766261Sjkh		}
6779406Sbde		if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) {
6789406Sbde			error = EBUSY;
6799406Sbde			goto out;
6809406Sbde		}
6819406Sbde	} else {
6829406Sbde		/*
6839406Sbde		 * The device isn't open, so there are no conflicts.
6849406Sbde		 * Initialize it.  Initialization is done twice in many
6859406Sbde		 * cases: to preempt sleeping callin opens if we are
6869406Sbde		 * callout, and to complete a callin open after DCD rises.
6879406Sbde		 */
6889406Sbde		tp->t_oproc = comstart;
6899406Sbde		tp->t_param = comparam;
6909406Sbde		tp->t_dev = dev;
6919406Sbde		tp->t_termios = mynor & CALLOUT_MASK
6929406Sbde				? com->it_out : com->it_in;
6939406Sbde#if 0
6949406Sbde		(void)commctl(com, TIOCM_DTR | TIOCM_RTS, DMSET);
6959406Sbde		com->ftl_max = com->ftl_init;
6969406Sbde		com->poll = com->no_irq;
6979406Sbde		com->poll_output = com->loses_outints;
6989406Sbde#endif
6999406Sbde		++com->wopeners;
7009406Sbde		iobase = com->iobase;
7016261Sjkh
7029406Sbde		/* reset this channel */
7039406Sbde		cd_outb(iobase, CD1400_CAR, unit & CD1400_CAR_CHAN);
7049406Sbde		cd1400_channel_cmd(iobase, CD1400_CCR_CMDRESET);
7056261Sjkh
7066261Sjkh		/*
7079406Sbde		 * Resetting disables the transmitter and receiver as well as
7089406Sbde		 * flushing the fifos so some of our cached state becomes
7099406Sbde		 * invalid.  The documentation suggests that all registers
7109406Sbde		 * for the current channel are reset to defaults, but
7119406Sbde		 * apparently none are.  We wouldn't want DTR cleared.
7129406Sbde		 */
7139406Sbde		com->channel_control = 0;
7149406Sbde
7159406Sbde		/* Encode per-board unit in LIVR for access in intr routines. */
7169406Sbde		cd_outb(iobase, CD1400_LIVR,
7179406Sbde			(unit & CD1400_xIVR_CHAN) << CD1400_xIVR_CHAN_SHIFT);
7189406Sbde
7199406Sbde		/*
7206261Sjkh		 * raise dtr and generally set things up correctly.  this
7216261Sjkh		 * has the side-effect of selecting the appropriate cd1400
7226261Sjkh		 * channel, to help us with subsequent channel control stuff
7236261Sjkh		 */
7249406Sbde		error = comparam(tp, &tp->t_termios);
7259406Sbde		--com->wopeners;
7269406Sbde		if (error != 0)
7279406Sbde			goto out;
7286261Sjkh		/*
7299406Sbde		 * XXX we should goto open_top if comparam() slept.
7306261Sjkh		 */
7316261Sjkh		ttsetwater(tp);
7329406Sbde#if 0
7339406Sbde		if (com->hasfifo) {
7349406Sbde			/*
7359406Sbde			 * (Re)enable and drain fifos.
7369406Sbde			 *
7379406Sbde			 * Certain SMC chips cause problems if the fifos
7389406Sbde			 * are enabled while input is ready.  Turn off the
7399406Sbde			 * fifo if necessary to clear the input.  We test
7409406Sbde			 * the input ready bit after enabling the fifos
7419406Sbde			 * since we've already enabled them in comparam()
7429406Sbde			 * and to handle races between enabling and fresh
7439406Sbde			 * input.
7449406Sbde			 */
7459406Sbde			while (TRUE) {
7469406Sbde				outb(iobase + com_fifo,
7479406Sbde				     FIFO_RCV_RST | FIFO_XMT_RST
7489406Sbde				     | FIFO_ENABLE | com->ftl);
7499406Sbde				DELAY(100);
7509406Sbde				if (!(inb(com->line_status_port) & LSR_RXRDY))
7519406Sbde					break;
7529406Sbde				outb(iobase + com_fifo, 0);
7539406Sbde				DELAY(100);
7549406Sbde				(void) inb(com->data_port);
7559406Sbde			}
7569406Sbde		}
7576261Sjkh
7589406Sbde		disable_intr();
7599406Sbde		(void) inb(com->line_status_port);
7609406Sbde		(void) inb(com->data_port);
7619406Sbde		com->prev_modem_status = com->last_modem_status
7629406Sbde		    = inb(com->modem_status_port);
7639406Sbde		outb(iobase + com_ier, IER_ERXRDY | IER_ETXRDY | IER_ERLS
7649406Sbde				       | IER_EMSC);
7659406Sbde		enable_intr();
7669406Sbde#else /* !0 */
7679406Sbde		/* XXX raise RTS too */
7689406Sbde		(void)commctl(com, TIOCM_DTR | TIOCM_RTS, DMSET);
7699406Sbde		disable_intr();
7709406Sbde		com->prev_modem_status = com->last_modem_status
7719406Sbde		    = cd_inb(iobase, CD1400_MSVR2);
7729406Sbde		cd_outb(iobase, CD1400_SRER,
7739406Sbde			com->intr_enable
7749406Sbde			    = CD1400_SRER_MDMCH | CD1400_SRER_RXDATA);
7759406Sbde		enable_intr();
7769406Sbde#endif /* 0 */
7779406Sbde		/*
7789406Sbde		 * Handle initial DCD.  Callout devices get a fake initial
7799406Sbde		 * DCD (trapdoor DCD).  If we are callout, then any sleeping
7809406Sbde		 * callin opens get woken up and resume sleeping on "cybi"
7819406Sbde		 * instead of "cydcd".
7829406Sbde		 */
7839406Sbde		/*
7849406Sbde		 * XXX `mynor & CALLOUT_MASK' should be
7859406Sbde		 * `tp->t_cflag & (SOFT_CARRIER | TRAPDOOR_CARRIER) where
7869406Sbde		 * TRAPDOOR_CARRIER is the default initial state for callout
7879406Sbde		 * devices and SOFT_CARRIER is like CLOCAL except it hides
7889406Sbde		 * the true carrier.
7899406Sbde		 */
7909406Sbde		if (com->prev_modem_status & MSR_DCD || mynor & CALLOUT_MASK)
7919406Sbde			(*linesw[tp->t_line].l_modem)(tp, 1);
7929406Sbde	}
7939406Sbde	/*
7949406Sbde	 * Wait for DCD if necessary.
7959406Sbde	 */
7969406Sbde	if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK)
7979406Sbde	    && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) {
7989406Sbde		++com->wopeners;
7999406Sbde		error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "cydcd", 0);
8009406Sbde		--com->wopeners;
8019406Sbde		if (error != 0)
8029406Sbde			goto out;
8039406Sbde		goto open_top;
8049406Sbde	}
8059406Sbde	error =	(*linesw[tp->t_line].l_open)(dev, tp);
8069406Sbde	disc_optim(tp, &tp->t_termios, com);
8079406Sbde	if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK)
8089406Sbde		com->active_out = TRUE;
8099406Sbde	siosettimeout();
8109406Sbdeout:
8119406Sbde	splx(s);
8129406Sbde	if (!(tp->t_state & TS_ISOPEN) && com->wopeners == 0)
8139406Sbde		comhardclose(com);
8146261Sjkh	return (error);
8159406Sbde}
8166261Sjkh
8176261Sjkhint
8189406Sbdesioclose(dev, flag, mode, p)
8199406Sbde	dev_t		dev;
8209406Sbde	int		flag;
8219406Sbde	int		mode;
8229406Sbde	struct proc	*p;
8236261Sjkh{
8249406Sbde	struct com_s	*com;
8259406Sbde	int		mynor;
8266261Sjkh	int		s;
8279406Sbde	struct tty	*tp;
8286261Sjkh
8299406Sbde	mynor = minor(dev);
8309406Sbde	if (mynor & CONTROL_MASK)
8319406Sbde		return (0);
8329406Sbde	com = com_addr(MINOR_TO_UNIT(mynor));
8339406Sbde	tp = com->tp;
8346261Sjkh	s = spltty();
8359406Sbde	(*linesw[tp->t_line].l_close)(tp, flag);
8369406Sbde	disc_optim(tp, &tp->t_termios, com);
8379406Sbde	siostop(tp, FREAD | FWRITE);
8389406Sbde	comhardclose(com);
8399406Sbde	ttyclose(tp);
8409406Sbde	siosettimeout();
8416261Sjkh	splx(s);
8426261Sjkh#ifdef broken /* session holds a ref to the tty; can't deallocate */
8436261Sjkh	ttyfree(tp);
8449406Sbde	com->tp = sio_tty[unit] = NULL;
8456261Sjkh#endif
8469406Sbde	return (0);
8479406Sbde}
8486261Sjkh
8499406Sbdestatic void
8509406Sbdecomhardclose(com)
8519406Sbde	struct com_s	*com;
8529406Sbde{
8539406Sbde	cy_addr		iobase;
8549406Sbde	int		s;
8559406Sbde	struct tty	*tp;
8569406Sbde	int		unit;
8576261Sjkh
8589406Sbde	unit = com->unit;
8599406Sbde	iobase = com->iobase;
8609406Sbde	s = spltty();
8619406Sbde#if 0
8629406Sbde	com->poll = FALSE;
8639406Sbde	com->poll_output = FALSE;
8649406Sbde#endif
8659406Sbde	com->do_timestamp = 0;
8669406Sbde	cd_outb(iobase, CD1400_CAR, unit & CD1400_CAR_CHAN);
8679406Sbde#if 0
8689406Sbde	outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK);
8699406Sbde#endif
8706261Sjkh
8719406Sbde#ifdef KGDB
8729406Sbde	/* do not disable interrupts or hang up if debugging */
8739406Sbde	if (kgdb_dev != makedev(commajor, unit))
8749406Sbde#endif
8759406Sbde	{
8769406Sbde#if 0
8779406Sbde		outb(iobase + com_ier, 0);
8789406Sbde#else
8799406Sbde		disable_intr();
8809406Sbde		cd_outb(iobase, CD1400_SRER, com->intr_enable = 0);
8819406Sbde		enable_intr();
8829406Sbde#endif
8839406Sbde		tp = com->tp;
8849406Sbde		if (tp->t_cflag & HUPCL
8859406Sbde		    /*
8869406Sbde		     * XXX we will miss any carrier drop between here and the
8879406Sbde		     * next open.  Perhaps we should watch DCD even when the
8889406Sbde		     * port is closed; it is not sufficient to check it at
8899406Sbde		     * the next open because it might go up and down while
8909406Sbde		     * we're not watching.
8919406Sbde		     */
8929406Sbde		    || !com->active_out
8939406Sbde		       && !(com->prev_modem_status & MSR_DCD)
8949406Sbde		       && !(com->it_in.c_cflag & CLOCAL)
8959406Sbde		    || !(tp->t_state & TS_ISOPEN)) {
8969406Sbde			(void)commctl(com, TIOCM_DTR, DMBIC);
8976261Sjkh
8989406Sbde			/* Disable receiver (leave transmitter enabled). */
8999406Sbde			com->channel_control = CD1400_CCR_CMDCHANCTL
9009406Sbde					       | CD1400_CCR_XMTEN
9019406Sbde					       | CD1400_CCR_RCVDIS;
9029406Sbde			cd1400_channel_cmd(iobase, com->channel_control);
9036261Sjkh
9049406Sbde			if (com->dtr_wait != 0) {
9059406Sbde				timeout(siodtrwakeup, com, com->dtr_wait);
9069406Sbde				com->state |= CS_DTR_OFF;
9079406Sbde			}
9089406Sbde		}
9099406Sbde	}
9109406Sbde	com->active_out = FALSE;
9119406Sbde	wakeup(&com->active_out);
9129406Sbde	wakeup(TSA_CARR_ON(tp));	/* restart any wopeners */
9139406Sbde	if (!(com->state & CS_DTR_OFF)
9149406Sbde	    && !(unit == comconsole && (COMCONSOLE || boothowto & RB_SERIAL)))
9159406Sbde		com->kdc.kdc_state = DC_IDLE;
9169406Sbde	splx(s);
9179406Sbde}
9189406Sbde
9196261Sjkhint
9209406Sbdesioread(dev, uio, flag)
9219406Sbde	dev_t		dev;
9229406Sbde	struct uio	*uio;
9239406Sbde	int		flag;
9246261Sjkh{
9259406Sbde	int		mynor;
9269406Sbde	struct tty	*tp;
9276261Sjkh
9289406Sbde	mynor = minor(dev);
9299406Sbde	if (mynor & CONTROL_MASK)
9309406Sbde		return (ENODEV);
9319406Sbde	tp = com_addr(MINOR_TO_UNIT(mynor))->tp;
9329406Sbde	return ((*linesw[tp->t_line].l_read)(tp, uio, flag));
9339406Sbde}
9346261Sjkh
9356261Sjkhint
9369406Sbdesiowrite(dev, uio, flag)
9379406Sbde	dev_t		dev;
9389406Sbde	struct uio	*uio;
9399406Sbde	int		flag;
9406261Sjkh{
9419406Sbde	int		mynor;
9429406Sbde	struct tty	*tp;
9439406Sbde	int		unit;
9446261Sjkh
9459406Sbde	mynor = minor(dev);
9469406Sbde	if (mynor & CONTROL_MASK)
9479406Sbde		return (ENODEV);
9489406Sbde
9499406Sbde	unit = MINOR_TO_UNIT(mynor);
9509406Sbde	tp = com_addr(unit)->tp;
9519406Sbde	/*
9529406Sbde	 * (XXX) We disallow virtual consoles if the physical console is
9539406Sbde	 * a serial port.  This is in case there is a display attached that
9549406Sbde	 * is not the console.  In that situation we don't need/want the X
9559406Sbde	 * server taking over the console.
9569406Sbde	 */
9579406Sbde	if (constty && unit == comconsole
9589406Sbde	    && (COMCONSOLE || boothowto & RB_SERIAL))
9599406Sbde		constty = NULL;
9606261Sjkh#ifdef Smarts
9616261Sjkh	/* XXX duplicate ttwrite(), but without so much output processing on
9626261Sjkh	 * CR & LF chars.  Hardly worth the effort, given that high-throughput
9636261Sjkh	 * sessions are raw anyhow.
9646261Sjkh	 */
9656261Sjkh#else
9669406Sbde	return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
9676261Sjkh#endif
9689406Sbde}
9696261Sjkh
9706261Sjkhstatic void
9719406Sbdesiodtrwakeup(chan)
9729406Sbde	void	*chan;
9736261Sjkh{
9749406Sbde	struct com_s	*com;
9756261Sjkh
9769406Sbde	com = (struct com_s *)chan;
9779406Sbde	com->state &= ~CS_DTR_OFF;
9789406Sbde	if (!(com->unit == comconsole && (COMCONSOLE || boothowto & RB_SERIAL)))
9799406Sbde		com->kdc.kdc_state = DC_IDLE;
9809406Sbde	wakeup(&com->dtr_wait);
9819406Sbde}
9826261Sjkh
9839406Sbde/* Interrupt routine for timekeeping purposes */
9849406Sbdevoid
9859406Sbdesiointrts(unit)
9869406Sbde	int	unit;
9876261Sjkh{
9889406Sbde	/*
9899406Sbde	 * XXX microtime() reenables CPU interrupts.  We can't afford to
9909406Sbde	 * be interrupted and don't want to slow down microtime(), so lock
9919406Sbde	 * out interrupts in another way.
9929406Sbde	 */
9939406Sbde	outb(IO_ICU1 + 1, 0xff);
9949406Sbde	microtime(&intr_timestamp);
9959406Sbde	disable_intr();
9969406Sbde	outb(IO_ICU1 + 1, imen);
9976261Sjkh
9989406Sbde	siointr(unit);
9999406Sbde}
10006261Sjkh
10016261Sjkhvoid
10029406Sbdesiointr(unit)
10039406Sbde	int	unit;
10046261Sjkh{
10059406Sbde	int	baseu;
10069406Sbde	cy_addr	cy_iobase;
10079406Sbde	int	cyu;
10089406Sbde	cy_addr	iobase;
10099406Sbde	u_char	status;
10106261Sjkh
10119406Sbde	baseu = unit * CY_MAX_PORTS;
10129406Sbde	cy_iobase = com_addr(baseu)->cy_iobase;
10136261Sjkh
10149406Sbde	/* check each CD1400 in turn */
10159406Sbde	for (cyu = 0, iobase = cy_iobase; cyu < cy_nr_cd1400s[unit];
10169406Sbde	     ++cyu, iobase += CY_CD1400_MEMSIZE) {
10179406Sbde		/* poll to see if it has any work */
10189406Sbde		status = cd_inb(iobase, CD1400_SVRR);
10199406Sbde		if (status == 0)
10209406Sbde			continue;
10216261Sjkh#ifdef CyDebug
10229406Sbde		++cy_svrr_probes;
10236261Sjkh#endif
10249406Sbde		/* service requests as appropriate, giving priority to RX */
10259406Sbde		if (status & CD1400_SVRR_RXRDY) {
10269406Sbde			struct com_s	*com;
10279406Sbde			u_int		count;
10289406Sbde			u_char		*ioptr;
10299406Sbde			u_char		line_status;
10309406Sbde			u_char		recv_data;
10319406Sbde			u_char		serv_type;
10329406Sbde#ifdef PollMode
10339406Sbde			u_char		save_car;
10349406Sbde			u_char		save_rir;
10356261Sjkh#endif
10366261Sjkh
10379406Sbde#ifdef PollMode
10389406Sbde			save_rir = cd_inb(iobase, CD1400_RIR);
10399406Sbde			save_car = cd_inb(iobase, CD1400_CAR);
10406261Sjkh
10419406Sbde			/* enter rx service */
10429406Sbde			cd_outb(iobase, CD1400_CAR, save_rir);
10436261Sjkh
10449406Sbde			serv_type = cd_inb(iobase, CD1400_RIVR);
10459406Sbde			com = com_addr(baseu
10469406Sbde				       + ((serv_type >> CD1400_xIVR_CHAN_SHIFT)
10479406Sbde					  & CD1400_xIVR_CHAN));
10486261Sjkh#else
10499406Sbde			/* ack receive service */
10509406Sbde			serv_type = cy_inb(iobase, CY8_SVCACKR);
10516261Sjkh
10529406Sbde			com = com_addr(baseu +
10539406Sbde				       + ((serv_type >> CD1400_xIVR_CHAN_SHIFT)
10549406Sbde					  & CD1400_xIVR_CHAN));
10556261Sjkh#endif
10566261Sjkh
10579406Sbde	if (com->do_timestamp)
10589406Sbde		/* XXX a little bloat here... */
10599406Sbde		com->timestamp = intr_timestamp;
10606261Sjkh
10619406Sbde		if (serv_type & CD1400_RIVR_EXCEPTION) {
10629406Sbde			++com->recv_exception;
10639406Sbde			line_status = cd_inb(iobase, CD1400_RDSR);
10649406Sbde			/* break/unnattached error bits or real input? */
10659406Sbde			recv_data = cd_inb(iobase, CD1400_RDSR);
10669406Sbde#ifndef SOFT_HOTCHAR
10679406Sbde			if (line_status & CD1400_RDSR_SPECIAL
10689406Sbde			    && com->hotchar != 0)
10699406Sbde				setsofttty();
10706261Sjkh#endif
10719406Sbde#if 1 /* XXX "intelligent" PFO error handling would break O error handling */
10729406Sbde			if (line_status & (LSR_PE|LSR_FE|LSR_BI)) {
10739406Sbde				/*
10749406Sbde				  Don't store PE if IGNPAR and BI if IGNBRK,
10759406Sbde				  this hack allows "raw" tty optimization
10769406Sbde				  works even if IGN* is set.
10779406Sbde				*/
10789406Sbde				if (   com->tp == NULL
10799406Sbde				    || !(com->tp->t_state & TS_ISOPEN)
10809406Sbde				    || (line_status & (LSR_PE|LSR_FE))
10819406Sbde				    &&  (com->tp->t_iflag & IGNPAR)
10829406Sbde				    || (line_status & LSR_BI)
10839406Sbde				    &&  (com->tp->t_iflag & IGNBRK))
10849406Sbde					goto cont;
10859406Sbde				if (   (line_status & (LSR_PE|LSR_FE))
10869406Sbde				    && (com->tp->t_state & TS_CAN_BYPASS_L_RINT)
10879406Sbde				    && ((line_status & LSR_FE)
10889406Sbde				    ||  (line_status & LSR_PE)
10899406Sbde				    &&  (com->tp->t_iflag & INPCK)))
10909406Sbde					recv_data = 0;
10919406Sbde			}
10929406Sbde#endif /* 1 */
10939406Sbde			++com->bytes_in;
10949406Sbde#ifdef SOFT_HOTCHAR
10959406Sbde			if (com->hotchar != 0 && recv_data == com->hotchar)
10969406Sbde				setsofttty();
10976261Sjkh#endif
10989406Sbde			ioptr = com->iptr;
10999406Sbde			if (ioptr >= com->ibufend)
11009406Sbde				CE_RECORD(com, CE_INTERRUPT_BUF_OVERFLOW);
11019406Sbde			else {
11029406Sbde				++com_events;
11039406Sbde				ioptr[0] = recv_data;
11049406Sbde				ioptr[CE_INPUT_OFFSET] = line_status;
11059406Sbde				com->iptr = ++ioptr;
11069406Sbde				if (ioptr == com->ihighwater
11079406Sbde				    && com->state & CS_RTS_IFLOW)
11089406Sbde#if 0
11099406Sbde					outb(com->modem_ctl_port,
11109406Sbde					     com->mcr_image &= ~MCR_RTS);
11119406Sbde#else
11129406Sbde					cd_outb(iobase, CD1400_MSVR1,
11139406Sbde						com->mcr_image &= ~MCR_RTS);
11146261Sjkh#endif
11159406Sbde				if (line_status & LSR_OE)
11169406Sbde					CE_RECORD(com, CE_OVERRUN);
11179406Sbde			}
11189406Sbde			goto cont;
11199406Sbde		} else {
11209406Sbde			int	ifree;
11216261Sjkh
11229406Sbde			count = cd_inb(iobase, CD1400_RDCR);
11239406Sbde			com->bytes_in += count;
11249406Sbde			ioptr = com->iptr;
11259406Sbde			ifree = com->ibufend - ioptr;
11269406Sbde			if (count > ifree) {
11279406Sbde				count -= ifree;
11289406Sbde				com_events += ifree;
11299406Sbde				while (ifree-- != 0) {
11309406Sbde					recv_data = cd_inb(iobase, CD1400_RDSR);
11319406Sbde#ifdef SOFT_HOTCHAR
11329406Sbde					if (com->hotchar != 0
11339406Sbde					    && recv_data == com->hotchar)
11349406Sbde						setsofttty();
11356261Sjkh#endif
11369406Sbde					ioptr[0] = recv_data;
11379406Sbde					ioptr[CE_INPUT_OFFSET] = 0;
11389406Sbde					++ioptr;
11399406Sbde				}
11409406Sbde				com->delta_error_counts
11419406Sbde				    [CE_INTERRUPT_BUF_OVERFLOW] += count;
11429406Sbde				do {
11439406Sbde					recv_data = cd_inb(iobase, CD1400_RDSR);
11449406Sbde#ifdef SOFT_HOTCHAR
11459406Sbde					if (com->hotchar != 0
11469406Sbde					    && recv_data == com->hotchar)
11479406Sbde						setsofttty();
11486261Sjkh#endif
11499406Sbde				} while (--count != 0);
11509406Sbde			} else {
11519406Sbde				if (ioptr <= com->ihighwater
11529406Sbde				    && ioptr + count > com->ihighwater
11539406Sbde				    && com->state & CS_RTS_IFLOW)
11549406Sbde#if 0
11559406Sbde					outb(com->modem_ctl_port,
11569406Sbde					     com->mcr_image &= ~MCR_RTS);
11576261Sjkh#else
11589406Sbde					cd_outb(iobase, CD1400_MSVR1,
11599406Sbde						com->mcr_image &= ~MCR_RTS);
11606261Sjkh#endif
11619406Sbde				com_events += count;
11629406Sbde				do {
11639406Sbde					recv_data = cd_inb(iobase, CD1400_RDSR);
11649406Sbde#ifdef SOFT_HOTCHAR
11659406Sbde					if (com->hotchar != 0
11669406Sbde					    && recv_data == com->hotchar)
11679406Sbde						setsofttty();
11686261Sjkh#endif
11699406Sbde					ioptr[0] = recv_data;
11709406Sbde					ioptr[CE_INPUT_OFFSET] = 0;
11719406Sbde					++ioptr;
11729406Sbde				} while (--count != 0);
11736261Sjkh			}
11749406Sbde			com->iptr = ioptr;
11756261Sjkh		}
11769406Sbdecont:
11776261Sjkh
11789406Sbde			/* terminate service context */
11796261Sjkh#ifdef PollMode
11809406Sbde			cd_outb(iobase, CD1400_RIR,
11819406Sbde				save_rir
11829406Sbde				& ~(CD1400_RIR_RDIREQ | CD1400_RIR_RBUSY));
11839406Sbde			cd_outb(iobase, CD1400_CAR, save_car);
11846261Sjkh#else
11859406Sbde			cd_outb(iobase, CD1400_EOSRR, 0);
11866261Sjkh#endif
11879406Sbde		}
11889406Sbde		if (status & CD1400_SVRR_MDMCH) {
11899406Sbde			struct com_s	*com;
11909406Sbde			u_char	modem_status;
11916261Sjkh#ifdef PollMode
11929406Sbde			u_char	save_car;
11939406Sbde			u_char	save_mir;
11946261Sjkh#else
11959406Sbde			u_char	vector;
11966261Sjkh#endif
11976261Sjkh
11986261Sjkh#ifdef PollMode
11999406Sbde			save_mir = cd_inb(iobase, CD1400_MIR);
12009406Sbde			save_car = cd_inb(iobase, CD1400_CAR);
12016261Sjkh
12029406Sbde			/* enter modem service */
12039406Sbde			cd_outb(iobase, CD1400_CAR, save_mir);
12046261Sjkh
12059406Sbde			com = com_addr(baseu + cyu * CD1400_NO_OF_CHANNELS
12069406Sbde				       + (save_mir & CD1400_MIR_CHAN));
12076261Sjkh#else
12089406Sbde			/* ack modem service */
12099406Sbde			vector = cy_inb(iobase, CY8_SVCACKM);
12106261Sjkh
12119406Sbde			com = com_addr(baseu
12129406Sbde				       + ((vector >> CD1400_xIVR_CHAN_SHIFT)
12139406Sbde					  & CD1400_xIVR_CHAN));
12149406Sbde#endif
12159406Sbde			++com->mdm;
12169406Sbde			modem_status = cd_inb(iobase, CD1400_MSVR2);
12179406Sbde		if (modem_status != com->last_modem_status) {
12189406Sbde			/*
12199406Sbde			 * Schedule high level to handle DCD changes.  Note
12209406Sbde			 * that we don't use the delta bits anywhere.  Some
12219406Sbde			 * UARTs mess them up, and it's easy to remember the
12229406Sbde			 * previous bits and calculate the delta.
12239406Sbde			 */
12249406Sbde			com->last_modem_status = modem_status;
12259406Sbde			if (!(com->state & CS_CHECKMSR)) {
12269406Sbde				com_events += LOTS_OF_EVENTS;
12279406Sbde				com->state |= CS_CHECKMSR;
12289406Sbde				setsofttty();
12299406Sbde			}
12306261Sjkh
12319406Sbde#ifdef SOFT_CTS_OFLOW
12329406Sbde			/* handle CTS change immediately for crisp flow ctl */
12339406Sbde			if (com->state & CS_CTS_OFLOW) {
12349406Sbde				if (modem_status & MSR_CTS) {
12359406Sbde					com->state |= CS_ODEVREADY;
12369406Sbde					if (com->state >= (CS_BUSY | CS_TTGO
12379406Sbde							   | CS_ODEVREADY)
12389406Sbde					    && !(com->intr_enable
12399406Sbde						 & CD1400_SRER_TXRDY))
12409406Sbde						cd_outb(iobase, CD1400_SRER,
12419406Sbde							com->intr_enable
12429406Sbde							|= CD1400_SRER_TXRDY);
12439406Sbde				} else {
12449406Sbde					com->state &= ~CS_ODEVREADY;
12459406Sbde					if (com->intr_enable & CD1400_SRER_TXRDY)
12469406Sbde						cd_outb(iobase, CD1400_SRER,
12479406Sbde							com->intr_enable
12489406Sbde							&= ~CD1400_SRER_TXRDY);
12499406Sbde				}
12509406Sbde			}
12516261Sjkh#endif
12529406Sbde		}
12536261Sjkh
12549406Sbde			/* terminate service context */
12556261Sjkh#ifdef PollMode
12569406Sbde			cd_outb(iobase, CD1400_MIR,
12579406Sbde				save_mir
12589406Sbde				& ~(CD1400_MIR_RDIREQ | CD1400_MIR_RBUSY));
12599406Sbde			cd_outb(iobase, CD1400_CAR, save_car);
12606261Sjkh#else
12619406Sbde			cd_outb(iobase, CD1400_EOSRR, 0);
12626261Sjkh#endif
12639406Sbde		}
12649406Sbde		if (status & CD1400_SVRR_TXRDY) {
12659406Sbde			struct com_s	*com;
12666261Sjkh#ifdef PollMode
12679406Sbde			u_char	save_car;
12689406Sbde			u_char	save_tir;
12696261Sjkh#else
12709406Sbde			u_char	vector;
12716261Sjkh#endif
12726261Sjkh
12736261Sjkh#ifdef PollMode
12749406Sbde			save_tir = cd_inb(iobase, CD1400_TIR);
12759406Sbde			save_car = cd_inb(iobase, CD1400_CAR);
12769406Sbde
12779406Sbde			/* enter tx service */
12789406Sbde			cd_outb(iobase, CD1400_CAR, save_tir);
12799406Sbde			com = com_addr(baseu
12809406Sbde				       + cyu * CD1400_NO_OF_CHANNELS
12819406Sbde				       + (save_tir & CD1400_TIR_CHAN));
12826261Sjkh#else
12839406Sbde			/* ack transmit service */
12849406Sbde			vector = cy_inb(iobase, CY8_SVCACKT);
12856261Sjkh
12869406Sbde			com = com_addr(baseu
12879406Sbde				       + ((vector >> CD1400_xIVR_CHAN_SHIFT)
12889406Sbde					  & CD1400_xIVR_CHAN));
12896261Sjkh#endif
12906261Sjkh
12919406Sbde		if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) {
12929406Sbde			u_char	*ioptr;
12939406Sbde			u_int	ocount;
12946261Sjkh
12959406Sbde			ioptr = com->obufq.l_head;
12969406Sbde				ocount = com->obufq.l_tail - ioptr;
12979406Sbde				if (ocount > CD1400_TX_FIFO_SIZE)
12989406Sbde					ocount = CD1400_TX_FIFO_SIZE;
12999406Sbde				com->bytes_out += ocount;
13009406Sbde				do
13019406Sbde					cd_outb(iobase, CD1400_TDR, *ioptr++);
13029406Sbde				while (--ocount != 0);
13039406Sbde			com->obufq.l_head = ioptr;
13049406Sbde			if (ioptr >= com->obufq.l_tail) {
13059406Sbde				struct lbq	*qp;
13066261Sjkh
13079406Sbde				qp = com->obufq.l_next;
13089406Sbde				qp->l_queued = FALSE;
13099406Sbde				qp = qp->l_next;
13109406Sbde				if (qp != NULL) {
13119406Sbde					com->obufq.l_head = qp->l_head;
13129406Sbde					com->obufq.l_tail = qp->l_tail;
13139406Sbde					com->obufq.l_next = qp;
13149406Sbde				} else {
13159406Sbde					/* output just completed */
13169406Sbde					com->state &= ~CS_BUSY;
13179406Sbde					cd_outb(iobase, CD1400_SRER,
13189406Sbde						com->intr_enable
13199406Sbde						&= ~CD1400_SRER_TXRDY);
13209406Sbde				}
13219406Sbde				if (!(com->state & CS_ODONE)) {
13229406Sbde					com_events += LOTS_OF_EVENTS;
13239406Sbde					com->state |= CS_ODONE;
13249406Sbde					setsofttty();	/* handle at high level ASAP */
13259406Sbde				}
13269406Sbde			}
13279406Sbde		}
13286261Sjkh
13299406Sbde			/* terminate service context */
13306261Sjkh#ifdef PollMode
13319406Sbde			cd_outb(iobase, CD1400_TIR,
13329406Sbde				save_tir
13339406Sbde				& ~(CD1400_TIR_RDIREQ | CD1400_TIR_RBUSY));
13349406Sbde			cd_outb(iobase, CD1400_CAR, save_car);
13356261Sjkh#else
13369406Sbde			cd_outb(iobase, CD1400_EOSRR, 0);
13376261Sjkh#endif
13389406Sbde		}
13396261Sjkh	}
13406261Sjkh
13419406Sbde	/* ensure an edge for the next interrupt */
13429406Sbde	cy_outb(cy_iobase, CY_CLEAR_INTR, 0);
13436261Sjkh
13449406Sbde	schedsofttty();
13459406Sbde}
13466261Sjkh
13479406Sbdestatic void
13489406Sbdesiointr1(com)
13499406Sbde	struct com_s	*com;
13509406Sbde{
13516261Sjkh}
13526261Sjkh
13536261Sjkhint
13549406Sbdesioioctl(dev, cmd, data, flag, p)
13559406Sbde	dev_t		dev;
13569406Sbde	int		cmd;
13579406Sbde	caddr_t		data;
13589406Sbde	int		flag;
13599406Sbde	struct proc	*p;
13606261Sjkh{
13619406Sbde	struct com_s	*com;
13626261Sjkh	int		error;
13639406Sbde	cy_addr		iobase;
13649406Sbde	int		mynor;
13659406Sbde	int		s;
13669406Sbde	struct tty	*tp;
13679406Sbde#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
13689406Sbde	int		oldcmd;
13699406Sbde	struct termios	term;
13709406Sbde#endif
13716261Sjkh
13729406Sbde	mynor = minor(dev);
13739406Sbde	com = com_addr(MINOR_TO_UNIT(mynor));
13749406Sbde	iobase = com->iobase;
13759406Sbde	if (mynor & CONTROL_MASK) {
13769406Sbde		struct termios	*ct;
13779406Sbde
13789406Sbde		switch (mynor & CONTROL_MASK) {
13799406Sbde		case CONTROL_INIT_STATE:
13809406Sbde			ct = mynor & CALLOUT_MASK ? &com->it_out : &com->it_in;
13819406Sbde			break;
13829406Sbde		case CONTROL_LOCK_STATE:
13839406Sbde			ct = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in;
13849406Sbde			break;
13859406Sbde		default:
13869406Sbde			return (ENODEV);	/* /dev/nodev */
13879406Sbde		}
13889406Sbde		switch (cmd) {
13899406Sbde		case TIOCSETA:
13909406Sbde			error = suser(p->p_ucred, &p->p_acflag);
13919406Sbde			if (error != 0)
13929406Sbde				return (error);
13939406Sbde			*ct = *(struct termios *)data;
13949406Sbde			return (0);
13959406Sbde		case TIOCGETA:
13969406Sbde			*(struct termios *)data = *ct;
13979406Sbde			return (0);
13989406Sbde		case TIOCGETD:
13999406Sbde			*(int *)data = TTYDISC;
14009406Sbde			return (0);
14019406Sbde		case TIOCGWINSZ:
14029406Sbde			bzero(data, sizeof(struct winsize));
14039406Sbde			return (0);
14049406Sbde		default:
14059406Sbde			return (ENOTTY);
14069406Sbde		}
14079406Sbde	}
14089406Sbde	tp = com->tp;
14099406Sbde#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
14109406Sbde	term = tp->t_termios;
14119406Sbde	oldcmd = cmd;
14129406Sbde	error = ttsetcompat(tp, &cmd, data, &term);
14139406Sbde	if (error != 0)
14146261Sjkh		return (error);
14159406Sbde	if (cmd != oldcmd)
14169406Sbde		data = (caddr_t)&term;
14176261Sjkh#endif
14189406Sbde	if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) {
14199406Sbde		int	cc;
14209406Sbde		struct termios *dt = (struct termios *)data;
14219406Sbde		struct termios *lt = mynor & CALLOUT_MASK
14229406Sbde				     ? &com->lt_out : &com->lt_in;
14239406Sbde
14249406Sbde		dt->c_iflag = (tp->t_iflag & lt->c_iflag)
14259406Sbde			      | (dt->c_iflag & ~lt->c_iflag);
14269406Sbde		dt->c_oflag = (tp->t_oflag & lt->c_oflag)
14279406Sbde			      | (dt->c_oflag & ~lt->c_oflag);
14289406Sbde		dt->c_cflag = (tp->t_cflag & lt->c_cflag)
14299406Sbde			      | (dt->c_cflag & ~lt->c_cflag);
14309406Sbde		dt->c_lflag = (tp->t_lflag & lt->c_lflag)
14319406Sbde			      | (dt->c_lflag & ~lt->c_lflag);
14329406Sbde		for (cc = 0; cc < NCCS; ++cc)
14339406Sbde			if (lt->c_cc[cc] != 0)
14349406Sbde				dt->c_cc[cc] = tp->t_cc[cc];
14359406Sbde		if (lt->c_ispeed != 0)
14369406Sbde			dt->c_ispeed = tp->t_ispeed;
14379406Sbde		if (lt->c_ospeed != 0)
14389406Sbde			dt->c_ospeed = tp->t_ospeed;
14399406Sbde	}
14409406Sbde	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
14416261Sjkh	if (error >= 0)
14426261Sjkh		return (error);
14439406Sbde	s = spltty();
14449406Sbde	error = ttioctl(tp, cmd, data, flag);
14459406Sbde	disc_optim(tp, &tp->t_termios, com);
14469406Sbde	if (error >= 0) {
14479406Sbde		splx(s);
14489406Sbde		return (error);
14499406Sbde	}
14509406Sbde	cd_outb(iobase, CD1400_CAR, MINOR_TO_UNIT(mynor) & CD1400_CAR_CHAN);
14516261Sjkh	switch (cmd) {
14529406Sbde#if 0
14536261Sjkh	case TIOCSBRK:
14549406Sbde		outb(iobase + com_cfcr, com->cfcr_image |= CFCR_SBREAK);
14556261Sjkh		break;
14566261Sjkh	case TIOCCBRK:
14579406Sbde		outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK);
14586261Sjkh		break;
14599406Sbde#endif /* 0 */
14606261Sjkh	case TIOCSDTR:
14619406Sbde		(void)commctl(com, TIOCM_DTR, DMBIS);
14626261Sjkh		break;
14636261Sjkh	case TIOCCDTR:
14649406Sbde		(void)commctl(com, TIOCM_DTR, DMBIC);
14656261Sjkh		break;
14666261Sjkh	case TIOCMSET:
14679406Sbde		(void)commctl(com, *(int *)data, DMSET);
14686261Sjkh		break;
14696261Sjkh	case TIOCMBIS:
14709406Sbde		(void)commctl(com, *(int *)data, DMBIS);
14716261Sjkh		break;
14726261Sjkh	case TIOCMBIC:
14739406Sbde		(void)commctl(com, *(int *)data, DMBIC);
14746261Sjkh		break;
14759406Sbde	case TIOCMGET:
14769406Sbde		*(int *)data = commctl(com, 0, DMGET);
14776261Sjkh		break;
14786261Sjkh	case TIOCMSDTRWAIT:
14799406Sbde		/* must be root since the wait applies to following logins */
14809406Sbde		error = suser(p->p_ucred, &p->p_acflag);
14819406Sbde		if (error != 0) {
14829406Sbde			splx(s);
14839406Sbde			return (error);
14849406Sbde		}
14859406Sbde		com->dtr_wait = *(int *)data * hz / 100;
14869406Sbde		break;
14876261Sjkh	case TIOCMGDTRWAIT:
14889406Sbde		*(int *)data = com->dtr_wait * 100 / hz;
14896261Sjkh		break;
14909406Sbde	case TIOCTIMESTAMP:
14919406Sbde		com->do_timestamp = TRUE;
14929406Sbde		*(struct timeval *)data = com->timestamp;
14939406Sbde		break;
14946261Sjkh	default:
14959406Sbde		splx(s);
14966261Sjkh		return (ENOTTY);
14976261Sjkh	}
14989406Sbde	splx(s);
14999406Sbde	return (0);
15009406Sbde}
15016261Sjkh
15029406Sbdevoid
15039406Sbdesiopoll()
15049406Sbde{
15059406Sbde	int		unit;
15066261Sjkh
15079406Sbde#ifdef CyDebug
15089406Sbde	++cy_timeouts;
15099406Sbde#endif
15109406Sbde	if (com_events == 0)
15119406Sbde		return;
15129406Sbderepeat:
15139406Sbde	for (unit = 0; unit < NSIO; ++unit) {
15149406Sbde		u_char		*buf;
15159406Sbde		struct com_s	*com;
15169406Sbde		u_char		*ibuf;
15179406Sbde		cy_addr		iobase;
15189406Sbde		int		incc;
15199406Sbde		struct tty	*tp;
15206261Sjkh
15219406Sbde		com = com_addr(unit);
15229406Sbde		if (com == NULL)
15239406Sbde			continue;
15249406Sbde		tp = com->tp;
15259406Sbde		if (tp == NULL) {
15269406Sbde			/*
15279406Sbde			 * XXX forget any events related to closed devices
15289406Sbde			 * (actually never opened devices) so that we don't
15299406Sbde			 * loop.
15309406Sbde			 */
15319406Sbde			disable_intr();
15329406Sbde			incc = com->iptr - com->ibuf;
15339406Sbde			com->iptr = com->ibuf;
15349406Sbde			if (com->state & CS_CHECKMSR) {
15359406Sbde				incc += LOTS_OF_EVENTS;
15369406Sbde				com->state &= ~CS_CHECKMSR;
15379406Sbde			}
15389406Sbde			com_events -= incc;
15399406Sbde			enable_intr();
15409406Sbde			if (incc != 0)
15419406Sbde				log(LOG_DEBUG,
15429406Sbde				    "sio%d: %d events for device with no tp\n",
15439406Sbde				    unit, incc);
15449406Sbde			continue;
15459406Sbde		}
15466261Sjkh
15479406Sbde		/* switch the role of the low-level input buffers */
15489406Sbde		if (com->iptr == (ibuf = com->ibuf)) {
15499406Sbde			buf = NULL;     /* not used, but compiler can't tell */
15509406Sbde			incc = 0;
15519406Sbde		} else {
15529406Sbde			buf = ibuf;
15539406Sbde			disable_intr();
15549406Sbde			incc = com->iptr - buf;
15559406Sbde			com_events -= incc;
15569406Sbde			if (ibuf == com->ibuf1)
15579406Sbde				ibuf = com->ibuf2;
15589406Sbde			else
15599406Sbde				ibuf = com->ibuf1;
15609406Sbde			com->ibufend = ibuf + RS_IBUFSIZE;
15619406Sbde			com->ihighwater = ibuf + RS_IHIGHWATER;
15629406Sbde			com->iptr = ibuf;
15636261Sjkh
15649406Sbde			/*
15659406Sbde			 * There is now room for another low-level buffer full
15669406Sbde			 * of input, so enable RTS if it is now disabled and
15679406Sbde			 * there is room in the high-level buffer.
15689406Sbde			 */
15699406Sbde			/*
15709406Sbde			 * XXX this used not to look at CS_RTS_IFLOW.  The
15719406Sbde			 * change is to allow full control of MCR_RTS via
15729406Sbde			 * ioctls after turning CS_RTS_IFLOW off.  Check
15739406Sbde			 * for races.  We shouldn't allow the ioctls while
15749406Sbde			 * CS_RTS_IFLOW is on.
15759406Sbde			 */
15769406Sbde			if ((com->state & CS_RTS_IFLOW)
15779406Sbde			    && !(com->mcr_image & MCR_RTS)
15789406Sbde			    && !(tp->t_state & TS_TBLOCK))
15799406Sbde#if 0
15809406Sbde				outb(com->modem_ctl_port,
15819406Sbde				     com->mcr_image |= MCR_RTS);
15829406Sbde#else
15839406Sbde				iobase = com->iobase,
15849406Sbde				cd_outb(iobase, CD1400_CAR,
15859406Sbde					unit & CD1400_CAR_CHAN),
15869406Sbde				cd_outb(iobase, CD1400_MSVR1,
15879406Sbde					com->mcr_image |= MCR_RTS);
15889406Sbde#endif
15899406Sbde			enable_intr();
15909406Sbde			com->ibuf = ibuf;
15919406Sbde		}
15926261Sjkh
15939406Sbde		if (com->state & CS_CHECKMSR) {
15949406Sbde			u_char	delta_modem_status;
15956261Sjkh
15969406Sbde			disable_intr();
15979406Sbde			delta_modem_status = com->last_modem_status
15989406Sbde					     ^ com->prev_modem_status;
15999406Sbde			com->prev_modem_status = com->last_modem_status;
16009406Sbde			com_events -= LOTS_OF_EVENTS;
16019406Sbde			com->state &= ~CS_CHECKMSR;
16029406Sbde			enable_intr();
16039406Sbde			if (delta_modem_status & MSR_DCD)
16049406Sbde				(*linesw[tp->t_line].l_modem)
16059406Sbde					(tp, com->prev_modem_status & MSR_DCD);
16069406Sbde		}
16079406Sbde		if (com->state & CS_ODONE) {
16089406Sbde			disable_intr();
16099406Sbde			com_events -= LOTS_OF_EVENTS;
16109406Sbde			com->state &= ~CS_ODONE;
16119406Sbde			if (!(com->state & CS_BUSY))
16129406Sbde				com->tp->t_state &= ~TS_BUSY;
16139406Sbde			enable_intr();
16149406Sbde			(*linesw[tp->t_line].l_start)(tp);
16159406Sbde		}
16169406Sbde		if (incc <= 0 || !(tp->t_state & TS_ISOPEN))
16179406Sbde			continue;
16189406Sbde		/*
16199406Sbde		 * XXX only do this when we bypass ttyinput.
16209406Sbde		 */
16219406Sbde		if (tp->t_rawq.c_cc + incc >= RB_I_HIGH_WATER
16229406Sbde		    && (com->state & CS_RTS_IFLOW || tp->t_iflag & IXOFF)
16239406Sbde		    && !(tp->t_state & TS_TBLOCK)
16249406Sbde		    /*
16259406Sbde		     * XXX - need flow control for all line disciplines.
16269406Sbde		     * Only have it in standard one now.
16279406Sbde		     */
16289406Sbde		    && linesw[tp->t_line].l_rint == ttyinput) {
16299406Sbde			int	putc_status = 0;
16306261Sjkh
16319406Sbde			if ((tp->t_iflag & IXOFF
16329406Sbde			     && tp->t_cc[VSTOP] != _POSIX_VDISABLE
16339406Sbde			     && (putc_status = putc(tp->t_cc[VSTOP],
16349406Sbde						    &tp->t_outq)) == 0)
16359406Sbde			    || com->state & CS_RTS_IFLOW) {
16369406Sbde				tp->t_state |= TS_TBLOCK;
16379406Sbde				ttstart(tp);
16389406Sbde				if (putc_status != 0)
16399406Sbde					/* Try again later. */
16409406Sbde					tp->t_state &= ~TS_TBLOCK;
16419406Sbde			}
16429406Sbde		}
16439406Sbde		/*
16449406Sbde		 * Avoid the grotesquely inefficient lineswitch routine
16459406Sbde		 * (ttyinput) in "raw" mode.  It usually takes about 450
16469406Sbde		 * instructions (that's without canonical processing or echo!).
16479406Sbde		 * slinput is reasonably fast (usually 40 instructions plus
16489406Sbde		 * call overhead).
16499406Sbde		 */
16509406Sbde		if (tp->t_state & TS_CAN_BYPASS_L_RINT) {
16519406Sbde			tk_nin += incc;
16529406Sbde			tk_rawcc += incc;
16539406Sbde			tp->t_rawcc += incc;
16549406Sbde			com->delta_error_counts[CE_TTY_BUF_OVERFLOW]
16559406Sbde				+= b_to_q((char *)buf, incc, &tp->t_rawq);
16569406Sbde			ttwakeup(tp);
16579406Sbde			if (tp->t_state & TS_TTSTOP
16589406Sbde			    && (tp->t_iflag & IXANY
16599406Sbde				|| tp->t_cc[VSTART] == tp->t_cc[VSTOP])) {
16609406Sbde				tp->t_state &= ~TS_TTSTOP;
16619406Sbde				tp->t_lflag &= ~FLUSHO;
16629406Sbde				ttstart(tp);
16639406Sbde			}
16649406Sbde		} else {
16659406Sbde			do {
16669406Sbde				u_char	line_status;
16679406Sbde				int	recv_data;
16686261Sjkh
16699406Sbde				line_status = (u_char) buf[CE_INPUT_OFFSET];
16709406Sbde				recv_data = (u_char) *buf++;
16719406Sbde				if (line_status
16729406Sbde				    & (LSR_BI | LSR_FE | LSR_OE | LSR_PE)) {
16739406Sbde					if (line_status & LSR_BI)
16749406Sbde						recv_data |= TTY_BI;
16759406Sbde					if (line_status & LSR_FE)
16769406Sbde						recv_data |= TTY_FE;
16779406Sbde					if (line_status & LSR_OE)
16789406Sbde						recv_data |= TTY_OE;
16799406Sbde					if (line_status & LSR_PE)
16809406Sbde						recv_data |= TTY_PE;
16819406Sbde				}
16829406Sbde				(*linesw[tp->t_line].l_rint)(recv_data, tp);
16839406Sbde			} while (--incc > 0);
16849406Sbde		}
16859406Sbde		if (com_events == 0)
16869406Sbde			break;
16879406Sbde	}
16889406Sbde	if (com_events >= LOTS_OF_EVENTS)
16899406Sbde		goto repeat;
16909406Sbde}
16916261Sjkh
16929406Sbdestatic int
16939406Sbdecomparam(tp, t)
16949406Sbde	struct tty	*tp;
16959406Sbde	struct termios	*t;
16969406Sbde{
16979406Sbde	int		bits;
16989406Sbde	int		cflag;
16999406Sbde	struct com_s	*com;
17009406Sbde	u_char		cor_change;
17019406Sbde	int		idivisor;
17029406Sbde	int		iflag;
17039406Sbde	cy_addr		iobase;
17049406Sbde	int		iprescaler;
17059406Sbde	int		itimeout;
17069406Sbde	int		odivisor;
17079406Sbde	int		oprescaler;
17089406Sbde	u_char		opt;
17099406Sbde	int		s;
17109406Sbde	int		unit;
17116261Sjkh
17129406Sbde	/* do historical conversions */
17139406Sbde	if (t->c_ispeed == 0)
17149406Sbde		t->c_ispeed = t->c_ospeed;
17156261Sjkh
17169406Sbde	/* check requested parameters */
17179406Sbde	idivisor = comspeed(t->c_ispeed, &iprescaler);
17189406Sbde	if (idivisor < 0)
17199406Sbde		return (EINVAL);
17209406Sbde	odivisor = comspeed(t->c_ospeed, &oprescaler);
17219406Sbde	if (odivisor < 0)
17229406Sbde		return (EINVAL);
17236261Sjkh
17249406Sbde	/* parameters are OK, convert them to the com struct and the device */
17259406Sbde	unit = DEV_TO_UNIT(tp->t_dev);
17269406Sbde	com = com_addr(unit);
17279406Sbde	iobase = com->iobase;
17289406Sbde	s = spltty();
17299406Sbde	cd_outb(iobase, CD1400_CAR, unit & CD1400_CAR_CHAN);
17309406Sbde	if (odivisor == 0)
17319406Sbde		(void)commctl(com, TIOCM_DTR, DMBIC);	/* hang up line */
17329406Sbde	else
17339406Sbde		(void)commctl(com, TIOCM_DTR, DMBIS);
17346261Sjkh
17359406Sbde	cd_outb(iobase, CD1400_RBPR, idivisor);
17369406Sbde	cd_outb(iobase, CD1400_RCOR, iprescaler);
17379406Sbde	cd_outb(iobase, CD1400_TBPR, odivisor);
17389406Sbde	cd_outb(iobase, CD1400_TCOR, oprescaler);
17396261Sjkh
17406261Sjkh	/*
17416261Sjkh	 * channel control
17426261Sjkh	 *	receiver enable
17436261Sjkh	 *	transmitter enable (always set)
17446261Sjkh	 */
17459406Sbde	cflag = t->c_cflag;
17469406Sbde	opt = CD1400_CCR_CMDCHANCTL | CD1400_CCR_XMTEN
17479406Sbde	      | (cflag & CREAD ? CD1400_CCR_RCVEN : CD1400_CCR_RCVDIS);
17489406Sbde	if (opt != com->channel_control) {
17499406Sbde		com->channel_control = opt;
17509406Sbde		cd1400_channel_cmd(iobase, opt);
17516261Sjkh	}
17526261Sjkh
17536261Sjkh#ifdef Smarts
17546261Sjkh	/* set special chars */
17559406Sbde	/* XXX if one is _POSIX_VDISABLE, can't use some others */
17569406Sbde	if (t->c_cc[VSTOP] != _POSIX_VDISABLE)
17579406Sbde		cd_outb(iobase, CD1400_SCHR1, t->c_cc[VSTOP]);
17589406Sbde	if (t->c_cc[VSTART] != _POSIX_VDISABLE)
17599406Sbde		cd_outb(iobase, CD1400_SCHR2, t->c_cc[VSTART]);
17609406Sbde	if (t->c_cc[VINTR] != _POSIX_VDISABLE)
17619406Sbde		cd_outb(iobase, CD1400_SCHR3, t->c_cc[VINTR]);
17629406Sbde	if (t->c_cc[VSUSP] != _POSIX_VDISABLE)
17639406Sbde		cd_outb(iobase, CD1400_SCHR4, t->c_cc[VSUSP]);
17646261Sjkh#endif
17656261Sjkh
17666261Sjkh	/*
17676261Sjkh	 * set channel option register 1 -
17686261Sjkh	 *	parity mode
17696261Sjkh	 *	stop bits
17706261Sjkh	 *	char length
17716261Sjkh	 */
17726261Sjkh	opt = 0;
17736261Sjkh	/* parity */
17746261Sjkh	if (cflag & PARENB) {
17759406Sbde		if (cflag & PARODD)
17769406Sbde			opt |= CD1400_COR1_PARODD;
17779406Sbde		opt |= CD1400_COR1_PARNORMAL;
17786261Sjkh	}
17799406Sbde	iflag = t->c_iflag;
17806261Sjkh	if (!(iflag & INPCK))
17819406Sbde		opt |= CD1400_COR1_NOINPCK;
17829406Sbde	bits = 1 + 1;
17836261Sjkh	/* stop bits */
17849406Sbde	if (cflag & CSTOPB) {
17859406Sbde		++bits;
17869406Sbde		opt |= CD1400_COR1_STOP2;
17879406Sbde	}
17886261Sjkh	/* char length */
17899406Sbde	switch (cflag & CSIZE) {
17909406Sbde	case CS5:
17919406Sbde		bits += 5;
17929406Sbde		opt |= CD1400_COR1_CS5;
17939406Sbde		break;
17949406Sbde	case CS6:
17959406Sbde		bits += 6;
17969406Sbde		opt |= CD1400_COR1_CS6;
17979406Sbde		break;
17989406Sbde	case CS7:
17999406Sbde		bits += 7;
18009406Sbde		opt |= CD1400_COR1_CS7;
18019406Sbde		break;
18029406Sbde	default:
18039406Sbde		bits += 8;
18049406Sbde		opt |= CD1400_COR1_CS8;
18059406Sbde		break;
18066261Sjkh	}
18079406Sbde	cor_change = 0;
18089406Sbde	if (opt != com->cor[0]) {
18099406Sbde		cor_change |= CD1400_CCR_COR1;
18109406Sbde		cd_outb(iobase, CD1400_COR1, com->cor[0] = opt);
18119406Sbde	}
18126261Sjkh
18136261Sjkh	/*
18149406Sbde	 * Set receive time-out period, normally to max(one char time, 5 ms).
18159406Sbde	 */
18169406Sbde	if (t->c_ispeed == 0)
18179406Sbde		itimeout = cd_inb(iobase, CD1400_RTPR);
18189406Sbde	else {
18199406Sbde		itimeout = (1000 * bits + t->c_ispeed - 1) / t->c_ispeed;
18209406Sbde#ifdef SOFT_HOTCHAR
18219406Sbde#define	MIN_RTP		1
18229406Sbde#else
18239406Sbde#define	MIN_RTP		5
18249406Sbde#endif
18259406Sbde		if (itimeout < MIN_RTP)
18269406Sbde			itimeout = MIN_RTP;
18279406Sbde	}
18289406Sbde	if (!(t->c_lflag & ICANON) && t->c_cc[VMIN] != 0 && t->c_cc[VTIME] != 0
18299406Sbde	    && t->c_cc[VTIME] * 10 > itimeout)
18309406Sbde		itimeout = t->c_cc[VTIME] * 10;
18319406Sbde	if (itimeout > 255)
18329406Sbde		itimeout = 255;
18339406Sbde	cd_outb(iobase, CD1400_RTPR, itimeout);
18349406Sbde
18359406Sbde	/*
18366261Sjkh	 * set channel option register 2 -
18376261Sjkh	 *	flow control
18386261Sjkh	 */
18396261Sjkh	opt = 0;
18406261Sjkh#ifdef Smarts
18416261Sjkh	if (iflag & IXANY)
18429406Sbde		opt |= CD1400_COR2_IXANY;
18436261Sjkh	if (iflag & IXOFF)
18449406Sbde		opt |= CD1400_COR2_IXOFF;
18456261Sjkh#endif
18469406Sbde#ifndef SOFT_CTS_OFLOW
18476261Sjkh	if (cflag & CCTS_OFLOW)
18489406Sbde		opt |= CD1400_COR2_CCTS_OFLOW;
18496261Sjkh#endif
18509406Sbde	if (opt != com->cor[1]) {
18519406Sbde		cor_change |= CD1400_CCR_COR2;
18529406Sbde		cd_outb(iobase, CD1400_COR2, com->cor[1] = opt);
18536261Sjkh	}
18546261Sjkh
18556261Sjkh	/*
18566261Sjkh	 * set channel option register 3 -
18576261Sjkh	 *	receiver FIFO interrupt threshold
18586261Sjkh	 *	flow control
18596261Sjkh	 */
18609406Sbde	opt = RxFifoThreshold;
18616261Sjkh#ifdef Smarts
18626261Sjkh	if (t->c_lflag & ICANON)
18639406Sbde		opt |= CD1400_COR3_SCD34;	/* detect INTR & SUSP chars */
18646261Sjkh	if (iflag & IXOFF)
18659406Sbde		/* detect and transparently handle START and STOP chars */
18669406Sbde		opt |= CD1400_COR3_FCT | CD1400_COR3_SCD12;
18676261Sjkh#endif
18689406Sbde	if (opt != com->cor[2]) {
18699406Sbde		cor_change |= CD1400_CCR_COR3;
18709406Sbde		cd_outb(iobase, CD1400_COR3, com->cor[2] = opt);
18716261Sjkh	}
18726261Sjkh
18736261Sjkh	/* notify the CD1400 if COR1-3 have changed */
18749406Sbde	if (cor_change)
18759406Sbde		cd1400_channel_cmd(iobase, CD1400_CCR_CMDCORCHG | cor_change);
18766261Sjkh
18776261Sjkh	/*
18786261Sjkh	 * set channel option register 4 -
18796261Sjkh	 *	CR/NL processing
18806261Sjkh	 *	break processing
18816261Sjkh	 *	received exception processing
18826261Sjkh	 */
18836261Sjkh	opt = 0;
18846261Sjkh	if (iflag & IGNCR)
18859406Sbde		opt |= CD1400_COR4_IGNCR;
18866261Sjkh#ifdef Smarts
18876261Sjkh	/*
18886261Sjkh	 * we need a new ttyinput() for this, as we don't want to
18896261Sjkh	 * have ICRNL && INLCR being done in both layers, or to have
18906261Sjkh	 * synchronisation problems
18916261Sjkh	 */
18926261Sjkh	if (iflag & ICRNL)
18939406Sbde		opt |= CD1400_COR4_ICRNL;
18946261Sjkh	if (iflag & INLCR)
18959406Sbde		opt |= CD1400_COR4_INLCR;
18966261Sjkh#endif
18976261Sjkh	if (iflag & IGNBRK)
18989406Sbde		opt |= CD1400_COR4_IGNBRK;
18996261Sjkh	if (!(iflag & BRKINT))
19009406Sbde		opt |= CD1400_COR4_NOBRKINT;
19019406Sbde#if 0
19029406Sbde	/* XXX using this "intelligence" breaks reporting of overruns. */
19036261Sjkh	if (iflag & IGNPAR)
19049406Sbde		opt |= CD1400_COR4_PFO_DISCARD;
19056261Sjkh	else {
19069406Sbde		if (iflag & PARMRK)
19079406Sbde			opt |= CD1400_COR4_PFO_ESC;
19089406Sbde		else
19099406Sbde			opt |= CD1400_COR4_PFO_NUL;
19109406Sbde	}
19116261Sjkh#else
19129406Sbde	opt |= CD1400_COR4_PFO_EXCEPTION;
19136261Sjkh#endif
19149406Sbde	cd_outb(iobase, CD1400_COR4, opt);
19156261Sjkh
19166261Sjkh	/*
19176261Sjkh	 * set channel option register 5 -
19186261Sjkh	 */
19196261Sjkh	opt = 0;
19206261Sjkh	if (iflag & ISTRIP)
19219406Sbde		opt |= CD1400_COR5_ISTRIP;
19229406Sbde	if (t->c_iflag & IEXTEN)
19239406Sbde		/* enable LNEXT (e.g. ctrl-v quoting) handling */
19249406Sbde		opt |= CD1400_COR5_LNEXT;
19256261Sjkh#ifdef Smarts
19266261Sjkh	if (t->c_oflag & ONLCR)
19279406Sbde		opt |= CD1400_COR5_ONLCR;
19286261Sjkh	if (t->c_oflag & OCRNL)
19299406Sbde		opt |= CD1400_COR5_OCRNL;
19306261Sjkh#endif
19319406Sbde	cd_outb(iobase, CD1400_COR5, opt);
19326261Sjkh
19336261Sjkh	/*
19349406Sbde	 * XXX we probably alway want to track carrier changes, so that
19359406Sbde	 * TS_CARR_ON gives the true carrier.  If we don't track them,
19369406Sbde	 * then we should set TS_CARR_ON when CLOCAL drops.
19379406Sbde	 */
19389406Sbde	/*
19396261Sjkh	 * set modem change option register 1
19406261Sjkh	 *	generate modem interrupts on which 1 -> 0 input transitions
19416261Sjkh	 *	also controls auto-DTR output flow-control, which we don't use
19426261Sjkh	 */
19439406Sbde	opt = cflag & CLOCAL ? 0 : CD1400_MCOR1_CDzd;
19449406Sbde#ifdef SOFT_CTS_OFLOW
19459406Sbde	if (cflag & CCTS_OFLOW)
19469406Sbde		opt |= CD1400_MCOR1_CTSzd;
19479406Sbde#endif
19489406Sbde	cd_outb(iobase, CD1400_MCOR1, opt);
19496261Sjkh
19506261Sjkh	/*
19516261Sjkh	 * set modem change option register 2
19526261Sjkh	 *	generate modem interrupts on specific 0 -> 1 input transitions
19536261Sjkh	 */
19549406Sbde	opt = cflag & CLOCAL ? 0 : CD1400_MCOR2_CDod;
19559406Sbde#ifdef SOFT_CTS_OFLOW
19569406Sbde	if (cflag & CCTS_OFLOW)
19579406Sbde		opt |= CD1400_MCOR2_CTSod;
19589406Sbde#endif
19599406Sbde	cd_outb(iobase, CD1400_MCOR2, opt);
19606261Sjkh
19619406Sbde	/*
19629406Sbde	 * XXX should have done this long ago, but there is too much state
19639406Sbde	 * to change all atomically.
19649406Sbde	 */
19659406Sbde	disable_intr();
19666261Sjkh
19679406Sbde	com->state &= ~CS_TTGO;
19689406Sbde	if (!(tp->t_state & TS_TTSTOP))
19699406Sbde		com->state |= CS_TTGO;
19709406Sbde	if (cflag & CRTS_IFLOW)
19719406Sbde		com->state |= CS_RTS_IFLOW;	/* XXX - secondary changes? */
19729406Sbde	else
19739406Sbde		com->state &= ~CS_RTS_IFLOW;
19746261Sjkh
19759406Sbde	/*
19769406Sbde	 * Set up state to handle output flow control.
19779406Sbde	 * XXX - worth handling MDMBUF (DCD) flow control at the lowest level?
19789406Sbde	 * Now has 10+ msec latency, while CTS flow has 50- usec latency.
19799406Sbde	 */
19809406Sbde	com->state |= CS_ODEVREADY;
19819406Sbde#ifdef SOFT_CTS_OFLOW
19829406Sbde	com->state &= ~CS_CTS_OFLOW;
19839406Sbde	if (cflag & CCTS_OFLOW) {
19849406Sbde		com->state |= CS_CTS_OFLOW;
19859406Sbde		if (!(com->last_modem_status & MSR_CTS))
19869406Sbde			com->state &= ~CS_ODEVREADY;
19879406Sbde	}
19889406Sbde#endif
19899406Sbde	/* XXX shouldn't call functions while intrs are disabled. */
19909406Sbde	disc_optim(tp, t, com);
19919406Sbde#if 0
19929406Sbde	/*
19939406Sbde	 * Recover from fiddling with CS_TTGO.  We used to call siointr1()
19949406Sbde	 * unconditionally, but that defeated the careful discarding of
19959406Sbde	 * stale input in sioopen().
19969406Sbde	 */
19979406Sbde	if (com->state >= (CS_BUSY | CS_TTGO))
19989406Sbde		siointr1(com);
19999406Sbde#endif
20009406Sbde	if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) {
20019406Sbde		if (!(com->intr_enable & CD1400_SRER_TXRDY))
20029406Sbde			cd_outb(iobase, CD1400_SRER,
20039406Sbde				com->intr_enable |= CD1400_SRER_TXRDY);
20049406Sbde	} else {
20059406Sbde		if (com->intr_enable & CD1400_SRER_TXRDY)
20069406Sbde			cd_outb(iobase, CD1400_SRER,
20079406Sbde				com->intr_enable &= ~CD1400_SRER_TXRDY);
20089406Sbde	}
20096261Sjkh
20109406Sbde	enable_intr();
20119406Sbde	splx(s);
20129406Sbde	return (0);
20139406Sbde}
20149406Sbde
20159406Sbdestatic void
20169406Sbdecomstart(tp)
20179406Sbde	struct tty	*tp;
20186261Sjkh{
20199406Sbde	struct com_s	*com;
20209406Sbde	cy_addr		iobase;
20216261Sjkh	int		s;
20229406Sbde#ifdef CyDebug
20239406Sbde	bool_t		started;
20249406Sbde#endif
20259406Sbde	int		unit;
20266261Sjkh
20279406Sbde	unit = DEV_TO_UNIT(tp->t_dev);
20289406Sbde	com = com_addr(unit);
20299406Sbde	iobase = com->iobase;
20309406Sbde	s = spltty();
20319406Sbde
20326261Sjkh#ifdef CyDebug
20339406Sbde	++com->start_count;
20349406Sbde	started = FALSE;
20356261Sjkh#endif
20366261Sjkh
20379406Sbde	disable_intr();
20389406Sbde	cd_outb(iobase, CD1400_CAR, unit & CD1400_CAR_CHAN);
20399406Sbde	if (tp->t_state & TS_TTSTOP) {
20409406Sbde		com->state &= ~CS_TTGO;
20419406Sbde		if (com->intr_enable & CD1400_SRER_TXRDY)
20429406Sbde			cd_outb(iobase, CD1400_SRER,
20439406Sbde				com->intr_enable &= ~CD1400_SRER_TXRDY);
20449406Sbde	} else {
20459406Sbde		com->state |= CS_TTGO;
20469406Sbde		if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)
20479406Sbde		    && !(com->intr_enable & CD1400_SRER_TXRDY))
20489406Sbde			cd_outb(iobase, CD1400_SRER,
20499406Sbde				com->intr_enable |= CD1400_SRER_TXRDY);
20509406Sbde	}
20519406Sbde	if (tp->t_state & TS_TBLOCK) {
20529406Sbde		if (com->mcr_image & MCR_RTS && com->state & CS_RTS_IFLOW)
20539406Sbde#if 0
20549406Sbde			outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS);
20559406Sbde#else
20569406Sbde			cd_outb(iobase, CD1400_MSVR1,
20579406Sbde				com->mcr_image &= ~MCR_RTS);
20589406Sbde#endif
20599406Sbde	} else {
20609406Sbde		/*
20619406Sbde		 * XXX don't raise MCR_RTS if CTS_RTS_IFLOW is off.  Set it
20629406Sbde		 * appropriately in comparam() if RTS-flow is being changed.
20639406Sbde		 * Check for races.
20649406Sbde		 */
20659406Sbde		if (!(com->mcr_image & MCR_RTS) && com->iptr < com->ihighwater)
20669406Sbde#if 0
20679406Sbde			outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS);
20689406Sbde#else
20699406Sbde			cd_outb(iobase, CD1400_MSVR1,
20709406Sbde				com->mcr_image |= MCR_RTS);
20719406Sbde#endif
20729406Sbde	}
20739406Sbde	enable_intr();
20749406Sbde	if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) {
20759406Sbde		splx(s);
20766261Sjkh		return;
20779406Sbde	}
20789406Sbde	if (tp->t_outq.c_cc != 0) {
20799406Sbde		struct lbq	*qp;
20809406Sbde		struct lbq	*next;
20816261Sjkh
20829406Sbde		if (!com->obufs[0].l_queued) {
20839406Sbde#ifdef CyDebug
20849406Sbde			started = TRUE;
20859406Sbde#endif
20869406Sbde			com->obufs[0].l_tail
20879406Sbde			    = com->obuf1 + q_to_b(&tp->t_outq, com->obuf1,
20889406Sbde						  sizeof com->obuf1);
20899406Sbde			com->obufs[0].l_next = NULL;
20909406Sbde			com->obufs[0].l_queued = TRUE;
20919406Sbde			disable_intr();
20929406Sbde			if (com->state & CS_BUSY) {
20939406Sbde				qp = com->obufq.l_next;
20949406Sbde				while ((next = qp->l_next) != NULL)
20959406Sbde					qp = next;
20969406Sbde				qp->l_next = &com->obufs[0];
20979406Sbde			} else {
20989406Sbde				com->obufq.l_head = com->obufs[0].l_head;
20999406Sbde				com->obufq.l_tail = com->obufs[0].l_tail;
21009406Sbde				com->obufq.l_next = &com->obufs[0];
21019406Sbde				com->state |= CS_BUSY;
21029406Sbde				if (com->state >= (CS_BUSY | CS_TTGO
21039406Sbde						   | CS_ODEVREADY))
21049406Sbde					cd_outb(iobase, CD1400_SRER,
21059406Sbde						com->intr_enable
21069406Sbde						|= CD1400_SRER_TXRDY);
21079406Sbde			}
21089406Sbde			enable_intr();
21099406Sbde		}
21109406Sbde		if (tp->t_outq.c_cc != 0 && !com->obufs[1].l_queued) {
21119406Sbde#ifdef CyDebug
21129406Sbde			started = TRUE;
21139406Sbde#endif
21149406Sbde			com->obufs[1].l_tail
21159406Sbde			    = com->obuf2 + q_to_b(&tp->t_outq, com->obuf2,
21169406Sbde						  sizeof com->obuf2);
21179406Sbde			com->obufs[1].l_next = NULL;
21189406Sbde			com->obufs[1].l_queued = TRUE;
21199406Sbde			disable_intr();
21209406Sbde			if (com->state & CS_BUSY) {
21219406Sbde				qp = com->obufq.l_next;
21229406Sbde				while ((next = qp->l_next) != NULL)
21239406Sbde					qp = next;
21249406Sbde				qp->l_next = &com->obufs[1];
21259406Sbde			} else {
21269406Sbde				com->obufq.l_head = com->obufs[1].l_head;
21279406Sbde				com->obufq.l_tail = com->obufs[1].l_tail;
21289406Sbde				com->obufq.l_next = &com->obufs[1];
21299406Sbde				com->state |= CS_BUSY;
21309406Sbde				if (com->state >= (CS_BUSY | CS_TTGO
21319406Sbde						   | CS_ODEVREADY))
21329406Sbde					cd_outb(iobase, CD1400_SRER,
21339406Sbde						com->intr_enable
21349406Sbde						|= CD1400_SRER_TXRDY);
21359406Sbde			}
21369406Sbde			enable_intr();
21379406Sbde		}
21389406Sbde		tp->t_state |= TS_BUSY;
21399406Sbde	}
21409406Sbde#ifdef CyDebug
21419406Sbde	if (started)
21429406Sbde		++com->start_real;
21439406Sbde#endif
21449406Sbde#if 0
21459406Sbde	disable_intr();
21469406Sbde	if (com->state >= (CS_BUSY | CS_TTGO)) {
21479406Sbde		siointr1(com);	/* fake interrupt to start output */
21489406Sbde	enable_intr();
21499406Sbde#endif
21509406Sbde
21519406Sbde#if 0 /* XXX TK2.0 */
21529406Sbde	if (tp->t_state & (TS_SO_OCOMPLETE | TS_SO_OLOWAT) || tp->t_wsel)
21539406Sbde		ttwwakeup(tp);
21549406Sbde#else
21556261Sjkh	if (tp->t_outq.c_cc <= tp->t_lowat) {
21569406Sbde		if (tp->t_state & TS_ASLEEP) {
21576261Sjkh			tp->t_state &= ~TS_ASLEEP;
21589406Sbde			wakeup(TSA_OLOWAT(tp));
21596261Sjkh		}
21606261Sjkh		selwakeup(&tp->t_wsel);
21616261Sjkh	}
21626261Sjkh#endif
21636261Sjkh
21649406Sbde	splx(s);
21659406Sbde}
21666261Sjkh
21679406Sbdevoid
21689406Sbdesiostop(tp, rw)
21699406Sbde	struct tty	*tp;
21709406Sbde	int		rw;
21719406Sbde{
21729406Sbde	struct com_s	*com;
21739406Sbde	cy_addr		iobase;
21749406Sbde	int		unit;
21756261Sjkh
21769406Sbde	unit = DEV_TO_UNIT(tp->t_dev);
21779406Sbde	com = com_addr(unit);
21789406Sbde	iobase = com->iobase;
21799406Sbde	disable_intr();
21809406Sbde	if (rw & FWRITE) {
21819406Sbde		com->obufs[0].l_queued = FALSE;
21829406Sbde		com->obufs[1].l_queued = FALSE;
21839406Sbde		if (com->state & CS_ODONE)
21849406Sbde			com_events -= LOTS_OF_EVENTS;
21859406Sbde		com->state &= ~(CS_ODONE | CS_BUSY);
21869406Sbde		com->tp->t_state &= ~TS_BUSY;
21879406Sbde	}
21889406Sbde	if (rw & FREAD) {
21899406Sbde		com_events -= (com->iptr - com->ibuf);
21909406Sbde		com->iptr = com->ibuf;
21919406Sbde	}
21929406Sbde	if (tp->t_state & TS_TTSTOP) {
21939406Sbde		com->state &= ~CS_TTGO;
21949406Sbde		if (com->intr_enable & CD1400_SRER_TXRDY) {
21959406Sbde			cd_outb(iobase, CD1400_CAR, unit & CD1400_CAR_CHAN);
21969406Sbde			cd_outb(iobase, CD1400_SRER,
21979406Sbde				com->intr_enable &= ~CD1400_SRER_TXRDY);
21989406Sbde		}
21999406Sbde	} else {
22009406Sbde		com->state |= CS_TTGO;
22019406Sbde		if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)
22029406Sbde		    && !(com->intr_enable & CD1400_SRER_TXRDY)) {
22039406Sbde			cd_outb(iobase, CD1400_CAR, unit & CD1400_CAR_CHAN);
22049406Sbde			cd_outb(iobase, CD1400_SRER,
22059406Sbde				com->intr_enable |= CD1400_SRER_TXRDY);
22069406Sbde		}
22079406Sbde	}
22089406Sbde	enable_intr();
22096261Sjkh
22109406Sbde	/* XXX should clear h/w fifos too. */
22119406Sbde}
22129406Sbde
22139406Sbdestruct tty *
22149406Sbdesiodevtotty(dev)
22159406Sbde	dev_t	dev;
22169406Sbde{
22179406Sbde	int	mynor;
22189406Sbde	int	unit;
22199406Sbde
22209406Sbde	mynor = minor(dev);
22219406Sbde	if (mynor & CONTROL_MASK)
22229406Sbde		return (NULL);
22239406Sbde	unit = MINOR_TO_UNIT(mynor);
22249406Sbde	if ((u_int) unit >= NSIO)
22259406Sbde		return (NULL);
22269406Sbde	return (&sio_tty[unit]);
22279406Sbde}
22289406Sbde
22299406Sbdestatic int
22309406Sbdecommctl(com, bits, how)
22319406Sbde	struct com_s	*com;
22329406Sbde	int		bits;
22339406Sbde	int		how;
22349406Sbde{
22359406Sbde	cy_addr	iobase;
22369406Sbde	int	mcr;
22379406Sbde	int	msr;
22389406Sbde
22399406Sbde	if (how == DMGET) {
22409406Sbde		if (com->channel_control & CD1400_CCR_RCVEN)
22419406Sbde			bits |= TIOCM_LE;
22429406Sbde		mcr = com->mcr_image;
22439406Sbde		if (mcr & MCR_DTR)
22449406Sbde			bits |= TIOCM_DTR;
22459406Sbde		if (mcr & MCR_RTS)
22469406Sbde			/* XXX wired on for Cyclom-8Ys */
22479406Sbde			bits |= TIOCM_RTS;
22489406Sbde		msr = com->prev_modem_status;
22499406Sbde		if (msr & MSR_CTS)
22509406Sbde			bits |= TIOCM_CTS;
22519406Sbde		if (msr & MSR_DCD)
22529406Sbde			bits |= TIOCM_CD;
22539406Sbde		if (msr & MSR_DSR)
22549406Sbde			bits |= TIOCM_DSR;
22559406Sbde		if (msr & MSR_RI)
22569406Sbde			/* XXX not connected except for Cyclom-16Y? */
22579406Sbde			bits |= TIOCM_RI;
22589406Sbde		return (bits);
22596261Sjkh	}
22609406Sbde	iobase = com->iobase;
22619406Sbde	mcr = 0;
22629406Sbde	if (bits & TIOCM_DTR)
22639406Sbde		mcr |= MCR_DTR;
22649406Sbde	if (bits & TIOCM_RTS)
22659406Sbde		mcr |= MCR_RTS;
22669406Sbde	disable_intr();
22679406Sbde	switch (how) {
22689406Sbde	case DMSET:
22699406Sbde		com->mcr_image = mcr;
22709406Sbde		cd_outb(iobase, CD1400_MSVR1, mcr);
22719406Sbde		cd_outb(iobase, CD1400_MSVR2, mcr);
22729406Sbde		break;
22739406Sbde	case DMBIS:
22749406Sbde		com->mcr_image = mcr = com->mcr_image | mcr;
22759406Sbde		cd_outb(iobase, CD1400_MSVR1, mcr);
22769406Sbde		cd_outb(iobase, CD1400_MSVR2, mcr);
22779406Sbde		break;
22789406Sbde	case DMBIC:
22799406Sbde		com->mcr_image = mcr = com->mcr_image & ~mcr;
22809406Sbde		cd_outb(iobase, CD1400_MSVR1, mcr);
22819406Sbde		cd_outb(iobase, CD1400_MSVR2, mcr);
22829406Sbde		break;
22839406Sbde	}
22849406Sbde	enable_intr();
22859406Sbde	return (0);
22869406Sbde}
22876261Sjkh
22889406Sbdestatic void
22899406Sbdesiosettimeout()
22909406Sbde{
22919406Sbde	struct com_s	*com;
22929406Sbde	bool_t		someopen;
22939406Sbde	int		unit;
22946261Sjkh
22959406Sbde	/*
22969406Sbde	 * Set our timeout period to 1 second if no polled devices are open.
22979406Sbde	 * Otherwise set it to max(1/200, 1/hz).
22989406Sbde	 * Enable timeouts iff some device is open.
22999406Sbde	 */
23009406Sbde	untimeout(comwakeup, (void *)NULL);
23019406Sbde	sio_timeout = hz;
23029406Sbde	someopen = FALSE;
23039406Sbde	for (unit = 0; unit < NSIO; ++unit) {
23049406Sbde		com = com_addr(unit);
23059406Sbde		if (com != NULL && com->tp != NULL
23069406Sbde		    && com->tp->t_state & TS_ISOPEN) {
23079406Sbde			someopen = TRUE;
23089406Sbde#if 0
23099406Sbde			if (com->poll || com->poll_output) {
23109406Sbde				sio_timeout = hz > 200 ? hz / 200 : 1;
23119406Sbde				break;
23129406Sbde			}
23139406Sbde#endif
23149406Sbde		}
23159406Sbde	}
23169406Sbde	if (someopen) {
23179406Sbde		sio_timeouts_until_log = hz / sio_timeout;
23189406Sbde		timeout(comwakeup, (void *)NULL, sio_timeout);
23199406Sbde	} else {
23209406Sbde		/* Flush error messages, if any. */
23219406Sbde		sio_timeouts_until_log = 1;
23229406Sbde		comwakeup((void *)NULL);
23239406Sbde		untimeout(comwakeup, (void *)NULL);
23249406Sbde	}
23259406Sbde}
23266261Sjkh
23279406Sbdestatic void
23289406Sbdecomwakeup(chan)
23299406Sbde	void	*chan;
23306261Sjkh{
23319406Sbde	struct com_s	*com;
23329406Sbde	int		unit;
23336261Sjkh
23349406Sbde	timeout(comwakeup, (void *)NULL, sio_timeout);
23356261Sjkh
23369406Sbde#if 0
23379406Sbde	/*
23389406Sbde	 * Recover from lost output interrupts.
23399406Sbde	 * Poll any lines that don't use interrupts.
23409406Sbde	 */
23419406Sbde	for (unit = 0; unit < NSIO; ++unit) {
23429406Sbde		com = com_addr(unit);
23439406Sbde		if (com != NULL
23449406Sbde		    && (com->state >= (CS_BUSY | CS_TTGO) || com->poll)) {
23459406Sbde			disable_intr();
23469406Sbde			siointr1(com);
23479406Sbde			enable_intr();
23489406Sbde		}
23499406Sbde	}
23509406Sbde#endif
23516261Sjkh
23529406Sbde	/*
23539406Sbde	 * Check for and log errors, but not too often.
23549406Sbde	 */
23559406Sbde	if (--sio_timeouts_until_log > 0)
23569406Sbde		return;
23579406Sbde	sio_timeouts_until_log = hz / sio_timeout;
23589406Sbde	for (unit = 0; unit < NSIO; ++unit) {
23599406Sbde		int	errnum;
23606261Sjkh
23619406Sbde		com = com_addr(unit);
23629406Sbde		if (com == NULL)
23639406Sbde			continue;
23649406Sbde		for (errnum = 0; errnum < CE_NTYPES; ++errnum) {
23659406Sbde			u_int	delta;
23669406Sbde			u_long	total;
23676261Sjkh
23689406Sbde			disable_intr();
23699406Sbde			delta = com->delta_error_counts[errnum];
23709406Sbde			com->delta_error_counts[errnum] = 0;
23719406Sbde			enable_intr();
23729406Sbde			if (delta == 0)
23739406Sbde				continue;
23749406Sbde			total = com->error_counts[errnum] += delta;
23759406Sbde			log(LOG_ERR, "cy%d: %u more %s%s (total %lu)\n",
23769406Sbde			    unit, delta, error_desc[errnum],
23779406Sbde			    delta == 1 ? "" : "s", total);
23789406Sbde#if 0
23799406Sbde			/*
23809406Sbde			 * XXX if we resurrect this then we should move
23819406Sbde			 * the dropping of the ftl to somewhere with less
23829406Sbde			 * latency.
23839406Sbde			 */
23849406Sbde			if (errnum == CE_OVERRUN && com->hasfifo
23859406Sbde			    && com->ftl > FIFO_TRIGGER_1) {
23869406Sbde				static	u_char	ftl_in_bytes[] =
23879406Sbde					{ 1, 4, 8, 14, };
23889406Sbde
23899406Sbde				com->ftl_init = FIFO_TRIGGER_8;
23909406Sbde#define	FIFO_TRIGGER_DELTA	FIFO_TRIGGER_4
23919406Sbde				com->ftl_max =
23929406Sbde				com->ftl -= FIFO_TRIGGER_DELTA;
23939406Sbde				outb(com->iobase + com_fifo,
23949406Sbde				     FIFO_ENABLE | com->ftl);
23959406Sbde				log(LOG_DEBUG,
23969406Sbde				    "sio%d: reduced fifo trigger level to %d\n",
23979406Sbde				    unit,
23989406Sbde				    ftl_in_bytes[com->ftl
23999406Sbde						 / FIFO_TRIGGER_DELTA]);
24009406Sbde			}
24019406Sbde#endif
24029406Sbde		}
24039406Sbde	}
24046261Sjkh}
24056261Sjkh
24069406Sbdestatic void
24079406Sbdedisc_optim(tp, t, com)
24089406Sbde	struct tty	*tp;
24099406Sbde	struct termios	*t;
24109406Sbde	struct com_s	*com;
24116712Spst{
24129406Sbde#ifndef SOFT_HOTCHAR
24139406Sbde	cy_addr	iobase;
24149406Sbde	u_char	opt;
24159406Sbde#endif
24166261Sjkh
24179406Sbde	/*
24189406Sbde	 * XXX can skip a lot more cases if Smarts.  Maybe
24199406Sbde	 * (IGNCR | ISTRIP | IXOFF | IXON) in c_iflag.  But perhaps we
24209406Sbde	 * shouldn't skip if (TS_CNTTB | TS_LNCH) is set in t_state.
24219406Sbde	 */
24229406Sbde	if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP
24239406Sbde			   | IXOFF | IXON))
24249406Sbde	    && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK))
24259406Sbde	    && (!(t->c_iflag & PARMRK) ||
24269406Sbde		(t->c_iflag & (IGNPAR|IGNBRK)) == (IGNPAR|IGNBRK))
24279406Sbde	    && !(t->c_lflag & (ECHO | ECHONL | ICANON | IEXTEN | ISIG
24289406Sbde			   | PENDIN))
24299406Sbde	    && linesw[tp->t_line].l_rint == ttyinput)
24309406Sbde		tp->t_state |= TS_CAN_BYPASS_L_RINT;
24319406Sbde	else
24329406Sbde		tp->t_state &= ~TS_CAN_BYPASS_L_RINT;
24339406Sbde	/*
24349406Sbde	 * Prepare to reduce input latency for packet
24359406Sbde	 * discplines with a end of packet character.
24369406Sbde	 */
24379406Sbde	if (tp->t_line == SLIPDISC)
24389406Sbde		com->hotchar = 0xc0;
24399406Sbde	else if (tp->t_line == PPPDISC)
24409406Sbde		com->hotchar = 0x7e;
24419406Sbde	else
24429406Sbde		com->hotchar = 0;
24439406Sbde#ifndef SOFT_HOTCHAR
24449406Sbde	iobase = com->iobase;
24459406Sbde	cd_outb(iobase, CD1400_CAR, com->unit & CD1400_CAR_CHAN);
24469406Sbde	opt = com->cor[2] & ~CD1400_COR3_SCD34;
24479406Sbde	if (com->hotchar != 0) {
24489406Sbde		cd_outb(iobase, CD1400_SCHR3, com->hotchar);
24499406Sbde		cd_outb(iobase, CD1400_SCHR4, com->hotchar);
24509406Sbde		opt |= CD1400_COR3_SCD34;
24519406Sbde	}
24529406Sbde	if (opt != com->cor[2]) {
24539406Sbde		cd_outb(iobase, CD1400_COR3, com->cor[2] = opt);
24549406Sbde		cd1400_channel_cmd(com->iobase,
24559406Sbde				   CD1400_CCR_CMDCORCHG | CD1400_CCR_COR3);
24569406Sbde	}
24579406Sbde#endif
24586712Spst}
24596712Spst
24609406Sbde#ifdef Smarts
24619406Sbde/* standard line discipline input routine */
24626261Sjkhint
24639406Sbdecyinput(c, tp)
24649406Sbde	int		c;
24659406Sbde	struct tty	*tp;
24666261Sjkh{
24679406Sbde	/* XXX duplicate ttyinput(), but without the IXOFF/IXON/ISTRIP/IPARMRK
24689406Sbde	 * bits, as they are done by the CD1400.  Hardly worth the effort,
24699406Sbde	 * given that high-throughput sessions are raw anyhow.
24709406Sbde	 */
24719406Sbde}
24729406Sbde#endif /* Smarts */
24736261Sjkh
24749406Sbdestatic int
24759406Sbdecomspeed(speed, prescaler_io)
24769406Sbde	speed_t	speed;
24779406Sbde	int	*prescaler_io;
24789406Sbde{
24799406Sbde	int	actual;
24809406Sbde	int	error;
24819406Sbde	int	divider;
24829406Sbde	int	prescaler;
24839406Sbde	int	prescaler_unit;
24846261Sjkh
24859406Sbde	if (speed == 0)
24869406Sbde		return (0);
24879406Sbde	if (speed < 0 || speed > 150000)
24889406Sbde		return (-1);
24896261Sjkh
24909406Sbde	/* determine which prescaler to use */
24919406Sbde	for (prescaler_unit = 4, prescaler = 2048; prescaler_unit;
24929406Sbde		prescaler_unit--, prescaler >>= 2) {
24939406Sbde		if (CY_CLOCK / prescaler / speed > 63)
24949406Sbde			break;
24959406Sbde	}
24966261Sjkh
24979406Sbde	divider = (CY_CLOCK / prescaler * 2 / speed + 1) / 2; /* round off */
24989406Sbde	if (divider > 255)
24999406Sbde		divider = 255;
25009406Sbde	actual = CY_CLOCK/prescaler/divider;
25019406Sbde	error = ((actual - speed) * 2000 / speed + 1) / 2;	/* percentage */
25026261Sjkh
25039406Sbde	/* 3.0% max error tolerance */
25049406Sbde	if (error < -30 || error > 30)
25059406Sbde		return (-1);
25066261Sjkh
25076261Sjkh#if 0
25089406Sbde	printf("prescaler = %d (%d)\n", prescaler, prescaler_unit);
25099406Sbde	printf("divider = %d (%x)\n", divider, divider);
25109406Sbde	printf("actual = %d\n", actual);
25119406Sbde	printf("error = %d\n", error);
25126261Sjkh#endif
25136261Sjkh
25149406Sbde	*prescaler_io = prescaler_unit;
25159406Sbde	return (divider);
25169406Sbde}
25176261Sjkh
25186261Sjkhstatic void
25199406Sbdecd1400_channel_cmd(iobase, cmd)
25209406Sbde	cy_addr	iobase;
25219406Sbde	int	cmd;
25226261Sjkh{
25239406Sbde	/* XXX hsu@clinet.fi: This is always more dependent on ISA bus speed,
25246261Sjkh	   as the card is probed every round?  Replaced delaycount with 8k.
25256261Sjkh	   Either delaycount has to be implemented in FreeBSD or more sensible
25266261Sjkh	   way of doing these should be implemented.  DELAY isn't enough here.
25276261Sjkh	   */
25289406Sbde	u_int	maxwait = 5 * 8 * 1024;	/* approx. 5 ms */
25296261Sjkh
25306261Sjkh	/* wait for processing of previous command to complete */
25319406Sbde	while (cd_inb(iobase, CD1400_CCR) && maxwait--)
25329406Sbde		;
25338876Srgrimes
25346261Sjkh	if (!maxwait)
25356261Sjkh		log(LOG_ERR, "cy: channel command timeout (%d loops) - arrgh\n",
25366261Sjkh		    5 * 8 * 1024);
25376261Sjkh
25389406Sbde	cd_outb(iobase, CD1400_CCR, cmd);
25399406Sbde}
25406261Sjkh
25416261Sjkh#ifdef CyDebug
25426261Sjkh/* useful in ddb */
25436261Sjkhvoid
25449406Sbdecystatus(unit)
25459406Sbde	int	unit;
25466261Sjkh{
25479406Sbde	struct com_s	*com;
25489406Sbde	cy_addr		iobase;
25499406Sbde	u_int		ocount;
25509406Sbde	struct tty	*tp;
25516261Sjkh
25529406Sbde	com = com_addr(unit);
25536261Sjkh	printf("info for channel %d\n", unit);
25546261Sjkh	printf("------------------\n");
25559406Sbde	printf("total cyclom service probes:\t%d\n", cy_svrr_probes);
25569406Sbde	printf("calls to upper layer:\t\t%d\n", cy_timeouts);
25579406Sbde	if (com == NULL)
25589406Sbde		return;
25599406Sbde	iobase = com->iobase;
25606261Sjkh	printf("\n");
25619406Sbde	printf("cd1400 base address:\\tt%p\n", iobase);
25629406Sbde	cd_outb(iobase, CD1400_CAR, unit & CD1400_CAR_CHAN);
25639406Sbde	printf("saved channel_control:\t\t0x%02x\n", com->channel_control);
25649406Sbde	printf("saved cor1-3:\t\t\t0x%02x 0x%02x 0x%02x\n",
25659406Sbde	       com->cor[0], com->cor[1], com->cor[2]);
25669406Sbde	printf("service request enable reg:\t0x%02x (0x%02x cached)\n",
25679406Sbde	       cd_inb(iobase, CD1400_SRER), com->intr_enable);
25689406Sbde	printf("service request register:\t0x%02x\n",
25699406Sbde	       cd_inb(iobase, CD1400_SVRR));
25709406Sbde	printf("modem status:\t\t\t0x%02x (0x%02x cached)\n",
25719406Sbde	       cd_inb(iobase, CD1400_MSVR2), com->prev_modem_status);
25729406Sbde	printf("rx/tx/mdm interrupt registers:\t0x%02x 0x%02x 0x%02x\n",
25739406Sbde	       cd_inb(iobase, CD1400_RIR), cd_inb(iobase, CD1400_TIR),
25749406Sbde	       cd_inb(iobase, CD1400_MIR));
25756261Sjkh	printf("\n");
25769406Sbde	printf("com state:\t\t\t0x%02x\n", com->state);
25779406Sbde	printf("calls to comstart():\t\t%d (%d useful)\n",
25789406Sbde	       com->start_count, com->start_real);
25799406Sbde	printf("rx buffer chars free:\t\t%d\n", com->iptr - com->ibuf);
25809406Sbde	ocount = 0;
25819406Sbde	if (com->obufs[0].l_queued)
25829406Sbde		ocount += com->obufs[0].l_tail - com->obufs[0].l_head;
25839406Sbde	if (com->obufs[1].l_queued)
25849406Sbde		ocount += com->obufs[1].l_tail - com->obufs[1].l_head;
25859406Sbde	printf("tx buffer chars:\t\t%u\n", ocount);
25869406Sbde	printf("received chars:\t\t\t%d\n", com->bytes_in);
25879406Sbde	printf("received exceptions:\t\t%d\n", com->recv_exception);
25889406Sbde	printf("modem signal deltas:\t\t%d\n", com->mdm);
25899406Sbde	printf("transmitted chars:\t\t%d\n", com->bytes_out);
25909406Sbde	printf("\n");
25919406Sbde	tp = com->tp;
25929406Sbde	if (tp != NULL) {
25939406Sbde		printf("tty state:\t\t\t0x%08x\n", tp->t_state);
25946261Sjkh		printf("upper layer queue lengths:\t%d raw, %d canon, %d output\n",
25959406Sbde		       tp->t_rawq.c_cc, tp->t_canq.c_cc, tp->t_outq.c_cc);
25969406Sbde	} else
25976261Sjkh		printf("tty state:\t\t\tclosed\n");
25989406Sbde}
25999406Sbde#endif /* CyDebug */
26006261Sjkh
26016261Sjkh#endif /* NCY > 0 */
2602