sgcn.c revision 7656:2621e50fdf4a
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 2008 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 */
73static int sgcn_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
74static int sgcn_attach(dev_info_t *, ddi_attach_cmd_t);
75static int sgcn_detach(dev_info_t *, ddi_detach_cmd_t);
76static int sgcn_open(queue_t *, dev_t *, int, int, cred_t *);
77static int sgcn_close(queue_t *, int, cred_t *);
78static int sgcn_wput(queue_t *, mblk_t *);
79static int sgcn_wsrv(queue_t *);
80static int sgcn_rsrv(queue_t *);
81
82/* interrupt handlers */
83static void sgcn_data_in_handler(caddr_t);
84static void sgcn_space_2_out_handler(caddr_t);
85static void sgcn_break_handler(caddr_t);
86
87/* other internal sgcn routines */
88static void sgcn_ioctl(queue_t *, mblk_t *);
89static void sgcn_reioctl(void *);
90static void sgcn_start(void);
91static int sgcn_transmit(queue_t *, mblk_t *);
92static void sgcn_flush(void);
93static int sgcn_read_header(int, cnsram_header *);
94static int sgcn_rw(int, caddr_t, int);
95static void sgcn_log_error(int, int);
96
97/* circular buffer routines */
98static int circular_buffer_write(int, int, int, int, caddr_t, int);
99static int circular_buffer_read(int, int, int, int, caddr_t, int);
100
101static boolean_t abort_charseq_recognize(uchar_t);
102static void sg_abort_seq_handler(char *);
103
104static	sgcn_t *sgcn_state;
105static uchar_t	sgcn_stopped = FALSE;
106static int	sgcn_timeout_period = 20;	/* time out in seconds */
107
108/* streams structures */
109static 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
118static 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
128static 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
138static struct streamtab sgcnstrinfo = {
139	&rinit,
140	&winit,
141	NULL,
142	NULL
143};
144
145/* standard device driver structures */
146static 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
164static 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
179static struct modldrv modldrv = {
180	&mod_driverops,
181	"Serengeti console driver",
182	&sgcn_ops
183};
184
185static struct modlinkage modlinkage = {
186	MODREV_1,
187	(void*)&modldrv,
188	NULL
189};
190
191
192/* driver configuration routines */
193int
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
211int
212_fini(void)
213{
214	/* can't remove console driver */
215	return (EBUSY);
216}
217
218int
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 */
228static int
229sgcn_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 */
291static int
292sgcn_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 */
305static int
306sgcn_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 */
331static int
332sgcn_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	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	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	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 */
384static int
385sgcn_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 */
419static int
420sgcn_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			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 */
523static void
524sgcn_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
599static void
600sgcn_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
620static void
621sgcn_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
682static int
683sgcn_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			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 */
719static void
720sgcn_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
735uint64_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 */
746void
747sgcn_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		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 */
825void
826sgcn_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 */
853void
854sgcn_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
883static void
884sgcn_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		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
910static int
911sgcn_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
944static int
945sgcn_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 */
1050static int
1051circular_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);
1087out:
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 */
1102static int
1103circular_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);
1135out:
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
1145static boolean_t
1146abort_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
1162static void
1163sg_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
1182static int
1183sgcn_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			putbq(q, mp);
1200		}
1201	}
1202
1203	return (0);
1204}
1205
1206/* ARGSUSED */
1207static int
1208sgcn_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