xref: /illumos-gate/usr/src/uts/sun4u/serengeti/io/sgcn.c (revision b7b0558ae6cf66a2c72f08f9104c1559d962bc84)
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 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 
28 /*
29  * Serengeti console driver, see sys/sgcn.h for more information
30  * This driver uses the QPAIR form of STREAMS Perimeters to serialize access
31  * to the read and write STREAMS queues.
32  */
33 
34 #include <sys/errno.h>
35 #include <sys/stat.h>
36 #include <sys/kmem.h>
37 #include <sys/conf.h>
38 #include <sys/termios.h>
39 #include <sys/modctl.h>
40 #include <sys/kbio.h>
41 #include <sys/stropts.h>
42 #include <sys/stream.h>
43 #include <sys/strsun.h>
44 #include <sys/sysmacros.h>
45 #include <sys/promif.h>
46 #include <sys/prom_plat.h>
47 #include <sys/sgsbbc.h>
48 #include <sys/sgsbbc_iosram.h>
49 #include <sys/sgcn.h>
50 #include <sys/serengeti.h>
51 #include <sys/ddi.h>
52 #include <sys/sunddi.h>
53 #include <sys/strsubr.h>
54 
55 /*
56  * Here we define several macros for accessing console IOSRAM
57  */
58 
59 #define	POINTER(base, field)	((caddr_t)&base.field)
60 #define	OFFSETOF(base, field)	((caddr_t)&base.field - (caddr_t)&base)
61 
62 #define	RW_CONSOLE_READ		0xAAAA
63 #define	RW_CONSOLE_WRITE	0xBBBB
64 
65 #define	CONSOLE_READ(buf, len)	sgcn_rw(RW_CONSOLE_READ, buf, len)
66 #define	CONSOLE_WRITE(buf, len)	sgcn_rw(RW_CONSOLE_WRITE, buf, len)
67 
68 #define	SGCN_MI_IDNUM		0xABCD
69 #define	SGCN_MI_HIWAT		2048*2048
70 #define	SGCN_MI_LOWAT		128
71 
72 /* dev_ops and cb_ops for device driver */
73 static int sgcn_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
74 static int sgcn_attach(dev_info_t *, ddi_attach_cmd_t);
75 static int sgcn_detach(dev_info_t *, ddi_detach_cmd_t);
76 static int sgcn_open(queue_t *, dev_t *, int, int, cred_t *);
77 static int sgcn_close(queue_t *, int, cred_t *);
78 static int sgcn_wput(queue_t *, mblk_t *);
79 static int sgcn_wsrv(queue_t *);
80 static int sgcn_rsrv(queue_t *);
81 
82 /* interrupt handlers */
83 static void sgcn_data_in_handler(caddr_t);
84 static void sgcn_space_2_out_handler(caddr_t);
85 static void sgcn_break_handler(caddr_t);
86 
87 /* other internal sgcn routines */
88 static void sgcn_ioctl(queue_t *, mblk_t *);
89 static void sgcn_reioctl(void *);
90 static void sgcn_start(void);
91 static int sgcn_transmit(queue_t *, mblk_t *);
92 static void sgcn_flush(void);
93 static int sgcn_read_header(int, cnsram_header *);
94 static int sgcn_rw(int, caddr_t, int);
95 static void sgcn_log_error(int, int);
96 
97 /* circular buffer routines */
98 static int circular_buffer_write(int, int, int, int, caddr_t, int);
99 static int circular_buffer_read(int, int, int, int, caddr_t, int);
100 
101 static boolean_t abort_charseq_recognize(uchar_t);
102 static void sg_abort_seq_handler(char *);
103 
104 static	sgcn_t *sgcn_state;
105 static uchar_t	sgcn_stopped = FALSE;
106 static int	sgcn_timeout_period = 20;	/* time out in seconds */
107 
108 /* streams structures */
109 static struct module_info minfo = {
110 	SGCN_MI_IDNUM,	/* mi_idnum		*/
111 	"sgcn",		/* mi_idname		*/
112 	0,		/* mi_minpsz		*/
113 	INFPSZ,		/* mi_maxpsz		*/
114 	SGCN_MI_HIWAT,	/* mi_hiwat		*/
115 	SGCN_MI_LOWAT	/* mi_lowat		*/
116 };
117 
118 static struct qinit rinit = {
119 	putq,		/* qi_putp		*/
120 	sgcn_rsrv,	/* qi_srvp		*/
121 	sgcn_open,	/* qi_qopen		*/
122 	sgcn_close,	/* qi_qclose		*/
123 	NULL,		/* qi_qadmin		*/
124 	&minfo,		/* qi_minfo		*/
125 	NULL		/* qi_mstat		*/
126 };
127 
128 static struct qinit winit = {
129 	sgcn_wput,	/* qi_putp		*/
130 	sgcn_wsrv,	/* qi_srvp		*/
131 	sgcn_open,	/* qi_qopen		*/
132 	sgcn_close,	/* qi_qclose		*/
133 	NULL,		/* qi_qadmin		*/
134 	&minfo,		/* qi_minfo		*/
135 	NULL		/* qi_mstat		*/
136 };
137 
138 static struct streamtab sgcnstrinfo = {
139 	&rinit,
140 	&winit,
141 	NULL,
142 	NULL
143 };
144 
145 /* standard device driver structures */
146 static struct cb_ops sgcn_cb_ops = {
147 	nulldev,		/* open()		*/
148 	nulldev,		/* close()		*/
149 	nodev,			/* strategy()		*/
150 	nodev,			/* print()		*/
151 	nodev,			/* dump()		*/
152 	nodev,			/* read()		*/
153 	nodev,			/* write()		*/
154 	nodev,			/* ioctl()		*/
155 	nodev,			/* devmap()		*/
156 	nodev,			/* mmap()		*/
157 	nodev,			/* segmap()		*/
158 	nochpoll,		/* poll()		*/
159 	ddi_prop_op,		/* prop_op()		*/
160 	&sgcnstrinfo,		/* cb_str		*/
161 	D_MP | D_MTQPAIR		/* cb_flag		*/
162 };
163 
164 static struct dev_ops sgcn_ops = {
165 	DEVO_REV,
166 	0,			/* refcnt		*/
167 	sgcn_getinfo,		/* getinfo()		*/
168 	nulldev,		/* identify()		*/
169 	nulldev,		/* probe()		*/
170 	sgcn_attach,		/* attach()		*/
171 	sgcn_detach,		/* detach()		*/
172 	nodev,			/* reset()		*/
173 	&sgcn_cb_ops,		/* cb_ops		*/
174 	(struct bus_ops *)NULL,	/* bus_ops		*/
175 	NULL,			/* power()		*/
176 	ddi_quiesce_not_supported,	/* quiesce	*/
177 };
178 
179 static struct modldrv modldrv = {
180 	&mod_driverops,
181 	"Serengeti console driver",
182 	&sgcn_ops
183 };
184 
185 static struct modlinkage modlinkage = {
186 	MODREV_1,
187 	(void*)&modldrv,
188 	NULL
189 };
190 
191 
192 /* driver configuration routines */
193 int
194 _init(void)
195 {
196 	int error;
197 
198 	sgcn_state = kmem_zalloc(sizeof (sgcn_t), KM_SLEEP);
199 
200 	error = mod_install(&modlinkage);
201 
202 	if (error == 0) {
203 		mutex_init(&sgcn_state->sgcn_lock, NULL, MUTEX_DRIVER, NULL);
204 	} else {
205 		kmem_free(sgcn_state, sizeof (sgcn_t));
206 	}
207 
208 	return (error);
209 }
210 
211 int
212 _fini(void)
213 {
214 	/* can't remove console driver */
215 	return (EBUSY);
216 }
217 
218 int
219 _info(struct modinfo *modinfop)
220 {
221 	return (mod_info(&modlinkage, modinfop));
222 }
223 
224 /*
225  * sgcn_attach is called at startup time.
226  * There is only once instance of this driver.
227  */
228 static int
229 sgcn_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
230 {
231 	extern int ddi_create_internal_pathname(
232 	    dev_info_t *, char *, int, minor_t);
233 	cnsram_header	header;
234 	int		rv;
235 
236 	if (cmd != DDI_ATTACH)
237 		return (DDI_FAILURE);
238 
239 	if (ddi_create_internal_pathname(dip, "sgcn", S_IFCHR, 0)
240 	    != DDI_SUCCESS)
241 		return (DDI_FAILURE);
242 
243 	/* prepare some data structures in soft state */
244 	mutex_enter(&sgcn_state->sgcn_lock);
245 
246 	sgcn_state->sgcn_dip = dip;
247 
248 	mutex_exit(&sgcn_state->sgcn_lock);
249 
250 	/*
251 	 * We need to verify IOSRAM is intact at startup time. If by
252 	 * any chance IOSRAM is corrupted, that means SC is not ready.
253 	 * All we can do is stopping.
254 	 */
255 	rv = iosram_read(SBBC_CONSOLE_KEY, 0, (caddr_t)&header,
256 	    sizeof (cnsram_header));
257 	if (rv != 0)
258 		cmn_err(CE_PANIC, "sgcn_attach(): Reading from IOSRAM failed");
259 	if (header.cnsram_magic != CNSRAM_MAGIC)
260 		cmn_err(CE_PANIC, "sgcn_attach(): Wrong IOSRAM console buffer");
261 	if (!header.cnsram_in_end && !header.cnsram_in_begin)
262 		cmn_err(CE_PANIC, "sgcn_attach(): Wrong IOSRAM input buffer");
263 	if (!header.cnsram_out_end && !header.cnsram_out_begin)
264 		cmn_err(CE_PANIC, "sgcn_attach(): Wrong IOSRAM output buffer");
265 	/*
266 	 * XXX need to add extra check for version no.
267 	 */
268 
269 	/* Allocate console input buffer */
270 	sgcn_state->sgcn_inbuf_size =
271 	    header.cnsram_in_end - header.cnsram_in_begin;
272 	sgcn_state->sgcn_inbuf =
273 	    kmem_alloc(sgcn_state->sgcn_inbuf_size, KM_SLEEP);
274 #ifdef SGCN_DEBUG
275 	prom_printf("Allocated %d(0x%X) bytes for console\n",
276 	    sgcn_state->sgcn_inbuf_size);
277 #endif
278 
279 	(void) prom_serengeti_set_console_input(SGCN_CLNT_STR);
280 
281 	abort_seq_handler = sg_abort_seq_handler;
282 
283 #ifdef SGCN_DEBUG
284 	prom_printf("sgcn_attach(): SGCN driver attached\n");
285 #endif
286 	return (DDI_SUCCESS);
287 
288 }
289 
290 /* ARGSUSED */
291 static int
292 sgcn_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
293 {
294 
295 	if (cmd == DDI_DETACH)
296 		return (DDI_FAILURE);
297 
298 #ifdef SGCN_DEBUG
299 	prom_printf("sgcn_detach(): SGCN driver detached\n");
300 #endif
301 	return (DDI_SUCCESS);
302 }
303 
304 /* ARGSUSED */
305 static int
306 sgcn_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
307 {
308 	int error = DDI_FAILURE;
309 
310 	switch (infocmd) {
311 	case DDI_INFO_DEVT2DEVINFO:
312 		if (sgcn_state) {
313 			*result = (void *) sgcn_state->sgcn_dip;
314 			error = DDI_SUCCESS;
315 		}
316 		break;
317 
318 	case DDI_INFO_DEVT2INSTANCE:
319 		if (getminor((dev_t)arg) == 0) {
320 			*result = (void *)0;
321 			error = DDI_SUCCESS;
322 		}
323 		break;
324 	}
325 
326 	return (error);
327 }
328 
329 /* streams open & close */
330 /* ARGSUSED */
331 static int
332 sgcn_open(queue_t *q, dev_t *devp, int oflag, int sflag, cred_t *credp)
333 {
334 	tty_common_t	*tty;
335 	int		unit = getminor(*devp);
336 
337 	if (unit != 0)
338 		return (ENXIO);
339 
340 	/* stream already open */
341 	if (q->q_ptr) {
342 		return (DDI_SUCCESS);
343 	}
344 
345 	if (!sgcn_state) {
346 		cmn_err(CE_WARN, "sgcn_open(): sgcn is not configured by\
347 				autoconfig\n");
348 		return (ENXIO);
349 	}
350 
351 	mutex_enter(&sgcn_state->sgcn_lock);
352 	tty = &(sgcn_state->sgcn_tty);
353 
354 	tty->t_readq = q;
355 	tty->t_writeq = WR(q);
356 
357 	/* Link the RD and WR Q's */
358 
359 	q->q_ptr = WR(q)->q_ptr = (caddr_t)sgcn_state;
360 	sgcn_state->sgcn_readq = RD(q);
361 	sgcn_state->sgcn_writeq = WR(q);
362 	qprocson(q);
363 
364 	mutex_exit(&sgcn_state->sgcn_lock);
365 
366 	/* initialize interrupt handler */
367 	(void) iosram_reg_intr(SBBC_CONSOLE_IN,
368 	    (sbbc_intrfunc_t)sgcn_data_in_handler, NULL,
369 	    &sgcn_state->sgcn_sbbc_in_state,
370 	    &sgcn_state->sgcn_sbbc_in_lock);
371 	(void) iosram_reg_intr(SBBC_CONSOLE_SPACE_OUT,
372 	    (sbbc_intrfunc_t)sgcn_space_2_out_handler, NULL,
373 	    &sgcn_state->sgcn_sbbc_outspace_state,
374 	    &sgcn_state->sgcn_sbbc_outspace_lock);
375 	(void) iosram_reg_intr(SBBC_CONSOLE_BRK,
376 	    (sbbc_intrfunc_t)sgcn_break_handler, NULL,
377 	    &sgcn_state->sgcn_sbbc_brk_state,
378 	    &sgcn_state->sgcn_sbbc_brk_lock);
379 
380 	return (DDI_SUCCESS);
381 }
382 
383 /* ARGSUSED */
384 static int
385 sgcn_close(queue_t *q, int flag, cred_t *credp)
386 {
387 	int ret;
388 
389 	ASSERT(sgcn_state == q->q_ptr);
390 
391 	if (sgcn_state->sgcn_wbufcid != 0) {
392 		unbufcall(sgcn_state->sgcn_wbufcid);
393 	}
394 
395 	ret = iosram_unreg_intr(SBBC_CONSOLE_BRK);
396 	ASSERT(ret == 0);
397 
398 	ret = iosram_unreg_intr(SBBC_CONSOLE_SPACE_OUT);
399 	ASSERT(ret == 0);
400 
401 	ret = iosram_unreg_intr(SBBC_CONSOLE_IN);
402 	ASSERT(ret == 0);
403 
404 	ttycommon_close(&sgcn_state->sgcn_tty);
405 
406 	qprocsoff(q);
407 	q->q_ptr = WR(q)->q_ptr = NULL;
408 	sgcn_state->sgcn_readq = NULL;
409 	sgcn_state->sgcn_writeq = NULL;
410 
411 	return (DDI_SUCCESS);
412 }
413 
414 /*
415  * Put procedure for write queue.
416  * Respond to M_IOCTL, M_DATA and M_FLUSH messages here;
417  * It put's the data onto internal sgcn_output_q.
418  */
419 static int
420 sgcn_wput(queue_t *q, mblk_t *mp)
421 {
422 
423 #ifdef SGCN_DEBUG
424 	struct iocblk *iocp;
425 	int i;
426 #endif
427 
428 	ASSERT(sgcn_state == q->q_ptr);
429 
430 	if (!mp->b_datap) {
431 		cmn_err(CE_PANIC, "sgcn_wput(): null datap");
432 	}
433 
434 #ifdef SGCN_DEBUG
435 	prom_printf("sgcn_wput(): SGCN wput q=%X mp=%X rd=%X wr=%X type=%X\n",
436 	    q, mp, mp->b_rptr, mp->b_wptr, mp->b_datap->db_type);
437 #endif
438 
439 	switch (mp->b_datap->db_type) {
440 	case M_IOCTL:
441 	case M_CTL:
442 #ifdef SGCN_DEBUG
443 		iocp = (struct iocblk *)mp->b_rptr;
444 		prom_printf("sgcn_wput(): M_IOCTL cmd=%X TIOC=%X\n",
445 		    iocp->ioc_cmd, TIOC);
446 #endif
447 		switch (((struct iocblk *)mp->b_rptr)->ioc_cmd) {
448 		case TCSETSW:
449 		case TCSETSF:
450 		case TCSETAW:
451 		case TCSETAF:
452 		case TCSBRK:
453 			/*
454 			 * The change do not take effect until all
455 			 * output queued before them is drained.
456 			 * Put this message on the queue, so that
457 			 * "sgcn_start" will see it when it's done
458 			 * with the output before it. Poke the start
459 			 * routine, just in case.
460 			 */
461 			(void) putq(q, mp);
462 			sgcn_start();
463 			break;
464 		default:
465 			sgcn_ioctl(q, mp);
466 		}
467 		break;
468 
469 	case M_FLUSH:
470 		if (*mp->b_rptr & FLUSHW) {
471 			flushq(q, FLUSHDATA);
472 			*mp->b_rptr &= ~FLUSHW;
473 		}
474 		if (*mp->b_rptr & FLUSHR) {
475 			flushq(RD(q), FLUSHDATA);
476 			qreply(q, mp);
477 		} else {
478 			freemsg(mp);
479 		}
480 		break;
481 
482 	case M_STOP:
483 		sgcn_stopped = TRUE;
484 		freemsg(mp);
485 		break;
486 
487 	case M_START:
488 		sgcn_stopped = FALSE;
489 		freemsg(mp);
490 		qenable(q);	/* Start up delayed messages */
491 		break;
492 
493 	case M_DATA:
494 		/*
495 		 * Queue the message up to be transmitted,
496 		 * and poke the start routine.
497 		 */
498 #ifdef SGCN_DEBUG
499 		if (mp->b_rptr < mp->b_wptr) {
500 		prom_printf("sgcn_wput(): DATA q=%X mp=%X rd=%X wr=%X\n",
501 		    q, mp, mp->b_rptr, mp->b_wptr);
502 		prom_printf("sgcn_wput(): [[[[[");
503 		for (i = 0; i < mp->b_wptr-mp->b_rptr; i++) {
504 			prom_printf("%c", *(mp->b_rptr+i));
505 		}
506 		prom_printf("]]]]]\n");
507 		}
508 #endif /* SGCN_DEBUG */
509 		(void) putq(q, mp);
510 		sgcn_start();
511 		break;
512 
513 	default:
514 		freemsg(mp);
515 	}
516 
517 	return (0);
518 }
519 
520 /*
521  * Process an "ioctl" message sent down to us.
522  */
523 static void
524 sgcn_ioctl(queue_t *q, mblk_t *mp)
525 {
526 	struct iocblk	*iocp;
527 	tty_common_t	*tty;
528 	mblk_t		*datamp;
529 	int		data_size;
530 	int		error = 0;
531 
532 #ifdef SGCN_DEBUG
533 	prom_printf("sgcn_ioctl(): q=%X mp=%X\n", q, mp);
534 #endif
535 	iocp = (struct iocblk *)mp->b_rptr;
536 	tty = &(sgcn_state->sgcn_tty);
537 
538 	if (tty->t_iocpending != NULL) {
539 		freemsg(tty->t_iocpending);
540 		tty->t_iocpending = NULL;
541 	}
542 	data_size = ttycommon_ioctl(tty, q, mp, &error);
543 	if (data_size != 0) {
544 		if (sgcn_state->sgcn_wbufcid)
545 			unbufcall(sgcn_state->sgcn_wbufcid);
546 		/* call sgcn_reioctl() */
547 		sgcn_state->sgcn_wbufcid =
548 		    bufcall(data_size, BPRI_HI, sgcn_reioctl, sgcn_state);
549 		return;
550 	}
551 
552 	if (error < 0) {
553 		iocp = (struct iocblk *)mp->b_rptr;
554 		/*
555 		 * "ttycommon_ioctl" didn't do anything; we process it here.
556 		 */
557 		error = 0;
558 		switch (iocp->ioc_cmd) {
559 		case TCSBRK:
560 		case TIOCSBRK:
561 		case TIOCCBRK:
562 		case TIOCMSET:
563 		case TIOCMBIS:
564 		case TIOCMBIC:
565 			if (iocp->ioc_count != TRANSPARENT)
566 				mioc2ack(mp, NULL, 0, 0);
567 			else
568 				mcopyin(mp, NULL, sizeof (int), NULL);
569 			break;
570 
571 		case TIOCMGET:
572 			datamp = allocb(sizeof (int), BPRI_MED);
573 			if (datamp == NULL) {
574 				error = EAGAIN;
575 				break;
576 			}
577 
578 			*(int *)datamp->b_rptr = 0;
579 
580 			if (iocp->ioc_count != TRANSPARENT)
581 				mioc2ack(mp, datamp, sizeof (int), 0);
582 			else
583 				mcopyout(mp, NULL, sizeof (int), NULL, datamp);
584 			break;
585 
586 		default:
587 			error = EINVAL;
588 			break;
589 		}
590 	}
591 	if (error != 0) {
592 		iocp->ioc_count = 0;
593 		iocp->ioc_error = error;
594 		mp->b_datap->db_type = M_IOCNAK;
595 	}
596 	qreply(q, mp);
597 }
598 
599 static void
600 sgcn_reioctl(void *unit)
601 {
602 	queue_t		*q;
603 	mblk_t		*mp;
604 	sgcn_t		*sgcnp = (sgcn_t *)unit;
605 
606 	if (!sgcnp->sgcn_wbufcid) {
607 		return;
608 	}
609 	sgcnp->sgcn_wbufcid = 0;
610 	if ((q = sgcnp->sgcn_tty.t_writeq) == NULL) {
611 		return;
612 	}
613 
614 	if ((mp = sgcnp->sgcn_tty.t_iocpending) != NULL) {
615 		sgcnp->sgcn_tty.t_iocpending = NULL;
616 		sgcn_ioctl(q, mp);
617 	}
618 }
619 
620 static void
621 sgcn_start()
622 {
623 
624 	queue_t *q;
625 	mblk_t *mp;
626 	int retval;
627 
628 	/*
629 	 * read stream queue and remove data from the queue and
630 	 * transmit them if possible
631 	 */
632 	q = sgcn_state->sgcn_writeq;
633 	ASSERT(q != NULL);
634 	while (mp = getq(q)) {
635 		switch (mp->b_datap->db_type) {
636 		case M_IOCTL:
637 			/*
638 			 * These are those IOCTLs queued up
639 			 * do it now
640 			 */
641 			sgcn_ioctl(q, mp);
642 			continue;
643 		default:
644 			/*
645 			 * M_DATA
646 			 * Copy it from stream queue buffer to
647 			 * sgcn buffer
648 			 */
649 			retval = sgcn_transmit(q, mp);
650 
651 			if (retval == EBUSY) {
652 				/*
653 				 * Console output buffer is full for
654 				 * sgcn_timeout_period seconds, assume
655 				 * SC is dead, drop all console output
656 				 * data from stream queue.
657 				 */
658 				if (sgcn_state->sgcn_sc_active <
659 				    gethrestime_sec() - sgcn_timeout_period)
660 					sgcn_flush();
661 				return;
662 			} else if (retval == EAGAIN) {
663 				/*
664 				 * Console output just became full
665 				 * return
666 				 */
667 				mutex_enter(&sgcn_state->sgcn_lock);
668 				sgcn_state->sgcn_sc_active = gethrestime_sec();
669 				mutex_exit(&sgcn_state->sgcn_lock);
670 				return;
671 			} else {
672 				/* send more console output */
673 				mutex_enter(&sgcn_state->sgcn_lock);
674 				sgcn_state->sgcn_sc_active = gethrestime_sec();
675 				mutex_exit(&sgcn_state->sgcn_lock);
676 			}
677 		} /* switch */
678 	}
679 
680 }
681 
682 static int
683 sgcn_transmit(queue_t *q, mblk_t *mp)
684 {
685 	caddr_t		buf;
686 	mblk_t		*bp;
687 	int		len, oldlen;
688 
689 #ifdef SGCN_DEBUG
690 	prom_printf("sgcn_transmit(): q=%X mp=%X\n", q, mp);
691 #endif
692 	do {
693 		bp = mp;
694 		oldlen = len = bp->b_wptr - bp->b_rptr;
695 		buf = (caddr_t)bp->b_rptr;
696 		len = CONSOLE_WRITE(buf, len);
697 		if (len > 0)
698 			(void) iosram_send_intr(SBBC_CONSOLE_OUT);
699 		if (len >= 0 && len < oldlen) {
700 			/* IOSRAM is full, we are not done with mp yet */
701 			bp->b_rptr += len;
702 			(void) putbq(q, mp);
703 			if (len)
704 				return (EAGAIN);
705 			else
706 				return (EBUSY);
707 		}
708 		mp = bp->b_cont;
709 		freeb(bp);
710 	} while (mp);
711 
712 	return (0);
713 }
714 
715 /*
716  * called when SC first establishes console connection
717  * drop all the data on the output queue
718  */
719 static void
720 sgcn_flush()
721 {
722 	queue_t *q;
723 	mblk_t *mp;
724 
725 	q = sgcn_state->sgcn_writeq;
726 
727 	prom_printf("sgcn_flush(): WARNING console output is dropped "
728 	    "time=%lX\n", gethrestime_sec());
729 	while (mp = getq(q)) {
730 		freemsg(mp);
731 	}
732 
733 }
734 
735 uint64_t sgcn_input_dropped;
736 
737 /*
738  * Interrupt handlers
739  * All handlers register with SBBC driver and must follow SBBC interrupt
740  * delivery conventions.
741  */
742 /*
743  * SC sends an interrupt when new data comes in
744  */
745 /* ARGSUSED */
746 void
747 sgcn_data_in_handler(caddr_t arg)
748 {
749 	caddr_t		buf = sgcn_state->sgcn_inbuf;
750 	int		i, len;
751 	mblk_t		*mp;
752 
753 	/*
754 	 * change interrupt state so that SBBC won't trigger
755 	 * another one.
756 	 */
757 	mutex_enter(&sgcn_state->sgcn_sbbc_in_lock);
758 	sgcn_state->sgcn_sbbc_in_state = SBBC_INTR_RUNNING;
759 	mutex_exit(&sgcn_state->sgcn_sbbc_in_lock);
760 
761 	/* update sgcn_state for SC activity information */
762 	mutex_enter(&sgcn_state->sgcn_lock);
763 	sgcn_state->sgcn_sc_active = gethrestime_sec();
764 	mutex_exit(&sgcn_state->sgcn_lock);
765 
766 	/* enter our perimeter */
767 	entersq(sgcn_state->sgcn_readq->q_syncq, SQ_CALLBACK);
768 
769 	for (;;) {
770 
771 		/* read from console input IOSRAM */
772 		len = CONSOLE_READ(buf, sgcn_state->sgcn_inbuf_size);
773 
774 		if (len <= 0) {
775 
776 			mutex_enter(&sgcn_state->sgcn_sbbc_in_lock);
777 
778 			len = CONSOLE_READ(buf, sgcn_state->sgcn_inbuf_size);
779 
780 			if (len <= 0) {
781 				sgcn_state->sgcn_sbbc_in_state = SBBC_INTR_IDLE;
782 				mutex_exit(&sgcn_state->sgcn_sbbc_in_lock);
783 
784 				/* leave our perimeter */
785 				leavesq(sgcn_state->sgcn_readq->q_syncq,
786 				    SQ_CALLBACK);
787 				return;
788 			} else {
789 				mutex_exit(&sgcn_state->sgcn_sbbc_in_lock);
790 			}
791 
792 		}
793 
794 		(void) iosram_send_intr(SBBC_CONSOLE_SPACE_IN);
795 
796 		if (abort_enable == KIOCABORTALTERNATE) {
797 			for (i = 0; i < len; i ++) {
798 				if (abort_charseq_recognize(buf[i]))
799 					abort_sequence_enter((char *)NULL);
800 			}
801 		}
802 
803 		/* put console input onto stream */
804 		if (sgcn_state->sgcn_readq) {
805 			if ((mp = allocb(len, BPRI_MED)) == (mblk_t *)NULL) {
806 				sgcn_input_dropped += len;
807 				cmn_err(CE_WARN,
808 				    "sgcn_data_in_handler(): allocb failed"
809 				    " (console input dropped.)");
810 			} else {
811 				bcopy(buf, mp->b_wptr, len);
812 				mp->b_wptr += len;
813 				putnext(sgcn_state->sgcn_readq, mp);
814 			}
815 		}
816 	}
817 
818 }
819 
820 /*
821  * SC sends an interrupt when it takes output data
822  * from a full IOSRAM
823  */
824 /* ARGSUSED */
825 void
826 sgcn_space_2_out_handler(caddr_t arg)
827 {
828 	/*
829 	 * change interrupt state so that SBBC won't trigger
830 	 * another one.
831 	 */
832 	mutex_enter(&sgcn_state->sgcn_sbbc_outspace_lock);
833 	sgcn_state->sgcn_sbbc_outspace_state = SBBC_INTR_RUNNING;
834 	mutex_exit(&sgcn_state->sgcn_sbbc_outspace_lock);
835 
836 	mutex_enter(&sgcn_state->sgcn_lock);
837 	sgcn_state->sgcn_sc_active = gethrestime_sec();
838 	mutex_exit(&sgcn_state->sgcn_lock);
839 
840 	if (sgcn_state->sgcn_writeq != NULL)
841 		qenable(sgcn_state->sgcn_writeq);
842 
843 	/* restore interrupt state */
844 	mutex_enter(&sgcn_state->sgcn_sbbc_outspace_lock);
845 	sgcn_state->sgcn_sbbc_outspace_state = SBBC_INTR_IDLE;
846 	mutex_exit(&sgcn_state->sgcn_sbbc_outspace_lock);
847 }
848 
849 /*
850  * SC sends an interrupt when it detects BREAK sequence
851  */
852 /* ARGSUSED */
853 void
854 sgcn_break_handler(caddr_t arg)
855 {
856 	/*
857 	 * change interrupt state so that SBBC won't trigger
858 	 * another one.
859 	 */
860 	mutex_enter(&sgcn_state->sgcn_sbbc_brk_lock);
861 	sgcn_state->sgcn_sbbc_brk_state = SBBC_INTR_RUNNING;
862 	mutex_exit(&sgcn_state->sgcn_sbbc_brk_lock);
863 
864 	if (abort_enable != KIOCABORTALTERNATE)
865 		abort_sequence_enter((char *)NULL);
866 
867 	/* restore interrupt state */
868 	mutex_enter(&sgcn_state->sgcn_sbbc_brk_lock);
869 	sgcn_state->sgcn_sbbc_brk_state = SBBC_INTR_IDLE;
870 	mutex_exit(&sgcn_state->sgcn_sbbc_brk_lock);
871 }
872 
873 /*
874  * reporting errors in console driver sgcn.
875  * since we can not trust console driver at this time, we need to
876  * log errors in other system logs
877  * error codes:
878  *	EIO - iosram interface failed
879  *	EPROTO - IOSRAM is corrupted
880  *	EINVAL - invalid argument
881  */
882 #define	SGCN_MAX_ERROR		100
883 static void
884 sgcn_log_error(int when, int what)
885 {
886 	char error_msg[256], error_code[256];
887 	static uint_t	error_counter = 0;
888 
889 	error_counter ++;
890 
891 	if (error_counter > SGCN_MAX_ERROR) {
892 		error_counter = 0;
893 		(void) strcpy(error_msg, "!Too many sgcn errors");
894 	} else {
895 		(void) sprintf(error_code, "Error %d", what);
896 
897 		(void) sprintf(error_msg, "!%s at %s",
898 		    (what == EIO) ? "IOSRAM interface failed" :
899 		    (what == EPROTO) ? "IOSRAM corrupted" :
900 		    (what == EINVAL) ? "Invalid argument" :
901 		    error_code,
902 		    (when == RW_CONSOLE_READ) ? "console input" :
903 		    (when == RW_CONSOLE_WRITE) ? "console output, dropped" :
904 		    "console I/O");
905 	}
906 
907 	cmn_err(CE_WARN, error_msg);
908 }
909 
910 static int
911 sgcn_read_header(int rw, cnsram_header *header)
912 {
913 	int	rv;
914 
915 	/* check IOSRAM contents and read pointers */
916 	rv = iosram_read(SBBC_CONSOLE_KEY, 0, (caddr_t)header,
917 	    sizeof (cnsram_header));
918 	if (rv != 0) {
919 		return (-1);
920 	}
921 
922 	/*
923 	 * Since the header is read in a byte-by-byte fashion
924 	 * using ddi_rep_get8, we need to re-read the producer
925 	 * or consumer pointer as integer in case it has changed
926 	 * after part of the previous value has been read.
927 	 */
928 	if (rw == RW_CONSOLE_READ) {
929 		rv = iosram_read(SBBC_CONSOLE_KEY,
930 		    OFFSETOF((*header), cnsram_in_wrptr),
931 		    POINTER((*header), cnsram_in_wrptr),
932 		    sizeof (header->cnsram_in_wrptr));
933 	} else if (rw == RW_CONSOLE_WRITE) {
934 		rv = iosram_read(SBBC_CONSOLE_KEY,
935 		    OFFSETOF((*header), cnsram_out_rdptr),
936 		    POINTER((*header), cnsram_out_rdptr),
937 		    sizeof (header->cnsram_out_rdptr));
938 	} else
939 		rv = -1;
940 
941 	return (rv);
942 }
943 
944 static int
945 sgcn_rw(int rw, caddr_t buf, int len)
946 {
947 	cnsram_header	header;
948 	int		rv, size, nbytes;
949 
950 #ifdef SGCN_DEBUG
951 	prom_printf("sgcn_rw() rw = %X buf = %p len = %d\n",
952 	    rw, buf, len);
953 #endif /* SGCN_DEBUG */
954 	if (len == 0)
955 		return (0);
956 
957 	/* sanity check */
958 	if (buf == NULL || len < 0) {
959 		sgcn_log_error(rw, EINVAL);
960 		return (-1);
961 	}
962 
963 	/* check IOSRAM contents and read pointers */
964 	rv = sgcn_read_header(rw, &header);
965 	if (rv != 0) {
966 		sgcn_log_error(rw, EIO);
967 		return (-1);
968 	}
969 	if (header.cnsram_magic != CNSRAM_MAGIC) {
970 		sgcn_log_error(rw, EPROTO);
971 		return (-1);
972 	}
973 
974 	if (rw == RW_CONSOLE_READ)
975 		size = header.cnsram_in_end - header.cnsram_in_begin;
976 	else if (rw == RW_CONSOLE_WRITE)
977 		size = header.cnsram_out_end - header.cnsram_out_begin;
978 	if (size < 0) {
979 		sgcn_log_error(rw, EPROTO);
980 		return (-1);
981 	}
982 
983 	if (rw == RW_CONSOLE_READ)
984 		nbytes = circular_buffer_read(
985 		    header.cnsram_in_begin,
986 		    header.cnsram_in_end,
987 		    header.cnsram_in_rdptr,
988 		    header.cnsram_in_wrptr, buf, len);
989 	else if (rw == RW_CONSOLE_WRITE)
990 		nbytes = circular_buffer_write(
991 		    header.cnsram_out_begin,
992 		    header.cnsram_out_end,
993 		    header.cnsram_out_rdptr,
994 		    header.cnsram_out_wrptr, buf, len);
995 
996 	/*
997 	 * error log was done in circular buffer routines,
998 	 * no need to call sgcn_log_error() here
999 	 */
1000 	if (nbytes < 0)
1001 		return (-1);
1002 
1003 	if (nbytes == 0)
1004 		return (0);
1005 
1006 	if (rw == RW_CONSOLE_READ) {
1007 		header.cnsram_in_rdptr =
1008 		    (header.cnsram_in_rdptr - header.cnsram_in_begin
1009 		    + nbytes)
1010 		    % size + header.cnsram_in_begin;
1011 		rv = iosram_write(SBBC_CONSOLE_KEY,
1012 		    OFFSETOF(header, cnsram_in_rdptr),
1013 		    POINTER(header, cnsram_in_rdptr),
1014 		    sizeof (header.cnsram_in_rdptr));
1015 	} else if (rw == RW_CONSOLE_WRITE) {
1016 		header.cnsram_out_wrptr =
1017 		    (header.cnsram_out_wrptr - header.cnsram_out_begin
1018 		    + nbytes)
1019 		    % size + header.cnsram_out_begin;
1020 		rv = iosram_write(SBBC_CONSOLE_KEY,
1021 		    OFFSETOF(header, cnsram_out_wrptr),
1022 		    POINTER(header, cnsram_out_wrptr),
1023 		    sizeof (header.cnsram_out_wrptr));
1024 	}
1025 	if (rv != 0) {
1026 		sgcn_log_error(rw, EIO);
1027 		return (-1);
1028 	}
1029 
1030 	return (nbytes);
1031 }
1032 
1033 /*
1034  * Circular buffer interfaces
1035  *
1036  * See sgcn.h for circular buffer structure
1037  *
1038  * The circular buffer is empty when read ptr == write ptr
1039  * and is full when read ptr is one ahead of write ptr
1040  */
1041 /*
1042  * Write to circular buffer in IOSRAM
1043  * input:
1044  *	buf	buffer in main memory, contains data to be written
1045  *	len	length of data in bytes
1046  *	begin, end, rd, wr	buffer pointers
1047  * return value:
1048  *	actual bytes written.
1049  */
1050 static int
1051 circular_buffer_write(int begin, int end, int rd, int wr, caddr_t buf, int len)
1052 {
1053 	int		size, space, space_at_end;
1054 	int		rv = 0;
1055 
1056 	size = end - begin;
1057 	if (size <= 0) {
1058 		rv = EINVAL;
1059 		goto out;
1060 	}
1061 
1062 	if ((len = ((len >= size) ? (size-1) : len)) == 0)
1063 		return (0);	/* The buffer's full, so just return 0 now. */
1064 
1065 	space = (rd - wr + size - 1) % size;
1066 	len = min(len, space);
1067 	space_at_end = end - wr;
1068 
1069 	if (rd > wr || rd <= wr && space_at_end >= len) { /* one piece */
1070 		/* write console data */
1071 		rv = iosram_write(SBBC_CONSOLE_KEY, wr, buf, len);
1072 		if (rv != 0) goto out;
1073 	} else { /* break into two pieces because of circular buffer */
1074 		/* write console data */
1075 		if (space_at_end) {
1076 			rv = iosram_write(SBBC_CONSOLE_KEY,
1077 			    wr, buf, space_at_end);
1078 			if (rv != 0) goto out;
1079 		}
1080 		if (len - space_at_end) {
1081 			rv = iosram_write(SBBC_CONSOLE_KEY,
1082 			    begin, buf+space_at_end, len-space_at_end);
1083 			if (rv != 0) goto out;
1084 		}
1085 	}
1086 	return (len);
1087 out:
1088 	sgcn_log_error(RW_CONSOLE_WRITE, rv);
1089 	return (-1);
1090 }
1091 
1092 /*
1093  * Read from circular buffer in IOSRAM
1094  * input:
1095  *	buf	preallocated buffer in memory
1096  *	len	size of buf
1097  *	begin, end, rd, wr	buffer pointers
1098  * return value:
1099  *	actual bytes read
1100  */
1101 /* ARGSUSED */
1102 static int
1103 circular_buffer_read(int begin, int end, int rd, int wr, caddr_t buf, int len)
1104 {
1105 	int		size, nbytes, nbytes_at_end;
1106 	int		rv = 0;
1107 
1108 	size = end - begin;
1109 	if (size <= 0) {
1110 		rv = EINVAL;
1111 		goto out;
1112 	}
1113 	nbytes = (wr - rd + size) % size;
1114 
1115 	nbytes = min(nbytes, len);
1116 
1117 	if (wr > rd) { /* one piece */
1118 		rv = iosram_read(SBBC_CONSOLE_KEY, rd, buf, nbytes);
1119 		if (rv != 0) goto out;
1120 	} else { /* break into two pieces because of circular buffer */
1121 		nbytes_at_end = min(nbytes, end - rd);
1122 		/* read console data */
1123 		if (nbytes_at_end) {
1124 			rv = iosram_read(SBBC_CONSOLE_KEY,
1125 			    rd, buf, nbytes_at_end);
1126 			if (rv != 0) goto out;
1127 		}
1128 		if (nbytes-nbytes_at_end) {
1129 			rv = iosram_read(SBBC_CONSOLE_KEY,
1130 			    begin, buf+nbytes_at_end, nbytes-nbytes_at_end);
1131 			if (rv != 0) goto out;
1132 		}
1133 	}
1134 	return (nbytes);
1135 out:
1136 	sgcn_log_error(RW_CONSOLE_READ, rv);
1137 	return (-1);
1138 }
1139 
1140 /*
1141  * Check for abort character sequence, copied from zs_async.c
1142  */
1143 #define	CNTRL(c) ((c)&037)
1144 
1145 static boolean_t
1146 abort_charseq_recognize(uchar_t ch)
1147 {
1148 	static int state = 0;
1149 	static char sequence[] = { '\r', '~', CNTRL('b') };
1150 
1151 	if (ch == sequence[state]) {
1152 		if (++state >= sizeof (sequence)) {
1153 			state = 0;
1154 			return (B_TRUE);
1155 		}
1156 	} else {
1157 		state = (ch == sequence[0]) ? 1 : 0;
1158 	}
1159 	return (B_FALSE);
1160 }
1161 
1162 static void
1163 sg_abort_seq_handler(char *msg)
1164 {
1165 	char	key_switch;
1166 	int	rv;
1167 
1168 	/* read virtual keyswitch position from IOSRAM */
1169 	rv = iosram_read(SBBC_KEYSWITCH_KEY, 0, &key_switch, 1);
1170 	if (rv != 0) {
1171 		/* default to not secure if read failed */
1172 		cmn_err(CE_NOTE, "!Read keyswitch failed (%d)", rv);
1173 		key_switch = 0;
1174 	}
1175 	if (key_switch & SG_KEYSWITCH_POSN_SECURE) {
1176 		cmn_err(CE_NOTE, "!Keyswitch is in secure mode");
1177 	} else {
1178 		debug_enter(msg);
1179 	}
1180 }
1181 
1182 static int
1183 sgcn_rsrv(queue_t *q)
1184 {
1185 	mblk_t	*mp;
1186 
1187 	if (sgcn_stopped == TRUE) {
1188 		return (0);
1189 	}
1190 
1191 	mutex_enter(&sgcn_state->sgcn_lock);
1192 	sgcn_state->sgcn_sc_active = gethrestime_sec();
1193 	mutex_exit(&sgcn_state->sgcn_lock);
1194 
1195 	while ((mp = getq(q)) != NULL) {
1196 		if (canputnext(q)) {
1197 			putnext(q, mp);
1198 		} else if (mp->b_datap->db_type >= QPCTL) {
1199 			(void) putbq(q, mp);
1200 		}
1201 	}
1202 
1203 	return (0);
1204 }
1205 
1206 /* ARGSUSED */
1207 static int
1208 sgcn_wsrv(queue_t *q)
1209 {
1210 	if (sgcn_stopped == TRUE)
1211 		return (0);
1212 
1213 	mutex_enter(&sgcn_state->sgcn_lock);
1214 	sgcn_state->sgcn_sc_active = gethrestime_sec();
1215 	mutex_exit(&sgcn_state->sgcn_lock);
1216 
1217 	if (sgcn_state->sgcn_writeq != NULL)
1218 		sgcn_start();
1219 
1220 	return (0);
1221 }
1222