cy_isa.c revision 123104
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 */
306261Sjkh
31115703Sobrien#include <sys/cdefs.h>
32115703Sobrien__FBSDID("$FreeBSD: head/sys/dev/cy/cy_isa.c 123104 2003-12-02 12:36:00Z bde $");
33115703Sobrien
3431778Seivind#include "opt_compat.h"
359406Sbde#include "cy.h"
3629047Sbde
376261Sjkh/*
389406Sbde * TODO:
399406Sbde * Atomic COR change.
409406Sbde * Consoles.
416261Sjkh */
426261Sjkh
436261Sjkh/*
449406Sbde * Temporary compile-time configuration options.
456261Sjkh */
469406Sbde#define	RxFifoThreshold	(CD1400_RX_FIFO_SIZE / 2)
479406Sbde			/* Number of chars in the receiver FIFO before an
489406Sbde			 * an interrupt is generated.  Should depend on
499406Sbde			 * line speed.  Needs to be about 6 on a 486DX33
509406Sbde			 * for 4 active ports at 115200 bps.  Why doesn't
519406Sbde			 * 10 work?
529406Sbde			 */
539406Sbde#define	PollMode	/* Use polling-based irq service routine, not the
549406Sbde			 * hardware svcack lines.  Must be defined for
559406Sbde			 * Cyclom-16Y boards.  Less efficient for Cyclom-8Ys,
569406Sbde			 * and stops 4 * 115200 bps from working.
579406Sbde			 */
589406Sbde#undef	Smarts		/* Enable slightly more CD1400 intelligence.  Mainly
599406Sbde			 * the output CR/LF processing, plus we can avoid a
609406Sbde			 * few checks usually done in ttyinput().
619406Sbde			 *
629406Sbde			 * XXX not fully implemented, and not particularly
639406Sbde			 * worthwhile.
649406Sbde			 */
659406Sbde#undef	CyDebug		/* Include debugging code (not very expensive). */
666261Sjkh
679406Sbde/* These will go away. */
689406Sbde#undef	SOFT_CTS_OFLOW
699406Sbde#define	SOFT_HOTCHAR
706261Sjkh
716261Sjkh#include <sys/param.h>
726261Sjkh#include <sys/systm.h>
7376166Smarkm#include <sys/bus.h>
746261Sjkh#include <sys/conf.h>
7524131Sbde#include <sys/fcntl.h>
7638246Sbde#include <sys/interrupt.h>
776261Sjkh#include <sys/kernel.h>
7876166Smarkm#include <sys/lock.h>
799406Sbde#include <sys/malloc.h>
8071846Sbde#include <sys/mutex.h>
816261Sjkh#include <sys/syslog.h>
8276166Smarkm#include <sys/tty.h>
8376166Smarkm
8447585Sbde#include <machine/psl.h>
856261Sjkh
866261Sjkh#include <i386/isa/isa_device.h>
879406Sbde#include <i386/isa/cyreg.h>
886261Sjkh#include <i386/isa/ic/cd1400.h>
896261Sjkh
9061011Speter#ifndef COMPAT_OLDISA
9161011Speter#error "The cy device requires the old isa compatibility shims"
9261011Speter#endif
9361011Speter
949406Sbde/*
959406Sbde * Dictionary so that I can name everything *sio* or *com* to compare with
969406Sbde * sio.c.  There is also lots of ugly formatting and unnecessary ifdefs to
979406Sbde * simplify the comparision.  These will go away.
989406Sbde */
999406Sbde#define	LSR_BI		CD1400_RDSR_BREAK
1009406Sbde#define	LSR_FE		CD1400_RDSR_FE
1019406Sbde#define	LSR_OE		CD1400_RDSR_OE
1029406Sbde#define	LSR_PE		CD1400_RDSR_PE
1039406Sbde#define	MCR_DTR		CD1400_MSVR2_DTR
1049406Sbde#define	MCR_RTS		CD1400_MSVR1_RTS
1059406Sbde#define	MSR_CTS		CD1400_MSVR2_CTS
1069406Sbde#define	MSR_DCD		CD1400_MSVR2_CD
1079406Sbde#define	MSR_DSR		CD1400_MSVR2_DSR
1089406Sbde#define	MSR_RI		CD1400_MSVR2_RI
1099406Sbde#define	NSIO		(NCY * CY_MAX_PORTS)
1109406Sbde#define	comconsole	cyconsole
1119406Sbde#define	comdefaultrate	cydefaultrate
1129406Sbde#define	com_events	cy_events
1139406Sbde#define	comhardclose	cyhardclose
1149406Sbde#define	commctl		cymctl
1159406Sbde#define	comparam	cyparam
1169406Sbde#define	comspeed	cyspeed
1179406Sbde#define	comstart	cystart
1189406Sbde#define	comwakeup	cywakeup
1199406Sbde#define	p_com_addr	p_cy_addr
1209406Sbde#define	sioattach	cyattach
1219406Sbde#define	sioclose	cyclose
1229406Sbde#define	siodriver	cydriver
1239406Sbde#define	siodtrwakeup	cydtrwakeup
12443611Sbde#define	sioinput	cyinput
125123104Sbde#define	siointr1	cyintr
12638445Sbde#define	sioioctl	cyioctl
1279406Sbde#define	sioopen		cyopen
1289406Sbde#define	siopoll		cypoll
1299406Sbde#define	sioprobe	cyprobe
1309406Sbde#define	siosettimeout	cysettimeout
13143611Sbde#define	siosetwater	cysetwater
13251654Sphk#define	comstop		cystop
1339406Sbde#define	siowrite	cywrite
134122799Sbde#define	sio_fast_ih	cy_fast_ih
135122771Sbde#define	sio_inited	cy_inited
13667551Sjhb#define	sio_irec	cy_irec
137122771Sbde#define	sio_lock	cy_lock
138122799Sbde#define	sio_slow_ih	cy_slow_ih
1399406Sbde#define	sio_timeout	cy_timeout
14038445Sbde#define	sio_timeout_handle cy_timeout_handle
1419406Sbde#define	sio_timeouts_until_log	cy_timeouts_until_log
1426261Sjkh
1439406Sbde#define	CY_MAX_PORTS		(CD1400_NO_OF_CHANNELS * CY_MAX_CD1400s)
1446261Sjkh
1459406Sbde/* We encode the cyclom unit number (cyu) in spare bits in the IVR's. */
1469406Sbde#define	CD1400_xIVR_CHAN_SHIFT	3
14729047Sbde#define	CD1400_xIVR_CHAN	0x1F
1486261Sjkh
14941903Sbde/*
15041903Sbde * ETC states.  com->etc may also contain a hardware ETC command value,
15141903Sbde * meaning that execution of that command is pending.
15241903Sbde */
15341903Sbde#define	ETC_NONE		0	/* we depend on bzero() setting this */
15441903Sbde#define	ETC_BREAK_STARTING	1
15541903Sbde#define	ETC_BREAK_STARTED	2
15641903Sbde#define	ETC_BREAK_ENDING	3
15741903Sbde#define	ETC_BREAK_ENDED		4
15841903Sbde
1599406Sbde#define	LOTS_OF_EVENTS	64	/* helps separate urgent events from input */
1606261Sjkh
1619406Sbde#define	CALLOUT_MASK		0x80
1629406Sbde#define	CONTROL_MASK		0x60
1639406Sbde#define	CONTROL_INIT_STATE	0x20
1649406Sbde#define	CONTROL_LOCK_STATE	0x40
1659406Sbde#define	DEV_TO_UNIT(dev)	(MINOR_TO_UNIT(minor(dev)))
1669406Sbde#define	MINOR_MAGIC_MASK	(CALLOUT_MASK | CONTROL_MASK)
16781580Sbde/*
16881580Sbde * Not all of the magic is parametrized in the following macros.  16 and
16981580Sbde * 0xff are related to the bitfields in a udev_t.  CY_MAX_PORTS must be
17081580Sbde * ((0xff & ~MINOR_MAGIC_MASK) + 1) for things to work.
17181580Sbde */
17229047Sbde#define	MINOR_TO_UNIT(mynor)	(((mynor) >> 16) * CY_MAX_PORTS \
17329047Sbde				 | (((mynor) & 0xff) & ~MINOR_MAGIC_MASK))
17481580Sbde#define	UNIT_TO_MINOR(unit)	(((unit) / CY_MAX_PORTS) << 16 \
17581580Sbde				 | (((unit) & 0xff) & ~MINOR_MAGIC_MASK))
1766261Sjkh
1779406Sbde/*
1789406Sbde * com state bits.
1799406Sbde * (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher
1809406Sbde * than the other bits so that they can be tested as a group without masking
1819406Sbde * off the low bits.
1829406Sbde *
1839406Sbde * The following com and tty flags correspond closely:
1849406Sbde *	CS_BUSY		= TS_BUSY (maintained by comstart(), siopoll() and
18551654Sphk *				   comstop())
1869754Sbde *	CS_TTGO		= ~TS_TTSTOP (maintained by comparam() and comstart())
1879406Sbde *	CS_CTS_OFLOW	= CCTS_OFLOW (maintained by comparam())
1889406Sbde *	CS_RTS_IFLOW	= CRTS_IFLOW (maintained by comparam())
1899406Sbde * TS_FLUSH is not used.
1909406Sbde * XXX I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON.
1919406Sbde * XXX CS_*FLOW should be CF_*FLOW in com->flags (control flags not state).
1929406Sbde */
1939406Sbde#define	CS_BUSY		0x80	/* output in progress */
1949406Sbde#define	CS_TTGO		0x40	/* output not stopped by XOFF */
1959406Sbde#define	CS_ODEVREADY	0x20	/* external device h/w ready (CTS) */
1969406Sbde#define	CS_CHECKMSR	1	/* check of MSR scheduled */
1979406Sbde#define	CS_CTS_OFLOW	2	/* use CTS output flow control */
1989406Sbde#define	CS_DTR_OFF	0x10	/* DTR held off */
1999406Sbde#define	CS_ODONE	4	/* output completed */
2009406Sbde#define	CS_RTS_IFLOW	8	/* use RTS input flow control */
20141388Sbde#define	CSE_ODONE	1	/* output transmitted */
2026261Sjkh
2039406Sbdestatic	char const * const	error_desc[] = {
2049406Sbde#define	CE_OVERRUN			0
2059406Sbde	"silo overflow",
2069406Sbde#define	CE_INTERRUPT_BUF_OVERFLOW	1
2079406Sbde	"interrupt-level buffer overflow",
2089406Sbde#define	CE_TTY_BUF_OVERFLOW		2
2099406Sbde	"tty-level buffer overflow",
2109406Sbde};
2116261Sjkh
2129406Sbde#define	CE_NTYPES			3
2139406Sbde#define	CE_RECORD(com, errnum)		(++(com)->delta_error_counts[errnum])
2146261Sjkh
215122771Sbde#ifdef SMP
216122771Sbde#define	COM_LOCK()	mtx_lock_spin(&sio_lock)
217122771Sbde#define	COM_UNLOCK()	mtx_unlock_spin(&sio_lock)
218122771Sbde#else
219122771Sbde#define	COM_LOCK()
220122771Sbde#define	COM_UNLOCK()
221122771Sbde#endif
222122771Sbde
2239406Sbde/* types.  XXX - should be elsewhere */
2249406Sbdetypedef u_char	bool_t;		/* boolean */
2259406Sbdetypedef u_char volatile *cy_addr;
2269406Sbde
2279406Sbde/* queue of linear buffers */
2289406Sbdestruct lbq {
2299406Sbde	u_char	*l_head;	/* next char to process */
2309406Sbde	u_char	*l_tail;	/* one past the last char to process */
2319406Sbde	struct lbq *l_next;	/* next in queue */
2329406Sbde	bool_t	l_queued;	/* nonzero if queued */
2339406Sbde};
2349406Sbde
2359406Sbde/* com device structure */
2369406Sbdestruct com_s {
2379406Sbde	u_char	state;		/* miscellaneous flag bits */
2389406Sbde	bool_t  active_out;	/* nonzero if the callout device is open */
2396261Sjkh#if 0
2409406Sbde	u_char	cfcr_image;	/* copy of value written to CFCR */
24141388Sbde#endif
24241903Sbde	u_char	etc;		/* pending Embedded Transmit Command */
24341388Sbde	u_char	extra_state;	/* more flag bits, separate for order trick */
24441388Sbde#if 0
24512962Sbde	u_char	fifo_image;	/* copy of value written to FIFO */
24638303Sbde#endif
24738303Sbde	u_char	gfrcr_image;	/* copy of value read from GFRCR */
24838303Sbde#if 0
2499406Sbde	bool_t	hasfifo;	/* nonzero for 16550 UARTs */
2509406Sbde	bool_t	loses_outints;	/* nonzero if device loses output interrupts */
2516261Sjkh#endif
25238303Sbde	u_char	mcr_dtr;	/* MCR bit that is wired to DTR */
2539406Sbde	u_char	mcr_image;	/* copy of value written to MCR */
25438303Sbde	u_char	mcr_rts;	/* MCR bit that is wired to RTS */
2559406Sbde#if 0
2569406Sbde#ifdef COM_MULTIPORT
2579406Sbde	bool_t	multiport;	/* is this unit part of a multiport device? */
2589406Sbde#endif /* COM_MULTIPORT */
2599406Sbde	bool_t	no_irq;		/* nonzero if irq is not attached */
2609406Sbde	bool_t	poll;		/* nonzero if polling is required */
2619406Sbde	bool_t	poll_output;	/* nonzero if polling for output is required */
2629406Sbde#endif
2639406Sbde	int	unit;		/* unit	number */
2649406Sbde	int	dtr_wait;	/* time to hold DTR down on close (* 1/hz) */
2659406Sbde#if 0
2669406Sbde	u_int	tx_fifo_size;
2679406Sbde#endif
2689406Sbde	u_int	wopeners;	/* # processes waiting for DCD in open() */
2696261Sjkh
2709406Sbde	/*
2719406Sbde	 * The high level of the driver never reads status registers directly
2729406Sbde	 * because there would be too many side effects to handle conveniently.
2739406Sbde	 * Instead, it reads copies of the registers stored here by the
2749406Sbde	 * interrupt handler.
2759406Sbde	 */
2769406Sbde	u_char	last_modem_status;	/* last MSR read by intr handler */
2779406Sbde	u_char	prev_modem_status;	/* last MSR handled by high level */
2786261Sjkh
2799406Sbde	u_char	hotchar;	/* ldisc-specific char to be handled ASAP */
2809406Sbde	u_char	*ibuf;		/* start of input buffer */
2819406Sbde	u_char	*ibufend;	/* end of input buffer */
28243611Sbde	u_char	*ibufold;	/* old input buffer, to be freed */
2839406Sbde	u_char	*ihighwater;	/* threshold in input buffer */
2849406Sbde	u_char	*iptr;		/* next free spot in input buffer */
28543611Sbde	int	ibufsize;	/* size of ibuf (not include error bytes) */
28643611Sbde	int	ierroff;	/* offset of error bytes in ibuf */
2876261Sjkh
2889406Sbde	struct lbq	obufq;	/* head of queue of output buffers */
2899406Sbde	struct lbq	obufs[2];	/* output buffers */
2906261Sjkh
29129047Sbde	int	cy_align;	/* index for register alignment */
2929406Sbde	cy_addr	cy_iobase;	/* base address of this port's cyclom */
2939406Sbde	cy_addr	iobase;		/* base address of this port's cd1400 */
29438303Sbde	int	mcr_rts_reg;	/* cd1400 reg number of reg holding mcr_rts */
2956261Sjkh
2969406Sbde	struct tty	*tp;	/* cross reference */
2979406Sbde
2989406Sbde	/* Initial state. */
2999406Sbde	struct termios	it_in;	/* should be in struct tty */
3009406Sbde	struct termios	it_out;
3019406Sbde
3029406Sbde	/* Lock state. */
3039406Sbde	struct termios	lt_in;	/* should be in struct tty */
3049406Sbde	struct termios	lt_out;
3059406Sbde
3069406Sbde	bool_t	do_timestamp;
30717354Sbde	bool_t	do_dcd_timestamp;
3089406Sbde	struct timeval	timestamp;
30917354Sbde	struct timeval	dcd_timestamp;
3109406Sbde
3119406Sbde	u_long	bytes_in;	/* statistics */
3129406Sbde	u_long	bytes_out;
3139406Sbde	u_int	delta_error_counts[CE_NTYPES];
3149406Sbde	u_long	error_counts[CE_NTYPES];
3159406Sbde
3169406Sbde	u_int	recv_exception;	/* exception chars received */
3179406Sbde	u_int	mdm;		/* modem signal changes */
3189406Sbde#ifdef CyDebug
3199406Sbde	u_int	start_count;	/* no. of calls to comstart() */
3209406Sbde	u_int	start_real;	/* no. of calls that did something */
3216261Sjkh#endif
32241293Sbde	u_char	car;		/* CD1400 CAR shadow (if first unit in cd) */
3239406Sbde	u_char	channel_control;/* CD1400 CCR control command shadow */
3249406Sbde	u_char	cor[3];		/* CD1400 COR1-3 shadows */
3259406Sbde	u_char	intr_enable;	/* CD1400 SRER shadow */
3266261Sjkh
3279406Sbde	/*
3289406Sbde	 * Data area for output buffers.  Someday we should build the output
3299406Sbde	 * buffer queue without copying data.
3309406Sbde	 */
3319406Sbde	u_char	obuf1[256];
3329406Sbde	u_char	obuf2[256];
3339406Sbde};
3349406Sbde
335123104Sbde/* PCI driver entry points. */
336123104Sbdevoid	*cyattach_common(cy_addr cy_iobase, int cy_align);
337123104Sbdedriver_intr_t	siointr1;
33829047Sbde
339123104Sbdestatic	ointhand2_t cyointr;
34093024Sbdestatic	int	cy_units(cy_addr cy_iobase, int cy_align);
34193024Sbdestatic	int	sioattach(struct isa_device *dev);
34292765Salfredstatic	void	cd1400_channel_cmd(struct com_s *com, int cmd);
34392765Salfredstatic	void	cd1400_channel_cmd_wait(struct com_s *com);
34493024Sbdestatic	void	cd_etc(struct com_s *com, int etc);
34593024Sbdestatic	int	cd_getreg(struct com_s *com, int reg);
34693024Sbdestatic	void	cd_setreg(struct com_s *com, int reg, int val);
3479406Sbdestatic	timeout_t siodtrwakeup;
34893024Sbdestatic	void	comhardclose(struct com_s *com);
34993024Sbdestatic	void	sioinput(struct com_s *com);
35093024Sbdestatic	int	commctl(struct com_s *com, int bits, int how);
35193024Sbdestatic	int	comparam(struct tty *tp, struct termios *t);
35293024Sbdestatic	void	siopoll(void *arg);
35393024Sbdestatic	int	sioprobe(struct isa_device *dev);
35493024Sbdestatic	void	siosettimeout(void);
35593024Sbdestatic	int	siosetwater(struct com_s *com, speed_t speed);
35693024Sbdestatic	int	comspeed(speed_t speed, u_long cy_clock, int *prescaler_io);
35793024Sbdestatic	void	comstart(struct tty *tp);
35893024Sbdestatic	void	comstop(struct tty *tp, int rw);
3599406Sbdestatic	timeout_t comwakeup;
36093024Sbdestatic	void	disc_optim(struct tty *tp, struct termios *t,
36193024Sbde		    struct com_s *com);
3629406Sbde
3636261Sjkh#ifdef CyDebug
36493024Sbdevoid	cystatus(int unit);
3656261Sjkh#endif
3669406Sbde
36772262Sjhbstatic char driver_name[] = "cy";
368122771Sbdestatic struct	mtx sio_lock;
369122771Sbdestatic int	sio_inited;
37012962Sbde
3719406Sbde/* table and macro for fast conversion from a unit number to its com struct */
3729406Sbdestatic	struct com_s	*p_com_addr[NSIO];
3739406Sbde#define	com_addr(unit)	(p_com_addr[unit])
3749406Sbde
37512962Sbdestruct isa_driver	siodriver = {
37665557Sjasone	INTR_TYPE_TTY | INTR_FAST,
37761011Speter	sioprobe,
37861011Speter	sioattach,
37961011Speter	driver_name
38012962Sbde};
38161011SpeterCOMPAT_ISA_DRIVER(cy, cydriver);	/* XXX */
38212675Sjulian
38312962Sbdestatic	d_open_t	sioopen;
38412962Sbdestatic	d_close_t	sioclose;
38512962Sbdestatic	d_write_t	siowrite;
38612962Sbdestatic	d_ioctl_t	sioioctl;
38712962Sbde
38838485Sbde#define	CDEV_MAJOR	48
38947625Sphkstatic struct cdevsw sio_cdevsw = {
390111815Sphk	.d_open =	sioopen,
391111815Sphk	.d_close =	sioclose,
392111815Sphk	.d_read =	ttyread,
393111815Sphk	.d_write =	siowrite,
394111815Sphk	.d_ioctl =	sioioctl,
395111815Sphk	.d_poll =	ttypoll,
396111815Sphk	.d_name =	driver_name,
397111815Sphk	.d_maj =	CDEV_MAJOR,
398111821Sphk	.d_flags =	D_TTY,
399111815Sphk	.d_kqfilter =	ttykqfilter,
4009406Sbde};
4019406Sbde
40211671Sbdestatic	int	comconsole = -1;
4039406Sbdestatic	speed_t	comdefaultrate = TTYDEF_SPEED;
4049406Sbdestatic	u_int	com_events;	/* input chars + weighted output completions */
405122799Sbdestatic	void	*sio_fast_ih;
406122799Sbdestatic	void	*sio_slow_ih;
4079406Sbdestatic	int	sio_timeout;
4089406Sbdestatic	int	sio_timeouts_until_log;
40929677Sgibbsstatic	struct	callout_handle sio_timeout_handle
41029677Sgibbs    = CALLOUT_HANDLE_INITIALIZER(&sio_timeout_handle);
4116261Sjkh
4126261Sjkh#ifdef CyDebug
4139406Sbdestatic	u_int	cd_inbs;
4149406Sbdestatic	u_int	cy_inbs;
4159406Sbdestatic	u_int	cd_outbs;
4169406Sbdestatic	u_int	cy_outbs;
4179406Sbdestatic	u_int	cy_svrr_probes;
4189406Sbdestatic	u_int	cy_timeouts;
4196261Sjkh#endif
4206261Sjkh
42129047Sbdestatic	int	cy_chip_offset[] = {
42231104Sbde	0x0000, 0x0400, 0x0800, 0x0c00, 0x0200, 0x0600, 0x0a00, 0x0e00,
42329047Sbde};
4249406Sbdestatic	int	cy_nr_cd1400s[NCY];
42518901Sdgstatic	int	cy_total_devices;
4269406Sbde#undef	RxFifoThreshold
4279406Sbdestatic	int	volatile RxFifoThreshold = (CD1400_RX_FIFO_SIZE / 2);
4286261Sjkh
4299406Sbdestatic int
4309406Sbdesioprobe(dev)
4319406Sbde	struct isa_device	*dev;
4329406Sbde{
4339406Sbde	cy_addr	iobase;
4346261Sjkh
435122771Sbde	while (sio_inited != 2)
436122771Sbde		if (atomic_cmpset_int(&sio_inited, 0, 1)) {
437122771Sbde			mtx_init(&sio_lock, driver_name, NULL, MTX_SPIN);
438122771Sbde			atomic_store_rel_int(&sio_inited, 2);
439122771Sbde		}
440122771Sbde
44111424Sdg	iobase = (cy_addr)dev->id_maddr;
4426261Sjkh
4439406Sbde	/* Cyclom-16Y hardware reset (Cyclom-8Ys don't care) */
44441309Sbde	cy_inb(iobase, CY16_RESET, 0);	/* XXX? */
4459406Sbde	DELAY(500);	/* wait for the board to get its act together */
4466261Sjkh
44711424Sdg	/* this is needed to get the board out of reset */
44841309Sbde	cy_outb(iobase, CY_CLEAR_INTR, 0, 0);
44911424Sdg	DELAY(500);
45011424Sdg
45118901Sdg	return (cy_units(iobase, 0) == 0 ? 0 : -1);
45218901Sdg}
45318901Sdg
45418901Sdgstatic int
45518901Sdgcy_units(cy_iobase, cy_align)
45629047Sbde	cy_addr	cy_iobase;
45729047Sbde	int	cy_align;
45818901Sdg{
45929047Sbde	int	cyu;
46029047Sbde	u_char	firmware_version;
46129047Sbde	int	i;
46229047Sbde	cy_addr	iobase;
46318901Sdg
46418679Sdg	for (cyu = 0; cyu < CY_MAX_CD1400s; ++cyu) {
46529047Sbde		iobase = cy_iobase + (cy_chip_offset[cyu] << cy_align);
4666261Sjkh
4679406Sbde		/* wait for chip to become ready for new command */
46811424Sdg		for (i = 0; i < 10; i++) {
4699406Sbde			DELAY(50);
47018901Sdg			if (!cd_inb(iobase, CD1400_CCR, cy_align))
4719406Sbde				break;
4729406Sbde		}
4736261Sjkh
4749406Sbde		/* clear the GFRCR register */
47518901Sdg		cd_outb(iobase, CD1400_GFRCR, cy_align, 0);
4766261Sjkh
4779406Sbde		/* issue a reset command */
47818901Sdg		cd_outb(iobase, CD1400_CCR, cy_align,
4799406Sbde			CD1400_CCR_CMDRESET | CD1400_CCR_FULLRESET);
4806261Sjkh
48191314Sbde		/* XXX bogus initialization to avoid a gcc bug/warning. */
48291314Sbde		firmware_version = 0;
48391314Sbde
4849406Sbde		/* wait for the CD1400 to initialize itself */
48511424Sdg		for (i = 0; i < 200; i++) {
4869406Sbde			DELAY(50);
4879406Sbde
4889406Sbde			/* retrieve firmware version */
48929047Sbde			firmware_version = cd_inb(iobase, CD1400_GFRCR,
49029047Sbde						  cy_align);
49111424Sdg			if ((firmware_version & 0xf0) == 0x40)
4929406Sbde				break;
4939406Sbde		}
4949406Sbde
4959406Sbde		/*
4969406Sbde		 * Anything in the 0x40-0x4F range is fine.
4979406Sbde		 * If one CD1400 is bad then we don't support higher
4989406Sbde		 * numbered good ones on this board.
4999406Sbde		 */
50011424Sdg		if ((firmware_version & 0xf0) != 0x40)
5019406Sbde			break;
5026261Sjkh	}
50318901Sdg	return (cyu);
5046261Sjkh}
5056261Sjkh
5069406Sbdestatic int
5079406Sbdesioattach(isdp)
50829047Sbde	struct isa_device	*isdp;
5096261Sjkh{
510123104Sbde	int		adapter;
511123104Sbde	struct com_s	*com;
5126261Sjkh
513123104Sbde	com = cyattach_common((cy_addr) isdp->id_maddr, 0);
514123104Sbde	if (com == NULL)
5159406Sbde		return (0);
516123104Sbde	adapter = com->unit / CY_MAX_PORTS;
51729047Sbde
51818901Sdg	/*
51918901Sdg	 * XXX
52018901Sdg	 * This kludge is to allow ISA/PCI device specifications in the
52118901Sdg	 * kernel config file to be in any order.
52218901Sdg	 */
52318901Sdg	if (isdp->id_unit != adapter) {
52418901Sdg		printf("cy%d: attached as cy%d\n", isdp->id_unit, adapter);
52529047Sbde		isdp->id_unit = adapter;	/* XXX */
52618901Sdg	}
527123104Sbde
528123104Sbde	isdp->id_ointr = cyointr;
52918901Sdg	return (1);
53018901Sdg}
5316261Sjkh
532123104Sbdevoid *
53318901Sdgcyattach_common(cy_iobase, cy_align)
53429047Sbde	cy_addr	cy_iobase;
53529047Sbde	int	cy_align;
53618901Sdg{
53729047Sbde	int	adapter;
53829047Sbde	int	cyu;
53938303Sbde	u_char	firmware_version;
54029047Sbde	cy_addr	iobase;
54181580Sbde	int	minorbase;
54229047Sbde	int	ncyu;
54329047Sbde	int	unit;
54418901Sdg
54518901Sdg	adapter = cy_total_devices;
54618901Sdg	if ((u_int)adapter >= NCY) {
54729047Sbde		printf(
54829047Sbde	"cy%d: can't attach adapter: insufficient cy devices configured\n",
54929047Sbde		       adapter);
550123104Sbde		return (NULL);
55118901Sdg	}
55218901Sdg	ncyu = cy_units(cy_iobase, cy_align);
55318901Sdg	if (ncyu == 0)
554123104Sbde		return (NULL);
55518901Sdg	cy_nr_cd1400s[adapter] = ncyu;
55618901Sdg	cy_total_devices++;
55718901Sdg
55818901Sdg	unit = adapter * CY_MAX_PORTS;
55918679Sdg	for (cyu = 0; cyu < ncyu; ++cyu) {
5609406Sbde		int	cdu;
5616261Sjkh
56229047Sbde		iobase = (cy_addr) (cy_iobase
56329047Sbde				    + (cy_chip_offset[cyu] << cy_align));
56438303Sbde		firmware_version = cd_inb(iobase, CD1400_GFRCR, cy_align);
56529047Sbde
5669406Sbde		/* Set up a receive timeout period of than 1+ ms. */
56738303Sbde		cd_outb(iobase, CD1400_PPR, cy_align,
56838303Sbde			howmany(CY_CLOCK(firmware_version)
56938303Sbde				/ CD1400_PPR_PRESCALER, 1000));
57038303Sbde
5719406Sbde		for (cdu = 0; cdu < CD1400_NO_OF_CHANNELS; ++cdu, ++unit) {
5729406Sbde			struct com_s	*com;
5739406Sbde			int		s;
5746261Sjkh
57569781Sdwmalone	com = malloc(sizeof *com, M_DEVBUF, M_NOWAIT | M_ZERO);
57629047Sbde	if (com == NULL)
57729047Sbde		break;
57829047Sbde	com->unit = unit;
57938303Sbde			com->gfrcr_image = firmware_version;
58038303Sbde			if (CY_RTS_DTR_SWAPPED(firmware_version)) {
58138303Sbde				com->mcr_dtr = MCR_RTS;
58238303Sbde				com->mcr_rts = MCR_DTR;
58338303Sbde				com->mcr_rts_reg = CD1400_MSVR2;
58438303Sbde			} else {
58538303Sbde				com->mcr_dtr = MCR_DTR;
58638303Sbde				com->mcr_rts = MCR_RTS;
58738303Sbde				com->mcr_rts_reg = CD1400_MSVR1;
58838303Sbde			}
58929047Sbde	com->dtr_wait = 3 * hz;
59029047Sbde	com->obufs[0].l_head = com->obuf1;
59129047Sbde	com->obufs[1].l_head = com->obuf2;
5926261Sjkh
59329047Sbde			com->cy_align = cy_align;
5949406Sbde			com->cy_iobase = cy_iobase;
59529047Sbde	com->iobase = iobase;
59641293Sbde			com->car = ~CD1400_CAR_CHAN;
5976261Sjkh
59829047Sbde	/*
59929047Sbde	 * We don't use all the flags from <sys/ttydefaults.h> since they
60029047Sbde	 * are only relevant for logins.  It's important to have echo off
60129047Sbde	 * initially so that the line doesn't start blathering before the
60229047Sbde	 * echo flag can be turned off.
60329047Sbde	 */
60429047Sbde	com->it_in.c_iflag = 0;
60529047Sbde	com->it_in.c_oflag = 0;
60629047Sbde	com->it_in.c_cflag = TTYDEF_CFLAG;
60729047Sbde	com->it_in.c_lflag = 0;
60829047Sbde	if (unit == comconsole) {
60929047Sbde		com->it_in.c_iflag = TTYDEF_IFLAG;
61029047Sbde		com->it_in.c_oflag = TTYDEF_OFLAG;
61129047Sbde		com->it_in.c_cflag = TTYDEF_CFLAG | CLOCAL;
61229047Sbde		com->it_in.c_lflag = TTYDEF_LFLAG;
61329047Sbde		com->lt_out.c_cflag = com->lt_in.c_cflag = CLOCAL;
61429047Sbde	}
61543611Sbde	if (siosetwater(com, com->it_in.c_ispeed) != 0) {
61643611Sbde		free(com, M_DEVBUF);
617123104Sbde		return (NULL);
61843611Sbde	}
61929047Sbde	termioschars(&com->it_in);
62029047Sbde	com->it_in.c_ispeed = com->it_in.c_ospeed = comdefaultrate;
62129047Sbde	com->it_out = com->it_in;
6226261Sjkh
62329047Sbde	s = spltty();
62429047Sbde	com_addr(unit) = com;
62529047Sbde	splx(s);
62612962Sbde
627122799Sbde	if (sio_fast_ih == NULL) {
62872242Sjhb		swi_add(&tty_ithd, "tty:cy", siopoll, NULL, SWI_TTY, 0,
629122799Sbde			&sio_fast_ih);
630122799Sbde		swi_add(&clk_ithd, "tty:cy", siopoll, NULL, SWI_TTY, 0,
631122799Sbde			&sio_slow_ih);
63238445Sbde	}
63381580Sbde	minorbase = UNIT_TO_MINOR(unit);
63481580Sbde	make_dev(&sio_cdevsw, minorbase,
63537683Sbde		UID_ROOT, GID_WHEEL, 0600, "ttyc%r%r", adapter,
63629047Sbde		unit % CY_MAX_PORTS);
63781580Sbde	make_dev(&sio_cdevsw, minorbase | CONTROL_INIT_STATE,
63837683Sbde		UID_ROOT, GID_WHEEL, 0600, "ttyic%r%r", adapter,
63929047Sbde		unit % CY_MAX_PORTS);
64081580Sbde	make_dev(&sio_cdevsw, minorbase | CONTROL_LOCK_STATE,
64137683Sbde		UID_ROOT, GID_WHEEL, 0600, "ttylc%r%r", adapter,
64229047Sbde		unit % CY_MAX_PORTS);
64381580Sbde	make_dev(&sio_cdevsw, minorbase | CALLOUT_MASK,
64437683Sbde		UID_UUCP, GID_DIALER, 0660, "cuac%r%r", adapter,
64529047Sbde		unit % CY_MAX_PORTS);
64681580Sbde	make_dev(&sio_cdevsw, minorbase | CALLOUT_MASK | CONTROL_INIT_STATE,
64737683Sbde		UID_UUCP, GID_DIALER, 0660, "cuaic%r%r", adapter,
64829047Sbde		unit % CY_MAX_PORTS);
64981580Sbde	make_dev(&sio_cdevsw, minorbase | CALLOUT_MASK | CONTROL_LOCK_STATE,
65037683Sbde		UID_UUCP, GID_DIALER, 0660, "cualc%r%r", adapter,
65129047Sbde		unit % CY_MAX_PORTS);
6529406Sbde		}
6536261Sjkh	}
6546261Sjkh
6559406Sbde	/* ensure an edge for the next interrupt */
65641309Sbde	cy_outb(cy_iobase, CY_CLEAR_INTR, cy_align, 0);
6576261Sjkh
658123104Sbde	return (com_addr(adapter * CY_MAX_PORTS));
6596261Sjkh}
6606261Sjkh
66112675Sjulianstatic int
66283366Sjuliansioopen(dev, flag, mode, td)
6639406Sbde	dev_t		dev;
6649406Sbde	int		flag;
6659406Sbde	int		mode;
66683366Sjulian	struct thread	*td;
6676261Sjkh{
6689406Sbde	struct com_s	*com;
6699406Sbde	int		error;
6709406Sbde	int		mynor;
6719406Sbde	int		s;
6726261Sjkh	struct tty	*tp;
6739406Sbde	int		unit;
6746261Sjkh
6759406Sbde	mynor = minor(dev);
6769406Sbde	unit = MINOR_TO_UNIT(mynor);
6779406Sbde	if ((u_int) unit >= NSIO || (com = com_addr(unit)) == NULL)
6786261Sjkh		return (ENXIO);
6799406Sbde	if (mynor & CONTROL_MASK)
6809406Sbde		return (0);
68181576Sbde	tp = dev->si_tty = com->tp = ttymalloc(com->tp);
6829406Sbde	s = spltty();
6839406Sbde	/*
6849406Sbde	 * We jump to this label after all non-interrupted sleeps to pick
6859406Sbde	 * up any changes of the device state.
6869406Sbde	 */
6879406Sbdeopen_top:
6889406Sbde	while (com->state & CS_DTR_OFF) {
6899406Sbde		error = tsleep(&com->dtr_wait, TTIPRI | PCATCH, "cydtr", 0);
6909406Sbde		if (error != 0)
6919406Sbde			goto out;
6929406Sbde	}
6939406Sbde	if (tp->t_state & TS_ISOPEN) {
6949406Sbde		/*
6959406Sbde		 * The device is open, so everything has been initialized.
6969406Sbde		 * Handle conflicts.
6979406Sbde		 */
6989406Sbde		if (mynor & CALLOUT_MASK) {
6999406Sbde			if (!com->active_out) {
7009406Sbde				error = EBUSY;
7019406Sbde				goto out;
7029406Sbde			}
7039406Sbde		} else {
7049406Sbde			if (com->active_out) {
7059406Sbde				if (flag & O_NONBLOCK) {
7069406Sbde					error = EBUSY;
7079406Sbde					goto out;
7089406Sbde				}
7099406Sbde				error =	tsleep(&com->active_out,
7109406Sbde					       TTIPRI | PCATCH, "cybi", 0);
7119406Sbde				if (error != 0)
7129406Sbde					goto out;
7139406Sbde				goto open_top;
7149406Sbde			}
7156261Sjkh		}
71643425Sphk		if (tp->t_state & TS_XCLUDE &&
71793593Sjhb		    suser(td)) {
7189406Sbde			error = EBUSY;
7199406Sbde			goto out;
7209406Sbde		}
7219406Sbde	} else {
7229406Sbde		/*
7239406Sbde		 * The device isn't open, so there are no conflicts.
7249406Sbde		 * Initialize it.  Initialization is done twice in many
7259406Sbde		 * cases: to preempt sleeping callin opens if we are
7269406Sbde		 * callout, and to complete a callin open after DCD rises.
7279406Sbde		 */
7289406Sbde		tp->t_oproc = comstart;
72951654Sphk		tp->t_stop = comstop;
7309406Sbde		tp->t_param = comparam;
7319406Sbde		tp->t_dev = dev;
7329406Sbde		tp->t_termios = mynor & CALLOUT_MASK
7339406Sbde				? com->it_out : com->it_in;
73442045Sbde
73542045Sbde		/* Encode per-board unit in LIVR for access in intr routines. */
73642045Sbde		cd_setreg(com, CD1400_LIVR,
73742045Sbde			  (unit & CD1400_xIVR_CHAN) << CD1400_xIVR_CHAN_SHIFT);
73842045Sbde
73942045Sbde		(void)commctl(com, TIOCM_DTR | TIOCM_RTS, DMSET);
7409406Sbde#if 0
7419406Sbde		com->poll = com->no_irq;
7429406Sbde		com->poll_output = com->loses_outints;
7439406Sbde#endif
7449406Sbde		++com->wopeners;
7459406Sbde		error = comparam(tp, &tp->t_termios);
7469406Sbde		--com->wopeners;
7479406Sbde		if (error != 0)
7489406Sbde			goto out;
7499406Sbde#if 0
7509406Sbde		if (com->hasfifo) {
7519406Sbde			/*
75242045Sbde			 * (Re)enable and flush fifos.
7539406Sbde			 *
7549406Sbde			 * Certain SMC chips cause problems if the fifos
7559406Sbde			 * are enabled while input is ready.  Turn off the
7569406Sbde			 * fifo if necessary to clear the input.  We test
7579406Sbde			 * the input ready bit after enabling the fifos
7589406Sbde			 * since we've already enabled them in comparam()
7599406Sbde			 * and to handle races between enabling and fresh
7609406Sbde			 * input.
7619406Sbde			 */
7629406Sbde			while (TRUE) {
7639406Sbde				outb(iobase + com_fifo,
7649406Sbde				     FIFO_RCV_RST | FIFO_XMT_RST
76512962Sbde				     | com->fifo_image);
7669406Sbde				DELAY(100);
7679406Sbde				if (!(inb(com->line_status_port) & LSR_RXRDY))
7689406Sbde					break;
7699406Sbde				outb(iobase + com_fifo, 0);
7709406Sbde				DELAY(100);
7719406Sbde				(void) inb(com->data_port);
7729406Sbde			}
7739406Sbde		}
7746261Sjkh
77588088Sjhb		critical_enter();
77672262Sjhb		COM_LOCK();
7779406Sbde		(void) inb(com->line_status_port);
7789406Sbde		(void) inb(com->data_port);
7799406Sbde		com->prev_modem_status = com->last_modem_status
7809406Sbde		    = inb(com->modem_status_port);
7819406Sbde		outb(iobase + com_ier, IER_ERXRDY | IER_ETXRDY | IER_ERLS
7829406Sbde				       | IER_EMSC);
78372262Sjhb		COM_UNLOCK();
78488088Sjhb		critical_exit();
7859406Sbde#else /* !0 */
78642045Sbde		/*
78742045Sbde		 * Flush fifos.  This requires a full channel reset which
78842045Sbde		 * also disables the transmitter and receiver.  Recover
78942045Sbde		 * from this.
79042045Sbde		 */
79142045Sbde		cd1400_channel_cmd(com,
79242045Sbde				   CD1400_CCR_CMDRESET | CD1400_CCR_CHANRESET);
79342045Sbde		cd1400_channel_cmd(com, com->channel_control);
79442045Sbde
79588088Sjhb		critical_enter();
79672262Sjhb		COM_LOCK();
7979406Sbde		com->prev_modem_status = com->last_modem_status
79841293Sbde		    = cd_getreg(com, CD1400_MSVR2);
79941293Sbde		cd_setreg(com, CD1400_SRER,
80041293Sbde			  com->intr_enable
80141293Sbde			  = CD1400_SRER_MDMCH | CD1400_SRER_RXDATA);
80272262Sjhb		COM_UNLOCK();
80388088Sjhb		critical_exit();
8049406Sbde#endif /* 0 */
8059406Sbde		/*
8069406Sbde		 * Handle initial DCD.  Callout devices get a fake initial
8079406Sbde		 * DCD (trapdoor DCD).  If we are callout, then any sleeping
8089406Sbde		 * callin opens get woken up and resume sleeping on "cybi"
8099406Sbde		 * instead of "cydcd".
8109406Sbde		 */
8119406Sbde		/*
8129406Sbde		 * XXX `mynor & CALLOUT_MASK' should be
8139406Sbde		 * `tp->t_cflag & (SOFT_CARRIER | TRAPDOOR_CARRIER) where
8149406Sbde		 * TRAPDOOR_CARRIER is the default initial state for callout
8159406Sbde		 * devices and SOFT_CARRIER is like CLOCAL except it hides
8169406Sbde		 * the true carrier.
8179406Sbde		 */
8189406Sbde		if (com->prev_modem_status & MSR_DCD || mynor & CALLOUT_MASK)
8199406Sbde			(*linesw[tp->t_line].l_modem)(tp, 1);
8209406Sbde	}
8219406Sbde	/*
8229406Sbde	 * Wait for DCD if necessary.
8239406Sbde	 */
8249406Sbde	if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK)
8259406Sbde	    && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) {
8269406Sbde		++com->wopeners;
8279406Sbde		error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "cydcd", 0);
8289406Sbde		--com->wopeners;
8299406Sbde		if (error != 0)
8309406Sbde			goto out;
8319406Sbde		goto open_top;
8329406Sbde	}
8339406Sbde	error =	(*linesw[tp->t_line].l_open)(dev, tp);
8349406Sbde	disc_optim(tp, &tp->t_termios, com);
8359406Sbde	if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK)
8369406Sbde		com->active_out = TRUE;
8379406Sbde	siosettimeout();
8389406Sbdeout:
8399406Sbde	splx(s);
8409406Sbde	if (!(tp->t_state & TS_ISOPEN) && com->wopeners == 0)
8419406Sbde		comhardclose(com);
8426261Sjkh	return (error);
8439406Sbde}
8446261Sjkh
84512675Sjulianstatic int
84683366Sjuliansioclose(dev, flag, mode, td)
8479406Sbde	dev_t		dev;
8489406Sbde	int		flag;
8499406Sbde	int		mode;
85083366Sjulian	struct thread	*td;
8516261Sjkh{
8529406Sbde	struct com_s	*com;
8539406Sbde	int		mynor;
8546261Sjkh	int		s;
8559406Sbde	struct tty	*tp;
8566261Sjkh
8579406Sbde	mynor = minor(dev);
8589406Sbde	if (mynor & CONTROL_MASK)
8599406Sbde		return (0);
8609406Sbde	com = com_addr(MINOR_TO_UNIT(mynor));
8619406Sbde	tp = com->tp;
8626261Sjkh	s = spltty();
86341903Sbde	cd_etc(com, CD1400_ETC_STOPBREAK);
8649406Sbde	(*linesw[tp->t_line].l_close)(tp, flag);
8659406Sbde	disc_optim(tp, &tp->t_termios, com);
86651654Sphk	comstop(tp, FREAD | FWRITE);
8679406Sbde	comhardclose(com);
8689406Sbde	ttyclose(tp);
8699406Sbde	siosettimeout();
8706261Sjkh	splx(s);
8716261Sjkh#ifdef broken /* session holds a ref to the tty; can't deallocate */
8726261Sjkh	ttyfree(tp);
87381576Sbde	com->tp = NULL;
8746261Sjkh#endif
8759406Sbde	return (0);
8769406Sbde}
8776261Sjkh
8789406Sbdestatic void
8799406Sbdecomhardclose(com)
8809406Sbde	struct com_s	*com;
8819406Sbde{
8829406Sbde	cy_addr		iobase;
8839406Sbde	int		s;
8849406Sbde	struct tty	*tp;
8859406Sbde	int		unit;
8866261Sjkh
8879406Sbde	unit = com->unit;
8889406Sbde	iobase = com->iobase;
8899406Sbde	s = spltty();
8909406Sbde#if 0
8919406Sbde	com->poll = FALSE;
8929406Sbde	com->poll_output = FALSE;
8939406Sbde#endif
8949406Sbde	com->do_timestamp = 0;
8959406Sbde#if 0
8969406Sbde	outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK);
89741903Sbde#else
89841903Sbde	/* XXX */
89988088Sjhb	critical_enter();
90072262Sjhb	COM_LOCK();
90141903Sbde	com->etc = ETC_NONE;
90241903Sbde	cd_setreg(com, CD1400_COR2, com->cor[1] &= ~CD1400_COR2_ETC);
90372262Sjhb	COM_UNLOCK();
90488088Sjhb	critical_exit();
90541903Sbde	cd1400_channel_cmd(com, CD1400_CCR_CMDRESET | CD1400_CCR_FTF);
9069406Sbde#endif
9076261Sjkh
9089406Sbde	{
9099406Sbde#if 0
9109406Sbde		outb(iobase + com_ier, 0);
9119406Sbde#else
91288088Sjhb		critical_enter();
91372262Sjhb		COM_LOCK();
91441293Sbde		cd_setreg(com, CD1400_SRER, com->intr_enable = 0);
91572262Sjhb		COM_UNLOCK();
91688088Sjhb		critical_exit();
9179406Sbde#endif
9189406Sbde		tp = com->tp;
91943314Sdillon		if ((tp->t_cflag & HUPCL)
9209406Sbde		    /*
9219406Sbde		     * XXX we will miss any carrier drop between here and the
9229406Sbde		     * next open.  Perhaps we should watch DCD even when the
9239406Sbde		     * port is closed; it is not sufficient to check it at
9249406Sbde		     * the next open because it might go up and down while
9259406Sbde		     * we're not watching.
9269406Sbde		     */
92743314Sdillon		    || (!com->active_out
9289406Sbde		       && !(com->prev_modem_status & MSR_DCD)
92943314Sdillon		       && !(com->it_in.c_cflag & CLOCAL))
9309406Sbde		    || !(tp->t_state & TS_ISOPEN)) {
9319406Sbde			(void)commctl(com, TIOCM_DTR, DMBIC);
9326261Sjkh
9339406Sbde			/* Disable receiver (leave transmitter enabled). */
9349406Sbde			com->channel_control = CD1400_CCR_CMDCHANCTL
9359406Sbde					       | CD1400_CCR_XMTEN
9369406Sbde					       | CD1400_CCR_RCVDIS;
93741293Sbde			cd1400_channel_cmd(com, com->channel_control);
9386261Sjkh
93941385Sbde			if (com->dtr_wait != 0 && !(com->state & CS_DTR_OFF)) {
9409406Sbde				timeout(siodtrwakeup, com, com->dtr_wait);
9419406Sbde				com->state |= CS_DTR_OFF;
9429406Sbde			}
9439406Sbde		}
9449406Sbde	}
94512962Sbde#if 0
94612962Sbde	if (com->hasfifo) {
94712962Sbde		/*
94812962Sbde		 * Disable fifos so that they are off after controlled
94912962Sbde		 * reboots.  Some BIOSes fail to detect 16550s when the
95012962Sbde		 * fifos are enabled.
95112962Sbde		 */
95212962Sbde		outb(iobase + com_fifo, 0);
95312962Sbde	}
95412962Sbde#endif
9559406Sbde	com->active_out = FALSE;
9569406Sbde	wakeup(&com->active_out);
9579406Sbde	wakeup(TSA_CARR_ON(tp));	/* restart any wopeners */
9589406Sbde	splx(s);
9599406Sbde}
9609406Sbde
96112675Sjulianstatic int
9629406Sbdesiowrite(dev, uio, flag)
9639406Sbde	dev_t		dev;
9649406Sbde	struct uio	*uio;
9659406Sbde	int		flag;
9666261Sjkh{
9679406Sbde	int		mynor;
9689406Sbde	struct tty	*tp;
9699406Sbde	int		unit;
9706261Sjkh
9719406Sbde	mynor = minor(dev);
9729406Sbde	if (mynor & CONTROL_MASK)
9739406Sbde		return (ENODEV);
9749406Sbde
9759406Sbde	unit = MINOR_TO_UNIT(mynor);
9769406Sbde	tp = com_addr(unit)->tp;
9779406Sbde	/*
9789406Sbde	 * (XXX) We disallow virtual consoles if the physical console is
9799406Sbde	 * a serial port.  This is in case there is a display attached that
9809406Sbde	 * is not the console.  In that situation we don't need/want the X
9819406Sbde	 * server taking over the console.
9829406Sbde	 */
98311671Sbde	if (constty != NULL && unit == comconsole)
9849406Sbde		constty = NULL;
9856261Sjkh#ifdef Smarts
9866261Sjkh	/* XXX duplicate ttwrite(), but without so much output processing on
9876261Sjkh	 * CR & LF chars.  Hardly worth the effort, given that high-throughput
9886261Sjkh	 * sessions are raw anyhow.
9896261Sjkh	 */
9906261Sjkh#else
9919406Sbde	return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
9926261Sjkh#endif
9939406Sbde}
9946261Sjkh
9956261Sjkhstatic void
9969406Sbdesiodtrwakeup(chan)
9979406Sbde	void	*chan;
9986261Sjkh{
9999406Sbde	struct com_s	*com;
10006261Sjkh
10019406Sbde	com = (struct com_s *)chan;
10029406Sbde	com->state &= ~CS_DTR_OFF;
10039406Sbde	wakeup(&com->dtr_wait);
10049406Sbde}
10056261Sjkh
100665557Sjasone/*
100772262Sjhb * This function:
100872262Sjhb *  a) needs to be called with COM_LOCK() held, and
100972262Sjhb *  b) needs to return with COM_LOCK() held.
101065557Sjasone */
101143611Sbdestatic void
101288088Sjhbsioinput(com)
101343611Sbde	struct com_s	*com;
101443611Sbde{
101543611Sbde	u_char		*buf;
101643611Sbde	int		incc;
101743611Sbde	u_char		line_status;
101843611Sbde	int		recv_data;
101943611Sbde	struct tty	*tp;
102043611Sbde
102143611Sbde	buf = com->ibuf;
102243611Sbde	tp = com->tp;
102343611Sbde	if (!(tp->t_state & TS_ISOPEN)) {
102443611Sbde		com_events -= (com->iptr - com->ibuf);
102543611Sbde		com->iptr = com->ibuf;
102643611Sbde		return;
102743611Sbde	}
102843611Sbde	if (tp->t_state & TS_CAN_BYPASS_L_RINT) {
102943611Sbde		/*
103043611Sbde		 * Avoid the grotesquely inefficient lineswitch routine
103143611Sbde		 * (ttyinput) in "raw" mode.  It usually takes about 450
103243611Sbde		 * instructions (that's without canonical processing or echo!).
103343611Sbde		 * slinput is reasonably fast (usually 40 instructions plus
103443611Sbde		 * call overhead).
103543611Sbde		 */
103665557Sjasone
103743611Sbde		do {
103865557Sjasone			/*
103965557Sjasone			 * This may look odd, but it is using save-and-enable
104065557Sjasone			 * semantics instead of the save-and-disable semantics
104165557Sjasone			 * that are used everywhere else.
104265557Sjasone			 */
104372262Sjhb			COM_UNLOCK();
104488088Sjhb			critical_exit();
104543611Sbde			incc = com->iptr - buf;
104643611Sbde			if (tp->t_rawq.c_cc + incc > tp->t_ihiwat
104743611Sbde			    && (com->state & CS_RTS_IFLOW
104843611Sbde				|| tp->t_iflag & IXOFF)
104943611Sbde			    && !(tp->t_state & TS_TBLOCK))
105043611Sbde				ttyblock(tp);
105143611Sbde			com->delta_error_counts[CE_TTY_BUF_OVERFLOW]
105243611Sbde				+= b_to_q((char *)buf, incc, &tp->t_rawq);
105343611Sbde			buf += incc;
105443611Sbde			tk_nin += incc;
105543611Sbde			tk_rawcc += incc;
105643611Sbde			tp->t_rawcc += incc;
105743611Sbde			ttwakeup(tp);
105843611Sbde			if (tp->t_state & TS_TTSTOP
105943611Sbde			    && (tp->t_iflag & IXANY
106043611Sbde				|| tp->t_cc[VSTART] == tp->t_cc[VSTOP])) {
106143611Sbde				tp->t_state &= ~TS_TTSTOP;
106243611Sbde				tp->t_lflag &= ~FLUSHO;
106343611Sbde				comstart(tp);
106443611Sbde			}
106588088Sjhb			critical_enter();
106672262Sjhb			COM_LOCK();
106743611Sbde		} while (buf < com->iptr);
106843611Sbde	} else {
106943611Sbde		do {
107065557Sjasone			/*
107165557Sjasone			 * This may look odd, but it is using save-and-enable
107265557Sjasone			 * semantics instead of the save-and-disable semantics
107365557Sjasone			 * that are used everywhere else.
107465557Sjasone			 */
107572262Sjhb			COM_UNLOCK();
107688088Sjhb			critical_exit();
107743611Sbde			line_status = buf[com->ierroff];
107843611Sbde			recv_data = *buf++;
107943611Sbde			if (line_status
108043611Sbde			    & (LSR_BI | LSR_FE | LSR_OE | LSR_PE)) {
108143611Sbde				if (line_status & LSR_BI)
108243611Sbde					recv_data |= TTY_BI;
108343611Sbde				if (line_status & LSR_FE)
108443611Sbde					recv_data |= TTY_FE;
108543611Sbde				if (line_status & LSR_OE)
108643611Sbde					recv_data |= TTY_OE;
108743611Sbde				if (line_status & LSR_PE)
108843611Sbde					recv_data |= TTY_PE;
108943611Sbde			}
109043611Sbde			(*linesw[tp->t_line].l_rint)(recv_data, tp);
109188088Sjhb			critical_enter();
109272262Sjhb			COM_LOCK();
109343611Sbde		} while (buf < com->iptr);
109443611Sbde	}
109543611Sbde	com_events -= (com->iptr - com->ibuf);
109643611Sbde	com->iptr = com->ibuf;
109743611Sbde
109843611Sbde	/*
109943611Sbde	 * There is now room for another low-level buffer full of input,
110043611Sbde	 * so enable RTS if it is now disabled and there is room in the
110143611Sbde	 * high-level buffer.
110243611Sbde	 */
110343611Sbde	if ((com->state & CS_RTS_IFLOW) && !(com->mcr_image & com->mcr_rts) &&
110443611Sbde	    !(tp->t_state & TS_TBLOCK))
110543611Sbde#if 0
110643611Sbde		outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS);
110743611Sbde#else
110843611Sbde		cd_setreg(com, com->mcr_rts_reg,
110943611Sbde			  com->mcr_image |= com->mcr_rts);
111043611Sbde#endif
111143611Sbde}
111243611Sbde
1113123104Sbdestatic void
1114123104Sbdecyointr(int unit)
1115123104Sbde{
1116123104Sbde	siointr1(com_addr(unit * CY_MAX_PORTS));
1117123104Sbde}
1118123104Sbde
11199406Sbdevoid
1120123104Sbdesiointr1(vcom)
1121123104Sbde	void	*vcom;
11226261Sjkh{
1123123104Sbde	struct com_s	*basecom;
112429047Sbde	int	baseu;
112529047Sbde	int	cy_align;
112629047Sbde	cy_addr	cy_iobase;
112729047Sbde	int	cyu;
112829047Sbde	cy_addr	iobase;
112929047Sbde	u_char	status;
1130123104Sbde	int	unit;
11316261Sjkh
113272262Sjhb	COM_LOCK();	/* XXX could this be placed down lower in the loop? */
113328921Sfsmp
1134123104Sbde	basecom = (struct com_s *)vcom;
1135123104Sbde	baseu = basecom->unit;
1136123104Sbde	cy_align = basecom->cy_align;
1137123104Sbde	cy_iobase = basecom->cy_iobase;
1138123104Sbde	unit = baseu / CY_MAX_PORTS;
11396261Sjkh
11409406Sbde	/* check each CD1400 in turn */
114118685Sdg	for (cyu = 0; cyu < cy_nr_cd1400s[unit]; ++cyu) {
114229047Sbde		iobase = (cy_addr) (cy_iobase
114329047Sbde				    + (cy_chip_offset[cyu] << cy_align));
11449406Sbde		/* poll to see if it has any work */
114518901Sdg		status = cd_inb(iobase, CD1400_SVRR, cy_align);
11469406Sbde		if (status == 0)
11479406Sbde			continue;
11486261Sjkh#ifdef CyDebug
11499406Sbde		++cy_svrr_probes;
11506261Sjkh#endif
11519406Sbde		/* service requests as appropriate, giving priority to RX */
11529406Sbde		if (status & CD1400_SVRR_RXRDY) {
11539406Sbde			struct com_s	*com;
11549406Sbde			u_int		count;
11559406Sbde			u_char		*ioptr;
11569406Sbde			u_char		line_status;
11579406Sbde			u_char		recv_data;
11589406Sbde			u_char		serv_type;
11599406Sbde#ifdef PollMode
11609406Sbde			u_char		save_rir;
11616261Sjkh#endif
11626261Sjkh
11639406Sbde#ifdef PollMode
116418901Sdg			save_rir = cd_inb(iobase, CD1400_RIR, cy_align);
11656261Sjkh
11669406Sbde			/* enter rx service */
116718901Sdg			cd_outb(iobase, CD1400_CAR, cy_align, save_rir);
116841293Sbde			com_addr(baseu + cyu * CD1400_NO_OF_CHANNELS)->car
116941293Sbde			= save_rir & CD1400_CAR_CHAN;
11706261Sjkh
117118901Sdg			serv_type = cd_inb(iobase, CD1400_RIVR, cy_align);
11729406Sbde			com = com_addr(baseu
11739406Sbde				       + ((serv_type >> CD1400_xIVR_CHAN_SHIFT)
11749406Sbde					  & CD1400_xIVR_CHAN));
11756261Sjkh#else
11769406Sbde			/* ack receive service */
117741309Sbde			serv_type = cy_inb(iobase, CY8_SVCACKR, cy_align);
11786261Sjkh
11799406Sbde			com = com_addr(baseu +
11809406Sbde				       + ((serv_type >> CD1400_xIVR_CHAN_SHIFT)
11819406Sbde					  & CD1400_xIVR_CHAN));
11826261Sjkh#endif
11836261Sjkh
11849406Sbde		if (serv_type & CD1400_RIVR_EXCEPTION) {
11859406Sbde			++com->recv_exception;
118618901Sdg			line_status = cd_inb(iobase, CD1400_RDSR, cy_align);
11879406Sbde			/* break/unnattached error bits or real input? */
118818901Sdg			recv_data = cd_inb(iobase, CD1400_RDSR, cy_align);
11899406Sbde#ifndef SOFT_HOTCHAR
11909406Sbde			if (line_status & CD1400_RDSR_SPECIAL
11919406Sbde			    && com->hotchar != 0)
1192122799Sbde				swi_sched(sio_fast_ih, 0);
119367551Sjhb
11946261Sjkh#endif
11959406Sbde#if 1 /* XXX "intelligent" PFO error handling would break O error handling */
11969406Sbde			if (line_status & (LSR_PE|LSR_FE|LSR_BI)) {
11979406Sbde				/*
11989406Sbde				  Don't store PE if IGNPAR and BI if IGNBRK,
11999406Sbde				  this hack allows "raw" tty optimization
12009406Sbde				  works even if IGN* is set.
12019406Sbde				*/
12029406Sbde				if (   com->tp == NULL
12039406Sbde				    || !(com->tp->t_state & TS_ISOPEN)
120443314Sdillon				    || ((line_status & (LSR_PE|LSR_FE))
120543314Sdillon				    &&  (com->tp->t_iflag & IGNPAR))
120643314Sdillon				    || ((line_status & LSR_BI)
120743314Sdillon				    &&  (com->tp->t_iflag & IGNBRK)))
12089406Sbde					goto cont;
12099406Sbde				if (   (line_status & (LSR_PE|LSR_FE))
12109406Sbde				    && (com->tp->t_state & TS_CAN_BYPASS_L_RINT)
12119406Sbde				    && ((line_status & LSR_FE)
121243314Sdillon				    ||  ((line_status & LSR_PE)
121343314Sdillon				    &&  (com->tp->t_iflag & INPCK))))
12149406Sbde					recv_data = 0;
12159406Sbde			}
12169406Sbde#endif /* 1 */
12179406Sbde			++com->bytes_in;
12189406Sbde#ifdef SOFT_HOTCHAR
12199406Sbde			if (com->hotchar != 0 && recv_data == com->hotchar)
1220122799Sbde				swi_sched(sio_fast_ih, 0);
12216261Sjkh#endif
12229406Sbde			ioptr = com->iptr;
12239406Sbde			if (ioptr >= com->ibufend)
12249406Sbde				CE_RECORD(com, CE_INTERRUPT_BUF_OVERFLOW);
12259406Sbde			else {
122617354Sbde				if (com->do_timestamp)
122717354Sbde					microtime(&com->timestamp);
12289406Sbde				++com_events;
12299406Sbde				ioptr[0] = recv_data;
123043611Sbde				ioptr[com->ierroff] = line_status;
12319406Sbde				com->iptr = ++ioptr;
12329406Sbde				if (ioptr == com->ihighwater
123338303Sbde				    && com->state & CS_RTS_IFLOW)
12349406Sbde#if 0
12359406Sbde					outb(com->modem_ctl_port,
12369406Sbde					     com->mcr_image &= ~MCR_RTS);
12379406Sbde#else
123838303Sbde					cd_outb(iobase, com->mcr_rts_reg,
123938303Sbde						cy_align,
124038303Sbde						com->mcr_image &=
124138303Sbde						~com->mcr_rts);
12426261Sjkh#endif
12439406Sbde				if (line_status & LSR_OE)
12449406Sbde					CE_RECORD(com, CE_OVERRUN);
12459406Sbde			}
12469406Sbde			goto cont;
12479406Sbde		} else {
12489406Sbde			int	ifree;
12496261Sjkh
125018901Sdg			count = cd_inb(iobase, CD1400_RDCR, cy_align);
125134661Sdg			if (!count)
125234661Sdg				goto cont;
12539406Sbde			com->bytes_in += count;
12549406Sbde			ioptr = com->iptr;
12559406Sbde			ifree = com->ibufend - ioptr;
12569406Sbde			if (count > ifree) {
12579406Sbde				count -= ifree;
12589406Sbde				com_events += ifree;
125917354Sbde				if (ifree != 0) {
126017354Sbde					if (com->do_timestamp)
126117354Sbde						microtime(&com->timestamp);
126217354Sbde					do {
126317354Sbde						recv_data = cd_inb(iobase,
126429047Sbde								   CD1400_RDSR,
126529047Sbde								   cy_align);
12669406Sbde#ifdef SOFT_HOTCHAR
126717354Sbde						if (com->hotchar != 0
126817354Sbde						    && recv_data
126917354Sbde						       == com->hotchar)
1270122799Sbde							swi_sched(sio_fast_ih,
1271122799Sbde								  0);
12726261Sjkh#endif
127317354Sbde						ioptr[0] = recv_data;
127443611Sbde						ioptr[com->ierroff] = 0;
127517354Sbde						++ioptr;
127617354Sbde					} while (--ifree != 0);
12779406Sbde				}
12789406Sbde				com->delta_error_counts
12799406Sbde				    [CE_INTERRUPT_BUF_OVERFLOW] += count;
12809406Sbde				do {
128129047Sbde					recv_data = cd_inb(iobase, CD1400_RDSR,
128229047Sbde							   cy_align);
12839406Sbde#ifdef SOFT_HOTCHAR
12849406Sbde					if (com->hotchar != 0
12859406Sbde					    && recv_data == com->hotchar)
1286122799Sbde						swi_sched(sio_fast_ih, 0);
12876261Sjkh#endif
12889406Sbde				} while (--count != 0);
12899406Sbde			} else {
129017354Sbde				if (com->do_timestamp)
129117354Sbde					microtime(&com->timestamp);
12929406Sbde				if (ioptr <= com->ihighwater
12939406Sbde				    && ioptr + count > com->ihighwater
12949406Sbde				    && com->state & CS_RTS_IFLOW)
12959406Sbde#if 0
12969406Sbde					outb(com->modem_ctl_port,
12979406Sbde					     com->mcr_image &= ~MCR_RTS);
12986261Sjkh#else
129938303Sbde					cd_outb(iobase, com->mcr_rts_reg,
130038303Sbde						cy_align,
130138303Sbde						com->mcr_image
130238303Sbde						&= ~com->mcr_rts);
13036261Sjkh#endif
13049406Sbde				com_events += count;
13059406Sbde				do {
130629047Sbde					recv_data = cd_inb(iobase, CD1400_RDSR,
130729047Sbde							   cy_align);
13089406Sbde#ifdef SOFT_HOTCHAR
13099406Sbde					if (com->hotchar != 0
13109406Sbde					    && recv_data == com->hotchar)
1311122799Sbde						swi_sched(sio_fast_ih, 0);
13126261Sjkh#endif
13139406Sbde					ioptr[0] = recv_data;
131443611Sbde					ioptr[com->ierroff] = 0;
13159406Sbde					++ioptr;
13169406Sbde				} while (--count != 0);
13176261Sjkh			}
13189406Sbde			com->iptr = ioptr;
13196261Sjkh		}
13209406Sbdecont:
13216261Sjkh
13229406Sbde			/* terminate service context */
13236261Sjkh#ifdef PollMode
132418901Sdg			cd_outb(iobase, CD1400_RIR, cy_align,
13259406Sbde				save_rir
13269406Sbde				& ~(CD1400_RIR_RDIREQ | CD1400_RIR_RBUSY));
13276261Sjkh#else
132818901Sdg			cd_outb(iobase, CD1400_EOSRR, cy_align, 0);
13296261Sjkh#endif
13309406Sbde		}
13319406Sbde		if (status & CD1400_SVRR_MDMCH) {
13329406Sbde			struct com_s	*com;
13339406Sbde			u_char	modem_status;
13346261Sjkh#ifdef PollMode
13359406Sbde			u_char	save_mir;
13366261Sjkh#else
13379406Sbde			u_char	vector;
13386261Sjkh#endif
13396261Sjkh
13406261Sjkh#ifdef PollMode
134118901Sdg			save_mir = cd_inb(iobase, CD1400_MIR, cy_align);
13426261Sjkh
13439406Sbde			/* enter modem service */
134418901Sdg			cd_outb(iobase, CD1400_CAR, cy_align, save_mir);
134541293Sbde			com_addr(baseu + cyu * CD1400_NO_OF_CHANNELS)->car
134641293Sbde			= save_mir & CD1400_CAR_CHAN;
13476261Sjkh
13489406Sbde			com = com_addr(baseu + cyu * CD1400_NO_OF_CHANNELS
13499406Sbde				       + (save_mir & CD1400_MIR_CHAN));
13506261Sjkh#else
13519406Sbde			/* ack modem service */
135241309Sbde			vector = cy_inb(iobase, CY8_SVCACKM, cy_align);
13536261Sjkh
13549406Sbde			com = com_addr(baseu
13559406Sbde				       + ((vector >> CD1400_xIVR_CHAN_SHIFT)
13569406Sbde					  & CD1400_xIVR_CHAN));
13579406Sbde#endif
13589406Sbde			++com->mdm;
135918901Sdg			modem_status = cd_inb(iobase, CD1400_MSVR2, cy_align);
13609406Sbde		if (modem_status != com->last_modem_status) {
136117354Sbde			if (com->do_dcd_timestamp
136217354Sbde			    && !(com->last_modem_status & MSR_DCD)
136317354Sbde			    && modem_status & MSR_DCD)
136417354Sbde				microtime(&com->dcd_timestamp);
136517354Sbde
13669406Sbde			/*
13679406Sbde			 * Schedule high level to handle DCD changes.  Note
13689406Sbde			 * that we don't use the delta bits anywhere.  Some
13699406Sbde			 * UARTs mess them up, and it's easy to remember the
13709406Sbde			 * previous bits and calculate the delta.
13719406Sbde			 */
13729406Sbde			com->last_modem_status = modem_status;
13739406Sbde			if (!(com->state & CS_CHECKMSR)) {
13749406Sbde				com_events += LOTS_OF_EVENTS;
13759406Sbde				com->state |= CS_CHECKMSR;
1376122799Sbde				swi_sched(sio_fast_ih, 0);
13779406Sbde			}
13786261Sjkh
13799406Sbde#ifdef SOFT_CTS_OFLOW
13809406Sbde			/* handle CTS change immediately for crisp flow ctl */
13819406Sbde			if (com->state & CS_CTS_OFLOW) {
13829406Sbde				if (modem_status & MSR_CTS) {
13839406Sbde					com->state |= CS_ODEVREADY;
13849406Sbde					if (com->state >= (CS_BUSY | CS_TTGO
13859406Sbde							   | CS_ODEVREADY)
13869406Sbde					    && !(com->intr_enable
13879406Sbde						 & CD1400_SRER_TXRDY))
138829047Sbde						cd_outb(iobase, CD1400_SRER,
138929047Sbde							cy_align,
13909406Sbde							com->intr_enable
139141388Sbde							= com->intr_enable
139241388Sbde							  & ~CD1400_SRER_TXMPTY
139341388Sbde							  | CD1400_SRER_TXRDY);
13949406Sbde				} else {
13959406Sbde					com->state &= ~CS_ODEVREADY;
139629047Sbde					if (com->intr_enable
139729047Sbde					    & CD1400_SRER_TXRDY)
139829047Sbde						cd_outb(iobase, CD1400_SRER,
139929047Sbde							cy_align,
14009406Sbde							com->intr_enable
140141388Sbde							= com->intr_enable
140241388Sbde							  & ~CD1400_SRER_TXRDY
140341388Sbde							  | CD1400_SRER_TXMPTY);
14049406Sbde				}
14059406Sbde			}
14066261Sjkh#endif
14079406Sbde		}
14086261Sjkh
14099406Sbde			/* terminate service context */
14106261Sjkh#ifdef PollMode
141118901Sdg			cd_outb(iobase, CD1400_MIR, cy_align,
14129406Sbde				save_mir
14139406Sbde				& ~(CD1400_MIR_RDIREQ | CD1400_MIR_RBUSY));
14146261Sjkh#else
141518901Sdg			cd_outb(iobase, CD1400_EOSRR, cy_align, 0);
14166261Sjkh#endif
14179406Sbde		}
14189406Sbde		if (status & CD1400_SVRR_TXRDY) {
14199406Sbde			struct com_s	*com;
14206261Sjkh#ifdef PollMode
14219406Sbde			u_char	save_tir;
14226261Sjkh#else
14239406Sbde			u_char	vector;
14246261Sjkh#endif
14256261Sjkh
14266261Sjkh#ifdef PollMode
142718901Sdg			save_tir = cd_inb(iobase, CD1400_TIR, cy_align);
14289406Sbde
14299406Sbde			/* enter tx service */
143018901Sdg			cd_outb(iobase, CD1400_CAR, cy_align, save_tir);
143141293Sbde			com_addr(baseu + cyu * CD1400_NO_OF_CHANNELS)->car
143241293Sbde			= save_tir & CD1400_CAR_CHAN;
143341293Sbde
14349406Sbde			com = com_addr(baseu
14359406Sbde				       + cyu * CD1400_NO_OF_CHANNELS
14369406Sbde				       + (save_tir & CD1400_TIR_CHAN));
14376261Sjkh#else
14389406Sbde			/* ack transmit service */
143941309Sbde			vector = cy_inb(iobase, CY8_SVCACKT, cy_align);
14406261Sjkh
14419406Sbde			com = com_addr(baseu
14429406Sbde				       + ((vector >> CD1400_xIVR_CHAN_SHIFT)
14439406Sbde					  & CD1400_xIVR_CHAN));
14446261Sjkh#endif
14456261Sjkh
144641903Sbde			if (com->etc != ETC_NONE) {
144741903Sbde				if (com->intr_enable & CD1400_SRER_TXRDY) {
144841903Sbde					/*
144941903Sbde					 * Here due to sloppy SRER_TXRDY
145041903Sbde					 * enabling.  Ignore.  Come back when
145141903Sbde					 * tx is empty.
145241903Sbde					 */
145341903Sbde					cd_outb(iobase, CD1400_SRER, cy_align,
145441903Sbde						com->intr_enable
145543314Sdillon						= (com->intr_enable
145643314Sdillon						  & ~CD1400_SRER_TXRDY)
145741903Sbde						  | CD1400_SRER_TXMPTY);
145841903Sbde					goto terminate_tx_service;
145941903Sbde				}
146041903Sbde				switch (com->etc) {
146141903Sbde				case CD1400_ETC_SENDBREAK:
146241903Sbde				case CD1400_ETC_STOPBREAK:
146341903Sbde					/*
146441903Sbde					 * Start the command.  Come back on
146541903Sbde					 * next tx empty interrupt, hopefully
146641903Sbde					 * after command has been executed.
146741903Sbde					 */
146841903Sbde					cd_outb(iobase, CD1400_COR2, cy_align,
146941903Sbde						com->cor[1] |= CD1400_COR2_ETC);
147041903Sbde					cd_outb(iobase, CD1400_TDR, cy_align,
147141903Sbde						CD1400_ETC_CMD);
147241903Sbde					cd_outb(iobase, CD1400_TDR, cy_align,
147341903Sbde						com->etc);
147441903Sbde					if (com->etc == CD1400_ETC_SENDBREAK)
147541903Sbde						com->etc = ETC_BREAK_STARTING;
147641903Sbde					else
147741903Sbde						com->etc = ETC_BREAK_ENDING;
147841903Sbde					goto terminate_tx_service;
147941903Sbde				case ETC_BREAK_STARTING:
148041903Sbde					/*
148141903Sbde					 * BREAK is now on.  Continue with
148241903Sbde					 * SRER_TXMPTY processing, hopefully
148341903Sbde					 * don't come back.
148441903Sbde					 */
148541903Sbde					com->etc = ETC_BREAK_STARTED;
148641903Sbde					break;
148741903Sbde				case ETC_BREAK_STARTED:
148841903Sbde					/*
148941903Sbde					 * Came back due to sloppy SRER_TXMPTY
149041903Sbde					 * enabling.  Hope again.
149141903Sbde					 */
149241903Sbde					break;
149341903Sbde				case ETC_BREAK_ENDING:
149441903Sbde					/*
149541903Sbde					 * BREAK is now off.  Continue with
149641903Sbde					 * SRER_TXMPTY processing and don't
149741903Sbde					 * come back.  The SWI handler will
149841903Sbde					 * restart tx interrupts if necessary.
149941903Sbde					 */
150041903Sbde					cd_outb(iobase, CD1400_COR2, cy_align,
150141903Sbde						com->cor[1]
150241903Sbde						&= ~CD1400_COR2_ETC);
150341903Sbde					com->etc = ETC_BREAK_ENDED;
150441903Sbde					if (!(com->state & CS_ODONE)) {
150541903Sbde						com_events += LOTS_OF_EVENTS;
150641903Sbde						com->state |= CS_ODONE;
1507122799Sbde						swi_sched(sio_fast_ih, 0);
150841903Sbde					}
150941903Sbde					break;
151041903Sbde				case ETC_BREAK_ENDED:
151141903Sbde					/*
151241903Sbde					 * Shouldn't get here.  Hope again.
151341903Sbde					 */
151441903Sbde					break;
151541903Sbde				}
151641903Sbde			}
151741388Sbde			if (com->intr_enable & CD1400_SRER_TXMPTY) {
151841388Sbde				if (!(com->extra_state & CSE_ODONE)) {
151941388Sbde					com_events += LOTS_OF_EVENTS;
152041388Sbde					com->extra_state |= CSE_ODONE;
1521122799Sbde					swi_sched(sio_fast_ih, 0);
152241388Sbde				}
152341388Sbde				cd_outb(iobase, CD1400_SRER, cy_align,
152441388Sbde					com->intr_enable
152541388Sbde					&= ~CD1400_SRER_TXMPTY);
152641388Sbde				goto terminate_tx_service;
152741388Sbde			}
15289406Sbde		if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) {
15299406Sbde			u_char	*ioptr;
15309406Sbde			u_int	ocount;
15316261Sjkh
15329406Sbde			ioptr = com->obufq.l_head;
15339406Sbde				ocount = com->obufq.l_tail - ioptr;
15349406Sbde				if (ocount > CD1400_TX_FIFO_SIZE)
15359406Sbde					ocount = CD1400_TX_FIFO_SIZE;
15369406Sbde				com->bytes_out += ocount;
15379406Sbde				do
153829047Sbde					cd_outb(iobase, CD1400_TDR, cy_align,
153929047Sbde						*ioptr++);
15409406Sbde				while (--ocount != 0);
15419406Sbde			com->obufq.l_head = ioptr;
15429406Sbde			if (ioptr >= com->obufq.l_tail) {
15439406Sbde				struct lbq	*qp;
15446261Sjkh
15459406Sbde				qp = com->obufq.l_next;
15469406Sbde				qp->l_queued = FALSE;
15479406Sbde				qp = qp->l_next;
15489406Sbde				if (qp != NULL) {
15499406Sbde					com->obufq.l_head = qp->l_head;
15509406Sbde					com->obufq.l_tail = qp->l_tail;
15519406Sbde					com->obufq.l_next = qp;
15529406Sbde				} else {
15539406Sbde					/* output just completed */
15549406Sbde					com->state &= ~CS_BUSY;
155541388Sbde
155641388Sbde					/*
155741388Sbde					 * The setting of CSE_ODONE may be
155841388Sbde					 * stale here.  We currently only
155941388Sbde					 * use it when CS_BUSY is set, and
156041388Sbde					 * fixing it when we clear CS_BUSY
156141388Sbde					 * is easiest.
156241388Sbde					 */
156341388Sbde					if (com->extra_state & CSE_ODONE) {
156441388Sbde						com_events -= LOTS_OF_EVENTS;
156541388Sbde						com->extra_state &= ~CSE_ODONE;
156641388Sbde					}
156741388Sbde
156818901Sdg					cd_outb(iobase, CD1400_SRER, cy_align,
15699406Sbde						com->intr_enable
157043314Sdillon						= (com->intr_enable
157143314Sdillon						  & ~CD1400_SRER_TXRDY)
157241388Sbde						  | CD1400_SRER_TXMPTY);
15739406Sbde				}
15749406Sbde				if (!(com->state & CS_ODONE)) {
15759406Sbde					com_events += LOTS_OF_EVENTS;
15769406Sbde					com->state |= CS_ODONE;
157729047Sbde
157829047Sbde					/* handle at high level ASAP */
1579122799Sbde					swi_sched(sio_fast_ih, 0);
15809406Sbde				}
15819406Sbde			}
15829406Sbde		}
15836261Sjkh
15849406Sbde			/* terminate service context */
158541388Sbdeterminate_tx_service:
15866261Sjkh#ifdef PollMode
158718901Sdg			cd_outb(iobase, CD1400_TIR, cy_align,
15889406Sbde				save_tir
15899406Sbde				& ~(CD1400_TIR_RDIREQ | CD1400_TIR_RBUSY));
15906261Sjkh#else
159118901Sdg			cd_outb(iobase, CD1400_EOSRR, cy_align, 0);
15926261Sjkh#endif
15939406Sbde		}
15946261Sjkh	}
15956261Sjkh
15969406Sbde	/* ensure an edge for the next interrupt */
159741309Sbde	cy_outb(cy_iobase, CY_CLEAR_INTR, cy_align, 0);
15986261Sjkh
1599122799Sbde	swi_sched(sio_slow_ih, SWI_DELAY);
160028921Sfsmp
160172262Sjhb	COM_UNLOCK();
16029406Sbde}
16036261Sjkh
160412675Sjulianstatic int
160583366Sjuliansioioctl(dev, cmd, data, flag, td)
16069406Sbde	dev_t		dev;
160736735Sdfr	u_long		cmd;
16089406Sbde	caddr_t		data;
16099406Sbde	int		flag;
161083366Sjulian	struct thread	*td;
16116261Sjkh{
16129406Sbde	struct com_s	*com;
16136261Sjkh	int		error;
16149406Sbde	int		mynor;
16159406Sbde	int		s;
16169406Sbde	struct tty	*tp;
16179406Sbde#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
16189406Sbde	int		oldcmd;
16199406Sbde	struct termios	term;
16209406Sbde#endif
16216261Sjkh
16229406Sbde	mynor = minor(dev);
16239406Sbde	com = com_addr(MINOR_TO_UNIT(mynor));
16249406Sbde	if (mynor & CONTROL_MASK) {
16259406Sbde		struct termios	*ct;
16269406Sbde
16279406Sbde		switch (mynor & CONTROL_MASK) {
16289406Sbde		case CONTROL_INIT_STATE:
16299406Sbde			ct = mynor & CALLOUT_MASK ? &com->it_out : &com->it_in;
16309406Sbde			break;
16319406Sbde		case CONTROL_LOCK_STATE:
16329406Sbde			ct = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in;
16339406Sbde			break;
16349406Sbde		default:
16359406Sbde			return (ENODEV);	/* /dev/nodev */
16369406Sbde		}
16379406Sbde		switch (cmd) {
16389406Sbde		case TIOCSETA:
163993593Sjhb			error = suser(td);
16409406Sbde			if (error != 0)
16419406Sbde				return (error);
16429406Sbde			*ct = *(struct termios *)data;
16439406Sbde			return (0);
16449406Sbde		case TIOCGETA:
16459406Sbde			*(struct termios *)data = *ct;
16469406Sbde			return (0);
16479406Sbde		case TIOCGETD:
16489406Sbde			*(int *)data = TTYDISC;
16499406Sbde			return (0);
16509406Sbde		case TIOCGWINSZ:
16519406Sbde			bzero(data, sizeof(struct winsize));
16529406Sbde			return (0);
16539406Sbde		default:
16549406Sbde			return (ENOTTY);
16559406Sbde		}
16569406Sbde	}
16579406Sbde	tp = com->tp;
16589406Sbde#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
16599406Sbde	term = tp->t_termios;
16609406Sbde	oldcmd = cmd;
16619406Sbde	error = ttsetcompat(tp, &cmd, data, &term);
16629406Sbde	if (error != 0)
16636261Sjkh		return (error);
16649406Sbde	if (cmd != oldcmd)
16659406Sbde		data = (caddr_t)&term;
16666261Sjkh#endif
16679406Sbde	if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) {
16689406Sbde		int	cc;
16699406Sbde		struct termios *dt = (struct termios *)data;
16709406Sbde		struct termios *lt = mynor & CALLOUT_MASK
16719406Sbde				     ? &com->lt_out : &com->lt_in;
16729406Sbde
16739406Sbde		dt->c_iflag = (tp->t_iflag & lt->c_iflag)
16749406Sbde			      | (dt->c_iflag & ~lt->c_iflag);
16759406Sbde		dt->c_oflag = (tp->t_oflag & lt->c_oflag)
16769406Sbde			      | (dt->c_oflag & ~lt->c_oflag);
16779406Sbde		dt->c_cflag = (tp->t_cflag & lt->c_cflag)
16789406Sbde			      | (dt->c_cflag & ~lt->c_cflag);
16799406Sbde		dt->c_lflag = (tp->t_lflag & lt->c_lflag)
16809406Sbde			      | (dt->c_lflag & ~lt->c_lflag);
16819406Sbde		for (cc = 0; cc < NCCS; ++cc)
16829406Sbde			if (lt->c_cc[cc] != 0)
16839406Sbde				dt->c_cc[cc] = tp->t_cc[cc];
16849406Sbde		if (lt->c_ispeed != 0)
16859406Sbde			dt->c_ispeed = tp->t_ispeed;
16869406Sbde		if (lt->c_ospeed != 0)
16879406Sbde			dt->c_ospeed = tp->t_ospeed;
16889406Sbde	}
168983366Sjulian	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, td);
169031577Sbde	if (error != ENOIOCTL)
16916261Sjkh		return (error);
16929406Sbde	s = spltty();
16939406Sbde	error = ttioctl(tp, cmd, data, flag);
16949406Sbde	disc_optim(tp, &tp->t_termios, com);
169531577Sbde	if (error != ENOIOCTL) {
16969406Sbde		splx(s);
16979406Sbde		return (error);
16989406Sbde	}
16996261Sjkh	switch (cmd) {
170041903Sbde	case TIOCSBRK:
17019406Sbde#if 0
17029406Sbde		outb(iobase + com_cfcr, com->cfcr_image |= CFCR_SBREAK);
170341903Sbde#else
170441903Sbde		cd_etc(com, CD1400_ETC_SENDBREAK);
170541903Sbde#endif
17066261Sjkh		break;
17076261Sjkh	case TIOCCBRK:
170841903Sbde#if 0
17099406Sbde		outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK);
171041903Sbde#else
171141903Sbde		cd_etc(com, CD1400_ETC_STOPBREAK);
171241903Sbde#endif
17136261Sjkh		break;
17146261Sjkh	case TIOCSDTR:
17159406Sbde		(void)commctl(com, TIOCM_DTR, DMBIS);
17166261Sjkh		break;
17176261Sjkh	case TIOCCDTR:
17189406Sbde		(void)commctl(com, TIOCM_DTR, DMBIC);
17196261Sjkh		break;
172019718Sbde	/*
172119718Sbde	 * XXX should disallow changing MCR_RTS if CS_RTS_IFLOW is set.  The
172219718Sbde	 * changes get undone on the next call to comparam().
172319718Sbde	 */
17246261Sjkh	case TIOCMSET:
17259406Sbde		(void)commctl(com, *(int *)data, DMSET);
17266261Sjkh		break;
17276261Sjkh	case TIOCMBIS:
17289406Sbde		(void)commctl(com, *(int *)data, DMBIS);
17296261Sjkh		break;
17306261Sjkh	case TIOCMBIC:
17319406Sbde		(void)commctl(com, *(int *)data, DMBIC);
17326261Sjkh		break;
17339406Sbde	case TIOCMGET:
17349406Sbde		*(int *)data = commctl(com, 0, DMGET);
17356261Sjkh		break;
17366261Sjkh	case TIOCMSDTRWAIT:
17379406Sbde		/* must be root since the wait applies to following logins */
173893593Sjhb		error = suser(td);
17399406Sbde		if (error != 0) {
17409406Sbde			splx(s);
17419406Sbde			return (error);
17429406Sbde		}
17439406Sbde		com->dtr_wait = *(int *)data * hz / 100;
17449406Sbde		break;
17456261Sjkh	case TIOCMGDTRWAIT:
17469406Sbde		*(int *)data = com->dtr_wait * 100 / hz;
17476261Sjkh		break;
17489406Sbde	case TIOCTIMESTAMP:
17499406Sbde		com->do_timestamp = TRUE;
17509406Sbde		*(struct timeval *)data = com->timestamp;
17519406Sbde		break;
175217354Sbde	case TIOCDCDTIMESTAMP:
175317354Sbde		com->do_dcd_timestamp = TRUE;
175417354Sbde		*(struct timeval *)data = com->dcd_timestamp;
175517354Sbde		break;
17566261Sjkh	default:
17579406Sbde		splx(s);
17586261Sjkh		return (ENOTTY);
17596261Sjkh	}
17609406Sbde	splx(s);
17619406Sbde	return (0);
17629406Sbde}
17636261Sjkh
176438246Sbdestatic void
176567584Sjhbsiopoll(void *arg)
17669406Sbde{
17679406Sbde	int		unit;
17686261Sjkh
17699406Sbde#ifdef CyDebug
17709406Sbde	++cy_timeouts;
17719406Sbde#endif
17729406Sbde	if (com_events == 0)
17739406Sbde		return;
17749406Sbderepeat:
17759406Sbde	for (unit = 0; unit < NSIO; ++unit) {
17769406Sbde		struct com_s	*com;
17779406Sbde		int		incc;
17789406Sbde		struct tty	*tp;
17796261Sjkh
17809406Sbde		com = com_addr(unit);
17819406Sbde		if (com == NULL)
17829406Sbde			continue;
17839406Sbde		tp = com->tp;
17849406Sbde		if (tp == NULL) {
17859406Sbde			/*
17869406Sbde			 * XXX forget any events related to closed devices
17879406Sbde			 * (actually never opened devices) so that we don't
17889406Sbde			 * loop.
17899406Sbde			 */
179088088Sjhb			critical_enter();
179172262Sjhb			COM_LOCK();
17929406Sbde			incc = com->iptr - com->ibuf;
17939406Sbde			com->iptr = com->ibuf;
17949406Sbde			if (com->state & CS_CHECKMSR) {
17959406Sbde				incc += LOTS_OF_EVENTS;
17969406Sbde				com->state &= ~CS_CHECKMSR;
17979406Sbde			}
17989406Sbde			com_events -= incc;
179972262Sjhb			COM_UNLOCK();
180088088Sjhb			critical_exit();
18019406Sbde			if (incc != 0)
18029406Sbde				log(LOG_DEBUG,
18039406Sbde				    "sio%d: %d events for device with no tp\n",
18049406Sbde				    unit, incc);
18059406Sbde			continue;
18069406Sbde		}
180743611Sbde		if (com->iptr != com->ibuf) {
180888088Sjhb			critical_enter();
180972262Sjhb			COM_LOCK();
181088088Sjhb			sioinput(com);
181172262Sjhb			COM_UNLOCK();
181288088Sjhb			critical_exit();
18139406Sbde		}
18149406Sbde		if (com->state & CS_CHECKMSR) {
18159406Sbde			u_char	delta_modem_status;
18166261Sjkh
181788088Sjhb			critical_enter();
181872262Sjhb			COM_LOCK();
181988088Sjhb			sioinput(com);
18209406Sbde			delta_modem_status = com->last_modem_status
18219406Sbde					     ^ com->prev_modem_status;
18229406Sbde			com->prev_modem_status = com->last_modem_status;
18239406Sbde			com_events -= LOTS_OF_EVENTS;
18249406Sbde			com->state &= ~CS_CHECKMSR;
182572262Sjhb			COM_UNLOCK();
182688088Sjhb			critical_exit();
18279406Sbde			if (delta_modem_status & MSR_DCD)
18289406Sbde				(*linesw[tp->t_line].l_modem)
18299406Sbde					(tp, com->prev_modem_status & MSR_DCD);
18309406Sbde		}
183141388Sbde		if (com->extra_state & CSE_ODONE) {
183288088Sjhb			critical_enter();
183372262Sjhb			COM_LOCK();
183441388Sbde			com_events -= LOTS_OF_EVENTS;
183541388Sbde			com->extra_state &= ~CSE_ODONE;
183672262Sjhb			COM_UNLOCK();
183788088Sjhb			critical_exit();
183841388Sbde			if (!(com->state & CS_BUSY)) {
183941388Sbde				tp->t_state &= ~TS_BUSY;
184041388Sbde				ttwwakeup(com->tp);
184141388Sbde			}
184241903Sbde			if (com->etc != ETC_NONE) {
184341903Sbde				if (com->etc == ETC_BREAK_ENDED)
184441903Sbde					com->etc = ETC_NONE;
184541903Sbde				wakeup(&com->etc);
184641903Sbde			}
184741388Sbde		}
184841903Sbde		if (com->state & CS_ODONE) {
184988088Sjhb			critical_enter();
185072262Sjhb			COM_LOCK();
185141903Sbde			com_events -= LOTS_OF_EVENTS;
185241903Sbde			com->state &= ~CS_ODONE;
185372262Sjhb			COM_UNLOCK();
185488088Sjhb			critical_exit();
185541903Sbde			(*linesw[tp->t_line].l_start)(tp);
185641903Sbde		}
18579406Sbde		if (com_events == 0)
18589406Sbde			break;
18599406Sbde	}
18609406Sbde	if (com_events >= LOTS_OF_EVENTS)
18619406Sbde		goto repeat;
18629406Sbde}
18636261Sjkh
18649406Sbdestatic int
18659406Sbdecomparam(tp, t)
18669406Sbde	struct tty	*tp;
18679406Sbde	struct termios	*t;
18689406Sbde{
18699406Sbde	int		bits;
18709406Sbde	int		cflag;
18719406Sbde	struct com_s	*com;
18729406Sbde	u_char		cor_change;
187338303Sbde	u_long		cy_clock;
18749406Sbde	int		idivisor;
18759406Sbde	int		iflag;
18769406Sbde	int		iprescaler;
18779406Sbde	int		itimeout;
18789406Sbde	int		odivisor;
18799406Sbde	int		oprescaler;
18809406Sbde	u_char		opt;
18819406Sbde	int		s;
18829406Sbde	int		unit;
18836261Sjkh
188438302Sbde	unit = DEV_TO_UNIT(tp->t_dev);
188538302Sbde	com = com_addr(unit);
188638302Sbde
18879406Sbde	/* check requested parameters */
188838303Sbde	cy_clock = CY_CLOCK(com->gfrcr_image);
188938302Sbde	idivisor = comspeed(t->c_ispeed, cy_clock, &iprescaler);
1890120512Sbde	if (idivisor <= 0)
18919406Sbde		return (EINVAL);
1892120512Sbde	odivisor = comspeed(t->c_ospeed != 0 ? t->c_ospeed : tp->t_ospeed,
1893120512Sbde			    cy_clock, &oprescaler);
1894120512Sbde	if (odivisor <= 0)
18959406Sbde		return (EINVAL);
18966261Sjkh
18979406Sbde	/* parameters are OK, convert them to the com struct and the device */
189838303Sbde	s = spltty();
1899120512Sbde	if (t->c_ospeed == 0)
19009406Sbde		(void)commctl(com, TIOCM_DTR, DMBIC);	/* hang up line */
19019406Sbde	else
19029406Sbde		(void)commctl(com, TIOCM_DTR, DMBIS);
19036261Sjkh
190443611Sbde	(void) siosetwater(com, t->c_ispeed);
190543611Sbde
190643611Sbde	/* XXX we don't actually change the speed atomically. */
190743611Sbde
1908120512Sbde	cd_setreg(com, CD1400_RBPR, idivisor);
1909120512Sbde	cd_setreg(com, CD1400_RCOR, iprescaler);
1910120512Sbde	cd_setreg(com, CD1400_TBPR, odivisor);
1911120512Sbde	cd_setreg(com, CD1400_TCOR, oprescaler);
19126261Sjkh
19136261Sjkh	/*
19146261Sjkh	 * channel control
19156261Sjkh	 *	receiver enable
19166261Sjkh	 *	transmitter enable (always set)
19176261Sjkh	 */
19189406Sbde	cflag = t->c_cflag;
19199406Sbde	opt = CD1400_CCR_CMDCHANCTL | CD1400_CCR_XMTEN
19209406Sbde	      | (cflag & CREAD ? CD1400_CCR_RCVEN : CD1400_CCR_RCVDIS);
19219406Sbde	if (opt != com->channel_control) {
19229406Sbde		com->channel_control = opt;
192341293Sbde		cd1400_channel_cmd(com, opt);
19246261Sjkh	}
19256261Sjkh
19266261Sjkh#ifdef Smarts
19276261Sjkh	/* set special chars */
19289406Sbde	/* XXX if one is _POSIX_VDISABLE, can't use some others */
19299406Sbde	if (t->c_cc[VSTOP] != _POSIX_VDISABLE)
193041293Sbde		cd_setreg(com, CD1400_SCHR1, t->c_cc[VSTOP]);
19319406Sbde	if (t->c_cc[VSTART] != _POSIX_VDISABLE)
193241293Sbde		cd_setreg(com, CD1400_SCHR2, t->c_cc[VSTART]);
19339406Sbde	if (t->c_cc[VINTR] != _POSIX_VDISABLE)
193441293Sbde		cd_setreg(com, CD1400_SCHR3, t->c_cc[VINTR]);
19359406Sbde	if (t->c_cc[VSUSP] != _POSIX_VDISABLE)
193641293Sbde		cd_setreg(com, CD1400_SCHR4, t->c_cc[VSUSP]);
19376261Sjkh#endif
19386261Sjkh
19396261Sjkh	/*
19406261Sjkh	 * set channel option register 1 -
19416261Sjkh	 *	parity mode
19426261Sjkh	 *	stop bits
19436261Sjkh	 *	char length
19446261Sjkh	 */
19456261Sjkh	opt = 0;
19466261Sjkh	/* parity */
19476261Sjkh	if (cflag & PARENB) {
19489406Sbde		if (cflag & PARODD)
19499406Sbde			opt |= CD1400_COR1_PARODD;
19509406Sbde		opt |= CD1400_COR1_PARNORMAL;
19516261Sjkh	}
19529406Sbde	iflag = t->c_iflag;
19536261Sjkh	if (!(iflag & INPCK))
19549406Sbde		opt |= CD1400_COR1_NOINPCK;
19559406Sbde	bits = 1 + 1;
19566261Sjkh	/* stop bits */
19579406Sbde	if (cflag & CSTOPB) {
19589406Sbde		++bits;
19599406Sbde		opt |= CD1400_COR1_STOP2;
19609406Sbde	}
19616261Sjkh	/* char length */
19629406Sbde	switch (cflag & CSIZE) {
19639406Sbde	case CS5:
19649406Sbde		bits += 5;
19659406Sbde		opt |= CD1400_COR1_CS5;
19669406Sbde		break;
19679406Sbde	case CS6:
19689406Sbde		bits += 6;
19699406Sbde		opt |= CD1400_COR1_CS6;
19709406Sbde		break;
19719406Sbde	case CS7:
19729406Sbde		bits += 7;
19739406Sbde		opt |= CD1400_COR1_CS7;
19749406Sbde		break;
19759406Sbde	default:
19769406Sbde		bits += 8;
19779406Sbde		opt |= CD1400_COR1_CS8;
19789406Sbde		break;
19796261Sjkh	}
19809406Sbde	cor_change = 0;
19819406Sbde	if (opt != com->cor[0]) {
19829406Sbde		cor_change |= CD1400_CCR_COR1;
198341293Sbde		cd_setreg(com, CD1400_COR1, com->cor[0] = opt);
19849406Sbde	}
19856261Sjkh
19866261Sjkh	/*
19879406Sbde	 * Set receive time-out period, normally to max(one char time, 5 ms).
19889406Sbde	 */
1989120512Sbde	itimeout = (1000 * bits + t->c_ispeed - 1) / t->c_ispeed;
19909406Sbde#ifdef SOFT_HOTCHAR
19919406Sbde#define	MIN_RTP		1
19929406Sbde#else
19939406Sbde#define	MIN_RTP		5
19949406Sbde#endif
1995120512Sbde	if (itimeout < MIN_RTP)
1996120512Sbde		itimeout = MIN_RTP;
19979406Sbde	if (!(t->c_lflag & ICANON) && t->c_cc[VMIN] != 0 && t->c_cc[VTIME] != 0
19989406Sbde	    && t->c_cc[VTIME] * 10 > itimeout)
19999406Sbde		itimeout = t->c_cc[VTIME] * 10;
20009406Sbde	if (itimeout > 255)
20019406Sbde		itimeout = 255;
200241293Sbde	cd_setreg(com, CD1400_RTPR, itimeout);
20039406Sbde
20049406Sbde	/*
20056261Sjkh	 * set channel option register 2 -
20066261Sjkh	 *	flow control
20076261Sjkh	 */
20086261Sjkh	opt = 0;
20096261Sjkh#ifdef Smarts
20106261Sjkh	if (iflag & IXANY)
20119406Sbde		opt |= CD1400_COR2_IXANY;
20126261Sjkh	if (iflag & IXOFF)
20139406Sbde		opt |= CD1400_COR2_IXOFF;
20146261Sjkh#endif
20159406Sbde#ifndef SOFT_CTS_OFLOW
20166261Sjkh	if (cflag & CCTS_OFLOW)
20179406Sbde		opt |= CD1400_COR2_CCTS_OFLOW;
20186261Sjkh#endif
201988088Sjhb	critical_enter();
202072262Sjhb	COM_LOCK();
20219406Sbde	if (opt != com->cor[1]) {
20229406Sbde		cor_change |= CD1400_CCR_COR2;
202341293Sbde		cd_setreg(com, CD1400_COR2, com->cor[1] = opt);
20246261Sjkh	}
202572262Sjhb	COM_UNLOCK();
202688088Sjhb	critical_exit();
20276261Sjkh
20286261Sjkh	/*
20296261Sjkh	 * set channel option register 3 -
20306261Sjkh	 *	receiver FIFO interrupt threshold
20316261Sjkh	 *	flow control
20326261Sjkh	 */
20339406Sbde	opt = RxFifoThreshold;
20346261Sjkh#ifdef Smarts
20356261Sjkh	if (t->c_lflag & ICANON)
20369406Sbde		opt |= CD1400_COR3_SCD34;	/* detect INTR & SUSP chars */
20376261Sjkh	if (iflag & IXOFF)
20389406Sbde		/* detect and transparently handle START and STOP chars */
20399406Sbde		opt |= CD1400_COR3_FCT | CD1400_COR3_SCD12;
20406261Sjkh#endif
20419406Sbde	if (opt != com->cor[2]) {
20429406Sbde		cor_change |= CD1400_CCR_COR3;
204341293Sbde		cd_setreg(com, CD1400_COR3, com->cor[2] = opt);
20446261Sjkh	}
20456261Sjkh
20466261Sjkh	/* notify the CD1400 if COR1-3 have changed */
20479406Sbde	if (cor_change)
204841293Sbde		cd1400_channel_cmd(com, CD1400_CCR_CMDCORCHG | cor_change);
20496261Sjkh
20506261Sjkh	/*
20516261Sjkh	 * set channel option register 4 -
20526261Sjkh	 *	CR/NL processing
20536261Sjkh	 *	break processing
20546261Sjkh	 *	received exception processing
20556261Sjkh	 */
20566261Sjkh	opt = 0;
20576261Sjkh	if (iflag & IGNCR)
20589406Sbde		opt |= CD1400_COR4_IGNCR;
20596261Sjkh#ifdef Smarts
20606261Sjkh	/*
20616261Sjkh	 * we need a new ttyinput() for this, as we don't want to
20626261Sjkh	 * have ICRNL && INLCR being done in both layers, or to have
20636261Sjkh	 * synchronisation problems
20646261Sjkh	 */
20656261Sjkh	if (iflag & ICRNL)
20669406Sbde		opt |= CD1400_COR4_ICRNL;
20676261Sjkh	if (iflag & INLCR)
20689406Sbde		opt |= CD1400_COR4_INLCR;
20696261Sjkh#endif
20706261Sjkh	if (iflag & IGNBRK)
207141905Sbde		opt |= CD1400_COR4_IGNBRK | CD1400_COR4_NOBRKINT;
207241905Sbde	/*
207341905Sbde	 * The `-ignbrk -brkint parmrk' case is not handled by the hardware,
207441905Sbde	 * so only tell the hardware about -brkint if -parmrk.
207541905Sbde	 */
207641905Sbde	if (!(iflag & (BRKINT | PARMRK)))
20779406Sbde		opt |= CD1400_COR4_NOBRKINT;
20789406Sbde#if 0
20799406Sbde	/* XXX using this "intelligence" breaks reporting of overruns. */
20806261Sjkh	if (iflag & IGNPAR)
20819406Sbde		opt |= CD1400_COR4_PFO_DISCARD;
20826261Sjkh	else {
20839406Sbde		if (iflag & PARMRK)
20849406Sbde			opt |= CD1400_COR4_PFO_ESC;
20859406Sbde		else
20869406Sbde			opt |= CD1400_COR4_PFO_NUL;
20879406Sbde	}
20886261Sjkh#else
20899406Sbde	opt |= CD1400_COR4_PFO_EXCEPTION;
20906261Sjkh#endif
209141293Sbde	cd_setreg(com, CD1400_COR4, opt);
20926261Sjkh
20936261Sjkh	/*
20946261Sjkh	 * set channel option register 5 -
20956261Sjkh	 */
20966261Sjkh	opt = 0;
20976261Sjkh	if (iflag & ISTRIP)
20989406Sbde		opt |= CD1400_COR5_ISTRIP;
20999406Sbde	if (t->c_iflag & IEXTEN)
21009406Sbde		/* enable LNEXT (e.g. ctrl-v quoting) handling */
21019406Sbde		opt |= CD1400_COR5_LNEXT;
21026261Sjkh#ifdef Smarts
21036261Sjkh	if (t->c_oflag & ONLCR)
21049406Sbde		opt |= CD1400_COR5_ONLCR;
21056261Sjkh	if (t->c_oflag & OCRNL)
21069406Sbde		opt |= CD1400_COR5_OCRNL;
21076261Sjkh#endif
210841293Sbde	cd_setreg(com, CD1400_COR5, opt);
21096261Sjkh
21106261Sjkh	/*
211120152Sbde	 * We always generate modem status change interrupts for CD changes.
211220152Sbde	 * Among other things, this is necessary to track TS_CARR_ON for
211320152Sbde	 * pstat to print even when the driver doesn't care.  CD changes
211420152Sbde	 * should be rare so interrupts for them are not worth extra code to
211520152Sbde	 * avoid.  We avoid interrupts for other modem status changes (except
211620152Sbde	 * for CTS changes when SOFT_CTS_OFLOW is configured) since this is
211720152Sbde	 * simplest and best.
21189406Sbde	 */
211920152Sbde
21209406Sbde	/*
21216261Sjkh	 * set modem change option register 1
21226261Sjkh	 *	generate modem interrupts on which 1 -> 0 input transitions
21236261Sjkh	 *	also controls auto-DTR output flow-control, which we don't use
21246261Sjkh	 */
212520152Sbde	opt = CD1400_MCOR1_CDzd;
21269406Sbde#ifdef SOFT_CTS_OFLOW
21279406Sbde	if (cflag & CCTS_OFLOW)
21289406Sbde		opt |= CD1400_MCOR1_CTSzd;
21299406Sbde#endif
213041293Sbde	cd_setreg(com, CD1400_MCOR1, opt);
21316261Sjkh
21326261Sjkh	/*
21336261Sjkh	 * set modem change option register 2
21346261Sjkh	 *	generate modem interrupts on specific 0 -> 1 input transitions
21356261Sjkh	 */
213620152Sbde	opt = CD1400_MCOR2_CDod;
21379406Sbde#ifdef SOFT_CTS_OFLOW
21389406Sbde	if (cflag & CCTS_OFLOW)
21399406Sbde		opt |= CD1400_MCOR2_CTSod;
21409406Sbde#endif
214141293Sbde	cd_setreg(com, CD1400_MCOR2, opt);
21426261Sjkh
21439406Sbde	/*
21449406Sbde	 * XXX should have done this long ago, but there is too much state
21459406Sbde	 * to change all atomically.
21469406Sbde	 */
214788088Sjhb	critical_enter();
214872262Sjhb	COM_LOCK();
21496261Sjkh
21509406Sbde	com->state &= ~CS_TTGO;
21519406Sbde	if (!(tp->t_state & TS_TTSTOP))
21529406Sbde		com->state |= CS_TTGO;
215319718Sbde	if (cflag & CRTS_IFLOW) {
215419718Sbde		com->state |= CS_RTS_IFLOW;
215519718Sbde		/*
215619718Sbde		 * If CS_RTS_IFLOW just changed from off to on, the change
215719718Sbde		 * needs to be propagated to MCR_RTS.  This isn't urgent,
215819718Sbde		 * so do it later by calling comstart() instead of repeating
215919718Sbde		 * a lot of code from comstart() here.
216019718Sbde		 */
216119718Sbde	} else if (com->state & CS_RTS_IFLOW) {
21629406Sbde		com->state &= ~CS_RTS_IFLOW;
216319718Sbde		/*
216419718Sbde		 * CS_RTS_IFLOW just changed from on to off.  Force MCR_RTS
216519718Sbde		 * on here, since comstart() won't do it later.
216619718Sbde		 */
216719718Sbde#if 0
216819718Sbde		outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS);
216919718Sbde#else
217041293Sbde		cd_setreg(com, com->mcr_rts_reg,
217141293Sbde			  com->mcr_image |= com->mcr_rts);
217219718Sbde#endif
217319718Sbde	}
21746261Sjkh
21759406Sbde	/*
21769406Sbde	 * Set up state to handle output flow control.
21779406Sbde	 * XXX - worth handling MDMBUF (DCD) flow control at the lowest level?
21789406Sbde	 * Now has 10+ msec latency, while CTS flow has 50- usec latency.
21799406Sbde	 */
21809406Sbde	com->state |= CS_ODEVREADY;
21819406Sbde#ifdef SOFT_CTS_OFLOW
21829406Sbde	com->state &= ~CS_CTS_OFLOW;
21839406Sbde	if (cflag & CCTS_OFLOW) {
21849406Sbde		com->state |= CS_CTS_OFLOW;
21859406Sbde		if (!(com->last_modem_status & MSR_CTS))
21869406Sbde			com->state &= ~CS_ODEVREADY;
21879406Sbde	}
21889406Sbde#endif
21899406Sbde	/* XXX shouldn't call functions while intrs are disabled. */
21909406Sbde	disc_optim(tp, t, com);
21919406Sbde#if 0
21929406Sbde	/*
21939406Sbde	 * Recover from fiddling with CS_TTGO.  We used to call siointr1()
21949406Sbde	 * unconditionally, but that defeated the careful discarding of
21959406Sbde	 * stale input in sioopen().
21969406Sbde	 */
21979406Sbde	if (com->state >= (CS_BUSY | CS_TTGO))
21989406Sbde		siointr1(com);
21999406Sbde#endif
22009406Sbde	if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) {
22019406Sbde		if (!(com->intr_enable & CD1400_SRER_TXRDY))
220241293Sbde			cd_setreg(com, CD1400_SRER,
220341388Sbde				  com->intr_enable
220443314Sdillon				  = (com->intr_enable & ~CD1400_SRER_TXMPTY)
220541388Sbde				    | CD1400_SRER_TXRDY);
22069406Sbde	} else {
22079406Sbde		if (com->intr_enable & CD1400_SRER_TXRDY)
220841293Sbde			cd_setreg(com, CD1400_SRER,
220941388Sbde				  com->intr_enable
221043314Sdillon				  = (com->intr_enable & ~CD1400_SRER_TXRDY)
221141388Sbde				    | CD1400_SRER_TXMPTY);
22129406Sbde	}
22136261Sjkh
221472262Sjhb	COM_UNLOCK();
221588088Sjhb	critical_exit();
22169406Sbde	splx(s);
221719718Sbde	comstart(tp);
221843611Sbde	if (com->ibufold != NULL) {
221943611Sbde		free(com->ibufold, M_DEVBUF);
222043611Sbde		com->ibufold = NULL;
222143611Sbde	}
22229406Sbde	return (0);
22239406Sbde}
22249406Sbde
222543611Sbdestatic int
222643611Sbdesiosetwater(com, speed)
222743611Sbde	struct com_s	*com;
222843611Sbde	speed_t		speed;
222943611Sbde{
223043611Sbde	int		cp4ticks;
223143611Sbde	u_char		*ibuf;
223243611Sbde	int		ibufsize;
223343611Sbde	struct tty	*tp;
223443611Sbde
223543611Sbde	/*
223643611Sbde	 * Make the buffer size large enough to handle a softtty interrupt
223743611Sbde	 * latency of about 2 ticks without loss of throughput or data
223843611Sbde	 * (about 3 ticks if input flow control is not used or not honoured,
223943611Sbde	 * but a bit less for CS5-CS7 modes).
224043611Sbde	 */
224143611Sbde	cp4ticks = speed / 10 / hz * 4;
224243611Sbde	for (ibufsize = 128; ibufsize < cp4ticks;)
224343611Sbde		ibufsize <<= 1;
224443611Sbde	if (ibufsize == com->ibufsize) {
224543611Sbde		return (0);
224643611Sbde	}
224743611Sbde
224843611Sbde	/*
224943611Sbde	 * Allocate input buffer.  The extra factor of 2 in the size is
225043611Sbde	 * to allow for an error byte for each input byte.
225143611Sbde	 */
225243611Sbde	ibuf = malloc(2 * ibufsize, M_DEVBUF, M_NOWAIT);
225343611Sbde	if (ibuf == NULL) {
225443611Sbde		return (ENOMEM);
225543611Sbde	}
225643611Sbde
225743611Sbde	/* Initialize non-critical variables. */
225843611Sbde	com->ibufold = com->ibuf;
225943611Sbde	com->ibufsize = ibufsize;
226043611Sbde	tp = com->tp;
226143611Sbde	if (tp != NULL) {
226243611Sbde		tp->t_ififosize = 2 * ibufsize;
226343611Sbde		tp->t_ispeedwat = (speed_t)-1;
226443611Sbde		tp->t_ospeedwat = (speed_t)-1;
226543611Sbde	}
226643611Sbde
226743611Sbde	/*
226843611Sbde	 * Read current input buffer, if any.  Continue with interrupts
226943611Sbde	 * disabled.
227043611Sbde	 */
227188088Sjhb	critical_enter();
227272262Sjhb	COM_LOCK();
227343611Sbde	if (com->iptr != com->ibuf)
227488088Sjhb		sioinput(com);
227543611Sbde
227643611Sbde	/*-
227743611Sbde	 * Initialize critical variables, including input buffer watermarks.
227843611Sbde	 * The external device is asked to stop sending when the buffer
227943611Sbde	 * exactly reaches high water, or when the high level requests it.
228043611Sbde	 * The high level is notified immediately (rather than at a later
228143611Sbde	 * clock tick) when this watermark is reached.
228243611Sbde	 * The buffer size is chosen so the watermark should almost never
228343611Sbde	 * be reached.
228443611Sbde	 * The low watermark is invisibly 0 since the buffer is always
228543611Sbde	 * emptied all at once.
228643611Sbde	 */
228743611Sbde	com->iptr = com->ibuf = ibuf;
228843611Sbde	com->ibufend = ibuf + ibufsize;
228943611Sbde	com->ierroff = ibufsize;
229043611Sbde	com->ihighwater = ibuf + 3 * ibufsize / 4;
229165557Sjasone
229272262Sjhb	COM_UNLOCK();
229388088Sjhb	critical_exit();
229443611Sbde	return (0);
229543611Sbde}
229643611Sbde
22979406Sbdestatic void
22989406Sbdecomstart(tp)
22999406Sbde	struct tty	*tp;
23006261Sjkh{
23019406Sbde	struct com_s	*com;
23026261Sjkh	int		s;
23039406Sbde#ifdef CyDebug
23049406Sbde	bool_t		started;
23059406Sbde#endif
23069406Sbde	int		unit;
23076261Sjkh
23089406Sbde	unit = DEV_TO_UNIT(tp->t_dev);
23099406Sbde	com = com_addr(unit);
23109406Sbde	s = spltty();
23119406Sbde
23126261Sjkh#ifdef CyDebug
23139406Sbde	++com->start_count;
23149406Sbde	started = FALSE;
23156261Sjkh#endif
23166261Sjkh
231788088Sjhb	critical_enter();
231872262Sjhb	COM_LOCK();
23199406Sbde	if (tp->t_state & TS_TTSTOP) {
23209406Sbde		com->state &= ~CS_TTGO;
23219406Sbde		if (com->intr_enable & CD1400_SRER_TXRDY)
232241293Sbde			cd_setreg(com, CD1400_SRER,
232341388Sbde				  com->intr_enable
232443314Sdillon				  = (com->intr_enable & ~CD1400_SRER_TXRDY)
232541388Sbde				    | CD1400_SRER_TXMPTY);
23269406Sbde	} else {
23279406Sbde		com->state |= CS_TTGO;
23289406Sbde		if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)
23299406Sbde		    && !(com->intr_enable & CD1400_SRER_TXRDY))
233041293Sbde			cd_setreg(com, CD1400_SRER,
233141388Sbde				  com->intr_enable
233243314Sdillon				  = (com->intr_enable & ~CD1400_SRER_TXMPTY)
233341388Sbde				    | CD1400_SRER_TXRDY);
23349406Sbde	}
23359406Sbde	if (tp->t_state & TS_TBLOCK) {
233638303Sbde		if (com->mcr_image & com->mcr_rts && com->state & CS_RTS_IFLOW)
23379406Sbde#if 0
23389406Sbde			outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS);
23399406Sbde#else
234041293Sbde			cd_setreg(com, com->mcr_rts_reg,
234141293Sbde				  com->mcr_image &= ~com->mcr_rts);
23429406Sbde#endif
23439406Sbde	} else {
234438303Sbde		if (!(com->mcr_image & com->mcr_rts)
234538302Sbde		    && com->iptr < com->ihighwater
234619718Sbde		    && com->state & CS_RTS_IFLOW)
23479406Sbde#if 0
23489406Sbde			outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS);
23499406Sbde#else
235041293Sbde			cd_setreg(com, com->mcr_rts_reg,
235141293Sbde				  com->mcr_image |= com->mcr_rts);
23529406Sbde#endif
23539406Sbde	}
235472262Sjhb	COM_UNLOCK();
235588088Sjhb	critical_exit();
23569406Sbde	if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) {
235732045Sbde		ttwwakeup(tp);
23589406Sbde		splx(s);
23596261Sjkh		return;
23609406Sbde	}
23619406Sbde	if (tp->t_outq.c_cc != 0) {
23629406Sbde		struct lbq	*qp;
23639406Sbde		struct lbq	*next;
23646261Sjkh
23659406Sbde		if (!com->obufs[0].l_queued) {
23669406Sbde#ifdef CyDebug
23679406Sbde			started = TRUE;
23689406Sbde#endif
23699406Sbde			com->obufs[0].l_tail
23709406Sbde			    = com->obuf1 + q_to_b(&tp->t_outq, com->obuf1,
23719406Sbde						  sizeof com->obuf1);
23729406Sbde			com->obufs[0].l_next = NULL;
23739406Sbde			com->obufs[0].l_queued = TRUE;
237488088Sjhb			critical_enter();
237572262Sjhb			COM_LOCK();
23769406Sbde			if (com->state & CS_BUSY) {
23779406Sbde				qp = com->obufq.l_next;
23789406Sbde				while ((next = qp->l_next) != NULL)
23799406Sbde					qp = next;
23809406Sbde				qp->l_next = &com->obufs[0];
23819406Sbde			} else {
23829406Sbde				com->obufq.l_head = com->obufs[0].l_head;
23839406Sbde				com->obufq.l_tail = com->obufs[0].l_tail;
23849406Sbde				com->obufq.l_next = &com->obufs[0];
23859406Sbde				com->state |= CS_BUSY;
23869406Sbde				if (com->state >= (CS_BUSY | CS_TTGO
23879406Sbde						   | CS_ODEVREADY))
238841293Sbde					cd_setreg(com, CD1400_SRER,
238941293Sbde						  com->intr_enable
239043314Sdillon						  = (com->intr_enable
239143314Sdillon						    & ~CD1400_SRER_TXMPTY)
239241388Sbde						    | CD1400_SRER_TXRDY);
23939406Sbde			}
239472262Sjhb			COM_UNLOCK();
239588088Sjhb			critical_exit();
23969406Sbde		}
23979406Sbde		if (tp->t_outq.c_cc != 0 && !com->obufs[1].l_queued) {
23989406Sbde#ifdef CyDebug
23999406Sbde			started = TRUE;
24009406Sbde#endif
24019406Sbde			com->obufs[1].l_tail
24029406Sbde			    = com->obuf2 + q_to_b(&tp->t_outq, com->obuf2,
24039406Sbde						  sizeof com->obuf2);
24049406Sbde			com->obufs[1].l_next = NULL;
24059406Sbde			com->obufs[1].l_queued = TRUE;
240688088Sjhb			critical_enter();
240772262Sjhb			COM_LOCK();
24089406Sbde			if (com->state & CS_BUSY) {
24099406Sbde				qp = com->obufq.l_next;
24109406Sbde				while ((next = qp->l_next) != NULL)
24119406Sbde					qp = next;
24129406Sbde				qp->l_next = &com->obufs[1];
24139406Sbde			} else {
24149406Sbde				com->obufq.l_head = com->obufs[1].l_head;
24159406Sbde				com->obufq.l_tail = com->obufs[1].l_tail;
24169406Sbde				com->obufq.l_next = &com->obufs[1];
24179406Sbde				com->state |= CS_BUSY;
24189406Sbde				if (com->state >= (CS_BUSY | CS_TTGO
24199406Sbde						   | CS_ODEVREADY))
242041293Sbde					cd_setreg(com, CD1400_SRER,
242141293Sbde						  com->intr_enable
242243314Sdillon						  = (com->intr_enable
242343314Sdillon						    & ~CD1400_SRER_TXMPTY)
242441388Sbde						    | CD1400_SRER_TXRDY);
24259406Sbde			}
242672262Sjhb			COM_UNLOCK();
242788088Sjhb			critical_exit();
24289406Sbde		}
24299406Sbde		tp->t_state |= TS_BUSY;
24309406Sbde	}
24319406Sbde#ifdef CyDebug
24329406Sbde	if (started)
24339406Sbde		++com->start_real;
24349406Sbde#endif
24359406Sbde#if 0
243688088Sjhb	critical_enter();
243772262Sjhb	COM_LOCK();
243812962Sbde	if (com->state >= (CS_BUSY | CS_TTGO))
24399406Sbde		siointr1(com);	/* fake interrupt to start output */
244072262Sjhb	COM_UNLOCK();
244188088Sjhb	critical_exit();
24429406Sbde#endif
24439626Sbde	ttwwakeup(tp);
24449406Sbde	splx(s);
24459406Sbde}
24466261Sjkh
244712675Sjulianstatic void
244851654Sphkcomstop(tp, rw)
24499406Sbde	struct tty	*tp;
24509406Sbde	int		rw;
24519406Sbde{
24529406Sbde	struct com_s	*com;
245341903Sbde	bool_t		wakeup_etc;
24546261Sjkh
24559754Sbde	com = com_addr(DEV_TO_UNIT(tp->t_dev));
245641903Sbde	wakeup_etc = FALSE;
245788088Sjhb	critical_enter();
245872262Sjhb	COM_LOCK();
24599406Sbde	if (rw & FWRITE) {
24609406Sbde		com->obufs[0].l_queued = FALSE;
24619406Sbde		com->obufs[1].l_queued = FALSE;
246241388Sbde		if (com->extra_state & CSE_ODONE) {
246341388Sbde			com_events -= LOTS_OF_EVENTS;
246441388Sbde			com->extra_state &= ~CSE_ODONE;
246541903Sbde			if (com->etc != ETC_NONE) {
246641903Sbde				if (com->etc == ETC_BREAK_ENDED)
246741903Sbde					com->etc = ETC_NONE;
246841903Sbde				wakeup_etc = TRUE;
246941903Sbde			}
247041388Sbde		}
24719406Sbde		com->tp->t_state &= ~TS_BUSY;
247241903Sbde		if (com->state & CS_ODONE)
247341903Sbde			com_events -= LOTS_OF_EVENTS;
247441903Sbde		com->state &= ~(CS_ODONE | CS_BUSY);
24759406Sbde	}
24769406Sbde	if (rw & FREAD) {
247741908Sbde		/* XXX no way to reset only input fifo. */
24789406Sbde		com_events -= (com->iptr - com->ibuf);
24799406Sbde		com->iptr = com->ibuf;
24809406Sbde	}
248172262Sjhb	COM_UNLOCK();
248288088Sjhb	critical_exit();
248341903Sbde	if (wakeup_etc)
248441903Sbde		wakeup(&com->etc);
248541908Sbde	if (rw & FWRITE && com->etc == ETC_NONE)
248641908Sbde		cd1400_channel_cmd(com, CD1400_CCR_CMDRESET | CD1400_CCR_FTF);
24879754Sbde	comstart(tp);
24889406Sbde}
24899406Sbde
24909406Sbdestatic int
24919406Sbdecommctl(com, bits, how)
24929406Sbde	struct com_s	*com;
24939406Sbde	int		bits;
24949406Sbde	int		how;
24959406Sbde{
24969406Sbde	int	mcr;
24979406Sbde	int	msr;
24989406Sbde
24999406Sbde	if (how == DMGET) {
25009406Sbde		if (com->channel_control & CD1400_CCR_RCVEN)
25019406Sbde			bits |= TIOCM_LE;
25029406Sbde		mcr = com->mcr_image;
250338303Sbde		if (mcr & com->mcr_dtr)
25049406Sbde			bits |= TIOCM_DTR;
250538303Sbde		if (mcr & com->mcr_rts)
25069406Sbde			/* XXX wired on for Cyclom-8Ys */
25079406Sbde			bits |= TIOCM_RTS;
250820152Sbde
250920152Sbde		/*
251020152Sbde		 * We must read the modem status from the hardware because
251120152Sbde		 * we don't generate modem status change interrupts for all
251220152Sbde		 * changes, so com->prev_modem_status is not guaranteed to
251320152Sbde		 * be up to date.  This is safe, unlike for sio, because
251420152Sbde		 * reading the status register doesn't clear pending modem
251520152Sbde		 * status change interrupts.
251620152Sbde		 */
251741293Sbde		msr = cd_getreg(com, CD1400_MSVR2);
251820152Sbde
25199406Sbde		if (msr & MSR_CTS)
25209406Sbde			bits |= TIOCM_CTS;
25219406Sbde		if (msr & MSR_DCD)
25229406Sbde			bits |= TIOCM_CD;
25239406Sbde		if (msr & MSR_DSR)
25249406Sbde			bits |= TIOCM_DSR;
25259406Sbde		if (msr & MSR_RI)
25269406Sbde			/* XXX not connected except for Cyclom-16Y? */
25279406Sbde			bits |= TIOCM_RI;
25289406Sbde		return (bits);
25296261Sjkh	}
25309406Sbde	mcr = 0;
253138303Sbde	if (bits & TIOCM_DTR)
253238303Sbde		mcr |= com->mcr_dtr;
253338303Sbde	if (bits & TIOCM_RTS)
253438303Sbde		mcr |= com->mcr_rts;
253588088Sjhb	critical_enter();
253672262Sjhb	COM_LOCK();
25379406Sbde	switch (how) {
25389406Sbde	case DMSET:
25399406Sbde		com->mcr_image = mcr;
254041293Sbde		cd_setreg(com, CD1400_MSVR1, mcr);
254141293Sbde		cd_setreg(com, CD1400_MSVR2, mcr);
25429406Sbde		break;
25439406Sbde	case DMBIS:
25449406Sbde		com->mcr_image = mcr = com->mcr_image | mcr;
254541293Sbde		cd_setreg(com, CD1400_MSVR1, mcr);
254641293Sbde		cd_setreg(com, CD1400_MSVR2, mcr);
25479406Sbde		break;
25489406Sbde	case DMBIC:
25499406Sbde		com->mcr_image = mcr = com->mcr_image & ~mcr;
255041293Sbde		cd_setreg(com, CD1400_MSVR1, mcr);
255141293Sbde		cd_setreg(com, CD1400_MSVR2, mcr);
25529406Sbde		break;
25539406Sbde	}
255472262Sjhb	COM_UNLOCK();
255588088Sjhb	critical_exit();
25569406Sbde	return (0);
25579406Sbde}
25586261Sjkh
25599406Sbdestatic void
25609406Sbdesiosettimeout()
25619406Sbde{
25629406Sbde	struct com_s	*com;
25639406Sbde	bool_t		someopen;
25649406Sbde	int		unit;
25656261Sjkh
25669406Sbde	/*
25679406Sbde	 * Set our timeout period to 1 second if no polled devices are open.
25689406Sbde	 * Otherwise set it to max(1/200, 1/hz).
25699406Sbde	 * Enable timeouts iff some device is open.
25709406Sbde	 */
257129677Sgibbs	untimeout(comwakeup, (void *)NULL, sio_timeout_handle);
25729406Sbde	sio_timeout = hz;
25739406Sbde	someopen = FALSE;
25749406Sbde	for (unit = 0; unit < NSIO; ++unit) {
25759406Sbde		com = com_addr(unit);
25769406Sbde		if (com != NULL && com->tp != NULL
25779406Sbde		    && com->tp->t_state & TS_ISOPEN) {
25789406Sbde			someopen = TRUE;
25799406Sbde#if 0
25809406Sbde			if (com->poll || com->poll_output) {
25819406Sbde				sio_timeout = hz > 200 ? hz / 200 : 1;
25829406Sbde				break;
25839406Sbde			}
25849406Sbde#endif
25859406Sbde		}
25869406Sbde	}
25879406Sbde	if (someopen) {
25889406Sbde		sio_timeouts_until_log = hz / sio_timeout;
258929677Sgibbs		sio_timeout_handle = timeout(comwakeup, (void *)NULL,
259029677Sgibbs					     sio_timeout);
25919406Sbde	} else {
25929406Sbde		/* Flush error messages, if any. */
25939406Sbde		sio_timeouts_until_log = 1;
25949406Sbde		comwakeup((void *)NULL);
259529677Sgibbs		untimeout(comwakeup, (void *)NULL, sio_timeout_handle);
25969406Sbde	}
25979406Sbde}
25986261Sjkh
25999406Sbdestatic void
26009406Sbdecomwakeup(chan)
26019406Sbde	void	*chan;
26026261Sjkh{
26039406Sbde	struct com_s	*com;
26049406Sbde	int		unit;
26056261Sjkh
260629677Sgibbs	sio_timeout_handle = timeout(comwakeup, (void *)NULL, sio_timeout);
26076261Sjkh
26089406Sbde#if 0
26099406Sbde	/*
26109406Sbde	 * Recover from lost output interrupts.
26119406Sbde	 * Poll any lines that don't use interrupts.
26129406Sbde	 */
26139406Sbde	for (unit = 0; unit < NSIO; ++unit) {
26149406Sbde		com = com_addr(unit);
26159406Sbde		if (com != NULL
26169406Sbde		    && (com->state >= (CS_BUSY | CS_TTGO) || com->poll)) {
261788088Sjhb			critical_enter();
261872262Sjhb			COM_LOCK();
26199406Sbde			siointr1(com);
262072262Sjhb			COM_UNLOCK();
262188088Sjhb			critical_exit();
26229406Sbde		}
26239406Sbde	}
26249406Sbde#endif
26256261Sjkh
26269406Sbde	/*
26279406Sbde	 * Check for and log errors, but not too often.
26289406Sbde	 */
26299406Sbde	if (--sio_timeouts_until_log > 0)
26309406Sbde		return;
26319406Sbde	sio_timeouts_until_log = hz / sio_timeout;
26329406Sbde	for (unit = 0; unit < NSIO; ++unit) {
26339406Sbde		int	errnum;
26346261Sjkh
26359406Sbde		com = com_addr(unit);
26369406Sbde		if (com == NULL)
26379406Sbde			continue;
26389406Sbde		for (errnum = 0; errnum < CE_NTYPES; ++errnum) {
26399406Sbde			u_int	delta;
26409406Sbde			u_long	total;
26416261Sjkh
264288088Sjhb			critical_enter();
264372262Sjhb			COM_LOCK();
26449406Sbde			delta = com->delta_error_counts[errnum];
26459406Sbde			com->delta_error_counts[errnum] = 0;
264672262Sjhb			COM_UNLOCK();
264788088Sjhb			critical_exit();
26489406Sbde			if (delta == 0)
26499406Sbde				continue;
26509406Sbde			total = com->error_counts[errnum] += delta;
26519406Sbde			log(LOG_ERR, "cy%d: %u more %s%s (total %lu)\n",
26529406Sbde			    unit, delta, error_desc[errnum],
26539406Sbde			    delta == 1 ? "" : "s", total);
26549406Sbde		}
26559406Sbde	}
26566261Sjkh}
26576261Sjkh
26589406Sbdestatic void
26599406Sbdedisc_optim(tp, t, com)
26609406Sbde	struct tty	*tp;
26619406Sbde	struct termios	*t;
26629406Sbde	struct com_s	*com;
26636712Spst{
26649406Sbde#ifndef SOFT_HOTCHAR
26659406Sbde	u_char	opt;
26669406Sbde#endif
26676261Sjkh
26689406Sbde	/*
26699406Sbde	 * XXX can skip a lot more cases if Smarts.  Maybe
26709757Sbde	 * (IGNCR | ISTRIP | IXON) in c_iflag.  But perhaps we
26719406Sbde	 * shouldn't skip if (TS_CNTTB | TS_LNCH) is set in t_state.
26729406Sbde	 */
26739757Sbde	if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON))
26749406Sbde	    && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK))
26759757Sbde	    && (!(t->c_iflag & PARMRK)
26769757Sbde		|| (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK))
26779757Sbde	    && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN))
26789406Sbde	    && linesw[tp->t_line].l_rint == ttyinput)
26799406Sbde		tp->t_state |= TS_CAN_BYPASS_L_RINT;
26809406Sbde	else
26819406Sbde		tp->t_state &= ~TS_CAN_BYPASS_L_RINT;
268233322Sphk	com->hotchar = linesw[tp->t_line].l_hotchar;
26839406Sbde#ifndef SOFT_HOTCHAR
26849406Sbde	opt = com->cor[2] & ~CD1400_COR3_SCD34;
26859406Sbde	if (com->hotchar != 0) {
268641293Sbde		cd_setreg(com, CD1400_SCHR3, com->hotchar);
268741293Sbde		cd_setreg(com, CD1400_SCHR4, com->hotchar);
26889406Sbde		opt |= CD1400_COR3_SCD34;
26899406Sbde	}
26909406Sbde	if (opt != com->cor[2]) {
269141293Sbde		cd_setreg(com, CD1400_COR3, com->cor[2] = opt);
269241293Sbde		cd1400_channel_cmd(com, CD1400_CCR_CMDCORCHG | CD1400_CCR_COR3);
26939406Sbde	}
26949406Sbde#endif
26956712Spst}
26966712Spst
26979406Sbde#ifdef Smarts
26989406Sbde/* standard line discipline input routine */
26996261Sjkhint
27009406Sbdecyinput(c, tp)
27019406Sbde	int		c;
27029406Sbde	struct tty	*tp;
27036261Sjkh{
27049406Sbde	/* XXX duplicate ttyinput(), but without the IXOFF/IXON/ISTRIP/IPARMRK
27059406Sbde	 * bits, as they are done by the CD1400.  Hardly worth the effort,
27069406Sbde	 * given that high-throughput sessions are raw anyhow.
27079406Sbde	 */
27089406Sbde}
27099406Sbde#endif /* Smarts */
27106261Sjkh
27119406Sbdestatic int
271238303Sbdecomspeed(speed, cy_clock, prescaler_io)
27139406Sbde	speed_t	speed;
271438303Sbde	u_long	cy_clock;
27159406Sbde	int	*prescaler_io;
27169406Sbde{
27179406Sbde	int	actual;
27189406Sbde	int	error;
27199406Sbde	int	divider;
27209406Sbde	int	prescaler;
27219406Sbde	int	prescaler_unit;
27226261Sjkh
27239406Sbde	if (speed == 0)
27249406Sbde		return (0);
27259406Sbde	if (speed < 0 || speed > 150000)
27269406Sbde		return (-1);
27276261Sjkh
27289406Sbde	/* determine which prescaler to use */
27299406Sbde	for (prescaler_unit = 4, prescaler = 2048; prescaler_unit;
27309406Sbde		prescaler_unit--, prescaler >>= 2) {
273138303Sbde		if (cy_clock / prescaler / speed > 63)
27329406Sbde			break;
27339406Sbde	}
27346261Sjkh
273538303Sbde	divider = (cy_clock / prescaler * 2 / speed + 1) / 2; /* round off */
27369406Sbde	if (divider > 255)
27379406Sbde		divider = 255;
273838303Sbde	actual = cy_clock/prescaler/divider;
27396261Sjkh
274037959Sbde	/* 10 times error in percent: */
274137959Sbde	error = ((actual - (long)speed) * 2000 / (long)speed + 1) / 2;
274237959Sbde
27439406Sbde	/* 3.0% max error tolerance */
27449406Sbde	if (error < -30 || error > 30)
27459406Sbde		return (-1);
27466261Sjkh
27476261Sjkh#if 0
27489406Sbde	printf("prescaler = %d (%d)\n", prescaler, prescaler_unit);
27499406Sbde	printf("divider = %d (%x)\n", divider, divider);
27509406Sbde	printf("actual = %d\n", actual);
27519406Sbde	printf("error = %d\n", error);
27526261Sjkh#endif
27536261Sjkh
27549406Sbde	*prescaler_io = prescaler_unit;
27559406Sbde	return (divider);
27569406Sbde}
27576261Sjkh
27586261Sjkhstatic void
275941293Sbdecd1400_channel_cmd(com, cmd)
276041293Sbde	struct com_s	*com;
276141293Sbde	int		cmd;
27626261Sjkh{
276341940Sbde	cd1400_channel_cmd_wait(com);
276441940Sbde	cd_setreg(com, CD1400_CCR, cmd);
276541940Sbde	cd1400_channel_cmd_wait(com);
276641940Sbde}
27676261Sjkh
276841940Sbdestatic void
276941940Sbdecd1400_channel_cmd_wait(com)
277041940Sbde	struct com_s	*com;
277141940Sbde{
277241940Sbde	struct timeval	start;
277341940Sbde	struct timeval	tv;
277441940Sbde	long		usec;
27758876Srgrimes
277641940Sbde	if (cd_getreg(com, CD1400_CCR) == 0)
277741940Sbde		return;
277841940Sbde	microtime(&start);
277941940Sbde	for (;;) {
278041940Sbde		if (cd_getreg(com, CD1400_CCR) == 0)
278141940Sbde			return;
278241940Sbde		microtime(&tv);
278341940Sbde		usec = 1000000 * (tv.tv_sec - start.tv_sec) +
278441940Sbde		    tv.tv_usec - start.tv_usec;
278541940Sbde		if (usec >= 5000) {
278641940Sbde			log(LOG_ERR,
278741940Sbde			    "cy%d: channel command timeout (%ld usec)\n",
278841940Sbde			    com->unit, usec);
278941940Sbde			return;
279041940Sbde		}
279141940Sbde	}
27929406Sbde}
27936261Sjkh
279441903Sbdestatic void
279541903Sbdecd_etc(com, etc)
279641903Sbde	struct com_s	*com;
279741903Sbde	int		etc;
279841903Sbde{
279965557Sjasone
280041903Sbde	/*
280141903Sbde	 * We can't change the hardware's ETC state while there are any
280241903Sbde	 * characters in the tx fifo, since those characters would be
280341903Sbde	 * interpreted as commands!  Unputting characters from the fifo
280441903Sbde	 * is difficult, so we wait up to 12 character times for the fifo
280541903Sbde	 * to drain.  The command will be delayed for up to 2 character
280641903Sbde	 * times for the tx to become empty.  Unputting characters from
280741903Sbde	 * the tx holding and shift registers is impossible, so we wait
280841903Sbde	 * for the tx to become empty so that the command is sure to be
280941903Sbde	 * executed soon after we issue it.
281041903Sbde	 */
281188088Sjhb	critical_enter();
281272262Sjhb	COM_LOCK();
281365557Sjasone	if (com->etc == etc)
281441903Sbde		goto wait;
281543314Sdillon	if ((etc == CD1400_ETC_SENDBREAK
281641903Sbde	    && (com->etc == ETC_BREAK_STARTING
281743314Sdillon		|| com->etc == ETC_BREAK_STARTED))
281843314Sdillon	    || (etc == CD1400_ETC_STOPBREAK
281941903Sbde	       && (com->etc == ETC_BREAK_ENDING || com->etc == ETC_BREAK_ENDED
282043314Sdillon		   || com->etc == ETC_NONE))) {
282172262Sjhb		COM_UNLOCK();
282288088Sjhb		critical_exit();
282341903Sbde		return;
282441903Sbde	}
282541903Sbde	com->etc = etc;
282641903Sbde	cd_setreg(com, CD1400_SRER,
282741903Sbde		  com->intr_enable
282843314Sdillon		  = (com->intr_enable & ~CD1400_SRER_TXRDY) | CD1400_SRER_TXMPTY);
282941903Sbdewait:
283072262Sjhb	COM_UNLOCK();
283188088Sjhb	critical_exit();
283241903Sbde	while (com->etc == etc
283341903Sbde	       && tsleep(&com->etc, TTIPRI | PCATCH, "cyetc", 0) == 0)
283441903Sbde		continue;
283541903Sbde}
283641903Sbde
283741293Sbdestatic int
283841293Sbdecd_getreg(com, reg)
283941293Sbde	struct com_s	*com;
284041293Sbde	int		reg;
284141293Sbde{
284241293Sbde	struct com_s	*basecom;
284341293Sbde	u_char	car;
284441293Sbde	int	cy_align;
284574903Sjhb	register_t	eflags;
284641293Sbde	cy_addr	iobase;
2847120504Sbde#ifdef SMP
2848120504Sbde	int	need_unlock;
2849120504Sbde#endif
285041293Sbde	int	val;
285141293Sbde
285241293Sbde	basecom = com_addr(com->unit & ~(CD1400_NO_OF_CHANNELS - 1));
285341293Sbde	car = com->unit & CD1400_CAR_CHAN;
285441293Sbde	cy_align = com->cy_align;
285541293Sbde	iobase = com->iobase;
285674903Sjhb	eflags = read_eflags();
285788088Sjhb	critical_enter();
2858120504Sbde#ifdef SMP
2859120504Sbde	need_unlock = 0;
2860122771Sbde	if (!mtx_owned(&sio_lock)) {
286172262Sjhb		COM_LOCK();
2862120504Sbde		need_unlock = 1;
2863120504Sbde	}
2864120504Sbde#endif
286541293Sbde	if (basecom->car != car)
286641293Sbde		cd_outb(iobase, CD1400_CAR, cy_align, basecom->car = car);
286741293Sbde	val = cd_inb(iobase, reg, cy_align);
2868120504Sbde#ifdef SMP
2869120504Sbde	if (need_unlock)
287072262Sjhb		COM_UNLOCK();
2871120504Sbde#endif
287288088Sjhb	critical_exit();
287341293Sbde	return (val);
287441293Sbde}
287541293Sbde
287641293Sbdestatic void
287741293Sbdecd_setreg(com, reg, val)
287841293Sbde	struct com_s	*com;
287941293Sbde	int		reg;
288041293Sbde	int		val;
288141293Sbde{
288241293Sbde	struct com_s	*basecom;
288341293Sbde	u_char	car;
288441293Sbde	int	cy_align;
288574903Sjhb	register_t	eflags;
288641293Sbde	cy_addr	iobase;
2887120504Sbde#ifdef SMP
2888120504Sbde	int	need_unlock;
2889120504Sbde#endif
289041293Sbde
289141293Sbde	basecom = com_addr(com->unit & ~(CD1400_NO_OF_CHANNELS - 1));
289241293Sbde	car = com->unit & CD1400_CAR_CHAN;
289341293Sbde	cy_align = com->cy_align;
289441293Sbde	iobase = com->iobase;
289574903Sjhb	eflags = read_eflags();
289688088Sjhb	critical_enter();
2897120504Sbde#ifdef SMP
2898120504Sbde	need_unlock = 0;
2899122771Sbde	if (!mtx_owned(&sio_lock)) {
290072262Sjhb		COM_LOCK();
2901120504Sbde		need_unlock = 1;
2902120504Sbde	}
2903120504Sbde#endif
290441293Sbde	if (basecom->car != car)
290541293Sbde		cd_outb(iobase, CD1400_CAR, cy_align, basecom->car = car);
290641293Sbde	cd_outb(iobase, reg, cy_align, val);
2907120504Sbde#ifdef SMP
2908120504Sbde	if (need_unlock)
290972262Sjhb		COM_UNLOCK();
2910120504Sbde#endif
291188088Sjhb	critical_exit();
291241293Sbde}
291341293Sbde
29146261Sjkh#ifdef CyDebug
29156261Sjkh/* useful in ddb */
29166261Sjkhvoid
29179406Sbdecystatus(unit)
29189406Sbde	int	unit;
29196261Sjkh{
29209406Sbde	struct com_s	*com;
29219406Sbde	cy_addr		iobase;
29229406Sbde	u_int		ocount;
29239406Sbde	struct tty	*tp;
29246261Sjkh
29259406Sbde	com = com_addr(unit);
29266261Sjkh	printf("info for channel %d\n", unit);
29276261Sjkh	printf("------------------\n");
29289406Sbde	printf("total cyclom service probes:\t%d\n", cy_svrr_probes);
29299406Sbde	printf("calls to upper layer:\t\t%d\n", cy_timeouts);
29309406Sbde	if (com == NULL)
29319406Sbde		return;
29329406Sbde	iobase = com->iobase;
29336261Sjkh	printf("\n");
29349406Sbde	printf("cd1400 base address:\\tt%p\n", iobase);
29359406Sbde	printf("saved channel_control:\t\t0x%02x\n", com->channel_control);
29369406Sbde	printf("saved cor1-3:\t\t\t0x%02x 0x%02x 0x%02x\n",
29379406Sbde	       com->cor[0], com->cor[1], com->cor[2]);
29389406Sbde	printf("service request enable reg:\t0x%02x (0x%02x cached)\n",
293941293Sbde	       cd_getreg(com, CD1400_SRER), com->intr_enable);
29409406Sbde	printf("service request register:\t0x%02x\n",
294118925Sdg	       cd_inb(iobase, CD1400_SVRR, com->cy_align));
29429406Sbde	printf("modem status:\t\t\t0x%02x (0x%02x cached)\n",
294341293Sbde	       cd_getreg(com, CD1400_MSVR2), com->prev_modem_status);
29449406Sbde	printf("rx/tx/mdm interrupt registers:\t0x%02x 0x%02x 0x%02x\n",
294518925Sdg	       cd_inb(iobase, CD1400_RIR, com->cy_align),
294618925Sdg	       cd_inb(iobase, CD1400_TIR, com->cy_align),
294718925Sdg	       cd_inb(iobase, CD1400_MIR, com->cy_align));
29486261Sjkh	printf("\n");
29499406Sbde	printf("com state:\t\t\t0x%02x\n", com->state);
29509406Sbde	printf("calls to comstart():\t\t%d (%d useful)\n",
29519406Sbde	       com->start_count, com->start_real);
29529406Sbde	printf("rx buffer chars free:\t\t%d\n", com->iptr - com->ibuf);
29539406Sbde	ocount = 0;
29549406Sbde	if (com->obufs[0].l_queued)
29559406Sbde		ocount += com->obufs[0].l_tail - com->obufs[0].l_head;
29569406Sbde	if (com->obufs[1].l_queued)
29579406Sbde		ocount += com->obufs[1].l_tail - com->obufs[1].l_head;
29589406Sbde	printf("tx buffer chars:\t\t%u\n", ocount);
29599406Sbde	printf("received chars:\t\t\t%d\n", com->bytes_in);
29609406Sbde	printf("received exceptions:\t\t%d\n", com->recv_exception);
29619406Sbde	printf("modem signal deltas:\t\t%d\n", com->mdm);
29629406Sbde	printf("transmitted chars:\t\t%d\n", com->bytes_out);
29639406Sbde	printf("\n");
29649406Sbde	tp = com->tp;
29659406Sbde	if (tp != NULL) {
29669406Sbde		printf("tty state:\t\t\t0x%08x\n", tp->t_state);
296729047Sbde		printf(
296829047Sbde		"upper layer queue lengths:\t%d raw, %d canon, %d output\n",
29699406Sbde		       tp->t_rawq.c_cc, tp->t_canq.c_cc, tp->t_outq.c_cc);
29709406Sbde	} else
29716261Sjkh		printf("tty state:\t\t\tclosed\n");
29729406Sbde}
29739406Sbde#endif /* CyDebug */
2974