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