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