xref: /onnv-gate/usr/src/uts/sun4u/serengeti/io/sgcn.c (revision 11311:639e7bc0b42f)
11708Sstevel /*
21708Sstevel  * CDDL HEADER START
31708Sstevel  *
41708Sstevel  * The contents of this file are subject to the terms of the
51708Sstevel  * Common Development and Distribution License (the "License").
61708Sstevel  * You may not use this file except in compliance with the License.
71708Sstevel  *
81708Sstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91708Sstevel  * or http://www.opensolaris.org/os/licensing.
101708Sstevel  * See the License for the specific language governing permissions
111708Sstevel  * and limitations under the License.
121708Sstevel  *
131708Sstevel  * When distributing Covered Code, include this CDDL HEADER in each
141708Sstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151708Sstevel  * If applicable, add the following below this CDDL HEADER, with the
161708Sstevel  * fields enclosed by brackets "[]" replaced with your own identifying
171708Sstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
181708Sstevel  *
191708Sstevel  * CDDL HEADER END
201708Sstevel  */
211708Sstevel 
221708Sstevel /*
23*11311SSurya.Prakki@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
241708Sstevel  * Use is subject to license terms.
251708Sstevel  */
261708Sstevel 
271708Sstevel 
281708Sstevel /*
291708Sstevel  * Serengeti console driver, see sys/sgcn.h for more information
301708Sstevel  * This driver uses the QPAIR form of STREAMS Perimeters to serialize access
311708Sstevel  * to the read and write STREAMS queues.
321708Sstevel  */
331708Sstevel 
341708Sstevel #include <sys/errno.h>
351708Sstevel #include <sys/stat.h>
361708Sstevel #include <sys/kmem.h>
371708Sstevel #include <sys/conf.h>
381708Sstevel #include <sys/termios.h>
391708Sstevel #include <sys/modctl.h>
401708Sstevel #include <sys/kbio.h>
411708Sstevel #include <sys/stropts.h>
421708Sstevel #include <sys/stream.h>
431708Sstevel #include <sys/strsun.h>
441708Sstevel #include <sys/sysmacros.h>
451708Sstevel #include <sys/promif.h>
461708Sstevel #include <sys/prom_plat.h>
471708Sstevel #include <sys/sgsbbc.h>
481708Sstevel #include <sys/sgsbbc_iosram.h>
491708Sstevel #include <sys/sgcn.h>
501708Sstevel #include <sys/serengeti.h>
511708Sstevel #include <sys/ddi.h>
521708Sstevel #include <sys/sunddi.h>
531708Sstevel #include <sys/strsubr.h>
541708Sstevel 
551708Sstevel /*
561708Sstevel  * Here we define several macros for accessing console IOSRAM
571708Sstevel  */
581708Sstevel 
591708Sstevel #define	POINTER(base, field)	((caddr_t)&base.field)
601708Sstevel #define	OFFSETOF(base, field)	((caddr_t)&base.field - (caddr_t)&base)
611708Sstevel 
621708Sstevel #define	RW_CONSOLE_READ		0xAAAA
631708Sstevel #define	RW_CONSOLE_WRITE	0xBBBB
641708Sstevel 
651708Sstevel #define	CONSOLE_READ(buf, len)	sgcn_rw(RW_CONSOLE_READ, buf, len)
661708Sstevel #define	CONSOLE_WRITE(buf, len)	sgcn_rw(RW_CONSOLE_WRITE, buf, len)
671708Sstevel 
681708Sstevel #define	SGCN_MI_IDNUM		0xABCD
691708Sstevel #define	SGCN_MI_HIWAT		2048*2048
701708Sstevel #define	SGCN_MI_LOWAT		128
711708Sstevel 
721708Sstevel /* dev_ops and cb_ops for device driver */
731708Sstevel static int sgcn_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
741708Sstevel static int sgcn_attach(dev_info_t *, ddi_attach_cmd_t);
751708Sstevel static int sgcn_detach(dev_info_t *, ddi_detach_cmd_t);
761708Sstevel static int sgcn_open(queue_t *, dev_t *, int, int, cred_t *);
771708Sstevel static int sgcn_close(queue_t *, int, cred_t *);
781708Sstevel static int sgcn_wput(queue_t *, mblk_t *);
791708Sstevel static int sgcn_wsrv(queue_t *);
801708Sstevel static int sgcn_rsrv(queue_t *);
811708Sstevel 
821708Sstevel /* interrupt handlers */
831708Sstevel static void sgcn_data_in_handler(caddr_t);
841708Sstevel static void sgcn_space_2_out_handler(caddr_t);
851708Sstevel static void sgcn_break_handler(caddr_t);
861708Sstevel 
871708Sstevel /* other internal sgcn routines */
881708Sstevel static void sgcn_ioctl(queue_t *, mblk_t *);
891708Sstevel static void sgcn_reioctl(void *);
901708Sstevel static void sgcn_start(void);
911708Sstevel static int sgcn_transmit(queue_t *, mblk_t *);
921708Sstevel static void sgcn_flush(void);
931708Sstevel static int sgcn_read_header(int, cnsram_header *);
941708Sstevel static int sgcn_rw(int, caddr_t, int);
951708Sstevel static void sgcn_log_error(int, int);
961708Sstevel 
971708Sstevel /* circular buffer routines */
981708Sstevel static int circular_buffer_write(int, int, int, int, caddr_t, int);
991708Sstevel static int circular_buffer_read(int, int, int, int, caddr_t, int);
1001708Sstevel 
1011708Sstevel static boolean_t abort_charseq_recognize(uchar_t);
1021708Sstevel static void sg_abort_seq_handler(char *);
1031708Sstevel 
1041708Sstevel static	sgcn_t *sgcn_state;
1051708Sstevel static uchar_t	sgcn_stopped = FALSE;
1061708Sstevel static int	sgcn_timeout_period = 20;	/* time out in seconds */
1071708Sstevel 
1081708Sstevel /* streams structures */
1091708Sstevel static struct module_info minfo = {
1101708Sstevel 	SGCN_MI_IDNUM,	/* mi_idnum		*/
1111708Sstevel 	"sgcn",		/* mi_idname		*/
1121708Sstevel 	0,		/* mi_minpsz		*/
1131708Sstevel 	INFPSZ,		/* mi_maxpsz		*/
1141708Sstevel 	SGCN_MI_HIWAT,	/* mi_hiwat		*/
1151708Sstevel 	SGCN_MI_LOWAT	/* mi_lowat		*/
1161708Sstevel };
1171708Sstevel 
1181708Sstevel static struct qinit rinit = {
1191708Sstevel 	putq,		/* qi_putp		*/
1201708Sstevel 	sgcn_rsrv,	/* qi_srvp		*/
1211708Sstevel 	sgcn_open,	/* qi_qopen		*/
1221708Sstevel 	sgcn_close,	/* qi_qclose		*/
1231708Sstevel 	NULL,		/* qi_qadmin		*/
1241708Sstevel 	&minfo,		/* qi_minfo		*/
1251708Sstevel 	NULL		/* qi_mstat		*/
1261708Sstevel };
1271708Sstevel 
1281708Sstevel static struct qinit winit = {
1291708Sstevel 	sgcn_wput,	/* qi_putp		*/
1301708Sstevel 	sgcn_wsrv,	/* qi_srvp		*/
1311708Sstevel 	sgcn_open,	/* qi_qopen		*/
1321708Sstevel 	sgcn_close,	/* qi_qclose		*/
1331708Sstevel 	NULL,		/* qi_qadmin		*/
1341708Sstevel 	&minfo,		/* qi_minfo		*/
1351708Sstevel 	NULL		/* qi_mstat		*/
1361708Sstevel };
1371708Sstevel 
1381708Sstevel static struct streamtab sgcnstrinfo = {
1391708Sstevel 	&rinit,
1401708Sstevel 	&winit,
1411708Sstevel 	NULL,
1421708Sstevel 	NULL
1431708Sstevel };
1441708Sstevel 
1451708Sstevel /* standard device driver structures */
1461708Sstevel static struct cb_ops sgcn_cb_ops = {
1471708Sstevel 	nulldev,		/* open()		*/
1481708Sstevel 	nulldev,		/* close()		*/
1491708Sstevel 	nodev,			/* strategy()		*/
1501708Sstevel 	nodev,			/* print()		*/
1511708Sstevel 	nodev,			/* dump()		*/
1521708Sstevel 	nodev,			/* read()		*/
1531708Sstevel 	nodev,			/* write()		*/
1541708Sstevel 	nodev,			/* ioctl()		*/
1551708Sstevel 	nodev,			/* devmap()		*/
1561708Sstevel 	nodev,			/* mmap()		*/
1571708Sstevel 	nodev,			/* segmap()		*/
1581708Sstevel 	nochpoll,		/* poll()		*/
1591708Sstevel 	ddi_prop_op,		/* prop_op()		*/
1601708Sstevel 	&sgcnstrinfo,		/* cb_str		*/
1611708Sstevel 	D_MP | D_MTQPAIR		/* cb_flag		*/
1621708Sstevel };
1631708Sstevel 
1641708Sstevel static struct dev_ops sgcn_ops = {
1651708Sstevel 	DEVO_REV,
1661708Sstevel 	0,			/* refcnt		*/
1671708Sstevel 	sgcn_getinfo,		/* getinfo()		*/
1681708Sstevel 	nulldev,		/* identify()		*/
1691708Sstevel 	nulldev,		/* probe()		*/
1701708Sstevel 	sgcn_attach,		/* attach()		*/
1711708Sstevel 	sgcn_detach,		/* detach()		*/
1721708Sstevel 	nodev,			/* reset()		*/
1731708Sstevel 	&sgcn_cb_ops,		/* cb_ops		*/
1741708Sstevel 	(struct bus_ops *)NULL,	/* bus_ops		*/
1757656SSherry.Moore@Sun.COM 	NULL,			/* power()		*/
1767656SSherry.Moore@Sun.COM 	ddi_quiesce_not_supported,	/* quiesce	*/
1771708Sstevel };
1781708Sstevel 
1791708Sstevel static struct modldrv modldrv = {
1801708Sstevel 	&mod_driverops,
1817656SSherry.Moore@Sun.COM 	"Serengeti console driver",
1821708Sstevel 	&sgcn_ops
1831708Sstevel };
1841708Sstevel 
1851708Sstevel static struct modlinkage modlinkage = {
1861708Sstevel 	MODREV_1,
1871708Sstevel 	(void*)&modldrv,
1881708Sstevel 	NULL
1891708Sstevel };
1901708Sstevel 
1911708Sstevel 
1921708Sstevel /* driver configuration routines */
1931708Sstevel int
_init(void)1941708Sstevel _init(void)
1951708Sstevel {
1961708Sstevel 	int error;
1971708Sstevel 
1981708Sstevel 	sgcn_state = kmem_zalloc(sizeof (sgcn_t), KM_SLEEP);
1991708Sstevel 
2001708Sstevel 	error = mod_install(&modlinkage);
2011708Sstevel 
2021708Sstevel 	if (error == 0) {
2031708Sstevel 		mutex_init(&sgcn_state->sgcn_lock, NULL, MUTEX_DRIVER, NULL);
2041708Sstevel 	} else {
2051708Sstevel 		kmem_free(sgcn_state, sizeof (sgcn_t));
2061708Sstevel 	}
2071708Sstevel 
2081708Sstevel 	return (error);
2091708Sstevel }
2101708Sstevel 
2111708Sstevel int
_fini(void)2121708Sstevel _fini(void)
2131708Sstevel {
2141708Sstevel 	/* can't remove console driver */
2151708Sstevel 	return (EBUSY);
2161708Sstevel }
2171708Sstevel 
2181708Sstevel int
_info(struct modinfo * modinfop)2191708Sstevel _info(struct modinfo *modinfop)
2201708Sstevel {
2211708Sstevel 	return (mod_info(&modlinkage, modinfop));
2221708Sstevel }
2231708Sstevel 
2241708Sstevel /*
2251708Sstevel  * sgcn_attach is called at startup time.
2261708Sstevel  * There is only once instance of this driver.
2271708Sstevel  */
2281708Sstevel static int
sgcn_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)2291708Sstevel sgcn_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
2301708Sstevel {
2311708Sstevel 	extern int ddi_create_internal_pathname(
2321708Sstevel 	    dev_info_t *, char *, int, minor_t);
2331708Sstevel 	cnsram_header	header;
2341708Sstevel 	int		rv;
2351708Sstevel 
2361708Sstevel 	if (cmd != DDI_ATTACH)
2371708Sstevel 		return (DDI_FAILURE);
2381708Sstevel 
2391708Sstevel 	if (ddi_create_internal_pathname(dip, "sgcn", S_IFCHR, 0)
2401708Sstevel 	    != DDI_SUCCESS)
2411708Sstevel 		return (DDI_FAILURE);
2421708Sstevel 
2431708Sstevel 	/* prepare some data structures in soft state */
2441708Sstevel 	mutex_enter(&sgcn_state->sgcn_lock);
2451708Sstevel 
2461708Sstevel 	sgcn_state->sgcn_dip = dip;
2471708Sstevel 
2481708Sstevel 	mutex_exit(&sgcn_state->sgcn_lock);
2491708Sstevel 
2501708Sstevel 	/*
2511708Sstevel 	 * We need to verify IOSRAM is intact at startup time. If by
2521708Sstevel 	 * any chance IOSRAM is corrupted, that means SC is not ready.
2531708Sstevel 	 * All we can do is stopping.
2541708Sstevel 	 */
2551708Sstevel 	rv = iosram_read(SBBC_CONSOLE_KEY, 0, (caddr_t)&header,
2567656SSherry.Moore@Sun.COM 	    sizeof (cnsram_header));
2571708Sstevel 	if (rv != 0)
2581708Sstevel 		cmn_err(CE_PANIC, "sgcn_attach(): Reading from IOSRAM failed");
2591708Sstevel 	if (header.cnsram_magic != CNSRAM_MAGIC)
2601708Sstevel 		cmn_err(CE_PANIC, "sgcn_attach(): Wrong IOSRAM console buffer");
2611708Sstevel 	if (!header.cnsram_in_end && !header.cnsram_in_begin)
2621708Sstevel 		cmn_err(CE_PANIC, "sgcn_attach(): Wrong IOSRAM input buffer");
2631708Sstevel 	if (!header.cnsram_out_end && !header.cnsram_out_begin)
2641708Sstevel 		cmn_err(CE_PANIC, "sgcn_attach(): Wrong IOSRAM output buffer");
2651708Sstevel 	/*
2661708Sstevel 	 * XXX need to add extra check for version no.
2671708Sstevel 	 */
2681708Sstevel 
2691708Sstevel 	/* Allocate console input buffer */
2701708Sstevel 	sgcn_state->sgcn_inbuf_size =
2717656SSherry.Moore@Sun.COM 	    header.cnsram_in_end - header.cnsram_in_begin;
2721708Sstevel 	sgcn_state->sgcn_inbuf =
2737656SSherry.Moore@Sun.COM 	    kmem_alloc(sgcn_state->sgcn_inbuf_size, KM_SLEEP);
2741708Sstevel #ifdef SGCN_DEBUG
2751708Sstevel 	prom_printf("Allocated %d(0x%X) bytes for console\n",
2767656SSherry.Moore@Sun.COM 	    sgcn_state->sgcn_inbuf_size);
2771708Sstevel #endif
2781708Sstevel 
2791708Sstevel 	(void) prom_serengeti_set_console_input(SGCN_CLNT_STR);
2801708Sstevel 
2811708Sstevel 	abort_seq_handler = sg_abort_seq_handler;
2821708Sstevel 
2831708Sstevel #ifdef SGCN_DEBUG
2841708Sstevel 	prom_printf("sgcn_attach(): SGCN driver attached\n");
2851708Sstevel #endif
2861708Sstevel 	return (DDI_SUCCESS);
2871708Sstevel 
2881708Sstevel }
2891708Sstevel 
2901708Sstevel /* ARGSUSED */
2911708Sstevel static int
sgcn_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)2921708Sstevel sgcn_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
2931708Sstevel {
2941708Sstevel 
2951708Sstevel 	if (cmd == DDI_DETACH)
2961708Sstevel 		return (DDI_FAILURE);
2971708Sstevel 
2981708Sstevel #ifdef SGCN_DEBUG
2991708Sstevel 	prom_printf("sgcn_detach(): SGCN driver detached\n");
3001708Sstevel #endif
3011708Sstevel 	return (DDI_SUCCESS);
3021708Sstevel }
3031708Sstevel 
3041708Sstevel /* ARGSUSED */
3051708Sstevel static int
sgcn_getinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)3061708Sstevel sgcn_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
3071708Sstevel {
3081708Sstevel 	int error = DDI_FAILURE;
3091708Sstevel 
3101708Sstevel 	switch (infocmd) {
3111708Sstevel 	case DDI_INFO_DEVT2DEVINFO:
3121708Sstevel 		if (sgcn_state) {
3131708Sstevel 			*result = (void *) sgcn_state->sgcn_dip;
3141708Sstevel 			error = DDI_SUCCESS;
3151708Sstevel 		}
3161708Sstevel 		break;
3171708Sstevel 
3181708Sstevel 	case DDI_INFO_DEVT2INSTANCE:
3191708Sstevel 		if (getminor((dev_t)arg) == 0) {
3201708Sstevel 			*result = (void *)0;
3211708Sstevel 			error = DDI_SUCCESS;
3221708Sstevel 		}
3231708Sstevel 		break;
3241708Sstevel 	}
3251708Sstevel 
3261708Sstevel 	return (error);
3271708Sstevel }
3281708Sstevel 
3291708Sstevel /* streams open & close */
3301708Sstevel /* ARGSUSED */
3311708Sstevel static int
sgcn_open(queue_t * q,dev_t * devp,int oflag,int sflag,cred_t * credp)3321708Sstevel sgcn_open(queue_t *q, dev_t *devp, int oflag, int sflag, cred_t *credp)
3331708Sstevel {
3341708Sstevel 	tty_common_t	*tty;
3351708Sstevel 	int		unit = getminor(*devp);
3361708Sstevel 
3371708Sstevel 	if (unit != 0)
3381708Sstevel 		return (ENXIO);
3391708Sstevel 
3401708Sstevel 	/* stream already open */
3411708Sstevel 	if (q->q_ptr) {
3421708Sstevel 		return (DDI_SUCCESS);
3431708Sstevel 	}
3441708Sstevel 
3451708Sstevel 	if (!sgcn_state) {
3461708Sstevel 		cmn_err(CE_WARN, "sgcn_open(): sgcn is not configured by\
3471708Sstevel 				autoconfig\n");
3481708Sstevel 		return (ENXIO);
3491708Sstevel 	}
3501708Sstevel 
3511708Sstevel 	mutex_enter(&sgcn_state->sgcn_lock);
3521708Sstevel 	tty = &(sgcn_state->sgcn_tty);
3531708Sstevel 
3541708Sstevel 	tty->t_readq = q;
3551708Sstevel 	tty->t_writeq = WR(q);
3561708Sstevel 
3571708Sstevel 	/* Link the RD and WR Q's */
3581708Sstevel 
3591708Sstevel 	q->q_ptr = WR(q)->q_ptr = (caddr_t)sgcn_state;
3601708Sstevel 	sgcn_state->sgcn_readq = RD(q);
3611708Sstevel 	sgcn_state->sgcn_writeq = WR(q);
3621708Sstevel 	qprocson(q);
3631708Sstevel 
3641708Sstevel 	mutex_exit(&sgcn_state->sgcn_lock);
3651708Sstevel 
3661708Sstevel 	/* initialize interrupt handler */
367*11311SSurya.Prakki@Sun.COM 	(void) iosram_reg_intr(SBBC_CONSOLE_IN,
3681708Sstevel 	    (sbbc_intrfunc_t)sgcn_data_in_handler, NULL,
3691708Sstevel 	    &sgcn_state->sgcn_sbbc_in_state,
3701708Sstevel 	    &sgcn_state->sgcn_sbbc_in_lock);
371*11311SSurya.Prakki@Sun.COM 	(void) iosram_reg_intr(SBBC_CONSOLE_SPACE_OUT,
3721708Sstevel 	    (sbbc_intrfunc_t)sgcn_space_2_out_handler, NULL,
3731708Sstevel 	    &sgcn_state->sgcn_sbbc_outspace_state,
3741708Sstevel 	    &sgcn_state->sgcn_sbbc_outspace_lock);
375*11311SSurya.Prakki@Sun.COM 	(void) iosram_reg_intr(SBBC_CONSOLE_BRK,
3761708Sstevel 	    (sbbc_intrfunc_t)sgcn_break_handler, NULL,
3771708Sstevel 	    &sgcn_state->sgcn_sbbc_brk_state,
3781708Sstevel 	    &sgcn_state->sgcn_sbbc_brk_lock);
3791708Sstevel 
3801708Sstevel 	return (DDI_SUCCESS);
3811708Sstevel }
3821708Sstevel 
3831708Sstevel /* ARGSUSED */
3841708Sstevel static int
sgcn_close(queue_t * q,int flag,cred_t * credp)3851708Sstevel sgcn_close(queue_t *q, int flag, cred_t *credp)
3861708Sstevel {
3871708Sstevel 	int ret;
3881708Sstevel 
3891708Sstevel 	ASSERT(sgcn_state == q->q_ptr);
3901708Sstevel 
3911708Sstevel 	if (sgcn_state->sgcn_wbufcid != 0) {
3921708Sstevel 		unbufcall(sgcn_state->sgcn_wbufcid);
3931708Sstevel 	}
3941708Sstevel 
3951708Sstevel 	ret = iosram_unreg_intr(SBBC_CONSOLE_BRK);
3961708Sstevel 	ASSERT(ret == 0);
3971708Sstevel 
3981708Sstevel 	ret = iosram_unreg_intr(SBBC_CONSOLE_SPACE_OUT);
3991708Sstevel 	ASSERT(ret == 0);
4001708Sstevel 
4011708Sstevel 	ret = iosram_unreg_intr(SBBC_CONSOLE_IN);
4021708Sstevel 	ASSERT(ret == 0);
4031708Sstevel 
4041708Sstevel 	ttycommon_close(&sgcn_state->sgcn_tty);
4051708Sstevel 
4061708Sstevel 	qprocsoff(q);
4071708Sstevel 	q->q_ptr = WR(q)->q_ptr = NULL;
4081708Sstevel 	sgcn_state->sgcn_readq = NULL;
4091708Sstevel 	sgcn_state->sgcn_writeq = NULL;
4101708Sstevel 
4111708Sstevel 	return (DDI_SUCCESS);
4121708Sstevel }
4131708Sstevel 
4141708Sstevel /*
4151708Sstevel  * Put procedure for write queue.
4161708Sstevel  * Respond to M_IOCTL, M_DATA and M_FLUSH messages here;
4171708Sstevel  * It put's the data onto internal sgcn_output_q.
4181708Sstevel  */
4191708Sstevel static int
sgcn_wput(queue_t * q,mblk_t * mp)4201708Sstevel sgcn_wput(queue_t *q, mblk_t *mp)
4211708Sstevel {
4221708Sstevel 
4231708Sstevel #ifdef SGCN_DEBUG
4241708Sstevel 	struct iocblk *iocp;
4251708Sstevel 	int i;
4261708Sstevel #endif
4271708Sstevel 
4281708Sstevel 	ASSERT(sgcn_state == q->q_ptr);
4291708Sstevel 
4301708Sstevel 	if (!mp->b_datap) {
4311708Sstevel 		cmn_err(CE_PANIC, "sgcn_wput(): null datap");
4321708Sstevel 	}
4331708Sstevel 
4341708Sstevel #ifdef SGCN_DEBUG
4351708Sstevel 	prom_printf("sgcn_wput(): SGCN wput q=%X mp=%X rd=%X wr=%X type=%X\n",
4367656SSherry.Moore@Sun.COM 	    q, mp, mp->b_rptr, mp->b_wptr, mp->b_datap->db_type);
4371708Sstevel #endif
4381708Sstevel 
4391708Sstevel 	switch (mp->b_datap->db_type) {
4401708Sstevel 	case M_IOCTL:
4411708Sstevel 	case M_CTL:
4421708Sstevel #ifdef SGCN_DEBUG
4431708Sstevel 		iocp = (struct iocblk *)mp->b_rptr;
4441708Sstevel 		prom_printf("sgcn_wput(): M_IOCTL cmd=%X TIOC=%X\n",
4457656SSherry.Moore@Sun.COM 		    iocp->ioc_cmd, TIOC);
4461708Sstevel #endif
4471708Sstevel 		switch (((struct iocblk *)mp->b_rptr)->ioc_cmd) {
4481708Sstevel 		case TCSETSW:
4491708Sstevel 		case TCSETSF:
4501708Sstevel 		case TCSETAW:
4511708Sstevel 		case TCSETAF:
4521708Sstevel 		case TCSBRK:
4531708Sstevel 			/*
4541708Sstevel 			 * The change do not take effect until all
4551708Sstevel 			 * output queued before them is drained.
4561708Sstevel 			 * Put this message on the queue, so that
4571708Sstevel 			 * "sgcn_start" will see it when it's done
4581708Sstevel 			 * with the output before it. Poke the start
4591708Sstevel 			 * routine, just in case.
4601708Sstevel 			 */
461*11311SSurya.Prakki@Sun.COM 			(void) putq(q, mp);
4621708Sstevel 			sgcn_start();
4631708Sstevel 			break;
4641708Sstevel 		default:
4651708Sstevel 			sgcn_ioctl(q, mp);
4661708Sstevel 		}
4671708Sstevel 		break;
4681708Sstevel 
4691708Sstevel 	case M_FLUSH:
4701708Sstevel 		if (*mp->b_rptr & FLUSHW) {
4711708Sstevel 			flushq(q, FLUSHDATA);
4721708Sstevel 			*mp->b_rptr &= ~FLUSHW;
4731708Sstevel 		}
4741708Sstevel 		if (*mp->b_rptr & FLUSHR) {
4751708Sstevel 			flushq(RD(q), FLUSHDATA);
4761708Sstevel 			qreply(q, mp);
4771708Sstevel 		} else {
4781708Sstevel 			freemsg(mp);
4791708Sstevel 		}
4801708Sstevel 		break;
4811708Sstevel 
4821708Sstevel 	case M_STOP:
4831708Sstevel 		sgcn_stopped = TRUE;
4841708Sstevel 		freemsg(mp);
4851708Sstevel 		break;
4861708Sstevel 
4871708Sstevel 	case M_START:
4881708Sstevel 		sgcn_stopped = FALSE;
4891708Sstevel 		freemsg(mp);
4901708Sstevel 		qenable(q);	/* Start up delayed messages */
4911708Sstevel 		break;
4921708Sstevel 
4931708Sstevel 	case M_DATA:
4941708Sstevel 		/*
4951708Sstevel 		 * Queue the message up to be transmitted,
4961708Sstevel 		 * and poke the start routine.
4971708Sstevel 		 */
4981708Sstevel #ifdef SGCN_DEBUG
4991708Sstevel 		if (mp->b_rptr < mp->b_wptr) {
5001708Sstevel 		prom_printf("sgcn_wput(): DATA q=%X mp=%X rd=%X wr=%X\n",
5017656SSherry.Moore@Sun.COM 		    q, mp, mp->b_rptr, mp->b_wptr);
5021708Sstevel 		prom_printf("sgcn_wput(): [[[[[");
5031708Sstevel 		for (i = 0; i < mp->b_wptr-mp->b_rptr; i++) {
5041708Sstevel 			prom_printf("%c", *(mp->b_rptr+i));
5051708Sstevel 		}
5061708Sstevel 		prom_printf("]]]]]\n");
5071708Sstevel 		}
5081708Sstevel #endif /* SGCN_DEBUG */
5091708Sstevel 		(void) putq(q, mp);
5101708Sstevel 		sgcn_start();
5111708Sstevel 		break;
5121708Sstevel 
5131708Sstevel 	default:
5141708Sstevel 		freemsg(mp);
5151708Sstevel 	}
5161708Sstevel 
5171708Sstevel 	return (0);
5181708Sstevel }
5191708Sstevel 
5201708Sstevel /*
5211708Sstevel  * Process an "ioctl" message sent down to us.
5221708Sstevel  */
5231708Sstevel static void
sgcn_ioctl(queue_t * q,mblk_t * mp)5241708Sstevel sgcn_ioctl(queue_t *q, mblk_t *mp)
5251708Sstevel {
5261708Sstevel 	struct iocblk	*iocp;
5271708Sstevel 	tty_common_t	*tty;
5281708Sstevel 	mblk_t		*datamp;
5291708Sstevel 	int		data_size;
5301708Sstevel 	int		error = 0;
5311708Sstevel 
5321708Sstevel #ifdef SGCN_DEBUG
5331708Sstevel 	prom_printf("sgcn_ioctl(): q=%X mp=%X\n", q, mp);
5341708Sstevel #endif
5351708Sstevel 	iocp = (struct iocblk *)mp->b_rptr;
5361708Sstevel 	tty = &(sgcn_state->sgcn_tty);
5371708Sstevel 
5381708Sstevel 	if (tty->t_iocpending != NULL) {
5391708Sstevel 		freemsg(tty->t_iocpending);
5401708Sstevel 		tty->t_iocpending = NULL;
5411708Sstevel 	}
5421708Sstevel 	data_size = ttycommon_ioctl(tty, q, mp, &error);
5431708Sstevel 	if (data_size != 0) {
5441708Sstevel 		if (sgcn_state->sgcn_wbufcid)
5451708Sstevel 			unbufcall(sgcn_state->sgcn_wbufcid);
5461708Sstevel 		/* call sgcn_reioctl() */
5471708Sstevel 		sgcn_state->sgcn_wbufcid =
5487656SSherry.Moore@Sun.COM 		    bufcall(data_size, BPRI_HI, sgcn_reioctl, sgcn_state);
5491708Sstevel 		return;
5501708Sstevel 	}
5511708Sstevel 
5521708Sstevel 	if (error < 0) {
5531708Sstevel 		iocp = (struct iocblk *)mp->b_rptr;
5541708Sstevel 		/*
5551708Sstevel 		 * "ttycommon_ioctl" didn't do anything; we process it here.
5561708Sstevel 		 */
5571708Sstevel 		error = 0;
5581708Sstevel 		switch (iocp->ioc_cmd) {
5591708Sstevel 		case TCSBRK:
5601708Sstevel 		case TIOCSBRK:
5611708Sstevel 		case TIOCCBRK:
5621708Sstevel 		case TIOCMSET:
5631708Sstevel 		case TIOCMBIS:
5641708Sstevel 		case TIOCMBIC:
5651708Sstevel 			if (iocp->ioc_count != TRANSPARENT)
5661708Sstevel 				mioc2ack(mp, NULL, 0, 0);
5671708Sstevel 			else
5681708Sstevel 				mcopyin(mp, NULL, sizeof (int), NULL);
5691708Sstevel 			break;
5701708Sstevel 
5711708Sstevel 		case TIOCMGET:
5721708Sstevel 			datamp = allocb(sizeof (int), BPRI_MED);
5731708Sstevel 			if (datamp == NULL) {
5741708Sstevel 				error = EAGAIN;
5751708Sstevel 				break;
5761708Sstevel 			}
5771708Sstevel 
5781708Sstevel 			*(int *)datamp->b_rptr = 0;
5791708Sstevel 
5801708Sstevel 			if (iocp->ioc_count != TRANSPARENT)
5811708Sstevel 				mioc2ack(mp, datamp, sizeof (int), 0);
5821708Sstevel 			else
5831708Sstevel 				mcopyout(mp, NULL, sizeof (int), NULL, datamp);
5841708Sstevel 			break;
5851708Sstevel 
5861708Sstevel 		default:
5871708Sstevel 			error = EINVAL;
5881708Sstevel 			break;
5891708Sstevel 		}
5901708Sstevel 	}
5911708Sstevel 	if (error != 0) {
5921708Sstevel 		iocp->ioc_count = 0;
5931708Sstevel 		iocp->ioc_error = error;
5941708Sstevel 		mp->b_datap->db_type = M_IOCNAK;
5951708Sstevel 	}
5961708Sstevel 	qreply(q, mp);
5971708Sstevel }
5981708Sstevel 
5991708Sstevel static void
sgcn_reioctl(void * unit)6001708Sstevel sgcn_reioctl(void *unit)
6011708Sstevel {
6021708Sstevel 	queue_t		*q;
6031708Sstevel 	mblk_t		*mp;
6041708Sstevel 	sgcn_t		*sgcnp = (sgcn_t *)unit;
6051708Sstevel 
6061708Sstevel 	if (!sgcnp->sgcn_wbufcid) {
6071708Sstevel 		return;
6081708Sstevel 	}
6091708Sstevel 	sgcnp->sgcn_wbufcid = 0;
6101708Sstevel 	if ((q = sgcnp->sgcn_tty.t_writeq) == NULL) {
6111708Sstevel 		return;
6121708Sstevel 	}
6131708Sstevel 
6141708Sstevel 	if ((mp = sgcnp->sgcn_tty.t_iocpending) != NULL) {
6151708Sstevel 		sgcnp->sgcn_tty.t_iocpending = NULL;
6161708Sstevel 		sgcn_ioctl(q, mp);
6171708Sstevel 	}
6181708Sstevel }
6191708Sstevel 
6201708Sstevel static void
sgcn_start()6211708Sstevel sgcn_start()
6221708Sstevel {
6231708Sstevel 
6241708Sstevel 	queue_t *q;
6251708Sstevel 	mblk_t *mp;
6261708Sstevel 	int retval;
6271708Sstevel 
6281708Sstevel 	/*
6291708Sstevel 	 * read stream queue and remove data from the queue and
6301708Sstevel 	 * transmit them if possible
6311708Sstevel 	 */
6321708Sstevel 	q = sgcn_state->sgcn_writeq;
6331708Sstevel 	ASSERT(q != NULL);
6341708Sstevel 	while (mp = getq(q)) {
6351708Sstevel 		switch (mp->b_datap->db_type) {
6361708Sstevel 		case M_IOCTL:
6371708Sstevel 			/*
6381708Sstevel 			 * These are those IOCTLs queued up
6391708Sstevel 			 * do it now
6401708Sstevel 			 */
6411708Sstevel 			sgcn_ioctl(q, mp);
6421708Sstevel 			continue;
6431708Sstevel 		default:
6441708Sstevel 			/*
6451708Sstevel 			 * M_DATA
6461708Sstevel 			 * Copy it from stream queue buffer to
6471708Sstevel 			 * sgcn buffer
6481708Sstevel 			 */
6491708Sstevel 			retval = sgcn_transmit(q, mp);
6501708Sstevel 
6511708Sstevel 			if (retval == EBUSY) {
6521708Sstevel 				/*
6531708Sstevel 				 * Console output buffer is full for
6541708Sstevel 				 * sgcn_timeout_period seconds, assume
6551708Sstevel 				 * SC is dead, drop all console output
6561708Sstevel 				 * data from stream queue.
6571708Sstevel 				 */
6581708Sstevel 				if (sgcn_state->sgcn_sc_active <
6591708Sstevel 				    gethrestime_sec() - sgcn_timeout_period)
6601708Sstevel 					sgcn_flush();
6611708Sstevel 				return;
6621708Sstevel 			} else if (retval == EAGAIN) {
6631708Sstevel 				/*
6641708Sstevel 				 * Console output just became full
6651708Sstevel 				 * return
6661708Sstevel 				 */
6671708Sstevel 				mutex_enter(&sgcn_state->sgcn_lock);
6681708Sstevel 				sgcn_state->sgcn_sc_active = gethrestime_sec();
6691708Sstevel 				mutex_exit(&sgcn_state->sgcn_lock);
6701708Sstevel 				return;
6711708Sstevel 			} else {
6721708Sstevel 				/* send more console output */
6731708Sstevel 				mutex_enter(&sgcn_state->sgcn_lock);
6741708Sstevel 				sgcn_state->sgcn_sc_active = gethrestime_sec();
6751708Sstevel 				mutex_exit(&sgcn_state->sgcn_lock);
6761708Sstevel 			}
6771708Sstevel 		} /* switch */
6781708Sstevel 	}
6791708Sstevel 
6801708Sstevel }
6811708Sstevel 
6821708Sstevel static int
sgcn_transmit(queue_t * q,mblk_t * mp)6831708Sstevel sgcn_transmit(queue_t *q, mblk_t *mp)
6841708Sstevel {
6851708Sstevel 	caddr_t		buf;
6861708Sstevel 	mblk_t		*bp;
6871708Sstevel 	int		len, oldlen;
6881708Sstevel 
6891708Sstevel #ifdef SGCN_DEBUG
6901708Sstevel 	prom_printf("sgcn_transmit(): q=%X mp=%X\n", q, mp);
6911708Sstevel #endif
6921708Sstevel 	do {
6931708Sstevel 		bp = mp;
6941708Sstevel 		oldlen = len = bp->b_wptr - bp->b_rptr;
6951708Sstevel 		buf = (caddr_t)bp->b_rptr;
6961708Sstevel 		len = CONSOLE_WRITE(buf, len);
6971708Sstevel 		if (len > 0)
698*11311SSurya.Prakki@Sun.COM 			(void) iosram_send_intr(SBBC_CONSOLE_OUT);
6991708Sstevel 		if (len >= 0 && len < oldlen) {
7001708Sstevel 			/* IOSRAM is full, we are not done with mp yet */
7011708Sstevel 			bp->b_rptr += len;
7021708Sstevel 			(void) putbq(q, mp);
7031708Sstevel 			if (len)
7041708Sstevel 				return (EAGAIN);
7051708Sstevel 			else
7061708Sstevel 				return (EBUSY);
7071708Sstevel 		}
7081708Sstevel 		mp = bp->b_cont;
7091708Sstevel 		freeb(bp);
7101708Sstevel 	} while (mp);
7111708Sstevel 
7121708Sstevel 	return (0);
7131708Sstevel }
7141708Sstevel 
7151708Sstevel /*
7161708Sstevel  * called when SC first establishes console connection
7171708Sstevel  * drop all the data on the output queue
7181708Sstevel  */
7191708Sstevel static void
sgcn_flush()7201708Sstevel sgcn_flush()
7211708Sstevel {
7221708Sstevel 	queue_t *q;
7231708Sstevel 	mblk_t *mp;
7241708Sstevel 
7251708Sstevel 	q = sgcn_state->sgcn_writeq;
7261708Sstevel 
7271708Sstevel 	prom_printf("sgcn_flush(): WARNING console output is dropped "
7287656SSherry.Moore@Sun.COM 	    "time=%lX\n", gethrestime_sec());
7291708Sstevel 	while (mp = getq(q)) {
7301708Sstevel 		freemsg(mp);
7311708Sstevel 	}
7321708Sstevel 
7331708Sstevel }
7341708Sstevel 
7351708Sstevel uint64_t sgcn_input_dropped;
7361708Sstevel 
7371708Sstevel /*
7381708Sstevel  * Interrupt handlers
7391708Sstevel  * All handlers register with SBBC driver and must follow SBBC interrupt
7401708Sstevel  * delivery conventions.
7411708Sstevel  */
7421708Sstevel /*
7431708Sstevel  * SC sends an interrupt when new data comes in
7441708Sstevel  */
7451708Sstevel /* ARGSUSED */
7461708Sstevel void
sgcn_data_in_handler(caddr_t arg)7471708Sstevel sgcn_data_in_handler(caddr_t arg)
7481708Sstevel {
7491708Sstevel 	caddr_t		buf = sgcn_state->sgcn_inbuf;
7501708Sstevel 	int		i, len;
7511708Sstevel 	mblk_t		*mp;
7521708Sstevel 
7531708Sstevel 	/*
7541708Sstevel 	 * change interrupt state so that SBBC won't trigger
7551708Sstevel 	 * another one.
7561708Sstevel 	 */
7571708Sstevel 	mutex_enter(&sgcn_state->sgcn_sbbc_in_lock);
7581708Sstevel 	sgcn_state->sgcn_sbbc_in_state = SBBC_INTR_RUNNING;
7591708Sstevel 	mutex_exit(&sgcn_state->sgcn_sbbc_in_lock);
7601708Sstevel 
7611708Sstevel 	/* update sgcn_state for SC activity information */
7621708Sstevel 	mutex_enter(&sgcn_state->sgcn_lock);
7631708Sstevel 	sgcn_state->sgcn_sc_active = gethrestime_sec();
7641708Sstevel 	mutex_exit(&sgcn_state->sgcn_lock);
7651708Sstevel 
7661708Sstevel 	/* enter our perimeter */
7671708Sstevel 	entersq(sgcn_state->sgcn_readq->q_syncq, SQ_CALLBACK);
7681708Sstevel 
7691708Sstevel 	for (;;) {
7701708Sstevel 
7711708Sstevel 		/* read from console input IOSRAM */
7721708Sstevel 		len = CONSOLE_READ(buf, sgcn_state->sgcn_inbuf_size);
7731708Sstevel 
7741708Sstevel 		if (len <= 0) {
7751708Sstevel 
7761708Sstevel 			mutex_enter(&sgcn_state->sgcn_sbbc_in_lock);
7771708Sstevel 
7781708Sstevel 			len = CONSOLE_READ(buf, sgcn_state->sgcn_inbuf_size);
7791708Sstevel 
7801708Sstevel 			if (len <= 0) {
7811708Sstevel 				sgcn_state->sgcn_sbbc_in_state = SBBC_INTR_IDLE;
7821708Sstevel 				mutex_exit(&sgcn_state->sgcn_sbbc_in_lock);
7831708Sstevel 
7841708Sstevel 				/* leave our perimeter */
7851708Sstevel 				leavesq(sgcn_state->sgcn_readq->q_syncq,
7867656SSherry.Moore@Sun.COM 				    SQ_CALLBACK);
7871708Sstevel 				return;
7881708Sstevel 			} else {
7891708Sstevel 				mutex_exit(&sgcn_state->sgcn_sbbc_in_lock);
7901708Sstevel 			}
7911708Sstevel 
7921708Sstevel 		}
7931708Sstevel 
794*11311SSurya.Prakki@Sun.COM 		(void) iosram_send_intr(SBBC_CONSOLE_SPACE_IN);
7951708Sstevel 
7961708Sstevel 		if (abort_enable == KIOCABORTALTERNATE) {
7971708Sstevel 			for (i = 0; i < len; i ++) {
7981708Sstevel 				if (abort_charseq_recognize(buf[i]))
7991708Sstevel 					abort_sequence_enter((char *)NULL);
8001708Sstevel 			}
8011708Sstevel 		}
8021708Sstevel 
8031708Sstevel 		/* put console input onto stream */
8041708Sstevel 		if (sgcn_state->sgcn_readq) {
8051708Sstevel 			if ((mp = allocb(len, BPRI_MED)) == (mblk_t *)NULL) {
8061708Sstevel 				sgcn_input_dropped += len;
8071708Sstevel 				cmn_err(CE_WARN,
8081708Sstevel 				    "sgcn_data_in_handler(): allocb failed"
8091708Sstevel 				    " (console input dropped.)");
8101708Sstevel 			} else {
8111708Sstevel 				bcopy(buf, mp->b_wptr, len);
8121708Sstevel 				mp->b_wptr += len;
8131708Sstevel 				putnext(sgcn_state->sgcn_readq, mp);
8141708Sstevel 			}
8151708Sstevel 		}
8161708Sstevel 	}
8171708Sstevel 
8181708Sstevel }
8191708Sstevel 
8201708Sstevel /*
8211708Sstevel  * SC sends an interrupt when it takes output data
8221708Sstevel  * from a full IOSRAM
8231708Sstevel  */
8241708Sstevel /* ARGSUSED */
8251708Sstevel void
sgcn_space_2_out_handler(caddr_t arg)8261708Sstevel sgcn_space_2_out_handler(caddr_t arg)
8271708Sstevel {
8281708Sstevel 	/*
8291708Sstevel 	 * change interrupt state so that SBBC won't trigger
8301708Sstevel 	 * another one.
8311708Sstevel 	 */
8321708Sstevel 	mutex_enter(&sgcn_state->sgcn_sbbc_outspace_lock);
8331708Sstevel 	sgcn_state->sgcn_sbbc_outspace_state = SBBC_INTR_RUNNING;
8341708Sstevel 	mutex_exit(&sgcn_state->sgcn_sbbc_outspace_lock);
8351708Sstevel 
8361708Sstevel 	mutex_enter(&sgcn_state->sgcn_lock);
8371708Sstevel 	sgcn_state->sgcn_sc_active = gethrestime_sec();
8381708Sstevel 	mutex_exit(&sgcn_state->sgcn_lock);
8391708Sstevel 
8401708Sstevel 	if (sgcn_state->sgcn_writeq != NULL)
8411708Sstevel 		qenable(sgcn_state->sgcn_writeq);
8421708Sstevel 
8431708Sstevel 	/* restore interrupt state */
8441708Sstevel 	mutex_enter(&sgcn_state->sgcn_sbbc_outspace_lock);
8451708Sstevel 	sgcn_state->sgcn_sbbc_outspace_state = SBBC_INTR_IDLE;
8461708Sstevel 	mutex_exit(&sgcn_state->sgcn_sbbc_outspace_lock);
8471708Sstevel }
8481708Sstevel 
8491708Sstevel /*
8501708Sstevel  * SC sends an interrupt when it detects BREAK sequence
8511708Sstevel  */
8521708Sstevel /* ARGSUSED */
8531708Sstevel void
sgcn_break_handler(caddr_t arg)8541708Sstevel sgcn_break_handler(caddr_t arg)
8551708Sstevel {
8561708Sstevel 	/*
8571708Sstevel 	 * change interrupt state so that SBBC won't trigger
8581708Sstevel 	 * another one.
8591708Sstevel 	 */
8601708Sstevel 	mutex_enter(&sgcn_state->sgcn_sbbc_brk_lock);
8611708Sstevel 	sgcn_state->sgcn_sbbc_brk_state = SBBC_INTR_RUNNING;
8621708Sstevel 	mutex_exit(&sgcn_state->sgcn_sbbc_brk_lock);
8631708Sstevel 
8641708Sstevel 	if (abort_enable != KIOCABORTALTERNATE)
8651708Sstevel 		abort_sequence_enter((char *)NULL);
8661708Sstevel 
8671708Sstevel 	/* restore interrupt state */
8681708Sstevel 	mutex_enter(&sgcn_state->sgcn_sbbc_brk_lock);
8691708Sstevel 	sgcn_state->sgcn_sbbc_brk_state = SBBC_INTR_IDLE;
8701708Sstevel 	mutex_exit(&sgcn_state->sgcn_sbbc_brk_lock);
8711708Sstevel }
8721708Sstevel 
8731708Sstevel /*
8741708Sstevel  * reporting errors in console driver sgcn.
8751708Sstevel  * since we can not trust console driver at this time, we need to
8761708Sstevel  * log errors in other system logs
8771708Sstevel  * error codes:
8781708Sstevel  *	EIO - iosram interface failed
8791708Sstevel  *	EPROTO - IOSRAM is corrupted
8801708Sstevel  *	EINVAL - invalid argument
8811708Sstevel  */
8821708Sstevel #define	SGCN_MAX_ERROR		100
8831708Sstevel static void
sgcn_log_error(int when,int what)8841708Sstevel sgcn_log_error(int when, int what)
8851708Sstevel {
8861708Sstevel 	char error_msg[256], error_code[256];
8871708Sstevel 	static uint_t	error_counter = 0;
8881708Sstevel 
8891708Sstevel 	error_counter ++;
8901708Sstevel 
8911708Sstevel 	if (error_counter > SGCN_MAX_ERROR) {
8921708Sstevel 		error_counter = 0;
893*11311SSurya.Prakki@Sun.COM 		(void) strcpy(error_msg, "!Too many sgcn errors");
8941708Sstevel 	} else {
8951708Sstevel 		(void) sprintf(error_code, "Error %d", what);
8961708Sstevel 
8971708Sstevel 		(void) sprintf(error_msg, "!%s at %s",
8981708Sstevel 		    (what == EIO) ? "IOSRAM interface failed" :
8991708Sstevel 		    (what == EPROTO) ? "IOSRAM corrupted" :
9001708Sstevel 		    (what == EINVAL) ? "Invalid argument" :
9011708Sstevel 		    error_code,
9021708Sstevel 		    (when == RW_CONSOLE_READ) ? "console input" :
9031708Sstevel 		    (when == RW_CONSOLE_WRITE) ? "console output, dropped" :
9041708Sstevel 		    "console I/O");
9051708Sstevel 	}
9061708Sstevel 
9071708Sstevel 	cmn_err(CE_WARN, error_msg);
9081708Sstevel }
9091708Sstevel 
9101708Sstevel static int
sgcn_read_header(int rw,cnsram_header * header)9111708Sstevel sgcn_read_header(int rw, cnsram_header *header)
9121708Sstevel {
9131708Sstevel 	int	rv;
9141708Sstevel 
9151708Sstevel 	/* check IOSRAM contents and read pointers */
9161708Sstevel 	rv = iosram_read(SBBC_CONSOLE_KEY, 0, (caddr_t)header,
9177656SSherry.Moore@Sun.COM 	    sizeof (cnsram_header));
9181708Sstevel 	if (rv != 0) {
9191708Sstevel 		return (-1);
9201708Sstevel 	}
9211708Sstevel 
9221708Sstevel 	/*
9231708Sstevel 	 * Since the header is read in a byte-by-byte fashion
9241708Sstevel 	 * using ddi_rep_get8, we need to re-read the producer
9251708Sstevel 	 * or consumer pointer as integer in case it has changed
9261708Sstevel 	 * after part of the previous value has been read.
9271708Sstevel 	 */
9281708Sstevel 	if (rw == RW_CONSOLE_READ) {
9291708Sstevel 		rv = iosram_read(SBBC_CONSOLE_KEY,
9307656SSherry.Moore@Sun.COM 		    OFFSETOF((*header), cnsram_in_wrptr),
9317656SSherry.Moore@Sun.COM 		    POINTER((*header), cnsram_in_wrptr),
9327656SSherry.Moore@Sun.COM 		    sizeof (header->cnsram_in_wrptr));
9331708Sstevel 	} else if (rw == RW_CONSOLE_WRITE) {
9341708Sstevel 		rv = iosram_read(SBBC_CONSOLE_KEY,
9357656SSherry.Moore@Sun.COM 		    OFFSETOF((*header), cnsram_out_rdptr),
9367656SSherry.Moore@Sun.COM 		    POINTER((*header), cnsram_out_rdptr),
9377656SSherry.Moore@Sun.COM 		    sizeof (header->cnsram_out_rdptr));
9381708Sstevel 	} else
9391708Sstevel 		rv = -1;
9401708Sstevel 
9411708Sstevel 	return (rv);
9421708Sstevel }
9431708Sstevel 
9441708Sstevel static int
sgcn_rw(int rw,caddr_t buf,int len)9451708Sstevel sgcn_rw(int rw, caddr_t buf, int len)
9461708Sstevel {
9471708Sstevel 	cnsram_header	header;
9481708Sstevel 	int		rv, size, nbytes;
9491708Sstevel 
9501708Sstevel #ifdef SGCN_DEBUG
9511708Sstevel 	prom_printf("sgcn_rw() rw = %X buf = %p len = %d\n",
9527656SSherry.Moore@Sun.COM 	    rw, buf, len);
9531708Sstevel #endif /* SGCN_DEBUG */
9541708Sstevel 	if (len == 0)
9551708Sstevel 		return (0);
9561708Sstevel 
9571708Sstevel 	/* sanity check */
9581708Sstevel 	if (buf == NULL || len < 0) {
9591708Sstevel 		sgcn_log_error(rw, EINVAL);
9601708Sstevel 		return (-1);
9611708Sstevel 	}
9621708Sstevel 
9631708Sstevel 	/* check IOSRAM contents and read pointers */
9641708Sstevel 	rv = sgcn_read_header(rw, &header);
9651708Sstevel 	if (rv != 0) {
9661708Sstevel 		sgcn_log_error(rw, EIO);
9671708Sstevel 		return (-1);
9681708Sstevel 	}
9691708Sstevel 	if (header.cnsram_magic != CNSRAM_MAGIC) {
9701708Sstevel 		sgcn_log_error(rw, EPROTO);
9711708Sstevel 		return (-1);
9721708Sstevel 	}
9731708Sstevel 
9741708Sstevel 	if (rw == RW_CONSOLE_READ)
9751708Sstevel 		size = header.cnsram_in_end - header.cnsram_in_begin;
9761708Sstevel 	else if (rw == RW_CONSOLE_WRITE)
9771708Sstevel 		size = header.cnsram_out_end - header.cnsram_out_begin;
9781708Sstevel 	if (size < 0) {
9791708Sstevel 		sgcn_log_error(rw, EPROTO);
9801708Sstevel 		return (-1);
9811708Sstevel 	}
9821708Sstevel 
9831708Sstevel 	if (rw == RW_CONSOLE_READ)
9841708Sstevel 		nbytes = circular_buffer_read(
9857656SSherry.Moore@Sun.COM 		    header.cnsram_in_begin,
9867656SSherry.Moore@Sun.COM 		    header.cnsram_in_end,
9877656SSherry.Moore@Sun.COM 		    header.cnsram_in_rdptr,
9887656SSherry.Moore@Sun.COM 		    header.cnsram_in_wrptr, buf, len);
9891708Sstevel 	else if (rw == RW_CONSOLE_WRITE)
9901708Sstevel 		nbytes = circular_buffer_write(
9917656SSherry.Moore@Sun.COM 		    header.cnsram_out_begin,
9927656SSherry.Moore@Sun.COM 		    header.cnsram_out_end,
9937656SSherry.Moore@Sun.COM 		    header.cnsram_out_rdptr,
9947656SSherry.Moore@Sun.COM 		    header.cnsram_out_wrptr, buf, len);
9951708Sstevel 
9961708Sstevel 	/*
9971708Sstevel 	 * error log was done in circular buffer routines,
9981708Sstevel 	 * no need to call sgcn_log_error() here
9991708Sstevel 	 */
10001708Sstevel 	if (nbytes < 0)
10011708Sstevel 		return (-1);
10021708Sstevel 
10031708Sstevel 	if (nbytes == 0)
10041708Sstevel 		return (0);
10051708Sstevel 
10061708Sstevel 	if (rw == RW_CONSOLE_READ) {
10071708Sstevel 		header.cnsram_in_rdptr =
10081708Sstevel 		    (header.cnsram_in_rdptr - header.cnsram_in_begin
10097656SSherry.Moore@Sun.COM 		    + nbytes)
10101708Sstevel 		    % size + header.cnsram_in_begin;
10111708Sstevel 		rv = iosram_write(SBBC_CONSOLE_KEY,
10127656SSherry.Moore@Sun.COM 		    OFFSETOF(header, cnsram_in_rdptr),
10137656SSherry.Moore@Sun.COM 		    POINTER(header, cnsram_in_rdptr),
10147656SSherry.Moore@Sun.COM 		    sizeof (header.cnsram_in_rdptr));
10151708Sstevel 	} else if (rw == RW_CONSOLE_WRITE) {
10161708Sstevel 		header.cnsram_out_wrptr =
10171708Sstevel 		    (header.cnsram_out_wrptr - header.cnsram_out_begin
10187656SSherry.Moore@Sun.COM 		    + nbytes)
10191708Sstevel 		    % size + header.cnsram_out_begin;
10201708Sstevel 		rv = iosram_write(SBBC_CONSOLE_KEY,
10217656SSherry.Moore@Sun.COM 		    OFFSETOF(header, cnsram_out_wrptr),
10227656SSherry.Moore@Sun.COM 		    POINTER(header, cnsram_out_wrptr),
10237656SSherry.Moore@Sun.COM 		    sizeof (header.cnsram_out_wrptr));
10241708Sstevel 	}
10251708Sstevel 	if (rv != 0) {
10261708Sstevel 		sgcn_log_error(rw, EIO);
10271708Sstevel 		return (-1);
10281708Sstevel 	}
10291708Sstevel 
10301708Sstevel 	return (nbytes);
10311708Sstevel }
10321708Sstevel 
10331708Sstevel /*
10341708Sstevel  * Circular buffer interfaces
10351708Sstevel  *
10361708Sstevel  * See sgcn.h for circular buffer structure
10371708Sstevel  *
10381708Sstevel  * The circular buffer is empty when read ptr == write ptr
10391708Sstevel  * and is full when read ptr is one ahead of write ptr
10401708Sstevel  */
10411708Sstevel /*
10421708Sstevel  * Write to circular buffer in IOSRAM
10431708Sstevel  * input:
10441708Sstevel  *	buf	buffer in main memory, contains data to be written
10451708Sstevel  *	len	length of data in bytes
10461708Sstevel  *	begin, end, rd, wr	buffer pointers
10471708Sstevel  * return value:
10481708Sstevel  *	actual bytes written.
10491708Sstevel  */
10501708Sstevel static int
circular_buffer_write(int begin,int end,int rd,int wr,caddr_t buf,int len)10511708Sstevel circular_buffer_write(int begin, int end, int rd, int wr, caddr_t buf, int len)
10521708Sstevel {
10531708Sstevel 	int		size, space, space_at_end;
10541708Sstevel 	int		rv = 0;
10551708Sstevel 
10561708Sstevel 	size = end - begin;
10571708Sstevel 	if (size <= 0) {
10581708Sstevel 		rv = EINVAL;
10591708Sstevel 		goto out;
10601708Sstevel 	}
10611708Sstevel 
10621708Sstevel 	if ((len = ((len >= size) ? (size-1) : len)) == 0)
10631708Sstevel 		return (0);	/* The buffer's full, so just return 0 now. */
10641708Sstevel 
10651708Sstevel 	space = (rd - wr + size - 1) % size;
10661708Sstevel 	len = min(len, space);
10671708Sstevel 	space_at_end = end - wr;
10681708Sstevel 
10691708Sstevel 	if (rd > wr || rd <= wr && space_at_end >= len) { /* one piece */
10701708Sstevel 		/* write console data */
10711708Sstevel 		rv = iosram_write(SBBC_CONSOLE_KEY, wr, buf, len);
10721708Sstevel 		if (rv != 0) goto out;
10731708Sstevel 	} else { /* break into two pieces because of circular buffer */
10741708Sstevel 		/* write console data */
10751708Sstevel 		if (space_at_end) {
10761708Sstevel 			rv = iosram_write(SBBC_CONSOLE_KEY,
10771708Sstevel 			    wr, buf, space_at_end);
10781708Sstevel 			if (rv != 0) goto out;
10791708Sstevel 		}
10801708Sstevel 		if (len - space_at_end) {
10811708Sstevel 			rv = iosram_write(SBBC_CONSOLE_KEY,
10821708Sstevel 			    begin, buf+space_at_end, len-space_at_end);
10831708Sstevel 			if (rv != 0) goto out;
10841708Sstevel 		}
10851708Sstevel 	}
10861708Sstevel 	return (len);
10871708Sstevel out:
10881708Sstevel 	sgcn_log_error(RW_CONSOLE_WRITE, rv);
10891708Sstevel 	return (-1);
10901708Sstevel }
10911708Sstevel 
10921708Sstevel /*
10931708Sstevel  * Read from circular buffer in IOSRAM
10941708Sstevel  * input:
10951708Sstevel  *	buf	preallocated buffer in memory
10961708Sstevel  *	len	size of buf
10971708Sstevel  *	begin, end, rd, wr	buffer pointers
10981708Sstevel  * return value:
10991708Sstevel  *	actual bytes read
11001708Sstevel  */
11011708Sstevel /* ARGSUSED */
11021708Sstevel static int
circular_buffer_read(int begin,int end,int rd,int wr,caddr_t buf,int len)11031708Sstevel circular_buffer_read(int begin, int end, int rd, int wr, caddr_t buf, int len)
11041708Sstevel {
11051708Sstevel 	int		size, nbytes, nbytes_at_end;
11061708Sstevel 	int		rv = 0;
11071708Sstevel 
11081708Sstevel 	size = end - begin;
11091708Sstevel 	if (size <= 0) {
11101708Sstevel 		rv = EINVAL;
11111708Sstevel 		goto out;
11121708Sstevel 	}
11131708Sstevel 	nbytes = (wr - rd + size) % size;
11141708Sstevel 
11151708Sstevel 	nbytes = min(nbytes, len);
11161708Sstevel 
11171708Sstevel 	if (wr > rd) { /* one piece */
11181708Sstevel 		rv = iosram_read(SBBC_CONSOLE_KEY, rd, buf, nbytes);
11191708Sstevel 		if (rv != 0) goto out;
11201708Sstevel 	} else { /* break into two pieces because of circular buffer */
11211708Sstevel 		nbytes_at_end = min(nbytes, end - rd);
11221708Sstevel 		/* read console data */
11231708Sstevel 		if (nbytes_at_end) {
11241708Sstevel 			rv = iosram_read(SBBC_CONSOLE_KEY,
11251708Sstevel 			    rd, buf, nbytes_at_end);
11261708Sstevel 			if (rv != 0) goto out;
11271708Sstevel 		}
11281708Sstevel 		if (nbytes-nbytes_at_end) {
11291708Sstevel 			rv = iosram_read(SBBC_CONSOLE_KEY,
11301708Sstevel 			    begin, buf+nbytes_at_end, nbytes-nbytes_at_end);
11311708Sstevel 			if (rv != 0) goto out;
11321708Sstevel 		}
11331708Sstevel 	}
11341708Sstevel 	return (nbytes);
11351708Sstevel out:
11361708Sstevel 	sgcn_log_error(RW_CONSOLE_READ, rv);
11371708Sstevel 	return (-1);
11381708Sstevel }
11391708Sstevel 
11401708Sstevel /*
11411708Sstevel  * Check for abort character sequence, copied from zs_async.c
11421708Sstevel  */
11431708Sstevel #define	CNTRL(c) ((c)&037)
11441708Sstevel 
11451708Sstevel static boolean_t
abort_charseq_recognize(uchar_t ch)11461708Sstevel abort_charseq_recognize(uchar_t ch)
11471708Sstevel {
11481708Sstevel 	static int state = 0;
11491708Sstevel 	static char sequence[] = { '\r', '~', CNTRL('b') };
11501708Sstevel 
11511708Sstevel 	if (ch == sequence[state]) {
11521708Sstevel 		if (++state >= sizeof (sequence)) {
11531708Sstevel 			state = 0;
11541708Sstevel 			return (B_TRUE);
11551708Sstevel 		}
11561708Sstevel 	} else {
11571708Sstevel 		state = (ch == sequence[0]) ? 1 : 0;
11581708Sstevel 	}
11591708Sstevel 	return (B_FALSE);
11601708Sstevel }
11611708Sstevel 
11621708Sstevel static void
sg_abort_seq_handler(char * msg)11631708Sstevel sg_abort_seq_handler(char *msg)
11641708Sstevel {
11651708Sstevel 	char	key_switch;
11661708Sstevel 	int	rv;
11671708Sstevel 
11681708Sstevel 	/* read virtual keyswitch position from IOSRAM */
11691708Sstevel 	rv = iosram_read(SBBC_KEYSWITCH_KEY, 0, &key_switch, 1);
11701708Sstevel 	if (rv != 0) {
11711708Sstevel 		/* default to not secure if read failed */
11721708Sstevel 		cmn_err(CE_NOTE, "!Read keyswitch failed (%d)", rv);
11731708Sstevel 		key_switch = 0;
11741708Sstevel 	}
11751708Sstevel 	if (key_switch & SG_KEYSWITCH_POSN_SECURE) {
11761708Sstevel 		cmn_err(CE_NOTE, "!Keyswitch is in secure mode");
11771708Sstevel 	} else {
11781708Sstevel 		debug_enter(msg);
11791708Sstevel 	}
11801708Sstevel }
11811708Sstevel 
11821708Sstevel static int
sgcn_rsrv(queue_t * q)11831708Sstevel sgcn_rsrv(queue_t *q)
11841708Sstevel {
11851708Sstevel 	mblk_t	*mp;
11861708Sstevel 
11871708Sstevel 	if (sgcn_stopped == TRUE) {
11881708Sstevel 		return (0);
11891708Sstevel 	}
11901708Sstevel 
11911708Sstevel 	mutex_enter(&sgcn_state->sgcn_lock);
11921708Sstevel 	sgcn_state->sgcn_sc_active = gethrestime_sec();
11931708Sstevel 	mutex_exit(&sgcn_state->sgcn_lock);
11941708Sstevel 
11951708Sstevel 	while ((mp = getq(q)) != NULL) {
11961708Sstevel 		if (canputnext(q)) {
11971708Sstevel 			putnext(q, mp);
11981708Sstevel 		} else if (mp->b_datap->db_type >= QPCTL) {
1199*11311SSurya.Prakki@Sun.COM 			(void) putbq(q, mp);
12001708Sstevel 		}
12011708Sstevel 	}
12021708Sstevel 
12031708Sstevel 	return (0);
12041708Sstevel }
12051708Sstevel 
12061708Sstevel /* ARGSUSED */
12071708Sstevel static int
sgcn_wsrv(queue_t * q)12081708Sstevel sgcn_wsrv(queue_t *q)
12091708Sstevel {
12101708Sstevel 	if (sgcn_stopped == TRUE)
12111708Sstevel 		return (0);
12121708Sstevel 
12131708Sstevel 	mutex_enter(&sgcn_state->sgcn_lock);
12141708Sstevel 	sgcn_state->sgcn_sc_active = gethrestime_sec();
12151708Sstevel 	mutex_exit(&sgcn_state->sgcn_lock);
12161708Sstevel 
12171708Sstevel 	if (sgcn_state->sgcn_writeq != NULL)
12181708Sstevel 		sgcn_start();
12191708Sstevel 
12201708Sstevel 	return (0);
12211708Sstevel }
1222