sio.c revision 66920
1184610Salfred/*-
2184610Salfred * Copyright (c) 1991 The Regents of the University of California.
3189002Sed * All rights reserved.
4184610Salfred *
5184610Salfred * Redistribution and use in source and binary forms, with or without
6184610Salfred * modification, are permitted provided that the following conditions
7184610Salfred * are met:
8184610Salfred * 1. Redistributions of source code must retain the above copyright
9184610Salfred *    notice, this list of conditions and the following disclaimer.
10184610Salfred * 2. Redistributions in binary form must reproduce the above copyright
11184610Salfred *    notice, this list of conditions and the following disclaimer in the
12184610Salfred *    documentation and/or other materials provided with the distribution.
13184610Salfred * 3. All advertising materials mentioning features or use of this software
14184610Salfred *    must display the following acknowledgement:
15184610Salfred *	This product includes software developed by the University of
16184610Salfred *	California, Berkeley and its contributors.
17184610Salfred * 4. Neither the name of the University nor the names of its contributors
18184610Salfred *    may be used to endorse or promote products derived from this software
19184610Salfred *    without specific prior written permission.
20184610Salfred *
21184610Salfred * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22184610Salfred * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23184610Salfred * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24184610Salfred * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25184610Salfred * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26184610Salfred * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27184610Salfred * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28184610Salfred * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29184610Salfred * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30184610Salfred * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31184610Salfred * SUCH DAMAGE.
32190754Sthompsa *
33184610Salfred * $FreeBSD: head/sys/dev/sio/sio.c 66920 2000-10-10 10:06:26Z tanimura $
34184610Salfred *	from: @(#)com.c	7.5 (Berkeley) 5/16/91
35188942Sthompsa *	from: i386/isa sio.c,v 1.234
36188942Sthompsa */
37188942Sthompsa
38188942Sthompsa#include "opt_comconsole.h"
39184610Salfred#include "opt_compat.h"
40184610Salfred#include "opt_ddb.h"
41184610Salfred#include "opt_sio.h"
42188942Sthompsa#include "card.h"
43188942Sthompsa#include "pci.h"
44188942Sthompsa#include "sio.h"
45188942Sthompsa
46188942Sthompsa/*
47188942Sthompsa * Serial driver, based on 386BSD-0.1 com driver.
48188942Sthompsa * Mostly rewritten to use pseudo-DMA.
49188942Sthompsa * Works for National Semiconductor NS8250-NS16550AF UARTs.
50184610Salfred * COM driver, based on HP dca driver.
51188942Sthompsa *
52188942Sthompsa * Changes for PC-Card integration:
53188942Sthompsa *	- Added PC-Card driver table and handlers
54184610Salfred */
55184610Salfred#include <sys/param.h>
56184610Salfred#include <sys/bus.h>
57190181Sthompsa#include <sys/systm.h>
58184610Salfred#include <sys/reboot.h>
59184610Salfred#include <sys/malloc.h>
60190180Sthompsa#include <sys/tty.h>
61184610Salfred#include <sys/proc.h>
62184610Salfred#include <sys/module.h>
63184610Salfred#include <sys/conf.h>
64184610Salfred#include <sys/dkstat.h>
65192502Sthompsa#include <sys/fcntl.h>
66192502Sthompsa#include <sys/interrupt.h>
67184610Salfred#include <sys/ipl.h>
68184610Salfred#include <sys/kernel.h>
69184610Salfred#include <sys/syslog.h>
70184610Salfred#include <sys/sysctl.h>
71184610Salfred#include <sys/bus.h>
72184610Salfred#include <machine/bus_pio.h>
73184610Salfred#include <machine/bus.h>
74192984Sthompsa#include <sys/rman.h>
75192984Sthompsa#include <sys/timetc.h>
76192984Sthompsa#include <sys/timepps.h>
77192984Sthompsa
78192984Sthompsa#include <isa/isareg.h>
79184610Salfred#include <isa/isavar.h>
80184610Salfred#if NPCI > 0
81184610Salfred#include <pci/pcireg.h>
82184610Salfred#include <pci/pcivar.h>
83184610Salfred#endif
84192984Sthompsa#include <machine/lock.h>
85192984Sthompsa
86192984Sthompsa#include <machine/clock.h>
87192984Sthompsa#ifndef SMP
88185948Sthompsa#include <machine/lock.h>
89185948Sthompsa#endif
90190735Sthompsa#include <machine/resource.h>
91184610Salfred
92184610Salfred#include <isa/sioreg.h>
93184610Salfred
94184610Salfred#ifdef COM_ESP
95184610Salfred#include <isa/ic/esp.h>
96184610Salfred#endif
97192984Sthompsa#include <isa/ic/ns16550.h>
98184610Salfred
99184610Salfred/* XXX - this is ok because we only do sio fast interrupts on i386 */
100184610Salfred#ifndef __i386__
101184610Salfred#define disable_intr()
102184610Salfred#define enable_intr()
103184610Salfred#endif
104184610Salfred
105184610Salfred#define	LOTS_OF_EVENTS	64	/* helps separate urgent events from input */
106184610Salfred
107184610Salfred#define	CALLOUT_MASK		0x80
108184610Salfred#define	CONTROL_MASK		0x60
109184610Salfred#define	CONTROL_INIT_STATE	0x20
110184610Salfred#define	CONTROL_LOCK_STATE	0x40
111184610Salfred#define	DEV_TO_UNIT(dev)	(MINOR_TO_UNIT(minor(dev)))
112184610Salfred#define	MINOR_MAGIC_MASK	(CALLOUT_MASK | CONTROL_MASK)
113184610Salfred#define	MINOR_TO_UNIT(mynor)	((mynor) & ~MINOR_MAGIC_MASK)
114184610Salfred
115184610Salfred#ifdef COM_MULTIPORT
116184610Salfred/* checks in flags for multiport and which is multiport "master chip"
117184610Salfred * for a given card
118184610Salfred */
119184610Salfred#define	COM_ISMULTIPORT(flags)	((flags) & 0x01)
120184610Salfred#define	COM_MPMASTER(flags)	(((flags) >> 8) & 0x0ff)
121184610Salfred#define	COM_NOTAST4(flags)	((flags) & 0x04)
122184610Salfred#endif /* COM_MULTIPORT */
123184610Salfred
124184610Salfred#define	COM_CONSOLE(flags)	((flags) & 0x10)
125184610Salfred#define	COM_FORCECONSOLE(flags)	((flags) & 0x20)
126184610Salfred#define	COM_LLCONSOLE(flags)	((flags) & 0x40)
127184610Salfred#define	COM_DEBUGGER(flags)	((flags) & 0x80)
128184610Salfred#define	COM_LOSESOUTINTS(flags)	((flags) & 0x08)
129184610Salfred#define	COM_NOFIFO(flags)		((flags) & 0x02)
130184610Salfred#define COM_ST16650A(flags)	((flags) & 0x20000)
131184610Salfred#define COM_C_NOPROBE		(0x40000)
132184610Salfred#define COM_NOPROBE(flags)	((flags) & COM_C_NOPROBE)
133184610Salfred#define COM_C_IIR_TXRDYBUG	(0x80000)
134184610Salfred#define COM_IIR_TXRDYBUG(flags)	((flags) & COM_C_IIR_TXRDYBUG)
135184610Salfred#define	COM_FIFOSIZE(flags)	(((flags) & 0xff000000) >> 24)
136184610Salfred
137184610Salfred#define	com_scr		7	/* scratch register for 16450-16550 (R/W) */
138184610Salfred
139184610Salfred#define	sio_getreg(com, off) \
140184610Salfred	(bus_space_read_1((com)->bst, (com)->bsh, (off)))
141184610Salfred#define	sio_setreg(com, off, value) \
142184610Salfred	(bus_space_write_1((com)->bst, (com)->bsh, (off), (value)))
143184610Salfred
144184610Salfred/*
145184610Salfred * com state bits.
146184610Salfred * (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher
147184610Salfred * than the other bits so that they can be tested as a group without masking
148184610Salfred * off the low bits.
149184610Salfred *
150184610Salfred * The following com and tty flags correspond closely:
151184610Salfred *	CS_BUSY		= TS_BUSY (maintained by comstart(), siopoll() and
152192984Sthompsa *				   comstop())
153192984Sthompsa *	CS_TTGO		= ~TS_TTSTOP (maintained by comparam() and comstart())
154184610Salfred *	CS_CTS_OFLOW	= CCTS_OFLOW (maintained by comparam())
155184610Salfred *	CS_RTS_IFLOW	= CRTS_IFLOW (maintained by comparam())
156184610Salfred * TS_FLUSH is not used.
157184610Salfred * XXX I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON.
158184610Salfred * XXX CS_*FLOW should be CF_*FLOW in com->flags (control flags not state).
159184610Salfred */
160184610Salfred#define	CS_BUSY		0x80	/* output in progress */
161184610Salfred#define	CS_TTGO		0x40	/* output not stopped by XOFF */
162184610Salfred#define	CS_ODEVREADY	0x20	/* external device h/w ready (CTS) */
163184610Salfred#define	CS_CHECKMSR	1	/* check of MSR scheduled */
164184610Salfred#define	CS_CTS_OFLOW	2	/* use CTS output flow control */
165184610Salfred#define	CS_DTR_OFF	0x10	/* DTR held off */
166184610Salfred#define	CS_ODONE	4	/* output completed */
167184610Salfred#define	CS_RTS_IFLOW	8	/* use RTS input flow control */
168184610Salfred#define	CSE_BUSYCHECK	1	/* siobusycheck() scheduled */
169184610Salfred
170184610Salfredstatic	char const * const	error_desc[] = {
171184610Salfred#define	CE_OVERRUN			0
172184610Salfred	"silo overflow",
173184610Salfred#define	CE_INTERRUPT_BUF_OVERFLOW	1
174184610Salfred	"interrupt-level buffer overflow",
175184610Salfred#define	CE_TTY_BUF_OVERFLOW		2
176184610Salfred	"tty-level buffer overflow",
177184610Salfred};
178184610Salfred
179184610Salfred#define	CE_NTYPES			3
180184610Salfred#define	CE_RECORD(com, errnum)		(++(com)->delta_error_counts[errnum])
181184610Salfred
182184610Salfred/* types.  XXX - should be elsewhere */
183184610Salfredtypedef u_int	Port_t;		/* hardware port */
184184610Salfredtypedef u_char	bool_t;		/* boolean */
185184610Salfred
186184610Salfred/* queue of linear buffers */
187184610Salfredstruct lbq {
188184610Salfred	u_char	*l_head;	/* next char to process */
189184610Salfred	u_char	*l_tail;	/* one past the last char to process */
190184610Salfred	struct lbq *l_next;	/* next in queue */
191184610Salfred	bool_t	l_queued;	/* nonzero if queued */
192184610Salfred};
193184610Salfred
194184610Salfred/* com device structure */
195184610Salfredstruct com_s {
196184610Salfred	u_int	flags;		/* Copy isa device flags */
197184610Salfred	u_char	state;		/* miscellaneous flag bits */
198184610Salfred	bool_t  active_out;	/* nonzero if the callout device is open */
199184610Salfred	u_char	cfcr_image;	/* copy of value written to CFCR */
200184610Salfred#ifdef COM_ESP
201184610Salfred	bool_t	esp;		/* is this unit a hayes esp board? */
202184610Salfred#endif
203184610Salfred	u_char	extra_state;	/* more flag bits, separate for order trick */
204184610Salfred	u_char	fifo_image;	/* copy of value written to FIFO */
205184610Salfred	bool_t	hasfifo;	/* nonzero for 16550 UARTs */
206184610Salfred	bool_t	st16650a;	/* Is a Startech 16650A or RTS/CTS compat */
207184610Salfred	bool_t	loses_outints;	/* nonzero if device loses output interrupts */
208184610Salfred	u_char	mcr_image;	/* copy of value written to MCR */
209184610Salfred#ifdef COM_MULTIPORT
210184610Salfred	bool_t	multiport;	/* is this unit part of a multiport device? */
211184610Salfred#endif /* COM_MULTIPORT */
212184610Salfred	bool_t	no_irq;		/* nonzero if irq is not attached */
213184610Salfred	bool_t  gone;		/* hardware disappeared */
214184610Salfred	bool_t	poll;		/* nonzero if polling is required */
215184610Salfred	bool_t	poll_output;	/* nonzero if polling for output is required */
216184610Salfred	int	unit;		/* unit	number */
217184610Salfred	int	dtr_wait;	/* time to hold DTR down on close (* 1/hz) */
218184610Salfred	u_int	tx_fifo_size;
219184610Salfred	u_int	wopeners;	/* # processes waiting for DCD in open() */
220184610Salfred
221184610Salfred	/*
222184610Salfred	 * The high level of the driver never reads status registers directly
223184610Salfred	 * because there would be too many side effects to handle conveniently.
224184610Salfred	 * Instead, it reads copies of the registers stored here by the
225184610Salfred	 * interrupt handler.
226192984Sthompsa	 */
227184610Salfred	u_char	last_modem_status;	/* last MSR read by intr handler */
228184610Salfred	u_char	prev_modem_status;	/* last MSR handled by high level */
229184610Salfred
230184610Salfred	u_char	hotchar;	/* ldisc-specific char to be handled ASAP */
231184610Salfred	u_char	*ibuf;		/* start of input buffer */
232184610Salfred	u_char	*ibufend;	/* end of input buffer */
233192448Sthompsa	u_char	*ibufold;	/* old input buffer, to be freed */
234184610Salfred	u_char	*ihighwater;	/* threshold in input buffer */
235184610Salfred	u_char	*iptr;		/* next free spot in input buffer */
236184610Salfred	int	ibufsize;	/* size of ibuf (not include error bytes) */
237192448Sthompsa	int	ierroff;	/* offset of error bytes in ibuf */
238184610Salfred
239184610Salfred	struct lbq	obufq;	/* head of queue of output buffers */
240184610Salfred	struct lbq	obufs[2];	/* output buffers */
241184610Salfred
242184610Salfred	bus_space_tag_t		bst;
243184610Salfred	bus_space_handle_t	bsh;
244184610Salfred
245184610Salfred	Port_t	data_port;	/* i/o ports */
246184610Salfred#ifdef COM_ESP
247190721Sthompsa	Port_t	esp_port;
248190721Sthompsa#endif
249190721Sthompsa	Port_t	int_id_port;
250184610Salfred	Port_t	modem_ctl_port;
251184610Salfred	Port_t	line_status_port;
252184610Salfred	Port_t	modem_status_port;
253184610Salfred	Port_t	intr_ctl_port;	/* Ports of IIR register */
254184610Salfred
255184610Salfred	struct tty	*tp;	/* cross reference */
256184610Salfred
257184610Salfred	/* Initial state. */
258184610Salfred	struct termios	it_in;	/* should be in struct tty */
259184610Salfred	struct termios	it_out;
260184610Salfred
261184610Salfred	/* Lock state. */
262184610Salfred	struct termios	lt_in;	/* should be in struct tty */
263192448Sthompsa	struct termios	lt_out;
264184610Salfred
265192448Sthompsa	bool_t	do_timestamp;
266184610Salfred	bool_t	do_dcd_timestamp;
267184610Salfred	struct timeval	timestamp;
268184610Salfred	struct timeval	dcd_timestamp;
269184610Salfred	struct	pps_state pps;
270184610Salfred
271184610Salfred	u_long	bytes_in;	/* statistics */
272190722Sthompsa	u_long	bytes_out;
273184610Salfred	u_int	delta_error_counts[CE_NTYPES];
274184610Salfred	u_long	error_counts[CE_NTYPES];
275184610Salfred
276184610Salfred	struct resource *irqres;
277190722Sthompsa	struct resource *ioportres;
278184610Salfred	void *cookie;
279184610Salfred	dev_t devs[6];
280184610Salfred
281192448Sthompsa	/*
282184610Salfred	 * Data area for output buffers.  Someday we should build the output
283184610Salfred	 * buffer queue without copying data.
284184610Salfred	 */
285192448Sthompsa	u_char	obuf1[256];
286184610Salfred	u_char	obuf2[256];
287184610Salfred};
288184610Salfred
289184610Salfred#ifdef COM_ESP
290184610Salfredstatic	int	espattach	__P((struct com_s *com, Port_t esp_port));
291184610Salfred#endif
292184610Salfredstatic	int	sioattach	__P((device_t dev, int rid));
293184610Salfredstatic	int	sio_isa_attach	__P((device_t dev));
294184610Salfred
295184610Salfredstatic	timeout_t siobusycheck;
296184610Salfredstatic	timeout_t siodtrwakeup;
297184610Salfredstatic	void	comhardclose	__P((struct com_s *com));
298184610Salfredstatic	void	sioinput	__P((struct com_s *com));
299184610Salfredstatic	void	siointr1	__P((struct com_s *com));
300192448Sthompsastatic	void	siointr		__P((void *arg));
301184610Salfredstatic	int	commctl		__P((struct com_s *com, int bits, int how));
302184610Salfredstatic	int	comparam	__P((struct tty *tp, struct termios *t));
303192448Sthompsastatic	swihand_t siopoll;
304184610Salfredstatic	int	sioprobe	__P((device_t dev, int xrid));
305184610Salfredstatic	int	sio_isa_probe	__P((device_t dev));
306184610Salfredstatic	void	siosettimeout	__P((void));
307184610Salfredstatic	int	siosetwater	__P((struct com_s *com, speed_t speed));
308184610Salfredstatic	void	comstart	__P((struct tty *tp));
309184610Salfredstatic	void	comstop		__P((struct tty *tp, int rw));
310184610Salfredstatic	timeout_t comwakeup;
311184610Salfredstatic	void	disc_optim	__P((struct tty	*tp, struct termios *t,
312184610Salfred				     struct com_s *com));
313184610Salfred
314184610Salfred#if NCARD > 0
315184610Salfredstatic	int	sio_pccard_attach __P((device_t dev));
316184610Salfredstatic	int	sio_pccard_detach __P((device_t dev));
317184610Salfredstatic	int	sio_pccard_probe __P((device_t dev));
318184610Salfred#endif /* NCARD > 0 */
319184610Salfred
320190722Sthompsa#if NPCI > 0
321190722Sthompsastatic	int	sio_pci_attach __P((device_t dev));
322190722Sthompsastatic	void	sio_pci_kludge_unit __P((device_t dev));
323190722Sthompsastatic	int	sio_pci_probe __P((device_t dev));
324192448Sthompsa#endif /* NPCI > 0 */
325190722Sthompsa
326190722Sthompsastatic char driver_name[] = "sio";
327192448Sthompsa
328190722Sthompsa/* table and macro for fast conversion from a unit number to its com struct */
329190722Sthompsastatic	devclass_t	sio_devclass;
330190722Sthompsa#define	com_addr(unit)	((struct com_s *) \
331184610Salfred			 devclass_get_softc(sio_devclass, unit))
332190721Sthompsa
333190721Sthompsastatic device_method_t sio_isa_methods[] = {
334190721Sthompsa	/* Device interface */
335190721Sthompsa	DEVMETHOD(device_probe,		sio_isa_probe),
336190721Sthompsa	DEVMETHOD(device_attach,	sio_isa_attach),
337190721Sthompsa
338190721Sthompsa	{ 0, 0 }
339190721Sthompsa};
340190721Sthompsa
341190721Sthompsastatic driver_t sio_isa_driver = {
342184610Salfred	driver_name,
343184610Salfred	sio_isa_methods,
344184610Salfred	sizeof(struct com_s),
345184610Salfred};
346184610Salfred
347184610Salfred#if NCARD > 0
348184610Salfredstatic device_method_t sio_pccard_methods[] = {
349184610Salfred	/* Device interface */
350184610Salfred	DEVMETHOD(device_probe,		sio_pccard_probe),
351184610Salfred	DEVMETHOD(device_attach,	sio_pccard_attach),
352184610Salfred	DEVMETHOD(device_detach,	sio_pccard_detach),
353184610Salfred
354184610Salfred	{ 0, 0 }
355184610Salfred};
356192984Sthompsa
357184610Salfredstatic driver_t sio_pccard_driver = {
358184610Salfred	driver_name,
359184610Salfred	sio_pccard_methods,
360184610Salfred	sizeof(struct com_s),
361184610Salfred};
362184610Salfred#endif /* NCARD > 0 */
363184610Salfred
364184610Salfred#if NPCI > 0
365184610Salfredstatic device_method_t sio_pci_methods[] = {
366184610Salfred	/* Device interface */
367184610Salfred	DEVMETHOD(device_probe,		sio_pci_probe),
368192448Sthompsa	DEVMETHOD(device_attach,	sio_pci_attach),
369184610Salfred
370184610Salfred	{ 0, 0 }
371184610Salfred};
372184610Salfred
373184610Salfredstatic driver_t sio_pci_driver = {
374192448Sthompsa	driver_name,
375184610Salfred	sio_pci_methods,
376184610Salfred	sizeof(struct com_s),
377192448Sthompsa};
378184610Salfred#endif /* NPCI > 0 */
379184610Salfred
380184610Salfredstatic	d_open_t	sioopen;
381184610Salfredstatic	d_close_t	sioclose;
382184610Salfredstatic	d_read_t	sioread;
383184610Salfredstatic	d_write_t	siowrite;
384184610Salfredstatic	d_ioctl_t	sioioctl;
385184610Salfred
386184610Salfred#define	CDEV_MAJOR	28
387184610Salfredstatic struct cdevsw sio_cdevsw = {
388184610Salfred	/* open */	sioopen,
389184610Salfred	/* close */	sioclose,
390184610Salfred	/* read */	sioread,
391184610Salfred	/* write */	siowrite,
392184610Salfred	/* ioctl */	sioioctl,
393184610Salfred	/* poll */	ttypoll,
394184610Salfred	/* mmap */	nommap,
395184610Salfred	/* strategy */	nostrategy,
396184610Salfred	/* name */	driver_name,
397184610Salfred	/* maj */	CDEV_MAJOR,
398184610Salfred	/* dump */	nodump,
399184610Salfred	/* psize */	nopsize,
400184610Salfred	/* flags */	D_TTY,
401184610Salfred	/* bmaj */	-1
402184610Salfred};
403184610Salfred
404184610Salfredint	comconsole = -1;
405184610Salfredstatic	volatile speed_t	comdefaultrate = CONSPEED;
406184610Salfred#ifdef __alpha__
407184610Salfredstatic	volatile speed_t	gdbdefaultrate = CONSPEED;
408184610Salfred#endif
409184610Salfredstatic	u_int	com_events;	/* input chars + weighted output completions */
410184610Salfredstatic	Port_t	siocniobase;
411184610Salfred#ifndef __alpha__
412184610Salfredstatic	int	siocnunit;
413192552Sthompsa#endif
414184610Salfredstatic	Port_t	siogdbiobase;
415184610Salfredstatic	int	siogdbunit = -1;
416192552Sthompsastatic	bool_t	sio_registered;
417184610Salfredstatic	int	sio_timeout;
418184610Salfredstatic	int	sio_timeouts_until_log;
419184610Salfredstatic	struct	callout_handle sio_timeout_handle
420184610Salfred    = CALLOUT_HANDLE_INITIALIZER(&sio_timeout_handle);
421184610Salfredstatic	int	sio_numunits;
422192448Sthompsa
423184610Salfredstatic	struct speedtab comspeedtab[] = {
424184610Salfred	{ 0,		0 },
425192448Sthompsa	{ 50,		COMBRD(50) },
426184610Salfred	{ 75,		COMBRD(75) },
427184610Salfred	{ 110,		COMBRD(110) },
428184610Salfred	{ 134,		COMBRD(134) },
429184610Salfred	{ 150,		COMBRD(150) },
430184610Salfred	{ 200,		COMBRD(200) },
431184610Salfred	{ 300,		COMBRD(300) },
432184610Salfred	{ 600,		COMBRD(600) },
433184610Salfred	{ 1200,		COMBRD(1200) },
434184610Salfred	{ 1800,		COMBRD(1800) },
435184610Salfred	{ 2400,		COMBRD(2400) },
436184610Salfred	{ 4800,		COMBRD(4800) },
437184610Salfred	{ 9600,		COMBRD(9600) },
438184610Salfred	{ 19200,	COMBRD(19200) },
439184610Salfred	{ 38400,	COMBRD(38400) },
440184610Salfred	{ 57600,	COMBRD(57600) },
441184610Salfred	{ 115200,	COMBRD(115200) },
442184610Salfred	{ -1,		-1 }
443184610Salfred};
444184610Salfred
445184610Salfred#ifdef COM_ESP
446184610Salfred/* XXX configure this properly. */
447184610Salfredstatic	Port_t	likely_com_ports[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, };
448184610Salfredstatic	Port_t	likely_esp_ports[] = { 0x140, 0x180, 0x280, 0 };
449184610Salfred#endif
450184610Salfred
451184610Salfred/*
452184610Salfred * handle sysctl read/write requests for console speed
453184610Salfred *
454184610Salfred * In addition to setting comdefaultrate for I/O through /dev/console,
455184610Salfred * also set the initial and lock values for the /dev/ttyXX device
456184610Salfred * if there is one associated with the console.  Finally, if the /dev/tty
457192448Sthompsa * device has already been open, change the speed on the open running port
458184610Salfred * itself.
459184610Salfred */
460184610Salfred
461184610Salfredstatic int
462184610Salfredsysctl_machdep_comdefaultrate(SYSCTL_HANDLER_ARGS)
463184610Salfred{
464184610Salfred	int error, s;
465184610Salfred	speed_t newspeed;
466184610Salfred	struct com_s *com;
467192448Sthompsa	struct tty *tp;
468184610Salfred
469184610Salfred	newspeed = comdefaultrate;
470192448Sthompsa
471184610Salfred	error = sysctl_handle_opaque(oidp, &newspeed, sizeof newspeed, req);
472184610Salfred	if (error || !req->newptr)
473184610Salfred		return (error);
474184610Salfred
475184610Salfred	comdefaultrate = newspeed;
476184610Salfred
477184610Salfred	if (comconsole < 0)		/* serial console not selected? */
478184610Salfred		return (0);
479184610Salfred
480184610Salfred	com = com_addr(comconsole);
481184610Salfred	if (com == NULL)
482184610Salfred		return (ENXIO);
483184610Salfred
484184610Salfred	/*
485184610Salfred	 * set the initial and lock rates for /dev/ttydXX and /dev/cuaXX
486184610Salfred	 * (note, the lock rates really are boolean -- if non-zero, disallow
487184610Salfred	 *  speed changes)
488184610Salfred	 */
489192984Sthompsa	com->it_in.c_ispeed  = com->it_in.c_ospeed =
490184610Salfred	com->lt_in.c_ispeed  = com->lt_in.c_ospeed =
491184610Salfred	com->it_out.c_ispeed = com->it_out.c_ospeed =
492184610Salfred	com->lt_out.c_ispeed = com->lt_out.c_ospeed = comdefaultrate;
493184610Salfred
494184610Salfred	/*
495184610Salfred	 * if we're open, change the running rate too
496184610Salfred	 */
497184610Salfred	tp = com->tp;
498192448Sthompsa	if (tp && (tp->t_state & TS_ISOPEN)) {
499184610Salfred		tp->t_termios.c_ispeed =
500184610Salfred		tp->t_termios.c_ospeed = comdefaultrate;
501184610Salfred		s = spltty();
502184610Salfred		error = comparam(tp, &tp->t_termios);
503184610Salfred		splx(s);
504184610Salfred	}
505192448Sthompsa	return error;
506184610Salfred}
507184610Salfred
508184610SalfredSYSCTL_PROC(_machdep, OID_AUTO, conspeed, CTLTYPE_INT | CTLFLAG_RW,
509192448Sthompsa	    0, 0, sysctl_machdep_comdefaultrate, "I", "");
510184610Salfred
511184610Salfred#define SET_FLAG(dev, bit) device_set_flags(dev, device_get_flags(dev) | (bit))
512184610Salfred#define CLR_FLAG(dev, bit) device_set_flags(dev, device_get_flags(dev) & ~(bit))
513184610Salfred
514184610Salfred#if NCARD > 0
515184610Salfredstatic int
516184610Salfredsio_pccard_probe(dev)
517184610Salfred	device_t	dev;
518184610Salfred{
519184610Salfred	/* Do not probe IRQ - pccard doesn't turn on the interrupt line */
520184610Salfred	/* until bus_setup_intr */
521184610Salfred	SET_FLAG(dev, COM_C_NOPROBE);
522184610Salfred
523184610Salfred	return (sioprobe(dev, 0));
524184610Salfred}
525184610Salfred
526184610Salfredstatic int
527184610Salfredsio_pccard_attach(dev)
528184610Salfred	device_t	dev;
529184610Salfred{
530184610Salfred	return (sioattach(dev, 0));
531184610Salfred}
532184610Salfred
533184610Salfred/*
534184610Salfred *	sio_detach - unload the driver and clear the table.
535184610Salfred *	XXX TODO:
536184610Salfred *	This is usually called when the card is ejected, but
537184610Salfred *	can be caused by a modunload of a controller driver.
538184610Salfred *	The idea is to reset the driver's view of the device
539184610Salfred *	and ensure that any driver entry points such as
540184610Salfred *	read and write do not hang.
541184610Salfred */
542184610Salfredstatic int
543184610Salfredsio_pccard_detach(dev)
544184610Salfred	device_t	dev;
545184610Salfred{
546184610Salfred	struct com_s	*com;
547184610Salfred	int i;
548184610Salfred
549184610Salfred	com = (struct com_s *) device_get_softc(dev);
550184610Salfred	if (com == NULL) {
551184610Salfred		device_printf(dev, "NULL com in siounload\n");
552184610Salfred		return (0);
553184610Salfred	}
554184610Salfred	com->gone = 1;
555184610Salfred	for (i = 0 ; i < 6; i++)
556192448Sthompsa		destroy_dev(com->devs[i]);
557184610Salfred	if (com->irqres) {
558184610Salfred		bus_teardown_intr(dev, com->irqres, com->cookie);
559184610Salfred		bus_release_resource(dev, SYS_RES_IRQ, 0, com->irqres);
560184610Salfred	}
561184610Salfred	if (com->ioportres)
562184610Salfred		bus_release_resource(dev, SYS_RES_IOPORT, 0, com->ioportres);
563184610Salfred	if (com->tp && (com->tp->t_state & TS_ISOPEN)) {
564184610Salfred		device_printf(dev, "still open, forcing close\n");
565184610Salfred		com->tp->t_gen++;
566192448Sthompsa		ttyclose(com->tp);
567184610Salfred		ttwakeup(com->tp);
568184610Salfred		ttwwakeup(com->tp);
569184610Salfred	} else {
570192448Sthompsa		if (com->ibuf != NULL)
571184610Salfred			free(com->ibuf, M_DEVBUF);
572184610Salfred	}
573184610Salfred	device_printf(dev, "unloaded\n");
574184610Salfred	return (0);
575184610Salfred}
576192552Sthompsa#endif /* NCARD > 0 */
577184610Salfred
578184610Salfred#if NPCI > 0
579192552Sthompsastruct pci_ids {
580184610Salfred	u_int32_t	type;
581184610Salfred	const char	*desc;
582184610Salfred	int		rid;
583184610Salfred};
584184610Salfred
585184610Salfredstatic struct pci_ids pci_ids[] = {
586184610Salfred	{ 0x100812b9, "3COM PCI FaxModem", 0x10 },
587184610Salfred	{ 0x048011c1, "ActionTec 56k FAX PCI Modem", 0x14 },
588184610Salfred	{ 0x00000000, NULL, 0 }
589184610Salfred};
590184610Salfred
591184610Salfredstatic int
592184610Salfredsio_pci_attach(dev)
593184610Salfred	device_t	dev;
594184610Salfred{
595184610Salfred	u_int32_t	type;
596184610Salfred	struct pci_ids	*id;
597184610Salfred
598184610Salfred	type = pci_get_devid(dev);
599184610Salfred	id = pci_ids;
600184610Salfred	while (id->type && id->type != type)
601184610Salfred		id++;
602184610Salfred	if (id->desc == NULL)
603192448Sthompsa		return (ENXIO);
604184610Salfred	sio_pci_kludge_unit(dev);
605184610Salfred	return (sioattach(dev, id->rid));
606184610Salfred}
607192448Sthompsa
608184610Salfred/*
609184610Salfred * Don't cut and paste this to other drivers.  It is a horrible kludge
610184610Salfred * which will fail to work and also be unnecessary in future versions.
611192448Sthompsa */
612184610Salfredstatic void
613184610Salfredsio_pci_kludge_unit(dev)
614184610Salfred	device_t dev;
615184610Salfred{
616184610Salfred	devclass_t	dc;
617184610Salfred	int		err;
618184610Salfred	int		start;
619184610Salfred	int		unit;
620184610Salfred
621184610Salfred	unit = 0;
622184610Salfred	start = 0;
623184610Salfred	while (resource_int_value("sio", unit, "port", &start) == 0 &&
624184610Salfred	    start > 0)
625184610Salfred		unit++;
626184610Salfred	if (device_get_unit(dev) < unit) {
627184610Salfred		dc = device_get_devclass(dev);
628184610Salfred		while (devclass_get_device(dc, unit))
629184610Salfred			unit++;
630184610Salfred		device_printf(dev, "moving to sio%d\n", unit);
631184610Salfred		err = device_set_unit(dev, unit);	/* EVIL DO NOT COPY */
632184610Salfred		if (err)
633184610Salfred			device_printf(dev, "error moving device %d\n", err);
634184610Salfred	}
635184610Salfred}
636184610Salfred
637184610Salfredstatic int
638184610Salfredsio_pci_probe(dev)
639184610Salfred	device_t	dev;
640184610Salfred{
641184610Salfred	u_int32_t	type;
642184610Salfred	struct pci_ids	*id;
643192984Sthompsa
644184610Salfred	type = pci_get_devid(dev);
645184610Salfred	id = pci_ids;
646184610Salfred	while (id->type && id->type != type)
647184610Salfred		id++;
648184610Salfred	if (id->desc == NULL)
649184610Salfred		return (ENXIO);
650184610Salfred	device_set_desc(dev, id->desc);
651184610Salfred	return (sioprobe(dev, id->rid));
652184610Salfred}
653184610Salfred#endif /* NPCI > 0 */
654184610Salfred
655184610Salfredstatic struct isa_pnp_id sio_ids[] = {
656184610Salfred	{0x0005d041, "Standard PC COM port"},	/* PNP0500 */
657184610Salfred	{0x0105d041, "16550A-compatible COM port"},	/* PNP0501 */
658184610Salfred	{0x0205d041, "Multiport serial device (non-intelligent 16550)"}, /* PNP0502 */
659184610Salfred	{0x1005d041, "Generic IRDA-compatible device"},	/* PNP0510 */
660184610Salfred	{0x1105d041, "Generic IRDA-compatible device"},	/* PNP0511 */
661184610Salfred	/* Devices that do not have a compatid */
662184610Salfred	{0x12206804, NULL},     /* ACH2012 - 5634BTS 56K Video Ready Modem */
663184610Salfred	{0x7602a904, NULL},	/* AEI0276 - 56K v.90 Fax Modem (LKT) */
664184610Salfred	{0x00007905, NULL},	/* AKY0000 - 56K Plug&Play Modem */
665184610Salfred	{0x01405407, NULL},	/* AZT4001 - AZT3000 PnP SOUND DEVICE, MODEM */
666184610Salfred	{0x56039008, NULL},	/* BDP0356 - Best Data 56x2 */
667184610Salfred	{0x36339008, NULL},	/* BDP3336 - Best Data Prods. 336F */
668184610Salfred	{0x0014490a, NULL},	/* BRI1400 - Boca 33.6 PnP */
669184610Salfred	{0x0015490a, NULL},	/* BRI1500 - Internal Fax Data */
670184610Salfred	{0x0034490a, NULL},	/* BRI3400 - Internal ACF Modem */
671184610Salfred	{0x0094490a, NULL},	/* BRI9400 - Boca K56Flex PnP */
672184610Salfred	{0x00b4490a, NULL},	/* BRIB400 - Boca 56k PnP */
673184610Salfred	{0x0030320d, NULL},	/* CIR3000 - Cirrus Logic V43 */
674184610Salfred	{0x0100440e, NULL},	/* CRD0001 - Cardinal MVP288IV ? */
675184610Salfred	{0x36033610, NULL},     /* DAV0336 - DAVICOM 336PNP MODEM */
676184610Salfred	{0x0000aa1a, NULL},	/* FUJ0000 - FUJITSU Modem 33600 PNP/I2 */
677184610Salfred	{0x1200c31e, NULL},	/* GVC0012 - VF1128HV-R9 (win modem?) */
678184610Salfred	{0x0303c31e, NULL},	/* GVC0303 - MaxTech 33.6 PnP D/F/V */
679184610Salfred	{0x0505c31e, NULL},	/* GVC0505 - GVC 56k Faxmodem */
680184610Salfred	{0x0050c31e, NULL},	/* GVC5000 - some GVC modem */
681184610Salfred	{0x3800f91e, NULL},	/* GWY0038 - Telepath with v.90 */
682184610Salfred	{0x9062f91e, NULL},	/* GWY6290 - Telepath with x2 Technology */
683184610Salfred	{0x21002534, NULL},	/* MAE0021 - Jetstream Int V.90 56k Voice Series 2*/
684184610Salfred	{0x0000f435, NULL},	/* MOT0000 - Motorola ModemSURFR 33.6 Intern */
685184610Salfred	{0x5015f435, NULL},	/* MOT1550 - Motorola ModemSURFR 56K Modem */
686184610Salfred	{0xf015f435, NULL},	/* MOT15F0 - Motorola VoiceSURFR 56K Modem */
687184610Salfred	{0x6045f435, NULL},	/* MOT4560 - Motorola ? */
688192984Sthompsa	{0x61e7a338, NULL},	/* NECE761 - 33.6Modem */
689184610Salfred	{0x0f804f3f, NULL},	/* OZO800f - Zoom 2812 (56k Modem) */
690184610Salfred	{0x39804f3f, NULL},	/* OZO8039 - Zoom 56k flex */
691184610Salfred	{0x3024a341, NULL},	/* PMC2430 - Pace 56 Voice Internal Modem */
692184610Salfred	{0x1000eb49, NULL},	/* ROK0010 - Rockwell ? */
693184610Salfred	{0x5002734a, NULL},	/* RSS0250 - 5614Jx3(G) Internal Modem */
694184610Salfred	{0x6202734a, NULL},	/* RSS0262 - 5614Jx3[G] V90+K56Flex Modem */
695184610Salfred	{0xc100ad4d, NULL},	/* SMM00C1 - Leopard 56k PnP */
696184610Salfred	{0x9012b04e, NULL},	/* SUP1290 - Supra ? */
697184610Salfred	{0x1013b04e, NULL},	/* SUP1310 - SupraExpress 336i PnP */
698184610Salfred	{0x8013b04e, NULL},	/* SUP1380 - SupraExpress 288i PnP Voice */
699184610Salfred	{0x8113b04e, NULL},	/* SUP1381 - SupraExpress 336i PnP Voice */
700184610Salfred	{0x5016b04e, NULL},	/* SUP1650 - Supra 336i Sp Intl */
701184610Salfred	{0x7016b04e, NULL},	/* SUP1670 - Supra 336i V+ Intl */
702184610Salfred	{0x7420b04e, NULL},	/* SUP2070 - Supra ? */
703184610Salfred	{0x8020b04e, NULL},	/* SUP2080 - Supra ? */
704184610Salfred	{0x8420b04e, NULL},	/* SUP2084 - SupraExpress 56i PnP */
705184610Salfred	{0x7121b04e, NULL},	/* SUP2171 - SupraExpress 56i Sp? */
706184610Salfred	{0x8024b04e, NULL},	/* SUP2480 - Supra ? */
707184610Salfred	{0x01007256, NULL},	/* USR0001 - U.S. Robotics Inc., Sportster W */
708184610Salfred	{0x02007256, NULL},	/* USR0002 - U.S. Robotics Inc. Sportster 33. */
709184610Salfred	{0x04007256, NULL},	/* USR0004 - USR Sportster 14.4k */
710184610Salfred	{0x06007256, NULL},	/* USR0006 - USR Sportster 33.6k */
711184610Salfred	{0x11007256, NULL},	/* USR0011 - USR ? */
712184610Salfred	{0x01017256, NULL},	/* USR0101 - USR ? */
713184610Salfred	{0x30207256, NULL},	/* USR2030 - U.S.Robotics Inc. Sportster 560 */
714184610Salfred	{0x50207256, NULL},	/* USR2050 - U.S.Robotics Inc. Sportster 33. */
715184610Salfred	{0x70207256, NULL},	/* USR2070 - U.S.Robotics Inc. Sportster 560 */
716184610Salfred	{0x30307256, NULL},	/* USR3030 - U.S. Robotics 56K FAX INT */
717184610Salfred	{0x31307256, NULL},	/* USR3031 - U.S. Robotics 56K FAX INT */
718184610Salfred	{0x50307256, NULL},	/* USR3050 - U.S. Robotics 56K FAX INT */
719184610Salfred	{0x70307256, NULL},	/* USR3070 - U.S. Robotics 56K Voice INT */
720184610Salfred	{0x90307256, NULL},	/* USR3090 - USR ? */
721184610Salfred	{0x70917256, NULL},	/* USR9170 - U.S. Robotics 56K FAX INT */
722184610Salfred	{0x90917256, NULL},	/* USR9190 - USR 56k Voice INT */
723184610Salfred	{0x0300695c, NULL},	/* WCI0003 - Fax/Voice/Modem/Speakphone/Asvd */
724184610Salfred	{0x01a0896a, NULL},	/* ZTIA001 - Zoom Internal V90 Faxmodem */
725184610Salfred	{0x61f7896a, NULL},	/* ZTIF761 - Zoom ComStar 33.6 */
726184824Sthompsa	{0}
727184610Salfred};
728184610Salfred
729184610Salfred
730184610Salfred
731184610Salfredstatic int
732184610Salfredsio_isa_probe(dev)
733184610Salfred	device_t	dev;
734184610Salfred{
735184610Salfred	/* Check isapnp ids */
736184610Salfred	if (ISA_PNP_PROBE(device_get_parent(dev), dev, sio_ids) == ENXIO)
737184610Salfred		return (ENXIO);
738184610Salfred	return (sioprobe(dev, 0));
739184610Salfred}
740184610Salfred
741184610Salfredstatic int
742184610Salfredsioprobe(dev, xrid)
743184610Salfred	device_t	dev;
744184610Salfred	int		xrid;
745184610Salfred{
746184610Salfred#if 0
747184610Salfred	static bool_t	already_init;
748184610Salfred	device_t	xdev;
749184610Salfred#endif
750184610Salfred	struct com_s	*com;
751184610Salfred	bool_t		failures[10];
752184610Salfred	int		fn;
753184610Salfred	device_t	idev;
754184610Salfred	Port_t		iobase;
755184610Salfred	intrmask_t	irqmap[4];
756184610Salfred	intrmask_t	irqs;
757184610Salfred	u_char		mcr_image;
758184610Salfred	int		result;
759184610Salfred	u_long		xirq;
760184610Salfred	u_int		flags = device_get_flags(dev);
761184610Salfred	int		rid;
762184610Salfred	struct resource *port;
763184610Salfred	int		intrsave;
764184610Salfred
765184610Salfred	rid = xrid;
766184610Salfred	port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid,
767184610Salfred				  0, ~0, IO_COMSIZE, RF_ACTIVE);
768184610Salfred	if (!port)
769184610Salfred		return (ENXIO);
770184610Salfred
771184610Salfred	com = device_get_softc(dev);
772184610Salfred	com->bst = rman_get_bustag(port);
773184610Salfred	com->bsh = rman_get_bushandle(port);
774184610Salfred
775184610Salfred#if 0
776184610Salfred	/*
777184610Salfred	 * XXX this is broken - when we are first called, there are no
778184610Salfred	 * previously configured IO ports.  We could hard code
779184610Salfred	 * 0x3f8, 0x2f8, 0x3e8, 0x2e8 etc but that's probably worse.
780184610Salfred	 * This code has been doing nothing since the conversion since
781184610Salfred	 * "count" is zero the first time around.
782184610Salfred	 */
783190735Sthompsa	if (!already_init) {
784184610Salfred		/*
785184610Salfred		 * Turn off MCR_IENABLE for all likely serial ports.  An unused
786184610Salfred		 * port with its MCR_IENABLE gate open will inhibit interrupts
787184610Salfred		 * from any used port that shares the interrupt vector.
788184610Salfred		 * XXX the gate enable is elsewhere for some multiports.
789184610Salfred		 */
790184610Salfred		device_t *devs;
791184610Salfred		int count, i, xioport;
792184610Salfred
793184610Salfred		devclass_get_devices(sio_devclass, &devs, &count);
794184610Salfred		for (i = 0; i < count; i++) {
795184824Sthompsa			xdev = devs[i];
796184610Salfred			if (device_is_enabled(xdev) &&
797184610Salfred			    bus_get_resource(xdev, SYS_RES_IOPORT, 0, &xioport,
798184610Salfred					     NULL) == 0)
799184610Salfred				outb(xioport + com_mcr, 0);
800184610Salfred		}
801184610Salfred		free(devs, M_TEMP);
802184610Salfred		already_init = TRUE;
803184610Salfred	}
804184610Salfred#endif
805184610Salfred
806184610Salfred	if (COM_LLCONSOLE(flags)) {
807184610Salfred		printf("sio%d: reserved for low-level i/o\n",
808184610Salfred		       device_get_unit(dev));
809184610Salfred		bus_release_resource(dev, SYS_RES_IOPORT, rid, port);
810184610Salfred		return (ENXIO);
811184610Salfred	}
812184610Salfred
813184610Salfred	/*
814184610Salfred	 * If the device is on a multiport card and has an AST/4
815184610Salfred	 * compatible interrupt control register, initialize this
816192552Sthompsa	 * register and prepare to leave MCR_IENABLE clear in the mcr.
817192552Sthompsa	 * Otherwise, prepare to set MCR_IENABLE in the mcr.
818184610Salfred	 * Point idev to the device struct giving the correct id_irq.
819184610Salfred	 * This is the struct for the master device if there is one.
820184610Salfred	 */
821184610Salfred	idev = dev;
822184610Salfred	mcr_image = MCR_IENABLE;
823192984Sthompsa#ifdef COM_MULTIPORT
824184610Salfred	if (COM_ISMULTIPORT(flags)) {
825184610Salfred		Port_t xiobase;
826184610Salfred		u_long io;
827184610Salfred
828184610Salfred		idev = devclass_get_device(sio_devclass, COM_MPMASTER(flags));
829184610Salfred		if (idev == NULL) {
830184610Salfred			printf("sio%d: master device %d not configured\n",
831184610Salfred			       device_get_unit(dev), COM_MPMASTER(flags));
832184610Salfred			idev = dev;
833187173Sthompsa		}
834184610Salfred		if (!COM_NOTAST4(flags)) {
835184610Salfred			if (bus_get_resource(idev, SYS_RES_IOPORT, 0, &io,
836184610Salfred					     NULL) == 0) {
837184610Salfred				xiobase = io;
838184610Salfred				if (bus_get_resource(idev, SYS_RES_IRQ, 0,
839184610Salfred				    NULL, NULL) == 0)
840184610Salfred					outb(xiobase + com_scr, 0x80);
841184610Salfred				else
842184610Salfred					outb(xiobase + com_scr, 0);
843184610Salfred			}
844184610Salfred			mcr_image = 0;
845190183Sthompsa		}
846184610Salfred	}
847192552Sthompsa#endif /* COM_MULTIPORT */
848184610Salfred	if (bus_get_resource(idev, SYS_RES_IRQ, 0, NULL, NULL) != 0)
849187173Sthompsa		mcr_image = 0;
850184610Salfred
851184610Salfred	bzero(failures, sizeof failures);
852184610Salfred	iobase = rman_get_start(port);
853184610Salfred
854184610Salfred	/*
855184610Salfred	 * We don't want to get actual interrupts, just masked ones.
856184610Salfred	 * Interrupts from this line should already be masked in the ICU,
857184610Salfred	 * but mask them in the processor as well in case there are some
858184610Salfred	 * (misconfigured) shared interrupts.
859184610Salfred	 */
860184610Salfred	intrsave = save_intr();
861190183Sthompsa	disable_intr();
862190183Sthompsa	COM_LOCK();
863190183Sthompsa/* EXTRA DELAY? */
864190183Sthompsa
865190183Sthompsa	/*
866190183Sthompsa	 * Initialize the speed and the word size and wait long enough to
867184610Salfred	 * drain the maximum of 16 bytes of junk in device output queues.
868184610Salfred	 * The speed is undefined after a master reset and must be set
869184610Salfred	 * before relying on anything related to output.  There may be
870184610Salfred	 * junk after a (very fast) soft reboot and (apparently) after
871184610Salfred	 * master reset.
872184610Salfred	 * XXX what about the UART bug avoided by waiting in comparam()?
873184610Salfred	 * We don't want to to wait long enough to drain at 2 bps.
874184610Salfred	 */
875184610Salfred	if (iobase == siocniobase)
876184610Salfred		DELAY((16 + 1) * 1000000 / (comdefaultrate / 10));
877184610Salfred	else {
878184610Salfred		sio_setreg(com, com_cfcr, CFCR_DLAB | CFCR_8BITS);
879184610Salfred		sio_setreg(com, com_dlbl, COMBRD(SIO_TEST_SPEED) & 0xff);
880184610Salfred		sio_setreg(com, com_dlbh, (u_int) COMBRD(SIO_TEST_SPEED) >> 8);
881184610Salfred		sio_setreg(com, com_cfcr, CFCR_8BITS);
882184610Salfred		DELAY((16 + 1) * 1000000 / (SIO_TEST_SPEED / 10));
883184610Salfred	}
884184610Salfred
885184610Salfred	/*
886184610Salfred	 * Enable the interrupt gate and disable device interupts.  This
887184610Salfred	 * should leave the device driving the interrupt line low and
888184610Salfred	 * guarantee an edge trigger if an interrupt can be generated.
889184610Salfred	 */
890184610Salfred/* EXTRA DELAY? */
891184610Salfred	sio_setreg(com, com_mcr, mcr_image);
892184610Salfred	sio_setreg(com, com_ier, 0);
893184610Salfred	DELAY(1000);		/* XXX */
894190183Sthompsa	irqmap[0] = isa_irq_pending();
895190183Sthompsa
896190183Sthompsa	/*
897190183Sthompsa	 * Attempt to set loopback mode so that we can send a null byte
898190183Sthompsa	 * without annoying any external device.
899190183Sthompsa	 */
900190183Sthompsa/* EXTRA DELAY? */
901184610Salfred	sio_setreg(com, com_mcr, mcr_image | MCR_LOOPBACK);
902184610Salfred
903184610Salfred	/*
904184610Salfred	 * Attempt to generate an output interrupt.  On 8250's, setting
905184610Salfred	 * IER_ETXRDY generates an interrupt independent of the current
906184610Salfred	 * setting and independent of whether the THR is empty.  On 16450's,
907184610Salfred	 * setting IER_ETXRDY generates an interrupt independent of the
908184610Salfred	 * current setting.  On 16550A's, setting IER_ETXRDY only
909184610Salfred	 * generates an interrupt when IER_ETXRDY is not already set.
910184610Salfred	 */
911184610Salfred	sio_setreg(com, com_ier, IER_ETXRDY);
912184610Salfred
913184610Salfred	/*
914184610Salfred	 * On some 16x50 incompatibles, setting IER_ETXRDY doesn't generate
915184610Salfred	 * an interrupt.  They'd better generate one for actually doing
916184610Salfred	 * output.  Loopback may be broken on the same incompatibles but
917184610Salfred	 * it's unlikely to do more than allow the null byte out.
918184610Salfred	 */
919184610Salfred	sio_setreg(com, com_data, 0);
920184610Salfred	DELAY((1 + 2) * 1000000 / (SIO_TEST_SPEED / 10));
921184610Salfred
922184610Salfred	/*
923184610Salfred	 * Turn off loopback mode so that the interrupt gate works again
924184610Salfred	 * (MCR_IENABLE was hidden).  This should leave the device driving
925190183Sthompsa	 * an interrupt line high.  It doesn't matter if the interrupt
926190183Sthompsa	 * line oscillates while we are not looking at it, since interrupts
927184610Salfred	 * are disabled.
928184610Salfred	 */
929190183Sthompsa/* EXTRA DELAY? */
930190183Sthompsa	sio_setreg(com, com_mcr, mcr_image);
931184610Salfred
932184610Salfred	/*
933190183Sthompsa	 * Some pcmcia cards have the "TXRDY bug", so we check everyone
934184610Salfred	 * for IIR_TXRDY implementation ( Palido 321s, DC-1S... )
935190183Sthompsa	 */
936190183Sthompsa	if (COM_NOPROBE(flags)) {
937190183Sthompsa		/* Reading IIR register twice */
938190183Sthompsa		for (fn = 0; fn < 2; fn ++) {
939190183Sthompsa			DELAY(10000);
940190183Sthompsa			failures[6] = sio_getreg(com, com_iir);
941190183Sthompsa		}
942190183Sthompsa		/* Check IIR_TXRDY clear ? */
943190183Sthompsa		result = 0;
944190183Sthompsa		if (failures[6] & IIR_TXRDY) {
945190183Sthompsa			/* Nop, Double check with clearing IER */
946190183Sthompsa			sio_setreg(com, com_ier, 0);
947190183Sthompsa			if (sio_getreg(com, com_iir) & IIR_NOPEND) {
948190183Sthompsa				/* Ok. we're familia this gang */
949184610Salfred				SET_FLAG(dev, COM_C_IIR_TXRDYBUG);
950184610Salfred			} else {
951184610Salfred				/* Unknown, Just omit this chip.. XXX */
952184610Salfred				result = ENXIO;
953190183Sthompsa			}
954190183Sthompsa		} else {
955190183Sthompsa			/* OK. this is well-known guys */
956190183Sthompsa			CLR_FLAG(dev, COM_C_IIR_TXRDYBUG);
957190183Sthompsa		}
958184610Salfred		sio_setreg(com, com_cfcr, CFCR_8BITS);
959184610Salfred		COM_UNLOCK();
960184610Salfred		restore_intr(intrsave);
961184610Salfred		bus_release_resource(dev, SYS_RES_IOPORT, rid, port);
962184610Salfred		return (iobase == siocniobase ? 0 : result);
963184610Salfred	}
964184610Salfred
965184610Salfred	/*
966184610Salfred	 * Check that
967184610Salfred	 *	o the CFCR, IER and MCR in UART hold the values written to them
968192984Sthompsa	 *	  (the values happen to be all distinct - this is good for
969184610Salfred	 *	  avoiding false positive tests from bus echoes).
970184610Salfred	 *	o an output interrupt is generated and its vector is correct.
971184610Salfred	 *	o the interrupt goes away when the IIR in the UART is read.
972187173Sthompsa	 */
973184610Salfred/* EXTRA DELAY? */
974184610Salfred	failures[0] = sio_getreg(com, com_cfcr) - CFCR_8BITS;
975184610Salfred	failures[1] = sio_getreg(com, com_ier) - IER_ETXRDY;
976184610Salfred	failures[2] = sio_getreg(com, com_mcr) - mcr_image;
977184610Salfred	DELAY(10000);		/* Some internal modems need this time */
978184610Salfred	irqmap[1] = isa_irq_pending();
979192984Sthompsa	failures[4] = (sio_getreg(com, com_iir) & IIR_IMASK) - IIR_TXRDY;
980184610Salfred	DELAY(1000);		/* XXX */
981187173Sthompsa	irqmap[2] = isa_irq_pending();
982184610Salfred	failures[6] = (sio_getreg(com, com_iir) & IIR_IMASK) - IIR_NOPEND;
983184610Salfred
984184610Salfred	/*
985184610Salfred	 * Turn off all device interrupts and check that they go off properly.
986184610Salfred	 * Leave MCR_IENABLE alone.  For ports without a master port, it gates
987184610Salfred	 * the OUT2 output of the UART to
988184610Salfred	 * the ICU input.  Closing the gate would give a floating ICU input
989184610Salfred	 * (unless there is another device driving it) and spurious interrupts.
990184610Salfred	 * (On the system that this was first tested on, the input floats high
991184610Salfred	 * and gives a (masked) interrupt as soon as the gate is closed.)
992184610Salfred	 */
993184610Salfred	sio_setreg(com, com_ier, 0);
994184610Salfred	sio_setreg(com, com_cfcr, CFCR_8BITS);	/* dummy to avoid bus echo */
995184610Salfred	failures[7] = sio_getreg(com, com_ier);
996184610Salfred	DELAY(1000);		/* XXX */
997184610Salfred	irqmap[3] = isa_irq_pending();
998184610Salfred	failures[9] = (sio_getreg(com, com_iir) & IIR_IMASK) - IIR_NOPEND;
999184610Salfred
1000184610Salfred	COM_UNLOCK();
1001184610Salfred	restore_intr(intrsave);
1002184610Salfred
1003184610Salfred	irqs = irqmap[1] & ~irqmap[0];
1004184610Salfred	if (bus_get_resource(idev, SYS_RES_IRQ, 0, &xirq, NULL) == 0 &&
1005184610Salfred	    ((1 << xirq) & irqs) == 0)
1006184610Salfred		printf(
1007184610Salfred		"sio%d: configured irq %ld not in bitmap of probed irqs %#x\n",
1008184610Salfred		    device_get_unit(dev), xirq, irqs);
1009184610Salfred	if (bootverbose)
1010184610Salfred		printf("sio%d: irq maps: %#x %#x %#x %#x\n",
1011184610Salfred		    device_get_unit(dev),
1012184610Salfred		    irqmap[0], irqmap[1], irqmap[2], irqmap[3]);
1013184610Salfred
1014184610Salfred	result = 0;
1015184610Salfred	for (fn = 0; fn < sizeof failures; ++fn)
1016184610Salfred		if (failures[fn]) {
1017184610Salfred			sio_setreg(com, com_mcr, 0);
1018192984Sthompsa			result = ENXIO;
1019184610Salfred			if (bootverbose) {
1020184610Salfred				printf("sio%d: probe failed test(s):",
1021184610Salfred				    device_get_unit(dev));
1022184610Salfred				for (fn = 0; fn < sizeof failures; ++fn)
1023184610Salfred					if (failures[fn])
1024184610Salfred						printf(" %d", fn);
1025184610Salfred				printf("\n");
1026184610Salfred			}
1027184610Salfred			break;
1028184610Salfred		}
1029184610Salfred	bus_release_resource(dev, SYS_RES_IOPORT, rid, port);
1030184610Salfred	return (iobase == siocniobase ? 0 : result);
1031184610Salfred}
1032184610Salfred
1033187173Sthompsa#ifdef COM_ESP
1034184610Salfredstatic int
1035184610Salfredespattach(com, esp_port)
1036184610Salfred	struct com_s		*com;
1037184610Salfred	Port_t			esp_port;
1038184610Salfred{
1039184610Salfred	u_char	dips;
1040184610Salfred	u_char	val;
1041184610Salfred
1042184610Salfred	/*
1043184610Salfred	 * Check the ESP-specific I/O port to see if we're an ESP
1044190735Sthompsa	 * card.  If not, return failure immediately.
1045184610Salfred	 */
1046184610Salfred	if ((inb(esp_port) & 0xf3) == 0) {
1047184610Salfred		printf(" port 0x%x is not an ESP board?\n", esp_port);
1048184824Sthompsa		return (0);
1049184610Salfred	}
1050184610Salfred
1051184610Salfred	/*
1052184610Salfred	 * We've got something that claims to be a Hayes ESP card.
1053190735Sthompsa	 * Let's hope so.
1054190735Sthompsa	 */
1055184610Salfred
1056184610Salfred	/* Get the dip-switch configuration */
1057184610Salfred	outb(esp_port + ESP_CMD1, ESP_GETDIPS);
1058192984Sthompsa	dips = inb(esp_port + ESP_STATUS1);
1059184610Salfred
1060184610Salfred	/*
1061184610Salfred	 * Bits 0,1 of dips say which COM port we are.
1062184610Salfred	 */
1063184610Salfred	if (rman_get_start(com->ioportres) == likely_com_ports[dips & 0x03])
1064184610Salfred		printf(" : ESP");
1065184610Salfred	else {
1066184610Salfred		printf(" esp_port has com %d\n", dips & 0x03);
1067184610Salfred		return (0);
1068184610Salfred	}
1069184610Salfred
1070184610Salfred	/*
1071184610Salfred	 * Check for ESP version 2.0 or later:  bits 4,5,6 = 010.
1072184610Salfred	 */
1073184610Salfred	outb(esp_port + ESP_CMD1, ESP_GETTEST);
1074184610Salfred	val = inb(esp_port + ESP_STATUS1);	/* clear reg 1 */
1075184610Salfred	val = inb(esp_port + ESP_STATUS2);
1076184610Salfred	if ((val & 0x70) < 0x20) {
1077184610Salfred		printf("-old (%o)", val & 0x70);
1078184610Salfred		return (0);
1079184610Salfred	}
1080184610Salfred
1081184610Salfred	/*
1082184610Salfred	 * Check for ability to emulate 16550:  bit 7 == 1
1083184610Salfred	 */
1084184610Salfred	if ((dips & 0x80) == 0) {
1085184610Salfred		printf(" slave");
1086184610Salfred		return (0);
1087184610Salfred	}
1088184610Salfred
1089184610Salfred	/*
1090184610Salfred	 * Okay, we seem to be a Hayes ESP card.  Whee.
1091184610Salfred	 */
1092184610Salfred	com->esp = TRUE;
1093184610Salfred	com->esp_port = esp_port;
1094184610Salfred	return (1);
1095184610Salfred}
1096184610Salfred#endif /* COM_ESP */
1097184610Salfred
1098184610Salfredstatic int
1099184610Salfredsio_isa_attach(dev)
1100184610Salfred	device_t	dev;
1101184610Salfred{
1102184610Salfred	return (sioattach(dev, 0));
1103184610Salfred}
1104184610Salfred
1105184610Salfredstatic int
1106184610Salfredsioattach(dev, xrid)
1107184610Salfred	device_t	dev;
1108184610Salfred	int		xrid;
1109184610Salfred{
1110184610Salfred	struct com_s	*com;
1111184610Salfred#ifdef COM_ESP
1112184610Salfred	Port_t		*espp;
1113184610Salfred#endif
1114184610Salfred	Port_t		iobase;
1115184610Salfred	int		unit;
1116184610Salfred	u_int		flags;
1117184610Salfred	int		rid;
1118184610Salfred	struct resource *port;
1119184610Salfred	int		ret;
1120184610Salfred	int		intrstate;
1121184610Salfred
1122192984Sthompsa	rid = xrid;
1123184610Salfred	port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid,
1124184610Salfred				  0, ~0, IO_COMSIZE, RF_ACTIVE);
1125184610Salfred	if (!port)
1126184610Salfred		return (ENXIO);
1127184610Salfred
1128184610Salfred	iobase = rman_get_start(port);
1129184610Salfred	unit = device_get_unit(dev);
1130184610Salfred	com = device_get_softc(dev);
1131184610Salfred	flags = device_get_flags(dev);
1132184610Salfred
1133184610Salfred	if (unit >= sio_numunits)
1134184610Salfred		sio_numunits = unit + 1;
1135184610Salfred	/*
1136184610Salfred	 * sioprobe() has initialized the device registers as follows:
1137184610Salfred	 *	o cfcr = CFCR_8BITS.
1138184610Salfred	 *	  It is most important that CFCR_DLAB is off, so that the
1139184610Salfred	 *	  data port is not hidden when we enable interrupts.
1140184610Salfred	 *	o ier = 0.
1141184610Salfred	 *	  Interrupts are only enabled when the line is open.
1142184610Salfred	 *	o mcr = MCR_IENABLE, or 0 if the port has AST/4 compatible
1143184610Salfred	 *	  interrupt control register or the config specifies no irq.
1144184610Salfred	 *	  Keeping MCR_DTR and MCR_RTS off might stop the external
1145184610Salfred	 *	  device from sending before we are ready.
1146184610Salfred	 */
1147184610Salfred	bzero(com, sizeof *com);
1148184610Salfred	com->unit = unit;
1149184610Salfred	com->ioportres = port;
1150184610Salfred	com->bst = rman_get_bustag(port);
1151184610Salfred	com->bsh = rman_get_bushandle(port);
1152184610Salfred	com->cfcr_image = CFCR_8BITS;
1153184610Salfred	com->dtr_wait = 3 * hz;
1154184610Salfred	com->loses_outints = COM_LOSESOUTINTS(flags) != 0;
1155184610Salfred	com->no_irq = bus_get_resource(dev, SYS_RES_IRQ, 0, NULL, NULL) != 0;
1156184610Salfred	com->tx_fifo_size = 1;
1157184610Salfred	com->obufs[0].l_head = com->obuf1;
1158184610Salfred	com->obufs[1].l_head = com->obuf2;
1159184610Salfred
1160184610Salfred	com->data_port = iobase + com_data;
1161184610Salfred	com->int_id_port = iobase + com_iir;
1162184610Salfred	com->modem_ctl_port = iobase + com_mcr;
1163184610Salfred	com->mcr_image = inb(com->modem_ctl_port);
1164184610Salfred	com->line_status_port = iobase + com_lsr;
1165184610Salfred	com->modem_status_port = iobase + com_msr;
1166184610Salfred	com->intr_ctl_port = iobase + com_ier;
1167184610Salfred
1168184610Salfred	/*
1169184610Salfred	 * We don't use all the flags from <sys/ttydefaults.h> since they
1170184610Salfred	 * are only relevant for logins.  It's important to have echo off
1171192984Sthompsa	 * initially so that the line doesn't start blathering before the
1172184610Salfred	 * echo flag can be turned off.
1173187173Sthompsa	 */
1174184610Salfred	com->it_in.c_iflag = 0;
1175184610Salfred	com->it_in.c_oflag = 0;
1176184610Salfred	com->it_in.c_cflag = TTYDEF_CFLAG;
1177184610Salfred	com->it_in.c_lflag = 0;
1178192499Sthompsa	if (unit == comconsole) {
1179184610Salfred		com->it_in.c_iflag = TTYDEF_IFLAG;
1180184610Salfred		com->it_in.c_oflag = TTYDEF_OFLAG;
1181184610Salfred		com->it_in.c_cflag = TTYDEF_CFLAG | CLOCAL;
1182184610Salfred		com->it_in.c_lflag = TTYDEF_LFLAG;
1183184610Salfred		com->lt_out.c_cflag = com->lt_in.c_cflag = CLOCAL;
1184184610Salfred		com->lt_out.c_ispeed = com->lt_out.c_ospeed =
1185184610Salfred		com->lt_in.c_ispeed = com->lt_in.c_ospeed =
1186192984Sthompsa		com->it_in.c_ispeed = com->it_in.c_ospeed = comdefaultrate;
1187192984Sthompsa	} else
1188184610Salfred		com->it_in.c_ispeed = com->it_in.c_ospeed = TTYDEF_SPEED;
1189184610Salfred	intrstate = save_intr();
1190184610Salfred	if (siosetwater(com, com->it_in.c_ispeed) != 0) {
1191184610Salfred		COM_UNLOCK();
1192184610Salfred		restore_intr(intrstate);
1193184610Salfred		/*
1194184610Salfred		 * Leave i/o resources allocated if this is a `cn'-level
1195184824Sthompsa		 * console, so that other devices can't snarf them.
1196184610Salfred		 */
1197184610Salfred		if (iobase != siocniobase)
1198184610Salfred			bus_release_resource(dev, SYS_RES_IOPORT, rid, port);
1199184610Salfred		return (ENOMEM);
1200184610Salfred	}
1201184610Salfred	COM_UNLOCK();
1202184610Salfred	restore_intr(intrstate);
1203184610Salfred	termioschars(&com->it_in);
1204184610Salfred	com->it_out = com->it_in;
1205184610Salfred
1206184610Salfred	/* attempt to determine UART type */
1207184610Salfred	printf("sio%d: type", unit);
1208184610Salfred
1209184610Salfred
1210184610Salfred#ifdef COM_MULTIPORT
1211184610Salfred	if (!COM_ISMULTIPORT(flags) && !COM_IIR_TXRDYBUG(flags))
1212184610Salfred#else
1213184610Salfred	if (!COM_IIR_TXRDYBUG(flags))
1214184610Salfred#endif
1215184610Salfred	{
1216184610Salfred		u_char	scr;
1217184610Salfred		u_char	scr1;
1218184610Salfred		u_char	scr2;
1219184610Salfred
1220184610Salfred		scr = sio_getreg(com, com_scr);
1221184610Salfred		sio_setreg(com, com_scr, 0xa5);
1222184610Salfred		scr1 = sio_getreg(com, com_scr);
1223184610Salfred		sio_setreg(com, com_scr, 0x5a);
1224184610Salfred		scr2 = sio_getreg(com, com_scr);
1225184610Salfred		sio_setreg(com, com_scr, scr);
1226184610Salfred		if (scr1 != 0xa5 || scr2 != 0x5a) {
1227184610Salfred			printf(" 8250");
1228184610Salfred			goto determined_type;
1229184610Salfred		}
1230184610Salfred	}
1231184610Salfred	sio_setreg(com, com_fifo, FIFO_ENABLE | FIFO_RX_HIGH);
1232184610Salfred	DELAY(100);
1233184610Salfred	com->st16650a = 0;
1234184610Salfred	switch (inb(com->int_id_port) & IIR_FIFO_MASK) {
1235184610Salfred	case FIFO_RX_LOW:
1236184610Salfred		printf(" 16450");
1237184610Salfred		break;
1238184610Salfred	case FIFO_RX_MEDL:
1239184610Salfred		printf(" 16450?");
1240184610Salfred		break;
1241184610Salfred	case FIFO_RX_MEDH:
1242184610Salfred		printf(" 16550?");
1243184610Salfred		break;
1244184610Salfred	case FIFO_RX_HIGH:
1245184610Salfred		if (COM_NOFIFO(flags)) {
1246184610Salfred			printf(" 16550A fifo disabled");
1247184610Salfred		} else {
1248184610Salfred			com->hasfifo = TRUE;
1249184610Salfred			if (COM_ST16650A(flags)) {
1250184610Salfred				com->st16650a = 1;
1251184610Salfred				com->tx_fifo_size = 32;
1252184610Salfred				printf(" ST16650A");
1253184610Salfred			} else {
1254184610Salfred				com->tx_fifo_size = COM_FIFOSIZE(flags);
1255184610Salfred				printf(" 16550A");
1256184610Salfred			}
1257184610Salfred		}
1258184610Salfred#ifdef COM_ESP
1259184610Salfred		for (espp = likely_esp_ports; *espp != 0; espp++)
1260184610Salfred			if (espattach(com, *espp)) {
1261184610Salfred				com->tx_fifo_size = 1024;
1262184610Salfred				break;
1263184610Salfred			}
1264184610Salfred#endif
1265184610Salfred		if (!com->st16650a) {
1266184610Salfred			if (!com->tx_fifo_size)
1267184610Salfred				com->tx_fifo_size = 16;
1268184610Salfred			else
1269184610Salfred				printf(" lookalike with %d bytes FIFO",
1270184610Salfred				    com->tx_fifo_size);
1271184610Salfred		}
1272184610Salfred
1273184610Salfred		break;
1274192984Sthompsa	}
1275184610Salfred
1276184610Salfred#ifdef COM_ESP
1277192984Sthompsa	if (com->esp) {
1278184610Salfred		/*
1279184824Sthompsa		 * Set 16550 compatibility mode.
1280184610Salfred		 * We don't use the ESP_MODE_SCALE bit to increase the
1281184610Salfred		 * fifo trigger levels because we can't handle large
1282184610Salfred		 * bursts of input.
1283184610Salfred		 * XXX flow control should be set in comparam(), not here.
1284192499Sthompsa		 */
1285184610Salfred		outb(com->esp_port + ESP_CMD1, ESP_SETMODE);
1286184610Salfred		outb(com->esp_port + ESP_CMD2, ESP_MODE_RTS | ESP_MODE_FIFO);
1287184610Salfred
1288184610Salfred		/* Set RTS/CTS flow control. */
1289184610Salfred		outb(com->esp_port + ESP_CMD1, ESP_SETFLOWTYPE);
1290184610Salfred		outb(com->esp_port + ESP_CMD2, ESP_FLOW_RTS);
1291184610Salfred		outb(com->esp_port + ESP_CMD2, ESP_FLOW_CTS);
1292184610Salfred
1293184610Salfred		/* Set flow-control levels. */
1294184610Salfred		outb(com->esp_port + ESP_CMD1, ESP_SETRXFLOW);
1295184610Salfred		outb(com->esp_port + ESP_CMD2, HIBYTE(768));
1296184610Salfred		outb(com->esp_port + ESP_CMD2, LOBYTE(768));
1297184610Salfred		outb(com->esp_port + ESP_CMD2, HIBYTE(512));
1298184610Salfred		outb(com->esp_port + ESP_CMD2, LOBYTE(512));
1299184610Salfred	}
1300184610Salfred#endif /* COM_ESP */
1301184610Salfred	sio_setreg(com, com_fifo, 0);
1302184610Salfreddetermined_type: ;
1303184610Salfred
1304192984Sthompsa#ifdef COM_MULTIPORT
1305184610Salfred	if (COM_ISMULTIPORT(flags)) {
1306184610Salfred		device_t masterdev;
1307184610Salfred
1308184610Salfred		com->multiport = TRUE;
1309184610Salfred		printf(" (multiport");
1310184610Salfred		if (unit == COM_MPMASTER(flags))
1311184610Salfred			printf(" master");
1312184610Salfred		printf(")");
1313184610Salfred		masterdev = devclass_get_device(sio_devclass,
1314184824Sthompsa		    COM_MPMASTER(flags));
1315184610Salfred		com->no_irq = (masterdev == NULL || bus_get_resource(masterdev,
1316184610Salfred		    SYS_RES_IRQ, 0, NULL, NULL) != 0);
1317184610Salfred	 }
1318184610Salfred#endif /* COM_MULTIPORT */
1319184610Salfred	if (unit == comconsole)
1320184610Salfred		printf(", console");
1321184610Salfred	if (COM_IIR_TXRDYBUG(flags))
1322184610Salfred		printf(" with a bogus IIR_TXRDY register");
1323184610Salfred	printf("\n");
1324184610Salfred
1325184610Salfred	if (!sio_registered) {
1326184610Salfred		register_swi(SWI_TTY, siopoll);
1327184610Salfred		sio_registered = TRUE;
1328184610Salfred	}
1329184610Salfred	com->devs[0] = make_dev(&sio_cdevsw, unit,
1330184610Salfred	    UID_ROOT, GID_WHEEL, 0600, "ttyd%r", unit);
1331184610Salfred	com->devs[1] = make_dev(&sio_cdevsw, unit | CONTROL_INIT_STATE,
1332184610Salfred	    UID_ROOT, GID_WHEEL, 0600, "ttyid%r", unit);
1333184824Sthompsa	com->devs[2] = make_dev(&sio_cdevsw, unit | CONTROL_LOCK_STATE,
1334184610Salfred	    UID_ROOT, GID_WHEEL, 0600, "ttyld%r", unit);
1335184610Salfred	com->devs[3] = make_dev(&sio_cdevsw, unit | CALLOUT_MASK,
1336184610Salfred	    UID_UUCP, GID_DIALER, 0660, "cuaa%r", unit);
1337184610Salfred	com->devs[4] = make_dev(&sio_cdevsw,
1338184610Salfred	    unit | CALLOUT_MASK | CONTROL_INIT_STATE,
1339184610Salfred	    UID_UUCP, GID_DIALER, 0660, "cuaia%r", unit);
1340184610Salfred	com->devs[5] = make_dev(&sio_cdevsw,
1341184610Salfred	    unit | CALLOUT_MASK | CONTROL_LOCK_STATE,
1342184610Salfred	    UID_UUCP, GID_DIALER, 0660, "cuala%r", unit);
1343184610Salfred	com->flags = flags;
1344188409Sthompsa	com->pps.ppscap = PPS_CAPTUREASSERT | PPS_CAPTURECLEAR;
1345184610Salfred	pps_init(&com->pps);
1346184610Salfred
1347184610Salfred	rid = 0;
1348184610Salfred	com->irqres = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0ul, ~0ul, 1,
1349184610Salfred	    RF_ACTIVE);
1350184824Sthompsa	if (com->irqres) {
1351184610Salfred		ret = BUS_SETUP_INTR(device_get_parent(dev), dev, com->irqres,
1352184610Salfred				     INTR_TYPE_TTY | INTR_FAST,
1353184610Salfred				     siointr, com, &com->cookie);
1354184610Salfred		if (ret) {
1355184610Salfred			ret = BUS_SETUP_INTR(device_get_parent(dev), dev,
1356184610Salfred					     com->irqres, INTR_TYPE_TTY,
1357186730Salfred					     siointr, com, &com->cookie);
1358184610Salfred			if (ret == 0)
1359184610Salfred				device_printf(dev, "unable to activate interrupt in fast mode - using normal mode");
1360184610Salfred		}
1361184610Salfred		if (ret)
1362184610Salfred			device_printf(dev, "could not activate interrupt\n");
1363184610Salfred	}
1364184610Salfred
1365184610Salfred	return (0);
1366184610Salfred}
1367184610Salfred
1368184610Salfredstatic int
1369184610Salfredsioopen(dev, flag, mode, p)
1370184610Salfred	dev_t		dev;
1371184610Salfred	int		flag;
1372184610Salfred	int		mode;
1373184610Salfred	struct proc	*p;
1374184610Salfred{
1375184610Salfred	struct com_s	*com;
1376184610Salfred	int		error;
1377184610Salfred	int		mynor;
1378184610Salfred	int		s;
1379184610Salfred	struct tty	*tp;
1380184610Salfred	int		unit;
1381184610Salfred
1382184610Salfred	mynor = minor(dev);
1383184610Salfred	unit = MINOR_TO_UNIT(mynor);
1384184610Salfred	com = com_addr(unit);
1385184610Salfred	if (com == NULL)
1386184610Salfred		return (ENXIO);
1387184610Salfred	if (com->gone)
1388184610Salfred		return (ENXIO);
1389184610Salfred	if (mynor & CONTROL_MASK)
1390184610Salfred		return (0);
1391184610Salfred	tp = dev->si_tty = com->tp = ttymalloc(com->tp);
1392184610Salfred	s = spltty();
1393184610Salfred	/*
1394184610Salfred	 * We jump to this label after all non-interrupted sleeps to pick
1395184610Salfred	 * up any changes of the device state.
1396184610Salfred	 */
1397184610Salfredopen_top:
1398184610Salfred	while (com->state & CS_DTR_OFF) {
1399184610Salfred		error = tsleep(&com->dtr_wait, TTIPRI | PCATCH, "siodtr", 0);
1400184610Salfred		if (com_addr(unit) == NULL)
1401184610Salfred			return (ENXIO);
1402184610Salfred		if (error != 0 || com->gone)
1403184610Salfred			goto out;
1404184610Salfred	}
1405184610Salfred	if (tp->t_state & TS_ISOPEN) {
1406184610Salfred		/*
1407184610Salfred		 * The device is open, so everything has been initialized.
1408184610Salfred		 * Handle conflicts.
1409184610Salfred		 */
1410184610Salfred		if (mynor & CALLOUT_MASK) {
1411184610Salfred			if (!com->active_out) {
1412184610Salfred				error = EBUSY;
1413184610Salfred				goto out;
1414184610Salfred			}
1415184610Salfred		} else {
1416184610Salfred			if (com->active_out) {
1417184610Salfred				if (flag & O_NONBLOCK) {
1418184610Salfred					error = EBUSY;
1419184610Salfred					goto out;
1420184610Salfred				}
1421184610Salfred				error =	tsleep(&com->active_out,
1422184610Salfred					       TTIPRI | PCATCH, "siobi", 0);
1423184610Salfred				if (com_addr(unit) == NULL)
1424184610Salfred					return (ENXIO);
1425184610Salfred				if (error != 0 || com->gone)
1426184610Salfred					goto out;
1427184610Salfred				goto open_top;
1428184610Salfred			}
1429184610Salfred		}
1430184610Salfred		if (tp->t_state & TS_XCLUDE &&
1431184610Salfred		    suser(p)) {
1432184610Salfred			error = EBUSY;
1433184610Salfred			goto out;
1434184610Salfred		}
1435184610Salfred	} else {
1436184610Salfred		int	intrsave;
1437184610Salfred
1438184610Salfred		/*
1439184610Salfred		 * The device isn't open, so there are no conflicts.
1440184610Salfred		 * Initialize it.  Initialization is done twice in many
1441184610Salfred		 * cases: to preempt sleeping callin opens if we are
1442184610Salfred		 * callout, and to complete a callin open after DCD rises.
1443184610Salfred		 */
1444184610Salfred		tp->t_oproc = comstart;
1445184610Salfred		tp->t_param = comparam;
1446184610Salfred		tp->t_stop = comstop;
1447184610Salfred		tp->t_dev = dev;
1448184610Salfred		tp->t_termios = mynor & CALLOUT_MASK
1449184610Salfred				? com->it_out : com->it_in;
1450184610Salfred		(void)commctl(com, TIOCM_DTR | TIOCM_RTS, DMSET);
1451184610Salfred		com->poll = com->no_irq;
1452184610Salfred		com->poll_output = com->loses_outints;
1453184610Salfred		++com->wopeners;
1454184610Salfred		error = comparam(tp, &tp->t_termios);
1455184610Salfred		--com->wopeners;
1456184610Salfred		if (error != 0)
1457184824Sthompsa			goto out;
1458184610Salfred		/*
1459184610Salfred		 * XXX we should goto open_top if comparam() slept.
1460184610Salfred		 */
1461184610Salfred		if (com->hasfifo) {
1462184610Salfred			/*
1463184610Salfred			 * (Re)enable and drain fifos.
1464184610Salfred			 *
1465184610Salfred			 * Certain SMC chips cause problems if the fifos
1466184610Salfred			 * are enabled while input is ready.  Turn off the
1467184610Salfred			 * fifo if necessary to clear the input.  We test
1468184610Salfred			 * the input ready bit after enabling the fifos
1469184610Salfred			 * since we've already enabled them in comparam()
1470184610Salfred			 * and to handle races between enabling and fresh
1471184824Sthompsa			 * input.
1472184610Salfred			 */
1473184610Salfred			while (TRUE) {
1474184610Salfred				sio_setreg(com, com_fifo,
1475184610Salfred					   FIFO_RCV_RST | FIFO_XMT_RST
1476184610Salfred					   | com->fifo_image);
1477184610Salfred				/*
1478184610Salfred				 * XXX the delays are for superstitious
1479184610Salfred				 * historical reasons.  It must be less than
1480184610Salfred				 * the character time at the maximum
1481184610Salfred				 * supported speed (87 usec at 115200 bps
1482184610Salfred				 * 8N1).  Otherwise we might loop endlessly
1483184610Salfred				 * if data is streaming in.  We used to use
1484184610Salfred				 * delays of 100.  That usually worked
1485184610Salfred				 * because DELAY(100) used to usually delay
1486184824Sthompsa				 * for about 85 usec instead of 100.
1487184610Salfred				 */
1488184610Salfred				DELAY(50);
1489184610Salfred				if (!(inb(com->line_status_port) & LSR_RXRDY))
1490184610Salfred					break;
1491184610Salfred				sio_setreg(com, com_fifo, 0);
1492184610Salfred				DELAY(50);
1493184610Salfred				(void) inb(com->data_port);
1494184610Salfred			}
1495184610Salfred		}
1496184610Salfred
1497184610Salfred		intrsave = save_intr();
1498184610Salfred		disable_intr();
1499184610Salfred		COM_LOCK();
1500184610Salfred		(void) inb(com->line_status_port);
1501184610Salfred		(void) inb(com->data_port);
1502192984Sthompsa		com->prev_modem_status = com->last_modem_status
1503184610Salfred		    = inb(com->modem_status_port);
1504184610Salfred		if (COM_IIR_TXRDYBUG(com->flags)) {
1505184610Salfred			outb(com->intr_ctl_port, IER_ERXRDY | IER_ERLS
1506184824Sthompsa						| IER_EMSC);
1507184610Salfred		} else {
1508184824Sthompsa			outb(com->intr_ctl_port, IER_ERXRDY | IER_ETXRDY
1509184610Salfred						| IER_ERLS | IER_EMSC);
1510184610Salfred		}
1511184610Salfred		COM_UNLOCK();
1512184610Salfred		restore_intr(intrsave);
1513184610Salfred		/*
1514184610Salfred		 * Handle initial DCD.  Callout devices get a fake initial
1515192984Sthompsa		 * DCD (trapdoor DCD).  If we are callout, then any sleeping
1516184610Salfred		 * callin opens get woken up and resume sleeping on "siobi"
1517184610Salfred		 * instead of "siodcd".
1518184610Salfred		 */
1519184610Salfred		/*
1520184610Salfred		 * XXX `mynor & CALLOUT_MASK' should be
1521192984Sthompsa		 * `tp->t_cflag & (SOFT_CARRIER | TRAPDOOR_CARRIER) where
1522184610Salfred		 * TRAPDOOR_CARRIER is the default initial state for callout
1523184610Salfred		 * devices and SOFT_CARRIER is like CLOCAL except it hides
1524184610Salfred		 * the true carrier.
1525184610Salfred		 */
1526184610Salfred		if (com->prev_modem_status & MSR_DCD || mynor & CALLOUT_MASK)
1527192984Sthompsa			(*linesw[tp->t_line].l_modem)(tp, 1);
1528184610Salfred	}
1529184610Salfred	/*
1530184610Salfred	 * Wait for DCD if necessary.
1531184610Salfred	 */
1532184610Salfred	if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK)
1533192984Sthompsa	    && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) {
1534184610Salfred		++com->wopeners;
1535184610Salfred		error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "siodcd", 0);
1536184610Salfred		if (com_addr(unit) == NULL)
1537184610Salfred			return (ENXIO);
1538184610Salfred		--com->wopeners;
1539184610Salfred		if (error != 0 || com->gone)
1540192984Sthompsa			goto out;
1541184610Salfred		goto open_top;
1542184610Salfred	}
1543184610Salfred	error =	(*linesw[tp->t_line].l_open)(dev, tp);
1544184610Salfred	disc_optim(tp, &tp->t_termios, com);
1545184610Salfred	if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK)
1546184610Salfred		com->active_out = TRUE;
1547184610Salfred	siosettimeout();
1548184610Salfredout:
1549184610Salfred	splx(s);
1550184610Salfred	if (!(tp->t_state & TS_ISOPEN) && com->wopeners == 0)
1551184610Salfred		comhardclose(com);
1552192984Sthompsa	return (error);
1553184610Salfred}
1554184610Salfred
1555184610Salfredstatic int
1556184610Salfredsioclose(dev, flag, mode, p)
1557184610Salfred	dev_t		dev;
1558192984Sthompsa	int		flag;
1559184610Salfred	int		mode;
1560184610Salfred	struct proc	*p;
1561184610Salfred{
1562184610Salfred	struct com_s	*com;
1563184610Salfred	int		mynor;
1564192984Sthompsa	int		s;
1565184610Salfred	struct tty	*tp;
1566184610Salfred
1567184610Salfred	mynor = minor(dev);
1568184610Salfred	if (mynor & CONTROL_MASK)
1569184610Salfred		return (0);
1570192984Sthompsa	com = com_addr(MINOR_TO_UNIT(mynor));
1571184610Salfred	if (com == NULL)
1572184610Salfred		return (ENODEV);
1573184610Salfred	tp = com->tp;
1574184610Salfred	s = spltty();
1575184610Salfred	(*linesw[tp->t_line].l_close)(tp, flag);
1576184610Salfred	disc_optim(tp, &tp->t_termios, com);
1577192984Sthompsa	comstop(tp, FREAD | FWRITE);
1578184610Salfred	comhardclose(com);
1579184610Salfred	ttyclose(tp);
1580184610Salfred	siosettimeout();
1581184610Salfred	splx(s);
1582184610Salfred	if (com->gone) {
1583184610Salfred		printf("sio%d: gone\n", com->unit);
1584184610Salfred		s = spltty();
1585184610Salfred		if (com->ibuf != NULL)
1586184610Salfred			free(com->ibuf, M_DEVBUF);
1587184610Salfred		bzero(tp, sizeof *tp);
1588184610Salfred		splx(s);
1589192984Sthompsa	}
1590184610Salfred	return (0);
1591184610Salfred}
1592184610Salfred
1593184610Salfredstatic void
1594184610Salfredcomhardclose(com)
1595192984Sthompsa	struct com_s	*com;
1596184610Salfred{
1597184610Salfred	int		s;
1598184610Salfred	struct tty	*tp;
1599184610Salfred	int		unit;
1600184610Salfred
1601192984Sthompsa	unit = com->unit;
1602184610Salfred	s = spltty();
1603184610Salfred	com->poll = FALSE;
1604184610Salfred	com->poll_output = FALSE;
1605184610Salfred	com->do_timestamp = FALSE;
1606184610Salfred	com->do_dcd_timestamp = FALSE;
1607192984Sthompsa	com->pps.ppsparam.mode = 0;
1608184610Salfred	sio_setreg(com, com_cfcr, com->cfcr_image &= ~CFCR_SBREAK);
1609184610Salfred	{
1610184610Salfred		sio_setreg(com, com_ier, 0);
1611184610Salfred		tp = com->tp;
1612184610Salfred		if (tp->t_cflag & HUPCL
1613184610Salfred		    /*
1614192984Sthompsa		     * XXX we will miss any carrier drop between here and the
1615184610Salfred		     * next open.  Perhaps we should watch DCD even when the
1616184610Salfred		     * port is closed; it is not sufficient to check it at
1617184610Salfred		     * the next open because it might go up and down while
1618184610Salfred		     * we're not watching.
1619184610Salfred		     */
1620184610Salfred		    || (!com->active_out
1621184610Salfred		        && !(com->prev_modem_status & MSR_DCD)
1622184610Salfred		        && !(com->it_in.c_cflag & CLOCAL))
1623184610Salfred		    || !(tp->t_state & TS_ISOPEN)) {
1624184610Salfred			(void)commctl(com, TIOCM_DTR, DMBIC);
1625184610Salfred			if (com->dtr_wait != 0 && !(com->state & CS_DTR_OFF)) {
1626192984Sthompsa				timeout(siodtrwakeup, com, com->dtr_wait);
1627184610Salfred				com->state |= CS_DTR_OFF;
1628184610Salfred			}
1629184610Salfred		}
1630184610Salfred	}
1631184610Salfred	if (com->hasfifo) {
1632192984Sthompsa		/*
1633184610Salfred		 * Disable fifos so that they are off after controlled
1634184610Salfred		 * reboots.  Some BIOSes fail to detect 16550s when the
1635184610Salfred		 * fifos are enabled.
1636184610Salfred		 */
1637184610Salfred		sio_setreg(com, com_fifo, 0);
1638192984Sthompsa	}
1639184610Salfred	com->active_out = FALSE;
1640187173Sthompsa	wakeup(&com->active_out);
1641184610Salfred	wakeup(TSA_CARR_ON(tp));	/* restart any wopeners */
1642184610Salfred	splx(s);
1643184610Salfred}
1644184610Salfred
1645184610Salfredstatic int
1646184610Salfredsioread(dev, uio, flag)
1647184610Salfred	dev_t		dev;
1648184610Salfred	struct uio	*uio;
1649184610Salfred	int		flag;
1650184610Salfred{
1651184610Salfred	int		mynor;
1652184610Salfred	struct com_s	*com;
1653184610Salfred
1654184610Salfred	mynor = minor(dev);
1655184610Salfred	if (mynor & CONTROL_MASK)
1656184610Salfred		return (ENODEV);
1657184610Salfred	com = com_addr(MINOR_TO_UNIT(mynor));
1658184610Salfred	if (com == NULL || com->gone)
1659184610Salfred		return (ENODEV);
1660184610Salfred	return ((*linesw[com->tp->t_line].l_read)(com->tp, uio, flag));
1661184610Salfred}
1662184610Salfred
1663184610Salfredstatic int
1664184610Salfredsiowrite(dev, uio, flag)
1665184610Salfred	dev_t		dev;
1666184610Salfred	struct uio	*uio;
1667184610Salfred	int		flag;
1668184610Salfred{
1669184610Salfred	int		mynor;
1670184610Salfred	struct com_s	*com;
1671184610Salfred	int		unit;
1672184610Salfred
1673184610Salfred	mynor = minor(dev);
1674184610Salfred	if (mynor & CONTROL_MASK)
1675184610Salfred		return (ENODEV);
1676184610Salfred
1677184610Salfred	unit = MINOR_TO_UNIT(mynor);
1678184610Salfred	com = com_addr(unit);
1679184610Salfred	if (com == NULL || com->gone)
1680184610Salfred		return (ENODEV);
1681184610Salfred	/*
1682184610Salfred	 * (XXX) We disallow virtual consoles if the physical console is
1683184610Salfred	 * a serial port.  This is in case there is a display attached that
1684184610Salfred	 * is not the console.  In that situation we don't need/want the X
1685184610Salfred	 * server taking over the console.
1686184610Salfred	 */
1687184610Salfred	if (constty != NULL && unit == comconsole)
1688184610Salfred		constty = NULL;
1689184610Salfred	return ((*linesw[com->tp->t_line].l_write)(com->tp, uio, flag));
1690192984Sthompsa}
1691184610Salfred
1692184610Salfredstatic void
1693184610Salfredsiobusycheck(chan)
1694184610Salfred	void	*chan;
1695184610Salfred{
1696192984Sthompsa	struct com_s	*com;
1697184610Salfred	int		s;
1698184610Salfred
1699184610Salfred	com = (struct com_s *)chan;
1700184610Salfred
1701184610Salfred	/*
1702184610Salfred	 * Clear TS_BUSY if low-level output is complete.
1703184610Salfred	 * spl locking is sufficient because siointr1() does not set CS_BUSY.
1704184610Salfred	 * If siointr1() clears CS_BUSY after we look at it, then we'll get
1705184610Salfred	 * called again.  Reading the line status port outside of siointr1()
1706184610Salfred	 * is safe because CS_BUSY is clear so there are no output interrupts
1707190735Sthompsa	 * to lose.
1708184610Salfred	 */
1709184610Salfred	s = spltty();
1710192984Sthompsa	if (com->state & CS_BUSY)
1711192984Sthompsa		com->extra_state &= ~CSE_BUSYCHECK;	/* False alarm. */
1712184610Salfred	else if ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY))
1713184610Salfred	    == (LSR_TSRE | LSR_TXRDY)) {
1714184610Salfred		com->tp->t_state &= ~TS_BUSY;
1715184610Salfred		ttwwakeup(com->tp);
1716184610Salfred		com->extra_state &= ~CSE_BUSYCHECK;
1717184610Salfred	} else
1718184610Salfred		timeout(siobusycheck, com, hz / 100);
1719184610Salfred	splx(s);
1720184610Salfred}
1721184610Salfred
1722184610Salfredstatic void
1723184610Salfredsiodtrwakeup(chan)
1724192984Sthompsa	void	*chan;
1725192984Sthompsa{
1726184610Salfred	struct com_s	*com;
1727184610Salfred
1728184610Salfred	com = (struct com_s *)chan;
1729184610Salfred	com->state &= ~CS_DTR_OFF;
1730184610Salfred	wakeup(&com->dtr_wait);
1731184610Salfred}
1732184610Salfred
1733184610Salfred/*
1734184610Salfred * Call this function with COM_LOCK.  It will return with the lock still held.
1735184610Salfred */
1736184610Salfredstatic void
1737192984Sthompsasioinput(com)
1738184610Salfred	struct com_s	*com;
1739184610Salfred{
1740184610Salfred	u_char		*buf;
1741184610Salfred	int		incc;
1742184610Salfred	u_char		line_status;
1743184610Salfred	int		recv_data;
1744184610Salfred	struct tty	*tp;
1745184610Salfred	int		intrsave;
1746184610Salfred
1747192984Sthompsa	buf = com->ibuf;
1748184610Salfred	tp = com->tp;
1749184610Salfred	if (!(tp->t_state & TS_ISOPEN) || !(tp->t_cflag & CREAD)) {
1750184610Salfred		com_events -= (com->iptr - com->ibuf);
1751184610Salfred		com->iptr = com->ibuf;
1752184610Salfred		return;
1753184610Salfred	}
1754184610Salfred	if (tp->t_state & TS_CAN_BYPASS_L_RINT) {
1755184610Salfred		/*
1756192984Sthompsa		 * Avoid the grotesquely inefficient lineswitch routine
1757184610Salfred		 * (ttyinput) in "raw" mode.  It usually takes about 450
1758184610Salfred		 * instructions (that's without canonical processing or echo!).
1759184610Salfred		 * slinput is reasonably fast (usually 40 instructions plus
1760184610Salfred		 * call overhead).
1761184610Salfred		 */
1762184610Salfred		do {
1763184610Salfred			/*
1764184610Salfred			 * This may look odd, but it is using save-and-enable
1765192984Sthompsa			 * semantics instead of the save-and-disable semantics
1766184610Salfred			 * that are used everywhere else.
1767184610Salfred			 */
1768184610Salfred			intrsave = save_intr();
1769184610Salfred			COM_UNLOCK();
1770184610Salfred			enable_intr();
1771184610Salfred			incc = com->iptr - buf;
1772187183Sthompsa			if (tp->t_rawq.c_cc + incc > tp->t_ihiwat
1773184610Salfred			    && (com->state & CS_RTS_IFLOW
1774184610Salfred				|| tp->t_iflag & IXOFF)
1775184610Salfred			    && !(tp->t_state & TS_TBLOCK))
1776184610Salfred				ttyblock(tp);
1777184610Salfred			com->delta_error_counts[CE_TTY_BUF_OVERFLOW]
1778184610Salfred				+= b_to_q((char *)buf, incc, &tp->t_rawq);
1779184610Salfred			buf += incc;
1780184610Salfred			tk_nin += incc;
1781184610Salfred			tk_rawcc += incc;
1782184610Salfred			tp->t_rawcc += incc;
1783184610Salfred			ttwakeup(tp);
1784184610Salfred			if (tp->t_state & TS_TTSTOP
1785184610Salfred			    && (tp->t_iflag & IXANY
1786184610Salfred				|| tp->t_cc[VSTART] == tp->t_cc[VSTOP])) {
1787184610Salfred				tp->t_state &= ~TS_TTSTOP;
1788184610Salfred				tp->t_lflag &= ~FLUSHO;
1789184610Salfred				comstart(tp);
1790184610Salfred			}
1791184610Salfred			restore_intr(intrsave);
1792184610Salfred			COM_LOCK();
1793191402Sthompsa		} while (buf < com->iptr);
1794192984Sthompsa	} else {
1795192984Sthompsa		do {
1796184610Salfred			/*
1797191402Sthompsa			 * This may look odd, but it is using save-and-enable
1798191402Sthompsa			 * semantics instead of the save-and-disable semantics
1799191402Sthompsa			 * that are used everywhere else.
1800184610Salfred			 */
1801184610Salfred			intrsave = save_intr();
1802191402Sthompsa			COM_UNLOCK();
1803184610Salfred			enable_intr();
1804184824Sthompsa			line_status = buf[com->ierroff];
1805184610Salfred			recv_data = *buf++;
1806184610Salfred			if (line_status
1807191402Sthompsa			    & (LSR_BI | LSR_FE | LSR_OE | LSR_PE)) {
1808191402Sthompsa				if (line_status & LSR_BI)
1809191402Sthompsa					recv_data |= TTY_BI;
1810184610Salfred				if (line_status & LSR_FE)
1811191402Sthompsa					recv_data |= TTY_FE;
1812191402Sthompsa				if (line_status & LSR_OE)
1813184610Salfred					recv_data |= TTY_OE;
1814184610Salfred				if (line_status & LSR_PE)
1815184610Salfred					recv_data |= TTY_PE;
1816191402Sthompsa			}
1817184610Salfred			(*linesw[tp->t_line].l_rint)(recv_data, tp);
1818191402Sthompsa			restore_intr(intrsave);
1819184610Salfred			COM_LOCK();
1820184610Salfred		} while (buf < com->iptr);
1821184610Salfred	}
1822184610Salfred	com_events -= (com->iptr - com->ibuf);
1823184610Salfred	com->iptr = com->ibuf;
1824184610Salfred
1825184610Salfred	/*
1826184610Salfred	 * There is now room for another low-level buffer full of input,
1827184610Salfred	 * so enable RTS if it is now disabled and there is room in the
1828184610Salfred	 * high-level buffer.
1829184610Salfred	 */
1830184610Salfred	if ((com->state & CS_RTS_IFLOW) && !(com->mcr_image & MCR_RTS) &&
1831191402Sthompsa	    !(tp->t_state & TS_TBLOCK))
1832184610Salfred		outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS);
1833184610Salfred}
1834184610Salfred
1835184610Salfredvoid
1836184610Salfredsiointr(arg)
1837184610Salfred	void		*arg;
1838184610Salfred{
1839184610Salfred#ifndef COM_MULTIPORT
1840184610Salfred	COM_LOCK();
1841184610Salfred	siointr1((struct com_s *) arg);
1842184610Salfred	COM_UNLOCK();
1843184610Salfred#else /* COM_MULTIPORT */
1844184610Salfred	bool_t		possibly_more_intrs;
1845184610Salfred	int		unit;
1846184610Salfred	struct com_s	*com;
1847191402Sthompsa
1848184610Salfred	/*
1849191402Sthompsa	 * Loop until there is no activity on any port.  This is necessary
1850184610Salfred	 * to get an interrupt edge more than to avoid another interrupt.
1851184610Salfred	 * If the IRQ signal is just an OR of the IRQ signals from several
1852184610Salfred	 * devices, then the edge from one may be lost because another is
1853184610Salfred	 * on.
1854184610Salfred	 */
1855184610Salfred	COM_LOCK();
1856184610Salfred	do {
1857184610Salfred		possibly_more_intrs = FALSE;
1858184610Salfred		for (unit = 0; unit < sio_numunits; ++unit) {
1859191402Sthompsa			com = com_addr(unit);
1860184610Salfred			/*
1861184610Salfred			 * XXX COM_LOCK();
1862184610Salfred			 * would it work here, or be counter-productive?
1863184610Salfred			 */
1864184610Salfred			if (com != NULL
1865184610Salfred			    && !com->gone
1866184610Salfred			    && (inb(com->int_id_port) & IIR_IMASK)
1867184610Salfred			       != IIR_NOPEND) {
1868184610Salfred				siointr1(com);
1869184610Salfred				possibly_more_intrs = TRUE;
1870184610Salfred			}
1871184610Salfred			/* XXX COM_UNLOCK(); */
1872184610Salfred		}
1873184610Salfred	} while (possibly_more_intrs);
1874184610Salfred	COM_UNLOCK();
1875184610Salfred#endif /* COM_MULTIPORT */
1876191402Sthompsa}
1877184610Salfred
1878184610Salfredstatic void
1879184610Salfredsiointr1(com)
1880184610Salfred	struct com_s	*com;
1881184610Salfred{
1882184610Salfred	u_char	line_status;
1883184610Salfred	u_char	modem_status;
1884184610Salfred	u_char	*ioptr;
1885191402Sthompsa	u_char	recv_data;
1886184610Salfred	u_char	int_ctl;
1887184610Salfred	u_char	int_ctl_new;
1888184610Salfred	struct	timecounter *tc;
1889184610Salfred	u_int	count;
1890184610Salfred
1891184610Salfred	int_ctl = inb(com->intr_ctl_port);
1892184610Salfred	int_ctl_new = int_ctl;
1893184610Salfred
1894184610Salfred	while (!com->gone) {
1895184610Salfred		if (com->pps.ppsparam.mode & PPS_CAPTUREBOTH) {
1896184610Salfred			modem_status = inb(com->modem_status_port);
1897191402Sthompsa		        if ((modem_status ^ com->last_modem_status) & MSR_DCD) {
1898184610Salfred				tc = timecounter;
1899184610Salfred				count = tc->tc_get_timecount(tc);
1900184610Salfred				pps_event(&com->pps, tc, count,
1901184610Salfred				    (modem_status & MSR_DCD) ?
1902184610Salfred				    PPS_CAPTUREASSERT : PPS_CAPTURECLEAR);
1903184610Salfred			}
1904184610Salfred		}
1905184610Salfred		line_status = inb(com->line_status_port);
1906184610Salfred
1907184610Salfred		/* input event? (check first to help avoid overruns) */
1908184610Salfred		while (line_status & LSR_RCV_MASK) {
1909184610Salfred			/* break/unnattached error bits or real input? */
1910184610Salfred			if (!(line_status & LSR_RXRDY))
1911184610Salfred				recv_data = 0;
1912184610Salfred			else
1913184610Salfred				recv_data = inb(com->data_port);
1914184610Salfred#if defined(DDB) && defined(ALT_BREAK_TO_DEBUGGER)
1915184610Salfred			/*
1916184610Salfred			 * Solaris implements a new BREAK which is initiated
1917184610Salfred			 * by a character sequence CR ~ ^b which is similar
1918191402Sthompsa			 * to a familiar pattern used on Sun servers by the
1919184610Salfred			 * Remote Console.
1920184610Salfred			 */
1921184610Salfred#define	KEY_CRTLB	2	/* ^B */
1922184610Salfred#define	KEY_CR		13	/* CR '\r' */
1923184610Salfred#define	KEY_TILDE	126	/* ~ */
1924184610Salfred
1925184610Salfred			if (com->unit == comconsole) {
1926184610Salfred				static int brk_state1 = 0, brk_state2 = 0;
1927184610Salfred				if (recv_data == KEY_CR) {
1928184610Salfred					brk_state1 = recv_data;
1929184610Salfred					brk_state2 = 0;
1930191402Sthompsa				} else if (brk_state1 == KEY_CR
1931184610Salfred					   && (recv_data == KEY_TILDE
1932184610Salfred					       || recv_data == KEY_CRTLB)) {
1933184610Salfred					if (recv_data == KEY_TILDE)
1934184610Salfred						brk_state2 = recv_data;
1935184610Salfred					else if (brk_state2 == KEY_TILDE
1936184610Salfred						 && recv_data == KEY_CRTLB) {
1937184610Salfred							breakpoint();
1938184610Salfred							brk_state1 = 0;
1939184610Salfred							brk_state2 = 0;
1940184610Salfred							goto cont;
1941184610Salfred					} else
1942184610Salfred						brk_state2 = 0;
1943184610Salfred				} else
1944184610Salfred					brk_state1 = 0;
1945184610Salfred			}
1946191402Sthompsa#endif
1947184610Salfred			if (line_status & (LSR_BI | LSR_FE | LSR_PE)) {
1948184610Salfred				/*
1949184610Salfred				 * Don't store BI if IGNBRK or FE/PE if IGNPAR.
1950184610Salfred				 * Otherwise, push the work to a higher level
1951184610Salfred				 * (to handle PARMRK) if we're bypassing.
1952184610Salfred				 * Otherwise, convert BI/FE and PE+INPCK to 0.
1953184610Salfred				 *
1954184610Salfred				 * This makes bypassing work right in the
1955184610Salfred				 * usual "raw" case (IGNBRK set, and IGNPAR
1956184610Salfred				 * and INPCK clear).
1957191402Sthompsa				 *
1958184610Salfred				 * Note: BI together with FE/PE means just BI.
1959184610Salfred				 */
1960184610Salfred				if (line_status & LSR_BI) {
1961184610Salfred#if defined(DDB) && defined(BREAK_TO_DEBUGGER)
1962184610Salfred					if (com->unit == comconsole) {
1963184610Salfred						breakpoint();
1964184610Salfred						goto cont;
1965184610Salfred					}
1966184610Salfred#endif
1967184610Salfred					if (com->tp == NULL
1968184610Salfred					    || com->tp->t_iflag & IGNBRK)
1969184610Salfred						goto cont;
1970184610Salfred				} else {
1971184610Salfred					if (com->tp == NULL
1972184610Salfred					    || com->tp->t_iflag & IGNPAR)
1973184610Salfred						goto cont;
1974184610Salfred				}
1975184610Salfred				if (com->tp->t_state & TS_CAN_BYPASS_L_RINT
1976184610Salfred				    && (line_status & (LSR_BI | LSR_FE)
1977184610Salfred					|| com->tp->t_iflag & INPCK))
1978191402Sthompsa					recv_data = 0;
1979191402Sthompsa			}
1980184610Salfred			++com->bytes_in;
1981184610Salfred			if (com->hotchar != 0 && recv_data == com->hotchar)
1982184610Salfred				setsofttty();
1983184610Salfred			ioptr = com->iptr;
1984184610Salfred			if (ioptr >= com->ibufend)
1985191402Sthompsa				CE_RECORD(com, CE_INTERRUPT_BUF_OVERFLOW);
1986191402Sthompsa			else {
1987184610Salfred				if (com->do_timestamp)
1988184610Salfred					microtime(&com->timestamp);
1989184610Salfred				++com_events;
1990184610Salfred				schedsofttty();
1991191402Sthompsa#if 0 /* for testing input latency vs efficiency */
1992191402Sthompsaif (com->iptr - com->ibuf == 8)
1993184610Salfred	setsofttty();
1994184610Salfred#endif
1995184610Salfred				ioptr[0] = recv_data;
1996191402Sthompsa				ioptr[com->ierroff] = line_status;
1997191402Sthompsa				com->iptr = ++ioptr;
1998184610Salfred				if (ioptr == com->ihighwater
1999184610Salfred				    && com->state & CS_RTS_IFLOW)
2000184610Salfred					outb(com->modem_ctl_port,
2001191402Sthompsa					     com->mcr_image &= ~MCR_RTS);
2002191402Sthompsa				if (line_status & LSR_OE)
2003184610Salfred					CE_RECORD(com, CE_OVERRUN);
2004184610Salfred			}
2005184610Salfredcont:
2006184610Salfred			/*
2007184610Salfred			 * "& 0x7F" is to avoid the gcc-1.40 generating a slow
2008184610Salfred			 * jump from the top of the loop to here
2009184610Salfred			 */
2010184610Salfred			line_status = inb(com->line_status_port) & 0x7F;
2011184610Salfred		}
2012184610Salfred
2013184610Salfred		/* modem status change? (always check before doing output) */
2014191402Sthompsa		modem_status = inb(com->modem_status_port);
2015184610Salfred		if (modem_status != com->last_modem_status) {
2016184610Salfred			if (com->do_dcd_timestamp
2017184610Salfred			    && !(com->last_modem_status & MSR_DCD)
2018184610Salfred			    && modem_status & MSR_DCD)
2019191402Sthompsa				microtime(&com->dcd_timestamp);
2020184610Salfred
2021184610Salfred			/*
2022184610Salfred			 * Schedule high level to handle DCD changes.  Note
2023184610Salfred			 * that we don't use the delta bits anywhere.  Some
2024184610Salfred			 * UARTs mess them up, and it's easy to remember the
2025184610Salfred			 * previous bits and calculate the delta.
2026184610Salfred			 */
2027184610Salfred			com->last_modem_status = modem_status;
2028184610Salfred			if (!(com->state & CS_CHECKMSR)) {
2029184610Salfred				com_events += LOTS_OF_EVENTS;
2030184610Salfred				com->state |= CS_CHECKMSR;
2031184610Salfred				setsofttty();
2032184610Salfred			}
2033184610Salfred
2034184610Salfred			/* handle CTS change immediately for crisp flow ctl */
2035184610Salfred			if (com->state & CS_CTS_OFLOW) {
2036184610Salfred				if (modem_status & MSR_CTS)
2037184610Salfred					com->state |= CS_ODEVREADY;
2038191402Sthompsa				else
2039184610Salfred					com->state &= ~CS_ODEVREADY;
2040184610Salfred			}
2041184610Salfred		}
2042184610Salfred
2043184610Salfred		/* output queued and everything ready? */
2044184610Salfred		if (line_status & LSR_TXRDY
2045184610Salfred		    && com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) {
2046191402Sthompsa			ioptr = com->obufq.l_head;
2047184610Salfred			if (com->tx_fifo_size > 1) {
2048184610Salfred				u_int	ocount;
2049184610Salfred
2050184610Salfred				ocount = com->obufq.l_tail - ioptr;
2051184610Salfred				if (ocount > com->tx_fifo_size)
2052184610Salfred					ocount = com->tx_fifo_size;
2053184610Salfred				com->bytes_out += ocount;
2054184610Salfred				do
2055184610Salfred					outb(com->data_port, *ioptr++);
2056184610Salfred				while (--ocount != 0);
2057184610Salfred			} else {
2058184610Salfred				outb(com->data_port, *ioptr++);
2059184610Salfred				++com->bytes_out;
2060184610Salfred			}
2061184610Salfred			com->obufq.l_head = ioptr;
2062184610Salfred			if (COM_IIR_TXRDYBUG(com->flags)) {
2063184610Salfred				int_ctl_new = int_ctl | IER_ETXRDY;
2064184610Salfred			}
2065184610Salfred			if (ioptr >= com->obufq.l_tail) {
2066184610Salfred				struct lbq	*qp;
2067184610Salfred
2068184610Salfred				qp = com->obufq.l_next;
2069184610Salfred				qp->l_queued = FALSE;
2070184610Salfred				qp = qp->l_next;
2071184610Salfred				if (qp != NULL) {
2072184610Salfred					com->obufq.l_head = qp->l_head;
2073184610Salfred					com->obufq.l_tail = qp->l_tail;
2074184610Salfred					com->obufq.l_next = qp;
2075184610Salfred				} else {
2076184610Salfred					/* output just completed */
2077184610Salfred					if (COM_IIR_TXRDYBUG(com->flags)) {
2078184610Salfred						int_ctl_new = int_ctl & ~IER_ETXRDY;
2079184610Salfred					}
2080184610Salfred					com->state &= ~CS_BUSY;
2081184610Salfred				}
2082184610Salfred				if (!(com->state & CS_ODONE)) {
2083184610Salfred					com_events += LOTS_OF_EVENTS;
2084184610Salfred					com->state |= CS_ODONE;
2085184610Salfred					setsofttty();	/* handle at high level ASAP */
2086184610Salfred				}
2087184610Salfred			}
2088184610Salfred			if (COM_IIR_TXRDYBUG(com->flags) && (int_ctl != int_ctl_new)) {
2089184610Salfred				outb(com->intr_ctl_port, int_ctl_new);
2090191402Sthompsa			}
2091184610Salfred		}
2092184610Salfred
2093184610Salfred		/* finished? */
2094184610Salfred#ifndef COM_MULTIPORT
2095184610Salfred		if ((inb(com->int_id_port) & IIR_IMASK) == IIR_NOPEND)
2096184610Salfred#endif /* COM_MULTIPORT */
2097184610Salfred			return;
2098184610Salfred	}
2099184610Salfred}
2100184610Salfred
2101184610Salfredstatic int
2102184610Salfredsioioctl(dev, cmd, data, flag, p)
2103184610Salfred	dev_t		dev;
2104184610Salfred	u_long		cmd;
2105184610Salfred	caddr_t		data;
2106184610Salfred	int		flag;
2107184610Salfred	struct proc	*p;
2108184610Salfred{
2109184610Salfred	struct com_s	*com;
2110184610Salfred	int		error;
2111184610Salfred	int		mynor;
2112184610Salfred	int		s;
2113184610Salfred	struct tty	*tp;
2114184610Salfred#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
2115191402Sthompsa	u_long		oldcmd;
2116184610Salfred	struct termios	term;
2117184610Salfred#endif
2118184610Salfred
2119184610Salfred	mynor = minor(dev);
2120184610Salfred	com = com_addr(MINOR_TO_UNIT(mynor));
2121184610Salfred	if (com == NULL || com->gone)
2122184610Salfred		return (ENODEV);
2123184610Salfred	if (mynor & CONTROL_MASK) {
2124184610Salfred		struct termios	*ct;
2125184610Salfred
2126184610Salfred		switch (mynor & CONTROL_MASK) {
2127184610Salfred		case CONTROL_INIT_STATE:
2128184610Salfred			ct = mynor & CALLOUT_MASK ? &com->it_out : &com->it_in;
2129184610Salfred			break;
2130184610Salfred		case CONTROL_LOCK_STATE:
2131184610Salfred			ct = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in;
2132184610Salfred			break;
2133184610Salfred		default:
2134184610Salfred			return (ENODEV);	/* /dev/nodev */
2135184610Salfred		}
2136184610Salfred		switch (cmd) {
2137184610Salfred		case TIOCSETA:
2138184610Salfred			error = suser(p);
2139184610Salfred			if (error != 0)
2140184610Salfred				return (error);
2141184610Salfred			*ct = *(struct termios *)data;
2142184610Salfred			return (0);
2143184610Salfred		case TIOCGETA:
2144184610Salfred			*(struct termios *)data = *ct;
2145184610Salfred			return (0);
2146184610Salfred		case TIOCGETD:
2147184610Salfred			*(int *)data = TTYDISC;
2148184610Salfred			return (0);
2149184610Salfred		case TIOCGWINSZ:
2150184610Salfred			bzero(data, sizeof(struct winsize));
2151184610Salfred			return (0);
2152184610Salfred		default:
2153184610Salfred			return (ENOTTY);
2154184610Salfred		}
2155184610Salfred	}
2156184610Salfred	tp = com->tp;
2157184610Salfred#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
2158184610Salfred	term = tp->t_termios;
2159184610Salfred	oldcmd = cmd;
2160184610Salfred	error = ttsetcompat(tp, &cmd, data, &term);
2161191402Sthompsa	if (error != 0)
2162184610Salfred		return (error);
2163184610Salfred	if (cmd != oldcmd)
2164184610Salfred		data = (caddr_t)&term;
2165184610Salfred#endif
2166184610Salfred	if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) {
2167184610Salfred		int	cc;
2168191402Sthompsa		struct termios *dt = (struct termios *)data;
2169191402Sthompsa		struct termios *lt = mynor & CALLOUT_MASK
2170184610Salfred				     ? &com->lt_out : &com->lt_in;
2171184610Salfred
2172184610Salfred		dt->c_iflag = (tp->t_iflag & lt->c_iflag)
2173191402Sthompsa			      | (dt->c_iflag & ~lt->c_iflag);
2174184610Salfred		dt->c_oflag = (tp->t_oflag & lt->c_oflag)
2175184610Salfred			      | (dt->c_oflag & ~lt->c_oflag);
2176191402Sthompsa		dt->c_cflag = (tp->t_cflag & lt->c_cflag)
2177191402Sthompsa			      | (dt->c_cflag & ~lt->c_cflag);
2178191402Sthompsa		dt->c_lflag = (tp->t_lflag & lt->c_lflag)
2179184610Salfred			      | (dt->c_lflag & ~lt->c_lflag);
2180184610Salfred		for (cc = 0; cc < NCCS; ++cc)
2181184610Salfred			if (lt->c_cc[cc] != 0)
2182192984Sthompsa				dt->c_cc[cc] = tp->t_cc[cc];
2183184610Salfred		if (lt->c_ispeed != 0)
2184192984Sthompsa			dt->c_ispeed = tp->t_ispeed;
2185184610Salfred		if (lt->c_ospeed != 0)
2186192984Sthompsa			dt->c_ospeed = tp->t_ospeed;
2187184610Salfred	}
2188184610Salfred	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
2189184610Salfred	if (error != ENOIOCTL)
2190184610Salfred		return (error);
2191184610Salfred	s = spltty();
2192184610Salfred	error = ttioctl(tp, cmd, data, flag);
2193184610Salfred	disc_optim(tp, &tp->t_termios, com);
2194184610Salfred	if (error != ENOIOCTL) {
2195184610Salfred		splx(s);
2196184610Salfred		return (error);
2197184610Salfred	}
2198184610Salfred	switch (cmd) {
2199184610Salfred	case TIOCSBRK:
2200184610Salfred		sio_setreg(com, com_cfcr, com->cfcr_image |= CFCR_SBREAK);
2201184610Salfred		break;
2202184610Salfred	case TIOCCBRK:
2203184610Salfred		sio_setreg(com, com_cfcr, com->cfcr_image &= ~CFCR_SBREAK);
2204184610Salfred		break;
2205184610Salfred	case TIOCSDTR:
2206184610Salfred		(void)commctl(com, TIOCM_DTR, DMBIS);
2207184610Salfred		break;
2208184610Salfred	case TIOCCDTR:
2209184610Salfred		(void)commctl(com, TIOCM_DTR, DMBIC);
2210184610Salfred		break;
2211184610Salfred	/*
2212184610Salfred	 * XXX should disallow changing MCR_RTS if CS_RTS_IFLOW is set.  The
2213184610Salfred	 * changes get undone on the next call to comparam().
2214184610Salfred	 */
2215184610Salfred	case TIOCMSET:
2216184610Salfred		(void)commctl(com, *(int *)data, DMSET);
2217184610Salfred		break;
2218184610Salfred	case TIOCMBIS:
2219184610Salfred		(void)commctl(com, *(int *)data, DMBIS);
2220184610Salfred		break;
2221184610Salfred	case TIOCMBIC:
2222184610Salfred		(void)commctl(com, *(int *)data, DMBIC);
2223184610Salfred		break;
2224184610Salfred	case TIOCMGET:
2225184610Salfred		*(int *)data = commctl(com, 0, DMGET);
2226184610Salfred		break;
2227184610Salfred	case TIOCMSDTRWAIT:
2228184610Salfred		/* must be root since the wait applies to following logins */
2229184610Salfred		error = suser(p);
2230184610Salfred		if (error != 0) {
2231184610Salfred			splx(s);
2232184610Salfred			return (error);
2233184610Salfred		}
2234184610Salfred		com->dtr_wait = *(int *)data * hz / 100;
2235184610Salfred		break;
2236184610Salfred	case TIOCMGDTRWAIT:
2237184610Salfred		*(int *)data = com->dtr_wait * 100 / hz;
2238184610Salfred		break;
2239184610Salfred	case TIOCTIMESTAMP:
2240184610Salfred		com->do_timestamp = TRUE;
2241184610Salfred		*(struct timeval *)data = com->timestamp;
2242184610Salfred		break;
2243184610Salfred	case TIOCDCDTIMESTAMP:
2244184610Salfred		com->do_dcd_timestamp = TRUE;
2245184610Salfred		*(struct timeval *)data = com->dcd_timestamp;
2246184610Salfred		break;
2247184610Salfred	default:
2248184610Salfred		splx(s);
2249184610Salfred		error = pps_ioctl(cmd, data, &com->pps);
2250184610Salfred		if (error == ENODEV)
2251184610Salfred			error = ENOTTY;
2252184610Salfred		return (error);
2253184610Salfred	}
2254184610Salfred	splx(s);
2255184610Salfred	return (0);
2256184610Salfred}
2257184610Salfred
2258184610Salfred/* software interrupt handler for SWI_TTY */
2259184610Salfredstatic void
2260184610Salfredsiopoll()
2261184610Salfred{
2262184610Salfred	int		unit;
2263184610Salfred	int		intrsave;
2264184610Salfred
2265184610Salfred	if (com_events == 0)
2266184610Salfred		return;
2267184610Salfredrepeat:
2268184610Salfred	for (unit = 0; unit < sio_numunits; ++unit) {
2269184610Salfred		struct com_s	*com;
2270184610Salfred		int		incc;
2271184610Salfred		struct tty	*tp;
2272184610Salfred
2273184610Salfred		com = com_addr(unit);
2274184610Salfred		if (com == NULL)
2275184610Salfred			continue;
2276184610Salfred		tp = com->tp;
2277184610Salfred		if (tp == NULL || com->gone) {
2278184610Salfred			/*
2279184610Salfred			 * Discard any events related to never-opened or
2280184610Salfred			 * going-away devices.
2281184610Salfred			 */
2282184610Salfred			intrsave = save_intr();
2283184610Salfred			disable_intr();
2284184610Salfred			COM_LOCK();
2285184610Salfred			incc = com->iptr - com->ibuf;
2286184610Salfred			com->iptr = com->ibuf;
2287184610Salfred			if (com->state & CS_CHECKMSR) {
2288184610Salfred				incc += LOTS_OF_EVENTS;
2289184610Salfred				com->state &= ~CS_CHECKMSR;
2290192984Sthompsa			}
2291184610Salfred			com_events -= incc;
2292184610Salfred			COM_UNLOCK();
2293184610Salfred			restore_intr(intrsave);
2294184610Salfred			continue;
2295184610Salfred		}
2296192984Sthompsa		if (com->iptr != com->ibuf) {
2297192984Sthompsa			intrsave = save_intr();
2298184610Salfred			disable_intr();
2299184610Salfred			COM_LOCK();
2300184610Salfred			sioinput(com);
2301184610Salfred			COM_UNLOCK();
2302184610Salfred			restore_intr(intrsave);
2303192499Sthompsa		}
2304184610Salfred		if (com->state & CS_CHECKMSR) {
2305184610Salfred			u_char	delta_modem_status;
2306190735Sthompsa
2307184610Salfred			intrsave = save_intr();
2308192499Sthompsa			disable_intr();
2309184610Salfred			COM_LOCK();
2310184610Salfred			delta_modem_status = com->last_modem_status
2311184610Salfred					     ^ com->prev_modem_status;
2312184610Salfred			com->prev_modem_status = com->last_modem_status;
2313184610Salfred			com_events -= LOTS_OF_EVENTS;
2314184610Salfred			com->state &= ~CS_CHECKMSR;
2315184610Salfred			COM_UNLOCK();
2316184610Salfred			restore_intr(intrsave);
2317184610Salfred			if (delta_modem_status & MSR_DCD)
2318184610Salfred				(*linesw[tp->t_line].l_modem)
2319184610Salfred					(tp, com->prev_modem_status & MSR_DCD);
2320184610Salfred		}
2321184610Salfred		if (com->state & CS_ODONE) {
2322184610Salfred			intrsave = save_intr();
2323184610Salfred			disable_intr();
2324184610Salfred			COM_LOCK();
2325184610Salfred			com_events -= LOTS_OF_EVENTS;
2326184610Salfred			com->state &= ~CS_ODONE;
2327184610Salfred			COM_UNLOCK();
2328184610Salfred			restore_intr(intrsave);
2329184610Salfred			if (!(com->state & CS_BUSY)
2330184610Salfred			    && !(com->extra_state & CSE_BUSYCHECK)) {
2331184610Salfred				timeout(siobusycheck, com, hz / 100);
2332184610Salfred				com->extra_state |= CSE_BUSYCHECK;
2333184610Salfred			}
2334184610Salfred			(*linesw[tp->t_line].l_start)(tp);
2335184610Salfred		}
2336192984Sthompsa		if (com_events == 0)
2337184610Salfred			break;
2338184610Salfred	}
2339184610Salfred	if (com_events >= LOTS_OF_EVENTS)
2340184610Salfred		goto repeat;
2341184610Salfred}
2342184610Salfred
2343184610Salfredstatic int
2344190735Sthompsacomparam(tp, t)
2345184610Salfred	struct tty	*tp;
2346	struct termios	*t;
2347{
2348	u_int		cfcr;
2349	int		cflag;
2350	struct com_s	*com;
2351	int		divisor;
2352	u_char		dlbh;
2353	u_char		dlbl;
2354	int		s;
2355	int		unit;
2356	int		intrsave;
2357
2358	/* do historical conversions */
2359	if (t->c_ispeed == 0)
2360		t->c_ispeed = t->c_ospeed;
2361
2362	/* check requested parameters */
2363	divisor = ttspeedtab(t->c_ospeed, comspeedtab);
2364	if (divisor < 0 || (divisor > 0 && t->c_ispeed != t->c_ospeed))
2365		return (EINVAL);
2366
2367	/* parameters are OK, convert them to the com struct and the device */
2368	unit = DEV_TO_UNIT(tp->t_dev);
2369	com = com_addr(unit);
2370	if (com == NULL)
2371		return (ENODEV);
2372	s = spltty();
2373	if (divisor == 0)
2374		(void)commctl(com, TIOCM_DTR, DMBIC);	/* hang up line */
2375	else
2376		(void)commctl(com, TIOCM_DTR, DMBIS);
2377	cflag = t->c_cflag;
2378	switch (cflag & CSIZE) {
2379	case CS5:
2380		cfcr = CFCR_5BITS;
2381		break;
2382	case CS6:
2383		cfcr = CFCR_6BITS;
2384		break;
2385	case CS7:
2386		cfcr = CFCR_7BITS;
2387		break;
2388	default:
2389		cfcr = CFCR_8BITS;
2390		break;
2391	}
2392	if (cflag & PARENB) {
2393		cfcr |= CFCR_PENAB;
2394		if (!(cflag & PARODD))
2395			cfcr |= CFCR_PEVEN;
2396	}
2397	if (cflag & CSTOPB)
2398		cfcr |= CFCR_STOPB;
2399
2400	if (com->hasfifo && divisor != 0) {
2401		/*
2402		 * Use a fifo trigger level low enough so that the input
2403		 * latency from the fifo is less than about 16 msec and
2404		 * the total latency is less than about 30 msec.  These
2405		 * latencies are reasonable for humans.  Serial comms
2406		 * protocols shouldn't expect anything better since modem
2407		 * latencies are larger.
2408		 */
2409		com->fifo_image = t->c_ospeed <= 4800
2410				  ? FIFO_ENABLE : FIFO_ENABLE | FIFO_RX_HIGH;
2411#ifdef COM_ESP
2412		/*
2413		 * The Hayes ESP card needs the fifo DMA mode bit set
2414		 * in compatibility mode.  If not, it will interrupt
2415		 * for each character received.
2416		 */
2417		if (com->esp)
2418			com->fifo_image |= FIFO_DMA_MODE;
2419#endif
2420		sio_setreg(com, com_fifo, com->fifo_image);
2421	}
2422
2423	/*
2424	 * This returns with interrupts disabled so that we can complete
2425	 * the speed change atomically.  Keeping interrupts disabled is
2426	 * especially important while com_data is hidden.
2427	 */
2428	intrsave = save_intr();
2429	(void) siosetwater(com, t->c_ispeed);
2430
2431	if (divisor != 0) {
2432		sio_setreg(com, com_cfcr, cfcr | CFCR_DLAB);
2433		/*
2434		 * Only set the divisor registers if they would change,
2435		 * since on some 16550 incompatibles (UMC8669F), setting
2436		 * them while input is arriving them loses sync until
2437		 * data stops arriving.
2438		 */
2439		dlbl = divisor & 0xFF;
2440		if (sio_getreg(com, com_dlbl) != dlbl)
2441			sio_setreg(com, com_dlbl, dlbl);
2442		dlbh = (u_int) divisor >> 8;
2443		if (sio_getreg(com, com_dlbh) != dlbh)
2444			sio_setreg(com, com_dlbh, dlbh);
2445	}
2446
2447	sio_setreg(com, com_cfcr, com->cfcr_image = cfcr);
2448
2449	if (!(tp->t_state & TS_TTSTOP))
2450		com->state |= CS_TTGO;
2451
2452	if (cflag & CRTS_IFLOW) {
2453		if (com->st16650a) {
2454			sio_setreg(com, com_cfcr, 0xbf);
2455			sio_setreg(com, com_fifo,
2456				   sio_getreg(com, com_fifo) | 0x40);
2457		}
2458		com->state |= CS_RTS_IFLOW;
2459		/*
2460		 * If CS_RTS_IFLOW just changed from off to on, the change
2461		 * needs to be propagated to MCR_RTS.  This isn't urgent,
2462		 * so do it later by calling comstart() instead of repeating
2463		 * a lot of code from comstart() here.
2464		 */
2465	} else if (com->state & CS_RTS_IFLOW) {
2466		com->state &= ~CS_RTS_IFLOW;
2467		/*
2468		 * CS_RTS_IFLOW just changed from on to off.  Force MCR_RTS
2469		 * on here, since comstart() won't do it later.
2470		 */
2471		outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS);
2472		if (com->st16650a) {
2473			sio_setreg(com, com_cfcr, 0xbf);
2474			sio_setreg(com, com_fifo,
2475				   sio_getreg(com, com_fifo) & ~0x40);
2476		}
2477	}
2478
2479
2480	/*
2481	 * Set up state to handle output flow control.
2482	 * XXX - worth handling MDMBUF (DCD) flow control at the lowest level?
2483	 * Now has 10+ msec latency, while CTS flow has 50- usec latency.
2484	 */
2485	com->state |= CS_ODEVREADY;
2486	com->state &= ~CS_CTS_OFLOW;
2487	if (cflag & CCTS_OFLOW) {
2488		com->state |= CS_CTS_OFLOW;
2489		if (!(com->last_modem_status & MSR_CTS))
2490			com->state &= ~CS_ODEVREADY;
2491		if (com->st16650a) {
2492			sio_setreg(com, com_cfcr, 0xbf);
2493			sio_setreg(com, com_fifo,
2494				   sio_getreg(com, com_fifo) | 0x80);
2495		}
2496	} else {
2497		if (com->st16650a) {
2498			sio_setreg(com, com_cfcr, 0xbf);
2499			sio_setreg(com, com_fifo,
2500				   sio_getreg(com, com_fifo) & ~0x80);
2501		}
2502	}
2503
2504	sio_setreg(com, com_cfcr, com->cfcr_image);
2505
2506	/* XXX shouldn't call functions while intrs are disabled. */
2507	disc_optim(tp, t, com);
2508	/*
2509	 * Recover from fiddling with CS_TTGO.  We used to call siointr1()
2510	 * unconditionally, but that defeated the careful discarding of
2511	 * stale input in sioopen().
2512	 */
2513	if (com->state >= (CS_BUSY | CS_TTGO))
2514		siointr1(com);
2515
2516	COM_UNLOCK();
2517	restore_intr(intrsave);
2518	splx(s);
2519	comstart(tp);
2520	if (com->ibufold != NULL) {
2521		free(com->ibufold, M_DEVBUF);
2522		com->ibufold = NULL;
2523	}
2524	return (0);
2525}
2526
2527/*
2528 * This function must be called with interrupts enabled and the com_lock
2529 * unlocked.  It will return with interrupts disabled and the com_lock locked.
2530 */
2531static int
2532siosetwater(com, speed)
2533	struct com_s	*com;
2534	speed_t		speed;
2535{
2536	int		cp4ticks;
2537	u_char		*ibuf;
2538	int		ibufsize;
2539	struct tty	*tp;
2540
2541	/*
2542	 * Make the buffer size large enough to handle a softtty interrupt
2543	 * latency of about 2 ticks without loss of throughput or data
2544	 * (about 3 ticks if input flow control is not used or not honoured,
2545	 * but a bit less for CS5-CS7 modes).
2546	 */
2547	cp4ticks = speed / 10 / hz * 4;
2548	for (ibufsize = 128; ibufsize < cp4ticks;)
2549		ibufsize <<= 1;
2550	if (ibufsize == com->ibufsize) {
2551		disable_intr();
2552		COM_LOCK();
2553		return (0);
2554	}
2555
2556	/*
2557	 * Allocate input buffer.  The extra factor of 2 in the size is
2558	 * to allow for an error byte for each input byte.
2559	 */
2560	ibuf = malloc(2 * ibufsize, M_DEVBUF, M_NOWAIT);
2561	if (ibuf == NULL) {
2562		disable_intr();
2563		COM_LOCK();
2564		return (ENOMEM);
2565	}
2566
2567	/* Initialize non-critical variables. */
2568	com->ibufold = com->ibuf;
2569	com->ibufsize = ibufsize;
2570	tp = com->tp;
2571	if (tp != NULL) {
2572		tp->t_ififosize = 2 * ibufsize;
2573		tp->t_ispeedwat = (speed_t)-1;
2574		tp->t_ospeedwat = (speed_t)-1;
2575	}
2576
2577	/*
2578	 * Read current input buffer, if any.  Continue with interrupts
2579	 * disabled.
2580	 */
2581	disable_intr();
2582	COM_LOCK();
2583	if (com->iptr != com->ibuf)
2584		sioinput(com);
2585
2586	/*-
2587	 * Initialize critical variables, including input buffer watermarks.
2588	 * The external device is asked to stop sending when the buffer
2589	 * exactly reaches high water, or when the high level requests it.
2590	 * The high level is notified immediately (rather than at a later
2591	 * clock tick) when this watermark is reached.
2592	 * The buffer size is chosen so the watermark should almost never
2593	 * be reached.
2594	 * The low watermark is invisibly 0 since the buffer is always
2595	 * emptied all at once.
2596	 */
2597	com->iptr = com->ibuf = ibuf;
2598	com->ibufend = ibuf + ibufsize;
2599	com->ierroff = ibufsize;
2600	com->ihighwater = ibuf + 3 * ibufsize / 4;
2601	return (0);
2602}
2603
2604static void
2605comstart(tp)
2606	struct tty	*tp;
2607{
2608	struct com_s	*com;
2609	int		s;
2610	int		unit;
2611	int		intrsave;
2612
2613	unit = DEV_TO_UNIT(tp->t_dev);
2614	com = com_addr(unit);
2615	if (com == NULL)
2616		return;
2617	s = spltty();
2618	intrsave = save_intr();
2619	disable_intr();
2620	COM_LOCK();
2621	if (tp->t_state & TS_TTSTOP)
2622		com->state &= ~CS_TTGO;
2623	else
2624		com->state |= CS_TTGO;
2625	if (tp->t_state & TS_TBLOCK) {
2626		if (com->mcr_image & MCR_RTS && com->state & CS_RTS_IFLOW)
2627			outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS);
2628	} else {
2629		if (!(com->mcr_image & MCR_RTS) && com->iptr < com->ihighwater
2630		    && com->state & CS_RTS_IFLOW)
2631			outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS);
2632	}
2633	COM_UNLOCK();
2634	restore_intr(intrsave);
2635	if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) {
2636		ttwwakeup(tp);
2637		splx(s);
2638		return;
2639	}
2640	if (tp->t_outq.c_cc != 0) {
2641		struct lbq	*qp;
2642		struct lbq	*next;
2643
2644		if (!com->obufs[0].l_queued) {
2645			com->obufs[0].l_tail
2646			    = com->obuf1 + q_to_b(&tp->t_outq, com->obuf1,
2647						  sizeof com->obuf1);
2648			com->obufs[0].l_next = NULL;
2649			com->obufs[0].l_queued = TRUE;
2650			intrsave = save_intr();
2651			disable_intr();
2652			COM_LOCK();
2653			if (com->state & CS_BUSY) {
2654				qp = com->obufq.l_next;
2655				while ((next = qp->l_next) != NULL)
2656					qp = next;
2657				qp->l_next = &com->obufs[0];
2658			} else {
2659				com->obufq.l_head = com->obufs[0].l_head;
2660				com->obufq.l_tail = com->obufs[0].l_tail;
2661				com->obufq.l_next = &com->obufs[0];
2662				com->state |= CS_BUSY;
2663			}
2664			COM_UNLOCK();
2665			restore_intr(intrsave);
2666		}
2667		if (tp->t_outq.c_cc != 0 && !com->obufs[1].l_queued) {
2668			com->obufs[1].l_tail
2669			    = com->obuf2 + q_to_b(&tp->t_outq, com->obuf2,
2670						  sizeof com->obuf2);
2671			com->obufs[1].l_next = NULL;
2672			com->obufs[1].l_queued = TRUE;
2673			intrsave = save_intr();
2674			disable_intr();
2675			COM_LOCK();
2676			if (com->state & CS_BUSY) {
2677				qp = com->obufq.l_next;
2678				while ((next = qp->l_next) != NULL)
2679					qp = next;
2680				qp->l_next = &com->obufs[1];
2681			} else {
2682				com->obufq.l_head = com->obufs[1].l_head;
2683				com->obufq.l_tail = com->obufs[1].l_tail;
2684				com->obufq.l_next = &com->obufs[1];
2685				com->state |= CS_BUSY;
2686			}
2687			COM_UNLOCK();
2688			restore_intr(intrsave);
2689		}
2690		tp->t_state |= TS_BUSY;
2691	}
2692	intrsave = save_intr();
2693	disable_intr();
2694	COM_LOCK();
2695	if (com->state >= (CS_BUSY | CS_TTGO))
2696		siointr1(com);	/* fake interrupt to start output */
2697	COM_UNLOCK();
2698	restore_intr(intrsave);
2699	ttwwakeup(tp);
2700	splx(s);
2701}
2702
2703static void
2704comstop(tp, rw)
2705	struct tty	*tp;
2706	int		rw;
2707{
2708	struct com_s	*com;
2709	int		intrsave;
2710
2711	com = com_addr(DEV_TO_UNIT(tp->t_dev));
2712	if (com == NULL || com->gone)
2713		return;
2714	intrsave = save_intr();
2715	disable_intr();
2716	COM_LOCK();
2717	if (rw & FWRITE) {
2718		if (com->hasfifo)
2719#ifdef COM_ESP
2720		    /* XXX avoid h/w bug. */
2721		    if (!com->esp)
2722#endif
2723			sio_setreg(com, com_fifo,
2724				   FIFO_XMT_RST | com->fifo_image);
2725		com->obufs[0].l_queued = FALSE;
2726		com->obufs[1].l_queued = FALSE;
2727		if (com->state & CS_ODONE)
2728			com_events -= LOTS_OF_EVENTS;
2729		com->state &= ~(CS_ODONE | CS_BUSY);
2730		com->tp->t_state &= ~TS_BUSY;
2731	}
2732	if (rw & FREAD) {
2733		if (com->hasfifo)
2734#ifdef COM_ESP
2735		    /* XXX avoid h/w bug. */
2736		    if (!com->esp)
2737#endif
2738			sio_setreg(com, com_fifo,
2739				   FIFO_RCV_RST | com->fifo_image);
2740		com_events -= (com->iptr - com->ibuf);
2741		com->iptr = com->ibuf;
2742	}
2743	COM_UNLOCK();
2744	restore_intr(intrsave);
2745	comstart(tp);
2746}
2747
2748static int
2749commctl(com, bits, how)
2750	struct com_s	*com;
2751	int		bits;
2752	int		how;
2753{
2754	int	mcr;
2755	int	msr;
2756	int	intrsave;
2757
2758	if (how == DMGET) {
2759		bits = TIOCM_LE;	/* XXX - always enabled while open */
2760		mcr = com->mcr_image;
2761		if (mcr & MCR_DTR)
2762			bits |= TIOCM_DTR;
2763		if (mcr & MCR_RTS)
2764			bits |= TIOCM_RTS;
2765		msr = com->prev_modem_status;
2766		if (msr & MSR_CTS)
2767			bits |= TIOCM_CTS;
2768		if (msr & MSR_DCD)
2769			bits |= TIOCM_CD;
2770		if (msr & MSR_DSR)
2771			bits |= TIOCM_DSR;
2772		/*
2773		 * XXX - MSR_RI is naturally volatile, and we make MSR_TERI
2774		 * more volatile by reading the modem status a lot.  Perhaps
2775		 * we should latch both bits until the status is read here.
2776		 */
2777		if (msr & (MSR_RI | MSR_TERI))
2778			bits |= TIOCM_RI;
2779		return (bits);
2780	}
2781	mcr = 0;
2782	if (bits & TIOCM_DTR)
2783		mcr |= MCR_DTR;
2784	if (bits & TIOCM_RTS)
2785		mcr |= MCR_RTS;
2786	if (com->gone)
2787		return(0);
2788	intrsave = save_intr();
2789	disable_intr();
2790	COM_LOCK();
2791	switch (how) {
2792	case DMSET:
2793		outb(com->modem_ctl_port,
2794		     com->mcr_image = mcr | (com->mcr_image & MCR_IENABLE));
2795		break;
2796	case DMBIS:
2797		outb(com->modem_ctl_port, com->mcr_image |= mcr);
2798		break;
2799	case DMBIC:
2800		outb(com->modem_ctl_port, com->mcr_image &= ~mcr);
2801		break;
2802	}
2803	COM_UNLOCK();
2804	restore_intr(intrsave);
2805	return (0);
2806}
2807
2808static void
2809siosettimeout()
2810{
2811	struct com_s	*com;
2812	bool_t		someopen;
2813	int		unit;
2814
2815	/*
2816	 * Set our timeout period to 1 second if no polled devices are open.
2817	 * Otherwise set it to max(1/200, 1/hz).
2818	 * Enable timeouts iff some device is open.
2819	 */
2820	untimeout(comwakeup, (void *)NULL, sio_timeout_handle);
2821	sio_timeout = hz;
2822	someopen = FALSE;
2823	for (unit = 0; unit < sio_numunits; ++unit) {
2824		com = com_addr(unit);
2825		if (com != NULL && com->tp != NULL
2826		    && com->tp->t_state & TS_ISOPEN && !com->gone) {
2827			someopen = TRUE;
2828			if (com->poll || com->poll_output) {
2829				sio_timeout = hz > 200 ? hz / 200 : 1;
2830				break;
2831			}
2832		}
2833	}
2834	if (someopen) {
2835		sio_timeouts_until_log = hz / sio_timeout;
2836		sio_timeout_handle = timeout(comwakeup, (void *)NULL,
2837					     sio_timeout);
2838	} else {
2839		/* Flush error messages, if any. */
2840		sio_timeouts_until_log = 1;
2841		comwakeup((void *)NULL);
2842		untimeout(comwakeup, (void *)NULL, sio_timeout_handle);
2843	}
2844}
2845
2846static void
2847comwakeup(chan)
2848	void	*chan;
2849{
2850	struct com_s	*com;
2851	int		unit;
2852	int		intrsave;
2853
2854	sio_timeout_handle = timeout(comwakeup, (void *)NULL, sio_timeout);
2855
2856	/*
2857	 * Recover from lost output interrupts.
2858	 * Poll any lines that don't use interrupts.
2859	 */
2860	for (unit = 0; unit < sio_numunits; ++unit) {
2861		com = com_addr(unit);
2862		if (com != NULL && !com->gone
2863		    && (com->state >= (CS_BUSY | CS_TTGO) || com->poll)) {
2864			intrsave = save_intr();
2865			disable_intr();
2866			COM_LOCK();
2867			siointr1(com);
2868			COM_UNLOCK();
2869			restore_intr(intrsave);
2870		}
2871	}
2872
2873	/*
2874	 * Check for and log errors, but not too often.
2875	 */
2876	if (--sio_timeouts_until_log > 0)
2877		return;
2878	sio_timeouts_until_log = hz / sio_timeout;
2879	for (unit = 0; unit < sio_numunits; ++unit) {
2880		int	errnum;
2881
2882		com = com_addr(unit);
2883		if (com == NULL)
2884			continue;
2885		if (com->gone)
2886			continue;
2887		for (errnum = 0; errnum < CE_NTYPES; ++errnum) {
2888			u_int	delta;
2889			u_long	total;
2890
2891			intrsave = save_intr();
2892			disable_intr();
2893			COM_LOCK();
2894			delta = com->delta_error_counts[errnum];
2895			com->delta_error_counts[errnum] = 0;
2896			COM_UNLOCK();
2897			restore_intr(intrsave);
2898			if (delta == 0)
2899				continue;
2900			total = com->error_counts[errnum] += delta;
2901			log(LOG_ERR, "sio%d: %u more %s%s (total %lu)\n",
2902			    unit, delta, error_desc[errnum],
2903			    delta == 1 ? "" : "s", total);
2904		}
2905	}
2906}
2907
2908static void
2909disc_optim(tp, t, com)
2910	struct tty	*tp;
2911	struct termios	*t;
2912	struct com_s	*com;
2913{
2914	if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON))
2915	    && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK))
2916	    && (!(t->c_iflag & PARMRK)
2917		|| (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK))
2918	    && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN))
2919	    && linesw[tp->t_line].l_rint == ttyinput)
2920		tp->t_state |= TS_CAN_BYPASS_L_RINT;
2921	else
2922		tp->t_state &= ~TS_CAN_BYPASS_L_RINT;
2923	com->hotchar = linesw[tp->t_line].l_hotchar;
2924}
2925
2926/*
2927 * Following are all routines needed for SIO to act as console
2928 */
2929#include <sys/cons.h>
2930
2931struct siocnstate {
2932	u_char	dlbl;
2933	u_char	dlbh;
2934	u_char	ier;
2935	u_char	cfcr;
2936	u_char	mcr;
2937};
2938
2939#ifndef __alpha__
2940static speed_t siocngetspeed __P((Port_t, struct speedtab *));
2941#endif
2942static void siocnclose	__P((struct siocnstate *sp, Port_t iobase));
2943static void siocnopen	__P((struct siocnstate *sp, Port_t iobase, int speed));
2944static void siocntxwait	__P((Port_t iobase));
2945
2946#ifdef __alpha__
2947int siocnattach __P((int port, int speed));
2948int siogdbattach __P((int port, int speed));
2949int siogdbgetc __P((void));
2950void siogdbputc __P((int c));
2951#else
2952static cn_probe_t siocnprobe;
2953static cn_init_t siocninit;
2954#endif
2955static cn_checkc_t siocncheckc;
2956static cn_getc_t siocngetc;
2957static cn_putc_t siocnputc;
2958
2959#ifdef __i386__
2960CONS_DRIVER(sio, siocnprobe, siocninit, NULL, siocngetc, siocncheckc,
2961	    siocnputc, NULL);
2962#endif
2963
2964/* To get the GDB related variables */
2965#if DDB > 0
2966#include <ddb/ddb.h>
2967#endif
2968
2969static void
2970siocntxwait(iobase)
2971	Port_t	iobase;
2972{
2973	int	timo;
2974
2975	/*
2976	 * Wait for any pending transmission to finish.  Required to avoid
2977	 * the UART lockup bug when the speed is changed, and for normal
2978	 * transmits.
2979	 */
2980	timo = 100000;
2981	while ((inb(iobase + com_lsr) & (LSR_TSRE | LSR_TXRDY))
2982	       != (LSR_TSRE | LSR_TXRDY) && --timo != 0)
2983		;
2984}
2985
2986#ifndef __alpha__
2987
2988/*
2989 * Read the serial port specified and try to figure out what speed
2990 * it's currently running at.  We're assuming the serial port has
2991 * been initialized and is basicly idle.  This routine is only intended
2992 * to be run at system startup.
2993 *
2994 * If the value read from the serial port doesn't make sense, return 0.
2995 */
2996
2997static speed_t
2998siocngetspeed(iobase, table)
2999	Port_t iobase;
3000	struct speedtab *table;
3001{
3002	int	code;
3003	u_char	dlbh;
3004	u_char	dlbl;
3005	u_char  cfcr;
3006
3007	cfcr = inb(iobase + com_cfcr);
3008	outb(iobase + com_cfcr, CFCR_DLAB | cfcr);
3009
3010	dlbl = inb(iobase + com_dlbl);
3011	dlbh = inb(iobase + com_dlbh);
3012
3013	outb(iobase + com_cfcr, cfcr);
3014
3015	code = dlbh << 8 | dlbl;
3016
3017	for (; table->sp_speed != -1; table++)
3018		if (table->sp_code == code)
3019			return (table->sp_speed);
3020
3021	return (0);	/* didn't match anything sane */
3022}
3023
3024#endif
3025
3026static void
3027siocnopen(sp, iobase, speed)
3028	struct siocnstate	*sp;
3029	Port_t			iobase;
3030	int			speed;
3031{
3032	int	divisor;
3033	u_char	dlbh;
3034	u_char	dlbl;
3035
3036	/*
3037	 * Save all the device control registers except the fifo register
3038	 * and set our default ones (cs8 -parenb speed=comdefaultrate).
3039	 * We can't save the fifo register since it is read-only.
3040	 */
3041	sp->ier = inb(iobase + com_ier);
3042	outb(iobase + com_ier, 0);	/* spltty() doesn't stop siointr() */
3043	siocntxwait(iobase);
3044	sp->cfcr = inb(iobase + com_cfcr);
3045	outb(iobase + com_cfcr, CFCR_DLAB | CFCR_8BITS);
3046	sp->dlbl = inb(iobase + com_dlbl);
3047	sp->dlbh = inb(iobase + com_dlbh);
3048	/*
3049	 * Only set the divisor registers if they would change, since on
3050	 * some 16550 incompatibles (Startech), setting them clears the
3051	 * data input register.  This also reduces the effects of the
3052	 * UMC8669F bug.
3053	 */
3054	divisor = ttspeedtab(speed, comspeedtab);
3055	dlbl = divisor & 0xFF;
3056	if (sp->dlbl != dlbl)
3057		outb(iobase + com_dlbl, dlbl);
3058	dlbh = (u_int) divisor >> 8;
3059	if (sp->dlbh != dlbh)
3060		outb(iobase + com_dlbh, dlbh);
3061	outb(iobase + com_cfcr, CFCR_8BITS);
3062	sp->mcr = inb(iobase + com_mcr);
3063	/*
3064	 * We don't want interrupts, but must be careful not to "disable"
3065	 * them by clearing the MCR_IENABLE bit, since that might cause
3066	 * an interrupt by floating the IRQ line.
3067	 */
3068	outb(iobase + com_mcr, (sp->mcr & MCR_IENABLE) | MCR_DTR | MCR_RTS);
3069}
3070
3071static void
3072siocnclose(sp, iobase)
3073	struct siocnstate	*sp;
3074	Port_t			iobase;
3075{
3076	/*
3077	 * Restore the device control registers.
3078	 */
3079	siocntxwait(iobase);
3080	outb(iobase + com_cfcr, CFCR_DLAB | CFCR_8BITS);
3081	if (sp->dlbl != inb(iobase + com_dlbl))
3082		outb(iobase + com_dlbl, sp->dlbl);
3083	if (sp->dlbh != inb(iobase + com_dlbh))
3084		outb(iobase + com_dlbh, sp->dlbh);
3085	outb(iobase + com_cfcr, sp->cfcr);
3086	/*
3087	 * XXX damp oscillations of MCR_DTR and MCR_RTS by not restoring them.
3088	 */
3089	outb(iobase + com_mcr, sp->mcr | MCR_DTR | MCR_RTS);
3090	outb(iobase + com_ier, sp->ier);
3091}
3092
3093#ifndef __alpha__
3094
3095static void
3096siocnprobe(cp)
3097	struct consdev	*cp;
3098{
3099	speed_t			boot_speed;
3100	u_char			cfcr;
3101	int			s, unit;
3102	struct siocnstate	sp;
3103
3104	/*
3105	 * Find our first enabled console, if any.  If it is a high-level
3106	 * console device, then initialize it and return successfully.
3107	 * If it is a low-level console device, then initialize it and
3108	 * return unsuccessfully.  It must be initialized in both cases
3109	 * for early use by console drivers and debuggers.  Initializing
3110	 * the hardware is not necessary in all cases, since the i/o
3111	 * routines initialize it on the fly, but it is necessary if
3112	 * input might arrive while the hardware is switched back to an
3113	 * uninitialized state.  We can't handle multiple console devices
3114	 * yet because our low-level routines don't take a device arg.
3115	 * We trust the user to set the console flags properly so that we
3116	 * don't need to probe.
3117	 */
3118	cp->cn_pri = CN_DEAD;
3119
3120	for (unit = 0; unit < 16; unit++) { /* XXX need to know how many */
3121		int flags;
3122		int disabled;
3123		if (resource_int_value("sio", unit, "disabled", &disabled) == 0) {
3124			if (disabled)
3125				continue;
3126		}
3127		if (resource_int_value("sio", unit, "flags", &flags))
3128			continue;
3129		if (COM_CONSOLE(flags) || COM_DEBUGGER(flags)) {
3130			int port;
3131			Port_t iobase;
3132
3133			if (resource_int_value("sio", unit, "port", &port))
3134				continue;
3135			iobase = port;
3136			s = spltty();
3137			if (boothowto & RB_SERIAL) {
3138				boot_speed = siocngetspeed(iobase, comspeedtab);
3139				if (boot_speed)
3140					comdefaultrate = boot_speed;
3141			}
3142
3143			/*
3144			 * Initialize the divisor latch.  We can't rely on
3145			 * siocnopen() to do this the first time, since it
3146			 * avoids writing to the latch if the latch appears
3147			 * to have the correct value.  Also, if we didn't
3148			 * just read the speed from the hardware, then we
3149			 * need to set the speed in hardware so that
3150			 * switching it later is null.
3151			 */
3152			cfcr = inb(iobase + com_cfcr);
3153			outb(iobase + com_cfcr, CFCR_DLAB | cfcr);
3154			outb(iobase + com_dlbl,
3155			     COMBRD(comdefaultrate) & 0xff);
3156			outb(iobase + com_dlbh,
3157			     (u_int) COMBRD(comdefaultrate) >> 8);
3158			outb(iobase + com_cfcr, cfcr);
3159
3160			siocnopen(&sp, iobase, comdefaultrate);
3161
3162			splx(s);
3163			if (COM_CONSOLE(flags) && !COM_LLCONSOLE(flags)) {
3164				cp->cn_dev = makedev(CDEV_MAJOR, unit);
3165				cp->cn_pri = COM_FORCECONSOLE(flags)
3166					     || boothowto & RB_SERIAL
3167					     ? CN_REMOTE : CN_NORMAL;
3168				siocniobase = iobase;
3169				siocnunit = unit;
3170			}
3171			if (COM_DEBUGGER(flags)) {
3172				printf("sio%d: gdb debugging port\n", unit);
3173				siogdbiobase = iobase;
3174				siogdbunit = unit;
3175#if DDB > 0
3176				gdbdev = makedev(CDEV_MAJOR, unit);
3177				gdb_getc = siocngetc;
3178				gdb_putc = siocnputc;
3179#endif
3180			}
3181		}
3182	}
3183#ifdef	__i386__
3184#if DDB > 0
3185	/*
3186	 * XXX Ugly Compatability.
3187	 * If no gdb port has been specified, set it to be the console
3188	 * as some configuration files don't specify the gdb port.
3189	 */
3190	if (gdbdev == NODEV && (boothowto & RB_GDB)) {
3191		printf("Warning: no GDB port specified. Defaulting to sio%d.\n",
3192			siocnunit);
3193		printf("Set flag 0x80 on desired GDB port in your\n");
3194		printf("configuration file (currently sio only).\n");
3195		siogdbiobase = siocniobase;
3196		siogdbunit = siocnunit;
3197		gdbdev = makedev(CDEV_MAJOR, siocnunit);
3198		gdb_getc = siocngetc;
3199		gdb_putc = siocnputc;
3200	}
3201#endif
3202#endif
3203}
3204
3205static void
3206siocninit(cp)
3207	struct consdev	*cp;
3208{
3209	comconsole = DEV_TO_UNIT(cp->cn_dev);
3210}
3211
3212#endif
3213
3214#ifdef __alpha__
3215
3216CONS_DRIVER(sio, NULL, NULL, NULL, siocngetc, siocncheckc, siocnputc, NULL);
3217
3218int
3219siocnattach(port, speed)
3220	int port;
3221	int speed;
3222{
3223	int			s;
3224	u_char			cfcr;
3225	struct siocnstate	sp;
3226
3227	siocniobase = port;
3228	comdefaultrate = speed;
3229	sio_consdev.cn_pri = CN_NORMAL;
3230	sio_consdev.cn_dev = makedev(CDEV_MAJOR, 0);
3231
3232	s = spltty();
3233
3234	/*
3235	 * Initialize the divisor latch.  We can't rely on
3236	 * siocnopen() to do this the first time, since it
3237	 * avoids writing to the latch if the latch appears
3238	 * to have the correct value.  Also, if we didn't
3239	 * just read the speed from the hardware, then we
3240	 * need to set the speed in hardware so that
3241	 * switching it later is null.
3242	 */
3243	cfcr = inb(siocniobase + com_cfcr);
3244	outb(siocniobase + com_cfcr, CFCR_DLAB | cfcr);
3245	outb(siocniobase + com_dlbl,
3246	     COMBRD(comdefaultrate) & 0xff);
3247	outb(siocniobase + com_dlbh,
3248	     (u_int) COMBRD(comdefaultrate) >> 8);
3249	outb(siocniobase + com_cfcr, cfcr);
3250
3251	siocnopen(&sp, siocniobase, comdefaultrate);
3252	splx(s);
3253
3254	cn_tab = &sio_consdev;
3255	return (0);
3256}
3257
3258int
3259siogdbattach(port, speed)
3260	int port;
3261	int speed;
3262{
3263	int			s;
3264	u_char			cfcr;
3265	struct siocnstate	sp;
3266	int			unit = 1;	/* XXX !!! */
3267
3268	siogdbiobase = port;
3269	gdbdefaultrate = speed;
3270
3271	printf("sio%d: gdb debugging port\n", unit);
3272	siogdbunit = unit;
3273#if DDB > 0
3274	gdbdev = makedev(CDEV_MAJOR, unit);
3275	gdb_getc = siocngetc;
3276	gdb_putc = siocnputc;
3277#endif
3278
3279	s = spltty();
3280
3281	/*
3282	 * Initialize the divisor latch.  We can't rely on
3283	 * siocnopen() to do this the first time, since it
3284	 * avoids writing to the latch if the latch appears
3285	 * to have the correct value.  Also, if we didn't
3286	 * just read the speed from the hardware, then we
3287	 * need to set the speed in hardware so that
3288	 * switching it later is null.
3289	 */
3290	cfcr = inb(siogdbiobase + com_cfcr);
3291	outb(siogdbiobase + com_cfcr, CFCR_DLAB | cfcr);
3292	outb(siogdbiobase + com_dlbl,
3293	     COMBRD(gdbdefaultrate) & 0xff);
3294	outb(siogdbiobase + com_dlbh,
3295	     (u_int) COMBRD(gdbdefaultrate) >> 8);
3296	outb(siogdbiobase + com_cfcr, cfcr);
3297
3298	siocnopen(&sp, siogdbiobase, gdbdefaultrate);
3299	splx(s);
3300
3301	return (0);
3302}
3303
3304#endif
3305
3306static int
3307siocncheckc(dev)
3308	dev_t	dev;
3309{
3310	int	c;
3311	Port_t	iobase;
3312	int	s;
3313	struct siocnstate	sp;
3314
3315	if (minor(dev) == siogdbunit)
3316		iobase = siogdbiobase;
3317	else
3318		iobase = siocniobase;
3319	s = spltty();
3320	siocnopen(&sp, iobase, comdefaultrate);
3321	if (inb(iobase + com_lsr) & LSR_RXRDY)
3322		c = inb(iobase + com_data);
3323	else
3324		c = -1;
3325	siocnclose(&sp, iobase);
3326	splx(s);
3327	return (c);
3328}
3329
3330
3331int
3332siocngetc(dev)
3333	dev_t	dev;
3334{
3335	int	c;
3336	Port_t	iobase;
3337	int	s;
3338	struct siocnstate	sp;
3339
3340	if (minor(dev) == siogdbunit)
3341		iobase = siogdbiobase;
3342	else
3343		iobase = siocniobase;
3344	s = spltty();
3345	siocnopen(&sp, iobase, comdefaultrate);
3346	while (!(inb(iobase + com_lsr) & LSR_RXRDY))
3347		;
3348	c = inb(iobase + com_data);
3349	siocnclose(&sp, iobase);
3350	splx(s);
3351	return (c);
3352}
3353
3354void
3355siocnputc(dev, c)
3356	dev_t	dev;
3357	int	c;
3358{
3359	int	s;
3360	struct siocnstate	sp;
3361	Port_t	iobase;
3362
3363	if (minor(dev) == siogdbunit)
3364		iobase = siogdbiobase;
3365	else
3366		iobase = siocniobase;
3367	s = spltty();
3368	siocnopen(&sp, iobase, comdefaultrate);
3369	siocntxwait(iobase);
3370	outb(iobase + com_data, c);
3371	siocnclose(&sp, iobase);
3372	splx(s);
3373}
3374
3375#ifdef __alpha__
3376int
3377siogdbgetc()
3378{
3379	int	c;
3380	Port_t	iobase;
3381	int	s;
3382	struct siocnstate	sp;
3383
3384	iobase = siogdbiobase;
3385	s = spltty();
3386	siocnopen(&sp, iobase, gdbdefaultrate);
3387	while (!(inb(iobase + com_lsr) & LSR_RXRDY))
3388		;
3389	c = inb(iobase + com_data);
3390	siocnclose(&sp, iobase);
3391	splx(s);
3392	return (c);
3393}
3394
3395void
3396siogdbputc(c)
3397	int	c;
3398{
3399	int	s;
3400	struct siocnstate	sp;
3401
3402	s = spltty();
3403	siocnopen(&sp, siogdbiobase, gdbdefaultrate);
3404	siocntxwait(siogdbiobase);
3405	outb(siogdbiobase + com_data, c);
3406	siocnclose(&sp, siogdbiobase);
3407	splx(s);
3408}
3409#endif
3410
3411DRIVER_MODULE(sio, isa, sio_isa_driver, sio_devclass, 0, 0);
3412#if NCARD > 0
3413DRIVER_MODULE(sio, pccard, sio_pccard_driver, sio_devclass, 0, 0);
3414#endif
3415#if NPCI > 0
3416DRIVER_MODULE(sio, pci, sio_pci_driver, sio_devclass, 0, 0);
3417#endif
3418