xref: /illumos-gate/usr/src/uts/common/io/asy.c (revision e49b90fb462012f1ca373a15a9e16657b22a36e6)
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 
22 /*	Copyright (c) 1990, 1991 UNIX System Laboratories, Inc.	*/
23 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T	*/
24 /*	  All Rights Reserved					*/
25 
26 /*
27  * Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved.
28  * Copyright 2012 Milan Jurik. All rights reserved.
29  * Copyright (c) 2016 by Delphix. All rights reserved.
30  * Copyright 2023 Oxide Computer Company
31  * Copyright 2024 Hans Rosenfeld
32  */
33 
34 
35 /*
36  * Serial I/O driver for 8250/16450/16550A/16650/16750 chips.
37  */
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/strtty.h>
50 #include <sys/debug.h>
51 #include <sys/kbio.h>
52 #include <sys/cred.h>
53 #include <sys/stat.h>
54 #include <sys/consdev.h>
55 #include <sys/mkdev.h>
56 #include <sys/kmem.h>
57 #include <sys/cred.h>
58 #include <sys/strsun.h>
59 #ifdef DEBUG
60 #include <sys/promif.h>
61 #endif
62 #include <sys/modctl.h>
63 #include <sys/ddi.h>
64 #include <sys/sunddi.h>
65 #include <sys/pci.h>
66 #include <sys/asy.h>
67 #include <sys/policy.h>
68 
69 /*
70  * set the RX FIFO trigger_level to half the RX FIFO size for now
71  * we may want to make this configurable later.
72  */
73 static	int asy_trig_level = FIFO_TRIG_8;
74 
75 int asy_drain_check = 15000000;		/* tunable: exit drain check time */
76 int asy_min_dtr_low = 500000;		/* tunable: minimum DTR down time */
77 int asy_min_utbrk = 100000;		/* tunable: minumum untimed brk time */
78 
79 int asymaxchip = ASY16750;	/* tunable: limit chip support we look for */
80 
81 /*
82  * Just in case someone has a chip with broken loopback mode, we provide a
83  * means to disable the loopback test. By default, we only loopback test
84  * UARTs which look like they have FIFOs bigger than 16 bytes.
85  * Set to 0 to suppress test, or to 2 to enable test on any size FIFO.
86  */
87 int asy_fifo_test = 1;		/* tunable: set to 0, 1, or 2 */
88 
89 /*
90  * Allow ability to switch off testing of the scratch register.
91  * Some UART emulators might not have it. This will also disable the test
92  * for Exar/Startech ST16C650, as that requires use of the SCR register.
93  */
94 int asy_scr_test = 1;		/* tunable: set to 0 to disable SCR reg test */
95 
96 /*
97  * As we don't yet support on-chip flow control, it's a bad idea to put a
98  * large number of characters in the TX FIFO, since if other end tells us
99  * to stop transmitting, we can only stop filling the TX FIFO, but it will
100  * still carry on draining by itself, so remote end still gets what's left
101  * in the FIFO.
102  */
103 int asy_max_tx_fifo = 16;	/* tunable: max fill of TX FIFO */
104 
105 #define	async_stopc	async_ttycommon.t_stopc
106 #define	async_startc	async_ttycommon.t_startc
107 
108 #define	ASY_INIT	1
109 #define	ASY_NOINIT	0
110 
111 /* enum value for sw and hw flow control action */
112 typedef enum {
113 	FLOW_CHECK,
114 	FLOW_STOP,
115 	FLOW_START
116 } async_flowc_action;
117 
118 #ifdef DEBUG
119 #define	ASY_DEBUG_INIT	0x0001	/* Output msgs during driver initialization. */
120 #define	ASY_DEBUG_INPUT	0x0002	/* Report characters received during int. */
121 #define	ASY_DEBUG_EOT	0x0004	/* Output msgs when wait for xmit to finish. */
122 #define	ASY_DEBUG_CLOSE	0x0008	/* Output msgs when driver open/close called */
123 #define	ASY_DEBUG_HFLOW	0x0010	/* Output msgs when H/W flowcontrol is active */
124 #define	ASY_DEBUG_PROCS	0x0020	/* Output each proc name as it is entered. */
125 #define	ASY_DEBUG_STATE	0x0040	/* Output value of Interrupt Service Reg. */
126 #define	ASY_DEBUG_INTR	0x0080	/* Output value of Interrupt Service Reg. */
127 #define	ASY_DEBUG_OUT	0x0100	/* Output msgs about output events. */
128 #define	ASY_DEBUG_BUSY	0x0200	/* Output msgs when xmit is enabled/disabled */
129 #define	ASY_DEBUG_MODEM	0x0400	/* Output msgs about modem status & control. */
130 #define	ASY_DEBUG_MODM2	0x0800	/* Output msgs about modem status & control. */
131 #define	ASY_DEBUG_IOCTL	0x1000	/* Output msgs about ioctl messages. */
132 #define	ASY_DEBUG_CHIP	0x2000	/* Output msgs about chip identification. */
133 #define	ASY_DEBUG_SFLOW	0x4000	/* Output msgs when S/W flowcontrol is active */
134 
135 static	int debug  = 0;
136 
137 #define	ASY_DEBUG(asy, x) (asy->asy_debug & (x))
138 #define	ASY_DPRINTF(asy, fac, format, ...) \
139 	if (ASY_DEBUG(asy, fac)) \
140 		asyerror(asy, CE_CONT, "!%s: " format, __func__, ##__VA_ARGS__)
141 #else
142 #define	ASY_DEBUG(asy, x) B_FALSE
143 #define	ASY_DPRINTF(asy, fac, format, ...)
144 #endif
145 
146 /* pnpISA compressed device ids */
147 #define	pnpMTS0219 0xb6930219	/* Multitech MT5634ZTX modem */
148 
149 /*
150  * PPS (Pulse Per Second) support.
151  */
152 void ddi_hardpps(struct timeval *, int);
153 /*
154  * This is protected by the asy_excl_hi of the port on which PPS event
155  * handling is enabled.  Note that only one port should have this enabled at
156  * any one time.  Enabling PPS handling on multiple ports will result in
157  * unpredictable (but benign) results.
158  */
159 static struct ppsclockev asy_ppsev;
160 
161 #ifdef PPSCLOCKLED
162 /* XXX Use these to observe PPS latencies and jitter on a scope */
163 #define	LED_ON
164 #define	LED_OFF
165 #else
166 #define	LED_ON
167 #define	LED_OFF
168 #endif
169 
170 static void	asysetsoft(struct asycom *);
171 static uint_t	asysoftintr(caddr_t, caddr_t);
172 static uint_t	asyintr(caddr_t, caddr_t);
173 
174 static boolean_t abort_charseq_recognize(uchar_t ch);
175 
176 /* The async interrupt entry points */
177 static void	async_txint(struct asycom *asy);
178 static void	async_rxint(struct asycom *asy, uchar_t lsr);
179 static void	async_msint(struct asycom *asy);
180 static void	async_softint(struct asycom *asy);
181 
182 static void	async_ioctl(struct asyncline *async, queue_t *q, mblk_t *mp);
183 static void	async_reioctl(void *unit);
184 static void	async_iocdata(queue_t *q, mblk_t *mp);
185 static void	async_restart(void *arg);
186 static void	async_start(struct asyncline *async);
187 static void	async_resume(struct asyncline *async);
188 static void	asy_program(struct asycom *asy, int mode);
189 static void	asyinit(struct asycom *asy);
190 static void	asy_waiteot(struct asycom *asy);
191 static void	asyputchar(cons_polledio_arg_t, uchar_t c);
192 static int	asygetchar(cons_polledio_arg_t);
193 static boolean_t	asyischar(cons_polledio_arg_t);
194 
195 static int	asymctl(struct asycom *, int, int);
196 static int	asytodm(int, int);
197 static int	dmtoasy(struct asycom *, int);
198 static void	asyerror(struct asycom *, int, const char *, ...)
199 	__KPRINTFLIKE(3);
200 static void	asy_parse_mode(dev_info_t *devi, struct asycom *asy);
201 static void	asy_soft_state_free(struct asycom *);
202 static char	*asy_hw_name(struct asycom *asy);
203 static void	async_hold_utbrk(void *arg);
204 static void	async_resume_utbrk(struct asyncline *async);
205 static void	async_dtr_free(struct asyncline *async);
206 static int	asy_identify_chip(dev_info_t *devi, struct asycom *asy);
207 static void	asy_reset_fifo(struct asycom *asy, uchar_t flags);
208 static int	asy_getproperty(dev_info_t *devi, struct asycom *asy,
209 		    const char *property);
210 static boolean_t	async_flowcontrol_sw_input(struct asycom *asy,
211 			    async_flowc_action onoff, int type);
212 static void	async_flowcontrol_sw_output(struct asycom *asy,
213 		    async_flowc_action onoff);
214 static void	async_flowcontrol_hw_input(struct asycom *asy,
215 		    async_flowc_action onoff, int type);
216 static void	async_flowcontrol_hw_output(struct asycom *asy,
217 		    async_flowc_action onoff);
218 
219 #define	GET_PROP(devi, pname, pflag, pval, plen) \
220 		(ddi_prop_op(DDI_DEV_T_ANY, (devi), PROP_LEN_AND_VAL_BUF, \
221 		(pflag), (pname), (caddr_t)(pval), (plen)))
222 
223 kmutex_t asy_glob_lock; /* lock protecting global data manipulation */
224 void *asy_soft_state;
225 
226 /* Standard COM port I/O addresses */
227 static const int standard_com_ports[] = {
228 	COM1_IOADDR, COM2_IOADDR, COM3_IOADDR, COM4_IOADDR
229 };
230 
231 static int *com_ports;
232 static uint_t num_com_ports;
233 
234 #ifdef	DEBUG
235 /*
236  * Set this to true to make the driver pretend to do a suspend.  Useful
237  * for debugging suspend/resume code with a serial debugger.
238  */
239 boolean_t	asy_nosuspend = B_FALSE;
240 #endif
241 
242 
243 /*
244  * Baud rate table. Indexed by #defines found in sys/termios.h
245  */
246 ushort_t asyspdtab[] = {
247 	0,	/* 0 baud rate */
248 	0x900,	/* 50 baud rate */
249 	0x600,	/* 75 baud rate */
250 	0x417,	/* 110 baud rate (%0.026) */
251 	0x359,	/* 134 baud rate (%0.058) */
252 	0x300,	/* 150 baud rate */
253 	0x240,	/* 200 baud rate */
254 	0x180,	/* 300 baud rate */
255 	0x0c0,	/* 600 baud rate */
256 	0x060,	/* 1200 baud rate */
257 	0x040,	/* 1800 baud rate */
258 	0x030,	/* 2400 baud rate */
259 	0x018,	/* 4800 baud rate */
260 	0x00c,	/* 9600 baud rate */
261 	0x006,	/* 19200 baud rate */
262 	0x003,	/* 38400 baud rate */
263 
264 	0x002,	/* 57600 baud rate */
265 	0x0,	/* 76800 baud rate not supported */
266 	0x001,	/* 115200 baud rate */
267 	0x0,	/* 153600 baud rate not supported */
268 	0x0,	/* 0x8002 (SMC chip) 230400 baud rate not supported */
269 	0x0,	/* 307200 baud rate not supported */
270 	0x0,	/* 0x8001 (SMC chip) 460800 baud rate not supported */
271 	0x0,	/* 921600 baud rate not supported */
272 	0x0,	/* 1000000 baud rate not supported */
273 	0x0,	/* 1152000 baud rate not supported */
274 	0x0,	/* 1500000 baud rate not supported */
275 	0x0,	/* 2000000 baud rate not supported */
276 	0x0,	/* 2500000 baud rate not supported */
277 	0x0,	/* 3000000 baud rate not supported */
278 	0x0,	/* 3500000 baud rate not supported */
279 	0x0,	/* 4000000 baud rate not supported */
280 };
281 
282 static int asyrsrv(queue_t *q);
283 static int asyopen(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr);
284 static int asyclose(queue_t *q, int flag, cred_t *credp);
285 static int asywputdo(queue_t *q, mblk_t *mp, boolean_t);
286 static int asywput(queue_t *q, mblk_t *mp);
287 
288 struct module_info asy_info = {
289 	0,
290 	"asy",
291 	0,
292 	INFPSZ,
293 	4096,
294 	128
295 };
296 
297 static struct qinit asy_rint = {
298 	putq,
299 	asyrsrv,
300 	asyopen,
301 	asyclose,
302 	NULL,
303 	&asy_info,
304 	NULL
305 };
306 
307 static struct qinit asy_wint = {
308 	asywput,
309 	NULL,
310 	NULL,
311 	NULL,
312 	NULL,
313 	&asy_info,
314 	NULL
315 };
316 
317 struct streamtab asy_str_info = {
318 	&asy_rint,
319 	&asy_wint,
320 	NULL,
321 	NULL
322 };
323 
324 static void asy_intr_free(struct asycom *);
325 static int asy_intr_setup(struct asycom *, int);
326 
327 static void asy_softintr_free(struct asycom *);
328 static int asy_softintr_setup(struct asycom *);
329 
330 static int asy_suspend(struct asycom *);
331 static int asy_resume(dev_info_t *);
332 
333 static int asyinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
334 		void **result);
335 static int asyprobe(dev_info_t *);
336 static int asyattach(dev_info_t *, ddi_attach_cmd_t);
337 static int asydetach(dev_info_t *, ddi_detach_cmd_t);
338 static int asyquiesce(dev_info_t *);
339 
340 static struct cb_ops cb_asy_ops = {
341 	nodev,			/* cb_open */
342 	nodev,			/* cb_close */
343 	nodev,			/* cb_strategy */
344 	nodev,			/* cb_print */
345 	nodev,			/* cb_dump */
346 	nodev,			/* cb_read */
347 	nodev,			/* cb_write */
348 	nodev,			/* cb_ioctl */
349 	nodev,			/* cb_devmap */
350 	nodev,			/* cb_mmap */
351 	nodev,			/* cb_segmap */
352 	nochpoll,		/* cb_chpoll */
353 	ddi_prop_op,		/* cb_prop_op */
354 	&asy_str_info,		/* cb_stream */
355 	D_MP			/* cb_flag */
356 };
357 
358 struct dev_ops asy_ops = {
359 	DEVO_REV,		/* devo_rev */
360 	0,			/* devo_refcnt */
361 	asyinfo,		/* devo_getinfo */
362 	nulldev,		/* devo_identify */
363 	asyprobe,		/* devo_probe */
364 	asyattach,		/* devo_attach */
365 	asydetach,		/* devo_detach */
366 	nodev,			/* devo_reset */
367 	&cb_asy_ops,		/* devo_cb_ops */
368 	NULL,			/* devo_bus_ops */
369 	NULL,			/* power */
370 	asyquiesce,		/* quiesce */
371 };
372 
373 static struct modldrv modldrv = {
374 	&mod_driverops, /* Type of module.  This one is a driver */
375 	"ASY driver",
376 	&asy_ops,	/* driver ops */
377 };
378 
379 static struct modlinkage modlinkage = {
380 	MODREV_1,
381 	(void *)&modldrv,
382 	NULL
383 };
384 
385 int
386 _init(void)
387 {
388 	int i;
389 
390 	i = ddi_soft_state_init(&asy_soft_state, sizeof (struct asycom), 2);
391 	if (i == 0) {
392 		mutex_init(&asy_glob_lock, NULL, MUTEX_DRIVER, NULL);
393 		if ((i = mod_install(&modlinkage)) != 0) {
394 			mutex_destroy(&asy_glob_lock);
395 			ddi_soft_state_fini(&asy_soft_state);
396 #ifdef DEBUG
397 		} else {
398 			if (debug & ASY_DEBUG_INIT)
399 				cmn_err(CE_NOTE, "!%s, debug = %x",
400 				    modldrv.drv_linkinfo, debug);
401 #endif
402 		}
403 	}
404 	return (i);
405 }
406 
407 int
408 _fini(void)
409 {
410 	int i;
411 
412 	if ((i = mod_remove(&modlinkage)) == 0) {
413 #ifdef DEBUG
414 		if (debug & ASY_DEBUG_INIT)
415 			cmn_err(CE_NOTE, "!%s unloading",
416 			    modldrv.drv_linkinfo);
417 #endif
418 		mutex_destroy(&asy_glob_lock);
419 		/* free "motherboard-serial-ports" property if allocated */
420 		if (com_ports != NULL && com_ports != (int *)standard_com_ports)
421 			ddi_prop_free(com_ports);
422 		com_ports = NULL;
423 		ddi_soft_state_fini(&asy_soft_state);
424 	}
425 	return (i);
426 }
427 
428 int
429 _info(struct modinfo *modinfop)
430 {
431 	return (mod_info(&modlinkage, modinfop));
432 }
433 
434 void
435 async_put_suspq(struct asycom *asy, mblk_t *mp)
436 {
437 	struct asyncline *async = asy->asy_priv;
438 
439 	ASSERT(mutex_owned(&asy->asy_excl));
440 
441 	if (async->async_suspqf == NULL)
442 		async->async_suspqf = mp;
443 	else
444 		async->async_suspqb->b_next = mp;
445 
446 	async->async_suspqb = mp;
447 }
448 
449 static mblk_t *
450 async_get_suspq(struct asycom *asy)
451 {
452 	struct asyncline *async = asy->asy_priv;
453 	mblk_t *mp;
454 
455 	ASSERT(mutex_owned(&asy->asy_excl));
456 
457 	if ((mp = async->async_suspqf) != NULL) {
458 		async->async_suspqf = mp->b_next;
459 		mp->b_next = NULL;
460 	} else {
461 		async->async_suspqb = NULL;
462 	}
463 	return (mp);
464 }
465 
466 static void
467 async_process_suspq(struct asycom *asy)
468 {
469 	struct asyncline *async = asy->asy_priv;
470 	mblk_t *mp;
471 
472 	ASSERT(mutex_owned(&asy->asy_excl));
473 
474 	while ((mp = async_get_suspq(asy)) != NULL) {
475 		queue_t *q;
476 
477 		q = async->async_ttycommon.t_writeq;
478 		ASSERT(q != NULL);
479 		mutex_exit(&asy->asy_excl);
480 		(void) asywputdo(q, mp, B_FALSE);
481 		mutex_enter(&asy->asy_excl);
482 	}
483 	async->async_flags &= ~ASYNC_DDI_SUSPENDED;
484 	cv_broadcast(&async->async_flags_cv);
485 }
486 
487 static int
488 asy_get_bus_type(dev_info_t *devinfo)
489 {
490 	char	parent_type[16];
491 	int	parentlen;
492 
493 	parentlen = sizeof (parent_type);
494 
495 	if (ddi_prop_op(DDI_DEV_T_ANY, devinfo, PROP_LEN_AND_VAL_BUF, 0,
496 	    "device_type", (caddr_t)parent_type, &parentlen)
497 	    != DDI_PROP_SUCCESS && ddi_prop_op(DDI_DEV_T_ANY, devinfo,
498 	    PROP_LEN_AND_VAL_BUF, 0, "bus-type", (caddr_t)parent_type,
499 	    &parentlen) != DDI_PROP_SUCCESS) {
500 		dev_err(devinfo, CE_WARN,
501 		    "!%s: can't figure out device type for parent \"%s\"",
502 		    __func__, ddi_get_name(ddi_get_parent(devinfo)));
503 		return (ASY_BUS_UNKNOWN);
504 	}
505 	if (strcmp(parent_type, "isa") == 0)
506 		return (ASY_BUS_ISA);
507 	else if (strcmp(parent_type, "pci") == 0)
508 		return (ASY_BUS_PCI);
509 	else
510 		return (ASY_BUS_UNKNOWN);
511 }
512 
513 static int
514 asy_get_io_regnum_pci(dev_info_t *devi, struct asycom *asy)
515 {
516 	int reglen, nregs;
517 	int regnum, i;
518 	uint64_t size;
519 	struct pci_phys_spec *reglist;
520 
521 	if (ddi_getlongprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,
522 	    "reg", (caddr_t)&reglist, &reglen) != DDI_PROP_SUCCESS) {
523 		dev_err(devi, CE_WARN, "!%s: reg property"
524 		    " not found in devices property list", __func__);
525 		return (-1);
526 	}
527 
528 	/*
529 	 * PCI devices are assumed to not have broken FIFOs;
530 	 * Agere/Lucent Venus PCI modem chipsets are an example
531 	 */
532 	if (asy)
533 		asy->asy_flags2 |= ASY2_NO_LOOPBACK;
534 
535 	regnum = -1;
536 	nregs = reglen / sizeof (*reglist);
537 	for (i = 0; i < nregs; i++) {
538 		switch (reglist[i].pci_phys_hi & PCI_ADDR_MASK) {
539 		case PCI_ADDR_IO:		/* I/O bus reg property */
540 			if (regnum == -1) /* use only the first one */
541 				regnum = i;
542 			break;
543 
544 		default:
545 			break;
546 		}
547 	}
548 
549 	/* check for valid count of registers */
550 	if (regnum >= 0) {
551 		size = ((uint64_t)reglist[regnum].pci_size_low) |
552 		    ((uint64_t)reglist[regnum].pci_size_hi) << 32;
553 		if (size < 8)
554 			regnum = -1;
555 	}
556 	kmem_free(reglist, reglen);
557 	return (regnum);
558 }
559 
560 static int
561 asy_get_io_regnum_isa(dev_info_t *devi, struct asycom *asy)
562 {
563 	int reglen, nregs;
564 	int regnum, i;
565 	struct {
566 		uint_t bustype;
567 		int base;
568 		int size;
569 	} *reglist;
570 
571 	if (ddi_getlongprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,
572 	    "reg", (caddr_t)&reglist, &reglen) != DDI_PROP_SUCCESS) {
573 		dev_err(devi, CE_WARN, "!%s: reg property not found "
574 		    "in devices property list", __func__);
575 		return (-1);
576 	}
577 
578 	regnum = -1;
579 	nregs = reglen / sizeof (*reglist);
580 	for (i = 0; i < nregs; i++) {
581 		switch (reglist[i].bustype) {
582 		case 1:			/* I/O bus reg property */
583 			if (regnum == -1) /* only use the first one */
584 				regnum = i;
585 			break;
586 
587 		case pnpMTS0219:	/* Multitech MT5634ZTX modem */
588 			/* Venus chipset can't do loopback test */
589 			if (asy)
590 				asy->asy_flags2 |= ASY2_NO_LOOPBACK;
591 			break;
592 
593 		default:
594 			break;
595 		}
596 	}
597 
598 	/* check for valid count of registers */
599 	if ((regnum < 0) || (reglist[regnum].size < 8))
600 		regnum = -1;
601 	kmem_free(reglist, reglen);
602 	return (regnum);
603 }
604 
605 static int
606 asy_get_io_regnum(dev_info_t *devinfo, struct asycom *asy)
607 {
608 	switch (asy_get_bus_type(devinfo)) {
609 	case ASY_BUS_ISA:
610 		return (asy_get_io_regnum_isa(devinfo, asy));
611 	case ASY_BUS_PCI:
612 		return (asy_get_io_regnum_pci(devinfo, asy));
613 	default:
614 		return (-1);
615 	}
616 }
617 
618 static void
619 asy_intr_free(struct asycom *asy)
620 {
621 	int i;
622 
623 	for (i = 0; i < asy->asy_intr_cnt; i++) {
624 		if (asy->asy_inth[i] == NULL)
625 			break;
626 
627 		if ((asy->asy_intr_cap & DDI_INTR_FLAG_BLOCK) != 0)
628 			(void) ddi_intr_block_disable(&asy->asy_inth[i], 1);
629 		else
630 			(void) ddi_intr_disable(asy->asy_inth[i]);
631 
632 		(void) ddi_intr_remove_handler(asy->asy_inth[i]);
633 		(void) ddi_intr_free(asy->asy_inth[i]);
634 	}
635 
636 	kmem_free(asy->asy_inth, asy->asy_inth_sz);
637 	asy->asy_inth = NULL;
638 	asy->asy_inth_sz = 0;
639 }
640 
641 static int
642 asy_intr_setup(struct asycom *asy, int intr_type)
643 {
644 	int nintrs, navail, count;
645 	int ret;
646 	int i;
647 
648 	if (asy->asy_intr_types == 0) {
649 		ret = ddi_intr_get_supported_types(asy->asy_dip,
650 		    &asy->asy_intr_types);
651 		if (ret != DDI_SUCCESS) {
652 			asyerror(asy, CE_WARN,
653 			    "ddi_intr_get_supported_types failed");
654 			return (ret);
655 		}
656 	}
657 
658 	if ((asy->asy_intr_types & intr_type) == 0)
659 		return (DDI_FAILURE);
660 
661 	ret = ddi_intr_get_nintrs(asy->asy_dip, intr_type, &nintrs);
662 	if (ret != DDI_SUCCESS) {
663 		asyerror(asy, CE_WARN, "ddi_intr_get_nintrs failed, type %d",
664 		    intr_type);
665 		return (ret);
666 	}
667 
668 	if (nintrs < 1) {
669 		asyerror(asy, CE_WARN, "no interrupts of type %d", intr_type);
670 		return (DDI_FAILURE);
671 	}
672 
673 	ret = ddi_intr_get_navail(asy->asy_dip, intr_type, &navail);
674 	if (ret != DDI_SUCCESS) {
675 		asyerror(asy, CE_WARN, "ddi_intr_get_navail failed, type %d",
676 		    intr_type);
677 		return (ret);
678 	}
679 
680 	if (navail < 1) {
681 		asyerror(asy, CE_WARN, "no available interrupts, type %d",
682 		    intr_type);
683 		return (DDI_FAILURE);
684 	}
685 
686 	/*
687 	 * Some PCI(e) RS232 adapters seem to support more than one interrupt,
688 	 * but the asy driver really doesn't.
689 	 */
690 	asy->asy_inth_sz = sizeof (ddi_intr_handle_t);
691 	asy->asy_inth = kmem_zalloc(asy->asy_inth_sz, KM_SLEEP);
692 	ret = ddi_intr_alloc(asy->asy_dip, asy->asy_inth, intr_type, 0, 1,
693 	    &count, 0);
694 	if (ret != DDI_SUCCESS) {
695 		asyerror(asy, CE_WARN, "ddi_intr_alloc failed, count %d, "
696 		    "type %d", navail, intr_type);
697 		goto fail;
698 	}
699 
700 	if (count != 1) {
701 		asyerror(asy, CE_WARN, "ddi_intr_alloc returned not 1 but %d "
702 		    "interrupts of type %d", count, intr_type);
703 		goto fail;
704 	}
705 
706 	asy->asy_intr_cnt = count;
707 
708 	ret = ddi_intr_get_pri(asy->asy_inth[0], &asy->asy_intr_pri);
709 	if (ret != DDI_SUCCESS) {
710 		asyerror(asy, CE_WARN, "ddi_intr_get_pri failed, type %d",
711 		    intr_type);
712 		goto fail;
713 	}
714 
715 	for (i = 0; i < count; i++) {
716 		ret = ddi_intr_add_handler(asy->asy_inth[i], asyintr,
717 		    (void *)asy, (void *)(uintptr_t)i);
718 		if (ret != DDI_SUCCESS) {
719 			asyerror(asy, CE_WARN, "ddi_intr_add_handler failed, "
720 			    "int %d, type %d", i, intr_type);
721 			goto fail;
722 		}
723 	}
724 
725 	(void) ddi_intr_get_cap(asy->asy_inth[0], &asy->asy_intr_cap);
726 
727 	for (i = 0; i < count; i++) {
728 		if (asy->asy_intr_cap & DDI_INTR_FLAG_BLOCK)
729 			ret = ddi_intr_block_enable(&asy->asy_inth[i], 1);
730 		else
731 			ret = ddi_intr_enable(asy->asy_inth[i]);
732 
733 		if (ret != DDI_SUCCESS) {
734 			asyerror(asy, CE_WARN,
735 			    "enabling interrupt %d failed, type %d",
736 			    i, intr_type);
737 			goto fail;
738 		}
739 	}
740 
741 	asy->asy_intr_type = intr_type;
742 	return (DDI_SUCCESS);
743 
744 fail:
745 	asy_intr_free(asy);
746 	return (ret);
747 }
748 
749 static void
750 asy_softintr_free(struct asycom *asy)
751 {
752 	(void) ddi_intr_remove_softint(asy->asy_soft_inth);
753 }
754 
755 static int
756 asy_softintr_setup(struct asycom *asy)
757 {
758 	int ret;
759 
760 	ret = ddi_intr_add_softint(asy->asy_dip, &asy->asy_soft_inth,
761 	    ASY_SOFT_INT_PRI, asysoftintr, asy);
762 	if (ret != DDI_SUCCESS) {
763 		asyerror(asy, CE_WARN, "ddi_intr_add_softint failed");
764 		return (ret);
765 	}
766 
767 	/*
768 	 * This may seem pointless since we specified ASY_SOFT_INT_PRI above,
769 	 * but then it's probably a good idea to consider the soft interrupt
770 	 * priority an opaque value and don't hardcode any assumptions about
771 	 * its actual value here.
772 	 */
773 	ret = ddi_intr_get_softint_pri(asy->asy_soft_inth,
774 	    &asy->asy_soft_intr_pri);
775 	if (ret != DDI_SUCCESS) {
776 		asyerror(asy, CE_WARN, "ddi_intr_get_softint_pri failed");
777 		return (ret);
778 	}
779 
780 	return (DDI_SUCCESS);
781 }
782 
783 
784 static int
785 asy_resume(dev_info_t *devi)
786 {
787 	struct asyncline *async;
788 	struct asycom *asy;
789 	int instance = ddi_get_instance(devi);	/* find out which unit */
790 
791 #ifdef	DEBUG
792 	if (asy_nosuspend)
793 		return (DDI_SUCCESS);
794 #endif
795 	asy = ddi_get_soft_state(asy_soft_state, instance);
796 	if (asy == NULL)
797 		return (DDI_FAILURE);
798 
799 	mutex_enter(&asy->asy_soft_sr);
800 	mutex_enter(&asy->asy_excl);
801 	mutex_enter(&asy->asy_excl_hi);
802 
803 	async = asy->asy_priv;
804 	/* Disable interrupts */
805 	ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 0);
806 	if (asy_identify_chip(devi, asy) != DDI_SUCCESS) {
807 		mutex_exit(&asy->asy_excl_hi);
808 		mutex_exit(&asy->asy_excl);
809 		mutex_exit(&asy->asy_soft_sr);
810 		asyerror(asy, CE_WARN, "Cannot identify UART chip at %p",
811 		    (void *)asy->asy_ioaddr);
812 		return (DDI_FAILURE);
813 	}
814 	asy->asy_flags &= ~ASY_DDI_SUSPENDED;
815 	if (async->async_flags & ASYNC_ISOPEN) {
816 		asy_program(asy, ASY_INIT);
817 		/* Kick off output */
818 		if (async->async_ocnt > 0) {
819 			async_resume(async);
820 		} else {
821 			mutex_exit(&asy->asy_excl_hi);
822 			if (async->async_xmitblk)
823 				freeb(async->async_xmitblk);
824 			async->async_xmitblk = NULL;
825 			async_start(async);
826 			mutex_enter(&asy->asy_excl_hi);
827 		}
828 		asysetsoft(asy);
829 	}
830 	mutex_exit(&asy->asy_excl_hi);
831 	mutex_exit(&asy->asy_excl);
832 	mutex_exit(&asy->asy_soft_sr);
833 
834 	mutex_enter(&asy->asy_excl);
835 	if (async->async_flags & ASYNC_RESUME_BUFCALL) {
836 		async->async_wbufcid = bufcall(async->async_wbufcds,
837 		    BPRI_HI, (void (*)(void *)) async_reioctl,
838 		    (void *)(intptr_t)async->async_common->asy_unit);
839 		async->async_flags &= ~ASYNC_RESUME_BUFCALL;
840 	}
841 	async_process_suspq(asy);
842 	mutex_exit(&asy->asy_excl);
843 	return (DDI_SUCCESS);
844 }
845 
846 static int
847 asy_suspend(struct asycom *asy)
848 {
849 	struct asyncline *async = asy->asy_priv;
850 	unsigned i;
851 	uchar_t lsr;
852 
853 #ifdef	DEBUG
854 	if (asy_nosuspend)
855 		return (DDI_SUCCESS);
856 #endif
857 	mutex_enter(&asy->asy_excl);
858 
859 	ASSERT(async->async_ops >= 0);
860 	while (async->async_ops > 0)
861 		cv_wait(&async->async_ops_cv, &asy->asy_excl);
862 
863 	async->async_flags |= ASYNC_DDI_SUSPENDED;
864 
865 	/* Wait for timed break and delay to complete */
866 	while ((async->async_flags & (ASYNC_BREAK|ASYNC_DELAY))) {
867 		if (cv_wait_sig(&async->async_flags_cv, &asy->asy_excl) == 0) {
868 			async_process_suspq(asy);
869 			mutex_exit(&asy->asy_excl);
870 			return (DDI_FAILURE);
871 		}
872 	}
873 
874 	/* Clear untimed break */
875 	if (async->async_flags & ASYNC_OUT_SUSPEND)
876 		async_resume_utbrk(async);
877 
878 	mutex_exit(&asy->asy_excl);
879 
880 	mutex_enter(&asy->asy_soft_sr);
881 	mutex_enter(&asy->asy_excl);
882 	if (async->async_wbufcid != 0) {
883 		bufcall_id_t bcid = async->async_wbufcid;
884 		async->async_wbufcid = 0;
885 		async->async_flags |= ASYNC_RESUME_BUFCALL;
886 		mutex_exit(&asy->asy_excl);
887 		unbufcall(bcid);
888 		mutex_enter(&asy->asy_excl);
889 	}
890 	mutex_enter(&asy->asy_excl_hi);
891 
892 	/* Disable interrupts from chip */
893 	ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 0);
894 	asy->asy_flags |= ASY_DDI_SUSPENDED;
895 
896 	/*
897 	 * Hardware interrupts are disabled we can drop our high level
898 	 * lock and proceed.
899 	 */
900 	mutex_exit(&asy->asy_excl_hi);
901 
902 	/* Process remaining RX characters and RX errors, if any */
903 	lsr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR);
904 	async_rxint(asy, lsr);
905 
906 	/* Wait for TX to drain */
907 	for (i = 1000; i > 0; i--) {
908 		lsr = ddi_get8(asy->asy_iohandle,
909 		    asy->asy_ioaddr + LSR);
910 		if ((lsr & (XSRE | XHRE)) == (XSRE | XHRE))
911 			break;
912 		delay(drv_usectohz(10000));
913 	}
914 	if (i == 0)
915 		asyerror(asy, CE_WARN, "transmitter wasn't drained before "
916 		    "driver was suspended");
917 
918 	mutex_exit(&asy->asy_excl);
919 	mutex_exit(&asy->asy_soft_sr);
920 
921 	return (DDI_SUCCESS);
922 }
923 
924 static int
925 asydetach(dev_info_t *devi, ddi_detach_cmd_t cmd)
926 {
927 	int instance;
928 	struct asycom *asy;
929 
930 	instance = ddi_get_instance(devi);	/* find out which unit */
931 
932 	asy = ddi_get_soft_state(asy_soft_state, instance);
933 	if (asy == NULL)
934 		return (DDI_FAILURE);
935 
936 	switch (cmd) {
937 	case DDI_DETACH:
938 		break;
939 
940 	case DDI_SUSPEND:
941 		return (asy_suspend(asy));
942 
943 	default:
944 		return (DDI_FAILURE);
945 	}
946 
947 	ASY_DPRINTF(asy, ASY_DEBUG_INIT, "%s shutdown", asy_hw_name(asy));
948 
949 	if ((asy->asy_progress & ASY_PROGRESS_ASYNC) != 0) {
950 		struct asyncline *async = asy->asy_priv;
951 
952 		/* cancel DTR hold timeout */
953 		if (async->async_dtrtid != 0) {
954 			(void) untimeout(async->async_dtrtid);
955 			async->async_dtrtid = 0;
956 		}
957 		cv_destroy(&async->async_flags_cv);
958 		kmem_free(async, sizeof (struct asyncline));
959 		asy->asy_priv = NULL;
960 	}
961 
962 	if ((asy->asy_progress & ASY_PROGRESS_MINOR) != 0)
963 		ddi_remove_minor_node(devi, NULL);
964 
965 	if ((asy->asy_progress & ASY_PROGRESS_MUTEX) != 0) {
966 		mutex_destroy(&asy->asy_excl);
967 		mutex_destroy(&asy->asy_excl_hi);
968 		mutex_destroy(&asy->asy_soft_lock);
969 	}
970 
971 	if ((asy->asy_progress & ASY_PROGRESS_INT) != 0)
972 		asy_intr_free(asy);
973 
974 	if ((asy->asy_progress & ASY_PROGRESS_SOFTINT) != 0)
975 		asy_softintr_free(asy);
976 
977 	if ((asy->asy_progress & ASY_PROGRESS_REGS) != 0)
978 		ddi_regs_map_free(&asy->asy_iohandle);
979 
980 	ASY_DPRINTF(asy, ASY_DEBUG_INIT, "shutdown complete");
981 	asy_soft_state_free(asy);
982 
983 	return (DDI_SUCCESS);
984 }
985 
986 /*
987  * asyprobe
988  * We don't bother probing for the hardware, as since Solaris 2.6, device
989  * nodes are only created for auto-detected hardware or nodes explicitly
990  * created by the user, e.g. via the DCA. However, we should check the
991  * device node is at least vaguely usable, i.e. we have a block of 8 i/o
992  * ports. This prevents attempting to attach to bogus serial ports which
993  * some BIOSs still partially report when they are disabled in the BIOS.
994  */
995 static int
996 asyprobe(dev_info_t *devi)
997 {
998 	return ((asy_get_io_regnum(devi, NULL) < 0) ?
999 	    DDI_PROBE_FAILURE : DDI_PROBE_DONTCARE);
1000 }
1001 
1002 static int
1003 asyattach(dev_info_t *devi, ddi_attach_cmd_t cmd)
1004 {
1005 	int instance;
1006 	int mcr;
1007 	int ret;
1008 	int regnum = 0;
1009 	int i;
1010 	struct asycom *asy;
1011 	char name[ASY_MINOR_LEN];
1012 	int status;
1013 	static ddi_device_acc_attr_t ioattr = {
1014 		DDI_DEVICE_ATTR_V0,
1015 		DDI_NEVERSWAP_ACC,
1016 		DDI_STRICTORDER_ACC,
1017 	};
1018 
1019 	switch (cmd) {
1020 	case DDI_ATTACH:
1021 		break;
1022 
1023 	case DDI_RESUME:
1024 		return (asy_resume(devi));
1025 
1026 	default:
1027 		return (DDI_FAILURE);
1028 	}
1029 
1030 	mutex_enter(&asy_glob_lock);
1031 	if (com_ports == NULL) {	/* need to initialize com_ports */
1032 		if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, devi, 0,
1033 		    "motherboard-serial-ports", &com_ports, &num_com_ports) !=
1034 		    DDI_PROP_SUCCESS) {
1035 			/* Use our built-in COM[1234] values */
1036 			com_ports = (int *)standard_com_ports;
1037 			num_com_ports = sizeof (standard_com_ports) /
1038 			    sizeof (standard_com_ports[0]);
1039 		}
1040 		if (num_com_ports > 10) {
1041 			/* We run out of single digits for device properties */
1042 			num_com_ports = 10;
1043 			asyerror(asy, CE_WARN,
1044 			    "More than %d motherboard-serial-ports",
1045 			    num_com_ports);
1046 		}
1047 	}
1048 	mutex_exit(&asy_glob_lock);
1049 
1050 
1051 	instance = ddi_get_instance(devi);	/* find out which unit */
1052 	ret = ddi_soft_state_zalloc(asy_soft_state, instance);
1053 	if (ret != DDI_SUCCESS)
1054 		return (DDI_FAILURE);
1055 	asy = ddi_get_soft_state(asy_soft_state, instance);
1056 
1057 	asy->asy_dip = devi;
1058 #ifdef DEBUG
1059 	asy->asy_debug = debug;
1060 #endif
1061 	asy->asy_unit = instance;
1062 
1063 	regnum = asy_get_io_regnum(devi, asy);
1064 
1065 	if (regnum < 0 ||
1066 	    ddi_regs_map_setup(devi, regnum, (caddr_t *)&asy->asy_ioaddr,
1067 	    (offset_t)0, (offset_t)0, &ioattr, &asy->asy_iohandle)
1068 	    != DDI_SUCCESS) {
1069 		asyerror(asy, CE_WARN, "could not map UART registers @ %p",
1070 		    (void *)asy->asy_ioaddr);
1071 		goto fail;
1072 	}
1073 
1074 	asy->asy_progress |= ASY_PROGRESS_REGS;
1075 
1076 	ASY_DPRINTF(asy, ASY_DEBUG_INIT, "UART @ %p", (void *)asy->asy_ioaddr);
1077 
1078 	/*
1079 	 * Lookup the i/o address to see if this is a standard COM port
1080 	 * in which case we assign it the correct tty[a-d] to match the
1081 	 * COM port number, or some other i/o address in which case it
1082 	 * will be assigned /dev/term/[0123...] in some rather arbitrary
1083 	 * fashion.
1084 	 */
1085 	for (i = 0; i < num_com_ports; i++) {
1086 		if (asy->asy_ioaddr == (uint8_t *)(uintptr_t)com_ports[i]) {
1087 			asy->asy_com_port = i + 1;
1088 			break;
1089 		}
1090 	}
1091 
1092 	/*
1093 	 * It appears that there was async hardware that on reset did not clear
1094 	 * IER.  Hence when we enable interrupts, this hardware would cause the
1095 	 * system to hang if there was input available.
1096 	 */
1097 
1098 	ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 0x00);
1099 
1100 	/*
1101 	 * Establish default settings:
1102 	 * - use RTS/DTR after open
1103 	 * - 8N1 data format
1104 	 * - 9600 baud
1105 	 */
1106 	asy->asy_mcr |= RTS|DTR;
1107 	asy->asy_lcr = STOP1|BITS8;
1108 	asy->asy_bidx = B9600;
1109 	asy->asy_fifo_buf = 1;
1110 	asy->asy_use_fifo = FIFO_OFF;
1111 
1112 #ifdef DEBUG
1113 	asy->asy_msint_cnt = 0;			/* # of times in async_msint */
1114 #endif
1115 	mcr = 0;				/* don't enable until open */
1116 
1117 	if (asy->asy_com_port != 0) {
1118 		/*
1119 		 * For motherboard ports, emulate tty eeprom properties.
1120 		 * Actually, we can't tell if a port is motherboard or not,
1121 		 * so for "motherboard ports", read standard DOS COM ports.
1122 		 */
1123 		switch (asy_getproperty(devi, asy, "ignore-cd")) {
1124 		case 0:				/* *-ignore-cd=False */
1125 			ASY_DPRINTF(asy, ASY_DEBUG_MODEM,
1126 			    "clear ASY_IGNORE_CD");
1127 			asy->asy_flags &= ~ASY_IGNORE_CD; /* wait for cd */
1128 			break;
1129 		case 1:				/* *-ignore-cd=True */
1130 			/*FALLTHRU*/
1131 		default:			/* *-ignore-cd not defined */
1132 			/*
1133 			 * We set rather silly defaults of soft carrier on
1134 			 * and DTR/RTS raised here because it might be that
1135 			 * one of the motherboard ports is the system console.
1136 			 */
1137 			ASY_DPRINTF(asy, ASY_DEBUG_MODEM,
1138 			    "set ASY_IGNORE_CD, set RTS & DTR");
1139 			mcr = asy->asy_mcr;		/* rts/dtr on */
1140 			asy->asy_flags |= ASY_IGNORE_CD;	/* ignore cd */
1141 			break;
1142 		}
1143 
1144 		/* Property for not raising DTR/RTS */
1145 		switch (asy_getproperty(devi, asy, "rts-dtr-off")) {
1146 		case 0:				/* *-rts-dtr-off=False */
1147 			asy->asy_flags |= ASY_RTS_DTR_OFF;	/* OFF */
1148 			mcr = asy->asy_mcr;		/* rts/dtr on */
1149 			ASY_DPRINTF(asy, ASY_DEBUG_MODEM,
1150 			    "ASY_RTS_DTR_OFF set and DTR & RTS set");
1151 			break;
1152 		case 1:				/* *-rts-dtr-off=True */
1153 			/*FALLTHRU*/
1154 		default:			/* *-rts-dtr-off undefined */
1155 			break;
1156 		}
1157 
1158 		/* Parse property for tty modes */
1159 		asy_parse_mode(devi, asy);
1160 	} else {
1161 		ASY_DPRINTF(asy, ASY_DEBUG_MODEM,
1162 		    "clear ASY_IGNORE_CD, clear RTS & DTR");
1163 		asy->asy_flags &= ~ASY_IGNORE_CD;	/* wait for cd */
1164 	}
1165 
1166 	/*
1167 	 * Install per instance software interrupt handler.
1168 	 */
1169 	if (asy_softintr_setup(asy) != DDI_SUCCESS) {
1170 		asyerror(asy, CE_WARN, "Cannot set soft interrupt");
1171 		goto fail;
1172 	}
1173 
1174 	asy->asy_progress |= ASY_PROGRESS_SOFTINT;
1175 
1176 	/*
1177 	 * Install interrupt handler for this device.
1178 	 */
1179 	if ((asy_intr_setup(asy, DDI_INTR_TYPE_MSIX) != DDI_SUCCESS) &&
1180 	    (asy_intr_setup(asy, DDI_INTR_TYPE_MSI) != DDI_SUCCESS) &&
1181 	    (asy_intr_setup(asy, DDI_INTR_TYPE_FIXED) != DDI_SUCCESS)) {
1182 		asyerror(asy, CE_WARN, "Cannot set device interrupt");
1183 		goto fail;
1184 	}
1185 
1186 	asy->asy_progress |= ASY_PROGRESS_INT;
1187 
1188 	/*
1189 	 * Initialize mutexes before accessing the hardware
1190 	 */
1191 	mutex_init(&asy->asy_soft_lock, NULL, MUTEX_DRIVER,
1192 	    DDI_INTR_PRI(asy->asy_soft_intr_pri));
1193 	mutex_init(&asy->asy_soft_sr, NULL, MUTEX_DRIVER,
1194 	    DDI_INTR_PRI(asy->asy_soft_intr_pri));
1195 
1196 	mutex_init(&asy->asy_excl, NULL, MUTEX_DRIVER, NULL);
1197 	mutex_init(&asy->asy_excl_hi, NULL, MUTEX_DRIVER,
1198 	    DDI_INTR_PRI(asy->asy_intr_pri));
1199 
1200 	asy->asy_progress |= ASY_PROGRESS_MUTEX;
1201 
1202 	mutex_enter(&asy->asy_excl);
1203 	mutex_enter(&asy->asy_excl_hi);
1204 
1205 	if (asy_identify_chip(devi, asy) != DDI_SUCCESS) {
1206 		asyerror(asy, CE_WARN, "Cannot identify UART chip at %p",
1207 		    (void *)asy->asy_ioaddr);
1208 		goto fail;
1209 	}
1210 
1211 	/* disable all interrupts */
1212 	ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 0);
1213 	/* select baud rate generator */
1214 	ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, DLAB);
1215 	/* Set the baud rate to 9600 */
1216 	ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + (DAT+DLL),
1217 	    asyspdtab[asy->asy_bidx] & 0xff);
1218 	ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + (DAT+DLH),
1219 	    (asyspdtab[asy->asy_bidx] >> 8) & 0xff);
1220 	ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, asy->asy_lcr);
1221 	ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR, mcr);
1222 
1223 	mutex_exit(&asy->asy_excl_hi);
1224 	mutex_exit(&asy->asy_excl);
1225 
1226 	asyinit(asy);	/* initialize the asyncline structure */
1227 	asy->asy_progress |= ASY_PROGRESS_ASYNC;
1228 
1229 	/* create minor device nodes for this device */
1230 	if (asy->asy_com_port != 0) {
1231 		/*
1232 		 * For DOS COM ports, add letter suffix so
1233 		 * devfsadm can create correct link names.
1234 		 */
1235 		name[0] = asy->asy_com_port + 'a' - 1;
1236 		name[1] = '\0';
1237 	} else {
1238 		/*
1239 		 * asy port which isn't a standard DOS COM
1240 		 * port gets a numeric name based on instance
1241 		 */
1242 		(void) snprintf(name, ASY_MINOR_LEN, "%d", instance);
1243 	}
1244 	status = ddi_create_minor_node(devi, name, S_IFCHR, instance,
1245 	    asy->asy_com_port != 0 ? DDI_NT_SERIAL_MB : DDI_NT_SERIAL, 0);
1246 	if (status == DDI_SUCCESS) {
1247 		(void) strcat(name, ",cu");
1248 		status = ddi_create_minor_node(devi, name, S_IFCHR,
1249 		    OUTLINE | instance,
1250 		    asy->asy_com_port != 0 ? DDI_NT_SERIAL_MB_DO :
1251 		    DDI_NT_SERIAL_DO, 0);
1252 	}
1253 
1254 	if (status != DDI_SUCCESS)
1255 		goto fail;
1256 
1257 	asy->asy_progress |= ASY_PROGRESS_MINOR;
1258 
1259 	/*
1260 	 * Fill in the polled I/O structure.
1261 	 */
1262 	asy->polledio.cons_polledio_version = CONSPOLLEDIO_V0;
1263 	asy->polledio.cons_polledio_argument = (cons_polledio_arg_t)asy;
1264 	asy->polledio.cons_polledio_putchar = asyputchar;
1265 	asy->polledio.cons_polledio_getchar = asygetchar;
1266 	asy->polledio.cons_polledio_ischar = asyischar;
1267 	asy->polledio.cons_polledio_enter = NULL;
1268 	asy->polledio.cons_polledio_exit = NULL;
1269 
1270 	ddi_report_dev(devi);
1271 	ASY_DPRINTF(asy, ASY_DEBUG_INIT, "done");
1272 	return (DDI_SUCCESS);
1273 
1274 fail:
1275 	(void) asydetach(devi, DDI_DETACH);
1276 	return (DDI_FAILURE);
1277 }
1278 
1279 /*ARGSUSED*/
1280 static int
1281 asyinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
1282     void **result)
1283 {
1284 	dev_t dev = (dev_t)arg;
1285 	int instance, error;
1286 	struct asycom *asy;
1287 
1288 	instance = UNIT(dev);
1289 
1290 	switch (infocmd) {
1291 	case DDI_INFO_DEVT2DEVINFO:
1292 		asy = ddi_get_soft_state(asy_soft_state, instance);
1293 		if ((asy == NULL) || (asy->asy_dip == NULL))
1294 			error = DDI_FAILURE;
1295 		else {
1296 			*result = (void *) asy->asy_dip;
1297 			error = DDI_SUCCESS;
1298 		}
1299 		break;
1300 	case DDI_INFO_DEVT2INSTANCE:
1301 		*result = (void *)(intptr_t)instance;
1302 		error = DDI_SUCCESS;
1303 		break;
1304 	default:
1305 		error = DDI_FAILURE;
1306 	}
1307 	return (error);
1308 }
1309 
1310 /* asy_getproperty -- walk through all name variants until we find a match */
1311 
1312 static int
1313 asy_getproperty(dev_info_t *devi, struct asycom *asy, const char *property)
1314 {
1315 	int len;
1316 	int ret;
1317 	char letter = asy->asy_com_port + 'a' - 1;	/* for ttya */
1318 	char number = asy->asy_com_port + '0';		/* for COM1 */
1319 	char val[40];
1320 	char name[40];
1321 
1322 	/* Property for ignoring DCD */
1323 	(void) sprintf(name, "tty%c-%s", letter, property);
1324 	len = sizeof (val);
1325 	ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, &len);
1326 	if (ret != DDI_PROP_SUCCESS) {
1327 		(void) sprintf(name, "com%c-%s", number, property);
1328 		len = sizeof (val);
1329 		ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, &len);
1330 	}
1331 	if (ret != DDI_PROP_SUCCESS) {
1332 		(void) sprintf(name, "tty0%c-%s", number, property);
1333 		len = sizeof (val);
1334 		ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, &len);
1335 	}
1336 	if (ret != DDI_PROP_SUCCESS) {
1337 		(void) sprintf(name, "port-%c-%s", letter, property);
1338 		len = sizeof (val);
1339 		ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, &len);
1340 	}
1341 	if (ret != DDI_PROP_SUCCESS)
1342 		return (-1);		/* property non-existant */
1343 	if (val[0] == 'f' || val[0] == 'F' || val[0] == '0')
1344 		return (0);		/* property false/0 */
1345 	return (1);			/* property true/!0 */
1346 }
1347 
1348 /* asy_soft_state_free - local wrapper for ddi_soft_state_free(9F) */
1349 
1350 static void
1351 asy_soft_state_free(struct asycom *asy)
1352 {
1353 	if (asy->asy_priv != NULL) {
1354 		kmem_free(asy->asy_priv, sizeof (struct asyncline));
1355 		asy->asy_priv = NULL;
1356 	}
1357 	ddi_soft_state_free(asy_soft_state, asy->asy_unit);
1358 }
1359 
1360 static char *
1361 asy_hw_name(struct asycom *asy)
1362 {
1363 	switch (asy->asy_hwtype) {
1364 	case ASY8250A:
1365 		return ("8250A/16450");
1366 	case ASY16550:
1367 		return ("16550");
1368 	case ASY16550A:
1369 		return ("16550A");
1370 	case ASY16650:
1371 		return ("16650");
1372 	case ASY16750:
1373 		return ("16750");
1374 	}
1375 
1376 	ASY_DPRINTF(asy, ASY_DEBUG_INIT, "unknown asy_hwtype: %d",
1377 	    asy->asy_hwtype);
1378 	return ("?");
1379 }
1380 
1381 static int
1382 asy_identify_chip(dev_info_t *devi, struct asycom *asy)
1383 {
1384 	int ret;
1385 	int mcr;
1386 	dev_t dev;
1387 	uint_t hwtype;
1388 
1389 	if (asy_scr_test) {
1390 		/* Check scratch register works. */
1391 
1392 		/* write to scratch register */
1393 		ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + SCR, SCRTEST);
1394 		/* make sure that pattern doesn't just linger on the bus */
1395 		ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + FIFOR, 0x00);
1396 		/* read data back from scratch register */
1397 		ret = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + SCR);
1398 		if (ret != SCRTEST) {
1399 			/*
1400 			 * Scratch register not working.
1401 			 * Probably not an async chip.
1402 			 * 8250 and 8250B don't have scratch registers,
1403 			 * but only worked in ancient PC XT's anyway.
1404 			 */
1405 			asyerror(asy, CE_WARN, "UART @ %p "
1406 			    "scratch register: expected 0x5a, got 0x%02x",
1407 			    (void *)asy->asy_ioaddr, ret);
1408 			return (DDI_FAILURE);
1409 		}
1410 	}
1411 	/*
1412 	 * Use 16550 fifo reset sequence specified in NS application
1413 	 * note. Disable fifos until chip is initialized.
1414 	 */
1415 	ddi_put8(asy->asy_iohandle,
1416 	    asy->asy_ioaddr + FIFOR, 0x00);	/* clear */
1417 	ddi_put8(asy->asy_iohandle,
1418 	    asy->asy_ioaddr + FIFOR, FIFO_ON);	/* enable */
1419 	ddi_put8(asy->asy_iohandle,
1420 	    asy->asy_ioaddr + FIFOR, FIFO_ON | FIFORXFLSH);
1421 						/* reset */
1422 	if (asymaxchip >= ASY16650 && asy_scr_test) {
1423 		/*
1424 		 * Reset 16650 enhanced regs also, in case we have one of these
1425 		 */
1426 		ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR,
1427 		    EFRACCESS);
1428 		ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + EFR,
1429 		    0);
1430 		ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR,
1431 		    STOP1|BITS8);
1432 	}
1433 
1434 	/*
1435 	 * See what sort of FIFO we have.
1436 	 * Try enabling it and see what chip makes of this.
1437 	 */
1438 
1439 	asy->asy_fifor = 0;
1440 	asy->asy_hwtype = asymaxchip; /* just for asy_reset_fifo() */
1441 	if (asymaxchip >= ASY16550A)
1442 		asy->asy_fifor |=
1443 		    FIFO_ON | FIFODMA | (asy_trig_level & 0xff);
1444 	if (asymaxchip >= ASY16650)
1445 		asy->asy_fifor |= FIFOEXTRA1 | FIFOEXTRA2;
1446 
1447 	asy_reset_fifo(asy, FIFOTXFLSH | FIFORXFLSH);
1448 
1449 	mcr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + MCR);
1450 	ret = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + ISR);
1451 
1452 	ASY_DPRINTF(asy, ASY_DEBUG_CHIP,
1453 	    "probe fifo FIFOR=0x%02x ISR=0x%02x MCR=0x%02x",
1454 	    asy->asy_fifor | FIFOTXFLSH | FIFORXFLSH, ret, mcr);
1455 	switch (ret & 0xf0) {
1456 	case 0x40:
1457 		hwtype = ASY16550; /* 16550 with broken FIFO */
1458 		asy->asy_fifor = 0;
1459 		break;
1460 	case 0xc0:
1461 		hwtype = ASY16550A;
1462 		asy->asy_fifo_buf = 16;
1463 		asy->asy_use_fifo = FIFO_ON;
1464 		asy->asy_fifor &= ~(FIFOEXTRA1 | FIFOEXTRA2);
1465 		break;
1466 	case 0xe0:
1467 		hwtype = ASY16650;
1468 		asy->asy_fifo_buf = 32;
1469 		asy->asy_use_fifo = FIFO_ON;
1470 		asy->asy_fifor &= ~(FIFOEXTRA1);
1471 		break;
1472 	case 0xf0:
1473 		/*
1474 		 * Note we get 0xff if chip didn't return us anything,
1475 		 * e.g. if there's no chip there.
1476 		 */
1477 		if (ret == 0xff) {
1478 			asyerror(asy, CE_WARN, "UART @ %p "
1479 			    "interrupt register: got 0xff",
1480 			    (void *)asy->asy_ioaddr);
1481 			return (DDI_FAILURE);
1482 		}
1483 		/*FALLTHRU*/
1484 	case 0xd0:
1485 		hwtype = ASY16750;
1486 		asy->asy_fifo_buf = 64;
1487 		asy->asy_use_fifo = FIFO_ON;
1488 		break;
1489 	default:
1490 		hwtype = ASY8250A; /* No FIFO */
1491 		asy->asy_fifor = 0;
1492 	}
1493 
1494 	if (hwtype > asymaxchip) {
1495 		asyerror(asy, CE_WARN, "UART @ %p "
1496 		    "unexpected probe result: "
1497 		    "FIFOR=0x%02x ISR=0x%02x MCR=0x%02x",
1498 		    (void *)asy->asy_ioaddr,
1499 		    asy->asy_fifor | FIFOTXFLSH | FIFORXFLSH, ret, mcr);
1500 		return (DDI_FAILURE);
1501 	}
1502 
1503 	/*
1504 	 * Now reset the FIFO operation appropriate for the chip type.
1505 	 * Note we must call asy_reset_fifo() before any possible
1506 	 * downgrade of the asy->asy_hwtype, or it may not disable
1507 	 * the more advanced features we specifically want downgraded.
1508 	 */
1509 	asy_reset_fifo(asy, 0);
1510 	asy->asy_hwtype = hwtype;
1511 
1512 	/*
1513 	 * Check for Exar/Startech ST16C650, which will still look like a
1514 	 * 16550A until we enable its enhanced mode.
1515 	 */
1516 	if (asy->asy_hwtype == ASY16550A && asymaxchip >= ASY16650 &&
1517 	    asy_scr_test) {
1518 		/* Enable enhanced mode register access */
1519 		ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR,
1520 		    EFRACCESS);
1521 		/* zero scratch register (not scratch register if enhanced) */
1522 		ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + SCR, 0);
1523 		/* Disable enhanced mode register access */
1524 		ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR,
1525 		    STOP1|BITS8);
1526 		/* read back scratch register */
1527 		ret = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + SCR);
1528 		if (ret == SCRTEST) {
1529 			/* looks like we have an ST16650 -- enable it */
1530 			ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR,
1531 			    EFRACCESS);
1532 			ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + EFR,
1533 			    ENHENABLE);
1534 			ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR,
1535 			    STOP1|BITS8);
1536 			asy->asy_hwtype = ASY16650;
1537 			asy->asy_fifo_buf = 32;
1538 			asy->asy_fifor |= 0x10; /* 24 byte txfifo trigger */
1539 			asy_reset_fifo(asy, 0);
1540 		}
1541 	}
1542 
1543 	/*
1544 	 * If we think we might have a FIFO larger than 16 characters,
1545 	 * measure FIFO size and check it against expected.
1546 	 */
1547 	if (asy_fifo_test > 0 &&
1548 	    !(asy->asy_flags2 & ASY2_NO_LOOPBACK) &&
1549 	    (asy->asy_fifo_buf > 16 ||
1550 	    (asy_fifo_test > 1 && asy->asy_use_fifo == FIFO_ON) ||
1551 	    ASY_DEBUG(asy, ASY_DEBUG_CHIP))) {
1552 		int i;
1553 
1554 		/* Set baud rate to 57600 (fairly arbitrary choice) */
1555 		ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR,
1556 		    DLAB);
1557 		ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + DAT,
1558 		    asyspdtab[B57600] & 0xff);
1559 		ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR,
1560 		    (asyspdtab[B57600] >> 8) & 0xff);
1561 		/* Set 8 bits, 1 stop bit */
1562 		ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR,
1563 		    STOP1|BITS8);
1564 		/* Set loopback mode */
1565 		ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR,
1566 		    DTR | RTS | ASY_LOOP | OUT1 | OUT2);
1567 
1568 		/* Overfill fifo */
1569 		for (i = 0; i < asy->asy_fifo_buf * 2; i++) {
1570 			ddi_put8(asy->asy_iohandle,
1571 			    asy->asy_ioaddr + DAT, i);
1572 		}
1573 		/*
1574 		 * Now there's an interesting question here about which
1575 		 * FIFO we're testing the size of, RX or TX. We just
1576 		 * filled the TX FIFO much faster than it can empty,
1577 		 * although it is possible one or two characters may
1578 		 * have gone from it to the TX shift register.
1579 		 * We wait for enough time for all the characters to
1580 		 * move into the RX FIFO and any excess characters to
1581 		 * have been lost, and then read all the RX FIFO. So
1582 		 * the answer we finally get will be the size which is
1583 		 * the MIN(RX FIFO,(TX FIFO + 1 or 2)). The critical
1584 		 * one is actually the TX FIFO, because if we overfill
1585 		 * it in normal operation, the excess characters are
1586 		 * lost with no warning.
1587 		 */
1588 		/*
1589 		 * Wait for characters to move into RX FIFO.
1590 		 * In theory, 200 * asy->asy_fifo_buf * 2 should be
1591 		 * enough. However, in practice it isn't always, so we
1592 		 * increase to 400 so some slow 16550A's finish, and we
1593 		 * increase to 3 so we spot more characters coming back
1594 		 * than we sent, in case that should ever happen.
1595 		 */
1596 		delay(drv_usectohz(400 * asy->asy_fifo_buf * 3));
1597 
1598 		/* Now see how many characters we can read back */
1599 		for (i = 0; i < asy->asy_fifo_buf * 3; i++) {
1600 			ret = ddi_get8(asy->asy_iohandle,
1601 			    asy->asy_ioaddr + LSR);
1602 			if (!(ret & RCA))
1603 				break;	/* FIFO emptied */
1604 			(void) ddi_get8(asy->asy_iohandle,
1605 			    asy->asy_ioaddr + DAT); /* lose another */
1606 		}
1607 
1608 		ASY_DPRINTF(asy, ASY_DEBUG_CHIP,
1609 		    "FIFO size: expected=%d, measured=%d",
1610 		    asy->asy_fifo_buf, i);
1611 
1612 		hwtype = asy->asy_hwtype;
1613 		if (i < asy->asy_fifo_buf) {
1614 			/*
1615 			 * FIFO is somewhat smaller than we anticipated.
1616 			 * If we have 16 characters usable, then this
1617 			 * UART will probably work well enough in
1618 			 * 16550A mode. If less than 16 characters,
1619 			 * then we'd better not use it at all.
1620 			 * UARTs with busted FIFOs do crop up.
1621 			 */
1622 			if (i >= 16 && asy->asy_fifo_buf >= 16) {
1623 				/* fall back to a 16550A */
1624 				hwtype = ASY16550A;
1625 				asy->asy_fifo_buf = 16;
1626 				asy->asy_fifor &= ~(FIFOEXTRA1 | FIFOEXTRA2);
1627 			} else {
1628 				/* fall back to no FIFO at all */
1629 				hwtype = ASY16550;
1630 				asy->asy_fifo_buf = 1;
1631 				asy->asy_use_fifo = FIFO_OFF;
1632 				asy->asy_fifor &=
1633 				    ~(FIFO_ON | FIFOEXTRA1 | FIFOEXTRA2);
1634 			}
1635 		}
1636 		/*
1637 		 * We will need to reprogram the FIFO if we changed
1638 		 * our mind about how to drive it above, and in any
1639 		 * case, it would be a good idea to flush any garbage
1640 		 * out incase the loopback test left anything behind.
1641 		 * Again as earlier above, we must call asy_reset_fifo()
1642 		 * before any possible downgrade of asy->asy_hwtype.
1643 		 */
1644 		if (asy->asy_hwtype >= ASY16650 && hwtype < ASY16650) {
1645 			/* Disable 16650 enhanced mode */
1646 			ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR,
1647 			    EFRACCESS);
1648 			ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + EFR,
1649 			    0);
1650 			ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR,
1651 			    STOP1|BITS8);
1652 		}
1653 		asy_reset_fifo(asy, FIFOTXFLSH | FIFORXFLSH);
1654 		asy->asy_hwtype = hwtype;
1655 
1656 		/* Clear loopback mode and restore DTR/RTS */
1657 		ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR, mcr);
1658 	}
1659 
1660 	ASY_DPRINTF(asy, ASY_DEBUG_CHIP, "%s @ %p",
1661 	    asy_hw_name(asy), (void *)asy->asy_ioaddr);
1662 
1663 	/* Make UART type visible in device tree for prtconf, etc */
1664 	dev = makedevice(DDI_MAJOR_T_UNKNOWN, asy->asy_unit);
1665 	(void) ddi_prop_update_string(dev, devi, "uart", asy_hw_name(asy));
1666 
1667 	if (asy->asy_hwtype == ASY16550)	/* for broken 16550's, */
1668 		asy->asy_hwtype = ASY8250A;	/* drive them as 8250A */
1669 
1670 	return (DDI_SUCCESS);
1671 }
1672 
1673 /*
1674  * asyinit() initializes the TTY protocol-private data for this channel
1675  * before enabling the interrupts.
1676  */
1677 static void
1678 asyinit(struct asycom *asy)
1679 {
1680 	struct asyncline *async;
1681 
1682 	asy->asy_priv = kmem_zalloc(sizeof (struct asyncline), KM_SLEEP);
1683 	async = asy->asy_priv;
1684 	mutex_enter(&asy->asy_excl);
1685 	async->async_common = asy;
1686 	cv_init(&async->async_flags_cv, NULL, CV_DRIVER, NULL);
1687 	mutex_exit(&asy->asy_excl);
1688 }
1689 
1690 /*ARGSUSED3*/
1691 static int
1692 asyopen(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr)
1693 {
1694 	struct asycom	*asy;
1695 	struct asyncline *async;
1696 	int		mcr;
1697 	int		unit;
1698 	int		len;
1699 	struct termios	*termiosp;
1700 
1701 	unit = UNIT(*dev);
1702 	asy = ddi_get_soft_state(asy_soft_state, unit);
1703 	if (asy == NULL)
1704 		return (ENXIO);		/* unit not configured */
1705 	ASY_DPRINTF(asy, ASY_DEBUG_CLOSE, "enter");
1706 	async = asy->asy_priv;
1707 	mutex_enter(&asy->asy_excl);
1708 
1709 again:
1710 	mutex_enter(&asy->asy_excl_hi);
1711 
1712 	/*
1713 	 * Block waiting for carrier to come up, unless this is a no-delay open.
1714 	 */
1715 	if (!(async->async_flags & ASYNC_ISOPEN)) {
1716 		/*
1717 		 * Set the default termios settings (cflag).
1718 		 * Others are set in ldterm.
1719 		 */
1720 		mutex_exit(&asy->asy_excl_hi);
1721 
1722 		if (ddi_getlongprop(DDI_DEV_T_ANY, ddi_root_node(),
1723 		    0, "ttymodes",
1724 		    (caddr_t)&termiosp, &len) == DDI_PROP_SUCCESS &&
1725 		    len == sizeof (struct termios)) {
1726 			async->async_ttycommon.t_cflag = termiosp->c_cflag;
1727 			kmem_free(termiosp, len);
1728 		} else {
1729 			asyerror(asy, CE_WARN,
1730 			    "couldn't get ttymodes property");
1731 		}
1732 		mutex_enter(&asy->asy_excl_hi);
1733 
1734 		/* eeprom mode support - respect properties */
1735 		if (asy->asy_cflag)
1736 			async->async_ttycommon.t_cflag = asy->asy_cflag;
1737 
1738 		async->async_ttycommon.t_iflag = 0;
1739 		async->async_ttycommon.t_iocpending = NULL;
1740 		async->async_ttycommon.t_size.ws_row = 0;
1741 		async->async_ttycommon.t_size.ws_col = 0;
1742 		async->async_ttycommon.t_size.ws_xpixel = 0;
1743 		async->async_ttycommon.t_size.ws_ypixel = 0;
1744 		async->async_dev = *dev;
1745 		async->async_wbufcid = 0;
1746 
1747 		async->async_startc = CSTART;
1748 		async->async_stopc = CSTOP;
1749 		asy_program(asy, ASY_INIT);
1750 	} else if ((async->async_ttycommon.t_flags & TS_XCLUDE) &&
1751 	    secpolicy_excl_open(cr) != 0) {
1752 		mutex_exit(&asy->asy_excl_hi);
1753 		mutex_exit(&asy->asy_excl);
1754 		return (EBUSY);
1755 	} else if ((*dev & OUTLINE) && !(async->async_flags & ASYNC_OUT)) {
1756 		mutex_exit(&asy->asy_excl_hi);
1757 		mutex_exit(&asy->asy_excl);
1758 		return (EBUSY);
1759 	}
1760 
1761 	if (*dev & OUTLINE)
1762 		async->async_flags |= ASYNC_OUT;
1763 
1764 	/* Raise DTR on every open, but delay if it was just lowered. */
1765 	while (async->async_flags & ASYNC_DTR_DELAY) {
1766 		ASY_DPRINTF(asy, ASY_DEBUG_MODEM,
1767 		    "waiting for the ASYNC_DTR_DELAY to be clear");
1768 		mutex_exit(&asy->asy_excl_hi);
1769 		if (cv_wait_sig(&async->async_flags_cv,
1770 		    &asy->asy_excl) == 0) {
1771 			ASY_DPRINTF(asy, ASY_DEBUG_MODEM,
1772 			    "interrupted by signal, exiting");
1773 			mutex_exit(&asy->asy_excl);
1774 			return (EINTR);
1775 		}
1776 		mutex_enter(&asy->asy_excl_hi);
1777 	}
1778 
1779 	mcr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + MCR);
1780 	ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR,
1781 	    mcr|(asy->asy_mcr&DTR));
1782 
1783 	ASY_DPRINTF(asy, ASY_DEBUG_INIT, "\"Raise DTR on every open\": "
1784 	    "make mcr = %x, make TS_SOFTCAR = %s", mcr | (asy->asy_mcr & DTR),
1785 	    (asy->asy_flags & ASY_IGNORE_CD) ? "ON" : "OFF");
1786 
1787 	if (asy->asy_flags & ASY_IGNORE_CD) {
1788 		ASY_DPRINTF(asy, ASY_DEBUG_MODEM,
1789 		    "ASY_IGNORE_CD set, set TS_SOFTCAR");
1790 		async->async_ttycommon.t_flags |= TS_SOFTCAR;
1791 	}
1792 	else
1793 		async->async_ttycommon.t_flags &= ~TS_SOFTCAR;
1794 
1795 	/*
1796 	 * Check carrier.
1797 	 */
1798 	asy->asy_msr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + MSR);
1799 	ASY_DPRINTF(asy, ASY_DEBUG_INIT, "TS_SOFTCAR is %s, MSR & DCD is %s",
1800 	    (async->async_ttycommon.t_flags & TS_SOFTCAR) ? "set" : "clear",
1801 	    (asy->asy_msr & DCD) ? "set" : "clear");
1802 
1803 	if (asy->asy_msr & DCD)
1804 		async->async_flags |= ASYNC_CARR_ON;
1805 	else
1806 		async->async_flags &= ~ASYNC_CARR_ON;
1807 	mutex_exit(&asy->asy_excl_hi);
1808 
1809 	/*
1810 	 * If FNDELAY and FNONBLOCK are clear, block until carrier up.
1811 	 * Quit on interrupt.
1812 	 */
1813 	if (!(flag & (FNDELAY|FNONBLOCK)) &&
1814 	    !(async->async_ttycommon.t_cflag & CLOCAL)) {
1815 		if ((!(async->async_flags & (ASYNC_CARR_ON|ASYNC_OUT)) &&
1816 		    !(async->async_ttycommon.t_flags & TS_SOFTCAR)) ||
1817 		    ((async->async_flags & ASYNC_OUT) &&
1818 		    !(*dev & OUTLINE))) {
1819 			async->async_flags |= ASYNC_WOPEN;
1820 			if (cv_wait_sig(&async->async_flags_cv,
1821 			    &asy->asy_excl) == B_FALSE) {
1822 				async->async_flags &= ~ASYNC_WOPEN;
1823 				mutex_exit(&asy->asy_excl);
1824 				return (EINTR);
1825 			}
1826 			async->async_flags &= ~ASYNC_WOPEN;
1827 			goto again;
1828 		}
1829 	} else if ((async->async_flags & ASYNC_OUT) && !(*dev & OUTLINE)) {
1830 		mutex_exit(&asy->asy_excl);
1831 		return (EBUSY);
1832 	}
1833 
1834 	async->async_ttycommon.t_readq = rq;
1835 	async->async_ttycommon.t_writeq = WR(rq);
1836 	rq->q_ptr = WR(rq)->q_ptr = (caddr_t)async;
1837 	mutex_exit(&asy->asy_excl);
1838 	/*
1839 	 * Caution here -- qprocson sets the pointers that are used by canput
1840 	 * called by async_softint.  ASYNC_ISOPEN must *not* be set until those
1841 	 * pointers are valid.
1842 	 */
1843 	qprocson(rq);
1844 	async->async_flags |= ASYNC_ISOPEN;
1845 	async->async_polltid = 0;
1846 	ASY_DPRINTF(asy, ASY_DEBUG_INIT, "done");
1847 	return (0);
1848 }
1849 
1850 static void
1851 async_progress_check(void *arg)
1852 {
1853 	struct asyncline *async = arg;
1854 	struct asycom	 *asy = async->async_common;
1855 	mblk_t *bp;
1856 
1857 	/*
1858 	 * We define "progress" as either waiting on a timed break or delay, or
1859 	 * having had at least one transmitter interrupt.  If none of these are
1860 	 * true, then just terminate the output and wake up that close thread.
1861 	 */
1862 	mutex_enter(&asy->asy_excl);
1863 	mutex_enter(&asy->asy_excl_hi);
1864 	if (!(async->async_flags & (ASYNC_BREAK|ASYNC_DELAY|ASYNC_PROGRESS))) {
1865 		async->async_ocnt = 0;
1866 		async->async_flags &= ~ASYNC_BUSY;
1867 		async->async_timer = 0;
1868 		bp = async->async_xmitblk;
1869 		async->async_xmitblk = NULL;
1870 		mutex_exit(&asy->asy_excl_hi);
1871 		if (bp != NULL)
1872 			freeb(bp);
1873 		/*
1874 		 * Since this timer is running, we know that we're in exit(2).
1875 		 * That means that the user can't possibly be waiting on any
1876 		 * valid ioctl(2) completion anymore, and we should just flush
1877 		 * everything.
1878 		 */
1879 		flushq(async->async_ttycommon.t_writeq, FLUSHALL);
1880 		cv_broadcast(&async->async_flags_cv);
1881 	} else {
1882 		async->async_flags &= ~ASYNC_PROGRESS;
1883 		async->async_timer = timeout(async_progress_check, async,
1884 		    drv_usectohz(asy_drain_check));
1885 		mutex_exit(&asy->asy_excl_hi);
1886 	}
1887 	mutex_exit(&asy->asy_excl);
1888 }
1889 
1890 /*
1891  * Release DTR so that asyopen() can raise it.
1892  */
1893 static void
1894 async_dtr_free(struct asyncline *async)
1895 {
1896 	struct asycom *asy = async->async_common;
1897 
1898 	ASY_DPRINTF(asy, ASY_DEBUG_MODEM,
1899 	    "async_dtr_free, clearing ASYNC_DTR_DELAY");
1900 	mutex_enter(&asy->asy_excl);
1901 	async->async_flags &= ~ASYNC_DTR_DELAY;
1902 	async->async_dtrtid = 0;
1903 	cv_broadcast(&async->async_flags_cv);
1904 	mutex_exit(&asy->asy_excl);
1905 }
1906 
1907 /*
1908  * Close routine.
1909  */
1910 /*ARGSUSED2*/
1911 static int
1912 asyclose(queue_t *q, int flag, cred_t *credp)
1913 {
1914 	struct asyncline *async;
1915 	struct asycom	 *asy;
1916 	int icr, lcr;
1917 
1918 	async = (struct asyncline *)q->q_ptr;
1919 	ASSERT(async != NULL);
1920 
1921 	asy = async->async_common;
1922 
1923 	ASY_DPRINTF(asy, ASY_DEBUG_CLOSE, "enter");
1924 
1925 	mutex_enter(&asy->asy_excl);
1926 	async->async_flags |= ASYNC_CLOSING;
1927 
1928 	/*
1929 	 * Turn off PPS handling early to avoid events occuring during
1930 	 * close.  Also reset the DCD edge monitoring bit.
1931 	 */
1932 	mutex_enter(&asy->asy_excl_hi);
1933 	asy->asy_flags &= ~(ASY_PPS | ASY_PPS_EDGE);
1934 	mutex_exit(&asy->asy_excl_hi);
1935 
1936 	/*
1937 	 * There are two flavors of break -- timed (M_BREAK or TCSBRK) and
1938 	 * untimed (TIOCSBRK).  For the timed case, these are enqueued on our
1939 	 * write queue and there's a timer running, so we don't have to worry
1940 	 * about them.  For the untimed case, though, the user obviously made a
1941 	 * mistake, because these are handled immediately.  We'll terminate the
1942 	 * break now and honor their implicit request by discarding the rest of
1943 	 * the data.
1944 	 */
1945 	if (async->async_flags & ASYNC_OUT_SUSPEND) {
1946 		if (async->async_utbrktid != 0) {
1947 			(void) untimeout(async->async_utbrktid);
1948 			async->async_utbrktid = 0;
1949 		}
1950 		mutex_enter(&asy->asy_excl_hi);
1951 		lcr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LCR);
1952 		ddi_put8(asy->asy_iohandle,
1953 		    asy->asy_ioaddr + LCR, (lcr & ~SETBREAK));
1954 		mutex_exit(&asy->asy_excl_hi);
1955 		async->async_flags &= ~ASYNC_OUT_SUSPEND;
1956 		goto nodrain;
1957 	}
1958 
1959 	/*
1960 	 * If the user told us not to delay the close ("non-blocking"), then
1961 	 * don't bother trying to drain.
1962 	 *
1963 	 * If the user did M_STOP (ASYNC_STOPPED), there's no hope of ever
1964 	 * getting an M_START (since these messages aren't enqueued), and the
1965 	 * only other way to clear the stop condition is by loss of DCD, which
1966 	 * would discard the queue data.  Thus, we drop the output data if
1967 	 * ASYNC_STOPPED is set.
1968 	 */
1969 	if ((flag & (FNDELAY|FNONBLOCK)) ||
1970 	    (async->async_flags & ASYNC_STOPPED)) {
1971 		goto nodrain;
1972 	}
1973 
1974 	/*
1975 	 * If there's any pending output, then we have to try to drain it.
1976 	 * There are two main cases to be handled:
1977 	 *	- called by close(2): need to drain until done or until
1978 	 *	  a signal is received.  No timeout.
1979 	 *	- called by exit(2): need to drain while making progress
1980 	 *	  or until a timeout occurs.  No signals.
1981 	 *
1982 	 * If we can't rely on receiving a signal to get us out of a hung
1983 	 * session, then we have to use a timer.  In this case, we set a timer
1984 	 * to check for progress in sending the output data -- all that we ask
1985 	 * (at each interval) is that there's been some progress made.  Since
1986 	 * the interrupt routine grabs buffers from the write queue, we can't
1987 	 * trust changes in async_ocnt.  Instead, we use a progress flag.
1988 	 *
1989 	 * Note that loss of carrier will cause the output queue to be flushed,
1990 	 * and we'll wake up again and finish normally.
1991 	 */
1992 	if (!ddi_can_receive_sig() && asy_drain_check != 0) {
1993 		async->async_flags &= ~ASYNC_PROGRESS;
1994 		async->async_timer = timeout(async_progress_check, async,
1995 		    drv_usectohz(asy_drain_check));
1996 	}
1997 	while (async->async_ocnt > 0 ||
1998 	    async->async_ttycommon.t_writeq->q_first != NULL ||
1999 	    (async->async_flags & (ASYNC_BUSY|ASYNC_BREAK|ASYNC_DELAY))) {
2000 		if (cv_wait_sig(&async->async_flags_cv, &asy->asy_excl) == 0)
2001 			break;
2002 	}
2003 	if (async->async_timer != 0) {
2004 		(void) untimeout(async->async_timer);
2005 		async->async_timer = 0;
2006 	}
2007 
2008 nodrain:
2009 	async->async_ocnt = 0;
2010 	if (async->async_xmitblk != NULL)
2011 		freeb(async->async_xmitblk);
2012 	async->async_xmitblk = NULL;
2013 
2014 	/*
2015 	 * If line has HUPCL set or is incompletely opened fix up the modem
2016 	 * lines.
2017 	 */
2018 	ASY_DPRINTF(asy, ASY_DEBUG_MODEM, "next check HUPCL flag");
2019 	mutex_enter(&asy->asy_excl_hi);
2020 	if ((async->async_ttycommon.t_cflag & HUPCL) ||
2021 	    (async->async_flags & ASYNC_WOPEN)) {
2022 		ASY_DPRINTF(asy, ASY_DEBUG_MODEM,
2023 		    "HUPCL flag = %x, ASYNC_WOPEN flag = %x",
2024 		    async->async_ttycommon.t_cflag & HUPCL,
2025 		    async->async_ttycommon.t_cflag & ASYNC_WOPEN);
2026 		async->async_flags |= ASYNC_DTR_DELAY;
2027 
2028 		/* turn off DTR, RTS but NOT interrupt to 386 */
2029 		if (asy->asy_flags & (ASY_IGNORE_CD|ASY_RTS_DTR_OFF)) {
2030 			ASY_DPRINTF(asy, ASY_DEBUG_MODEM,
2031 			    "ASY_IGNORE_CD flag = %x, "
2032 			    "ASY_RTS_DTR_OFF flag = %x",
2033 			    asy->asy_flags & ASY_IGNORE_CD,
2034 			    asy->asy_flags & ASY_RTS_DTR_OFF);
2035 
2036 			ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR,
2037 			    asy->asy_mcr|OUT2);
2038 		} else {
2039 			ASY_DPRINTF(asy, ASY_DEBUG_MODEM,
2040 			    "Dropping DTR and RTS");
2041 			ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR,
2042 			    OUT2);
2043 		}
2044 		async->async_dtrtid =
2045 		    timeout((void (*)())async_dtr_free,
2046 		    (caddr_t)async, drv_usectohz(asy_min_dtr_low));
2047 	}
2048 	/*
2049 	 * If nobody's using it now, turn off receiver interrupts.
2050 	 */
2051 	if ((async->async_flags & (ASYNC_WOPEN|ASYNC_ISOPEN)) == 0) {
2052 		icr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + ICR);
2053 		ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR,
2054 		    (icr & ~RIEN));
2055 	}
2056 	mutex_exit(&asy->asy_excl_hi);
2057 out:
2058 	ttycommon_close(&async->async_ttycommon);
2059 
2060 	/*
2061 	 * Cancel outstanding "bufcall" request.
2062 	 */
2063 	if (async->async_wbufcid != 0) {
2064 		unbufcall(async->async_wbufcid);
2065 		async->async_wbufcid = 0;
2066 	}
2067 
2068 	/* Note that qprocsoff can't be done until after interrupts are off */
2069 	qprocsoff(q);
2070 	q->q_ptr = WR(q)->q_ptr = NULL;
2071 	async->async_ttycommon.t_readq = NULL;
2072 	async->async_ttycommon.t_writeq = NULL;
2073 
2074 	/*
2075 	 * Clear out device state, except persistant device property flags.
2076 	 */
2077 	async->async_flags &= (ASYNC_DTR_DELAY|ASY_RTS_DTR_OFF);
2078 	cv_broadcast(&async->async_flags_cv);
2079 	mutex_exit(&asy->asy_excl);
2080 
2081 	ASY_DPRINTF(asy, ASY_DEBUG_CLOSE, "done");
2082 	return (0);
2083 }
2084 
2085 static boolean_t
2086 asy_isbusy(struct asycom *asy)
2087 {
2088 	struct asyncline *async;
2089 
2090 	ASY_DPRINTF(asy, ASY_DEBUG_EOT, "enter");
2091 	async = asy->asy_priv;
2092 	ASSERT(mutex_owned(&asy->asy_excl));
2093 	ASSERT(mutex_owned(&asy->asy_excl_hi));
2094 /*
2095  * XXXX this should be recoded
2096  */
2097 	return ((async->async_ocnt > 0) ||
2098 	    ((ddi_get8(asy->asy_iohandle,
2099 	    asy->asy_ioaddr + LSR) & (XSRE|XHRE)) == 0));
2100 }
2101 
2102 static void
2103 asy_waiteot(struct asycom *asy)
2104 {
2105 	/*
2106 	 * Wait for the current transmission block and the
2107 	 * current fifo data to transmit. Once this is done
2108 	 * we may go on.
2109 	 */
2110 	ASY_DPRINTF(asy, ASY_DEBUG_EOT, "enter");
2111 	ASSERT(mutex_owned(&asy->asy_excl));
2112 	ASSERT(mutex_owned(&asy->asy_excl_hi));
2113 	while (asy_isbusy(asy)) {
2114 		mutex_exit(&asy->asy_excl_hi);
2115 		mutex_exit(&asy->asy_excl);
2116 		drv_usecwait(10000);		/* wait .01 */
2117 		mutex_enter(&asy->asy_excl);
2118 		mutex_enter(&asy->asy_excl_hi);
2119 	}
2120 }
2121 
2122 /* asy_reset_fifo -- flush fifos and [re]program fifo control register */
2123 static void
2124 asy_reset_fifo(struct asycom *asy, uchar_t flush)
2125 {
2126 	uchar_t lcr = 0;
2127 
2128 	/* On a 16750, we have to set DLAB in order to set FIFOEXTRA. */
2129 
2130 	if (asy->asy_hwtype >= ASY16750) {
2131 		lcr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LCR);
2132 		ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR,
2133 		    lcr | DLAB);
2134 	}
2135 
2136 	ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + FIFOR,
2137 	    asy->asy_fifor | flush);
2138 
2139 	/* Clear DLAB */
2140 
2141 	if (asy->asy_hwtype >= ASY16750) {
2142 		ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, lcr);
2143 	}
2144 }
2145 
2146 /*
2147  * Program the ASY port. Most of the async operation is based on the values
2148  * of 'c_iflag' and 'c_cflag'.
2149  */
2150 
2151 #define	BAUDINDEX(cflg)	(((cflg) & CBAUDEXT) ? \
2152 			(((cflg) & CBAUD) + CBAUD + 1) : ((cflg) & CBAUD))
2153 
2154 static void
2155 asy_program(struct asycom *asy, int mode)
2156 {
2157 	struct asyncline *async;
2158 	int baudrate, c_flag;
2159 	int icr, lcr;
2160 	int flush_reg;
2161 	int ocflags;
2162 
2163 	ASSERT(mutex_owned(&asy->asy_excl));
2164 	ASSERT(mutex_owned(&asy->asy_excl_hi));
2165 
2166 	async = asy->asy_priv;
2167 	ASY_DPRINTF(asy, ASY_DEBUG_PROCS, "mode = 0x%08X, enter", mode);
2168 
2169 	baudrate = BAUDINDEX(async->async_ttycommon.t_cflag);
2170 
2171 	async->async_ttycommon.t_cflag &= ~(CIBAUD);
2172 
2173 	if (baudrate > CBAUD) {
2174 		async->async_ttycommon.t_cflag |= CIBAUDEXT;
2175 		async->async_ttycommon.t_cflag |=
2176 		    (((baudrate - CBAUD - 1) << IBSHIFT) & CIBAUD);
2177 	} else {
2178 		async->async_ttycommon.t_cflag &= ~CIBAUDEXT;
2179 		async->async_ttycommon.t_cflag |=
2180 		    ((baudrate << IBSHIFT) & CIBAUD);
2181 	}
2182 
2183 	c_flag = async->async_ttycommon.t_cflag &
2184 	    (CLOCAL|CREAD|CSTOPB|CSIZE|PARENB|PARODD|CBAUD|CBAUDEXT);
2185 
2186 	/* disable interrupts */
2187 	ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 0);
2188 
2189 	ocflags = asy->asy_ocflag;
2190 
2191 	/* flush/reset the status registers */
2192 	(void) ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + ISR);
2193 	(void) ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR);
2194 	asy->asy_msr = flush_reg = ddi_get8(asy->asy_iohandle,
2195 	    asy->asy_ioaddr + MSR);
2196 	/*
2197 	 * The device is programmed in the open sequence, if we
2198 	 * have to hardware handshake, then this is a good time
2199 	 * to check if the device can receive any data.
2200 	 */
2201 
2202 	if ((CRTSCTS & async->async_ttycommon.t_cflag) && !(flush_reg & CTS)) {
2203 		async_flowcontrol_hw_output(asy, FLOW_STOP);
2204 	} else {
2205 		/*
2206 		 * We can not use async_flowcontrol_hw_output(asy, FLOW_START)
2207 		 * here, because if CRTSCTS is clear, we need clear
2208 		 * ASYNC_HW_OUT_FLW bit.
2209 		 */
2210 		async->async_flags &= ~ASYNC_HW_OUT_FLW;
2211 	}
2212 
2213 	/*
2214 	 * If IXON is not set, clear ASYNC_SW_OUT_FLW;
2215 	 * If IXON is set, no matter what IXON flag is before this
2216 	 * function call to asy_program,
2217 	 * we will use the old ASYNC_SW_OUT_FLW status.
2218 	 * Because of handling IXON in the driver, we also should re-calculate
2219 	 * the value of ASYNC_OUT_FLW_RESUME bit, but in fact,
2220 	 * the TCSET* commands which call asy_program
2221 	 * are put into the write queue, so there is no output needed to
2222 	 * be resumed at this point.
2223 	 */
2224 	if (!(IXON & async->async_ttycommon.t_iflag))
2225 		async->async_flags &= ~ASYNC_SW_OUT_FLW;
2226 
2227 	/* manually flush receive buffer or fifo (workaround for buggy fifos) */
2228 	if (mode == ASY_INIT)
2229 		if (asy->asy_use_fifo == FIFO_ON) {
2230 			for (flush_reg = asy->asy_fifo_buf; flush_reg-- > 0; ) {
2231 				(void) ddi_get8(asy->asy_iohandle,
2232 				    asy->asy_ioaddr + DAT);
2233 			}
2234 		} else {
2235 			flush_reg = ddi_get8(asy->asy_iohandle,
2236 			    asy->asy_ioaddr + DAT);
2237 		}
2238 
2239 	if (ocflags != (c_flag & ~CLOCAL) || mode == ASY_INIT) {
2240 		/* Set line control */
2241 		lcr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LCR);
2242 		lcr &= ~(WLS0|WLS1|STB|PEN|EPS);
2243 
2244 		if (c_flag & CSTOPB)
2245 			lcr |= STB;	/* 2 stop bits */
2246 
2247 		if (c_flag & PARENB)
2248 			lcr |= PEN;
2249 
2250 		if ((c_flag & PARODD) == 0)
2251 			lcr |= EPS;
2252 
2253 		switch (c_flag & CSIZE) {
2254 		case CS5:
2255 			lcr |= BITS5;
2256 			break;
2257 		case CS6:
2258 			lcr |= BITS6;
2259 			break;
2260 		case CS7:
2261 			lcr |= BITS7;
2262 			break;
2263 		case CS8:
2264 			lcr |= BITS8;
2265 			break;
2266 		}
2267 
2268 		/* set the baud rate, unless it is "0" */
2269 		ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, DLAB);
2270 
2271 		if (baudrate != 0) {
2272 			ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + DAT,
2273 			    asyspdtab[baudrate] & 0xff);
2274 			ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR,
2275 			    (asyspdtab[baudrate] >> 8) & 0xff);
2276 		}
2277 		/* set the line control modes */
2278 		ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, lcr);
2279 
2280 		/*
2281 		 * If we have a FIFO buffer, enable/flush
2282 		 * at intialize time, flush if transitioning from
2283 		 * CREAD off to CREAD on.
2284 		 */
2285 		if ((ocflags & CREAD) == 0 && (c_flag & CREAD) ||
2286 		    mode == ASY_INIT)
2287 			if (asy->asy_use_fifo == FIFO_ON)
2288 				asy_reset_fifo(asy, FIFORXFLSH);
2289 
2290 		/* remember the new cflags */
2291 		asy->asy_ocflag = c_flag & ~CLOCAL;
2292 	}
2293 
2294 	if (baudrate == 0)
2295 		ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR,
2296 		    (asy->asy_mcr & RTS) | OUT2);
2297 	else
2298 		ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR,
2299 		    asy->asy_mcr | OUT2);
2300 
2301 	/*
2302 	 * Call the modem status interrupt handler to check for the carrier
2303 	 * in case CLOCAL was turned off after the carrier came on.
2304 	 * (Note: Modem status interrupt is not enabled if CLOCAL is ON.)
2305 	 */
2306 	async_msint(asy);
2307 
2308 	/* Set interrupt control */
2309 	ASY_DPRINTF(asy, ASY_DEBUG_MODM2,
2310 	    "c_flag & CLOCAL = %x t_cflag & CRTSCTS = %x",
2311 	    c_flag & CLOCAL, async->async_ttycommon.t_cflag & CRTSCTS);
2312 
2313 	if ((c_flag & CLOCAL) && !(async->async_ttycommon.t_cflag & CRTSCTS))
2314 		/*
2315 		 * direct-wired line ignores DCD, so we don't enable modem
2316 		 * status interrupts.
2317 		 */
2318 		icr = (TIEN | SIEN);
2319 	else
2320 		icr = (TIEN | SIEN | MIEN);
2321 
2322 	if (c_flag & CREAD)
2323 		icr |= RIEN;
2324 
2325 	ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, icr);
2326 	ASY_DPRINTF(asy, ASY_DEBUG_PROCS, "done");
2327 }
2328 
2329 static boolean_t
2330 asy_baudok(struct asycom *asy)
2331 {
2332 	struct asyncline *async = asy->asy_priv;
2333 	int baudrate;
2334 
2335 
2336 	baudrate = BAUDINDEX(async->async_ttycommon.t_cflag);
2337 
2338 	if (baudrate >= sizeof (asyspdtab)/sizeof (*asyspdtab))
2339 		return (0);
2340 
2341 	return (baudrate == 0 || asyspdtab[baudrate]);
2342 }
2343 
2344 /*
2345  * asyintr() is the High Level Interrupt Handler.
2346  *
2347  * There are four different interrupt types indexed by ISR register values:
2348  *		0: modem
2349  *		1: Tx holding register is empty, ready for next char
2350  *		2: Rx register now holds a char to be picked up
2351  *		3: error or break on line
2352  * This routine checks the Bit 0 (interrupt-not-pending) to determine if
2353  * the interrupt is from this port.
2354  */
2355 uint_t
2356 asyintr(caddr_t argasy, caddr_t argunused __unused)
2357 {
2358 	struct asycom		*asy = (struct asycom *)argasy;
2359 	struct asyncline	*async = asy->asy_priv;
2360 	int			ret_status = DDI_INTR_UNCLAIMED;
2361 
2362 	if ((async == NULL) ||
2363 	    !(async->async_flags & (ASYNC_ISOPEN|ASYNC_WOPEN))) {
2364 		const uint8_t intr_id = ddi_get8(asy->asy_iohandle,
2365 		    asy->asy_ioaddr + ISR) & 0x0F;
2366 
2367 		if (intr_id & NOINTERRUPT) {
2368 			return (DDI_INTR_UNCLAIMED);
2369 		} else {
2370 			/*
2371 			 * reset the device by:
2372 			 *	reading line status
2373 			 *	reading any data from data status register
2374 			 *	reading modem status
2375 			 */
2376 			(void) ddi_get8(asy->asy_iohandle,
2377 			    asy->asy_ioaddr + LSR);
2378 			(void) ddi_get8(asy->asy_iohandle,
2379 			    asy->asy_ioaddr + DAT);
2380 			asy->asy_msr = ddi_get8(asy->asy_iohandle,
2381 			    asy->asy_ioaddr + MSR);
2382 			return (DDI_INTR_CLAIMED);
2383 		}
2384 	}
2385 
2386 	mutex_enter(&asy->asy_excl_hi);
2387 
2388 	if (asy->asy_flags & ASY_DDI_SUSPENDED) {
2389 		mutex_exit(&asy->asy_excl_hi);
2390 		return (DDI_INTR_CLAIMED);
2391 	}
2392 
2393 	/*
2394 	 * We will loop until the interrupt line is pulled low. asy
2395 	 * interrupt is edge triggered.
2396 	 */
2397 	for (;;) {
2398 		const uint8_t intr_id = ddi_get8(asy->asy_iohandle,
2399 		    asy->asy_ioaddr + ISR) & 0x0F;
2400 
2401 		if (intr_id & NOINTERRUPT)
2402 			break;
2403 		ret_status = DDI_INTR_CLAIMED;
2404 
2405 		ASY_DPRINTF(asy, ASY_DEBUG_INTR, "interrupt_id = 0x%x",
2406 		    intr_id);
2407 
2408 		const uint8_t lsr = ddi_get8(asy->asy_iohandle,
2409 		    asy->asy_ioaddr + LSR);
2410 
2411 		switch (intr_id) {
2412 		case TxRDY:
2413 			/*
2414 			 * The transmit-ready interrupt implies an empty
2415 			 * transmit-hold register (or FIFO).  Check that it is
2416 			 * present before attempting to transmit more data.
2417 			 */
2418 			if ((lsr & XHRE) == 0) {
2419 				/*
2420 				 * Taking a TxRDY interrupt only to find XHRE
2421 				 * absent would be a surprise, except for a
2422 				 * racing asyputchar(), which ignores the
2423 				 * excl_hi mutex when writing to the device.
2424 				 */
2425 				continue;
2426 			}
2427 			async_txint(asy);
2428 			/*
2429 			 * Unlike the other interrupts which fall through to
2430 			 * attempting to fill the output register/FIFO, TxRDY
2431 			 * has no need having just done so.
2432 			 */
2433 			continue;
2434 
2435 		case RxRDY:
2436 		case RSTATUS:
2437 		case FFTMOUT:
2438 			/* receiver interrupt or receiver errors */
2439 			async_rxint(asy, lsr);
2440 			break;
2441 		case MSTATUS:
2442 			/* modem status interrupt */
2443 			async_msint(asy);
2444 			break;
2445 		}
2446 		/* Refill the output FIFO if it has gone empty */
2447 		if ((lsr & XHRE) && (async->async_flags & ASYNC_BUSY) &&
2448 		    (async->async_ocnt > 0))
2449 			async_txint(asy);
2450 	}
2451 	mutex_exit(&asy->asy_excl_hi);
2452 	return (ret_status);
2453 }
2454 
2455 /*
2456  * Transmitter interrupt service routine.
2457  * If there is more data to transmit in the current pseudo-DMA block,
2458  * send the next character if output is not stopped or draining.
2459  * Otherwise, queue up a soft interrupt.
2460  *
2461  * XXX -  Needs review for HW FIFOs.
2462  */
2463 static void
2464 async_txint(struct asycom *asy)
2465 {
2466 	struct asyncline *async = asy->asy_priv;
2467 	int		fifo_len;
2468 
2469 	ASSERT(MUTEX_HELD(&asy->asy_excl_hi));
2470 
2471 	/*
2472 	 * If ASYNC_BREAK or ASYNC_OUT_SUSPEND has been set, return to
2473 	 * asyintr()'s context to claim the interrupt without performing
2474 	 * any action. No character will be loaded into FIFO/THR until
2475 	 * timed or untimed break is removed
2476 	 */
2477 	if (async->async_flags & (ASYNC_BREAK|ASYNC_OUT_SUSPEND))
2478 		return;
2479 
2480 	fifo_len = asy->asy_fifo_buf; /* with FIFO buffers */
2481 	if (fifo_len > asy_max_tx_fifo)
2482 		fifo_len = asy_max_tx_fifo;
2483 
2484 	if (async_flowcontrol_sw_input(asy, FLOW_CHECK, IN_FLOW_NULL))
2485 		fifo_len--;
2486 
2487 	if (async->async_ocnt > 0 && fifo_len > 0 &&
2488 	    !(async->async_flags &
2489 	    (ASYNC_HW_OUT_FLW|ASYNC_SW_OUT_FLW|ASYNC_STOPPED))) {
2490 		while (fifo_len-- > 0 && async->async_ocnt-- > 0) {
2491 			ddi_put8(asy->asy_iohandle,
2492 			    asy->asy_ioaddr + DAT, *async->async_optr++);
2493 		}
2494 		async->async_flags |= ASYNC_PROGRESS;
2495 	}
2496 
2497 	if (fifo_len <= 0)
2498 		return;
2499 
2500 	asysetsoft(asy);
2501 }
2502 
2503 /*
2504  * Interrupt on port: handle PPS event.  This function is only called
2505  * for a port on which PPS event handling has been enabled.
2506  */
2507 static void
2508 asy_ppsevent(struct asycom *asy, int msr)
2509 {
2510 	ASSERT(MUTEX_HELD(&asy->asy_excl_hi));
2511 
2512 	if (asy->asy_flags & ASY_PPS_EDGE) {
2513 		/* Have seen leading edge, now look for and record drop */
2514 		if ((msr & DCD) == 0)
2515 			asy->asy_flags &= ~ASY_PPS_EDGE;
2516 		/*
2517 		 * Waiting for leading edge, look for rise; stamp event and
2518 		 * calibrate kernel clock.
2519 		 */
2520 	} else if (msr & DCD) {
2521 			/*
2522 			 * This code captures a timestamp at the designated
2523 			 * transition of the PPS signal (DCD asserted).  The
2524 			 * code provides a pointer to the timestamp, as well
2525 			 * as the hardware counter value at the capture.
2526 			 *
2527 			 * Note: the kernel has nano based time values while
2528 			 * NTP requires micro based, an in-line fast algorithm
2529 			 * to convert nsec to usec is used here -- see hrt2ts()
2530 			 * in common/os/timers.c for a full description.
2531 			 */
2532 			struct timeval *tvp = &asy_ppsev.tv;
2533 			timestruc_t ts;
2534 			long nsec, usec;
2535 
2536 			asy->asy_flags |= ASY_PPS_EDGE;
2537 			LED_OFF;
2538 			gethrestime(&ts);
2539 			LED_ON;
2540 			nsec = ts.tv_nsec;
2541 			usec = nsec + (nsec >> 2);
2542 			usec = nsec + (usec >> 1);
2543 			usec = nsec + (usec >> 2);
2544 			usec = nsec + (usec >> 4);
2545 			usec = nsec - (usec >> 3);
2546 			usec = nsec + (usec >> 2);
2547 			usec = nsec + (usec >> 3);
2548 			usec = nsec + (usec >> 4);
2549 			usec = nsec + (usec >> 1);
2550 			usec = nsec + (usec >> 6);
2551 			tvp->tv_usec = usec >> 10;
2552 			tvp->tv_sec = ts.tv_sec;
2553 
2554 			++asy_ppsev.serial;
2555 
2556 			/*
2557 			 * Because the kernel keeps a high-resolution time,
2558 			 * pass the current highres timestamp in tvp and zero
2559 			 * in usec.
2560 			 */
2561 			ddi_hardpps(tvp, 0);
2562 	}
2563 }
2564 
2565 /*
2566  * Receiver interrupt: RxRDY interrupt, FIFO timeout interrupt or receive
2567  * error interrupt.
2568  * Try to put the character into the circular buffer for this line; if it
2569  * overflows, indicate a circular buffer overrun. If this port is always
2570  * to be serviced immediately, or the character is a STOP character, or
2571  * more than 15 characters have arrived, queue up a soft interrupt to
2572  * drain the circular buffer.
2573  * XXX - needs review for hw FIFOs support.
2574  */
2575 
2576 static void
2577 async_rxint(struct asycom *asy, uchar_t lsr)
2578 {
2579 	struct asyncline *async = asy->asy_priv;
2580 	uchar_t c;
2581 	uint_t s, needsoft = 0;
2582 	tty_common_t *tp;
2583 	int looplim = asy->asy_fifo_buf * 2;
2584 
2585 	ASSERT(MUTEX_HELD(&asy->asy_excl_hi));
2586 
2587 	tp = &async->async_ttycommon;
2588 	if (!(tp->t_cflag & CREAD)) {
2589 		while (lsr & (RCA|PARERR|FRMERR|BRKDET|OVRRUN)) {
2590 			(void) (ddi_get8(asy->asy_iohandle,
2591 			    asy->asy_ioaddr + DAT) & 0xff);
2592 			lsr = ddi_get8(asy->asy_iohandle,
2593 			    asy->asy_ioaddr + LSR);
2594 			if (looplim-- < 0)		/* limit loop */
2595 				break;
2596 		}
2597 		return; /* line is not open for read? */
2598 	}
2599 
2600 	while (lsr & (RCA|PARERR|FRMERR|BRKDET|OVRRUN)) {
2601 		c = 0;
2602 		s = 0;				/* reset error status */
2603 		if (lsr & RCA) {
2604 			c = ddi_get8(asy->asy_iohandle,
2605 			    asy->asy_ioaddr + DAT) & 0xff;
2606 
2607 			/*
2608 			 * We handle XON/XOFF char if IXON is set,
2609 			 * but if received char is _POSIX_VDISABLE,
2610 			 * we left it to the up level module.
2611 			 */
2612 			if (tp->t_iflag & IXON) {
2613 				if ((c == async->async_stopc) &&
2614 				    (c != _POSIX_VDISABLE)) {
2615 					async_flowcontrol_sw_output(asy,
2616 					    FLOW_STOP);
2617 					goto check_looplim;
2618 				} else if ((c == async->async_startc) &&
2619 				    (c != _POSIX_VDISABLE)) {
2620 					async_flowcontrol_sw_output(asy,
2621 					    FLOW_START);
2622 					needsoft = 1;
2623 					goto check_looplim;
2624 				}
2625 				if ((tp->t_iflag & IXANY) &&
2626 				    (async->async_flags & ASYNC_SW_OUT_FLW)) {
2627 					async_flowcontrol_sw_output(asy,
2628 					    FLOW_START);
2629 					needsoft = 1;
2630 				}
2631 			}
2632 		}
2633 
2634 		/*
2635 		 * Check for character break sequence
2636 		 */
2637 		if ((abort_enable == KIOCABORTALTERNATE) &&
2638 		    (asy->asy_flags & ASY_CONSOLE)) {
2639 			if (abort_charseq_recognize(c))
2640 				abort_sequence_enter((char *)NULL);
2641 		}
2642 
2643 		/* Handle framing errors */
2644 		if (lsr & (PARERR|FRMERR|BRKDET|OVRRUN)) {
2645 			if (lsr & PARERR) {
2646 				if (tp->t_iflag & INPCK) /* parity enabled */
2647 					s |= PERROR;
2648 			}
2649 
2650 			if (lsr & (FRMERR|BRKDET))
2651 				s |= FRERROR;
2652 			if (lsr & OVRRUN) {
2653 				async->async_hw_overrun = 1;
2654 				s |= OVERRUN;
2655 			}
2656 		}
2657 
2658 		if (s == 0)
2659 			if ((tp->t_iflag & PARMRK) &&
2660 			    !(tp->t_iflag & (IGNPAR|ISTRIP)) &&
2661 			    (c == 0377))
2662 				if (RING_POK(async, 2)) {
2663 					RING_PUT(async, 0377);
2664 					RING_PUT(async, c);
2665 				} else
2666 					async->async_sw_overrun = 1;
2667 			else
2668 				if (RING_POK(async, 1))
2669 					RING_PUT(async, c);
2670 				else
2671 					async->async_sw_overrun = 1;
2672 		else
2673 			if (s & FRERROR) /* Handle framing errors */
2674 				if (c == 0)
2675 					if ((asy->asy_flags & ASY_CONSOLE) &&
2676 					    (abort_enable !=
2677 					    KIOCABORTALTERNATE))
2678 						abort_sequence_enter((char *)0);
2679 					else
2680 						async->async_break++;
2681 				else
2682 					if (RING_POK(async, 1))
2683 						RING_MARK(async, c, s);
2684 					else
2685 						async->async_sw_overrun = 1;
2686 			else /* Parity errors are handled by ldterm */
2687 				if (RING_POK(async, 1))
2688 					RING_MARK(async, c, s);
2689 				else
2690 					async->async_sw_overrun = 1;
2691 check_looplim:
2692 		lsr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR);
2693 		if (looplim-- < 0)		/* limit loop */
2694 			break;
2695 	}
2696 	if ((RING_CNT(async) > (RINGSIZE * 3)/4) &&
2697 	    !(async->async_inflow_source & IN_FLOW_RINGBUFF)) {
2698 		async_flowcontrol_hw_input(asy, FLOW_STOP, IN_FLOW_RINGBUFF);
2699 		(void) async_flowcontrol_sw_input(asy, FLOW_STOP,
2700 		    IN_FLOW_RINGBUFF);
2701 	}
2702 
2703 	if ((async->async_flags & ASYNC_SERVICEIMM) || needsoft ||
2704 	    (RING_FRAC(async)) || (async->async_polltid == 0)) {
2705 		asysetsoft(asy);	/* need a soft interrupt */
2706 	}
2707 }
2708 
2709 /*
2710  * Modem status interrupt.
2711  *
2712  * (Note: It is assumed that the MSR hasn't been read by asyintr().)
2713  */
2714 
2715 static void
2716 async_msint(struct asycom *asy)
2717 {
2718 	struct asyncline *async = asy->asy_priv;
2719 	int msr, t_cflag = async->async_ttycommon.t_cflag;
2720 
2721 	ASSERT(MUTEX_HELD(&asy->asy_excl_hi));
2722 
2723 async_msint_retry:
2724 	/* this resets the interrupt */
2725 	msr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + MSR);
2726 	ASY_DPRINTF(asy, ASY_DEBUG_STATE, "call #%d:",
2727 	    ++(asy->asy_msint_cnt));
2728 	ASY_DPRINTF(asy, ASY_DEBUG_STATE, "   transition: %3s %3s %3s %3s",
2729 	    (msr & DCTS) ? "DCTS" : "    ",
2730 	    (msr & DDSR) ? "DDSR" : "    ",
2731 	    (msr & DRI)  ? "DRI"  : "    ",
2732 	    (msr & DDCD) ? "DDCD" : "    ");
2733 	ASY_DPRINTF(asy, ASY_DEBUG_STATE, "current state: %3s %3s %3s %3s",
2734 	    (msr & CTS)  ? "CTS " : "    ",
2735 	    (msr & DSR)  ? "DSR " : "    ",
2736 	    (msr & RI)   ? "RI  " : "    ",
2737 	    (msr & DCD)  ? "DCD " : "    ");
2738 
2739 	/* If CTS status is changed, do H/W output flow control */
2740 	if ((t_cflag & CRTSCTS) && (((asy->asy_msr ^ msr) & CTS) != 0))
2741 		async_flowcontrol_hw_output(asy,
2742 		    msr & CTS ? FLOW_START : FLOW_STOP);
2743 	/*
2744 	 * Reading MSR resets the interrupt, we save the
2745 	 * value of msr so that other functions could examine MSR by
2746 	 * looking at asy_msr.
2747 	 */
2748 	asy->asy_msr = (uchar_t)msr;
2749 
2750 	/* Handle PPS event */
2751 	if (asy->asy_flags & ASY_PPS)
2752 		asy_ppsevent(asy, msr);
2753 
2754 	async->async_ext++;
2755 	asysetsoft(asy);
2756 	/*
2757 	 * We will make sure that the modem status presented to us
2758 	 * during the previous read has not changed. If the chip samples
2759 	 * the modem status on the falling edge of the interrupt line,
2760 	 * and uses this state as the base for detecting change of modem
2761 	 * status, we would miss a change of modem status event that occured
2762 	 * after we initiated a read MSR operation.
2763 	 */
2764 	msr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + MSR);
2765 	if (STATES(msr) != STATES(asy->asy_msr))
2766 		goto	async_msint_retry;
2767 }
2768 
2769 /*
2770  * Pend a soft interrupt if one isn't already pending.
2771  */
2772 static void
2773 asysetsoft(struct asycom *asy)
2774 {
2775 	ASSERT(MUTEX_HELD(&asy->asy_excl_hi));
2776 
2777 	if (mutex_tryenter(&asy->asy_soft_lock) == 0)
2778 		return;
2779 
2780 	asy->asy_flags |= ASY_NEEDSOFT;
2781 	if (!asy->asysoftpend) {
2782 		asy->asysoftpend = 1;
2783 		mutex_exit(&asy->asy_soft_lock);
2784 		(void) ddi_intr_trigger_softint(asy->asy_soft_inth, NULL);
2785 	} else {
2786 		mutex_exit(&asy->asy_soft_lock);
2787 	}
2788 }
2789 
2790 /*
2791  * Handle a second-stage interrupt.
2792  */
2793 /*ARGSUSED*/
2794 uint_t
2795 asysoftintr(caddr_t intarg, caddr_t unusedarg __unused)
2796 {
2797 	struct asycom *asy = (struct asycom *)intarg;
2798 	struct asyncline *async;
2799 	int rv;
2800 	uint_t cc;
2801 
2802 	/*
2803 	 * Test and clear soft interrupt.
2804 	 */
2805 	mutex_enter(&asy->asy_soft_lock);
2806 	ASY_DPRINTF(asy, ASY_DEBUG_PROCS, "enter");
2807 	rv = asy->asysoftpend;
2808 	if (rv != 0)
2809 		asy->asysoftpend = 0;
2810 	mutex_exit(&asy->asy_soft_lock);
2811 
2812 	if (rv) {
2813 		if (asy->asy_priv == NULL)
2814 			return (rv ? DDI_INTR_CLAIMED : DDI_INTR_UNCLAIMED);
2815 		async = (struct asyncline *)asy->asy_priv;
2816 		mutex_enter(&asy->asy_excl_hi);
2817 		if (asy->asy_flags & ASY_NEEDSOFT) {
2818 			asy->asy_flags &= ~ASY_NEEDSOFT;
2819 			mutex_exit(&asy->asy_excl_hi);
2820 			async_softint(asy);
2821 			mutex_enter(&asy->asy_excl_hi);
2822 		}
2823 
2824 		/*
2825 		 * There are some instances where the softintr is not
2826 		 * scheduled and hence not called. It so happens that
2827 		 * causes the last few characters to be stuck in the
2828 		 * ringbuffer. Hence, call the handler once again so
2829 		 * the last few characters are cleared.
2830 		 */
2831 		cc = RING_CNT(async);
2832 		mutex_exit(&asy->asy_excl_hi);
2833 		if (cc > 0)
2834 			(void) async_softint(asy);
2835 	}
2836 	return (rv ? DDI_INTR_CLAIMED : DDI_INTR_UNCLAIMED);
2837 }
2838 
2839 /*
2840  * Handle a software interrupt.
2841  */
2842 static void
2843 async_softint(struct asycom *asy)
2844 {
2845 	struct asyncline *async = asy->asy_priv;
2846 	uint_t	cc;
2847 	mblk_t	*bp;
2848 	queue_t	*q;
2849 	uchar_t	val;
2850 	uchar_t	c;
2851 	tty_common_t	*tp;
2852 	int nb;
2853 	int instance = UNIT(async->async_dev);
2854 
2855 	ASY_DPRINTF(asy, ASY_DEBUG_PROCS, "enter");
2856 	mutex_enter(&asy->asy_excl_hi);
2857 	if (asy->asy_flags & ASY_DOINGSOFT) {
2858 		asy->asy_flags |= ASY_DOINGSOFT_RETRY;
2859 		mutex_exit(&asy->asy_excl_hi);
2860 		return;
2861 	}
2862 	asy->asy_flags |= ASY_DOINGSOFT;
2863 begin:
2864 	asy->asy_flags &= ~ASY_DOINGSOFT_RETRY;
2865 	mutex_exit(&asy->asy_excl_hi);
2866 	mutex_enter(&asy->asy_excl);
2867 	tp = &async->async_ttycommon;
2868 	q = tp->t_readq;
2869 	if (async->async_flags & ASYNC_OUT_FLW_RESUME) {
2870 		if (async->async_ocnt > 0) {
2871 			mutex_enter(&asy->asy_excl_hi);
2872 			async_resume(async);
2873 			mutex_exit(&asy->asy_excl_hi);
2874 		} else {
2875 			if (async->async_xmitblk)
2876 				freeb(async->async_xmitblk);
2877 			async->async_xmitblk = NULL;
2878 			async_start(async);
2879 		}
2880 		async->async_flags &= ~ASYNC_OUT_FLW_RESUME;
2881 	}
2882 	mutex_enter(&asy->asy_excl_hi);
2883 	if (async->async_ext) {
2884 		async->async_ext = 0;
2885 		/* check for carrier up */
2886 		ASY_DPRINTF(asy, ASY_DEBUG_MODM2,
2887 		    "asy_msr & DCD = %x, tp->t_flags & TS_SOFTCAR = %x",
2888 		    asy->asy_msr & DCD, tp->t_flags & TS_SOFTCAR);
2889 
2890 		if (asy->asy_msr & DCD) {
2891 			/* carrier present */
2892 			if ((async->async_flags & ASYNC_CARR_ON) == 0) {
2893 				ASY_DPRINTF(asy, ASY_DEBUG_MODM2,
2894 				    "set ASYNC_CARR_ON");
2895 				async->async_flags |= ASYNC_CARR_ON;
2896 				if (async->async_flags & ASYNC_ISOPEN) {
2897 					mutex_exit(&asy->asy_excl_hi);
2898 					mutex_exit(&asy->asy_excl);
2899 					(void) putctl(q, M_UNHANGUP);
2900 					mutex_enter(&asy->asy_excl);
2901 					mutex_enter(&asy->asy_excl_hi);
2902 				}
2903 				cv_broadcast(&async->async_flags_cv);
2904 			}
2905 		} else {
2906 			if ((async->async_flags & ASYNC_CARR_ON) &&
2907 			    !(tp->t_cflag & CLOCAL) &&
2908 			    !(tp->t_flags & TS_SOFTCAR)) {
2909 				int flushflag;
2910 
2911 				ASY_DPRINTF(asy, ASY_DEBUG_MODEM,
2912 				    "carrier dropped, so drop DTR");
2913 				/*
2914 				 * Carrier went away.
2915 				 * Drop DTR, abort any output in
2916 				 * progress, indicate that output is
2917 				 * not stopped, and send a hangup
2918 				 * notification upstream.
2919 				 */
2920 				val = ddi_get8(asy->asy_iohandle,
2921 				    asy->asy_ioaddr + MCR);
2922 				ddi_put8(asy->asy_iohandle,
2923 				    asy->asy_ioaddr + MCR, (val & ~DTR));
2924 
2925 				if (async->async_flags & ASYNC_BUSY) {
2926 					ASY_DPRINTF(asy, ASY_DEBUG_BUSY,
2927 					    "Carrier dropped.  "
2928 					    "Clearing async_ocnt");
2929 					async->async_ocnt = 0;
2930 				}	/* if */
2931 
2932 				async->async_flags &= ~ASYNC_STOPPED;
2933 				if (async->async_flags & ASYNC_ISOPEN) {
2934 					mutex_exit(&asy->asy_excl_hi);
2935 					mutex_exit(&asy->asy_excl);
2936 					(void) putctl(q, M_HANGUP);
2937 					mutex_enter(&asy->asy_excl);
2938 					ASY_DPRINTF(asy, ASY_DEBUG_MODEM,
2939 					    "putctl(q, M_HANGUP)");
2940 					/*
2941 					 * Flush FIFO buffers
2942 					 * Any data left in there is invalid now
2943 					 */
2944 					if (asy->asy_use_fifo == FIFO_ON)
2945 						asy_reset_fifo(asy, FIFOTXFLSH);
2946 					/*
2947 					 * Flush our write queue if we have one.
2948 					 * If we're in the midst of close, then
2949 					 * flush everything. Don't leave stale
2950 					 * ioctls lying about.
2951 					 */
2952 					flushflag = (async->async_flags &
2953 					    ASYNC_CLOSING) ? FLUSHALL :
2954 					    FLUSHDATA;
2955 					flushq(tp->t_writeq, flushflag);
2956 
2957 					/* active msg */
2958 					bp = async->async_xmitblk;
2959 					if (bp != NULL) {
2960 						freeb(bp);
2961 						async->async_xmitblk = NULL;
2962 					}
2963 
2964 					mutex_enter(&asy->asy_excl_hi);
2965 					async->async_flags &= ~ASYNC_BUSY;
2966 					/*
2967 					 * This message warns of Carrier loss
2968 					 * with data left to transmit can hang
2969 					 * the system.
2970 					 */
2971 					ASY_DPRINTF(asy, ASY_DEBUG_MODEM,
2972 					    "Flushing to prevent HUPCL "
2973 					    "hanging");
2974 				}	/* if (ASYNC_ISOPEN) */
2975 			}	/* if (ASYNC_CARR_ON && CLOCAL) */
2976 			async->async_flags &= ~ASYNC_CARR_ON;
2977 			cv_broadcast(&async->async_flags_cv);
2978 		}	/* else */
2979 	}	/* if (async->async_ext) */
2980 
2981 	mutex_exit(&asy->asy_excl_hi);
2982 
2983 	/*
2984 	 * If data has been added to the circular buffer, remove
2985 	 * it from the buffer, and send it up the stream if there's
2986 	 * somebody listening. Try to do it 16 bytes at a time. If we
2987 	 * have more than 16 bytes to move, move 16 byte chunks and
2988 	 * leave the rest for next time around (maybe it will grow).
2989 	 */
2990 	mutex_enter(&asy->asy_excl_hi);
2991 	if (!(async->async_flags & ASYNC_ISOPEN)) {
2992 		RING_INIT(async);
2993 		goto rv;
2994 	}
2995 	if ((cc = RING_CNT(async)) == 0)
2996 		goto rv;
2997 	mutex_exit(&asy->asy_excl_hi);
2998 
2999 	if (!canput(q)) {
3000 		mutex_enter(&asy->asy_excl_hi);
3001 		if (!(async->async_inflow_source & IN_FLOW_STREAMS)) {
3002 			async_flowcontrol_hw_input(asy, FLOW_STOP,
3003 			    IN_FLOW_STREAMS);
3004 			(void) async_flowcontrol_sw_input(asy, FLOW_STOP,
3005 			    IN_FLOW_STREAMS);
3006 		}
3007 		goto rv;
3008 	}
3009 	if (async->async_inflow_source & IN_FLOW_STREAMS) {
3010 		mutex_enter(&asy->asy_excl_hi);
3011 		async_flowcontrol_hw_input(asy, FLOW_START,
3012 		    IN_FLOW_STREAMS);
3013 		(void) async_flowcontrol_sw_input(asy, FLOW_START,
3014 		    IN_FLOW_STREAMS);
3015 		mutex_exit(&asy->asy_excl_hi);
3016 	}
3017 
3018 	ASY_DPRINTF(asy, ASY_DEBUG_INPUT, "%d char(s) in queue", cc);
3019 
3020 	if (!(bp = allocb(cc, BPRI_MED))) {
3021 		mutex_exit(&asy->asy_excl);
3022 		ttycommon_qfull(&async->async_ttycommon, q);
3023 		mutex_enter(&asy->asy_excl);
3024 		mutex_enter(&asy->asy_excl_hi);
3025 		goto rv;
3026 	}
3027 	mutex_enter(&asy->asy_excl_hi);
3028 	do {
3029 		if (RING_ERR(async, S_ERRORS)) {
3030 			RING_UNMARK(async);
3031 			c = RING_GET(async);
3032 			break;
3033 		} else
3034 			*bp->b_wptr++ = RING_GET(async);
3035 	} while (--cc);
3036 	mutex_exit(&asy->asy_excl_hi);
3037 	mutex_exit(&asy->asy_excl);
3038 	if (bp->b_wptr > bp->b_rptr) {
3039 			if (!canput(q)) {
3040 				asyerror(asy, CE_WARN, "local queue full");
3041 				freemsg(bp);
3042 			} else
3043 				(void) putq(q, bp);
3044 	} else
3045 		freemsg(bp);
3046 	/*
3047 	 * If we have a parity error, then send
3048 	 * up an M_BREAK with the "bad"
3049 	 * character as an argument. Let ldterm
3050 	 * figure out what to do with the error.
3051 	 */
3052 	if (cc)
3053 		(void) putctl1(q, M_BREAK, c);
3054 	mutex_enter(&asy->asy_excl);
3055 	mutex_enter(&asy->asy_excl_hi);
3056 	if (cc) {
3057 		asysetsoft(asy);	/* finish cc chars */
3058 	}
3059 rv:
3060 	if ((RING_CNT(async) < (RINGSIZE/4)) &&
3061 	    (async->async_inflow_source & IN_FLOW_RINGBUFF)) {
3062 		async_flowcontrol_hw_input(asy, FLOW_START, IN_FLOW_RINGBUFF);
3063 		(void) async_flowcontrol_sw_input(asy, FLOW_START,
3064 		    IN_FLOW_RINGBUFF);
3065 	}
3066 
3067 	/*
3068 	 * If a transmission has finished, indicate that it's finished,
3069 	 * and start that line up again.
3070 	 */
3071 	if (async->async_break > 0) {
3072 		nb = async->async_break;
3073 		async->async_break = 0;
3074 		if (async->async_flags & ASYNC_ISOPEN) {
3075 			mutex_exit(&asy->asy_excl_hi);
3076 			mutex_exit(&asy->asy_excl);
3077 			for (; nb > 0; nb--)
3078 				(void) putctl(q, M_BREAK);
3079 			mutex_enter(&asy->asy_excl);
3080 			mutex_enter(&asy->asy_excl_hi);
3081 		}
3082 	}
3083 	if (async->async_ocnt <= 0 && (async->async_flags & ASYNC_BUSY)) {
3084 		ASY_DPRINTF(asy, ASY_DEBUG_BUSY,
3085 		    "Clearing ASYNC_BUSY, async_ocnt=%d", async->async_ocnt);
3086 		async->async_flags &= ~ASYNC_BUSY;
3087 		mutex_exit(&asy->asy_excl_hi);
3088 		if (async->async_xmitblk)
3089 			freeb(async->async_xmitblk);
3090 		async->async_xmitblk = NULL;
3091 		async_start(async);
3092 		/*
3093 		 * If the flag isn't set after doing the async_start above, we
3094 		 * may have finished all the queued output.  Signal any thread
3095 		 * stuck in close.
3096 		 */
3097 		if (!(async->async_flags & ASYNC_BUSY))
3098 			cv_broadcast(&async->async_flags_cv);
3099 		mutex_enter(&asy->asy_excl_hi);
3100 	}
3101 	/*
3102 	 * A note about these overrun bits: all they do is *tell* someone
3103 	 * about an error- They do not track multiple errors. In fact,
3104 	 * you could consider them latched register bits if you like.
3105 	 * We are only interested in printing the error message once for
3106 	 * any cluster of overrun errors.
3107 	 */
3108 	if (async->async_hw_overrun) {
3109 		if (async->async_flags & ASYNC_ISOPEN) {
3110 			mutex_exit(&asy->asy_excl_hi);
3111 			mutex_exit(&asy->asy_excl);
3112 			asyerror(asy, CE_WARN, "silo overflow");
3113 			mutex_enter(&asy->asy_excl);
3114 			mutex_enter(&asy->asy_excl_hi);
3115 		}
3116 		async->async_hw_overrun = 0;
3117 	}
3118 	if (async->async_sw_overrun) {
3119 		if (async->async_flags & ASYNC_ISOPEN) {
3120 			mutex_exit(&asy->asy_excl_hi);
3121 			mutex_exit(&asy->asy_excl);
3122 			asyerror(asy, CE_WARN, "ring buffer overflow");
3123 			mutex_enter(&asy->asy_excl);
3124 			mutex_enter(&asy->asy_excl_hi);
3125 		}
3126 		async->async_sw_overrun = 0;
3127 	}
3128 	if (asy->asy_flags & ASY_DOINGSOFT_RETRY) {
3129 		mutex_exit(&asy->asy_excl);
3130 		goto begin;
3131 	}
3132 	asy->asy_flags &= ~ASY_DOINGSOFT;
3133 	mutex_exit(&asy->asy_excl_hi);
3134 	mutex_exit(&asy->asy_excl);
3135 	ASY_DPRINTF(asy, ASY_DEBUG_PROCS, "done");
3136 }
3137 
3138 /*
3139  * Restart output on a line after a delay or break timer expired.
3140  */
3141 static void
3142 async_restart(void *arg)
3143 {
3144 	struct asyncline *async = (struct asyncline *)arg;
3145 	struct asycom *asy = async->async_common;
3146 	uchar_t lcr;
3147 
3148 	ASY_DPRINTF(asy, ASY_DEBUG_PROCS, "enter");
3149 
3150 	/*
3151 	 * If break timer expired, turn off the break bit.
3152 	 */
3153 
3154 	mutex_enter(&asy->asy_excl);
3155 	/*
3156 	 * If ASYNC_OUT_SUSPEND is also set, we don't really
3157 	 * clean the HW break, TIOCCBRK is responsible for this.
3158 	 */
3159 	if ((async->async_flags & ASYNC_BREAK) &&
3160 	    !(async->async_flags & ASYNC_OUT_SUSPEND)) {
3161 		mutex_enter(&asy->asy_excl_hi);
3162 		lcr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LCR);
3163 		ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR,
3164 		    (lcr & ~SETBREAK));
3165 		mutex_exit(&asy->asy_excl_hi);
3166 	}
3167 	async->async_flags &= ~(ASYNC_DELAY|ASYNC_BREAK);
3168 	cv_broadcast(&async->async_flags_cv);
3169 	async_start(async);
3170 
3171 	mutex_exit(&asy->asy_excl);
3172 }
3173 
3174 /*
3175  * Start output on a line, unless it's busy, frozen, or otherwise.
3176  */
3177 static void
3178 async_start(struct asyncline *async)
3179 {
3180 	struct asycom *asy = async->async_common;
3181 	int cc;
3182 	queue_t *q;
3183 	mblk_t *bp;
3184 	uchar_t *xmit_addr;
3185 	uchar_t	val;
3186 	int	fifo_len = 1;
3187 	boolean_t didsome;
3188 	mblk_t *nbp;
3189 
3190 #ifdef DEBUG
3191 	int instance = UNIT(async->async_dev);
3192 
3193 	ASY_DPRINTF(asy, ASY_DEBUG_PROCS, "enter");
3194 #endif
3195 	if (asy->asy_use_fifo == FIFO_ON) {
3196 		fifo_len = asy->asy_fifo_buf; /* with FIFO buffers */
3197 		if (fifo_len > asy_max_tx_fifo)
3198 			fifo_len = asy_max_tx_fifo;
3199 	}
3200 
3201 	ASSERT(mutex_owned(&asy->asy_excl));
3202 
3203 	/*
3204 	 * If the chip is busy (i.e., we're waiting for a break timeout
3205 	 * to expire, or for the current transmission to finish, or for
3206 	 * output to finish draining from chip), don't grab anything new.
3207 	 */
3208 	if (async->async_flags & (ASYNC_BREAK|ASYNC_BUSY)) {
3209 		ASY_DPRINTF(asy, ASY_DEBUG_OUT, "%s",
3210 		    async->async_flags & ASYNC_BREAK ? "break" : "busy");
3211 		return;
3212 	}
3213 
3214 	/*
3215 	 * Check only pended sw input flow control.
3216 	 */
3217 	mutex_enter(&asy->asy_excl_hi);
3218 	if (async_flowcontrol_sw_input(asy, FLOW_CHECK, IN_FLOW_NULL))
3219 		fifo_len--;
3220 	mutex_exit(&asy->asy_excl_hi);
3221 
3222 	/*
3223 	 * If we're waiting for a delay timeout to expire, don't grab
3224 	 * anything new.
3225 	 */
3226 	if (async->async_flags & ASYNC_DELAY) {
3227 		ASY_DPRINTF(asy, ASY_DEBUG_OUT, "start ASYNC_DELAY");
3228 		return;
3229 	}
3230 
3231 	if ((q = async->async_ttycommon.t_writeq) == NULL) {
3232 		ASY_DPRINTF(asy, ASY_DEBUG_OUT, "start writeq is null");
3233 		return;	/* not attached to a stream */
3234 	}
3235 
3236 	for (;;) {
3237 		if ((bp = getq(q)) == NULL)
3238 			return;	/* no data to transmit */
3239 
3240 		/*
3241 		 * We have a message block to work on.
3242 		 * Check whether it's a break, a delay, or an ioctl (the latter
3243 		 * occurs if the ioctl in question was waiting for the output
3244 		 * to drain).  If it's one of those, process it immediately.
3245 		 */
3246 		switch (bp->b_datap->db_type) {
3247 
3248 		case M_BREAK:
3249 			/*
3250 			 * Set the break bit, and arrange for "async_restart"
3251 			 * to be called in 1/4 second; it will turn the
3252 			 * break bit off, and call "async_start" to grab
3253 			 * the next message.
3254 			 */
3255 			mutex_enter(&asy->asy_excl_hi);
3256 			val = ddi_get8(asy->asy_iohandle,
3257 			    asy->asy_ioaddr + LCR);
3258 			ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR,
3259 			    (val | SETBREAK));
3260 			mutex_exit(&asy->asy_excl_hi);
3261 			async->async_flags |= ASYNC_BREAK;
3262 			(void) timeout(async_restart, (caddr_t)async,
3263 			    drv_usectohz(1000000)/4);
3264 			freemsg(bp);
3265 			return;	/* wait for this to finish */
3266 
3267 		case M_DELAY:
3268 			/*
3269 			 * Arrange for "async_restart" to be called when the
3270 			 * delay expires; it will turn ASYNC_DELAY off,
3271 			 * and call "async_start" to grab the next message.
3272 			 */
3273 			(void) timeout(async_restart, (caddr_t)async,
3274 			    (int)(*(unsigned char *)bp->b_rptr + 6));
3275 			async->async_flags |= ASYNC_DELAY;
3276 			freemsg(bp);
3277 			return;	/* wait for this to finish */
3278 
3279 		case M_IOCTL:
3280 			/*
3281 			 * This ioctl was waiting for the output ahead of
3282 			 * it to drain; obviously, it has.  Do it, and
3283 			 * then grab the next message after it.
3284 			 */
3285 			mutex_exit(&asy->asy_excl);
3286 			async_ioctl(async, q, bp);
3287 			mutex_enter(&asy->asy_excl);
3288 			continue;
3289 		}
3290 
3291 		while (bp != NULL && ((cc = MBLKL(bp)) == 0)) {
3292 			nbp = bp->b_cont;
3293 			freeb(bp);
3294 			bp = nbp;
3295 		}
3296 		if (bp != NULL)
3297 			break;
3298 	}
3299 
3300 	/*
3301 	 * We have data to transmit.  If output is stopped, put
3302 	 * it back and try again later.
3303 	 */
3304 	if (async->async_flags & (ASYNC_HW_OUT_FLW | ASYNC_SW_OUT_FLW |
3305 	    ASYNC_STOPPED | ASYNC_OUT_SUSPEND)) {
3306 		(void) putbq(q, bp);
3307 		return;
3308 	}
3309 
3310 	async->async_xmitblk = bp;
3311 	xmit_addr = bp->b_rptr;
3312 	bp = bp->b_cont;
3313 	if (bp != NULL)
3314 		(void) putbq(q, bp);	/* not done with this message yet */
3315 
3316 	/*
3317 	 * In 5-bit mode, the high order bits are used
3318 	 * to indicate character sizes less than five,
3319 	 * so we need to explicitly mask before transmitting
3320 	 */
3321 	if ((async->async_ttycommon.t_cflag & CSIZE) == CS5) {
3322 		unsigned char *p = xmit_addr;
3323 		int cnt = cc;
3324 
3325 		while (cnt--)
3326 			*p++ &= (unsigned char) 0x1f;
3327 	}
3328 
3329 	/*
3330 	 * Set up this block for pseudo-DMA.
3331 	 */
3332 	mutex_enter(&asy->asy_excl_hi);
3333 	/*
3334 	 * If the transmitter is ready, shove the first
3335 	 * character out.
3336 	 */
3337 	didsome = B_FALSE;
3338 	while (--fifo_len >= 0 && cc > 0) {
3339 		if (!(ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR) &
3340 		    XHRE))
3341 			break;
3342 		ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + DAT,
3343 		    *xmit_addr++);
3344 		cc--;
3345 		didsome = B_TRUE;
3346 	}
3347 	async->async_optr = xmit_addr;
3348 	async->async_ocnt = cc;
3349 	if (didsome)
3350 		async->async_flags |= ASYNC_PROGRESS;
3351 	ASY_DPRINTF(asy, ASY_DEBUG_BUSY, "Set ASYNC_BUSY, async_ocnt=%d",
3352 	    async->async_ocnt);
3353 	async->async_flags |= ASYNC_BUSY;
3354 	mutex_exit(&asy->asy_excl_hi);
3355 }
3356 
3357 /*
3358  * Resume output by poking the transmitter.
3359  */
3360 static void
3361 async_resume(struct asyncline *async)
3362 {
3363 	struct asycom *asy = async->async_common;
3364 
3365 	ASY_DPRINTF(asy, ASY_DEBUG_PROCS, "enter");
3366 	ASSERT(mutex_owned(&asy->asy_excl_hi));
3367 
3368 	if (ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR) & XHRE) {
3369 		if (async_flowcontrol_sw_input(asy, FLOW_CHECK, IN_FLOW_NULL))
3370 			return;
3371 		if (async->async_ocnt > 0 &&
3372 		    !(async->async_flags &
3373 		    (ASYNC_HW_OUT_FLW|ASYNC_SW_OUT_FLW|ASYNC_OUT_SUSPEND))) {
3374 			ddi_put8(asy->asy_iohandle,
3375 			    asy->asy_ioaddr + DAT, *async->async_optr++);
3376 			async->async_ocnt--;
3377 			async->async_flags |= ASYNC_PROGRESS;
3378 		}
3379 	}
3380 }
3381 
3382 /*
3383  * Hold the untimed break to last the minimum time.
3384  */
3385 static void
3386 async_hold_utbrk(void *arg)
3387 {
3388 	struct asyncline *async = arg;
3389 	struct asycom *asy = async->async_common;
3390 
3391 	mutex_enter(&asy->asy_excl);
3392 	async->async_flags &= ~ASYNC_HOLD_UTBRK;
3393 	cv_broadcast(&async->async_flags_cv);
3394 	async->async_utbrktid = 0;
3395 	mutex_exit(&asy->asy_excl);
3396 }
3397 
3398 /*
3399  * Resume the untimed break.
3400  */
3401 static void
3402 async_resume_utbrk(struct asyncline *async)
3403 {
3404 	uchar_t	val;
3405 	struct asycom *asy = async->async_common;
3406 	ASSERT(mutex_owned(&asy->asy_excl));
3407 
3408 	/*
3409 	 * Because the wait time is very short,
3410 	 * so we use uninterruptably wait.
3411 	 */
3412 	while (async->async_flags & ASYNC_HOLD_UTBRK) {
3413 		cv_wait(&async->async_flags_cv, &asy->asy_excl);
3414 	}
3415 	mutex_enter(&asy->asy_excl_hi);
3416 	/*
3417 	 * Timed break and untimed break can exist simultaneously,
3418 	 * if ASYNC_BREAK is also set at here, we don't
3419 	 * really clean the HW break.
3420 	 */
3421 	if (!(async->async_flags & ASYNC_BREAK)) {
3422 		val = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LCR);
3423 		ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR,
3424 		    (val & ~SETBREAK));
3425 	}
3426 	async->async_flags &= ~ASYNC_OUT_SUSPEND;
3427 	cv_broadcast(&async->async_flags_cv);
3428 	if (async->async_ocnt > 0) {
3429 		async_resume(async);
3430 		mutex_exit(&asy->asy_excl_hi);
3431 	} else {
3432 		async->async_flags &= ~ASYNC_BUSY;
3433 		mutex_exit(&asy->asy_excl_hi);
3434 		if (async->async_xmitblk != NULL) {
3435 			freeb(async->async_xmitblk);
3436 			async->async_xmitblk = NULL;
3437 		}
3438 		async_start(async);
3439 	}
3440 }
3441 
3442 /*
3443  * Process an "ioctl" message sent down to us.
3444  * Note that we don't need to get any locks until we are ready to access
3445  * the hardware.  Nothing we access until then is going to be altered
3446  * outside of the STREAMS framework, so we should be safe.
3447  */
3448 int asydelay = 10000;
3449 static void
3450 async_ioctl(struct asyncline *async, queue_t *wq, mblk_t *mp)
3451 {
3452 	struct asycom *asy = async->async_common;
3453 	tty_common_t  *tp = &async->async_ttycommon;
3454 	struct iocblk *iocp;
3455 	unsigned datasize;
3456 	int error = 0;
3457 	uchar_t val;
3458 	mblk_t *datamp;
3459 	unsigned int index;
3460 
3461 	ASY_DPRINTF(asy, ASY_DEBUG_PROCS, "enter");
3462 
3463 	if (tp->t_iocpending != NULL) {
3464 		/*
3465 		 * We were holding an "ioctl" response pending the
3466 		 * availability of an "mblk" to hold data to be passed up;
3467 		 * another "ioctl" came through, which means that "ioctl"
3468 		 * must have timed out or been aborted.
3469 		 */
3470 		freemsg(async->async_ttycommon.t_iocpending);
3471 		async->async_ttycommon.t_iocpending = NULL;
3472 	}
3473 
3474 	iocp = (struct iocblk *)mp->b_rptr;
3475 
3476 	/*
3477 	 * For TIOCMGET and the PPS ioctls, do NOT call ttycommon_ioctl()
3478 	 * because this function frees up the message block (mp->b_cont) that
3479 	 * contains the user location where we pass back the results.
3480 	 *
3481 	 * Similarly, CONSOPENPOLLEDIO needs ioc_count, which ttycommon_ioctl
3482 	 * zaps.  We know that ttycommon_ioctl doesn't know any CONS*
3483 	 * ioctls, so keep the others safe too.
3484 	 */
3485 	ASY_DPRINTF(asy, ASY_DEBUG_IOCTL, "%s",
3486 	    iocp->ioc_cmd == TIOCMGET ? "TIOCMGET" :
3487 	    iocp->ioc_cmd == TIOCMSET ? "TIOCMSET" :
3488 	    iocp->ioc_cmd == TIOCMBIS ? "TIOCMBIS" :
3489 	    iocp->ioc_cmd == TIOCMBIC ? "TIOCMBIC" :
3490 	    "other");
3491 
3492 	switch (iocp->ioc_cmd) {
3493 	case TIOCMGET:
3494 	case TIOCGPPS:
3495 	case TIOCSPPS:
3496 	case TIOCGPPSEV:
3497 	case CONSOPENPOLLEDIO:
3498 	case CONSCLOSEPOLLEDIO:
3499 	case CONSSETABORTENABLE:
3500 	case CONSGETABORTENABLE:
3501 		error = -1; /* Do Nothing */
3502 		break;
3503 	default:
3504 
3505 		/*
3506 		 * The only way in which "ttycommon_ioctl" can fail is if the
3507 		 * "ioctl" requires a response containing data to be returned
3508 		 * to the user, and no mblk could be allocated for the data.
3509 		 * No such "ioctl" alters our state.  Thus, we always go ahead
3510 		 * and do any state-changes the "ioctl" calls for.  If we
3511 		 * couldn't allocate the data, "ttycommon_ioctl" has stashed
3512 		 * the "ioctl" away safely, so we just call "bufcall" to
3513 		 * request that we be called back when we stand a better
3514 		 * chance of allocating the data.
3515 		 */
3516 		if ((datasize = ttycommon_ioctl(tp, wq, mp, &error)) != 0) {
3517 			if (async->async_wbufcid)
3518 				unbufcall(async->async_wbufcid);
3519 			async->async_wbufcid = bufcall(datasize, BPRI_HI,
3520 			    (void (*)(void *)) async_reioctl,
3521 			    (void *)(intptr_t)async->async_common->asy_unit);
3522 			return;
3523 		}
3524 	}
3525 
3526 	mutex_enter(&asy->asy_excl);
3527 
3528 	if (error == 0) {
3529 		/*
3530 		 * "ttycommon_ioctl" did most of the work; we just use the
3531 		 * data it set up.
3532 		 */
3533 		switch (iocp->ioc_cmd) {
3534 
3535 		case TCSETS:
3536 			mutex_enter(&asy->asy_excl_hi);
3537 			if (asy_baudok(asy))
3538 				asy_program(asy, ASY_NOINIT);
3539 			else
3540 				error = EINVAL;
3541 			mutex_exit(&asy->asy_excl_hi);
3542 			break;
3543 		case TCSETSF:
3544 		case TCSETSW:
3545 		case TCSETA:
3546 		case TCSETAW:
3547 		case TCSETAF:
3548 			mutex_enter(&asy->asy_excl_hi);
3549 			if (!asy_baudok(asy))
3550 				error = EINVAL;
3551 			else {
3552 				if (asy_isbusy(asy))
3553 					asy_waiteot(asy);
3554 				asy_program(asy, ASY_NOINIT);
3555 			}
3556 			mutex_exit(&asy->asy_excl_hi);
3557 			break;
3558 		}
3559 	} else if (error < 0) {
3560 		/*
3561 		 * "ttycommon_ioctl" didn't do anything; we process it here.
3562 		 */
3563 		error = 0;
3564 		switch (iocp->ioc_cmd) {
3565 
3566 		case TIOCGPPS:
3567 			/*
3568 			 * Get PPS on/off.
3569 			 */
3570 			if (mp->b_cont != NULL)
3571 				freemsg(mp->b_cont);
3572 
3573 			mp->b_cont = allocb(sizeof (int), BPRI_HI);
3574 			if (mp->b_cont == NULL) {
3575 				error = ENOMEM;
3576 				break;
3577 			}
3578 			if (asy->asy_flags & ASY_PPS)
3579 				*(int *)mp->b_cont->b_wptr = 1;
3580 			else
3581 				*(int *)mp->b_cont->b_wptr = 0;
3582 			mp->b_cont->b_wptr += sizeof (int);
3583 			mp->b_datap->db_type = M_IOCACK;
3584 			iocp->ioc_count = sizeof (int);
3585 			break;
3586 
3587 		case TIOCSPPS:
3588 			/*
3589 			 * Set PPS on/off.
3590 			 */
3591 			error = miocpullup(mp, sizeof (int));
3592 			if (error != 0)
3593 				break;
3594 
3595 			mutex_enter(&asy->asy_excl_hi);
3596 			if (*(int *)mp->b_cont->b_rptr)
3597 				asy->asy_flags |= ASY_PPS;
3598 			else
3599 				asy->asy_flags &= ~ASY_PPS;
3600 			/* Reset edge sense */
3601 			asy->asy_flags &= ~ASY_PPS_EDGE;
3602 			mutex_exit(&asy->asy_excl_hi);
3603 			mp->b_datap->db_type = M_IOCACK;
3604 			break;
3605 
3606 		case TIOCGPPSEV:
3607 		{
3608 			/*
3609 			 * Get PPS event data.
3610 			 */
3611 			mblk_t *bp;
3612 			void *buf;
3613 #ifdef _SYSCALL32_IMPL
3614 			struct ppsclockev32 p32;
3615 #endif
3616 			struct ppsclockev ppsclockev;
3617 
3618 			if (mp->b_cont != NULL) {
3619 				freemsg(mp->b_cont);
3620 				mp->b_cont = NULL;
3621 			}
3622 
3623 			if ((asy->asy_flags & ASY_PPS) == 0) {
3624 				error = ENXIO;
3625 				break;
3626 			}
3627 
3628 			/* Protect from incomplete asy_ppsev */
3629 			mutex_enter(&asy->asy_excl_hi);
3630 			ppsclockev = asy_ppsev;
3631 			mutex_exit(&asy->asy_excl_hi);
3632 
3633 #ifdef _SYSCALL32_IMPL
3634 			if ((iocp->ioc_flag & IOC_MODELS) != IOC_NATIVE) {
3635 				TIMEVAL_TO_TIMEVAL32(&p32.tv, &ppsclockev.tv);
3636 				p32.serial = ppsclockev.serial;
3637 				buf = &p32;
3638 				iocp->ioc_count = sizeof (struct ppsclockev32);
3639 			} else
3640 #endif
3641 			{
3642 				buf = &ppsclockev;
3643 				iocp->ioc_count = sizeof (struct ppsclockev);
3644 			}
3645 
3646 			if ((bp = allocb(iocp->ioc_count, BPRI_HI)) == NULL) {
3647 				error = ENOMEM;
3648 				break;
3649 			}
3650 			mp->b_cont = bp;
3651 
3652 			bcopy(buf, bp->b_wptr, iocp->ioc_count);
3653 			bp->b_wptr += iocp->ioc_count;
3654 			mp->b_datap->db_type = M_IOCACK;
3655 			break;
3656 		}
3657 
3658 		case TCSBRK:
3659 			error = miocpullup(mp, sizeof (int));
3660 			if (error != 0)
3661 				break;
3662 
3663 			if (*(int *)mp->b_cont->b_rptr == 0) {
3664 
3665 				/*
3666 				 * XXX Arrangements to ensure that a break
3667 				 * isn't in progress should be sufficient.
3668 				 * This ugly delay() is the only thing
3669 				 * that seems to work on the NCR Worldmark.
3670 				 * It should be replaced. Note that an
3671 				 * asy_waiteot() also does not work.
3672 				 */
3673 				if (asydelay)
3674 					delay(drv_usectohz(asydelay));
3675 
3676 				while (async->async_flags & ASYNC_BREAK) {
3677 					cv_wait(&async->async_flags_cv,
3678 					    &asy->asy_excl);
3679 				}
3680 				mutex_enter(&asy->asy_excl_hi);
3681 				/*
3682 				 * We loop until the TSR is empty and then
3683 				 * set the break.  ASYNC_BREAK has been set
3684 				 * to ensure that no characters are
3685 				 * transmitted while the TSR is being
3686 				 * flushed and SOUT is being used for the
3687 				 * break signal.
3688 				 *
3689 				 * The wait period is equal to
3690 				 * clock / (baud * 16) * 16 * 2.
3691 				 */
3692 				index = BAUDINDEX(
3693 				    async->async_ttycommon.t_cflag);
3694 				async->async_flags |= ASYNC_BREAK;
3695 
3696 				while ((ddi_get8(asy->asy_iohandle,
3697 				    asy->asy_ioaddr + LSR) & XSRE) == 0) {
3698 					mutex_exit(&asy->asy_excl_hi);
3699 					mutex_exit(&asy->asy_excl);
3700 					drv_usecwait(
3701 					    32*asyspdtab[index] & 0xfff);
3702 					mutex_enter(&asy->asy_excl);
3703 					mutex_enter(&asy->asy_excl_hi);
3704 				}
3705 				/*
3706 				 * Arrange for "async_restart"
3707 				 * to be called in 1/4 second;
3708 				 * it will turn the break bit off, and call
3709 				 * "async_start" to grab the next message.
3710 				 */
3711 				val = ddi_get8(asy->asy_iohandle,
3712 				    asy->asy_ioaddr + LCR);
3713 				ddi_put8(asy->asy_iohandle,
3714 				    asy->asy_ioaddr + LCR,
3715 				    (val | SETBREAK));
3716 				mutex_exit(&asy->asy_excl_hi);
3717 				(void) timeout(async_restart, (caddr_t)async,
3718 				    drv_usectohz(1000000)/4);
3719 			} else {
3720 				ASY_DPRINTF(asy, ASY_DEBUG_OUT,
3721 				    "wait for flush");
3722 				mutex_enter(&asy->asy_excl_hi);
3723 				asy_waiteot(asy);
3724 				mutex_exit(&asy->asy_excl_hi);
3725 				ASY_DPRINTF(asy, ASY_DEBUG_OUT,
3726 				    "ldterm satisfied");
3727 			}
3728 			break;
3729 
3730 		case TIOCSBRK:
3731 			if (!(async->async_flags & ASYNC_OUT_SUSPEND)) {
3732 				mutex_enter(&asy->asy_excl_hi);
3733 				async->async_flags |= ASYNC_OUT_SUSPEND;
3734 				async->async_flags |= ASYNC_HOLD_UTBRK;
3735 				index = BAUDINDEX(
3736 				    async->async_ttycommon.t_cflag);
3737 				while ((ddi_get8(asy->asy_iohandle,
3738 				    asy->asy_ioaddr + LSR) & XSRE) == 0) {
3739 					mutex_exit(&asy->asy_excl_hi);
3740 					mutex_exit(&asy->asy_excl);
3741 					drv_usecwait(
3742 					    32*asyspdtab[index] & 0xfff);
3743 					mutex_enter(&asy->asy_excl);
3744 					mutex_enter(&asy->asy_excl_hi);
3745 				}
3746 				val = ddi_get8(asy->asy_iohandle,
3747 				    asy->asy_ioaddr + LCR);
3748 				ddi_put8(asy->asy_iohandle,
3749 				    asy->asy_ioaddr + LCR, (val | SETBREAK));
3750 				mutex_exit(&asy->asy_excl_hi);
3751 				/* wait for 100ms to hold BREAK */
3752 				async->async_utbrktid =
3753 				    timeout((void (*)())async_hold_utbrk,
3754 				    (caddr_t)async,
3755 				    drv_usectohz(asy_min_utbrk));
3756 			}
3757 			mioc2ack(mp, NULL, 0, 0);
3758 			break;
3759 
3760 		case TIOCCBRK:
3761 			if (async->async_flags & ASYNC_OUT_SUSPEND)
3762 				async_resume_utbrk(async);
3763 			mioc2ack(mp, NULL, 0, 0);
3764 			break;
3765 
3766 		case TIOCMSET:
3767 		case TIOCMBIS:
3768 		case TIOCMBIC:
3769 			if (iocp->ioc_count != TRANSPARENT) {
3770 				ASY_DPRINTF(asy, ASY_DEBUG_IOCTL,
3771 				    "non-transparent");
3772 
3773 				error = miocpullup(mp, sizeof (int));
3774 				if (error != 0)
3775 					break;
3776 
3777 				mutex_enter(&asy->asy_excl_hi);
3778 				(void) asymctl(asy,
3779 				    dmtoasy(asy, *(int *)mp->b_cont->b_rptr),
3780 				    iocp->ioc_cmd);
3781 				mutex_exit(&asy->asy_excl_hi);
3782 				iocp->ioc_error = 0;
3783 				mp->b_datap->db_type = M_IOCACK;
3784 			} else {
3785 				ASY_DPRINTF(asy, ASY_DEBUG_IOCTL,
3786 				    "transparent");
3787 				mcopyin(mp, NULL, sizeof (int), NULL);
3788 			}
3789 			break;
3790 
3791 		case TIOCMGET:
3792 			datamp = allocb(sizeof (int), BPRI_MED);
3793 			if (datamp == NULL) {
3794 				error = EAGAIN;
3795 				break;
3796 			}
3797 
3798 			mutex_enter(&asy->asy_excl_hi);
3799 			*(int *)datamp->b_rptr = asymctl(asy, 0, TIOCMGET);
3800 			mutex_exit(&asy->asy_excl_hi);
3801 
3802 			if (iocp->ioc_count == TRANSPARENT) {
3803 				ASY_DPRINTF(asy, ASY_DEBUG_IOCTL,
3804 				    "transparent");
3805 				mcopyout(mp, NULL, sizeof (int), NULL, datamp);
3806 			} else {
3807 				ASY_DPRINTF(asy, ASY_DEBUG_IOCTL,
3808 				    "non-transparent");
3809 				mioc2ack(mp, datamp, sizeof (int), 0);
3810 			}
3811 			break;
3812 
3813 		case CONSOPENPOLLEDIO:
3814 			error = miocpullup(mp, sizeof (struct cons_polledio *));
3815 			if (error != 0)
3816 				break;
3817 
3818 			*(struct cons_polledio **)mp->b_cont->b_rptr =
3819 			    &asy->polledio;
3820 
3821 			mp->b_datap->db_type = M_IOCACK;
3822 			break;
3823 
3824 		case CONSCLOSEPOLLEDIO:
3825 			mp->b_datap->db_type = M_IOCACK;
3826 			iocp->ioc_error = 0;
3827 			iocp->ioc_rval = 0;
3828 			break;
3829 
3830 		case CONSSETABORTENABLE:
3831 			error = secpolicy_console(iocp->ioc_cr);
3832 			if (error != 0)
3833 				break;
3834 
3835 			if (iocp->ioc_count != TRANSPARENT) {
3836 				error = EINVAL;
3837 				break;
3838 			}
3839 
3840 			mutex_enter(&asy->asy_excl_hi);
3841 			if (*(intptr_t *)mp->b_cont->b_rptr)
3842 				asy->asy_flags |= ASY_CONSOLE;
3843 			else
3844 				asy->asy_flags &= ~ASY_CONSOLE;
3845 			mutex_exit(&asy->asy_excl_hi);
3846 
3847 			mp->b_datap->db_type = M_IOCACK;
3848 			iocp->ioc_error = 0;
3849 			iocp->ioc_rval = 0;
3850 			break;
3851 
3852 		case CONSGETABORTENABLE:
3853 			/*CONSTANTCONDITION*/
3854 			ASSERT(sizeof (boolean_t) <= sizeof (boolean_t *));
3855 			/*
3856 			 * Store the return value right in the payload
3857 			 * we were passed.  Crude.
3858 			 */
3859 			mcopyout(mp, NULL, sizeof (boolean_t), NULL, NULL);
3860 			*(boolean_t *)mp->b_cont->b_rptr =
3861 			    (asy->asy_flags & ASY_CONSOLE) != 0;
3862 			break;
3863 
3864 		default:
3865 			/*
3866 			 * If we don't understand it, it's an error.  NAK it.
3867 			 */
3868 			error = EINVAL;
3869 			break;
3870 		}
3871 	}
3872 	if (error != 0) {
3873 		iocp->ioc_error = error;
3874 		mp->b_datap->db_type = M_IOCNAK;
3875 	}
3876 	mutex_exit(&asy->asy_excl);
3877 	qreply(wq, mp);
3878 	ASY_DPRINTF(asy, ASY_DEBUG_PROCS, "done");
3879 }
3880 
3881 static int
3882 asyrsrv(queue_t *q)
3883 {
3884 	mblk_t *bp;
3885 	struct asyncline *async;
3886 	struct asycom *asy;
3887 
3888 	async = (struct asyncline *)q->q_ptr;
3889 	asy = (struct asycom *)async->async_common;
3890 
3891 	while (canputnext(q) && (bp = getq(q)))
3892 		putnext(q, bp);
3893 	mutex_enter(&asy->asy_excl_hi);
3894 	asysetsoft(asy);
3895 	mutex_exit(&asy->asy_excl_hi);
3896 	async->async_polltid = 0;
3897 	return (0);
3898 }
3899 
3900 /*
3901  * The ASYWPUTDO_NOT_SUSP macro indicates to asywputdo() whether it should
3902  * handle messages as though the driver is operating normally or is
3903  * suspended.  In the suspended case, some or all of the processing may have
3904  * to be delayed until the driver is resumed.
3905  */
3906 #define	ASYWPUTDO_NOT_SUSP(async, wput) \
3907 	!((wput) && ((async)->async_flags & ASYNC_DDI_SUSPENDED))
3908 
3909 /*
3910  * Processing for write queue put procedure.
3911  * Respond to M_STOP, M_START, M_IOCTL, and M_FLUSH messages here;
3912  * set the flow control character for M_STOPI and M_STARTI messages;
3913  * queue up M_BREAK, M_DELAY, and M_DATA messages for processing
3914  * by the start routine, and then call the start routine; discard
3915  * everything else.  Note that this driver does not incorporate any
3916  * mechanism to negotiate to handle the canonicalization process.
3917  * It expects that these functions are handled in upper module(s),
3918  * as we do in ldterm.
3919  */
3920 static int
3921 asywputdo(queue_t *q, mblk_t *mp, boolean_t wput)
3922 {
3923 	struct asyncline *async;
3924 	struct asycom *asy;
3925 	int error;
3926 
3927 	async = (struct asyncline *)q->q_ptr;
3928 	asy = async->async_common;
3929 
3930 	switch (mp->b_datap->db_type) {
3931 
3932 	case M_STOP:
3933 		/*
3934 		 * Since we don't do real DMA, we can just let the
3935 		 * chip coast to a stop after applying the brakes.
3936 		 */
3937 		mutex_enter(&asy->asy_excl);
3938 		async->async_flags |= ASYNC_STOPPED;
3939 		mutex_exit(&asy->asy_excl);
3940 		freemsg(mp);
3941 		break;
3942 
3943 	case M_START:
3944 		mutex_enter(&asy->asy_excl);
3945 		if (async->async_flags & ASYNC_STOPPED) {
3946 			async->async_flags &= ~ASYNC_STOPPED;
3947 			if (ASYWPUTDO_NOT_SUSP(async, wput)) {
3948 				/*
3949 				 * If an output operation is in progress,
3950 				 * resume it.  Otherwise, prod the start
3951 				 * routine.
3952 				 */
3953 				if (async->async_ocnt > 0) {
3954 					mutex_enter(&asy->asy_excl_hi);
3955 					async_resume(async);
3956 					mutex_exit(&asy->asy_excl_hi);
3957 				} else {
3958 					async_start(async);
3959 				}
3960 			}
3961 		}
3962 		mutex_exit(&asy->asy_excl);
3963 		freemsg(mp);
3964 		break;
3965 
3966 	case M_IOCTL:
3967 		switch (((struct iocblk *)mp->b_rptr)->ioc_cmd) {
3968 
3969 		case TCSBRK:
3970 			error = miocpullup(mp, sizeof (int));
3971 			if (error != 0) {
3972 				miocnak(q, mp, 0, error);
3973 				return (0);
3974 			}
3975 
3976 			if (*(int *)mp->b_cont->b_rptr != 0) {
3977 				ASY_DPRINTF(asy, ASY_DEBUG_OUT,
3978 				    "flush request");
3979 				(void) putq(q, mp);
3980 
3981 				mutex_enter(&asy->asy_excl);
3982 				if (ASYWPUTDO_NOT_SUSP(async, wput)) {
3983 					/*
3984 					 * If an TIOCSBRK is in progress,
3985 					 * clean it as TIOCCBRK does,
3986 					 * then kick off output.
3987 					 * If TIOCSBRK is not in progress,
3988 					 * just kick off output.
3989 					 */
3990 					async_resume_utbrk(async);
3991 				}
3992 				mutex_exit(&asy->asy_excl);
3993 				break;
3994 			}
3995 			/*FALLTHROUGH*/
3996 		case TCSETSW:
3997 		case TCSETSF:
3998 		case TCSETAW:
3999 		case TCSETAF:
4000 			/*
4001 			 * The changes do not take effect until all
4002 			 * output queued before them is drained.
4003 			 * Put this message on the queue, so that
4004 			 * "async_start" will see it when it's done
4005 			 * with the output before it.  Poke the
4006 			 * start routine, just in case.
4007 			 */
4008 			(void) putq(q, mp);
4009 
4010 			mutex_enter(&asy->asy_excl);
4011 			if (ASYWPUTDO_NOT_SUSP(async, wput)) {
4012 				/*
4013 				 * If an TIOCSBRK is in progress,
4014 				 * clean it as TIOCCBRK does.
4015 				 * then kick off output.
4016 				 * If TIOCSBRK is not in progress,
4017 				 * just kick off output.
4018 				 */
4019 				async_resume_utbrk(async);
4020 			}
4021 			mutex_exit(&asy->asy_excl);
4022 			break;
4023 
4024 		default:
4025 			/*
4026 			 * Do it now.
4027 			 */
4028 			mutex_enter(&asy->asy_excl);
4029 			if (ASYWPUTDO_NOT_SUSP(async, wput)) {
4030 				mutex_exit(&asy->asy_excl);
4031 				async_ioctl(async, q, mp);
4032 				break;
4033 			}
4034 			async_put_suspq(asy, mp);
4035 			mutex_exit(&asy->asy_excl);
4036 			break;
4037 		}
4038 		break;
4039 
4040 	case M_FLUSH:
4041 		if (*mp->b_rptr & FLUSHW) {
4042 			mutex_enter(&asy->asy_excl);
4043 
4044 			/*
4045 			 * Abort any output in progress.
4046 			 */
4047 			mutex_enter(&asy->asy_excl_hi);
4048 			if (async->async_flags & ASYNC_BUSY) {
4049 				ASY_DPRINTF(asy, ASY_DEBUG_BUSY,
4050 				    "Clearing async_ocnt, "
4051 				    "leaving ASYNC_BUSY set");
4052 				async->async_ocnt = 0;
4053 				async->async_flags &= ~ASYNC_BUSY;
4054 			} /* if */
4055 
4056 			if (ASYWPUTDO_NOT_SUSP(async, wput)) {
4057 				/* Flush FIFO buffers */
4058 				if (asy->asy_use_fifo == FIFO_ON) {
4059 					asy_reset_fifo(asy, FIFOTXFLSH);
4060 				}
4061 			}
4062 			mutex_exit(&asy->asy_excl_hi);
4063 
4064 			/* Flush FIFO buffers */
4065 			if (asy->asy_use_fifo == FIFO_ON) {
4066 				asy_reset_fifo(asy, FIFOTXFLSH);
4067 			}
4068 
4069 			/*
4070 			 * Flush our write queue.
4071 			 */
4072 			flushq(q, FLUSHDATA);	/* XXX doesn't flush M_DELAY */
4073 			if (async->async_xmitblk != NULL) {
4074 				freeb(async->async_xmitblk);
4075 				async->async_xmitblk = NULL;
4076 			}
4077 			mutex_exit(&asy->asy_excl);
4078 			*mp->b_rptr &= ~FLUSHW;	/* it has been flushed */
4079 		}
4080 		if (*mp->b_rptr & FLUSHR) {
4081 			if (ASYWPUTDO_NOT_SUSP(async, wput)) {
4082 				/* Flush FIFO buffers */
4083 				if (asy->asy_use_fifo == FIFO_ON) {
4084 					asy_reset_fifo(asy, FIFORXFLSH);
4085 				}
4086 			}
4087 			flushq(RD(q), FLUSHDATA);
4088 			qreply(q, mp);	/* give the read queues a crack at it */
4089 		} else {
4090 			freemsg(mp);
4091 		}
4092 
4093 		/*
4094 		 * We must make sure we process messages that survive the
4095 		 * write-side flush.
4096 		 */
4097 		if (ASYWPUTDO_NOT_SUSP(async, wput)) {
4098 			mutex_enter(&asy->asy_excl);
4099 			async_start(async);
4100 			mutex_exit(&asy->asy_excl);
4101 		}
4102 		break;
4103 
4104 	case M_BREAK:
4105 	case M_DELAY:
4106 	case M_DATA:
4107 		/*
4108 		 * Queue the message up to be transmitted,
4109 		 * and poke the start routine.
4110 		 */
4111 		(void) putq(q, mp);
4112 		if (ASYWPUTDO_NOT_SUSP(async, wput)) {
4113 			mutex_enter(&asy->asy_excl);
4114 			async_start(async);
4115 			mutex_exit(&asy->asy_excl);
4116 		}
4117 		break;
4118 
4119 	case M_STOPI:
4120 		mutex_enter(&asy->asy_excl);
4121 		if (ASYWPUTDO_NOT_SUSP(async, wput)) {
4122 			mutex_enter(&asy->asy_excl_hi);
4123 			if (!(async->async_inflow_source & IN_FLOW_USER)) {
4124 				async_flowcontrol_hw_input(asy, FLOW_STOP,
4125 				    IN_FLOW_USER);
4126 				(void) async_flowcontrol_sw_input(asy,
4127 				    FLOW_STOP, IN_FLOW_USER);
4128 			}
4129 			mutex_exit(&asy->asy_excl_hi);
4130 			mutex_exit(&asy->asy_excl);
4131 			freemsg(mp);
4132 			break;
4133 		}
4134 		async_put_suspq(asy, mp);
4135 		mutex_exit(&asy->asy_excl);
4136 		break;
4137 
4138 	case M_STARTI:
4139 		mutex_enter(&asy->asy_excl);
4140 		if (ASYWPUTDO_NOT_SUSP(async, wput)) {
4141 			mutex_enter(&asy->asy_excl_hi);
4142 			if (async->async_inflow_source & IN_FLOW_USER) {
4143 				async_flowcontrol_hw_input(asy, FLOW_START,
4144 				    IN_FLOW_USER);
4145 				(void) async_flowcontrol_sw_input(asy,
4146 				    FLOW_START, IN_FLOW_USER);
4147 			}
4148 			mutex_exit(&asy->asy_excl_hi);
4149 			mutex_exit(&asy->asy_excl);
4150 			freemsg(mp);
4151 			break;
4152 		}
4153 		async_put_suspq(asy, mp);
4154 		mutex_exit(&asy->asy_excl);
4155 		break;
4156 
4157 	case M_CTL:
4158 		if (MBLKL(mp) >= sizeof (struct iocblk) &&
4159 		    ((struct iocblk *)mp->b_rptr)->ioc_cmd == MC_POSIXQUERY) {
4160 			mutex_enter(&asy->asy_excl);
4161 			if (ASYWPUTDO_NOT_SUSP(async, wput)) {
4162 				((struct iocblk *)mp->b_rptr)->ioc_cmd =
4163 				    MC_HAS_POSIX;
4164 				mutex_exit(&asy->asy_excl);
4165 				qreply(q, mp);
4166 				break;
4167 			} else {
4168 				async_put_suspq(asy, mp);
4169 			}
4170 		} else {
4171 			/*
4172 			 * These MC_SERVICE type messages are used by upper
4173 			 * modules to tell this driver to send input up
4174 			 * immediately, or that it can wait for normal
4175 			 * processing that may or may not be done.  Sun
4176 			 * requires these for the mouse module.
4177 			 * (XXX - for x86?)
4178 			 */
4179 			mutex_enter(&asy->asy_excl);
4180 			switch (*mp->b_rptr) {
4181 
4182 			case MC_SERVICEIMM:
4183 				async->async_flags |= ASYNC_SERVICEIMM;
4184 				break;
4185 
4186 			case MC_SERVICEDEF:
4187 				async->async_flags &= ~ASYNC_SERVICEIMM;
4188 				break;
4189 			}
4190 			mutex_exit(&asy->asy_excl);
4191 			freemsg(mp);
4192 		}
4193 		break;
4194 
4195 	case M_IOCDATA:
4196 		mutex_enter(&asy->asy_excl);
4197 		if (ASYWPUTDO_NOT_SUSP(async, wput)) {
4198 			mutex_exit(&asy->asy_excl);
4199 			async_iocdata(q, mp);
4200 			break;
4201 		}
4202 		async_put_suspq(asy, mp);
4203 		mutex_exit(&asy->asy_excl);
4204 		break;
4205 
4206 	default:
4207 		freemsg(mp);
4208 		break;
4209 	}
4210 	return (0);
4211 }
4212 
4213 static int
4214 asywput(queue_t *q, mblk_t *mp)
4215 {
4216 	return (asywputdo(q, mp, B_TRUE));
4217 }
4218 
4219 /*
4220  * Retry an "ioctl", now that "bufcall" claims we may be able to allocate
4221  * the buffer we need.
4222  */
4223 static void
4224 async_reioctl(void *unit)
4225 {
4226 	int instance = (uintptr_t)unit;
4227 	struct asyncline *async;
4228 	struct asycom *asy;
4229 	queue_t	*q;
4230 	mblk_t	*mp;
4231 
4232 	asy = ddi_get_soft_state(asy_soft_state, instance);
4233 	ASSERT(asy != NULL);
4234 	async = asy->asy_priv;
4235 
4236 	/*
4237 	 * The bufcall is no longer pending.
4238 	 */
4239 	mutex_enter(&asy->asy_excl);
4240 	async->async_wbufcid = 0;
4241 	if ((q = async->async_ttycommon.t_writeq) == NULL) {
4242 		mutex_exit(&asy->asy_excl);
4243 		return;
4244 	}
4245 	if ((mp = async->async_ttycommon.t_iocpending) != NULL) {
4246 		/* not pending any more */
4247 		async->async_ttycommon.t_iocpending = NULL;
4248 		mutex_exit(&asy->asy_excl);
4249 		async_ioctl(async, q, mp);
4250 	} else
4251 		mutex_exit(&asy->asy_excl);
4252 }
4253 
4254 static void
4255 async_iocdata(queue_t *q, mblk_t *mp)
4256 {
4257 	struct asyncline	*async = (struct asyncline *)q->q_ptr;
4258 	struct asycom		*asy;
4259 	struct iocblk *ip;
4260 	struct copyresp *csp;
4261 #ifdef DEBUG
4262 	int instance = UNIT(async->async_dev);
4263 #endif
4264 
4265 	asy = async->async_common;
4266 	ip = (struct iocblk *)mp->b_rptr;
4267 	csp = (struct copyresp *)mp->b_rptr;
4268 
4269 	if (csp->cp_rval != 0) {
4270 		if (csp->cp_private)
4271 			freemsg(csp->cp_private);
4272 		freemsg(mp);
4273 		return;
4274 	}
4275 
4276 	mutex_enter(&asy->asy_excl);
4277 	ASY_DPRINTF(asy, ASY_DEBUG_MODEM, "case %s",
4278 	    csp->cp_cmd == TIOCMGET ? "TIOCMGET" :
4279 	    csp->cp_cmd == TIOCMSET ? "TIOCMSET" :
4280 	    csp->cp_cmd == TIOCMBIS ? "TIOCMBIS" :
4281 	    "TIOCMBIC");
4282 	switch (csp->cp_cmd) {
4283 
4284 	case TIOCMGET:
4285 		if (mp->b_cont) {
4286 			freemsg(mp->b_cont);
4287 			mp->b_cont = NULL;
4288 		}
4289 		mp->b_datap->db_type = M_IOCACK;
4290 		ip->ioc_error = 0;
4291 		ip->ioc_count = 0;
4292 		ip->ioc_rval = 0;
4293 		mp->b_wptr = mp->b_rptr + sizeof (struct iocblk);
4294 		break;
4295 
4296 	case TIOCMSET:
4297 	case TIOCMBIS:
4298 	case TIOCMBIC:
4299 		mutex_enter(&asy->asy_excl_hi);
4300 		(void) asymctl(asy, dmtoasy(asy, *(int *)mp->b_cont->b_rptr),
4301 		    csp->cp_cmd);
4302 		mutex_exit(&asy->asy_excl_hi);
4303 		mioc2ack(mp, NULL, 0, 0);
4304 		break;
4305 
4306 	default:
4307 		mp->b_datap->db_type = M_IOCNAK;
4308 		ip->ioc_error = EINVAL;
4309 		break;
4310 	}
4311 	qreply(q, mp);
4312 	mutex_exit(&asy->asy_excl);
4313 }
4314 
4315 /*
4316  * debugger/console support routines.
4317  */
4318 
4319 /*
4320  * put a character out
4321  * Do not use interrupts.  If char is LF, put out CR, LF.
4322  */
4323 static void
4324 asyputchar(cons_polledio_arg_t arg, uchar_t c)
4325 {
4326 	struct asycom *asy = (struct asycom *)arg;
4327 
4328 	if (c == '\n')
4329 		asyputchar(arg, '\r');
4330 
4331 	while ((ddi_get8(asy->asy_iohandle,
4332 	    asy->asy_ioaddr + LSR) & XHRE) == 0) {
4333 		/* wait for xmit to finish */
4334 		drv_usecwait(10);
4335 	}
4336 
4337 	/* put the character out */
4338 	ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + DAT, c);
4339 }
4340 
4341 /*
4342  * See if there's a character available. If no character is
4343  * available, return 0. Run in polled mode, no interrupts.
4344  */
4345 static boolean_t
4346 asyischar(cons_polledio_arg_t arg)
4347 {
4348 	struct asycom *asy = (struct asycom *)arg;
4349 
4350 	return ((ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR) & RCA)
4351 	    != 0);
4352 }
4353 
4354 /*
4355  * Get a character. Run in polled mode, no interrupts.
4356  */
4357 static int
4358 asygetchar(cons_polledio_arg_t arg)
4359 {
4360 	struct asycom *asy = (struct asycom *)arg;
4361 
4362 	while (!asyischar(arg))
4363 		drv_usecwait(10);
4364 	return (ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + DAT));
4365 }
4366 
4367 /*
4368  * Set or get the modem control status.
4369  */
4370 static int
4371 asymctl(struct asycom *asy, int bits, int how)
4372 {
4373 	int mcr_r, msr_r;
4374 	int instance = asy->asy_unit;
4375 
4376 	ASSERT(mutex_owned(&asy->asy_excl_hi));
4377 	ASSERT(mutex_owned(&asy->asy_excl));
4378 
4379 	/* Read Modem Control Registers */
4380 	mcr_r = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + MCR);
4381 
4382 	switch (how) {
4383 
4384 	case TIOCMSET:
4385 		ASY_DPRINTF(asy, ASY_DEBUG_MODEM, "TIOCMSET, bits = %x", bits);
4386 		mcr_r = bits;		/* Set bits	*/
4387 		break;
4388 
4389 	case TIOCMBIS:
4390 		ASY_DPRINTF(asy, ASY_DEBUG_MODEM, "TIOCMBIS, bits = %x", bits);
4391 		mcr_r |= bits;		/* Mask in bits	*/
4392 		break;
4393 
4394 	case TIOCMBIC:
4395 		ASY_DPRINTF(asy, ASY_DEBUG_MODEM, "TIOCMBIC, bits = %x", bits);
4396 		mcr_r &= ~bits;		/* Mask out bits */
4397 		break;
4398 
4399 	case TIOCMGET:
4400 		/* Read Modem Status Registers */
4401 		/*
4402 		 * If modem interrupts are enabled, we return the
4403 		 * saved value of msr. We read MSR only in async_msint()
4404 		 */
4405 		if (ddi_get8(asy->asy_iohandle,
4406 		    asy->asy_ioaddr + ICR) & MIEN) {
4407 			msr_r = asy->asy_msr;
4408 			ASY_DPRINTF(asy, ASY_DEBUG_MODEM,
4409 			    "TIOCMGET, read msr_r = %x", msr_r);
4410 		} else {
4411 			msr_r = ddi_get8(asy->asy_iohandle,
4412 			    asy->asy_ioaddr + MSR);
4413 			ASY_DPRINTF(asy, ASY_DEBUG_MODEM,
4414 			    "TIOCMGET, read MSR = %x", msr_r);
4415 		}
4416 		ASY_DPRINTF(asy, ASY_DEBUG_MODEM, "modem_lines = %x",
4417 		    asytodm(mcr_r, msr_r));
4418 		return (asytodm(mcr_r, msr_r));
4419 	}
4420 
4421 	ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR, mcr_r);
4422 
4423 	return (mcr_r);
4424 }
4425 
4426 static int
4427 asytodm(int mcr_r, int msr_r)
4428 {
4429 	int b = 0;
4430 
4431 	/* MCR registers */
4432 	if (mcr_r & RTS)
4433 		b |= TIOCM_RTS;
4434 
4435 	if (mcr_r & DTR)
4436 		b |= TIOCM_DTR;
4437 
4438 	/* MSR registers */
4439 	if (msr_r & DCD)
4440 		b |= TIOCM_CAR;
4441 
4442 	if (msr_r & CTS)
4443 		b |= TIOCM_CTS;
4444 
4445 	if (msr_r & DSR)
4446 		b |= TIOCM_DSR;
4447 
4448 	if (msr_r & RI)
4449 		b |= TIOCM_RNG;
4450 	return (b);
4451 }
4452 
4453 static int
4454 dmtoasy(struct asycom *asy, int bits)
4455 {
4456 	int b = 0;
4457 
4458 	ASY_DPRINTF(asy, ASY_DEBUG_MODEM, "bits = %x", bits);
4459 #ifdef	CAN_NOT_SET	/* only DTR and RTS can be set */
4460 	if (bits & TIOCM_CAR)
4461 		b |= DCD;
4462 	if (bits & TIOCM_CTS)
4463 		b |= CTS;
4464 	if (bits & TIOCM_DSR)
4465 		b |= DSR;
4466 	if (bits & TIOCM_RNG)
4467 		b |= RI;
4468 #endif
4469 
4470 	if (bits & TIOCM_RTS) {
4471 		ASY_DPRINTF(asy, ASY_DEBUG_MODEM, "set b & RTS");
4472 		b |= RTS;
4473 	}
4474 	if (bits & TIOCM_DTR) {
4475 		ASY_DPRINTF(asy, ASY_DEBUG_MODEM, "set b & DTR");
4476 		b |= DTR;
4477 	}
4478 
4479 	return (b);
4480 }
4481 
4482 static void
4483 asyerror(struct asycom *asy, int level, const char *fmt, ...)
4484 {
4485 	va_list adx;
4486 	static	time_t	last;
4487 	static	const char *lastfmt;
4488 	time_t	now;
4489 
4490 	/*
4491 	 * Don't print the same error message too often.
4492 	 * Print the message only if we have not printed the
4493 	 * message within the last second.
4494 	 * Note: that fmt cannot be a pointer to a string
4495 	 * stored on the stack. The fmt pointer
4496 	 * must be in the data segment otherwise lastfmt would point
4497 	 * to non-sense.
4498 	 */
4499 	now = gethrestime_sec();
4500 	if (last == now && lastfmt == fmt)
4501 		return;
4502 
4503 	last = now;
4504 	lastfmt = fmt;
4505 
4506 	va_start(adx, fmt);
4507 	vdev_err(asy->asy_dip, level, fmt, adx);
4508 	va_end(adx);
4509 }
4510 
4511 /*
4512  * asy_parse_mode(dev_info_t *devi, struct asycom *asy)
4513  * The value of this property is in the form of "9600,8,n,1,-"
4514  * 1) speed: 9600, 4800, ...
4515  * 2) data bits
4516  * 3) parity: n(none), e(even), o(odd)
4517  * 4) stop bits
4518  * 5) handshake: -(none), h(hardware: rts/cts), s(software: xon/off)
4519  *
4520  * This parsing came from a SPARCstation eeprom.
4521  */
4522 static void
4523 asy_parse_mode(dev_info_t *devi, struct asycom *asy)
4524 {
4525 	char		name[40];
4526 	char		val[40];
4527 	int		len;
4528 	int		ret;
4529 	char		*p;
4530 	char		*p1;
4531 
4532 	ASSERT(asy->asy_com_port != 0);
4533 
4534 	/*
4535 	 * Parse the ttyx-mode property
4536 	 */
4537 	(void) sprintf(name, "tty%c-mode", asy->asy_com_port + 'a' - 1);
4538 	len = sizeof (val);
4539 	ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, &len);
4540 	if (ret != DDI_PROP_SUCCESS) {
4541 		(void) sprintf(name, "com%c-mode", asy->asy_com_port + '0');
4542 		len = sizeof (val);
4543 		ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, &len);
4544 	}
4545 
4546 	/* no property to parse */
4547 	asy->asy_cflag = 0;
4548 	if (ret != DDI_PROP_SUCCESS)
4549 		return;
4550 
4551 	p = val;
4552 	/* ---- baud rate ---- */
4553 	asy->asy_cflag = CREAD|B9600;		/* initial default */
4554 	if (p && (p1 = strchr(p, ',')) != 0) {
4555 		*p1++ = '\0';
4556 	} else {
4557 		asy->asy_cflag |= BITS8;	/* add default bits */
4558 		return;
4559 	}
4560 
4561 	if (strcmp(p, "110") == 0)
4562 		asy->asy_bidx = B110;
4563 	else if (strcmp(p, "150") == 0)
4564 		asy->asy_bidx = B150;
4565 	else if (strcmp(p, "300") == 0)
4566 		asy->asy_bidx = B300;
4567 	else if (strcmp(p, "600") == 0)
4568 		asy->asy_bidx = B600;
4569 	else if (strcmp(p, "1200") == 0)
4570 		asy->asy_bidx = B1200;
4571 	else if (strcmp(p, "2400") == 0)
4572 		asy->asy_bidx = B2400;
4573 	else if (strcmp(p, "4800") == 0)
4574 		asy->asy_bidx = B4800;
4575 	else if (strcmp(p, "9600") == 0)
4576 		asy->asy_bidx = B9600;
4577 	else if (strcmp(p, "19200") == 0)
4578 		asy->asy_bidx = B19200;
4579 	else if (strcmp(p, "38400") == 0)
4580 		asy->asy_bidx = B38400;
4581 	else if (strcmp(p, "57600") == 0)
4582 		asy->asy_bidx = B57600;
4583 	else if (strcmp(p, "115200") == 0)
4584 		asy->asy_bidx = B115200;
4585 	else
4586 		asy->asy_bidx = B9600;
4587 
4588 	asy->asy_cflag &= ~CBAUD;
4589 	if (asy->asy_bidx > CBAUD) {	/* > 38400 uses the CBAUDEXT bit */
4590 		asy->asy_cflag |= CBAUDEXT;
4591 		asy->asy_cflag |= asy->asy_bidx - CBAUD - 1;
4592 	} else {
4593 		asy->asy_cflag |= asy->asy_bidx;
4594 	}
4595 
4596 	ASSERT(asy->asy_bidx == BAUDINDEX(asy->asy_cflag));
4597 
4598 	/* ---- Next item is data bits ---- */
4599 	p = p1;
4600 	if (p && (p1 = strchr(p, ',')) != 0)  {
4601 		*p1++ = '\0';
4602 	} else {
4603 		asy->asy_cflag |= BITS8;	/* add default bits */
4604 		return;
4605 	}
4606 	switch (*p) {
4607 		default:
4608 		case '8':
4609 			asy->asy_cflag |= CS8;
4610 			asy->asy_lcr = BITS8;
4611 			break;
4612 		case '7':
4613 			asy->asy_cflag |= CS7;
4614 			asy->asy_lcr = BITS7;
4615 			break;
4616 		case '6':
4617 			asy->asy_cflag |= CS6;
4618 			asy->asy_lcr = BITS6;
4619 			break;
4620 		case '5':
4621 			/* LINTED: CS5 is currently zero (but might change) */
4622 			asy->asy_cflag |= CS5;
4623 			asy->asy_lcr = BITS5;
4624 			break;
4625 	}
4626 
4627 	/* ---- Parity info ---- */
4628 	p = p1;
4629 	if (p && (p1 = strchr(p, ',')) != 0)  {
4630 		*p1++ = '\0';
4631 	} else {
4632 		return;
4633 	}
4634 	switch (*p)  {
4635 		default:
4636 		case 'n':
4637 			break;
4638 		case 'e':
4639 			asy->asy_cflag |= PARENB;
4640 			asy->asy_lcr |= PEN; break;
4641 		case 'o':
4642 			asy->asy_cflag |= PARENB|PARODD;
4643 			asy->asy_lcr |= PEN|EPS;
4644 			break;
4645 	}
4646 
4647 	/* ---- Find stop bits ---- */
4648 	p = p1;
4649 	if (p && (p1 = strchr(p, ',')) != 0)  {
4650 		*p1++ = '\0';
4651 	} else {
4652 		return;
4653 	}
4654 	if (*p == '2') {
4655 		asy->asy_cflag |= CSTOPB;
4656 		asy->asy_lcr |= STB;
4657 	}
4658 
4659 	/* ---- handshake is next ---- */
4660 	p = p1;
4661 	if (p) {
4662 		if ((p1 = strchr(p, ',')) != 0)
4663 			*p1++ = '\0';
4664 
4665 		if (*p == 'h')
4666 			asy->asy_cflag |= CRTSCTS;
4667 		else if (*p == 's')
4668 			asy->asy_cflag |= CRTSXOFF;
4669 	}
4670 }
4671 
4672 /*
4673  * Check for abort character sequence
4674  */
4675 static boolean_t
4676 abort_charseq_recognize(uchar_t ch)
4677 {
4678 	static int state = 0;
4679 #define	CNTRL(c) ((c)&037)
4680 	static char sequence[] = { '\r', '~', CNTRL('b') };
4681 
4682 	if (ch == sequence[state]) {
4683 		if (++state >= sizeof (sequence)) {
4684 			state = 0;
4685 			return (B_TRUE);
4686 		}
4687 	} else {
4688 		state = (ch == sequence[0]) ? 1 : 0;
4689 	}
4690 	return (B_FALSE);
4691 }
4692 
4693 /*
4694  * Flow control functions
4695  */
4696 /*
4697  * Software input flow control
4698  * This function can execute software input flow control sucessfully
4699  * at most of situations except that the line is in BREAK status
4700  * (timed and untimed break).
4701  * INPUT VALUE of onoff:
4702  *               FLOW_START means to send out a XON char
4703  *                          and clear SW input flow control flag.
4704  *               FLOW_STOP means to send out a XOFF char
4705  *                          and set SW input flow control flag.
4706  *               FLOW_CHECK means to check whether there is pending XON/XOFF
4707  *                          if it is true, send it out.
4708  * INPUT VALUE of type:
4709  *		 IN_FLOW_RINGBUFF means flow control is due to RING BUFFER
4710  *		 IN_FLOW_STREAMS means flow control is due to STREAMS
4711  *		 IN_FLOW_USER means flow control is due to user's commands
4712  * RETURN VALUE: B_FALSE means no flow control char is sent
4713  *               B_TRUE means one flow control char is sent
4714  */
4715 static boolean_t
4716 async_flowcontrol_sw_input(struct asycom *asy, async_flowc_action onoff,
4717     int type)
4718 {
4719 	struct asyncline *async = asy->asy_priv;
4720 	int instance = UNIT(async->async_dev);
4721 	int rval = B_FALSE;
4722 
4723 	ASSERT(mutex_owned(&asy->asy_excl_hi));
4724 
4725 	if (!(async->async_ttycommon.t_iflag & IXOFF))
4726 		return (rval);
4727 
4728 	/*
4729 	 * If we get this far, then we know IXOFF is set.
4730 	 */
4731 	switch (onoff) {
4732 	case FLOW_STOP:
4733 		async->async_inflow_source |= type;
4734 
4735 		/*
4736 		 * We'll send an XOFF character for each of up to
4737 		 * three different input flow control attempts to stop input.
4738 		 * If we already send out one XOFF, but FLOW_STOP comes again,
4739 		 * it seems that input flow control becomes more serious,
4740 		 * then send XOFF again.
4741 		 */
4742 		if (async->async_inflow_source & (IN_FLOW_RINGBUFF |
4743 		    IN_FLOW_STREAMS | IN_FLOW_USER))
4744 			async->async_flags |= ASYNC_SW_IN_FLOW |
4745 			    ASYNC_SW_IN_NEEDED;
4746 		ASY_DPRINTF(asy, ASY_DEBUG_SFLOW, "input sflow stop, type = %x",
4747 		    async->async_inflow_source);
4748 		break;
4749 	case FLOW_START:
4750 		async->async_inflow_source &= ~type;
4751 		if (async->async_inflow_source == 0) {
4752 			async->async_flags = (async->async_flags &
4753 			    ~ASYNC_SW_IN_FLOW) | ASYNC_SW_IN_NEEDED;
4754 			ASY_DPRINTF(asy, ASY_DEBUG_SFLOW, "input sflow start");
4755 		}
4756 		break;
4757 	default:
4758 		break;
4759 	}
4760 
4761 	if (((async->async_flags & (ASYNC_SW_IN_NEEDED | ASYNC_BREAK |
4762 	    ASYNC_OUT_SUSPEND)) == ASYNC_SW_IN_NEEDED) &&
4763 	    (ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR) & XHRE)) {
4764 		/*
4765 		 * If we get this far, then we know we need to send out
4766 		 * XON or XOFF char.
4767 		 */
4768 		async->async_flags = (async->async_flags &
4769 		    ~ASYNC_SW_IN_NEEDED) | ASYNC_BUSY;
4770 		ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + DAT,
4771 		    async->async_flags & ASYNC_SW_IN_FLOW ?
4772 		    async->async_stopc : async->async_startc);
4773 		rval = B_TRUE;
4774 	}
4775 	return (rval);
4776 }
4777 
4778 /*
4779  * Software output flow control
4780  * This function can be executed sucessfully at any situation.
4781  * It does not handle HW, and just change the SW output flow control flag.
4782  * INPUT VALUE of onoff:
4783  *                 FLOW_START means to clear SW output flow control flag,
4784  *			also combine with HW output flow control status to
4785  *			determine if we need to set ASYNC_OUT_FLW_RESUME.
4786  *                 FLOW_STOP means to set SW output flow control flag,
4787  *			also clear ASYNC_OUT_FLW_RESUME.
4788  */
4789 static void
4790 async_flowcontrol_sw_output(struct asycom *asy, async_flowc_action onoff)
4791 {
4792 	struct asyncline *async = asy->asy_priv;
4793 	int instance = UNIT(async->async_dev);
4794 
4795 	ASSERT(mutex_owned(&asy->asy_excl_hi));
4796 
4797 	if (!(async->async_ttycommon.t_iflag & IXON))
4798 		return;
4799 
4800 	switch (onoff) {
4801 	case FLOW_STOP:
4802 		async->async_flags |= ASYNC_SW_OUT_FLW;
4803 		async->async_flags &= ~ASYNC_OUT_FLW_RESUME;
4804 		ASY_DPRINTF(asy, ASY_DEBUG_SFLOW, "output sflow stop");
4805 		break;
4806 	case FLOW_START:
4807 		async->async_flags &= ~ASYNC_SW_OUT_FLW;
4808 		if (!(async->async_flags & ASYNC_HW_OUT_FLW))
4809 			async->async_flags |= ASYNC_OUT_FLW_RESUME;
4810 		ASY_DPRINTF(asy, ASY_DEBUG_SFLOW, "output sflow start");
4811 		break;
4812 	default:
4813 		break;
4814 	}
4815 }
4816 
4817 /*
4818  * Hardware input flow control
4819  * This function can be executed sucessfully at any situation.
4820  * It directly changes RTS depending on input parameter onoff.
4821  * INPUT VALUE of onoff:
4822  *       FLOW_START means to clear HW input flow control flag,
4823  *                  and pull up RTS if it is low.
4824  *       FLOW_STOP means to set HW input flow control flag,
4825  *                  and low RTS if it is high.
4826  * INPUT VALUE of type:
4827  *		 IN_FLOW_RINGBUFF means flow control is due to RING BUFFER
4828  *		 IN_FLOW_STREAMS means flow control is due to STREAMS
4829  *		 IN_FLOW_USER means flow control is due to user's commands
4830  */
4831 static void
4832 async_flowcontrol_hw_input(struct asycom *asy, async_flowc_action onoff,
4833     int type)
4834 {
4835 	uchar_t	mcr;
4836 	uchar_t	flag;
4837 	struct asyncline *async = asy->asy_priv;
4838 	int instance = UNIT(async->async_dev);
4839 
4840 	ASSERT(mutex_owned(&asy->asy_excl_hi));
4841 
4842 	if (!(async->async_ttycommon.t_cflag & CRTSXOFF))
4843 		return;
4844 
4845 	switch (onoff) {
4846 	case FLOW_STOP:
4847 		async->async_inflow_source |= type;
4848 		if (async->async_inflow_source & (IN_FLOW_RINGBUFF |
4849 		    IN_FLOW_STREAMS | IN_FLOW_USER))
4850 			async->async_flags |= ASYNC_HW_IN_FLOW;
4851 		ASY_DPRINTF(asy, ASY_DEBUG_HFLOW, "input hflow stop, type = %x",
4852 		    async->async_inflow_source);
4853 		break;
4854 	case FLOW_START:
4855 		async->async_inflow_source &= ~type;
4856 		if (async->async_inflow_source == 0) {
4857 			async->async_flags &= ~ASYNC_HW_IN_FLOW;
4858 			ASY_DPRINTF(asy, ASY_DEBUG_HFLOW, "input hflow start");
4859 		}
4860 		break;
4861 	default:
4862 		break;
4863 	}
4864 	mcr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + MCR);
4865 	flag = (async->async_flags & ASYNC_HW_IN_FLOW) ? 0 : RTS;
4866 
4867 	if (((mcr ^ flag) & RTS) != 0) {
4868 		ddi_put8(asy->asy_iohandle,
4869 		    asy->asy_ioaddr + MCR, (mcr ^ RTS));
4870 	}
4871 }
4872 
4873 /*
4874  * Hardware output flow control
4875  * This function can execute HW output flow control sucessfully
4876  * at any situation.
4877  * It doesn't really change RTS, and just change
4878  * HW output flow control flag depending on CTS status.
4879  * INPUT VALUE of onoff:
4880  *                FLOW_START means to clear HW output flow control flag.
4881  *			also combine with SW output flow control status to
4882  *			determine if we need to set ASYNC_OUT_FLW_RESUME.
4883  *                FLOW_STOP means to set HW output flow control flag.
4884  *			also clear ASYNC_OUT_FLW_RESUME.
4885  */
4886 static void
4887 async_flowcontrol_hw_output(struct asycom *asy, async_flowc_action onoff)
4888 {
4889 	struct asyncline *async = asy->asy_priv;
4890 	int instance = UNIT(async->async_dev);
4891 
4892 	ASSERT(mutex_owned(&asy->asy_excl_hi));
4893 
4894 	if (!(async->async_ttycommon.t_cflag & CRTSCTS))
4895 		return;
4896 
4897 	switch (onoff) {
4898 	case FLOW_STOP:
4899 		async->async_flags |= ASYNC_HW_OUT_FLW;
4900 		async->async_flags &= ~ASYNC_OUT_FLW_RESUME;
4901 		ASY_DPRINTF(asy, ASY_DEBUG_HFLOW, "output hflow stop");
4902 		break;
4903 	case FLOW_START:
4904 		async->async_flags &= ~ASYNC_HW_OUT_FLW;
4905 		if (!(async->async_flags & ASYNC_SW_OUT_FLW))
4906 			async->async_flags |= ASYNC_OUT_FLW_RESUME;
4907 		ASY_DPRINTF(asy, ASY_DEBUG_HFLOW, "output hflow start");
4908 		break;
4909 	default:
4910 		break;
4911 	}
4912 }
4913 
4914 
4915 /*
4916  * quiesce(9E) entry point.
4917  *
4918  * This function is called when the system is single-threaded at high
4919  * PIL with preemption disabled. Therefore, this function must not be
4920  * blocked.
4921  *
4922  * This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure.
4923  * DDI_FAILURE indicates an error condition and should almost never happen.
4924  */
4925 static int
4926 asyquiesce(dev_info_t *devi)
4927 {
4928 	int instance;
4929 	struct asycom *asy;
4930 
4931 	instance = ddi_get_instance(devi);	/* find out which unit */
4932 
4933 	asy = ddi_get_soft_state(asy_soft_state, instance);
4934 	if (asy == NULL)
4935 		return (DDI_FAILURE);
4936 
4937 	/* disable all interrupts */
4938 	ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 0);
4939 
4940 	/* reset the FIFO */
4941 	asy_reset_fifo(asy, FIFOTXFLSH | FIFORXFLSH);
4942 
4943 	return (DDI_SUCCESS);
4944 }
4945