xref: /illumos-gate/usr/src/uts/sun4v/io/qcn.c (revision ec16669a)
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 /*
23  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 
27 /*
28  * sun4v console driver
29  */
30 
31 #include <sys/errno.h>
32 #include <sys/stat.h>
33 #include <sys/kmem.h>
34 #include <sys/conf.h>
35 #include <sys/termios.h>
36 #include <sys/modctl.h>
37 #include <sys/kbio.h>
38 #include <sys/stropts.h>
39 #include <sys/stream.h>
40 #include <sys/strsun.h>
41 #include <sys/sysmacros.h>
42 #include <sys/promif.h>
43 #include <sys/ddi.h>
44 #include <sys/sunddi.h>
45 #include <sys/cyclic.h>
46 #include <sys/intr.h>
47 #include <sys/spl.h>
48 #include <sys/qcn.h>
49 #include <sys/hypervisor_api.h>
50 #include <sys/hsvc.h>
51 #include <sys/machsystm.h>
52 #include <sys/consdev.h>
53 
54 /* dev_ops and cb_ops for device driver */
55 static int qcn_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
56 static int qcn_attach(dev_info_t *, ddi_attach_cmd_t);
57 static int qcn_detach(dev_info_t *, ddi_detach_cmd_t);
58 static int qcn_open(queue_t *, dev_t *, int, int, cred_t *);
59 static int qcn_close(queue_t *, int, cred_t *);
60 static int qcn_wput(queue_t *, mblk_t *);
61 static int qcn_wsrv(queue_t *);
62 static int qcn_rsrv(queue_t *);
63 
64 /* other internal qcn routines */
65 static void qcn_ioctl(queue_t *, mblk_t *);
66 static void qcn_reioctl(void *);
67 static void qcn_ack(mblk_t *, mblk_t *, uint_t);
68 static void qcn_start(void);
69 static int qcn_transmit_write(queue_t *, mblk_t *);
70 static int qcn_transmit_putchr(queue_t *, mblk_t *);
71 static void qcn_receive_read(void);
72 static void qcn_receive_getchr(void);
73 static void qcn_flush(void);
74 static uint_t qcn_hi_intr(caddr_t arg);
75 static uint_t qcn_soft_intr(caddr_t arg1, caddr_t arg2);
76 
77 /* functions required for polled io */
78 static boolean_t qcn_polledio_ischar(cons_polledio_arg_t arg);
79 static int qcn_polledio_getchar(cons_polledio_arg_t arg);
80 static void qcn_polledio_putchar(cons_polledio_arg_t arg, uchar_t c);
81 static void qcn_polledio_enter(cons_polledio_arg_t arg);
82 static void qcn_polledio_exit(cons_polledio_arg_t arg);
83 
84 
85 static boolean_t abort_charseq_recognize(uchar_t);
86 
87 static qcn_t *qcn_state;
88 static uchar_t qcn_stopped = B_FALSE;
89 static int qcn_timeout_period = 20;	/* time out in seconds */
90 size_t qcn_input_dropped;	/* dropped input character counter */
91 
92 #ifdef QCN_POLLING
93 static void qcn_poll_handler(void *unused);
94 static cyc_time_t qcn_poll_time;
95 static cyc_handler_t qcn_poll_cychandler = {
96 	qcn_poll_handler,
97 	NULL,
98 	CY_LOW_LEVEL		/* XXX need softint to make this high */
99 };
100 static cyclic_id_t qcn_poll_cycid = CYCLIC_NONE;
101 static uint64_t	qcn_poll_interval = 5;  /* milli sec */
102 static uint64_t sb_interval = 0;
103 uint_t qcn_force_polling = 0;
104 #endif
105 
106 #define	QCN_MI_IDNUM		0xABCE
107 #define	QCN_MI_HIWAT		8192
108 #define	QCN_MI_LOWAT		128
109 
110 /* streams structures */
111 static struct module_info minfo = {
112 	QCN_MI_IDNUM,	/* mi_idnum		*/
113 	"qcn",		/* mi_idname		*/
114 	0,		/* mi_minpsz		*/
115 	INFPSZ,		/* mi_maxpsz		*/
116 	QCN_MI_HIWAT,	/* mi_hiwat		*/
117 	QCN_MI_LOWAT	/* mi_lowat		*/
118 };
119 
120 static struct qinit rinit = {
121 	putq,		/* qi_putp		*/
122 	qcn_rsrv,	/* qi_srvp		*/
123 	qcn_open,	/* qi_qopen		*/
124 	qcn_close,	/* qi_qclose		*/
125 	NULL,		/* qi_qadmin		*/
126 	&minfo,		/* qi_minfo		*/
127 	NULL		/* qi_mstat		*/
128 };
129 
130 static struct qinit winit = {
131 	qcn_wput,	/* qi_putp		*/
132 	qcn_wsrv,	/* qi_srvp		*/
133 	qcn_open,	/* qi_qopen		*/
134 	qcn_close,	/* qi_qclose		*/
135 	NULL,		/* qi_qadmin		*/
136 	&minfo,		/* qi_minfo		*/
137 	NULL		/* qi_mstat		*/
138 };
139 
140 static struct streamtab qcnstrinfo = {
141 	&rinit,
142 	&winit,
143 	NULL,
144 	NULL
145 };
146 
147 /* standard device driver structures */
148 static struct cb_ops qcn_cb_ops = {
149 	nulldev,		/* open()		*/
150 	nulldev,		/* close()		*/
151 	nodev,			/* strategy()		*/
152 	nodev,			/* print()		*/
153 	nodev,			/* dump()		*/
154 	nodev,			/* read()		*/
155 	nodev,			/* write()		*/
156 	nodev,			/* ioctl()		*/
157 	nodev,			/* devmap()		*/
158 	nodev,			/* mmap()		*/
159 	nodev,			/* segmap()		*/
160 	nochpoll,		/* poll()		*/
161 	ddi_prop_op,		/* prop_op()		*/
162 	&qcnstrinfo,		/* cb_str		*/
163 	D_NEW | D_MP		/* cb_flag		*/
164 };
165 
166 static struct dev_ops qcn_ops = {
167 	DEVO_REV,
168 	0,			/* refcnt		*/
169 	qcn_getinfo,		/* getinfo()		*/
170 	nulldev,		/* identify()		*/
171 	nulldev,		/* probe()		*/
172 	qcn_attach,		/* attach()		*/
173 	qcn_detach,		/* detach()		*/
174 	nodev,			/* reset()		*/
175 	&qcn_cb_ops,		/* cb_ops		*/
176 	(struct bus_ops *)NULL,	/* bus_ops		*/
177 	NULL,			/* power()		*/
178 	ddi_quiesce_not_needed,		/* quiesce()		*/
179 };
180 
181 static struct modldrv modldrv = {
182 	&mod_driverops,
183 	"sun4v console driver",
184 	&qcn_ops
185 };
186 
187 static struct modlinkage modlinkage = {
188 	MODREV_1,
189 	(void*)&modldrv,
190 	NULL
191 };
192 
193 /* driver configuration routines */
194 int
_init(void)195 _init(void)
196 {
197 	int error;
198 	uint64_t	major, minor;
199 
200 	qcn_state = kmem_zalloc(sizeof (qcn_t), KM_SLEEP);
201 	qcn_state->qcn_ring = contig_mem_alloc(RINGSIZE);
202 	if (qcn_state->qcn_ring == NULL)
203 		cmn_err(CE_PANIC, "console ring allocation failed");
204 
205 	error = mod_install(&modlinkage);
206 	if (error != 0) {
207 		contig_mem_free(qcn_state->qcn_ring, RINGSIZE);
208 		kmem_free(qcn_state, sizeof (qcn_t));
209 		return (error);
210 	}
211 	/*
212 	 * check minor number to see if CONS_WRITE is supported
213 	 * if so, set up real address of the buffers for hv calls.
214 	 */
215 
216 	if (((hsvc_version(HSVC_GROUP_CORE, &major, &minor) == 0) &&
217 	    (major == QCN_API_MAJOR) && (minor >= QCN_API_MINOR))) {
218 		qcn_state->cons_write_buffer =
219 		    contig_mem_alloc(CONS_WR_BUF_SIZE);
220 		if (qcn_state->cons_write_buffer != NULL) {
221 			qcn_state->cons_write_buf_ra =
222 			    va_to_pa(qcn_state->cons_write_buffer);
223 			qcn_state->cons_transmit = qcn_transmit_write;
224 			qcn_state->cons_receive = qcn_receive_read;
225 			qcn_state->cons_read_buf_ra =
226 			    va_to_pa((char *)RING_ADDR(qcn_state));
227 		}
228 	}
229 	if (qcn_state->cons_transmit == NULL) {
230 		qcn_state->cons_transmit = qcn_transmit_putchr;
231 		qcn_state->cons_receive = qcn_receive_getchr;
232 	}
233 	return (0);
234 }
235 
236 int
_fini(void)237 _fini(void)
238 {
239 	/* can't remove console driver */
240 	return (EBUSY);
241 }
242 
243 int
_info(struct modinfo * modinfop)244 _info(struct modinfo *modinfop)
245 {
246 	return (mod_info(&modlinkage, modinfop));
247 }
248 
249 static int
qcn_add_intrs(void)250 qcn_add_intrs(void)
251 {
252 	dev_info_t	*devinfo = qcn_state->qcn_dip;
253 	int		actual, count = 0;
254 	int 		x, y, rc, inum = 0;
255 
256 
257 	/* get number of interrupts */
258 	rc = ddi_intr_get_nintrs(devinfo, DDI_INTR_TYPE_FIXED, &count);
259 	if ((rc != DDI_SUCCESS) || (count == 0)) {
260 		return (DDI_FAILURE);
261 	}
262 
263 	/* Allocate an array of interrupt handles */
264 	qcn_state->qcn_intr_size = count * sizeof (ddi_intr_handle_t);
265 	qcn_state->qcn_htable = kmem_zalloc(qcn_state->qcn_intr_size, KM_SLEEP);
266 
267 	/* call ddi_intr_alloc() */
268 	rc = ddi_intr_alloc(devinfo, qcn_state->qcn_htable,
269 	    DDI_INTR_TYPE_FIXED, inum, count, &actual,
270 	    DDI_INTR_ALLOC_STRICT);
271 
272 	if ((rc != DDI_SUCCESS) || (actual == 0)) {
273 		kmem_free(qcn_state->qcn_htable, qcn_state->qcn_intr_size);
274 		return (DDI_FAILURE);
275 	}
276 
277 	if (actual < count) {
278 		for (x = 0; x < actual; x++) {
279 			(void) ddi_intr_free(qcn_state->qcn_htable[x]);
280 		}
281 
282 		kmem_free(qcn_state->qcn_htable, qcn_state->qcn_intr_size);
283 		return (DDI_FAILURE);
284 	}
285 
286 	qcn_state->qcn_intr_cnt = actual;
287 
288 	/* Get intr priority */
289 	if (ddi_intr_get_pri(qcn_state->qcn_htable[0],
290 	    &qcn_state->qcn_intr_pri) != DDI_SUCCESS) {
291 		for (x = 0; x < actual; x++) {
292 			(void) ddi_intr_free(qcn_state->qcn_htable[x]);
293 		}
294 
295 		kmem_free(qcn_state->qcn_htable, qcn_state->qcn_intr_size);
296 		return (DDI_FAILURE);
297 	}
298 
299 	/* Call ddi_intr_add_handler() */
300 	for (x = 0; x < actual; x++) {
301 		if (ddi_intr_add_handler(qcn_state->qcn_htable[x],
302 		    (ddi_intr_handler_t *)qcn_hi_intr,
303 		    (caddr_t)qcn_state, NULL) != DDI_SUCCESS) {
304 
305 			for (y = 0; y < x; y++) {
306 				(void) ddi_intr_remove_handler(
307 				    qcn_state->qcn_htable[y]);
308 			}
309 
310 			for (y = 0; y < actual; y++) {
311 				(void) ddi_intr_free(qcn_state->qcn_htable[y]);
312 			}
313 
314 			kmem_free(qcn_state->qcn_htable,
315 			    qcn_state->qcn_intr_size);
316 			return (DDI_FAILURE);
317 		}
318 	}
319 
320 	return (DDI_SUCCESS);
321 }
322 
323 static void
qcn_remove_intrs(void)324 qcn_remove_intrs(void)
325 {
326 	int x;
327 	for (x = 0; x < qcn_state->qcn_intr_cnt; x++) {
328 		(void) ddi_intr_disable(qcn_state->qcn_htable[x]);
329 		(void) ddi_intr_remove_handler(qcn_state->qcn_htable[x]);
330 		(void) ddi_intr_free(qcn_state->qcn_htable[x]);
331 	}
332 	kmem_free(qcn_state->qcn_htable, qcn_state->qcn_intr_size);
333 }
334 
335 static void
qcn_intr_enable(void)336 qcn_intr_enable(void)
337 {
338 	int x;
339 
340 	for (x = 0; x < qcn_state->qcn_intr_cnt; x++) {
341 		(void) ddi_intr_enable(qcn_state->qcn_htable[x]);
342 	}
343 }
344 
345 /*
346  * qcn_attach is called at startup time.
347  * There is only once instance of this driver.
348  */
349 static int
qcn_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)350 qcn_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
351 {
352 	extern int ddi_create_internal_pathname(dev_info_t *, char *,
353 	    int, minor_t);
354 	uint_t soft_prip;
355 
356 #ifdef QCN_POLLING
357 	char *binding_name;
358 #endif
359 	if (cmd != DDI_ATTACH)
360 		return (DDI_FAILURE);
361 
362 	if (ddi_create_internal_pathname(dip, "qcn",
363 	    S_IFCHR, 0) != DDI_SUCCESS)
364 		return (DDI_FAILURE);
365 
366 	qcn_state->qcn_soft_pend = 0;
367 	qcn_state->qcn_hangup = 0;
368 	qcn_state->qcn_rbuf_overflow = 0;
369 
370 	/* prepare some data structures in soft state */
371 
372 	qcn_state->qcn_dip = dip;
373 
374 	qcn_state->qcn_polling = 0;
375 
376 #ifdef QCN_POLLING
377 	/*
378 	 * This test is for the sole purposes of allowing
379 	 * the console to work on older firmware releases.
380 	 */
381 	binding_name = ddi_binding_name(qcn_state->qcn_dip);
382 	if ((strcmp(binding_name, "qcn") == 0) ||
383 	    (qcn_force_polling))
384 		qcn_state->qcn_polling = 1;
385 
386 	if (qcn_state->qcn_polling) {
387 		qcn_poll_time.cyt_when = 0ull;
388 		qcn_poll_time.cyt_interval =
389 		    qcn_poll_interval * 1000ull * 1000ull;
390 		mutex_enter(&cpu_lock);
391 		qcn_poll_cycid = cyclic_add(&qcn_poll_cychandler,
392 		    &qcn_poll_time);
393 		mutex_exit(&cpu_lock);
394 	}
395 #endif
396 
397 	if (!qcn_state->qcn_polling) {
398 		if (qcn_add_intrs() != DDI_SUCCESS) {
399 			cmn_err(CE_WARN, "qcn_attach: add_intr failed\n");
400 			return (DDI_FAILURE);
401 		}
402 		if (ddi_intr_add_softint(dip, &qcn_state->qcn_softint_hdl,
403 		    DDI_INTR_SOFTPRI_MAX, qcn_soft_intr,
404 		    (caddr_t)qcn_state) != DDI_SUCCESS) {
405 			cmn_err(CE_WARN, "qcn_attach: add_soft_intr failed\n");
406 			qcn_remove_intrs();
407 			return (DDI_FAILURE);
408 		}
409 		if (ddi_intr_get_softint_pri(qcn_state->qcn_softint_hdl,
410 		    &soft_prip) != DDI_SUCCESS) {
411 			cmn_err(CE_WARN, "qcn_attach: softint_pri failed\n");
412 			(void) ddi_intr_remove_softint(
413 			    qcn_state->qcn_softint_hdl);
414 			qcn_remove_intrs();
415 			return (DDI_FAILURE);
416 		}
417 
418 	mutex_init(&qcn_state->qcn_hi_lock, NULL, MUTEX_DRIVER,
419 	    (void *)(uintptr_t)(qcn_state->qcn_intr_pri));
420 	}
421 
422 	mutex_init(&qcn_state->qcn_lock, NULL, MUTEX_DRIVER, NULL);
423 
424 	/*
425 	 * Fill in the polled I/O structure.
426 	 */
427 	qcn_state->qcn_polledio.cons_polledio_version = CONSPOLLEDIO_V1;
428 	qcn_state->qcn_polledio.cons_polledio_argument =
429 	    (cons_polledio_arg_t)qcn_state;
430 	qcn_state->qcn_polledio.cons_polledio_putchar = qcn_polledio_putchar;
431 	qcn_state->qcn_polledio.cons_polledio_getchar = qcn_polledio_getchar;
432 	qcn_state->qcn_polledio.cons_polledio_ischar = qcn_polledio_ischar;
433 	qcn_state->qcn_polledio.cons_polledio_enter = qcn_polledio_enter;
434 	qcn_state->qcn_polledio.cons_polledio_exit = qcn_polledio_exit;
435 
436 	/*
437 	 *  Enable  interrupts
438 	 */
439 	if (!qcn_state->qcn_polling) {
440 		qcn_intr_enable();
441 	}
442 #ifdef QCN_DEBUG
443 	prom_printf("qcn_attach(): qcn driver attached\n");
444 #endif
445 
446 	return (DDI_SUCCESS);
447 
448 }
449 
450 /* ARGSUSED */
451 static int
qcn_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)452 qcn_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
453 {
454 
455 	if (cmd != DDI_DETACH)
456 		return (DDI_FAILURE);
457 
458 
459 #ifdef QCN_DEBUG
460 	prom_printf("qcn_detach(): QCN driver detached\n");
461 #endif
462 
463 #ifdef QCN_POLLING
464 	if (qcn_state->qcn_polling) {
465 		mutex_enter(&cpu_lock);
466 		if (qcn_poll_cycid != CYCLIC_NONE)
467 			cyclic_remove(qcn_poll_cycid);
468 		qcn_poll_cycid = CYCLIC_NONE;
469 		mutex_exit(&cpu_lock);
470 	}
471 #endif
472 
473 	if (!qcn_state->qcn_polling)
474 		qcn_remove_intrs();
475 
476 	return (DDI_SUCCESS);
477 }
478 
479 /* ARGSUSED */
480 static int
qcn_getinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)481 qcn_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
482 {
483 	int error = DDI_FAILURE;
484 	int instance = 0;
485 	switch (infocmd) {
486 	case DDI_INFO_DEVT2DEVINFO:
487 		if (qcn_state) {
488 #ifdef QCN_DEBUG
489 			prom_printf("qcn_getinfo(): devt2dip %lx\n", arg);
490 #endif
491 			*result = (void *)qcn_state->qcn_dip;
492 			error = DDI_SUCCESS;
493 		}
494 		break;
495 
496 	case DDI_INFO_DEVT2INSTANCE:
497 #ifdef QCN_DEBUG
498 		prom_printf("qcn_getinfo(): devt2instance %lx\n", arg);
499 #endif
500 		if (getminor((dev_t)arg) == 0) {
501 			*result = (void *)(uintptr_t)instance;
502 			error = DDI_SUCCESS;
503 		}
504 		break;
505 	}
506 
507 	return (error);
508 }
509 
510 /* streams open & close */
511 /* ARGSUSED */
512 static int
qcn_open(queue_t * q,dev_t * devp,int oflag,int sflag,cred_t * credp)513 qcn_open(queue_t *q, dev_t *devp, int oflag, int sflag, cred_t *credp)
514 {
515 	tty_common_t *tty;
516 	int unit = getminor(*devp);
517 
518 #ifdef QCN_DEBUG
519 	prom_printf("qcn_open(): minor %x\n", unit);
520 #endif
521 
522 	if (unit != 0)
523 		return (ENXIO);
524 
525 	/* stream already open */
526 	if (q->q_ptr != NULL)
527 		return (DDI_SUCCESS);
528 
529 	if (!qcn_state) {
530 		cmn_err(CE_WARN, "qcn_open: console was not configured by "
531 		    "autoconfig\n");
532 		return (ENXIO);
533 	}
534 
535 	mutex_enter(&qcn_state->qcn_lock);
536 	tty = &(qcn_state->qcn_tty);
537 
538 	tty->t_readq = q;
539 	tty->t_writeq = WR(q);
540 
541 	/* Link the RD and WR Q's */
542 	q->q_ptr = WR(q)->q_ptr = (caddr_t)qcn_state;
543 	qcn_state->qcn_readq = RD(q);
544 	qcn_state->qcn_writeq = WR(q);
545 	qprocson(q);
546 
547 	mutex_exit(&qcn_state->qcn_lock);
548 
549 #ifdef QCN_DEBUG
550 	prom_printf("qcn_open: opened as dev %lx\n", *devp);
551 #endif
552 
553 	return (DDI_SUCCESS);
554 }
555 
556 /* ARGSUSED */
557 static int
qcn_close(queue_t * q,int flag,cred_t * credp)558 qcn_close(queue_t *q, int flag, cred_t *credp)
559 {
560 
561 	ASSERT(qcn_state == q->q_ptr);
562 
563 	if (qcn_state->qcn_wbufcid != 0) {
564 		unbufcall(qcn_state->qcn_wbufcid);
565 	}
566 	ttycommon_close(&qcn_state->qcn_tty);
567 
568 	qprocsoff(q);
569 	q->q_ptr = WR(q)->q_ptr = NULL;
570 	qcn_state->qcn_readq = NULL;
571 	qcn_state->qcn_writeq = NULL;
572 
573 	return (DDI_SUCCESS);
574 }
575 
576 /*
577  * Put procedure for write queue.
578  * Respond to M_IOCTL, M_DATA and M_FLUSH messages here;
579  * It put's the data onto internal qcn_output_q.
580  */
581 static int
qcn_wput(queue_t * q,mblk_t * mp)582 qcn_wput(queue_t *q, mblk_t *mp)
583 {
584 
585 #ifdef QCN_DEBUG
586 	struct iocblk *iocp;
587 	int i;
588 #endif
589 
590 	ASSERT(qcn_state == q->q_ptr);
591 
592 	if (!mp->b_datap) {
593 		cmn_err(CE_PANIC, "qcn_wput: null datap");
594 	}
595 
596 #ifdef QCN_DEBUG
597 	prom_printf("qcn_wput(): QCN wput q=%X mp=%X rd=%X wr=%X type=%X\n",
598 	    q, mp, mp->b_rptr, mp->b_wptr, mp->b_datap->db_type);
599 #endif
600 
601 	mutex_enter(&qcn_state->qcn_lock);
602 
603 	switch (mp->b_datap->db_type) {
604 	case M_IOCTL:
605 	case M_CTL:
606 #ifdef QCN_DEBUG
607 		iocp = (struct iocblk *)mp->b_rptr;
608 		prom_printf("qcn_wput(): M_IOCTL cmd=%X TIOC=%X\n",
609 		    iocp->ioc_cmd, TIOC);
610 #endif
611 		switch (((struct iocblk *)mp->b_rptr)->ioc_cmd) {
612 		case TCSETSW:
613 		case TCSETSF:
614 		case TCSETAW:
615 		case TCSETAF:
616 		case TCSBRK:
617 			/*
618 			 * The change do not take effect until all
619 			 * output queued before them is drained.
620 			 * Put this message on the queue, so that
621 			 * "qcn_start" will see it when it's done
622 			 * with the output before it. Poke the start
623 			 * routine, just in case.
624 			 */
625 			(void) putq(q, mp);
626 			qcn_start();
627 			break;
628 		default:
629 			mutex_exit(&qcn_state->qcn_lock);
630 			qcn_ioctl(q, mp);
631 			mutex_enter(&qcn_state->qcn_lock);
632 		}
633 		break;
634 
635 	case M_FLUSH:
636 		if (*mp->b_rptr & FLUSHW) {
637 			flushq(q, FLUSHDATA);
638 			*mp->b_rptr &= ~FLUSHW;
639 		}
640 		if (*mp->b_rptr & FLUSHR) {
641 			flushq(RD(q), FLUSHDATA);
642 			qreply(q, mp);
643 		} else {
644 			freemsg(mp);
645 		}
646 		break;
647 
648 	case M_STOP:
649 		qcn_stopped = B_TRUE;
650 		freemsg(mp);
651 		break;
652 
653 	case M_START:
654 		qcn_stopped = B_FALSE;
655 		freemsg(mp);
656 		qenable(q);	/* Start up delayed messages */
657 		break;
658 
659 	case M_DATA:
660 		/*
661 		 * Queue the message up to be transmitted,
662 		 * and poke the start routine.
663 		 */
664 #ifdef QCN_DEBUG
665 		if (mp->b_rptr < mp->b_wptr) {
666 		prom_printf("qcn_wput(): DATA q=%X mp=%X rd=%X wr=%X\n",
667 		    q, mp, mp->b_rptr, mp->b_wptr);
668 		prom_printf("qcn_wput(): [");
669 		for (i = 0; i < mp->b_wptr-mp->b_rptr; i++) {
670 			prom_printf("%c", *(mp->b_rptr+i));
671 		}
672 		prom_printf("]\n");
673 		}
674 #endif /* QCN_DEBUG */
675 		(void) putq(q, mp);
676 		qcn_start();
677 		break;
678 
679 	default:
680 		freemsg(mp);
681 	}
682 
683 	mutex_exit(&qcn_state->qcn_lock);
684 
685 	return (0);
686 }
687 
688 /*
689  * Process an "ioctl" message sent down to us.
690  */
691 static void
qcn_ioctl(queue_t * q,mblk_t * mp)692 qcn_ioctl(queue_t *q, mblk_t *mp)
693 {
694 	struct iocblk	*iocp;
695 	tty_common_t	*tty;
696 	mblk_t		*datamp;
697 	int		data_size;
698 	int		error = 0;
699 
700 #ifdef QCN_DEBUG
701 	prom_printf("qcn_ioctl(): q=%X mp=%X\n", q, mp);
702 #endif
703 
704 	iocp = (struct iocblk *)mp->b_rptr;
705 
706 	tty = &(qcn_state->qcn_tty);
707 
708 	if (tty->t_iocpending != NULL) {
709 		freemsg(tty->t_iocpending);
710 		tty->t_iocpending = NULL;
711 	}
712 
713 	/*
714 	 * Handle the POLLEDIO ioctls now because ttycommon_ioctl
715 	 * (below) frees up the message block (mp->b_cont) which
716 	 * contains the pointer used to pass back results.
717 	 */
718 	switch (iocp->ioc_cmd) {
719 	case CONSOPENPOLLEDIO:
720 		error = miocpullup(mp, sizeof (struct cons_polledio *));
721 		if (error != 0)
722 			break;
723 
724 		*(struct cons_polledio **)mp->b_cont->b_rptr =
725 		    &qcn_state->qcn_polledio;
726 
727 		mp->b_datap->db_type = M_IOCACK;
728 		break;
729 
730 	case CONSCLOSEPOLLEDIO:
731 		mp->b_datap->db_type = M_IOCACK;
732 		iocp->ioc_error = 0;
733 		iocp->ioc_rval = 0;
734 		break;
735 
736 	default:
737 		data_size = ttycommon_ioctl(tty, q, mp, &error);
738 		if (data_size != 0) {
739 			if (qcn_state->qcn_wbufcid)
740 				unbufcall(qcn_state->qcn_wbufcid);
741 			/* call qcn_reioctl() */
742 			qcn_state->qcn_wbufcid =
743 			    bufcall(data_size, BPRI_HI, qcn_reioctl, qcn_state);
744 			return;
745 		}
746 	}
747 
748 	mutex_enter(&qcn_state->qcn_lock);
749 
750 	if (error < 0) {
751 		iocp = (struct iocblk *)mp->b_rptr;
752 		/*
753 		 * "ttycommon_ioctl" didn't do anything; we process it here.
754 		 */
755 		error = 0;
756 		switch (iocp->ioc_cmd) {
757 		case TCSBRK:
758 		case TIOCSBRK:
759 		case TIOCCBRK:
760 		case TIOCMSET:
761 		case TIOCMBIS:
762 		case TIOCMBIC:
763 			if (iocp->ioc_count != TRANSPARENT)
764 				qcn_ack(mp, NULL, 0);
765 			else
766 				mcopyin(mp, NULL, sizeof (int), NULL);
767 			break;
768 
769 		case TIOCMGET:
770 			datamp = allocb(sizeof (int), BPRI_MED);
771 			if (datamp == NULL) {
772 				error = EAGAIN;
773 				break;
774 			}
775 
776 			*(int *)datamp->b_rptr = 0;
777 
778 			if (iocp->ioc_count != TRANSPARENT)
779 				qcn_ack(mp, datamp, sizeof (int));
780 			else
781 				mcopyout(mp, NULL, sizeof (int), NULL, datamp);
782 			break;
783 
784 		default:
785 			error = EINVAL;
786 			break;
787 		}
788 	}
789 	if (error != 0) {
790 		iocp->ioc_count = 0;
791 		iocp->ioc_error = error;
792 		mp->b_datap->db_type = M_IOCNAK;
793 	}
794 	mutex_exit(&qcn_state->qcn_lock);
795 	qreply(q, mp);
796 }
797 
798 static void
qcn_reioctl(void * unit)799 qcn_reioctl(void *unit)
800 {
801 	queue_t		*q;
802 	mblk_t		*mp;
803 	qcn_t		*qcnp = (qcn_t *)unit;
804 
805 	if (!qcnp->qcn_wbufcid)
806 		return;
807 
808 	qcnp->qcn_wbufcid = 0;
809 	if ((q = qcnp->qcn_tty.t_writeq) == NULL)
810 		return;
811 
812 	if ((mp = qcnp->qcn_tty.t_iocpending) == NULL)
813 		return;
814 
815 	qcnp->qcn_tty.t_iocpending = NULL;
816 	qcn_ioctl(q, mp);
817 }
818 
819 static void
qcn_ack(mblk_t * mp,mblk_t * dp,uint_t size)820 qcn_ack(mblk_t *mp, mblk_t *dp, uint_t size)
821 {
822 	struct iocblk  *iocp = (struct iocblk *)mp->b_rptr;
823 
824 	mp->b_datap->db_type = M_IOCACK;
825 	iocp->ioc_count = size;
826 	iocp->ioc_error = 0;
827 	iocp->ioc_rval = 0;
828 	if (mp->b_cont != NULL)
829 		freeb(mp->b_cont);
830 	if (dp != NULL) {
831 		mp->b_cont = dp;
832 		dp->b_wptr += size;
833 	} else
834 		mp->b_cont = NULL;
835 }
836 
837 static void
qcn_start(void)838 qcn_start(void)
839 {
840 
841 	queue_t *q;
842 	mblk_t *mp;
843 	int rv;
844 
845 	ASSERT(MUTEX_HELD(&qcn_state->qcn_lock));
846 
847 	/*
848 	 * read stream queue and remove data from the queue and
849 	 * transmit them if possible
850 	 */
851 	q = qcn_state->qcn_writeq;
852 	ASSERT(q != NULL);
853 	while (mp = getq(q)) {
854 		if (mp->b_datap->db_type == M_IOCTL) {
855 			/*
856 			 * These are those IOCTLs queued up
857 			 * do it now
858 			 */
859 			mutex_exit(&qcn_state->qcn_lock);
860 			qcn_ioctl(q, mp);
861 			mutex_enter(&qcn_state->qcn_lock);
862 			continue;
863 		}
864 		/*
865 		 * M_DATA
866 		 */
867 		rv = qcn_state->cons_transmit(q, mp);
868 		if (rv == EBUSY || rv == EAGAIN)
869 			return;
870 	}
871 }
872 
873 static int
qcn_transmit_write(queue_t * q,mblk_t * mp)874 qcn_transmit_write(queue_t *q, mblk_t *mp)
875 {
876 	mblk_t		*bp;
877 	size_t		len;
878 	uint64_t	i;
879 	uint64_t	retval = 0;
880 
881 #ifdef QCN_DEBUG
882 	prom_printf("qcn_transmit_write(): q=%X mp=%X\n", q, mp);
883 #endif
884 
885 	while (mp) {
886 		bp = mp;
887 		len = bp->b_wptr - bp->b_rptr;
888 		/*
889 		 * Use the console write call to send a block of characters to
890 		 * the console.
891 		 */
892 		i = (len > CONS_WR_BUF_SIZE) ? CONS_WR_BUF_SIZE : len;
893 		bcopy(bp->b_rptr, qcn_state->cons_write_buffer, i);
894 		retval = hv_cnwrite(qcn_state->cons_write_buf_ra, i, &i);
895 
896 		if (retval == H_EOK) {
897 			len -= i;
898 			bp->b_rptr += i;
899 			/*
900 			 * if we have finished with this buf, free
901 			 * and get the next buf if present.
902 			 */
903 			if (len == 0) {
904 				mp = bp->b_cont;
905 				freeb(bp);
906 			}
907 		} else {
908 			(void) putbq(q, mp);
909 
910 			switch (retval) {
911 
912 			case H_EWOULDBLOCK :
913 				/*
914 				 * hypervisor cannot process the request -
915 				 * channel busy.  Try again later.
916 				 */
917 				return (EAGAIN);
918 
919 			case H_EIO :
920 				return (EIO);
921 			default :
922 				return (ENXIO);
923 			}
924 		}
925 	}
926 	return (0);
927 }
928 
929 static int
qcn_transmit_putchr(queue_t * q,mblk_t * mp)930 qcn_transmit_putchr(queue_t *q, mblk_t *mp)
931 {
932 	caddr_t		buf;
933 	mblk_t		*bp;
934 	size_t		len;
935 	uint64_t	i;
936 
937 #ifdef QCN_DEBUG
938 	prom_printf("qcn_transmit_putchr(): q=%X mp=%X\n", q, mp);
939 #endif
940 	while (mp) {
941 		bp = mp;
942 		len = bp->b_wptr - bp->b_rptr;
943 		buf = (caddr_t)bp->b_rptr;
944 		for (i = 0; i < len; i++) {
945 			if (hv_cnputchar(buf[i]) == H_EWOULDBLOCK)
946 				break;
947 		}
948 		if (i != len) {
949 			bp->b_rptr += i;
950 			(void) putbq(q, mp);
951 			return (EAGAIN);
952 		}
953 		mp = bp->b_cont;
954 		freeb(bp);
955 	}
956 	return (0);
957 }
958 
959 /*
960  * called when SC first establishes console connection
961  * drop all the data on the output queue
962  */
963 static void
qcn_flush(void)964 qcn_flush(void)
965 {
966 	queue_t *q;
967 	mblk_t *mp;
968 
969 	ASSERT(MUTEX_HELD(&qcn_state->qcn_lock));
970 
971 	q = qcn_state->qcn_writeq;
972 
973 	prom_printf("qcn_flush(): WARNING console output is dropped time=%lx\n",
974 	    gethrestime_sec());
975 	while (mp = getq(q))
976 		freemsg(mp);
977 }
978 
979 static void
qcn_trigger_softint(void)980 qcn_trigger_softint(void)
981 {
982 	/*
983 	 * if we are not currently servicing a software interrupt
984 	 * (qcn_soft_pend == 0), trigger the service routine to run.
985 	 */
986 	if (atomic_swap_uint(&qcn_state->qcn_soft_pend, QCN_SP_DO) ==
987 	    QCN_SP_IDL) {
988 		(void) ddi_intr_trigger_softint(
989 		    qcn_state->qcn_softint_hdl, NULL);
990 	}
991 }
992 
993 /*ARGSUSED*/
994 static uint_t
qcn_soft_intr(caddr_t arg1,caddr_t arg2)995 qcn_soft_intr(caddr_t arg1, caddr_t arg2)
996 {
997 	mblk_t *mp;
998 	int	cc;
999 	int	overflow_check;
1000 
1001 	do {
1002 		(void) atomic_swap_uint(&qcn_state->qcn_soft_pend, QCN_SP_IP);
1003 		mutex_enter(&qcn_state->qcn_hi_lock);
1004 		cc = RING_CNT(qcn_state);
1005 		mutex_exit(&qcn_state->qcn_hi_lock);
1006 		if (cc <= 0) {
1007 			goto out;
1008 		}
1009 
1010 		if ((mp = allocb(cc, BPRI_MED)) == NULL) {
1011 			mutex_enter(&qcn_state->qcn_hi_lock);
1012 			qcn_input_dropped += cc;
1013 			mutex_exit(&qcn_state->qcn_hi_lock);
1014 			cmn_err(CE_WARN, "qcn_intr: allocb"
1015 			    "failed (console input dropped)");
1016 			goto out;
1017 		}
1018 
1019 		mutex_enter(&qcn_state->qcn_hi_lock);
1020 		do {
1021 			/* put console input onto stream */
1022 			*(char *)mp->b_wptr++ = RING_GET(qcn_state);
1023 		} while (--cc);
1024 
1025 		if ((overflow_check = qcn_state->qcn_rbuf_overflow) != 0) {
1026 			qcn_state->qcn_rbuf_overflow = 0;
1027 		}
1028 		mutex_exit(&qcn_state->qcn_hi_lock);
1029 
1030 		if (overflow_check) {
1031 			cmn_err(CE_WARN, "qcn: Ring buffer overflow\n");
1032 		}
1033 
1034 		if (qcn_state->qcn_readq) {
1035 			putnext(qcn_state->qcn_readq, mp);
1036 		}
1037 out:
1038 		/*
1039 		 * If there are pending transmits because hypervisor
1040 		 * returned EWOULDBLOCK poke start now.
1041 		 */
1042 
1043 		if (qcn_state->qcn_writeq != NULL) {
1044 			if (qcn_state->qcn_hangup) {
1045 				(void) putctl(qcn_state->qcn_readq, M_HANGUP);
1046 				flushq(qcn_state->qcn_writeq, FLUSHDATA);
1047 				qcn_state->qcn_hangup = 0;
1048 			} else {
1049 				mutex_enter(&qcn_state->qcn_lock);
1050 				qcn_start();
1051 				mutex_exit(&qcn_state->qcn_lock);
1052 			}
1053 		}
1054 		/*
1055 		 * now loop if another interrupt came in (qcn_trigger_softint
1056 		 * called) while we were processing the loop
1057 		 */
1058 	} while (atomic_swap_uint(&qcn_state->qcn_soft_pend, QCN_SP_IDL) ==
1059 	    QCN_SP_DO);
1060 	return (DDI_INTR_CLAIMED);
1061 }
1062 
1063 /*ARGSUSED*/
1064 static uint_t
qcn_hi_intr(caddr_t arg)1065 qcn_hi_intr(caddr_t arg)
1066 {
1067 	mutex_enter(&qcn_state->qcn_hi_lock);
1068 
1069 	qcn_state->cons_receive();
1070 
1071 	mutex_exit(&qcn_state->qcn_hi_lock);
1072 	qcn_trigger_softint();
1073 
1074 	return (DDI_INTR_CLAIMED);
1075 }
1076 
1077 static void
qcn_receive_read(void)1078 qcn_receive_read(void)
1079 {
1080 	int64_t rv;
1081 	uint8_t *bufp;
1082 	int64_t	retcount = 0;
1083 	int	i;
1084 
1085 	do {
1086 		/*
1087 		 * Maximize available buffer size
1088 		 */
1089 		if (RING_CNT(qcn_state) <= 0) {
1090 			RING_INIT(qcn_state);
1091 		}
1092 		rv = hv_cnread(qcn_state->cons_read_buf_ra +
1093 		    RING_POFF(qcn_state),
1094 		    RING_LEFT(qcn_state),
1095 		    &retcount);
1096 		bufp = RING_ADDR(qcn_state);
1097 		if (rv == H_EOK) {
1098 			/*
1099 			 * if the alternate break sequence is enabled, test
1100 			 * the buffer for the sequence and if it is there,
1101 			 * enter the debugger.
1102 			 */
1103 			if (abort_enable == KIOCABORTALTERNATE) {
1104 				for (i = 0; i < retcount; i++) {
1105 					if (abort_charseq_recognize(*bufp++)) {
1106 						abort_sequence_enter(
1107 						    (char *)NULL);
1108 					}
1109 				}
1110 			}
1111 
1112 			/* put console input onto stream */
1113 			if (retcount > 0) {
1114 				/*
1115 				 * the characters are already in the ring,
1116 				 * just update the pointer so the characters
1117 				 * can be retrieved.
1118 				 */
1119 				RING_UPD(qcn_state, retcount);
1120 			}
1121 		} else {
1122 			switch (rv) {
1123 
1124 			case H_EWOULDBLOCK :
1125 				/*
1126 				 * hypervisor cannot handle the request.
1127 				 * Try again later.
1128 				 */
1129 				break;
1130 
1131 
1132 			case H_BREAK :
1133 				/*
1134 				 * on break enter the debugger
1135 				 */
1136 				if (abort_enable != KIOCABORTALTERNATE)
1137 					abort_sequence_enter((char *)NULL);
1138 				break;
1139 
1140 			case H_HUP :
1141 				qcn_state->qcn_hangup = 1;
1142 				break;
1143 
1144 			default :
1145 				break;
1146 			}
1147 		}
1148 	} while (rv == H_EOK);
1149 }
1150 
1151 static void
qcn_receive_getchr(void)1152 qcn_receive_getchr(void)
1153 {
1154 	int64_t rv;
1155 	uint8_t	buf;
1156 
1157 	do {
1158 		rv = hv_cngetchar(&buf);
1159 		if (rv == H_EOK) {
1160 			if (abort_enable == KIOCABORTALTERNATE) {
1161 				if (abort_charseq_recognize(buf)) {
1162 					abort_sequence_enter((char *)NULL);
1163 				}
1164 			}
1165 
1166 			/* put console input onto stream */
1167 			if (RING_POK(qcn_state, 1)) {
1168 				RING_PUT(qcn_state, buf);
1169 			} else {
1170 				qcn_state->qcn_rbuf_overflow++;
1171 			}
1172 		} else {
1173 			if (rv == H_BREAK) {
1174 				if (abort_enable != KIOCABORTENABLE)
1175 					abort_sequence_enter((char *)NULL);
1176 			}
1177 
1178 			if (rv == H_HUP)  {
1179 				qcn_state->qcn_hangup = 1;
1180 			}
1181 			return;
1182 		}
1183 	} while (rv == H_EOK);
1184 }
1185 
1186 #ifdef QCN_POLLING
1187 /*ARGSUSED*/
1188 static void
qcn_poll_handler(void * unused)1189 qcn_poll_handler(void *unused)
1190 {
1191 	mblk_t *mp;
1192 	int64_t rv;
1193 	uint8_t buf;
1194 	int qcn_writeq_flush = 0;
1195 
1196 	/* LINTED: E_CONSTANT_CONDITION */
1197 	while (1) {
1198 		rv = hv_cngetchar(&buf);
1199 		if (rv == H_BREAK) {
1200 			if (abort_enable != KIOCABORTALTERNATE)
1201 				abort_sequence_enter((char *)NULL);
1202 		}
1203 
1204 		if (rv == H_HUP)  {
1205 			if (qcn_state->qcn_readq) {
1206 				(void) putctl(qcn_state->qcn_readq, M_HANGUP);
1207 				qcn_writeq_flush = 1;
1208 			}
1209 			goto out;
1210 		}
1211 
1212 		if (rv != H_EOK)
1213 			goto out;
1214 
1215 		if (abort_enable == KIOCABORTALTERNATE) {
1216 			if (abort_charseq_recognize(buf)) {
1217 				abort_sequence_enter((char *)NULL);
1218 			}
1219 		}
1220 
1221 		/* put console input onto stream */
1222 		if (qcn_state->qcn_readq) {
1223 			if ((mp = allocb(1, BPRI_MED)) == NULL) {
1224 				qcn_input_dropped++;
1225 				cmn_err(CE_WARN, "qcn_intr: allocb"
1226 				    "failed (console input dropped)");
1227 				return;
1228 			}
1229 			*(char *)mp->b_wptr++ = buf;
1230 			putnext(qcn_state->qcn_readq, mp);
1231 		}
1232 	}
1233 out:
1234 /*
1235  * If there are pending transmits because hypervisor
1236  * returned EWOULDBLOCK poke start now.
1237  */
1238 
1239 	mutex_enter(&qcn_state->qcn_lock);
1240 	if (qcn_state->qcn_writeq != NULL) {
1241 		if (qcn_writeq_flush) {
1242 			flushq(qcn_state->qcn_writeq, FLUSHDATA);
1243 		} else {
1244 			qcn_start();
1245 		}
1246 	}
1247 	mutex_exit(&qcn_state->qcn_lock);
1248 }
1249 #endif
1250 
1251 /*
1252  * Check for abort character sequence, copied from zs_async.c
1253  */
1254 #define	CNTRL(c) ((c)&037)
1255 
1256 static boolean_t
abort_charseq_recognize(uchar_t ch)1257 abort_charseq_recognize(uchar_t ch)
1258 {
1259 	static int state = 0;
1260 	static char sequence[] = { '\r', '~', CNTRL('b') };
1261 
1262 	if (ch == sequence[state]) {
1263 		if (++state >= sizeof (sequence)) {
1264 			state = 0;
1265 			return (B_TRUE);
1266 		}
1267 	} else {
1268 		state = (ch == sequence[0]) ? 1 : 0;
1269 	}
1270 	return (B_FALSE);
1271 }
1272 
1273 
1274 static int
qcn_rsrv(queue_t * q)1275 qcn_rsrv(queue_t *q)
1276 {
1277 	mblk_t	*mp;
1278 
1279 	if (qcn_stopped == B_TRUE)
1280 		return (0);
1281 
1282 	mutex_enter(&qcn_state->qcn_lock);
1283 
1284 	while ((mp = getq(q)) != NULL) {
1285 		if (canputnext(q))
1286 			putnext(q, mp);
1287 		else if (mp->b_datap->db_type >= QPCTL)
1288 			(void) putbq(q, mp);
1289 	}
1290 
1291 	mutex_exit(&qcn_state->qcn_lock);
1292 
1293 	return (0);
1294 }
1295 
1296 /* ARGSUSED */
1297 static int
qcn_wsrv(queue_t * q)1298 qcn_wsrv(queue_t *q)
1299 {
1300 	if (qcn_stopped == B_TRUE)
1301 		return (0);
1302 
1303 	mutex_enter(&qcn_state->qcn_lock);
1304 
1305 	if (qcn_state->qcn_writeq != NULL)
1306 		qcn_start();
1307 
1308 	mutex_exit(&qcn_state->qcn_lock);
1309 
1310 	return (0);
1311 }
1312 
1313 static boolean_t
qcn_polledio_ischar(cons_polledio_arg_t arg)1314 qcn_polledio_ischar(cons_polledio_arg_t arg)
1315 {
1316 	qcn_t *state = (qcn_t *)arg;
1317 
1318 	if (state->qcn_char_available)
1319 		return (B_TRUE);
1320 
1321 	return (state->qcn_char_available =
1322 	    (hv_cngetchar(&state->qcn_hold_char) == H_EOK));
1323 }
1324 
1325 
1326 static int
qcn_polledio_getchar(cons_polledio_arg_t arg)1327 qcn_polledio_getchar(cons_polledio_arg_t arg)
1328 {
1329 	qcn_t *state = (qcn_t *)arg;
1330 
1331 	while (!qcn_polledio_ischar(arg))
1332 		drv_usecwait(10);
1333 
1334 	state->qcn_char_available = B_FALSE;
1335 
1336 	return ((int)state->qcn_hold_char);
1337 }
1338 
1339 static void
qcn_polledio_putchar(cons_polledio_arg_t arg,uchar_t c)1340 qcn_polledio_putchar(cons_polledio_arg_t arg, uchar_t c)
1341 {
1342 	if (c == '\n')
1343 		qcn_polledio_putchar(arg, '\r');
1344 
1345 	while (hv_cnputchar((uint8_t)c) == H_EWOULDBLOCK)
1346 		drv_usecwait(10);
1347 }
1348 
1349 static void
qcn_polledio_enter(cons_polledio_arg_t arg)1350 qcn_polledio_enter(cons_polledio_arg_t arg)
1351 {
1352 	qcn_t *state = (qcn_t *)arg;
1353 
1354 	state->qcn_char_available = B_FALSE;
1355 }
1356 
1357 /* ARGSUSED */
1358 static void
qcn_polledio_exit(cons_polledio_arg_t arg)1359 qcn_polledio_exit(cons_polledio_arg_t arg)
1360 {
1361 }
1362