1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*	Copyright (c) 1990, 1991 UNIX System Laboratories, Inc.	*/
22/*	Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T	*/
23/*	  All Rights Reserved					*/
24
25/*
26 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
27 * Use is subject to license terms.
28 */
29
30
31/*
32 *	Serial I/O driver for 82510/8250/16450/16550AF/16C554D chips.
33 *	Modified as sparc keyboard/mouse driver.
34 */
35#define	SU_REGISTER_FILE_NO 0
36#define	SU_REGOFFSET 0
37#define	SU_REGISTER_LEN 8
38
39#include <sys/param.h>
40#include <sys/types.h>
41#include <sys/signal.h>
42#include <sys/stream.h>
43#include <sys/termio.h>
44#include <sys/errno.h>
45#include <sys/file.h>
46#include <sys/cmn_err.h>
47#include <sys/stropts.h>
48#include <sys/strsubr.h>
49#include <sys/strsun.h>
50#include <sys/strtty.h>
51#include <sys/debug.h>
52#include <sys/kbio.h>
53#include <sys/cred.h>
54#include <sys/modctl.h>
55#include <sys/stat.h>
56#include <sys/consdev.h>
57#include <sys/mkdev.h>
58#include <sys/kmem.h>
59#include <sys/cred.h>
60#ifdef DEBUG
61#include <sys/promif.h>
62#endif
63#include <sys/ddi.h>
64#include <sys/sunddi.h>
65#include <sys/sudev.h>
66#include <sys/note.h>
67#include <sys/timex.h>
68#include <sys/policy.h>
69
70#define	async_stopc	async_ttycommon.t_stopc
71#define	async_startc	async_ttycommon.t_startc
72
73#define	ASY_INIT	1
74#define	ASY_NOINIT	0
75
76#ifdef DEBUG
77#define	ASY_DEBUG_INIT	0x001
78#define	ASY_DEBUG_INPUT	0x002
79#define	ASY_DEBUG_EOT	0x004
80#define	ASY_DEBUG_CLOSE	0x008
81#define	ASY_DEBUG_HFLOW	0x010
82#define	ASY_DEBUG_PROCS	0x020
83#define	ASY_DEBUG_STATE	0x040
84#define	ASY_DEBUG_INTR	0x080
85static	int asydebug = 0;
86#endif
87static	int su_log = 0;
88
89int su_drain_check = 15000000;		/* tunable: exit drain check time */
90
91static	struct ppsclockev asy_ppsev;
92
93static	int max_asy_instance = -1;
94static	void	*su_asycom;	/* soft state asycom pointer */
95static	void	*su_asyncline;	/* soft state asyncline pointer */
96static	boolean_t abort_charseq_recognize(uchar_t ch);
97
98static	uint_t	asysoftintr(caddr_t intarg);
99static	uint_t	asyintr(caddr_t argasy);
100
101/* The async interrupt entry points */
102static void	async_txint(struct asycom *asy, uchar_t lsr);
103static void	async_rxint(struct asycom *asy, uchar_t lsr);
104static void	async_msint(struct asycom *asy);
105static int	async_softint(struct asycom *asy);
106
107static void	async_ioctl(struct asyncline *async, queue_t *q, mblk_t *mp,
108    boolean_t iswput);
109static void	async_reioctl(void *);
110static void	async_iocdata(queue_t *q, mblk_t *mp);
111static void	async_restart(void *);
112static void	async_start(struct asyncline *async);
113static void	async_nstart(struct asyncline *async, int mode);
114static void	async_resume(struct asyncline *async);
115static int	asy_program(struct asycom *asy, int mode);
116
117/* Polled mode functions */
118static void	asyputchar(cons_polledio_arg_t, uchar_t c);
119static int	asygetchar(cons_polledio_arg_t);
120static boolean_t	asyischar(cons_polledio_arg_t);
121static void	asy_polled_enter(cons_polledio_arg_t);
122static void	asy_polled_exit(cons_polledio_arg_t);
123
124static int	asymctl(struct asycom *, int, int);
125static int	asytodm(int, int);
126static int	dmtoasy(int);
127static void	asycheckflowcontrol_hw(struct asycom *asy);
128static boolean_t asycheckflowcontrol_sw(struct asycom *asy);
129static void	asy_ppsevent(struct asycom *asy, int msr);
130
131extern kcondvar_t lbolt_cv;
132extern int ddi_create_internal_pathname(dev_info_t *dip, char *name,
133		int spec_type, minor_t minor_num);
134
135
136/*
137 * Baud rate table. Indexed by #defines found in sys/termios.h
138 */
139ushort_t asyspdtab[] = {
140	0,	/* 0 baud rate */
141	0x900,	/* 50 baud rate */
142	0x600,	/* 75 baud rate */
143	0x417,	/* 110 baud rate (%0.026) */
144	0x359,	/* 134 baud rate (%0.058) */
145	0x300,	/* 150 baud rate */
146	0x240,	/* 200 baud rate */
147	0x180,	/* 300 baud rate */
148	0x0c0,	/* 600 baud rate */
149	0x060,	/* 1200 baud rate */
150	0x040,	/* 1800 baud rate */
151	0x030,	/* 2400 baud rate */
152	0x018,	/* 4800 baud rate */
153	0x00c,	/* 9600 baud rate */
154	0x006,	/* 19200 baud rate */
155	0x003,	/* 38400 baud rate */
156	0x002,	/* 57600 baud rate */
157	0,	/* 76800 baud rate - not supported */
158	0x001,	/* 115200 baud rate */
159	0,	/* 153600 baud rate - not supported */
160	0x8002,	/* 230400 baud rate - supported on specific platforms */
161	0,	/* 307200 baud rate - not supported */
162	0x8001	/* 460800 baud rate - supported on specific platforms */
163};
164
165/*
166 * Number of speeds supported is the number of entries in
167 * the above table.
168 */
169#define	N_SU_SPEEDS	(sizeof (asyspdtab)/sizeof (ushort_t))
170
171/*
172 * Human-readable baud rate table.
173 * Indexed by #defines found in sys/termios.h
174 */
175int baudtable[] = {
176	0,	/* 0 baud rate */
177	50,	/* 50 baud rate */
178	75,	/* 75 baud rate */
179	110,	/* 110 baud rate */
180	134,	/* 134 baud rate */
181	150,	/* 150 baud rate */
182	200,	/* 200 baud rate */
183	300,	/* 300 baud rate */
184	600,	/* 600 baud rate */
185	1200,	/* 1200 baud rate */
186	1800,	/* 1800 baud rate */
187	2400,	/* 2400 baud rate */
188	4800,	/* 4800 baud rate */
189	9600,	/* 9600 baud rate */
190	19200,	/* 19200 baud rate */
191	38400,	/* 38400 baud rate */
192	57600,	/* 57600 baud rate */
193	76800,	/* 76800 baud rate */
194	115200,	/* 115200 baud rate */
195	153600,	/* 153600 baud rate */
196	230400,	/* 230400 baud rate */
197	307200,	/* 307200 baud rate */
198	460800	/* 460800 baud rate */
199};
200
201static int asyopen(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr);
202static int asyclose(queue_t *q, int flag);
203static void asywput(queue_t *q, mblk_t *mp);
204static void asyrsrv(queue_t *q);
205
206struct module_info asy_info = {
207	0,
208	"su",
209	0,
210	INFPSZ,
211	32*4096,
212	4096
213};
214
215static struct qinit asy_rint = {
216	putq,
217	(int (*)())asyrsrv,
218	asyopen,
219	asyclose,
220	NULL,
221	&asy_info,
222	NULL
223};
224
225static struct qinit asy_wint = {
226	(int (*)())asywput,
227	NULL,
228	NULL,
229	NULL,
230	NULL,
231	&asy_info,
232	NULL
233};
234
235struct streamtab asy_str_info = {
236	&asy_rint,
237	&asy_wint,
238	NULL,
239	NULL
240};
241
242static int asyinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
243		void **result);
244static int asyprobe(dev_info_t *);
245static int asyattach(dev_info_t *, ddi_attach_cmd_t);
246static int asydetach(dev_info_t *, ddi_detach_cmd_t);
247
248static 	struct cb_ops cb_asy_ops = {
249	nodev,			/* cb_open */
250	nodev,			/* cb_close */
251	nodev,			/* cb_strategy */
252	nodev,			/* cb_print */
253	nodev,			/* cb_dump */
254	nodev,			/* cb_read */
255	nodev,			/* cb_write */
256	nodev,			/* cb_ioctl */
257	nodev,			/* cb_devmap */
258	nodev,			/* cb_mmap */
259	nodev,			/* cb_segmap */
260	nochpoll,		/* cb_chpoll */
261	ddi_prop_op,		/* cb_prop_op */
262	&asy_str_info,		/* cb_stream */
263	D_MP			/* cb_flag */
264};
265
266struct dev_ops asy_ops = {
267	DEVO_REV,		/* devo_rev */
268	0,			/* devo_refcnt */
269	asyinfo,		/* devo_getinfo */
270	nulldev,		/* devo_identify */
271	asyprobe,		/* devo_probe */
272	asyattach,		/* devo_attach */
273	asydetach,		/* devo_detach */
274	nodev,			/* devo_reset */
275	&cb_asy_ops,		/* devo_cb_ops */
276	NULL,			/* devo_bus_ops */
277	NULL,			/* devo_power */
278	ddi_quiesce_not_supported,	/* devo_quiesce */
279};
280
281/*
282 * Module linkage information for the kernel.
283 */
284
285static struct modldrv modldrv = {
286	&mod_driverops, /* Type of module.  This one is a driver */
287	"su driver",
288	&asy_ops,	/* driver ops */
289};
290
291static struct modlinkage modlinkage = {
292	MODREV_1,
293	&modldrv,
294	NULL
295};
296
297int
298_init(void)
299{
300	int status;
301
302	status = ddi_soft_state_init(&su_asycom, sizeof (struct asycom),
303	    SU_INITIAL_SOFT_ITEMS);
304	if (status != 0)
305		return (status);
306	status = ddi_soft_state_init(&su_asyncline, sizeof (struct asyncline),
307	    SU_INITIAL_SOFT_ITEMS);
308	if (status != 0) {
309		ddi_soft_state_fini(&su_asycom);
310		return (status);
311	}
312
313	if ((status = mod_install(&modlinkage)) != 0) {
314		ddi_soft_state_fini(&su_asycom);
315		ddi_soft_state_fini(&su_asyncline);
316	}
317
318	return (status);
319}
320
321int
322_fini(void)
323{
324	int i;
325
326	i = mod_remove(&modlinkage);
327	if (i == 0) {
328		ddi_soft_state_fini(&su_asycom);
329		ddi_soft_state_fini(&su_asyncline);
330	}
331
332	return (i);
333}
334
335int
336_info(struct modinfo *modinfop)
337{
338	return (mod_info(&modlinkage, modinfop));
339}
340
341static int
342asyprobe(dev_info_t *devi)
343{
344	int		instance;
345	ddi_acc_handle_t handle;
346	uchar_t *addr;
347	ddi_device_acc_attr_t attr;
348
349	attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
350	attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
351	attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
352	if (ddi_regs_map_setup(devi, SU_REGISTER_FILE_NO, (caddr_t *)&addr,
353	    SU_REGOFFSET, SU_REGISTER_LEN, &attr, &handle) != DDI_SUCCESS) {
354		cmn_err(CE_WARN, "asyprobe regs map setup failed");
355		return (DDI_PROBE_FAILURE);
356	}
357#ifdef DEBUG
358	if (asydebug)
359		printf("Probe address mapped %p\n", (void *)addr);
360#endif
361
362	/*
363	 * Probe for the device:
364	 * 	Ser. int. uses bits 0,1,2; FIFO uses 3,6,7; 4,5 wired low.
365	 * 	If bit 4 or 5 appears on inb() ISR, board is not there.
366	 */
367	if (ddi_get8(handle, addr+ISR) & 0x30) {
368		ddi_regs_map_free(&handle);
369		return (DDI_PROBE_FAILURE);
370	}
371
372	instance = ddi_get_instance(devi);
373	if (max_asy_instance < instance)
374		max_asy_instance = instance;
375	ddi_regs_map_free(&handle);
376
377	return (DDI_PROBE_SUCCESS); /* hw is present */
378}
379
380static int
381asydetach(dev_info_t *devi, ddi_detach_cmd_t cmd)
382{
383	register int	instance;
384	struct asycom	*asy;
385	struct asyncline *async;
386	char		name[16];
387
388	instance = ddi_get_instance(devi);	/* find out which unit */
389
390	asy = (struct asycom *)ddi_get_soft_state(su_asycom, instance);
391	async = (struct asyncline *)ddi_get_soft_state(su_asyncline, instance);
392
393	switch (cmd) {
394		case DDI_DETACH:
395			break;
396		case DDI_SUSPEND:
397			/* grab both mutex locks */
398			mutex_enter(asy->asy_excl);
399			mutex_enter(asy->asy_excl_hi);
400			if (asy->suspended) {
401				mutex_exit(asy->asy_excl_hi);
402				mutex_exit(asy->asy_excl);
403				return (DDI_SUCCESS);
404			}
405			asy->suspended = B_TRUE;
406
407			/*
408			 * The quad UART ST16C554D, version D2 (made by EXAR)
409			 * has an anomaly of generating spurious interrupts
410			 * when the ICR is loaded with zero. The workaround
411			 * would be to read/write any register with DATA1 bit
412			 * set to 0 before such write.
413			 */
414			if (asy->asy_hwtype == ASY16C554D)
415				OUTB(SPR, 0);
416
417			/* Disable further interrupts */
418			OUTB(ICR, 0);
419			mutex_exit(asy->asy_excl_hi);
420			mutex_exit(asy->asy_excl);
421			return (DDI_SUCCESS);
422
423		default:
424			return (DDI_FAILURE);
425	}
426
427#ifdef DEBUG
428	if (asydebug & ASY_DEBUG_INIT)
429		cmn_err(CE_NOTE, "su%d: ASY%s shutdown.", instance,
430		    asy->asy_hwtype == ASY82510 ? "82510" :
431		    asy->asy_hwtype == ASY16550AF ? "16550AF" :
432		    asy->asy_hwtype == ASY16C554D ? "16C554D" :
433		    "8250");
434#endif
435	/*
436	 * Before removing interrupts it is always better to disable
437	 * interrupts if the chip gives a provision to disable the
438	 * serial port interrupts.
439	 */
440	mutex_enter(asy->asy_excl);
441	mutex_enter(asy->asy_excl_hi);
442	/* disable interrupts, see EXAR bug */
443	if (asy->asy_hwtype == ASY16C554D)
444		OUTB(SPR, 0);
445	OUTB(ICR, 0);
446	mutex_exit(asy->asy_excl_hi);
447	mutex_exit(asy->asy_excl);
448
449	/* remove minor device node(s) for this device */
450	(void) sprintf(name, "%c", (instance+'a'));	/* serial-port */
451	ddi_remove_minor_node(devi, name);
452	(void) sprintf(name, "%c,cu", (instance+'a')); /* serial-port:dailout */
453	ddi_remove_minor_node(devi, name);
454
455	mutex_destroy(asy->asy_excl);
456	mutex_destroy(asy->asy_excl_hi);
457	kmem_free(asy->asy_excl, sizeof (kmutex_t));
458	kmem_free(asy->asy_excl_hi, sizeof (kmutex_t));
459	cv_destroy(&async->async_flags_cv);
460	kstat_delete(asy->sukstat);
461	ddi_remove_intr(devi, 0, asy->asy_iblock);
462	ddi_regs_map_free(&asy->asy_handle);
463	ddi_remove_softintr(asy->asy_softintr_id);
464	mutex_destroy(asy->asy_soft_lock);
465	kmem_free(asy->asy_soft_lock, sizeof (kmutex_t));
466	ddi_soft_state_free(su_asycom, instance);
467	ddi_soft_state_free(su_asyncline, instance);
468	return (DDI_SUCCESS);
469}
470
471static int
472asyattach(dev_info_t *devi, ddi_attach_cmd_t cmd)
473{
474	register int	instance;
475	struct asycom	*asy;
476	struct asyncline *async;
477	char		name[40];
478	ddi_device_acc_attr_t attr;
479	enum states { EMPTY, SOFTSTATE, REGSMAP, MUTEXES, ADDINTR,
480	    SOFTINTR, ASYINIT, KSTAT, MINORNODE };
481	enum states state = EMPTY;
482	char *hwtype;
483
484	instance = ddi_get_instance(devi);	/* find out which unit */
485
486	/* cannot attach a device that has not been probed first */
487	if (instance > max_asy_instance)
488		return (DDI_FAILURE);
489
490	if (cmd != DDI_RESUME) {
491		/* Allocate soft state space */
492		if (ddi_soft_state_zalloc(su_asycom, instance) != DDI_SUCCESS) {
493			cmn_err(CE_WARN, "su%d: cannot allocate soft state",
494			    instance);
495			goto error;
496		}
497	}
498	state = SOFTSTATE;
499
500	asy = (struct asycom *)ddi_get_soft_state(su_asycom, instance);
501
502	if (asy == NULL) {
503		cmn_err(CE_WARN, "su%d: cannot get soft state", instance);
504		goto error;
505	}
506
507	switch (cmd) {
508		case DDI_ATTACH:
509			break;
510		case DDI_RESUME: {
511			struct asyncline *async;
512
513			/* grab both mutex locks */
514			mutex_enter(asy->asy_excl);
515			mutex_enter(asy->asy_excl_hi);
516			if (!asy->suspended) {
517				mutex_exit(asy->asy_excl_hi);
518				mutex_exit(asy->asy_excl);
519				return (DDI_SUCCESS);
520			}
521			/*
522			 * re-setup all the registers and enable interrupts if
523			 * needed
524			 */
525			async = (struct asyncline *)asy->asy_priv;
526			if ((async) && (async->async_flags & ASYNC_ISOPEN))
527				(void) asy_program(asy, ASY_INIT);
528			asy->suspended = B_FALSE;
529			mutex_exit(asy->asy_excl_hi);
530			mutex_exit(asy->asy_excl);
531			return (DDI_SUCCESS);
532		}
533		default:
534			goto error;
535	}
536
537	attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
538	attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
539	attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
540
541	if (ddi_regs_map_setup(devi, SU_REGISTER_FILE_NO,
542	    (caddr_t *)&asy->asy_ioaddr, SU_REGOFFSET, SU_REGISTER_LEN,
543	    &attr, &asy->asy_handle) != DDI_SUCCESS) {
544		cmn_err(CE_WARN, "asyprobe regs map setup failed");
545		goto error;
546	}
547	state = REGSMAP;
548
549#ifdef DEBUG
550	if (asydebug)
551		printf("su attach mapped %p\n", (void *)asy->asy_ioaddr);
552#endif
553
554	/*
555	 * Initialize the port with default settings.
556	 */
557	asy->asy_fifo_buf = 1;
558	asy->asy_use_fifo = FIFO_OFF;
559
560	/*
561	 * Check for baudrate generator's "baud-divisor-factor" property setup
562	 * by OBP, since different UART chips might have different baudrate
563	 * generator divisor. e.g., in case of NSPG's Sputnik platform, the
564	 * baud-divisor-factor is 13, it uses dedicated 16552 "DUART" chip
565	 * instead of SuperIO. Since the baud-divisor-factor must be a positive
566	 * integer, the divisors will always be at least as large as the values
567	 * in asyspdtab[].  Make the default factor 1.
568	 */
569	asy->asy_baud_divisor_factor = ddi_prop_get_int(DDI_DEV_T_ANY, devi,
570	    DDI_PROP_DONTPASS, "baud-divisor-factor", 1);
571
572	/* set speed cap */
573	asy->asy_speed_cap = ddi_prop_get_int(DDI_DEV_T_ANY, devi,
574	    DDI_PROP_DONTPASS, "serial-speed-cap", 115200);
575
576	/* check for ASY82510 chip */
577	OUTB(ISR, 0x20);
578	if (INB(ISR) & 0x20) { /* 82510 chip is present */
579		/*
580		 * Since most of the general operation of the 82510 chip
581		 * can be done from BANK 0 (8250A/16450 compatable mode)
582		 * we will default to BANK 0.
583		 */
584		asy->asy_hwtype = ASY82510;
585		OUTB(DAT+7, 0x04); /* clear status */
586		OUTB(ISR, 0x40); /* set to bank 2 */
587		OUTB(MCR, 0x08); /* IMD */
588		OUTB(DAT, 0x21); /* FMD */
589		OUTB(ISR, 0x00); /* set to bank 0 */
590		asy->asy_trig_level = 0;
591	} else { /* Set the UART in FIFO mode if it has FIFO buffers */
592		asy->asy_hwtype = ASY16550AF;
593		OUTB(FIFOR, 0x00); /* clear fifo register */
594		asy->asy_trig_level = 0x00; /* sets the fifo Threshold to 1 */
595
596		/* set/Enable FIFO */
597		OUTB(FIFOR, FIFO_ON | FIFODMA | FIFOTXFLSH | FIFORXFLSH |
598		    (asy->asy_trig_level & 0xff));
599
600		if ((INB(ISR) & 0xc0) == 0xc0)
601			asy->asy_use_fifo = FIFO_ON;
602		else {
603			asy->asy_hwtype = ASY8250;
604			OUTB(FIFOR, 0x00); /* NO FIFOs */
605			asy->asy_trig_level = 0;
606		}
607	}
608
609	/* check for ST16C554D chip */
610	if ((ddi_prop_lookup_string(DDI_DEV_T_ANY, devi, DDI_PROP_NOTPROM |
611	    DDI_PROP_DONTPASS, "hwtype", &hwtype)) == DDI_PROP_SUCCESS) {
612		if (strcmp(hwtype, "ST16C554D") == 0)
613			asy->asy_hwtype = ASY16C554D;
614		ddi_prop_free(hwtype);
615	}
616
617	/* disable interrupts, see EXAR bug */
618	if (asy->asy_hwtype == ASY16C554D)
619		OUTB(SPR, 0);
620	OUTB(ICR, 0);
621	OUTB(LCR, DLAB); /* select baud rate generator */
622	/* Set the baud rate to 9600 */
623	OUTB(DAT+DLL, (ASY9600*asy->asy_baud_divisor_factor) & 0xff);
624	OUTB(DAT+DLH, ((ASY9600*asy->asy_baud_divisor_factor) >> 8) & 0xff);
625	OUTB(LCR, STOP1|BITS8);
626	OUTB(MCR, (DTR | RTS| OUT2));
627
628	/*
629	 * Set up the other components of the asycom structure for this port.
630	 */
631	asy->asy_excl = (kmutex_t *)
632	    kmem_zalloc(sizeof (kmutex_t), KM_SLEEP);
633	asy->asy_excl_hi = (kmutex_t *)
634	    kmem_zalloc(sizeof (kmutex_t), KM_SLEEP);
635	asy->asy_soft_lock = (kmutex_t *)
636	    kmem_zalloc(sizeof (kmutex_t), KM_SLEEP);
637	asy->asy_unit = instance;
638	asy->asy_dip = devi;
639
640	if (ddi_get_iblock_cookie(devi, 0, &asy->asy_iblock) != DDI_SUCCESS) {
641		cmn_err(CE_NOTE,
642		    "Get iblock_cookie failed-Device interrupt%x\n", instance);
643		goto error;
644	}
645
646	if (ddi_get_soft_iblock_cookie(devi, DDI_SOFTINT_HIGH,
647	    &asy->asy_soft_iblock) != DDI_SUCCESS) {
648		cmn_err(CE_NOTE, "Get iblock_cookie failed -soft interrupt%x\n",
649		    instance);
650		goto error;
651	}
652
653	mutex_init(asy->asy_soft_lock, NULL, MUTEX_DRIVER,
654	    (void *)asy->asy_soft_iblock);
655	mutex_init(asy->asy_excl, NULL, MUTEX_DRIVER, NULL);
656	mutex_init(asy->asy_excl_hi, NULL, MUTEX_DRIVER,
657	    (void *)asy->asy_iblock);
658	state = MUTEXES;
659
660	/*
661	 * Install interrupt handlers for this device.
662	 */
663	if (ddi_add_intr(devi, 0, &(asy->asy_iblock), 0, asyintr,
664	    (caddr_t)asy) != DDI_SUCCESS) {
665		cmn_err(CE_CONT,
666		    "Cannot set device interrupt for su driver\n");
667		goto error;
668	}
669	state = ADDINTR;
670
671	if (ddi_add_softintr(devi, DDI_SOFTINT_HIGH, &(asy->asy_softintr_id),
672	    &asy->asy_soft_iblock, 0, asysoftintr, (caddr_t)asy)
673	    != DDI_SUCCESS) {
674		cmn_err(CE_CONT, "Cannot set soft interrupt for su driver\n");
675		goto error;
676	}
677	state = SOFTINTR;
678
679	/* initialize the asyncline structure */
680	if (ddi_soft_state_zalloc(su_asyncline, instance) != DDI_SUCCESS) {
681		cmn_err(CE_CONT, "su%d: cannot allocate soft state", instance);
682		goto error;
683	}
684	state = ASYINIT;
685
686	async = (struct asyncline *)ddi_get_soft_state(su_asyncline, instance);
687
688	mutex_enter(asy->asy_excl);
689	async->async_common = asy;
690	cv_init(&async->async_flags_cv, NULL, CV_DEFAULT, NULL);
691	mutex_exit(asy->asy_excl);
692
693	if ((asy->sukstat = kstat_create("su", instance, "serialstat",
694	    "misc", KSTAT_TYPE_NAMED, 2, KSTAT_FLAG_VIRTUAL)) != NULL) {
695		asy->sukstat->ks_data = &asy->kstats;
696		kstat_named_init(&asy->kstats.ringover, "ring buffer overflow",
697		    KSTAT_DATA_UINT64);
698		kstat_named_init(&asy->kstats.siloover, "silo overflow",
699		    KSTAT_DATA_UINT64);
700		kstat_install(asy->sukstat);
701	}
702	state = KSTAT;
703
704	if (strcmp(ddi_node_name(devi), "rsc-console") == 0) {
705		/*
706		 * If the device is configured as the 'rsc-console'
707		 * create the minor device for this node.
708		 */
709		if (ddi_create_minor_node(devi, "ssp", S_IFCHR,
710		    asy->asy_unit | RSC_DEVICE, DDI_PSEUDO, NULL)
711		    == DDI_FAILURE) {
712			cmn_err(CE_WARN,
713			    "%s%d: Failed to create node rsc-console",
714			    ddi_get_name(devi), ddi_get_instance(devi));
715			goto error;
716		}
717
718		asy->asy_lom_console = 0;
719		asy->asy_rsc_console = 1;
720		asy->asy_rsc_control = 0;
721		asy->asy_device_type = ASY_SERIAL;
722		asy->asy_flags |= ASY_IGNORE_CD;
723
724	} else if (strcmp(ddi_node_name(devi), "lom-console") == 0) {
725		/*
726		 * If the device is configured as the 'lom-console'
727		 * create the minor device for this node.
728		 * Do not create a dialout device.
729		 * Use the same minor numbers as would be used for standard
730		 * serial instances.
731		 */
732		if (ddi_create_minor_node(devi, "lom-console", S_IFCHR,
733		    instance, DDI_NT_SERIAL_LOMCON, NULL) == DDI_FAILURE) {
734			cmn_err(CE_WARN,
735			    "%s%d: Failed to create node lom-console",
736			    ddi_get_name(devi), ddi_get_instance(devi));
737			goto error;
738		}
739		asy->asy_lom_console = 1;
740		asy->asy_rsc_console = 0;
741		asy->asy_rsc_control = 0;
742		asy->asy_device_type = ASY_SERIAL;
743		asy->asy_flags |= ASY_IGNORE_CD;
744
745	} else if (strcmp(ddi_node_name(devi), "rsc-control") == 0) {
746		/*
747		 * If the device is configured as the 'rsc-control'
748		 * create the minor device for this node.
749		 */
750		if (ddi_create_minor_node(devi, "sspctl", S_IFCHR,
751		    asy->asy_unit | RSC_DEVICE, DDI_PSEUDO, NULL)
752		    == DDI_FAILURE) {
753			cmn_err(CE_WARN, "%s%d: Failed to create rsc-control",
754			    ddi_get_name(devi), ddi_get_instance(devi));
755			goto error;
756		}
757
758		asy->asy_lom_console = 0;
759		asy->asy_rsc_console = 0;
760		asy->asy_rsc_control = 1;
761		asy->asy_device_type = ASY_SERIAL;
762		asy->asy_flags |= ASY_IGNORE_CD;
763
764	} else if (ddi_getprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,
765	    "keyboard", 0)) {
766		/*
767		 * If the device is a keyboard, then create an internal
768		 * pathname so that the dacf code will link the node into
769		 * the keyboard console stream.  See dacf.conf.
770		 */
771		if (ddi_create_internal_pathname(devi, "keyboard",
772		    S_IFCHR, instance) == DDI_FAILURE) {
773			goto error;
774		}
775		asy->asy_flags |= ASY_IGNORE_CD;	/* ignore cd */
776		asy->asy_device_type = ASY_KEYBOARD; 	/* Device type */
777	} else if (ddi_getprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,
778	    "mouse", 0)) {
779		/*
780		 * If the device is a mouse, then create an internal
781		 * pathname so that the dacf code will link the node into
782		 * the mouse stream.  See dacf.conf.
783		 */
784		if (ddi_create_internal_pathname(devi, "mouse", S_IFCHR,
785		    instance) == DDI_FAILURE) {
786			goto error;
787		}
788		asy->asy_flags |= ASY_IGNORE_CD;	/* ignore cd */
789		asy->asy_device_type = ASY_MOUSE;
790	} else {
791		/*
792		 * If not used for keyboard/mouse, create minor devices nodes
793		 * for this device
794		 */
795		/* serial-port */
796		(void) sprintf(name, "%c", (instance+'a'));
797		if (ddi_create_minor_node(devi, name, S_IFCHR, instance,
798		    DDI_NT_SERIAL_MB, NULL) == DDI_FAILURE) {
799			goto error;
800		}
801		state = MINORNODE;
802		/* serial-port:dailout */
803		(void) sprintf(name, "%c,cu", (instance+'a'));
804		if (ddi_create_minor_node(devi, name, S_IFCHR, instance|OUTLINE,
805		    DDI_NT_SERIAL_MB_DO, NULL) == DDI_FAILURE) {
806			goto error;
807		}
808		/* Property for ignoring DCD */
809		if (ddi_getprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,
810		    "ignore-cd", 0)) {
811			asy->asy_flags |= ASY_IGNORE_CD;  /* ignore cd */
812		} else {
813			asy->asy_flags &= ~ASY_IGNORE_CD;
814			/*
815			 * if ignore-cd is not available it could be
816			 * some old legacy platform, try to see
817			 * whether the old legacy property exists
818			 */
819			(void) sprintf(name,
820			    "port-%c-ignore-cd", (instance+ 'a'));
821			if (ddi_getprop(DDI_DEV_T_ANY, devi,
822			    DDI_PROP_DONTPASS, name, 0))
823				asy->asy_flags |= ASY_IGNORE_CD;
824		}
825		asy->asy_device_type = ASY_SERIAL;
826	}
827
828	/*
829	 * Fill in the polled I/O structure
830	 */
831	asy->polledio.cons_polledio_version = CONSPOLLEDIO_V0;
832	asy->polledio.cons_polledio_argument = (cons_polledio_arg_t)asy;
833	asy->polledio.cons_polledio_putchar =  asyputchar;
834	asy->polledio.cons_polledio_getchar = asygetchar;
835	asy->polledio.cons_polledio_ischar = asyischar;
836	asy->polledio.cons_polledio_enter = asy_polled_enter;
837	asy->polledio.cons_polledio_exit = asy_polled_exit;
838
839	/* Initialize saved ICR and polled_enter */
840	asy->polled_icr = 0;
841	asy->polled_enter = B_FALSE;
842
843	ddi_report_dev(devi);
844	return (DDI_SUCCESS);
845
846error:
847	if (state == MINORNODE) {
848		(void) sprintf(name, "%c", (instance+'a'));
849		ddi_remove_minor_node(devi, name);
850	}
851	if (state >= KSTAT)
852		kstat_delete(asy->sukstat);
853	if (state >= ASYINIT) {
854		cv_destroy(&async->async_flags_cv);
855		ddi_soft_state_free(su_asyncline, instance);
856	}
857	if (state >= SOFTINTR)
858		ddi_remove_softintr(asy->asy_softintr_id);
859	if (state >= ADDINTR)
860		ddi_remove_intr(devi, 0, asy->asy_iblock);
861	if (state >= MUTEXES) {
862		mutex_destroy(asy->asy_excl_hi);
863		mutex_destroy(asy->asy_excl);
864		mutex_destroy(asy->asy_soft_lock);
865		kmem_free(asy->asy_excl_hi, sizeof (kmutex_t));
866		kmem_free(asy->asy_excl, sizeof (kmutex_t));
867		kmem_free(asy->asy_soft_lock, sizeof (kmutex_t));
868	}
869	if (state >= REGSMAP)
870		ddi_regs_map_free(&asy->asy_handle);
871	if (state >= SOFTSTATE)
872		ddi_soft_state_free(su_asycom, instance);
873	/* no action for EMPTY state */
874	return (DDI_FAILURE);
875}
876
877static int
878asyinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
879	void **result)
880{
881	_NOTE(ARGUNUSED(dip))
882	register dev_t dev = (dev_t)arg;
883	register int instance, error;
884	struct asycom *asy;
885
886	if ((instance = UNIT(dev)) > max_asy_instance)
887		return (DDI_FAILURE);
888
889	switch (infocmd) {
890		case DDI_INFO_DEVT2DEVINFO:
891			asy = (struct asycom *)ddi_get_soft_state(su_asycom,
892			    instance);
893			if (asy->asy_dip == NULL)
894				error = DDI_FAILURE;
895			else {
896				*result = (void *) asy->asy_dip;
897				error = DDI_SUCCESS;
898			}
899			break;
900		case DDI_INFO_DEVT2INSTANCE:
901			*result = (void *)(uintptr_t)instance;
902			error = DDI_SUCCESS;
903			break;
904		default:
905			error = DDI_FAILURE;
906	}
907	return (error);
908}
909
910static int
911asyopen(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr)
912{
913	_NOTE(ARGUNUSED(sflag))
914	struct asycom	*asy;
915	struct asyncline *async;
916	int		mcr;
917	int		unit;
918	int 		len;
919	struct termios 	*termiosp;
920
921#ifdef DEBUG
922	if (asydebug & ASY_DEBUG_CLOSE)
923		printf("open\n");
924#endif
925	unit = UNIT(*dev);
926	if (unit > max_asy_instance)
927		return (ENXIO);		/* unit not configured */
928
929	async = (struct asyncline *)ddi_get_soft_state(su_asyncline, unit);
930	if (async == NULL)
931		return (ENXIO);
932
933	asy = async->async_common;
934	if (asy == NULL)
935		return (ENXIO);		/* device not found by autoconfig */
936
937	mutex_enter(asy->asy_excl);
938	asy->asy_priv = (caddr_t)async;
939
940again:
941	mutex_enter(asy->asy_excl_hi);
942	/*
943	 * Block waiting for carrier to come up, unless this is a no-delay open.
944	 */
945	if (!(async->async_flags & ASYNC_ISOPEN)) {
946		/*
947		 * If this port is for a RSC console or control
948		 * use the following termio info
949		 */
950		if (asy->asy_rsc_console || asy->asy_rsc_control) {
951			async->async_ttycommon.t_cflag = CIBAUDEXT | CBAUDEXT |
952			    (B115200 & CBAUD);
953			async->async_ttycommon.t_cflag |= ((B115200 << IBSHIFT)
954			    & CIBAUD);
955			async->async_ttycommon.t_cflag |= CS8 | CREAD | CLOCAL;
956		} else if (asy->asy_lom_console) {
957			async->async_ttycommon.t_cflag = B9600 & CBAUD;
958			async->async_ttycommon.t_cflag |= ((B9600 << IBSHIFT)
959			    & CIBAUD);
960			async->async_ttycommon.t_cflag |= CS8 | CREAD | CLOCAL;
961		} else {
962
963			/*
964			 * Set the default termios settings (cflag).
965			 * Others are set in ldterm.  Release the spin
966			 * mutex as we can block here, reaquire before
967			 * calling asy_program.
968			 */
969			mutex_exit(asy->asy_excl_hi);
970			if (ddi_getlongprop(DDI_DEV_T_ANY, ddi_root_node(),
971			    0, "ttymodes", (caddr_t)&termiosp, &len)
972			    == DDI_PROP_SUCCESS &&
973			    len == sizeof (struct termios)) {
974				async->async_ttycommon.t_cflag =
975				    termiosp->c_cflag;
976				kmem_free(termiosp, len);
977			} else {
978				cmn_err(CE_WARN,
979					"su: couldn't get ttymodes property!");
980			}
981			mutex_enter(asy->asy_excl_hi);
982		}
983		async->async_ttycommon.t_iflag = 0;
984		async->async_ttycommon.t_iocpending = NULL;
985		async->async_ttycommon.t_size.ws_row = 0;
986		async->async_ttycommon.t_size.ws_col = 0;
987		async->async_ttycommon.t_size.ws_xpixel = 0;
988		async->async_ttycommon.t_size.ws_ypixel = 0;
989		async->async_dev = *dev;
990		async->async_wbufcid = 0;
991
992		async->async_startc = CSTART;
993		async->async_stopc = CSTOP;
994		(void) asy_program(asy, ASY_INIT);
995	} else if ((async->async_ttycommon.t_flags & TS_XCLUDE) &&
996	    secpolicy_excl_open(cr) != 0) {
997		mutex_exit(asy->asy_excl_hi);
998		mutex_exit(asy->asy_excl);
999		return (EBUSY);
1000	} else if ((*dev & OUTLINE) && !(async->async_flags & ASYNC_OUT)) {
1001		mutex_exit(asy->asy_excl_hi);
1002		mutex_exit(asy->asy_excl);
1003		return (EBUSY);
1004	}
1005
1006	if (*dev & OUTLINE)
1007		async->async_flags |= ASYNC_OUT;
1008
1009	/* Raise DTR on every open */
1010	mcr = INB(MCR);
1011	OUTB(MCR, mcr|DTR);
1012
1013	/*
1014	 * Check carrier.
1015	 */
1016	if (asy->asy_flags & ASY_IGNORE_CD)
1017		async->async_ttycommon.t_flags |= TS_SOFTCAR;
1018	if ((async->async_ttycommon.t_flags & TS_SOFTCAR) ||
1019	    (INB(MSR) & DCD))
1020		async->async_flags |= ASYNC_CARR_ON;
1021	else
1022		async->async_flags &= ~ASYNC_CARR_ON;
1023	mutex_exit(asy->asy_excl_hi);
1024
1025	/*
1026	 * If FNDELAY and FNONBLOCK are clear, block until carrier up.
1027	 * Quit on interrupt.
1028	 */
1029	if (!(flag & (FNDELAY|FNONBLOCK)) &&
1030	    !(async->async_ttycommon.t_cflag & CLOCAL)) {
1031		if (!(async->async_flags & (ASYNC_CARR_ON|ASYNC_OUT)) ||
1032		    ((async->async_flags & ASYNC_OUT) &&
1033		    !(*dev & OUTLINE))) {
1034				async->async_flags |= ASYNC_WOPEN;
1035				if (cv_wait_sig(&async->async_flags_cv,
1036				    asy->asy_excl) == 0) {
1037					async->async_flags &= ~ASYNC_WOPEN;
1038					mutex_exit(asy->asy_excl);
1039					return (EINTR);
1040				}
1041				async->async_flags &= ~ASYNC_WOPEN;
1042				goto again;
1043		}
1044	} else if ((async->async_flags & ASYNC_OUT) && !(*dev & OUTLINE)) {
1045		mutex_exit(asy->asy_excl);
1046		return (EBUSY);
1047	}
1048
1049	if (asy->suspended) {
1050		mutex_exit(asy->asy_excl);
1051		(void) ddi_dev_is_needed(asy->asy_dip, 0, 1);
1052		mutex_enter(asy->asy_excl);
1053	}
1054
1055	async->async_ttycommon.t_readq = rq;
1056	async->async_ttycommon.t_writeq = WR(rq);
1057	rq->q_ptr = WR(rq)->q_ptr = (caddr_t)async;
1058	mutex_exit(asy->asy_excl);
1059	qprocson(rq);
1060	async->async_flags |= ASYNC_ISOPEN;
1061	async->async_polltid = 0;
1062	return (0);
1063}
1064
1065static void
1066async_progress_check(void *arg)
1067{
1068	struct asyncline *async = arg;
1069	struct asycom	 *asy = async->async_common;
1070	mblk_t *bp;
1071
1072	/*
1073	 * We define "progress" as either waiting on a timed break or delay, or
1074	 * having had at least one transmitter interrupt.  If none of these are
1075	 * true, then just terminate the output and wake up that close thread.
1076	 */
1077	mutex_enter(asy->asy_excl);
1078	mutex_enter(asy->asy_excl_hi);
1079	if (!(async->async_flags & (ASYNC_BREAK|ASYNC_DELAY|ASYNC_PROGRESS))) {
1080		async->async_ocnt = 0;
1081		async->async_flags &= ~ASYNC_BUSY;
1082		async->async_timer = 0;
1083		bp = async->async_xmitblk;
1084		async->async_xmitblk = NULL;
1085		mutex_exit(asy->asy_excl_hi);
1086		if (bp != NULL)
1087			freeb(bp);
1088		/*
1089		 * Since this timer is running, we know that we're in exit(2).
1090		 * That means that the user can't possibly be waiting on any
1091		 * valid ioctl(2) completion anymore, and we should just flush
1092		 * everything.
1093		 */
1094		flushq(async->async_ttycommon.t_writeq, FLUSHALL);
1095		cv_broadcast(&async->async_flags_cv);
1096	} else {
1097		async->async_flags &= ~ASYNC_PROGRESS;
1098		async->async_timer = timeout(async_progress_check, async,
1099		    drv_usectohz(su_drain_check));
1100		mutex_exit(asy->asy_excl_hi);
1101	}
1102	mutex_exit(asy->asy_excl);
1103}
1104
1105/*
1106 * Close routine.
1107 */
1108static int
1109asyclose(queue_t *q, int flag)
1110{
1111	struct asyncline *async;
1112	struct asycom	 *asy;
1113	int icr, lcr;
1114	int		nohupcl;
1115
1116
1117#ifdef DEBUG
1118	if (asydebug & ASY_DEBUG_CLOSE)
1119		printf("close\n");
1120#endif
1121	async = q->q_ptr;
1122	ASSERT(async != NULL);
1123	asy = async->async_common;
1124
1125	/* get the nohupcl OBP property of this device */
1126	nohupcl = ddi_getprop(DDI_DEV_T_ANY, asy->asy_dip, DDI_PROP_DONTPASS,
1127	    "nohupcl", 0);
1128
1129	mutex_enter(asy->asy_excl);
1130	async->async_flags |= ASYNC_CLOSING;
1131
1132	/*
1133	 * Turn off PPS handling early to avoid events occuring during
1134	 * close.  Also reset the DCD edge monitoring bit.
1135	 */
1136	mutex_enter(asy->asy_excl_hi);
1137	asy->asy_flags &= ~(ASY_PPS | ASY_PPS_EDGE);
1138	mutex_exit(asy->asy_excl_hi);
1139
1140	/*
1141	 * There are two flavors of break -- timed (M_BREAK or TCSBRK) and
1142	 * untimed (TIOCSBRK).  For the timed case, these are enqueued on our
1143	 * write queue and there's a timer running, so we don't have to worry
1144	 * about them.  For the untimed case, though, the user obviously made a
1145	 * mistake, because these are handled immediately.  We'll terminate the
1146	 * break now and honor his implicit request by discarding the rest of
1147	 * the data.
1148	 */
1149	if (!(async->async_flags & ASYNC_BREAK)) {
1150		mutex_enter(asy->asy_excl_hi);
1151		lcr = INB(LCR);
1152		if (lcr & SETBREAK) {
1153			OUTB(LCR, (lcr & ~SETBREAK));
1154		}
1155		mutex_exit(asy->asy_excl_hi);
1156		if (lcr & SETBREAK)
1157			goto nodrain;
1158	}
1159
1160	/*
1161	 * If the user told us not to delay the close ("non-blocking"), then
1162	 * don't bother trying to drain.
1163	 *
1164	 * If the user did M_STOP (ASYNC_STOPPED), there's no hope of ever
1165	 * getting an M_START (since these messages aren't enqueued), and the
1166	 * only other way to clear the stop condition is by loss of DCD, which
1167	 * would discard the queue data.  Thus, we drop the output data if
1168	 * ASYNC_STOPPED is set.
1169	 */
1170	if ((flag & (FNDELAY|FNONBLOCK)) ||
1171	    (async->async_flags & ASYNC_STOPPED)) {
1172		goto nodrain;
1173	}
1174
1175	/*
1176	 * If there's any pending output, then we have to try to drain it.
1177	 * There are two main cases to be handled:
1178	 *	- called by close(2): need to drain until done or until
1179	 *	  a signal is received.  No timeout.
1180	 *	- called by exit(2): need to drain while making progress
1181	 *	  or until a timeout occurs.  No signals.
1182	 *
1183	 * If we can't rely on receiving a signal to get us out of a hung
1184	 * session, then we have to use a timer.  In this case, we set a timer
1185	 * to check for progress in sending the output data -- all that we ask
1186	 * (at each interval) is that there's been some progress made.  Since
1187	 * the interrupt routine grabs buffers from the write queue, we can't
1188	 * trust async_ocnt.  Instead, we use a flag.
1189	 *
1190	 * Note that loss of carrier will cause the output queue to be flushed,
1191	 * and we'll wake up again and finish normally.
1192	 */
1193	if (!ddi_can_receive_sig() && su_drain_check != 0) {
1194		async->async_flags &= ~ASYNC_PROGRESS;
1195		async->async_timer = timeout(async_progress_check, async,
1196		    drv_usectohz(su_drain_check));
1197	}
1198
1199	while (async->async_ocnt > 0 ||
1200	    async->async_ttycommon.t_writeq->q_first != NULL ||
1201	    (async->async_flags & (ASYNC_BUSY|ASYNC_BREAK|ASYNC_DELAY))) {
1202		if (cv_wait_sig(&async->async_flags_cv, asy->asy_excl) == 0)
1203			break;
1204	}
1205	if (async->async_timer != 0) {
1206		(void) untimeout(async->async_timer);
1207		async->async_timer = 0;
1208	}
1209
1210nodrain:
1211	mutex_enter(asy->asy_excl_hi);
1212
1213	/* turn off the loopback mode */
1214	if ((async->async_dev != rconsdev) &&
1215	    (async->async_dev != kbddev) &&
1216	    (async->async_dev != stdindev)) {
1217		OUTB(MCR, INB(MCR) & ~ ASY_LOOP);
1218	}
1219
1220	async->async_ocnt = 0;
1221	if (async->async_xmitblk != NULL)
1222		freeb(async->async_xmitblk);
1223	async->async_xmitblk = NULL;
1224
1225	/*
1226	 * If the "nohupcl" OBP property is set for this device, do
1227	 * not turn off DTR and RTS no matter what.  Otherwise, if the
1228	 * line has HUPCL set or is incompletely opened, turn off DTR
1229	 * and RTS to fix the modem line.
1230	 */
1231	if (!nohupcl && ((async->async_ttycommon.t_cflag & HUPCL) ||
1232	    (async->async_flags & ASYNC_WOPEN))) {
1233		/* turn off DTR, RTS but NOT interrupt to 386 */
1234		OUTB(MCR, OUT2);
1235		mutex_exit(asy->asy_excl_hi);
1236		/*
1237		 * Don't let an interrupt in the middle of close
1238		 * bounce us back to the top; just continue closing
1239		 * as if nothing had happened.
1240		 */
1241		if (cv_wait_sig(&lbolt_cv, asy->asy_excl) == 0)
1242			goto out;
1243		mutex_enter(asy->asy_excl_hi);
1244	}
1245
1246	/*
1247	 * If nobody's using it now, turn off receiver interrupts.
1248	 */
1249	if ((async->async_flags & (ASYNC_WOPEN|ASYNC_ISOPEN)) == 0) {
1250		icr = INB(ICR);
1251		OUTB(ICR, (icr & ~RIEN));
1252	}
1253	mutex_exit(asy->asy_excl_hi);
1254out:
1255	/*
1256	 * Clear out device state.
1257	 */
1258	async->async_flags = 0;
1259	ttycommon_close(&async->async_ttycommon);
1260	cv_broadcast(&async->async_flags_cv);
1261
1262	/*
1263	 * Clear ASY_DOINGSOFT and ASY_NEEDSOFT in case we were in
1264	 * async_softint or an interrupt was pending when the process
1265	 * using the port exited.
1266	 */
1267	asy->asy_flags &= ~ASY_DOINGSOFT & ~ASY_NEEDSOFT;
1268
1269	/*
1270	 * Cancel outstanding "bufcall" request.
1271	 */
1272	if (async->async_wbufcid) {
1273		unbufcall(async->async_wbufcid);
1274		async->async_wbufcid = 0;
1275	}
1276
1277	/*
1278	 * If inperim is true, it means the port is closing while there's
1279	 * a pending software interrupt.  async_flags has been zeroed out,
1280	 * so this instance of leaveq() needs to be called before we call
1281	 * qprocsoff() to disable services on the q.  If inperim is false,
1282	 * leaveq() has already been called or we're not in a perimeter.
1283	 */
1284	if (asy->inperim == B_TRUE) {
1285		asy->inperim = B_FALSE;
1286		mutex_exit(asy->asy_excl);
1287		leaveq(q);
1288	} else {
1289		mutex_exit(asy->asy_excl);
1290	}
1291
1292	/* Note that qprocsoff can't be done until after interrupts are off */
1293	qprocsoff(q);
1294	q->q_ptr = WR(q)->q_ptr = NULL;
1295	async->async_ttycommon.t_readq = NULL;
1296	async->async_ttycommon.t_writeq = NULL;
1297
1298	return (0);
1299}
1300
1301/*
1302 * Checks to see if the serial port is still transmitting
1303 * characters.  It returns true when there are characters
1304 * queued to transmit,  when the holding register contains
1305 * a byte, or when the shifting register still contains
1306 * data to send.
1307 *
1308 */
1309static boolean_t
1310asy_isbusy(struct asycom *asy)
1311{
1312	struct asyncline *async;
1313
1314#ifdef DEBUG
1315	if (asydebug & ASY_DEBUG_EOT)
1316		printf("isbusy\n");
1317#endif
1318	async = (struct asyncline *)asy->asy_priv;
1319	ASSERT(mutex_owned(asy->asy_excl));
1320	ASSERT(mutex_owned(asy->asy_excl_hi));
1321	return ((async->async_ocnt > 0) ||
1322	    ((INB(LSR) & XSRE) == 0));
1323}
1324
1325/*
1326 * Program the ASY port. Most of the async operation is based on the values
1327 * of 'c_iflag' and 'c_cflag'.
1328 */
1329static int
1330asy_program(struct asycom *asy, int mode)
1331{
1332	struct asyncline *async;
1333	int baudrate, c_flag;
1334	int icr, lcr;
1335	int ocflags;
1336	int error = 0;
1337
1338	ASSERT(mutex_owned(asy->asy_excl));
1339	ASSERT(mutex_owned(asy->asy_excl_hi));
1340
1341#ifdef DEBUG
1342	if (asydebug & ASY_DEBUG_PROCS)
1343		printf("program\n");
1344#endif
1345	async = (struct asyncline *)asy->asy_priv;
1346
1347	baudrate = async->async_ttycommon.t_cflag & CBAUD;
1348	if (async->async_ttycommon.t_cflag & CBAUDEXT)
1349		baudrate += 16;
1350
1351	/* Limit baudrate so it can't index out of baudtable */
1352	if (baudrate >= N_SU_SPEEDS) baudrate = B9600;
1353
1354	/*
1355	 * If baud rate requested is greater than the speed cap
1356	 * or is an unsupported baud rate then reset t_cflag baud
1357	 * to the last valid baud rate.  If this is the initial
1358	 * pass through asy_program then set it to 9600.
1359	 */
1360	if (((baudrate > 0) && (asyspdtab[baudrate] == 0)) ||
1361	    (baudtable[baudrate] > asy->asy_speed_cap)) {
1362		async->async_ttycommon.t_cflag &= ~CBAUD & ~CBAUDEXT &
1363		    ~CIBAUD & ~CIBAUDEXT;
1364		if (mode == ASY_INIT) {
1365			async->async_ttycommon.t_cflag |= B9600;
1366			async->async_ttycommon.t_cflag |= B9600 << IBSHIFT;
1367			baudrate = B9600;
1368		} else {
1369			async->async_ttycommon.t_cflag |=
1370			    (asy->asy_ocflags & (CBAUD | CBAUDEXT |
1371			    CIBAUD | CIBAUDEXT));
1372			error = EINVAL;
1373			goto end;
1374		}
1375	}
1376
1377	/*
1378	 * If CIBAUD and CIBAUDEXT are zero then we should set them to
1379	 * the equivelant output baud bits.  Else, if CIBAUD and CIBAUDEXT
1380	 * don't match CBAUD and CBAUDEXT respectively then we should
1381	 * notify the requestor that we do not support split speeds.
1382	 */
1383	if ((async->async_ttycommon.t_cflag  & (CIBAUD|CIBAUDEXT)) == 0) {
1384		async->async_ttycommon.t_cflag |=
1385		    (async->async_ttycommon.t_cflag & CBAUD) << IBSHIFT;
1386		if (async->async_ttycommon.t_cflag & CBAUDEXT)
1387			async->async_ttycommon.t_cflag |= CIBAUDEXT;
1388	} else {
1389		if ((((async->async_ttycommon.t_cflag & CBAUD) << IBSHIFT) !=
1390		    (async->async_ttycommon.t_cflag & CIBAUD)) ||
1391		    !(((async->async_ttycommon.t_cflag & (CBAUDEXT |
1392		    CIBAUDEXT)) == (CBAUDEXT | CIBAUDEXT)) ||
1393		    ((async->async_ttycommon.t_cflag & (CBAUDEXT |
1394		    CIBAUDEXT)) == 0))) {
1395			async->async_ttycommon.t_cflag &= ~CBAUD & ~CBAUDEXT &
1396			    ~CIBAUD & ~CIBAUDEXT;
1397			async->async_ttycommon.t_cflag |=
1398			    (asy->asy_ocflags & (CBAUD | CBAUDEXT |
1399			    CIBAUD | CIBAUDEXT));
1400			error = EINVAL;
1401			goto end;
1402		}
1403	}
1404
1405	c_flag = async->async_ttycommon.t_cflag &
1406	    (CLOCAL | CREAD | CSTOPB | CSIZE | PARENB | PARODD | CBAUD |
1407	    CBAUDEXT | CIBAUD | CIBAUDEXT);
1408
1409	/* disable interrupts, see EXAR bug */
1410	if (asy->asy_hwtype == ASY16C554D)
1411		OUTB(SPR, 0);
1412	OUTB(ICR, 0);
1413
1414	ocflags = asy->asy_ocflags;
1415
1416	/* flush/reset the status registers */
1417	if (mode == ASY_INIT) {
1418		(void) INB(DAT);
1419		(void) INB(ISR);
1420		(void) INB(LSR);
1421		(void) INB(MSR);
1422	}
1423
1424	if (ocflags != (c_flag & ~CLOCAL) || mode == ASY_INIT) {
1425		/* Set line control */
1426		lcr = INB(LCR);
1427		lcr &= ~(WLS0|WLS1|STB|PEN|EPS);
1428
1429		if (c_flag & CSTOPB)
1430			lcr |= STB;	/* 2 stop bits */
1431
1432		if (c_flag & PARENB)
1433			lcr |= PEN;
1434
1435		if ((c_flag & PARODD) == 0)
1436			lcr |= EPS;
1437
1438		switch (c_flag & CSIZE) {
1439		case CS5:
1440			lcr |= BITS5;
1441			break;
1442		case CS6:
1443			lcr |= BITS6;
1444			break;
1445		case CS7:
1446			lcr |= BITS7;
1447			break;
1448		case CS8:
1449			lcr |= BITS8;
1450			break;
1451		}
1452
1453		/* set the baud rate when the rate is NOT B0 */
1454		if (baudrate != 0) {
1455			OUTB(LCR, DLAB);
1456			OUTB(DAT, (asyspdtab[baudrate] *
1457			    asy->asy_baud_divisor_factor) & 0xff);
1458			OUTB(ICR, ((asyspdtab[baudrate] *
1459			    asy->asy_baud_divisor_factor) >> 8) & 0xff);
1460		}
1461		/* set the line control modes */
1462		OUTB(LCR, lcr);
1463
1464		/*
1465		 * if transitioning from CREAD off to CREAD on,
1466		 * flush the FIFO buffer if we have one.
1467		 */
1468		if ((ocflags & CREAD) == 0 && (c_flag & CREAD)) {
1469			if (asy->asy_use_fifo == FIFO_ON) {
1470				OUTB(FIFOR, FIFO_ON | FIFODMA | FIFORXFLSH |
1471				    (asy->asy_trig_level & 0xff));
1472			}
1473		}
1474
1475		/* remember the new cflags */
1476		asy->asy_ocflags = c_flag & ~CLOCAL;
1477	}
1478
1479	/* whether or not CLOCAL is set, modify the modem control lines */
1480	if (baudrate == 0)
1481		/* B0 has been issued, lower DTR */
1482		OUTB(MCR, RTS|OUT2);
1483	else
1484		/* raise DTR */
1485		OUTB(MCR, DTR|RTS|OUT2);
1486
1487	/*
1488	 * Call the modem status interrupt handler to check for the carrier
1489	 * in case CLOCAL was turned off after the carrier came on.
1490	 * (Note: Modem status interrupt is not enabled if CLOCAL is ON.)
1491	 */
1492	async_msint(asy);
1493
1494	/* Set interrupt control */
1495	if ((c_flag & CLOCAL) && !(async->async_ttycommon.t_cflag & CRTSCTS))
1496		/*
1497		 * direct-wired line ignores DCD, so we don't enable modem
1498		 * status interrupts.
1499		 */
1500		icr = (TIEN | SIEN);
1501	else
1502		icr = (TIEN | SIEN | MIEN);
1503
1504	if (c_flag & CREAD)
1505		icr |= RIEN;
1506
1507	OUTB(ICR, icr);
1508end:
1509	return (error);
1510}
1511
1512/*
1513 * Polled mode support -- all functions called with interrupts
1514 * disabled.
1515 */
1516
1517static void
1518asyputchar(cons_polledio_arg_t arg, uchar_t c)
1519{
1520	struct asycom *asy = (struct asycom *)arg;
1521
1522	/*
1523	 * If we see a line feed make sure to also
1524	 * put out a carriage return.
1525	 */
1526	if (c == '\n')
1527		asyputchar(arg, '\r');
1528
1529	while ((INB(LSR) & XHRE) == 0) {
1530		/* wait for the transmission to complete */
1531		drv_usecwait(10);
1532	}
1533
1534	/* ouput the character */
1535	OUTB(DAT, c);
1536}
1537
1538/*
1539 * Determines if there is a character avaialable for
1540 * reading.
1541 */
1542static boolean_t
1543asyischar(cons_polledio_arg_t arg)
1544{
1545	struct asycom *asy = (struct asycom *)arg;
1546	return ((INB(LSR) & RCA) != 0);
1547}
1548
1549static int
1550asygetchar(cons_polledio_arg_t arg)
1551{
1552	struct asycom *asy = (struct asycom *)arg;
1553
1554	/*
1555	 * Spin waiting for a character to be
1556	 * available to read.
1557	 */
1558	while (!asyischar(arg))
1559		drv_usecwait(10);
1560
1561	return (INB(DAT));
1562}
1563
1564/*
1565 * Called when machine is transitioning to polled mode
1566 */
1567static void
1568asy_polled_enter(cons_polledio_arg_t arg)
1569{
1570	struct asycom *asy = (struct asycom *)arg;
1571
1572	mutex_enter(asy->asy_excl);
1573	mutex_enter(asy->asy_excl_hi);
1574
1575	/*
1576	 * If this is the first time that asy_polled_enter()
1577	 * has been called, during this transition request,
1578	 * save the ICR. Clear the software interrupt
1579	 * flags since we won't be able to handle these when
1580	 * we are in polled mode.
1581	 */
1582	if (!asy->polled_enter) {
1583		asy->polled_enter = B_TRUE;
1584		asy->polled_icr = INB(ICR);
1585
1586		/* Disable HW interrupts */
1587		if (asy->asy_hwtype == ASY16C554D)
1588			OUTB(SPR, 0);
1589		OUTB(ICR, 0);
1590
1591		asy->asy_flags &= ~ASY_DOINGSOFT & ~ASY_NEEDSOFT;
1592	}
1593	mutex_exit(asy->asy_excl_hi);
1594	mutex_exit(asy->asy_excl);
1595}
1596
1597/*
1598 * Called when machine is transitioning from polled mode.
1599 */
1600static void
1601asy_polled_exit(cons_polledio_arg_t arg)
1602{
1603	struct asycom *asy = (struct asycom *)arg;
1604
1605	mutex_enter(asy->asy_excl);
1606	mutex_enter(asy->asy_excl_hi);
1607
1608	/* Restore the ICR */
1609	OUTB(ICR, asy->polled_icr);
1610
1611	/*
1612	 * We have finished this polled IO transition.
1613	 * Set polled_enter to B_FALSE to note this.
1614	 */
1615	asy->polled_enter = B_FALSE;
1616	mutex_exit(asy->asy_excl_hi);
1617	mutex_exit(asy->asy_excl);
1618}
1619
1620/*
1621 * asyintr() is the High Level Interrupt Handler.
1622 *
1623 * There are four different interrupt types indexed by ISR register values:
1624 *		0: modem
1625 *		1: Tx holding register is empty, ready for next char
1626 *		2: Rx register now holds a char to be picked up
1627 *		3: error or break on line
1628 * This routine checks the Bit 0 (interrupt-not-pending) to determine if
1629 * the interrupt is from this port.
1630 */
1631uint_t
1632asyintr(caddr_t argasy)
1633{
1634	struct asycom		*asy = (struct asycom *)argasy;
1635	struct asyncline	*async;
1636	int			ret_status = DDI_INTR_UNCLAIMED;
1637	uchar_t			interrupt_id, lsr;
1638
1639	interrupt_id = INB(ISR) & 0x0F;
1640	async = (struct asyncline *)asy->asy_priv;
1641	if ((async == NULL) ||
1642	    !(async->async_flags & (ASYNC_ISOPEN|ASYNC_WOPEN))) {
1643		if (interrupt_id & NOINTERRUPT)  {
1644			return (DDI_INTR_UNCLAIMED);
1645		} else {
1646			lsr = INB(LSR);
1647			if ((lsr & BRKDET) &&
1648			    ((abort_enable == KIOCABORTENABLE) &&
1649			    (async->async_dev == rconsdev)))
1650				abort_sequence_enter((char *)NULL);
1651			else {
1652				/* reset line status */
1653				(void) INB(LSR);
1654				/* discard any data */
1655				(void) INB(DAT);
1656				/* reset modem status */
1657				(void) INB(MSR);
1658				return (DDI_INTR_CLAIMED);
1659			}
1660		}
1661	}
1662	/*
1663	 * Spurious interrupts happen in this driver
1664	 * because of the transmission on serial port not handled
1665	 * properly.
1666	 *
1667	 * The reasons for Spurious interrupts are:
1668	 *    1. There is a path in async_nstart which transmits
1669	 *	 characters without going through interrupt services routine
1670	 *	 which causes spurious interrupts to happen.
1671	 *    2. In the async_txint more than one character is sent
1672	 *	 in one interrupt service.
1673	 *    3. In async_rxint more than one characters are received in
1674	 *	 in one interrupt service.
1675	 *
1676	 * Hence we have flags to indicate that such scenerio has happened.
1677	 * and claim only such interrupts and others we donot claim it
1678	 * as it could be a indicator of some hardware problem.
1679	 *
1680	 */
1681	if (interrupt_id & NOINTERRUPT) {
1682		mutex_enter(asy->asy_excl_hi);
1683		if ((asy->asy_xmit_count > 1) ||
1684		    (asy->asy_out_of_band_xmit > 0) ||
1685		    (asy->asy_rx_count > 1)) {
1686			asy->asy_xmit_count = 0;
1687			asy->asy_out_of_band_xmit = 0;
1688			asy->asy_rx_count = 0;
1689			mutex_exit(asy->asy_excl_hi);
1690			return (DDI_INTR_CLAIMED);
1691		} else {
1692			mutex_exit(asy->asy_excl_hi);
1693			return (DDI_INTR_UNCLAIMED);
1694		}
1695	}
1696	ret_status = DDI_INTR_CLAIMED;
1697	mutex_enter(asy->asy_excl_hi);
1698	if (asy->asy_hwtype == ASY82510)
1699		OUTB(ISR, 0x00); /* set bank 0 */
1700
1701#ifdef DEBUG
1702	if (asydebug & ASY_DEBUG_INTR)
1703		prom_printf("l");
1704#endif
1705	lsr = INB(LSR);
1706	switch (interrupt_id) {
1707	case RxRDY:
1708	case RSTATUS:
1709	case FFTMOUT:
1710		/* receiver interrupt or receiver errors */
1711		async_rxint(asy, lsr);
1712		break;
1713	case TxRDY:
1714		/* transmit interrupt */
1715		async_txint(asy, lsr);
1716		break;
1717	case MSTATUS:
1718		/* modem status interrupt */
1719		async_msint(asy);
1720		break;
1721	}
1722	mutex_exit(asy->asy_excl_hi);
1723	return (ret_status);
1724}
1725
1726/*
1727 * Transmitter interrupt service routine.
1728 * If there is more data to transmit in the current pseudo-DMA block,
1729 * send the next character if output is not stopped or draining.
1730 * Otherwise, queue up a soft interrupt.
1731 *
1732 * XXX -  Needs review for HW FIFOs.
1733 */
1734static void
1735async_txint(struct asycom *asy, uchar_t lsr)
1736{
1737	struct asyncline *async = (struct asyncline *)asy->asy_priv;
1738	int		fifo_len;
1739	int		xmit_progress;
1740
1741	asycheckflowcontrol_hw(asy);
1742
1743	/*
1744	 * If ASYNC_BREAK has been set, return to asyintr()'s context to
1745	 * claim the interrupt without performing any action.
1746	 */
1747	if (async->async_flags & ASYNC_BREAK)
1748		return;
1749
1750	fifo_len = asy->asy_fifo_buf; /* with FIFO buffers */
1751
1752	/*
1753	 * Check for flow control and do the needed action.
1754	 */
1755	if (asycheckflowcontrol_sw(asy)) {
1756		return;
1757	}
1758
1759	if (async->async_ocnt > 0 &&
1760	    !(async->async_flags & (ASYNC_HW_OUT_FLW|ASYNC_STOPPED))) {
1761		xmit_progress = 0;
1762		while (fifo_len > 0 && async->async_ocnt > 0) {
1763			if (lsr & XHRE) {
1764				OUTB(DAT, *async->async_optr++);
1765				fifo_len--;
1766				async->async_ocnt--;
1767				xmit_progress++;
1768			}
1769			/*
1770			 * Reading the lsr, (moved reading at the end of
1771			 * while loop) as already we have read once at
1772			 * the beginning of interrupt service
1773			 */
1774			lsr = INB(LSR);
1775		}
1776		asy->asy_xmit_count = xmit_progress;
1777		if (xmit_progress > 0)
1778			async->async_flags |= ASYNC_PROGRESS;
1779	}
1780
1781	if (fifo_len == 0) {
1782		return;
1783	}
1784
1785
1786	ASYSETSOFT(asy);
1787}
1788
1789/*
1790 * Receiver interrupt: RxRDY interrupt, FIFO timeout interrupt or receive
1791 * error interrupt.
1792 * Try to put the character into the circular buffer for this line; if it
1793 * overflows, indicate a circular buffer overrun. If this port is always
1794 * to be serviced immediately, or the character is a STOP character, or
1795 * more than 15 characters have arrived, queue up a soft interrupt to
1796 * drain the circular buffer.
1797 * XXX - needs review for hw FIFOs support.
1798 */
1799
1800static void
1801async_rxint(struct asycom *asy, uchar_t lsr)
1802{
1803	struct asyncline *async = (struct asyncline *)asy->asy_priv;
1804	uchar_t c = 0;
1805	uint_t s = 0, needsoft = 0;
1806	register tty_common_t *tp;
1807
1808	tp = &async->async_ttycommon;
1809	if (!(tp->t_cflag & CREAD)) {
1810		if (lsr & (RCA|PARERR|FRMERR|BRKDET|OVRRUN)) {
1811			(void) (INB(DAT) & 0xff);
1812		}
1813		return; /* line is not open for read? */
1814	}
1815	asy->asy_rx_count = 0;
1816	while (lsr & (RCA|PARERR|FRMERR|BRKDET|OVRRUN)) {
1817		c = 0;
1818		s = 0;
1819		asy->asy_rx_count++;
1820		if (lsr & RCA) {
1821			c = INB(DAT) & 0xff;
1822			/*
1823			 * Even a single character is received
1824			 * we need Soft interrupt to pass it to
1825			 * higher layers.
1826			 */
1827			needsoft = 1;
1828		}
1829
1830		/* Check for character break sequence */
1831		if ((abort_enable == KIOCABORTALTERNATE) &&
1832		    (async->async_dev == rconsdev)) {
1833			if (abort_charseq_recognize(c))
1834				abort_sequence_enter((char *)NULL);
1835			}
1836
1837		/* Handle framing errors */
1838		if (lsr & (PARERR|FRMERR|BRKDET|OVRRUN)) {
1839			if (lsr & PARERR) {
1840				if (tp->t_iflag & INPCK) /* parity enabled */
1841					s |= PERROR;
1842			}
1843			if (lsr & (FRMERR|BRKDET))
1844				s |= FRERROR;
1845			if (lsr & OVRRUN) {
1846				async->async_hw_overrun = 1;
1847				s |= OVERRUN;
1848			}
1849		}
1850
1851		if (s == 0)
1852			if ((tp->t_iflag & PARMRK) &&
1853			    !(tp->t_iflag & (IGNPAR|ISTRIP)) &&
1854			    (c == 0377))
1855				if (RING_POK(async, 2)) {
1856					RING_PUT(async, 0377);
1857					RING_PUT(async, c);
1858				} else
1859					async->async_sw_overrun = 1;
1860			else
1861				if (RING_POK(async, 1))
1862					RING_PUT(async, c);
1863				else
1864					async->async_sw_overrun = 1;
1865		else
1866			if (s & FRERROR) { /* Handle framing errors */
1867				if (c == 0)  {
1868		/* Look for break on kbd, stdin, or rconsdev */
1869					if ((async->async_dev == kbddev) ||
1870					    ((async->async_dev == rconsdev) ||
1871					    (async->async_dev == stdindev)) &&
1872					    (abort_enable !=
1873					    KIOCABORTALTERNATE))
1874						abort_sequence_enter((char *)0);
1875					else
1876						async->async_break++;
1877				} else {
1878					if (RING_POK(async, 1))
1879						RING_MARK(async, c, s);
1880					else
1881						async->async_sw_overrun = 1;
1882				}
1883			} else  { /* Parity errors  handled by ldterm */
1884				if (RING_POK(async, 1))
1885					RING_MARK(async, c, s);
1886				else
1887					async->async_sw_overrun = 1;
1888			}
1889		lsr = INB(LSR);
1890		if (asy->asy_rx_count > 16) break;
1891	}
1892	/* Check whether there is a request for hw/sw inbound/input flow ctrl */
1893	if ((async->async_ttycommon.t_cflag & CRTSXOFF) ||
1894	    (async->async_ttycommon.t_iflag & IXOFF))
1895		if ((int)(RING_CNT(async)) > (RINGSIZE * 3)/4) {
1896#ifdef DEBUG
1897			if (asydebug & ASY_DEBUG_HFLOW)
1898				printf("asy%d: hardware flow stop input.\n",
1899				    UNIT(async->async_dev));
1900#endif
1901			async->async_flags |= ASYNC_HW_IN_FLOW;
1902			async->async_flowc = async->async_stopc;
1903			async->async_ringbuf_overflow = 1;
1904		}
1905
1906	if ((async->async_flags & ASYNC_SERVICEIMM) || needsoft ||
1907	    (RING_FRAC(async)) || (async->async_polltid == 0))
1908		ASYSETSOFT(asy);	/* need a soft interrupt */
1909}
1910
1911/*
1912 * Interrupt on port: handle PPS event.  This function is only called
1913 * for a port on which PPS event handling has been enabled.
1914 */
1915static void
1916asy_ppsevent(struct asycom *asy, int msr)
1917{
1918	if (asy->asy_flags & ASY_PPS_EDGE) {
1919		/* Have seen leading edge, now look for and record drop */
1920		if ((msr & DCD) == 0)
1921			asy->asy_flags &= ~ASY_PPS_EDGE;
1922		/*
1923		 * Waiting for leading edge, look for rise; stamp event and
1924		 * calibrate kernel clock.
1925		 */
1926	} else if (msr & DCD) {
1927		/*
1928		 * This code captures a timestamp at the designated
1929		 * transition of the PPS signal (DCD asserted).  The
1930		 * code provides a pointer to the timestamp, as well
1931		 * as the hardware counter value at the capture.
1932		 *
1933		 * Note: the kernel has nano based time values while
1934		 * NTP requires micro based, an in-line fast algorithm
1935		 * to convert nsec to usec is used here -- see hrt2ts()
1936		 * in common/os/timers.c for a full description.
1937		 */
1938		struct timeval *tvp = &asy_ppsev.tv;
1939		timestruc_t ts;
1940		long nsec, usec;
1941
1942		asy->asy_flags |= ASY_PPS_EDGE;
1943		gethrestime(&ts);
1944		nsec = ts.tv_nsec;
1945		usec = nsec + (nsec >> 2);
1946		usec = nsec + (usec >> 1);
1947		usec = nsec + (usec >> 2);
1948		usec = nsec + (usec >> 4);
1949		usec = nsec - (usec >> 3);
1950		usec = nsec + (usec >> 2);
1951		usec = nsec + (usec >> 3);
1952		usec = nsec + (usec >> 4);
1953		usec = nsec + (usec >> 1);
1954		usec = nsec + (usec >> 6);
1955		tvp->tv_usec = usec >> 10;
1956		tvp->tv_sec = ts.tv_sec;
1957
1958		++asy_ppsev.serial;
1959
1960		/*
1961		 * Because the kernel keeps a high-resolution time,
1962		 * pass the current highres timestamp in tvp and zero
1963		 * in usec.
1964		 */
1965		ddi_hardpps(tvp, 0);
1966	}
1967}
1968
1969/*
1970 * Modem status interrupt.
1971 *
1972 * (Note: It is assumed that the MSR hasn't been read by asyintr().)
1973 */
1974
1975static void
1976async_msint(struct asycom *asy)
1977{
1978	struct asyncline *async = (struct asyncline *)asy->asy_priv;
1979	int msr;
1980
1981	msr = INB(MSR);	/* this resets the interrupt */
1982	asy->asy_cached_msr = msr;
1983#ifdef DEBUG
1984	if (asydebug & ASY_DEBUG_STATE) {
1985		printf("   transition: %3s %3s %3s %3s\n"
1986		    "current state: %3s %3s %3s %3s\n",
1987		    (msr & DCTS) ? "CTS" : "   ",
1988		    (msr & DDSR) ? "DSR" : "   ",
1989		    (msr & DRI) ?  "RI " : "   ",
1990		    (msr & DDCD) ? "DCD" : "   ",
1991		    (msr & CTS) ?  "CTS" : "   ",
1992		    (msr & DSR) ?  "DSR" : "   ",
1993		    (msr & RI) ?   "RI " : "   ",
1994		    (msr & DCD) ?  "DCD" : "   ");
1995	}
1996#endif
1997	if (async->async_ttycommon.t_cflag & CRTSCTS && !(msr & CTS)) {
1998#ifdef DEBUG
1999		if (asydebug & ASY_DEBUG_HFLOW)
2000			printf("asy%d: hflow start\n",
2001			    UNIT(async->async_dev));
2002#endif
2003		async->async_flags |= ASYNC_HW_OUT_FLW;
2004	}
2005	if (asy->asy_hwtype == ASY82510)
2006		OUTB(MSR, (msr & 0xF0));
2007
2008	/* Handle PPS event */
2009	if (asy->asy_flags & ASY_PPS)
2010		asy_ppsevent(asy, msr);
2011
2012	async->async_ext++;
2013	ASYSETSOFT(asy);
2014}
2015
2016/*
2017 * Handle a second-stage interrupt.
2018 */
2019uint_t
2020asysoftintr(caddr_t intarg)
2021{
2022	struct asycom *asy = (struct asycom *)intarg;
2023	struct asyncline *async;
2024	int rv;
2025	int cc;
2026	/*
2027	 * Test and clear soft interrupt.
2028	 */
2029	mutex_enter(asy->asy_soft_lock);
2030#ifdef DEBUG
2031	if (asydebug & ASY_DEBUG_PROCS)
2032		printf("softintr\n");
2033#endif
2034	rv = asy->asysoftpend;
2035	if (rv != 0)
2036		asy->asysoftpend = 0;
2037	mutex_exit(asy->asy_soft_lock);
2038
2039	if (rv) {
2040		if (asy->asy_priv == NULL)
2041			return (rv);
2042		async = (struct asyncline *)asy->asy_priv;
2043		mutex_enter(asy->asy_excl_hi);
2044		if (asy->asy_flags & ASY_NEEDSOFT) {
2045			asy->asy_flags &= ~ASY_NEEDSOFT;
2046			mutex_exit(asy->asy_excl_hi);
2047			(void) async_softint(asy);
2048			mutex_enter(asy->asy_excl_hi);
2049		}
2050		/*
2051		 * There are some instances where the softintr is not
2052		 * scheduled and hence not called. It so happened that makes
2053		 * the last few characters to be stuck in ringbuffer.
2054		 * Hence, call once again the  handler so that the last few
2055		 * characters are cleared.
2056		 */
2057		cc = RING_CNT(async);
2058		mutex_exit(asy->asy_excl_hi);
2059		if (cc > 0) {
2060			(void) async_softint(asy);
2061		}
2062	}
2063	return (rv);
2064}
2065
2066/*
2067 * Handle a software interrupt.
2068 */
2069static int
2070async_softint(struct asycom *asy)
2071{
2072	struct asyncline *async = (struct asyncline *)asy->asy_priv;
2073	uint_t	cc;
2074	mblk_t	*bp;
2075	queue_t	*q;
2076	uchar_t	val;
2077	uchar_t	c;
2078	tty_common_t	*tp;
2079
2080#ifdef DEBUG
2081	if (asydebug & ASY_DEBUG_PROCS)
2082		printf("process\n");
2083#endif
2084	mutex_enter(asy->asy_excl);
2085	if (asy->asy_flags & ASY_DOINGSOFT) {
2086		mutex_exit(asy->asy_excl);
2087		return (0);
2088	}
2089	tp = &async->async_ttycommon;
2090	q = tp->t_readq;
2091	if (q != NULL) {
2092		mutex_exit(asy->asy_excl);
2093		enterq(q);
2094		mutex_enter(asy->asy_excl);
2095	}
2096	mutex_enter(asy->asy_excl_hi);
2097	asy->asy_flags |= ASY_DOINGSOFT;
2098
2099	if (INB(ICR) & MIEN)
2100		val = asy->asy_cached_msr & 0xFF;
2101	else
2102		val = INB(MSR) & 0xFF;
2103
2104	if (async->async_ttycommon.t_cflag & CRTSCTS) {
2105		if ((val & CTS) && (async->async_flags & ASYNC_HW_OUT_FLW)) {
2106#ifdef DEBUG
2107			if (asydebug & ASY_DEBUG_HFLOW)
2108				printf("asy%d: hflow start\n",
2109				    UNIT(async->async_dev));
2110#endif
2111			async->async_flags &= ~ASYNC_HW_OUT_FLW;
2112			mutex_exit(asy->asy_excl_hi);
2113			if (async->async_ocnt > 0) {
2114				mutex_enter(asy->asy_excl_hi);
2115				async_resume(async);
2116				mutex_exit(asy->asy_excl_hi);
2117			} else {
2118				async_start(async);
2119			}
2120			mutex_enter(asy->asy_excl_hi);
2121		}
2122	}
2123	if (async->async_ext) {
2124		async->async_ext = 0;
2125		/* check for carrier up */
2126		if ((val & DCD) || (tp->t_flags & TS_SOFTCAR)) {
2127			/* carrier present */
2128			if ((async->async_flags & ASYNC_CARR_ON) == 0) {
2129				async->async_flags |= ASYNC_CARR_ON;
2130				mutex_exit(asy->asy_excl_hi);
2131				mutex_exit(asy->asy_excl);
2132				if (async->async_flags & ASYNC_ISOPEN)
2133					(void) putctl(q, M_UNHANGUP);
2134				cv_broadcast(&async->async_flags_cv);
2135				mutex_enter(asy->asy_excl);
2136				mutex_enter(asy->asy_excl_hi);
2137			}
2138		} else {
2139			if ((async->async_flags & ASYNC_CARR_ON) &&
2140			    !(tp->t_cflag & CLOCAL)) {
2141				int flushflag;
2142
2143				/*
2144				 * Carrier went away.
2145				 * Drop DTR, abort any output in
2146				 * progress, indicate that output is
2147				 * not stopped, and send a hangup
2148				 * notification upstream.
2149				 *
2150				 * If we're in the midst of close, then flush
2151				 * everything.  Don't leave stale ioctls lying
2152				 * about.
2153				 */
2154				val = INB(MCR);
2155				OUTB(MCR, (val & ~DTR));
2156				flushflag = (async->async_flags &
2157				    ASYNC_CLOSING) ? FLUSHALL : FLUSHDATA;
2158				if (tp->t_writeq != NULL) {
2159					flushq(tp->t_writeq, flushflag);
2160				}
2161				if (async->async_xmitblk != NULL) {
2162					freeb(async->async_xmitblk);
2163					async->async_xmitblk = NULL;
2164				}
2165				if (async->async_flags & ASYNC_BUSY) {
2166					async->async_ocnt = 0;
2167					async->async_flags &= ~ASYNC_BUSY;
2168				}
2169				async->async_flags &= ~ASYNC_STOPPED;
2170				if (async->async_flags & ASYNC_ISOPEN) {
2171					mutex_exit(asy->asy_excl_hi);
2172					mutex_exit(asy->asy_excl);
2173					(void) putctl(q, M_HANGUP);
2174					mutex_enter(asy->asy_excl);
2175					mutex_enter(asy->asy_excl_hi);
2176				}
2177				async->async_flags &= ~ASYNC_CARR_ON;
2178				mutex_exit(asy->asy_excl_hi);
2179				cv_broadcast(&async->async_flags_cv);
2180				mutex_enter(asy->asy_excl_hi);
2181			}
2182		}
2183	}
2184
2185	/*
2186	 * If data has been added to the circular buffer, remove
2187	 * it from the buffer, and send it up the stream if there's
2188	 * somebody listening. Try to do it 16 bytes at a time. If we
2189	 * have more than 16 bytes to move, move 16 byte chunks and
2190	 * leave the rest for next time around (maybe it will grow).
2191	 */
2192	if (!(async->async_flags & ASYNC_ISOPEN)) {
2193		RING_INIT(async);
2194		goto rv;
2195	}
2196	if ((cc = RING_CNT(async)) == 0) {
2197		goto rv;
2198	}
2199	mutex_exit(asy->asy_excl_hi);
2200
2201	if (!canput(q)) {
2202		if ((async->async_flags & ASYNC_HW_IN_FLOW) == 0) {
2203#ifdef DEBUG
2204			if (!(asydebug & ASY_DEBUG_HFLOW)) {
2205				printf("asy%d: hflow stop input.\n",
2206				    UNIT(async->async_dev));
2207				if (canputnext(q))
2208					printf("asy%d: next queue is "
2209					    "ready\n",
2210					    UNIT(async->async_dev));
2211			}
2212#endif
2213			mutex_enter(asy->asy_excl_hi);
2214			async->async_flags |= ASYNC_HW_IN_FLOW;
2215			async->async_flowc = async->async_stopc;
2216		} else mutex_enter(asy->asy_excl_hi);
2217		goto rv;
2218	}
2219
2220	if (async->async_ringbuf_overflow) {
2221		if ((async->async_flags & ASYNC_HW_IN_FLOW) &&
2222		    ((int)(RING_CNT(async)) < (RINGSIZE/4))) {
2223#ifdef DEBUG
2224			if (asydebug & ASY_DEBUG_HFLOW)
2225				printf("asy%d: hflow start input.\n",
2226				    UNIT(async->async_dev));
2227#endif
2228			mutex_enter(asy->asy_excl_hi);
2229			async->async_flags &= ~ASYNC_HW_IN_FLOW;
2230			async->async_flowc = async->async_startc;
2231			async->async_ringbuf_overflow = 0;
2232			goto rv;
2233		}
2234	}
2235#ifdef DEBUG
2236	if (asydebug & ASY_DEBUG_INPUT)
2237		printf("asy%d: %d char(s) in queue.\n",
2238		    UNIT(async->async_dev), cc);
2239#endif
2240	/*
2241	 * Before you pull the characters from the RING BUF
2242	 * Check whether you can put into the queue again
2243	 */
2244	if ((!canputnext(q)) || (!canput(q))) {
2245		mutex_enter(asy->asy_excl_hi);
2246		if ((async->async_flags & ASYNC_HW_IN_FLOW) == 0) {
2247			async->async_flags |= ASYNC_HW_IN_FLOW;
2248			async->async_flowc = async->async_stopc;
2249			async->async_queue_full = 1;
2250		}
2251		goto rv;
2252	}
2253	mutex_enter(asy->asy_excl_hi);
2254	if (async->async_queue_full) {
2255		/*
2256		 * Last time the Stream queue didnot allow
2257		 * now it allows so, relax, the flow control
2258		 */
2259		if (async->async_flags & ASYNC_HW_IN_FLOW) {
2260			async->async_flags &= ~ASYNC_HW_IN_FLOW;
2261			async->async_queue_full = 0;
2262			async->async_flowc = async->async_startc;
2263			goto rv;
2264		} else
2265			async->async_queue_full = 0;
2266	}
2267	mutex_exit(asy->asy_excl_hi);
2268	if (!(bp = allocb(cc, BPRI_MED))) {
2269		ttycommon_qfull(&async->async_ttycommon, q);
2270		mutex_enter(asy->asy_excl_hi);
2271		goto rv;
2272	}
2273	mutex_enter(asy->asy_excl_hi);
2274	do {
2275		if (RING_ERR(async, S_ERRORS)) {
2276			RING_UNMARK(async);
2277			c = RING_GET(async);
2278			break;
2279		} else {
2280			*bp->b_wptr++ = RING_GET(async);
2281		}
2282	} while (--cc);
2283
2284	mutex_exit(asy->asy_excl_hi);
2285	mutex_exit(asy->asy_excl);
2286	if (bp->b_wptr > bp->b_rptr) {
2287		if (!canputnext(q)) {
2288			if (!canput(q)) {
2289				/*
2290				 * Even after taking all precautions that
2291				 * Still we are unable to queue, then we
2292				 * cannot do anything, just drop the block
2293				 */
2294				cmn_err(CE_NOTE,
2295				    "su%d: local queue full\n",
2296				    UNIT(async->async_dev));
2297				freemsg(bp);
2298				mutex_enter(asy->asy_excl_hi);
2299				if ((async->async_flags &
2300				    ASYNC_HW_IN_FLOW) == 0) {
2301					async->async_flags |=
2302					    ASYNC_HW_IN_FLOW;
2303					async->async_flowc =
2304					    async->async_stopc;
2305					async->async_queue_full = 1;
2306				}
2307				mutex_exit(asy->asy_excl_hi);
2308			} else {
2309				(void) putq(q, bp);
2310			}
2311		} else {
2312			putnext(q, bp);
2313		}
2314	} else {
2315		freemsg(bp);
2316	}
2317	/*
2318	 * If we have a parity error, then send
2319	 * up an M_BREAK with the "bad"
2320	 * character as an argument. Let ldterm
2321	 * figure out what to do with the error.
2322	 */
2323	if (cc)
2324		(void) putctl1(q, M_BREAK, c);
2325	mutex_enter(asy->asy_excl);
2326	mutex_enter(asy->asy_excl_hi);
2327rv:
2328	/*
2329	 * If a transmission has finished, indicate that it's finished,
2330	 * and start that line up again.
2331	 */
2332	if (async->async_break) {
2333		async->async_break = 0;
2334		if (async->async_flags & ASYNC_ISOPEN) {
2335			mutex_exit(asy->asy_excl_hi);
2336			mutex_exit(asy->asy_excl);
2337			(void) putctl(q, M_BREAK);
2338			mutex_enter(asy->asy_excl);
2339			mutex_enter(asy->asy_excl_hi);
2340		}
2341	}
2342	if ((async->async_ocnt <= 0 && (async->async_flags & ASYNC_BUSY)) ||
2343	    (async->async_flowc != '\0')) {
2344		async->async_flags &= ~ASYNC_BUSY;
2345		mutex_exit(asy->asy_excl_hi);
2346		if (async->async_xmitblk)
2347			freeb(async->async_xmitblk);
2348		async->async_xmitblk = NULL;
2349		if (async->async_flags & ASYNC_ISOPEN) {
2350			asy->inperim = B_TRUE;
2351			mutex_exit(asy->asy_excl);
2352			enterq(async->async_ttycommon.t_writeq);
2353			mutex_enter(asy->asy_excl);
2354		}
2355		async_start(async);
2356		/*
2357		 * We need to check for inperim and ISOPEN due to
2358		 * multi-threading implications; it's possible to close the
2359		 * port and nullify async_flags while completing the software
2360		 * interrupt.  If the port is closed, leaveq() will have already
2361		 * been called.  We don't want to call it twice.
2362		 */
2363		if ((asy->inperim) && (async->async_flags & ASYNC_ISOPEN)) {
2364			mutex_exit(asy->asy_excl);
2365			leaveq(async->async_ttycommon.t_writeq);
2366			mutex_enter(asy->asy_excl);
2367			asy->inperim = B_FALSE;
2368		}
2369		if (!(async->async_flags & ASYNC_BUSY))
2370			cv_broadcast(&async->async_flags_cv);
2371		mutex_enter(asy->asy_excl_hi);
2372	}
2373	/*
2374	 * A note about these overrun bits: all they do is *tell* someone
2375	 * about an error- They do not track multiple errors. In fact,
2376	 * you could consider them latched register bits if you like.
2377	 * We are only interested in printing the error message once for
2378	 * any cluster of overrun errrors.
2379	 */
2380	if (async->async_hw_overrun) {
2381		if (async->async_flags & ASYNC_ISOPEN) {
2382			if (su_log > 0) {
2383				mutex_exit(asy->asy_excl_hi);
2384				mutex_exit(asy->asy_excl);
2385				cmn_err(CE_NOTE, "su%d: silo overflow\n",
2386				    UNIT(async->async_dev));
2387				mutex_enter(asy->asy_excl);
2388				mutex_enter(asy->asy_excl_hi);
2389			}
2390			INC64_KSTAT(asy, siloover);
2391		}
2392		async->async_hw_overrun = 0;
2393	}
2394	if (async->async_sw_overrun) {
2395		if (async->async_flags & ASYNC_ISOPEN) {
2396			if (su_log > 0) {
2397				mutex_exit(asy->asy_excl_hi);
2398				mutex_exit(asy->asy_excl);
2399				cmn_err(CE_NOTE, "su%d: ring buffer overflow\n",
2400				    UNIT(async->async_dev));
2401				mutex_enter(asy->asy_excl);
2402				mutex_enter(asy->asy_excl_hi);
2403			}
2404			INC64_KSTAT(asy, ringover);
2405		}
2406		async->async_sw_overrun = 0;
2407	}
2408	asy->asy_flags &= ~ASY_DOINGSOFT;
2409	mutex_exit(asy->asy_excl_hi);
2410	mutex_exit(asy->asy_excl);
2411	if (q != NULL)
2412		leaveq(q);
2413	return (0);
2414}
2415
2416/*
2417 * Restart output on a line after a delay or break timer expired.
2418 */
2419static void
2420async_restart(void *arg)
2421{
2422	struct asyncline *async = arg;
2423	struct asycom *asy = async->async_common;
2424	queue_t *q;
2425	uchar_t lcr;
2426
2427	/*
2428	 * If break timer expired, turn off the break bit.
2429	 */
2430#ifdef DEBUG
2431	if (asydebug & ASY_DEBUG_PROCS)
2432		printf("restart\n");
2433#endif
2434	mutex_enter(asy->asy_excl);
2435	if (async->async_flags & ASYNC_BREAK) {
2436		unsigned int rate;
2437
2438		mutex_enter(asy->asy_excl_hi);
2439		lcr = INB(LCR);
2440		OUTB(LCR, (lcr & ~SETBREAK));
2441
2442		/*
2443		 * Go to sleep for the time it takes for at least one
2444		 * stop bit to be received by the device at the other
2445		 * end of the line as stated in the RS-232 specification.
2446		 * The wait period is equal to:
2447		 * 2 clock cycles * (1 MICROSEC / baud rate)
2448		 */
2449		rate = async->async_ttycommon.t_cflag & CBAUD;
2450		if (async->async_ttycommon.t_cflag & CBAUDEXT)
2451			rate += 16;
2452		if (rate >= N_SU_SPEEDS || rate == B0) {
2453			rate = B9600;
2454		}
2455
2456		mutex_exit(asy->asy_excl_hi);
2457		mutex_exit(asy->asy_excl);
2458		drv_usecwait(2 * MICROSEC / baudtable[rate]);
2459		mutex_enter(asy->asy_excl);
2460	}
2461	async->async_flags &= ~(ASYNC_DELAY|ASYNC_BREAK|ASYNC_DRAINING);
2462	if ((q = async->async_ttycommon.t_writeq) != NULL) {
2463		mutex_exit(asy->asy_excl);
2464		enterq(q);
2465		mutex_enter(asy->asy_excl);
2466	}
2467	async_start(async);
2468	mutex_exit(asy->asy_excl);
2469	if (q != NULL)
2470		leaveq(q);
2471
2472	/* cleared break or delay flag; may have made some output progress */
2473	cv_broadcast(&async->async_flags_cv);
2474}
2475
2476static void
2477async_start(struct asyncline *async)
2478{
2479	async_nstart(async, 0);
2480}
2481
2482/*
2483 * Start output on a line, unless it's busy, frozen, or otherwise.
2484 */
2485static void
2486async_nstart(struct asyncline *async, int mode)
2487{
2488	register struct asycom *asy = async->async_common;
2489	register int cc;
2490	register queue_t *q;
2491	mblk_t *bp, *nbp;
2492	uchar_t *xmit_addr;
2493	uchar_t	val;
2494	int	fifo_len = 1;
2495	int	xmit_progress;
2496
2497#ifdef DEBUG
2498	if (asydebug & ASY_DEBUG_PROCS)
2499		printf("start\n");
2500#endif
2501	if (asy->asy_use_fifo == FIFO_ON)
2502		fifo_len = asy->asy_fifo_buf; /* with FIFO buffers */
2503
2504	ASSERT(mutex_owned(asy->asy_excl));
2505	mutex_enter(asy->asy_excl_hi);
2506	asycheckflowcontrol_hw(asy);
2507
2508	/*
2509	 * If the chip is busy (i.e., we're waiting for a break timeout
2510	 * to expire, or for the current transmission to finish, or for
2511	 * output to finish draining from chip), don't grab anything new.
2512	 */
2513	if (async->async_flags & (ASYNC_BREAK|ASYNC_BUSY|ASYNC_DRAINING)) {
2514		mutex_exit(asy->asy_excl_hi);
2515#ifdef DEBUG
2516		if (mode && asydebug & ASY_DEBUG_CLOSE)
2517			printf("asy%d: start %s.\n",
2518			    UNIT(async->async_dev),
2519			    async->async_flags & ASYNC_BREAK
2520			    ? "break" : "busy");
2521#endif
2522		return;
2523	}
2524
2525	/*
2526	 * If we have a flow-control character to transmit, do it now.
2527	 */
2528	if (asycheckflowcontrol_sw(asy)) {
2529		mutex_exit(asy->asy_excl_hi);
2530		return;
2531	}
2532	mutex_exit(asy->asy_excl_hi);
2533	/*
2534	 * If we're waiting for a delay timeout to expire, don't grab
2535	 * anything new.
2536	 */
2537	if (async->async_flags & ASYNC_DELAY) {
2538#ifdef DEBUG
2539		if (mode && asydebug & ASY_DEBUG_CLOSE)
2540			printf("asy%d: start ASYNC_DELAY.\n",
2541			    UNIT(async->async_dev));
2542#endif
2543		return;
2544	}
2545
2546	if ((q = async->async_ttycommon.t_writeq) == NULL) {
2547#ifdef DEBUG
2548		if (mode && asydebug & ASY_DEBUG_CLOSE)
2549			printf("asy%d: start writeq is null.\n",
2550			    UNIT(async->async_dev));
2551#endif
2552		return;	/* not attached to a stream */
2553	}
2554
2555	for (;;) {
2556		if ((bp = getq(q)) == NULL)
2557			return;	/* no data to transmit */
2558
2559		/*
2560		 * We have a message block to work on.
2561		 * Check whether it's a break, a delay, or an ioctl (the latter
2562		 * occurs if the ioctl in question was waiting for the output
2563		 * to drain).  If it's one of those, process it immediately.
2564		 */
2565		switch (bp->b_datap->db_type) {
2566
2567		case M_BREAK:
2568			/*
2569			 * Set the break bit, and arrange for "async_restart"
2570			 * to be called in 1/4 second; it will turn the
2571			 * break bit off, and call "async_start" to grab
2572			 * the next message.
2573			 */
2574			mutex_enter(asy->asy_excl_hi);
2575			val = INB(LCR);
2576			OUTB(LCR, (val | SETBREAK));
2577			mutex_exit(asy->asy_excl_hi);
2578			async->async_flags |= ASYNC_BREAK;
2579			(void) timeout(async_restart, async, hz / 4);
2580			freemsg(bp);
2581			return;	/* wait for this to finish */
2582
2583		case M_DELAY:
2584			/*
2585			 * Arrange for "async_restart" to be called when the
2586			 * delay expires; it will turn ASYNC_DELAY off,
2587			 * and call "async_start" to grab the next message.
2588			 */
2589			(void) timeout(async_restart, async,
2590			    (clock_t)(*(unsigned char *)bp->b_rptr + 6));
2591			async->async_flags |= ASYNC_DELAY;
2592			freemsg(bp);
2593			return;	/* wait for this to finish */
2594
2595		case M_IOCTL:
2596			/*
2597			 * This ioctl needs to wait for the output ahead of
2598			 * it to drain.  Try to do it, and then either
2599			 * redo the ioctl at a later time or grab the next
2600			 * message after it.
2601			 */
2602
2603			mutex_enter(asy->asy_excl_hi);
2604			if (asy_isbusy(asy)) {
2605				/*
2606				 * Get the divisor by calculating the rate
2607				 */
2608				unsigned int rate;
2609
2610				mutex_exit(asy->asy_excl_hi);
2611				rate = async->async_ttycommon.t_cflag & CBAUD;
2612				if (async->async_ttycommon.t_cflag & CBAUDEXT)
2613					rate += 16;
2614				if (rate >= N_SU_SPEEDS || rate == B0) {
2615					rate = B9600;
2616				}
2617
2618				/*
2619				 * We need to do a callback as the port will
2620				 * be set to drain
2621				 */
2622				async->async_flags |= ASYNC_DRAINING;
2623
2624				/*
2625				 * Put the message we just processed back onto
2626				 * the end of the queue
2627				 */
2628				if (putq(q, bp) == 0)
2629					freemsg(bp);
2630
2631				/*
2632				 * We need to delay until the TSR and THR
2633				 * have been exhausted.  We base the delay on
2634				 * the amount of time it takes to transmit
2635				 * 2 chars at the current baud rate in
2636				 * microseconds.
2637				 *
2638				 * Therefore, the wait period is:
2639				 *
2640				 * (#TSR bits + #THR bits) *
2641				 * 	1 MICROSEC / baud rate
2642				 */
2643				(void) timeout(async_restart, async,
2644				    drv_usectohz(16 * MICROSEC /
2645				    baudtable[rate]));
2646				return;
2647			}
2648			mutex_exit(asy->asy_excl_hi);
2649			mutex_exit(asy->asy_excl);
2650			async_ioctl(async, q, bp, B_FALSE);
2651			mutex_enter(asy->asy_excl);
2652			continue;
2653		}
2654
2655		while (bp != NULL && (cc = bp->b_wptr - bp->b_rptr) == 0) {
2656			nbp = bp->b_cont;
2657			freeb(bp);
2658			bp = nbp;
2659		}
2660		if (bp != NULL)
2661			break;
2662	}
2663
2664	/*
2665	 * We have data to transmit.  If output is stopped, put
2666	 * it back and try again later.
2667	 */
2668	if (async->async_flags & (ASYNC_HW_OUT_FLW|ASYNC_STOPPED)) {
2669#ifdef DEBUG
2670		if (asydebug & ASY_DEBUG_HFLOW &&
2671		    async->async_flags & ASYNC_HW_OUT_FLW)
2672			printf("asy%d: output hflow in effect.\n",
2673			    UNIT(async->async_dev));
2674#endif
2675		mutex_exit(asy->asy_excl);
2676		(void) putbq(q, bp);
2677		/*
2678		 * We entered the routine owning the lock, we need to
2679		 * exit the routine owning the lock.
2680		 */
2681		mutex_enter(asy->asy_excl);
2682		return;
2683	}
2684
2685	async->async_xmitblk = bp;
2686	xmit_addr = bp->b_rptr;
2687	bp = bp->b_cont;
2688	if (bp != NULL) {
2689		mutex_exit(asy->asy_excl);
2690		(void) putbq(q, bp);	/* not done with this message yet */
2691		mutex_enter(asy->asy_excl);
2692	}
2693
2694	/*
2695	 * In 5-bit mode, the high order bits are used
2696	 * to indicate character sizes less than five,
2697	 * so we need to explicitly mask before transmitting
2698	 */
2699	if ((async->async_ttycommon.t_cflag & CSIZE) == CS5) {
2700		register unsigned char *p = xmit_addr;
2701		register int cnt = cc;
2702
2703		while (cnt--)
2704			*p++ &= (unsigned char) 0x1f;
2705	}
2706
2707	/*
2708	 * Set up this block for pseudo-DMA.
2709	 */
2710	mutex_enter(asy->asy_excl_hi);
2711	async->async_optr = xmit_addr;
2712	async->async_ocnt = cc;
2713	/*
2714	 * If the transmitter is ready, shove some
2715	 * characters out.
2716	 */
2717	xmit_progress = 0;
2718	while (fifo_len-- && async->async_ocnt) {
2719		if (INB(LSR) & XHRE) {
2720			OUTB(DAT, *async->async_optr++);
2721			async->async_ocnt--;
2722			xmit_progress++;
2723		}
2724	}
2725	asy->asy_out_of_band_xmit = xmit_progress;
2726	if (xmit_progress > 0)
2727		async->async_flags |= ASYNC_PROGRESS;
2728	async->async_flags |= ASYNC_BUSY;
2729	mutex_exit(asy->asy_excl_hi);
2730}
2731
2732/*
2733 * Resume output by poking the transmitter.
2734 */
2735static void
2736async_resume(struct asyncline *async)
2737{
2738	register struct asycom *asy = async->async_common;
2739
2740	ASSERT(mutex_owned(asy->asy_excl_hi));
2741#ifdef DEBUG
2742	if (asydebug & ASY_DEBUG_PROCS)
2743		printf("resume\n");
2744#endif
2745
2746	asycheckflowcontrol_hw(asy);
2747
2748	if (INB(LSR) & XHRE) {
2749		if (asycheckflowcontrol_sw(asy)) {
2750			return;
2751		} else if (async->async_ocnt > 0) {
2752			OUTB(DAT, *async->async_optr++);
2753			async->async_ocnt--;
2754			async->async_flags |= ASYNC_PROGRESS;
2755		}
2756	}
2757}
2758
2759/*
2760 * Process an "ioctl" message sent down to us.
2761 * Note that we don't need to get any locks until we are ready to access
2762 * the hardware.  Nothing we access until then is going to be altered
2763 * outside of the STREAMS framework, so we should be safe.
2764 */
2765static void
2766async_ioctl(struct asyncline *async, queue_t *wq, mblk_t *mp, boolean_t iswput)
2767{
2768	register struct asycom *asy = async->async_common;
2769	register tty_common_t  *tp = &async->async_ttycommon;
2770	register struct iocblk *iocp;
2771	register unsigned datasize;
2772	size_t ioc_count;
2773	mblk_t *datamp;
2774	int error = 0;
2775	uchar_t val, icr;
2776#ifdef DEBUG
2777	if (asydebug & ASY_DEBUG_PROCS)
2778		printf("ioctl\n");
2779#endif
2780
2781	if (tp->t_iocpending != NULL) {
2782		/*
2783		 * We were holding an "ioctl" response pending the
2784		 * availability of an "mblk" to hold data to be passed up;
2785		 * another "ioctl" came through, which means that "ioctl"
2786		 * must have timed out or been aborted.
2787		 */
2788		freemsg(async->async_ttycommon.t_iocpending);
2789		async->async_ttycommon.t_iocpending = NULL;
2790	}
2791
2792	iocp = (struct iocblk *)mp->b_rptr;
2793
2794	/*
2795	 * Save off the ioc count in case we need to restore it
2796	 * because we are queuing a message block.
2797	 */
2798	ioc_count = iocp->ioc_count;
2799
2800	/*
2801	 * For TIOCMGET, TIOCMBIC, TIOCMBIS, TIOCMSET, and PPS, do NOT call
2802	 * ttycommon_ioctl() because this function frees up the message block
2803	 * (mp->b_cont) that contains the address of the user variable where
2804	 * we need to pass back the bit array.
2805	 *
2806	 * Similarly, ttycommon_ioctl() does not know about CONSOPENPOLLEDIO
2807	 * and CONSCLOSEPOLLEDIO, so don't let ttycommon_ioctl() touch them.
2808	 */
2809	if (iocp->ioc_cmd == TIOCMGET ||
2810	    iocp->ioc_cmd == TIOCMBIC ||
2811	    iocp->ioc_cmd == TIOCMBIS ||
2812	    iocp->ioc_cmd == TIOCMSET ||
2813	    iocp->ioc_cmd == TIOCGPPS ||
2814	    iocp->ioc_cmd == TIOCSPPS ||
2815	    iocp->ioc_cmd == TIOCGPPSEV ||
2816	    iocp->ioc_cmd == CONSOPENPOLLEDIO ||
2817	    iocp->ioc_cmd == CONSCLOSEPOLLEDIO)
2818		error = -1; /* Do Nothing */
2819	else
2820
2821	/*
2822	 * The only way in which "ttycommon_ioctl" can fail is if the "ioctl"
2823	 * requires a response containing data to be returned to the user,
2824	 * and no mblk could be allocated for the data.
2825	 * No such "ioctl" alters our state.  Thus, we always go ahead and
2826	 * do any state-changes the "ioctl" calls for.  If we couldn't allocate
2827	 * the data, "ttycommon_ioctl" has stashed the "ioctl" away safely, so
2828	 * we just call "bufcall" to request that we be called back when we
2829	 * stand a better chance of allocating the data.
2830	 */
2831	if ((datasize = ttycommon_ioctl(tp, wq, mp, &error)) != 0) {
2832		if (async->async_wbufcid)
2833			unbufcall(async->async_wbufcid);
2834		async->async_wbufcid = bufcall(datasize, BPRI_HI, async_reioctl,
2835		    async);
2836		return;
2837	}
2838
2839	mutex_enter(asy->asy_excl);
2840
2841	if (error == 0) {
2842		/*
2843		 * "ttycommon_ioctl" did most of the work; we just use the
2844		 * data it set up.
2845		 */
2846		switch (iocp->ioc_cmd) {
2847
2848		case TCSETS:
2849			if (!(asy->asy_rsc_console || asy->asy_rsc_control ||
2850			    asy->asy_lom_console)) {
2851				mutex_enter(asy->asy_excl_hi);
2852				error = asy_program(asy, ASY_NOINIT);
2853				mutex_exit(asy->asy_excl_hi);
2854			}
2855			break;
2856		case TCSETSF:
2857		case TCSETSW:
2858		case TCSETA:
2859		case TCSETAW:
2860		case TCSETAF:
2861			if (!(asy->asy_rsc_console || asy->asy_rsc_control ||
2862			    asy->asy_lom_console)) {
2863				mutex_enter(asy->asy_excl_hi);
2864				if (iswput && asy_isbusy(asy)) {
2865					/*
2866					 * ttycommon_ioctl sets the db_type to
2867					 * M_IOCACK and ioc_count to zero
2868					 * we need to undo this when we
2869					 * queue a control message. This will
2870					 * allow the control messages to be
2871					 * processed again when the chip
2872					 * becomes available.
2873					 */
2874					mp->b_datap->db_type = M_IOCTL;
2875					iocp->ioc_count = ioc_count;
2876
2877					if (putq(wq, mp) == 0)
2878						freemsg(mp);
2879					mutex_exit(asy->asy_excl_hi);
2880					mutex_exit(asy->asy_excl);
2881					return;
2882				}
2883
2884				/*
2885				 * TCSETA, TCSETAW, and TCSETAF make use of
2886				 * the termio structure and therefore have
2887				 * no concept of any speed except what can
2888				 * be represented by CBAUD. This is because
2889				 * of legacy SVR4 code. Therefore, if we see
2890				 * one of the aforementioned IOCTL commands
2891				 * we should zero out CBAUDEXT, CIBAUD, and
2892				 * CIBAUDEXT as to not break legacy
2893				 * functionality. This is because CBAUDEXT,
2894				 * CIBAUD, and CIBAUDEXT can't be stored in
2895				 * an unsigned short. By zeroing out CBAUDEXT,
2896				 * CIBAUD, and CIBAUDEXT in the t_cflag of the
2897				 * termios structure asy_program() will set the
2898				 * input baud rate to the output baud rate.
2899				 */
2900				if (iocp->ioc_cmd == TCSETA ||
2901				    iocp->ioc_cmd == TCSETAW ||
2902				    iocp->ioc_cmd == TCSETAF)
2903					tp->t_cflag &= ~(CIBAUD |
2904					    CIBAUDEXT | CBAUDEXT);
2905
2906				error = asy_program(asy, ASY_NOINIT);
2907				mutex_exit(asy->asy_excl_hi);
2908			}
2909			break;
2910		case TIOCSSOFTCAR:
2911			/* Set the driver state appropriately */
2912			mutex_enter(asy->asy_excl_hi);
2913			if (tp->t_flags & TS_SOFTCAR)
2914				asy->asy_flags |= ASY_IGNORE_CD;
2915			else
2916				asy->asy_flags &= ~ASY_IGNORE_CD;
2917			mutex_exit(asy->asy_excl_hi);
2918			break;
2919		}
2920	} else if (error < 0) {
2921		/*
2922		 * "ttycommon_ioctl" didn't do anything; we process it here.
2923		 */
2924		error = 0;
2925		switch (iocp->ioc_cmd) {
2926
2927		case TIOCGPPS:
2928			/*
2929			 * Get PPS on/off.
2930			 */
2931			if (mp->b_cont != NULL)
2932				freemsg(mp->b_cont);
2933
2934			mp->b_cont = allocb(sizeof (int), BPRI_HI);
2935			if (mp->b_cont == NULL) {
2936				error = ENOMEM;
2937				break;
2938			}
2939			if (asy->asy_flags & ASY_PPS)
2940				*(int *)mp->b_cont->b_wptr = 1;
2941			else
2942				*(int *)mp->b_cont->b_wptr = 0;
2943			mp->b_cont->b_wptr += sizeof (int);
2944			mp->b_datap->db_type = M_IOCACK;
2945			iocp->ioc_count = sizeof (int);
2946			break;
2947
2948		case TIOCSPPS:
2949			/*
2950			 * Set PPS on/off.
2951			 */
2952			error = miocpullup(mp, sizeof (int));
2953			if (error != 0)
2954				break;
2955
2956			mutex_enter(asy->asy_excl_hi);
2957			if (*(int *)mp->b_cont->b_rptr)
2958				asy->asy_flags |= ASY_PPS;
2959			else
2960				asy->asy_flags &= ~ASY_PPS;
2961			/* Reset edge sense */
2962			asy->asy_flags &= ~ASY_PPS_EDGE;
2963			mutex_exit(asy->asy_excl_hi);
2964			mp->b_datap->db_type = M_IOCACK;
2965			break;
2966
2967		case TIOCGPPSEV: {
2968			/*
2969			 * Get PPS event data.
2970			 */
2971			mblk_t *bp;
2972			void *buf;
2973#ifdef _SYSCALL32_IMPL
2974			struct ppsclockev32 p32;
2975#endif
2976			struct ppsclockev ppsclockev;
2977
2978			if (mp->b_cont != NULL) {
2979				freemsg(mp->b_cont);
2980				mp->b_cont = NULL;
2981			}
2982
2983			if ((asy->asy_flags & ASY_PPS) == 0) {
2984				error = ENXIO;
2985				break;
2986			}
2987
2988			/* Protect from incomplete asy_ppsev */
2989			mutex_enter(asy->asy_excl_hi);
2990			ppsclockev = asy_ppsev;
2991			mutex_exit(asy->asy_excl_hi);
2992
2993#ifdef _SYSCALL32_IMPL
2994			if ((iocp->ioc_flag & IOC_MODELS) != IOC_NATIVE) {
2995				TIMEVAL_TO_TIMEVAL32(&p32.tv, &ppsclockev.tv);
2996				p32.serial = ppsclockev.serial;
2997				buf = &p32;
2998				iocp->ioc_count = sizeof (struct ppsclockev32);
2999			} else
3000#endif
3001			{
3002				buf = &ppsclockev;
3003				iocp->ioc_count = sizeof (struct ppsclockev);
3004			}
3005
3006			if ((bp = allocb(iocp->ioc_count, BPRI_HI)) == NULL) {
3007				error = ENOMEM;
3008				break;
3009			}
3010			mp->b_cont = bp;
3011
3012			bcopy(buf, bp->b_wptr, iocp->ioc_count);
3013			bp->b_wptr += iocp->ioc_count;
3014			mp->b_datap->db_type = M_IOCACK;
3015			break;
3016		}
3017
3018		case TCSBRK:
3019			error = miocpullup(mp, sizeof (int));
3020			if (error != 0)
3021				break;
3022
3023			mutex_enter(asy->asy_excl_hi);
3024			if (*(int *)mp->b_cont->b_rptr == 0) {
3025				/*
3026				 * Get the divisor by calculating the rate
3027				 */
3028				unsigned int rate, divisor;
3029				rate = async->async_ttycommon.t_cflag & CBAUD;
3030				if (async->async_ttycommon.t_cflag & CBAUDEXT)
3031					rate += 16;
3032				if (rate >= N_SU_SPEEDS) rate = B9600;
3033				divisor = asyspdtab[rate] & 0xfff;
3034
3035				/*
3036				 * To ensure that erroneous characters are
3037				 * not sent out when the break is set, SB
3038				 * recommends three steps:
3039				 *
3040				 * 1) pad the TSR with 0 bits
3041				 * 2) When the TSR is full, set break
3042				 * 3) When the TSR has been flushed, unset
3043				 *    the break when transmission must be
3044				 *    restored.
3045				 *
3046				 * We loop until the TSR is empty and then
3047				 * set the break.  ASYNC_BREAK has been set
3048				 * to ensure that no characters are
3049				 * transmitted while the TSR is being
3050				 * flushed and SOUT is being used for the
3051				 * break signal.
3052				 *
3053				 * The wait period is equal to
3054				 * clock / (baud * 16) * 16 * 2.
3055				 */
3056				async->async_flags |= ASYNC_BREAK;
3057				while ((INB(LSR) & XSRE) == 0) {
3058					mutex_exit(asy->asy_excl_hi);
3059					mutex_exit(asy->asy_excl);
3060					drv_usecwait(32*divisor);
3061					mutex_enter(asy->asy_excl);
3062					mutex_enter(asy->asy_excl_hi);
3063				}
3064
3065				/*
3066				 * Set the break bit, and arrange for
3067				 * "async_restart" to be called in 1/4 second;
3068				 * it will turn the break bit off, and call
3069				 * "async_start" to grab the next message.
3070				 */
3071				val = INB(LCR);
3072				OUTB(LCR, (val | SETBREAK));
3073				mutex_exit(asy->asy_excl_hi);
3074				(void) timeout(async_restart, async, hz / 4);
3075			} else {
3076#ifdef DEBUG
3077				if (asydebug & ASY_DEBUG_CLOSE)
3078					printf("asy%d: wait for flush.\n",
3079					    UNIT(async->async_dev));
3080#endif
3081				if (iswput && asy_isbusy(asy)) {
3082					if (putq(wq, mp) == 0)
3083						freemsg(mp);
3084					mutex_exit(asy->asy_excl_hi);
3085					mutex_exit(asy->asy_excl);
3086					return;
3087				}
3088				mutex_exit(asy->asy_excl_hi);
3089#ifdef DEBUG
3090				if (asydebug & ASY_DEBUG_CLOSE)
3091					printf("asy%d: ldterm satisfied.\n",
3092					    UNIT(async->async_dev));
3093#endif
3094			}
3095			break;
3096
3097		case TIOCSBRK:
3098			mutex_enter(asy->asy_excl_hi);
3099			val = INB(LCR);
3100			OUTB(LCR, (val | SETBREAK));
3101			mutex_exit(asy->asy_excl_hi);
3102			mutex_exit(asy->asy_excl);
3103			miocack(wq, mp, 0, 0);
3104			return;
3105
3106		case TIOCCBRK:
3107			mutex_enter(asy->asy_excl_hi);
3108			val = INB(LCR);
3109			OUTB(LCR, (val & ~SETBREAK));
3110			mutex_exit(asy->asy_excl_hi);
3111			mutex_exit(asy->asy_excl);
3112			miocack(wq, mp, 0, 0);
3113			return;
3114
3115		case TIOCMSET:
3116		case TIOCMBIS:
3117		case TIOCMBIC:
3118			if (iocp->ioc_count == TRANSPARENT)
3119				mcopyin(mp, NULL, sizeof (int), NULL);
3120			else {
3121				error = miocpullup(mp, sizeof (int));
3122				if (error != 0)
3123					break;
3124
3125				mutex_enter(asy->asy_excl_hi);
3126
3127				(void) asymctl(asy,
3128				    dmtoasy(*(int *)mp->b_cont->b_rptr),
3129				    iocp->ioc_cmd);
3130
3131				mutex_exit(asy->asy_excl_hi);
3132				iocp->ioc_error = 0;
3133				mp->b_datap->db_type = M_IOCACK;
3134			}
3135			break;
3136
3137		case TIOCSILOOP:
3138			mutex_enter(asy->asy_excl_hi);
3139			/*
3140			 * If somebody misues this Ioctl when used for
3141			 * driving keyboard and mouse indicate not supported
3142			 */
3143			if ((asy->asy_device_type == ASY_KEYBOARD) ||
3144			    (asy->asy_device_type == ASY_MOUSE)) {
3145				mutex_exit(asy->asy_excl_hi);
3146				error = ENOTTY;
3147				break;
3148			}
3149
3150			/* should not use when we're the console */
3151			if ((async->async_dev == kbddev) ||
3152			    (async->async_dev == rconsdev) ||
3153			    (async->async_dev == stdindev)) {
3154				mutex_exit(asy->asy_excl_hi);
3155				error = EINVAL;
3156				break;
3157			}
3158
3159			val = INB(MCR);
3160			icr = INB(ICR);
3161			/*
3162			 * Disable the Modem Status Interrupt
3163			 * The reason for disabling is  the status of
3164			 * modem signal are in the higher 4 bits instead of
3165			 * lower four bits when in loopback mode,
3166			 * so, donot worry about Modem interrupt when
3167			 * you are planning to set
3168			 * this in loopback mode until it is cleared by
3169			 * another ioctl to get out of the loopback mode
3170			 */
3171			OUTB(ICR, icr & ~ MIEN);
3172			OUTB(MCR, val | ASY_LOOP);
3173			mutex_exit(asy->asy_excl_hi);
3174			iocp->ioc_error = 0;
3175			mp->b_datap->db_type = M_IOCACK;
3176			break;
3177
3178		case TIOCMGET:
3179			datamp = allocb(sizeof (int), BPRI_MED);
3180			if (datamp == NULL) {
3181				error = EAGAIN;
3182				break;
3183			}
3184
3185			mutex_enter(asy->asy_excl_hi);
3186			*(int *)datamp->b_rptr = asymctl(asy, 0, TIOCMGET);
3187			mutex_exit(asy->asy_excl_hi);
3188
3189			if (iocp->ioc_count == TRANSPARENT) {
3190				mcopyout(mp, NULL, sizeof (int), NULL, datamp);
3191			} else {
3192				if (mp->b_cont != NULL)
3193					freemsg(mp->b_cont);
3194				mp->b_cont = datamp;
3195				mp->b_cont->b_wptr += sizeof (int);
3196				mp->b_datap->db_type = M_IOCACK;
3197				iocp->ioc_count = sizeof (int);
3198			}
3199			break;
3200
3201		case CONSOPENPOLLEDIO:
3202			/*
3203			 * If we are driving a keyboard there is nothing
3204			 * upstream to translate the scan codes. Therefore,
3205			 * set the error code to ENOTSUP and NAK the request
3206			 */
3207			if (asy->asy_device_type == ASY_KEYBOARD) {
3208				error = ENOTSUP;
3209				break;
3210			}
3211
3212			error = miocpullup(mp, sizeof (struct cons_polledio *));
3213			if (error != 0)
3214				break;
3215
3216			/*
3217			 * send up a message block containing the
3218			 * cons_polledio structure. This provides
3219			 * handles to the putchar, getchar, ischar,
3220			 * polledio_enter and polledio_exit functions.
3221			 */
3222			*(struct cons_polledio **)mp->b_cont->b_rptr =
3223			    &asy->polledio;
3224
3225			mp->b_datap->db_type = M_IOCACK;
3226			break;
3227
3228		case CONSCLOSEPOLLEDIO:
3229			/*
3230			 * If we are driving a keyboard we never successfully
3231			 * called CONSOPENPOLLEDIO so set the error to
3232			 * ENOTSUP and NAK the request.
3233			 */
3234			if (asy->asy_device_type == ASY_KEYBOARD) {
3235				error = ENOTSUP;
3236				break;
3237			}
3238
3239			mp->b_datap->db_type = M_IOCACK;
3240			iocp->ioc_error = 0;
3241			iocp->ioc_rval = 0;
3242			break;
3243
3244		default: /* unexpected ioctl type */
3245			/*
3246			 * If we don't understand it, it's an error.  NAK it.
3247			 */
3248			error = EINVAL;
3249			break;
3250		}
3251	}
3252	if (error != 0) {
3253		iocp->ioc_error = error;
3254		mp->b_datap->db_type = M_IOCNAK;
3255	}
3256	mutex_exit(asy->asy_excl);
3257	qreply(wq, mp);
3258}
3259
3260static void
3261asyrsrv(queue_t *q)
3262{
3263	mblk_t *bp;
3264	struct asyncline *async;
3265
3266	async = (struct asyncline *)q->q_ptr;
3267
3268	while (canputnext(q) && (bp = getq(q)))
3269		putnext(q, bp);
3270	ASYSETSOFT(async->async_common);
3271	async->async_polltid = 0;
3272}
3273
3274/*
3275 * Put procedure for write queue.
3276 * Respond to M_STOP, M_START, M_IOCTL, and M_FLUSH messages here;
3277 * set the flow control character for M_STOPI and M_STARTI messages;
3278 * queue up M_BREAK, M_DELAY, and M_DATA messages for processing
3279 * by the start routine, and then call the start routine; discard
3280 * everything else.  Note that this driver does not incorporate any
3281 * mechanism to negotiate to handle the canonicalization process.
3282 * It expects that these functions are handled in upper module(s),
3283 * as we do in ldterm.
3284 */
3285static void
3286asywput(queue_t *q, mblk_t *mp)
3287{
3288	register struct asyncline *async;
3289	register struct asycom *asy;
3290	int error;
3291
3292	async = (struct asyncline *)q->q_ptr;
3293	asy = async->async_common;
3294
3295	switch (mp->b_datap->db_type) {
3296
3297	case M_STOP:
3298		/*
3299		 * Since we don't do real DMA, we can just let the
3300		 * chip coast to a stop after applying the brakes.
3301		 */
3302		mutex_enter(asy->asy_excl);
3303		async->async_flags |= ASYNC_STOPPED;
3304		mutex_exit(asy->asy_excl);
3305		freemsg(mp);
3306		break;
3307
3308	case M_START:
3309		mutex_enter(asy->asy_excl);
3310		if (async->async_flags & ASYNC_STOPPED) {
3311			async->async_flags &= ~ASYNC_STOPPED;
3312			/*
3313			 * If an output operation is in progress,
3314			 * resume it.  Otherwise, prod the start
3315			 * routine.
3316			 */
3317			if (async->async_ocnt > 0) {
3318				mutex_enter(asy->asy_excl_hi);
3319				async_resume(async);
3320				mutex_exit(asy->asy_excl_hi);
3321			} else {
3322				async_start(async);
3323			}
3324		}
3325		mutex_exit(asy->asy_excl);
3326		freemsg(mp);
3327		break;
3328
3329	case M_IOCTL:
3330		switch (((struct iocblk *)mp->b_rptr)->ioc_cmd) {
3331
3332		case TCSBRK:
3333			error = miocpullup(mp, sizeof (int));
3334			if (error != 0) {
3335				miocnak(q, mp, 0, error);
3336				return;
3337			}
3338
3339			if (*(int *)mp->b_cont->b_rptr != 0) {
3340#ifdef DEBUG
3341				if (asydebug & ASY_DEBUG_CLOSE)
3342					printf("asy%d: flush request.\n",
3343					    UNIT(async->async_dev));
3344#endif
3345				(void) putq(q, mp);
3346				mutex_enter(asy->asy_excl);
3347				async_nstart(async, 1);
3348				mutex_exit(asy->asy_excl);
3349				break;
3350			}
3351			/*FALLTHROUGH*/
3352		case TCSETSW:
3353		case TCSETSF:
3354		case TCSETAW:
3355		case TCSETAF:
3356			/*
3357			 * The changes do not take effect until all
3358			 * output queued before them is drained.
3359			 * Put this message on the queue, so that
3360			 * "async_start" will see it when it's done
3361			 * with the output before it.  Poke the
3362			 * start routine, just in case.
3363			 */
3364			(void) putq(q, mp);
3365			mutex_enter(asy->asy_excl);
3366			async_start(async);
3367			mutex_exit(asy->asy_excl);
3368			break;
3369
3370		default:
3371			/*
3372			 * Do it now.
3373			 */
3374			async_ioctl(async, q, mp, B_TRUE);
3375			break;
3376		}
3377		break;
3378
3379	case M_FLUSH:
3380		if (*mp->b_rptr & FLUSHW) {
3381			mutex_enter(asy->asy_excl);
3382
3383			/*
3384			 * Abort any output in progress.
3385			 */
3386			mutex_enter(asy->asy_excl_hi);
3387			if (async->async_flags & ASYNC_BUSY) {
3388				async->async_ocnt = 0;
3389				async->async_flags &= ~ASYNC_BUSY;
3390			}
3391			mutex_exit(asy->asy_excl_hi);
3392
3393			/* Flush FIFO buffers */
3394			if (asy->asy_use_fifo == FIFO_ON) {
3395				OUTB(FIFOR, FIFO_ON | FIFODMA | FIFOTXFLSH |
3396				    (asy->asy_trig_level & 0xff));
3397			}
3398
3399			/*
3400			 * Flush our write queue.
3401			 */
3402			flushq(q, FLUSHDATA);	/* XXX doesn't flush M_DELAY */
3403			if (async->async_xmitblk != NULL) {
3404				freeb(async->async_xmitblk);
3405				async->async_xmitblk = NULL;
3406			}
3407
3408			mutex_exit(asy->asy_excl);
3409			*mp->b_rptr &= ~FLUSHW;	/* it has been flushed */
3410		}
3411		if (*mp->b_rptr & FLUSHR) {
3412			/* Flush FIFO buffers */
3413			if (asy->asy_use_fifo == FIFO_ON) {
3414				OUTB(FIFOR, FIFO_ON | FIFODMA | FIFORXFLSH |
3415				    (asy->asy_trig_level & 0xff));
3416			}
3417			flushq(RD(q), FLUSHDATA);
3418			qreply(q, mp);	/* give the read queues a crack at it */
3419		} else {
3420			freemsg(mp);
3421		}
3422
3423		/*
3424		 * We must make sure we process messages that survive the
3425		 * write-side flush.  Without this call, the close protocol
3426		 * with ldterm can hang forever.  (ldterm will have sent us a
3427		 * TCSBRK ioctl that it expects a response to.)
3428		 */
3429		mutex_enter(asy->asy_excl);
3430		async_start(async);
3431		mutex_exit(asy->asy_excl);
3432		break;
3433	case M_BREAK:
3434	case M_DELAY:
3435	case M_DATA:
3436		/*
3437		 * Queue the message up to be transmitted,
3438		 * and poke the start routine.
3439		 */
3440		(void) putq(q, mp);
3441		mutex_enter(asy->asy_excl);
3442		async_start(async);
3443		mutex_exit(asy->asy_excl);
3444		break;
3445
3446	case M_STOPI:
3447		mutex_enter(asy->asy_excl);
3448		async->async_flowc = async->async_stopc;
3449		async_start(async);		/* poke the start routine */
3450		mutex_exit(asy->asy_excl);
3451		freemsg(mp);
3452		break;
3453
3454	case M_STARTI:
3455		mutex_enter(asy->asy_excl);
3456		async->async_flowc = async->async_startc;
3457		async_start(async);		/* poke the start routine */
3458		mutex_exit(asy->asy_excl);
3459		freemsg(mp);
3460		break;
3461
3462	case M_CTL:
3463		if (MBLKL(mp) >= sizeof (struct iocblk) &&
3464		    ((struct iocblk *)mp->b_rptr)->ioc_cmd == MC_POSIXQUERY) {
3465			((struct iocblk *)mp->b_rptr)->ioc_cmd = MC_HAS_POSIX;
3466			qreply(q, mp);
3467		} else {
3468			/*
3469			 * These MC_SERVICE type messages are used by upper
3470			 * modules to tell this driver to send input up
3471			 * immediately, or that it can wait for normal
3472			 * processing that may or may not be done.  Sun
3473			 * requires these for the mouse module.
3474			 * (XXX - for x86?)
3475			 */
3476			mutex_enter(asy->asy_excl);
3477			switch (*mp->b_rptr) {
3478
3479			case MC_SERVICEIMM:
3480				async->async_flags |= ASYNC_SERVICEIMM;
3481				break;
3482
3483			case MC_SERVICEDEF:
3484				async->async_flags &= ~ASYNC_SERVICEIMM;
3485				break;
3486			}
3487			mutex_exit(asy->asy_excl);
3488			freemsg(mp);
3489		}
3490		break;
3491
3492	case M_IOCDATA:
3493		async_iocdata(q, mp);
3494		break;
3495
3496	default:
3497		freemsg(mp);
3498		break;
3499	}
3500}
3501
3502/*
3503 * Retry an "ioctl", now that "bufcall" claims we may be able to allocate
3504 * the buffer we need.
3505 */
3506static void
3507async_reioctl(void *arg)
3508{
3509	struct asyncline *async = arg;
3510	struct asycom *asy = async->async_common;
3511	queue_t	*q;
3512	mblk_t		*mp;
3513
3514	/*
3515	 * The bufcall is no longer pending.
3516	 */
3517	mutex_enter(asy->asy_excl);
3518	async->async_wbufcid = 0;
3519	if ((q = async->async_ttycommon.t_writeq) == NULL) {
3520		mutex_exit(asy->asy_excl);
3521		return;
3522	}
3523	if ((mp = async->async_ttycommon.t_iocpending) != NULL) {
3524		/* not pending any more */
3525		async->async_ttycommon.t_iocpending = NULL;
3526		mutex_exit(asy->asy_excl);
3527		/* not in STREAMS queue; we no longer know if we're in wput */
3528		async_ioctl(async, q, mp, B_TRUE);
3529	} else
3530		mutex_exit(asy->asy_excl);
3531}
3532
3533static void
3534async_iocdata(queue_t *q, mblk_t *mp)
3535{
3536	struct asyncline	*async = (struct asyncline *)q->q_ptr;
3537	struct asycom		*asy;
3538	struct copyresp *csp;
3539
3540	asy = async->async_common;
3541	csp = (struct copyresp *)mp->b_rptr;
3542
3543	if (csp->cp_rval != 0) {
3544		freemsg(mp);
3545		return;
3546	}
3547
3548	mutex_enter(asy->asy_excl);
3549
3550	switch (csp->cp_cmd) {
3551	case TIOCMSET:
3552	case TIOCMBIS:
3553	case TIOCMBIC:
3554		if (mp->b_cont == NULL) {
3555			mutex_exit(asy->asy_excl);
3556			miocnak(q, mp, 0, EINVAL);
3557			break;
3558		}
3559
3560		mutex_enter(asy->asy_excl_hi);
3561		(void) asymctl(asy, dmtoasy(*(int *)mp->b_cont->b_rptr),
3562		    csp->cp_cmd);
3563		mutex_exit(asy->asy_excl_hi);
3564
3565		freemsg(mp->b_cont);
3566		mp->b_cont = NULL;
3567		mutex_exit(asy->asy_excl);
3568		miocack(q, mp, 0, 0);
3569		break;
3570
3571	case TIOCMGET:
3572		if (mp->b_cont != NULL) {
3573			freemsg(mp->b_cont);
3574			mp->b_cont = NULL;
3575		}
3576		mutex_exit(asy->asy_excl);
3577		miocack(q, mp, 0, 0);
3578		break;
3579
3580	default:
3581		mutex_exit(asy->asy_excl);
3582		miocnak(q, mp, 0, EINVAL);
3583		break;
3584	}
3585}
3586
3587
3588/*
3589 * Set or get the modem control status.
3590 */
3591static int
3592asymctl(struct asycom *asy, int bits, int how)
3593{
3594	register int mcr_r, msr_r;
3595
3596	ASSERT(mutex_owned(asy->asy_excl_hi));
3597	ASSERT(mutex_owned(asy->asy_excl));
3598
3599	/* Read Modem Control Registers */
3600	mcr_r = INB(MCR);
3601
3602	switch (how) {
3603
3604	case TIOCMSET:
3605		mcr_r = bits;
3606		break;
3607
3608	case TIOCMBIS:
3609		mcr_r |= bits;			/* Set bits from input	*/
3610		break;
3611
3612	case TIOCMBIC:
3613		mcr_r &= ~bits;			/* Set ~bits from input	*/
3614		break;
3615
3616	case TIOCMGET:
3617		/* Read Modem Status Registers */
3618		if (INB(ICR) & MIEN)
3619			msr_r = asy->asy_cached_msr;
3620		else
3621			msr_r = INB(MSR);
3622		return (asytodm(mcr_r, msr_r));
3623	}
3624
3625	OUTB(MCR, mcr_r);
3626
3627	return (mcr_r);
3628}
3629
3630static int
3631asytodm(int mcr_r, int msr_r)
3632{
3633	register int b = 0;
3634
3635
3636	/* MCR registers */
3637	if (mcr_r & RTS)
3638		b |= TIOCM_RTS;
3639
3640	if (mcr_r & DTR)
3641		b |= TIOCM_DTR;
3642
3643	/* MSR registers */
3644	if (msr_r & DCD)
3645		b |= TIOCM_CAR;
3646
3647	if (msr_r & CTS)
3648		b |= TIOCM_CTS;
3649
3650	if (msr_r & DSR)
3651		b |= TIOCM_DSR;
3652
3653	if (msr_r & RI)
3654		b |= TIOCM_RNG;
3655
3656	return (b);
3657}
3658
3659static int
3660dmtoasy(int bits)
3661{
3662	register int b = 0;
3663
3664#ifdef	CAN_NOT_SET	/* only DTR and RTS can be set */
3665	if (bits & TIOCM_CAR)
3666		b |= DCD;
3667	if (bits & TIOCM_CTS)
3668		b |= CTS;
3669	if (bits & TIOCM_DSR)
3670		b |= DSR;
3671	if (bits & TIOCM_RNG)
3672		b |= RI;
3673#endif
3674
3675	if (bits & TIOCM_RTS)
3676		b |= RTS;
3677	if (bits & TIOCM_DTR)
3678		b |= DTR;
3679
3680	return (b);
3681}
3682
3683static void
3684asycheckflowcontrol_hw(struct asycom *asy)
3685{
3686	struct asyncline *async;
3687	uchar_t	mcr, flag;
3688
3689	ASSERT(mutex_owned(asy->asy_excl_hi));
3690
3691	async = (struct asyncline *)asy->asy_priv;
3692	ASSERT(async != NULL);
3693
3694	if (async->async_ttycommon.t_cflag & CRTSXOFF) {
3695		mcr = INB(MCR);
3696		flag = (async->async_flags & ASYNC_HW_IN_FLOW) ? 0 : RTS;
3697		if (((mcr ^ flag) & RTS) != 0) {
3698			OUTB(MCR, (mcr ^ RTS));
3699		}
3700	}
3701}
3702
3703static boolean_t
3704asycheckflowcontrol_sw(struct asycom *asy)
3705{
3706	uchar_t		ss;
3707	struct asyncline *async;
3708	int rval = B_FALSE;
3709
3710	ASSERT(mutex_owned(asy->asy_excl_hi));
3711
3712	async = (struct asyncline *)asy->asy_priv;
3713	ASSERT(async != NULL);
3714
3715	if ((ss = async->async_flowc) != '\0' && (INB(LSR) & XHRE)) {
3716		/*
3717		 * If we get this far, then we know that flowc is non-zero and
3718		 * that there's transmit room available.  We've "handled" the
3719		 * request now, so clear it.  If the user didn't ask for IXOFF,
3720		 * then don't actually send anything, but wait for the next
3721		 * opportunity.
3722		 */
3723		async->async_flowc = '\0';
3724		if (async->async_ttycommon.t_iflag & IXOFF) {
3725			async->async_flags |= ASYNC_BUSY;
3726			OUTB(DAT, ss);
3727			rval = B_TRUE;
3728		}
3729	}
3730
3731	return (rval);
3732}
3733
3734/*
3735 * Check for abort character sequence
3736 */
3737static boolean_t
3738abort_charseq_recognize(uchar_t ch)
3739{
3740	static int state = 0;
3741#define	CNTRL(c) ((c)&037)
3742	static char sequence[] = { '\r', '~', CNTRL('b') };
3743
3744	if (ch == sequence[state]) {
3745		if (++state >= sizeof (sequence)) {
3746			state = 0;
3747			return (B_TRUE);
3748		}
3749	} else {
3750		state = (ch == sequence[0]) ? 1 : 0;
3751	}
3752	return (B_FALSE);
3753}
3754