sio.c revision 45267
118334Speter/*-
218334Speter * Copyright (c) 1991 The Regents of the University of California.
352561Sobrien * All rights reserved.
418334Speter *
518334Speter * Redistribution and use in source and binary forms, with or without
618334Speter * modification, are permitted provided that the following conditions
718334Speter * are met:
818334Speter * 1. Redistributions of source code must retain the above copyright
918334Speter *    notice, this list of conditions and the following disclaimer.
1018334Speter * 2. Redistributions in binary form must reproduce the above copyright
1118334Speter *    notice, this list of conditions and the following disclaimer in the
1218334Speter *    documentation and/or other materials provided with the distribution.
1318334Speter * 3. All advertising materials mentioning features or use of this software
1418334Speter *    must display the following acknowledgement:
1518334Speter *	This product includes software developed by the University of
1618334Speter *	California, Berkeley and its contributors.
1718334Speter * 4. Neither the name of the University nor the names of its contributors
1818334Speter *    may be used to endorse or promote products derived from this software
1918334Speter *    without specific prior written permission.
2018334Speter *
2118334Speter * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2218334Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2318334Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2418334Speter * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2518334Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2618334Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2718334Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2818334Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2952561Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3052561Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3118334Speter * SUCH DAMAGE.
3218334Speter *
3318334Speter *	from: @(#)com.c	7.5 (Berkeley) 5/16/91
3418334Speter *	$Id: sio.c,v 1.84 1999/04/01 13:44:15 kato Exp $
3518334Speter */
3650600Sobrien
3750600Sobrien#include "opt_comconsole.h"
3850600Sobrien#include "opt_compat.h"
3950600Sobrien#include "opt_ddb.h"
4050600Sobrien#include "opt_devfs.h"
4150600Sobrien#include "opt_sio.h"
4250600Sobrien#include "sio.h"
4350600Sobrien#include "pnp.h"
4450600Sobrien
4550600Sobrien/*
4650600Sobrien * Serial driver, based on 386BSD-0.1 com driver.
4718334Speter * Mostly rewritten to use pseudo-DMA.
4818334Speter * Works for National Semiconductor NS8250-NS16550AF UARTs.
4918334Speter * COM driver, based on HP dca driver.
5018334Speter *
5118334Speter * Changes for PC-Card integration:
5218334Speter *	- Added PC-Card driver table and handlers
5318334Speter */
5418334Speter/*===============================================================
5518334Speter * 386BSD(98),FreeBSD-1.1x(98) com driver.
5618334Speter * -----
5718334Speter * modified for PC9801 by M.Ishii
5850600Sobrien *			Kyoto University Microcomputer Club (KMC)
5918334Speter * Chou "TEFUTEFU" Hirotomi
6018334Speter *			Kyoto Univ.  the faculty of medicine
6118334Speter *===============================================================
6250600Sobrien * FreeBSD-2.0.1(98) sio driver.
6350600Sobrien * -----
6450600Sobrien * modified for pc98 Internal i8251 and MICRO CORE MC16550II
6550600Sobrien *			T.Koike(hfc01340@niftyserve.or.jp)
6650600Sobrien * implement kernel device configuration
6750600Sobrien *			aizu@orient.center.nitech.ac.jp
6850600Sobrien *
6918334Speter * Notes.
7018334Speter * -----
7118334Speter *  PC98 localization based on 386BSD(98) com driver. Using its PC98 local
7218334Speter *  functions.
7318334Speter *  This driver is under debugging,has bugs.
7418334Speter *
7518334Speter * 1) config
7618334Speter *  options COM_MULTIPORT  #if using MC16550II
7752561Sobrien *  device sio0 at nec? port 0x30  tty irq 4             #internal
7852561Sobrien *  device sio1 at nec? port 0xd2  tty irq 5 flags 0x101 #mc1
7952561Sobrien *  device sio2 at nec? port 0x8d2 tty flags 0x101       #mc2
8052561Sobrien *                         # ~~~~~iobase        ~~multi port flag
8118334Speter *                         #                   ~  master device is sio1
8218334Speter * 2) device
8318334Speter *  cd /dev; MAKEDEV ttyd0 ttyd1 ..
8418334Speter * 3) /etc/rc.serial
8518334Speter *  57600bps is too fast for sio0(internal8251)
8618334Speter *  my ex.
8718334Speter *    #set default speed 9600
8818334Speter *    modem()
8918334Speter *       :
9018334Speter *      stty </dev/ttyid$i crtscts 9600
9118334Speter *       :                 #       ~~~~ default speed(can change after init.)
9218334Speter *    modem 0 1 2
9318334Speter * 4) COMCONSOLE
9418334Speter *  not changed.
9518334Speter * 5) PC9861K,PIO9032B,B98_01
9618334Speter *  not tested.
9718334Speter */
9818334Speter/*
9952561Sobrien * modified for AIWA B98-01
10018334Speter * by T.Hatanou <hatanou@yasuda.comm.waseda.ac.jp>  last update: 15 Sep.1995
10118334Speter *
10252561Sobrien * How to configure...
10318334Speter *   # options COM_MULTIPORT         # support for MICROCORE MC16550II
10418334Speter *      ... comment-out this line, which will conflict with B98_01.
10518334Speter *   options "B98_01"                # support for AIWA B98-01
10618334Speter *   device  sio1 at nec? port 0x00d1 tty irq ?
10718334Speter *   device  sio2 at nec? port 0x00d5 tty irq ?
10818334Speter *      ... you can leave these lines `irq ?', irq will be autodetected.
10918334Speter */
11018334Speter/*
11118334Speter * Modified by Y.Takahashi of Kogakuin University.
11218334Speter */
11318334Speter
11418334Speter#ifdef PC98
11518334Speter#define COM_IF_INTERNAL		0x00
11618334Speter#define COM_IF_PC9861K_1	0x01
11718334Speter#define COM_IF_PC9861K_2	0x02
11818334Speter#define COM_IF_IND_SS_1		0x03
11918334Speter#define COM_IF_IND_SS_2		0x04
12018334Speter#define COM_IF_PIO9032B_1	0x05
12118334Speter#define COM_IF_PIO9032B_2	0x06
12218334Speter#define COM_IF_B98_01_1		0x07
12318334Speter#define COM_IF_B98_01_2		0x08
12418334Speter#define COM_IF_END1		COM_IF_B98_01_2
12518334Speter#define COM_IF_RSA98		0x10	/* same as COM_IF_NS16550 */
12618334Speter#define COM_IF_NS16550		0x11
12718334Speter#define COM_IF_SECOND_CCU	0x12	/* same as COM_IF_NS16550 */
12818334Speter#define COM_IF_MC16550II	0x13
12918334Speter#define COM_IF_MCRS98		0x14	/* same as COM_IF_MC16550II */
13018334Speter#define COM_IF_RSB3000		0x15
13118334Speter#define COM_IF_RSB384		0x16
13218334Speter#define COM_IF_MODEM_CARD	0x17	/* same as COM_IF_NS16550 */
13318334Speter#define COM_IF_RSA98III		0x18
13418334Speter#define COM_IF_ESP98		0x19
13518334Speter#define COM_IF_END2		COM_IF_ESP98
13618334Speter#endif /* PC98 */
13718334Speter
13818334Speter#include <sys/param.h>
13918334Speter#include <sys/systm.h>
14018334Speter#include <sys/reboot.h>
14118334Speter#include <sys/malloc.h>
14218334Speter#include <sys/tty.h>
14318334Speter#include <sys/proc.h>
14418334Speter#include <sys/conf.h>
14518334Speter#include <sys/dkstat.h>
14618334Speter#include <sys/fcntl.h>
14718334Speter#include <sys/interrupt.h>
14818334Speter#include <sys/kernel.h>
14918334Speter#include <sys/syslog.h>
15018334Speter#include <sys/sysctl.h>
15118334Speter#ifdef DEVFS
15218334Speter#include <sys/devfsext.h>
15352561Sobrien#endif
15418334Speter#include <sys/timepps.h>
15518334Speter
15652561Sobrien#include <machine/clock.h>
15718334Speter#include <machine/ipl.h>
15818334Speter#ifndef SMP
15918334Speter#include <machine/lock.h>
16018334Speter#endif
16118334Speter
16218334Speter#ifdef PC98
16318334Speter#include <pc98/pc98/pc98.h>
16418334Speter#include <pc98/pc98/pc98_machdep.h>
16550600Sobrien#include <i386/isa/icu.h>
16618334Speter#include <i386/isa/ic/i8251.h>
16718334Speter#else
16818334Speter#include <i386/isa/isa.h>
16918334Speter#endif
17018334Speter#include <i386/isa/isa_device.h>
17118334Speter#include <i386/isa/sioreg.h>
17218334Speter#include <i386/isa/intr_machdep.h>
17318334Speter
17418334Speter#ifdef COM_ESP
17518334Speter#include <i386/isa/ic/esp.h>
17618334Speter#endif
17718334Speter#include <i386/isa/ic/ns16550.h>
17818334Speter#ifdef PC98
17950600Sobrien#include <i386/isa/ic/rsa.h>
18050600Sobrien#endif
18118334Speter
18218334Speter#include "card.h"
18350600Sobrien#if NCARD > 0
18418334Speter#include <sys/module.h>
18518334Speter#include <pccard/cardinfo.h>
18618334Speter#include <pccard/slot.h>
18718334Speter#endif
18818334Speter
18918334Speter#if NPNP > 0
19018334Speter#include <i386/isa/pnp.h>
19118334Speter#endif
19218334Speter
19318334Speter#ifdef SMP
19418334Speter#define disable_intr()	COM_DISABLE_INTR()
19518334Speter#define enable_intr()	COM_ENABLE_INTR()
19618334Speter#endif /* SMP */
19718334Speter
19818334Speter#ifndef EXTRA_SIO
19918334Speter#if NPNP > 0
20018334Speter#define EXTRA_SIO MAX_PNP_CARDS
20118334Speter#else
20218334Speter#define EXTRA_SIO 0
20318334Speter#endif
20418334Speter#endif
20518334Speter
20618334Speter#define NSIOTOT (NSIO + EXTRA_SIO)
20718334Speter
20818334Speter#define	LOTS_OF_EVENTS	64	/* helps separate urgent events from input */
20918334Speter
21018334Speter#define	CALLOUT_MASK		0x80
21118334Speter#define	CONTROL_MASK		0x60
21218334Speter#define	CONTROL_INIT_STATE	0x20
21350600Sobrien#define	CONTROL_LOCK_STATE	0x40
21418334Speter#define	DEV_TO_UNIT(dev)	(MINOR_TO_UNIT(minor(dev)))
21518334Speter#define	MINOR_MAGIC_MASK	(CALLOUT_MASK | CONTROL_MASK)
21618334Speter#define	MINOR_TO_UNIT(mynor)	((mynor) & ~MINOR_MAGIC_MASK)
21718334Speter
21818334Speter#ifdef COM_MULTIPORT
21918334Speter/* checks in flags for multiport and which is multiport "master chip"
22018334Speter * for a given card
22118334Speter */
22218334Speter#define	COM_ISMULTIPORT(dev)	((dev)->id_flags & 0x01)
22318334Speter#define	COM_MPMASTER(dev)	(((dev)->id_flags >> 8) & 0x0ff)
22418334Speter#define	COM_NOTAST4(dev)	((dev)->id_flags & 0x04)
22518334Speter#endif /* COM_MULTIPORT */
22618334Speter
22718334Speter#define	COM_CONSOLE(dev)	((dev)->id_flags & 0x10)
22818334Speter#define	COM_FORCECONSOLE(dev)	((dev)->id_flags & 0x20)
22918334Speter#define	COM_LLCONSOLE(dev)	((dev)->id_flags & 0x40)
23018334Speter#define	COM_LOSESOUTINTS(dev)	((dev)->id_flags & 0x08)
23118334Speter#define	COM_NOFIFO(dev)		((dev)->id_flags & 0x02)
23218334Speter#define COM_ST16650A(dev)	((dev)->id_flags & 0x20000)
23318334Speter#define COM_C_NOPROBE     (0x40000)
23418334Speter#define COM_NOPROBE(dev)  ((dev)->id_flags & COM_C_NOPROBE)
23518334Speter#define COM_C_IIR_TXRDYBUG    (0x80000)
23618334Speter#define COM_IIR_TXRDYBUG(dev) ((dev)->id_flags & COM_C_IIR_TXRDYBUG)
23718334Speter#define	COM_FIFOSIZE(dev)	(((dev)->id_flags & 0xff000000) >> 24)
23818334Speter
23918334Speter#ifdef PC98
24018334Speter#define	com_emr		com_msr	/* Extension mode register for RSB-2000/3000 */
24118334Speter#else
24218334Speter#define	com_scr		7	/* scratch register for 16450-16550 (R/W) */
24350600Sobrien#endif
24418334Speter
24518334Speter/*
24618334Speter * com state bits.
24718334Speter * (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher
24818334Speter * than the other bits so that they can be tested as a group without masking
24918334Speter * off the low bits.
25018334Speter *
25118334Speter * The following com and tty flags correspond closely:
25218334Speter *	CS_BUSY		= TS_BUSY (maintained by comstart(), siopoll() and
25318334Speter *				   siostop())
25418334Speter *	CS_TTGO		= ~TS_TTSTOP (maintained by comparam() and comstart())
25518334Speter *	CS_CTS_OFLOW	= CCTS_OFLOW (maintained by comparam())
25618334Speter *	CS_RTS_IFLOW	= CRTS_IFLOW (maintained by comparam())
25718334Speter * TS_FLUSH is not used.
25818334Speter * XXX I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON.
25918334Speter * XXX CS_*FLOW should be CF_*FLOW in com->flags (control flags not state).
26018334Speter */
26118334Speter#define	CS_BUSY		0x80	/* output in progress */
26218334Speter#define	CS_TTGO		0x40	/* output not stopped by XOFF */
26318334Speter#define	CS_ODEVREADY	0x20	/* external device h/w ready (CTS) */
26418334Speter#define	CS_CHECKMSR	1	/* check of MSR scheduled */
26518334Speter#define	CS_CTS_OFLOW	2	/* use CTS output flow control */
26618334Speter#define	CS_DTR_OFF	0x10	/* DTR held off */
26718334Speter#define	CS_ODONE	4	/* output completed */
26818334Speter#define	CS_RTS_IFLOW	8	/* use RTS input flow control */
26918334Speter#define	CSE_BUSYCHECK	1	/* siobusycheck() scheduled */
27018334Speter
27118334Speterstatic	char const * const	error_desc[] = {
27218334Speter#define	CE_OVERRUN			0
27318334Speter	"silo overflow",
27450600Sobrien#define	CE_INTERRUPT_BUF_OVERFLOW	1
27518334Speter	"interrupt-level buffer overflow",
27618334Speter#define	CE_TTY_BUF_OVERFLOW		2
27718334Speter	"tty-level buffer overflow",
27818334Speter};
27918334Speter
28018334Speter#define	CE_NTYPES			3
28118334Speter#define	CE_RECORD(com, errnum)		(++(com)->delta_error_counts[errnum])
28218334Speter
28318334Speter/* types.  XXX - should be elsewhere */
28418334Spetertypedef u_int	Port_t;		/* hardware port */
28518334Spetertypedef u_char	bool_t;		/* boolean */
28618334Speter
28718334Speter/* queue of linear buffers */
28818334Speterstruct lbq {
28918334Speter	u_char	*l_head;	/* next char to process */
29018334Speter	u_char	*l_tail;	/* one past the last char to process */
29118334Speter	struct lbq *l_next;	/* next in queue */
29218334Speter	bool_t	l_queued;	/* nonzero if queued */
29318334Speter};
29450600Sobrien
29518334Speter/* com device structure */
29618334Speterstruct com_s {
29718334Speter	u_int	id_flags;	/* Copy isa device falgas */
29818334Speter	u_char	state;		/* miscellaneous flag bits */
29918334Speter	bool_t  active_out;	/* nonzero if the callout device is open */
30018334Speter	u_char	cfcr_image;	/* copy of value written to CFCR */
30118334Speter#ifdef COM_ESP
30218334Speter	bool_t	esp;		/* is this unit a hayes esp board? */
30318334Speter#endif
30418334Speter	u_char	extra_state;	/* more flag bits, separate for order trick */
30518334Speter	u_char	fifo_image;	/* copy of value written to FIFO */
30618334Speter	bool_t	hasfifo;	/* nonzero for 16550 UARTs */
30718334Speter	bool_t	st16650a;	/* Is a Startech 16650A or RTS/CTS compat */
30818334Speter	bool_t	loses_outints;	/* nonzero if device loses output interrupts */
30918334Speter	u_char	mcr_image;	/* copy of value written to MCR */
31018334Speter#ifdef COM_MULTIPORT
31118334Speter	bool_t	multiport;	/* is this unit part of a multiport device? */
31218334Speter#endif /* COM_MULTIPORT */
31350600Sobrien	bool_t	no_irq;		/* nonzero if irq is not attached */
31418334Speter	bool_t  gone;		/* hardware disappeared */
31518334Speter	bool_t	poll;		/* nonzero if polling is required */
31618334Speter	bool_t	poll_output;	/* nonzero if polling for output is required */
31718334Speter	int	unit;		/* unit	number */
31818334Speter	int	dtr_wait;	/* time to hold DTR down on close (* 1/hz) */
31918334Speter	u_int	tx_fifo_size;
32018334Speter	u_int	wopeners;	/* # processes waiting for DCD in open() */
32118334Speter
32218334Speter	/*
32318334Speter	 * The high level of the driver never reads status registers directly
32418334Speter	 * because there would be too many side effects to handle conveniently.
32518334Speter	 * Instead, it reads copies of the registers stored here by the
32618334Speter	 * interrupt handler.
32718334Speter	 */
32818334Speter	u_char	last_modem_status;	/* last MSR read by intr handler */
32918334Speter	u_char	prev_modem_status;	/* last MSR handled by high level */
33018334Speter
33118334Speter	u_char	hotchar;	/* ldisc-specific char to be handled ASAP */
33218334Speter	u_char	*ibuf;		/* start of input buffer */
33318334Speter	u_char	*ibufend;	/* end of input buffer */
33418334Speter	u_char	*ibufold;	/* old input buffer, to be freed */
33518334Speter	u_char	*ihighwater;	/* threshold in input buffer */
33618334Speter	u_char	*iptr;		/* next free spot in input buffer */
33718334Speter	int	ibufsize;	/* size of ibuf (not include error bytes) */
33818334Speter	int	ierroff;	/* offset of error bytes in ibuf */
33918334Speter
34018334Speter	struct lbq	obufq;	/* head of queue of output buffers */
34118334Speter	struct lbq	obufs[2];	/* output buffers */
34218334Speter
34318334Speter#ifdef PC98
34418334Speter	Port_t	cmd_port;
34518334Speter	Port_t	sts_port;
34618334Speter	Port_t	in_modem_port;
34718334Speter	Port_t	intr_ctrl_port;
34818334Speter	int	intr_enable;
34918334Speter	int	pc98_prev_modem_status;
35018334Speter	int	pc98_modem_delta;
35118334Speter	int	modem_car_chg_timer;
35218334Speter	int	pc98_prev_siocmd;
35318334Speter	int	pc98_prev_siomod;
35418334Speter	int	modem_checking;
35518334Speter	int	pc98_if_type;
35618334Speter#endif /* PC98 */
35718334Speter	Port_t	data_port;	/* i/o ports */
35818334Speter#ifdef COM_ESP
35918334Speter	Port_t	esp_port;
36018334Speter#endif
36118334Speter	Port_t	int_id_port;
36218334Speter	Port_t	iobase;
36318334Speter#ifdef PC98
36418334Speter	Port_t	rsabase;	/* iobase address of a I/O-DATA RSA board */
36518334Speter#endif
36618334Speter	Port_t	modem_ctl_port;
36718334Speter	Port_t	line_status_port;
36818334Speter	Port_t	modem_status_port;
36918334Speter	Port_t	intr_ctl_port;	/* Ports of IIR register */
37018334Speter
37118334Speter	struct tty	*tp;	/* cross reference */
37218334Speter
37318334Speter	/* Initial state. */
37418334Speter	struct termios	it_in;	/* should be in struct tty */
37518334Speter	struct termios	it_out;
37618334Speter
37718334Speter	/* Lock state. */
37818334Speter	struct termios	lt_in;	/* should be in struct tty */
37918334Speter	struct termios	lt_out;
38018334Speter
38118334Speter	bool_t	do_timestamp;
38218334Speter	bool_t	do_dcd_timestamp;
38318334Speter	struct timeval	timestamp;
38418334Speter	struct timeval	dcd_timestamp;
38518334Speter	struct	pps_state pps;
38618334Speter
38718334Speter	u_long	bytes_in;	/* statistics */
38818334Speter	u_long	bytes_out;
38918334Speter	u_int	delta_error_counts[CE_NTYPES];
39018334Speter	u_long	error_counts[CE_NTYPES];
39118334Speter
39218334Speter	/*
39318334Speter	 * Data area for output buffers.  Someday we should build the output
39418334Speter	 * buffer queue without copying data.
39518334Speter	 */
39618334Speter#ifdef PC98
39718334Speter	int	obufsize;
39818334Speter 	u_char	*obuf1;
39918334Speter 	u_char	*obuf2;
40018334Speter#else
40118334Speter	u_char	obuf1[256];
40218334Speter	u_char	obuf2[256];
40318334Speter#endif
40418334Speter#ifdef DEVFS
40518334Speter	void	*devfs_token_ttyd;
40618334Speter	void	*devfs_token_ttyl;
40718334Speter	void	*devfs_token_ttyi;
40818334Speter	void	*devfs_token_cuaa;
40918334Speter	void	*devfs_token_cual;
41018334Speter	void	*devfs_token_cuai;
41150600Sobrien#endif
41250600Sobrien};
41350600Sobrien
41450600Sobrien#ifdef COM_ESP
41550600Sobrienstatic	int	espattach	__P((struct isa_device *isdp, struct com_s *com,
41650600Sobrien				     Port_t esp_port));
41750600Sobrien#endif
41818334Speterstatic	int	sioattach	__P((struct isa_device *dev));
41918334Speterstatic	timeout_t siobusycheck;
42018334Speterstatic	timeout_t siodtrwakeup;
42118334Speterstatic	void	comhardclose	__P((struct com_s *com));
42218334Speterstatic	void	sioinput	__P((struct com_s *com));
42318334Speterstatic	ointhand2_t	siointr;
42418334Speterstatic	void	siointr1	__P((struct com_s *com));
42518334Speterstatic	int	commctl		__P((struct com_s *com, int bits, int how));
42618334Speterstatic	int	comparam	__P((struct tty *tp, struct termios *t));
42718334Speterstatic	swihand_t siopoll;
42818334Speterstatic	int	sioprobe	__P((struct isa_device *dev));
42918334Speterstatic	void	siosettimeout	__P((void));
43018334Speterstatic	int	siosetwater	__P((struct com_s *com, speed_t speed));
43118334Speterstatic	void	comstart	__P((struct tty *tp));
43218334Speterstatic	timeout_t comwakeup;
43318334Speterstatic	void	disc_optim	__P((struct tty	*tp, struct termios *t,
43418334Speter				     struct com_s *com));
43518334Speter
43618334Speter
43718334Speterstatic char driver_name[] = "sio";
43818334Speter
43918334Speter/* table and macro for fast conversion from a unit number to its com struct */
44018334Speterstatic	struct com_s	*p_com_addr[NSIOTOT];
44118334Speter#define	com_addr(unit)	(p_com_addr[unit])
44218334Speter
44318334Speterstruct isa_driver	siodriver = {
44450600Sobrien	sioprobe, sioattach, driver_name
44518334Speter};
44618334Speter
44718334Speterstatic	d_open_t	sioopen;
44818334Speterstatic	d_close_t	sioclose;
44918334Speterstatic	d_read_t	sioread;
45018334Speterstatic	d_write_t	siowrite;
45118334Speterstatic	d_ioctl_t	sioioctl;
45218334Speterstatic	d_stop_t	siostop;
45318334Speterstatic	d_devtotty_t	siodevtotty;
45418334Speter
45518334Speter#define	CDEV_MAJOR	28
45618334Speterstatic	struct cdevsw	sio_cdevsw = {
45718334Speter	sioopen,	sioclose,	sioread,	siowrite,
45818334Speter	sioioctl,	siostop,	noreset,	siodevtotty,
45918334Speter	ttpoll,		nommap,		NULL,		driver_name,
46018334Speter	NULL,		-1,		nodump,		nopsize,
46118334Speter	D_TTY,
46218334Speter};
46318334Speter
46418334Speterstatic	int	comconsole = -1;
46518334Speterstatic	volatile speed_t	comdefaultrate = CONSPEED;
46618334Speterstatic	u_int	com_events;	/* input chars + weighted output completions */
46718334Speterstatic	Port_t	siocniobase;
46818334Speterstatic	bool_t	sio_registered;
46918334Speterstatic	int	sio_timeout;
47018334Speterstatic	int	sio_timeouts_until_log;
47118334Speterstatic	struct	callout_handle sio_timeout_handle
47218334Speter    = CALLOUT_HANDLE_INITIALIZER(&sio_timeout_handle);
47318334Speter#if 0 /* XXX */
47418334Speterstatic struct tty	*sio_tty[NSIOTOT];
47518334Speter#else
47618334Speterstatic struct tty	sio_tty[NSIOTOT];
47718334Speter#endif
47818334Speterstatic	const int	nsio_tty = NSIOTOT;
47918334Speter
48018334Speter#ifdef PC98
48118334Speterstruct	siodev	{
48218334Speter	short	if_type;
48318334Speter	short	irq;
48418334Speter	Port_t	cmd, sts, ctrl, mod;
48518334Speter};
48618334Speterstatic	int	sysclock;
48718334Speter
48818334Speter#define	COM_INT_DISABLE		{int previpri; previpri=spltty();
48918334Speter#define	COM_INT_ENABLE		splx(previpri);}
49018334Speter#define IEN_TxFLAG		IEN_Tx
49118334Speter
49218334Speter#define COM_CARRIER_DETECT_EMULATE	0
49318334Speter#define	PC98_CHECK_MODEM_INTERVAL	(hz/10)
49418334Speter#define DCD_OFF_TOLERANCE		2
49518334Speter#define DCD_ON_RECOGNITION		2
49618334Speter#define	IS_8251(if_type)		(!(if_type & 0x10))
49718334Speter#define COM1_EXT_CLOCK			0x40000
49818334Speter
49918334Speterstatic	void	commint		__P((dev_t dev));
50018334Speterstatic	void	com_tiocm_set	__P((struct com_s *com, int msr));
50118334Speterstatic	void	com_tiocm_bis	__P((struct com_s *com, int msr));
50218334Speterstatic	void	com_tiocm_bic	__P((struct com_s *com, int msr));
50318334Speterstatic	int	com_tiocm_get	__P((struct com_s *com));
50418334Speterstatic	int	com_tiocm_get_delta	__P((struct com_s *com));
50518334Speterstatic	void	pc98_msrint_start	__P((dev_t dev));
50618334Speterstatic	void	com_cflag_and_speed_set	__P((struct com_s *com, int cflag, int speed));
50718334Speterstatic	int	pc98_ttspeedtab		__P((struct com_s *com, int speed));
50818334Speterstatic	int	pc98_get_modem_status	__P((struct com_s *com));
50918334Speterstatic	timeout_t	pc98_check_msr;
51018334Speterstatic	void	pc98_set_baud_rate	__P((struct com_s *com, int count));
51118334Speterstatic	void	pc98_i8251_reset	__P((struct com_s *com, int mode, int command));
51218334Speterstatic	void	pc98_disable_i8251_interrupt	__P((struct com_s *com, int mod));
51318334Speterstatic	void	pc98_enable_i8251_interrupt	__P((struct com_s *com, int mod));
51418334Speterstatic	int	pc98_check_i8251_interrupt	__P((struct com_s *com));
51518334Speterstatic	int	pc98_i8251_get_cmd	__P((struct com_s *com));
51618334Speterstatic	int	pc98_i8251_get_mod	__P((struct com_s *com));
51718334Speterstatic	void	pc98_i8251_set_cmd	__P((struct com_s *com, int x));
51818334Speterstatic	void	pc98_i8251_or_cmd	__P((struct com_s *com, int x));
51918334Speterstatic	void	pc98_i8251_clear_cmd	__P((struct com_s *com, int x));
52018334Speterstatic	void	pc98_i8251_clear_or_cmd	__P((struct com_s *com, int clr, int x));
52118334Speterstatic	int	pc98_check_if_type	__P((struct isa_device *dev, struct siodev *iod));
52218334Speterstatic	void	pc98_check_sysclock	__P((void));
52318334Speterstatic	int	pc98_set_ioport		__P((struct com_s *com, int id_flags));
52418334Speter
52518334Speter#define com_int_Tx_disable(com) \
52618334Speter		pc98_disable_i8251_interrupt(com,IEN_Tx|IEN_TxEMP)
52718334Speter#define com_int_Tx_enable(com) \
52818334Speter		pc98_enable_i8251_interrupt(com,IEN_TxFLAG)
52918334Speter#define com_int_Rx_disable(com) \
53018334Speter		pc98_disable_i8251_interrupt(com,IEN_Rx)
53118334Speter#define com_int_Rx_enable(com) \
53218334Speter		pc98_enable_i8251_interrupt(com,IEN_Rx)
53318334Speter#define com_int_TxRx_disable(com) \
53418334Speter		pc98_disable_i8251_interrupt(com,IEN_Tx|IEN_TxEMP|IEN_Rx)
53518334Speter#define com_int_TxRx_enable(com) \
53618334Speter		pc98_enable_i8251_interrupt(com,IEN_TxFLAG|IEN_Rx)
53718334Speter#define com_send_break_on(com) \
53818334Speter		pc98_i8251_or_cmd(com,CMD8251_SBRK)
53918334Speter#define com_send_break_off(com) \
54018334Speter		pc98_i8251_clear_cmd(com,CMD8251_SBRK)
54118334Speter
54218334Speterstatic struct speedtab pc98speedtab[] = {	/* internal RS232C interface */
54318334Speter	{ 0,		0, },
54418334Speter	{ 50,		50, },
54518334Speter	{ 75,		75, },
54618334Speter	{ 150,		150, },
54718334Speter	{ 200,		200, },
54818334Speter	{ 300,		300, },
54918334Speter	{ 600,		600, },
55018334Speter	{ 1200,		1200, },
55118334Speter	{ 2400,		2400, },
55218334Speter	{ 4800,		4800, },
55318334Speter	{ 9600,		9600, },
55450600Sobrien	{ 19200,	19200, },
55518334Speter	{ 38400,	38400, },
55618334Speter	{ 51200,	51200, },
55718334Speter	{ 76800,	76800, },
55818334Speter	{ 20800,	20800, },
55918334Speter	{ 31200,	31200, },
56018334Speter	{ 41600,	41600, },
56118334Speter	{ 62400,	62400, },
56218334Speter	{ -1,		-1 }
56318334Speter};
56418334Speterstatic struct speedtab pc98fast_speedtab[] = {
56518334Speter	{ 9600,		0x80 | COMBRD(9600), },
56618334Speter	{ 19200,	0x80 | COMBRD(19200), },
56718334Speter	{ 38400,	0x80 | COMBRD(38400), },
56818334Speter	{ 57600,	0x80 | COMBRD(57600), },
56918334Speter	{ 115200,	0x80 | COMBRD(115200), },
57018334Speter	{ -1,		-1 }
57118334Speter};
57218334Speterstatic struct speedtab comspeedtab_pio9032b[] = {
57318334Speter	{ 300,		6, },
57418334Speter	{ 600,		5, },
57518334Speter	{ 1200,		4, },
57618334Speter	{ 2400,		3, },
57718334Speter	{ 4800,		2, },
57818334Speter	{ 9600,		1, },
57918334Speter	{ 19200,	0, },
58018334Speter	{ 38400,	7, },
58118334Speter	{ -1,		-1 }
58218334Speter};
58318334Speterstatic struct speedtab comspeedtab_b98_01[] = {
58418334Speter	{ 75,		11, },
58518334Speter	{ 150,		10, },
58618334Speter	{ 300,		9, },
58718334Speter	{ 600,		8, },
58818334Speter	{ 1200,		7, },
58918334Speter	{ 2400,		6, },
59018334Speter	{ 4800,		5, },
59118334Speter	{ 9600,		4, },
59218334Speter	{ 19200,	3, },
59318334Speter	{ 38400,	2, },
59418334Speter	{ 76800,	1, },
59518334Speter	{ 153600,	0, },
59618334Speter	{ -1,		-1 }
59718334Speter};
59818334Speterstatic struct speedtab comspeedtab_mc16550[] = {
59918334Speter	{ 300,		1536, },
60018334Speter	{ 600,		768, },
60118334Speter	{ 1200,		384, },
60218334Speter	{ 2400,		192, },
60318334Speter	{ 4800,		96, },
60418334Speter	{ 9600,		48, },
60518334Speter	{ 19200,	24, },
60618334Speter	{ 38400,	12, },
60718334Speter	{ 57600,	8, },
60818334Speter	{ 115200,	4, },
60918334Speter	{ 153600,	3, },
61018334Speter	{ 230400,	2, },
61118334Speter	{ 460800,	1, },
61218334Speter	{ -1,		-1 }
61318334Speter};
61418334Speterstatic struct speedtab comspeedtab_rsb384[] = {
61518334Speter	{ 300,		3840, },
61618334Speter	{ 600,		1920, },
61718334Speter	{ 1200,		960, },
61818334Speter	{ 2400,		480, },
61918334Speter	{ 4800,		240, },
62018334Speter	{ 9600,		120, },
62118334Speter	{ 19200,	60, },
62218334Speter	{ 38400,	30, },
62318334Speter	{ 57600,	20, },
62418334Speter	{ 115200,	10, },
62518334Speter	{ 128000,	9, },
62618334Speter	{ 144000,	8, },
62718334Speter	{ 192000,	6, },
62818334Speter	{ 230400,	5, },
62918334Speter	{ 288000,	4, },
63018334Speter	{ 384000,	3, },
63118334Speter	{ 576000,	2, },
63218334Speter	{ 1152000,	1, },
63318334Speter	{ -1,		-1 }
63418334Speter};
63518334Speterstatic  struct speedtab comspeedtab_rsa[] = {
63618334Speter        { 0,		0 },
63718334Speter	{ 50,		COMBRD_RSA(50) },
63818334Speter	{ 75,		COMBRD_RSA(75) },
63918334Speter	{ 110,		COMBRD_RSA(110) },
64018334Speter	{ 134,		COMBRD_RSA(134) },
64118334Speter	{ 150,		COMBRD_RSA(150) },
64218334Speter	{ 200,		COMBRD_RSA(200) },
64318334Speter	{ 300,		COMBRD_RSA(300) },
64418334Speter	{ 600,		COMBRD_RSA(600) },
64518334Speter	{ 1200,		COMBRD_RSA(1200) },
64618334Speter	{ 1800,		COMBRD_RSA(1800) },
64718334Speter	{ 2400,		COMBRD_RSA(2400) },
64818334Speter	{ 4800,		COMBRD_RSA(4800) },
64918334Speter	{ 9600,		COMBRD_RSA(9600) },
65018334Speter	{ 19200,	COMBRD_RSA(19200) },
65118334Speter	{ 38400,	COMBRD_RSA(38400) },
65218334Speter	{ 57600,	COMBRD_RSA(57600) },
65318334Speter	{ 115200,	COMBRD_RSA(115200) },
65418334Speter	{ 230400,	COMBRD_RSA(230400) },
65518334Speter	{ 460800,	COMBRD_RSA(460800) },
65618334Speter	{ 921600,	COMBRD_RSA(921600) },
65718334Speter	{ -1,           -1 }
65818334Speter};
65918334Speter#endif /* PC98 */
66018334Speter
66118334Speterstatic	struct speedtab comspeedtab[] = {
66218334Speter	{ 0,		0 },
66318334Speter	{ 50,		COMBRD(50) },
66418334Speter	{ 75,		COMBRD(75) },
66550600Sobrien	{ 110,		COMBRD(110) },
66618334Speter	{ 134,		COMBRD(134) },
66718334Speter	{ 150,		COMBRD(150) },
66818334Speter	{ 200,		COMBRD(200) },
66918334Speter	{ 300,		COMBRD(300) },
67018334Speter	{ 600,		COMBRD(600) },
67118334Speter	{ 1200,		COMBRD(1200) },
67218334Speter	{ 1800,		COMBRD(1800) },
67318334Speter	{ 2400,		COMBRD(2400) },
67418334Speter	{ 4800,		COMBRD(4800) },
67518334Speter	{ 9600,		COMBRD(9600) },
67618334Speter	{ 19200,	COMBRD(19200) },
67718334Speter	{ 38400,	COMBRD(38400) },
67818334Speter	{ 57600,	COMBRD(57600) },
67918334Speter	{ 115200,	COMBRD(115200) },
68018334Speter	{ -1,		-1 }
68118334Speter};
68218334Speter
68318334Speter#ifdef PC98
68418334Speterstruct {
68518334Speter	char	*name;
68618334Speter	short	port_table[7];
68718334Speter	short	irr_mask;
68818334Speter	struct speedtab	*speedtab;
68918334Speter	short	check_irq;
69018334Speter} if_8251_type[] = {
69118334Speter	/* COM_IF_INTERNAL */
69250600Sobrien	{ " (internal)", {0x30, 0x32, 0x32, 0x33, 0x35, -1, -1},
69318334Speter	     -1, pc98speedtab, 1 },
69418334Speter	/* COM_IF_PC9861K_1 */
69518334Speter	{ " (PC9861K)", {0xb1, 0xb3, 0xb3, 0xb0, 0xb0, -1, -1},
69618334Speter	     3, NULL, 1 },
69718334Speter	/* COM_IF_PC9861K_2 */
69818334Speter	{ " (PC9861K)", {0xb9, 0xbb, 0xbb, 0xb2, 0xb2, -1, -1},
69918334Speter	      3, NULL, 1 },
70018334Speter	/* COM_IF_IND_SS_1 */
70118334Speter	{ " (IND-SS)", {0xb1, 0xb3, 0xb3, 0xb0, 0xb0, 0xb3, -1},
70218334Speter	     3, comspeedtab_mc16550, 1 },
70318334Speter	/* COM_IF_IND_SS_2 */
70418334Speter	{ " (IND-SS)", {0xb9, 0xbb, 0xbb, 0xb2, 0xb2, 0xbb, -1},
70518334Speter	     3, comspeedtab_mc16550, 1 },
70618334Speter	/* COM_IF_PIO9032B_1 */
70718334Speter	{ " (PIO9032B)", {0xb1, 0xb3, 0xb3, 0xb0, 0xb0, 0xb8, -1},
70818334Speter	      7, comspeedtab_pio9032b, 1 },
70918334Speter	/* COM_IF_PIO9032B_2 */
71018334Speter	{ " (PIO9032B)", {0xb9, 0xbb, 0xbb, 0xb2, 0xb2, 0xba, -1},
71118334Speter	      7, comspeedtab_pio9032b, 1 },
71218334Speter	/* COM_IF_B98_01_1 */
71318334Speter	{ " (B98-01)", {0xb1, 0xb3, 0xb3, 0xb0, 0xb0, 0xd1, 0xd3},
71418334Speter	      7, comspeedtab_b98_01, 0 },
71518334Speter	/* COM_IF_B98_01_2 */
71618334Speter	{ " (B98-01)", {0xb9, 0xbb, 0xbb, 0xb2, 0xb2, 0xd5, 0xd7},
71718334Speter	     7, comspeedtab_b98_01, 0 },
71850600Sobrien};
71918334Speter#define	PC98SIO_data_port(type)		(if_8251_type[type].port_table[0])
72018334Speter#define	PC98SIO_cmd_port(type)		(if_8251_type[type].port_table[1])
72118334Speter#define	PC98SIO_sts_port(type)		(if_8251_type[type].port_table[2])
72218334Speter#define	PC98SIO_in_modem_port(type)	(if_8251_type[type].port_table[3])
72318334Speter#define	PC98SIO_intr_ctrl_port(type)	(if_8251_type[type].port_table[4])
72418334Speter#define	PC98SIO_baud_rate_port(type)	(if_8251_type[type].port_table[5])
72518334Speter#define	PC98SIO_func_port(type)		(if_8251_type[type].port_table[6])
72618334Speter
72718334Speterstruct {
72818334Speter	char	*name;
72918334Speter	short	irr_read;
73018334Speter	short	irr_write;
73150600Sobrien	short	port_shift;
73218334Speter	short	io_size;
73318334Speter	struct speedtab	*speedtab;
73418334Speter} if_16550a_type[] = {
73518334Speter	/* COM_IF_RSA98 */
73618334Speter        { " (RSA-98)", -1, -1, 0, IO_COMSIZE, comspeedtab },
73718334Speter	/* COM_IF_NS16550 */
73818334Speter	{ "", -1, -1, 0, IO_COMSIZE, comspeedtab },
73950600Sobrien	/* COM_IF_SECOND_CCU */
74018334Speter	{ "", -1, -1, 0, IO_COMSIZE, comspeedtab },
74118334Speter	/* COM_IF_MC16550II */
74218334Speter	{ " (MC16550II)", -1, 0x1000, 8, 1, comspeedtab_mc16550 },
74318334Speter	/* COM_IF_MCRS98 */
74418334Speter	{ " (MC-RS98)", -1, 0x1000, 8, 1, comspeedtab_mc16550 },
74518334Speter	/* COM_IF_RSB3000 */
74618334Speter	{ " (RSB-3000)", 0xbf, -1, 1, 1, comspeedtab_rsb384 },
74718334Speter	/* COM_IF_RSB384 */
74818334Speter	{ " (RSB-384)", 0xbf, -1, 1, 1, comspeedtab_rsb384 },
74918334Speter	/* COM_IF_MODEM_CARD */
75018334Speter	{ "", -1, -1, 0, IO_COMSIZE, comspeedtab },
75118334Speter	/* COM_IF_RSA98III */
75218334Speter	{ " (RSA-98III)", -1, -1, 0, 16, comspeedtab_rsa },
75318334Speter	/* COM_IF_ESP98 */
75418334Speter	{ " (ESP98)", -1, -1, 1, 1, comspeedtab_mc16550 },
75518334Speter};
75618334Speter#endif /* PC98 */
75718334Speter
75818334Speter#ifdef COM_ESP
75950600Sobrien#ifdef PC98
76018334Speter
76118334Speter/* XXX configure this properly. */
76218334Speterstatic  Port_t  likely_com_ports[] = { 0, 0xb0, 0xb1, 0 };
76318334Speterstatic  Port_t  likely_esp_ports[] = { 0xc0d0, 0 };
76418334Speter
76518334Speter#define	ESP98_CMD1	(ESP_CMD1 * 0x100)
76618334Speter#define	ESP98_CMD2	(ESP_CMD2 * 0x100)
76718334Speter#define	ESP98_STATUS1	(ESP_STATUS1 * 0x100)
76818334Speter#define	ESP98_STATUS2	(ESP_STATUS2 * 0x100)
76918334Speter
77018334Speter#else /* PC98 */
77118334Speter
77218334Speter/* XXX configure this properly. */
77318334Speterstatic	Port_t	likely_com_ports[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, };
77418334Speterstatic	Port_t	likely_esp_ports[] = { 0x140, 0x180, 0x280, 0 };
77518334Speter
77618334Speter#endif /* PC98 */
77752561Sobrien#endif
77818334Speter
77918334Speter/*
78018334Speter * handle sysctl read/write requests for console speed
78118334Speter *
78250600Sobrien * In addition to setting comdefaultrate for I/O through /dev/console,
78318334Speter * also set the initial and lock values for the /dev/ttyXX device
78418334Speter * if there is one associated with the console.  Finally, if the /dev/tty
78518334Speter * device has already been open, change the speed on the open running port
78618334Speter * itself.
78718334Speter */
78818334Speter
78918334Speterstatic int
79018334Spetersysctl_machdep_comdefaultrate SYSCTL_HANDLER_ARGS
79118334Speter{
79218334Speter	int error, s;
79318334Speter	speed_t newspeed;
79418334Speter	struct com_s *com;
79518334Speter	struct tty *tp;
79618334Speter
79718334Speter	newspeed = comdefaultrate;
79818334Speter
79918334Speter	error = sysctl_handle_opaque(oidp, &newspeed, sizeof newspeed, req);
80018334Speter	if (error || !req->newptr)
80118334Speter		return (error);
80218334Speter
80318334Speter	comdefaultrate = newspeed;
80418334Speter
80518334Speter	if (comconsole < 0)		/* serial console not selected? */
80618334Speter		return (0);
80718334Speter
80818334Speter	com = com_addr(comconsole);
80952561Sobrien	if (!com)
81018334Speter		return (ENXIO);
81150600Sobrien
81218334Speter	/*
81318334Speter	 * set the initial and lock rates for /dev/ttydXX and /dev/cuaXX
81418334Speter	 * (note, the lock rates really are boolean -- if non-zero, disallow
81518334Speter	 *  speed changes)
81618334Speter	 */
81718334Speter	com->it_in.c_ispeed  = com->it_in.c_ospeed =
81818334Speter	com->lt_in.c_ispeed  = com->lt_in.c_ospeed =
81952561Sobrien	com->it_out.c_ispeed = com->it_out.c_ospeed =
82018334Speter	com->lt_out.c_ispeed = com->lt_out.c_ospeed = comdefaultrate;
82118334Speter
82218334Speter	/*
82318334Speter	 * if we're open, change the running rate too
82450600Sobrien	 */
82518334Speter	tp = com->tp;
82618334Speter	if (tp && (tp->t_state & TS_ISOPEN)) {
82718334Speter		tp->t_termios.c_ispeed =
82818334Speter		tp->t_termios.c_ospeed = comdefaultrate;
82918334Speter		s = spltty();
83018334Speter		error = comparam(tp, &tp->t_termios);
83118334Speter		splx(s);
83218334Speter	}
83318334Speter	return error;
83418334Speter}
83518334Speter
83618334SpeterSYSCTL_PROC(_machdep, OID_AUTO, conspeed, CTLTYPE_INT | CTLFLAG_RW,
83718334Speter	    0, 0, sysctl_machdep_comdefaultrate, "I", "");
83818334Speter
83918334Speter#if NCARD > 0
84018334Speter/*
84118334Speter *	PC-Card (PCMCIA) specific code.
84218334Speter */
84318334Speterstatic int	sioinit		__P((struct pccard_devinfo *));
84418334Speterstatic void	siounload	__P((struct pccard_devinfo *));
84518334Speterstatic int	card_intr	__P((struct pccard_devinfo *));
84618334Speter
84718334SpeterPCCARD_MODULE(sio, sioinit, siounload, card_intr, 0, tty_imask);
84818334Speter
84918334Speter/*
85018334Speter *	Initialize the device - called from Slot manager.
85152561Sobrien */
85218334Speterint
85350600Sobriensioinit(struct pccard_devinfo *devi)
85418334Speter{
85518334Speter
85618334Speter	/* validate unit number. */
85718334Speter	if (devi->isahd.id_unit >= (NSIOTOT))
85818334Speter		return(ENODEV);
85918334Speter	/* Make sure it isn't already probed. */
86018334Speter	if (com_addr(devi->isahd.id_unit))
86118334Speter		return(EBUSY);
86218334Speter
86318334Speter	/* It's already probed as serial by Upper */
86418334Speter	devi->isahd.id_flags |= COM_C_NOPROBE;
86518334Speter
86650600Sobrien	/*
86718334Speter	 * Probe the device. If a value is returned, the
86818334Speter	 * device was found at the location.
86918334Speter	 */
87018334Speter	if (sioprobe(&devi->isahd) == 0)
87118334Speter		return(ENXIO);
87218334Speter	if (sioattach(&devi->isahd) == 0)
87318334Speter		return(ENXIO);
87418334Speter
87518334Speter	return(0);
87618334Speter}
87718334Speter
87818334Speter/*
87918334Speter *	siounload - unload the driver and clear the table.
88018334Speter *	XXX TODO:
88118334Speter *	This is usually called when the card is ejected, but
88218334Speter *	can be caused by a modunload of a controller driver.
88318334Speter *	The idea is to reset the driver's view of the device
88418334Speter *	and ensure that any driver entry points such as
88518334Speter *	read and write do not hang.
88618334Speter */
88718334Speterstatic void
88818334Spetersiounload(struct pccard_devinfo *devi)
88918334Speter{
89018334Speter	struct com_s	*com;
89118334Speter
89218334Speter	if (!devi) {
89318334Speter		printf("NULL devi in siounload\n");
89418334Speter		return;
89550600Sobrien	}
89618334Speter	com = com_addr(devi->isahd.id_unit);
89718334Speter	if (!com) {
89818334Speter		printf("NULL com in siounload\n");
89918334Speter		return;
90018334Speter	}
90118334Speter	if (!com->iobase) {
90218334Speter		printf("sio%d already unloaded!\n",devi->isahd.id_unit);
90318334Speter		return;
90418334Speter	}
90518334Speter	if (com->tp && (com->tp->t_state & TS_ISOPEN)) {
90618334Speter		com->gone = 1;
90718334Speter		printf("sio%d: unload\n", devi->isahd.id_unit);
90818334Speter		com->tp->t_gen++;
90918334Speter		ttyclose(com->tp);
91018334Speter		ttwakeup(com->tp);
91118334Speter		ttwwakeup(com->tp);
91218334Speter	} else {
91318334Speter		com_addr(com->unit) = NULL;
91418334Speter		if (com->ibuf != NULL)
91518334Speter			free(com->ibuf, M_DEVBUF);
91618334Speter		free(com, M_DEVBUF);
91718334Speter		printf("sio%d: unload,gone\n", devi->isahd.id_unit);
91818334Speter	}
91918334Speter}
92018334Speter
92118334Speter/*
92218334Speter *	card_intr - Shared interrupt called from
92318334Speter *	front end of PC-Card handler.
92418334Speter */
92518334Speterstatic int
92618334Spetercard_intr(struct pccard_devinfo *devi)
92718334Speter{
92818334Speter	struct com_s	*com;
92918334Speter
93018334Speter	COM_LOCK();
93118334Speter	com = com_addr(devi->isahd.id_unit);
93218334Speter	if (com && !com->gone)
93318334Speter		siointr1(com_addr(devi->isahd.id_unit));
93418334Speter	COM_UNLOCK();
93518334Speter	return(1);
93618334Speter}
93718334Speter#endif /* NCARD > 0 */
93818334Speter
93918334Speterstatic int
94018334Spetersioprobe(dev)
94118334Speter	struct isa_device	*dev;
94218334Speter{
94318334Speter	static bool_t	already_init;
94418334Speter	bool_t		failures[10];
94518334Speter	int		fn;
94618334Speter	struct isa_device	*idev;
94718334Speter	Port_t		iobase;
94818334Speter	intrmask_t	irqmap[4];
94952561Sobrien	intrmask_t	irqs;
95018334Speter	u_char		mcr_image;
95118334Speter	int		result;
95218334Speter	struct isa_device	*xdev;
95318334Speter#ifdef PC98
95418334Speter	int		irqout=0;
95550600Sobrien	int		ret = 0;
95618334Speter	int		tmp;
95718334Speter	int		port_shift = 0;
95818334Speter	struct siodev	iod;
95950600Sobrien	Port_t		rsabase = NULL;
96018334Speter#endif
96118334Speter
96218334Speter	if (!already_init) {
96318334Speter		/*
96450600Sobrien		 * Turn off MCR_IENABLE for all likely serial ports.  An unused
96518334Speter		 * port with its MCR_IENABLE gate open will inhibit interrupts
96618334Speter		 * from any used port that shares the interrupt vector.
96718334Speter		 * XXX the gate enable is elsewhere for some multiports.
96852561Sobrien		 */
96918334Speter		for (xdev = isa_devtab_tty; xdev->id_driver != NULL; xdev++)
97018334Speter#ifdef PC98
97118334Speter		    if (xdev->id_driver == &siodriver && xdev->id_enabled) {
97218334Speter			tmp = (xdev->id_flags >> 24) & 0xff;
97318334Speter			if (IS_8251(tmp))
97450600Sobrien			    outb((xdev->id_iobase & 0xff00) | PC98SIO_cmd_port(tmp & 0x0f), 0xf2);
97518334Speter			else
97618334Speter			    if (tmp == COM_IF_RSA98III) {
97718334Speter			        rsabase = xdev->id_iobase & 0xfff0;
97850600Sobrien#if 0
97918334Speter				if (rsabase != xdev->id_iobase)
98018334Speter				    return(0);
98118334Speter#endif
98218334Speter				outb(xdev->id_iobase + 8 + (com_mcr << if_16550a_type[tmp & 0x0f].port_shift), 0);
98350600Sobrien			    } else
98418334Speter				outb(xdev->id_iobase + (com_mcr << if_16550a_type[tmp & 0x0f].port_shift), 0);
98518334Speter		    }
98618334Speter#else
98718334Speter			if (xdev->id_driver == &siodriver && xdev->id_enabled)
98818334Speter				outb(xdev->id_iobase + com_mcr, 0);
98918334Speter#endif
99018334Speter		already_init = TRUE;
99118334Speter	}
99218334Speter
99350600Sobrien	if (COM_LLCONSOLE(dev)) {
99418334Speter		printf("sio%d: reserved for low-level i/o\n", dev->id_unit);
99518334Speter		return (0);
99618334Speter	}
99750600Sobrien
99818334Speter#ifdef PC98
99918334Speter	DELAY(10);
100018334Speter
100118334Speter	/*
100250600Sobrien	 * If the port is i8251 UART (internal, B98_01)
100318334Speter	 */
100418334Speter	if (pc98_check_if_type(dev, &iod) == -1)
100518334Speter	    return 0;
100618334Speter	if (iod.irq > 0)
100718334Speter	    dev->id_irq = 1 << iod.irq;
100818334Speter	if (IS_8251(iod.if_type)) {
100918334Speter		outb(iod.cmd, 0);
101018334Speter		DELAY(10);
101118334Speter		outb(iod.cmd, 0);
101218334Speter		DELAY(10);
101318334Speter		outb(iod.cmd, 0);
101418334Speter		DELAY(10);
101518334Speter		outb(iod.cmd, CMD8251_RESET);
101618334Speter		DELAY(1000);		/* for a while...*/
101718334Speter		outb(iod.cmd, 0xf2);	/* MODE (dummy) */
101818334Speter		DELAY(10);
101918334Speter		outb(iod.cmd, 0x01);	/* CMD (dummy) */
102018334Speter		DELAY(1000);		/* for a while...*/
102118334Speter		if (( inb(iod.sts) & STS8251_TxEMP ) == 0 ) {
102218334Speter			ret = 0;
102318334Speter		}
102418334Speter		if (if_8251_type[iod.if_type & 0x0f].check_irq) {
102518334Speter		    COM_INT_DISABLE
102618334Speter		    tmp = ( inb( iod.ctrl ) & ~(IEN_Rx|IEN_TxEMP|IEN_Tx));
102718334Speter		    outb( iod.ctrl, tmp|IEN_TxEMP );
102818334Speter		    DELAY(10);
102918334Speter		    ret = isa_irq_pending() ? 4 : 0;
103018334Speter		    outb( iod.ctrl, tmp );
103118334Speter		    COM_INT_ENABLE
103218334Speter		} else {
103318334Speter		    /*
103418334Speter		     * B98_01 doesn't activate TxEMP interrupt line
103518334Speter		     * when being reset, so we can't check irq pending.
103618334Speter		     */
103718334Speter		    ret = 4;
103818334Speter		}
103918334Speter		if (epson_machine_id==0x20) {	/* XXX */
104018334Speter			ret = 4;
104150600Sobrien		}
104218334Speter		return ret;
104318334Speter	}
104418334Speter#endif /* PC98 */
104518334Speter	/*
104618334Speter	 * If the device is on a multiport card and has an AST/4
104718334Speter	 * compatible interrupt control register, initialize this
104818334Speter	 * register and prepare to leave MCR_IENABLE clear in the mcr.
104918334Speter	 * Otherwise, prepare to set MCR_IENABLE in the mcr.
105018334Speter	 * Point idev to the device struct giving the correct id_irq.
105118334Speter	 * This is the struct for the master device if there is one.
105218334Speter	 */
105318334Speter	idev = dev;
105418334Speter	mcr_image = MCR_IENABLE;
105518334Speter#ifdef PC98
105618334Speter        if (iod.if_type == COM_IF_RSA98III) {
105718334Speter		mcr_image = 0;
105850600Sobrien		rsabase = idev->id_iobase & 0xfff0;
105950600Sobrien		if (rsabase != idev->id_iobase)
106018334Speter			return(0);
106118334Speter		outb(rsabase + rsa_msr,   0x04);
106218334Speter		outb(rsabase + rsa_frr,   0x00);
106318334Speter		if ((inb(rsabase + rsa_srr) & 0x36) != 0x36)
106418334Speter			return (0);
106550600Sobrien		outb(rsabase + rsa_ier,   0x00);
106618334Speter		outb(rsabase + rsa_frr,   0x00);
106718334Speter		outb(rsabase + rsa_tivsr, 0x00);
106818334Speter		outb(rsabase + rsa_tcr,   0x00);
106918334Speter	}
107050600Sobrien#endif /* PC98 */
107118334Speter#ifdef COM_MULTIPORT
107218334Speter	if (COM_ISMULTIPORT(dev)) {
107318334Speter		idev = find_isadev(isa_devtab_tty, &siodriver,
107452561Sobrien				   COM_MPMASTER(dev));
107518334Speter		if (idev == NULL) {
107618334Speter			printf("sio%d: master device %d not configured\n",
107718334Speter			       dev->id_unit, COM_MPMASTER(dev));
107818334Speter			dev->id_irq = 0;
107918334Speter			idev = dev;
108018334Speter		}
108118334Speter#ifndef PC98
108218334Speter		if (!COM_NOTAST4(dev)) {
108318334Speter			outb(idev->id_iobase + com_scr,
108418334Speter			     idev->id_irq ? 0x80 : 0);
108518334Speter			mcr_image = 0;
108618334Speter		}
108718334Speter#endif /* !PC98 */
108850600Sobrien	}
108918334Speter#endif /* COM_MULTIPORT */
109018334Speter	if (idev->id_irq == 0)
109118334Speter		mcr_image = 0;
109218334Speter
109318334Speter#ifdef PC98
109418334Speter	tmp = if_16550a_type[iod.if_type & 0x0f].irr_write;
109518334Speter	if (tmp != -1) {
109618334Speter	    /* MC16550II */
109718334Speter	    switch (idev->id_irq) {
109818334Speter	    case IRQ3: irqout = 4; break;
109918334Speter	    case IRQ5: irqout = 5; break;
110018334Speter	    case IRQ6: irqout = 6; break;
110118334Speter	    case IRQ12: irqout = 7; break;
110218334Speter	    default:
110318334Speter		printf("sio%d: irq configuration error\n", dev->id_unit);
110418334Speter		return (0);
110518334Speter	    }
110618334Speter	    outb((dev->id_iobase & 0x00ff) | tmp, irqout);
110718334Speter	}
110818334Speter	port_shift = if_16550a_type[iod.if_type & 0x0f].port_shift;
110918334Speter#endif
111050600Sobrien	bzero(failures, sizeof failures);
111118334Speter#ifdef PC98
111218334Speter        if (iod.if_type == COM_IF_RSA98III)
111318334Speter		iobase = dev->id_iobase + 8;
111418334Speter	else
111518334Speter#endif
111618334Speter	iobase = dev->id_iobase;
111718334Speter
111818334Speter	/*
111918334Speter	 * We don't want to get actual interrupts, just masked ones.
112018334Speter	 * Interrupts from this line should already be masked in the ICU,
112118334Speter	 * but mask them in the processor as well in case there are some
112218334Speter	 * (misconfigured) shared interrupts.
112318334Speter	 */
112418334Speter	disable_intr();
112518334Speter/* EXTRA DELAY? */
112618334Speter
112718334Speter	/*
112818334Speter	 * Initialize the speed and the word size and wait long enough to
112918334Speter	 * drain the maximum of 16 bytes of junk in device output queues.
113018334Speter	 * The speed is undefined after a master reset and must be set
113118334Speter	 * before relying on anything related to output.  There may be
113218334Speter	 * junk after a (very fast) soft reboot and (apparently) after
113318334Speter	 * master reset.
113418334Speter	 * XXX what about the UART bug avoided by waiting in comparam()?
113518334Speter	 * We don't want to to wait long enough to drain at 2 bps.
113618334Speter	 */
113718334Speter	if (iobase == siocniobase)
113818334Speter		DELAY((16 + 1) * 1000000 / (comdefaultrate / 10));
113918334Speter	else {
114018334Speter#ifdef PC98
114118334Speter		tmp = ttspeedtab(SIO_TEST_SPEED,
114218334Speter				 if_16550a_type[iod.if_type & 0x0f].speedtab);
114318334Speter		outb(iobase + (com_cfcr << port_shift), CFCR_DLAB|CFCR_8BITS);
114418334Speter		outb(iobase + (com_dlbl << port_shift), tmp & 0xff);
114518334Speter		outb(iobase + (com_dlbh << port_shift), (tmp >> 8) & 0xff);
114618334Speter		outb(iobase + (com_cfcr << port_shift), CFCR_8BITS);
114718334Speter#else
114818334Speter		outb(iobase + com_cfcr, CFCR_DLAB | CFCR_8BITS);
114918334Speter		outb(iobase + com_dlbl, COMBRD(SIO_TEST_SPEED) & 0xff);
115018334Speter		outb(iobase + com_dlbh, (u_int) COMBRD(SIO_TEST_SPEED) >> 8);
115118334Speter		outb(iobase + com_cfcr, CFCR_8BITS);
115218334Speter#endif
115318334Speter		DELAY((16 + 1) * 1000000 / (SIO_TEST_SPEED / 10));
115418334Speter	}
115518334Speter
115618334Speter	/*
115718334Speter	 * Enable the interrupt gate and disable device interupts.  This
115818334Speter	 * should leave the device driving the interrupt line low and
115918334Speter	 * guarantee an edge trigger if an interrupt can be generated.
116018334Speter	 */
116118334Speter/* EXTRA DELAY? */
116218334Speter#ifdef PC98
116318334Speter	outb(iobase + (com_mcr << port_shift), mcr_image);
116450600Sobrien	outb(iobase + (com_ier << port_shift), 0);
116518334Speter#else
116618334Speter	outb(iobase + com_mcr, mcr_image);
116718334Speter	outb(iobase + com_ier, 0);
116818334Speter#endif
116918334Speter	DELAY(1000);		/* XXX */
117018334Speter	irqmap[0] = isa_irq_pending();
117118334Speter
117218334Speter	/*
117318334Speter	 * Attempt to set loopback mode so that we can send a null byte
117418334Speter	 * without annoying any external device.
117518334Speter	 */
117618334Speter/* EXTRA DELAY? */
117718334Speter#ifdef PC98
117850600Sobrien	outb(iobase + (com_mcr << port_shift), mcr_image | MCR_LOOPBACK);
117950600Sobrien#else
118050600Sobrien	outb(iobase + com_mcr, mcr_image | MCR_LOOPBACK);
118150600Sobrien#endif
118250600Sobrien
118318334Speter	/*
118418334Speter	 * Attempt to generate an output interrupt.  On 8250's, setting
118518334Speter	 * IER_ETXRDY generates an interrupt independent of the current
118618334Speter	 * setting and independent of whether the THR is empty.  On 16450's,
118718334Speter	 * setting IER_ETXRDY generates an interrupt independent of the
118818334Speter	 * current setting.  On 16550A's, setting IER_ETXRDY only
118918334Speter	 * generates an interrupt when IER_ETXRDY is not already set.
119018334Speter	 */
119118334Speter#ifdef PC98
119218334Speter	outb(iobase + (com_ier << port_shift), IER_ETXRDY);
119318334Speter        if (iod.if_type == COM_IF_RSA98III) {
119418334Speter		outb(rsabase + rsa_ier,   0x04);
119518334Speter	}
119618334Speter#else
119718334Speter	outb(iobase + com_ier, IER_ETXRDY);
119818334Speter#endif /* PC98 */
119918334Speter
120018334Speter	/*
120118334Speter	 * On some 16x50 incompatibles, setting IER_ETXRDY doesn't generate
120218334Speter	 * an interrupt.  They'd better generate one for actually doing
120318334Speter	 * output.  Loopback may be broken on the same incompatibles but
120418334Speter	 * it's unlikely to do more than allow the null byte out.
120518334Speter	 */
120618334Speter#ifdef PC98
120718334Speter 	outb(iobase + (com_data << port_shift), 0);
120818334Speter#else
120918334Speter	outb(iobase + com_data, 0);
121018334Speter#endif
121118334Speter	DELAY((1 + 2) * 1000000 / (SIO_TEST_SPEED / 10));
121218334Speter
121318334Speter	/*
121418334Speter	 * Turn off loopback mode so that the interrupt gate works again
121518334Speter	 * (MCR_IENABLE was hidden).  This should leave the device driving
121618334Speter	 * an interrupt line high.  It doesn't matter if the interrupt
121718334Speter	 * line oscillates while we are not looking at it, since interrupts
121818334Speter	 * are disabled.
121918334Speter	 */
122018334Speter/* EXTRA DELAY? */
122118334Speter#ifdef PC98
122218334Speter	outb(iobase + (com_mcr << port_shift), mcr_image);
122318334Speter#else
122418334Speter	outb(iobase + com_mcr, mcr_image);
122518334Speter#endif /* PC98 */
122618334Speter
122718334Speter    /*
122818334Speter	 * It's a definitly Serial PCMCIA(16550A), but still be required
122918334Speter	 * for IIR_TXRDY implementation ( Palido 321s, DC-1S... )
123018334Speter	 */
123118334Speter	if ( COM_NOPROBE(dev) ) {
123218334Speter		/* Reading IIR register twice */
123318334Speter		for ( fn = 0; fn < 2; fn ++ ) {
123418334Speter			DELAY(10000);
123518334Speter#ifdef PC98
123618334Speter			failures[6] = inb(iobase + (com_iir << port_shift));
123718334Speter#else
123818334Speter			failures[6] = inb(iobase + com_iir);
123918334Speter#endif
124018334Speter		}
124118334Speter		/* Check IIR_TXRDY clear ? */
124218334Speter#ifdef PC98
124318334Speter		result = if_16550a_type[iod.if_type & 0x0f].io_size;
124418334Speter#else
124518334Speter		result = IO_COMSIZE;
124618334Speter#endif
124718334Speter		if ( failures[6] & IIR_TXRDY ) {
124818334Speter			/* Nop, Double check with clearing IER */
124918334Speter#ifdef PC98
125018334Speter			outb(iobase + (com_ier << port_shift), 0);
125118334Speter			if (inb(iobase +
125218334Speter				(com_iir << port_shift)) & IIR_NOPEND) {
125318334Speter#else
125418334Speter			outb(iobase + com_ier, 0);
125518334Speter			if ( inb(iobase + com_iir) & IIR_NOPEND ) {
125618334Speter#endif
125718334Speter				/* Ok. we're familia this gang */
125818334Speter				dev->id_flags |= COM_C_IIR_TXRDYBUG; /* Set IIR_TXRDYBUG */
125918334Speter			} else {
126018334Speter				/* Unknow, Just omit this chip.. XXX*/
126118334Speter				result = 0;
126218334Speter			}
126318334Speter		} else {
126418334Speter			/* OK. this is well-known guys */
126518334Speter			dev->id_flags &= ~COM_C_IIR_TXRDYBUG; /*Clear IIR_TXRDYBUG*/
126618334Speter		}
126718334Speter#ifdef PC98
126818334Speter		outb(iobase + (com_cfcr << port_shift), CFCR_8BITS);
126918334Speter#else
127018334Speter		outb(iobase + com_cfcr, CFCR_8BITS);
127118334Speter#endif
127218334Speter		enable_intr();
127318334Speter		return (iobase == siocniobase ? IO_COMSIZE : result);
127418334Speter	}
127518334Speter
127618334Speter	/*
127718334Speter	 * Check that
127818334Speter	 *	o the CFCR, IER and MCR in UART hold the values written to them
127918334Speter	 *	  (the values happen to be all distinct - this is good for
128018334Speter	 *	  avoiding false positive tests from bus echoes).
128118334Speter	 *	o an output interrupt is generated and its vector is correct.
128218334Speter	 *	o the interrupt goes away when the IIR in the UART is read.
128318334Speter	 */
128418334Speter/* EXTRA DELAY? */
128518334Speter#ifdef PC98
128618334Speter	failures[0] = inb(iobase + (com_cfcr << port_shift)) - CFCR_8BITS;
128718334Speter	failures[1] = inb(iobase + (com_ier << port_shift)) - IER_ETXRDY;
128818334Speter	failures[2] = inb(iobase + (com_mcr << port_shift)) - mcr_image;
128918334Speter#else
129018334Speter	failures[0] = inb(iobase + com_cfcr) - CFCR_8BITS;
129118334Speter	failures[1] = inb(iobase + com_ier) - IER_ETXRDY;
129218334Speter	failures[2] = inb(iobase + com_mcr) - mcr_image;
129318334Speter#endif
129418334Speter	DELAY(10000);		/* Some internal modems need this time */
129518334Speter	irqmap[1] = isa_irq_pending();
129618334Speter#ifdef PC98
129718334Speter	failures[4] = (inb(iobase + (com_iir << port_shift)) & IIR_IMASK)
129818334Speter	    - IIR_TXRDY;
129918334Speter        if (iod.if_type == COM_IF_RSA98III) {
130018334Speter		inb(rsabase + rsa_srr);
130118334Speter	}
130218334Speter#else
130318334Speter	failures[4] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_TXRDY;
130418334Speter#endif
130518334Speter	DELAY(1000);		/* XXX */
130618334Speter	irqmap[2] = isa_irq_pending();
130718334Speter#ifdef PC98
130818334Speter	failures[6] = (inb(iobase + (com_iir << port_shift)) & IIR_IMASK)
130918334Speter	    - IIR_NOPEND;
131018334Speter        if (iod.if_type == COM_IF_RSA98III) {
131118334Speter		inb(rsabase + rsa_srr);
131218334Speter	}
131318334Speter#else
131418334Speter	failures[6] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND;
131518334Speter#endif
131618334Speter
131718334Speter	/*
131818334Speter	 * Turn off all device interrupts and check that they go off properly.
131918334Speter	 * Leave MCR_IENABLE alone.  For ports without a master port, it gates
132018334Speter	 * the OUT2 output of the UART to
132118334Speter	 * the ICU input.  Closing the gate would give a floating ICU input
132218334Speter	 * (unless there is another device driving it) and spurious interrupts.
132318334Speter	 * (On the system that this was first tested on, the input floats high
132418334Speter	 * and gives a (masked) interrupt as soon as the gate is closed.)
132518334Speter	 */
132618334Speter#ifdef PC98
132718334Speter	outb(iobase + (com_ier << port_shift), 0);
132818334Speter	outb(iobase + (com_cfcr << port_shift), CFCR_8BITS);
132918334Speter	failures[7] = inb(iobase + (com_ier << port_shift));
133018334Speter        if (iod.if_type == COM_IF_RSA98III) {
133118334Speter		outb(rsabase + rsa_ier,   0x00);
133218334Speter	}
133318334Speter#else
133418334Speter	outb(iobase + com_ier, 0);
133518334Speter	outb(iobase + com_cfcr, CFCR_8BITS);	/* dummy to avoid bus echo */
133618334Speter	failures[7] = inb(iobase + com_ier);
133718334Speter#endif
133818334Speter	DELAY(1000);		/* XXX */
133918334Speter	irqmap[3] = isa_irq_pending();
134018334Speter#ifdef PC98
134118334Speter	failures[9] = (inb(iobase + (com_iir << port_shift)) & IIR_IMASK)
134218334Speter	    - IIR_NOPEND;
134318334Speter        if (iod.if_type == COM_IF_RSA98III) {
134418334Speter		inb(rsabase + rsa_srr);
134518334Speter		outb(rsabase + rsa_frr, 0x00);
134618334Speter	}
134718334Speter#else
134818334Speter	failures[9] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND;
134918334Speter#endif
135018334Speter
135118334Speter	enable_intr();
135218334Speter
135318334Speter	irqs = irqmap[1] & ~irqmap[0];
135418334Speter	if (idev->id_irq != 0 && (idev->id_irq & irqs) == 0)
135518334Speter		printf(
135618334Speter		"sio%d: configured irq %d not in bitmap of probed irqs %#x\n",
135718334Speter		    dev->id_unit, ffs(idev->id_irq) - 1, irqs);
135818334Speter	if (bootverbose)
135918334Speter		printf("sio%d: irq maps: %#x %#x %#x %#x\n",
136018334Speter		    dev->id_unit, irqmap[0], irqmap[1], irqmap[2], irqmap[3]);
136118334Speter
136218334Speter#ifdef PC98
136318334Speter	result = if_16550a_type[iod.if_type & 0x0f].io_size;
136418334Speter#else
136518334Speter	result = IO_COMSIZE;
136618334Speter#endif
136718334Speter	for (fn = 0; fn < sizeof failures; ++fn)
136818334Speter		if (failures[fn]) {
136918334Speter#ifdef PC98
137018334Speter			outb(iobase + (com_mcr << port_shift), 0);
137118334Speter#else
137218334Speter			outb(iobase + com_mcr, 0);
137318334Speter#endif
137418334Speter			result = 0;
137518334Speter			if (bootverbose) {
137618334Speter				printf("sio%d: probe failed test(s):",
137718334Speter				    dev->id_unit);
137818334Speter				for (fn = 0; fn < sizeof failures; ++fn)
137918334Speter					if (failures[fn])
138018334Speter						printf(" %d", fn);
138150600Sobrien				printf("\n");
138250600Sobrien			}
138350600Sobrien			break;
138418334Speter		}
138518334Speter	return (iobase == siocniobase ? IO_COMSIZE : result);
138618334Speter}
138718334Speter
138818334Speter#ifdef COM_ESP
138918334Speterstatic int
139018334Speterespattach(isdp, com, esp_port)
139118334Speter	struct isa_device	*isdp;
139218334Speter	struct com_s		*com;
139350600Sobrien	Port_t			esp_port;
139418334Speter{
139518334Speter	u_char	dips;
139618334Speter	u_char	val;
139718334Speter
139818334Speter	/*
139918334Speter	 * Check the ESP-specific I/O port to see if we're an ESP
140018334Speter	 * card.  If not, return failure immediately.
140118334Speter	 */
140218334Speter	if ((inb(esp_port) & 0xf3) == 0) {
140318334Speter		printf(" port 0x%x is not an ESP board?\n", esp_port);
140418334Speter		return (0);
140518334Speter	}
140618334Speter
140718334Speter	/*
140818334Speter	 * We've got something that claims to be a Hayes ESP card.
140918334Speter	 * Let's hope so.
141018334Speter	 */
141118334Speter
141252561Sobrien	/* Get the dip-switch configuration */
141352561Sobrien#ifdef PC98
141452561Sobrien	outb(esp_port + ESP98_CMD1, ESP_GETDIPS);
141518334Speter	dips = inb(esp_port + ESP98_STATUS1);
141650600Sobrien#else
141750600Sobrien	outb(esp_port + ESP_CMD1, ESP_GETDIPS);
141818334Speter	dips = inb(esp_port + ESP_STATUS1);
141918334Speter#endif
142018334Speter
142118334Speter	/*
142218334Speter	 * Bits 0,1 of dips say which COM port we are.
142318334Speter	 */
142418334Speter#ifdef PC98
142518334Speter	if ((com->iobase & 0xff) == likely_com_ports[dips & 0x03])
142618334Speter#else
142718334Speter	if (com->iobase == likely_com_ports[dips & 0x03])
142818334Speter#endif
142918334Speter		printf(" : ESP");
143018334Speter	else {
143118334Speter		printf(" esp_port has com %d\n", dips & 0x03);
143218334Speter		return (0);
143318334Speter	}
143418334Speter
143518334Speter	/*
143618334Speter	 * Check for ESP version 2.0 or later:  bits 4,5,6 = 010.
143718334Speter	 */
143818334Speter#ifdef PC98
143918334Speter	outb(esp_port + ESP98_CMD1, ESP_GETTEST);
144018334Speter	val = inb(esp_port + ESP98_STATUS1);	/* clear reg 1 */
144118334Speter	val = inb(esp_port + ESP98_STATUS2);
144218334Speter#else
144318334Speter	outb(esp_port + ESP_CMD1, ESP_GETTEST);
144450600Sobrien	val = inb(esp_port + ESP_STATUS1);	/* clear reg 1 */
144518334Speter	val = inb(esp_port + ESP_STATUS2);
144618334Speter#endif
144718334Speter	if ((val & 0x70) < 0x20) {
144818334Speter		printf("-old (%o)", val & 0x70);
144918334Speter		return (0);
145018334Speter	}
145118334Speter
145218334Speter	/*
145318334Speter	 * Check for ability to emulate 16550:  bit 7 == 1
145418334Speter	 */
145518334Speter	if ((dips & 0x80) == 0) {
145618334Speter		printf(" slave");
145718334Speter		return (0);
145818334Speter	}
145918334Speter
146050600Sobrien	/*
146150600Sobrien	 * Okay, we seem to be a Hayes ESP card.  Whee.
146250600Sobrien	 */
146318334Speter	com->esp = TRUE;
146418334Speter	com->esp_port = esp_port;
146518334Speter	return (1);
146618334Speter}
146718334Speter#endif /* COM_ESP */
146818334Speter
146918334Speterstatic int
147018334Spetersioattach(isdp)
147118334Speter	struct isa_device	*isdp;
147218334Speter{
147318334Speter	struct com_s	*com;
147418334Speter	dev_t		dev;
147518334Speter#ifdef COM_ESP
147618334Speter	Port_t		*espp;
147718334Speter#endif
147818334Speter#ifdef COM_MULTIPORT
147918334Speter	struct isa_device	*idev;
148018334Speter#endif
148118334Speter	Port_t		iobase;
148218334Speter	int		s;
148318334Speter	int		unit;
148418334Speter#ifdef PC98
148518334Speter	int		port_shift = 0;
148650600Sobrien	u_long		obufsize;
148718334Speter#endif
148850600Sobrien
148918334Speter	isdp->id_ointr = siointr;
149050600Sobrien	isdp->id_ri_flags |= RI_FAST;
149150600Sobrien#ifdef PC98
149250600Sobrien        if (((isdp->id_flags >> 24) & 0xff) == COM_IF_RSA98III)
149350600Sobrien		iobase = isdp->id_iobase + 8;
149450600Sobrien	else
149550600Sobrien#endif
149650600Sobrien	iobase = isdp->id_iobase;
149750600Sobrien	unit = isdp->id_unit;
149850600Sobrien#ifndef PC98
149950600Sobrien	com = malloc(sizeof *com, M_DEVBUF, M_NOWAIT);
150050600Sobrien#else
150150600Sobrien	obufsize = 256;
150250600Sobrien	if (((isdp->id_flags >> 24) & 0xff) == COM_IF_RSA98III)
150350600Sobrien		obufsize = 2048;
150450600Sobrien	com = malloc((sizeof *com) + obufsize * 2, M_DEVBUF, M_NOWAIT);
150550600Sobrien#endif
150650600Sobrien	if (com == NULL)
150750600Sobrien		return (0);
150850600Sobrien
150950600Sobrien	/*
151050600Sobrien	 * sioprobe() has initialized the device registers as follows:
151150600Sobrien	 *	o cfcr = CFCR_8BITS.
151250600Sobrien	 *	  It is most important that CFCR_DLAB is off, so that the
151350600Sobrien	 *	  data port is not hidden when we enable interrupts.
151450600Sobrien	 *	o ier = 0.
151550600Sobrien	 *	  Interrupts are only enabled when the line is open.
151650600Sobrien	 *	o mcr = MCR_IENABLE, or 0 if the port has AST/4 compatible
151750600Sobrien	 *	  interrupt control register or the config specifies no irq.
151850600Sobrien	 *	  Keeping MCR_DTR and MCR_RTS off might stop the external
151950600Sobrien	 *	  device from sending before we are ready.
152050600Sobrien	 */
152150600Sobrien	bzero(com, sizeof *com);
152250600Sobrien#ifdef PC98
152350600Sobrien	com->obufsize = obufsize;
152450600Sobrien	com->obuf1 = (u_char *)com + (sizeof *com);
152550600Sobrien	com->obuf2 = com->obuf1 + obufsize;
152650600Sobrien	bzero(com->obuf1, obufsize * 2);
152750600Sobrien#endif
152850600Sobrien	com->unit = unit;
152950600Sobrien	com->cfcr_image = CFCR_8BITS;
153050600Sobrien	com->dtr_wait = 3 * hz;
153150600Sobrien	com->loses_outints = COM_LOSESOUTINTS(isdp) != 0;
153250600Sobrien	com->no_irq = isdp->id_irq == 0;
153350600Sobrien	com->tx_fifo_size = 1;
153450600Sobrien	com->obufs[0].l_head = com->obuf1;
153550600Sobrien	com->obufs[1].l_head = com->obuf2;
153650600Sobrien
153750600Sobrien	com->iobase = iobase;
153850600Sobrien#ifdef PC98
153950600Sobrien	if (pc98_set_ioport(com, isdp->id_flags) == -1) {
154050600Sobrien	    com->pc98_if_type = (isdp->id_flags >> 24) & 0xff;
154150600Sobrien	    port_shift = if_16550a_type[com->pc98_if_type & 0x0f].port_shift;
154250600Sobrien	    com->data_port = iobase + (com_data << port_shift);
154350600Sobrien	    com->int_id_port = iobase + (com_iir << port_shift);
154450600Sobrien	    com->modem_ctl_port = iobase + (com_mcr << port_shift);
154550600Sobrien	    com->mcr_image = inb(com->modem_ctl_port);
154650600Sobrien	    com->line_status_port = iobase + (com_lsr << port_shift);
154750600Sobrien	    com->modem_status_port = iobase + (com_msr << port_shift);
154850600Sobrien	    com->intr_ctl_port = iobase + (com_ier << port_shift);
154950600Sobrien	}
155050600Sobrien#else /* not PC98 */
155150600Sobrien	com->data_port = iobase + com_data;
155250600Sobrien	com->int_id_port = iobase + com_iir;
155350600Sobrien	com->modem_ctl_port = iobase + com_mcr;
155450600Sobrien	com->mcr_image = inb(com->modem_ctl_port);
155550600Sobrien	com->line_status_port = iobase + com_lsr;
155650600Sobrien	com->modem_status_port = iobase + com_msr;
155750600Sobrien	com->intr_ctl_port = iobase + com_ier;
155850600Sobrien#endif
155950600Sobrien
156050600Sobrien	/*
156150600Sobrien	 * We don't use all the flags from <sys/ttydefaults.h> since they
156250600Sobrien	 * are only relevant for logins.  It's important to have echo off
156350600Sobrien	 * initially so that the line doesn't start blathering before the
156450600Sobrien	 * echo flag can be turned off.
156550600Sobrien	 */
156650600Sobrien	com->it_in.c_iflag = 0;
156750600Sobrien	com->it_in.c_oflag = 0;
156850600Sobrien	com->it_in.c_cflag = TTYDEF_CFLAG;
156950600Sobrien	com->it_in.c_lflag = 0;
157050600Sobrien	if (unit == comconsole) {
157150600Sobrien#ifdef PC98
157250600Sobrien		if (IS_8251(com->pc98_if_type))
157350600Sobrien			DELAY(100000);
157450600Sobrien#endif
157550600Sobrien		com->it_in.c_iflag = TTYDEF_IFLAG;
157650600Sobrien		com->it_in.c_oflag = TTYDEF_OFLAG;
157750600Sobrien		com->it_in.c_cflag = TTYDEF_CFLAG | CLOCAL;
157850600Sobrien		com->it_in.c_lflag = TTYDEF_LFLAG;
157950600Sobrien		com->lt_out.c_cflag = com->lt_in.c_cflag = CLOCAL;
158050600Sobrien		com->lt_out.c_ispeed = com->lt_out.c_ospeed =
158150600Sobrien		com->lt_in.c_ispeed = com->lt_in.c_ospeed =
158250600Sobrien		com->it_in.c_ispeed = com->it_in.c_ospeed = comdefaultrate;
158350600Sobrien	} else
158450600Sobrien		com->it_in.c_ispeed = com->it_in.c_ospeed = TTYDEF_SPEED;
158550600Sobrien	if (siosetwater(com, com->it_in.c_ispeed) != 0) {
158650600Sobrien		enable_intr();
158750600Sobrien		free(com, M_DEVBUF);
158850600Sobrien		return (0);
158950600Sobrien	}
159050600Sobrien	enable_intr();
159150600Sobrien	termioschars(&com->it_in);
159218334Speter	com->it_out = com->it_in;
159318334Speter
159418334Speter	/* attempt to determine UART type */
159518334Speter	printf("sio%d: type", unit);
159618334Speter
159718334Speter
159818334Speter#ifndef PC98
159918334Speter#ifdef COM_MULTIPORT
160018334Speter	if (!COM_ISMULTIPORT(isdp) && !COM_IIR_TXRDYBUG(isdp))
160150600Sobrien#else
160218334Speter	if (!COM_IIR_TXRDYBUG(isdp))
160318334Speter#endif
160418334Speter	{
160550600Sobrien		u_char	scr;
160618334Speter		u_char	scr1;
160718334Speter		u_char	scr2;
160818334Speter
160918334Speter		scr = inb(iobase + com_scr);
161018334Speter		outb(iobase + com_scr, 0xa5);
161150600Sobrien		scr1 = inb(iobase + com_scr);
161218334Speter		outb(iobase + com_scr, 0x5a);
161318334Speter		scr2 = inb(iobase + com_scr);
161450600Sobrien		outb(iobase + com_scr, scr);
161550600Sobrien		if (scr1 != 0xa5 || scr2 != 0x5a) {
161650600Sobrien			printf(" 8250");
161718334Speter			goto determined_type;
161818334Speter		}
161950600Sobrien	}
162018334Speter#endif /* !PC98 */
162118334Speter#ifdef PC98
162218334Speter	if (IS_8251(com->pc98_if_type)) {
162318334Speter	    com_int_TxRx_disable( com );
162418334Speter	    com_cflag_and_speed_set( com, com->it_in.c_cflag, comdefaultrate );
162518334Speter	    com_tiocm_bic( com, TIOCM_DTR|TIOCM_RTS|TIOCM_LE );
162618334Speter	    com_send_break_off( com );
162718334Speter	    printf(" 8251%s", if_8251_type[com->pc98_if_type & 0x0f].name);
162818334Speter	} else {
162918334Speter	outb(iobase + (com_fifo << port_shift), FIFO_ENABLE | FIFO_RX_HIGH);
163018334Speter#else
163118334Speter	outb(iobase + com_fifo, FIFO_ENABLE | FIFO_RX_HIGH);
163218334Speter#endif /* PC98 */
163318334Speter	DELAY(100);
163418334Speter	com->st16650a = 0;
163518334Speter	switch (inb(com->int_id_port) & IIR_FIFO_MASK) {
163618334Speter	case FIFO_RX_LOW:
163718334Speter		printf(" 16450");
163818334Speter		break;
163918334Speter	case FIFO_RX_MEDL:
164018334Speter		printf(" 16450?");
164118334Speter		break;
164218334Speter	case FIFO_RX_MEDH:
164350600Sobrien		printf(" 16550?");
164418334Speter		break;
164518334Speter	case FIFO_RX_HIGH:
164618334Speter		if (COM_NOFIFO(isdp)) {
164718334Speter			printf(" 16550A fifo disabled");
164818334Speter		} else {
164918334Speter			com->hasfifo = TRUE;
165018334Speter#ifdef PC98
165118334Speter			com->tx_fifo_size = 0;	/* XXX flag conflicts. */
165218334Speter			printf(" 16550A");
165318334Speter#else
165418334Speter			if (COM_ST16650A(isdp)) {
165518334Speter				com->st16650a = 1;
165618334Speter				com->tx_fifo_size = 32;
165718334Speter				printf(" ST16650A");
165818334Speter			} else {
165918334Speter				com->tx_fifo_size = COM_FIFOSIZE(isdp);
166018334Speter				printf(" 16550A");
166118334Speter			}
166218334Speter#endif
166318334Speter		}
166418334Speter#ifdef PC98
166518334Speter		if (com->pc98_if_type == COM_IF_RSA98III) {
166618334Speter			com->tx_fifo_size = 2048;
166718334Speter			com->rsabase = isdp->id_iobase;
166818334Speter			outb(com->rsabase + rsa_ier, 0x00);
166918334Speter			outb(com->rsabase + rsa_frr, 0x00);
167018334Speter		}
167118334Speter#endif
167218334Speter
167318334Speter#ifdef COM_ESP
167450600Sobrien#ifdef PC98
167518334Speter		if (com->pc98_if_type == COM_IF_ESP98)
167650600Sobrien#endif
167718334Speter		for (espp = likely_esp_ports; *espp != 0; espp++)
167850600Sobrien			if (espattach(isdp, com, *espp)) {
167950600Sobrien				com->tx_fifo_size = 1024;
168050600Sobrien				break;
168150600Sobrien			}
168218334Speter#endif
168318334Speter		if (!com->st16650a) {
168418334Speter			if (!com->tx_fifo_size)
168518334Speter				com->tx_fifo_size = 16;
168618334Speter			else
168718334Speter				printf(" lookalike with %d bytes FIFO",
168818334Speter				    com->tx_fifo_size);
168918334Speter		}
169018334Speter
169118334Speter		break;
169218334Speter	}
169318334Speter
169418334Speter#ifdef PC98
169518334Speter	if (com->pc98_if_type == COM_IF_RSB3000) {
169618334Speter	    /* Set RSB-2000/3000 Extended Buffer mode. */
169718334Speter	    u_char lcr;
169818334Speter	    lcr = inb(iobase + (com_cfcr << port_shift));
169918334Speter	    outb(iobase + (com_cfcr << port_shift), lcr | CFCR_DLAB);
170018334Speter	    outb(iobase + (com_emr << port_shift), EMR_EXBUFF | EMR_EFMODE);
170118334Speter	    outb(iobase + (com_cfcr << port_shift), lcr);
170218334Speter	}
170318334Speter#endif
170418334Speter
170518334Speter#ifdef COM_ESP
170618334Speter	if (com->esp) {
170718334Speter		/*
170818334Speter		 * Set 16550 compatibility mode.
170950600Sobrien		 * We don't use the ESP_MODE_SCALE bit to increase the
171018334Speter		 * fifo trigger levels because we can't handle large
171118334Speter		 * bursts of input.
171218334Speter		 * XXX flow control should be set in comparam(), not here.
171318334Speter		 */
171418334Speter#ifdef PC98
171518334Speter		outb(com->esp_port + ESP98_CMD1, ESP_SETMODE);
171618334Speter		outb(com->esp_port + ESP98_CMD2, ESP_MODE_RTS | ESP_MODE_FIFO);
171718334Speter#else
171818334Speter		outb(com->esp_port + ESP_CMD1, ESP_SETMODE);
171918334Speter		outb(com->esp_port + ESP_CMD2, ESP_MODE_RTS | ESP_MODE_FIFO);
172018334Speter#endif
172118334Speter
172218334Speter		/* Set RTS/CTS flow control. */
172318334Speter#ifdef PC98
172418334Speter		outb(com->esp_port + ESP98_CMD1, ESP_SETFLOWTYPE);
172518334Speter		outb(com->esp_port + ESP98_CMD2, ESP_FLOW_RTS);
172650600Sobrien		outb(com->esp_port + ESP98_CMD2, ESP_FLOW_CTS);
172750600Sobrien#else
172850600Sobrien		outb(com->esp_port + ESP_CMD1, ESP_SETFLOWTYPE);
172950600Sobrien		outb(com->esp_port + ESP_CMD2, ESP_FLOW_RTS);
173050600Sobrien		outb(com->esp_port + ESP_CMD2, ESP_FLOW_CTS);
173150600Sobrien#endif
173218334Speter
173350600Sobrien		/* Set flow-control levels. */
173418334Speter#ifdef PC98
173550600Sobrien		outb(com->esp_port + ESP98_CMD1, ESP_SETRXFLOW);
173618334Speter		outb(com->esp_port + ESP98_CMD2, HIBYTE(768));
173750600Sobrien		outb(com->esp_port + ESP98_CMD2, LOBYTE(768));
173850600Sobrien		outb(com->esp_port + ESP98_CMD2, HIBYTE(512));
173950600Sobrien		outb(com->esp_port + ESP98_CMD2, LOBYTE(512));
174050600Sobrien#else
174150600Sobrien		outb(com->esp_port + ESP_CMD1, ESP_SETRXFLOW);
174250600Sobrien		outb(com->esp_port + ESP_CMD2, HIBYTE(768));
174350600Sobrien		outb(com->esp_port + ESP_CMD2, LOBYTE(768));
174450600Sobrien		outb(com->esp_port + ESP_CMD2, HIBYTE(512));
174550600Sobrien		outb(com->esp_port + ESP_CMD2, LOBYTE(512));
174650600Sobrien#endif
174718334Speter
174850600Sobrien#ifdef PC98
174950600Sobrien                /* Set UART clock prescaler. */
175050600Sobrien                outb(com->esp_port + ESP98_CMD1, ESP_SETCLOCK);
175150600Sobrien                outb(com->esp_port + ESP98_CMD2, 2);	/* 4 times */
175250600Sobrien#endif
175318334Speter	}
175450600Sobrien#endif /* COM_ESP */
175550600Sobrien#ifdef PC98
175650600Sobrien	printf("%s", if_16550a_type[com->pc98_if_type & 0x0f].name);
175750600Sobrien	outb(iobase + (com_fifo << port_shift), 0);
175850600Sobrien#else
175950600Sobrien	outb(iobase + com_fifo, 0);
176050600Sobriendetermined_type: ;
176150600Sobrien#endif
176250600Sobrien
176350600Sobrien#ifdef COM_MULTIPORT
176450600Sobrien	if (COM_ISMULTIPORT(isdp)) {
176550600Sobrien		com->multiport = TRUE;
176650600Sobrien		printf(" (multiport");
176750600Sobrien		if (unit == COM_MPMASTER(isdp))
176850600Sobrien			printf(" master");
176950600Sobrien		printf(")");
177050600Sobrien		idev = find_isadev(isa_devtab_tty, &siodriver,
177150600Sobrien				   COM_MPMASTER(isdp));
177250600Sobrien		com->no_irq = (idev == NULL || idev->id_irq == 0);
177350600Sobrien	 }
177450600Sobrien#endif /* COM_MULTIPORT */
177550600Sobrien#ifdef PC98
177650600Sobrien	}
177750600Sobrien#endif
177850600Sobrien	if (unit == comconsole)
177950600Sobrien		printf(", console");
178050600Sobrien	if ( COM_IIR_TXRDYBUG(isdp) )
178150600Sobrien		printf(" with a bogus IIR_TXRDY register");
178250600Sobrien	printf("\n");
178350600Sobrien
178450600Sobrien	s = spltty();
178550600Sobrien	com_addr(unit) = com;
178650600Sobrien	splx(s);
178750600Sobrien
178850600Sobrien	if (!sio_registered) {
178950600Sobrien		dev = makedev(CDEV_MAJOR, 0);
179050600Sobrien		cdevsw_add(&dev, &sio_cdevsw, NULL);
179150600Sobrien		register_swi(SWI_TTY, siopoll);
179250600Sobrien		sio_registered = TRUE;
179350600Sobrien	}
179450600Sobrien#ifdef DEVFS
179550600Sobrien	com->devfs_token_ttyd = devfs_add_devswf(&sio_cdevsw,
179650600Sobrien		unit, DV_CHR,
179750600Sobrien		UID_ROOT, GID_WHEEL, 0600, "ttyd%r", unit);
179850600Sobrien	com->devfs_token_ttyi = devfs_add_devswf(&sio_cdevsw,
179950600Sobrien		unit | CONTROL_INIT_STATE, DV_CHR,
180050600Sobrien		UID_ROOT, GID_WHEEL, 0600, "ttyid%r", unit);
180150600Sobrien	com->devfs_token_ttyl = devfs_add_devswf(&sio_cdevsw,
180250600Sobrien		unit | CONTROL_LOCK_STATE, DV_CHR,
180350600Sobrien		UID_ROOT, GID_WHEEL, 0600, "ttyld%r", unit);
180450600Sobrien	com->devfs_token_cuaa = devfs_add_devswf(&sio_cdevsw,
180550600Sobrien		unit | CALLOUT_MASK, DV_CHR,
180650600Sobrien		UID_UUCP, GID_DIALER, 0660, "cuaa%r", unit);
180750600Sobrien	com->devfs_token_cuai = devfs_add_devswf(&sio_cdevsw,
180850600Sobrien		unit | CALLOUT_MASK | CONTROL_INIT_STATE, DV_CHR,
180950600Sobrien		UID_UUCP, GID_DIALER, 0660, "cuaia%r", unit);
181050600Sobrien	com->devfs_token_cual = devfs_add_devswf(&sio_cdevsw,
181150600Sobrien		unit | CALLOUT_MASK | CONTROL_LOCK_STATE, DV_CHR,
181250600Sobrien		UID_UUCP, GID_DIALER, 0660, "cuala%r", unit);
181350600Sobrien#endif
181450600Sobrien	com->id_flags = isdp->id_flags; /* Heritate id_flags for later */
181550600Sobrien	com->pps.ppscap = PPS_CAPTUREASSERT | PPS_CAPTURECLEAR;
181650600Sobrien	pps_init(&com->pps);
181750600Sobrien	return (1);
181850600Sobrien}
181950600Sobrien
182050600Sobrienstatic int
182150600Sobriensioopen(dev, flag, mode, p)
182250600Sobrien	dev_t		dev;
182350600Sobrien	int		flag;
182450600Sobrien	int		mode;
182550600Sobrien	struct proc	*p;
182650600Sobrien{
182750600Sobrien	struct com_s	*com;
182850600Sobrien	int		error;
182950600Sobrien	Port_t		iobase;
183050600Sobrien	int		mynor;
183150600Sobrien	int		s;
183250600Sobrien	struct tty	*tp;
183350600Sobrien	int		unit;
183418334Speter#ifdef PC98
183550600Sobrien	int		port_shift = 0;
183650600Sobrien#endif
183750600Sobrien
183850600Sobrien	mynor = minor(dev);
183950600Sobrien	unit = MINOR_TO_UNIT(mynor);
184050600Sobrien	if ((u_int) unit >= NSIOTOT || (com = com_addr(unit)) == NULL)
184150600Sobrien		return (ENXIO);
184250600Sobrien	if (com->gone)
184350600Sobrien		return (ENXIO);
184450600Sobrien	if (mynor & CONTROL_MASK)
184550600Sobrien		return (0);
184650600Sobrien#if 0 /* XXX */
184750600Sobrien	tp = com->tp = sio_tty[unit] = ttymalloc(sio_tty[unit]);
184850600Sobrien#else
184950600Sobrien	tp = com->tp = &sio_tty[unit];
185050600Sobrien#endif
185150600Sobrien	s = spltty();
185250600Sobrien
185350600Sobrien#ifdef PC98
185450600Sobrien	if (!IS_8251(com->pc98_if_type))
185550600Sobrien	    port_shift = if_16550a_type[com->pc98_if_type & 0x0f].port_shift;
185650600Sobrien#endif
185750600Sobrien	/*
185850600Sobrien	 * We jump to this label after all non-interrupted sleeps to pick
185918334Speter	 * up any changes of the device state.
186050600Sobrien	 */
186150600Sobrienopen_top:
186250600Sobrien	while (com->state & CS_DTR_OFF) {
186350600Sobrien		error = tsleep(&com->dtr_wait, TTIPRI | PCATCH, "siodtr", 0);
186450600Sobrien		if (com_addr(unit) == NULL)
186550600Sobrien			return (ENXIO);
186650600Sobrien		if (error != 0 || com->gone)
186750600Sobrien			goto out;
186850600Sobrien	}
186950600Sobrien	if (tp->t_state & TS_ISOPEN) {
187050600Sobrien		/*
187150600Sobrien		 * The device is open, so everything has been initialized.
187250600Sobrien		 * Handle conflicts.
187350600Sobrien		 */
187450600Sobrien		if (mynor & CALLOUT_MASK) {
187550600Sobrien			if (!com->active_out) {
187650600Sobrien				error = EBUSY;
187750600Sobrien				goto out;
187850600Sobrien			}
187950600Sobrien		} else {
188050600Sobrien			if (com->active_out) {
188150600Sobrien				if (flag & O_NONBLOCK) {
188250600Sobrien					error = EBUSY;
188350600Sobrien					goto out;
188450600Sobrien				}
188550600Sobrien				error =	tsleep(&com->active_out,
188650600Sobrien					       TTIPRI | PCATCH, "siobi", 0);
188750600Sobrien				if (com_addr(unit) == NULL)
188850600Sobrien					return (ENXIO);
188950600Sobrien				if (error != 0 || com->gone)
189050600Sobrien					goto out;
189150600Sobrien				goto open_top;
189250600Sobrien			}
189350600Sobrien		}
189450600Sobrien		if (tp->t_state & TS_XCLUDE &&
189550600Sobrien		    suser(p->p_ucred, &p->p_acflag)) {
189650600Sobrien			error = EBUSY;
189750600Sobrien			goto out;
189850600Sobrien		}
189950600Sobrien	} else {
190050600Sobrien		/*
190150600Sobrien		 * The device isn't open, so there are no conflicts.
190250600Sobrien		 * Initialize it.  Initialization is done twice in many
190350600Sobrien		 * cases: to preempt sleeping callin opens if we are
190450600Sobrien		 * callout, and to complete a callin open after DCD rises.
190550600Sobrien		 */
190650600Sobrien		tp->t_oproc = comstart;
190750600Sobrien		tp->t_param = comparam;
190850600Sobrien		tp->t_dev = dev;
190950600Sobrien		tp->t_termios = mynor & CALLOUT_MASK
191050600Sobrien				? com->it_out : com->it_in;
191150600Sobrien#ifdef PC98
191250600Sobrien		if (!IS_8251(com->pc98_if_type))
191350600Sobrien#endif
191450600Sobrien		(void)commctl(com, TIOCM_DTR | TIOCM_RTS, DMSET);
191550600Sobrien		com->poll = com->no_irq;
191650600Sobrien		com->poll_output = com->loses_outints;
191750600Sobrien		++com->wopeners;
191850600Sobrien		error = comparam(tp, &tp->t_termios);
191950600Sobrien		--com->wopeners;
192050600Sobrien		if (error != 0)
192150600Sobrien			goto out;
192250600Sobrien#ifdef PC98
192350600Sobrien		if (IS_8251(com->pc98_if_type)) {
192450600Sobrien			com_tiocm_bis(com, TIOCM_DTR|TIOCM_RTS);
192550600Sobrien			pc98_msrint_start(dev);
192650600Sobrien		}
192750600Sobrien#endif
192850600Sobrien		/*
192950600Sobrien		 * XXX we should goto open_top if comparam() slept.
193050600Sobrien		 */
193118334Speter		iobase = com->iobase;
193250600Sobrien		if (com->hasfifo) {
193350600Sobrien			/*
193450600Sobrien			 * (Re)enable and drain fifos.
193550600Sobrien			 *
193650600Sobrien			 * Certain SMC chips cause problems if the fifos
193750600Sobrien			 * are enabled while input is ready.  Turn off the
193850600Sobrien			 * fifo if necessary to clear the input.  We test
193950600Sobrien			 * the input ready bit after enabling the fifos
194050600Sobrien			 * since we've already enabled them in comparam()
194150600Sobrien			 * and to handle races between enabling and fresh
194250600Sobrien			 * input.
194350600Sobrien			 */
194450600Sobrien			while (TRUE) {
194550600Sobrien#ifdef PC98
194650600Sobrien 				outb(iobase + (com_fifo << port_shift),
194750600Sobrien 				     FIFO_RCV_RST | FIFO_XMT_RST
194850600Sobrien 				     | com->fifo_image);
194950600Sobrien				if (com->pc98_if_type == COM_IF_RSA98III)
195050600Sobrien				  outb(com->rsabase + rsa_frr , 0x00);
195150600Sobrien#else
195250600Sobrien				outb(iobase + com_fifo,
195350600Sobrien				     FIFO_RCV_RST | FIFO_XMT_RST
195450600Sobrien				     | com->fifo_image);
195550600Sobrien#endif
195650600Sobrien				/*
195750600Sobrien				 * XXX the delays are for superstitious
195850600Sobrien				 * historical reasons.  It must be less than
195950600Sobrien				 * the character time at the maximum
196050600Sobrien				 * supported speed (87 usec at 115200 bps
196150600Sobrien				 * 8N1).  Otherwise we might loop endlessly
196250600Sobrien				 * if data is streaming in.  We used to use
196350600Sobrien				 * delays of 100.  That usually worked
196450600Sobrien				 * because DELAY(100) used to usually delay
196550600Sobrien				 * for about 85 usec instead of 100.
196650600Sobrien				 */
196750600Sobrien				DELAY(50);
196850600Sobrien#ifndef PC98
196950600Sobrien				if (!(inb(com->line_status_port) & LSR_RXRDY))
197050600Sobrien#else
197150600Sobrien				if (com->pc98_if_type == COM_IF_RSA98III
197250600Sobrien				    ? !(inb(com->rsabase + rsa_srr) & 0x08)
197350600Sobrien				    : !(inb(com->line_status_port) & LSR_RXRDY))
197450600Sobrien#endif
197550600Sobrien					break;
197650600Sobrien#ifdef PC98
197750600Sobrien 				outb(iobase + (com_fifo << port_shift), 0);
197850600Sobrien#else
197950600Sobrien				outb(iobase + com_fifo, 0);
198050600Sobrien#endif
198150600Sobrien				DELAY(50);
198250600Sobrien				(void) inb(com->data_port);
198350600Sobrien			}
198450600Sobrien		}
198550600Sobrien
198650600Sobrien		disable_intr();
198750600Sobrien#ifdef PC98
198850600Sobrien		if (IS_8251(com->pc98_if_type)) {
198950600Sobrien		    com_tiocm_bis(com, TIOCM_LE);
199050600Sobrien		    com->pc98_prev_modem_status = pc98_get_modem_status(com);
199150600Sobrien		    com_int_Rx_enable(com);
199250600Sobrien		} else {
199350600Sobrien#endif
199450600Sobrien		(void) inb(com->line_status_port);
199550600Sobrien		(void) inb(com->data_port);
199650600Sobrien		com->prev_modem_status = com->last_modem_status
199750600Sobrien		    = inb(com->modem_status_port);
199850600Sobrien		if (COM_IIR_TXRDYBUG(com)) {
199950600Sobrien			outb(com->intr_ctl_port, IER_ERXRDY | IER_ERLS
200050600Sobrien						| IER_EMSC);
200150600Sobrien		} else {
200250600Sobrien			outb(com->intr_ctl_port, IER_ERXRDY | IER_ETXRDY
200350600Sobrien						| IER_ERLS | IER_EMSC);
200450600Sobrien		}
200550600Sobrien#ifdef PC98
200650600Sobrien		if (com->pc98_if_type == COM_IF_RSA98III) {
200750600Sobrien			outb(com->rsabase + rsa_ier, 0x1d);
200850600Sobrien			outb(com->intr_ctl_port, IER_ERLS | IER_EMSC);
200950600Sobrien		}
201050600Sobrien#endif
201150600Sobrien#ifdef PC98
201250600Sobrien		}
201350600Sobrien#endif
201450600Sobrien		enable_intr();
201550600Sobrien		/*
201650600Sobrien		 * Handle initial DCD.  Callout devices get a fake initial
201750600Sobrien		 * DCD (trapdoor DCD).  If we are callout, then any sleeping
201850600Sobrien		 * callin opens get woken up and resume sleeping on "siobi"
201950600Sobrien		 * instead of "siodcd".
202050600Sobrien		 */
202150600Sobrien		/*
202250600Sobrien		 * XXX `mynor & CALLOUT_MASK' should be
202350600Sobrien		 * `tp->t_cflag & (SOFT_CARRIER | TRAPDOOR_CARRIER) where
202450600Sobrien		 * TRAPDOOR_CARRIER is the default initial state for callout
202550600Sobrien		 * devices and SOFT_CARRIER is like CLOCAL except it hides
202650600Sobrien		 * the true carrier.
202750600Sobrien		 */
202850600Sobrien#ifdef PC98
202950600Sobrien		if ((IS_8251(com->pc98_if_type) &&
203050600Sobrien			(pc98_get_modem_status(com) & TIOCM_CAR)) ||
203150600Sobrien		    (!IS_8251(com->pc98_if_type) &&
203250600Sobrien			(com->prev_modem_status & MSR_DCD)) ||
203350600Sobrien		    mynor & CALLOUT_MASK)
203450600Sobrien#else
203550600Sobrien		if (com->prev_modem_status & MSR_DCD || mynor & CALLOUT_MASK)
203650600Sobrien#endif
203750600Sobrien			(*linesw[tp->t_line].l_modem)(tp, 1);
203850600Sobrien	}
203950600Sobrien	/*
204050600Sobrien	 * Wait for DCD if necessary.
204150600Sobrien	 */
204250600Sobrien	if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK)
204350600Sobrien	    && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) {
204450600Sobrien		++com->wopeners;
204550600Sobrien		error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "siodcd", 0);
204650600Sobrien		if (com_addr(unit) == NULL)
204750600Sobrien			return (ENXIO);
204850600Sobrien		--com->wopeners;
204950600Sobrien		if (error != 0 || com->gone)
205050600Sobrien			goto out;
205150600Sobrien		goto open_top;
205250600Sobrien	}
205350600Sobrien	error =	(*linesw[tp->t_line].l_open)(dev, tp);
205450600Sobrien	disc_optim(tp, &tp->t_termios, com);
205550600Sobrien	if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK)
205650600Sobrien		com->active_out = TRUE;
205750600Sobrien	siosettimeout();
205850600Sobrienout:
205950600Sobrien	splx(s);
206050600Sobrien	if (!(tp->t_state & TS_ISOPEN) && com->wopeners == 0)
206150600Sobrien		comhardclose(com);
206250600Sobrien	return (error);
206350600Sobrien}
206450600Sobrien
206550600Sobrienstatic int
206650600Sobriensioclose(dev, flag, mode, p)
206750600Sobrien	dev_t		dev;
206850600Sobrien	int		flag;
206950600Sobrien	int		mode;
207050600Sobrien	struct proc	*p;
207150600Sobrien{
207250600Sobrien	struct com_s	*com;
207350600Sobrien	int		mynor;
207450600Sobrien	int		s;
207518334Speter	struct tty	*tp;
207618334Speter
207750600Sobrien	mynor = minor(dev);
207818334Speter	if (mynor & CONTROL_MASK)
207950600Sobrien		return (0);
208050600Sobrien	com = com_addr(MINOR_TO_UNIT(mynor));
208150600Sobrien	tp = com->tp;
208218334Speter	s = spltty();
208350600Sobrien	(*linesw[tp->t_line].l_close)(tp, flag);
208450600Sobrien#ifdef PC98
208550600Sobrien	com->modem_checking = 0;
208650600Sobrien#endif
208750600Sobrien	disc_optim(tp, &tp->t_termios, com);
208850600Sobrien	siostop(tp, FREAD | FWRITE);
208950600Sobrien	comhardclose(com);
209050600Sobrien	ttyclose(tp);
209150600Sobrien	siosettimeout();
209250600Sobrien	splx(s);
209350600Sobrien	if (com->gone) {
209450600Sobrien		printf("sio%d: gone\n", com->unit);
209550600Sobrien		s = spltty();
209650600Sobrien		com_addr(com->unit) = NULL;
209750600Sobrien		if (com->ibuf != NULL)
209850600Sobrien			free(com->ibuf, M_DEVBUF);
209950600Sobrien		bzero(tp, sizeof *tp);
210050600Sobrien		free(com, M_DEVBUF);
210150600Sobrien		splx(s);
210250600Sobrien	}
210350600Sobrien	return (0);
210450600Sobrien}
210550600Sobrien
210650600Sobrienstatic void
210750600Sobriencomhardclose(com)
210850600Sobrien	struct com_s	*com;
210950600Sobrien{
211050600Sobrien	Port_t		iobase;
211150600Sobrien	int		s;
211250600Sobrien	struct tty	*tp;
211350600Sobrien	int		unit;
211450600Sobrien#ifdef PC98
211550600Sobrien	int		port_shift = 0;
211650600Sobrien#endif
211750600Sobrien
211850600Sobrien	unit = com->unit;
211950600Sobrien	iobase = com->iobase;
212050600Sobrien	s = spltty();
212150600Sobrien	com->poll = FALSE;
212250600Sobrien	com->poll_output = FALSE;
212350600Sobrien	com->do_timestamp = FALSE;
212450600Sobrien	com->do_dcd_timestamp = FALSE;
212550600Sobrien	com->pps.ppsparam.mode = 0;
212650600Sobrien#ifdef PC98
212750600Sobrien	if (IS_8251(com->pc98_if_type))
212850600Sobrien	    com_send_break_off(com);
212950600Sobrien	else {
213050600Sobrien	    port_shift = if_16550a_type[com->pc98_if_type & 0x0f].port_shift;
213150600Sobrien	    outb(iobase + (com_cfcr << port_shift),
213250600Sobrien		 com->cfcr_image &= ~CFCR_SBREAK);
213350600Sobrien	}
213450600Sobrien#else
213550600Sobrien	outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK);
213650600Sobrien#endif
213750600Sobrien	{
213850600Sobrien#ifdef PC98
213950600Sobrien		int tmp;
214050600Sobrien		if (IS_8251(com->pc98_if_type))
214150600Sobrien			com_int_TxRx_disable(com);
214250600Sobrien		else
214350600Sobrien			outb(iobase + (com_ier << port_shift), 0);
214450600Sobrien		if (com->pc98_if_type == COM_IF_RSA98III) {
214550600Sobrien			outb(com->rsabase + rsa_ier, 0x00);
214650600Sobrien		}
214750600Sobrien#else
214850600Sobrien		outb(iobase + com_ier, 0);
214950600Sobrien#endif
215050600Sobrien		tp = com->tp;
215150600Sobrien#ifdef PC98
215250600Sobrien		if (IS_8251(com->pc98_if_type))
215350600Sobrien			tmp = pc98_get_modem_status(com) & TIOCM_CAR;
215450600Sobrien		else
215550600Sobrien			tmp = com->prev_modem_status & MSR_DCD;
215650600Sobrien#endif
215718334Speter		if (tp->t_cflag & HUPCL
215850600Sobrien		    /*
215950600Sobrien		     * XXX we will miss any carrier drop between here and the
216050600Sobrien		     * next open.  Perhaps we should watch DCD even when the
216150600Sobrien		     * port is closed; it is not sufficient to check it at
216250600Sobrien		     * the next open because it might go up and down while
216350600Sobrien		     * we're not watching.
216450600Sobrien		     */
216550600Sobrien		    || !com->active_out
216650600Sobrien#ifdef PC98
216750600Sobrien		       && !(tmp)
216850600Sobrien#else
216950600Sobrien		       && !(com->prev_modem_status & MSR_DCD)
217050600Sobrien#endif
217150600Sobrien		       && !(com->it_in.c_cflag & CLOCAL)
217250600Sobrien		    || !(tp->t_state & TS_ISOPEN)) {
217350600Sobrien#ifdef PC98
217450600Sobrien			if (IS_8251(com->pc98_if_type))
217550600Sobrien			    com_tiocm_bic(com, TIOCM_DTR|TIOCM_RTS|TIOCM_LE);
217650600Sobrien			else
217750600Sobrien#endif
217850600Sobrien			(void)commctl(com, TIOCM_DTR, DMBIC);
217950600Sobrien			if (com->dtr_wait != 0 && !(com->state & CS_DTR_OFF)) {
218050600Sobrien				timeout(siodtrwakeup, com, com->dtr_wait);
218118334Speter				com->state |= CS_DTR_OFF;
218250600Sobrien			}
218350600Sobrien		}
218450600Sobrien#ifdef PC98
218550600Sobrien		else {
218650600Sobrien			if (IS_8251(com->pc98_if_type))
218750600Sobrien				com_tiocm_bic(com, TIOCM_LE );
218850600Sobrien		}
218918334Speter#endif
219050600Sobrien	}
219150600Sobrien	if (com->hasfifo) {
219250600Sobrien		/*
219350600Sobrien		 * Disable fifos so that they are off after controlled
219450600Sobrien		 * reboots.  Some BIOSes fail to detect 16550s when the
219550600Sobrien		 * fifos are enabled.
219650600Sobrien		 */
219750600Sobrien#ifdef PC98
219850600Sobrien		outb(iobase + (com_fifo << port_shift), 0);
219950600Sobrien#else
220050600Sobrien		outb(iobase + com_fifo, 0);
220150600Sobrien#endif
220250600Sobrien	}
220350600Sobrien	com->active_out = FALSE;
220450600Sobrien	wakeup(&com->active_out);
220550600Sobrien	wakeup(TSA_CARR_ON(tp));	/* restart any wopeners */
220650600Sobrien	splx(s);
220750600Sobrien}
220850600Sobrien
220950600Sobrienstatic int
221050600Sobriensioread(dev, uio, flag)
221150600Sobrien	dev_t		dev;
221250600Sobrien	struct uio	*uio;
221350600Sobrien	int		flag;
221450600Sobrien{
221550600Sobrien	int		mynor;
221650600Sobrien	int		unit;
221750600Sobrien	struct tty	*tp;
221850600Sobrien
221950600Sobrien	mynor = minor(dev);
222050600Sobrien	if (mynor & CONTROL_MASK)
222150600Sobrien		return (ENODEV);
222250600Sobrien	unit = MINOR_TO_UNIT(mynor);
222350600Sobrien	if (com_addr(unit)->gone)
222450600Sobrien		return (ENODEV);
222550600Sobrien	tp = com_addr(unit)->tp;
222650600Sobrien	return ((*linesw[tp->t_line].l_read)(tp, uio, flag));
222750600Sobrien}
222850600Sobrien
222950600Sobrienstatic int
223050600Sobriensiowrite(dev, uio, flag)
223150600Sobrien	dev_t		dev;
223250600Sobrien	struct uio	*uio;
223350600Sobrien	int		flag;
223450600Sobrien{
223550600Sobrien	int		mynor;
223650600Sobrien	struct tty	*tp;
223750600Sobrien	int		unit;
223850600Sobrien
223950600Sobrien	mynor = minor(dev);
224050600Sobrien	if (mynor & CONTROL_MASK)
224150600Sobrien		return (ENODEV);
224250600Sobrien
224350600Sobrien	unit = MINOR_TO_UNIT(mynor);
224450600Sobrien	if (com_addr(unit)->gone)
224550600Sobrien		return (ENODEV);
224650600Sobrien	tp = com_addr(unit)->tp;
224750600Sobrien	/*
224850600Sobrien	 * (XXX) We disallow virtual consoles if the physical console is
224950600Sobrien	 * a serial port.  This is in case there is a display attached that
225050600Sobrien	 * is not the console.  In that situation we don't need/want the X
225150600Sobrien	 * server taking over the console.
225250600Sobrien	 */
225350600Sobrien	if (constty != NULL && unit == comconsole)
225450600Sobrien		constty = NULL;
225550600Sobrien	return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
225618334Speter}
225718334Speter
225850600Sobrienstatic void
225918334Spetersiobusycheck(chan)
226050600Sobrien	void	*chan;
226150600Sobrien{
226250600Sobrien	struct com_s	*com;
226350600Sobrien	int		s;
226418334Speter
226550600Sobrien	com = (struct com_s *)chan;
226650600Sobrien
226718334Speter	/*
226850600Sobrien	 * Clear TS_BUSY if low-level output is complete.
226950600Sobrien	 * spl locking is sufficient because siointr1() does not set CS_BUSY.
227050600Sobrien	 * If siointr1() clears CS_BUSY after we look at it, then we'll get
227118334Speter	 * called again.  Reading the line status port outside of siointr1()
227250600Sobrien	 * is safe because CS_BUSY is clear so there are no output interrupts
227350600Sobrien	 * to lose.
227450600Sobrien	 */
227518334Speter	s = spltty();
227650600Sobrien	if (com->state & CS_BUSY)
227750600Sobrien		com->extra_state &= ~CSE_BUSYCHECK;	/* False alarm. */
227850600Sobrien#ifdef	PC98
227950600Sobrien	else if ((IS_8251(com->pc98_if_type) &&
228050600Sobrien		 (inb(com->sts_port) & (STS8251_TxRDY | STS8251_TxEMP))
228150600Sobrien		 == (STS8251_TxRDY | STS8251_TxEMP)) ||
228250600Sobrien		 (inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY))
228350600Sobrien		 == (LSR_TSRE | LSR_TXRDY)) {
228450600Sobrien#else
228550600Sobrien	else if ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY))
228650600Sobrien	    == (LSR_TSRE | LSR_TXRDY)) {
228750600Sobrien#endif
228850600Sobrien		com->tp->t_state &= ~TS_BUSY;
228950600Sobrien		ttwwakeup(com->tp);
229050600Sobrien		com->extra_state &= ~CSE_BUSYCHECK;
229150600Sobrien	} else
229250600Sobrien		timeout(siobusycheck, com, hz / 100);
229350600Sobrien	splx(s);
229450600Sobrien}
229550600Sobrien
229650600Sobrienstatic void
229750600Sobriensiodtrwakeup(chan)
229850600Sobrien	void	*chan;
229950600Sobrien{
230050600Sobrien	struct com_s	*com;
230150600Sobrien
230250600Sobrien	com = (struct com_s *)chan;
230350600Sobrien	com->state &= ~CS_DTR_OFF;
230450600Sobrien	wakeup(&com->dtr_wait);
230550600Sobrien}
230650600Sobrien
230750600Sobrienstatic void
230850600Sobriensioinput(com)
230950600Sobrien	struct com_s	*com;
231050600Sobrien{
231150600Sobrien	u_char		*buf;
231250600Sobrien	int		incc;
231350600Sobrien	u_char		line_status;
231450600Sobrien	int		recv_data;
231550600Sobrien	struct tty	*tp;
231650600Sobrien#ifdef PC98
231750600Sobrien	u_char		tmp;
231850600Sobrien#endif
231950600Sobrien
232050600Sobrien	buf = com->ibuf;
232150600Sobrien	tp = com->tp;
232250600Sobrien	if (!(tp->t_state & TS_ISOPEN) || !(tp->t_cflag & CREAD)) {
232318334Speter		com_events -= (com->iptr - com->ibuf);
232418334Speter		com->iptr = com->ibuf;
232550600Sobrien		return;
232650600Sobrien	}
232750600Sobrien	if (tp->t_state & TS_CAN_BYPASS_L_RINT) {
232850600Sobrien		/*
232950600Sobrien		 * Avoid the grotesquely inefficient lineswitch routine
233050600Sobrien		 * (ttyinput) in "raw" mode.  It usually takes about 450
233150600Sobrien		 * instructions (that's without canonical processing or echo!).
233250600Sobrien		 * slinput is reasonably fast (usually 40 instructions plus
233350600Sobrien		 * call overhead).
233450600Sobrien		 */
233550600Sobrien		do {
233650600Sobrien			enable_intr();
233750600Sobrien			incc = com->iptr - buf;
233850600Sobrien			if (tp->t_rawq.c_cc + incc > tp->t_ihiwat
233950600Sobrien			    && (com->state & CS_RTS_IFLOW
234050600Sobrien				|| tp->t_iflag & IXOFF)
234150600Sobrien			    && !(tp->t_state & TS_TBLOCK))
234250600Sobrien				ttyblock(tp);
234350600Sobrien			com->delta_error_counts[CE_TTY_BUF_OVERFLOW]
234450600Sobrien				+= b_to_q((char *)buf, incc, &tp->t_rawq);
234550600Sobrien			buf += incc;
234650600Sobrien			tk_nin += incc;
234750600Sobrien			tk_rawcc += incc;
234850600Sobrien			tp->t_rawcc += incc;
234950600Sobrien			ttwakeup(tp);
235050600Sobrien			if (tp->t_state & TS_TTSTOP
235150600Sobrien			    && (tp->t_iflag & IXANY
235250600Sobrien				|| tp->t_cc[VSTART] == tp->t_cc[VSTOP])) {
235350600Sobrien				tp->t_state &= ~TS_TTSTOP;
235418334Speter				tp->t_lflag &= ~FLUSHO;
235518334Speter				comstart(tp);
235650600Sobrien			}
235718334Speter			disable_intr();
235818334Speter		} while (buf < com->iptr);
235950600Sobrien	} else {
236018334Speter		do {
236150600Sobrien			enable_intr();
236250600Sobrien			line_status = buf[com->ierroff];
236350600Sobrien			recv_data = *buf++;
236450600Sobrien			if (line_status
236550600Sobrien			    & (LSR_BI | LSR_FE | LSR_OE | LSR_PE)) {
236650600Sobrien				if (line_status & LSR_BI)
236750600Sobrien					recv_data |= TTY_BI;
236850600Sobrien				if (line_status & LSR_FE)
236950600Sobrien					recv_data |= TTY_FE;
237050600Sobrien				if (line_status & LSR_OE)
237150600Sobrien					recv_data |= TTY_OE;
237250600Sobrien				if (line_status & LSR_PE)
237350600Sobrien					recv_data |= TTY_PE;
237450600Sobrien			}
237550600Sobrien			(*linesw[tp->t_line].l_rint)(recv_data, tp);
237650600Sobrien			disable_intr();
237750600Sobrien		} while (buf < com->iptr);
237818334Speter	}
237918334Speter	com_events -= (com->iptr - com->ibuf);
238050600Sobrien	com->iptr = com->ibuf;
238118334Speter
238218334Speter	/*
238350600Sobrien	 * There is now room for another low-level buffer full of input,
238418334Speter	 * so enable RTS if it is now disabled and there is room in the
238550600Sobrien	 * high-level buffer.
238618334Speter	 */
238750600Sobrien#ifdef PC98
238818334Speter	if (IS_8251(com->pc98_if_type))
238950600Sobrien		tmp = com_tiocm_get(com) & TIOCM_RTS;
239050600Sobrien	else
239150600Sobrien		tmp = com->mcr_image & MCR_RTS;
239250600Sobrien	if ((com->state & CS_RTS_IFLOW) && !(tmp) &&
239350600Sobrien	    !(tp->t_state & TS_TBLOCK))
239450600Sobrien		if (IS_8251(com->pc98_if_type))
239550600Sobrien			com_tiocm_bis(com, TIOCM_RTS);
239650600Sobrien		else
239750600Sobrien			outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS);
239818334Speter#else
239950600Sobrien	if ((com->state & CS_RTS_IFLOW) && !(com->mcr_image & MCR_RTS) &&
240050600Sobrien	    !(tp->t_state & TS_TBLOCK))
240150600Sobrien		outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS);
240250600Sobrien#endif
240350600Sobrien}
240450600Sobrien
240550600Sobrienstatic void
240650600Sobriensiointr(unit)
240750600Sobrien	int	unit;
240850600Sobrien{
240950600Sobrien#ifndef COM_MULTIPORT
241050600Sobrien	COM_LOCK();
241150600Sobrien	siointr1(com_addr(unit));
241250600Sobrien	COM_UNLOCK();
241350600Sobrien#else /* COM_MULTIPORT */
241450600Sobrien	struct com_s    *com;
241550600Sobrien	bool_t		possibly_more_intrs;
241650600Sobrien#ifdef PC98
241750600Sobrien	u_char		rsa_buf_status;
241850600Sobrien#endif
241950600Sobrien
242050600Sobrien	/*
242150600Sobrien	 * Loop until there is no activity on any port.  This is necessary
242250600Sobrien	 * to get an interrupt edge more than to avoid another interrupt.
242350600Sobrien	 * If the IRQ signal is just an OR of the IRQ signals from several
242450600Sobrien	 * devices, then the edge from one may be lost because another is
242550600Sobrien	 * on.
242650600Sobrien	 */
242750600Sobrien	COM_LOCK();
242850600Sobrien	do {
242950600Sobrien		possibly_more_intrs = FALSE;
243050600Sobrien		for (unit = 0; unit < NSIOTOT; ++unit) {
243150600Sobrien			com = com_addr(unit);
243250600Sobrien			/*
243350600Sobrien			 * XXX COM_LOCK();
243450600Sobrien			 * would it work here, or be counter-productive?
243550600Sobrien			 */
243650600Sobrien#ifdef PC98
243750600Sobrien			if (com != NULL
243850600Sobrien			    && !com->gone
243950600Sobrien			    && IS_8251(com->pc98_if_type)){
244050600Sobrien				siointr1(com);
244150600Sobrien			} else
244250600Sobrien#endif /* PC98 */
244350600Sobrien#ifdef PC98
244418334Speter			if (com != NULL
244518334Speter			    && !com->gone
244650600Sobrien			    && com->pc98_if_type == COM_IF_RSA98III) {
244750600Sobrien			  rsa_buf_status = inb(com->rsabase + rsa_srr) & 0xc9;
244850600Sobrien			  if ((rsa_buf_status & 0xc8)
244918334Speter			      || !(rsa_buf_status & 0x01)) {
245018334Speter			    siointr1(com);
245118334Speter			    if(rsa_buf_status
245218334Speter			       != (inb(com->rsabase + rsa_srr) & 0xc9))
245318334Speter			      possibly_more_intrs = TRUE;
245418334Speter			  }
245518334Speter			} else
245618334Speter#endif
245718334Speter			if (com != NULL
245818334Speter			    && !com->gone
245918334Speter			    && (inb(com->int_id_port) & IIR_IMASK)
246018334Speter			       != IIR_NOPEND) {
246118334Speter				siointr1(com);
246218334Speter				possibly_more_intrs = TRUE;
246318334Speter			}
246418334Speter			/* XXX COM_UNLOCK(); */
246518334Speter		}
246618334Speter	} while (possibly_more_intrs);
246718334Speter	COM_UNLOCK();
246818334Speter#endif /* COM_MULTIPORT */
246950600Sobrien}
247018334Speter
247118334Speterstatic void
247218334Spetersiointr1(com)
247318334Speter	struct com_s	*com;
247418334Speter{
247518334Speter	u_char	line_status;
247618334Speter	u_char	modem_status;
247718334Speter	u_char	*ioptr;
247818334Speter	u_char	recv_data;
247918334Speter	u_char	int_ctl;
248018334Speter	u_char	int_ctl_new;
248118334Speter	struct	timecounter *tc;
248218334Speter	u_int	count;
248318334Speter
248418334Speter#ifdef PC98
248518334Speter	u_char	tmp=0;
248618334Speter	u_char	rsa_buf_status = 0;
248718334Speter	int	rsa_tx_fifo_size=0;
248818334Speter	recv_data=0;
248918334Speter#endif /* PC98 */
249018334Speter
249118334Speter	int_ctl = inb(com->intr_ctl_port);
249218334Speter	int_ctl_new = int_ctl;
249318334Speter
249418334Speter	while (!com->gone) {
249518334Speter#ifdef PC98
249618334Speterstatus_read:;
249718334Speter		if (IS_8251(com->pc98_if_type)) {
249818334Speter			tmp = inb(com->sts_port);
249950600Sobrienmore_intr:
250018334Speter			line_status = 0;
250118334Speter			if (tmp & STS8251_TxRDY) line_status |= LSR_TXRDY;
250218334Speter			if (tmp & STS8251_RxRDY) line_status |= LSR_RXRDY;
250318334Speter			if (tmp & STS8251_TxEMP) line_status |= LSR_TSRE;
250418334Speter			if (tmp & STS8251_PE)    line_status |= LSR_PE;
250518334Speter			if (tmp & STS8251_OE)    line_status |= LSR_OE;
250618334Speter			if (tmp & STS8251_FE)    line_status |= LSR_FE;
250718334Speter			if (tmp & STS8251_BD_SD) line_status |= LSR_BI;
250818334Speter		} else
250918334Speter#endif /* PC98 */
251018334Speter		if (com->pps.ppsparam.mode & PPS_CAPTUREBOTH) {
251118334Speter			modem_status = inb(com->modem_status_port);
251218334Speter		        if ((modem_status ^ com->last_modem_status) & MSR_DCD) {
251318334Speter				tc = timecounter;
251418334Speter				count = tc->tc_get_timecount(tc);
251518334Speter				pps_event(&com->pps, tc, count,
251618334Speter				    (modem_status & MSR_DCD) ?
251718334Speter				    PPS_CAPTURECLEAR : PPS_CAPTUREASSERT);
251818334Speter			}
251918334Speter		}
252018334Speter		line_status = inb(com->line_status_port);
252118334Speter#ifdef PC98
252218334Speter		if (com->pc98_if_type == COM_IF_RSA98III)
252318334Speter			rsa_buf_status = inb(com->rsabase + rsa_srr);
252418334Speter#endif /* PC98 */
252518334Speter
252618334Speter		/* input event? (check first to help avoid overruns) */
252718334Speter#ifndef PC98
252818334Speter		while (line_status & LSR_RCV_MASK) {
252918334Speter#else
253018334Speter		while ((line_status & LSR_RCV_MASK)
253118334Speter		       || (com->pc98_if_type == COM_IF_RSA98III
253218334Speter			   && (rsa_buf_status & 0x08))) {
253318334Speter#endif /* PC98 */
253418334Speter			/* break/unnattached error bits or real input? */
253518334Speter#ifdef PC98
253618334Speter			if (IS_8251(com->pc98_if_type)) {
253718334Speter				recv_data = inb(com->data_port);
253818334Speter				if (tmp & 0x78) {
253918334Speter					pc98_i8251_or_cmd(com,CMD8251_ER);
254018334Speter					recv_data = 0;
254118334Speter				}
254218334Speter			} else {
254318334Speter#endif /* PC98 */
254418334Speter#ifdef PC98
254518334Speter			if (com->pc98_if_type == COM_IF_RSA98III) {
254618334Speter			  if (!(rsa_buf_status & 0x08))
254718334Speter			    recv_data = 0;
254818334Speter			  else {
254918334Speter			    recv_data = inb(com->data_port);
255018334Speter			  }
255118334Speter			} else
255218334Speter#endif
255318334Speter			if (!(line_status & LSR_RXRDY))
255418334Speter				recv_data = 0;
255518334Speter			else
255618334Speter				recv_data = inb(com->data_port);
255718334Speter#ifdef PC98
255818334Speter			}
255918334Speter#endif
256018334Speter			if (line_status & (LSR_BI | LSR_FE | LSR_PE)) {
256118334Speter				/*
256218334Speter				 * Don't store BI if IGNBRK or FE/PE if IGNPAR.
256318334Speter				 * Otherwise, push the work to a higher level
256418334Speter				 * (to handle PARMRK) if we're bypassing.
256518334Speter				 * Otherwise, convert BI/FE and PE+INPCK to 0.
256618334Speter				 *
256718334Speter				 * This makes bypassing work right in the
256818334Speter				 * usual "raw" case (IGNBRK set, and IGNPAR
256918334Speter				 * and INPCK clear).
257018334Speter				 *
257118334Speter				 * Note: BI together with FE/PE means just BI.
257218334Speter				 */
257318334Speter				if (line_status & LSR_BI) {
257418334Speter#if defined(DDB) && defined(BREAK_TO_DEBUGGER)
257518334Speter					if (com->unit == comconsole) {
257618334Speter						breakpoint();
257718334Speter						goto cont;
257818334Speter					}
257918334Speter#endif
258018334Speter					if (com->tp == NULL
258152561Sobrien					    || com->tp->t_iflag & IGNBRK)
258218334Speter						goto cont;
258318334Speter				} else {
258418334Speter					if (com->tp == NULL
258518334Speter					    || com->tp->t_iflag & IGNPAR)
258618334Speter						goto cont;
258718334Speter				}
258818334Speter				if (com->tp->t_state & TS_CAN_BYPASS_L_RINT
258918334Speter				    && (line_status & (LSR_BI | LSR_FE)
259018334Speter					|| com->tp->t_iflag & INPCK))
259118334Speter					recv_data = 0;
259250600Sobrien			}
259350600Sobrien			++com->bytes_in;
259450600Sobrien			if (com->hotchar != 0 && recv_data == com->hotchar)
259550600Sobrien				setsofttty();
259650600Sobrien			ioptr = com->iptr;
259750600Sobrien			if (ioptr >= com->ibufend)
259818334Speter				CE_RECORD(com, CE_INTERRUPT_BUF_OVERFLOW);
259918334Speter			else {
260018334Speter				if (com->do_timestamp)
260150600Sobrien					microtime(&com->timestamp);
260250600Sobrien				++com_events;
260350600Sobrien				schedsofttty();
260450600Sobrien#if 0 /* for testing input latency vs efficiency */
260550600Sobrienif (com->iptr - com->ibuf == 8)
260650600Sobrien	setsofttty();
260750600Sobrien#endif
260850600Sobrien				ioptr[0] = recv_data;
260950600Sobrien				ioptr[com->ierroff] = line_status;
261050600Sobrien				com->iptr = ++ioptr;
261150600Sobrien				if (ioptr == com->ihighwater
261250600Sobrien				    && com->state & CS_RTS_IFLOW)
261318334Speter#ifdef PC98
261418334Speter					if (IS_8251(com->pc98_if_type))
261518334Speter						com_tiocm_bic(com, TIOCM_RTS);
261618334Speter					else
261718334Speter#endif
261818334Speter					outb(com->modem_ctl_port,
261918334Speter					     com->mcr_image &= ~MCR_RTS);
262052561Sobrien				if (line_status & LSR_OE)
262118334Speter					CE_RECORD(com, CE_OVERRUN);
262218334Speter			}
262318334Spetercont:
262418334Speter			/*
262518334Speter			 * "& 0x7F" is to avoid the gcc-1.40 generating a slow
262618334Speter			 * jump from the top of the loop to here
262718334Speter			 */
262818334Speter#ifdef PC98
262918334Speter			if (IS_8251(com->pc98_if_type))
263018334Speter				goto status_read;
263118334Speter			else
263218334Speter#endif
263318334Speter			line_status = inb(com->line_status_port) & 0x7F;
263418334Speter#ifdef PC98
263518334Speter			if (com->pc98_if_type == COM_IF_RSA98III)
263618334Speter				rsa_buf_status = inb(com->rsabase + rsa_srr);
263750600Sobrien#endif /* PC98 */
263818334Speter		}
263918334Speter
264018334Speter		/* modem status change? (always check before doing output) */
264118334Speter#ifdef PC98
264218334Speter		if (!IS_8251(com->pc98_if_type)) {
264318334Speter#endif
264418334Speter		modem_status = inb(com->modem_status_port);
264518334Speter		if (modem_status != com->last_modem_status) {
264618334Speter			if (com->do_dcd_timestamp
264718334Speter			    && !(com->last_modem_status & MSR_DCD)
264818334Speter			    && modem_status & MSR_DCD)
264918334Speter				microtime(&com->dcd_timestamp);
265018334Speter
265118334Speter			/*
265218334Speter			 * Schedule high level to handle DCD changes.  Note
265318334Speter			 * that we don't use the delta bits anywhere.  Some
265418334Speter			 * UARTs mess them up, and it's easy to remember the
265518334Speter			 * previous bits and calculate the delta.
265618334Speter			 */
265718334Speter			com->last_modem_status = modem_status;
265818334Speter			if (!(com->state & CS_CHECKMSR)) {
265918334Speter				com_events += LOTS_OF_EVENTS;
266018334Speter				com->state |= CS_CHECKMSR;
266118334Speter				setsofttty();
266218334Speter			}
266318334Speter
266418334Speter			/* handle CTS change immediately for crisp flow ctl */
266518334Speter			if (com->state & CS_CTS_OFLOW) {
266618334Speter				if (modem_status & MSR_CTS)
266718334Speter					com->state |= CS_ODEVREADY;
266818334Speter				else
266918334Speter					com->state &= ~CS_ODEVREADY;
267018334Speter			}
267118334Speter		}
267218334Speter#ifdef PC98
267318334Speter		}
267418334Speter#endif
267518334Speter
267618334Speter		/* output queued and everything ready? */
267718334Speter#ifndef PC98
267818334Speter		if (line_status & LSR_TXRDY
267918334Speter		    && com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) {
268018334Speter#else
268118334Speter		if (((com->pc98_if_type == COM_IF_RSA98III)
268218334Speter		     ? (rsa_buf_status & 0x02)
268318334Speter		     : (line_status & LSR_TXRDY))
268418334Speter		    && com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) {
268550600Sobrien#endif
268618334Speter			ioptr = com->obufq.l_head;
268718334Speter			if (com->tx_fifo_size > 1) {
268818334Speter				u_int	ocount;
268918334Speter
269050600Sobrien				ocount = com->obufq.l_tail - ioptr;
269118334Speter#ifdef PC98
269250600Sobrien				if (com->pc98_if_type == COM_IF_RSA98III) {
269318334Speter				  rsa_buf_status = inb(com->rsabase + rsa_srr);
269418334Speter				  rsa_tx_fifo_size = 1024;
269518334Speter				  if (!(rsa_buf_status & 0x01))
269618334Speter				    rsa_tx_fifo_size = 2048;
269718334Speter				  if (ocount > rsa_tx_fifo_size)
269818334Speter				    ocount = rsa_tx_fifo_size;
269918334Speter				} else
270018334Speter#endif
270118334Speter				if (ocount > com->tx_fifo_size)
270218334Speter					ocount = com->tx_fifo_size;
270318334Speter				com->bytes_out += ocount;
270418334Speter				do
270518334Speter					outb(com->data_port, *ioptr++);
270618334Speter				while (--ocount != 0);
270718334Speter			} else {
270818334Speter				outb(com->data_port, *ioptr++);
270918334Speter				++com->bytes_out;
271018334Speter			}
271118334Speter#ifdef PC98
271218334Speter			if (IS_8251(com->pc98_if_type))
271318334Speter			    if (!(pc98_check_i8251_interrupt(com) & IEN_TxFLAG))
271418334Speter					com_int_Tx_enable(com);
271518334Speter#endif
271618334Speter			com->obufq.l_head = ioptr;
271718334Speter			if (COM_IIR_TXRDYBUG(com)) {
271818334Speter				int_ctl_new = int_ctl | IER_ETXRDY;
271918334Speter			}
272050600Sobrien			if (ioptr >= com->obufq.l_tail) {
272118334Speter				struct lbq	*qp;
272250600Sobrien
272350600Sobrien				qp = com->obufq.l_next;
272450600Sobrien				qp->l_queued = FALSE;
272550600Sobrien				qp = qp->l_next;
272650600Sobrien				if (qp != NULL) {
272750600Sobrien					com->obufq.l_head = qp->l_head;
272850600Sobrien					com->obufq.l_tail = qp->l_tail;
272950600Sobrien					com->obufq.l_next = qp;
273050600Sobrien				} else {
273150600Sobrien					/* output just completed */
273250600Sobrien					if ( COM_IIR_TXRDYBUG(com) ) {
273350600Sobrien						int_ctl_new = int_ctl & ~IER_ETXRDY;
273450600Sobrien					}
273550600Sobrien					com->state &= ~CS_BUSY;
273650600Sobrien#if defined(PC98)
273750600Sobrien					if (IS_8251(com->pc98_if_type))
273850600Sobrien					    if ( pc98_check_i8251_interrupt(com) & IEN_TxFLAG )
273950600Sobrien						com_int_Tx_disable(com);
274050600Sobrien#endif
274150600Sobrien				}
274250600Sobrien				if (!(com->state & CS_ODONE)) {
274350600Sobrien					com_events += LOTS_OF_EVENTS;
274450600Sobrien					com->state |= CS_ODONE;
274550600Sobrien					setsofttty();	/* handle at high level ASAP */
274650600Sobrien				}
274750600Sobrien			}
274850600Sobrien			if ( COM_IIR_TXRDYBUG(com) && (int_ctl != int_ctl_new)) {
274950600Sobrien#ifdef PC98
275050600Sobrien				if (com->pc98_if_type == COM_IF_RSA98III) {
275150600Sobrien				  int_ctl_new &= ~(IER_ETXRDY | IER_ERXRDY);
275250600Sobrien				  outb(com->intr_ctl_port, int_ctl_new);
275350600Sobrien				  outb(com->rsabase + rsa_ier, 0x1d);
275450600Sobrien				} else
275550600Sobrien#endif
275650600Sobrien				outb(com->intr_ctl_port, int_ctl_new);
275750600Sobrien			}
275850600Sobrien		}
275950600Sobrien#ifdef PC98
276050600Sobrien		else if (line_status & LSR_TXRDY) {
276150600Sobrien		    if (IS_8251(com->pc98_if_type))
276250600Sobrien			if ( pc98_check_i8251_interrupt(com) & IEN_TxFLAG )
276350600Sobrien			    com_int_Tx_disable(com);
276450600Sobrien		}
276550600Sobrien		if (IS_8251(com->pc98_if_type))
276650600Sobrien		    if ((tmp = inb(com->sts_port)) & STS8251_RxRDY)
276718334Speter			goto more_intr;
276818334Speter#endif
276918334Speter
277018334Speter		/* finished? */
277118334Speter#ifndef COM_MULTIPORT
277218334Speter#ifdef PC98
277318334Speter		if (IS_8251(com->pc98_if_type))
277418334Speter			return;
277518334Speter#endif
277618334Speter		if ((inb(com->int_id_port) & IIR_IMASK) == IIR_NOPEND)
277750600Sobrien#endif /* COM_MULTIPORT */
277818334Speter			return;
277918334Speter	}
278018334Speter}
278118334Speter
278218334Speterstatic int
278318334Spetersioioctl(dev, cmd, data, flag, p)
278418334Speter	dev_t		dev;
278518334Speter	u_long		cmd;
278618334Speter	caddr_t		data;
278718334Speter	int		flag;
278818334Speter	struct proc	*p;
278918334Speter{
279018334Speter	struct com_s	*com;
279118334Speter	int		error;
279250600Sobrien	Port_t		iobase;
279350600Sobrien	int		mynor;
279450600Sobrien	int		s;
279550600Sobrien	struct tty	*tp;
279650600Sobrien#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
279750600Sobrien	int		oldcmd;
279850600Sobrien	struct termios	term;
279950600Sobrien#endif
280050600Sobrien
280150600Sobrien	mynor = minor(dev);
280250600Sobrien	com = com_addr(MINOR_TO_UNIT(mynor));
280350600Sobrien	if (com->gone)
280450600Sobrien		return (ENODEV);
280550600Sobrien	iobase = com->iobase;
280650600Sobrien	if (mynor & CONTROL_MASK) {
280750600Sobrien		struct termios	*ct;
280850600Sobrien
280950600Sobrien		switch (mynor & CONTROL_MASK) {
281050600Sobrien		case CONTROL_INIT_STATE:
281150600Sobrien			ct = mynor & CALLOUT_MASK ? &com->it_out : &com->it_in;
281250600Sobrien			break;
281350600Sobrien		case CONTROL_LOCK_STATE:
281450600Sobrien			ct = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in;
281550600Sobrien			break;
281650600Sobrien		default:
281718334Speter			return (ENODEV);	/* /dev/nodev */
281818334Speter		}
281952561Sobrien		switch (cmd) {
282018334Speter		case TIOCSETA:
282118334Speter			error = suser(p->p_ucred, &p->p_acflag);
282218334Speter			if (error != 0)
282318334Speter				return (error);
282418334Speter			*ct = *(struct termios *)data;
282518334Speter			return (0);
282650600Sobrien		case TIOCGETA:
282718334Speter			*(struct termios *)data = *ct;
282818334Speter			return (0);
282918334Speter		case TIOCGETD:
283018334Speter			*(int *)data = TTYDISC;
283118334Speter			return (0);
283250600Sobrien		case TIOCGWINSZ:
283350600Sobrien			bzero(data, sizeof(struct winsize));
283450600Sobrien			return (0);
283550600Sobrien		default:
283650600Sobrien			return (ENOTTY);
283750600Sobrien		}
283818334Speter	}
283918334Speter	tp = com->tp;
284018334Speter#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
284118334Speter	term = tp->t_termios;
284218334Speter	oldcmd = cmd;
284318334Speter	error = ttsetcompat(tp, &cmd, data, &term);
284418334Speter	if (error != 0)
284518334Speter		return (error);
284650600Sobrien	if (cmd != oldcmd)
284750600Sobrien		data = (caddr_t)&term;
284850600Sobrien#endif
284950600Sobrien	if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) {
285050600Sobrien		int	cc;
285150600Sobrien		struct termios *dt = (struct termios *)data;
285218334Speter		struct termios *lt = mynor & CALLOUT_MASK
285318334Speter				     ? &com->lt_out : &com->lt_in;
285450600Sobrien
285518334Speter		dt->c_iflag = (tp->t_iflag & lt->c_iflag)
285650600Sobrien			      | (dt->c_iflag & ~lt->c_iflag);
285718334Speter		dt->c_oflag = (tp->t_oflag & lt->c_oflag)
285818334Speter			      | (dt->c_oflag & ~lt->c_oflag);
285918334Speter		dt->c_cflag = (tp->t_cflag & lt->c_cflag)
286018334Speter			      | (dt->c_cflag & ~lt->c_cflag);
286118334Speter		dt->c_lflag = (tp->t_lflag & lt->c_lflag)
286218334Speter			      | (dt->c_lflag & ~lt->c_lflag);
286318334Speter		for (cc = 0; cc < NCCS; ++cc)
286418334Speter			if (lt->c_cc[cc] != 0)
286518334Speter				dt->c_cc[cc] = tp->t_cc[cc];
286618334Speter		if (lt->c_ispeed != 0)
286718334Speter			dt->c_ispeed = tp->t_ispeed;
286818334Speter		if (lt->c_ospeed != 0)
286918334Speter			dt->c_ospeed = tp->t_ospeed;
287018334Speter	}
287118334Speter	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
287218334Speter	if (error != ENOIOCTL)
287318334Speter		return (error);
287418334Speter	s = spltty();
287518334Speter	error = ttioctl(tp, cmd, data, flag);
287650600Sobrien	disc_optim(tp, &tp->t_termios, com);
287718334Speter	if (error != ENOIOCTL) {
287850600Sobrien		splx(s);
287918334Speter		return (error);
288018334Speter	}
288118334Speter#ifdef PC98
288218334Speter	if (IS_8251(com->pc98_if_type)) {
288318334Speter	    switch (cmd) {
288418334Speter	    case TIOCSBRK:
288518334Speter		com_send_break_on( com );
288618334Speter		break;
288718334Speter	    case TIOCCBRK:
288818334Speter		com_send_break_off( com );
288918334Speter		break;
289018334Speter	    case TIOCSDTR:
289118334Speter		com_tiocm_bis(com, TIOCM_DTR | TIOCM_RTS );
289218334Speter		break;
289318334Speter	    case TIOCCDTR:
289418334Speter		com_tiocm_bic(com, TIOCM_DTR);
289518334Speter		break;
289618334Speter	/*
289718334Speter	 * XXX should disallow changing MCR_RTS if CS_RTS_IFLOW is set.  The
289850600Sobrien	 * changes get undone on the next call to comparam().
289918334Speter	 */
290018334Speter	    case TIOCMSET:
290152561Sobrien		com_tiocm_set( com, *(int *)data );
290218334Speter		break;
290318334Speter	    case TIOCMBIS:
290418334Speter		com_tiocm_bis( com, *(int *)data );
290518334Speter		break;
290618334Speter	    case TIOCMBIC:
290718334Speter		com_tiocm_bic( com, *(int *)data );
290818334Speter		break;
290918334Speter	    case TIOCMGET:
291018334Speter		*(int *)data = com_tiocm_get(com);
291118334Speter		break;
291218334Speter	    case TIOCMSDTRWAIT:
291352561Sobrien		/* must be root since the wait applies to following logins */
291452561Sobrien		error = suser(p->p_ucred, &p->p_acflag);
291552561Sobrien		if (error != 0) {
291652561Sobrien			splx(s);
291752561Sobrien			return (error);
291852561Sobrien		}
291952561Sobrien		com->dtr_wait = *(int *)data * hz / 100;
292050600Sobrien		break;
292152561Sobrien	    case TIOCMGDTRWAIT:
292218334Speter		*(int *)data = com->dtr_wait * 100 / hz;
292318334Speter		break;
292418334Speter	    case TIOCTIMESTAMP:
292518334Speter		com->do_timestamp = TRUE;
292618334Speter		*(struct timeval *)data = com->timestamp;
292718334Speter		break;
292818334Speter	    case TIOCDCDTIMESTAMP:
292918334Speter		com->do_dcd_timestamp = TRUE;
293018334Speter		*(struct timeval *)data = com->dcd_timestamp;
293118334Speter		break;
293218334Speter	    default:
293318334Speter		splx(s);
293418334Speter		return (ENOTTY);
293550600Sobrien	    }
293650600Sobrien	} else {
293750600Sobrien	    int port_shift;
293850600Sobrien	    port_shift = if_16550a_type[com->pc98_if_type & 0x0f].port_shift;
293950600Sobrien#endif
294050600Sobrien	switch (cmd) {
294150600Sobrien	case TIOCSBRK:
294218334Speter#ifdef PC98
294318334Speter		outb(iobase + (com_cfcr << port_shift),
294450600Sobrien		     com->cfcr_image |= CFCR_SBREAK);
294550600Sobrien#else
294650600Sobrien		outb(iobase + com_cfcr, com->cfcr_image |= CFCR_SBREAK);
294750600Sobrien#endif
294850600Sobrien		break;
294950600Sobrien	case TIOCCBRK:
295050600Sobrien#ifdef PC98
295150600Sobrien		outb(iobase + (com_cfcr << port_shift),
295250600Sobrien		     com->cfcr_image &= ~CFCR_SBREAK);
295350600Sobrien#else
295450600Sobrien		outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK);
295550600Sobrien#endif
295650600Sobrien		break;
295750600Sobrien	case TIOCSDTR:
295850600Sobrien		(void)commctl(com, TIOCM_DTR, DMBIS);
295950600Sobrien		break;
296050600Sobrien	case TIOCCDTR:
296150600Sobrien		(void)commctl(com, TIOCM_DTR, DMBIC);
296250600Sobrien		break;
296350600Sobrien	/*
296450600Sobrien	 * XXX should disallow changing MCR_RTS if CS_RTS_IFLOW is set.  The
296550600Sobrien	 * changes get undone on the next call to comparam().
296650600Sobrien	 */
296750600Sobrien	case TIOCMSET:
296850600Sobrien		(void)commctl(com, *(int *)data, DMSET);
296950600Sobrien		break;
297050600Sobrien	case TIOCMBIS:
297150600Sobrien		(void)commctl(com, *(int *)data, DMBIS);
297250600Sobrien		break;
297350600Sobrien	case TIOCMBIC:
297450600Sobrien		(void)commctl(com, *(int *)data, DMBIC);
297518334Speter		break;
297618334Speter	case TIOCMGET:
297718334Speter		*(int *)data = commctl(com, 0, DMGET);
297818334Speter		break;
297918334Speter	case TIOCMSDTRWAIT:
298050600Sobrien		/* must be root since the wait applies to following logins */
298118334Speter		error = suser(p->p_ucred, &p->p_acflag);
298218334Speter		if (error != 0) {
298318334Speter			splx(s);
298418334Speter			return (error);
298550600Sobrien		}
298618334Speter		com->dtr_wait = *(int *)data * hz / 100;
298718334Speter		break;
298850600Sobrien	case TIOCMGDTRWAIT:
298950600Sobrien		*(int *)data = com->dtr_wait * 100 / hz;
299050600Sobrien		break;
299150600Sobrien	case TIOCTIMESTAMP:
299250600Sobrien		com->do_timestamp = TRUE;
299350600Sobrien		*(struct timeval *)data = com->timestamp;
299450600Sobrien		break;
299550600Sobrien	case TIOCDCDTIMESTAMP:
299650600Sobrien		com->do_dcd_timestamp = TRUE;
299750600Sobrien		*(struct timeval *)data = com->dcd_timestamp;
299850600Sobrien		break;
299950600Sobrien	default:
300018334Speter		splx(s);
300150600Sobrien		error = pps_ioctl(cmd, data, &com->pps);
300250600Sobrien		if (error == ENODEV)
300350600Sobrien			error = ENOTTY;
300450600Sobrien		return (error);
300550600Sobrien	}
300650600Sobrien#ifdef PC98
300750600Sobrien	}
300850600Sobrien#endif
300950600Sobrien	splx(s);
301018334Speter	return (0);
301150600Sobrien}
301218334Speter
301318334Speterstatic void
301418334Spetersiopoll()
301518334Speter{
301618334Speter	int		unit;
301718334Speter
301818334Speter	if (com_events == 0)
301918334Speter		return;
302050600Sobrienrepeat:
302118334Speter	for (unit = 0; unit < NSIOTOT; ++unit) {
302218334Speter		struct com_s	*com;
302350600Sobrien		int		incc;
302450600Sobrien		struct tty	*tp;
302550600Sobrien
302650600Sobrien		com = com_addr(unit);
302750600Sobrien		if (com == NULL)
302850600Sobrien			continue;
302950600Sobrien		tp = com->tp;
303050600Sobrien		if (tp == NULL || com->gone) {
303150600Sobrien			/*
303218334Speter			 * Discard any events related to never-opened or
303318334Speter			 * going-away devices.
303418334Speter			 */
303518334Speter			disable_intr();
303650600Sobrien			incc = com->iptr - com->ibuf;
303718334Speter			com->iptr = com->ibuf;
303850600Sobrien			if (com->state & CS_CHECKMSR) {
303918334Speter				incc += LOTS_OF_EVENTS;
304052561Sobrien				com->state &= ~CS_CHECKMSR;
304152561Sobrien			}
304250600Sobrien			com_events -= incc;
304350600Sobrien			enable_intr();
304418334Speter			continue;
304550600Sobrien		}
304650600Sobrien		if (com->iptr != com->ibuf) {
304750600Sobrien			disable_intr();
304850600Sobrien			sioinput(com);
304950600Sobrien			enable_intr();
305050600Sobrien		}
305150600Sobrien		if (com->state & CS_CHECKMSR) {
305250600Sobrien			u_char	delta_modem_status;
305350600Sobrien
305450600Sobrien#ifdef PC98
305550600Sobrien			if (!IS_8251(com->pc98_if_type)) {
305650600Sobrien#endif
305750600Sobrien			disable_intr();
305850600Sobrien			delta_modem_status = com->last_modem_status
305950600Sobrien					     ^ com->prev_modem_status;
306050600Sobrien			com->prev_modem_status = com->last_modem_status;
306150600Sobrien			com_events -= LOTS_OF_EVENTS;
306250600Sobrien			com->state &= ~CS_CHECKMSR;
306350600Sobrien			enable_intr();
306450600Sobrien			if (delta_modem_status & MSR_DCD)
306550600Sobrien				(*linesw[tp->t_line].l_modem)
306650600Sobrien					(tp, com->prev_modem_status & MSR_DCD);
306750600Sobrien#ifdef PC98
306850600Sobrien			}
306950600Sobrien#endif
307050600Sobrien		}
307150600Sobrien		if (com->state & CS_ODONE) {
307250600Sobrien			disable_intr();
307350600Sobrien			com_events -= LOTS_OF_EVENTS;
307450600Sobrien			com->state &= ~CS_ODONE;
307550600Sobrien			enable_intr();
307650600Sobrien			if (!(com->state & CS_BUSY)
307752561Sobrien			    && !(com->extra_state & CSE_BUSYCHECK)) {
307852561Sobrien				timeout(siobusycheck, com, hz / 100);
307952561Sobrien				com->extra_state |= CSE_BUSYCHECK;
308050600Sobrien			}
308150600Sobrien			(*linesw[tp->t_line].l_start)(tp);
308250600Sobrien		}
308350600Sobrien		if (com_events == 0)
308450600Sobrien			break;
308550600Sobrien	}
308650600Sobrien	if (com_events >= LOTS_OF_EVENTS)
308750600Sobrien		goto repeat;
308850600Sobrien}
308950600Sobrien
309050600Sobrienstatic int
309150600Sobriencomparam(tp, t)
309250600Sobrien	struct tty	*tp;
309350600Sobrien	struct termios	*t;
309450600Sobrien{
309550600Sobrien	u_int		cfcr;
309650600Sobrien	int		cflag;
309750600Sobrien	struct com_s	*com;
309850600Sobrien	int		divisor;
309950600Sobrien	u_char		dlbh;
310050600Sobrien	u_char		dlbl;
310150600Sobrien	Port_t		iobase;
310250600Sobrien	int		s;
310350600Sobrien	int		unit;
310450600Sobrien#ifdef PC98
310550600Sobrien	int		port_shift = 0;
310650600Sobrien	u_char		param = 0;
310750600Sobrien#endif
310850600Sobrien
310950600Sobrien#ifdef PC98
311050600Sobrien	cfcr = 0;
311150600Sobrien	unit = DEV_TO_UNIT(tp->t_dev);
311250600Sobrien	com = com_addr(unit);
311350600Sobrien	iobase = com->iobase;
311450600Sobrien	if (IS_8251(com->pc98_if_type)) {
311550600Sobrien	    divisor = pc98_ttspeedtab(com, t->c_ospeed);
311650600Sobrien	} else {
311750600Sobrien	    port_shift = if_16550a_type[com->pc98_if_type & 0x0f].port_shift;
311850600Sobrien
311950600Sobrien	    /* do historical conversions */
312050600Sobrien	    if (t->c_ispeed == 0)
312150600Sobrien		t->c_ispeed = t->c_ospeed;
312250600Sobrien
312350600Sobrien	    /* check requested parameters */
312450600Sobrien	    divisor = ttspeedtab(t->c_ospeed,
312550600Sobrien			if_16550a_type[com->pc98_if_type & 0x0f].speedtab);
312650600Sobrien	}
312750600Sobrien#else
312850600Sobrien	/* do historical conversions */
312950600Sobrien	if (t->c_ispeed == 0)
313050600Sobrien		t->c_ispeed = t->c_ospeed;
313150600Sobrien
313250600Sobrien	/* check requested parameters */
313350600Sobrien	divisor = ttspeedtab(t->c_ospeed, comspeedtab);
313450600Sobrien#endif
313550600Sobrien	if (divisor < 0 || divisor > 0 && t->c_ispeed != t->c_ospeed)
313650600Sobrien		return (EINVAL);
313750600Sobrien
313850600Sobrien	/* parameters are OK, convert them to the com struct and the device */
313950600Sobrien#ifndef PC98
314050600Sobrien	unit = DEV_TO_UNIT(tp->t_dev);
314150600Sobrien	com = com_addr(unit);
314250600Sobrien	iobase = com->iobase;
314350600Sobrien#endif
314450600Sobrien	s = spltty();
314550600Sobrien#ifdef PC98
314650600Sobrien	if (IS_8251(com->pc98_if_type)) {
314750600Sobrien		if (divisor == 0)
314850600Sobrien			com_tiocm_bic( com, TIOCM_DTR|TIOCM_RTS|TIOCM_LE );
314950600Sobrien		else
315050600Sobrien			com_tiocm_bis( com, TIOCM_DTR|TIOCM_RTS|TIOCM_LE );
315150600Sobrien	} else {
315250600Sobrien#endif
315350600Sobrien	if (divisor == 0)
315450600Sobrien		(void)commctl(com, TIOCM_DTR, DMBIC);	/* hang up line */
315550600Sobrien	else
315650600Sobrien		(void)commctl(com, TIOCM_DTR, DMBIS);
315750600Sobrien#ifdef PC98
315850600Sobrien	}
315950600Sobrien#endif
316050600Sobrien	cflag = t->c_cflag;
316150600Sobrien#ifdef PC98
316250600Sobrien	if (!IS_8251(com->pc98_if_type)) {
316350600Sobrien#endif
316450600Sobrien	switch (cflag & CSIZE) {
316550600Sobrien	case CS5:
316650600Sobrien		cfcr = CFCR_5BITS;
316750600Sobrien		break;
316850600Sobrien	case CS6:
316950600Sobrien		cfcr = CFCR_6BITS;
317050600Sobrien		break;
317150600Sobrien	case CS7:
317250600Sobrien		cfcr = CFCR_7BITS;
317350600Sobrien		break;
317450600Sobrien	default:
317550600Sobrien		cfcr = CFCR_8BITS;
317650600Sobrien		break;
317750600Sobrien	}
317850600Sobrien	if (cflag & PARENB) {
317918334Speter		cfcr |= CFCR_PENAB;
318050600Sobrien		if (!(cflag & PARODD))
318150600Sobrien			cfcr |= CFCR_PEVEN;
318218334Speter	}
318350600Sobrien	if (cflag & CSTOPB)
318450600Sobrien		cfcr |= CFCR_STOPB;
318550600Sobrien
318650600Sobrien	if (com->hasfifo && divisor != 0) {
318750600Sobrien		/*
318850600Sobrien		 * Use a fifo trigger level low enough so that the input
318950600Sobrien		 * latency from the fifo is less than about 16 msec and
319050600Sobrien		 * the total latency is less than about 30 msec.  These
319150600Sobrien		 * latencies are reasonable for humans.  Serial comms
319218334Speter		 * protocols shouldn't expect anything better since modem
319318334Speter		 * latencies are larger.
319450600Sobrien		 */
319518334Speter		com->fifo_image = t->c_ospeed <= 4800
319650600Sobrien				  ? FIFO_ENABLE : FIFO_ENABLE | FIFO_RX_HIGH;
319750600Sobrien#ifdef COM_ESP
319850600Sobrien		/*
319952561Sobrien		 * The Hayes ESP card needs the fifo DMA mode bit set
320052561Sobrien		 * in compatibility mode.  If not, it will interrupt
320152561Sobrien		 * for each character received.
320252561Sobrien		 */
320352561Sobrien		if (com->esp)
320452561Sobrien			com->fifo_image |= FIFO_DMA_MODE;
320552561Sobrien#endif
320652561Sobrien#ifdef PC98
320752561Sobrien		outb(iobase + (com_fifo << port_shift), com->fifo_image);
320852561Sobrien#else
320952561Sobrien		outb(iobase + com_fifo, com->fifo_image);
321050600Sobrien#endif
321118334Speter	}
321250600Sobrien#ifdef PC98
321350600Sobrien	}
321418334Speter#endif
321550600Sobrien
321650600Sobrien	/*
321750600Sobrien	 * This returns with interrupts disabled so that we can complete
321850600Sobrien	 * the speed change atomically.  Keeping interrupts disabled is
321950600Sobrien	 * especially important while com_data is hidden.
322050600Sobrien	 */
322150600Sobrien	(void) siosetwater(com, t->c_ispeed);
322250600Sobrien
322350600Sobrien#ifdef PC98
322450600Sobrien	if (IS_8251(com->pc98_if_type))
322550600Sobrien	    com_cflag_and_speed_set(com, cflag, t->c_ospeed);
322618334Speter	else {
322750600Sobrien#endif
322850600Sobrien	if (divisor != 0) {
322950600Sobrien#ifdef PC98
323050600Sobrien		outb(iobase + (com_cfcr << port_shift), cfcr | CFCR_DLAB);
323150600Sobrien#else
323218334Speter		outb(iobase + com_cfcr, cfcr | CFCR_DLAB);
323350600Sobrien#endif
323450600Sobrien		/*
323550600Sobrien		 * Only set the divisor registers if they would change,
323650600Sobrien		 * since on some 16550 incompatibles (UMC8669F), setting
323750600Sobrien		 * them while input is arriving them loses sync until
323850600Sobrien		 * data stops arriving.
323918334Speter		 */
324018334Speter		dlbl = divisor & 0xFF;
324150600Sobrien#ifdef PC98
324250600Sobrien		if (inb(iobase + (com_dlbl << port_shift)) != dlbl)
324318334Speter			outb(iobase + (com_dlbl << port_shift), dlbl);
324450600Sobrien		dlbh = (u_int) divisor >> 8;
324550600Sobrien		if (inb(iobase + (com_dlbh << port_shift)) != dlbh)
324650600Sobrien			outb(iobase + (com_dlbh << port_shift), dlbh);
324750600Sobrien#else
324850600Sobrien		if (inb(iobase + com_dlbl) != dlbl)
324950600Sobrien			outb(iobase + com_dlbl, dlbl);
325018334Speter		dlbh = (u_int) divisor >> 8;
325150600Sobrien		if (inb(iobase + com_dlbh) != dlbh)
325250600Sobrien			outb(iobase + com_dlbh, dlbh);
325350600Sobrien#endif
325450600Sobrien	}
325550600Sobrien
325650600Sobrien
325750600Sobrien#ifdef PC98
325850600Sobrien	}
325952561Sobrien	outb(iobase + (com_cfcr << port_shift), com->cfcr_image = cfcr);
326052561Sobrien#else
326150600Sobrien	outb(iobase + com_cfcr, com->cfcr_image = cfcr);
326250600Sobrien#endif
326350600Sobrien
326450600Sobrien	if (!(tp->t_state & TS_TTSTOP))
326550600Sobrien		com->state |= CS_TTGO;
326650600Sobrien
326750600Sobrien	if (cflag & CRTS_IFLOW) {
326850600Sobrien		if (com->st16650a) {
326950600Sobrien			outb(iobase + com_cfcr, 0xbf);
327050600Sobrien			outb(iobase + com_fifo, inb(iobase + com_fifo) | 0x40);
327150600Sobrien		}
327250600Sobrien		com->state |= CS_RTS_IFLOW;
327350600Sobrien		/*
327450600Sobrien		 * If CS_RTS_IFLOW just changed from off to on, the change
327550600Sobrien		 * needs to be propagated to MCR_RTS.  This isn't urgent,
327650600Sobrien		 * so do it later by calling comstart() instead of repeating
327718334Speter		 * a lot of code from comstart() here.
327850600Sobrien		 */
327950600Sobrien	} else if (com->state & CS_RTS_IFLOW) {
328050600Sobrien		com->state &= ~CS_RTS_IFLOW;
328150600Sobrien		/*
328250600Sobrien		 * CS_RTS_IFLOW just changed from on to off.  Force MCR_RTS
328350600Sobrien		 * on here, since comstart() won't do it later.
328450600Sobrien		 */
328550600Sobrien#ifdef PC98
328650600Sobrien		if (IS_8251(com->pc98_if_type))
328750600Sobrien			com_tiocm_bis(com, TIOCM_RTS);
328818334Speter		else
328950600Sobrien#endif
329050600Sobrien		outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS);
329150600Sobrien		if (com->st16650a) {
329250600Sobrien			outb(iobase + com_cfcr, 0xbf);
329350600Sobrien			outb(iobase + com_fifo, inb(iobase + com_fifo) & ~0x40);
329450600Sobrien		}
329550600Sobrien	}
329650600Sobrien
329750600Sobrien
329850600Sobrien	/*
329950600Sobrien	 * Set up state to handle output flow control.
330050600Sobrien	 * XXX - worth handling MDMBUF (DCD) flow control at the lowest level?
330150600Sobrien	 * Now has 10+ msec latency, while CTS flow has 50- usec latency.
330250600Sobrien	 */
330350600Sobrien	com->state |= CS_ODEVREADY;
330450600Sobrien	com->state &= ~CS_CTS_OFLOW;
330550600Sobrien#ifdef PC98
330650600Sobrien	if (com->pc98_if_type == COM_IF_RSA98III) {
330750600Sobrien		param = inb(com->rsabase + rsa_msr);
330818334Speter		outb(com->rsabase + rsa_msr, param & 0x14);
330950600Sobrien	}
331050600Sobrien#endif
331150600Sobrien	if (cflag & CCTS_OFLOW) {
331250600Sobrien		com->state |= CS_CTS_OFLOW;
331350600Sobrien#ifdef PC98
331450600Sobrien		if (IS_8251(com->pc98_if_type)) {
331550600Sobrien			if (!(pc98_get_modem_status(com) & TIOCM_CTS))
331650600Sobrien				com->state &= ~CS_ODEVREADY;
331750600Sobrien		} else {
331850600Sobrien#endif
331950600Sobrien#ifdef PC98
332050600Sobrien		if (com->pc98_if_type == COM_IF_RSA98III) {
332150600Sobrien			/* Set automatic flow control mode */
332250600Sobrien			outb(com->rsabase + rsa_msr, param | 0x08);
332350600Sobrien		} else
332450600Sobrien#endif
332550600Sobrien		if (!(com->last_modem_status & MSR_CTS))
332650600Sobrien			com->state &= ~CS_ODEVREADY;
332718334Speter		if (com->st16650a) {
332850600Sobrien			outb(iobase + com_cfcr, 0xbf);
332918334Speter			outb(iobase + com_fifo, inb(iobase + com_fifo) | 0x80);
333050600Sobrien		}
333118334Speter#ifdef PC98
333250600Sobrien		}
333350600Sobrien#endif
333450600Sobrien	} else {
333550600Sobrien		if (com->st16650a) {
333650600Sobrien			outb(iobase + com_cfcr, 0xbf);
333752561Sobrien			outb(iobase + com_fifo, inb(iobase + com_fifo) & ~0x80);
333852561Sobrien		}
333950600Sobrien	}
334050600Sobrien
334150600Sobrien
334250600Sobrien#ifdef PC98
334350600Sobrien	outb(iobase + (com_cfcr << port_shift), com->cfcr_image);
334450600Sobrien#else
334550600Sobrien	outb(iobase + com_cfcr, com->cfcr_image);
334650600Sobrien#endif
334750600Sobrien
334850600Sobrien
334950600Sobrien	/* XXX shouldn't call functions while intrs are disabled. */
335050600Sobrien	disc_optim(tp, t, com);
335150600Sobrien	/*
335250600Sobrien	 * Recover from fiddling with CS_TTGO.  We used to call siointr1()
335350600Sobrien	 * unconditionally, but that defeated the careful discarding of
335450600Sobrien	 * stale input in sioopen().
335550600Sobrien	 */
335650600Sobrien	if (com->state >= (CS_BUSY | CS_TTGO))
335750600Sobrien		siointr1(com);
335850600Sobrien
335950600Sobrien	enable_intr();
336050600Sobrien	splx(s);
336150600Sobrien	comstart(tp);
336250600Sobrien	if (com->ibufold != NULL) {
336350600Sobrien		free(com->ibufold, M_DEVBUF);
336450600Sobrien		com->ibufold = NULL;
336518334Speter	}
336650600Sobrien	return (0);
336750600Sobrien}
336850600Sobrien
336950600Sobrienstatic int
337050600Sobriensiosetwater(com, speed)
337150600Sobrien	struct com_s	*com;
337250600Sobrien	speed_t		speed;
337350600Sobrien{
337450600Sobrien	int		cp4ticks;
337550600Sobrien	u_char		*ibuf;
337650600Sobrien	int		ibufsize;
337750600Sobrien	struct tty	*tp;
337850600Sobrien
337950600Sobrien	/*
338050600Sobrien	 * Make the buffer size large enough to handle a softtty interrupt
338150600Sobrien	 * latency of about 2 ticks without loss of throughput or data
338250600Sobrien	 * (about 3 ticks if input flow control is not used or not honoured,
338350600Sobrien	 * but a bit less for CS5-CS7 modes).
338450600Sobrien	 */
338550600Sobrien	cp4ticks = speed / 10 / hz * 4;
338650600Sobrien	for (ibufsize = 128; ibufsize < cp4ticks;)
338750600Sobrien		ibufsize <<= 1;
338850600Sobrien#ifdef PC98
338950600Sobrien	if (com->pc98_if_type == COM_IF_RSA98III)
339050600Sobrien		ibufsize = 2048;
339118334Speter#endif
339250600Sobrien	if (ibufsize == com->ibufsize) {
339350600Sobrien		disable_intr();
339418334Speter		return (0);
339552561Sobrien	}
339652561Sobrien
339752561Sobrien	/*
339852561Sobrien	 * Allocate input buffer.  The extra factor of 2 in the size is
339952561Sobrien	 * to allow for an error byte for each input byte.
340052561Sobrien	 */
340152561Sobrien	ibuf = malloc(2 * ibufsize, M_DEVBUF, M_NOWAIT);
340252561Sobrien	if (ibuf == NULL) {
340352561Sobrien		disable_intr();
340452561Sobrien		return (ENOMEM);
340552561Sobrien	}
340652561Sobrien
340752561Sobrien	/* Initialize non-critical variables. */
340852561Sobrien	com->ibufold = com->ibuf;
340952561Sobrien	com->ibufsize = ibufsize;
341052561Sobrien	tp = com->tp;
341152561Sobrien	if (tp != NULL) {
341252561Sobrien		tp->t_ififosize = 2 * ibufsize;
341352561Sobrien		tp->t_ispeedwat = (speed_t)-1;
341452561Sobrien		tp->t_ospeedwat = (speed_t)-1;
341550600Sobrien	}
341650600Sobrien
341750600Sobrien	/*
341850600Sobrien	 * Read current input buffer, if any.  Continue with interrupts
341950600Sobrien	 * disabled.
342050600Sobrien	 */
342150600Sobrien	disable_intr();
342250600Sobrien	if (com->iptr != com->ibuf)
342350600Sobrien		sioinput(com);
342450600Sobrien
342550600Sobrien	/*-
342650600Sobrien	 * Initialize critical variables, including input buffer watermarks.
342750600Sobrien	 * The external device is asked to stop sending when the buffer
342850600Sobrien	 * exactly reaches high water, or when the high level requests it.
342950600Sobrien	 * The high level is notified immediately (rather than at a later
343050600Sobrien	 * clock tick) when this watermark is reached.
343118334Speter	 * The buffer size is chosen so the watermark should almost never
343250600Sobrien	 * be reached.
343318334Speter	 * The low watermark is invisibly 0 since the buffer is always
343418334Speter	 * emptied all at once.
343550600Sobrien	 */
343650600Sobrien	com->iptr = com->ibuf = ibuf;
343750600Sobrien	com->ibufend = ibuf + ibufsize;
343850600Sobrien	com->ierroff = ibufsize;
343918334Speter	com->ihighwater = ibuf + 3 * ibufsize / 4;
344050600Sobrien	return (0);
344150600Sobrien}
344218334Speter
344350600Sobrienstatic void
344450600Sobriencomstart(tp)
344550600Sobrien	struct tty	*tp;
344650600Sobrien{
344718334Speter	struct com_s	*com;
344850600Sobrien	int		s;
344950600Sobrien	int		unit;
345018334Speter#ifdef PC98
345150600Sobrien	int		tmp;
345250600Sobrien#endif
345350600Sobrien
345450600Sobrien	unit = DEV_TO_UNIT(tp->t_dev);
345518334Speter	com = com_addr(unit);
345650600Sobrien	s = spltty();
345750600Sobrien	disable_intr();
345850600Sobrien	if (tp->t_state & TS_TTSTOP)
345950600Sobrien		com->state &= ~CS_TTGO;
346050600Sobrien	else
346150600Sobrien		com->state |= CS_TTGO;
346250600Sobrien	if (tp->t_state & TS_TBLOCK) {
346350600Sobrien#ifdef PC98
346450600Sobrien		if (IS_8251(com->pc98_if_type))
346550600Sobrien			tmp = com_tiocm_get(com) & TIOCM_RTS;
346650600Sobrien		else
346750600Sobrien			tmp = com->mcr_image & MCR_RTS;
346850600Sobrien		if (tmp && (com->state & CS_RTS_IFLOW))
346950600Sobrien#else
347050600Sobrien		if (com->mcr_image & MCR_RTS && com->state & CS_RTS_IFLOW)
347150600Sobrien#endif
347250600Sobrien#ifdef PC98
347350600Sobrien			if (IS_8251(com->pc98_if_type))
347450600Sobrien				com_tiocm_bic(com, TIOCM_RTS);
347550600Sobrien			else
347650600Sobrien#endif
347718334Speter			outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS);
347818334Speter	} else {
347950600Sobrien#ifdef PC98
348018334Speter		if (IS_8251(com->pc98_if_type))
348118334Speter			tmp = com_tiocm_get(com) & TIOCM_RTS;
348252561Sobrien		else
348352561Sobrien			tmp = com->mcr_image & MCR_RTS;
348452561Sobrien		if (!(tmp) && com->iptr < com->ihighwater
348552561Sobrien			&& com->state & CS_RTS_IFLOW)
348652561Sobrien#else
348752561Sobrien		if (!(com->mcr_image & MCR_RTS) && com->iptr < com->ihighwater
348852561Sobrien		    && com->state & CS_RTS_IFLOW)
348952561Sobrien#endif
349052561Sobrien#ifdef PC98
349152561Sobrien			if (IS_8251(com->pc98_if_type))
349252561Sobrien				com_tiocm_bis(com, TIOCM_RTS);
349352561Sobrien			else
349452561Sobrien#endif
349550600Sobrien			outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS);
349652561Sobrien	}
349752561Sobrien	enable_intr();
349818334Speter	if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) {
349952561Sobrien		ttwwakeup(tp);
350052561Sobrien#ifdef PC98
350152561Sobrien/*		if(IS_8251(com->pc98_if_type))
350250600Sobrien			com_int_Tx_enable(com); */
350350600Sobrien#endif
350452561Sobrien		splx(s);
350550600Sobrien		return;
350650600Sobrien	}
350718334Speter	if (tp->t_outq.c_cc != 0) {
350850600Sobrien		struct lbq	*qp;
350950600Sobrien		struct lbq	*next;
351050600Sobrien
351152561Sobrien		if (!com->obufs[0].l_queued) {
351252561Sobrien			com->obufs[0].l_tail
351352561Sobrien			    = com->obuf1 + q_to_b(&tp->t_outq, com->obuf1,
351452561Sobrien#ifndef PC98
351552561Sobrien						  sizeof com->obuf1);
351652561Sobrien#else
351752561Sobrien						  com->obufsize);
351852561Sobrien#endif
351952561Sobrien			com->obufs[0].l_next = NULL;
352052561Sobrien			com->obufs[0].l_queued = TRUE;
352152561Sobrien			disable_intr();
352252561Sobrien			if (com->state & CS_BUSY) {
352352561Sobrien				qp = com->obufq.l_next;
352452561Sobrien				while ((next = qp->l_next) != NULL)
352550600Sobrien					qp = next;
352650600Sobrien				qp->l_next = &com->obufs[0];
352750600Sobrien			} else {
352852561Sobrien				com->obufq.l_head = com->obufs[0].l_head;
352950600Sobrien				com->obufq.l_tail = com->obufs[0].l_tail;
353050600Sobrien				com->obufq.l_next = &com->obufs[0];
353150600Sobrien				com->state |= CS_BUSY;
353250600Sobrien			}
353350600Sobrien			enable_intr();
353452561Sobrien		}
353550600Sobrien		if (tp->t_outq.c_cc != 0 && !com->obufs[1].l_queued) {
353650600Sobrien			com->obufs[1].l_tail
353750600Sobrien			    = com->obuf2 + q_to_b(&tp->t_outq, com->obuf2,
353852561Sobrien#ifndef PC98
353952561Sobrien						  sizeof com->obuf2);
354052561Sobrien#else
354152561Sobrien						  com->obufsize);
354252561Sobrien#endif
354352561Sobrien			com->obufs[1].l_next = NULL;
354452561Sobrien			com->obufs[1].l_queued = TRUE;
354552561Sobrien			disable_intr();
354652561Sobrien			if (com->state & CS_BUSY) {
354750600Sobrien				qp = com->obufq.l_next;
354850600Sobrien				while ((next = qp->l_next) != NULL)
354950600Sobrien					qp = next;
355052561Sobrien				qp->l_next = &com->obufs[1];
355152561Sobrien			} else {
355252561Sobrien				com->obufq.l_head = com->obufs[1].l_head;
355352561Sobrien				com->obufq.l_tail = com->obufs[1].l_tail;
355450600Sobrien				com->obufq.l_next = &com->obufs[1];
355550600Sobrien				com->state |= CS_BUSY;
355650600Sobrien			}
355752561Sobrien			enable_intr();
355818334Speter		}
355950600Sobrien		tp->t_state |= TS_BUSY;
356050600Sobrien	}
356150600Sobrien	disable_intr();
356250600Sobrien	if (com->state >= (CS_BUSY | CS_TTGO))
356350600Sobrien		siointr1(com);	/* fake interrupt to start output */
356450600Sobrien	enable_intr();
356550600Sobrien#ifdef PC98
356650600Sobrien/*		if(IS_8251(com->pc98_if_type))
356750600Sobrien			com_int_Tx_enable(com); */
356850600Sobrien#endif
356950600Sobrien	ttwwakeup(tp);
357050600Sobrien	splx(s);
357150600Sobrien}
357250600Sobrien
357350600Sobrienstatic void
357452561Sobriensiostop(tp, rw)
357552561Sobrien	struct tty	*tp;
357652561Sobrien	int		rw;
357752561Sobrien{
357852561Sobrien	struct com_s	*com;
357952561Sobrien#ifdef PC98
358052561Sobrien	int		port_shift = 0;
358152561Sobrien	int		rsa98_tmp  = 0;
358252561Sobrien#endif
358352561Sobrien
358452561Sobrien	com = com_addr(DEV_TO_UNIT(tp->t_dev));
358552561Sobrien	if (com->gone)
358652561Sobrien		return;
358752561Sobrien#ifdef PC98
358852561Sobrien	if (IS_8251(com->pc98_if_type))
358952561Sobrien	    port_shift = if_16550a_type[com->pc98_if_type & 0x0f].port_shift;
359052561Sobrien#endif
359152561Sobrien	disable_intr();
359252561Sobrien	if (rw & FWRITE) {
359352561Sobrien		if (com->hasfifo)
359452561Sobrien#ifdef COM_ESP
359552561Sobrien		    /* XXX avoid h/w bug. */
359652561Sobrien		    if (!com->esp)
359752561Sobrien#endif
359852561Sobrien#ifdef PC98
359952561Sobrien			outb(com->iobase + (com_fifo << port_shift),
360052561Sobrien			     FIFO_XMT_RST | com->fifo_image);
360152561Sobrien			if (com->pc98_if_type == COM_IF_RSA98III)
360252561Sobrien			    for(rsa98_tmp = 0; rsa98_tmp < 2048; rsa98_tmp++)
360352561Sobrien				outb(com->iobase + (com_fifo << port_shift),
360450600Sobrien				     FIFO_XMT_RST | com->fifo_image);
360550600Sobrien#else
360652561Sobrien			outb(com->iobase + com_fifo,
360752561Sobrien			     FIFO_XMT_RST | com->fifo_image);
360818334Speter#endif
360952561Sobrien		com->obufs[0].l_queued = FALSE;
361052561Sobrien		com->obufs[1].l_queued = FALSE;
361152561Sobrien		if (com->state & CS_ODONE)
361252561Sobrien			com_events -= LOTS_OF_EVENTS;
361352561Sobrien		com->state &= ~(CS_ODONE | CS_BUSY);
361452561Sobrien		com->tp->t_state &= ~TS_BUSY;
361552561Sobrien	}
361652561Sobrien	if (rw & FREAD) {
361752561Sobrien		if (com->hasfifo)
361850600Sobrien#ifdef COM_ESP
361952561Sobrien		    /* XXX avoid h/w bug. */
362050600Sobrien		    if (!com->esp)
362150600Sobrien#endif
362250600Sobrien#ifdef PC98
362318334Speter			if (com->pc98_if_type == COM_IF_RSA98III) {
362452561Sobrien			    for(rsa98_tmp = 0; rsa98_tmp < 2048; rsa98_tmp++)
362552561Sobrien				inb(com->data_port);
362652561Sobrien			}
362752561Sobrien			outb(com->iobase + (com_fifo << port_shift),
362852561Sobrien			     FIFO_RCV_RST | com->fifo_image);
362952561Sobrien#else
363052561Sobrien			outb(com->iobase + com_fifo,
363152561Sobrien			     FIFO_RCV_RST | com->fifo_image);
363252561Sobrien#endif
363350600Sobrien		com_events -= (com->iptr - com->ibuf);
363450600Sobrien		com->iptr = com->ibuf;
363552561Sobrien	}
363650600Sobrien	enable_intr();
363750600Sobrien	comstart(tp);
363852561Sobrien}
363918334Speter
364050600Sobrienstatic struct tty *
364150600Sobriensiodevtotty(dev)
364250600Sobrien	dev_t	dev;
364350600Sobrien{
364450600Sobrien	int	mynor;
364550600Sobrien	int	unit;
364650600Sobrien
364718334Speter	mynor = minor(dev);
364852561Sobrien	if (mynor & CONTROL_MASK)
364952561Sobrien		return (NULL);
365052561Sobrien	unit = MINOR_TO_UNIT(mynor);
365152561Sobrien	if ((u_int) unit >= NSIOTOT)
365250600Sobrien		return (NULL);
365318334Speter	return (&sio_tty[unit]);
365452561Sobrien}
365550600Sobrien
365650600Sobrienstatic int
365750600Sobriencommctl(com, bits, how)
365850600Sobrien	struct com_s	*com;
365950600Sobrien	int		bits;
366050600Sobrien	int		how;
366118334Speter{
366250600Sobrien	int	mcr;
366350600Sobrien	int	msr;
366450600Sobrien
366550600Sobrien	if (how == DMGET) {
366650600Sobrien		bits = TIOCM_LE;	/* XXX - always enabled while open */
366718334Speter		mcr = com->mcr_image;
366850600Sobrien		if (mcr & MCR_DTR)
366950600Sobrien			bits |= TIOCM_DTR;
367050600Sobrien		if (mcr & MCR_RTS)
367118334Speter			bits |= TIOCM_RTS;
367250600Sobrien		msr = com->prev_modem_status;
367350600Sobrien		if (msr & MSR_CTS)
367418334Speter			bits |= TIOCM_CTS;
367550600Sobrien		if (msr & MSR_DCD)
367650600Sobrien			bits |= TIOCM_CD;
367750600Sobrien		if (msr & MSR_DSR)
367850600Sobrien			bits |= TIOCM_DSR;
367950600Sobrien		/*
368050600Sobrien		 * XXX - MSR_RI is naturally volatile, and we make MSR_TERI
368150600Sobrien		 * more volatile by reading the modem status a lot.  Perhaps
368250600Sobrien		 * we should latch both bits until the status is read here.
368350600Sobrien		 */
368450600Sobrien		if (msr & (MSR_RI | MSR_TERI))
368550600Sobrien			bits |= TIOCM_RI;
368650600Sobrien		return (bits);
368750600Sobrien	}
368850600Sobrien	mcr = 0;
368950600Sobrien	if (bits & TIOCM_DTR)
369050600Sobrien		mcr |= MCR_DTR;
369150600Sobrien	if (bits & TIOCM_RTS)
369218334Speter		mcr |= MCR_RTS;
369350600Sobrien	if (com->gone)
369452561Sobrien		return(0);
369552561Sobrien	disable_intr();
369652561Sobrien	switch (how) {
369750600Sobrien	case DMSET:
369850600Sobrien		outb(com->modem_ctl_port,
369950600Sobrien		     com->mcr_image = mcr | (com->mcr_image & MCR_IENABLE));
370052561Sobrien		break;
370152561Sobrien	case DMBIS:
370252561Sobrien		outb(com->modem_ctl_port, com->mcr_image |= mcr);
370352561Sobrien		break;
370452561Sobrien	case DMBIC:
370552561Sobrien		outb(com->modem_ctl_port, com->mcr_image &= ~mcr);
370652561Sobrien		break;
370750600Sobrien	}
370852561Sobrien	enable_intr();
370952561Sobrien	return (0);
371052561Sobrien}
371152561Sobrien
371250600Sobrienstatic void
371352561Sobriensiosettimeout()
371452561Sobrien{
371552561Sobrien	struct com_s	*com;
371652561Sobrien	bool_t		someopen;
371752561Sobrien	int		unit;
371852561Sobrien
371918334Speter	/*
372052561Sobrien	 * Set our timeout period to 1 second if no polled devices are open.
372152561Sobrien	 * Otherwise set it to max(1/200, 1/hz).
372252561Sobrien	 * Enable timeouts iff some device is open.
372352561Sobrien	 */
372452561Sobrien	untimeout(comwakeup, (void *)NULL, sio_timeout_handle);
372552561Sobrien	sio_timeout = hz;
372652561Sobrien	someopen = FALSE;
372752561Sobrien	for (unit = 0; unit < NSIOTOT; ++unit) {
372852561Sobrien		com = com_addr(unit);
372952561Sobrien		if (com != NULL && com->tp != NULL
373052561Sobrien		    && com->tp->t_state & TS_ISOPEN && !com->gone) {
373152561Sobrien			someopen = TRUE;
373252561Sobrien			if (com->poll || com->poll_output) {
373350600Sobrien				sio_timeout = hz > 200 ? hz / 200 : 1;
373452561Sobrien				break;
373550600Sobrien			}
373652561Sobrien		}
373752561Sobrien	}
373852561Sobrien	if (someopen) {
373950600Sobrien		sio_timeouts_until_log = hz / sio_timeout;
374052561Sobrien		sio_timeout_handle = timeout(comwakeup, (void *)NULL,
374150600Sobrien					     sio_timeout);
374252561Sobrien	} else {
374350600Sobrien		/* Flush error messages, if any. */
374450600Sobrien		sio_timeouts_until_log = 1;
374550600Sobrien		comwakeup((void *)NULL);
374650600Sobrien		untimeout(comwakeup, (void *)NULL, sio_timeout_handle);
374750600Sobrien	}
374850600Sobrien}
374950600Sobrien
375050600Sobrienstatic void
375150600Sobriencomwakeup(chan)
375250600Sobrien	void	*chan;
375350600Sobrien{
375452561Sobrien	struct com_s	*com;
375550600Sobrien	int		unit;
375652561Sobrien
375750600Sobrien	sio_timeout_handle = timeout(comwakeup, (void *)NULL, sio_timeout);
375850600Sobrien
375952561Sobrien	/*
376052561Sobrien	 * Recover from lost output interrupts.
376152561Sobrien	 * Poll any lines that don't use interrupts.
376252561Sobrien	 */
376352561Sobrien	for (unit = 0; unit < NSIOTOT; ++unit) {
376452561Sobrien		com = com_addr(unit);
376552561Sobrien		if (com != NULL && !com->gone
376650600Sobrien		    && (com->state >= (CS_BUSY | CS_TTGO) || com->poll)) {
376752561Sobrien			disable_intr();
376852561Sobrien			siointr1(com);
376952561Sobrien			enable_intr();
377052561Sobrien		}
377152561Sobrien	}
377250600Sobrien
377352561Sobrien	/*
377452561Sobrien	 * Check for and log errors, but not too often.
377552561Sobrien	 */
377650600Sobrien	if (--sio_timeouts_until_log > 0)
377750600Sobrien		return;
377852561Sobrien	sio_timeouts_until_log = hz / sio_timeout;
377952561Sobrien	for (unit = 0; unit < NSIOTOT; ++unit) {
378052561Sobrien		int	errnum;
378152561Sobrien
378252561Sobrien		com = com_addr(unit);
378352561Sobrien		if (com == NULL)
378452561Sobrien			continue;
378552561Sobrien		if (com->gone)
378652561Sobrien			continue;
378752561Sobrien		for (errnum = 0; errnum < CE_NTYPES; ++errnum) {
378852561Sobrien			u_int	delta;
378952561Sobrien			u_long	total;
379052561Sobrien
379152561Sobrien			disable_intr();
379252561Sobrien			delta = com->delta_error_counts[errnum];
379352561Sobrien			com->delta_error_counts[errnum] = 0;
379450600Sobrien			enable_intr();
379550600Sobrien			if (delta == 0)
379650600Sobrien				continue;
379750600Sobrien			total = com->error_counts[errnum] += delta;
379850600Sobrien			log(LOG_ERR, "sio%d: %u more %s%s (total %lu)\n",
379950600Sobrien			    unit, delta, error_desc[errnum],
380050600Sobrien			    delta == 1 ? "" : "s", total);
380152561Sobrien		}
380252561Sobrien	}
380352561Sobrien}
380452561Sobrien
380552561Sobrien#ifdef PC98
380652561Sobrien/* commint is called when modem control line changes */
380752561Sobrienstatic void
380852561Sobriencommint(dev_t dev)
380952561Sobrien{
381050600Sobrien	register struct tty *tp;
381150600Sobrien	int	stat,delta;
381252561Sobrien	struct com_s *com;
381352561Sobrien	int	mynor,unit;
381450600Sobrien
381550600Sobrien	mynor = minor(dev);
381650600Sobrien	unit = MINOR_TO_UNIT(mynor);
381752561Sobrien	com = com_addr(unit);
381852561Sobrien	tp = com->tp;
381950600Sobrien
382050600Sobrien	stat = com_tiocm_get(com);
382150600Sobrien	delta = com_tiocm_get_delta(com);
382250600Sobrien
382350600Sobrien	if (com->state & CS_CTS_OFLOW) {
382450600Sobrien		if (stat & TIOCM_CTS)
382550600Sobrien			com->state |= CS_ODEVREADY;
382650600Sobrien		else
382750600Sobrien			com->state &= ~CS_ODEVREADY;
382850600Sobrien	}
382950600Sobrien	if ((delta & TIOCM_CAR) && (mynor & CALLOUT_MASK) == 0) {
383050600Sobrien	    if (stat & TIOCM_CAR )
383150600Sobrien		(void)(*linesw[tp->t_line].l_modem)(tp, 1);
383250600Sobrien	    else if ((*linesw[tp->t_line].l_modem)(tp, 0) == 0) {
383350600Sobrien		/* negate DTR, RTS */
383450600Sobrien		com_tiocm_bic(com, (tp->t_cflag & HUPCL) ?
383550600Sobrien				TIOCM_DTR|TIOCM_RTS|TIOCM_LE : TIOCM_LE );
383650600Sobrien		/* disable IENABLE */
383750600Sobrien		com_int_TxRx_disable( com );
383850600Sobrien	    }
383950600Sobrien	}
384050600Sobrien}
384150600Sobrien#endif
384250600Sobrien
384350600Sobrienstatic void
384450600Sobriendisc_optim(tp, t, com)
384550600Sobrien	struct tty	*tp;
384650600Sobrien	struct termios	*t;
384750600Sobrien	struct com_s	*com;
384850600Sobrien{
384950600Sobrien	if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON))
385050600Sobrien	    && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK))
385150600Sobrien	    && (!(t->c_iflag & PARMRK)
385250600Sobrien		|| (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK))
385350600Sobrien	    && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN))
385450600Sobrien	    && linesw[tp->t_line].l_rint == ttyinput)
385550600Sobrien		tp->t_state |= TS_CAN_BYPASS_L_RINT;
385650600Sobrien	else
385750600Sobrien		tp->t_state &= ~TS_CAN_BYPASS_L_RINT;
385850600Sobrien	com->hotchar = linesw[tp->t_line].l_hotchar;
385950600Sobrien}
386050600Sobrien
386150600Sobrien/*
386250600Sobrien * Following are all routines needed for SIO to act as console
386350600Sobrien */
386450600Sobrien#include <machine/cons.h>
386550600Sobrien
386650600Sobrienstruct siocnstate {
386750600Sobrien	u_char	dlbl;
386850600Sobrien	u_char	dlbh;
386950600Sobrien	u_char	ier;
387050600Sobrien	u_char	cfcr;
387152561Sobrien	u_char	mcr;
387252561Sobrien};
387352561Sobrien
387452561Sobrienstatic speed_t siocngetspeed __P((Port_t, struct speedtab *));
387552561Sobrienstatic void siocnclose	__P((struct siocnstate *sp));
387650600Sobrienstatic void siocnopen	__P((struct siocnstate *sp));
387752561Sobrienstatic void siocntxwait	__P((void));
387850600Sobrien
387952561Sobrien/*
388050600Sobrien * XXX: sciocnget() and sciocnputc() are not declared static, as they are
388152561Sobrien * referred to from i386/i386/i386-gdbstub.c.
388252561Sobrien */
388352561Sobrienstatic cn_probe_t siocnprobe;
388450600Sobrienstatic cn_init_t siocninit;
388550600Sobrienstatic cn_checkc_t siocncheckc;
388652561Sobrien       cn_getc_t siocngetc;
388752561Sobrien       cn_putc_t siocnputc;
388852561Sobrien
388952561SobrienCONS_DRIVER(sio, siocnprobe, siocninit, siocngetc, siocncheckc, siocnputc);
389052561Sobrien
389152561Sobrienstatic void
389252561Sobriensiocntxwait()
389352561Sobrien{
389452561Sobrien	int	timo;
389552561Sobrien
389652561Sobrien	/*
389752561Sobrien	 * Wait for any pending transmission to finish.  Required to avoid
389852561Sobrien	 * the UART lockup bug when the speed is changed, and for normal
389952561Sobrien	 * transmits.
390052561Sobrien	 */
390152561Sobrien	timo = 100000;
390252561Sobrien	while ((inb(siocniobase + com_lsr) & (LSR_TSRE | LSR_TXRDY))
390352561Sobrien	       != (LSR_TSRE | LSR_TXRDY) && --timo != 0)
390452561Sobrien		;
390552561Sobrien}
390652561Sobrien
390752561Sobrien/*
390852561Sobrien * Read the serial port specified and try to figure out what speed
390952561Sobrien * it's currently running at.  We're assuming the serial port has
391052561Sobrien * been initialized and is basicly idle.  This routine is only intended
391152561Sobrien * to be run at system startup.
391252561Sobrien *
391352561Sobrien * If the value read from the serial port doesn't make sense, return 0.
391452561Sobrien */
391552561Sobrien
391652561Sobrienstatic speed_t
391752561Sobriensiocngetspeed(iobase, table)
391852561Sobrien	Port_t iobase;
391952561Sobrien	struct speedtab *table;
392052561Sobrien{
392152561Sobrien	int	code;
392252561Sobrien	u_char	dlbh;
392352561Sobrien	u_char	dlbl;
392452561Sobrien	u_char  cfcr;
392552561Sobrien
392652561Sobrien	cfcr = inb(iobase + com_cfcr);
392752561Sobrien	outb(iobase + com_cfcr, CFCR_DLAB | cfcr);
392852561Sobrien
392952561Sobrien	dlbl = inb(iobase + com_dlbl);
393052561Sobrien	dlbh = inb(iobase + com_dlbh);
393152561Sobrien
393252561Sobrien	outb(iobase + com_cfcr, cfcr);
393352561Sobrien
393452561Sobrien	code = dlbh << 8 | dlbl;
393550600Sobrien
393650600Sobrien	for ( ; table->sp_speed != -1; table++)
393718334Speter		if (table->sp_code == code)
393852561Sobrien			return (table->sp_speed);
393952561Sobrien
394052561Sobrien	return 0;	/* didn't match anything sane */
394152561Sobrien}
394252561Sobrien
394352561Sobrienstatic void
394452561Sobriensiocnopen(sp)
394552561Sobrien	struct siocnstate	*sp;
394652561Sobrien{
394752561Sobrien	int	divisor;
394852561Sobrien	u_char	dlbh;
394952561Sobrien	u_char	dlbl;
395052561Sobrien	Port_t	iobase;
395152561Sobrien
395252561Sobrien	/*
395352561Sobrien	 * Save all the device control registers except the fifo register
395452561Sobrien	 * and set our default ones (cs8 -parenb speed=comdefaultrate).
395552561Sobrien	 * We can't save the fifo register since it is read-only.
395652561Sobrien	 */
395752561Sobrien	iobase = siocniobase;
395852561Sobrien	sp->ier = inb(iobase + com_ier);
395952561Sobrien	outb(iobase + com_ier, 0);	/* spltty() doesn't stop siointr() */
396052561Sobrien	siocntxwait();
396152561Sobrien	sp->cfcr = inb(iobase + com_cfcr);
396252561Sobrien	outb(iobase + com_cfcr, CFCR_DLAB | CFCR_8BITS);
396352561Sobrien	sp->dlbl = inb(iobase + com_dlbl);
396452561Sobrien	sp->dlbh = inb(iobase + com_dlbh);
396552561Sobrien	/*
396652561Sobrien	 * Only set the divisor registers if they would change, since on
396752561Sobrien	 * some 16550 incompatibles (Startech), setting them clears the
396852561Sobrien	 * data input register.  This also reduces the effects of the
396952561Sobrien	 * UMC8669F bug.
397052561Sobrien	 */
397152561Sobrien	divisor = ttspeedtab(comdefaultrate, comspeedtab);
397252561Sobrien	dlbl = divisor & 0xFF;
397352561Sobrien	if (sp->dlbl != dlbl)
397452561Sobrien		outb(iobase + com_dlbl, dlbl);
397552561Sobrien	dlbh = (u_int) divisor >> 8;
397652561Sobrien	if (sp->dlbh != dlbh)
397752561Sobrien		outb(iobase + com_dlbh, dlbh);
397852561Sobrien	outb(iobase + com_cfcr, CFCR_8BITS);
397952561Sobrien	sp->mcr = inb(iobase + com_mcr);
398052561Sobrien	/*
398152561Sobrien	 * We don't want interrupts, but must be careful not to "disable"
398252561Sobrien	 * them by clearing the MCR_IENABLE bit, since that might cause
398352561Sobrien	 * an interrupt by floating the IRQ line.
398452561Sobrien	 */
398552561Sobrien	outb(iobase + com_mcr, (sp->mcr & MCR_IENABLE) | MCR_DTR | MCR_RTS);
398652561Sobrien}
398752561Sobrien
398852561Sobrienstatic void
398950600Sobriensiocnclose(sp)
399050600Sobrien	struct siocnstate	*sp;
399118334Speter{
399218334Speter	Port_t	iobase;
399318334Speter
399418334Speter	/*
399518334Speter	 * Restore the device control registers.
399618334Speter	 */
399718334Speter	siocntxwait();
399818334Speter	iobase = siocniobase;
399918334Speter	outb(iobase + com_cfcr, CFCR_DLAB | CFCR_8BITS);
400018334Speter	if (sp->dlbl != inb(iobase + com_dlbl))
400118334Speter		outb(iobase + com_dlbl, sp->dlbl);
400218334Speter	if (sp->dlbh != inb(iobase + com_dlbh))
400318334Speter		outb(iobase + com_dlbh, sp->dlbh);
400418334Speter	outb(iobase + com_cfcr, sp->cfcr);
400518334Speter	/*
400618334Speter	 * XXX damp oscillations of MCR_DTR and MCR_RTS by not restoring them.
400718334Speter	 */
400818334Speter	outb(iobase + com_mcr, sp->mcr | MCR_DTR | MCR_RTS);
400918334Speter	outb(iobase + com_ier, sp->ier);
401018334Speter}
401118334Speter
401218334Speterstatic void
401318334Spetersiocnprobe(cp)
401452561Sobrien	struct consdev	*cp;
401518334Speter{
401618334Speter	speed_t			boot_speed;
4017	u_char			cfcr;
4018	struct isa_device	*dvp;
4019	int			s;
4020	struct siocnstate	sp;
4021
4022	/*
4023	 * Find our first enabled console, if any.  If it is a high-level
4024	 * console device, then initialize it and return successfully.
4025	 * If it is a low-level console device, then initialize it and
4026	 * return unsuccessfully.  It must be initialized in both cases
4027	 * for early use by console drivers and debuggers.  Initializing
4028	 * the hardware is not necessary in all cases, since the i/o
4029	 * routines initialize it on the fly, but it is necessary if
4030	 * input might arrive while the hardware is switched back to an
4031	 * uninitialized state.  We can't handle multiple console devices
4032	 * yet because our low-level routines don't take a device arg.
4033	 * We trust the user to set the console flags properly so that we
4034	 * don't need to probe.
4035	 */
4036	cp->cn_pri = CN_DEAD;
4037	for (dvp = isa_devtab_tty; dvp->id_driver != NULL; dvp++)
4038		if (dvp->id_driver == &siodriver && dvp->id_enabled
4039		    && COM_CONSOLE(dvp)) {
4040			siocniobase = dvp->id_iobase;
4041			s = spltty();
4042			if (boothowto & RB_SERIAL) {
4043				boot_speed = siocngetspeed(siocniobase,
4044							   comspeedtab);
4045				if (boot_speed)
4046					comdefaultrate = boot_speed;
4047			}
4048
4049			/*
4050			 * Initialize the divisor latch.  We can't rely on
4051			 * siocnopen() to do this the first time, since it
4052			 * avoids writing to the latch if the latch appears
4053			 * to have the correct value.  Also, if we didn't
4054			 * just read the speed from the hardware, then we
4055			 * need to set the speed in hardware so that
4056			 * switching it later is null.
4057			 */
4058			cfcr = inb(siocniobase + com_cfcr);
4059			outb(siocniobase + com_cfcr, CFCR_DLAB | cfcr);
4060			outb(siocniobase + com_dlbl,
4061			     COMBRD(comdefaultrate) & 0xff);
4062			outb(siocniobase + com_dlbh,
4063			     (u_int) COMBRD(comdefaultrate) >> 8);
4064			outb(siocniobase + com_cfcr, cfcr);
4065
4066			siocnopen(&sp);
4067			splx(s);
4068			if (!COM_LLCONSOLE(dvp)) {
4069				cp->cn_dev = makedev(CDEV_MAJOR, dvp->id_unit);
4070				cp->cn_pri = COM_FORCECONSOLE(dvp)
4071					     || boothowto & RB_SERIAL
4072					     ? CN_REMOTE : CN_NORMAL;
4073			}
4074			break;
4075		}
4076}
4077
4078static void
4079siocninit(cp)
4080	struct consdev	*cp;
4081{
4082	comconsole = DEV_TO_UNIT(cp->cn_dev);
4083}
4084
4085static int
4086siocncheckc(dev)
4087	dev_t	dev;
4088{
4089	int	c;
4090	Port_t	iobase;
4091	int	s;
4092	struct siocnstate	sp;
4093
4094	iobase = siocniobase;
4095	s = spltty();
4096	siocnopen(&sp);
4097	if (inb(iobase + com_lsr) & LSR_RXRDY)
4098		c = inb(iobase + com_data);
4099	else
4100		c = -1;
4101	siocnclose(&sp);
4102	splx(s);
4103	return (c);
4104}
4105
4106
4107int
4108siocngetc(dev)
4109	dev_t	dev;
4110{
4111	int	c;
4112	Port_t	iobase;
4113	int	s;
4114	struct siocnstate	sp;
4115
4116	iobase = siocniobase;
4117	s = spltty();
4118	siocnopen(&sp);
4119	while (!(inb(iobase + com_lsr) & LSR_RXRDY))
4120		;
4121	c = inb(iobase + com_data);
4122	siocnclose(&sp);
4123	splx(s);
4124	return (c);
4125}
4126
4127void
4128siocnputc(dev, c)
4129	dev_t	dev;
4130	int	c;
4131{
4132	int	s;
4133	struct siocnstate	sp;
4134
4135	s = spltty();
4136	siocnopen(&sp);
4137	siocntxwait();
4138	outb(siocniobase + com_data, c);
4139	siocnclose(&sp);
4140	splx(s);
4141}
4142
4143
4144/*
4145 * support PnP cards if we are using 'em
4146 */
4147
4148#if NPNP > 0
4149
4150static pnpid_t siopnp_ids[] = {
4151	{ 0x5015f435, "MOT1550"},
4152	{ 0x8113b04e, "Supra1381"},
4153	{ 0x9012b04e, "Supra1290"},
4154	{ 0x7121b04e, "SupraExpress 56i Sp"},
4155	{ 0x11007256, "USR0011"},
4156	{ 0x30207256, "USR2030"},
4157	{ 0x31307256, "USR3031"},
4158	{ 0x90307256, "USR3090"},
4159	{ 0x0100440e, "Cardinal MVP288IV"},
4160	{ 0 }
4161};
4162
4163static char *siopnp_probe(u_long csn, u_long vend_id);
4164static void siopnp_attach(u_long csn, u_long vend_id, char *name,
4165	struct isa_device *dev);
4166static u_long nsiopnp = NSIO;
4167
4168static struct pnp_device siopnp = {
4169	"siopnp",
4170	siopnp_probe,
4171	siopnp_attach,
4172	&nsiopnp,
4173	&tty_imask
4174};
4175DATA_SET (pnpdevice_set, siopnp);
4176
4177static char *
4178siopnp_probe(u_long csn, u_long vend_id)
4179{
4180	pnpid_t *id;
4181	char *s = NULL;
4182
4183	for(id = siopnp_ids; id->vend_id != 0; id++) {
4184		if (vend_id == id->vend_id) {
4185			s = id->id_str;
4186			break;
4187		}
4188	}
4189
4190	if (s) {
4191		struct pnp_cinfo d;
4192		read_pnp_parms(&d, 0);
4193		if (d.enable == 0 || d.flags & 1) {
4194			printf("CSN %lu is disabled.\n", csn);
4195			return (NULL);
4196		}
4197
4198	}
4199
4200	return (s);
4201}
4202
4203static void
4204siopnp_attach(u_long csn, u_long vend_id, char *name, struct isa_device *dev)
4205{
4206	struct pnp_cinfo d;
4207	struct isa_device *dvp;
4208
4209	if (dev->id_unit >= NSIOTOT)
4210		return;
4211
4212	if (read_pnp_parms(&d, 0) == 0) {
4213		printf("failed to read pnp parms\n");
4214		return;
4215	}
4216
4217	write_pnp_parms(&d, 0);
4218
4219	enable_pnp_card();
4220
4221	dev->id_iobase = d.port[0];
4222	dev->id_irq = (1 << d.irq[0]);
4223	dev->id_ointr = siointr;
4224	dev->id_ri_flags = RI_FAST;
4225	dev->id_drq = -1;
4226
4227	if (dev->id_driver == NULL) {
4228		dev->id_driver = &siodriver;
4229		dvp = find_isadev(isa_devtab_tty, &siodriver, 0);
4230		if (dvp != NULL)
4231			dev->id_id = dvp->id_id;
4232	}
4233
4234	if ((dev->id_alive = sioprobe(dev)) != 0)
4235		sioattach(dev);
4236	else
4237		printf("sio%d: probe failed\n", dev->id_unit);
4238}
4239#endif
4240
4241#ifdef PC98
4242/*
4243 *  pc98 local function
4244 */
4245
4246static void
4247com_tiocm_set(struct com_s *com, int msr)
4248{
4249	int	s;
4250	int	tmp = 0;
4251	int	mask = CMD8251_TxEN|CMD8251_RxEN|CMD8251_DTR|CMD8251_RTS;
4252
4253	s=spltty();
4254	com->pc98_prev_modem_status = ( msr & (TIOCM_LE|TIOCM_DTR|TIOCM_RTS) )
4255	   | ( com->pc98_prev_modem_status & ~(TIOCM_LE|TIOCM_DTR|TIOCM_RTS) );
4256	tmp |= (CMD8251_TxEN|CMD8251_RxEN);
4257	if ( msr & TIOCM_DTR ) tmp |= CMD8251_DTR;
4258	if ( msr & TIOCM_RTS ) tmp |= CMD8251_RTS;
4259	pc98_i8251_clear_or_cmd( com, mask, tmp );
4260	splx(s);
4261}
4262
4263static void
4264com_tiocm_bis(struct com_s *com, int msr)
4265{
4266	int	s;
4267	int	tmp = 0;
4268
4269	s=spltty();
4270	com->pc98_prev_modem_status |= ( msr & (TIOCM_LE|TIOCM_DTR|TIOCM_RTS) );
4271	tmp |= CMD8251_TxEN|CMD8251_RxEN;
4272	if ( msr & TIOCM_DTR ) tmp |= CMD8251_DTR;
4273	if ( msr & TIOCM_RTS ) tmp |= CMD8251_RTS;
4274
4275	pc98_i8251_or_cmd( com, tmp );
4276	splx(s);
4277}
4278
4279static void
4280com_tiocm_bic(struct com_s *com, int msr)
4281{
4282	int	s;
4283	int	tmp = msr;
4284
4285	s=spltty();
4286	com->pc98_prev_modem_status &= ~( msr & (TIOCM_LE|TIOCM_DTR|TIOCM_RTS) );
4287	if ( msr & TIOCM_DTR ) tmp |= CMD8251_DTR;
4288	if ( msr & TIOCM_RTS ) tmp |= CMD8251_RTS;
4289
4290	pc98_i8251_clear_cmd( com, tmp );
4291	splx(s);
4292}
4293
4294static int
4295com_tiocm_get(struct com_s *com)
4296{
4297	return( com->pc98_prev_modem_status );
4298}
4299
4300static int
4301com_tiocm_get_delta(struct com_s *com)
4302{
4303	int	tmp;
4304
4305	tmp = com->pc98_modem_delta;
4306	com->pc98_modem_delta = 0;
4307	return( tmp );
4308}
4309
4310/* convert to TIOCM_?? ( ioctl.h ) */
4311static int
4312pc98_get_modem_status(struct com_s *com)
4313{
4314	int	stat, stat2;
4315	register int	msr;
4316
4317	stat  = inb(com->sts_port);
4318	stat2 = inb(com->in_modem_port);
4319	msr = com->pc98_prev_modem_status
4320			& ~(TIOCM_CAR|TIOCM_RI|TIOCM_DSR|TIOCM_CTS);
4321	if ( !(stat2 & CICSCD_CD) ) msr |= TIOCM_CAR;
4322	if ( !(stat2 & CICSCD_CI) ) msr |= TIOCM_RI;
4323	if (   stat & STS8251_DSR ) msr |= TIOCM_DSR;
4324	if ( !(stat2 & CICSCD_CS) ) msr |= TIOCM_CTS;
4325#if COM_CARRIER_DETECT_EMULATE
4326	if ( msr & (TIOCM_DSR|TIOCM_CTS) ) {
4327		msr |= TIOCM_CAR;
4328	}
4329#endif
4330	return(msr);
4331}
4332
4333static void
4334pc98_check_msr(void* chan)
4335{
4336	int	msr, delta;
4337	int	s;
4338	register struct tty *tp;
4339	struct	com_s *com;
4340	int	mynor;
4341	int	unit;
4342	dev_t	dev;
4343
4344	dev=(dev_t)chan;
4345	mynor = minor(dev);
4346	unit = MINOR_TO_UNIT(mynor);
4347	com = com_addr(unit);
4348	tp = com->tp;
4349
4350	s = spltty();
4351	msr = pc98_get_modem_status(com);
4352	/* make change flag */
4353	delta = msr ^ com->pc98_prev_modem_status;
4354	if ( delta & TIOCM_CAR ) {
4355	    if ( com->modem_car_chg_timer ) {
4356		if ( -- com->modem_car_chg_timer )
4357		    msr ^= TIOCM_CAR;
4358	    } else {
4359		if ((com->modem_car_chg_timer = (msr & TIOCM_CAR) ?
4360		     DCD_ON_RECOGNITION : DCD_OFF_TOLERANCE) != 0)
4361		    msr ^= TIOCM_CAR;
4362	    }
4363	} else
4364	    com->modem_car_chg_timer = 0;
4365	delta = ( msr ^ com->pc98_prev_modem_status ) &
4366			(TIOCM_CAR|TIOCM_RI|TIOCM_DSR|TIOCM_CTS);
4367	com->pc98_prev_modem_status = msr;
4368	delta = ( com->pc98_modem_delta |= delta );
4369	splx(s);
4370	if ( com->modem_checking || (tp->t_state & (TS_ISOPEN)) ) {
4371		if ( delta ) {
4372			commint(dev);
4373		}
4374		timeout(pc98_check_msr, (caddr_t)dev,
4375					PC98_CHECK_MODEM_INTERVAL);
4376	} else {
4377		com->modem_checking = 0;
4378	}
4379}
4380
4381static void
4382pc98_msrint_start(dev_t dev)
4383{
4384	struct	com_s *com;
4385	int	mynor;
4386	int	unit;
4387	int	s = spltty();
4388
4389	mynor = minor(dev);
4390	unit = MINOR_TO_UNIT(mynor);
4391	com = com_addr(unit);
4392	/* modem control line check routine envoke interval is 1/10 sec */
4393	if ( com->modem_checking == 0 ) {
4394		com->pc98_prev_modem_status = pc98_get_modem_status(com);
4395		com->pc98_modem_delta = 0;
4396		timeout(pc98_check_msr, (caddr_t)dev,
4397					PC98_CHECK_MODEM_INTERVAL);
4398		com->modem_checking = 1;
4399	}
4400	splx(s);
4401}
4402
4403static void
4404pc98_disable_i8251_interrupt(struct com_s *com, int mod)
4405{
4406	/* disable interrupt */
4407	register int	tmp;
4408
4409	mod |= ~(IEN_Tx|IEN_TxEMP|IEN_Rx);
4410	COM_INT_DISABLE
4411	tmp = inb( com->intr_ctrl_port ) & ~(IEN_Tx|IEN_TxEMP|IEN_Rx);
4412	outb( com->intr_ctrl_port, (com->intr_enable&=~mod) | tmp );
4413	COM_INT_ENABLE
4414}
4415
4416static void
4417pc98_enable_i8251_interrupt(struct com_s *com, int mod)
4418{
4419	register int	tmp;
4420
4421	COM_INT_DISABLE
4422	tmp = inb( com->intr_ctrl_port ) & ~(IEN_Tx|IEN_TxEMP|IEN_Rx);
4423	outb( com->intr_ctrl_port, (com->intr_enable|=mod) | tmp );
4424	COM_INT_ENABLE
4425}
4426
4427static int
4428pc98_check_i8251_interrupt(struct com_s *com)
4429{
4430	return ( com->intr_enable & 0x07 );
4431}
4432
4433static void
4434pc98_i8251_clear_cmd(struct com_s *com, int x)
4435{
4436	int	tmp;
4437
4438	COM_INT_DISABLE
4439	tmp = com->pc98_prev_siocmd & ~(x);
4440	outb(com->cmd_port, tmp);
4441	com->pc98_prev_siocmd = tmp & ~(CMD8251_ER|CMD8251_RESET|CMD8251_EH);
4442	COM_INT_ENABLE
4443}
4444
4445static void
4446pc98_i8251_or_cmd(struct com_s *com, int x)
4447{
4448	int	tmp;
4449
4450	COM_INT_DISABLE
4451	tmp = com->pc98_prev_siocmd | (x);
4452	outb(com->cmd_port, tmp);
4453	com->pc98_prev_siocmd = tmp & ~(CMD8251_ER|CMD8251_RESET|CMD8251_EH);
4454	COM_INT_ENABLE
4455}
4456
4457static void
4458pc98_i8251_set_cmd(struct com_s *com, int x)
4459{
4460	int	tmp;
4461
4462	COM_INT_DISABLE
4463	tmp = (x);
4464	outb(com->cmd_port, tmp);
4465	com->pc98_prev_siocmd = tmp & ~(CMD8251_ER|CMD8251_RESET|CMD8251_EH);
4466	COM_INT_ENABLE
4467}
4468
4469static void
4470pc98_i8251_clear_or_cmd(struct com_s *com, int clr, int x)
4471{
4472	int	tmp;
4473	COM_INT_DISABLE
4474	tmp = com->pc98_prev_siocmd & ~(clr);
4475	tmp |= (x);
4476	outb(com->cmd_port, tmp);
4477	com->pc98_prev_siocmd = tmp & ~(CMD8251_ER|CMD8251_RESET|CMD8251_EH);
4478	COM_INT_ENABLE
4479}
4480
4481static int
4482pc98_i8251_get_cmd(struct com_s *com)
4483{
4484	return com->pc98_prev_siocmd;
4485}
4486
4487static int
4488pc98_i8251_get_mod(struct com_s *com)
4489{
4490	return com->pc98_prev_siomod;
4491}
4492
4493static void
4494pc98_i8251_reset(struct com_s *com, int mode, int command)
4495{
4496	outb(com->cmd_port, 0);	/* dummy */
4497	DELAY(2);
4498	outb(com->cmd_port, 0);	/* dummy */
4499	DELAY(2);
4500	outb(com->cmd_port, 0);	/* dummy */
4501	DELAY(2);
4502	outb(com->cmd_port, CMD8251_RESET);	/* internal reset */
4503	DELAY(2);
4504	outb(com->cmd_port, mode );	/* mode register */
4505	com->pc98_prev_siomod = mode;
4506	DELAY(2);
4507	pc98_i8251_set_cmd( com, (command|CMD8251_ER) );
4508}
4509
4510static void
4511pc98_check_sysclock(void)
4512{
4513	/* get system clock from port */
4514	if ( pc98_machine_type & M_8M ) {
4515	/* 8 MHz system & H98 */
4516		sysclock = 8;
4517	} else {
4518	/* 5 MHz system */
4519		sysclock = 5;
4520	}
4521}
4522
4523static void
4524com_cflag_and_speed_set( struct com_s *com, int cflag, int speed)
4525{
4526	int	cfcr=0, count;
4527	int	previnterrupt;
4528
4529	count = pc98_ttspeedtab( com, speed );
4530	if ( count < 0 ) return;
4531
4532	previnterrupt = pc98_check_i8251_interrupt(com);
4533	pc98_disable_i8251_interrupt( com, IEN_Tx|IEN_TxEMP|IEN_Rx );
4534
4535	switch ( cflag&CSIZE ) {
4536	  case CS5:
4537		cfcr = MOD8251_5BITS; break;
4538	  case CS6:
4539		cfcr = MOD8251_6BITS; break;
4540	  case CS7:
4541		cfcr = MOD8251_7BITS; break;
4542	  case CS8:
4543		cfcr = MOD8251_8BITS; break;
4544	}
4545	if ( cflag&PARENB ) {
4546	    if ( cflag&PARODD )
4547		cfcr |= MOD8251_PODD;
4548	    else
4549		cfcr |= MOD8251_PEVEN;
4550	} else
4551		cfcr |= MOD8251_PDISAB;
4552
4553	if ( cflag&CSTOPB )
4554		cfcr |= MOD8251_STOP2;
4555	else
4556		cfcr |= MOD8251_STOP1;
4557
4558	if ( count & 0x10000 )
4559		cfcr |= MOD8251_CLKX1;
4560	else
4561		cfcr |= MOD8251_CLKX16;
4562
4563	if (epson_machine_id != 0x20) {	/* XXX */
4564		int	tmp;
4565		while (!((tmp = inb(com->sts_port)) & STS8251_TxEMP))
4566			;
4567	}
4568	/* set baud rate from ospeed */
4569	pc98_set_baud_rate( com, count );
4570
4571	if ( cfcr != pc98_i8251_get_mod(com) )
4572		pc98_i8251_reset(com, cfcr, pc98_i8251_get_cmd(com) );
4573
4574	pc98_enable_i8251_interrupt( com, previnterrupt );
4575}
4576
4577static int
4578pc98_ttspeedtab(struct com_s *com, int speed)
4579{
4580	int	if_type, effect_sp, count = -1, mod;
4581
4582	if_type = com->pc98_if_type & 0x0f;
4583
4584	switch (com->pc98_if_type) {
4585	case COM_IF_INTERNAL:
4586	    if (PC98SIO_baud_rate_port(if_type) != -1) {
4587		count = ttspeedtab(speed, if_8251_type[if_type].speedtab);
4588		if (count > 0) {
4589		    count |= COM1_EXT_CLOCK;
4590		    break;
4591		}
4592	    }
4593
4594	    /* for *1CLK asynchronous! mode, TEFUTEFU */
4595	    mod = (sysclock == 5) ? 2457600 : 1996800;
4596	    effect_sp = ttspeedtab( speed, pc98speedtab );
4597	    if ( effect_sp < 0 )	/* XXX */
4598		effect_sp = ttspeedtab( (speed - 1), pc98speedtab );
4599	    if ( effect_sp <= 0 )
4600		return effect_sp;
4601	    if ( effect_sp == speed )
4602		mod /= 16;
4603	    if ( mod % effect_sp )
4604		return(-1);
4605	    count = mod / effect_sp;
4606	    if ( count > 65535 )
4607		return(-1);
4608	    if ( effect_sp != speed )
4609		count |= 0x10000;
4610	    break;
4611	case COM_IF_PC9861K_1:
4612	case COM_IF_PC9861K_2:
4613	    count = 1;
4614	    break;
4615	case COM_IF_IND_SS_1:
4616	case COM_IF_IND_SS_2:
4617	case COM_IF_PIO9032B_1:
4618	case COM_IF_PIO9032B_2:
4619	    if ( speed == 0 ) return 0;
4620	    count = ttspeedtab( speed, if_8251_type[if_type].speedtab );
4621	    break;
4622	case COM_IF_B98_01_1:
4623	case COM_IF_B98_01_2:
4624	    if ( speed == 0 ) return 0;
4625	    count = ttspeedtab( speed, if_8251_type[if_type].speedtab );
4626#ifdef B98_01_OLD
4627	    if (count == 0 || count == 1) {
4628		count += 4;
4629		count |= 0x20000;  /* x1 mode for 76800 and 153600 */
4630	    }
4631#endif
4632	    break;
4633	}
4634
4635	return count;
4636}
4637
4638static void
4639pc98_set_baud_rate( struct com_s *com, int count )
4640{
4641	int	if_type, io, s;
4642
4643	if_type = com->pc98_if_type & 0x0f;
4644	io = com->iobase & 0xff00;
4645
4646	switch (com->pc98_if_type) {
4647	case COM_IF_INTERNAL:
4648	    if (PC98SIO_baud_rate_port(if_type) != -1) {
4649		if (count & COM1_EXT_CLOCK) {
4650		    outb((Port_t)PC98SIO_baud_rate_port(if_type), count & 0xff);
4651		    break;
4652		} else {
4653		    outb((Port_t)PC98SIO_baud_rate_port(if_type), 0x09);
4654		}
4655	    }
4656
4657	    if ( count < 0 ) {
4658		printf( "[ Illegal count : %d ]", count );
4659		return;
4660	    } else if ( count == 0 )
4661		return;
4662	    /* set i8253 */
4663	    s = splclock();
4664	    if (count != 3)
4665		outb( 0x77, 0xb6 );
4666	    else
4667		outb( 0x77, 0xb4 );
4668	    outb( 0x5f, 0);
4669	    outb( 0x75, count & 0xff );
4670	    outb( 0x5f, 0);
4671	    outb( 0x75, (count >> 8) & 0xff );
4672	    splx(s);
4673	    break;
4674	case COM_IF_IND_SS_1:
4675	case COM_IF_IND_SS_2:
4676	    outb(io | PC98SIO_intr_ctrl_port(if_type), 0);
4677	    outb(io | PC98SIO_baud_rate_port(if_type), 0);
4678	    outb(io | PC98SIO_baud_rate_port(if_type), 0xc0);
4679	    outb(io | PC98SIO_baud_rate_port(if_type), (count >> 8) | 0x80);
4680	    outb(io | PC98SIO_baud_rate_port(if_type), count & 0xff);
4681	    break;
4682	case COM_IF_PIO9032B_1:
4683	case COM_IF_PIO9032B_2:
4684	    outb(io | PC98SIO_baud_rate_port(if_type), count);
4685	    break;
4686	case COM_IF_B98_01_1:
4687	case COM_IF_B98_01_2:
4688	    outb(io | PC98SIO_baud_rate_port(if_type), count & 0x0f);
4689#ifdef B98_01_OLD
4690	    /*
4691	     * Some old B98_01 board should be controlled
4692	     * in different way, but this hasn't been tested yet.
4693	     */
4694	    outb(io | PC98SIO_func_port(if_type),
4695		 (count & 0x20000) ? 0xf0 : 0xf2);
4696#endif
4697	    break;
4698	}
4699}
4700static int
4701pc98_check_if_type(struct isa_device *dev, struct siodev *iod)
4702{
4703	int	irr, io, if_type, tmp;
4704	static  short	irq_tab[2][8] = {
4705		{  3,  5,  6,  9, 10, 12, 13, -1},
4706		{  3, 10, 12, 13,  5,  6,  9, -1}
4707	};
4708
4709	iod->if_type = if_type = (dev->id_flags >> 24) & 0xff;
4710	if ((if_type < 0 || if_type > COM_IF_END1) &&
4711	    (if_type < 0x10 || if_type > COM_IF_END2))
4712	    return(-1);
4713	if_type &= 0x0f;
4714	iod->irq = 0;
4715	io = dev->id_iobase & 0xff00;
4716
4717	if (IS_8251(iod->if_type)) {
4718	    if (PC98SIO_func_port(if_type) != -1) {
4719		outb(io | PC98SIO_func_port(if_type), 0xf2);
4720		tmp = ttspeedtab(9600, if_8251_type[if_type].speedtab);
4721		if (tmp != -1 && PC98SIO_baud_rate_port(if_type) != -1)
4722		    outb(io | PC98SIO_baud_rate_port(if_type), tmp);
4723	    }
4724
4725	    iod->cmd  = io | PC98SIO_cmd_port(if_type);
4726	    iod->sts  = io | PC98SIO_sts_port(if_type);
4727	    iod->mod  = io | PC98SIO_in_modem_port(if_type);
4728	    iod->ctrl = io | PC98SIO_intr_ctrl_port(if_type);
4729
4730	    if (iod->if_type == COM_IF_INTERNAL) {
4731		iod->irq = 4;
4732
4733		/* XXX check new internal port. */
4734		outb(0x138, 0);
4735		DELAY(10);
4736		for (tmp = 0; tmp < 100; tmp++) {
4737		    if ((inb(0x138) & 1) == 0) {
4738			PC98SIO_baud_rate_port(if_type) = 0x13a;
4739			if_8251_type[if_type].name = " (internal fast)";
4740			if_8251_type[if_type].speedtab = pc98fast_speedtab;
4741			break;
4742		    }
4743		    DELAY(1);
4744		}
4745	    } else {
4746		tmp = inb( iod->mod ) & if_8251_type[if_type].irr_mask;
4747		if ((dev->id_iobase & 0xff) == IO_COM2)
4748		    iod->irq = irq_tab[0][tmp];
4749		else
4750		    iod->irq = irq_tab[1][tmp];
4751	    }
4752	} else {
4753	    irr = if_16550a_type[if_type].irr_read;
4754#ifdef COM_MULTIPORT
4755	    if (!COM_ISMULTIPORT(dev) || dev->id_unit == COM_MPMASTER(dev))
4756#endif
4757	    if (irr != -1) {
4758		tmp = inb(io | irr);
4759		if (dev->id_iobase & 0x01)	/* XXX depend on RSB-384 */
4760		    iod->irq = irq_tab[1][tmp >> 3];
4761		else
4762		    iod->irq = irq_tab[0][tmp & 0x07];
4763	    }
4764	}
4765	if ( iod->irq == -1 ) return -1;
4766
4767	return 0;
4768}
4769static int
4770pc98_set_ioport( struct com_s *com, int id_flags )
4771{
4772	int	io, if_type;
4773
4774	if_type = (id_flags >> 24) & 0xff;
4775	if (IS_8251(if_type)) {
4776	    pc98_check_sysclock();
4777	    io = com->iobase & 0xff00;
4778	    com->pc98_if_type	= if_type;
4779	    if_type &= 0x0f;
4780	    com->data_port	= io | PC98SIO_data_port(if_type);
4781	    com->cmd_port	= io | PC98SIO_cmd_port(if_type);
4782	    com->sts_port	= io | PC98SIO_sts_port(if_type);
4783	    com->in_modem_port	= io | PC98SIO_in_modem_port(if_type);
4784	    com->intr_ctrl_port	= io | PC98SIO_intr_ctrl_port(if_type);
4785	    return 0;
4786	}
4787
4788	return -1;
4789}
4790#endif /* PC98 defined */
4791